Custom compression methods
Hello hackers!
I've attached a patch that implements custom compression
methods. This patch is based on Nikita Glukhov's code (which he hasn't
publish in mailing lists) for jsonb compression. This is early but
working version of the patch, and there are still few fixes and features
that should be implemented (like pg_dump support and support of
compression options for types), and it requires more testing. But I'd
like to get some feedback at the current stage first.
There's been a proposal [1]/messages/by-id/CAPpHfdsdTA5uZeq6MNXL5ZRuNx+Sig4ykWzWEAfkC6ZKMDy6=Q@mail.gmail.com -- --- Ildus Kurbangaliev Postgres Professional: http://www.postgrespro.com Russian Postgres Company of Alexander Korotkov and some discussion
about custom compression methods before. This is an implementation of
per-datum compression. Syntax is similar to the one in proposal but not
the same.
Syntax:
CREATE COMPRESSION METHOD <cmname> HANDLER <compression_handler>;
DROP COMPRESSION METHOD <cmname>;
Compression handler is a function that returns a structure containing
compression routines:
- configure - function called when the compression method applied to
an attribute
- drop - called when the compression method is removed from an attribute
- compress - compress function
- decompress - decompress function
User can create compressed columns with the commands below:
CREATE TABLE t(a tsvector COMPRESSED <cmname> WITH <options>);
ALTER TABLE t ALTER COLUMN a SET COMPRESSED <cmname> WITH <options>;
ALTER TABLE t ALTER COLUMN a SET NOT COMPRESSED;
Also there is syntax of binding compression methods to types:
ALTER TYPE <type> SET COMPRESSED <cmname>;
ALTER TYPE <type> SET NOT COMPRESSED;
There are two new tables in the catalog, pg_compression and
pg_compression_opt. pg_compression is used as storage of compression
methods, and pg_compression_opt is used to store specific compression
options for particular column.
When user binds a compression method to some column a new record in
pg_compression_opt is created and all further attribute values will
contain compression options Oid while old values will remain unchanged.
And when we alter a compression method for
the attribute it won't change previous record in pg_compression_opt.
Instead it'll create a new one and new values will be stored
with new Oid. That way there is no need of recompression of the old
tuples. And also tuples containing compressed datums can be copied to
other tables so records in pg_compression_opt shouldn't be removed. In
the current patch they can be removed with DROP COMPRESSION METHOD
CASCADE, but after that decompression won't be possible on compressed
tuples. Maybe CASCADE should keep compression options.
I haven't changed the base logic of working with compressed datums. It
means that custom compressed datums behave exactly the same as current
LZ compressed datums, and the logic differs only in toast_compress_datum
and toast_decompress_datum.
This patch doesn't break backward compability and should work seamlessly
with older version of database. I used one of two free bits in
`va_rawsize` from `varattrib_4b->va_compressed` as flag of custom
compressed datums. Also I renamed it to `va_info` since it contains not
only rawsize now.
The patch also includes custom compression method for tsvector which is
used in tests.
[1]: /messages/by-id/CAPpHfdsdTA5uZeq6MNXL5ZRuNx+Sig4ykWzWEAfkC6ZKMDy6=Q@mail.gmail.com -- --- Ildus Kurbangaliev Postgres Professional: http://www.postgrespro.com Russian Postgres Company
/messages/by-id/CAPpHfdsdTA5uZeq6MNXL5ZRuNx+Sig4ykWzWEAfkC6ZKMDy6=Q@mail.gmail.com
--
---
Ildus Kurbangaliev
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company
Attachments:
custom_compression_methods_v1.patchtext/x-patchDownload
diff --git a/contrib/test_decoding/expected/ddl.out b/contrib/test_decoding/expected/ddl.out
index 1e22c1eefc..766ced401f 100644
--- a/contrib/test_decoding/expected/ddl.out
+++ b/contrib/test_decoding/expected/ddl.out
@@ -416,12 +416,12 @@ CREATE TABLE replication_metadata (
WITH (user_catalog_table = true)
;
\d+ replication_metadata
- Table "public.replication_metadata"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------+---------+-----------+----------+--------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | |
- relation | name | | not null | | plain | |
- options | text[] | | | | extended | |
+ Table "public.replication_metadata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+----------+---------+-----------+----------+--------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | |
+ relation | name | | not null | | plain | | |
+ options | text[] | | | | extended | | |
Indexes:
"replication_metadata_pkey" PRIMARY KEY, btree (id)
Options: user_catalog_table=true
@@ -430,12 +430,12 @@ INSERT INTO replication_metadata(relation, options)
VALUES ('foo', ARRAY['a', 'b']);
ALTER TABLE replication_metadata RESET (user_catalog_table);
\d+ replication_metadata
- Table "public.replication_metadata"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------+---------+-----------+----------+--------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | |
- relation | name | | not null | | plain | |
- options | text[] | | | | extended | |
+ Table "public.replication_metadata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+----------+---------+-----------+----------+--------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | |
+ relation | name | | not null | | plain | | |
+ options | text[] | | | | extended | | |
Indexes:
"replication_metadata_pkey" PRIMARY KEY, btree (id)
@@ -443,12 +443,12 @@ INSERT INTO replication_metadata(relation, options)
VALUES ('bar', ARRAY['a', 'b']);
ALTER TABLE replication_metadata SET (user_catalog_table = true);
\d+ replication_metadata
- Table "public.replication_metadata"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------+---------+-----------+----------+--------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | |
- relation | name | | not null | | plain | |
- options | text[] | | | | extended | |
+ Table "public.replication_metadata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+----------+---------+-----------+----------+--------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | |
+ relation | name | | not null | | plain | | |
+ options | text[] | | | | extended | | |
Indexes:
"replication_metadata_pkey" PRIMARY KEY, btree (id)
Options: user_catalog_table=true
@@ -461,13 +461,13 @@ ALTER TABLE replication_metadata ALTER COLUMN rewritemeornot TYPE text;
ERROR: cannot rewrite table "replication_metadata" used as a catalog table
ALTER TABLE replication_metadata SET (user_catalog_table = false);
\d+ replication_metadata
- Table "public.replication_metadata"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------------+---------+-----------+----------+--------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | |
- relation | name | | not null | | plain | |
- options | text[] | | | | extended | |
- rewritemeornot | integer | | | | plain | |
+ Table "public.replication_metadata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+----------------+---------+-----------+----------+--------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | |
+ relation | name | | not null | | plain | | |
+ options | text[] | | | | extended | | |
+ rewritemeornot | integer | | | | plain | | |
Indexes:
"replication_metadata_pkey" PRIMARY KEY, btree (id)
Options: user_catalog_table=false
diff --git a/src/backend/access/common/indextuple.c b/src/backend/access/common/indextuple.c
index 138671410a..145f59b949 100644
--- a/src/backend/access/common/indextuple.c
+++ b/src/backend/access/common/indextuple.c
@@ -92,7 +92,8 @@ index_form_tuple(TupleDesc tupleDescriptor,
VARSIZE(DatumGetPointer(untoasted_values[i])) > TOAST_INDEX_TARGET &&
(att->attstorage == 'x' || att->attstorage == 'm'))
{
- Datum cvalue = toast_compress_datum(untoasted_values[i]);
+ Datum cvalue = toast_compress_datum(untoasted_values[i],
+ TupleDescAttrCompression(tupleDescriptor, i));
if (DatumGetPointer(cvalue) != NULL)
{
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index ec10762529..21e375ad96 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -935,11 +935,48 @@ untransformRelOptions(Datum options)
val = (Node *) makeString(pstrdup(p));
}
result = lappend(result, makeDefElem(pstrdup(s), val, -1));
+ pfree(s);
}
return result;
}
+char *
+formatRelOptions(List *options)
+{
+ StringInfoData buf;
+ ListCell *cell;
+
+ initStringInfo(&buf);
+
+ foreach(cell, options)
+ {
+ DefElem *def = (DefElem *) lfirst(cell);
+
+ appendStringInfo(&buf, "%s%s=%s", buf.len > 0 ? ", " : "",
+ def->defname, defGetString(def));
+ }
+
+ return buf.data;
+}
+
+void
+freeRelOptions(List *options)
+{
+ ListCell *cell;
+
+ Assert(options != NIL);
+ foreach(cell, options)
+ {
+ DefElem *def = (DefElem *) lfirst(cell);
+
+ pfree(def->defname);
+ pfree(defGetString(def));
+ pfree(def->arg);
+ }
+ list_free_deep(options);
+}
+
/*
* Extract and parse reloptions from a pg_class tuple.
*
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index 4436c86361..7e79b89beb 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -19,8 +19,10 @@
#include "postgres.h"
+#include "access/compression.h"
#include "access/hash.h"
#include "access/htup_details.h"
+#include "access/reloptions.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
@@ -65,6 +67,7 @@ CreateTemplateTupleDesc(int natts, bool hasoid)
desc->tdtypmod = -1;
desc->tdhasoid = hasoid;
desc->tdrefcount = -1; /* assume not reference-counted */
+ desc->tdcompression = NULL;
return desc;
}
@@ -91,6 +94,27 @@ CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs)
return desc;
}
+static AttributeCompression *
+TupleDescCopyCompression(TupleDesc tupdesc)
+{
+ AttributeCompression *src = tupdesc->tdcompression;
+ AttributeCompression *dst = (AttributeCompression *)
+ palloc0(sizeof(*dst) * tupdesc->natts);
+ int i;
+
+ for (i = 0; i < tupdesc->natts; i++)
+ if (src[i].routine)
+ {
+ dst[i].routine = palloc(sizeof(*dst[i].routine));
+ memcpy(dst[i].routine, src[i].routine, sizeof(*dst[i].routine));
+
+ dst[i].cmoptoid = src[i].cmoptoid;
+ dst[i].options = copyObject(src[i].options);
+ }
+
+ return dst;
+}
+
/*
* CreateTupleDescCopy
* This function creates a new TupleDesc by copying from an existing
@@ -119,6 +143,9 @@ CreateTupleDescCopy(TupleDesc tupdesc)
desc->tdtypeid = tupdesc->tdtypeid;
desc->tdtypmod = tupdesc->tdtypmod;
+ if (tupdesc->tdcompression)
+ desc->tdcompression = TupleDescCopyCompression(tupdesc);
+
return desc;
}
@@ -178,6 +205,9 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc)
desc->constr = cpy;
}
+ if (tupdesc->tdcompression)
+ desc->tdcompression = TupleDescCopyCompression(tupdesc);
+
desc->tdtypeid = tupdesc->tdtypeid;
desc->tdtypmod = tupdesc->tdtypmod;
@@ -226,6 +256,7 @@ TupleDescCopyEntry(TupleDesc dst, AttrNumber dstAttno,
dstAtt->attnotnull = false;
dstAtt->atthasdef = false;
dstAtt->attidentity = '\0';
+ dstAtt->attcompression = InvalidOid;
}
/*
@@ -271,6 +302,23 @@ FreeTupleDesc(TupleDesc tupdesc)
pfree(tupdesc->constr);
}
+ if (tupdesc->tdcompression)
+ {
+ for (i = 0; i < tupdesc->natts; i++)
+ {
+ AttributeCompression *ac = &tupdesc->tdcompression[i];
+
+ if (ac->routine)
+ {
+ pfree(ac->routine);
+ if (ac->options)
+ freeRelOptions(ac->options);
+ }
+ }
+
+ pfree(tupdesc->tdcompression);
+ }
+
pfree(tupdesc);
}
@@ -380,6 +428,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
return false;
if (attr1->attcollation != attr2->attcollation)
return false;
+ if (attr1->attcompression != attr2->attcompression)
+ return false;
/* attacl, attoptions and attfdwoptions are not even present... */
}
@@ -442,6 +492,22 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
}
else if (tupdesc2->constr != NULL)
return false;
+
+ if ((tupdesc1->tdcompression == NULL) != (tupdesc2->tdcompression == NULL))
+ return false;
+
+ if (tupdesc1->tdcompression == NULL)
+ return true;
+
+ for (i = 0; i < tupdesc1->natts; i++)
+ {
+ AttributeCompression *ac1 = &tupdesc1->tdcompression[i];
+ AttributeCompression *ac2 = &tupdesc2->tdcompression[i];
+
+ if (ac1->cmoptoid != ac2->cmoptoid)
+ return false;
+ }
+
return true;
}
@@ -547,6 +613,7 @@ TupleDescInitEntry(TupleDesc desc,
att->attalign = typeForm->typalign;
att->attstorage = typeForm->typstorage;
att->attcollation = typeForm->typcollation;
+ att->attcompression = InvalidOid;
ReleaseSysCache(tuple);
}
@@ -659,6 +726,26 @@ TupleDescInitEntryCollation(TupleDesc desc,
TupleDescAttr(desc, attributeNumber - 1)->attcollation = collationid;
}
+void
+TupleDescInitAttrCompression(TupleDesc desc,
+ AttrNumber attnum,
+ Oid cmoptoid)
+{
+ CompressionMethodRoutine *cmr;
+ AttributeCompression *ac;
+
+ cmr = GetCompressionRoutine(cmoptoid);
+
+ /* initialize array with compression routines */
+ if (!desc->tdcompression)
+ desc->tdcompression = (AttributeCompression *)
+ palloc0(desc->natts * sizeof(AttributeCompression));
+
+ ac = TupleDescAttrCompression(desc, attnum - 1);
+ ac->routine = cmr;
+ ac->options = GetCompressionOptionsList(cmoptoid);
+ ac->cmoptoid = cmoptoid;
+}
/*
* BuildDescForRelation
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index 5a8f1dab83..694e16f103 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -30,8 +30,10 @@
#include <unistd.h>
#include <fcntl.h>
+#include "access/compression.h"
#include "access/genam.h"
#include "access/heapam.h"
+#include "access/reloptions.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -53,19 +55,43 @@
typedef struct toast_compress_header
{
int32 vl_len_; /* varlena header (do not touch directly!) */
- int32 rawsize;
+ uint32 info; /* flags (2 bits) and rawsize */
} toast_compress_header;
+/*
+ * If the compression method were used, then data also contains
+ * Oid of compression options
+ */
+typedef struct toast_compress_header_custom
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ uint32 info; /* flags (2 high bits) and rawsize */
+ Oid cmoptoid; /* Oid from pg_compression_opt */
+} toast_compress_header_custom;
+
+#define RAWSIZEMASK (0x3FFFFFFFU)
+
/*
* Utilities for manipulation of header information for compressed
* toast entries.
*/
-#define TOAST_COMPRESS_HDRSZ ((int32) sizeof(toast_compress_header))
-#define TOAST_COMPRESS_RAWSIZE(ptr) (((toast_compress_header *) (ptr))->rawsize)
+#define TOAST_COMPRESS_HDRSZ ((int32) sizeof(toast_compress_header))
+#define TOAST_COMPRESS_HDRSZ_CUSTOM ((int32) sizeof(toast_compress_header_custom))
+#define TOAST_COMPRESS_RAWSIZE(ptr) (((toast_compress_header *) (ptr))->info & RAWSIZEMASK)
#define TOAST_COMPRESS_RAWDATA(ptr) \
(((char *) (ptr)) + TOAST_COMPRESS_HDRSZ)
#define TOAST_COMPRESS_SET_RAWSIZE(ptr, len) \
- (((toast_compress_header *) (ptr))->rawsize = (len))
+do { \
+ ((toast_compress_header *) (ptr))->info &= 0xC0000000; \
+ ((toast_compress_header *) (ptr))->info |= ((uint32)(len) & RAWSIZEMASK); \
+} while (0)
+#define TOAST_COMPRESS_SET_CMOPTOID(ptr, oid) \
+ (((toast_compress_header_custom *) (ptr))->cmoptoid = (oid))
+#define TOAST_COMPRESS_SET_CUSTOM(ptr) \
+do { \
+ (((toast_compress_header *) (ptr))->info |= (1 << 31)); \
+ (((toast_compress_header *) (ptr))->info &= ~(1 << 30)); \
+} while (0)
static void toast_delete_datum(Relation rel, Datum value, bool is_speculative);
static Datum toast_save_datum(Relation rel, Datum value,
@@ -773,7 +799,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
if (TupleDescAttr(tupleDesc, i)->attstorage == 'x')
{
old_value = toast_values[i];
- new_value = toast_compress_datum(old_value);
+ new_value = toast_compress_datum(old_value,
+ TupleDescAttrCompression(tupleDesc, i));
if (DatumGetPointer(new_value) != NULL)
{
@@ -914,7 +941,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
*/
i = biggest_attno;
old_value = toast_values[i];
- new_value = toast_compress_datum(old_value);
+ new_value = toast_compress_datum(old_value,
+ TupleDescAttrCompression(tupleDesc, i));
if (DatumGetPointer(new_value) != NULL)
{
@@ -1229,7 +1257,9 @@ toast_flatten_tuple_to_datum(HeapTupleHeader tup,
if (VARATT_IS_EXTERNAL(new_value) ||
VARATT_IS_COMPRESSED(new_value))
{
- new_value = heap_tuple_untoast_attr(new_value);
+ struct varlena *untoasted_value = heap_tuple_untoast_attr(new_value);
+
+ new_value = untoasted_value;
toast_values[i] = PointerGetDatum(new_value);
toast_free[i] = true;
}
@@ -1353,7 +1383,6 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
return new_tuple;
}
-
/* ----------
* toast_compress_datum -
*
@@ -1368,11 +1397,12 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
* ----------
*/
Datum
-toast_compress_datum(Datum value)
+toast_compress_datum(Datum value, AttributeCompression * ac)
{
struct varlena *tmp;
- int32 valsize = VARSIZE_ANY_EXHDR(DatumGetPointer(value));
- int32 len;
+ int32 valsize,
+ len = 0;
+ bool custom = (ac != NULL);
Assert(!VARATT_IS_EXTERNAL(DatumGetPointer(value)));
Assert(!VARATT_IS_COMPRESSED(DatumGetPointer(value)));
@@ -1381,12 +1411,27 @@ toast_compress_datum(Datum value)
* No point in wasting a palloc cycle if value size is out of the allowed
* range for compression
*/
- if (valsize < PGLZ_strategy_default->min_input_size ||
- valsize > PGLZ_strategy_default->max_input_size)
+ valsize = VARSIZE_ANY_EXHDR(DatumGetPointer(value));
+ if (!custom && (valsize < PGLZ_strategy_default->min_input_size ||
+ valsize > PGLZ_strategy_default->max_input_size))
return PointerGetDatum(NULL);
- tmp = (struct varlena *) palloc(PGLZ_MAX_OUTPUT(valsize) +
- TOAST_COMPRESS_HDRSZ);
+ if (custom)
+ {
+ Assert(ac->routine);
+ tmp = ac->routine->compress(ac, (const struct varlena *) value);
+ if (!tmp)
+ return PointerGetDatum(NULL);
+ }
+ else
+ {
+ tmp = (struct varlena *) palloc(PGLZ_MAX_OUTPUT(valsize) +
+ TOAST_COMPRESS_HDRSZ);
+ len = pglz_compress(VARDATA_ANY(DatumGetPointer(value)),
+ valsize,
+ TOAST_COMPRESS_RAWDATA(tmp),
+ PGLZ_strategy_default);
+ }
/*
* We recheck the actual size even if pglz_compress() reports success,
@@ -1398,11 +1443,7 @@ toast_compress_datum(Datum value)
* only one header byte and no padding if the value is short enough. So
* we insist on a savings of more than 2 bytes to ensure we have a gain.
*/
- len = pglz_compress(VARDATA_ANY(DatumGetPointer(value)),
- valsize,
- TOAST_COMPRESS_RAWDATA(tmp),
- PGLZ_strategy_default);
- if (len >= 0 &&
+ if (!custom && len >= 0 &&
len + TOAST_COMPRESS_HDRSZ < valsize - 2)
{
TOAST_COMPRESS_SET_RAWSIZE(tmp, valsize);
@@ -1410,6 +1451,14 @@ toast_compress_datum(Datum value)
/* successful compression */
return PointerGetDatum(tmp);
}
+ else if (custom && VARSIZE(tmp) < valsize - 2)
+ {
+ TOAST_COMPRESS_SET_CUSTOM(tmp);
+ TOAST_COMPRESS_SET_RAWSIZE(tmp, valsize);
+ TOAST_COMPRESS_SET_CMOPTOID(tmp, ac->cmoptoid);
+ /* successful compression */
+ return PointerGetDatum(tmp);
+ }
else
{
/* incompressible data */
@@ -2280,15 +2329,29 @@ toast_decompress_datum(struct varlena *attr)
Assert(VARATT_IS_COMPRESSED(attr));
- result = (struct varlena *)
- palloc(TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
- SET_VARSIZE(result, TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
-
- if (pglz_decompress(TOAST_COMPRESS_RAWDATA(attr),
- VARSIZE(attr) - TOAST_COMPRESS_HDRSZ,
- VARDATA(result),
- TOAST_COMPRESS_RAWSIZE(attr)) < 0)
- elog(ERROR, "compressed data is corrupted");
+ if (VARATT_IS_CUSTOM_COMPRESSED(attr))
+ {
+ CompressionMethodRoutine *cmr;
+ toast_compress_header_custom *hdr = \
+ (toast_compress_header_custom *) attr;
+ List *options;
+
+ /* TODO: add some cache for options and routines */
+ cmr = GetCompressionRoutine(hdr->cmoptoid);
+ options = GetCompressionOptionsList(hdr->cmoptoid);
+ result = cmr->decompress(attr, options);
+ }
+ else
+ {
+ result = (struct varlena *)
+ palloc(TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
+ SET_VARSIZE(result, TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
+ if (pglz_decompress(TOAST_COMPRESS_RAWDATA(attr),
+ VARSIZE(attr) - TOAST_COMPRESS_HDRSZ,
+ VARDATA(result),
+ TOAST_COMPRESS_RAWSIZE(attr)) < 0)
+ elog(ERROR, "compressed data is corrupted");
+ }
return result;
}
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 0453fd4ac1..53feb17abc 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -718,6 +718,7 @@ DefineAttr(char *name, char *type, int attnum, int nullness)
attrtypes[attnum]->attcacheoff = -1;
attrtypes[attnum]->atttypmod = -1;
attrtypes[attnum]->attislocal = true;
+ attrtypes[attnum]->attcompression = InvalidOid;
if (nullness == BOOTCOL_NULL_FORCE_NOT_NULL)
{
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index fd33426bad..c7cea974b1 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -46,7 +46,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_collation.h pg_partitioned_table.h pg_range.h pg_transform.h \
pg_sequence.h pg_publication.h pg_publication_rel.h pg_subscription.h \
pg_subscription_rel.h toasting.h indexing.h \
- toasting.h indexing.h \
+ pg_compression.h pg_compression_opt.h \
)
# location of Catalog.pm
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index ccde66a7dd..fd733a34a0 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -3340,6 +3340,8 @@ static const char *const no_priv_msg[MAX_ACL_KIND] =
gettext_noop("permission denied for publication %s"),
/* ACL_KIND_SUBSCRIPTION */
gettext_noop("permission denied for subscription %s"),
+ /* ACL_KIND_COMPRESSION_METHOD */
+ gettext_noop("permission denied for compression method %s"),
};
static const char *const not_owner_msg[MAX_ACL_KIND] =
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 6fffc290fa..faaf7fb7f1 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -28,6 +28,8 @@
#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_collation_fn.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_conversion.h"
@@ -173,7 +175,9 @@ static const Oid object_classes[] = {
PublicationRelationId, /* OCLASS_PUBLICATION */
PublicationRelRelationId, /* OCLASS_PUBLICATION_REL */
SubscriptionRelationId, /* OCLASS_SUBSCRIPTION */
- TransformRelationId /* OCLASS_TRANSFORM */
+ TransformRelationId, /* OCLASS_TRANSFORM */
+ CompressionMethodRelationId, /* OCLASS_COMPRESSION_METHOD */
+ CompressionOptRelationId, /* OCLASS_COMPRESSION_OPTIONS */
};
@@ -1271,6 +1275,14 @@ doDeletion(const ObjectAddress *object, int flags)
DropTransformById(object->objectId);
break;
+ case OCLASS_COMPRESSION_METHOD:
+ RemoveCompressionMethodById(object->objectId);
+ break;
+
+ case OCLASS_COMPRESSION_OPTIONS:
+ RemoveCompressionOptionsById(object->objectId);
+ break;
+
/*
* These global object types are not supported here.
*/
@@ -2459,6 +2471,12 @@ getObjectClass(const ObjectAddress *object)
case TransformRelationId:
return OCLASS_TRANSFORM;
+
+ case CompressionMethodRelationId:
+ return OCLASS_COMPRESSION_METHOD;
+
+ case CompressionOptRelationId:
+ return OCLASS_COMPRESSION_OPTIONS;
}
/* shouldn't get here */
diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl
index 2eebb061b7..36c7fa0484 100644
--- a/src/backend/catalog/genbki.pl
+++ b/src/backend/catalog/genbki.pl
@@ -449,6 +449,7 @@ sub emit_pgattr_row
attisdropped => 'f',
attislocal => 't',
attinhcount => '0',
+ attcompression=> '0',
attacl => '_null_',
attoptions => '_null_',
attfdwoptions => '_null_');
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 45ee9ac8b9..88fe220e2e 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -29,8 +29,10 @@
*/
#include "postgres.h"
+#include "access/compression.h"
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/reloptions.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "access/xact.h"
@@ -44,6 +46,8 @@
#include "catalog/partition.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_foreign_table.h"
@@ -628,6 +632,7 @@ InsertPgAttributeTuple(Relation pg_attribute_rel,
values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal);
values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount);
values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(new_attribute->attcollation);
+ values[Anum_pg_attribute_attcompression - 1] = ObjectIdGetDatum(new_attribute->attcompression);
/* start out with empty permissions and empty options */
nulls[Anum_pg_attribute_attacl - 1] = true;
@@ -707,6 +712,13 @@ AddNewAttributeTuples(Oid new_rel_oid,
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+
+ if (OidIsValid(attr->attcompression))
+ {
+ ObjectAddressSet(referenced, CompressionOptRelationId,
+ attr->attcompression);
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
}
/*
@@ -1453,6 +1465,22 @@ DeleteRelationTuple(Oid relid)
heap_close(pg_class_desc, RowExclusiveLock);
}
+static void
+DropAttributeCompression(Form_pg_attribute att)
+{
+ CompressionMethodRoutine *cmr = GetCompressionRoutine(att->attcompression);
+
+ if (cmr->drop)
+ {
+ bool attisdropped = att->attisdropped;
+ List *options = GetCompressionOptionsList(att->attcompression);
+
+ att->attisdropped = true;
+ cmr->drop(att, options);
+ att->attisdropped = attisdropped;
+ }
+}
+
/*
* DeleteAttributeTuples
*
@@ -1483,7 +1511,14 @@ DeleteAttributeTuples(Oid relid)
/* Delete all the matching tuples */
while ((atttup = systable_getnext(scan)) != NULL)
+ {
+ Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(atttup);
+
+ if (OidIsValid(att->attcompression))
+ DropAttributeCompression(att);
+
CatalogTupleDelete(attrel, &atttup->t_self);
+ }
/* Clean up after the scan */
systable_endscan(scan);
@@ -1576,6 +1611,8 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
else
{
/* Dropping user attributes is lots harder */
+ if (OidIsValid(attStruct->attcompression))
+ DropAttributeCompression(attStruct);
/* Mark the attribute as dropped */
attStruct->attisdropped = true;
@@ -1597,6 +1634,8 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
/* We don't want to keep stats for it anymore */
attStruct->attstattarget = 0;
+ attStruct->attcompression = InvalidOid;
+
/*
* Change the column name to something that isn't likely to conflict
*/
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index c7b2f031f0..11dc107ab7 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -393,6 +393,7 @@ ConstructTupleDescriptor(Relation heapRelation,
to->atttypmod = exprTypmod(indexkey);
to->attislocal = true;
to->attcollation = collationObjectId[i];
+ to->attcompression = InvalidOid;
ReleaseSysCache(tuple);
@@ -471,6 +472,7 @@ ConstructTupleDescriptor(Relation heapRelation,
to->attbyval = typeTup->typbyval;
to->attalign = typeTup->typalign;
to->attstorage = typeTup->typstorage;
+ to->attcompression = InvalidOid;
ReleaseSysCache(tuple);
}
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 6cac2dfd1d..d2b5285a5a 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -29,6 +29,8 @@
#include "catalog/pg_default_acl.h"
#include "catalog/pg_event_trigger.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_conversion.h"
@@ -490,6 +492,30 @@ static const ObjectPropertyType ObjectProperty[] =
InvalidAttrNumber, /* no ACL (same as relation) */
ACL_KIND_STATISTICS,
true
+ },
+ {
+ CompressionMethodRelationId,
+ CompressionMethodOidIndexId,
+ COMPRESSIONMETHODOID,
+ COMPRESSIONMETHODNAME,
+ Anum_pg_compression_cmname,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ true
+ },
+ {
+ CompressionOptRelationId,
+ CompressionOptionsOidIndexId,
+ COMPRESSIONOPTIONSOID,
+ -1,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ false,
}
};
@@ -712,6 +738,10 @@ static const struct object_type_map
/* OBJECT_STATISTIC_EXT */
{
"statistics object", OBJECT_STATISTIC_EXT
+ },
+ /* OCLASS_COMPRESSION_METHOD */
+ {
+ "compression method", OBJECT_COMPRESSION_METHOD
}
};
@@ -876,6 +906,7 @@ get_object_address(ObjectType objtype, Node *object,
case OBJECT_ACCESS_METHOD:
case OBJECT_PUBLICATION:
case OBJECT_SUBSCRIPTION:
+ case OBJECT_COMPRESSION_METHOD:
address = get_object_address_unqualified(objtype,
(Value *) object, missing_ok);
break;
@@ -1182,6 +1213,11 @@ get_object_address_unqualified(ObjectType objtype,
address.objectId = get_subscription_oid(name, missing_ok);
address.objectSubId = 0;
break;
+ case OBJECT_COMPRESSION_METHOD:
+ address.classId = CompressionMethodRelationId;
+ address.objectId = get_compression_method_oid(name, missing_ok);
+ address.objectSubId = 0;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
/* placate compiler, which doesn't know elog won't return */
@@ -2139,6 +2175,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_SCHEMA:
case OBJECT_SUBSCRIPTION:
case OBJECT_TABLESPACE:
+ case OBJECT_COMPRESSION_METHOD:
if (list_length(name) != 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -2395,12 +2432,14 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
case OBJECT_ACCESS_METHOD:
+ case OBJECT_COMPRESSION_METHOD:
/* We treat these object types as being owned by superusers */
if (!superuser_arg(roleid))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser")));
break;
+
case OBJECT_STATISTIC_EXT:
if (!pg_statistics_object_ownercheck(address.objectId, roleid))
aclcheck_error_type(ACLCHECK_NOT_OWNER, address.objectId);
@@ -3398,6 +3437,27 @@ getObjectDescription(const ObjectAddress *object)
break;
}
+ case OCLASS_COMPRESSION_METHOD:
+ {
+ char *name = get_compression_method_name(object->objectId);
+
+ if (!name)
+ elog(ERROR, "cache lookup failed for compression method %u",
+ object->objectId);
+ appendStringInfo(&buffer, _("compression method %s"), name);
+ pfree(name);
+ break;
+ }
+
+ case OCLASS_COMPRESSION_OPTIONS:
+ {
+ char *name = get_compression_method_name_for_opt(object->objectId);
+
+ appendStringInfo(&buffer, _("compression options for %s"), name);
+ pfree(name);
+ break;
+ }
+
case OCLASS_TRANSFORM:
{
HeapTuple trfTup;
@@ -3919,6 +3979,14 @@ getObjectTypeDescription(const ObjectAddress *object)
appendStringInfoString(&buffer, "transform");
break;
+ case OCLASS_COMPRESSION_METHOD:
+ appendStringInfoString(&buffer, "compression method");
+ break;
+
+ case OCLASS_COMPRESSION_OPTIONS:
+ appendStringInfoString(&buffer, "compression options");
+ break;
+
/*
* There's intentionally no default: case here; we want the
* compiler to warn if a new OCLASS hasn't been handled above.
@@ -4160,6 +4228,30 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case OCLASS_COMPRESSION_METHOD:
+ {
+ char *cmname = get_compression_method_name(object->objectId);
+
+ if (!cmname)
+ elog(ERROR, "cache lookup failed for compression method %u",
+ object->objectId);
+ appendStringInfoString(&buffer, quote_identifier(cmname));
+ if (objname)
+ *objname = list_make1(cmname);
+ else
+ pfree(cmname);
+ break;
+ }
+
+ case OCLASS_COMPRESSION_OPTIONS:
+ {
+ appendStringInfo(&buffer, "%u",
+ object->objectId);
+ if (objname)
+ *objname = list_make1(psprintf("%u", object->objectId));
+ break;
+ }
+
case OCLASS_CONSTRAINT:
{
HeapTuple conTup;
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 59ffd2104d..6e806b3334 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -22,6 +22,7 @@
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
@@ -118,6 +119,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(-1);
values[Anum_pg_type_typndims - 1] = Int32GetDatum(0);
values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(InvalidOid);
+ values[Anum_pg_type_typdefaultcm - 1] = ObjectIdGetDatum(InvalidOid);
nulls[Anum_pg_type_typdefaultbin - 1] = true;
nulls[Anum_pg_type_typdefault - 1] = true;
nulls[Anum_pg_type_typacl - 1] = true;
@@ -362,6 +364,7 @@ TypeCreate(Oid newTypeOid,
values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(typeMod);
values[Anum_pg_type_typndims - 1] = Int32GetDatum(typNDims);
values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(typeCollation);
+ values[Anum_pg_type_typdefaultcm - 1] = ObjectIdGetDatum(InvalidOid);
/*
* initialize the default binary value for this type. Check for nulls of
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 4a6c99e090..e23abf64f1 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -13,8 +13,8 @@ top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
- collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
- dbcommands.o define.o discard.o dropcmds.o \
+ collationcmds.o compressioncmds.o constraint.o conversioncmds.o copy.o \
+ createas.o dbcommands.o define.o discard.o dropcmds.o \
event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o matview.o operatorcmds.o opclasscmds.o \
policy.o portalcmds.o prepare.o proclang.o publicationcmds.o \
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 4f8147907c..4f18e4083f 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -385,6 +385,7 @@ ExecRenameStmt(RenameStmt *stmt)
case OBJECT_TSTEMPLATE:
case OBJECT_PUBLICATION:
case OBJECT_SUBSCRIPTION:
+ case OBJECT_COMPRESSION_METHOD:
{
ObjectAddress address;
Relation catalog;
@@ -500,6 +501,7 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
case OBJECT_TSDICTIONARY:
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
+ case OBJECT_COMPRESSION_METHOD:
{
Relation catalog;
Relation relation;
@@ -627,6 +629,8 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_COMPRESSION_METHOD:
+ case OCLASS_COMPRESSION_OPTIONS:
/* ignore object types that don't have schema-qualified names */
break;
diff --git a/src/backend/commands/compressioncmds.c b/src/backend/commands/compressioncmds.c
new file mode 100644
index 0000000000..32cbcc3efe
--- /dev/null
+++ b/src/backend/commands/compressioncmds.c
@@ -0,0 +1,485 @@
+/*-------------------------------------------------------------------------
+ *
+ * compressioncmds.c
+ * Routines for SQL commands that manipulate compression methods.
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/commands/compressioncmds.c
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "miscadmin.h"
+
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/compression.h"
+#include "access/reloptions.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "parser/parse_func.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+static CompressionMethodRoutine *get_compression_method_routine(Oid cmhandler, Oid typeid);
+
+/*
+ * Convert a handler function name to an Oid. If the return type of the
+ * function doesn't match the given AM type, an error is raised.
+ *
+ * This function either return valid function Oid or throw an error.
+ */
+static Oid
+LookupCompressionHandlerFunc(List *handlerName)
+{
+ static const Oid funcargtypes[1] = {INTERNALOID};
+ Oid handlerOid;
+
+ if (handlerName == NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("handler function is not specified")));
+
+ /* handlers have one argument of type internal */
+ handlerOid = LookupFuncName(handlerName, 1, funcargtypes, false);
+
+ /* check that handler has the correct return type */
+ if (get_func_rettype(handlerOid) != COMPRESSION_HANDLEROID)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("function %s must return type %s",
+ NameListToString(handlerName),
+ "compression_handler")));
+
+ return handlerOid;
+}
+
+static ObjectAddress
+CreateCompressionMethod(char *cmName, List *handlerName)
+{
+ Relation rel;
+ ObjectAddress myself;
+ ObjectAddress referenced;
+ Oid cmoid;
+ Oid cmhandler;
+ bool nulls[Natts_pg_compression];
+ Datum values[Natts_pg_compression];
+ HeapTuple tup;
+
+ rel = heap_open(CompressionMethodRelationId, RowExclusiveLock);
+
+ /* Check if name is used */
+ cmoid = GetSysCacheOid1(COMPRESSIONMETHODNAME, CStringGetDatum(cmName));
+ if (OidIsValid(cmoid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("compression method \"%s\" already exists",
+ cmName)));
+
+ /*
+ * Get the handler function oid and compression method routine
+ */
+ cmhandler = LookupCompressionHandlerFunc(handlerName);
+
+ /*
+ * Insert tuple into pg_compression.
+ */
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+
+ values[Anum_pg_compression_cmname - 1] =
+ DirectFunctionCall1(namein, CStringGetDatum(cmName));
+ values[Anum_pg_compression_cmhandler - 1] = ObjectIdGetDatum(cmhandler);
+
+ tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+
+ cmoid = CatalogTupleInsert(rel, tup);
+ heap_freetuple(tup);
+
+ ObjectAddressSet(myself, CompressionMethodRelationId, cmoid);
+
+ /* Record dependency on handler function */
+ ObjectAddressSet(referenced, ProcedureRelationId, cmhandler);
+
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ recordDependencyOnCurrentExtension(&myself, false);
+
+ heap_close(rel, RowExclusiveLock);
+
+ return myself;
+}
+
+ObjectAddress
+DefineCompressionMethod(List *names, List *parameters)
+{
+ char *cmName;
+ ListCell *pl;
+ DefElem *handlerEl = NULL;
+
+ if (list_length(names) != 1)
+ elog(ERROR, "compression method name cannot be qualified");
+
+ cmName = strVal(linitial(names));
+
+ /* Must be super user */
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to create compression method \"%s\"",
+ cmName),
+ errhint("Must be superuser to create an compression method.")));
+
+ foreach(pl, parameters)
+ {
+ DefElem *defel = (DefElem *) lfirst(pl);
+ DefElem **defelp;
+
+ if (pg_strcasecmp(defel->defname, "handler") == 0)
+ defelp = &handlerEl;
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("compression method attribute \"%s\" not recognized",
+ defel->defname)));
+ break;
+ }
+
+ *defelp = defel;
+ }
+
+ if (!handlerEl)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("compression method handler is not specified")));
+
+ return CreateCompressionMethod(cmName, (List *) handlerEl->arg);
+}
+
+void
+RemoveCompressionMethodById(Oid cmOid)
+{
+ Relation relation;
+ HeapTuple tup;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to drop compression methods")));
+
+ relation = heap_open(CompressionMethodRelationId, RowExclusiveLock);
+
+ tup = SearchSysCache1(COMPRESSIONMETHODOID, ObjectIdGetDatum(cmOid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for compression method %u", cmOid);
+
+ CatalogTupleDelete(relation, &tup->t_self);
+
+ ReleaseSysCache(tup);
+
+ heap_close(relation, RowExclusiveLock);
+}
+
+/*
+ * Create new record in pg_compression_opt
+ */
+Oid
+CreateCompressionOptions(Form_pg_attribute attr, Oid cmid, List *options)
+{
+ Relation rel;
+ HeapTuple tuple;
+ Oid result,
+ cmhandler;
+ Datum values[Natts_pg_compression_opt];
+ bool nulls[Natts_pg_compression_opt];
+ ObjectAddress myself,
+ ref1,
+ ref2,
+ ref3;
+ CompressionMethodRoutine *routine;
+
+ /* Initialize buffers for new tuple values */
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+
+ /* Get handler function OID for the compression method */
+ tuple = SearchSysCache1(COMPRESSIONMETHODOID, ObjectIdGetDatum(cmid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for compression method %u", cmid);
+ cmhandler = ((Form_pg_compression) GETSTRUCT(tuple))->cmhandler;
+ ReleaseSysCache(tuple);
+
+ routine = get_compression_method_routine(cmhandler, attr->atttypid);
+
+ if (routine->configure && options != NIL)
+ routine->configure(attr, options);
+
+ values[Anum_pg_compression_opt_cmid - 1] = ObjectIdGetDatum(cmid);
+ values[Anum_pg_compression_opt_cmhandler - 1] = ObjectIdGetDatum(cmhandler);
+
+ if (options)
+ values[Anum_pg_compression_opt_cmoptions - 1] = optionListToArray(options);
+ else
+ nulls[Anum_pg_compression_opt_cmoptions - 1] = true;
+
+ rel = heap_open(CompressionOptRelationId, RowExclusiveLock);
+ tuple = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+ result = CatalogTupleInsert(rel, tuple);
+ heap_freetuple(tuple);
+
+ ObjectAddressSet(myself, CompressionOptRelationId, result);
+ ObjectAddressSet(ref1, ProcedureRelationId, cmhandler);
+ ObjectAddressSubSet(ref2, RelationRelationId, attr->attrelid, attr->attnum);
+ ObjectAddressSet(ref3, CompressionMethodRelationId, cmid);
+
+ recordDependencyOn(&myself, &ref1, DEPENDENCY_NORMAL);
+ recordDependencyOn(&ref2, &myself, DEPENDENCY_NORMAL);
+ recordDependencyOn(&myself, &ref3, DEPENDENCY_NORMAL);
+ recordDependencyOnCurrentExtension(&myself, false);
+ heap_close(rel, RowExclusiveLock);
+
+ return result;
+}
+
+void
+RemoveCompressionOptionsById(Oid cmoptoid)
+{
+ Relation relation;
+ HeapTuple tup;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to drop compression options")));
+
+ relation = heap_open(CompressionOptRelationId, RowExclusiveLock);
+
+ tup = SearchSysCache1(COMPRESSIONOPTIONSOID, ObjectIdGetDatum(cmoptoid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+
+ CatalogTupleDelete(relation, &tup->t_self);
+ ReleaseSysCache(tup);
+ heap_close(relation, RowExclusiveLock);
+}
+
+ColumnCompression *
+GetColumnCompressionForAttribute(Form_pg_attribute att)
+{
+ HeapTuple tuple;
+ Form_pg_compression_opt cmopt;
+ ColumnCompression *compression = makeNode(ColumnCompression);
+
+ /* Get handler function OID for the compression method */
+ tuple = SearchSysCache1(COMPRESSIONOPTIONSOID,
+ ObjectIdGetDatum(att->attcompression));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for compression options %u",
+ att->attcompression);
+
+ cmopt = (Form_pg_compression_opt) GETSTRUCT(tuple);
+ compression->methodName = NULL;
+ compression->methodOid = cmopt->cmid;
+ compression->options = GetCompressionOptionsList(att->attcompression);
+ ReleaseSysCache(tuple);
+
+ return compression;
+}
+
+void
+CheckCompressionMismatch(ColumnCompression * c1, ColumnCompression * c2,
+ const char *attributeName)
+{
+ char *cmname1 = c1->methodName ? c1->methodName :
+ get_compression_method_name(c1->methodOid);
+ char *cmname2 = c2->methodName ? c2->methodName :
+ get_compression_method_name(c2->methodOid);
+
+ if (strcmp(cmname1, cmname2))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("column \"%s\" has a compression method conflict",
+ attributeName),
+ errdetail("%s versus %s", cmname1, cmname2)));
+
+ if (!equal(c1->options, c2->options))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("column \"%s\" has a compression options conflict",
+ attributeName),
+ errdetail("(%s) versus (%s)",
+ formatRelOptions(c1->options),
+ formatRelOptions(c2->options))));
+}
+
+/*
+ * get_compression_method_oid
+ *
+ * If missing_ok is false, throw an error if compression method not found.
+ * If missing_ok is true, just return InvalidOid.
+ */
+Oid
+get_compression_method_oid(const char *cmname, bool missing_ok)
+{
+ HeapTuple tup;
+ Oid oid = InvalidOid;
+
+ tup = SearchSysCache1(COMPRESSIONMETHODNAME, CStringGetDatum(cmname));
+ if (HeapTupleIsValid(tup))
+ {
+ oid = HeapTupleGetOid(tup);
+ ReleaseSysCache(tup);
+ }
+
+ if (!OidIsValid(oid) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("compression method \"%s\" does not exist", cmname)));
+
+ return oid;
+}
+
+/*
+ * get_compression_method_name
+ *
+ * given an compression method OID, look up its name.
+ */
+char *
+get_compression_method_name(Oid cmOid)
+{
+ HeapTuple tup;
+ char *result = NULL;
+
+ tup = SearchSysCache1(COMPRESSIONMETHODOID, ObjectIdGetDatum(cmOid));
+ if (HeapTupleIsValid(tup))
+ {
+ Form_pg_compression cmform = (Form_pg_compression) GETSTRUCT(tup);
+
+ result = pstrdup(NameStr(cmform->cmname));
+ ReleaseSysCache(tup);
+ }
+ return result;
+}
+
+/*
+ * get_compression_method_name_for_opt
+ *
+ * given an compression options OID, look up a name for used compression method.
+ */
+char *
+get_compression_method_name_for_opt(Oid cmoptoid)
+{
+ HeapTuple tup;
+ Oid cmid;
+ char *result = NULL;
+ Form_pg_compression cmform;
+
+ tup = SearchSysCache1(COMPRESSIONOPTIONSOID, ObjectIdGetDatum(cmoptoid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+
+ cmid = ((Form_pg_compression_opt) GETSTRUCT(tup))->cmid;
+ ReleaseSysCache(tup);
+
+ /* now we can get the name */
+ tup = SearchSysCache1(COMPRESSIONMETHODOID, ObjectIdGetDatum(cmid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for compression method %u", cmid);
+
+ cmform = (Form_pg_compression) GETSTRUCT(tup);
+ result = pstrdup(NameStr(cmform->cmname));
+ ReleaseSysCache(tup);
+ return result;
+}
+
+/* get_compression_options */
+List *
+GetCompressionOptionsList(Oid cmoptoid)
+{
+ HeapTuple tuple;
+ List *result = NULL;
+ bool isnull;
+ Datum cmoptions;
+
+ /* Get handler function OID for the compression method */
+ tuple = SearchSysCache1(COMPRESSIONOPTIONSOID, ObjectIdGetDatum(cmoptoid));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+
+ cmoptions = SysCacheGetAttr(COMPRESSIONOPTIONSOID, tuple,
+ Anum_pg_compression_opt_cmoptions, &isnull);
+
+ if (!isnull)
+ result = untransformRelOptions(cmoptions);
+
+ ReleaseSysCache(tuple);
+ return result;
+}
+
+CompressionMethodRoutine *
+GetCompressionRoutine(Oid cmoptoid)
+{
+ HeapTuple tuple;
+ Form_pg_compression_opt cmopt;
+ regproc cmhandler;
+
+ /* Get handler function OID for the compression method */
+ tuple = SearchSysCache1(COMPRESSIONOPTIONSOID, ObjectIdGetDatum(cmoptoid));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+
+ cmopt = (Form_pg_compression_opt) GETSTRUCT(tuple);
+ cmhandler = cmopt->cmhandler;
+
+ /* Complain if handler OID is invalid */
+ if (!RegProcedureIsValid(cmhandler))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("could not find compression method handler for compression options %u",
+ cmoptoid)));
+
+ ReleaseSysCache(tuple);
+
+ /* And finally, call the handler function to get the API struct. */
+ return get_compression_method_routine(cmhandler, InvalidOid);
+}
+
+/*
+ * Call the specified compression method handler
+ * routine to get its CompressionMethodRoutine struct,
+ * which will be palloc'd in the caller's context.
+ */
+static CompressionMethodRoutine *
+get_compression_method_routine(Oid cmhandler, Oid typeid)
+{
+ Datum datum;
+ CompressionMethodRoutine *routine;
+ CompressionMethodOpArgs opargs;
+
+ opargs.typeid = typeid;
+ opargs.cmhanderid = cmhandler;
+
+ datum = OidFunctionCall1(cmhandler, PointerGetDatum(&opargs));
+ routine = (CompressionMethodRoutine *) DatumGetPointer(datum);
+
+ if (routine == NULL || !IsA(routine, CompressionMethodRoutine))
+ elog(ERROR, "compression method handler function %u "
+ "did not return an CompressionMethodRoutine struct",
+ cmhandler);
+
+ return routine;
+}
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index cfa3f059c2..8cef3be086 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2758,8 +2758,8 @@ CopyFrom(CopyState cstate)
List *recheckIndexes = NIL;
/* OK, store the tuple and create index entries for it */
- heap_insert(resultRelInfo->ri_RelationDesc, tuple, mycid,
- hi_options, bistate);
+ heap_insert(resultRelInfo->ri_RelationDesc, tuple,
+ mycid, hi_options, bistate);
if (resultRelInfo->ri_NumIndices > 0)
recheckIndexes = ExecInsertIndexTuples(slot,
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e60210cb24..010bdb644b 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -112,7 +112,8 @@ create_ctas_internal(List *attrList, IntoClause *into)
* Create the relation. (This will error out if there's an existing view,
* so we don't need more code to complain if "replace" is false.)
*/
- intoRelationAddr = DefineRelation(create, relkind, InvalidOid, NULL, NULL);
+ intoRelationAddr = DefineRelation(create, relkind, InvalidOid, NULL, NULL,
+ NULL);
/*
* If necessary, create a TOAST table for the target table. Note that
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index 2b30677d6f..8611db22ec 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -275,6 +275,10 @@ does_not_exist_skipping(ObjectType objtype, Node *object)
name = NameListToString(castNode(List, object));
}
break;
+ case OBJECT_COMPRESSION_METHOD:
+ msg = gettext_noop("compression method \"%s\" does not exist, skipping");
+ name = strVal((Value *) object);
+ break;
case OBJECT_CONVERSION:
if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
{
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 938133bbe4..f520c7fe7b 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -91,6 +91,7 @@ static event_trigger_support_data event_trigger_support[] = {
{"CAST", true},
{"CONSTRAINT", true},
{"COLLATION", true},
+ {"COMPRESSION METHOD", true},
{"CONVERSION", true},
{"DATABASE", false},
{"DOMAIN", true},
@@ -1085,6 +1086,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
case OBJECT_CAST:
case OBJECT_COLUMN:
case OBJECT_COLLATION:
+ case OBJECT_COMPRESSION_METHOD:
case OBJECT_CONVERSION:
case OBJECT_DEFACL:
case OBJECT_DEFAULT:
@@ -1144,6 +1146,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
case OCLASS_ROLE:
+ case OCLASS_COMPRESSION_OPTIONS:
/* no support for global objects */
return false;
case OCLASS_EVENT_TRIGGER:
@@ -1183,6 +1186,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_COMPRESSION_METHOD:
return true;
/*
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index 9ad991507f..b840309d03 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -61,7 +61,7 @@ static void import_error_callback(void *arg);
* processing, hence any validation should be done before this
* conversion.
*/
-static Datum
+Datum
optionListToArray(List *options)
{
ArrayBuildState *astate = NULL;
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 62937124ef..857eb14b64 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -212,7 +212,8 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
stmt->tablespacename = NULL;
stmt->if_not_exists = seq->if_not_exists;
- address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL, NULL);
+ address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL, NULL,
+ NULL);
seqoid = address.objectId;
Assert(seqoid != InvalidOid);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index c8fc9cb7fe..a715093a66 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -14,6 +14,7 @@
*/
#include "postgres.h"
+#include "access/compression.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "access/multixact.h"
@@ -33,6 +34,8 @@
#include "catalog/partition.h"
#include "catalog/pg_am.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_depend.h"
@@ -41,6 +44,7 @@
#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
+#include "catalog/pg_proc.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
@@ -90,6 +94,7 @@
#include "storage/smgr.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+#include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
@@ -456,6 +461,8 @@ static void ATExecGenericOptions(Relation rel, List *options);
static void ATExecEnableRowSecurity(Relation rel);
static void ATExecDisableRowSecurity(Relation rel);
static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
+static void ATExecAlterColumnCompression(AlteredTableInfo *tab, Relation rel,
+ const char *column, ColumnCompression * compression, LOCKMODE lockmode);
static void copy_relation_data(SMgrRelation rel, SMgrRelation dst,
ForkNumber forkNum, char relpersistence);
@@ -501,7 +508,8 @@ static ObjectAddress ATExecDetachPartition(Relation rel, RangeVar *name);
*/
ObjectAddress
DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
- ObjectAddress *typaddress, const char *queryString)
+ ObjectAddress *typaddress, const char *queryString,
+ Node **pAlterStmt)
{
char relname[NAMEDATALEN];
Oid namespaceId;
@@ -521,6 +529,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
Oid ofTypeId;
ObjectAddress address;
+ AlterTableStmt *alterStmt = NULL;
/*
* Truncate relname to appropriate length (probably a waste of time, as
@@ -722,6 +731,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
if (colDef->identity)
attr->attidentity = colDef->identity;
+
+ transformColumnCompression(colDef, stmt->relation, &alterStmt);
}
/*
@@ -876,6 +887,11 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
*/
relation_close(rel, NoLock);
+ if (pAlterStmt)
+ *pAlterStmt = (Node *) alterStmt;
+ else
+ Assert(!alterStmt);
+
return address;
}
@@ -1565,6 +1581,7 @@ storage_name(char c)
}
}
+
/*----------
* MergeAttributes
* Returns new schema given initial schema and superclasses.
@@ -1899,6 +1916,19 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
storage_name(def->storage),
storage_name(attribute->attstorage))));
+ if (OidIsValid(attribute->attcompression))
+ {
+ ColumnCompression *compression =
+ GetColumnCompressionForAttribute(attribute);
+
+ if (!def->compression)
+ def->compression = compression;
+ else
+ CheckCompressionMismatch(def->compression,
+ compression,
+ attributeName);
+ }
+
def->inhcount++;
/* Merge of NOT NULL constraints = OR 'em together */
def->is_not_null |= attribute->attnotnull;
@@ -1926,6 +1956,9 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
def->collOid = attribute->attcollation;
def->constraints = NIL;
def->location = -1;
+ if (OidIsValid(attribute->attcompression))
+ def->compression =
+ GetColumnCompressionForAttribute(attribute);
inhSchema = lappend(inhSchema, def);
newattno[parent_attno - 1] = ++child_attno;
}
@@ -2135,6 +2168,13 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
storage_name(def->storage),
storage_name(newdef->storage))));
+ if (!def->compression)
+ def->compression = newdef->compression;
+ else if (newdef->compression)
+ CheckCompressionMismatch(def->compression,
+ newdef->compression,
+ attributeName);
+
/* Mark the column as locally defined */
def->is_local = true;
/* Merge of NOT NULL constraints = OR 'em together */
@@ -3234,6 +3274,7 @@ AlterTableGetLockLevel(List *cmds)
*/
case AT_GenericOptions:
case AT_AlterColumnGenericOptions:
+ case AT_AlterColumnCompression:
cmd_lockmode = AccessExclusiveLock;
break;
@@ -3724,6 +3765,12 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* No command-specific prep needed */
pass = AT_PASS_MISC;
break;
+ case AT_AlterColumnCompression:
+ ATSimplePermissions(rel, ATT_TABLE);
+ /* FIXME This command never recurses */
+ /* No command-specific prep needed */
+ pass = AT_PASS_MISC;
+ break;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
@@ -4063,6 +4110,11 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
case AT_DetachPartition:
ATExecDetachPartition(rel, ((PartitionCmd *) cmd->def)->name);
break;
+ case AT_AlterColumnCompression:
+ ATExecAlterColumnCompression(tab, rel, cmd->name,
+ (ColumnCompression *) cmd->def,
+ lockmode);
+ break;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
@@ -5274,6 +5326,18 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
attribute.attislocal = colDef->is_local;
attribute.attinhcount = colDef->inhcount;
attribute.attcollation = collOid;
+ attribute.attcompression = InvalidOid;
+
+ if (!colDef->compression)
+ {
+ /* colDef->compression is handled in subsequent ALTER TABLE statement */
+ Oid cmid = get_base_typdefaultcm(typeTuple);
+
+ if (OidIsValid(cmid))
+ attribute.attcompression = CreateCompressionOptions(&attribute,
+ cmid, NULL);
+ }
+
/* attribute.attacl is handled by InsertPgAttributeTuple */
ReleaseSysCache(typeTuple);
@@ -6139,8 +6203,8 @@ ATPrepSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newVa
RelationGetRelationName(rel))));
/*
- * We allow referencing columns by numbers only for indexes, since
- * table column numbers could contain gaps if columns are later dropped.
+ * We allow referencing columns by numbers only for indexes, since table
+ * column numbers could contain gaps if columns are later dropped.
*/
if (rel->rd_rel->relkind != RELKIND_INDEX && !colName)
ereport(ERROR,
@@ -6374,6 +6438,11 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE loc
errmsg("cannot alter system column \"%s\"",
colName)));
+ if (attrtuple->attcompression && newstorage != 'x' && newstorage != 'm')
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("compressed columns can only have storage MAIN or EXTENDED")));
+
/*
* safety check: do not allow toasted storage modes unless column datatype
* is TOAST-aware.
@@ -9019,6 +9088,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
SysScanDesc scan;
HeapTuple depTup;
ObjectAddress address;
+ Oid cmid;
attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
@@ -9294,6 +9364,8 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_COMPRESSION_METHOD:
+ case OCLASS_COMPRESSION_OPTIONS:
/*
* We don't expect any of these sorts of objects to depend on
@@ -9343,7 +9415,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
if (!(foundDep->refclassid == TypeRelationId &&
foundDep->refobjid == attTup->atttypid) &&
!(foundDep->refclassid == CollationRelationId &&
- foundDep->refobjid == attTup->attcollation))
+ foundDep->refobjid == attTup->attcollation) &&
+ !(foundDep->refclassid == CompressionMethodRelationId &&
+ foundDep->refobjid == attTup->attcompression))
elog(ERROR, "found unexpected dependency for column");
CatalogTupleDelete(depRel, &depTup->t_self);
@@ -9366,6 +9440,10 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
attTup->attalign = tform->typalign;
attTup->attstorage = tform->typstorage;
+ cmid = get_base_typdefaultcm(typeTuple);
+ if (OidIsValid(cmid))
+ attTup->attcompression = CreateCompressionOptions(attTup, cmid, NULL);
+
ReleaseSysCache(typeTuple);
CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
@@ -12362,6 +12440,86 @@ ATExecGenericOptions(Relation rel, List *options)
heap_freetuple(tuple);
}
+static void
+ATExecAlterColumnCompression(AlteredTableInfo *tab,
+ Relation rel,
+ const char *column,
+ ColumnCompression * compression,
+ LOCKMODE lockmode)
+{
+ Relation attrel;
+ HeapTuple atttuple;
+ Form_pg_attribute atttableform;
+ AttrNumber attnum;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_attribute];
+ bool nulls[Natts_pg_attribute];
+ bool replace[Natts_pg_attribute];
+
+ attrel = heap_open(AttributeRelationId, RowExclusiveLock);
+
+ atttuple = SearchSysCacheAttName(RelationGetRelid(rel), column);
+ if (!HeapTupleIsValid(atttuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("column \"%s\" of relation \"%s\" does not exist",
+ column, RelationGetRelationName(rel))));
+
+ /* Prevent them from altering a system attribute */
+ atttableform = (Form_pg_attribute) GETSTRUCT(atttuple);
+ attnum = atttableform->attnum;
+ if (attnum <= 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot alter system column \"%s\"", column)));
+
+ if (atttableform->attstorage != 'x' && atttableform->attstorage != 'm')
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("storage for \"%s\" should be MAIN or EXTENDED", column)));
+
+ /* Initialize buffers for new tuple values */
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+ memset(replace, false, sizeof(replace));
+
+ if (compression->methodName || OidIsValid(compression->methodOid))
+ {
+ /* SET COMPRESSED */
+ Oid cmid,
+ cmoptoid;
+
+ cmid = compression->methodName
+ ? get_compression_method_oid(compression->methodName, false)
+ : compression->methodOid;
+
+ cmoptoid = CreateCompressionOptions(atttableform, cmid, compression->options);
+
+ values[Anum_pg_attribute_attcompression - 1] = ObjectIdGetDatum(cmoptoid);
+ replace[Anum_pg_attribute_attcompression - 1] = true;
+
+ }
+ else
+ {
+ /* SET NOT COMPRESSED */
+ values[Anum_pg_attribute_attcompression - 1] = ObjectIdGetDatum(InvalidOid);
+ replace[Anum_pg_attribute_attcompression - 1] = true;
+ }
+
+ newtuple = heap_modify_tuple(atttuple, RelationGetDescr(attrel),
+ values, nulls, replace);
+ CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
+ heap_freetuple(newtuple);
+
+ InvokeObjectPostAlterHook(RelationRelationId,
+ RelationGetRelid(rel),
+ atttableform->attnum);
+
+ ReleaseSysCache(atttuple);
+ heap_close(attrel, RowExclusiveLock);
+}
+
+
/*
* Preparation phase for SET LOGGED/UNLOGGED
*
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 7ed16aeff4..2d39f57ab0 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -40,6 +40,7 @@
#include "catalog/pg_am.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_depend.h"
@@ -2110,7 +2111,7 @@ DefineCompositeType(RangeVar *typevar, List *coldeflist)
* Finally create the relation. This also creates the type.
*/
DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, &address,
- NULL);
+ NULL, NULL);
return address;
}
@@ -3638,3 +3639,95 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
return oldNspOid;
}
+
+
+/*
+ * Execute ALTER TYPE SET COMPRESSED <cm> [WITH (<option>, ...)]
+ */
+static void
+AlterTypeDefaultCompression(Oid typeid, ColumnCompression * compression)
+{
+ Oid cmoid;
+ Type oldtup = typeidType(typeid);
+ Form_pg_type oldtype = (Form_pg_type) GETSTRUCT(oldtup);
+
+ cmoid = compression->methodName
+ ? get_compression_method_oid(compression->methodName, false)
+ : InvalidOid;
+
+ if (oldtype->typdefaultcm != cmoid)
+ {
+ Relation typrel;
+ HeapTuple newtup;
+ Datum values[Natts_pg_type];
+ bool nulls[Natts_pg_type];
+ bool replace[Natts_pg_type];
+
+ typrel = heap_open(TypeRelationId, RowExclusiveLock);
+
+ memset(values, 0, sizeof(values));
+ memset(nulls, 0, sizeof(nulls));
+ memset(replace, 0, sizeof(replace));
+
+ values[Anum_pg_type_typdefaultcm - 1] = ObjectIdGetDatum(cmoid);
+ nulls[Anum_pg_type_typdefaultcm - 1] = false;
+ replace[Anum_pg_type_typdefaultcm - 1] = true;
+
+ newtup = heap_modify_tuple(oldtup, RelationGetDescr(typrel),
+ values, nulls, replace);
+
+ CatalogTupleUpdate(typrel, &newtup->t_self, newtup);
+
+ heap_freetuple(newtup);
+ heap_close(typrel, RowExclusiveLock);
+
+ if (OidIsValid(oldtype->typdefaultcm))
+ deleteDependencyRecordsForClass(TypeRelationId, typeid,
+ CompressionMethodRelationId,
+ DEPENDENCY_NORMAL);
+
+ if (OidIsValid(cmoid))
+ {
+ ObjectAddress myself,
+ referenced;
+
+ ObjectAddressSet(myself, TypeRelationId, typeid);
+ ObjectAddressSet(referenced, CompressionMethodRelationId, cmoid);
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
+
+ InvokeObjectPostAlterHook(TypeRelationId, typeid, 0);
+ }
+
+ ReleaseSysCache(oldtup);
+}
+
+/*
+ * Execute ALTER TYPE <typeName> <command>, ...
+ */
+void
+AlterType(AlterTypeStmt * stmt)
+{
+ TypeName *typename;
+ Oid typeid;
+ ListCell *lcmd;
+
+ /* Make a TypeName so we can use standard type lookup machinery */
+ typename = makeTypeNameFromNameList(stmt->typeName);
+ typeid = typenameTypeId(NULL, typename);
+
+ foreach(lcmd, stmt->cmds)
+ {
+ AlterTypeCmd *cmd = (AlterTypeCmd *) lfirst(lcmd);
+
+ switch (cmd->cmdtype)
+ {
+ case AT_AlterTypeCompression:
+ AlterTypeDefaultCompression(typeid,
+ (ColumnCompression *) cmd->def);
+ break;
+ default:
+ elog(ERROR, "unknown ALTER TYPE command %d", cmd->cmdtype);
+ }
+ }
+}
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index 076e2a3a40..9af0f11856 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -251,7 +251,7 @@ DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
* false).
*/
address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid, NULL,
- NULL);
+ NULL, NULL);
Assert(address.objectId != InvalidOid);
/* Make the new view relation visible */
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index 47a34a044a..a58af7ee92 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -300,6 +300,9 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc
var->vartypmod != -1))
return false; /* type mismatch */
+ if (OidIsValid(att_tup->attcompression))
+ return false;
+
tlist_item = lnext(tlist_item);
}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 9bae2647fd..6d2d775ed3 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2805,6 +2805,7 @@ _copyColumnDef(const ColumnDef *from)
COPY_STRING_FIELD(colname);
COPY_NODE_FIELD(typeName);
+ COPY_NODE_FIELD(compression);
COPY_SCALAR_FIELD(inhcount);
COPY_SCALAR_FIELD(is_local);
COPY_SCALAR_FIELD(is_not_null);
@@ -2823,6 +2824,19 @@ _copyColumnDef(const ColumnDef *from)
return newnode;
}
+static ColumnCompression *
+_copyColumnCompression(const ColumnCompression * from)
+{
+ ColumnCompression *newnode = makeNode(ColumnCompression);
+
+ COPY_STRING_FIELD(methodName);
+ COPY_SCALAR_FIELD(methodOid);
+ COPY_NODE_FIELD(options);
+
+ return newnode;
+}
+
+
static Constraint *
_copyConstraint(const Constraint *from)
{
@@ -4547,6 +4561,28 @@ _copyDropSubscriptionStmt(const DropSubscriptionStmt *from)
return newnode;
}
+static AlterTypeStmt *
+_copyAlterTypeStmt(const AlterTypeStmt * from)
+{
+ AlterTypeStmt *newnode = makeNode(AlterTypeStmt);
+
+ COPY_NODE_FIELD(typeName);
+ COPY_NODE_FIELD(cmds);
+
+ return newnode;
+}
+
+static AlterTypeCmd *
+_copyAlterTypeCmd(const AlterTypeCmd * from)
+{
+ AlterTypeCmd *newnode = makeNode(AlterTypeCmd);
+
+ COPY_SCALAR_FIELD(cmdtype);
+ COPY_NODE_FIELD(def);
+
+ return newnode;
+}
+
/* ****************************************************************
* pg_list.h copy functions
* ****************************************************************
@@ -5455,6 +5491,9 @@ copyObjectImpl(const void *from)
case T_ColumnDef:
retval = _copyColumnDef(from);
break;
+ case T_ColumnCompression:
+ retval = _copyColumnCompression(from);
+ break;
case T_Constraint:
retval = _copyConstraint(from);
break;
@@ -5538,6 +5577,14 @@ copyObjectImpl(const void *from)
retval = _copyForeignKeyCacheInfo(from);
break;
+ case T_AlterTypeStmt:
+ retval = _copyAlterTypeStmt(from);
+ break;
+
+ case T_AlterTypeCmd:
+ retval = _copyAlterTypeCmd(from);
+ break;
+
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(from));
retval = 0; /* keep compiler quiet */
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 11731da80a..9b94330db6 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2536,6 +2536,7 @@ _equalColumnDef(const ColumnDef *a, const ColumnDef *b)
{
COMPARE_STRING_FIELD(colname);
COMPARE_NODE_FIELD(typeName);
+ COMPARE_NODE_FIELD(compression);
COMPARE_SCALAR_FIELD(inhcount);
COMPARE_SCALAR_FIELD(is_local);
COMPARE_SCALAR_FIELD(is_not_null);
@@ -2554,6 +2555,16 @@ _equalColumnDef(const ColumnDef *a, const ColumnDef *b)
return true;
}
+static bool
+_equalColumnCompression(const ColumnCompression * a, const ColumnCompression * b)
+{
+ COMPARE_STRING_FIELD(methodName);
+ COMPARE_SCALAR_FIELD(methodOid);
+ COMPARE_NODE_FIELD(options);
+
+ return true;
+}
+
static bool
_equalConstraint(const Constraint *a, const Constraint *b)
{
@@ -2866,6 +2877,24 @@ _equalPartitionCmd(const PartitionCmd *a, const PartitionCmd *b)
return true;
}
+static bool
+_equalAlterTypeStmt(const AlterTypeStmt * a, const AlterTypeStmt * b)
+{
+ COMPARE_NODE_FIELD(typeName);
+ COMPARE_NODE_FIELD(cmds);
+
+ return true;
+}
+
+static bool
+_equalAlterTypeCmd(const AlterTypeCmd * a, const AlterTypeCmd * b)
+{
+ COMPARE_SCALAR_FIELD(cmdtype);
+ COMPARE_NODE_FIELD(def);
+
+ return true;
+}
+
/*
* Stuff from pg_list.h
*/
@@ -3601,6 +3630,9 @@ equal(const void *a, const void *b)
case T_ColumnDef:
retval = _equalColumnDef(a, b);
break;
+ case T_ColumnCompression:
+ retval = _equalColumnCompression(a, b);
+ break;
case T_Constraint:
retval = _equalConstraint(a, b);
break;
@@ -3676,6 +3708,12 @@ equal(const void *a, const void *b)
case T_PartitionCmd:
retval = _equalPartitionCmd(a, b);
break;
+ case T_AlterTypeStmt:
+ retval = _equalAlterTypeStmt(a, b);
+ break;
+ case T_AlterTypeCmd:
+ retval = _equalAlterTypeCmd(a, b);
+ break;
default:
elog(ERROR, "unrecognized node type: %d",
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index e3eb0c5788..c1bea10eff 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -3623,6 +3623,8 @@ raw_expression_tree_walker(Node *node,
if (walker(coldef->typeName, context))
return true;
+ if (walker(coldef->compression, context))
+ return true;
if (walker(coldef->raw_default, context))
return true;
if (walker(coldef->collClause, context))
@@ -3630,6 +3632,14 @@ raw_expression_tree_walker(Node *node,
/* for now, constraints are ignored */
}
break;
+ case T_ColumnCompression:
+ {
+ ColumnCompression *colcmp = (ColumnCompression *) node;
+
+ if (walker(colcmp->options, context))
+ return true;
+ }
+ break;
case T_IndexElem:
{
IndexElem *indelem = (IndexElem *) node;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 9ee3e23761..a02f4b3fda 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2792,6 +2792,7 @@ _outColumnDef(StringInfo str, const ColumnDef *node)
WRITE_STRING_FIELD(colname);
WRITE_NODE_FIELD(typeName);
+ WRITE_NODE_FIELD(compression);
WRITE_INT_FIELD(inhcount);
WRITE_BOOL_FIELD(is_local);
WRITE_BOOL_FIELD(is_not_null);
@@ -2808,6 +2809,16 @@ _outColumnDef(StringInfo str, const ColumnDef *node)
WRITE_LOCATION_FIELD(location);
}
+static void
+_outColumnCompression(StringInfo str, const ColumnCompression * node)
+{
+ WRITE_NODE_TYPE("COLUMNCOMPRESSION");
+
+ WRITE_STRING_FIELD(methodName);
+ WRITE_OID_FIELD(methodOid);
+ WRITE_NODE_FIELD(options);
+}
+
static void
_outTypeName(StringInfo str, const TypeName *node)
{
@@ -4096,6 +4107,9 @@ outNode(StringInfo str, const void *obj)
case T_ColumnDef:
_outColumnDef(str, obj);
break;
+ case T_ColumnCompression:
+ _outColumnCompression(str, obj);
+ break;
case T_TypeName:
_outTypeName(str, obj);
break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 5eb398118e..d06b098650 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -282,6 +282,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
CreateMatViewStmt RefreshMatViewStmt CreateAmStmt
CreatePublicationStmt AlterPublicationStmt
CreateSubscriptionStmt AlterSubscriptionStmt DropSubscriptionStmt
+ AlterTypeStmt
%type <node> select_no_parens select_with_parens select_clause
simple_select values_clause
@@ -290,8 +291,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <ival> add_drop opt_asc_desc opt_nulls_order
%type <node> alter_table_cmd alter_type_cmd opt_collate_clause
- replica_identity partition_cmd
-%type <list> alter_table_cmds alter_type_cmds
+ replica_identity partition_cmd alterTypeCmd
+%type <list> alter_table_cmds alter_type_cmds alterTypeCmds
%type <list> alter_identity_column_option_list
%type <defelt> alter_identity_column_option
@@ -396,6 +397,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
transform_element_list transform_type_list
TriggerTransitions TriggerReferencing
publication_name_list
+ optCompressionParameters
%type <list> group_by_list
%type <node> group_by_item empty_grouping_set rollup_clause cube_clause
@@ -579,6 +581,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <node> partbound_datum PartitionRangeDatum
%type <list> partbound_datum_list range_datum_list
+%type <node> columnCompression optColumnCompression compressedClause
+
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
* They must be listed first so that their numeric codes do not depend on
@@ -611,9 +615,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
CLUSTER COALESCE COLLATE COLLATION COLUMN COLUMNS COMMENT COMMENTS COMMIT
- COMMITTED CONCURRENTLY CONFIGURATION CONFLICT CONNECTION CONSTRAINT
- CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE
- CROSS CSV CUBE CURRENT_P
+ COMMITTED COMPRESSED COMPRESSION CONCURRENTLY CONFIGURATION CONFLICT
+ CONNECTION CONSTRAINT CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY
+ COST CREATE CROSS CSV CUBE CURRENT_P
CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA
CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
@@ -840,6 +844,7 @@ stmt :
| AlterSubscriptionStmt
| AlterTSConfigurationStmt
| AlterTSDictionaryStmt
+ | AlterTypeStmt
| AlterUserMappingStmt
| AnalyzeStmt
| CheckPointStmt
@@ -2165,6 +2170,15 @@ alter_table_cmd:
n->missing_ok = true;
$$ = (Node *)n;
}
+ /* ALTER TABLE <name> ALTER [COLUMN] <colname> SET (NOT COMPRESSED | COMPRESSED <cm> [WITH (<options>)]) */
+ | ALTER opt_column ColId SET columnCompression
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_AlterColumnCompression;
+ n->name = $3;
+ n->def = $5;
+ $$ = (Node *)n;
+ }
/* ALTER TABLE <name> DROP [COLUMN] IF EXISTS <colname> [RESTRICT|CASCADE] */
| DROP opt_column IF_P EXISTS ColId opt_drop_behavior
{
@@ -2720,6 +2734,32 @@ PartitionRangeDatum:
* really variants of the ALTER TABLE subcommands with different spellings
*****************************************************************************/
+AlterTypeStmt:
+ ALTER TYPE_P any_name alterTypeCmds
+ {
+ AlterTypeStmt *n = makeNode(AlterTypeStmt);
+ n->typeName = $3;
+ n->cmds = $4;
+ $$ = (Node *) n;
+ }
+ ;
+
+alterTypeCmds:
+ alterTypeCmd { $$ = list_make1($1); }
+ | alterTypeCmds ',' alterTypeCmd { $$ = lappend($1, $3); }
+ ;
+
+alterTypeCmd:
+ /* ALTER TYPE <name> SET (NOT COMPRESSED | COMPRESSED <cm> [WITH (<options>)]) */
+ SET columnCompression
+ {
+ AlterTypeCmd *n = makeNode(AlterTypeCmd);
+ n->cmdtype = AT_AlterTypeCompression;
+ n->def = $2;
+ $$ = (Node *)n;
+ }
+ ;
+
AlterCompositeTypeStmt:
ALTER TYPE_P any_name alter_type_cmds
{
@@ -3245,11 +3285,12 @@ TypedTableElement:
| TableConstraint { $$ = $1; }
;
-columnDef: ColId Typename create_generic_options ColQualList
+columnDef: ColId Typename optColumnCompression create_generic_options ColQualList
{
ColumnDef *n = makeNode(ColumnDef);
n->colname = $1;
n->typeName = $2;
+ n->compression = (ColumnCompression *) $3;
n->inhcount = 0;
n->is_local = true;
n->is_not_null = false;
@@ -3259,8 +3300,8 @@ columnDef: ColId Typename create_generic_options ColQualList
n->raw_default = NULL;
n->cooked_default = NULL;
n->collOid = InvalidOid;
- n->fdwoptions = $3;
- SplitColQualList($4, &n->constraints, &n->collClause,
+ n->fdwoptions = $4;
+ SplitColQualList($5, &n->constraints, &n->collClause,
yyscanner);
n->location = @1;
$$ = (Node *)n;
@@ -3307,6 +3348,39 @@ columnOptions: ColId ColQualList
}
;
+compressedClause:
+ COMPRESSED name optCompressionParameters
+ {
+ ColumnCompression *n = makeNode(ColumnCompression);
+ n->methodName = $2;
+ n->methodOid = InvalidOid;
+ n->options = (List *) $3;
+ $$ = (Node *) n;
+ }
+ ;
+
+columnCompression:
+ compressedClause |
+ NOT COMPRESSED
+ {
+ ColumnCompression *n = makeNode(ColumnCompression);
+ n->methodName = NULL;
+ n->methodOid = InvalidOid;
+ n->options = NIL;
+ $$ = (Node *) n;
+ }
+ ;
+
+optColumnCompression:
+ compressedClause /* FIXME shift/reduce conflict on NOT COMPRESSED/NOT NULL */
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
+optCompressionParameters:
+ WITH '(' generic_option_list ')' { $$ = $3; }
+ | /*EMPTY*/ { $$ = NIL; }
+ ;
+
ColQualList:
ColQualList ColConstraint { $$ = lappend($1, $2); }
| /*EMPTY*/ { $$ = NIL; }
@@ -5667,6 +5741,15 @@ DefineStmt:
n->if_not_exists = true;
$$ = (Node *)n;
}
+ | CREATE COMPRESSION METHOD any_name HANDLER handler_name
+ {
+ DefineStmt *n = makeNode(DefineStmt);
+ n->kind = OBJECT_COMPRESSION_METHOD;
+ n->args = NIL;
+ n->defnames = $4;
+ n->definition = list_make1(makeDefElem("handler", (Node *) $6, @6));
+ $$ = (Node *) n;
+ }
;
definition: '(' def_list ')' { $$ = $2; }
@@ -6175,6 +6258,7 @@ drop_type_any_name:
/* object types taking name_list */
drop_type_name:
ACCESS METHOD { $$ = OBJECT_ACCESS_METHOD; }
+ | COMPRESSION METHOD { $$ = OBJECT_COMPRESSION_METHOD; }
| EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; }
| EXTENSION { $$ = OBJECT_EXTENSION; }
| FOREIGN DATA_P WRAPPER { $$ = OBJECT_FDW; }
@@ -6238,7 +6322,7 @@ opt_restart_seqs:
* The COMMENT ON statement can take different forms based upon the type of
* the object associated with the comment. The form of the statement is:
*
- * COMMENT ON [ [ ACCESS METHOD | CONVERSION | COLLATION |
+ * COMMENT ON [ [ ACCESS METHOD | COMPRESSION METHOD | CONVERSION | COLLATION |
* DATABASE | DOMAIN |
* EXTENSION | EVENT TRIGGER | FOREIGN DATA WRAPPER |
* FOREIGN TABLE | INDEX | [PROCEDURAL] LANGUAGE |
@@ -6428,6 +6512,7 @@ comment_type_any_name:
/* object types taking name */
comment_type_name:
ACCESS METHOD { $$ = OBJECT_ACCESS_METHOD; }
+ | COMPRESSION METHOD { $$ = OBJECT_COMPRESSION_METHOD; }
| DATABASE { $$ = OBJECT_DATABASE; }
| EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; }
| EXTENSION { $$ = OBJECT_EXTENSION; }
@@ -14636,6 +14721,8 @@ unreserved_keyword:
| COMMENTS
| COMMIT
| COMMITTED
+ | COMPRESSED
+ | COMPRESSION
| CONFIGURATION
| CONFLICT
| CONNECTION
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 20586797cc..0b1f0ff9aa 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -27,6 +27,7 @@
#include "postgres.h"
#include "access/amapi.h"
+#include "access/compression.h"
#include "access/htup_details.h"
#include "access/reloptions.h"
#include "catalog/dependency.h"
@@ -493,6 +494,49 @@ generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column,
*sname_p = sname;
}
+void
+transformColumnCompression(ColumnDef *column, RangeVar *relation,
+ AlterTableStmt **alterStmt)
+{
+ if (!column->compression && column->typeName)
+ {
+ Type tup = typenameType(NULL, column->typeName, NULL);
+ Oid cmoid = get_base_typdefaultcm(tup);
+
+ ReleaseSysCache(tup);
+
+ if (OidIsValid(cmoid))
+ {
+ column->compression = makeNode(ColumnCompression);
+ column->compression->methodName = NULL;
+ column->compression->methodOid = cmoid;
+ column->compression->options = NIL;
+ }
+ }
+
+ if (column->compression)
+ {
+ AlterTableCmd *cmd;
+
+ cmd = makeNode(AlterTableCmd);
+ cmd->subtype = AT_AlterColumnCompression;
+ cmd->name = column->colname;
+ cmd->def = (Node *) column->compression;
+ cmd->behavior = DROP_RESTRICT;
+ cmd->missing_ok = false;
+
+ if (!*alterStmt)
+ {
+ *alterStmt = makeNode(AlterTableStmt);
+ (*alterStmt)->relation = relation;
+ (*alterStmt)->relkind = OBJECT_TABLE;
+ (*alterStmt)->cmds = NIL;
+ }
+
+ (*alterStmt)->cmds = lappend((*alterStmt)->cmds, cmd);
+ }
+}
+
/*
* transformColumnDefinition -
* transform a single ColumnDef within CREATE TABLE
@@ -793,6 +837,16 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
cxt->alist = lappend(cxt->alist, stmt);
}
+
+ if (cxt->isalter)
+ {
+ AlterTableStmt *stmt = NULL;
+
+ transformColumnCompression(column, cxt->relation, &stmt);
+
+ if (stmt)
+ cxt->alist = lappend(cxt->alist, stmt);
+ }
}
/*
@@ -1002,6 +1056,10 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
def->collOid = attribute->attcollation;
def->constraints = NIL;
def->location = -1;
+ if (attribute->attcompression)
+ def->compression = GetColumnCompressionForAttribute(attribute);
+ else
+ def->compression = NULL;
/*
* Add to column list
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 775477c6cf..a07327fbf7 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -217,6 +217,7 @@ check_xact_readonly(Node *parsetree)
case T_CreateSubscriptionStmt:
case T_AlterSubscriptionStmt:
case T_DropSubscriptionStmt:
+ case T_AlterTypeStmt:
PreventCommandIfReadOnly(CreateCommandTag(parsetree));
PreventCommandIfParallelMode(CreateCommandTag(parsetree));
break;
@@ -998,6 +999,7 @@ ProcessUtilitySlow(ParseState *pstate,
foreach(l, stmts)
{
Node *stmt = (Node *) lfirst(l);
+ Node *alterStmt = NULL;
if (IsA(stmt, CreateStmt))
{
@@ -1008,7 +1010,9 @@ ProcessUtilitySlow(ParseState *pstate,
address = DefineRelation((CreateStmt *) stmt,
RELKIND_RELATION,
InvalidOid, NULL,
- queryString);
+ queryString,
+ &alterStmt);
+
EventTriggerCollectSimpleCommand(address,
secondaryObject,
stmt);
@@ -1042,7 +1046,8 @@ ProcessUtilitySlow(ParseState *pstate,
address = DefineRelation((CreateStmt *) stmt,
RELKIND_FOREIGN_TABLE,
InvalidOid, NULL,
- queryString);
+ queryString,
+ &alterStmt);
CreateForeignTable((CreateForeignTableStmt *) stmt,
address.objectId);
EventTriggerCollectSimpleCommand(address,
@@ -1074,6 +1079,9 @@ ProcessUtilitySlow(ParseState *pstate,
NULL);
}
+ if (alterStmt)
+ lappend(stmts, alterStmt);
+
/* Need CCI between commands */
if (lnext(l) != NULL)
CommandCounterIncrement();
@@ -1283,6 +1291,11 @@ ProcessUtilitySlow(ParseState *pstate,
stmt->definition,
stmt->if_not_exists);
break;
+ case OBJECT_COMPRESSION_METHOD:
+ Assert(stmt->args == NIL);
+ address = DefineCompressionMethod(stmt->defnames,
+ stmt->definition);
+ break;
default:
elog(ERROR, "unrecognized define stmt type: %d",
(int) stmt->kind);
@@ -1643,6 +1656,10 @@ ProcessUtilitySlow(ParseState *pstate,
address = AlterCollation((AlterCollationStmt *) parsetree);
break;
+ case T_AlterTypeStmt:
+ AlterType((AlterTypeStmt *) parsetree);
+ break;
+
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(parsetree));
@@ -1696,6 +1713,11 @@ ExecDropStmt(DropStmt *stmt, bool isTopLevel)
case OBJECT_FOREIGN_TABLE:
RemoveRelations(stmt);
break;
+ case OBJECT_COMPRESSION_METHOD:
+ if (stmt->behavior == DROP_CASCADE)
+ {
+ /* TODO decompress columns instead of their deletion */
+ }
default:
RemoveObjects(stmt);
break;
@@ -2309,6 +2331,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_STATISTIC_EXT:
tag = "DROP STATISTICS";
break;
+ case OBJECT_COMPRESSION_METHOD:
+ tag = "DROP COMPRESSION METHOD";
+ break;
default:
tag = "???";
}
@@ -2412,6 +2437,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_ACCESS_METHOD:
tag = "CREATE ACCESS METHOD";
break;
+ case OBJECT_COMPRESSION_METHOD:
+ tag = "CREATE COMPRESSION METHOD";
+ break;
default:
tag = "???";
}
@@ -2846,6 +2874,10 @@ CreateCommandTag(Node *parsetree)
}
break;
+ case T_AlterTypeStmt:
+ tag = "ALTER TYPE";
+ break;
+
default:
elog(WARNING, "unrecognized node type: %d",
(int) nodeTag(parsetree));
@@ -3291,6 +3323,10 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_DDL;
break;
+ case T_AlterTypeStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
/* already-planned queries */
case T_PlannedStmt:
{
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index be793539a3..a5cfbe3d4c 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -418,3 +418,4 @@ PSEUDOTYPE_DUMMY_IO_FUNCS(internal);
PSEUDOTYPE_DUMMY_IO_FUNCS(opaque);
PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement);
PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray);
+PSEUDOTYPE_DUMMY_IO_FUNCS(compression_handler);
diff --git a/src/backend/utils/adt/tsvector.c b/src/backend/utils/adt/tsvector.c
index 6f66c1f58c..ec4e89bf94 100644
--- a/src/backend/utils/adt/tsvector.c
+++ b/src/backend/utils/adt/tsvector.c
@@ -14,11 +14,14 @@
#include "postgres.h"
+#include "access/compression.h"
+#include "catalog/pg_type.h"
#include "libpq/pqformat.h"
#include "tsearch/ts_locale.h"
#include "tsearch/ts_utils.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
+#include "common/pg_lzcompress.h"
typedef struct
{
@@ -548,3 +551,92 @@ tsvectorrecv(PG_FUNCTION_ARGS)
PG_RETURN_TSVECTOR(vec);
}
+
+/*
+ * Compress tsvector using LZ compression.
+ * Instead of trying to compress whole tsvector we compress only text part
+ * here. This approach gives more compressibility for tsvectors.
+ */
+static struct varlena *
+tsvector_compress(AttributeCompression * ac, const struct varlena *data)
+{
+ char *tmp;
+ int32 valsize = VARSIZE_ANY_EXHDR(data);
+ int32 len = valsize + VARHDRSZ_CUSTOM_COMPRESSED,
+ lenc;
+
+ char *arr = VARDATA(data),
+ *str = STRPTR((TSVector) data);
+ int32 arrsize = str - arr;
+
+ Assert(!VARATT_IS_COMPRESSED(data));
+ tmp = palloc0(len);
+
+ /* we try to compress string part of tsvector first */
+ lenc = pglz_compress(str,
+ valsize - arrsize,
+ tmp + VARHDRSZ_CUSTOM_COMPRESSED + arrsize,
+ PGLZ_strategy_default);
+
+ if (lenc >= 0)
+ {
+ /* tsvector is compressible, copy size and entries to its beginning */
+ memcpy(tmp + VARHDRSZ_CUSTOM_COMPRESSED, arr, arrsize);
+ SET_VARSIZE_COMPRESSED(tmp, arrsize + lenc + VARHDRSZ_CUSTOM_COMPRESSED);
+ return (struct varlena *) tmp;
+ }
+
+ pfree(tmp);
+ return NULL;
+}
+
+static void
+tsvector_configure(Form_pg_attribute attr, List *options)
+{
+ if (options != NIL)
+ elog(ERROR, "the compression method for tsvector doesn't take any options");
+}
+
+static struct varlena *
+tsvector_decompress(const struct varlena *data, List *options)
+{
+ char *tmp,
+ *raw_data = (char *) data + VARHDRSZ_CUSTOM_COMPRESSED;
+ int32 count,
+ arrsize,
+ len = VARRAWSIZE_4B_C(data) + VARHDRSZ;
+
+ Assert(VARATT_IS_CUSTOM_COMPRESSED(data));
+ tmp = palloc0(len);
+ SET_VARSIZE(tmp, len);
+ count = *((uint32 *) raw_data);
+ arrsize = sizeof(uint32) + count * sizeof(WordEntry);
+ memcpy(VARDATA(tmp), raw_data, arrsize);
+
+ if (pglz_decompress(raw_data + arrsize,
+ VARSIZE(data) - VARHDRSZ_CUSTOM_COMPRESSED - arrsize,
+ VARDATA(tmp) + arrsize,
+ VARRAWSIZE_4B_C(data) - arrsize) < 0)
+ elog(ERROR, "compressed tsvector is corrupted");
+
+ return (struct varlena *) tmp;
+}
+
+Datum
+tsvector_compression_handler(PG_FUNCTION_ARGS)
+{
+ CompressionMethodOpArgs *opargs = (CompressionMethodOpArgs *)
+ PG_GETARG_POINTER(0);
+ CompressionMethodRoutine *cmr = makeNode(CompressionMethodRoutine);
+ Oid typeid = opargs->typeid;
+
+ if (OidIsValid(typeid) && typeid != TSVECTOROID)
+ elog(ERROR, "unexpected type %d for tsvector compression handler", typeid);
+
+ cmr->configure = tsvector_configure;
+ cmr->drop = NULL;
+ cmr->compress = tsvector_compress;
+ cmr->decompress = tsvector_decompress;
+
+ PG_RETURN_POINTER(cmr);
+}
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index b7a14dc87e..ae7320707c 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -2276,16 +2276,12 @@ getBaseType(Oid typid)
}
/*
- * getBaseTypeAndTypmod
- * If the given type is a domain, return its base type and typmod;
- * otherwise return the type's own OID, and leave *typmod unchanged.
- *
* Note that the "applied typmod" should be -1 for every domain level
* above the bottommost; therefore, if the passed-in typid is indeed
* a domain, *typmod should be -1.
*/
-Oid
-getBaseTypeAndTypmod(Oid typid, int32 *typmod)
+static inline HeapTuple
+getBaseTypeTuple(Oid *typid, int32 *typmod)
{
/*
* We loop to find the bottom base type in a stack of domains.
@@ -2295,24 +2291,33 @@ getBaseTypeAndTypmod(Oid typid, int32 *typmod)
HeapTuple tup;
Form_pg_type typTup;
- tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*typid));
if (!HeapTupleIsValid(tup))
- elog(ERROR, "cache lookup failed for type %u", typid);
+ elog(ERROR, "cache lookup failed for type %u", *typid);
typTup = (Form_pg_type) GETSTRUCT(tup);
if (typTup->typtype != TYPTYPE_DOMAIN)
- {
/* Not a domain, so done */
- ReleaseSysCache(tup);
- break;
- }
+ return tup;
Assert(*typmod == -1);
- typid = typTup->typbasetype;
+ *typid = typTup->typbasetype;
*typmod = typTup->typtypmod;
ReleaseSysCache(tup);
}
+}
+/*
+ * getBaseTypeAndTypmod
+ * If the given type is a domain, return its base type and typmod;
+ * otherwise return the type's own OID, and leave *typmod unchanged.
+ */
+Oid
+getBaseTypeAndTypmod(Oid typid, int32 *typmod)
+{
+ HeapTuple tup = getBaseTypeTuple(&typid, typmod);
+
+ ReleaseSysCache(tup);
return typid;
}
@@ -2808,6 +2813,39 @@ type_is_collatable(Oid typid)
return OidIsValid(get_typcollation(typid));
}
+/*
+ * get_base_typdefaultcm
+ *
+ * Given the type tuple, return the base type's typdefaultcm attribute.
+ */
+Oid
+get_base_typdefaultcm(HeapTuple typtup)
+{
+ Oid typid;
+ Oid base;
+ Oid cm = InvalidOid;
+
+ for (typid = (Oid) -1; !OidIsValid(cm) && OidIsValid(typid); typid = base)
+ {
+ Form_pg_type type;
+
+ if (typid != (Oid) -1)
+ {
+ typtup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ if (!HeapTupleIsValid(typtup))
+ elog(ERROR, "cache lookup failed for type %u", typid);
+ }
+
+ type = (Form_pg_type) GETSTRUCT(typtup);
+ base = type->typtype == TYPTYPE_DOMAIN ? type->typbasetype : InvalidOid;
+ cm = type->typdefaultcm;
+
+ if (typid != (Oid) -1)
+ ReleaseSysCache(typtup);
+ }
+
+ return cm;
+}
/* ---------- STATISTICS CACHE ---------- */
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index b8e37809b0..5f54fa0a3e 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -30,6 +30,7 @@
#include <fcntl.h>
#include <unistd.h>
+#include "access/compression.h"
#include "access/htup_details.h"
#include "access/multixact.h"
#include "access/nbtree.h"
@@ -76,6 +77,7 @@
#include "storage/smgr.h"
#include "utils/array.h"
#include "utils/builtins.h"
+#include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
@@ -538,6 +540,7 @@ RelationBuildTupleDesc(Relation relation)
while (HeapTupleIsValid(pg_attribute_tuple = systable_getnext(pg_attribute_scan)))
{
Form_pg_attribute attp;
+ Oid cmoptoid;
attp = (Form_pg_attribute) GETSTRUCT(pg_attribute_tuple);
@@ -565,6 +568,16 @@ RelationBuildTupleDesc(Relation relation)
attrdef[ndef].adbin = NULL;
ndef++;
}
+
+ cmoptoid = attp->attcompression;
+ if (!attp->attisdropped && OidIsValid(cmoptoid))
+ {
+ MemoryContext oldctx = MemoryContextSwitchTo(CacheMemoryContext);
+
+ TupleDescInitAttrCompression(relation->rd_att, attp->attnum, cmoptoid);
+ MemoryContextSwitchTo(oldctx);
+ }
+
need--;
if (need == 0)
break;
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index fcbb683a99..634482e326 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -31,6 +31,8 @@
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
@@ -309,6 +311,39 @@ static const struct cachedesc cacheinfo[] = {
},
8
},
+ {CompressionMethodRelationId, /* COMPRESSIONMETHODOID */
+ CompressionMethodOidIndexId,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 8
+ },
+ {CompressionMethodRelationId, /* COMPRESSIONMETHODNAME */
+ CompressionMethodNameIndexId,
+ 1,
+ {
+ Anum_pg_compression_cmname,
+ 0,
+ 0,
+ 0
+ },
+ 8
+ },
+ {CompressionOptRelationId, /* COMPRESSIONOPTIONSOID */
+ CompressionOptionsOidIndexId,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 8,
+ },
{ConversionRelationId, /* CONDEFAULT */
ConversionDefaultIndexId,
4,
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 041b5e0c87..0ded023858 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -735,7 +735,10 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
success = listConversions(pattern, show_verbose, show_system);
break;
case 'C':
- success = listCasts(pattern, show_verbose);
+ if (cmd[2] == 'M')
+ success = describeCompressionMethods(pattern, show_verbose);
+ else
+ success = listCasts(pattern, show_verbose);
break;
case 'd':
if (strncmp(cmd, "ddp", 3) == 0)
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 6fb9bdd063..9939ca1cdb 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -200,6 +200,69 @@ describeAccessMethods(const char *pattern, bool verbose)
return true;
}
+/*
+ * \dCM
+ * Takes an optional regexp to select particular compression methods
+ */
+bool
+describeCompressionMethods(const char *pattern, bool verbose)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+ static const bool translate_columns[] = {false, false, false};
+
+ if (pset.sversion < 100000)
+ {
+ char sverbuf[32];
+
+ psql_error("The server (version %s) does not support compression methods.\n",
+ formatPGVersionNumber(pset.sversion, false,
+ sverbuf, sizeof(sverbuf)));
+ return true;
+ }
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT cmname AS \"%s\"",
+ gettext_noop("Name"));
+
+ if (verbose)
+ {
+ appendPQExpBuffer(&buf,
+ ",\n cmhandler AS \"%s\",\n"
+ " pg_catalog.obj_description(oid, 'pg_compression') AS \"%s\"",
+ gettext_noop("Handler"),
+ gettext_noop("Description"));
+ }
+
+ appendPQExpBufferStr(&buf,
+ "\nFROM pg_catalog.pg_compression\n");
+
+ processSQLNamePattern(pset.db, &buf, pattern, false, false,
+ NULL, "cmname", NULL,
+ NULL);
+
+ appendPQExpBufferStr(&buf, "ORDER BY 1;");
+
+ res = PSQLexec(buf.data);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of compression methods");
+ myopt.translate_header = true;
+ myopt.translate_columns = translate_columns;
+ myopt.n_translate_columns = lengthof(translate_columns);
+
+ printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+ PQclear(res);
+ return true;
+}
+
/*
* \db
* Takes an optional regexp to select particular tablespaces
@@ -1620,6 +1683,22 @@ describeOneTableDetails(const char *schemaname,
if (verbose)
{
appendPQExpBufferStr(&buf, ",\n a.attstorage");
+
+ if (pset.sversion >= 100000)
+ appendPQExpBufferStr(&buf, ",\n CASE WHEN attcompression = 0 THEN NULL ELSE "
+ " (SELECT cm.cmname || "
+ " (CASE WHEN cmoptions IS NULL "
+ " THEN '' "
+ " ELSE '(' || array_to_string(ARRAY(SELECT quote_ident(option_name) || ' ' || quote_literal(option_value)"
+ " FROM pg_options_to_table(cmoptions)), ', ') || ')'"
+ " END) "
+ " FROM pg_catalog.pg_compression_opt c "
+ " JOIN pg_catalog.pg_compression cm ON (cm.oid = c.cmid) "
+ " WHERE c.oid = a.attcompression) "
+ " END AS attcmname");
+ else
+ appendPQExpBufferStr(&buf, "\n NULL AS attcmname");
+
appendPQExpBufferStr(&buf, ",\n CASE WHEN a.attstattarget=-1 THEN NULL ELSE a.attstattarget END AS attstattarget");
/*
@@ -1741,6 +1820,10 @@ describeOneTableDetails(const char *schemaname,
if (verbose)
{
headers[cols++] = gettext_noop("Storage");
+
+ if (tableinfo.relkind == RELKIND_RELATION)
+ headers[cols++] = gettext_noop("Compression");
+
if (tableinfo.relkind == RELKIND_RELATION ||
tableinfo.relkind == RELKIND_INDEX ||
tableinfo.relkind == RELKIND_MATVIEW ||
@@ -1840,6 +1923,11 @@ describeOneTableDetails(const char *schemaname,
"???")))),
false, false);
+ /* Column compression. */
+ if (tableinfo.relkind == RELKIND_RELATION)
+ printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1),
+ false, false);
+
/* Statistics target, if the relkind supports this feature */
if (tableinfo.relkind == RELKIND_RELATION ||
tableinfo.relkind == RELKIND_INDEX ||
@@ -1847,7 +1935,7 @@ describeOneTableDetails(const char *schemaname,
tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
{
- printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1),
+ printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2),
false, false);
}
@@ -1858,7 +1946,7 @@ describeOneTableDetails(const char *schemaname,
tableinfo.relkind == RELKIND_COMPOSITE_TYPE ||
tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
- printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2),
+ printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 3),
false, false);
}
}
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 14a5667f3e..0ab8518a36 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -18,6 +18,9 @@ extern bool describeAccessMethods(const char *pattern, bool verbose);
/* \db */
extern bool describeTablespaces(const char *pattern, bool verbose);
+/* \dCM */
+extern bool describeCompressionMethods(const char *pattern, bool verbose);
+
/* \df, \dfa, \dfn, \dft, \dfw, etc. */
extern bool describeFunctions(const char *functypes, const char *pattern, bool verbose, bool showSystem);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 4d1c0ec3c6..307f8bfbe5 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -227,6 +227,7 @@ slashUsage(unsigned short int pager)
fprintf(output, _(" \\db[+] [PATTERN] list tablespaces\n"));
fprintf(output, _(" \\dc[S+] [PATTERN] list conversions\n"));
fprintf(output, _(" \\dC[+] [PATTERN] list casts\n"));
+ fprintf(output, _(" \\dCM[+] [PATTERN] list compression methods\n"));
fprintf(output, _(" \\dd[S] [PATTERN] show object descriptions not displayed elsewhere\n"));
fprintf(output, _(" \\dD[S+] [PATTERN] list domains\n"));
fprintf(output, _(" \\ddp [PATTERN] list default privileges\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 2ab8809fa5..2477df82e4 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -889,6 +889,11 @@ static const SchemaQuery Query_for_list_of_statistics = {
" AND d.datname = pg_catalog.current_database() "\
" AND s.subdbid = d.oid"
+#define Query_for_list_of_compression_methods \
+" SELECT pg_catalog.quote_ident(cmname) "\
+" FROM pg_catalog.pg_compression "\
+" WHERE substring(pg_catalog.quote_ident(cmname),1,%d)='%s'"
+
/* the silly-looking length condition is just to eat up the current word */
#define Query_for_list_of_arguments \
"SELECT pg_catalog.oidvectortypes(proargtypes)||')' "\
@@ -1011,6 +1016,7 @@ static const pgsql_thing_t words_after_create[] = {
* CREATE CONSTRAINT TRIGGER is not supported here because it is designed
* to be used only by pg_dump.
*/
+ {"COMPRESSION METHOD", NULL, NULL},
{"CONFIGURATION", Query_for_list_of_ts_configurations, NULL, THING_NO_SHOW},
{"CONVERSION", "SELECT pg_catalog.quote_ident(conname) FROM pg_catalog.pg_conversion WHERE substring(pg_catalog.quote_ident(conname),1,%d)='%s'"},
{"DATABASE", Query_for_list_of_databases},
@@ -1424,8 +1430,8 @@ psql_completion(const char *text, int start, int end)
"\\a",
"\\connect", "\\conninfo", "\\C", "\\cd", "\\copy",
"\\copyright", "\\crosstabview",
- "\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
- "\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
+ "\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dCM", "\\dd", "\\ddp",
+ "\\dD", "\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
"\\dm", "\\dn", "\\do", "\\dO", "\\dp",
"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
@@ -1954,11 +1960,17 @@ psql_completion(const char *text, int start, int end)
/* ALTER TABLE ALTER [COLUMN] <foo> SET */
else if (Matches7("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET") ||
Matches6("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET"))
- COMPLETE_WITH_LIST5("(", "DEFAULT", "NOT NULL", "STATISTICS", "STORAGE");
+ COMPLETE_WITH_LIST6("(", "COMPRESSED", "DEFAULT", "NOT NULL", "STATISTICS", "STORAGE");
/* ALTER TABLE ALTER [COLUMN] <foo> SET ( */
else if (Matches8("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "(") ||
Matches7("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "("))
COMPLETE_WITH_LIST2("n_distinct", "n_distinct_inherited");
+ else if (Matches8("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "COMPRESSED") ||
+ Matches7("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "COMPRESSED"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_compression_methods);
+ else if (Matches9("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "COMPRESSED", MatchAny) ||
+ Matches8("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "COMPRESSED", MatchAny))
+ COMPLETE_WITH_CONST("WITH (");
/* ALTER TABLE ALTER [COLUMN] <foo> SET STORAGE */
else if (Matches8("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "STORAGE") ||
Matches7("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "STORAGE"))
@@ -2177,12 +2189,14 @@ psql_completion(const char *text, int start, int end)
"SCHEMA", "SEQUENCE", "STATISTICS", "SUBSCRIPTION",
"TABLE", "TYPE", "VIEW", "MATERIALIZED VIEW", "COLUMN", "AGGREGATE", "FUNCTION",
"OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", "LARGE OBJECT",
- "TABLESPACE", "TEXT SEARCH", "ROLE", NULL};
+ "TABLESPACE", "TEXT SEARCH", "ROLE", "COMPRESSION METHOD", NULL};
COMPLETE_WITH_LIST(list_COMMENT);
}
else if (Matches4("COMMENT", "ON", "ACCESS", "METHOD"))
COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
+ else if (Matches4("COMMENT", "ON", "COMPRESSION", "METHOD"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_compression_methods);
else if (Matches3("COMMENT", "ON", "FOREIGN"))
COMPLETE_WITH_LIST2("DATA WRAPPER", "TABLE");
else if (Matches4("COMMENT", "ON", "TEXT", "SEARCH"))
@@ -2255,6 +2269,14 @@ psql_completion(const char *text, int start, int end)
else if (Matches6("CREATE", "ACCESS", "METHOD", MatchAny, "TYPE", MatchAny))
COMPLETE_WITH_CONST("HANDLER");
+ /* CREATE COMPRESSION METHOD */
+ /* Complete "CREATE COMPRESSION METHOD <name>" */
+ else if (Matches4("CREATE", "COMPRESSION", "METHOD", MatchAny))
+ COMPLETE_WITH_CONST("HANDLER");
+ /* Complete "CREATE COMPRESSION METHOD <name> HANDLER" */
+ else if (Matches5("CREATE", "COMPRESSION", "METHOD", MatchAny, "HANDLER"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
+
/* CREATE DATABASE */
else if (Matches3("CREATE", "DATABASE", MatchAny))
COMPLETE_WITH_LIST9("OWNER", "TEMPLATE", "ENCODING", "TABLESPACE",
@@ -2687,6 +2709,7 @@ psql_completion(const char *text, int start, int end)
Matches4("DROP", "ACCESS", "METHOD", MatchAny) ||
(Matches4("DROP", "AGGREGATE|FUNCTION", MatchAny, MatchAny) &&
ends_with(prev_wd, ')')) ||
+ Matches4("DROP", "COMPRESSION", "METHOD", MatchAny) ||
Matches4("DROP", "EVENT", "TRIGGER", MatchAny) ||
Matches5("DROP", "FOREIGN", "DATA", "WRAPPER", MatchAny) ||
Matches4("DROP", "FOREIGN", "TABLE", MatchAny) ||
@@ -2775,6 +2798,12 @@ psql_completion(const char *text, int start, int end)
else if (Matches5("DROP", "RULE", MatchAny, "ON", MatchAny))
COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
+ /* DROP COMPRESSION METHOD */
+ else if (Matches2("DROP", "COMPRESSION"))
+ COMPLETE_WITH_CONST("METHOD");
+ else if (Matches3("DROP", "COMPRESSION", "METHOD"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_compression_methods);
+
/* EXECUTE */
else if (Matches1("EXECUTE"))
COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements);
@@ -3407,6 +3436,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
else if (TailMatchesCS1("\\db*"))
COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
+ else if (TailMatchesCS1("\\dCM*"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_compression_methods);
else if (TailMatchesCS1("\\dD*"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL);
else if (TailMatchesCS1("\\des*"))
diff --git a/src/include/access/compression.h b/src/include/access/compression.h
new file mode 100644
index 0000000000..b969bff3c1
--- /dev/null
+++ b/src/include/access/compression.h
@@ -0,0 +1,69 @@
+/*-------------------------------------------------------------------------
+ *
+ * compression.h
+ * API for Postgres compression methods.
+ *
+ * Copyright (c) 2015-2016, PostgreSQL Global Development Group
+ *
+ * src/include/access/compression.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef COMPRESSION_H
+#define COMPRESSION_H
+
+#include "postgres.h"
+#include "catalog/pg_attribute.h"
+#include "nodes/nodes.h"
+#include "nodes/pg_list.h"
+
+/* parsenodes.h */
+typedef struct ColumnCompression ColumnCompression;
+typedef struct CompressionMethodRoutine CompressionMethodRoutine;
+
+typedef struct
+{
+ CompressionMethodRoutine *routine;
+ List *options;
+ Oid cmoptoid;
+} AttributeCompression;
+
+typedef void (*CompressionConfigureRoutine)
+ (Form_pg_attribute attr, List *options);
+typedef void (*CompressionDropRoutine)
+ (Form_pg_attribute attr, List *options);
+typedef struct varlena *(*CompressionRoutine)
+ (AttributeCompression * ac, const struct varlena *data);
+typedef struct varlena *(*DecompressionRoutine)
+ (const struct varlena *data, List *options);
+
+/*
+ * API struct for an compression method.
+ * Note this must be stored in a single palloc'd chunk of memory.
+ */
+typedef struct CompressionMethodRoutine
+{
+ NodeTag type;
+
+ CompressionConfigureRoutine configure;
+ CompressionDropRoutine drop;
+ CompressionRoutine compress;
+ DecompressionRoutine decompress;
+} CompressionMethodRoutine;
+
+typedef struct CompressionMethodOpArgs
+{
+ Oid cmhanderid;
+ Oid typeid;
+} CompressionMethodOpArgs;
+
+extern CompressionMethodRoutine * GetCompressionRoutine(Oid cmoptoid);
+extern List *GetCompressionOptionsList(Oid cmoptoid);
+extern Oid CreateCompressionOptions(Form_pg_attribute attr, Oid cmid,
+ List *options);
+extern ColumnCompression * GetColumnCompressionForAttribute(Form_pg_attribute att);
+extern void CheckCompressionMismatch(ColumnCompression * c1,
+ ColumnCompression * c2, const char *attributeName);
+
+#endif /* COMPRESSION_H */
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 5cdaa3bff1..573512367a 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -258,7 +258,6 @@ extern void add_string_reloption(bits32 kinds, char *name, char *desc,
extern Datum transformRelOptions(Datum oldOptions, List *defList,
char *namspace, char *validnsps[],
bool ignoreOids, bool isReset);
-extern List *untransformRelOptions(Datum options);
extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
amoptions_function amoptions);
extern relopt_value *parseRelOptions(Datum options, bool validate,
@@ -269,6 +268,9 @@ extern void fillRelOptions(void *rdopts, Size basesize,
relopt_value *options, int numoptions,
bool validate,
const relopt_parse_elt *elems, int nelems);
+extern char *formatRelOptions(List *options);
+extern void freeRelOptions(List *options);
+extern List *untransformRelOptions(Datum options);
extern bytea *default_reloptions(Datum reloptions, bool validate,
relopt_kind kind);
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index 989fe738bb..e2807276d0 100644
--- a/src/include/access/tupdesc.h
+++ b/src/include/access/tupdesc.h
@@ -14,7 +14,9 @@
#ifndef TUPDESC_H
#define TUPDESC_H
+#include "postgres.h"
#include "access/attnum.h"
+#include "access/compression.h"
#include "catalog/pg_attribute.h"
#include "nodes/pg_list.h"
@@ -76,12 +78,16 @@ typedef struct tupleDesc
bool tdhasoid; /* tuple has oid attribute in its header */
int tdrefcount; /* reference count, or -1 if not counting */
TupleConstr *constr; /* constraints, or NULL if none */
+ AttributeCompression *tdcompression;
/* attrs[N] is the description of Attribute Number N+1 */
FormData_pg_attribute attrs[FLEXIBLE_ARRAY_MEMBER];
} *TupleDesc;
/* Accessor for the i'th attribute of tupdesc. */
#define TupleDescAttr(tupdesc, i) (&(tupdesc)->attrs[(i)])
+#define TupleDescAttrCompression(tupdesc, i) \
+ ((tupdesc)->tdcompression? &((tupdesc)->tdcompression[i]) : NULL)
+
extern TupleDesc CreateTemplateTupleDesc(int natts, bool hasoid);
@@ -134,6 +140,10 @@ extern void TupleDescInitEntryCollation(TupleDesc desc,
AttrNumber attributeNumber,
Oid collationid);
+extern void TupleDescInitAttrCompression(TupleDesc desc,
+ AttrNumber attnum,
+ Oid cmoptoid);
+
extern TupleDesc BuildDescForRelation(List *schema);
extern TupleDesc BuildDescFromLists(List *names, List *types, List *typmods, List *collations);
diff --git a/src/include/access/tuptoaster.h b/src/include/access/tuptoaster.h
index fd9f83ac44..955b6620cd 100644
--- a/src/include/access/tuptoaster.h
+++ b/src/include/access/tuptoaster.h
@@ -210,7 +210,7 @@ extern HeapTuple toast_build_flattened_tuple(TupleDesc tupleDesc,
* Create a compressed version of a varlena datum, if possible
* ----------
*/
-extern Datum toast_compress_datum(Datum value);
+extern Datum toast_compress_datum(Datum value, AttributeCompression * ac);
/* ----------
* toast_raw_datum_size -
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index b9f98423cc..36cadd409e 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -165,10 +165,12 @@ typedef enum ObjectClass
OCLASS_PUBLICATION, /* pg_publication */
OCLASS_PUBLICATION_REL, /* pg_publication_rel */
OCLASS_SUBSCRIPTION, /* pg_subscription */
- OCLASS_TRANSFORM /* pg_transform */
+ OCLASS_TRANSFORM, /* pg_transform */
+ OCLASS_COMPRESSION_METHOD, /* pg_compression */
+ OCLASS_COMPRESSION_OPTIONS /* pg_compression_opt */
} ObjectClass;
-#define LAST_OCLASS OCLASS_TRANSFORM
+#define LAST_OCLASS OCLASS_COMPRESSION_OPTIONS
/* flag bits for performDeletion/performMultipleDeletions: */
#define PERFORM_DELETION_INTERNAL 0x0001 /* internal action */
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index ef8493674c..b580f1971a 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -120,6 +120,14 @@ DECLARE_UNIQUE_INDEX(pg_collation_name_enc_nsp_index, 3164, on pg_collation usin
DECLARE_UNIQUE_INDEX(pg_collation_oid_index, 3085, on pg_collation using btree(oid oid_ops));
#define CollationOidIndexId 3085
+DECLARE_UNIQUE_INDEX(pg_compression_oid_index, 3422, on pg_compression using btree(oid oid_ops));
+#define CompressionMethodOidIndexId 3422
+DECLARE_UNIQUE_INDEX(pg_compression_name_index, 3423, on pg_compression using btree(cmname name_ops));
+#define CompressionMethodNameIndexId 3423
+
+DECLARE_UNIQUE_INDEX(pg_compression_opt_oid_index, 3424, on pg_compression_opt using btree(oid oid_ops));
+#define CompressionOptionsOidIndexId 3424
+
DECLARE_INDEX(pg_constraint_conname_nsp_index, 2664, on pg_constraint using btree(conname name_ops, connamespace oid_ops));
#define ConstraintNameNspIndexId 2664
DECLARE_INDEX(pg_constraint_conrelid_index, 2665, on pg_constraint using btree(conrelid oid_ops));
diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h
index bcf28e8f04..caadd61031 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -156,6 +156,9 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
/* attribute's collation */
Oid attcollation;
+ /* attribute's compression options or InvalidOid */
+ Oid attcompression;
+
#ifdef CATALOG_VARLEN /* variable-length fields start here */
/* NOTE: The following fields are not present in tuple descriptors. */
@@ -174,10 +177,10 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
* ATTRIBUTE_FIXED_PART_SIZE is the size of the fixed-layout,
* guaranteed-not-null part of a pg_attribute row. This is in fact as much
* of the row as gets copied into tuple descriptors, so don't expect you
- * can access fields beyond attcollation except in a real tuple!
+ * can access fields beyond attcompression except in a real tuple!
*/
#define ATTRIBUTE_FIXED_PART_SIZE \
- (offsetof(FormData_pg_attribute,attcollation) + sizeof(Oid))
+ (offsetof(FormData_pg_attribute,attcompression) + sizeof(Oid))
/* ----------------
* Form_pg_attribute corresponds to a pointer to a tuple with
@@ -191,29 +194,30 @@ typedef FormData_pg_attribute *Form_pg_attribute;
* ----------------
*/
-#define Natts_pg_attribute 22
-#define Anum_pg_attribute_attrelid 1
-#define Anum_pg_attribute_attname 2
-#define Anum_pg_attribute_atttypid 3
-#define Anum_pg_attribute_attstattarget 4
-#define Anum_pg_attribute_attlen 5
-#define Anum_pg_attribute_attnum 6
-#define Anum_pg_attribute_attndims 7
-#define Anum_pg_attribute_attcacheoff 8
-#define Anum_pg_attribute_atttypmod 9
-#define Anum_pg_attribute_attbyval 10
-#define Anum_pg_attribute_attstorage 11
-#define Anum_pg_attribute_attalign 12
-#define Anum_pg_attribute_attnotnull 13
-#define Anum_pg_attribute_atthasdef 14
-#define Anum_pg_attribute_attidentity 15
-#define Anum_pg_attribute_attisdropped 16
-#define Anum_pg_attribute_attislocal 17
-#define Anum_pg_attribute_attinhcount 18
-#define Anum_pg_attribute_attcollation 19
-#define Anum_pg_attribute_attacl 20
-#define Anum_pg_attribute_attoptions 21
-#define Anum_pg_attribute_attfdwoptions 22
+#define Natts_pg_attribute 23
+#define Anum_pg_attribute_attrelid 1
+#define Anum_pg_attribute_attname 2
+#define Anum_pg_attribute_atttypid 3
+#define Anum_pg_attribute_attstattarget 4
+#define Anum_pg_attribute_attlen 5
+#define Anum_pg_attribute_attnum 6
+#define Anum_pg_attribute_attndims 7
+#define Anum_pg_attribute_attcacheoff 8
+#define Anum_pg_attribute_atttypmod 9
+#define Anum_pg_attribute_attbyval 10
+#define Anum_pg_attribute_attstorage 11
+#define Anum_pg_attribute_attalign 12
+#define Anum_pg_attribute_attnotnull 13
+#define Anum_pg_attribute_atthasdef 14
+#define Anum_pg_attribute_attidentity 15
+#define Anum_pg_attribute_attisdropped 16
+#define Anum_pg_attribute_attislocal 17
+#define Anum_pg_attribute_attinhcount 18
+#define Anum_pg_attribute_attcollation 19
+#define Anum_pg_attribute_attcompression 20
+#define Anum_pg_attribute_attacl 21
+#define Anum_pg_attribute_attoptions 22
+#define Anum_pg_attribute_attfdwoptions 23
/* ----------------
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index b256657bda..04c7c18d70 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -147,9 +147,9 @@ typedef FormData_pg_class *Form_pg_class;
* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId;
* similarly, "1" in relminmxid stands for FirstMultiXactId
*/
-DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 30 0 t f f f f f f t n f 3 1 _null_ _null_ _null_));
+DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 31 0 t f f f f f f t n f 3 1 _null_ _null_ _null_));
DESCR("");
-DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 22 0 f f f f f f f t n f 3 1 _null_ _null_ _null_));
+DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 23 0 f f f f f f f t n f 3 1 _null_ _null_ _null_));
DESCR("");
DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f p r 29 0 t f f f f f f t n f 3 1 _null_ _null_ _null_));
DESCR("");
diff --git a/src/include/catalog/pg_compression.h b/src/include/catalog/pg_compression.h
new file mode 100644
index 0000000000..1d5f9ac479
--- /dev/null
+++ b/src/include/catalog/pg_compression.h
@@ -0,0 +1,55 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_compression.h
+ * definition of the system "compression method" relation (pg_compression)
+ * along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_compression.h
+ *
+ * NOTES
+ * the genbki.pl script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ * XXX do NOT break up DATA() statements into multiple lines!
+ * the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_COMPRESSION_H
+#define PG_COMPRESSION_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ * pg_compression definition. cpp turns this into
+ * typedef struct FormData_pg_compression
+ * ----------------
+ */
+#define CompressionMethodRelationId 3419
+
+CATALOG(pg_compression,3419)
+{
+ NameData cmname; /* compression method name */
+ regproc cmhandler; /* compression handler */
+} FormData_pg_compression;
+
+/* ----------------
+ * Form_pg_compression corresponds to a pointer to a tuple with
+ * the format of pg_compression relation.
+ * ----------------
+ */
+typedef FormData_pg_compression * Form_pg_compression;
+
+/* ----------------
+ * compiler constants for pg_compression
+ * ----------------
+ */
+#define Natts_pg_compression 2
+#define Anum_pg_compression_cmname 1
+#define Anum_pg_compression_cmhandler 2
+
+#endif /* PG_COMPRESSION_H */
diff --git a/src/include/catalog/pg_compression_opt.h b/src/include/catalog/pg_compression_opt.h
new file mode 100644
index 0000000000..343d3355d9
--- /dev/null
+++ b/src/include/catalog/pg_compression_opt.h
@@ -0,0 +1,54 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_compression_opt.h
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_compression_opt.h
+ *
+ * NOTES
+ * the genbki.pl script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ * XXX do NOT break up DATA() statements into multiple lines!
+ * the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_COMPRESSION_OPT_H
+#define PG_COMPRESSION_OPT_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ * pg_compression_opt definition. cpp turns this into
+ * typedef struct FormData_pg_compression_opt
+ * ----------------
+ */
+#define CompressionOptRelationId 3420
+
+CATALOG(pg_compression_opt,3420)
+{
+ Oid cmid; /* compression method oid */
+ regproc cmhandler; /* compression handler */
+ text cmoptions[1]; /* specific options from WITH */
+} FormData_pg_compression_opt;
+
+/* ----------------
+ * Form_pg_compression_opt corresponds to a pointer to a tuple with
+ * the format of pg_compression_opt relation.
+ * ----------------
+ */
+typedef FormData_pg_compression_opt * Form_pg_compression_opt;
+
+/* ----------------
+ * compiler constants for pg_compression_opt
+ * ----------------
+ */
+#define Natts_pg_compression_opt 3
+#define Anum_pg_compression_opt_cmid 1
+#define Anum_pg_compression_opt_cmhandler 2
+#define Anum_pg_compression_opt_cmoptions 3
+
+#endif /* PG_COMPRESSION_OPT_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index d820b56aa1..5a2be99f0b 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3874,6 +3874,10 @@ DATA(insert OID = 3311 ( tsm_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s
DESCR("I/O");
DATA(insert OID = 3312 ( tsm_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3310" _null_ _null_ _null_ _null_ _null_ tsm_handler_out _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 3425 ( compression_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3421 "2275" _null_ _null_ _null_ _null_ _null_ compression_handler_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 3426 ( compression_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3421" _null_ _null_ _null_ _null_ _null_ compression_handler_out _null_ _null_ _null_ ));
+DESCR("I/O");
/* tablesample method handlers */
DATA(insert OID = 3313 ( bernoulli PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3310 "2281" _null_ _null_ _null_ _null_ _null_ tsm_bernoulli_handler _null_ _null_ _null_ ));
@@ -4676,6 +4680,8 @@ DATA(insert OID = 3646 ( gtsvectorin PGNSP PGUID 12 1 0 0 0 f f f f t f i s
DESCR("I/O");
DATA(insert OID = 3647 ( gtsvectorout PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3642" _null_ _null_ _null_ _null_ _null_ gtsvectorout _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 3453 ( tsvector_compression_handler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3421 "2281" _null_ _null_ _null_ _null_ _null_ tsvector_compression_handler _null_ _null_ _null_ ));
+DESCR("tsvector compression handler");
DATA(insert OID = 3616 ( tsvector_lt PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "3614 3614" _null_ _null_ _null_ _null_ _null_ tsvector_lt _null_ _null_ _null_ ));
DATA(insert OID = 3617 ( tsvector_le PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "3614 3614" _null_ _null_ _null_ _null_ _null_ tsvector_le _null_ _null_ _null_ ));
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index ffdb452b02..23921db534 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -199,6 +199,9 @@ CATALOG(pg_type,1247) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71) BKI_SCHEMA_MACRO
*/
Oid typcollation;
+ /* Default compression method for the datatype or InvalidOid */
+ Oid typdefaultcm;
+
#ifdef CATALOG_VARLEN /* variable-length fields start here */
/*
@@ -236,7 +239,7 @@ typedef FormData_pg_type *Form_pg_type;
* compiler constants for pg_type
* ----------------
*/
-#define Natts_pg_type 30
+#define Natts_pg_type 31
#define Anum_pg_type_typname 1
#define Anum_pg_type_typnamespace 2
#define Anum_pg_type_typowner 3
@@ -264,9 +267,10 @@ typedef FormData_pg_type *Form_pg_type;
#define Anum_pg_type_typtypmod 25
#define Anum_pg_type_typndims 26
#define Anum_pg_type_typcollation 27
-#define Anum_pg_type_typdefaultbin 28
-#define Anum_pg_type_typdefault 29
-#define Anum_pg_type_typacl 30
+#define Anum_pg_type_typdefaultcm 28
+#define Anum_pg_type_typdefaultbin 29
+#define Anum_pg_type_typdefault 30
+#define Anum_pg_type_typacl 31
/* ----------------
@@ -283,102 +287,102 @@ typedef FormData_pg_type *Form_pg_type;
*/
/* OIDS 1 - 99 */
-DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("boolean, 'true'/'false'");
#define BOOLOID 16
-DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("variable-length string, binary values escaped");
#define BYTEAOID 17
-DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("single character");
#define CHAROID 18
-DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("63-byte type for storing system identifiers");
#define NAMEOID 19
-DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("~18 digit integer, 8-byte storage");
#define INT8OID 20
-DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("-32 thousand to 32 thousand, 2-byte storage");
#define INT2OID 21
-DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("array of int2, used in system tables");
#define INT2VECTOROID 22
-DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("-2 billion to 2 billion integer, 4-byte storage");
#define INT4OID 23
-DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("registered procedure");
#define REGPROCOID 24
-DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 100 0 _null_ _null_ _null_ ));
DESCR("variable-length string, no limit specified");
#define TEXTOID 25
-DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("object identifier(oid), maximum 4 billion");
#define OIDOID 26
-DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("(block, offset), physical location of tuple");
#define TIDOID 27
-DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("transaction id");
#define XIDOID 28
-DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("command identifier type, sequence in transaction id");
#define CIDOID 29
-DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("array of oids, used in system tables");
#define OIDVECTOROID 30
/* hand-built rowtype entries for bootstrapped catalogs */
/* NB: OIDs assigned here must match the BKI_ROWTYPE_OID declarations */
-DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
/* OIDS 100 - 199 */
-DATA(insert OID = 114 ( json PGNSP PGUID -1 f b U f t \054 0 0 199 json_in json_out json_recv json_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 114 ( json PGNSP PGUID -1 f b U f t \054 0 0 199 json_in json_out json_recv json_send - - - i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define JSONOID 114
-DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("XML content");
#define XMLOID 142
-DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 199 ( _json PGNSP PGUID -1 f b A f t \054 0 114 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 199 ( _json PGNSP PGUID -1 f b A f t \054 0 114 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 194 ( pg_node_tree PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 194 ( pg_node_tree PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 100 0 _null_ _null_ _null_ ));
DESCR("string representing an internal node tree");
#define PGNODETREEOID 194
-DATA(insert OID = 3361 ( pg_ndistinct PGNSP PGUID -1 f b S f t \054 0 0 0 pg_ndistinct_in pg_ndistinct_out pg_ndistinct_recv pg_ndistinct_send - - - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 3361 ( pg_ndistinct PGNSP PGUID -1 f b S f t \054 0 0 0 pg_ndistinct_in pg_ndistinct_out pg_ndistinct_recv pg_ndistinct_send - - - i x f 0 -1 0 100 0 _null_ _null_ _null_ ));
DESCR("multivariate ndistinct coefficients");
#define PGNDISTINCTOID 3361
-DATA(insert OID = 3402 ( pg_dependencies PGNSP PGUID -1 f b S f t \054 0 0 0 pg_dependencies_in pg_dependencies_out pg_dependencies_recv pg_dependencies_send - - - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 3402 ( pg_dependencies PGNSP PGUID -1 f b S f t \054 0 0 0 pg_dependencies_in pg_dependencies_out pg_dependencies_recv pg_dependencies_send - - - i x f 0 -1 0 100 0 _null_ _null_ _null_ ));
DESCR("multivariate dependencies");
#define PGDEPENDENCIESOID 3402
-DATA(insert OID = 32 ( pg_ddl_command PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 pg_ddl_command_in pg_ddl_command_out pg_ddl_command_recv pg_ddl_command_send - - - ALIGNOF_POINTER p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 32 ( pg_ddl_command PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 pg_ddl_command_in pg_ddl_command_out pg_ddl_command_recv pg_ddl_command_send - - - ALIGNOF_POINTER p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("internal type for passing CollectedCommand");
#define PGDDLCOMMANDOID 32
/* OIDS 200 - 299 */
-DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("storage manager");
/* OIDS 300 - 399 */
@@ -388,280 +392,280 @@ DESCR("storage manager");
/* OIDS 500 - 599 */
/* OIDS 600 - 699 */
-DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("geometric point '(x, y)'");
#define POINTOID 600
-DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("geometric line segment '(pt1,pt2)'");
#define LSEGOID 601
-DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("geometric path '(pt1,...)'");
#define PATHOID 602
-DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("geometric box '(lower left,upper right)'");
#define BOXOID 603
-DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("geometric polygon '(pt1,...)'");
#define POLYGONOID 604
-DATA(insert OID = 628 ( line PGNSP PGUID 24 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 628 ( line PGNSP PGUID 24 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("geometric line");
#define LINEOID 628
-DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
/* OIDS 700 - 799 */
-DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("single-precision floating point number, 4-byte storage");
#define FLOAT4OID 700
-DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("double-precision floating point number, 8-byte storage");
#define FLOAT8OID 701
-DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("absolute, limited-range date and time (Unix system time)");
#define ABSTIMEOID 702
-DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("relative, limited-range time interval (Unix delta time)");
#define RELTIMEOID 703
-DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("(abstime,abstime), time interval");
#define TINTERVALOID 704
-DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f p X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f p X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("");
#define UNKNOWNOID 705
-DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("geometric circle '(center,radius)'");
#define CIRCLEOID 718
-DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("monetary amounts, $d,ddd.cc");
#define CASHOID 790
-DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
/* OIDS 800 - 899 */
-DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("XX:XX:XX:XX:XX:XX, MAC address");
#define MACADDROID 829
-DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("IP address/netmask, host address, netmask optional");
#define INETOID 869
-DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("network IP address/netmask, network address");
#define CIDROID 650
-DATA(insert OID = 774 ( macaddr8 PGNSP PGUID 8 f b U f t \054 0 0 775 macaddr8_in macaddr8_out macaddr8_recv macaddr8_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 774 ( macaddr8 PGNSP PGUID 8 f b U f t \054 0 0 775 macaddr8_in macaddr8_out macaddr8_recv macaddr8_send - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("XX:XX:XX:XX:XX:XX:XX:XX, MAC address");
#define MACADDR8OID 774
/* OIDS 900 - 999 */
/* OIDS 1000 - 1099 */
-DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define INT2ARRAYOID 1005
-DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define INT4ARRAYOID 1007
-DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 100 0 _null_ _null_ _null_ ));
#define TEXTARRAYOID 1009
-DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define OIDARRAYOID 1028
-DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
-DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
-DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout array_typanalyze i x f 0 -1 0 100 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout array_typanalyze i x f 0 -1 0 100 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define FLOAT4ARRAYOID 1021
-DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("access control list");
#define ACLITEMOID 1033
-DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 775 ( _macaddr8 PGNSP PGUID -1 f b A f t \054 0 774 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 775 ( _macaddr8 PGNSP PGUID -1 f b A f t \054 0 774 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define CSTRINGARRAYOID 1263
-DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 100 0 _null_ _null_ _null_ ));
DESCR("char(length), blank-padded string, fixed storage length");
#define BPCHAROID 1042
-DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 100 0 _null_ _null_ _null_ ));
DESCR("varchar(length), non-blank-padded string, variable storage length");
#define VARCHAROID 1043
-DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("date");
#define DATEOID 1082
-DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("time of day");
#define TIMEOID 1083
/* OIDS 1100 - 1199 */
-DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("date and time");
#define TIMESTAMPOID 1114
-DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("date and time with time zone");
#define TIMESTAMPTZOID 1184
-DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("@ <number> <units>, time interval");
#define INTERVALOID 1186
-DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
/* OIDS 1200 - 1299 */
-DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("time of day with time zone");
#define TIMETZOID 1266
-DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
/* OIDS 1500 - 1599 */
-DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("fixed-length bit string");
#define BITOID 1560
-DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("variable-length bit string");
#define VARBITOID 1562
-DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
/* OIDS 1600 - 1699 */
/* OIDS 1700 - 1799 */
-DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("numeric(precision, decimal), arbitrary precision number");
#define NUMERICOID 1700
-DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("reference to cursor (portal name)");
#define REFCURSOROID 1790
/* OIDS 2200 - 2299 */
-DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("registered procedure (with args)");
#define REGPROCEDUREOID 2202
-DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("registered operator");
#define REGOPEROID 2203
-DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("registered operator (with args)");
#define REGOPERATOROID 2204
-DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("registered class");
#define REGCLASSOID 2205
-DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("registered type");
#define REGTYPEOID 2206
-DATA(insert OID = 4096 ( regrole PGNSP PGUID 4 t b N f t \054 0 0 4097 regrolein regroleout regrolerecv regrolesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 4096 ( regrole PGNSP PGUID 4 t b N f t \054 0 0 4097 regrolein regroleout regrolerecv regrolesend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("registered role");
#define REGROLEOID 4096
-DATA(insert OID = 4089 ( regnamespace PGNSP PGUID 4 t b N f t \054 0 0 4090 regnamespacein regnamespaceout regnamespacerecv regnamespacesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 4089 ( regnamespace PGNSP PGUID 4 t b N f t \054 0 0 4090 regnamespacein regnamespaceout regnamespacerecv regnamespacesend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("registered namespace");
#define REGNAMESPACEOID 4089
-DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define REGTYPEARRAYOID 2211
-DATA(insert OID = 4097 ( _regrole PGNSP PGUID -1 f b A f t \054 0 4096 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 4090 ( _regnamespace PGNSP PGUID -1 f b A f t \054 0 4089 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 4097 ( _regrole PGNSP PGUID -1 f b A f t \054 0 4096 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 4090 ( _regnamespace PGNSP PGUID -1 f b A f t \054 0 4089 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
/* uuid */
-DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("UUID datatype");
#define UUIDOID 2950
-DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
/* pg_lsn */
-DATA(insert OID = 3220 ( pg_lsn PGNSP PGUID 8 FLOAT8PASSBYVAL b U f t \054 0 0 3221 pg_lsn_in pg_lsn_out pg_lsn_recv pg_lsn_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3220 ( pg_lsn PGNSP PGUID 8 FLOAT8PASSBYVAL b U f t \054 0 0 3221 pg_lsn_in pg_lsn_out pg_lsn_recv pg_lsn_send - - - d p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("PostgreSQL LSN datatype");
#define LSNOID 3220
-DATA(insert OID = 3221 ( _pg_lsn PGNSP PGUID -1 f b A f t \054 0 3220 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3221 ( _pg_lsn PGNSP PGUID -1 f b A f t \054 0 3220 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
/* text search */
-DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("text representation for text search");
#define TSVECTOROID 3614
-DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("GiST index internal text representation for text search");
#define GTSVECTOROID 3642
-DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("query representation for text search");
#define TSQUERYOID 3615
-DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("registered text search configuration");
#define REGCONFIGOID 3734
-DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("registered text search dictionary");
#define REGDICTIONARYOID 3769
-DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
/* jsonb */
-DATA(insert OID = 3802 ( jsonb PGNSP PGUID -1 f b U f t \054 0 0 3807 jsonb_in jsonb_out jsonb_recv jsonb_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3802 ( jsonb PGNSP PGUID -1 f b U f t \054 0 0 3807 jsonb_in jsonb_out jsonb_recv jsonb_send - - - i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("Binary JSON");
#define JSONBOID 3802
-DATA(insert OID = 3807 ( _jsonb PGNSP PGUID -1 f b A f t \054 0 3802 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3807 ( _jsonb PGNSP PGUID -1 f b A f t \054 0 3802 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("txid snapshot");
-DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
/* range types */
-DATA(insert OID = 3904 ( int4range PGNSP PGUID -1 f r R f t \054 0 0 3905 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3904 ( int4range PGNSP PGUID -1 f r R f t \054 0 0 3905 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("range of integers");
#define INT4RANGEOID 3904
-DATA(insert OID = 3905 ( _int4range PGNSP PGUID -1 f b A f t \054 0 3904 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3906 ( numrange PGNSP PGUID -1 f r R f t \054 0 0 3907 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3905 ( _int4range PGNSP PGUID -1 f b A f t \054 0 3904 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3906 ( numrange PGNSP PGUID -1 f r R f t \054 0 0 3907 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("range of numerics");
-DATA(insert OID = 3907 ( _numrange PGNSP PGUID -1 f b A f t \054 0 3906 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3908 ( tsrange PGNSP PGUID -1 f r R f t \054 0 0 3909 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3907 ( _numrange PGNSP PGUID -1 f b A f t \054 0 3906 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3908 ( tsrange PGNSP PGUID -1 f r R f t \054 0 0 3909 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("range of timestamps without time zone");
-DATA(insert OID = 3909 ( _tsrange PGNSP PGUID -1 f b A f t \054 0 3908 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3910 ( tstzrange PGNSP PGUID -1 f r R f t \054 0 0 3911 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3909 ( _tsrange PGNSP PGUID -1 f b A f t \054 0 3908 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3910 ( tstzrange PGNSP PGUID -1 f r R f t \054 0 0 3911 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("range of timestamps with time zone");
-DATA(insert OID = 3911 ( _tstzrange PGNSP PGUID -1 f b A f t \054 0 3910 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3912 ( daterange PGNSP PGUID -1 f r R f t \054 0 0 3913 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3911 ( _tstzrange PGNSP PGUID -1 f b A f t \054 0 3910 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3912 ( daterange PGNSP PGUID -1 f r R f t \054 0 0 3913 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("range of dates");
-DATA(insert OID = 3913 ( _daterange PGNSP PGUID -1 f b A f t \054 0 3912 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3926 ( int8range PGNSP PGUID -1 f r R f t \054 0 0 3927 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3913 ( _daterange PGNSP PGUID -1 f b A f t \054 0 3912 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3926 ( int8range PGNSP PGUID -1 f r R f t \054 0 0 3927 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("range of bigints");
-DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
/*
* pseudo-types
@@ -676,42 +680,44 @@ DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 arr
* but there is now support for it in records and arrays. Perhaps we should
* just treat it as a regular base type?
*/
-DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define RECORDOID 2249
-DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define RECORDARRAYOID 2287
-DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define CSTRINGOID 2275
-DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define ANYOID 2276
-DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define ANYARRAYOID 2277
-DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out void_recv void_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out void_recv void_send - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define VOIDOID 2278
-DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define TRIGGEROID 2279
-DATA(insert OID = 3838 ( event_trigger PGNSP PGUID 4 t p P f t \054 0 0 0 event_trigger_in event_trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3838 ( event_trigger PGNSP PGUID 4 t p P f t \054 0 0 0 event_trigger_in event_trigger_out - - - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define EVTTRIGGEROID 3838
-DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define LANGUAGE_HANDLEROID 2280
-DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define INTERNALOID 2281
-DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define OPAQUEOID 2282
-DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define ANYELEMENTOID 2283
-DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define ANYNONARRAYOID 2776
-DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define ANYENUMOID 3500
-DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_handler_in fdw_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_handler_in fdw_handler_out - - - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define FDW_HANDLEROID 3115
-DATA(insert OID = 325 ( index_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 325 ( index_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define INDEX_AM_HANDLEROID 325
-DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define TSM_HANDLEROID 3310
-DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define ANYRANGEOID 3831
+DATA(insert OID = 3421 ( compression_handler PGNSP PGUID 4 t p P f t \054 0 0 0 compression_handler_in compression_handler_out - - - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
+#define COMPRESSION_HANDLEROID 3421
/*
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index f7bb4a54f7..01c542e829 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -140,6 +140,7 @@ extern Oid RemoveUserMapping(DropUserMappingStmt *stmt);
extern void RemoveUserMappingById(Oid umId);
extern void CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid);
extern void ImportForeignSchema(ImportForeignSchemaStmt *stmt);
+extern Datum optionListToArray(List *options);
extern Datum transformGenericOptions(Oid catalogId,
Datum oldOptions,
List *options,
@@ -152,6 +153,14 @@ extern Oid get_index_am_oid(const char *amname, bool missing_ok);
extern Oid get_am_oid(const char *amname, bool missing_ok);
extern char *get_am_name(Oid amOid);
+/* commands/compressioncmds.c */
+extern ObjectAddress DefineCompressionMethod(List *names, List *parameters);
+extern void RemoveCompressionMethodById(Oid cmOid);
+extern void RemoveCompressionOptionsById(Oid cmoptoid);
+extern Oid get_compression_method_oid(const char *cmname, bool missing_ok);
+extern char *get_compression_method_name(Oid cmOid);
+extern char *get_compression_method_name_for_opt(Oid cmoptoid);
+
/* support routines in commands/define.c */
extern char *defGetString(DefElem *def);
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index abd31b68d4..a834cb4480 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -23,7 +23,8 @@
extern ObjectAddress DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
- ObjectAddress *typaddress, const char *queryString);
+ ObjectAddress *typaddress, const char *queryString,
+ Node **pAlterStmt);
extern void RemoveRelations(DropStmt *drop);
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index 34f6fe328f..d198fd1d05 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -53,5 +53,6 @@ extern Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
bool isImplicitArray,
bool errorOnTableType,
ObjectAddresses *objsMoved);
+extern void AlterType(AlterTypeStmt * stmt);
#endif /* TYPECMDS_H */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 27bd4f3363..6c1c6350e3 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -413,6 +413,8 @@ typedef enum NodeTag
T_DropSubscriptionStmt,
T_CreateStatsStmt,
T_AlterCollationStmt,
+ T_AlterTypeStmt,
+ T_AlterTypeCmd,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
@@ -468,6 +470,7 @@ typedef enum NodeTag
T_PartitionBoundSpec,
T_PartitionRangeDatum,
T_PartitionCmd,
+ T_ColumnCompression,
/*
* TAGS FOR REPLICATION GRAMMAR PARSE NODES (replnodes.h)
@@ -497,7 +500,8 @@ typedef enum NodeTag
T_FdwRoutine, /* in foreign/fdwapi.h */
T_IndexAmRoutine, /* in access/amapi.h */
T_TsmRoutine, /* in access/tsmapi.h */
- T_ForeignKeyCacheInfo /* in utils/rel.h */
+ T_ForeignKeyCacheInfo, /* in utils/rel.h */
+ T_CompressionMethodRoutine, /* in access/compression.h */
} NodeTag;
/*
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 3171815320..82a9810e8a 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -615,6 +615,14 @@ typedef struct RangeTableSample
int location; /* method name location, or -1 if unknown */
} RangeTableSample;
+typedef struct ColumnCompression
+{
+ NodeTag type;
+ char *methodName;
+ Oid methodOid;
+ List *options;
+} ColumnCompression;
+
/*
* ColumnDef - column definition (used in various creates)
*
@@ -638,6 +646,7 @@ typedef struct ColumnDef
NodeTag type;
char *colname; /* name of column */
TypeName *typeName; /* type of column */
+ ColumnCompression *compression;
int inhcount; /* number of times column is inherited */
bool is_local; /* column has local (non-inherited) def'n */
bool is_not_null; /* NOT NULL constraint specified? */
@@ -1615,6 +1624,7 @@ typedef enum ObjectType
OBJECT_CAST,
OBJECT_COLUMN,
OBJECT_COLLATION,
+ OBJECT_COMPRESSION_METHOD,
OBJECT_CONVERSION,
OBJECT_DATABASE,
OBJECT_DEFAULT,
@@ -1761,7 +1771,8 @@ typedef enum AlterTableType
AT_DetachPartition, /* DETACH PARTITION */
AT_AddIdentity, /* ADD IDENTITY */
AT_SetIdentity, /* SET identity column options */
- AT_DropIdentity /* DROP IDENTITY */
+ AT_DropIdentity, /* DROP IDENTITY */
+ AT_AlterColumnCompression /* ALTER COLUMN name COMPRESSED cm WITH (...) */
} AlterTableType;
typedef struct ReplicaIdentityStmt
@@ -1777,8 +1788,8 @@ typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */
AlterTableType subtype; /* Type of table alteration to apply */
char *name; /* column, constraint, or trigger to act on,
* or tablespace */
- int16 num; /* attribute number for columns referenced
- * by number */
+ int16 num; /* attribute number for columns referenced by
+ * number */
RoleSpec *newowner;
Node *def; /* definition of new column, index,
* constraint, or parent table */
@@ -3431,4 +3442,24 @@ typedef struct DropSubscriptionStmt
DropBehavior behavior; /* RESTRICT or CASCADE behavior */
} DropSubscriptionStmt;
+typedef enum AlterTypeCmdType
+{
+ AT_AlterTypeCompression, /* ALTER TYPE name COMPRESSED cm WITH
+ * (options) */
+} AlterTypeCmdType;
+
+typedef struct AlterTypeCmd
+{
+ NodeTag type;
+ AlterTypeCmdType cmdtype;
+ Node *def;
+} AlterTypeCmd;
+
+typedef struct AlterTypeStmt
+{
+ NodeTag type;
+ List *typeName;
+ List *cmds;
+} AlterTypeStmt;
+
#endif /* PARSENODES_H */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index f50e45e886..7bfc6e6be4 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -87,6 +87,8 @@ PG_KEYWORD("comment", COMMENT, UNRESERVED_KEYWORD)
PG_KEYWORD("comments", COMMENTS, UNRESERVED_KEYWORD)
PG_KEYWORD("commit", COMMIT, UNRESERVED_KEYWORD)
PG_KEYWORD("committed", COMMITTED, UNRESERVED_KEYWORD)
+PG_KEYWORD("compressed", COMPRESSED, UNRESERVED_KEYWORD)
+PG_KEYWORD("compression", COMPRESSION, UNRESERVED_KEYWORD)
PG_KEYWORD("concurrently", CONCURRENTLY, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("configuration", CONFIGURATION, UNRESERVED_KEYWORD)
PG_KEYWORD("conflict", CONFLICT, UNRESERVED_KEYWORD)
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index e749432ef0..5cab77457a 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -22,10 +22,12 @@ extern List *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
const char *queryString);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
+void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
extern List *transformCreateSchemaStmt(CreateSchemaStmt *stmt);
extern PartitionBoundSpec *transformPartitionBound(ParseState *pstate, Relation parent,
PartitionBoundSpec *spec);
+extern void transformColumnCompression(ColumnDef *column, RangeVar *relation,
+ AlterTableStmt **alterStmt);
#endif /* PARSE_UTILCMD_H */
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 1ca9b60ea1..f5c879ae60 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -146,9 +146,18 @@ typedef union
struct /* Compressed-in-line format */
{
uint32 va_header;
- uint32 va_rawsize; /* Original data size (excludes header) */
+ uint32 va_info; /* Original data size (excludes header) and
+ * flags */
char va_data[FLEXIBLE_ARRAY_MEMBER]; /* Compressed data */
} va_compressed;
+ struct /* Compressed-in-line format */
+ {
+ uint32 va_header;
+ uint32 va_info; /* Original data size (excludes header) and
+ * flags */
+ Oid va_cmoptoid; /* Oid of compression options */
+ char va_data[FLEXIBLE_ARRAY_MEMBER]; /* Compressed data */
+ } va_custom_compressed;
} varattrib_4b;
typedef struct
@@ -282,7 +291,12 @@ typedef struct
#define VARDATA_1B_E(PTR) (((varattrib_1b_e *) (PTR))->va_data)
#define VARRAWSIZE_4B_C(PTR) \
- (((varattrib_4b *) (PTR))->va_compressed.va_rawsize)
+ (((varattrib_4b *) (PTR))->va_compressed.va_info & 0x3FFFFFFF)
+#define VARFLAGS_4B_C(PTR) \
+ (((varattrib_4b *) (PTR))->va_compressed.va_info >> 30)
+
+#define VARHDRSZ_CUSTOM_COMPRESSED \
+ (offsetof(varattrib_4b, va_custom_compressed.va_data))
/* Externally visible macros */
@@ -311,6 +325,8 @@ typedef struct
#define VARDATA_EXTERNAL(PTR) VARDATA_1B_E(PTR)
#define VARATT_IS_COMPRESSED(PTR) VARATT_IS_4B_C(PTR)
+#define VARATT_IS_CUSTOM_COMPRESSED(PTR) (VARATT_IS_4B_C(PTR) && \
+ (VARFLAGS_4B_C(PTR) == 0x02))
#define VARATT_IS_EXTERNAL(PTR) VARATT_IS_1B_E(PTR)
#define VARATT_IS_EXTERNAL_ONDISK(PTR) \
(VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_ONDISK)
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 43273eaab5..fa3bbbc49e 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -202,6 +202,7 @@ typedef enum AclObjectKind
ACL_KIND_EXTENSION, /* pg_extension */
ACL_KIND_PUBLICATION, /* pg_publication */
ACL_KIND_SUBSCRIPTION, /* pg_subscription */
+ ACL_KIND_COMPRESSION_METHOD, /* pg_compression */
MAX_ACL_KIND /* MUST BE LAST */
} AclObjectKind;
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 07208b56ce..00db7fead1 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -166,6 +166,7 @@ extern void getTypeBinaryOutputInfo(Oid type, Oid *typSend, bool *typIsVarlena);
extern Oid get_typmodin(Oid typid);
extern Oid get_typcollation(Oid typid);
extern bool type_is_collatable(Oid typid);
+extern Oid get_base_typdefaultcm(HeapTuple typtup);
extern Oid getBaseType(Oid typid);
extern Oid getBaseTypeAndTypmod(Oid typid, int32 *typmod);
extern int32 get_typavgwidth(Oid typid, int32 typmod);
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 8a92ea27ac..889f9c775a 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -48,6 +48,9 @@ enum SysCacheIdentifier
CLAOID,
COLLNAMEENCNSP,
COLLOID,
+ COMPRESSIONMETHODOID,
+ COMPRESSIONMETHODNAME,
+ COMPRESSIONOPTIONSOID,
CONDEFAULT,
CONNAMENSP,
CONSTROID,
diff --git a/src/include/utils/tuplesort.h b/src/include/utils/tuplesort.h
index b6b8c8ef8c..7b641e2d84 100644
--- a/src/include/utils/tuplesort.h
+++ b/src/include/utils/tuplesort.h
@@ -43,20 +43,20 @@ typedef enum
SORT_TYPE_QUICKSORT,
SORT_TYPE_EXTERNAL_SORT,
SORT_TYPE_EXTERNAL_MERGE
-} TuplesortMethod;
+} TuplesortMethod;
typedef enum
{
SORT_SPACE_TYPE_DISK,
SORT_SPACE_TYPE_MEMORY
-} TuplesortSpaceType;
+} TuplesortSpaceType;
typedef struct TuplesortInstrumentation
{
TuplesortMethod sortMethod; /* sort algorithm used */
TuplesortSpaceType spaceType; /* type of space spaceUsed represents */
long spaceUsed; /* space consumption, in kB */
-} TuplesortInstrumentation;
+} TuplesortInstrumentation;
/*
@@ -135,7 +135,7 @@ extern bool tuplesort_skiptuples(Tuplesortstate *state, int64 ntuples,
extern void tuplesort_end(Tuplesortstate *state);
extern void tuplesort_get_stats(Tuplesortstate *state,
- TuplesortInstrumentation *stats);
+ TuplesortInstrumentation * stats);
extern const char *tuplesort_method_name(TuplesortMethod m);
extern const char *tuplesort_space_type_name(TuplesortSpaceType t);
diff --git a/src/test/regress/expected/copy2.out b/src/test/regress/expected/copy2.out
index 65e9c626b3..112f0eda47 100644
--- a/src/test/regress/expected/copy2.out
+++ b/src/test/regress/expected/copy2.out
@@ -438,10 +438,10 @@ begin
end $$ language plpgsql immutable;
alter table check_con_tbl add check (check_con_function(check_con_tbl.*));
\d+ check_con_tbl
- Table "public.check_con_tbl"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- f1 | integer | | | | plain | |
+ Table "public.check_con_tbl"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ f1 | integer | | | | plain | | |
Check constraints:
"check_con_tbl_check" CHECK (check_con_function(check_con_tbl.*))
diff --git a/src/test/regress/expected/create_cm.out b/src/test/regress/expected/create_cm.out
new file mode 100644
index 0000000000..47c071abb2
--- /dev/null
+++ b/src/test/regress/expected/create_cm.out
@@ -0,0 +1,127 @@
+CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
+DROP COMPRESSION METHOD ts1;
+CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
+CREATE TABLE cmtest(fts tsvector COMPRESSED ts1);
+DROP COMPRESSION METHOD ts1;
+ERROR: cannot drop compression method ts1 because other objects depend on it
+DETAIL: compression options for ts1 depends on compression method ts1
+table cmtest column fts depends on compression options for ts1
+HINT: Use DROP ... CASCADE to drop the dependent objects too.
+SELECT * FROM pg_compression;
+ cmname | cmhandler
+--------+------------------------------
+ ts1 | tsvector_compression_handler
+(1 row)
+
+SELECT cmhandler, cmoptions FROM pg_compression_opt;
+ cmhandler | cmoptions
+------------------------------+-----------
+ tsvector_compression_handler |
+(1 row)
+
+\dCM
+List of compression methods
+ Name
+------
+ ts1
+(1 row)
+
+\d+ cmtest
+ Table "public.cmtest"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+
+INSERT INTO cmtest
+ SELECT to_tsvector(string_agg(repeat(substr(i::text,1,1), i), ' '))
+ FROM generate_series(1,200) i;
+SELECT length(fts) FROM cmtest;
+ length
+--------
+ 200
+(1 row)
+
+ALTER TABLE cmtest ALTER COLUMN fts SET NOT COMPRESSED;
+\d+ cmtest
+ Table "public.cmtest"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | | |
+
+ALTER TABLE cmtest ALTER COLUMN fts SET COMPRESSED ts1 WITH (format 'lz');
+ERROR: the compression method for tsvector doesn't take any options
+ALTER TABLE cmtest ALTER COLUMN fts SET COMPRESSED ts1;
+\d+ cmtest
+ Table "public.cmtest"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+
+SELECT * INTO cmtest2 FROM cmtest;
+CREATE TABLE cmtest3 (LIKE cmtest);
+CREATE TABLE cmtest4(fts tsvector, a int) inherits (cmtest);
+NOTICE: merging column "fts" with inherited definition
+\d+ cmtest3
+ Table "public.cmtest3"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+
+\d+ cmtest4
+ Table "public.cmtest4"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+ a | integer | | | | plain | | |
+Inherits: cmtest
+
+DROP TABLE cmtest CASCADE;
+NOTICE: drop cascades to table cmtest4
+SELECT length(fts) FROM cmtest2;
+ length
+--------
+ 200
+(1 row)
+
+SELECT * FROM pg_compression;
+ cmname | cmhandler
+--------+------------------------------
+ ts1 | tsvector_compression_handler
+(1 row)
+
+SELECT cmhandler, cmoptions FROM pg_compression_opt;
+ cmhandler | cmoptions
+------------------------------+-----------
+ tsvector_compression_handler |
+ tsvector_compression_handler |
+ tsvector_compression_handler |
+ tsvector_compression_handler |
+(4 rows)
+
+DROP TABLE cmtest2;
+DROP TABLE cmtest3;
+ALTER TYPE tsvector SET COMPRESSED ts1;
+CREATE TABLE cmtest(fts tsvector);
+\d+ cmtest
+ Table "public.cmtest"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+
+DROP TABLE cmtest;
+ALTER TYPE tsvector SET NOT COMPRESSED;
+CREATE TABLE cmtest(fts tsvector);
+\d+ cmtest
+ Table "public.cmtest"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | | |
+
+DROP TABLE cmtest;
+DROP COMPRESSION METHOD ts1 CASCADE;
+NOTICE: drop cascades to 5 other objects
+DETAIL: drop cascades to compression options for ts1
+drop cascades to compression options for ts1
+drop cascades to compression options for ts1
+drop cascades to compression options for ts1
+drop cascades to compression options for ts1
diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out
index babda8978c..bdcc2bef2a 100644
--- a/src/test/regress/expected/create_table.out
+++ b/src/test/regress/expected/create_table.out
@@ -543,10 +543,10 @@ CREATE TABLE oids_parted (
) PARTITION BY RANGE (a) WITH OIDS;
CREATE TABLE part_forced_oids PARTITION OF oids_parted FOR VALUES FROM (1) TO (10) WITHOUT OIDS;
\d+ part_forced_oids
- Table "public.part_forced_oids"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
+ Table "public.part_forced_oids"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
Partition of: oids_parted FOR VALUES FROM (1) TO (10)
Partition constraint: ((a IS NOT NULL) AND (a >= 1) AND (a < 10))
Has OIDs: yes
@@ -649,11 +649,11 @@ CREATE TABLE part_c PARTITION OF parted (b WITH OPTIONS NOT NULL DEFAULT 0) FOR
CREATE TABLE part_c_1_10 PARTITION OF part_c FOR VALUES FROM (1) TO (10);
-- Partition bound in describe output
\d+ part_b
- Table "public.part_b"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | not null | 1 | plain | |
+ Table "public.part_b"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | not null | 1 | plain | | |
Partition of: parted FOR VALUES IN ('b')
Partition constraint: ((a IS NOT NULL) AND (a = ANY (ARRAY['b'::text])))
Check constraints:
@@ -676,11 +676,11 @@ Partitions: part_c_1_10 FOR VALUES FROM (1) TO (10)
-- a level-2 partition's constraint will include the parent's expressions
\d+ part_c_1_10
- Table "public.part_c_1_10"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | not null | 0 | plain | |
+ Table "public.part_c_1_10"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | not null | 0 | plain | | |
Partition of: part_c FOR VALUES FROM (1) TO (10)
Partition constraint: ((a IS NOT NULL) AND (a = ANY (ARRAY['c'::text])) AND (b IS NOT NULL) AND (b >= 1) AND (b < 10))
Check constraints:
@@ -705,46 +705,46 @@ Number of partitions: 3 (Use \d+ to list them.)
CREATE TABLE range_parted4 (a int, b int, c int) PARTITION BY RANGE (abs(a), abs(b), c);
CREATE TABLE unbounded_range_part PARTITION OF range_parted4 FOR VALUES FROM (MINVALUE, 0, 0) TO (MAXVALUE, 0, 0);
\d+ unbounded_range_part
- Table "public.unbounded_range_part"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
+ Table "public.unbounded_range_part"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
Partition of: range_parted4 FOR VALUES FROM (MINVALUE, 0, 0) TO (MAXVALUE, 0, 0)
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL))
DROP TABLE unbounded_range_part;
CREATE TABLE range_parted4_1 PARTITION OF range_parted4 FOR VALUES FROM (MINVALUE, 0, 0) TO (1, MAXVALUE, 0);
\d+ range_parted4_1
- Table "public.range_parted4_1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
+ Table "public.range_parted4_1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
Partition of: range_parted4 FOR VALUES FROM (MINVALUE, 0, 0) TO (1, MAXVALUE, 0)
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND (abs(a) <= 1))
CREATE TABLE range_parted4_2 PARTITION OF range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, MAXVALUE);
\d+ range_parted4_2
- Table "public.range_parted4_2"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
+ Table "public.range_parted4_2"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
Partition of: range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, MAXVALUE)
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND ((abs(a) > 3) OR ((abs(a) = 3) AND (abs(b) > 4)) OR ((abs(a) = 3) AND (abs(b) = 4) AND (c >= 5))) AND ((abs(a) < 6) OR ((abs(a) = 6) AND (abs(b) <= 7))))
CREATE TABLE range_parted4_3 PARTITION OF range_parted4 FOR VALUES FROM (6, 8, MINVALUE) TO (9, MAXVALUE, 0);
\d+ range_parted4_3
- Table "public.range_parted4_3"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
+ Table "public.range_parted4_3"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
Partition of: range_parted4 FOR VALUES FROM (6, 8, MINVALUE) TO (9, MAXVALUE, 0)
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND ((abs(a) > 6) OR ((abs(a) = 6) AND (abs(b) >= 8))) AND (abs(a) <= 9))
diff --git a/src/test/regress/expected/create_table_like.out b/src/test/regress/expected/create_table_like.out
index 3f405c94ce..b5ca8b820b 100644
--- a/src/test/regress/expected/create_table_like.out
+++ b/src/test/regress/expected/create_table_like.out
@@ -156,32 +156,32 @@ CREATE TABLE ctlt4 (a text, c text);
ALTER TABLE ctlt4 ALTER COLUMN c SET STORAGE EXTERNAL;
CREATE TABLE ctlt12_storage (LIKE ctlt1 INCLUDING STORAGE, LIKE ctlt2 INCLUDING STORAGE);
\d+ ctlt12_storage
- Table "public.ctlt12_storage"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | |
- b | text | | | | extended | |
- c | text | | | | external | |
+ Table "public.ctlt12_storage"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | |
+ b | text | | | | extended | | |
+ c | text | | | | external | | |
CREATE TABLE ctlt12_comments (LIKE ctlt1 INCLUDING COMMENTS, LIKE ctlt2 INCLUDING COMMENTS);
\d+ ctlt12_comments
- Table "public.ctlt12_comments"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | extended | | A
- b | text | | | | extended | | B
- c | text | | | | extended | | C
+ Table "public.ctlt12_comments"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | extended | | | A
+ b | text | | | | extended | | | B
+ c | text | | | | extended | | | C
CREATE TABLE ctlt1_inh (LIKE ctlt1 INCLUDING CONSTRAINTS INCLUDING COMMENTS) INHERITS (ctlt1);
NOTICE: merging column "a" with inherited definition
NOTICE: merging column "b" with inherited definition
NOTICE: merging constraint "ctlt1_a_check" with inherited definition
\d+ ctlt1_inh
- Table "public.ctlt1_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | | A
- b | text | | | | extended | | B
+ Table "public.ctlt1_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | | A
+ b | text | | | | extended | | | B
Check constraints:
"ctlt1_a_check" CHECK (length(a) > 2)
Inherits: ctlt1
@@ -195,12 +195,12 @@ SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_con
CREATE TABLE ctlt13_inh () INHERITS (ctlt1, ctlt3);
NOTICE: merging multiple inherited definitions of column "a"
\d+ ctlt13_inh
- Table "public.ctlt13_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | |
- b | text | | | | extended | |
- c | text | | | | external | |
+ Table "public.ctlt13_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | |
+ b | text | | | | extended | | |
+ c | text | | | | external | | |
Check constraints:
"ctlt1_a_check" CHECK (length(a) > 2)
"ctlt3_a_check" CHECK (length(a) < 5)
@@ -210,12 +210,12 @@ Inherits: ctlt1,
CREATE TABLE ctlt13_like (LIKE ctlt3 INCLUDING CONSTRAINTS INCLUDING COMMENTS INCLUDING STORAGE) INHERITS (ctlt1);
NOTICE: merging column "a" with inherited definition
\d+ ctlt13_like
- Table "public.ctlt13_like"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | | A3
- b | text | | | | extended | |
- c | text | | | | external | | C
+ Table "public.ctlt13_like"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | | A3
+ b | text | | | | extended | | |
+ c | text | | | | external | | | C
Check constraints:
"ctlt1_a_check" CHECK (length(a) > 2)
"ctlt3_a_check" CHECK (length(a) < 5)
@@ -229,11 +229,11 @@ SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_con
CREATE TABLE ctlt_all (LIKE ctlt1 INCLUDING ALL);
\d+ ctlt_all
- Table "public.ctlt_all"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | | A
- b | text | | | | extended | | B
+ Table "public.ctlt_all"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | | A
+ b | text | | | | extended | | | B
Indexes:
"ctlt_all_pkey" PRIMARY KEY, btree (a)
"ctlt_all_b_idx" btree (b)
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index 3acc696863..fe873fa5e1 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -296,10 +296,10 @@ create rule silly as on delete to dcomptable do instead
update dcomptable set d1[1].r = d1[1].r - 1, d1[1].i = d1[1].i + 1
where d1[1].i > 0;
\d+ dcomptable
- Table "public.dcomptable"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------------+-----------+----------+---------+----------+--------------+-------------
- d1 | dcomptypea | | | | extended | |
+ Table "public.dcomptable"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------------+-----------+----------+---------+----------+-------------+--------------+-------------
+ d1 | dcomptypea | | | | extended | | |
Indexes:
"dcomptable_d1_key" UNIQUE CONSTRAINT, btree (d1)
Rules:
diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out
index 927d0189a0..d7250ce52c 100644
--- a/src/test/regress/expected/foreign_data.out
+++ b/src/test/regress/expected/foreign_data.out
@@ -1314,12 +1314,12 @@ CREATE TABLE pt1 (
CREATE FOREIGN TABLE ft2 () INHERITS (pt1)
SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value');
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Child tables: ft2
\d+ ft2
@@ -1335,12 +1335,12 @@ Inherits: pt1
DROP FOREIGN TABLE ft2;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
CREATE FOREIGN TABLE ft2 (
c1 integer NOT NULL,
@@ -1359,12 +1359,12 @@ FDW options: (delimiter ',', quote '"', "be quoted" 'value')
ALTER FOREIGN TABLE ft2 INHERIT pt1;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Child tables: ft2
\d+ ft2
@@ -1402,12 +1402,12 @@ Child tables: ct3,
ft3
\d+ ct3
- Table "public.ct3"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.ct3"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Inherits: ft2
\d+ ft3
@@ -1427,17 +1427,17 @@ ALTER TABLE pt1 ADD COLUMN c6 integer;
ALTER TABLE pt1 ADD COLUMN c7 integer NOT NULL;
ALTER TABLE pt1 ADD COLUMN c8 integer;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
- c4 | integer | | | | plain | |
- c5 | integer | | | 0 | plain | |
- c6 | integer | | | | plain | |
- c7 | integer | | not null | | plain | |
- c8 | integer | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
+ c4 | integer | | | | plain | | |
+ c5 | integer | | | 0 | plain | | |
+ c6 | integer | | | | plain | | |
+ c7 | integer | | not null | | plain | | |
+ c8 | integer | | | | plain | | |
Child tables: ft2
\d+ ft2
@@ -1459,17 +1459,17 @@ Child tables: ct3,
ft3
\d+ ct3
- Table "public.ct3"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
- c4 | integer | | | | plain | |
- c5 | integer | | | 0 | plain | |
- c6 | integer | | | | plain | |
- c7 | integer | | not null | | plain | |
- c8 | integer | | | | plain | |
+ Table "public.ct3"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
+ c4 | integer | | | | plain | | |
+ c5 | integer | | | 0 | plain | | |
+ c6 | integer | | | | plain | | |
+ c7 | integer | | not null | | plain | | |
+ c8 | integer | | | | plain | | |
Inherits: ft2
\d+ ft3
@@ -1501,17 +1501,17 @@ ALTER TABLE pt1 ALTER COLUMN c1 SET (n_distinct = 100);
ALTER TABLE pt1 ALTER COLUMN c8 SET STATISTICS -1;
ALTER TABLE pt1 ALTER COLUMN c8 SET STORAGE EXTERNAL;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
- c4 | integer | | | 0 | plain | |
- c5 | integer | | | | plain | |
- c6 | integer | | not null | | plain | |
- c7 | integer | | | | plain | |
- c8 | text | | | | external | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
+ c4 | integer | | | 0 | plain | | |
+ c5 | integer | | | | plain | | |
+ c6 | integer | | not null | | plain | | |
+ c7 | integer | | | | plain | | |
+ c8 | text | | | | external | | |
Child tables: ft2
\d+ ft2
@@ -1539,12 +1539,12 @@ ALTER TABLE pt1 DROP COLUMN c6;
ALTER TABLE pt1 DROP COLUMN c7;
ALTER TABLE pt1 DROP COLUMN c8;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Child tables: ft2
\d+ ft2
@@ -1576,12 +1576,12 @@ SELECT relname, conname, contype, conislocal, coninhcount, connoinherit
-- child does not inherit NO INHERIT constraints
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk1" CHECK (c1 > 0) NO INHERIT
"pt1chk2" CHECK (c2 <> ''::text)
@@ -1620,12 +1620,12 @@ ALTER FOREIGN TABLE ft2 ADD CONSTRAINT pt1chk2 CHECK (c2 <> '');
ALTER FOREIGN TABLE ft2 INHERIT pt1;
-- child does not inherit NO INHERIT constraints
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk1" CHECK (c1 > 0) NO INHERIT
"pt1chk2" CHECK (c2 <> ''::text)
@@ -1651,12 +1651,12 @@ ALTER TABLE pt1 DROP CONSTRAINT pt1chk2 CASCADE;
INSERT INTO pt1 VALUES (1, 'pt1'::text, '1994-01-01'::date);
ALTER TABLE pt1 ADD CONSTRAINT pt1chk3 CHECK (c2 <> '') NOT VALID;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk3" CHECK (c2 <> ''::text) NOT VALID
Child tables: ft2
@@ -1678,12 +1678,12 @@ Inherits: pt1
-- VALIDATE CONSTRAINT need do nothing on foreign tables
ALTER TABLE pt1 VALIDATE CONSTRAINT pt1chk3;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk3" CHECK (c2 <> ''::text)
Child tables: ft2
@@ -1705,12 +1705,12 @@ Inherits: pt1
-- OID system column
ALTER TABLE pt1 SET WITH OIDS;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk3" CHECK (c2 <> ''::text)
Child tables: ft2
@@ -1735,12 +1735,12 @@ ALTER TABLE ft2 SET WITHOUT OIDS; -- ERROR
ERROR: cannot drop inherited column "oid"
ALTER TABLE pt1 SET WITHOUT OIDS;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk3" CHECK (c2 <> ''::text)
Child tables: ft2
@@ -1766,12 +1766,12 @@ ALTER TABLE pt1 RENAME COLUMN c3 TO f3;
-- changes name of a constraint recursively
ALTER TABLE pt1 RENAME CONSTRAINT pt1chk3 TO f2_check;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- f1 | integer | | not null | | plain | 10000 |
- f2 | text | | | | extended | |
- f3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ f1 | integer | | not null | | plain | | 10000 |
+ f2 | text | | | | extended | | |
+ f3 | date | | | | plain | | |
Check constraints:
"f2_check" CHECK (f2 <> ''::text)
Child tables: ft2
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index 1fa9650ec9..b472007866 100644
--- a/src/test/regress/expected/inherit.out
+++ b/src/test/regress/expected/inherit.out
@@ -1001,13 +1001,13 @@ ALTER TABLE inhts RENAME aa TO aaa; -- to be failed
ERROR: cannot rename inherited column "aa"
ALTER TABLE inhts RENAME d TO dd;
\d+ inhts
- Table "public.inhts"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- aa | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
- dd | integer | | | | plain | |
+ Table "public.inhts"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ aa | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
+ dd | integer | | | | plain | | |
Inherits: inht1,
inhs1
@@ -1020,14 +1020,14 @@ NOTICE: merging multiple inherited definitions of column "aa"
NOTICE: merging multiple inherited definitions of column "b"
ALTER TABLE inht1 RENAME aa TO aaa;
\d+ inht4
- Table "public.inht4"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- aaa | integer | | | | plain | |
- b | integer | | | | plain | |
- x | integer | | | | plain | |
- y | integer | | | | plain | |
- z | integer | | | | plain | |
+ Table "public.inht4"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ aaa | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ x | integer | | | | plain | | |
+ y | integer | | | | plain | | |
+ z | integer | | | | plain | | |
Inherits: inht2,
inht3
@@ -1037,14 +1037,14 @@ ALTER TABLE inht1 RENAME aaa TO aaaa;
ALTER TABLE inht1 RENAME b TO bb; -- to be failed
ERROR: cannot rename inherited column "b"
\d+ inhts
- Table "public.inhts"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- aaaa | integer | | | | plain | |
- b | integer | | | | plain | |
- x | integer | | | | plain | |
- c | integer | | | | plain | |
- d | integer | | | | plain | |
+ Table "public.inhts"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ aaaa | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ x | integer | | | | plain | | |
+ c | integer | | | | plain | | |
+ d | integer | | | | plain | | |
Inherits: inht2,
inhs1
@@ -1084,33 +1084,33 @@ drop cascades to table inht4
CREATE TABLE test_constraints (id int, val1 varchar, val2 int, UNIQUE(val1, val2));
CREATE TABLE test_constraints_inh () INHERITS (test_constraints);
\d+ test_constraints
- Table "public.test_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+-------------------+-----------+----------+---------+----------+--------------+-------------
- id | integer | | | | plain | |
- val1 | character varying | | | | extended | |
- val2 | integer | | | | plain | |
+ Table "public.test_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+-------------------+-----------+----------+---------+----------+-------------+--------------+-------------
+ id | integer | | | | plain | | |
+ val1 | character varying | | | | extended | | |
+ val2 | integer | | | | plain | | |
Indexes:
"test_constraints_val1_val2_key" UNIQUE CONSTRAINT, btree (val1, val2)
Child tables: test_constraints_inh
ALTER TABLE ONLY test_constraints DROP CONSTRAINT test_constraints_val1_val2_key;
\d+ test_constraints
- Table "public.test_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+-------------------+-----------+----------+---------+----------+--------------+-------------
- id | integer | | | | plain | |
- val1 | character varying | | | | extended | |
- val2 | integer | | | | plain | |
+ Table "public.test_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+-------------------+-----------+----------+---------+----------+-------------+--------------+-------------
+ id | integer | | | | plain | | |
+ val1 | character varying | | | | extended | | |
+ val2 | integer | | | | plain | | |
Child tables: test_constraints_inh
\d+ test_constraints_inh
- Table "public.test_constraints_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+-------------------+-----------+----------+---------+----------+--------------+-------------
- id | integer | | | | plain | |
- val1 | character varying | | | | extended | |
- val2 | integer | | | | plain | |
+ Table "public.test_constraints_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+-------------------+-----------+----------+---------+----------+-------------+--------------+-------------
+ id | integer | | | | plain | | |
+ val1 | character varying | | | | extended | | |
+ val2 | integer | | | | plain | | |
Inherits: test_constraints
DROP TABLE test_constraints_inh;
@@ -1121,27 +1121,27 @@ CREATE TABLE test_ex_constraints (
);
CREATE TABLE test_ex_constraints_inh () INHERITS (test_ex_constraints);
\d+ test_ex_constraints
- Table "public.test_ex_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+--------+-----------+----------+---------+---------+--------------+-------------
- c | circle | | | | plain | |
+ Table "public.test_ex_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+--------+-----------+----------+---------+---------+-------------+--------------+-------------
+ c | circle | | | | plain | | |
Indexes:
"test_ex_constraints_c_excl" EXCLUDE USING gist (c WITH &&)
Child tables: test_ex_constraints_inh
ALTER TABLE test_ex_constraints DROP CONSTRAINT test_ex_constraints_c_excl;
\d+ test_ex_constraints
- Table "public.test_ex_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+--------+-----------+----------+---------+---------+--------------+-------------
- c | circle | | | | plain | |
+ Table "public.test_ex_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+--------+-----------+----------+---------+---------+-------------+--------------+-------------
+ c | circle | | | | plain | | |
Child tables: test_ex_constraints_inh
\d+ test_ex_constraints_inh
- Table "public.test_ex_constraints_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+--------+-----------+----------+---------+---------+--------------+-------------
- c | circle | | | | plain | |
+ Table "public.test_ex_constraints_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+--------+-----------+----------+---------+---------+-------------+--------------+-------------
+ c | circle | | | | plain | | |
Inherits: test_ex_constraints
DROP TABLE test_ex_constraints_inh;
@@ -1151,37 +1151,37 @@ CREATE TABLE test_primary_constraints(id int PRIMARY KEY);
CREATE TABLE test_foreign_constraints(id1 int REFERENCES test_primary_constraints(id));
CREATE TABLE test_foreign_constraints_inh () INHERITS (test_foreign_constraints);
\d+ test_primary_constraints
- Table "public.test_primary_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- id | integer | | not null | | plain | |
+ Table "public.test_primary_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ id | integer | | not null | | plain | | |
Indexes:
"test_primary_constraints_pkey" PRIMARY KEY, btree (id)
Referenced by:
TABLE "test_foreign_constraints" CONSTRAINT "test_foreign_constraints_id1_fkey" FOREIGN KEY (id1) REFERENCES test_primary_constraints(id)
\d+ test_foreign_constraints
- Table "public.test_foreign_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- id1 | integer | | | | plain | |
+ Table "public.test_foreign_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ id1 | integer | | | | plain | | |
Foreign-key constraints:
"test_foreign_constraints_id1_fkey" FOREIGN KEY (id1) REFERENCES test_primary_constraints(id)
Child tables: test_foreign_constraints_inh
ALTER TABLE test_foreign_constraints DROP CONSTRAINT test_foreign_constraints_id1_fkey;
\d+ test_foreign_constraints
- Table "public.test_foreign_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- id1 | integer | | | | plain | |
+ Table "public.test_foreign_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ id1 | integer | | | | plain | | |
Child tables: test_foreign_constraints_inh
\d+ test_foreign_constraints_inh
- Table "public.test_foreign_constraints_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- id1 | integer | | | | plain | |
+ Table "public.test_foreign_constraints_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ id1 | integer | | | | plain | | |
Inherits: test_foreign_constraints
DROP TABLE test_foreign_constraints_inh;
diff --git a/src/test/regress/expected/insert.out b/src/test/regress/expected/insert.out
index e159d62b66..7aa0bc8be2 100644
--- a/src/test/regress/expected/insert.out
+++ b/src/test/regress/expected/insert.out
@@ -142,11 +142,11 @@ create rule irule3 as on insert to inserttest2 do also
insert into inserttest (f4[1].if1, f4[1].if2[2])
select new.f1, new.f2;
\d+ inserttest2
- Table "public.inserttest2"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+--------+-----------+----------+---------+----------+--------------+-------------
- f1 | bigint | | | | plain | |
- f2 | text | | | | extended | |
+ Table "public.inserttest2"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+--------+-----------+----------+---------+----------+-------------+--------------+-------------
+ f1 | bigint | | | | plain | | |
+ f2 | text | | | | extended | | |
Rules:
irule1 AS
ON INSERT TO inserttest2 DO INSERT INTO inserttest (f3.if2[1], f3.if2[2])
@@ -568,74 +568,74 @@ Partitions: mcrparted1_lt_b FOR VALUES FROM (MINVALUE, 0) TO ('b', MINVALUE),
mcrparted8_ge_d FOR VALUES FROM ('d', MINVALUE) TO (MAXVALUE, 0)
\d+ mcrparted1_lt_b
- Table "public.mcrparted1_lt_b"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted1_lt_b"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM (MINVALUE, 0) TO ('b', MINVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a < 'b'::text))
\d+ mcrparted2_b
- Table "public.mcrparted2_b"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted2_b"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('b', MINVALUE) TO ('c', MINVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'b'::text) AND (a < 'c'::text))
\d+ mcrparted3_c_to_common
- Table "public.mcrparted3_c_to_common"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted3_c_to_common"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('c', MINVALUE) TO ('common', MINVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'c'::text) AND (a < 'common'::text))
\d+ mcrparted4_common_lt_0
- Table "public.mcrparted4_common_lt_0"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted4_common_lt_0"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('common', MINVALUE) TO ('common', 0)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::text) AND (b < 0))
\d+ mcrparted5_common_0_to_10
- Table "public.mcrparted5_common_0_to_10"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted5_common_0_to_10"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('common', 0) TO ('common', 10)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::text) AND (b >= 0) AND (b < 10))
\d+ mcrparted6_common_ge_10
- Table "public.mcrparted6_common_ge_10"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted6_common_ge_10"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('common', 10) TO ('common', MAXVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::text) AND (b >= 10))
\d+ mcrparted7_gt_common_lt_d
- Table "public.mcrparted7_gt_common_lt_d"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted7_gt_common_lt_d"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('common', MAXVALUE) TO ('d', MINVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a > 'common'::text) AND (a < 'd'::text))
\d+ mcrparted8_ge_d
- Table "public.mcrparted8_ge_d"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted8_ge_d"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('d', MINVALUE) TO (MAXVALUE, 0)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'd'::text))
diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out
index b101331d69..f805e13d75 100644
--- a/src/test/regress/expected/publication.out
+++ b/src/test/regress/expected/publication.out
@@ -65,11 +65,11 @@ SELECT pubname, puballtables FROM pg_publication WHERE pubname = 'testpub_forall
(1 row)
\d+ testpub_tbl2
- Table "public.testpub_tbl2"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('testpub_tbl2_id_seq'::regclass) | plain | |
- data | text | | | | extended | |
+ Table "public.testpub_tbl2"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('testpub_tbl2_id_seq'::regclass) | plain | | |
+ data | text | | | | extended | | |
Indexes:
"testpub_tbl2_pkey" PRIMARY KEY, btree (id)
Publications:
@@ -141,22 +141,22 @@ ALTER PUBLICATION testpub_default SET TABLE testpub_tbl1;
ALTER PUBLICATION testpub_default ADD TABLE pub_test.testpub_nopk;
ALTER PUBLICATION testpib_ins_trunct ADD TABLE pub_test.testpub_nopk, testpub_tbl1;
\d+ pub_test.testpub_nopk
- Table "pub_test.testpub_nopk"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- foo | integer | | | | plain | |
- bar | integer | | | | plain | |
+ Table "pub_test.testpub_nopk"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ foo | integer | | | | plain | | |
+ bar | integer | | | | plain | | |
Publications:
"testpib_ins_trunct"
"testpub_default"
"testpub_fortbl"
\d+ testpub_tbl1
- Table "public.testpub_tbl1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | |
- data | text | | | | extended | |
+ Table "public.testpub_tbl1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | | |
+ data | text | | | | extended | | |
Indexes:
"testpub_tbl1_pkey" PRIMARY KEY, btree (id)
Publications:
@@ -178,11 +178,11 @@ ALTER PUBLICATION testpub_default DROP TABLE testpub_tbl1, pub_test.testpub_nopk
ALTER PUBLICATION testpub_default DROP TABLE pub_test.testpub_nopk;
ERROR: relation "testpub_nopk" is not part of the publication
\d+ testpub_tbl1
- Table "public.testpub_tbl1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | |
- data | text | | | | extended | |
+ Table "public.testpub_tbl1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | | |
+ data | text | | | | extended | | |
Indexes:
"testpub_tbl1_pkey" PRIMARY KEY, btree (id)
Publications:
diff --git a/src/test/regress/expected/replica_identity.out b/src/test/regress/expected/replica_identity.out
index 67c34a92a4..1526437bf8 100644
--- a/src/test/regress/expected/replica_identity.out
+++ b/src/test/regress/expected/replica_identity.out
@@ -158,13 +158,13 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass;
(1 row)
\d+ test_replica_identity
- Table "public.test_replica_identity"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('test_replica_identity_id_seq'::regclass) | plain | |
- keya | text | | not null | | extended | |
- keyb | text | | not null | | extended | |
- nonkey | text | | | | extended | |
+ Table "public.test_replica_identity"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('test_replica_identity_id_seq'::regclass) | plain | | |
+ keya | text | | not null | | extended | | |
+ keyb | text | | not null | | extended | | |
+ nonkey | text | | | | extended | | |
Indexes:
"test_replica_identity_pkey" PRIMARY KEY, btree (id)
"test_replica_identity_expr" UNIQUE, btree (keya, keyb, (3))
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index d582bc9ee4..a110ae1f5b 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2799,11 +2799,11 @@ select * from rules_log;
create rule r3 as on delete to rules_src do notify rules_src_deletion;
\d+ rules_src
- Table "public.rules_src"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- f1 | integer | | | | plain | |
- f2 | integer | | | | plain | |
+ Table "public.rules_src"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ f1 | integer | | | | plain | | |
+ f2 | integer | | | | plain | | |
Rules:
r1 AS
ON UPDATE TO rules_src DO INSERT INTO rules_log (f1, f2, tag) VALUES (old.f1,old.f2,'old'::text), (new.f1,new.f2,'new'::text)
@@ -2819,11 +2819,11 @@ Rules:
create rule r4 as on insert to rules_src do instead insert into rules_log AS trgt SELECT NEW.* RETURNING trgt.f1, trgt.f2;
create rule r5 as on update to rules_src do instead UPDATE rules_log AS trgt SET tag = 'updated' WHERE trgt.f1 = new.f1;
\d+ rules_src
- Table "public.rules_src"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- f1 | integer | | | | plain | |
- f2 | integer | | | | plain | |
+ Table "public.rules_src"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ f1 | integer | | | | plain | | |
+ f2 | integer | | | | plain | | |
Rules:
r1 AS
ON UPDATE TO rules_src DO INSERT INTO rules_log (f1, f2, tag) VALUES (old.f1,old.f2,'old'::text), (new.f1,new.f2,'new'::text)
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 6750152e0f..accae702f4 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -107,6 +107,8 @@ pg_authid|t
pg_cast|t
pg_class|t
pg_collation|t
+pg_compression|t
+pg_compression_opt|t
pg_constraint|t
pg_conversion|t
pg_database|t
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 2fd3f2b1b1..9a4bacc54b 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -30,7 +30,7 @@ test: point lseg line box path polygon circle date time timetz timestamp timesta
# geometry depends on point, lseg, box, path, polygon and circle
# horology depends on interval, timetz, timestamp, timestamptz, reltime and abstime
# ----------
-test: geometry horology regex oidjoins type_sanity opr_sanity misc_sanity comments expressions
+test: geometry horology regex oidjoins type_sanity opr_sanity misc_sanity comments expressions create_cm
# ----------
# These four each depend on the previous one
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 76b0de30a7..b49808e814 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -43,6 +43,7 @@ test: inet
test: macaddr
test: macaddr8
test: tstypes
+test: create_cm
test: geometry
test: horology
test: regex
diff --git a/src/test/regress/sql/create_cm.sql b/src/test/regress/sql/create_cm.sql
new file mode 100644
index 0000000000..50fafe36d7
--- /dev/null
+++ b/src/test/regress/sql/create_cm.sql
@@ -0,0 +1,47 @@
+CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
+DROP COMPRESSION METHOD ts1;
+CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
+CREATE TABLE cmtest(fts tsvector COMPRESSED ts1);
+DROP COMPRESSION METHOD ts1;
+SELECT * FROM pg_compression;
+SELECT cmhandler, cmoptions FROM pg_compression_opt;
+
+\dCM
+\d+ cmtest
+
+INSERT INTO cmtest
+ SELECT to_tsvector(string_agg(repeat(substr(i::text,1,1), i), ' '))
+ FROM generate_series(1,200) i;
+SELECT length(fts) FROM cmtest;
+
+ALTER TABLE cmtest ALTER COLUMN fts SET NOT COMPRESSED;
+\d+ cmtest
+ALTER TABLE cmtest ALTER COLUMN fts SET COMPRESSED ts1 WITH (format 'lz');
+ALTER TABLE cmtest ALTER COLUMN fts SET COMPRESSED ts1;
+\d+ cmtest
+
+SELECT * INTO cmtest2 FROM cmtest;
+CREATE TABLE cmtest3 (LIKE cmtest);
+CREATE TABLE cmtest4(fts tsvector, a int) inherits (cmtest);
+\d+ cmtest3
+\d+ cmtest4
+DROP TABLE cmtest CASCADE;
+
+SELECT length(fts) FROM cmtest2;
+SELECT * FROM pg_compression;
+SELECT cmhandler, cmoptions FROM pg_compression_opt;
+
+DROP TABLE cmtest2;
+DROP TABLE cmtest3;
+
+ALTER TYPE tsvector SET COMPRESSED ts1;
+CREATE TABLE cmtest(fts tsvector);
+\d+ cmtest
+DROP TABLE cmtest;
+
+ALTER TYPE tsvector SET NOT COMPRESSED;
+CREATE TABLE cmtest(fts tsvector);
+\d+ cmtest
+DROP TABLE cmtest;
+
+DROP COMPRESSION METHOD ts1 CASCADE;
On Thu, 7 Sep 2017 19:42:36 +0300
Ildus Kurbangaliev <i.kurbangaliev@postgrespro.ru> wrote:
Hello hackers!
I've attached a patch that implements custom compression
methods. This patch is based on Nikita Glukhov's code (which he hasn't
publish in mailing lists) for jsonb compression. This is early but
working version of the patch, and there are still few fixes and
features that should be implemented (like pg_dump support and support
of compression options for types), and it requires more testing. But
I'd like to get some feedback at the current stage first.There's been a proposal [1] of Alexander Korotkov and some discussion
about custom compression methods before. This is an implementation of
per-datum compression. Syntax is similar to the one in proposal but
not the same.Syntax:
CREATE COMPRESSION METHOD <cmname> HANDLER <compression_handler>;
DROP COMPRESSION METHOD <cmname>;Compression handler is a function that returns a structure containing
compression routines:- configure - function called when the compression method applied to
an attribute
- drop - called when the compression method is removed from an
attribute
- compress - compress function
- decompress - decompress functionUser can create compressed columns with the commands below:
CREATE TABLE t(a tsvector COMPRESSED <cmname> WITH <options>);
ALTER TABLE t ALTER COLUMN a SET COMPRESSED <cmname> WITH <options>;
ALTER TABLE t ALTER COLUMN a SET NOT COMPRESSED;Also there is syntax of binding compression methods to types:
ALTER TYPE <type> SET COMPRESSED <cmname>;
ALTER TYPE <type> SET NOT COMPRESSED;There are two new tables in the catalog, pg_compression and
pg_compression_opt. pg_compression is used as storage of compression
methods, and pg_compression_opt is used to store specific compression
options for particular column.When user binds a compression method to some column a new record in
pg_compression_opt is created and all further attribute values will
contain compression options Oid while old values will remain
unchanged. And when we alter a compression method for
the attribute it won't change previous record in pg_compression_opt.
Instead it'll create a new one and new values will be stored
with new Oid. That way there is no need of recompression of the old
tuples. And also tuples containing compressed datums can be copied to
other tables so records in pg_compression_opt shouldn't be removed. In
the current patch they can be removed with DROP COMPRESSION METHOD
CASCADE, but after that decompression won't be possible on compressed
tuples. Maybe CASCADE should keep compression options.I haven't changed the base logic of working with compressed datums. It
means that custom compressed datums behave exactly the same as current
LZ compressed datums, and the logic differs only in
toast_compress_datum and toast_decompress_datum.This patch doesn't break backward compability and should work
seamlessly with older version of database. I used one of two free
bits in `va_rawsize` from `varattrib_4b->va_compressed` as flag of
custom compressed datums. Also I renamed it to `va_info` since it
contains not only rawsize now.The patch also includes custom compression method for tsvector which
is used in tests.[1]
/messages/by-id/CAPpHfdsdTA5uZeq6MNXL5ZRuNx+Sig4ykWzWEAfkC6ZKMDy6=Q@mail.gmail.com
Attached rebased version of the patch. Added support of pg_dump, the
code was simplified, and a separate cache for compression options was
added.
--
---
Ildus Kurbangaliev
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company
Attachments:
custom_compression_methods_v2.patchtext/x-patchDownload
diff --git a/contrib/test_decoding/expected/ddl.out b/contrib/test_decoding/expected/ddl.out
index 1e22c1eefc..766ced401f 100644
--- a/contrib/test_decoding/expected/ddl.out
+++ b/contrib/test_decoding/expected/ddl.out
@@ -416,12 +416,12 @@ CREATE TABLE replication_metadata (
WITH (user_catalog_table = true)
;
\d+ replication_metadata
- Table "public.replication_metadata"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------+---------+-----------+----------+--------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | |
- relation | name | | not null | | plain | |
- options | text[] | | | | extended | |
+ Table "public.replication_metadata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+----------+---------+-----------+----------+--------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | |
+ relation | name | | not null | | plain | | |
+ options | text[] | | | | extended | | |
Indexes:
"replication_metadata_pkey" PRIMARY KEY, btree (id)
Options: user_catalog_table=true
@@ -430,12 +430,12 @@ INSERT INTO replication_metadata(relation, options)
VALUES ('foo', ARRAY['a', 'b']);
ALTER TABLE replication_metadata RESET (user_catalog_table);
\d+ replication_metadata
- Table "public.replication_metadata"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------+---------+-----------+----------+--------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | |
- relation | name | | not null | | plain | |
- options | text[] | | | | extended | |
+ Table "public.replication_metadata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+----------+---------+-----------+----------+--------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | |
+ relation | name | | not null | | plain | | |
+ options | text[] | | | | extended | | |
Indexes:
"replication_metadata_pkey" PRIMARY KEY, btree (id)
@@ -443,12 +443,12 @@ INSERT INTO replication_metadata(relation, options)
VALUES ('bar', ARRAY['a', 'b']);
ALTER TABLE replication_metadata SET (user_catalog_table = true);
\d+ replication_metadata
- Table "public.replication_metadata"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------+---------+-----------+----------+--------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | |
- relation | name | | not null | | plain | |
- options | text[] | | | | extended | |
+ Table "public.replication_metadata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+----------+---------+-----------+----------+--------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | |
+ relation | name | | not null | | plain | | |
+ options | text[] | | | | extended | | |
Indexes:
"replication_metadata_pkey" PRIMARY KEY, btree (id)
Options: user_catalog_table=true
@@ -461,13 +461,13 @@ ALTER TABLE replication_metadata ALTER COLUMN rewritemeornot TYPE text;
ERROR: cannot rewrite table "replication_metadata" used as a catalog table
ALTER TABLE replication_metadata SET (user_catalog_table = false);
\d+ replication_metadata
- Table "public.replication_metadata"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------------+---------+-----------+----------+--------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | |
- relation | name | | not null | | plain | |
- options | text[] | | | | extended | |
- rewritemeornot | integer | | | | plain | |
+ Table "public.replication_metadata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+----------------+---------+-----------+----------+--------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | |
+ relation | name | | not null | | plain | | |
+ options | text[] | | | | extended | | |
+ rewritemeornot | integer | | | | plain | | |
Indexes:
"replication_metadata_pkey" PRIMARY KEY, btree (id)
Options: user_catalog_table=false
diff --git a/src/backend/access/common/indextuple.c b/src/backend/access/common/indextuple.c
index 138671410a..9efa5a9648 100644
--- a/src/backend/access/common/indextuple.c
+++ b/src/backend/access/common/indextuple.c
@@ -92,7 +92,8 @@ index_form_tuple(TupleDesc tupleDescriptor,
VARSIZE(DatumGetPointer(untoasted_values[i])) > TOAST_INDEX_TARGET &&
(att->attstorage == 'x' || att->attstorage == 'm'))
{
- Datum cvalue = toast_compress_datum(untoasted_values[i]);
+ Datum cvalue = toast_compress_datum(untoasted_values[i],
+ att->attcompression);
if (DatumGetPointer(cvalue) != NULL)
{
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index ec10762529..21e375ad96 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -935,11 +935,48 @@ untransformRelOptions(Datum options)
val = (Node *) makeString(pstrdup(p));
}
result = lappend(result, makeDefElem(pstrdup(s), val, -1));
+ pfree(s);
}
return result;
}
+char *
+formatRelOptions(List *options)
+{
+ StringInfoData buf;
+ ListCell *cell;
+
+ initStringInfo(&buf);
+
+ foreach(cell, options)
+ {
+ DefElem *def = (DefElem *) lfirst(cell);
+
+ appendStringInfo(&buf, "%s%s=%s", buf.len > 0 ? ", " : "",
+ def->defname, defGetString(def));
+ }
+
+ return buf.data;
+}
+
+void
+freeRelOptions(List *options)
+{
+ ListCell *cell;
+
+ Assert(options != NIL);
+ foreach(cell, options)
+ {
+ DefElem *def = (DefElem *) lfirst(cell);
+
+ pfree(def->defname);
+ pfree(defGetString(def));
+ pfree(def->arg);
+ }
+ list_free_deep(options);
+}
+
/*
* Extract and parse reloptions from a pg_class tuple.
*
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index 4436c86361..8ec03457e0 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -19,8 +19,10 @@
#include "postgres.h"
+#include "access/compression.h"
#include "access/hash.h"
#include "access/htup_details.h"
+#include "access/reloptions.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
@@ -226,6 +228,7 @@ TupleDescCopyEntry(TupleDesc dst, AttrNumber dstAttno,
dstAtt->attnotnull = false;
dstAtt->atthasdef = false;
dstAtt->attidentity = '\0';
+ dstAtt->attcompression = InvalidOid;
}
/*
@@ -380,6 +383,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
return false;
if (attr1->attcollation != attr2->attcollation)
return false;
+ if (attr1->attcompression != attr2->attcompression)
+ return false;
/* attacl, attoptions and attfdwoptions are not even present... */
}
@@ -442,6 +447,7 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
}
else if (tupdesc2->constr != NULL)
return false;
+
return true;
}
@@ -547,6 +553,7 @@ TupleDescInitEntry(TupleDesc desc,
att->attalign = typeForm->typalign;
att->attstorage = typeForm->typstorage;
att->attcollation = typeForm->typcollation;
+ att->attcompression = InvalidOid;
ReleaseSysCache(tuple);
}
@@ -659,7 +666,6 @@ TupleDescInitEntryCollation(TupleDesc desc,
TupleDescAttr(desc, attributeNumber - 1)->attcollation = collationid;
}
-
/*
* BuildDescForRelation
*
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index 5a8f1dab83..83ba21e8c7 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -30,8 +30,10 @@
#include <unistd.h>
#include <fcntl.h>
+#include "access/compression.h"
#include "access/genam.h"
#include "access/heapam.h"
+#include "access/reloptions.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -39,6 +41,8 @@
#include "miscadmin.h"
#include "utils/expandeddatum.h"
#include "utils/fmgroids.h"
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/typcache.h"
@@ -53,19 +57,46 @@
typedef struct toast_compress_header
{
int32 vl_len_; /* varlena header (do not touch directly!) */
- int32 rawsize;
+ uint32 info; /* flags (2 bits) and rawsize */
} toast_compress_header;
+/*
+ * If the compression method were used, then data also contains
+ * Oid of compression options
+ */
+typedef struct toast_compress_header_custom
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ uint32 info; /* flags (2 high bits) and rawsize */
+ Oid cmoptoid; /* Oid from pg_compression_opt */
+} toast_compress_header_custom;
+
+static HTAB *compression_options_htab = NULL;
+static MemoryContext compression_options_mcxt = NULL;
+
+#define RAWSIZEMASK (0x3FFFFFFFU)
+
/*
* Utilities for manipulation of header information for compressed
* toast entries.
*/
-#define TOAST_COMPRESS_HDRSZ ((int32) sizeof(toast_compress_header))
-#define TOAST_COMPRESS_RAWSIZE(ptr) (((toast_compress_header *) (ptr))->rawsize)
+#define TOAST_COMPRESS_HDRSZ ((int32) sizeof(toast_compress_header))
+#define TOAST_COMPRESS_HDRSZ_CUSTOM ((int32) sizeof(toast_compress_header_custom))
+#define TOAST_COMPRESS_RAWSIZE(ptr) (((toast_compress_header *) (ptr))->info & RAWSIZEMASK)
#define TOAST_COMPRESS_RAWDATA(ptr) \
(((char *) (ptr)) + TOAST_COMPRESS_HDRSZ)
#define TOAST_COMPRESS_SET_RAWSIZE(ptr, len) \
- (((toast_compress_header *) (ptr))->rawsize = (len))
+do { \
+ ((toast_compress_header *) (ptr))->info &= 0xC0000000; \
+ ((toast_compress_header *) (ptr))->info |= ((uint32)(len) & RAWSIZEMASK); \
+} while (0)
+#define TOAST_COMPRESS_SET_CMOPTOID(ptr, oid) \
+ (((toast_compress_header_custom *) (ptr))->cmoptoid = (oid))
+#define TOAST_COMPRESS_SET_CUSTOM(ptr) \
+do { \
+ (((toast_compress_header *) (ptr))->info |= (1 << 31)); \
+ (((toast_compress_header *) (ptr))->info &= ~(1 << 30)); \
+} while (0)
static void toast_delete_datum(Relation rel, Datum value, bool is_speculative);
static Datum toast_save_datum(Relation rel, Datum value,
@@ -83,6 +114,8 @@ static int toast_open_indexes(Relation toastrel,
static void toast_close_indexes(Relation *toastidxs, int num_indexes,
LOCKMODE lock);
static void init_toast_snapshot(Snapshot toast_snapshot);
+static void init_compression_options_htab(void);
+static AttributeCompression *get_compression_options_info(Oid cmoptoid);
/* ----------
@@ -741,6 +774,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
Datum old_value;
Datum new_value;
+ Form_pg_attribute att;
+
/*
* Search for the biggest yet unprocessed internal attribute
*/
@@ -770,10 +805,11 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
* Attempt to compress it inline, if it has attstorage 'x'
*/
i = biggest_attno;
- if (TupleDescAttr(tupleDesc, i)->attstorage == 'x')
+ att = TupleDescAttr(tupleDesc, i);
+ if (att->attstorage == 'x')
{
old_value = toast_values[i];
- new_value = toast_compress_datum(old_value);
+ new_value = toast_compress_datum(old_value, att->attcompression);
if (DatumGetPointer(new_value) != NULL)
{
@@ -914,7 +950,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
*/
i = biggest_attno;
old_value = toast_values[i];
- new_value = toast_compress_datum(old_value);
+ new_value = toast_compress_datum(old_value,
+ TupleDescAttr(tupleDesc, i)->attcompression);
if (DatumGetPointer(new_value) != NULL)
{
@@ -1229,7 +1266,9 @@ toast_flatten_tuple_to_datum(HeapTupleHeader tup,
if (VARATT_IS_EXTERNAL(new_value) ||
VARATT_IS_COMPRESSED(new_value))
{
- new_value = heap_tuple_untoast_attr(new_value);
+ struct varlena *untoasted_value = heap_tuple_untoast_attr(new_value);
+
+ new_value = untoasted_value;
toast_values[i] = PointerGetDatum(new_value);
toast_free[i] = true;
}
@@ -1353,7 +1392,6 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
return new_tuple;
}
-
/* ----------
* toast_compress_datum -
*
@@ -1368,25 +1406,43 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
* ----------
*/
Datum
-toast_compress_datum(Datum value)
+toast_compress_datum(Datum value, Oid cmoptoid)
{
- struct varlena *tmp;
- int32 valsize = VARSIZE_ANY_EXHDR(DatumGetPointer(value));
- int32 len;
+ struct varlena *tmp = NULL;
+ int32 valsize,
+ len = 0;
+ AttributeCompression *ac = NULL;
Assert(!VARATT_IS_EXTERNAL(DatumGetPointer(value)));
Assert(!VARATT_IS_COMPRESSED(DatumGetPointer(value)));
+ if (OidIsValid(cmoptoid))
+ ac = get_compression_options_info(cmoptoid);
+
/*
* No point in wasting a palloc cycle if value size is out of the allowed
* range for compression
*/
- if (valsize < PGLZ_strategy_default->min_input_size ||
- valsize > PGLZ_strategy_default->max_input_size)
+ valsize = VARSIZE_ANY_EXHDR(DatumGetPointer(value));
+ if (!ac && (valsize < PGLZ_strategy_default->min_input_size ||
+ valsize > PGLZ_strategy_default->max_input_size))
return PointerGetDatum(NULL);
- tmp = (struct varlena *) palloc(PGLZ_MAX_OUTPUT(valsize) +
- TOAST_COMPRESS_HDRSZ);
+ if (ac)
+ {
+ tmp = ac->routine->compress(ac, (const struct varlena *) value);
+ if (!tmp)
+ return PointerGetDatum(NULL);
+ }
+ else
+ {
+ tmp = (struct varlena *) palloc(PGLZ_MAX_OUTPUT(valsize) +
+ TOAST_COMPRESS_HDRSZ);
+ len = pglz_compress(VARDATA_ANY(DatumGetPointer(value)),
+ valsize,
+ TOAST_COMPRESS_RAWDATA(tmp),
+ PGLZ_strategy_default);
+ }
/*
* We recheck the actual size even if pglz_compress() reports success,
@@ -1398,11 +1454,7 @@ toast_compress_datum(Datum value)
* only one header byte and no padding if the value is short enough. So
* we insist on a savings of more than 2 bytes to ensure we have a gain.
*/
- len = pglz_compress(VARDATA_ANY(DatumGetPointer(value)),
- valsize,
- TOAST_COMPRESS_RAWDATA(tmp),
- PGLZ_strategy_default);
- if (len >= 0 &&
+ if (!ac && len >= 0 &&
len + TOAST_COMPRESS_HDRSZ < valsize - 2)
{
TOAST_COMPRESS_SET_RAWSIZE(tmp, valsize);
@@ -1410,10 +1462,20 @@ toast_compress_datum(Datum value)
/* successful compression */
return PointerGetDatum(tmp);
}
+ else if (ac && VARSIZE(tmp) < valsize - 2)
+ {
+ TOAST_COMPRESS_SET_CUSTOM(tmp);
+ TOAST_COMPRESS_SET_RAWSIZE(tmp, valsize);
+ TOAST_COMPRESS_SET_CMOPTOID(tmp, ac->cmoptoid);
+ /* successful compression */
+ return PointerGetDatum(tmp);
+ }
else
{
/* incompressible data */
- pfree(tmp);
+ if (tmp)
+ pfree(tmp);
+
return PointerGetDatum(NULL);
}
}
@@ -2280,15 +2342,26 @@ toast_decompress_datum(struct varlena *attr)
Assert(VARATT_IS_COMPRESSED(attr));
- result = (struct varlena *)
- palloc(TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
- SET_VARSIZE(result, TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
+ if (VARATT_IS_CUSTOM_COMPRESSED(attr))
+ {
+ AttributeCompression *ac;
+ toast_compress_header_custom *hdr;
- if (pglz_decompress(TOAST_COMPRESS_RAWDATA(attr),
- VARSIZE(attr) - TOAST_COMPRESS_HDRSZ,
- VARDATA(result),
- TOAST_COMPRESS_RAWSIZE(attr)) < 0)
- elog(ERROR, "compressed data is corrupted");
+ hdr = (toast_compress_header_custom *) attr;
+ ac = get_compression_options_info(hdr->cmoptoid);
+ result = ac->routine->decompress(ac, attr);
+ }
+ else
+ {
+ result = (struct varlena *)
+ palloc(TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
+ SET_VARSIZE(result, TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
+ if (pglz_decompress(TOAST_COMPRESS_RAWDATA(attr),
+ VARSIZE(attr) - TOAST_COMPRESS_HDRSZ,
+ VARDATA(result),
+ TOAST_COMPRESS_RAWSIZE(attr)) < 0)
+ elog(ERROR, "compressed data is corrupted");
+ }
return result;
}
@@ -2390,3 +2463,44 @@ init_toast_snapshot(Snapshot toast_snapshot)
InitToastSnapshot(*toast_snapshot, snapshot->lsn, snapshot->whenTaken);
}
+
+static void
+init_compression_options_htab(void)
+{
+ HASHCTL ctl;
+
+ compression_options_mcxt = AllocSetContextCreate(TopMemoryContext,
+ "compression options cache context",
+ ALLOCSET_DEFAULT_SIZES);
+ MemSet(&ctl, 0, sizeof(ctl));
+ ctl.keysize = sizeof(Oid);
+ ctl.entrysize = sizeof(AttributeCompression);
+ ctl.hcxt = compression_options_mcxt;
+ compression_options_htab = hash_create("compression options cache", 100, &ctl,
+ HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
+}
+
+static AttributeCompression *
+get_compression_options_info(Oid cmoptoid)
+{
+ bool found;
+ AttributeCompression *result;
+
+ Assert(OidIsValid(cmoptoid));
+ if (!compression_options_htab)
+ init_compression_options_htab();
+
+ result = hash_search(compression_options_htab, &cmoptoid, HASH_ENTER, &found);
+ if (!found)
+ {
+ MemoryContext oldcxt;
+
+ Assert(compression_options_mcxt);
+ oldcxt = MemoryContextSwitchTo(compression_options_mcxt);
+ result->routine = GetCompressionRoutine(cmoptoid);
+ result->options = GetCompressionOptionsList(cmoptoid);
+ result->cmoptoid = cmoptoid;
+ MemoryContextSwitchTo(oldcxt);
+ }
+ return result;
+}
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 0453fd4ac1..53feb17abc 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -718,6 +718,7 @@ DefineAttr(char *name, char *type, int attnum, int nullness)
attrtypes[attnum]->attcacheoff = -1;
attrtypes[attnum]->atttypmod = -1;
attrtypes[attnum]->attislocal = true;
+ attrtypes[attnum]->attcompression = InvalidOid;
if (nullness == BOOTCOL_NULL_FORCE_NOT_NULL)
{
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index fd33426bad..c7cea974b1 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -46,7 +46,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_collation.h pg_partitioned_table.h pg_range.h pg_transform.h \
pg_sequence.h pg_publication.h pg_publication_rel.h pg_subscription.h \
pg_subscription_rel.h toasting.h indexing.h \
- toasting.h indexing.h \
+ pg_compression.h pg_compression_opt.h \
)
# location of Catalog.pm
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index ccde66a7dd..fd733a34a0 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -3340,6 +3340,8 @@ static const char *const no_priv_msg[MAX_ACL_KIND] =
gettext_noop("permission denied for publication %s"),
/* ACL_KIND_SUBSCRIPTION */
gettext_noop("permission denied for subscription %s"),
+ /* ACL_KIND_COMPRESSION_METHOD */
+ gettext_noop("permission denied for compression method %s"),
};
static const char *const not_owner_msg[MAX_ACL_KIND] =
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 6fffc290fa..faaf7fb7f1 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -28,6 +28,8 @@
#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_collation_fn.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_conversion.h"
@@ -173,7 +175,9 @@ static const Oid object_classes[] = {
PublicationRelationId, /* OCLASS_PUBLICATION */
PublicationRelRelationId, /* OCLASS_PUBLICATION_REL */
SubscriptionRelationId, /* OCLASS_SUBSCRIPTION */
- TransformRelationId /* OCLASS_TRANSFORM */
+ TransformRelationId, /* OCLASS_TRANSFORM */
+ CompressionMethodRelationId, /* OCLASS_COMPRESSION_METHOD */
+ CompressionOptRelationId, /* OCLASS_COMPRESSION_OPTIONS */
};
@@ -1271,6 +1275,14 @@ doDeletion(const ObjectAddress *object, int flags)
DropTransformById(object->objectId);
break;
+ case OCLASS_COMPRESSION_METHOD:
+ RemoveCompressionMethodById(object->objectId);
+ break;
+
+ case OCLASS_COMPRESSION_OPTIONS:
+ RemoveCompressionOptionsById(object->objectId);
+ break;
+
/*
* These global object types are not supported here.
*/
@@ -2459,6 +2471,12 @@ getObjectClass(const ObjectAddress *object)
case TransformRelationId:
return OCLASS_TRANSFORM;
+
+ case CompressionMethodRelationId:
+ return OCLASS_COMPRESSION_METHOD;
+
+ case CompressionOptRelationId:
+ return OCLASS_COMPRESSION_OPTIONS;
}
/* shouldn't get here */
diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl
index 2eebb061b7..36c7fa0484 100644
--- a/src/backend/catalog/genbki.pl
+++ b/src/backend/catalog/genbki.pl
@@ -449,6 +449,7 @@ sub emit_pgattr_row
attisdropped => 'f',
attislocal => 't',
attinhcount => '0',
+ attcompression=> '0',
attacl => '_null_',
attoptions => '_null_',
attfdwoptions => '_null_');
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 05e70818e7..b08e857e3a 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -29,8 +29,10 @@
*/
#include "postgres.h"
+#include "access/compression.h"
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/reloptions.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "access/xact.h"
@@ -44,6 +46,8 @@
#include "catalog/partition.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_foreign_table.h"
@@ -628,6 +632,7 @@ InsertPgAttributeTuple(Relation pg_attribute_rel,
values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal);
values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount);
values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(new_attribute->attcollation);
+ values[Anum_pg_attribute_attcompression - 1] = ObjectIdGetDatum(new_attribute->attcompression);
/* start out with empty permissions and empty options */
nulls[Anum_pg_attribute_attacl - 1] = true;
@@ -707,6 +712,13 @@ AddNewAttributeTuples(Oid new_rel_oid,
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+
+ if (OidIsValid(attr->attcompression))
+ {
+ ObjectAddressSet(referenced, CompressionOptRelationId,
+ attr->attcompression);
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
}
/*
@@ -1453,6 +1465,22 @@ DeleteRelationTuple(Oid relid)
heap_close(pg_class_desc, RowExclusiveLock);
}
+static void
+DropAttributeCompression(Form_pg_attribute att)
+{
+ CompressionMethodRoutine *cmr = GetCompressionRoutine(att->attcompression);
+
+ if (cmr->drop)
+ {
+ bool attisdropped = att->attisdropped;
+ List *options = GetCompressionOptionsList(att->attcompression);
+
+ att->attisdropped = true;
+ cmr->drop(att, options);
+ att->attisdropped = attisdropped;
+ }
+}
+
/*
* DeleteAttributeTuples
*
@@ -1483,7 +1511,14 @@ DeleteAttributeTuples(Oid relid)
/* Delete all the matching tuples */
while ((atttup = systable_getnext(scan)) != NULL)
+ {
+ Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(atttup);
+
+ if (OidIsValid(att->attcompression))
+ DropAttributeCompression(att);
+
CatalogTupleDelete(attrel, &atttup->t_self);
+ }
/* Clean up after the scan */
systable_endscan(scan);
@@ -1576,6 +1611,8 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
else
{
/* Dropping user attributes is lots harder */
+ if (OidIsValid(attStruct->attcompression))
+ DropAttributeCompression(attStruct);
/* Mark the attribute as dropped */
attStruct->attisdropped = true;
@@ -1597,6 +1634,8 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
/* We don't want to keep stats for it anymore */
attStruct->attstattarget = 0;
+ attStruct->attcompression = InvalidOid;
+
/*
* Change the column name to something that isn't likely to conflict
*/
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index c7b2f031f0..11dc107ab7 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -393,6 +393,7 @@ ConstructTupleDescriptor(Relation heapRelation,
to->atttypmod = exprTypmod(indexkey);
to->attislocal = true;
to->attcollation = collationObjectId[i];
+ to->attcompression = InvalidOid;
ReleaseSysCache(tuple);
@@ -471,6 +472,7 @@ ConstructTupleDescriptor(Relation heapRelation,
to->attbyval = typeTup->typbyval;
to->attalign = typeTup->typalign;
to->attstorage = typeTup->typstorage;
+ to->attcompression = InvalidOid;
ReleaseSysCache(tuple);
}
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 6cac2dfd1d..d2b5285a5a 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -29,6 +29,8 @@
#include "catalog/pg_default_acl.h"
#include "catalog/pg_event_trigger.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_conversion.h"
@@ -490,6 +492,30 @@ static const ObjectPropertyType ObjectProperty[] =
InvalidAttrNumber, /* no ACL (same as relation) */
ACL_KIND_STATISTICS,
true
+ },
+ {
+ CompressionMethodRelationId,
+ CompressionMethodOidIndexId,
+ COMPRESSIONMETHODOID,
+ COMPRESSIONMETHODNAME,
+ Anum_pg_compression_cmname,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ true
+ },
+ {
+ CompressionOptRelationId,
+ CompressionOptionsOidIndexId,
+ COMPRESSIONOPTIONSOID,
+ -1,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ false,
}
};
@@ -712,6 +738,10 @@ static const struct object_type_map
/* OBJECT_STATISTIC_EXT */
{
"statistics object", OBJECT_STATISTIC_EXT
+ },
+ /* OCLASS_COMPRESSION_METHOD */
+ {
+ "compression method", OBJECT_COMPRESSION_METHOD
}
};
@@ -876,6 +906,7 @@ get_object_address(ObjectType objtype, Node *object,
case OBJECT_ACCESS_METHOD:
case OBJECT_PUBLICATION:
case OBJECT_SUBSCRIPTION:
+ case OBJECT_COMPRESSION_METHOD:
address = get_object_address_unqualified(objtype,
(Value *) object, missing_ok);
break;
@@ -1182,6 +1213,11 @@ get_object_address_unqualified(ObjectType objtype,
address.objectId = get_subscription_oid(name, missing_ok);
address.objectSubId = 0;
break;
+ case OBJECT_COMPRESSION_METHOD:
+ address.classId = CompressionMethodRelationId;
+ address.objectId = get_compression_method_oid(name, missing_ok);
+ address.objectSubId = 0;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
/* placate compiler, which doesn't know elog won't return */
@@ -2139,6 +2175,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_SCHEMA:
case OBJECT_SUBSCRIPTION:
case OBJECT_TABLESPACE:
+ case OBJECT_COMPRESSION_METHOD:
if (list_length(name) != 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -2395,12 +2432,14 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
case OBJECT_ACCESS_METHOD:
+ case OBJECT_COMPRESSION_METHOD:
/* We treat these object types as being owned by superusers */
if (!superuser_arg(roleid))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser")));
break;
+
case OBJECT_STATISTIC_EXT:
if (!pg_statistics_object_ownercheck(address.objectId, roleid))
aclcheck_error_type(ACLCHECK_NOT_OWNER, address.objectId);
@@ -3398,6 +3437,27 @@ getObjectDescription(const ObjectAddress *object)
break;
}
+ case OCLASS_COMPRESSION_METHOD:
+ {
+ char *name = get_compression_method_name(object->objectId);
+
+ if (!name)
+ elog(ERROR, "cache lookup failed for compression method %u",
+ object->objectId);
+ appendStringInfo(&buffer, _("compression method %s"), name);
+ pfree(name);
+ break;
+ }
+
+ case OCLASS_COMPRESSION_OPTIONS:
+ {
+ char *name = get_compression_method_name_for_opt(object->objectId);
+
+ appendStringInfo(&buffer, _("compression options for %s"), name);
+ pfree(name);
+ break;
+ }
+
case OCLASS_TRANSFORM:
{
HeapTuple trfTup;
@@ -3919,6 +3979,14 @@ getObjectTypeDescription(const ObjectAddress *object)
appendStringInfoString(&buffer, "transform");
break;
+ case OCLASS_COMPRESSION_METHOD:
+ appendStringInfoString(&buffer, "compression method");
+ break;
+
+ case OCLASS_COMPRESSION_OPTIONS:
+ appendStringInfoString(&buffer, "compression options");
+ break;
+
/*
* There's intentionally no default: case here; we want the
* compiler to warn if a new OCLASS hasn't been handled above.
@@ -4160,6 +4228,30 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case OCLASS_COMPRESSION_METHOD:
+ {
+ char *cmname = get_compression_method_name(object->objectId);
+
+ if (!cmname)
+ elog(ERROR, "cache lookup failed for compression method %u",
+ object->objectId);
+ appendStringInfoString(&buffer, quote_identifier(cmname));
+ if (objname)
+ *objname = list_make1(cmname);
+ else
+ pfree(cmname);
+ break;
+ }
+
+ case OCLASS_COMPRESSION_OPTIONS:
+ {
+ appendStringInfo(&buffer, "%u",
+ object->objectId);
+ if (objname)
+ *objname = list_make1(psprintf("%u", object->objectId));
+ break;
+ }
+
case OCLASS_CONSTRAINT:
{
HeapTuple conTup;
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 59ffd2104d..6e806b3334 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -22,6 +22,7 @@
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
@@ -118,6 +119,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(-1);
values[Anum_pg_type_typndims - 1] = Int32GetDatum(0);
values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(InvalidOid);
+ values[Anum_pg_type_typdefaultcm - 1] = ObjectIdGetDatum(InvalidOid);
nulls[Anum_pg_type_typdefaultbin - 1] = true;
nulls[Anum_pg_type_typdefault - 1] = true;
nulls[Anum_pg_type_typacl - 1] = true;
@@ -362,6 +364,7 @@ TypeCreate(Oid newTypeOid,
values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(typeMod);
values[Anum_pg_type_typndims - 1] = Int32GetDatum(typNDims);
values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(typeCollation);
+ values[Anum_pg_type_typdefaultcm - 1] = ObjectIdGetDatum(InvalidOid);
/*
* initialize the default binary value for this type. Check for nulls of
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 4a6c99e090..e23abf64f1 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -13,8 +13,8 @@ top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
- collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
- dbcommands.o define.o discard.o dropcmds.o \
+ collationcmds.o compressioncmds.o constraint.o conversioncmds.o copy.o \
+ createas.o dbcommands.o define.o discard.o dropcmds.o \
event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o matview.o operatorcmds.o opclasscmds.o \
policy.o portalcmds.o prepare.o proclang.o publicationcmds.o \
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 4f8147907c..4f18e4083f 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -385,6 +385,7 @@ ExecRenameStmt(RenameStmt *stmt)
case OBJECT_TSTEMPLATE:
case OBJECT_PUBLICATION:
case OBJECT_SUBSCRIPTION:
+ case OBJECT_COMPRESSION_METHOD:
{
ObjectAddress address;
Relation catalog;
@@ -500,6 +501,7 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
case OBJECT_TSDICTIONARY:
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
+ case OBJECT_COMPRESSION_METHOD:
{
Relation catalog;
Relation relation;
@@ -627,6 +629,8 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_COMPRESSION_METHOD:
+ case OCLASS_COMPRESSION_OPTIONS:
/* ignore object types that don't have schema-qualified names */
break;
diff --git a/src/backend/commands/compressioncmds.c b/src/backend/commands/compressioncmds.c
new file mode 100644
index 0000000000..32cbcc3efe
--- /dev/null
+++ b/src/backend/commands/compressioncmds.c
@@ -0,0 +1,485 @@
+/*-------------------------------------------------------------------------
+ *
+ * compressioncmds.c
+ * Routines for SQL commands that manipulate compression methods.
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/commands/compressioncmds.c
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "miscadmin.h"
+
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/compression.h"
+#include "access/reloptions.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "parser/parse_func.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+static CompressionMethodRoutine *get_compression_method_routine(Oid cmhandler, Oid typeid);
+
+/*
+ * Convert a handler function name to an Oid. If the return type of the
+ * function doesn't match the given AM type, an error is raised.
+ *
+ * This function either return valid function Oid or throw an error.
+ */
+static Oid
+LookupCompressionHandlerFunc(List *handlerName)
+{
+ static const Oid funcargtypes[1] = {INTERNALOID};
+ Oid handlerOid;
+
+ if (handlerName == NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("handler function is not specified")));
+
+ /* handlers have one argument of type internal */
+ handlerOid = LookupFuncName(handlerName, 1, funcargtypes, false);
+
+ /* check that handler has the correct return type */
+ if (get_func_rettype(handlerOid) != COMPRESSION_HANDLEROID)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("function %s must return type %s",
+ NameListToString(handlerName),
+ "compression_handler")));
+
+ return handlerOid;
+}
+
+static ObjectAddress
+CreateCompressionMethod(char *cmName, List *handlerName)
+{
+ Relation rel;
+ ObjectAddress myself;
+ ObjectAddress referenced;
+ Oid cmoid;
+ Oid cmhandler;
+ bool nulls[Natts_pg_compression];
+ Datum values[Natts_pg_compression];
+ HeapTuple tup;
+
+ rel = heap_open(CompressionMethodRelationId, RowExclusiveLock);
+
+ /* Check if name is used */
+ cmoid = GetSysCacheOid1(COMPRESSIONMETHODNAME, CStringGetDatum(cmName));
+ if (OidIsValid(cmoid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("compression method \"%s\" already exists",
+ cmName)));
+
+ /*
+ * Get the handler function oid and compression method routine
+ */
+ cmhandler = LookupCompressionHandlerFunc(handlerName);
+
+ /*
+ * Insert tuple into pg_compression.
+ */
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+
+ values[Anum_pg_compression_cmname - 1] =
+ DirectFunctionCall1(namein, CStringGetDatum(cmName));
+ values[Anum_pg_compression_cmhandler - 1] = ObjectIdGetDatum(cmhandler);
+
+ tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+
+ cmoid = CatalogTupleInsert(rel, tup);
+ heap_freetuple(tup);
+
+ ObjectAddressSet(myself, CompressionMethodRelationId, cmoid);
+
+ /* Record dependency on handler function */
+ ObjectAddressSet(referenced, ProcedureRelationId, cmhandler);
+
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ recordDependencyOnCurrentExtension(&myself, false);
+
+ heap_close(rel, RowExclusiveLock);
+
+ return myself;
+}
+
+ObjectAddress
+DefineCompressionMethod(List *names, List *parameters)
+{
+ char *cmName;
+ ListCell *pl;
+ DefElem *handlerEl = NULL;
+
+ if (list_length(names) != 1)
+ elog(ERROR, "compression method name cannot be qualified");
+
+ cmName = strVal(linitial(names));
+
+ /* Must be super user */
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to create compression method \"%s\"",
+ cmName),
+ errhint("Must be superuser to create an compression method.")));
+
+ foreach(pl, parameters)
+ {
+ DefElem *defel = (DefElem *) lfirst(pl);
+ DefElem **defelp;
+
+ if (pg_strcasecmp(defel->defname, "handler") == 0)
+ defelp = &handlerEl;
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("compression method attribute \"%s\" not recognized",
+ defel->defname)));
+ break;
+ }
+
+ *defelp = defel;
+ }
+
+ if (!handlerEl)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("compression method handler is not specified")));
+
+ return CreateCompressionMethod(cmName, (List *) handlerEl->arg);
+}
+
+void
+RemoveCompressionMethodById(Oid cmOid)
+{
+ Relation relation;
+ HeapTuple tup;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to drop compression methods")));
+
+ relation = heap_open(CompressionMethodRelationId, RowExclusiveLock);
+
+ tup = SearchSysCache1(COMPRESSIONMETHODOID, ObjectIdGetDatum(cmOid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for compression method %u", cmOid);
+
+ CatalogTupleDelete(relation, &tup->t_self);
+
+ ReleaseSysCache(tup);
+
+ heap_close(relation, RowExclusiveLock);
+}
+
+/*
+ * Create new record in pg_compression_opt
+ */
+Oid
+CreateCompressionOptions(Form_pg_attribute attr, Oid cmid, List *options)
+{
+ Relation rel;
+ HeapTuple tuple;
+ Oid result,
+ cmhandler;
+ Datum values[Natts_pg_compression_opt];
+ bool nulls[Natts_pg_compression_opt];
+ ObjectAddress myself,
+ ref1,
+ ref2,
+ ref3;
+ CompressionMethodRoutine *routine;
+
+ /* Initialize buffers for new tuple values */
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+
+ /* Get handler function OID for the compression method */
+ tuple = SearchSysCache1(COMPRESSIONMETHODOID, ObjectIdGetDatum(cmid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for compression method %u", cmid);
+ cmhandler = ((Form_pg_compression) GETSTRUCT(tuple))->cmhandler;
+ ReleaseSysCache(tuple);
+
+ routine = get_compression_method_routine(cmhandler, attr->atttypid);
+
+ if (routine->configure && options != NIL)
+ routine->configure(attr, options);
+
+ values[Anum_pg_compression_opt_cmid - 1] = ObjectIdGetDatum(cmid);
+ values[Anum_pg_compression_opt_cmhandler - 1] = ObjectIdGetDatum(cmhandler);
+
+ if (options)
+ values[Anum_pg_compression_opt_cmoptions - 1] = optionListToArray(options);
+ else
+ nulls[Anum_pg_compression_opt_cmoptions - 1] = true;
+
+ rel = heap_open(CompressionOptRelationId, RowExclusiveLock);
+ tuple = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+ result = CatalogTupleInsert(rel, tuple);
+ heap_freetuple(tuple);
+
+ ObjectAddressSet(myself, CompressionOptRelationId, result);
+ ObjectAddressSet(ref1, ProcedureRelationId, cmhandler);
+ ObjectAddressSubSet(ref2, RelationRelationId, attr->attrelid, attr->attnum);
+ ObjectAddressSet(ref3, CompressionMethodRelationId, cmid);
+
+ recordDependencyOn(&myself, &ref1, DEPENDENCY_NORMAL);
+ recordDependencyOn(&ref2, &myself, DEPENDENCY_NORMAL);
+ recordDependencyOn(&myself, &ref3, DEPENDENCY_NORMAL);
+ recordDependencyOnCurrentExtension(&myself, false);
+ heap_close(rel, RowExclusiveLock);
+
+ return result;
+}
+
+void
+RemoveCompressionOptionsById(Oid cmoptoid)
+{
+ Relation relation;
+ HeapTuple tup;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to drop compression options")));
+
+ relation = heap_open(CompressionOptRelationId, RowExclusiveLock);
+
+ tup = SearchSysCache1(COMPRESSIONOPTIONSOID, ObjectIdGetDatum(cmoptoid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+
+ CatalogTupleDelete(relation, &tup->t_self);
+ ReleaseSysCache(tup);
+ heap_close(relation, RowExclusiveLock);
+}
+
+ColumnCompression *
+GetColumnCompressionForAttribute(Form_pg_attribute att)
+{
+ HeapTuple tuple;
+ Form_pg_compression_opt cmopt;
+ ColumnCompression *compression = makeNode(ColumnCompression);
+
+ /* Get handler function OID for the compression method */
+ tuple = SearchSysCache1(COMPRESSIONOPTIONSOID,
+ ObjectIdGetDatum(att->attcompression));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for compression options %u",
+ att->attcompression);
+
+ cmopt = (Form_pg_compression_opt) GETSTRUCT(tuple);
+ compression->methodName = NULL;
+ compression->methodOid = cmopt->cmid;
+ compression->options = GetCompressionOptionsList(att->attcompression);
+ ReleaseSysCache(tuple);
+
+ return compression;
+}
+
+void
+CheckCompressionMismatch(ColumnCompression * c1, ColumnCompression * c2,
+ const char *attributeName)
+{
+ char *cmname1 = c1->methodName ? c1->methodName :
+ get_compression_method_name(c1->methodOid);
+ char *cmname2 = c2->methodName ? c2->methodName :
+ get_compression_method_name(c2->methodOid);
+
+ if (strcmp(cmname1, cmname2))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("column \"%s\" has a compression method conflict",
+ attributeName),
+ errdetail("%s versus %s", cmname1, cmname2)));
+
+ if (!equal(c1->options, c2->options))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("column \"%s\" has a compression options conflict",
+ attributeName),
+ errdetail("(%s) versus (%s)",
+ formatRelOptions(c1->options),
+ formatRelOptions(c2->options))));
+}
+
+/*
+ * get_compression_method_oid
+ *
+ * If missing_ok is false, throw an error if compression method not found.
+ * If missing_ok is true, just return InvalidOid.
+ */
+Oid
+get_compression_method_oid(const char *cmname, bool missing_ok)
+{
+ HeapTuple tup;
+ Oid oid = InvalidOid;
+
+ tup = SearchSysCache1(COMPRESSIONMETHODNAME, CStringGetDatum(cmname));
+ if (HeapTupleIsValid(tup))
+ {
+ oid = HeapTupleGetOid(tup);
+ ReleaseSysCache(tup);
+ }
+
+ if (!OidIsValid(oid) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("compression method \"%s\" does not exist", cmname)));
+
+ return oid;
+}
+
+/*
+ * get_compression_method_name
+ *
+ * given an compression method OID, look up its name.
+ */
+char *
+get_compression_method_name(Oid cmOid)
+{
+ HeapTuple tup;
+ char *result = NULL;
+
+ tup = SearchSysCache1(COMPRESSIONMETHODOID, ObjectIdGetDatum(cmOid));
+ if (HeapTupleIsValid(tup))
+ {
+ Form_pg_compression cmform = (Form_pg_compression) GETSTRUCT(tup);
+
+ result = pstrdup(NameStr(cmform->cmname));
+ ReleaseSysCache(tup);
+ }
+ return result;
+}
+
+/*
+ * get_compression_method_name_for_opt
+ *
+ * given an compression options OID, look up a name for used compression method.
+ */
+char *
+get_compression_method_name_for_opt(Oid cmoptoid)
+{
+ HeapTuple tup;
+ Oid cmid;
+ char *result = NULL;
+ Form_pg_compression cmform;
+
+ tup = SearchSysCache1(COMPRESSIONOPTIONSOID, ObjectIdGetDatum(cmoptoid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+
+ cmid = ((Form_pg_compression_opt) GETSTRUCT(tup))->cmid;
+ ReleaseSysCache(tup);
+
+ /* now we can get the name */
+ tup = SearchSysCache1(COMPRESSIONMETHODOID, ObjectIdGetDatum(cmid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for compression method %u", cmid);
+
+ cmform = (Form_pg_compression) GETSTRUCT(tup);
+ result = pstrdup(NameStr(cmform->cmname));
+ ReleaseSysCache(tup);
+ return result;
+}
+
+/* get_compression_options */
+List *
+GetCompressionOptionsList(Oid cmoptoid)
+{
+ HeapTuple tuple;
+ List *result = NULL;
+ bool isnull;
+ Datum cmoptions;
+
+ /* Get handler function OID for the compression method */
+ tuple = SearchSysCache1(COMPRESSIONOPTIONSOID, ObjectIdGetDatum(cmoptoid));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+
+ cmoptions = SysCacheGetAttr(COMPRESSIONOPTIONSOID, tuple,
+ Anum_pg_compression_opt_cmoptions, &isnull);
+
+ if (!isnull)
+ result = untransformRelOptions(cmoptions);
+
+ ReleaseSysCache(tuple);
+ return result;
+}
+
+CompressionMethodRoutine *
+GetCompressionRoutine(Oid cmoptoid)
+{
+ HeapTuple tuple;
+ Form_pg_compression_opt cmopt;
+ regproc cmhandler;
+
+ /* Get handler function OID for the compression method */
+ tuple = SearchSysCache1(COMPRESSIONOPTIONSOID, ObjectIdGetDatum(cmoptoid));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+
+ cmopt = (Form_pg_compression_opt) GETSTRUCT(tuple);
+ cmhandler = cmopt->cmhandler;
+
+ /* Complain if handler OID is invalid */
+ if (!RegProcedureIsValid(cmhandler))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("could not find compression method handler for compression options %u",
+ cmoptoid)));
+
+ ReleaseSysCache(tuple);
+
+ /* And finally, call the handler function to get the API struct. */
+ return get_compression_method_routine(cmhandler, InvalidOid);
+}
+
+/*
+ * Call the specified compression method handler
+ * routine to get its CompressionMethodRoutine struct,
+ * which will be palloc'd in the caller's context.
+ */
+static CompressionMethodRoutine *
+get_compression_method_routine(Oid cmhandler, Oid typeid)
+{
+ Datum datum;
+ CompressionMethodRoutine *routine;
+ CompressionMethodOpArgs opargs;
+
+ opargs.typeid = typeid;
+ opargs.cmhanderid = cmhandler;
+
+ datum = OidFunctionCall1(cmhandler, PointerGetDatum(&opargs));
+ routine = (CompressionMethodRoutine *) DatumGetPointer(datum);
+
+ if (routine == NULL || !IsA(routine, CompressionMethodRoutine))
+ elog(ERROR, "compression method handler function %u "
+ "did not return an CompressionMethodRoutine struct",
+ cmhandler);
+
+ return routine;
+}
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index cfa3f059c2..8cef3be086 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2758,8 +2758,8 @@ CopyFrom(CopyState cstate)
List *recheckIndexes = NIL;
/* OK, store the tuple and create index entries for it */
- heap_insert(resultRelInfo->ri_RelationDesc, tuple, mycid,
- hi_options, bistate);
+ heap_insert(resultRelInfo->ri_RelationDesc, tuple,
+ mycid, hi_options, bistate);
if (resultRelInfo->ri_NumIndices > 0)
recheckIndexes = ExecInsertIndexTuples(slot,
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e60210cb24..010bdb644b 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -112,7 +112,8 @@ create_ctas_internal(List *attrList, IntoClause *into)
* Create the relation. (This will error out if there's an existing view,
* so we don't need more code to complain if "replace" is false.)
*/
- intoRelationAddr = DefineRelation(create, relkind, InvalidOid, NULL, NULL);
+ intoRelationAddr = DefineRelation(create, relkind, InvalidOid, NULL, NULL,
+ NULL);
/*
* If necessary, create a TOAST table for the target table. Note that
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index 2b30677d6f..8611db22ec 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -275,6 +275,10 @@ does_not_exist_skipping(ObjectType objtype, Node *object)
name = NameListToString(castNode(List, object));
}
break;
+ case OBJECT_COMPRESSION_METHOD:
+ msg = gettext_noop("compression method \"%s\" does not exist, skipping");
+ name = strVal((Value *) object);
+ break;
case OBJECT_CONVERSION:
if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
{
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 938133bbe4..f520c7fe7b 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -91,6 +91,7 @@ static event_trigger_support_data event_trigger_support[] = {
{"CAST", true},
{"CONSTRAINT", true},
{"COLLATION", true},
+ {"COMPRESSION METHOD", true},
{"CONVERSION", true},
{"DATABASE", false},
{"DOMAIN", true},
@@ -1085,6 +1086,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
case OBJECT_CAST:
case OBJECT_COLUMN:
case OBJECT_COLLATION:
+ case OBJECT_COMPRESSION_METHOD:
case OBJECT_CONVERSION:
case OBJECT_DEFACL:
case OBJECT_DEFAULT:
@@ -1144,6 +1146,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
case OCLASS_ROLE:
+ case OCLASS_COMPRESSION_OPTIONS:
/* no support for global objects */
return false;
case OCLASS_EVENT_TRIGGER:
@@ -1183,6 +1186,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_COMPRESSION_METHOD:
return true;
/*
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index 9ad991507f..b840309d03 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -61,7 +61,7 @@ static void import_error_callback(void *arg);
* processing, hence any validation should be done before this
* conversion.
*/
-static Datum
+Datum
optionListToArray(List *options)
{
ArrayBuildState *astate = NULL;
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 62937124ef..857eb14b64 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -212,7 +212,8 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
stmt->tablespacename = NULL;
stmt->if_not_exists = seq->if_not_exists;
- address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL, NULL);
+ address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL, NULL,
+ NULL);
seqoid = address.objectId;
Assert(seqoid != InvalidOid);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 2e389e08da..11c5eea243 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -14,6 +14,7 @@
*/
#include "postgres.h"
+#include "access/compression.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "access/multixact.h"
@@ -33,6 +34,8 @@
#include "catalog/partition.h"
#include "catalog/pg_am.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_depend.h"
@@ -41,6 +44,7 @@
#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
+#include "catalog/pg_proc.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
@@ -90,6 +94,7 @@
#include "storage/smgr.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+#include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
@@ -458,6 +463,8 @@ static void ATExecGenericOptions(Relation rel, List *options);
static void ATExecEnableRowSecurity(Relation rel);
static void ATExecDisableRowSecurity(Relation rel);
static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
+static void ATExecAlterColumnCompression(AlteredTableInfo *tab, Relation rel,
+ const char *column, ColumnCompression * compression, LOCKMODE lockmode);
static void copy_relation_data(SMgrRelation rel, SMgrRelation dst,
ForkNumber forkNum, char relpersistence);
@@ -502,7 +509,8 @@ static ObjectAddress ATExecDetachPartition(Relation rel, RangeVar *name);
*/
ObjectAddress
DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
- ObjectAddress *typaddress, const char *queryString)
+ ObjectAddress *typaddress, const char *queryString,
+ Node **pAlterStmt)
{
char relname[NAMEDATALEN];
Oid namespaceId;
@@ -522,6 +530,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
Oid ofTypeId;
ObjectAddress address;
+ AlterTableStmt *alterStmt = NULL;
/*
* Truncate relname to appropriate length (probably a waste of time, as
@@ -723,6 +732,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
if (colDef->identity)
attr->attidentity = colDef->identity;
+
+ transformColumnCompression(colDef, stmt->relation, &alterStmt);
}
/*
@@ -920,6 +931,11 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
*/
relation_close(rel, NoLock);
+ if (pAlterStmt)
+ *pAlterStmt = (Node *) alterStmt;
+ else
+ Assert(!alterStmt);
+
return address;
}
@@ -1609,6 +1625,7 @@ storage_name(char c)
}
}
+
/*----------
* MergeAttributes
* Returns new schema given initial schema and superclasses.
@@ -1943,6 +1960,19 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
storage_name(def->storage),
storage_name(attribute->attstorage))));
+ if (OidIsValid(attribute->attcompression))
+ {
+ ColumnCompression *compression =
+ GetColumnCompressionForAttribute(attribute);
+
+ if (!def->compression)
+ def->compression = compression;
+ else
+ CheckCompressionMismatch(def->compression,
+ compression,
+ attributeName);
+ }
+
def->inhcount++;
/* Merge of NOT NULL constraints = OR 'em together */
def->is_not_null |= attribute->attnotnull;
@@ -1970,6 +2000,9 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
def->collOid = attribute->attcollation;
def->constraints = NIL;
def->location = -1;
+ if (OidIsValid(attribute->attcompression))
+ def->compression =
+ GetColumnCompressionForAttribute(attribute);
inhSchema = lappend(inhSchema, def);
newattno[parent_attno - 1] = ++child_attno;
}
@@ -2179,6 +2212,13 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
storage_name(def->storage),
storage_name(newdef->storage))));
+ if (!def->compression)
+ def->compression = newdef->compression;
+ else if (newdef->compression)
+ CheckCompressionMismatch(def->compression,
+ newdef->compression,
+ attributeName);
+
/* Mark the column as locally defined */
def->is_local = true;
/* Merge of NOT NULL constraints = OR 'em together */
@@ -3278,6 +3318,7 @@ AlterTableGetLockLevel(List *cmds)
*/
case AT_GenericOptions:
case AT_AlterColumnGenericOptions:
+ case AT_AlterColumnCompression:
cmd_lockmode = AccessExclusiveLock;
break;
@@ -3768,6 +3809,12 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* No command-specific prep needed */
pass = AT_PASS_MISC;
break;
+ case AT_AlterColumnCompression:
+ ATSimplePermissions(rel, ATT_TABLE);
+ /* FIXME This command never recurses */
+ /* No command-specific prep needed */
+ pass = AT_PASS_MISC;
+ break;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
@@ -4107,6 +4154,11 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
case AT_DetachPartition:
ATExecDetachPartition(rel, ((PartitionCmd *) cmd->def)->name);
break;
+ case AT_AlterColumnCompression:
+ ATExecAlterColumnCompression(tab, rel, cmd->name,
+ (ColumnCompression *) cmd->def,
+ lockmode);
+ break;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
@@ -5325,6 +5377,18 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
attribute.attislocal = colDef->is_local;
attribute.attinhcount = colDef->inhcount;
attribute.attcollation = collOid;
+ attribute.attcompression = InvalidOid;
+
+ if (!colDef->compression)
+ {
+ /* colDef->compression is handled in subsequent ALTER TABLE statement */
+ Oid cmid = get_base_typdefaultcm(typeTuple);
+
+ if (OidIsValid(cmid))
+ attribute.attcompression = CreateCompressionOptions(&attribute,
+ cmid, NULL);
+ }
+
/* attribute.attacl is handled by InsertPgAttributeTuple */
ReleaseSysCache(typeTuple);
@@ -6425,6 +6489,11 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE loc
errmsg("cannot alter system column \"%s\"",
colName)));
+ if (attrtuple->attcompression && newstorage != 'x' && newstorage != 'm')
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("compressed columns can only have storage MAIN or EXTENDED")));
+
/*
* safety check: do not allow toasted storage modes unless column datatype
* is TOAST-aware.
@@ -9070,6 +9139,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
SysScanDesc scan;
HeapTuple depTup;
ObjectAddress address;
+ Oid cmid;
attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
@@ -9345,6 +9415,8 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_COMPRESSION_METHOD:
+ case OCLASS_COMPRESSION_OPTIONS:
/*
* We don't expect any of these sorts of objects to depend on
@@ -9394,7 +9466,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
if (!(foundDep->refclassid == TypeRelationId &&
foundDep->refobjid == attTup->atttypid) &&
!(foundDep->refclassid == CollationRelationId &&
- foundDep->refobjid == attTup->attcollation))
+ foundDep->refobjid == attTup->attcollation) &&
+ !(foundDep->refclassid == CompressionMethodRelationId &&
+ foundDep->refobjid == attTup->attcompression))
elog(ERROR, "found unexpected dependency for column");
CatalogTupleDelete(depRel, &depTup->t_self);
@@ -9417,6 +9491,10 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
attTup->attalign = tform->typalign;
attTup->attstorage = tform->typstorage;
+ cmid = get_base_typdefaultcm(typeTuple);
+ if (OidIsValid(cmid))
+ attTup->attcompression = CreateCompressionOptions(attTup, cmid, NULL);
+
ReleaseSysCache(typeTuple);
CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
@@ -12413,6 +12491,86 @@ ATExecGenericOptions(Relation rel, List *options)
heap_freetuple(tuple);
}
+static void
+ATExecAlterColumnCompression(AlteredTableInfo *tab,
+ Relation rel,
+ const char *column,
+ ColumnCompression * compression,
+ LOCKMODE lockmode)
+{
+ Relation attrel;
+ HeapTuple atttuple;
+ Form_pg_attribute atttableform;
+ AttrNumber attnum;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_attribute];
+ bool nulls[Natts_pg_attribute];
+ bool replace[Natts_pg_attribute];
+
+ attrel = heap_open(AttributeRelationId, RowExclusiveLock);
+
+ atttuple = SearchSysCacheAttName(RelationGetRelid(rel), column);
+ if (!HeapTupleIsValid(atttuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("column \"%s\" of relation \"%s\" does not exist",
+ column, RelationGetRelationName(rel))));
+
+ /* Prevent them from altering a system attribute */
+ atttableform = (Form_pg_attribute) GETSTRUCT(atttuple);
+ attnum = atttableform->attnum;
+ if (attnum <= 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot alter system column \"%s\"", column)));
+
+ if (atttableform->attstorage != 'x' && atttableform->attstorage != 'm')
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("storage for \"%s\" should be MAIN or EXTENDED", column)));
+
+ /* Initialize buffers for new tuple values */
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+ memset(replace, false, sizeof(replace));
+
+ if (compression->methodName || OidIsValid(compression->methodOid))
+ {
+ /* SET COMPRESSED */
+ Oid cmid,
+ cmoptoid;
+
+ cmid = compression->methodName
+ ? get_compression_method_oid(compression->methodName, false)
+ : compression->methodOid;
+
+ cmoptoid = CreateCompressionOptions(atttableform, cmid, compression->options);
+
+ values[Anum_pg_attribute_attcompression - 1] = ObjectIdGetDatum(cmoptoid);
+ replace[Anum_pg_attribute_attcompression - 1] = true;
+
+ }
+ else
+ {
+ /* SET NOT COMPRESSED */
+ values[Anum_pg_attribute_attcompression - 1] = ObjectIdGetDatum(InvalidOid);
+ replace[Anum_pg_attribute_attcompression - 1] = true;
+ }
+
+ newtuple = heap_modify_tuple(atttuple, RelationGetDescr(attrel),
+ values, nulls, replace);
+ CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
+ heap_freetuple(newtuple);
+
+ InvokeObjectPostAlterHook(RelationRelationId,
+ RelationGetRelid(rel),
+ atttableform->attnum);
+
+ ReleaseSysCache(atttuple);
+ heap_close(attrel, RowExclusiveLock);
+}
+
+
/*
* Preparation phase for SET LOGGED/UNLOGGED
*
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 7ed16aeff4..38a086b584 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -40,6 +40,7 @@
#include "catalog/pg_am.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_depend.h"
@@ -2110,7 +2111,7 @@ DefineCompositeType(RangeVar *typevar, List *coldeflist)
* Finally create the relation. This also creates the type.
*/
DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, &address,
- NULL);
+ NULL, NULL);
return address;
}
@@ -3638,3 +3639,100 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
return oldNspOid;
}
+
+
+/*
+ * Execute ALTER TYPE SET COMPRESSED <cm> [WITH (<option>, ...)]
+ */
+static void
+AlterTypeDefaultCompression(Oid typeid, ColumnCompression * compression)
+{
+ Oid cmoid;
+ Type oldtup = typeidType(typeid);
+ Form_pg_type oldtype = (Form_pg_type) GETSTRUCT(oldtup);
+
+ if (oldtype->typtype != TYPTYPE_BASE)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("compression can be applied only to base types")));
+
+ cmoid = compression->methodName
+ ? get_compression_method_oid(compression->methodName, false)
+ : InvalidOid;
+
+ if (oldtype->typdefaultcm != cmoid)
+ {
+ Relation typrel;
+ HeapTuple newtup;
+ Datum values[Natts_pg_type];
+ bool nulls[Natts_pg_type];
+ bool replace[Natts_pg_type];
+
+ typrel = heap_open(TypeRelationId, RowExclusiveLock);
+
+ memset(values, 0, sizeof(values));
+ memset(nulls, 0, sizeof(nulls));
+ memset(replace, 0, sizeof(replace));
+
+ values[Anum_pg_type_typdefaultcm - 1] = ObjectIdGetDatum(cmoid);
+ nulls[Anum_pg_type_typdefaultcm - 1] = false;
+ replace[Anum_pg_type_typdefaultcm - 1] = true;
+
+ newtup = heap_modify_tuple(oldtup, RelationGetDescr(typrel),
+ values, nulls, replace);
+
+ CatalogTupleUpdate(typrel, &newtup->t_self, newtup);
+
+ heap_freetuple(newtup);
+ heap_close(typrel, RowExclusiveLock);
+
+ if (OidIsValid(oldtype->typdefaultcm))
+ deleteDependencyRecordsForClass(TypeRelationId, typeid,
+ CompressionMethodRelationId,
+ DEPENDENCY_NORMAL);
+
+ if (OidIsValid(cmoid))
+ {
+ ObjectAddress myself,
+ referenced;
+
+ ObjectAddressSet(myself, TypeRelationId, typeid);
+ ObjectAddressSet(referenced, CompressionMethodRelationId, cmoid);
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
+
+ InvokeObjectPostAlterHook(TypeRelationId, typeid, 0);
+ }
+
+ ReleaseSysCache(oldtup);
+}
+
+/*
+ * Execute ALTER TYPE <typeName> <command>, ...
+ */
+void
+AlterType(AlterTypeStmt * stmt)
+{
+ TypeName *typename;
+ Oid typeid;
+ ListCell *lcmd;
+
+ /* Make a TypeName so we can use standard type lookup machinery */
+ typename = makeTypeNameFromNameList(stmt->typeName);
+ typeid = typenameTypeId(NULL, typename);
+
+ foreach(lcmd, stmt->cmds)
+ {
+ AlterTypeCmd *cmd = (AlterTypeCmd *) lfirst(lcmd);
+
+ switch (cmd->cmdtype)
+ {
+ case AT_AlterTypeCompression:
+ AlterTypeDefaultCompression(typeid,
+ (ColumnCompression *) cmd->def);
+ break;
+ default:
+ elog(ERROR, "unknown ALTER TYPE command %d", cmd->cmdtype);
+ }
+ }
+}
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index 076e2a3a40..9af0f11856 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -251,7 +251,7 @@ DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
* false).
*/
address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid, NULL,
- NULL);
+ NULL, NULL);
Assert(address.objectId != InvalidOid);
/* Make the new view relation visible */
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index 47a34a044a..a58af7ee92 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -300,6 +300,9 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc
var->vartypmod != -1))
return false; /* type mismatch */
+ if (OidIsValid(att_tup->attcompression))
+ return false;
+
tlist_item = lnext(tlist_item);
}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index f1bed14e2b..e406828ff2 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2805,6 +2805,7 @@ _copyColumnDef(const ColumnDef *from)
COPY_STRING_FIELD(colname);
COPY_NODE_FIELD(typeName);
+ COPY_NODE_FIELD(compression);
COPY_SCALAR_FIELD(inhcount);
COPY_SCALAR_FIELD(is_local);
COPY_SCALAR_FIELD(is_not_null);
@@ -2823,6 +2824,19 @@ _copyColumnDef(const ColumnDef *from)
return newnode;
}
+static ColumnCompression *
+_copyColumnCompression(const ColumnCompression * from)
+{
+ ColumnCompression *newnode = makeNode(ColumnCompression);
+
+ COPY_STRING_FIELD(methodName);
+ COPY_SCALAR_FIELD(methodOid);
+ COPY_NODE_FIELD(options);
+
+ return newnode;
+}
+
+
static Constraint *
_copyConstraint(const Constraint *from)
{
@@ -4548,6 +4562,28 @@ _copyDropSubscriptionStmt(const DropSubscriptionStmt *from)
return newnode;
}
+static AlterTypeStmt *
+_copyAlterTypeStmt(const AlterTypeStmt * from)
+{
+ AlterTypeStmt *newnode = makeNode(AlterTypeStmt);
+
+ COPY_NODE_FIELD(typeName);
+ COPY_NODE_FIELD(cmds);
+
+ return newnode;
+}
+
+static AlterTypeCmd *
+_copyAlterTypeCmd(const AlterTypeCmd * from)
+{
+ AlterTypeCmd *newnode = makeNode(AlterTypeCmd);
+
+ COPY_SCALAR_FIELD(cmdtype);
+ COPY_NODE_FIELD(def);
+
+ return newnode;
+}
+
/* ****************************************************************
* pg_list.h copy functions
* ****************************************************************
@@ -5456,6 +5492,9 @@ copyObjectImpl(const void *from)
case T_ColumnDef:
retval = _copyColumnDef(from);
break;
+ case T_ColumnCompression:
+ retval = _copyColumnCompression(from);
+ break;
case T_Constraint:
retval = _copyConstraint(from);
break;
@@ -5539,6 +5578,14 @@ copyObjectImpl(const void *from)
retval = _copyForeignKeyCacheInfo(from);
break;
+ case T_AlterTypeStmt:
+ retval = _copyAlterTypeStmt(from);
+ break;
+
+ case T_AlterTypeCmd:
+ retval = _copyAlterTypeCmd(from);
+ break;
+
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(from));
retval = 0; /* keep compiler quiet */
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 8b56b9146a..8591050ac3 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2536,6 +2536,7 @@ _equalColumnDef(const ColumnDef *a, const ColumnDef *b)
{
COMPARE_STRING_FIELD(colname);
COMPARE_NODE_FIELD(typeName);
+ COMPARE_NODE_FIELD(compression);
COMPARE_SCALAR_FIELD(inhcount);
COMPARE_SCALAR_FIELD(is_local);
COMPARE_SCALAR_FIELD(is_not_null);
@@ -2554,6 +2555,16 @@ _equalColumnDef(const ColumnDef *a, const ColumnDef *b)
return true;
}
+static bool
+_equalColumnCompression(const ColumnCompression * a, const ColumnCompression * b)
+{
+ COMPARE_STRING_FIELD(methodName);
+ COMPARE_SCALAR_FIELD(methodOid);
+ COMPARE_NODE_FIELD(options);
+
+ return true;
+}
+
static bool
_equalConstraint(const Constraint *a, const Constraint *b)
{
@@ -2867,6 +2878,24 @@ _equalPartitionCmd(const PartitionCmd *a, const PartitionCmd *b)
return true;
}
+static bool
+_equalAlterTypeStmt(const AlterTypeStmt * a, const AlterTypeStmt * b)
+{
+ COMPARE_NODE_FIELD(typeName);
+ COMPARE_NODE_FIELD(cmds);
+
+ return true;
+}
+
+static bool
+_equalAlterTypeCmd(const AlterTypeCmd * a, const AlterTypeCmd * b)
+{
+ COMPARE_SCALAR_FIELD(cmdtype);
+ COMPARE_NODE_FIELD(def);
+
+ return true;
+}
+
/*
* Stuff from pg_list.h
*/
@@ -3602,6 +3631,9 @@ equal(const void *a, const void *b)
case T_ColumnDef:
retval = _equalColumnDef(a, b);
break;
+ case T_ColumnCompression:
+ retval = _equalColumnCompression(a, b);
+ break;
case T_Constraint:
retval = _equalConstraint(a, b);
break;
@@ -3677,6 +3709,12 @@ equal(const void *a, const void *b)
case T_PartitionCmd:
retval = _equalPartitionCmd(a, b);
break;
+ case T_AlterTypeStmt:
+ retval = _equalAlterTypeStmt(a, b);
+ break;
+ case T_AlterTypeCmd:
+ retval = _equalAlterTypeCmd(a, b);
+ break;
default:
elog(ERROR, "unrecognized node type: %d",
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index e3eb0c5788..c1bea10eff 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -3623,6 +3623,8 @@ raw_expression_tree_walker(Node *node,
if (walker(coldef->typeName, context))
return true;
+ if (walker(coldef->compression, context))
+ return true;
if (walker(coldef->raw_default, context))
return true;
if (walker(coldef->collClause, context))
@@ -3630,6 +3632,14 @@ raw_expression_tree_walker(Node *node,
/* for now, constraints are ignored */
}
break;
+ case T_ColumnCompression:
+ {
+ ColumnCompression *colcmp = (ColumnCompression *) node;
+
+ if (walker(colcmp->options, context))
+ return true;
+ }
+ break;
case T_IndexElem:
{
IndexElem *indelem = (IndexElem *) node;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index b83d919e40..a051b9a01c 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2792,6 +2792,7 @@ _outColumnDef(StringInfo str, const ColumnDef *node)
WRITE_STRING_FIELD(colname);
WRITE_NODE_FIELD(typeName);
+ WRITE_NODE_FIELD(compression);
WRITE_INT_FIELD(inhcount);
WRITE_BOOL_FIELD(is_local);
WRITE_BOOL_FIELD(is_not_null);
@@ -2808,6 +2809,16 @@ _outColumnDef(StringInfo str, const ColumnDef *node)
WRITE_LOCATION_FIELD(location);
}
+static void
+_outColumnCompression(StringInfo str, const ColumnCompression * node)
+{
+ WRITE_NODE_TYPE("COLUMNCOMPRESSION");
+
+ WRITE_STRING_FIELD(methodName);
+ WRITE_OID_FIELD(methodOid);
+ WRITE_NODE_FIELD(options);
+}
+
static void
_outTypeName(StringInfo str, const TypeName *node)
{
@@ -4097,6 +4108,9 @@ outNode(StringInfo str, const void *obj)
case T_ColumnDef:
_outColumnDef(str, obj);
break;
+ case T_ColumnCompression:
+ _outColumnCompression(str, obj);
+ break;
case T_TypeName:
_outTypeName(str, obj);
break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c303818c9b..dba93aa3e2 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -282,6 +282,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
CreateMatViewStmt RefreshMatViewStmt CreateAmStmt
CreatePublicationStmt AlterPublicationStmt
CreateSubscriptionStmt AlterSubscriptionStmt DropSubscriptionStmt
+ AlterTypeStmt
%type <node> select_no_parens select_with_parens select_clause
simple_select values_clause
@@ -290,8 +291,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <ival> add_drop opt_asc_desc opt_nulls_order
%type <node> alter_table_cmd alter_type_cmd opt_collate_clause
- replica_identity partition_cmd
-%type <list> alter_table_cmds alter_type_cmds
+ replica_identity partition_cmd alterTypeCmd
+%type <list> alter_table_cmds alter_type_cmds alterTypeCmds
%type <list> alter_identity_column_option_list
%type <defelt> alter_identity_column_option
@@ -396,6 +397,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
transform_element_list transform_type_list
TriggerTransitions TriggerReferencing
publication_name_list
+ optCompressionParameters
%type <list> group_by_list
%type <node> group_by_item empty_grouping_set rollup_clause cube_clause
@@ -579,6 +581,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <node> partbound_datum PartitionRangeDatum
%type <list> partbound_datum_list range_datum_list
+%type <node> columnCompression optColumnCompression compressedClause
+
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
* They must be listed first so that their numeric codes do not depend on
@@ -611,9 +615,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
CLUSTER COALESCE COLLATE COLLATION COLUMN COLUMNS COMMENT COMMENTS COMMIT
- COMMITTED CONCURRENTLY CONFIGURATION CONFLICT CONNECTION CONSTRAINT
- CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE
- CROSS CSV CUBE CURRENT_P
+ COMMITTED COMPRESSED COMPRESSION CONCURRENTLY CONFIGURATION CONFLICT
+ CONNECTION CONSTRAINT CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY
+ COST CREATE CROSS CSV CUBE CURRENT_P
CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA
CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
@@ -840,6 +844,7 @@ stmt :
| AlterSubscriptionStmt
| AlterTSConfigurationStmt
| AlterTSDictionaryStmt
+ | AlterTypeStmt
| AlterUserMappingStmt
| AnalyzeStmt
| CheckPointStmt
@@ -2165,6 +2170,15 @@ alter_table_cmd:
n->missing_ok = true;
$$ = (Node *)n;
}
+ /* ALTER TABLE <name> ALTER [COLUMN] <colname> SET (NOT COMPRESSED | COMPRESSED <cm> [WITH (<options>)]) */
+ | ALTER opt_column ColId SET columnCompression
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_AlterColumnCompression;
+ n->name = $3;
+ n->def = $5;
+ $$ = (Node *)n;
+ }
/* ALTER TABLE <name> DROP [COLUMN] IF EXISTS <colname> [RESTRICT|CASCADE] */
| DROP opt_column IF_P EXISTS ColId opt_drop_behavior
{
@@ -2733,6 +2747,32 @@ PartitionRangeDatum:
* really variants of the ALTER TABLE subcommands with different spellings
*****************************************************************************/
+AlterTypeStmt:
+ ALTER TYPE_P any_name alterTypeCmds
+ {
+ AlterTypeStmt *n = makeNode(AlterTypeStmt);
+ n->typeName = $3;
+ n->cmds = $4;
+ $$ = (Node *) n;
+ }
+ ;
+
+alterTypeCmds:
+ alterTypeCmd { $$ = list_make1($1); }
+ | alterTypeCmds ',' alterTypeCmd { $$ = lappend($1, $3); }
+ ;
+
+alterTypeCmd:
+ /* ALTER TYPE <name> SET (NOT COMPRESSED | COMPRESSED <cm> [WITH (<options>)]) */
+ SET columnCompression
+ {
+ AlterTypeCmd *n = makeNode(AlterTypeCmd);
+ n->cmdtype = AT_AlterTypeCompression;
+ n->def = $2;
+ $$ = (Node *)n;
+ }
+ ;
+
AlterCompositeTypeStmt:
ALTER TYPE_P any_name alter_type_cmds
{
@@ -3258,11 +3298,12 @@ TypedTableElement:
| TableConstraint { $$ = $1; }
;
-columnDef: ColId Typename create_generic_options ColQualList
+columnDef: ColId Typename optColumnCompression create_generic_options ColQualList
{
ColumnDef *n = makeNode(ColumnDef);
n->colname = $1;
n->typeName = $2;
+ n->compression = (ColumnCompression *) $3;
n->inhcount = 0;
n->is_local = true;
n->is_not_null = false;
@@ -3272,8 +3313,8 @@ columnDef: ColId Typename create_generic_options ColQualList
n->raw_default = NULL;
n->cooked_default = NULL;
n->collOid = InvalidOid;
- n->fdwoptions = $3;
- SplitColQualList($4, &n->constraints, &n->collClause,
+ n->fdwoptions = $4;
+ SplitColQualList($5, &n->constraints, &n->collClause,
yyscanner);
n->location = @1;
$$ = (Node *)n;
@@ -3320,6 +3361,39 @@ columnOptions: ColId ColQualList
}
;
+compressedClause:
+ COMPRESSED name optCompressionParameters
+ {
+ ColumnCompression *n = makeNode(ColumnCompression);
+ n->methodName = $2;
+ n->methodOid = InvalidOid;
+ n->options = (List *) $3;
+ $$ = (Node *) n;
+ }
+ ;
+
+columnCompression:
+ compressedClause |
+ NOT COMPRESSED
+ {
+ ColumnCompression *n = makeNode(ColumnCompression);
+ n->methodName = NULL;
+ n->methodOid = InvalidOid;
+ n->options = NIL;
+ $$ = (Node *) n;
+ }
+ ;
+
+optColumnCompression:
+ compressedClause /* FIXME shift/reduce conflict on NOT COMPRESSED/NOT NULL */
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
+optCompressionParameters:
+ WITH '(' generic_option_list ')' { $$ = $3; }
+ | /*EMPTY*/ { $$ = NIL; }
+ ;
+
ColQualList:
ColQualList ColConstraint { $$ = lappend($1, $2); }
| /*EMPTY*/ { $$ = NIL; }
@@ -5680,6 +5754,15 @@ DefineStmt:
n->if_not_exists = true;
$$ = (Node *)n;
}
+ | CREATE COMPRESSION METHOD any_name HANDLER handler_name
+ {
+ DefineStmt *n = makeNode(DefineStmt);
+ n->kind = OBJECT_COMPRESSION_METHOD;
+ n->args = NIL;
+ n->defnames = $4;
+ n->definition = list_make1(makeDefElem("handler", (Node *) $6, @6));
+ $$ = (Node *) n;
+ }
;
definition: '(' def_list ')' { $$ = $2; }
@@ -6188,6 +6271,7 @@ drop_type_any_name:
/* object types taking name_list */
drop_type_name:
ACCESS METHOD { $$ = OBJECT_ACCESS_METHOD; }
+ | COMPRESSION METHOD { $$ = OBJECT_COMPRESSION_METHOD; }
| EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; }
| EXTENSION { $$ = OBJECT_EXTENSION; }
| FOREIGN DATA_P WRAPPER { $$ = OBJECT_FDW; }
@@ -6251,7 +6335,7 @@ opt_restart_seqs:
* The COMMENT ON statement can take different forms based upon the type of
* the object associated with the comment. The form of the statement is:
*
- * COMMENT ON [ [ ACCESS METHOD | CONVERSION | COLLATION |
+ * COMMENT ON [ [ ACCESS METHOD | COMPRESSION METHOD | CONVERSION | COLLATION |
* DATABASE | DOMAIN |
* EXTENSION | EVENT TRIGGER | FOREIGN DATA WRAPPER |
* FOREIGN TABLE | INDEX | [PROCEDURAL] LANGUAGE |
@@ -6441,6 +6525,7 @@ comment_type_any_name:
/* object types taking name */
comment_type_name:
ACCESS METHOD { $$ = OBJECT_ACCESS_METHOD; }
+ | COMPRESSION METHOD { $$ = OBJECT_COMPRESSION_METHOD; }
| DATABASE { $$ = OBJECT_DATABASE; }
| EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; }
| EXTENSION { $$ = OBJECT_EXTENSION; }
@@ -14649,6 +14734,8 @@ unreserved_keyword:
| COMMENTS
| COMMIT
| COMMITTED
+ | COMPRESSED
+ | COMPRESSION
| CONFIGURATION
| CONFLICT
| CONNECTION
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 655da02c10..78544a8785 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -27,6 +27,7 @@
#include "postgres.h"
#include "access/amapi.h"
+#include "access/compression.h"
#include "access/htup_details.h"
#include "access/reloptions.h"
#include "catalog/dependency.h"
@@ -493,6 +494,49 @@ generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column,
*sname_p = sname;
}
+void
+transformColumnCompression(ColumnDef *column, RangeVar *relation,
+ AlterTableStmt **alterStmt)
+{
+ if (!column->compression && column->typeName)
+ {
+ Type tup = typenameType(NULL, column->typeName, NULL);
+ Oid cmoid = get_base_typdefaultcm(tup);
+
+ ReleaseSysCache(tup);
+
+ if (OidIsValid(cmoid))
+ {
+ column->compression = makeNode(ColumnCompression);
+ column->compression->methodName = NULL;
+ column->compression->methodOid = cmoid;
+ column->compression->options = NIL;
+ }
+ }
+
+ if (column->compression)
+ {
+ AlterTableCmd *cmd;
+
+ cmd = makeNode(AlterTableCmd);
+ cmd->subtype = AT_AlterColumnCompression;
+ cmd->name = column->colname;
+ cmd->def = (Node *) column->compression;
+ cmd->behavior = DROP_RESTRICT;
+ cmd->missing_ok = false;
+
+ if (!*alterStmt)
+ {
+ *alterStmt = makeNode(AlterTableStmt);
+ (*alterStmt)->relation = relation;
+ (*alterStmt)->relkind = OBJECT_TABLE;
+ (*alterStmt)->cmds = NIL;
+ }
+
+ (*alterStmt)->cmds = lappend((*alterStmt)->cmds, cmd);
+ }
+}
+
/*
* transformColumnDefinition -
* transform a single ColumnDef within CREATE TABLE
@@ -793,6 +837,16 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
cxt->alist = lappend(cxt->alist, stmt);
}
+
+ if (cxt->isalter)
+ {
+ AlterTableStmt *stmt = NULL;
+
+ transformColumnCompression(column, cxt->relation, &stmt);
+
+ if (stmt)
+ cxt->alist = lappend(cxt->alist, stmt);
+ }
}
/*
@@ -1002,6 +1056,10 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
def->collOid = attribute->attcollation;
def->constraints = NIL;
def->location = -1;
+ if (attribute->attcompression)
+ def->compression = GetColumnCompressionForAttribute(attribute);
+ else
+ def->compression = NULL;
/*
* Add to column list
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 775477c6cf..a07327fbf7 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -217,6 +217,7 @@ check_xact_readonly(Node *parsetree)
case T_CreateSubscriptionStmt:
case T_AlterSubscriptionStmt:
case T_DropSubscriptionStmt:
+ case T_AlterTypeStmt:
PreventCommandIfReadOnly(CreateCommandTag(parsetree));
PreventCommandIfParallelMode(CreateCommandTag(parsetree));
break;
@@ -998,6 +999,7 @@ ProcessUtilitySlow(ParseState *pstate,
foreach(l, stmts)
{
Node *stmt = (Node *) lfirst(l);
+ Node *alterStmt = NULL;
if (IsA(stmt, CreateStmt))
{
@@ -1008,7 +1010,9 @@ ProcessUtilitySlow(ParseState *pstate,
address = DefineRelation((CreateStmt *) stmt,
RELKIND_RELATION,
InvalidOid, NULL,
- queryString);
+ queryString,
+ &alterStmt);
+
EventTriggerCollectSimpleCommand(address,
secondaryObject,
stmt);
@@ -1042,7 +1046,8 @@ ProcessUtilitySlow(ParseState *pstate,
address = DefineRelation((CreateStmt *) stmt,
RELKIND_FOREIGN_TABLE,
InvalidOid, NULL,
- queryString);
+ queryString,
+ &alterStmt);
CreateForeignTable((CreateForeignTableStmt *) stmt,
address.objectId);
EventTriggerCollectSimpleCommand(address,
@@ -1074,6 +1079,9 @@ ProcessUtilitySlow(ParseState *pstate,
NULL);
}
+ if (alterStmt)
+ lappend(stmts, alterStmt);
+
/* Need CCI between commands */
if (lnext(l) != NULL)
CommandCounterIncrement();
@@ -1283,6 +1291,11 @@ ProcessUtilitySlow(ParseState *pstate,
stmt->definition,
stmt->if_not_exists);
break;
+ case OBJECT_COMPRESSION_METHOD:
+ Assert(stmt->args == NIL);
+ address = DefineCompressionMethod(stmt->defnames,
+ stmt->definition);
+ break;
default:
elog(ERROR, "unrecognized define stmt type: %d",
(int) stmt->kind);
@@ -1643,6 +1656,10 @@ ProcessUtilitySlow(ParseState *pstate,
address = AlterCollation((AlterCollationStmt *) parsetree);
break;
+ case T_AlterTypeStmt:
+ AlterType((AlterTypeStmt *) parsetree);
+ break;
+
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(parsetree));
@@ -1696,6 +1713,11 @@ ExecDropStmt(DropStmt *stmt, bool isTopLevel)
case OBJECT_FOREIGN_TABLE:
RemoveRelations(stmt);
break;
+ case OBJECT_COMPRESSION_METHOD:
+ if (stmt->behavior == DROP_CASCADE)
+ {
+ /* TODO decompress columns instead of their deletion */
+ }
default:
RemoveObjects(stmt);
break;
@@ -2309,6 +2331,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_STATISTIC_EXT:
tag = "DROP STATISTICS";
break;
+ case OBJECT_COMPRESSION_METHOD:
+ tag = "DROP COMPRESSION METHOD";
+ break;
default:
tag = "???";
}
@@ -2412,6 +2437,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_ACCESS_METHOD:
tag = "CREATE ACCESS METHOD";
break;
+ case OBJECT_COMPRESSION_METHOD:
+ tag = "CREATE COMPRESSION METHOD";
+ break;
default:
tag = "???";
}
@@ -2846,6 +2874,10 @@ CreateCommandTag(Node *parsetree)
}
break;
+ case T_AlterTypeStmt:
+ tag = "ALTER TYPE";
+ break;
+
default:
elog(WARNING, "unrecognized node type: %d",
(int) nodeTag(parsetree));
@@ -3291,6 +3323,10 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_DDL;
break;
+ case T_AlterTypeStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
/* already-planned queries */
case T_PlannedStmt:
{
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index be793539a3..a5cfbe3d4c 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -418,3 +418,4 @@ PSEUDOTYPE_DUMMY_IO_FUNCS(internal);
PSEUDOTYPE_DUMMY_IO_FUNCS(opaque);
PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement);
PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray);
+PSEUDOTYPE_DUMMY_IO_FUNCS(compression_handler);
diff --git a/src/backend/utils/adt/tsvector.c b/src/backend/utils/adt/tsvector.c
index 6f66c1f58c..9a2b5c5b1f 100644
--- a/src/backend/utils/adt/tsvector.c
+++ b/src/backend/utils/adt/tsvector.c
@@ -14,11 +14,14 @@
#include "postgres.h"
+#include "access/compression.h"
+#include "catalog/pg_type.h"
#include "libpq/pqformat.h"
#include "tsearch/ts_locale.h"
#include "tsearch/ts_utils.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
+#include "common/pg_lzcompress.h"
typedef struct
{
@@ -548,3 +551,92 @@ tsvectorrecv(PG_FUNCTION_ARGS)
PG_RETURN_TSVECTOR(vec);
}
+
+/*
+ * Compress tsvector using LZ compression.
+ * Instead of trying to compress whole tsvector we compress only text part
+ * here. This approach gives more compressibility for tsvectors.
+ */
+static struct varlena *
+tsvector_compress(AttributeCompression *ac, const struct varlena *data)
+{
+ char *tmp;
+ int32 valsize = VARSIZE_ANY_EXHDR(data);
+ int32 len = valsize + VARHDRSZ_CUSTOM_COMPRESSED,
+ lenc;
+
+ char *arr = VARDATA(data),
+ *str = STRPTR((TSVector) data);
+ int32 arrsize = str - arr;
+
+ Assert(!VARATT_IS_COMPRESSED(data));
+ tmp = palloc0(len);
+
+ /* we try to compress string part of tsvector first */
+ lenc = pglz_compress(str,
+ valsize - arrsize,
+ tmp + VARHDRSZ_CUSTOM_COMPRESSED + arrsize,
+ PGLZ_strategy_default);
+
+ if (lenc >= 0)
+ {
+ /* tsvector is compressible, copy size and entries to its beginning */
+ memcpy(tmp + VARHDRSZ_CUSTOM_COMPRESSED, arr, arrsize);
+ SET_VARSIZE_COMPRESSED(tmp, arrsize + lenc + VARHDRSZ_CUSTOM_COMPRESSED);
+ return (struct varlena *) tmp;
+ }
+
+ pfree(tmp);
+ return NULL;
+}
+
+static void
+tsvector_configure(Form_pg_attribute attr, List *options)
+{
+ if (options != NIL)
+ elog(ERROR, "the compression method for tsvector doesn't take any options");
+}
+
+static struct varlena *
+tsvector_decompress(AttributeCompression *ac, const struct varlena *data)
+{
+ char *tmp,
+ *raw_data = (char *) data + VARHDRSZ_CUSTOM_COMPRESSED;
+ int32 count,
+ arrsize,
+ len = VARRAWSIZE_4B_C(data) + VARHDRSZ;
+
+ Assert(VARATT_IS_CUSTOM_COMPRESSED(data));
+ tmp = palloc0(len);
+ SET_VARSIZE(tmp, len);
+ count = *((uint32 *) raw_data);
+ arrsize = sizeof(uint32) + count * sizeof(WordEntry);
+ memcpy(VARDATA(tmp), raw_data, arrsize);
+
+ if (pglz_decompress(raw_data + arrsize,
+ VARSIZE(data) - VARHDRSZ_CUSTOM_COMPRESSED - arrsize,
+ VARDATA(tmp) + arrsize,
+ VARRAWSIZE_4B_C(data) - arrsize) < 0)
+ elog(ERROR, "compressed tsvector is corrupted");
+
+ return (struct varlena *) tmp;
+}
+
+Datum
+tsvector_compression_handler(PG_FUNCTION_ARGS)
+{
+ CompressionMethodOpArgs *opargs = (CompressionMethodOpArgs *)
+ PG_GETARG_POINTER(0);
+ CompressionMethodRoutine *cmr = makeNode(CompressionMethodRoutine);
+ Oid typeid = opargs->typeid;
+
+ if (OidIsValid(typeid) && typeid != TSVECTOROID)
+ elog(ERROR, "unexpected type %d for tsvector compression handler", typeid);
+
+ cmr->configure = tsvector_configure;
+ cmr->drop = NULL;
+ cmr->compress = tsvector_compress;
+ cmr->decompress = tsvector_decompress;
+
+ PG_RETURN_POINTER(cmr);
+}
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index b7a14dc87e..ae7320707c 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -2276,16 +2276,12 @@ getBaseType(Oid typid)
}
/*
- * getBaseTypeAndTypmod
- * If the given type is a domain, return its base type and typmod;
- * otherwise return the type's own OID, and leave *typmod unchanged.
- *
* Note that the "applied typmod" should be -1 for every domain level
* above the bottommost; therefore, if the passed-in typid is indeed
* a domain, *typmod should be -1.
*/
-Oid
-getBaseTypeAndTypmod(Oid typid, int32 *typmod)
+static inline HeapTuple
+getBaseTypeTuple(Oid *typid, int32 *typmod)
{
/*
* We loop to find the bottom base type in a stack of domains.
@@ -2295,24 +2291,33 @@ getBaseTypeAndTypmod(Oid typid, int32 *typmod)
HeapTuple tup;
Form_pg_type typTup;
- tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*typid));
if (!HeapTupleIsValid(tup))
- elog(ERROR, "cache lookup failed for type %u", typid);
+ elog(ERROR, "cache lookup failed for type %u", *typid);
typTup = (Form_pg_type) GETSTRUCT(tup);
if (typTup->typtype != TYPTYPE_DOMAIN)
- {
/* Not a domain, so done */
- ReleaseSysCache(tup);
- break;
- }
+ return tup;
Assert(*typmod == -1);
- typid = typTup->typbasetype;
+ *typid = typTup->typbasetype;
*typmod = typTup->typtypmod;
ReleaseSysCache(tup);
}
+}
+/*
+ * getBaseTypeAndTypmod
+ * If the given type is a domain, return its base type and typmod;
+ * otherwise return the type's own OID, and leave *typmod unchanged.
+ */
+Oid
+getBaseTypeAndTypmod(Oid typid, int32 *typmod)
+{
+ HeapTuple tup = getBaseTypeTuple(&typid, typmod);
+
+ ReleaseSysCache(tup);
return typid;
}
@@ -2808,6 +2813,39 @@ type_is_collatable(Oid typid)
return OidIsValid(get_typcollation(typid));
}
+/*
+ * get_base_typdefaultcm
+ *
+ * Given the type tuple, return the base type's typdefaultcm attribute.
+ */
+Oid
+get_base_typdefaultcm(HeapTuple typtup)
+{
+ Oid typid;
+ Oid base;
+ Oid cm = InvalidOid;
+
+ for (typid = (Oid) -1; !OidIsValid(cm) && OidIsValid(typid); typid = base)
+ {
+ Form_pg_type type;
+
+ if (typid != (Oid) -1)
+ {
+ typtup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ if (!HeapTupleIsValid(typtup))
+ elog(ERROR, "cache lookup failed for type %u", typid);
+ }
+
+ type = (Form_pg_type) GETSTRUCT(typtup);
+ base = type->typtype == TYPTYPE_DOMAIN ? type->typbasetype : InvalidOid;
+ cm = type->typdefaultcm;
+
+ if (typid != (Oid) -1)
+ ReleaseSysCache(typtup);
+ }
+
+ return cm;
+}
/* ---------- STATISTICS CACHE ---------- */
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index b8e37809b0..f8849c89e7 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -30,6 +30,7 @@
#include <fcntl.h>
#include <unistd.h>
+#include "access/compression.h"
#include "access/htup_details.h"
#include "access/multixact.h"
#include "access/nbtree.h"
@@ -76,6 +77,7 @@
#include "storage/smgr.h"
#include "utils/array.h"
#include "utils/builtins.h"
+#include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
@@ -565,6 +567,7 @@ RelationBuildTupleDesc(Relation relation)
attrdef[ndef].adbin = NULL;
ndef++;
}
+
need--;
if (need == 0)
break;
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index fcbb683a99..634482e326 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -31,6 +31,8 @@
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
@@ -309,6 +311,39 @@ static const struct cachedesc cacheinfo[] = {
},
8
},
+ {CompressionMethodRelationId, /* COMPRESSIONMETHODOID */
+ CompressionMethodOidIndexId,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 8
+ },
+ {CompressionMethodRelationId, /* COMPRESSIONMETHODNAME */
+ CompressionMethodNameIndexId,
+ 1,
+ {
+ Anum_pg_compression_cmname,
+ 0,
+ 0,
+ 0
+ },
+ 8
+ },
+ {CompressionOptRelationId, /* COMPRESSIONOPTIONSOID */
+ CompressionOptionsOidIndexId,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 8,
+ },
{ConversionRelationId, /* CONDEFAULT */
ConversionDefaultIndexId,
4,
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 4b47951de1..2d6827f1f3 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -54,6 +54,7 @@ static DumpableObject **oprinfoindex;
static DumpableObject **collinfoindex;
static DumpableObject **nspinfoindex;
static DumpableObject **extinfoindex;
+static DumpableObject **cminfoindex;
static int numTables;
static int numTypes;
static int numFuncs;
@@ -61,6 +62,7 @@ static int numOperators;
static int numCollations;
static int numNamespaces;
static int numExtensions;
+static int numCompressionMethods;
/* This is an array of object identities, not actual DumpableObjects */
static ExtensionMemberId *extmembers;
@@ -93,6 +95,8 @@ getSchemaData(Archive *fout, int *numTablesPtr)
NamespaceInfo *nspinfo;
ExtensionInfo *extinfo;
InhInfo *inhinfo;
+ CompressionMethodInfo *cminfo;
+
int numAggregates;
int numInherits;
int numRules;
@@ -289,6 +293,11 @@ getSchemaData(Archive *fout, int *numTablesPtr)
write_msg(NULL, "reading subscriptions\n");
getSubscriptions(fout);
+ if (g_verbose)
+ write_msg(NULL, "reading compression methods\n");
+ cminfo = getCompressionMethods(fout, &numCompressionMethods);
+ cminfoindex = buildIndexArray(cminfo, numCompressionMethods, sizeof(CompressionMethodInfo));
+
*numTablesPtr = numTables;
return tblinfo;
}
@@ -827,6 +836,17 @@ findExtensionByOid(Oid oid)
return (ExtensionInfo *) findObjectByOid(oid, extinfoindex, numExtensions);
}
+/*
+ * findCompressionMethodByOid
+ * finds the entry (in cminfo) of the compression method with the given oid
+ * returns NULL if not found
+ */
+CompressionMethodInfo *
+findCompressionMethodByOid(Oid oid)
+{
+ return (CompressionMethodInfo *) findObjectByOid(oid, cminfoindex,
+ numCompressionMethods);
+}
/*
* setExtensionMembership
diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index ce3100f09d..6b962a0ba0 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -77,6 +77,7 @@ typedef struct _restoreOptions
int no_publications; /* Skip publication entries */
int no_security_labels; /* Skip security label entries */
int no_subscriptions; /* Skip subscription entries */
+ int no_compression_methods; /* Skip compression methods */
int strict_names;
const char *filename;
@@ -149,6 +150,7 @@ typedef struct _dumpOptions
int no_security_labels;
int no_publications;
int no_subscriptions;
+ int no_compression_methods;
int no_synchronized_snapshots;
int no_unlogged_table_data;
int serializable_deferrable;
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 75f08cd792..5cd9f74780 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -358,6 +358,7 @@ main(int argc, char **argv)
{"no-synchronized-snapshots", no_argument, &dopt.no_synchronized_snapshots, 1},
{"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
{"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
+ {"no-compression-methods", no_argument, &dopt.no_compression_methods, 1},
{"no-sync", no_argument, NULL, 7},
{NULL, 0, NULL, 0}
@@ -1500,6 +1501,11 @@ selectDumpableType(TypeInfo *tyinfo, Archive *fout)
/* Dump based on if the contents of the namespace are being dumped */
tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump_contains;
+
+ /* Add flag of compression is type was altered */
+ if (tyinfo->typdefaultcm)
+ tyinfo->dobj.dump |= DUMP_COMPONENT_MODIFICATION;
+
}
/*
@@ -3946,6 +3952,99 @@ dumpSubscription(Archive *fout, SubscriptionInfo *subinfo)
destroyPQExpBuffer(query);
}
+/*
+ * getCompressionMethods
+ * get information about compression methods
+ */
+CompressionMethodInfo *
+getCompressionMethods(Archive *fout, int *numMethods)
+{
+ DumpOptions *dopt = fout->dopt;
+ PQExpBuffer query;
+ PGresult *res;
+ CompressionMethodInfo *cminfo;
+ int i_tableoid;
+ int i_oid;
+ int i_handler;
+ int i_name;
+ int i,
+ ntups;
+
+ if (dopt->no_compression_methods || fout->remoteVersion < 110000)
+ return NULL;
+
+ query = createPQExpBuffer();
+ resetPQExpBuffer(query);
+
+ /* Get the compression methods in current database. */
+ appendPQExpBuffer(query,
+ "SELECT c.tableoid, c.oid, c.cmname, c.cmhandler "
+ "FROM pg_catalog.pg_compression c");
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_name = PQfnumber(res, "cmname");
+ i_handler = PQfnumber(res, "cmhandler");
+
+ cminfo = pg_malloc(ntups * sizeof(CompressionMethodInfo));
+
+ for (i = 0; i < ntups; i++)
+ {
+ cminfo[i].dobj.objType = DO_COMPRESSION_METHOD;
+ cminfo[i].dobj.catId.tableoid =
+ atooid(PQgetvalue(res, i, i_tableoid));
+ cminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&cminfo[i].dobj);
+ cminfo[i].cmhandler = pg_strdup(PQgetvalue(res, i, i_handler));
+ cminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_name));
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(cminfo[i].dobj), fout);
+ }
+ if (numMethods)
+ *numMethods = ntups;
+
+ PQclear(res);
+ destroyPQExpBuffer(query);
+
+ return cminfo;
+}
+
+/*
+ * dumpCompressionMethod
+ * dump the definition of the given compression method
+ */
+static void
+dumpCompressionMethod(Archive *fout, CompressionMethodInfo *cminfo)
+{
+ PQExpBuffer query;
+
+ if (!(cminfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
+ return;
+
+ query = createPQExpBuffer();
+ appendPQExpBuffer(query, "CREATE COMPRESSION METHOD %s HANDLER",
+ fmtId(cminfo->dobj.name));
+ appendPQExpBuffer(query, " %s;\n", fmtId(cminfo->cmhandler));
+
+ ArchiveEntry(fout,
+ cminfo->dobj.catId,
+ cminfo->dobj.dumpId,
+ cminfo->dobj.name,
+ NULL,
+ NULL,
+ "", false,
+ "COMPRESSION METHOD", SECTION_PRE_DATA,
+ query->data, "", NULL,
+ NULL, 0,
+ NULL, NULL);
+
+ destroyPQExpBuffer(query);
+}
+
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
@@ -4422,6 +4521,7 @@ getTypes(Archive *fout, int *numTypes)
int i_typtype;
int i_typisdefined;
int i_isarray;
+ int i_typdefaultcm;
/*
* we include even the built-in types because those may be used as array
@@ -4442,7 +4542,48 @@ getTypes(Archive *fout, int *numTypes)
/* Make sure we are in proper schema */
selectSourceSchema(fout, "pg_catalog");
- if (fout->remoteVersion >= 90600)
+ if (fout->remoteVersion >= 110000)
+ {
+ PQExpBuffer acl_subquery = createPQExpBuffer();
+ PQExpBuffer racl_subquery = createPQExpBuffer();
+ PQExpBuffer initacl_subquery = createPQExpBuffer();
+ PQExpBuffer initracl_subquery = createPQExpBuffer();
+
+ buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
+ initracl_subquery, "t.typacl", "t.typowner", "'T'",
+ dopt->binary_upgrade);
+
+ appendPQExpBuffer(query, "SELECT t.tableoid, t.oid, t.typname, "
+ "t.typnamespace, "
+ "t.typdefaultcm, "
+ "%s AS typacl, "
+ "%s AS rtypacl, "
+ "%s AS inittypacl, "
+ "%s AS initrtypacl, "
+ "(%s t.typowner) AS rolname, "
+ "t.typelem, t.typrelid, "
+ "CASE WHEN t.typrelid = 0 THEN ' '::\"char\" "
+ "ELSE (SELECT relkind FROM pg_class WHERE oid = t.typrelid) END AS typrelkind, "
+ "t.typtype, t.typisdefined, "
+ "t.typname[0] = '_' AND t.typelem != 0 AND "
+ "(SELECT typarray FROM pg_type te WHERE oid = t.typelem) = t.oid AS isarray "
+ "FROM pg_type t "
+ "LEFT JOIN pg_init_privs pip ON "
+ "(t.oid = pip.objoid "
+ "AND pip.classoid = 'pg_type'::regclass "
+ "AND pip.objsubid = 0) ",
+ acl_subquery->data,
+ racl_subquery->data,
+ initacl_subquery->data,
+ initracl_subquery->data,
+ username_subquery);
+
+ destroyPQExpBuffer(acl_subquery);
+ destroyPQExpBuffer(racl_subquery);
+ destroyPQExpBuffer(initacl_subquery);
+ destroyPQExpBuffer(initracl_subquery);
+ }
+ else if (fout->remoteVersion >= 90600)
{
PQExpBuffer acl_subquery = createPQExpBuffer();
PQExpBuffer racl_subquery = createPQExpBuffer();
@@ -4455,6 +4596,7 @@ getTypes(Archive *fout, int *numTypes)
appendPQExpBuffer(query, "SELECT t.tableoid, t.oid, t.typname, "
"t.typnamespace, "
+ "NULL AS typdefaultcm, "
"%s AS typacl, "
"%s AS rtypacl, "
"%s AS inittypacl, "
@@ -4487,6 +4629,7 @@ getTypes(Archive *fout, int *numTypes)
appendPQExpBuffer(query, "SELECT tableoid, oid, typname, "
"typnamespace, typacl, NULL as rtypacl, "
"NULL AS inittypacl, NULL AS initrtypacl, "
+ "NULL AS typdefaultcm, "
"(%s typowner) AS rolname, "
"typelem, typrelid, "
"CASE WHEN typrelid = 0 THEN ' '::\"char\" "
@@ -4502,6 +4645,7 @@ getTypes(Archive *fout, int *numTypes)
appendPQExpBuffer(query, "SELECT tableoid, oid, typname, "
"typnamespace, NULL AS typacl, NULL as rtypacl, "
"NULL AS inittypacl, NULL AS initrtypacl, "
+ "NULL AS typdefaultcm, "
"(%s typowner) AS rolname, "
"typelem, typrelid, "
"CASE WHEN typrelid = 0 THEN ' '::\"char\" "
@@ -4517,6 +4661,7 @@ getTypes(Archive *fout, int *numTypes)
appendPQExpBuffer(query, "SELECT tableoid, oid, typname, "
"typnamespace, NULL AS typacl, NULL as rtypacl, "
"NULL AS inittypacl, NULL AS initrtypacl, "
+ "NULL AS typdefaultcm, "
"(%s typowner) AS rolname, "
"typelem, typrelid, "
"CASE WHEN typrelid = 0 THEN ' '::\"char\" "
@@ -4548,6 +4693,7 @@ getTypes(Archive *fout, int *numTypes)
i_typtype = PQfnumber(res, "typtype");
i_typisdefined = PQfnumber(res, "typisdefined");
i_isarray = PQfnumber(res, "isarray");
+ i_typdefaultcm = PQfnumber(res, "typdefaultcm");
for (i = 0; i < ntups; i++)
{
@@ -4569,6 +4715,7 @@ getTypes(Archive *fout, int *numTypes)
tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
tyinfo[i].shellType = NULL;
+ tyinfo[i].typdefaultcm = atooid(PQgetvalue(res, i, i_typdefaultcm));
if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
tyinfo[i].isDefined = true;
@@ -7853,6 +8000,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
int i_attoptions;
int i_attcollation;
int i_attfdwoptions;
+ int i_attcmoptions;
+ int i_attcmname;
PGresult *res;
int ntups;
bool hasdefaults;
@@ -7888,7 +8037,48 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
resetPQExpBuffer(q);
- if (fout->remoteVersion >= 100000)
+ if (fout->remoteVersion >= 110000)
+ {
+ /*
+ * attcompression is new in version 11
+ */
+ appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, "
+ "a.attstattarget, a.attstorage, t.typstorage, "
+ "a.attnotnull, a.atthasdef, a.attisdropped, "
+ "a.attlen, a.attalign, a.attislocal, "
+ "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
+ "array_to_string(a.attoptions, ', ') AS attoptions, "
+ "CASE WHEN a.attcollation <> t.typcollation "
+ "THEN a.attcollation ELSE 0 END AS attcollation, "
+ "a.attidentity, "
+ /* fdw options */
+ "pg_catalog.array_to_string(ARRAY("
+ "SELECT pg_catalog.quote_ident(option_name) || "
+ "' ' || pg_catalog.quote_literal(option_value) "
+ "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
+ "ORDER BY option_name"
+ "), E',\n ') AS attfdwoptions, "
+ /* compression options */
+ "pg_catalog.array_to_string(ARRAY("
+ "SELECT pg_catalog.quote_ident(option_name) || "
+ "' ' || pg_catalog.quote_literal(option_value) "
+ "FROM pg_catalog.pg_options_to_table(c.cmoptions) "
+ "ORDER BY option_name"
+ "), E',\n ') AS attcmoptions, "
+ "cm.cmname AS attcmname "
+ /* FROM */
+ "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
+ "ON a.atttypid = t.oid "
+ "LEFT JOIN pg_catalog.pg_compression_opt c "
+ "ON a.attcompression = c.oid "
+ "LEFT JOIN pg_catalog.pg_compression cm "
+ "ON c.cmid = cm.oid "
+ "WHERE a.attrelid = '%u'::pg_catalog.oid "
+ "AND a.attnum > 0::pg_catalog.int2 "
+ "ORDER BY a.attnum",
+ tbinfo->dobj.catId.oid);
+ }
+ else if (fout->remoteVersion >= 100000)
{
/*
* attidentity is new in version 10.
@@ -7907,9 +8097,13 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"' ' || pg_catalog.quote_literal(option_value) "
"FROM pg_catalog.pg_options_to_table(attfdwoptions) "
"ORDER BY option_name"
- "), E',\n ') AS attfdwoptions "
+ "), E',\n ') AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
+ "LEFT JOIN pg_catalog.pg_compression_opt c "
+ "ON a.attcompression = c.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
"AND a.attnum > 0::pg_catalog.int2 "
"ORDER BY a.attnum",
@@ -7933,7 +8127,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"' ' || pg_catalog.quote_literal(option_value) "
"FROM pg_catalog.pg_options_to_table(attfdwoptions) "
"ORDER BY option_name"
- "), E',\n ') AS attfdwoptions "
+ "), E',\n ') AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -7957,7 +8153,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"array_to_string(a.attoptions, ', ') AS attoptions, "
"CASE WHEN a.attcollation <> t.typcollation "
"THEN a.attcollation ELSE 0 END AS attcollation, "
- "NULL AS attfdwoptions "
+ "NULL AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -7975,7 +8173,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
"array_to_string(a.attoptions, ', ') AS attoptions, "
"0 AS attcollation, "
- "NULL AS attfdwoptions "
+ "NULL AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -7992,7 +8192,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"a.attlen, a.attalign, a.attislocal, "
"pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
"'' AS attoptions, 0 AS attcollation, "
- "NULL AS attfdwoptions "
+ "NULL AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -8022,6 +8224,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
i_attoptions = PQfnumber(res, "attoptions");
i_attcollation = PQfnumber(res, "attcollation");
i_attfdwoptions = PQfnumber(res, "attfdwoptions");
+ i_attcmname = PQfnumber(res, "attcmname");
+ i_attcmoptions = PQfnumber(res, "attcmoptions");
tbinfo->numatts = ntups;
tbinfo->attnames = (char **) pg_malloc(ntups * sizeof(char *));
@@ -8038,6 +8242,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
tbinfo->attoptions = (char **) pg_malloc(ntups * sizeof(char *));
tbinfo->attcollation = (Oid *) pg_malloc(ntups * sizeof(Oid));
tbinfo->attfdwoptions = (char **) pg_malloc(ntups * sizeof(char *));
+ tbinfo->attcmoptions = (char **) pg_malloc(ntups * sizeof(char *));
+ tbinfo->attcmnames = (char **) pg_malloc(ntups * sizeof(char *));
tbinfo->notnull = (bool *) pg_malloc(ntups * sizeof(bool));
tbinfo->inhNotNull = (bool *) pg_malloc(ntups * sizeof(bool));
tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(ntups * sizeof(AttrDefInfo *));
@@ -8065,6 +8271,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, j, i_attoptions));
tbinfo->attcollation[j] = atooid(PQgetvalue(res, j, i_attcollation));
tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, j, i_attfdwoptions));
+ tbinfo->attcmoptions[j] = pg_strdup(PQgetvalue(res, j, i_attcmoptions));
+ tbinfo->attcmnames[j] = pg_strdup(PQgetvalue(res, j, i_attcmname));
tbinfo->attrdefs[j] = NULL; /* fix below */
if (PQgetvalue(res, j, i_atthasdef)[0] == 't')
hasdefaults = true;
@@ -9554,6 +9762,9 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
case DO_SUBSCRIPTION:
dumpSubscription(fout, (SubscriptionInfo *) dobj);
break;
+ case DO_COMPRESSION_METHOD:
+ dumpCompressionMethod(fout, (CompressionMethodInfo *) dobj);
+ break;
case DO_PRE_DATA_BOUNDARY:
case DO_POST_DATA_BOUNDARY:
/* never dumped, nothing to do */
@@ -10398,6 +10609,26 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo)
NULL, 0,
NULL, NULL);
+ if ((tyinfo->dobj.dump & DUMP_COMPONENT_MODIFICATION) && tyinfo->typdefaultcm)
+ {
+ PQExpBuffer alterq = createPQExpBuffer();
+ CompressionMethodInfo *cminfo = findCompressionMethodByOid(tyinfo->typdefaultcm);
+
+ appendPQExpBuffer(alterq, "ALTER TYPE %s SET COMPRESSED %s,", qtypname,
+ cminfo->dobj.name);
+ ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
+ tyinfo->dobj.name,
+ tyinfo->dobj.namespace->dobj.name,
+ NULL,
+ tyinfo->rolname, false,
+ "TYPE CM", SECTION_PRE_DATA,
+ alterq->data, "", NULL,
+ NULL, 0,
+ NULL, NULL);
+
+ destroyPQExpBuffer(alterq);
+ }
+
/* Dump Type Comments and Security Labels */
if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
dumpComment(fout, labelq->data,
@@ -15379,6 +15610,15 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
}
}
+ if (tbinfo->attcmnames[j] && strlen(tbinfo->attcmnames[j]))
+ {
+ appendPQExpBuffer(q, " COMPRESSED %s",
+ fmtId(tbinfo->attcmnames[j]));
+ if (nonemptyReloptions(tbinfo->attcmoptions[j]))
+ appendPQExpBuffer(q, " WITH %s",
+ fmtId(tbinfo->attcmoptions[j]));
+ }
+
if (has_default)
appendPQExpBuffer(q, " DEFAULT %s",
tbinfo->attrdefs[j]->adef_expr);
@@ -17641,6 +17881,7 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
case DO_FOREIGN_SERVER:
case DO_TRANSFORM:
case DO_BLOB:
+ case DO_COMPRESSION_METHOD:
/* Pre-data objects: must come before the pre-data boundary */
addObjectDependency(preDataBound, dobj->dumpId);
break;
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index e7593e6da7..5fc612ee5d 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -83,7 +83,8 @@ typedef enum
DO_POLICY,
DO_PUBLICATION,
DO_PUBLICATION_REL,
- DO_SUBSCRIPTION
+ DO_SUBSCRIPTION,
+ DO_COMPRESSION_METHOD
} DumpableObjectType;
/* component types of an object which can be selected for dumping */
@@ -96,6 +97,7 @@ typedef uint32 DumpComponents; /* a bitmask of dump object components */
#define DUMP_COMPONENT_ACL (1 << 4)
#define DUMP_COMPONENT_POLICY (1 << 5)
#define DUMP_COMPONENT_USERMAP (1 << 6)
+#define DUMP_COMPONENT_MODIFICATION (1 << 7)
#define DUMP_COMPONENT_ALL (0xFFFF)
/*
@@ -179,6 +181,7 @@ typedef struct _typeInfo
char typtype; /* 'b', 'c', etc */
bool isArray; /* true if auto-generated array type */
bool isDefined; /* true if typisdefined */
+ Oid typdefaultcm;
/* If needed, we'll create a "shell type" entry for it; link that here: */
struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */
/* If it's a domain, we store links to its constraints here: */
@@ -315,6 +318,8 @@ typedef struct _tableInfo
char **attoptions; /* per-attribute options */
Oid *attcollation; /* per-attribute collation selection */
char **attfdwoptions; /* per-attribute fdw options */
+ char **attcmoptions; /* per-attribute compression options */
+ char **attcmnames; /* per-attribute compression method names */
bool *notnull; /* NOT NULL constraints on attributes */
bool *inhNotNull; /* true if NOT NULL is inherited */
struct _attrDefInfo **attrdefs; /* DEFAULT expressions */
@@ -611,6 +616,13 @@ typedef struct _SubscriptionInfo
char *subpublications;
} SubscriptionInfo;
+/* The CompressionMethodInfo struct is used to represent compression method */
+typedef struct _CompressionMethodInfo
+{
+ DumpableObject dobj;
+ char *cmhandler;
+} CompressionMethodInfo;
+
/*
* We build an array of these with an entry for each object that is an
* extension member according to pg_depend.
@@ -654,6 +666,7 @@ extern OprInfo *findOprByOid(Oid oid);
extern CollInfo *findCollationByOid(Oid oid);
extern NamespaceInfo *findNamespaceByOid(Oid oid);
extern ExtensionInfo *findExtensionByOid(Oid oid);
+extern CompressionMethodInfo *findCompressionMethodByOid(Oid oid);
extern void setExtensionMembership(ExtensionMemberId *extmems, int nextmems);
extern ExtensionInfo *findOwningExtension(CatalogId catalogId);
@@ -711,5 +724,7 @@ extern void getPublications(Archive *fout);
extern void getPublicationTables(Archive *fout, TableInfo tblinfo[],
int numTables);
extern void getSubscriptions(Archive *fout);
+extern CompressionMethodInfo *getCompressionMethods(Archive *fout,
+ int *numMethods);
#endif /* PG_DUMP_H */
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index 5044a76787..7195f54cdc 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -40,47 +40,48 @@ static const int dbObjectTypePriority[] =
{
1, /* DO_NAMESPACE */
4, /* DO_EXTENSION */
- 5, /* DO_TYPE */
- 5, /* DO_SHELL_TYPE */
- 6, /* DO_FUNC */
- 7, /* DO_AGG */
- 8, /* DO_OPERATOR */
- 8, /* DO_ACCESS_METHOD */
- 9, /* DO_OPCLASS */
- 9, /* DO_OPFAMILY */
- 3, /* DO_COLLATION */
- 11, /* DO_CONVERSION */
- 18, /* DO_TABLE */
- 20, /* DO_ATTRDEF */
- 28, /* DO_INDEX */
- 29, /* DO_STATSEXT */
- 30, /* DO_RULE */
- 31, /* DO_TRIGGER */
- 27, /* DO_CONSTRAINT */
- 32, /* DO_FK_CONSTRAINT */
- 2, /* DO_PROCLANG */
- 10, /* DO_CAST */
- 23, /* DO_TABLE_DATA */
- 24, /* DO_SEQUENCE_SET */
- 19, /* DO_DUMMY_TYPE */
- 12, /* DO_TSPARSER */
- 14, /* DO_TSDICT */
- 13, /* DO_TSTEMPLATE */
- 15, /* DO_TSCONFIG */
- 16, /* DO_FDW */
- 17, /* DO_FOREIGN_SERVER */
- 32, /* DO_DEFAULT_ACL */
- 3, /* DO_TRANSFORM */
- 21, /* DO_BLOB */
- 25, /* DO_BLOB_DATA */
- 22, /* DO_PRE_DATA_BOUNDARY */
- 26, /* DO_POST_DATA_BOUNDARY */
- 33, /* DO_EVENT_TRIGGER */
- 38, /* DO_REFRESH_MATVIEW */
- 34, /* DO_POLICY */
- 35, /* DO_PUBLICATION */
- 36, /* DO_PUBLICATION_REL */
- 37 /* DO_SUBSCRIPTION */
+ 6, /* DO_TYPE */
+ 6, /* DO_SHELL_TYPE */
+ 7, /* DO_FUNC */
+ 8, /* DO_AGG */
+ 9, /* DO_OPERATOR */
+ 9, /* DO_ACCESS_METHOD */
+ 10, /* DO_OPCLASS */
+ 10, /* DO_OPFAMILY */
+ 4, /* DO_COLLATION */
+ 12, /* DO_CONVERSION */
+ 19, /* DO_TABLE */
+ 21, /* DO_ATTRDEF */
+ 29, /* DO_INDEX */
+ 30, /* DO_STATSEXT */
+ 31, /* DO_RULE */
+ 32, /* DO_TRIGGER */
+ 28, /* DO_CONSTRAINT */
+ 33, /* DO_FK_CONSTRAINT */
+ 3, /* DO_PROCLANG */
+ 11, /* DO_CAST */
+ 24, /* DO_TABLE_DATA */
+ 25, /* DO_SEQUENCE_SET */
+ 20, /* DO_DUMMY_TYPE */
+ 13, /* DO_TSPARSER */
+ 15, /* DO_TSDICT */
+ 14, /* DO_TSTEMPLATE */
+ 16, /* DO_TSCONFIG */
+ 17, /* DO_FDW */
+ 18, /* DO_FOREIGN_SERVER */
+ 33, /* DO_DEFAULT_ACL */
+ 4, /* DO_TRANSFORM */
+ 22, /* DO_BLOB */
+ 26, /* DO_BLOB_DATA */
+ 23, /* DO_PRE_DATA_BOUNDARY */
+ 27, /* DO_POST_DATA_BOUNDARY */
+ 34, /* DO_EVENT_TRIGGER */
+ 39, /* DO_REFRESH_MATVIEW */
+ 35, /* DO_POLICY */
+ 36, /* DO_PUBLICATION */
+ 37, /* DO_PUBLICATION_REL */
+ 38, /* DO_SUBSCRIPTION */
+ 5 /* DO_COMPRESSION_METHOD */
};
static DumpId preDataBoundId;
@@ -1436,6 +1437,11 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
"POST-DATA BOUNDARY (ID %d)",
obj->dumpId);
return;
+ case DO_COMPRESSION_METHOD:
+ snprintf(buf, bufsize,
+ "COMPRESSION METHOD %s (ID %d OID %u)",
+ obj->name, obj->dumpId, obj->catId.oid);
+ return;
}
/* shouldn't get here */
snprintf(buf, bufsize,
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 41c5ff89b7..6b72ccf9ea 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -77,6 +77,7 @@ static int use_setsessauth = 0;
static int no_publications = 0;
static int no_security_labels = 0;
static int no_subscriptions = 0;
+static int no_compression_methods = 0;
static int no_unlogged_table_data = 0;
static int no_role_passwords = 0;
static int server_version;
@@ -137,6 +138,7 @@ main(int argc, char *argv[])
{"no-role-passwords", no_argument, &no_role_passwords, 1},
{"no-security-labels", no_argument, &no_security_labels, 1},
{"no-subscriptions", no_argument, &no_subscriptions, 1},
+ {"no-compression-methods", no_argument, &no_compression_methods, 1},
{"no-sync", no_argument, NULL, 4},
{"no-unlogged-table-data", no_argument, &no_unlogged_table_data, 1},
@@ -405,6 +407,8 @@ main(int argc, char *argv[])
appendPQExpBufferStr(pgdumpopts, " --no-security-labels");
if (no_subscriptions)
appendPQExpBufferStr(pgdumpopts, " --no-subscriptions");
+ if (no_compression_methods)
+ appendPQExpBufferStr(pgdumpopts, " --no-compression-methods");
if (no_unlogged_table_data)
appendPQExpBufferStr(pgdumpopts, " --no-unlogged-table-data");
@@ -628,6 +632,7 @@ help(void)
printf(_(" --no-role-passwords do not dump passwords for roles\n"));
printf(_(" --no-security-labels do not dump security label assignments\n"));
printf(_(" --no-subscriptions do not dump subscriptions\n"));
+ printf(_(" --no-compression-methods do not dump compression methods\n"));
printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c
index 860a211a3c..810403ec9c 100644
--- a/src/bin/pg_dump/pg_restore.c
+++ b/src/bin/pg_dump/pg_restore.c
@@ -74,6 +74,7 @@ main(int argc, char **argv)
static int no_publications = 0;
static int no_security_labels = 0;
static int no_subscriptions = 0;
+ static int no_compression_methods = 0;
static int strict_names = 0;
struct option cmdopts[] = {
@@ -122,6 +123,7 @@ main(int argc, char **argv)
{"no-publications", no_argument, &no_publications, 1},
{"no-security-labels", no_argument, &no_security_labels, 1},
{"no-subscriptions", no_argument, &no_subscriptions, 1},
+ {"no-compression-methods", no_argument, &no_compression_methods, 1},
{NULL, 0, NULL, 0}
};
@@ -361,6 +363,7 @@ main(int argc, char **argv)
opts->no_publications = no_publications;
opts->no_security_labels = no_security_labels;
opts->no_subscriptions = no_subscriptions;
+ opts->no_compression_methods = no_compression_methods;
if (if_exists && !opts->dropSchema)
{
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 041b5e0c87..0ded023858 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -735,7 +735,10 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
success = listConversions(pattern, show_verbose, show_system);
break;
case 'C':
- success = listCasts(pattern, show_verbose);
+ if (cmd[2] == 'M')
+ success = describeCompressionMethods(pattern, show_verbose);
+ else
+ success = listCasts(pattern, show_verbose);
break;
case 'd':
if (strncmp(cmd, "ddp", 3) == 0)
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index d22ec68431..57862497d0 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -200,6 +200,69 @@ describeAccessMethods(const char *pattern, bool verbose)
return true;
}
+/*
+ * \dCM
+ * Takes an optional regexp to select particular compression methods
+ */
+bool
+describeCompressionMethods(const char *pattern, bool verbose)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+ static const bool translate_columns[] = {false, false, false};
+
+ if (pset.sversion < 100000)
+ {
+ char sverbuf[32];
+
+ psql_error("The server (version %s) does not support compression methods.\n",
+ formatPGVersionNumber(pset.sversion, false,
+ sverbuf, sizeof(sverbuf)));
+ return true;
+ }
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT cmname AS \"%s\"",
+ gettext_noop("Name"));
+
+ if (verbose)
+ {
+ appendPQExpBuffer(&buf,
+ ",\n cmhandler AS \"%s\",\n"
+ " pg_catalog.obj_description(oid, 'pg_compression') AS \"%s\"",
+ gettext_noop("Handler"),
+ gettext_noop("Description"));
+ }
+
+ appendPQExpBufferStr(&buf,
+ "\nFROM pg_catalog.pg_compression\n");
+
+ processSQLNamePattern(pset.db, &buf, pattern, false, false,
+ NULL, "cmname", NULL,
+ NULL);
+
+ appendPQExpBufferStr(&buf, "ORDER BY 1;");
+
+ res = PSQLexec(buf.data);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of compression methods");
+ myopt.translate_header = true;
+ myopt.translate_columns = translate_columns;
+ myopt.n_translate_columns = lengthof(translate_columns);
+
+ printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+ PQclear(res);
+ return true;
+}
+
/*
* \db
* Takes an optional regexp to select particular tablespaces
@@ -1620,6 +1683,22 @@ describeOneTableDetails(const char *schemaname,
if (verbose)
{
appendPQExpBufferStr(&buf, ",\n a.attstorage");
+
+ if (pset.sversion >= 100000)
+ appendPQExpBufferStr(&buf, ",\n CASE WHEN attcompression = 0 THEN NULL ELSE "
+ " (SELECT cm.cmname || "
+ " (CASE WHEN cmoptions IS NULL "
+ " THEN '' "
+ " ELSE '(' || array_to_string(ARRAY(SELECT quote_ident(option_name) || ' ' || quote_literal(option_value)"
+ " FROM pg_options_to_table(cmoptions)), ', ') || ')'"
+ " END) "
+ " FROM pg_catalog.pg_compression_opt c "
+ " JOIN pg_catalog.pg_compression cm ON (cm.oid = c.cmid) "
+ " WHERE c.oid = a.attcompression) "
+ " END AS attcmname");
+ else
+ appendPQExpBufferStr(&buf, "\n NULL AS attcmname");
+
appendPQExpBufferStr(&buf, ",\n CASE WHEN a.attstattarget=-1 THEN NULL ELSE a.attstattarget END AS attstattarget");
/*
@@ -1741,6 +1820,10 @@ describeOneTableDetails(const char *schemaname,
if (verbose)
{
headers[cols++] = gettext_noop("Storage");
+
+ if (tableinfo.relkind == RELKIND_RELATION)
+ headers[cols++] = gettext_noop("Compression");
+
if (tableinfo.relkind == RELKIND_RELATION ||
tableinfo.relkind == RELKIND_INDEX ||
tableinfo.relkind == RELKIND_MATVIEW ||
@@ -1840,6 +1923,11 @@ describeOneTableDetails(const char *schemaname,
"???")))),
false, false);
+ /* Column compression. */
+ if (tableinfo.relkind == RELKIND_RELATION)
+ printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1),
+ false, false);
+
/* Statistics target, if the relkind supports this feature */
if (tableinfo.relkind == RELKIND_RELATION ||
tableinfo.relkind == RELKIND_INDEX ||
@@ -1847,7 +1935,7 @@ describeOneTableDetails(const char *schemaname,
tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
{
- printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1),
+ printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2),
false, false);
}
@@ -1858,7 +1946,7 @@ describeOneTableDetails(const char *schemaname,
tableinfo.relkind == RELKIND_COMPOSITE_TYPE ||
tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
- printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2),
+ printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 3),
false, false);
}
}
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 14a5667f3e..0ab8518a36 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -18,6 +18,9 @@ extern bool describeAccessMethods(const char *pattern, bool verbose);
/* \db */
extern bool describeTablespaces(const char *pattern, bool verbose);
+/* \dCM */
+extern bool describeCompressionMethods(const char *pattern, bool verbose);
+
/* \df, \dfa, \dfn, \dft, \dfw, etc. */
extern bool describeFunctions(const char *functypes, const char *pattern, bool verbose, bool showSystem);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 4d1c0ec3c6..307f8bfbe5 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -227,6 +227,7 @@ slashUsage(unsigned short int pager)
fprintf(output, _(" \\db[+] [PATTERN] list tablespaces\n"));
fprintf(output, _(" \\dc[S+] [PATTERN] list conversions\n"));
fprintf(output, _(" \\dC[+] [PATTERN] list casts\n"));
+ fprintf(output, _(" \\dCM[+] [PATTERN] list compression methods\n"));
fprintf(output, _(" \\dd[S] [PATTERN] show object descriptions not displayed elsewhere\n"));
fprintf(output, _(" \\dD[S+] [PATTERN] list domains\n"));
fprintf(output, _(" \\ddp [PATTERN] list default privileges\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index a09c49d6cf..1327abdfef 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -889,6 +889,11 @@ static const SchemaQuery Query_for_list_of_statistics = {
" AND d.datname = pg_catalog.current_database() "\
" AND s.subdbid = d.oid"
+#define Query_for_list_of_compression_methods \
+" SELECT pg_catalog.quote_ident(cmname) "\
+" FROM pg_catalog.pg_compression "\
+" WHERE substring(pg_catalog.quote_ident(cmname),1,%d)='%s'"
+
/* the silly-looking length condition is just to eat up the current word */
#define Query_for_list_of_arguments \
"SELECT pg_catalog.oidvectortypes(proargtypes)||')' "\
@@ -1011,6 +1016,7 @@ static const pgsql_thing_t words_after_create[] = {
* CREATE CONSTRAINT TRIGGER is not supported here because it is designed
* to be used only by pg_dump.
*/
+ {"COMPRESSION METHOD", NULL, NULL},
{"CONFIGURATION", Query_for_list_of_ts_configurations, NULL, THING_NO_SHOW},
{"CONVERSION", "SELECT pg_catalog.quote_ident(conname) FROM pg_catalog.pg_conversion WHERE substring(pg_catalog.quote_ident(conname),1,%d)='%s'"},
{"DATABASE", Query_for_list_of_databases},
@@ -1424,8 +1430,8 @@ psql_completion(const char *text, int start, int end)
"\\a",
"\\connect", "\\conninfo", "\\C", "\\cd", "\\copy",
"\\copyright", "\\crosstabview",
- "\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
- "\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
+ "\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dCM", "\\dd", "\\ddp",
+ "\\dD", "\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
"\\dm", "\\dn", "\\do", "\\dO", "\\dp",
"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
@@ -1954,11 +1960,17 @@ psql_completion(const char *text, int start, int end)
/* ALTER TABLE ALTER [COLUMN] <foo> SET */
else if (Matches7("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET") ||
Matches6("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET"))
- COMPLETE_WITH_LIST5("(", "DEFAULT", "NOT NULL", "STATISTICS", "STORAGE");
+ COMPLETE_WITH_LIST6("(", "COMPRESSED", "DEFAULT", "NOT NULL", "STATISTICS", "STORAGE");
/* ALTER TABLE ALTER [COLUMN] <foo> SET ( */
else if (Matches8("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "(") ||
Matches7("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "("))
COMPLETE_WITH_LIST2("n_distinct", "n_distinct_inherited");
+ else if (Matches8("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "COMPRESSED") ||
+ Matches7("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "COMPRESSED"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_compression_methods);
+ else if (Matches9("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "COMPRESSED", MatchAny) ||
+ Matches8("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "COMPRESSED", MatchAny))
+ COMPLETE_WITH_CONST("WITH (");
/* ALTER TABLE ALTER [COLUMN] <foo> SET STORAGE */
else if (Matches8("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "STORAGE") ||
Matches7("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "STORAGE"))
@@ -2177,12 +2189,14 @@ psql_completion(const char *text, int start, int end)
"SCHEMA", "SEQUENCE", "STATISTICS", "SUBSCRIPTION",
"TABLE", "TYPE", "VIEW", "MATERIALIZED VIEW", "COLUMN", "AGGREGATE", "FUNCTION",
"OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", "LARGE OBJECT",
- "TABLESPACE", "TEXT SEARCH", "ROLE", NULL};
+ "TABLESPACE", "TEXT SEARCH", "ROLE", "COMPRESSION METHOD", NULL};
COMPLETE_WITH_LIST(list_COMMENT);
}
else if (Matches4("COMMENT", "ON", "ACCESS", "METHOD"))
COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
+ else if (Matches4("COMMENT", "ON", "COMPRESSION", "METHOD"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_compression_methods);
else if (Matches3("COMMENT", "ON", "FOREIGN"))
COMPLETE_WITH_LIST2("DATA WRAPPER", "TABLE");
else if (Matches4("COMMENT", "ON", "TEXT", "SEARCH"))
@@ -2255,6 +2269,14 @@ psql_completion(const char *text, int start, int end)
else if (Matches6("CREATE", "ACCESS", "METHOD", MatchAny, "TYPE", MatchAny))
COMPLETE_WITH_CONST("HANDLER");
+ /* CREATE COMPRESSION METHOD */
+ /* Complete "CREATE COMPRESSION METHOD <name>" */
+ else if (Matches4("CREATE", "COMPRESSION", "METHOD", MatchAny))
+ COMPLETE_WITH_CONST("HANDLER");
+ /* Complete "CREATE COMPRESSION METHOD <name> HANDLER" */
+ else if (Matches5("CREATE", "COMPRESSION", "METHOD", MatchAny, "HANDLER"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
+
/* CREATE DATABASE */
else if (Matches3("CREATE", "DATABASE", MatchAny))
COMPLETE_WITH_LIST9("OWNER", "TEMPLATE", "ENCODING", "TABLESPACE",
@@ -2687,6 +2709,7 @@ psql_completion(const char *text, int start, int end)
Matches4("DROP", "ACCESS", "METHOD", MatchAny) ||
(Matches4("DROP", "AGGREGATE|FUNCTION", MatchAny, MatchAny) &&
ends_with(prev_wd, ')')) ||
+ Matches4("DROP", "COMPRESSION", "METHOD", MatchAny) ||
Matches4("DROP", "EVENT", "TRIGGER", MatchAny) ||
Matches5("DROP", "FOREIGN", "DATA", "WRAPPER", MatchAny) ||
Matches4("DROP", "FOREIGN", "TABLE", MatchAny) ||
@@ -2775,6 +2798,12 @@ psql_completion(const char *text, int start, int end)
else if (Matches5("DROP", "RULE", MatchAny, "ON", MatchAny))
COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
+ /* DROP COMPRESSION METHOD */
+ else if (Matches2("DROP", "COMPRESSION"))
+ COMPLETE_WITH_CONST("METHOD");
+ else if (Matches3("DROP", "COMPRESSION", "METHOD"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_compression_methods);
+
/* EXECUTE */
else if (Matches1("EXECUTE"))
COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements);
@@ -3407,6 +3436,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
else if (TailMatchesCS1("\\db*"))
COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
+ else if (TailMatchesCS1("\\dCM*"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_compression_methods);
else if (TailMatchesCS1("\\dD*"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL);
else if (TailMatchesCS1("\\des*"))
diff --git a/src/include/access/compression.h b/src/include/access/compression.h
new file mode 100644
index 0000000000..1320d8f882
--- /dev/null
+++ b/src/include/access/compression.h
@@ -0,0 +1,65 @@
+/*-------------------------------------------------------------------------
+ *
+ * compression.h
+ * API for Postgres compression methods.
+ *
+ * Copyright (c) 2015-2016, PostgreSQL Global Development Group
+ *
+ * src/include/access/compression.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef COMPRESSION_H
+#define COMPRESSION_H
+
+#include "postgres.h"
+#include "catalog/pg_attribute.h"
+#include "nodes/nodes.h"
+#include "nodes/pg_list.h"
+
+/* parsenodes.h */
+typedef struct ColumnCompression ColumnCompression;
+typedef struct CompressionMethodRoutine CompressionMethodRoutine;
+
+typedef struct
+{
+ CompressionMethodRoutine *routine;
+ List *options;
+ Oid cmoptoid;
+} AttributeCompression;
+
+typedef void (*CompressionConfigureRoutine)
+ (Form_pg_attribute attr, List *options);
+typedef struct varlena *(*CompressionRoutine)
+ (AttributeCompression * ac, const struct varlena *data);
+
+/*
+ * API struct for an compression method.
+ * Note this must be stored in a single palloc'd chunk of memory.
+ */
+typedef struct CompressionMethodRoutine
+{
+ NodeTag type;
+
+ CompressionConfigureRoutine configure;
+ CompressionConfigureRoutine drop;
+ CompressionRoutine compress;
+ CompressionRoutine decompress;
+} CompressionMethodRoutine;
+
+typedef struct CompressionMethodOpArgs
+{
+ Oid cmhanderid;
+ Oid typeid;
+} CompressionMethodOpArgs;
+
+extern CompressionMethodRoutine * GetCompressionRoutine(Oid cmoptoid);
+extern List *GetCompressionOptionsList(Oid cmoptoid);
+extern Oid CreateCompressionOptions(Form_pg_attribute attr, Oid cmid,
+ List *options);
+extern ColumnCompression * GetColumnCompressionForAttribute(Form_pg_attribute att);
+extern void CheckCompressionMismatch(ColumnCompression * c1,
+ ColumnCompression * c2, const char *attributeName);
+
+#endif /* COMPRESSION_H */
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 5cdaa3bff1..573512367a 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -258,7 +258,6 @@ extern void add_string_reloption(bits32 kinds, char *name, char *desc,
extern Datum transformRelOptions(Datum oldOptions, List *defList,
char *namspace, char *validnsps[],
bool ignoreOids, bool isReset);
-extern List *untransformRelOptions(Datum options);
extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
amoptions_function amoptions);
extern relopt_value *parseRelOptions(Datum options, bool validate,
@@ -269,6 +268,9 @@ extern void fillRelOptions(void *rdopts, Size basesize,
relopt_value *options, int numoptions,
bool validate,
const relopt_parse_elt *elems, int nelems);
+extern char *formatRelOptions(List *options);
+extern void freeRelOptions(List *options);
+extern List *untransformRelOptions(Datum options);
extern bytea *default_reloptions(Datum reloptions, bool validate,
relopt_kind kind);
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index 989fe738bb..9aad2f01b1 100644
--- a/src/include/access/tupdesc.h
+++ b/src/include/access/tupdesc.h
@@ -14,7 +14,9 @@
#ifndef TUPDESC_H
#define TUPDESC_H
+#include "postgres.h"
#include "access/attnum.h"
+#include "access/compression.h"
#include "catalog/pg_attribute.h"
#include "nodes/pg_list.h"
@@ -82,6 +84,9 @@ typedef struct tupleDesc
/* Accessor for the i'th attribute of tupdesc. */
#define TupleDescAttr(tupdesc, i) (&(tupdesc)->attrs[(i)])
+#define TupleDescAttrCompression(tupdesc, i) \
+ ((tupdesc)->tdcompression? &((tupdesc)->tdcompression[i]) : NULL)
+
extern TupleDesc CreateTemplateTupleDesc(int natts, bool hasoid);
diff --git a/src/include/access/tuptoaster.h b/src/include/access/tuptoaster.h
index fd9f83ac44..11092e5e9d 100644
--- a/src/include/access/tuptoaster.h
+++ b/src/include/access/tuptoaster.h
@@ -210,7 +210,7 @@ extern HeapTuple toast_build_flattened_tuple(TupleDesc tupleDesc,
* Create a compressed version of a varlena datum, if possible
* ----------
*/
-extern Datum toast_compress_datum(Datum value);
+extern Datum toast_compress_datum(Datum value, Oid cmoptoid);
/* ----------
* toast_raw_datum_size -
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index b9f98423cc..36cadd409e 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -165,10 +165,12 @@ typedef enum ObjectClass
OCLASS_PUBLICATION, /* pg_publication */
OCLASS_PUBLICATION_REL, /* pg_publication_rel */
OCLASS_SUBSCRIPTION, /* pg_subscription */
- OCLASS_TRANSFORM /* pg_transform */
+ OCLASS_TRANSFORM, /* pg_transform */
+ OCLASS_COMPRESSION_METHOD, /* pg_compression */
+ OCLASS_COMPRESSION_OPTIONS /* pg_compression_opt */
} ObjectClass;
-#define LAST_OCLASS OCLASS_TRANSFORM
+#define LAST_OCLASS OCLASS_COMPRESSION_OPTIONS
/* flag bits for performDeletion/performMultipleDeletions: */
#define PERFORM_DELETION_INTERNAL 0x0001 /* internal action */
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index ef8493674c..b580f1971a 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -120,6 +120,14 @@ DECLARE_UNIQUE_INDEX(pg_collation_name_enc_nsp_index, 3164, on pg_collation usin
DECLARE_UNIQUE_INDEX(pg_collation_oid_index, 3085, on pg_collation using btree(oid oid_ops));
#define CollationOidIndexId 3085
+DECLARE_UNIQUE_INDEX(pg_compression_oid_index, 3422, on pg_compression using btree(oid oid_ops));
+#define CompressionMethodOidIndexId 3422
+DECLARE_UNIQUE_INDEX(pg_compression_name_index, 3423, on pg_compression using btree(cmname name_ops));
+#define CompressionMethodNameIndexId 3423
+
+DECLARE_UNIQUE_INDEX(pg_compression_opt_oid_index, 3424, on pg_compression_opt using btree(oid oid_ops));
+#define CompressionOptionsOidIndexId 3424
+
DECLARE_INDEX(pg_constraint_conname_nsp_index, 2664, on pg_constraint using btree(conname name_ops, connamespace oid_ops));
#define ConstraintNameNspIndexId 2664
DECLARE_INDEX(pg_constraint_conrelid_index, 2665, on pg_constraint using btree(conrelid oid_ops));
diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h
index bcf28e8f04..caadd61031 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -156,6 +156,9 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
/* attribute's collation */
Oid attcollation;
+ /* attribute's compression options or InvalidOid */
+ Oid attcompression;
+
#ifdef CATALOG_VARLEN /* variable-length fields start here */
/* NOTE: The following fields are not present in tuple descriptors. */
@@ -174,10 +177,10 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
* ATTRIBUTE_FIXED_PART_SIZE is the size of the fixed-layout,
* guaranteed-not-null part of a pg_attribute row. This is in fact as much
* of the row as gets copied into tuple descriptors, so don't expect you
- * can access fields beyond attcollation except in a real tuple!
+ * can access fields beyond attcompression except in a real tuple!
*/
#define ATTRIBUTE_FIXED_PART_SIZE \
- (offsetof(FormData_pg_attribute,attcollation) + sizeof(Oid))
+ (offsetof(FormData_pg_attribute,attcompression) + sizeof(Oid))
/* ----------------
* Form_pg_attribute corresponds to a pointer to a tuple with
@@ -191,29 +194,30 @@ typedef FormData_pg_attribute *Form_pg_attribute;
* ----------------
*/
-#define Natts_pg_attribute 22
-#define Anum_pg_attribute_attrelid 1
-#define Anum_pg_attribute_attname 2
-#define Anum_pg_attribute_atttypid 3
-#define Anum_pg_attribute_attstattarget 4
-#define Anum_pg_attribute_attlen 5
-#define Anum_pg_attribute_attnum 6
-#define Anum_pg_attribute_attndims 7
-#define Anum_pg_attribute_attcacheoff 8
-#define Anum_pg_attribute_atttypmod 9
-#define Anum_pg_attribute_attbyval 10
-#define Anum_pg_attribute_attstorage 11
-#define Anum_pg_attribute_attalign 12
-#define Anum_pg_attribute_attnotnull 13
-#define Anum_pg_attribute_atthasdef 14
-#define Anum_pg_attribute_attidentity 15
-#define Anum_pg_attribute_attisdropped 16
-#define Anum_pg_attribute_attislocal 17
-#define Anum_pg_attribute_attinhcount 18
-#define Anum_pg_attribute_attcollation 19
-#define Anum_pg_attribute_attacl 20
-#define Anum_pg_attribute_attoptions 21
-#define Anum_pg_attribute_attfdwoptions 22
+#define Natts_pg_attribute 23
+#define Anum_pg_attribute_attrelid 1
+#define Anum_pg_attribute_attname 2
+#define Anum_pg_attribute_atttypid 3
+#define Anum_pg_attribute_attstattarget 4
+#define Anum_pg_attribute_attlen 5
+#define Anum_pg_attribute_attnum 6
+#define Anum_pg_attribute_attndims 7
+#define Anum_pg_attribute_attcacheoff 8
+#define Anum_pg_attribute_atttypmod 9
+#define Anum_pg_attribute_attbyval 10
+#define Anum_pg_attribute_attstorage 11
+#define Anum_pg_attribute_attalign 12
+#define Anum_pg_attribute_attnotnull 13
+#define Anum_pg_attribute_atthasdef 14
+#define Anum_pg_attribute_attidentity 15
+#define Anum_pg_attribute_attisdropped 16
+#define Anum_pg_attribute_attislocal 17
+#define Anum_pg_attribute_attinhcount 18
+#define Anum_pg_attribute_attcollation 19
+#define Anum_pg_attribute_attcompression 20
+#define Anum_pg_attribute_attacl 21
+#define Anum_pg_attribute_attoptions 22
+#define Anum_pg_attribute_attfdwoptions 23
/* ----------------
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index b256657bda..04c7c18d70 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -147,9 +147,9 @@ typedef FormData_pg_class *Form_pg_class;
* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId;
* similarly, "1" in relminmxid stands for FirstMultiXactId
*/
-DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 30 0 t f f f f f f t n f 3 1 _null_ _null_ _null_));
+DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 31 0 t f f f f f f t n f 3 1 _null_ _null_ _null_));
DESCR("");
-DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 22 0 f f f f f f f t n f 3 1 _null_ _null_ _null_));
+DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 23 0 f f f f f f f t n f 3 1 _null_ _null_ _null_));
DESCR("");
DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f p r 29 0 t f f f f f f t n f 3 1 _null_ _null_ _null_));
DESCR("");
diff --git a/src/include/catalog/pg_compression.h b/src/include/catalog/pg_compression.h
new file mode 100644
index 0000000000..1d5f9ac479
--- /dev/null
+++ b/src/include/catalog/pg_compression.h
@@ -0,0 +1,55 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_compression.h
+ * definition of the system "compression method" relation (pg_compression)
+ * along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_compression.h
+ *
+ * NOTES
+ * the genbki.pl script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ * XXX do NOT break up DATA() statements into multiple lines!
+ * the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_COMPRESSION_H
+#define PG_COMPRESSION_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ * pg_compression definition. cpp turns this into
+ * typedef struct FormData_pg_compression
+ * ----------------
+ */
+#define CompressionMethodRelationId 3419
+
+CATALOG(pg_compression,3419)
+{
+ NameData cmname; /* compression method name */
+ regproc cmhandler; /* compression handler */
+} FormData_pg_compression;
+
+/* ----------------
+ * Form_pg_compression corresponds to a pointer to a tuple with
+ * the format of pg_compression relation.
+ * ----------------
+ */
+typedef FormData_pg_compression * Form_pg_compression;
+
+/* ----------------
+ * compiler constants for pg_compression
+ * ----------------
+ */
+#define Natts_pg_compression 2
+#define Anum_pg_compression_cmname 1
+#define Anum_pg_compression_cmhandler 2
+
+#endif /* PG_COMPRESSION_H */
diff --git a/src/include/catalog/pg_compression_opt.h b/src/include/catalog/pg_compression_opt.h
new file mode 100644
index 0000000000..343d3355d9
--- /dev/null
+++ b/src/include/catalog/pg_compression_opt.h
@@ -0,0 +1,54 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_compression_opt.h
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_compression_opt.h
+ *
+ * NOTES
+ * the genbki.pl script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ * XXX do NOT break up DATA() statements into multiple lines!
+ * the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_COMPRESSION_OPT_H
+#define PG_COMPRESSION_OPT_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ * pg_compression_opt definition. cpp turns this into
+ * typedef struct FormData_pg_compression_opt
+ * ----------------
+ */
+#define CompressionOptRelationId 3420
+
+CATALOG(pg_compression_opt,3420)
+{
+ Oid cmid; /* compression method oid */
+ regproc cmhandler; /* compression handler */
+ text cmoptions[1]; /* specific options from WITH */
+} FormData_pg_compression_opt;
+
+/* ----------------
+ * Form_pg_compression_opt corresponds to a pointer to a tuple with
+ * the format of pg_compression_opt relation.
+ * ----------------
+ */
+typedef FormData_pg_compression_opt * Form_pg_compression_opt;
+
+/* ----------------
+ * compiler constants for pg_compression_opt
+ * ----------------
+ */
+#define Natts_pg_compression_opt 3
+#define Anum_pg_compression_opt_cmid 1
+#define Anum_pg_compression_opt_cmhandler 2
+#define Anum_pg_compression_opt_cmoptions 3
+
+#endif /* PG_COMPRESSION_OPT_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index d820b56aa1..5a2be99f0b 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3874,6 +3874,10 @@ DATA(insert OID = 3311 ( tsm_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s
DESCR("I/O");
DATA(insert OID = 3312 ( tsm_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3310" _null_ _null_ _null_ _null_ _null_ tsm_handler_out _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 3425 ( compression_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3421 "2275" _null_ _null_ _null_ _null_ _null_ compression_handler_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 3426 ( compression_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3421" _null_ _null_ _null_ _null_ _null_ compression_handler_out _null_ _null_ _null_ ));
+DESCR("I/O");
/* tablesample method handlers */
DATA(insert OID = 3313 ( bernoulli PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3310 "2281" _null_ _null_ _null_ _null_ _null_ tsm_bernoulli_handler _null_ _null_ _null_ ));
@@ -4676,6 +4680,8 @@ DATA(insert OID = 3646 ( gtsvectorin PGNSP PGUID 12 1 0 0 0 f f f f t f i s
DESCR("I/O");
DATA(insert OID = 3647 ( gtsvectorout PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3642" _null_ _null_ _null_ _null_ _null_ gtsvectorout _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 3453 ( tsvector_compression_handler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3421 "2281" _null_ _null_ _null_ _null_ _null_ tsvector_compression_handler _null_ _null_ _null_ ));
+DESCR("tsvector compression handler");
DATA(insert OID = 3616 ( tsvector_lt PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "3614 3614" _null_ _null_ _null_ _null_ _null_ tsvector_lt _null_ _null_ _null_ ));
DATA(insert OID = 3617 ( tsvector_le PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "3614 3614" _null_ _null_ _null_ _null_ _null_ tsvector_le _null_ _null_ _null_ ));
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index ffdb452b02..23921db534 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -199,6 +199,9 @@ CATALOG(pg_type,1247) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71) BKI_SCHEMA_MACRO
*/
Oid typcollation;
+ /* Default compression method for the datatype or InvalidOid */
+ Oid typdefaultcm;
+
#ifdef CATALOG_VARLEN /* variable-length fields start here */
/*
@@ -236,7 +239,7 @@ typedef FormData_pg_type *Form_pg_type;
* compiler constants for pg_type
* ----------------
*/
-#define Natts_pg_type 30
+#define Natts_pg_type 31
#define Anum_pg_type_typname 1
#define Anum_pg_type_typnamespace 2
#define Anum_pg_type_typowner 3
@@ -264,9 +267,10 @@ typedef FormData_pg_type *Form_pg_type;
#define Anum_pg_type_typtypmod 25
#define Anum_pg_type_typndims 26
#define Anum_pg_type_typcollation 27
-#define Anum_pg_type_typdefaultbin 28
-#define Anum_pg_type_typdefault 29
-#define Anum_pg_type_typacl 30
+#define Anum_pg_type_typdefaultcm 28
+#define Anum_pg_type_typdefaultbin 29
+#define Anum_pg_type_typdefault 30
+#define Anum_pg_type_typacl 31
/* ----------------
@@ -283,102 +287,102 @@ typedef FormData_pg_type *Form_pg_type;
*/
/* OIDS 1 - 99 */
-DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("boolean, 'true'/'false'");
#define BOOLOID 16
-DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("variable-length string, binary values escaped");
#define BYTEAOID 17
-DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("single character");
#define CHAROID 18
-DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("63-byte type for storing system identifiers");
#define NAMEOID 19
-DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("~18 digit integer, 8-byte storage");
#define INT8OID 20
-DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("-32 thousand to 32 thousand, 2-byte storage");
#define INT2OID 21
-DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("array of int2, used in system tables");
#define INT2VECTOROID 22
-DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("-2 billion to 2 billion integer, 4-byte storage");
#define INT4OID 23
-DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("registered procedure");
#define REGPROCOID 24
-DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 100 0 _null_ _null_ _null_ ));
DESCR("variable-length string, no limit specified");
#define TEXTOID 25
-DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("object identifier(oid), maximum 4 billion");
#define OIDOID 26
-DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("(block, offset), physical location of tuple");
#define TIDOID 27
-DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("transaction id");
#define XIDOID 28
-DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("command identifier type, sequence in transaction id");
#define CIDOID 29
-DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("array of oids, used in system tables");
#define OIDVECTOROID 30
/* hand-built rowtype entries for bootstrapped catalogs */
/* NB: OIDs assigned here must match the BKI_ROWTYPE_OID declarations */
-DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
/* OIDS 100 - 199 */
-DATA(insert OID = 114 ( json PGNSP PGUID -1 f b U f t \054 0 0 199 json_in json_out json_recv json_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 114 ( json PGNSP PGUID -1 f b U f t \054 0 0 199 json_in json_out json_recv json_send - - - i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define JSONOID 114
-DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("XML content");
#define XMLOID 142
-DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 199 ( _json PGNSP PGUID -1 f b A f t \054 0 114 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 199 ( _json PGNSP PGUID -1 f b A f t \054 0 114 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 194 ( pg_node_tree PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 194 ( pg_node_tree PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 100 0 _null_ _null_ _null_ ));
DESCR("string representing an internal node tree");
#define PGNODETREEOID 194
-DATA(insert OID = 3361 ( pg_ndistinct PGNSP PGUID -1 f b S f t \054 0 0 0 pg_ndistinct_in pg_ndistinct_out pg_ndistinct_recv pg_ndistinct_send - - - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 3361 ( pg_ndistinct PGNSP PGUID -1 f b S f t \054 0 0 0 pg_ndistinct_in pg_ndistinct_out pg_ndistinct_recv pg_ndistinct_send - - - i x f 0 -1 0 100 0 _null_ _null_ _null_ ));
DESCR("multivariate ndistinct coefficients");
#define PGNDISTINCTOID 3361
-DATA(insert OID = 3402 ( pg_dependencies PGNSP PGUID -1 f b S f t \054 0 0 0 pg_dependencies_in pg_dependencies_out pg_dependencies_recv pg_dependencies_send - - - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 3402 ( pg_dependencies PGNSP PGUID -1 f b S f t \054 0 0 0 pg_dependencies_in pg_dependencies_out pg_dependencies_recv pg_dependencies_send - - - i x f 0 -1 0 100 0 _null_ _null_ _null_ ));
DESCR("multivariate dependencies");
#define PGDEPENDENCIESOID 3402
-DATA(insert OID = 32 ( pg_ddl_command PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 pg_ddl_command_in pg_ddl_command_out pg_ddl_command_recv pg_ddl_command_send - - - ALIGNOF_POINTER p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 32 ( pg_ddl_command PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 pg_ddl_command_in pg_ddl_command_out pg_ddl_command_recv pg_ddl_command_send - - - ALIGNOF_POINTER p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("internal type for passing CollectedCommand");
#define PGDDLCOMMANDOID 32
/* OIDS 200 - 299 */
-DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("storage manager");
/* OIDS 300 - 399 */
@@ -388,280 +392,280 @@ DESCR("storage manager");
/* OIDS 500 - 599 */
/* OIDS 600 - 699 */
-DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("geometric point '(x, y)'");
#define POINTOID 600
-DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("geometric line segment '(pt1,pt2)'");
#define LSEGOID 601
-DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("geometric path '(pt1,...)'");
#define PATHOID 602
-DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("geometric box '(lower left,upper right)'");
#define BOXOID 603
-DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("geometric polygon '(pt1,...)'");
#define POLYGONOID 604
-DATA(insert OID = 628 ( line PGNSP PGUID 24 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 628 ( line PGNSP PGUID 24 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("geometric line");
#define LINEOID 628
-DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
/* OIDS 700 - 799 */
-DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("single-precision floating point number, 4-byte storage");
#define FLOAT4OID 700
-DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("double-precision floating point number, 8-byte storage");
#define FLOAT8OID 701
-DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("absolute, limited-range date and time (Unix system time)");
#define ABSTIMEOID 702
-DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("relative, limited-range time interval (Unix delta time)");
#define RELTIMEOID 703
-DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("(abstime,abstime), time interval");
#define TINTERVALOID 704
-DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f p X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f p X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("");
#define UNKNOWNOID 705
-DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("geometric circle '(center,radius)'");
#define CIRCLEOID 718
-DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("monetary amounts, $d,ddd.cc");
#define CASHOID 790
-DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
/* OIDS 800 - 899 */
-DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("XX:XX:XX:XX:XX:XX, MAC address");
#define MACADDROID 829
-DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("IP address/netmask, host address, netmask optional");
#define INETOID 869
-DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("network IP address/netmask, network address");
#define CIDROID 650
-DATA(insert OID = 774 ( macaddr8 PGNSP PGUID 8 f b U f t \054 0 0 775 macaddr8_in macaddr8_out macaddr8_recv macaddr8_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 774 ( macaddr8 PGNSP PGUID 8 f b U f t \054 0 0 775 macaddr8_in macaddr8_out macaddr8_recv macaddr8_send - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("XX:XX:XX:XX:XX:XX:XX:XX, MAC address");
#define MACADDR8OID 774
/* OIDS 900 - 999 */
/* OIDS 1000 - 1099 */
-DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define INT2ARRAYOID 1005
-DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define INT4ARRAYOID 1007
-DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 100 0 _null_ _null_ _null_ ));
#define TEXTARRAYOID 1009
-DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define OIDARRAYOID 1028
-DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
-DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
-DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout array_typanalyze i x f 0 -1 0 100 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout array_typanalyze i x f 0 -1 0 100 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define FLOAT4ARRAYOID 1021
-DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("access control list");
#define ACLITEMOID 1033
-DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 775 ( _macaddr8 PGNSP PGUID -1 f b A f t \054 0 774 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 775 ( _macaddr8 PGNSP PGUID -1 f b A f t \054 0 774 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define CSTRINGARRAYOID 1263
-DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 100 0 _null_ _null_ _null_ ));
DESCR("char(length), blank-padded string, fixed storage length");
#define BPCHAROID 1042
-DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 100 0 _null_ _null_ _null_ ));
DESCR("varchar(length), non-blank-padded string, variable storage length");
#define VARCHAROID 1043
-DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("date");
#define DATEOID 1082
-DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("time of day");
#define TIMEOID 1083
/* OIDS 1100 - 1199 */
-DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("date and time");
#define TIMESTAMPOID 1114
-DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("date and time with time zone");
#define TIMESTAMPTZOID 1184
-DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("@ <number> <units>, time interval");
#define INTERVALOID 1186
-DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
/* OIDS 1200 - 1299 */
-DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("time of day with time zone");
#define TIMETZOID 1266
-DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
/* OIDS 1500 - 1599 */
-DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("fixed-length bit string");
#define BITOID 1560
-DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("variable-length bit string");
#define VARBITOID 1562
-DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
/* OIDS 1600 - 1699 */
/* OIDS 1700 - 1799 */
-DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("numeric(precision, decimal), arbitrary precision number");
#define NUMERICOID 1700
-DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("reference to cursor (portal name)");
#define REFCURSOROID 1790
/* OIDS 2200 - 2299 */
-DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("registered procedure (with args)");
#define REGPROCEDUREOID 2202
-DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("registered operator");
#define REGOPEROID 2203
-DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("registered operator (with args)");
#define REGOPERATOROID 2204
-DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("registered class");
#define REGCLASSOID 2205
-DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("registered type");
#define REGTYPEOID 2206
-DATA(insert OID = 4096 ( regrole PGNSP PGUID 4 t b N f t \054 0 0 4097 regrolein regroleout regrolerecv regrolesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 4096 ( regrole PGNSP PGUID 4 t b N f t \054 0 0 4097 regrolein regroleout regrolerecv regrolesend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("registered role");
#define REGROLEOID 4096
-DATA(insert OID = 4089 ( regnamespace PGNSP PGUID 4 t b N f t \054 0 0 4090 regnamespacein regnamespaceout regnamespacerecv regnamespacesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 4089 ( regnamespace PGNSP PGUID 4 t b N f t \054 0 0 4090 regnamespacein regnamespaceout regnamespacerecv regnamespacesend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("registered namespace");
#define REGNAMESPACEOID 4089
-DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define REGTYPEARRAYOID 2211
-DATA(insert OID = 4097 ( _regrole PGNSP PGUID -1 f b A f t \054 0 4096 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 4090 ( _regnamespace PGNSP PGUID -1 f b A f t \054 0 4089 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 4097 ( _regrole PGNSP PGUID -1 f b A f t \054 0 4096 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 4090 ( _regnamespace PGNSP PGUID -1 f b A f t \054 0 4089 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
/* uuid */
-DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("UUID datatype");
#define UUIDOID 2950
-DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
/* pg_lsn */
-DATA(insert OID = 3220 ( pg_lsn PGNSP PGUID 8 FLOAT8PASSBYVAL b U f t \054 0 0 3221 pg_lsn_in pg_lsn_out pg_lsn_recv pg_lsn_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3220 ( pg_lsn PGNSP PGUID 8 FLOAT8PASSBYVAL b U f t \054 0 0 3221 pg_lsn_in pg_lsn_out pg_lsn_recv pg_lsn_send - - - d p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("PostgreSQL LSN datatype");
#define LSNOID 3220
-DATA(insert OID = 3221 ( _pg_lsn PGNSP PGUID -1 f b A f t \054 0 3220 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3221 ( _pg_lsn PGNSP PGUID -1 f b A f t \054 0 3220 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
/* text search */
-DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("text representation for text search");
#define TSVECTOROID 3614
-DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("GiST index internal text representation for text search");
#define GTSVECTOROID 3642
-DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("query representation for text search");
#define TSQUERYOID 3615
-DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("registered text search configuration");
#define REGCONFIGOID 3734
-DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("registered text search dictionary");
#define REGDICTIONARYOID 3769
-DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
/* jsonb */
-DATA(insert OID = 3802 ( jsonb PGNSP PGUID -1 f b U f t \054 0 0 3807 jsonb_in jsonb_out jsonb_recv jsonb_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3802 ( jsonb PGNSP PGUID -1 f b U f t \054 0 0 3807 jsonb_in jsonb_out jsonb_recv jsonb_send - - - i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("Binary JSON");
#define JSONBOID 3802
-DATA(insert OID = 3807 ( _jsonb PGNSP PGUID -1 f b A f t \054 0 3802 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3807 ( _jsonb PGNSP PGUID -1 f b A f t \054 0 3802 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("txid snapshot");
-DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
/* range types */
-DATA(insert OID = 3904 ( int4range PGNSP PGUID -1 f r R f t \054 0 0 3905 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3904 ( int4range PGNSP PGUID -1 f r R f t \054 0 0 3905 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("range of integers");
#define INT4RANGEOID 3904
-DATA(insert OID = 3905 ( _int4range PGNSP PGUID -1 f b A f t \054 0 3904 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3906 ( numrange PGNSP PGUID -1 f r R f t \054 0 0 3907 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3905 ( _int4range PGNSP PGUID -1 f b A f t \054 0 3904 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3906 ( numrange PGNSP PGUID -1 f r R f t \054 0 0 3907 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("range of numerics");
-DATA(insert OID = 3907 ( _numrange PGNSP PGUID -1 f b A f t \054 0 3906 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3908 ( tsrange PGNSP PGUID -1 f r R f t \054 0 0 3909 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3907 ( _numrange PGNSP PGUID -1 f b A f t \054 0 3906 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3908 ( tsrange PGNSP PGUID -1 f r R f t \054 0 0 3909 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("range of timestamps without time zone");
-DATA(insert OID = 3909 ( _tsrange PGNSP PGUID -1 f b A f t \054 0 3908 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3910 ( tstzrange PGNSP PGUID -1 f r R f t \054 0 0 3911 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3909 ( _tsrange PGNSP PGUID -1 f b A f t \054 0 3908 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3910 ( tstzrange PGNSP PGUID -1 f r R f t \054 0 0 3911 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("range of timestamps with time zone");
-DATA(insert OID = 3911 ( _tstzrange PGNSP PGUID -1 f b A f t \054 0 3910 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3912 ( daterange PGNSP PGUID -1 f r R f t \054 0 0 3913 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3911 ( _tstzrange PGNSP PGUID -1 f b A f t \054 0 3910 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3912 ( daterange PGNSP PGUID -1 f r R f t \054 0 0 3913 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("range of dates");
-DATA(insert OID = 3913 ( _daterange PGNSP PGUID -1 f b A f t \054 0 3912 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3926 ( int8range PGNSP PGUID -1 f r R f t \054 0 0 3927 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3913 ( _daterange PGNSP PGUID -1 f b A f t \054 0 3912 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3926 ( int8range PGNSP PGUID -1 f r R f t \054 0 0 3927 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
DESCR("range of bigints");
-DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
/*
* pseudo-types
@@ -676,42 +680,44 @@ DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 arr
* but there is now support for it in records and arrays. Perhaps we should
* just treat it as a regular base type?
*/
-DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define RECORDOID 2249
-DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define RECORDARRAYOID 2287
-DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define CSTRINGOID 2275
-DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define ANYOID 2276
-DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define ANYARRAYOID 2277
-DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out void_recv void_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out void_recv void_send - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define VOIDOID 2278
-DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define TRIGGEROID 2279
-DATA(insert OID = 3838 ( event_trigger PGNSP PGUID 4 t p P f t \054 0 0 0 event_trigger_in event_trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3838 ( event_trigger PGNSP PGUID 4 t p P f t \054 0 0 0 event_trigger_in event_trigger_out - - - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define EVTTRIGGEROID 3838
-DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define LANGUAGE_HANDLEROID 2280
-DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define INTERNALOID 2281
-DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define OPAQUEOID 2282
-DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define ANYELEMENTOID 2283
-DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define ANYNONARRAYOID 2776
-DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define ANYENUMOID 3500
-DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_handler_in fdw_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_handler_in fdw_handler_out - - - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define FDW_HANDLEROID 3115
-DATA(insert OID = 325 ( index_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 325 ( index_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define INDEX_AM_HANDLEROID 325
-DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define TSM_HANDLEROID 3310
-DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 0 _null_ _null_ _null_ ));
#define ANYRANGEOID 3831
+DATA(insert OID = 3421 ( compression_handler PGNSP PGUID 4 t p P f t \054 0 0 0 compression_handler_in compression_handler_out - - - - - i p f 0 -1 0 0 0 _null_ _null_ _null_ ));
+#define COMPRESSION_HANDLEROID 3421
/*
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index f7bb4a54f7..01c542e829 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -140,6 +140,7 @@ extern Oid RemoveUserMapping(DropUserMappingStmt *stmt);
extern void RemoveUserMappingById(Oid umId);
extern void CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid);
extern void ImportForeignSchema(ImportForeignSchemaStmt *stmt);
+extern Datum optionListToArray(List *options);
extern Datum transformGenericOptions(Oid catalogId,
Datum oldOptions,
List *options,
@@ -152,6 +153,14 @@ extern Oid get_index_am_oid(const char *amname, bool missing_ok);
extern Oid get_am_oid(const char *amname, bool missing_ok);
extern char *get_am_name(Oid amOid);
+/* commands/compressioncmds.c */
+extern ObjectAddress DefineCompressionMethod(List *names, List *parameters);
+extern void RemoveCompressionMethodById(Oid cmOid);
+extern void RemoveCompressionOptionsById(Oid cmoptoid);
+extern Oid get_compression_method_oid(const char *cmname, bool missing_ok);
+extern char *get_compression_method_name(Oid cmOid);
+extern char *get_compression_method_name_for_opt(Oid cmoptoid);
+
/* support routines in commands/define.c */
extern char *defGetString(DefElem *def);
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index da3ff5dbee..c50d9525d0 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -24,7 +24,8 @@
extern ObjectAddress DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
- ObjectAddress *typaddress, const char *queryString);
+ ObjectAddress *typaddress, const char *queryString,
+ Node **pAlterStmt);
extern void RemoveRelations(DropStmt *drop);
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index 34f6fe328f..d198fd1d05 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -53,5 +53,6 @@ extern Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
bool isImplicitArray,
bool errorOnTableType,
ObjectAddresses *objsMoved);
+extern void AlterType(AlterTypeStmt * stmt);
#endif /* TYPECMDS_H */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 27bd4f3363..6c1c6350e3 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -413,6 +413,8 @@ typedef enum NodeTag
T_DropSubscriptionStmt,
T_CreateStatsStmt,
T_AlterCollationStmt,
+ T_AlterTypeStmt,
+ T_AlterTypeCmd,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
@@ -468,6 +470,7 @@ typedef enum NodeTag
T_PartitionBoundSpec,
T_PartitionRangeDatum,
T_PartitionCmd,
+ T_ColumnCompression,
/*
* TAGS FOR REPLICATION GRAMMAR PARSE NODES (replnodes.h)
@@ -497,7 +500,8 @@ typedef enum NodeTag
T_FdwRoutine, /* in foreign/fdwapi.h */
T_IndexAmRoutine, /* in access/amapi.h */
T_TsmRoutine, /* in access/tsmapi.h */
- T_ForeignKeyCacheInfo /* in utils/rel.h */
+ T_ForeignKeyCacheInfo, /* in utils/rel.h */
+ T_CompressionMethodRoutine, /* in access/compression.h */
} NodeTag;
/*
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index f2a5ab12f6..16aa08f8e8 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -615,6 +615,14 @@ typedef struct RangeTableSample
int location; /* method name location, or -1 if unknown */
} RangeTableSample;
+typedef struct ColumnCompression
+{
+ NodeTag type;
+ char *methodName;
+ Oid methodOid;
+ List *options;
+} ColumnCompression;
+
/*
* ColumnDef - column definition (used in various creates)
*
@@ -638,6 +646,7 @@ typedef struct ColumnDef
NodeTag type;
char *colname; /* name of column */
TypeName *typeName; /* type of column */
+ ColumnCompression *compression;
int inhcount; /* number of times column is inherited */
bool is_local; /* column has local (non-inherited) def'n */
bool is_not_null; /* NOT NULL constraint specified? */
@@ -1616,6 +1625,7 @@ typedef enum ObjectType
OBJECT_CAST,
OBJECT_COLUMN,
OBJECT_COLLATION,
+ OBJECT_COMPRESSION_METHOD,
OBJECT_CONVERSION,
OBJECT_DATABASE,
OBJECT_DEFAULT,
@@ -1762,7 +1772,8 @@ typedef enum AlterTableType
AT_DetachPartition, /* DETACH PARTITION */
AT_AddIdentity, /* ADD IDENTITY */
AT_SetIdentity, /* SET identity column options */
- AT_DropIdentity /* DROP IDENTITY */
+ AT_DropIdentity, /* DROP IDENTITY */
+ AT_AlterColumnCompression /* ALTER COLUMN name COMPRESSED cm WITH (...) */
} AlterTableType;
typedef struct ReplicaIdentityStmt
@@ -3432,4 +3443,24 @@ typedef struct DropSubscriptionStmt
DropBehavior behavior; /* RESTRICT or CASCADE behavior */
} DropSubscriptionStmt;
+typedef enum AlterTypeCmdType
+{
+ AT_AlterTypeCompression, /* ALTER TYPE name COMPRESSED cm WITH
+ * (options) */
+} AlterTypeCmdType;
+
+typedef struct AlterTypeCmd
+{
+ NodeTag type;
+ AlterTypeCmdType cmdtype;
+ Node *def;
+} AlterTypeCmd;
+
+typedef struct AlterTypeStmt
+{
+ NodeTag type;
+ List *typeName;
+ List *cmds;
+} AlterTypeStmt;
+
#endif /* PARSENODES_H */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index f50e45e886..7bfc6e6be4 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -87,6 +87,8 @@ PG_KEYWORD("comment", COMMENT, UNRESERVED_KEYWORD)
PG_KEYWORD("comments", COMMENTS, UNRESERVED_KEYWORD)
PG_KEYWORD("commit", COMMIT, UNRESERVED_KEYWORD)
PG_KEYWORD("committed", COMMITTED, UNRESERVED_KEYWORD)
+PG_KEYWORD("compressed", COMPRESSED, UNRESERVED_KEYWORD)
+PG_KEYWORD("compression", COMPRESSION, UNRESERVED_KEYWORD)
PG_KEYWORD("concurrently", CONCURRENTLY, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("configuration", CONFIGURATION, UNRESERVED_KEYWORD)
PG_KEYWORD("conflict", CONFLICT, UNRESERVED_KEYWORD)
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index e749432ef0..5cab77457a 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -22,10 +22,12 @@ extern List *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
const char *queryString);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
+void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
extern List *transformCreateSchemaStmt(CreateSchemaStmt *stmt);
extern PartitionBoundSpec *transformPartitionBound(ParseState *pstate, Relation parent,
PartitionBoundSpec *spec);
+extern void transformColumnCompression(ColumnDef *column, RangeVar *relation,
+ AlterTableStmt **alterStmt);
#endif /* PARSE_UTILCMD_H */
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 1ca9b60ea1..f5c879ae60 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -146,9 +146,18 @@ typedef union
struct /* Compressed-in-line format */
{
uint32 va_header;
- uint32 va_rawsize; /* Original data size (excludes header) */
+ uint32 va_info; /* Original data size (excludes header) and
+ * flags */
char va_data[FLEXIBLE_ARRAY_MEMBER]; /* Compressed data */
} va_compressed;
+ struct /* Compressed-in-line format */
+ {
+ uint32 va_header;
+ uint32 va_info; /* Original data size (excludes header) and
+ * flags */
+ Oid va_cmoptoid; /* Oid of compression options */
+ char va_data[FLEXIBLE_ARRAY_MEMBER]; /* Compressed data */
+ } va_custom_compressed;
} varattrib_4b;
typedef struct
@@ -282,7 +291,12 @@ typedef struct
#define VARDATA_1B_E(PTR) (((varattrib_1b_e *) (PTR))->va_data)
#define VARRAWSIZE_4B_C(PTR) \
- (((varattrib_4b *) (PTR))->va_compressed.va_rawsize)
+ (((varattrib_4b *) (PTR))->va_compressed.va_info & 0x3FFFFFFF)
+#define VARFLAGS_4B_C(PTR) \
+ (((varattrib_4b *) (PTR))->va_compressed.va_info >> 30)
+
+#define VARHDRSZ_CUSTOM_COMPRESSED \
+ (offsetof(varattrib_4b, va_custom_compressed.va_data))
/* Externally visible macros */
@@ -311,6 +325,8 @@ typedef struct
#define VARDATA_EXTERNAL(PTR) VARDATA_1B_E(PTR)
#define VARATT_IS_COMPRESSED(PTR) VARATT_IS_4B_C(PTR)
+#define VARATT_IS_CUSTOM_COMPRESSED(PTR) (VARATT_IS_4B_C(PTR) && \
+ (VARFLAGS_4B_C(PTR) == 0x02))
#define VARATT_IS_EXTERNAL(PTR) VARATT_IS_1B_E(PTR)
#define VARATT_IS_EXTERNAL_ONDISK(PTR) \
(VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_ONDISK)
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 43273eaab5..fa3bbbc49e 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -202,6 +202,7 @@ typedef enum AclObjectKind
ACL_KIND_EXTENSION, /* pg_extension */
ACL_KIND_PUBLICATION, /* pg_publication */
ACL_KIND_SUBSCRIPTION, /* pg_subscription */
+ ACL_KIND_COMPRESSION_METHOD, /* pg_compression */
MAX_ACL_KIND /* MUST BE LAST */
} AclObjectKind;
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 07208b56ce..00db7fead1 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -166,6 +166,7 @@ extern void getTypeBinaryOutputInfo(Oid type, Oid *typSend, bool *typIsVarlena);
extern Oid get_typmodin(Oid typid);
extern Oid get_typcollation(Oid typid);
extern bool type_is_collatable(Oid typid);
+extern Oid get_base_typdefaultcm(HeapTuple typtup);
extern Oid getBaseType(Oid typid);
extern Oid getBaseTypeAndTypmod(Oid typid, int32 *typmod);
extern int32 get_typavgwidth(Oid typid, int32 typmod);
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 8a92ea27ac..889f9c775a 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -48,6 +48,9 @@ enum SysCacheIdentifier
CLAOID,
COLLNAMEENCNSP,
COLLOID,
+ COMPRESSIONMETHODOID,
+ COMPRESSIONMETHODNAME,
+ COMPRESSIONOPTIONSOID,
CONDEFAULT,
CONNAMENSP,
CONSTROID,
diff --git a/src/test/regress/expected/copy2.out b/src/test/regress/expected/copy2.out
index 65e9c626b3..112f0eda47 100644
--- a/src/test/regress/expected/copy2.out
+++ b/src/test/regress/expected/copy2.out
@@ -438,10 +438,10 @@ begin
end $$ language plpgsql immutable;
alter table check_con_tbl add check (check_con_function(check_con_tbl.*));
\d+ check_con_tbl
- Table "public.check_con_tbl"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- f1 | integer | | | | plain | |
+ Table "public.check_con_tbl"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ f1 | integer | | | | plain | | |
Check constraints:
"check_con_tbl_check" CHECK (check_con_function(check_con_tbl.*))
diff --git a/src/test/regress/expected/create_cm.out b/src/test/regress/expected/create_cm.out
new file mode 100644
index 0000000000..47c071abb2
--- /dev/null
+++ b/src/test/regress/expected/create_cm.out
@@ -0,0 +1,127 @@
+CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
+DROP COMPRESSION METHOD ts1;
+CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
+CREATE TABLE cmtest(fts tsvector COMPRESSED ts1);
+DROP COMPRESSION METHOD ts1;
+ERROR: cannot drop compression method ts1 because other objects depend on it
+DETAIL: compression options for ts1 depends on compression method ts1
+table cmtest column fts depends on compression options for ts1
+HINT: Use DROP ... CASCADE to drop the dependent objects too.
+SELECT * FROM pg_compression;
+ cmname | cmhandler
+--------+------------------------------
+ ts1 | tsvector_compression_handler
+(1 row)
+
+SELECT cmhandler, cmoptions FROM pg_compression_opt;
+ cmhandler | cmoptions
+------------------------------+-----------
+ tsvector_compression_handler |
+(1 row)
+
+\dCM
+List of compression methods
+ Name
+------
+ ts1
+(1 row)
+
+\d+ cmtest
+ Table "public.cmtest"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+
+INSERT INTO cmtest
+ SELECT to_tsvector(string_agg(repeat(substr(i::text,1,1), i), ' '))
+ FROM generate_series(1,200) i;
+SELECT length(fts) FROM cmtest;
+ length
+--------
+ 200
+(1 row)
+
+ALTER TABLE cmtest ALTER COLUMN fts SET NOT COMPRESSED;
+\d+ cmtest
+ Table "public.cmtest"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | | |
+
+ALTER TABLE cmtest ALTER COLUMN fts SET COMPRESSED ts1 WITH (format 'lz');
+ERROR: the compression method for tsvector doesn't take any options
+ALTER TABLE cmtest ALTER COLUMN fts SET COMPRESSED ts1;
+\d+ cmtest
+ Table "public.cmtest"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+
+SELECT * INTO cmtest2 FROM cmtest;
+CREATE TABLE cmtest3 (LIKE cmtest);
+CREATE TABLE cmtest4(fts tsvector, a int) inherits (cmtest);
+NOTICE: merging column "fts" with inherited definition
+\d+ cmtest3
+ Table "public.cmtest3"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+
+\d+ cmtest4
+ Table "public.cmtest4"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+ a | integer | | | | plain | | |
+Inherits: cmtest
+
+DROP TABLE cmtest CASCADE;
+NOTICE: drop cascades to table cmtest4
+SELECT length(fts) FROM cmtest2;
+ length
+--------
+ 200
+(1 row)
+
+SELECT * FROM pg_compression;
+ cmname | cmhandler
+--------+------------------------------
+ ts1 | tsvector_compression_handler
+(1 row)
+
+SELECT cmhandler, cmoptions FROM pg_compression_opt;
+ cmhandler | cmoptions
+------------------------------+-----------
+ tsvector_compression_handler |
+ tsvector_compression_handler |
+ tsvector_compression_handler |
+ tsvector_compression_handler |
+(4 rows)
+
+DROP TABLE cmtest2;
+DROP TABLE cmtest3;
+ALTER TYPE tsvector SET COMPRESSED ts1;
+CREATE TABLE cmtest(fts tsvector);
+\d+ cmtest
+ Table "public.cmtest"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+
+DROP TABLE cmtest;
+ALTER TYPE tsvector SET NOT COMPRESSED;
+CREATE TABLE cmtest(fts tsvector);
+\d+ cmtest
+ Table "public.cmtest"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | | |
+
+DROP TABLE cmtest;
+DROP COMPRESSION METHOD ts1 CASCADE;
+NOTICE: drop cascades to 5 other objects
+DETAIL: drop cascades to compression options for ts1
+drop cascades to compression options for ts1
+drop cascades to compression options for ts1
+drop cascades to compression options for ts1
+drop cascades to compression options for ts1
diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out
index 58c755be50..5f04ca3e2a 100644
--- a/src/test/regress/expected/create_table.out
+++ b/src/test/regress/expected/create_table.out
@@ -547,10 +547,10 @@ CREATE TABLE oids_parted (
) PARTITION BY RANGE (a) WITH OIDS;
CREATE TABLE part_forced_oids PARTITION OF oids_parted FOR VALUES FROM (1) TO (10) WITHOUT OIDS;
\d+ part_forced_oids
- Table "public.part_forced_oids"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
+ Table "public.part_forced_oids"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
Partition of: oids_parted FOR VALUES FROM (1) TO (10)
Partition constraint: ((a IS NOT NULL) AND (a >= 1) AND (a < 10))
Has OIDs: yes
@@ -669,11 +669,11 @@ CREATE TABLE part_c PARTITION OF parted (b WITH OPTIONS NOT NULL DEFAULT 0) FOR
CREATE TABLE part_c_1_10 PARTITION OF part_c FOR VALUES FROM (1) TO (10);
-- Partition bound in describe output
\d+ part_b
- Table "public.part_b"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | not null | 1 | plain | |
+ Table "public.part_b"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | not null | 1 | plain | | |
Partition of: parted FOR VALUES IN ('b')
Partition constraint: ((a IS NOT NULL) AND (a = ANY (ARRAY['b'::text])))
Check constraints:
@@ -696,11 +696,11 @@ Partitions: part_c_1_10 FOR VALUES FROM (1) TO (10)
-- a level-2 partition's constraint will include the parent's expressions
\d+ part_c_1_10
- Table "public.part_c_1_10"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | not null | 0 | plain | |
+ Table "public.part_c_1_10"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | not null | 0 | plain | | |
Partition of: part_c FOR VALUES FROM (1) TO (10)
Partition constraint: ((a IS NOT NULL) AND (a = ANY (ARRAY['c'::text])) AND (b IS NOT NULL) AND (b >= 1) AND (b < 10))
Check constraints:
@@ -725,46 +725,46 @@ Number of partitions: 3 (Use \d+ to list them.)
CREATE TABLE range_parted4 (a int, b int, c int) PARTITION BY RANGE (abs(a), abs(b), c);
CREATE TABLE unbounded_range_part PARTITION OF range_parted4 FOR VALUES FROM (MINVALUE, 0, 0) TO (MAXVALUE, 0, 0);
\d+ unbounded_range_part
- Table "public.unbounded_range_part"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
+ Table "public.unbounded_range_part"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
Partition of: range_parted4 FOR VALUES FROM (MINVALUE, 0, 0) TO (MAXVALUE, 0, 0)
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL))
DROP TABLE unbounded_range_part;
CREATE TABLE range_parted4_1 PARTITION OF range_parted4 FOR VALUES FROM (MINVALUE, 0, 0) TO (1, MAXVALUE, 0);
\d+ range_parted4_1
- Table "public.range_parted4_1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
+ Table "public.range_parted4_1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
Partition of: range_parted4 FOR VALUES FROM (MINVALUE, 0, 0) TO (1, MAXVALUE, 0)
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND (abs(a) <= 1))
CREATE TABLE range_parted4_2 PARTITION OF range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, MAXVALUE);
\d+ range_parted4_2
- Table "public.range_parted4_2"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
+ Table "public.range_parted4_2"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
Partition of: range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, MAXVALUE)
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND ((abs(a) > 3) OR ((abs(a) = 3) AND (abs(b) > 4)) OR ((abs(a) = 3) AND (abs(b) = 4) AND (c >= 5))) AND ((abs(a) < 6) OR ((abs(a) = 6) AND (abs(b) <= 7))))
CREATE TABLE range_parted4_3 PARTITION OF range_parted4 FOR VALUES FROM (6, 8, MINVALUE) TO (9, MAXVALUE, 0);
\d+ range_parted4_3
- Table "public.range_parted4_3"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
+ Table "public.range_parted4_3"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
Partition of: range_parted4 FOR VALUES FROM (6, 8, MINVALUE) TO (9, MAXVALUE, 0)
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND ((abs(a) > 6) OR ((abs(a) = 6) AND (abs(b) >= 8))) AND (abs(a) <= 9))
diff --git a/src/test/regress/expected/create_table_like.out b/src/test/regress/expected/create_table_like.out
index 3f405c94ce..b5ca8b820b 100644
--- a/src/test/regress/expected/create_table_like.out
+++ b/src/test/regress/expected/create_table_like.out
@@ -156,32 +156,32 @@ CREATE TABLE ctlt4 (a text, c text);
ALTER TABLE ctlt4 ALTER COLUMN c SET STORAGE EXTERNAL;
CREATE TABLE ctlt12_storage (LIKE ctlt1 INCLUDING STORAGE, LIKE ctlt2 INCLUDING STORAGE);
\d+ ctlt12_storage
- Table "public.ctlt12_storage"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | |
- b | text | | | | extended | |
- c | text | | | | external | |
+ Table "public.ctlt12_storage"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | |
+ b | text | | | | extended | | |
+ c | text | | | | external | | |
CREATE TABLE ctlt12_comments (LIKE ctlt1 INCLUDING COMMENTS, LIKE ctlt2 INCLUDING COMMENTS);
\d+ ctlt12_comments
- Table "public.ctlt12_comments"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | extended | | A
- b | text | | | | extended | | B
- c | text | | | | extended | | C
+ Table "public.ctlt12_comments"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | extended | | | A
+ b | text | | | | extended | | | B
+ c | text | | | | extended | | | C
CREATE TABLE ctlt1_inh (LIKE ctlt1 INCLUDING CONSTRAINTS INCLUDING COMMENTS) INHERITS (ctlt1);
NOTICE: merging column "a" with inherited definition
NOTICE: merging column "b" with inherited definition
NOTICE: merging constraint "ctlt1_a_check" with inherited definition
\d+ ctlt1_inh
- Table "public.ctlt1_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | | A
- b | text | | | | extended | | B
+ Table "public.ctlt1_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | | A
+ b | text | | | | extended | | | B
Check constraints:
"ctlt1_a_check" CHECK (length(a) > 2)
Inherits: ctlt1
@@ -195,12 +195,12 @@ SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_con
CREATE TABLE ctlt13_inh () INHERITS (ctlt1, ctlt3);
NOTICE: merging multiple inherited definitions of column "a"
\d+ ctlt13_inh
- Table "public.ctlt13_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | |
- b | text | | | | extended | |
- c | text | | | | external | |
+ Table "public.ctlt13_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | |
+ b | text | | | | extended | | |
+ c | text | | | | external | | |
Check constraints:
"ctlt1_a_check" CHECK (length(a) > 2)
"ctlt3_a_check" CHECK (length(a) < 5)
@@ -210,12 +210,12 @@ Inherits: ctlt1,
CREATE TABLE ctlt13_like (LIKE ctlt3 INCLUDING CONSTRAINTS INCLUDING COMMENTS INCLUDING STORAGE) INHERITS (ctlt1);
NOTICE: merging column "a" with inherited definition
\d+ ctlt13_like
- Table "public.ctlt13_like"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | | A3
- b | text | | | | extended | |
- c | text | | | | external | | C
+ Table "public.ctlt13_like"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | | A3
+ b | text | | | | extended | | |
+ c | text | | | | external | | | C
Check constraints:
"ctlt1_a_check" CHECK (length(a) > 2)
"ctlt3_a_check" CHECK (length(a) < 5)
@@ -229,11 +229,11 @@ SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_con
CREATE TABLE ctlt_all (LIKE ctlt1 INCLUDING ALL);
\d+ ctlt_all
- Table "public.ctlt_all"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | | A
- b | text | | | | extended | | B
+ Table "public.ctlt_all"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | | A
+ b | text | | | | extended | | | B
Indexes:
"ctlt_all_pkey" PRIMARY KEY, btree (a)
"ctlt_all_b_idx" btree (b)
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index 3acc696863..fe873fa5e1 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -296,10 +296,10 @@ create rule silly as on delete to dcomptable do instead
update dcomptable set d1[1].r = d1[1].r - 1, d1[1].i = d1[1].i + 1
where d1[1].i > 0;
\d+ dcomptable
- Table "public.dcomptable"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------------+-----------+----------+---------+----------+--------------+-------------
- d1 | dcomptypea | | | | extended | |
+ Table "public.dcomptable"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------------+-----------+----------+---------+----------+-------------+--------------+-------------
+ d1 | dcomptypea | | | | extended | | |
Indexes:
"dcomptable_d1_key" UNIQUE CONSTRAINT, btree (d1)
Rules:
diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out
index c6e558b07f..f147740a87 100644
--- a/src/test/regress/expected/foreign_data.out
+++ b/src/test/regress/expected/foreign_data.out
@@ -1314,12 +1314,12 @@ CREATE TABLE pt1 (
CREATE FOREIGN TABLE ft2 () INHERITS (pt1)
SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value');
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Child tables: ft2
\d+ ft2
@@ -1335,12 +1335,12 @@ Inherits: pt1
DROP FOREIGN TABLE ft2;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
CREATE FOREIGN TABLE ft2 (
c1 integer NOT NULL,
@@ -1359,12 +1359,12 @@ FDW options: (delimiter ',', quote '"', "be quoted" 'value')
ALTER FOREIGN TABLE ft2 INHERIT pt1;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Child tables: ft2
\d+ ft2
@@ -1402,12 +1402,12 @@ Child tables: ct3,
ft3
\d+ ct3
- Table "public.ct3"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.ct3"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Inherits: ft2
\d+ ft3
@@ -1427,17 +1427,17 @@ ALTER TABLE pt1 ADD COLUMN c6 integer;
ALTER TABLE pt1 ADD COLUMN c7 integer NOT NULL;
ALTER TABLE pt1 ADD COLUMN c8 integer;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
- c4 | integer | | | | plain | |
- c5 | integer | | | 0 | plain | |
- c6 | integer | | | | plain | |
- c7 | integer | | not null | | plain | |
- c8 | integer | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
+ c4 | integer | | | | plain | | |
+ c5 | integer | | | 0 | plain | | |
+ c6 | integer | | | | plain | | |
+ c7 | integer | | not null | | plain | | |
+ c8 | integer | | | | plain | | |
Child tables: ft2
\d+ ft2
@@ -1459,17 +1459,17 @@ Child tables: ct3,
ft3
\d+ ct3
- Table "public.ct3"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
- c4 | integer | | | | plain | |
- c5 | integer | | | 0 | plain | |
- c6 | integer | | | | plain | |
- c7 | integer | | not null | | plain | |
- c8 | integer | | | | plain | |
+ Table "public.ct3"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
+ c4 | integer | | | | plain | | |
+ c5 | integer | | | 0 | plain | | |
+ c6 | integer | | | | plain | | |
+ c7 | integer | | not null | | plain | | |
+ c8 | integer | | | | plain | | |
Inherits: ft2
\d+ ft3
@@ -1501,17 +1501,17 @@ ALTER TABLE pt1 ALTER COLUMN c1 SET (n_distinct = 100);
ALTER TABLE pt1 ALTER COLUMN c8 SET STATISTICS -1;
ALTER TABLE pt1 ALTER COLUMN c8 SET STORAGE EXTERNAL;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
- c4 | integer | | | 0 | plain | |
- c5 | integer | | | | plain | |
- c6 | integer | | not null | | plain | |
- c7 | integer | | | | plain | |
- c8 | text | | | | external | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
+ c4 | integer | | | 0 | plain | | |
+ c5 | integer | | | | plain | | |
+ c6 | integer | | not null | | plain | | |
+ c7 | integer | | | | plain | | |
+ c8 | text | | | | external | | |
Child tables: ft2
\d+ ft2
@@ -1539,12 +1539,12 @@ ALTER TABLE pt1 DROP COLUMN c6;
ALTER TABLE pt1 DROP COLUMN c7;
ALTER TABLE pt1 DROP COLUMN c8;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Child tables: ft2
\d+ ft2
@@ -1576,12 +1576,12 @@ SELECT relname, conname, contype, conislocal, coninhcount, connoinherit
-- child does not inherit NO INHERIT constraints
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk1" CHECK (c1 > 0) NO INHERIT
"pt1chk2" CHECK (c2 <> ''::text)
@@ -1620,12 +1620,12 @@ ALTER FOREIGN TABLE ft2 ADD CONSTRAINT pt1chk2 CHECK (c2 <> '');
ALTER FOREIGN TABLE ft2 INHERIT pt1;
-- child does not inherit NO INHERIT constraints
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk1" CHECK (c1 > 0) NO INHERIT
"pt1chk2" CHECK (c2 <> ''::text)
@@ -1651,12 +1651,12 @@ ALTER TABLE pt1 DROP CONSTRAINT pt1chk2 CASCADE;
INSERT INTO pt1 VALUES (1, 'pt1'::text, '1994-01-01'::date);
ALTER TABLE pt1 ADD CONSTRAINT pt1chk3 CHECK (c2 <> '') NOT VALID;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk3" CHECK (c2 <> ''::text) NOT VALID
Child tables: ft2
@@ -1678,12 +1678,12 @@ Inherits: pt1
-- VALIDATE CONSTRAINT need do nothing on foreign tables
ALTER TABLE pt1 VALIDATE CONSTRAINT pt1chk3;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk3" CHECK (c2 <> ''::text)
Child tables: ft2
@@ -1705,12 +1705,12 @@ Inherits: pt1
-- OID system column
ALTER TABLE pt1 SET WITH OIDS;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk3" CHECK (c2 <> ''::text)
Child tables: ft2
@@ -1735,12 +1735,12 @@ ALTER TABLE ft2 SET WITHOUT OIDS; -- ERROR
ERROR: cannot drop inherited column "oid"
ALTER TABLE pt1 SET WITHOUT OIDS;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk3" CHECK (c2 <> ''::text)
Child tables: ft2
@@ -1766,12 +1766,12 @@ ALTER TABLE pt1 RENAME COLUMN c3 TO f3;
-- changes name of a constraint recursively
ALTER TABLE pt1 RENAME CONSTRAINT pt1chk3 TO f2_check;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- f1 | integer | | not null | | plain | 10000 |
- f2 | text | | | | extended | |
- f3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ f1 | integer | | not null | | plain | | 10000 |
+ f2 | text | | | | extended | | |
+ f3 | date | | | | plain | | |
Check constraints:
"f2_check" CHECK (f2 <> ''::text)
Child tables: ft2
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index 1fa9650ec9..b472007866 100644
--- a/src/test/regress/expected/inherit.out
+++ b/src/test/regress/expected/inherit.out
@@ -1001,13 +1001,13 @@ ALTER TABLE inhts RENAME aa TO aaa; -- to be failed
ERROR: cannot rename inherited column "aa"
ALTER TABLE inhts RENAME d TO dd;
\d+ inhts
- Table "public.inhts"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- aa | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
- dd | integer | | | | plain | |
+ Table "public.inhts"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ aa | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
+ dd | integer | | | | plain | | |
Inherits: inht1,
inhs1
@@ -1020,14 +1020,14 @@ NOTICE: merging multiple inherited definitions of column "aa"
NOTICE: merging multiple inherited definitions of column "b"
ALTER TABLE inht1 RENAME aa TO aaa;
\d+ inht4
- Table "public.inht4"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- aaa | integer | | | | plain | |
- b | integer | | | | plain | |
- x | integer | | | | plain | |
- y | integer | | | | plain | |
- z | integer | | | | plain | |
+ Table "public.inht4"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ aaa | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ x | integer | | | | plain | | |
+ y | integer | | | | plain | | |
+ z | integer | | | | plain | | |
Inherits: inht2,
inht3
@@ -1037,14 +1037,14 @@ ALTER TABLE inht1 RENAME aaa TO aaaa;
ALTER TABLE inht1 RENAME b TO bb; -- to be failed
ERROR: cannot rename inherited column "b"
\d+ inhts
- Table "public.inhts"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- aaaa | integer | | | | plain | |
- b | integer | | | | plain | |
- x | integer | | | | plain | |
- c | integer | | | | plain | |
- d | integer | | | | plain | |
+ Table "public.inhts"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ aaaa | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ x | integer | | | | plain | | |
+ c | integer | | | | plain | | |
+ d | integer | | | | plain | | |
Inherits: inht2,
inhs1
@@ -1084,33 +1084,33 @@ drop cascades to table inht4
CREATE TABLE test_constraints (id int, val1 varchar, val2 int, UNIQUE(val1, val2));
CREATE TABLE test_constraints_inh () INHERITS (test_constraints);
\d+ test_constraints
- Table "public.test_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+-------------------+-----------+----------+---------+----------+--------------+-------------
- id | integer | | | | plain | |
- val1 | character varying | | | | extended | |
- val2 | integer | | | | plain | |
+ Table "public.test_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+-------------------+-----------+----------+---------+----------+-------------+--------------+-------------
+ id | integer | | | | plain | | |
+ val1 | character varying | | | | extended | | |
+ val2 | integer | | | | plain | | |
Indexes:
"test_constraints_val1_val2_key" UNIQUE CONSTRAINT, btree (val1, val2)
Child tables: test_constraints_inh
ALTER TABLE ONLY test_constraints DROP CONSTRAINT test_constraints_val1_val2_key;
\d+ test_constraints
- Table "public.test_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+-------------------+-----------+----------+---------+----------+--------------+-------------
- id | integer | | | | plain | |
- val1 | character varying | | | | extended | |
- val2 | integer | | | | plain | |
+ Table "public.test_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+-------------------+-----------+----------+---------+----------+-------------+--------------+-------------
+ id | integer | | | | plain | | |
+ val1 | character varying | | | | extended | | |
+ val2 | integer | | | | plain | | |
Child tables: test_constraints_inh
\d+ test_constraints_inh
- Table "public.test_constraints_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+-------------------+-----------+----------+---------+----------+--------------+-------------
- id | integer | | | | plain | |
- val1 | character varying | | | | extended | |
- val2 | integer | | | | plain | |
+ Table "public.test_constraints_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+-------------------+-----------+----------+---------+----------+-------------+--------------+-------------
+ id | integer | | | | plain | | |
+ val1 | character varying | | | | extended | | |
+ val2 | integer | | | | plain | | |
Inherits: test_constraints
DROP TABLE test_constraints_inh;
@@ -1121,27 +1121,27 @@ CREATE TABLE test_ex_constraints (
);
CREATE TABLE test_ex_constraints_inh () INHERITS (test_ex_constraints);
\d+ test_ex_constraints
- Table "public.test_ex_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+--------+-----------+----------+---------+---------+--------------+-------------
- c | circle | | | | plain | |
+ Table "public.test_ex_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+--------+-----------+----------+---------+---------+-------------+--------------+-------------
+ c | circle | | | | plain | | |
Indexes:
"test_ex_constraints_c_excl" EXCLUDE USING gist (c WITH &&)
Child tables: test_ex_constraints_inh
ALTER TABLE test_ex_constraints DROP CONSTRAINT test_ex_constraints_c_excl;
\d+ test_ex_constraints
- Table "public.test_ex_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+--------+-----------+----------+---------+---------+--------------+-------------
- c | circle | | | | plain | |
+ Table "public.test_ex_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+--------+-----------+----------+---------+---------+-------------+--------------+-------------
+ c | circle | | | | plain | | |
Child tables: test_ex_constraints_inh
\d+ test_ex_constraints_inh
- Table "public.test_ex_constraints_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+--------+-----------+----------+---------+---------+--------------+-------------
- c | circle | | | | plain | |
+ Table "public.test_ex_constraints_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+--------+-----------+----------+---------+---------+-------------+--------------+-------------
+ c | circle | | | | plain | | |
Inherits: test_ex_constraints
DROP TABLE test_ex_constraints_inh;
@@ -1151,37 +1151,37 @@ CREATE TABLE test_primary_constraints(id int PRIMARY KEY);
CREATE TABLE test_foreign_constraints(id1 int REFERENCES test_primary_constraints(id));
CREATE TABLE test_foreign_constraints_inh () INHERITS (test_foreign_constraints);
\d+ test_primary_constraints
- Table "public.test_primary_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- id | integer | | not null | | plain | |
+ Table "public.test_primary_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ id | integer | | not null | | plain | | |
Indexes:
"test_primary_constraints_pkey" PRIMARY KEY, btree (id)
Referenced by:
TABLE "test_foreign_constraints" CONSTRAINT "test_foreign_constraints_id1_fkey" FOREIGN KEY (id1) REFERENCES test_primary_constraints(id)
\d+ test_foreign_constraints
- Table "public.test_foreign_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- id1 | integer | | | | plain | |
+ Table "public.test_foreign_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ id1 | integer | | | | plain | | |
Foreign-key constraints:
"test_foreign_constraints_id1_fkey" FOREIGN KEY (id1) REFERENCES test_primary_constraints(id)
Child tables: test_foreign_constraints_inh
ALTER TABLE test_foreign_constraints DROP CONSTRAINT test_foreign_constraints_id1_fkey;
\d+ test_foreign_constraints
- Table "public.test_foreign_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- id1 | integer | | | | plain | |
+ Table "public.test_foreign_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ id1 | integer | | | | plain | | |
Child tables: test_foreign_constraints_inh
\d+ test_foreign_constraints_inh
- Table "public.test_foreign_constraints_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- id1 | integer | | | | plain | |
+ Table "public.test_foreign_constraints_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ id1 | integer | | | | plain | | |
Inherits: test_foreign_constraints
DROP TABLE test_foreign_constraints_inh;
diff --git a/src/test/regress/expected/insert.out b/src/test/regress/expected/insert.out
index 73a5600f19..18699c5069 100644
--- a/src/test/regress/expected/insert.out
+++ b/src/test/regress/expected/insert.out
@@ -142,11 +142,11 @@ create rule irule3 as on insert to inserttest2 do also
insert into inserttest (f4[1].if1, f4[1].if2[2])
select new.f1, new.f2;
\d+ inserttest2
- Table "public.inserttest2"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+--------+-----------+----------+---------+----------+--------------+-------------
- f1 | bigint | | | | plain | |
- f2 | text | | | | extended | |
+ Table "public.inserttest2"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+--------+-----------+----------+---------+----------+-------------+--------------+-------------
+ f1 | bigint | | | | plain | | |
+ f2 | text | | | | extended | | |
Rules:
irule1 AS
ON INSERT TO inserttest2 DO INSERT INTO inserttest (f3.if2[1], f3.if2[2])
@@ -389,10 +389,10 @@ drop table range_parted, list_parted;
create table list_parted (a int) partition by list (a);
create table part_default partition of list_parted default;
\d+ part_default
- Table "public.part_default"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
+ Table "public.part_default"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
Partition of: list_parted DEFAULT
No partition constraint
@@ -691,74 +691,74 @@ Partitions: mcrparted1_lt_b FOR VALUES FROM (MINVALUE, 0) TO ('b', MINVALUE),
mcrparted8_ge_d FOR VALUES FROM ('d', MINVALUE) TO (MAXVALUE, 0)
\d+ mcrparted1_lt_b
- Table "public.mcrparted1_lt_b"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted1_lt_b"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM (MINVALUE, 0) TO ('b', MINVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a < 'b'::text))
\d+ mcrparted2_b
- Table "public.mcrparted2_b"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted2_b"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('b', MINVALUE) TO ('c', MINVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'b'::text) AND (a < 'c'::text))
\d+ mcrparted3_c_to_common
- Table "public.mcrparted3_c_to_common"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted3_c_to_common"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('c', MINVALUE) TO ('common', MINVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'c'::text) AND (a < 'common'::text))
\d+ mcrparted4_common_lt_0
- Table "public.mcrparted4_common_lt_0"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted4_common_lt_0"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('common', MINVALUE) TO ('common', 0)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::text) AND (b < 0))
\d+ mcrparted5_common_0_to_10
- Table "public.mcrparted5_common_0_to_10"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted5_common_0_to_10"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('common', 0) TO ('common', 10)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::text) AND (b >= 0) AND (b < 10))
\d+ mcrparted6_common_ge_10
- Table "public.mcrparted6_common_ge_10"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted6_common_ge_10"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('common', 10) TO ('common', MAXVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::text) AND (b >= 10))
\d+ mcrparted7_gt_common_lt_d
- Table "public.mcrparted7_gt_common_lt_d"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted7_gt_common_lt_d"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('common', MAXVALUE) TO ('d', MINVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a > 'common'::text) AND (a < 'd'::text))
\d+ mcrparted8_ge_d
- Table "public.mcrparted8_ge_d"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted8_ge_d"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('d', MINVALUE) TO (MAXVALUE, 0)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'd'::text))
diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out
index b101331d69..f805e13d75 100644
--- a/src/test/regress/expected/publication.out
+++ b/src/test/regress/expected/publication.out
@@ -65,11 +65,11 @@ SELECT pubname, puballtables FROM pg_publication WHERE pubname = 'testpub_forall
(1 row)
\d+ testpub_tbl2
- Table "public.testpub_tbl2"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('testpub_tbl2_id_seq'::regclass) | plain | |
- data | text | | | | extended | |
+ Table "public.testpub_tbl2"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('testpub_tbl2_id_seq'::regclass) | plain | | |
+ data | text | | | | extended | | |
Indexes:
"testpub_tbl2_pkey" PRIMARY KEY, btree (id)
Publications:
@@ -141,22 +141,22 @@ ALTER PUBLICATION testpub_default SET TABLE testpub_tbl1;
ALTER PUBLICATION testpub_default ADD TABLE pub_test.testpub_nopk;
ALTER PUBLICATION testpib_ins_trunct ADD TABLE pub_test.testpub_nopk, testpub_tbl1;
\d+ pub_test.testpub_nopk
- Table "pub_test.testpub_nopk"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- foo | integer | | | | plain | |
- bar | integer | | | | plain | |
+ Table "pub_test.testpub_nopk"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ foo | integer | | | | plain | | |
+ bar | integer | | | | plain | | |
Publications:
"testpib_ins_trunct"
"testpub_default"
"testpub_fortbl"
\d+ testpub_tbl1
- Table "public.testpub_tbl1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | |
- data | text | | | | extended | |
+ Table "public.testpub_tbl1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | | |
+ data | text | | | | extended | | |
Indexes:
"testpub_tbl1_pkey" PRIMARY KEY, btree (id)
Publications:
@@ -178,11 +178,11 @@ ALTER PUBLICATION testpub_default DROP TABLE testpub_tbl1, pub_test.testpub_nopk
ALTER PUBLICATION testpub_default DROP TABLE pub_test.testpub_nopk;
ERROR: relation "testpub_nopk" is not part of the publication
\d+ testpub_tbl1
- Table "public.testpub_tbl1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | |
- data | text | | | | extended | |
+ Table "public.testpub_tbl1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | | |
+ data | text | | | | extended | | |
Indexes:
"testpub_tbl1_pkey" PRIMARY KEY, btree (id)
Publications:
diff --git a/src/test/regress/expected/replica_identity.out b/src/test/regress/expected/replica_identity.out
index 67c34a92a4..1526437bf8 100644
--- a/src/test/regress/expected/replica_identity.out
+++ b/src/test/regress/expected/replica_identity.out
@@ -158,13 +158,13 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass;
(1 row)
\d+ test_replica_identity
- Table "public.test_replica_identity"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('test_replica_identity_id_seq'::regclass) | plain | |
- keya | text | | not null | | extended | |
- keyb | text | | not null | | extended | |
- nonkey | text | | | | extended | |
+ Table "public.test_replica_identity"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('test_replica_identity_id_seq'::regclass) | plain | | |
+ keya | text | | not null | | extended | | |
+ keyb | text | | not null | | extended | | |
+ nonkey | text | | | | extended | | |
Indexes:
"test_replica_identity_pkey" PRIMARY KEY, btree (id)
"test_replica_identity_expr" UNIQUE, btree (keya, keyb, (3))
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index f1c1b44d6f..e358ff539c 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2799,11 +2799,11 @@ select * from rules_log;
create rule r3 as on delete to rules_src do notify rules_src_deletion;
\d+ rules_src
- Table "public.rules_src"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- f1 | integer | | | | plain | |
- f2 | integer | | | | plain | |
+ Table "public.rules_src"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ f1 | integer | | | | plain | | |
+ f2 | integer | | | | plain | | |
Rules:
r1 AS
ON UPDATE TO rules_src DO INSERT INTO rules_log (f1, f2, tag) VALUES (old.f1,old.f2,'old'::text), (new.f1,new.f2,'new'::text)
@@ -2819,11 +2819,11 @@ Rules:
create rule r4 as on insert to rules_src do instead insert into rules_log AS trgt SELECT NEW.* RETURNING trgt.f1, trgt.f2;
create rule r5 as on update to rules_src do instead UPDATE rules_log AS trgt SET tag = 'updated' WHERE trgt.f1 = new.f1;
\d+ rules_src
- Table "public.rules_src"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- f1 | integer | | | | plain | |
- f2 | integer | | | | plain | |
+ Table "public.rules_src"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ f1 | integer | | | | plain | | |
+ f2 | integer | | | | plain | | |
Rules:
r1 AS
ON UPDATE TO rules_src DO INSERT INTO rules_log (f1, f2, tag) VALUES (old.f1,old.f2,'old'::text), (new.f1,new.f2,'new'::text)
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index e996640593..62b06da011 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -111,6 +111,8 @@ pg_authid|t
pg_cast|t
pg_class|t
pg_collation|t
+pg_compression|t
+pg_compression_opt|t
pg_constraint|t
pg_conversion|t
pg_database|t
diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out
index cef70b1a1e..a652883172 100644
--- a/src/test/regress/expected/update.out
+++ b/src/test/regress/expected/update.out
@@ -221,11 +221,11 @@ update range_parted set b = b + 1 where b = 10;
-- Creating default partition for range
create table part_def partition of range_parted default;
\d+ part_def
- Table "public.part_def"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.part_def"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: range_parted DEFAULT
Partition constraint: (NOT (((a = 'a'::text) AND (b >= 1) AND (b < 10)) OR ((a = 'a'::text) AND (b >= 10) AND (b < 20)) OR ((a = 'b'::text) AND (b >= 1) AND (b < 10)) OR ((a = 'b'::text) AND (b >= 10) AND (b < 20))))
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 2fd3f2b1b1..9a4bacc54b 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -30,7 +30,7 @@ test: point lseg line box path polygon circle date time timetz timestamp timesta
# geometry depends on point, lseg, box, path, polygon and circle
# horology depends on interval, timetz, timestamp, timestamptz, reltime and abstime
# ----------
-test: geometry horology regex oidjoins type_sanity opr_sanity misc_sanity comments expressions
+test: geometry horology regex oidjoins type_sanity opr_sanity misc_sanity comments expressions create_cm
# ----------
# These four each depend on the previous one
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 76b0de30a7..b49808e814 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -43,6 +43,7 @@ test: inet
test: macaddr
test: macaddr8
test: tstypes
+test: create_cm
test: geometry
test: horology
test: regex
diff --git a/src/test/regress/sql/create_cm.sql b/src/test/regress/sql/create_cm.sql
new file mode 100644
index 0000000000..50fafe36d7
--- /dev/null
+++ b/src/test/regress/sql/create_cm.sql
@@ -0,0 +1,47 @@
+CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
+DROP COMPRESSION METHOD ts1;
+CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
+CREATE TABLE cmtest(fts tsvector COMPRESSED ts1);
+DROP COMPRESSION METHOD ts1;
+SELECT * FROM pg_compression;
+SELECT cmhandler, cmoptions FROM pg_compression_opt;
+
+\dCM
+\d+ cmtest
+
+INSERT INTO cmtest
+ SELECT to_tsvector(string_agg(repeat(substr(i::text,1,1), i), ' '))
+ FROM generate_series(1,200) i;
+SELECT length(fts) FROM cmtest;
+
+ALTER TABLE cmtest ALTER COLUMN fts SET NOT COMPRESSED;
+\d+ cmtest
+ALTER TABLE cmtest ALTER COLUMN fts SET COMPRESSED ts1 WITH (format 'lz');
+ALTER TABLE cmtest ALTER COLUMN fts SET COMPRESSED ts1;
+\d+ cmtest
+
+SELECT * INTO cmtest2 FROM cmtest;
+CREATE TABLE cmtest3 (LIKE cmtest);
+CREATE TABLE cmtest4(fts tsvector, a int) inherits (cmtest);
+\d+ cmtest3
+\d+ cmtest4
+DROP TABLE cmtest CASCADE;
+
+SELECT length(fts) FROM cmtest2;
+SELECT * FROM pg_compression;
+SELECT cmhandler, cmoptions FROM pg_compression_opt;
+
+DROP TABLE cmtest2;
+DROP TABLE cmtest3;
+
+ALTER TYPE tsvector SET COMPRESSED ts1;
+CREATE TABLE cmtest(fts tsvector);
+\d+ cmtest
+DROP TABLE cmtest;
+
+ALTER TYPE tsvector SET NOT COMPRESSED;
+CREATE TABLE cmtest(fts tsvector);
+\d+ cmtest
+DROP TABLE cmtest;
+
+DROP COMPRESSION METHOD ts1 CASCADE;
On 9/12/17 10:55, Ildus Kurbangaliev wrote:
The patch also includes custom compression method for tsvector which
is used in tests.[1]
/messages/by-id/CAPpHfdsdTA5uZeq6MNXL5ZRuNx+Sig4ykWzWEAfkC6ZKMDy6=Q@mail.gmail.comAttached rebased version of the patch. Added support of pg_dump, the
code was simplified, and a separate cache for compression options was
added.
I would like to see some more examples of how this would be used, so we
can see how it should all fit together.
So far, it's not clear to me that we need a compression method as a
standalone top-level object. It would make sense, perhaps, to have a
compression function attached to a type, so a type can provide a
compression function that is suitable for its specific storage.
The proposal here is very general: You can use any of the eligible
compression methods for any attribute. That seems very complicated to
manage. Any attribute could be compressed using either a choice of
general compression methods or a type-specific compression method, or
perhaps another type-specific compression method. That's a lot. Is
this about packing certain types better, or trying out different
compression algorithms, or about changing the TOAST thresholds, and so on?
Ideally, we would like something that just works, with minimal
configuration and nudging. Let's see a list of problems to be solved
and then we can discuss what the right set of primitives might be to
address them.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Wed, 1 Nov 2017 17:05:58 -0400
Peter Eisentraut <peter.eisentraut@2ndquadrant.com> wrote:
On 9/12/17 10:55, Ildus Kurbangaliev wrote:
The patch also includes custom compression method for tsvector
which is used in tests.[1]
/messages/by-id/CAPpHfdsdTA5uZeq6MNXL5ZRuNx+Sig4ykWzWEAfkC6ZKMDy6=Q@mail.gmail.comAttached rebased version of the patch. Added support of pg_dump, the
code was simplified, and a separate cache for compression options
was added.I would like to see some more examples of how this would be used, so
we can see how it should all fit together.So far, it's not clear to me that we need a compression method as a
standalone top-level object. It would make sense, perhaps, to have a
compression function attached to a type, so a type can provide a
compression function that is suitable for its specific storage.
In this patch compression methods is suitable for MAIN and EXTENDED
storages like in current implementation in postgres. Just instead only
of LZ4 you can specify any other compression method.
Idea is not to change compression for some types, but give the user and
extension developers opportunity to change how data in some attribute
will be compressed because they know about it more than database itself.
The proposal here is very general: You can use any of the eligible
compression methods for any attribute. That seems very complicated to
manage. Any attribute could be compressed using either a choice of
general compression methods or a type-specific compression method, or
perhaps another type-specific compression method. That's a lot. Is
this about packing certain types better, or trying out different
compression algorithms, or about changing the TOAST thresholds, and
so on?
It is about extensibility of postgres, for example if you
need to store a lot of time series data you can create an extension that
stores array of timestamps in more optimized way, using delta encoding
or something else. I'm not sure that such specialized things should be
in core.
In case of array of timestamps in could look like this:
CREATE EXTENSION timeseries; -- some extension that provides compression
method
Extension installs a compression method:
CREATE OR REPLACE FUNCTION timestamps_compression_handler(INTERNAL)
RETURNS COMPRESSION_HANDLER AS 'MODULE_PATHNAME',
'timestamps_compression_handler' LANGUAGE C STRICT;
CREATE COMPRESSION METHOD cm1 HANDLER timestamps_compression_handler;
And user can specify it in his table:
CREATE TABLE t1 (
time_series_data timestamp[] COMPRESSED cm1;
)
I think generalization of some method to a type is not a good idea. For
some attribute you could be happy with builtin LZ4, for other you can
need more compressibility and so on.
--
---
Ildus Kurbangaliev
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Tue, 12 Sep 2017 17:55:05 +0300
Ildus Kurbangaliev <i.kurbangaliev@postgrespro.ru> wrote:
Attached rebased version of the patch. Added support of pg_dump, the
code was simplified, and a separate cache for compression options was
added.
Attached version 3 of the patch. Rebased to the current master, removed
ALTER TYPE .. SET COMPRESSED syntax, fixed bug in compression options
cache.
--
---
Ildus Kurbangaliev
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company
Attachments:
custom_compression_methods_v3.patchtext/x-patchDownload
diff --git a/contrib/test_decoding/expected/ddl.out b/contrib/test_decoding/expected/ddl.out
index 1e22c1eefc..766ced401f 100644
--- a/contrib/test_decoding/expected/ddl.out
+++ b/contrib/test_decoding/expected/ddl.out
@@ -416,12 +416,12 @@ CREATE TABLE replication_metadata (
WITH (user_catalog_table = true)
;
\d+ replication_metadata
- Table "public.replication_metadata"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------+---------+-----------+----------+--------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | |
- relation | name | | not null | | plain | |
- options | text[] | | | | extended | |
+ Table "public.replication_metadata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+----------+---------+-----------+----------+--------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | |
+ relation | name | | not null | | plain | | |
+ options | text[] | | | | extended | | |
Indexes:
"replication_metadata_pkey" PRIMARY KEY, btree (id)
Options: user_catalog_table=true
@@ -430,12 +430,12 @@ INSERT INTO replication_metadata(relation, options)
VALUES ('foo', ARRAY['a', 'b']);
ALTER TABLE replication_metadata RESET (user_catalog_table);
\d+ replication_metadata
- Table "public.replication_metadata"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------+---------+-----------+----------+--------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | |
- relation | name | | not null | | plain | |
- options | text[] | | | | extended | |
+ Table "public.replication_metadata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+----------+---------+-----------+----------+--------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | |
+ relation | name | | not null | | plain | | |
+ options | text[] | | | | extended | | |
Indexes:
"replication_metadata_pkey" PRIMARY KEY, btree (id)
@@ -443,12 +443,12 @@ INSERT INTO replication_metadata(relation, options)
VALUES ('bar', ARRAY['a', 'b']);
ALTER TABLE replication_metadata SET (user_catalog_table = true);
\d+ replication_metadata
- Table "public.replication_metadata"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------+---------+-----------+----------+--------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | |
- relation | name | | not null | | plain | |
- options | text[] | | | | extended | |
+ Table "public.replication_metadata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+----------+---------+-----------+----------+--------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | |
+ relation | name | | not null | | plain | | |
+ options | text[] | | | | extended | | |
Indexes:
"replication_metadata_pkey" PRIMARY KEY, btree (id)
Options: user_catalog_table=true
@@ -461,13 +461,13 @@ ALTER TABLE replication_metadata ALTER COLUMN rewritemeornot TYPE text;
ERROR: cannot rewrite table "replication_metadata" used as a catalog table
ALTER TABLE replication_metadata SET (user_catalog_table = false);
\d+ replication_metadata
- Table "public.replication_metadata"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------------+---------+-----------+----------+--------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | |
- relation | name | | not null | | plain | |
- options | text[] | | | | extended | |
- rewritemeornot | integer | | | | plain | |
+ Table "public.replication_metadata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+----------------+---------+-----------+----------+--------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | |
+ relation | name | | not null | | plain | | |
+ options | text[] | | | | extended | | |
+ rewritemeornot | integer | | | | plain | | |
Indexes:
"replication_metadata_pkey" PRIMARY KEY, btree (id)
Options: user_catalog_table=false
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index 01acc2ef9d..f43f09cc19 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -59,6 +59,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY createAggregate SYSTEM "create_aggregate.sgml">
<!ENTITY createCast SYSTEM "create_cast.sgml">
<!ENTITY createCollation SYSTEM "create_collation.sgml">
+<!ENTITY createCompressionMethod SYSTEM "create_compression_method.sgml">
<!ENTITY createConversion SYSTEM "create_conversion.sgml">
<!ENTITY createDatabase SYSTEM "create_database.sgml">
<!ENTITY createDomain SYSTEM "create_domain.sgml">
diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index 41acda003f..edc46a2966 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -53,6 +53,8 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET ( <replaceable class="parameter">attribute_option</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] )
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> RESET ( <replaceable class="parameter">attribute_option</replaceable> [, ... ] )
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN }
+ ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET COMPRESSED <replaceable class="parameter">compression_method_name</replaceable> [ WITH (<replaceable class="parameter">compression_method_options</replaceable>) ]
+ ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET NOT COMPRESSED
ADD <replaceable class="parameter">table_constraint</replaceable> [ NOT VALID ]
ADD <replaceable class="parameter">table_constraint_using_index</replaceable>
ALTER CONSTRAINT <replaceable class="parameter">constraint_name</replaceable> [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -320,6 +322,34 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>
+ <literal>SET COMPRESSED <replaceable class="parameter">compression_method_name</replaceable></literal>
+ </term>
+ <listitem>
+ <para>
+ This form adds compression to a column. Compression method should be
+ created with <xref linkend="SQL-CREATECOMPRESSIONMETHOD">. If compression
+ method has options they could be specified with <literal>WITH</literal>
+ parameter. Setting a compression method doesn't change anything in the
+ table and affects only future table updates.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <literal>SET NOT COMPRESSED</literal>
+ </term>
+ <listitem>
+ <para>
+ This form removes compression from a column. Removing compresssion from
+ a column doesn't change already compressed tuples and affects only future
+ table updates.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>ADD <replaceable class="parameter">table_constraint</replaceable> [ NOT VALID ]</literal></term>
<listitem>
diff --git a/doc/src/sgml/ref/create_compression_method.sgml b/doc/src/sgml/ref/create_compression_method.sgml
new file mode 100644
index 0000000000..663010ecd9
--- /dev/null
+++ b/doc/src/sgml/ref/create_compression_method.sgml
@@ -0,0 +1,50 @@
+<!--
+doc/src/sgml/ref/create_compression_method.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-CREATECOMPRESSIONMETHOD">
+ <indexterm zone="sql-createcompressionmethod">
+ <primary>CREATE COMPRESSION METHOD</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>CREATE COMPRESSION METHOD</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>CREATE COMPRESSION METHOD</refname>
+ <refpurpose>define a new compression method</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+CREATE COMPRESSION METHOD <replaceable class="parameter">compression_method_name</replaceable>
+ HANDLER <replaceable class="parameter">compression_method_handler</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>CREATE COMPRESSION METHOD</command> creates a new compression method
+ with <replaceable class="parameter">compression_method_name</replaceable>.
+ </para>
+
+ <para>
+ A compression method links a name with a compression handler. And the
+ handler is a special function that returns collection of methods that
+ can be used for compression.
+ </para>
+
+ <para>
+ After a compression method is created, you can specify it in
+ <xref linkend="SQL-CREATETABLE"> or <xref linkend="SQL-ALTERTABLE">
+ statements.
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 4f7b741526..8ae763db8b 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -65,6 +65,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( <replaceable>sequence_options</replaceable> ) ] |
UNIQUE <replaceable class="parameter">index_parameters</replaceable> |
PRIMARY KEY <replaceable class="parameter">index_parameters</replaceable> |
+ COMPRESSED <replaceable class="parameter">compression_method_name</replaceable> [ WITH (<replaceable class="parameter">compression_method_options</replaceable>) ] |
REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -783,6 +784,18 @@ FROM ( { <replaceable class="parameter">numeric_literal</replaceable> | <replace
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>COMPRESSED <replaceable class="parameter">compression_method_name</replaceable> [ WITH (<replaceable class="parameter">compression_method_options</replaceable>) ]</literal></term>
+ <listitem>
+ <para>
+ This clause adds compression to a column. Compression method should be
+ created with <xref linkend="SQL-CREATECOMPRESSIONMETHOD">. If compression
+ method has options they could be specified with <literal>WITH</literal>
+ parameter.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="sql-createtable-exclude">
<term><literal>EXCLUDE [ USING <replaceable class="parameter">index_method</replaceable> ] ( <replaceable class="parameter">exclude_element</replaceable> WITH <replaceable class="parameter">operator</replaceable> [, ... ] ) <replaceable class="parameter">index_parameters</replaceable> [ WHERE ( <replaceable class="parameter">predicate</replaceable> ) ]</literal></term>
<listitem>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index 9000b3aaaa..cc0bd70be3 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -87,6 +87,7 @@
&createAggregate;
&createCast;
&createCollation;
+ &createCompressionMethod;
&createConversion;
&createDatabase;
&createDomain;
diff --git a/src/backend/access/common/indextuple.c b/src/backend/access/common/indextuple.c
index 138671410a..9efa5a9648 100644
--- a/src/backend/access/common/indextuple.c
+++ b/src/backend/access/common/indextuple.c
@@ -92,7 +92,8 @@ index_form_tuple(TupleDesc tupleDescriptor,
VARSIZE(DatumGetPointer(untoasted_values[i])) > TOAST_INDEX_TARGET &&
(att->attstorage == 'x' || att->attstorage == 'm'))
{
- Datum cvalue = toast_compress_datum(untoasted_values[i]);
+ Datum cvalue = toast_compress_datum(untoasted_values[i],
+ att->attcompression);
if (DatumGetPointer(cvalue) != NULL)
{
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index ec10762529..21e375ad96 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -935,11 +935,48 @@ untransformRelOptions(Datum options)
val = (Node *) makeString(pstrdup(p));
}
result = lappend(result, makeDefElem(pstrdup(s), val, -1));
+ pfree(s);
}
return result;
}
+char *
+formatRelOptions(List *options)
+{
+ StringInfoData buf;
+ ListCell *cell;
+
+ initStringInfo(&buf);
+
+ foreach(cell, options)
+ {
+ DefElem *def = (DefElem *) lfirst(cell);
+
+ appendStringInfo(&buf, "%s%s=%s", buf.len > 0 ? ", " : "",
+ def->defname, defGetString(def));
+ }
+
+ return buf.data;
+}
+
+void
+freeRelOptions(List *options)
+{
+ ListCell *cell;
+
+ Assert(options != NIL);
+ foreach(cell, options)
+ {
+ DefElem *def = (DefElem *) lfirst(cell);
+
+ pfree(def->defname);
+ pfree(defGetString(def));
+ pfree(def->arg);
+ }
+ list_free_deep(options);
+}
+
/*
* Extract and parse reloptions from a pg_class tuple.
*
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index 9e37ca73a8..d206cce18e 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -19,8 +19,10 @@
#include "postgres.h"
+#include "access/compression.h"
#include "access/hash.h"
#include "access/htup_details.h"
+#include "access/reloptions.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
@@ -242,6 +244,7 @@ TupleDescCopyEntry(TupleDesc dst, AttrNumber dstAttno,
dstAtt->attnotnull = false;
dstAtt->atthasdef = false;
dstAtt->attidentity = '\0';
+ dstAtt->attcompression = InvalidOid;
}
/*
@@ -396,6 +399,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
return false;
if (attr1->attcollation != attr2->attcollation)
return false;
+ if (attr1->attcompression != attr2->attcompression)
+ return false;
/* attacl, attoptions and attfdwoptions are not even present... */
}
@@ -458,6 +463,7 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
}
else if (tupdesc2->constr != NULL)
return false;
+
return true;
}
@@ -563,6 +569,7 @@ TupleDescInitEntry(TupleDesc desc,
att->attalign = typeForm->typalign;
att->attstorage = typeForm->typstorage;
att->attcollation = typeForm->typcollation;
+ att->attcompression = InvalidOid;
ReleaseSysCache(tuple);
}
@@ -675,7 +682,6 @@ TupleDescInitEntryCollation(TupleDesc desc,
TupleDescAttr(desc, attributeNumber - 1)->attcollation = collationid;
}
-
/*
* BuildDescForRelation
*
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index 5a8f1dab83..bd50ef9af9 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -30,8 +30,10 @@
#include <unistd.h>
#include <fcntl.h>
+#include "access/compression.h"
#include "access/genam.h"
#include "access/heapam.h"
+#include "access/reloptions.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -39,6 +41,8 @@
#include "miscadmin.h"
#include "utils/expandeddatum.h"
#include "utils/fmgroids.h"
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/typcache.h"
@@ -53,19 +57,46 @@
typedef struct toast_compress_header
{
int32 vl_len_; /* varlena header (do not touch directly!) */
- int32 rawsize;
+ uint32 info; /* flags (2 bits) and rawsize */
} toast_compress_header;
+/*
+ * If the compression method were used, then data also contains
+ * Oid of compression options
+ */
+typedef struct toast_compress_header_custom
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ uint32 info; /* flags (2 high bits) and rawsize */
+ Oid cmoptoid; /* Oid from pg_compression_opt */
+} toast_compress_header_custom;
+
+static HTAB *compression_options_htab = NULL;
+static MemoryContext compression_options_mcxt = NULL;
+
+#define RAWSIZEMASK (0x3FFFFFFFU)
+
/*
* Utilities for manipulation of header information for compressed
* toast entries.
*/
-#define TOAST_COMPRESS_HDRSZ ((int32) sizeof(toast_compress_header))
-#define TOAST_COMPRESS_RAWSIZE(ptr) (((toast_compress_header *) (ptr))->rawsize)
+#define TOAST_COMPRESS_HDRSZ ((int32) sizeof(toast_compress_header))
+#define TOAST_COMPRESS_HDRSZ_CUSTOM ((int32) sizeof(toast_compress_header_custom))
+#define TOAST_COMPRESS_RAWSIZE(ptr) (((toast_compress_header *) (ptr))->info & RAWSIZEMASK)
#define TOAST_COMPRESS_RAWDATA(ptr) \
(((char *) (ptr)) + TOAST_COMPRESS_HDRSZ)
#define TOAST_COMPRESS_SET_RAWSIZE(ptr, len) \
- (((toast_compress_header *) (ptr))->rawsize = (len))
+do { \
+ ((toast_compress_header *) (ptr))->info &= 0xC0000000; \
+ ((toast_compress_header *) (ptr))->info |= ((uint32)(len) & RAWSIZEMASK); \
+} while (0)
+#define TOAST_COMPRESS_SET_CMOPTOID(ptr, oid) \
+ (((toast_compress_header_custom *) (ptr))->cmoptoid = (oid))
+#define TOAST_COMPRESS_SET_CUSTOM(ptr) \
+do { \
+ (((toast_compress_header *) (ptr))->info |= (1 << 31)); \
+ (((toast_compress_header *) (ptr))->info &= ~(1 << 30)); \
+} while (0)
static void toast_delete_datum(Relation rel, Datum value, bool is_speculative);
static Datum toast_save_datum(Relation rel, Datum value,
@@ -83,6 +114,8 @@ static int toast_open_indexes(Relation toastrel,
static void toast_close_indexes(Relation *toastidxs, int num_indexes,
LOCKMODE lock);
static void init_toast_snapshot(Snapshot toast_snapshot);
+static void init_compression_options_htab(void);
+static AttributeCompression *get_compression_options_info(Oid cmoptoid);
/* ----------
@@ -741,6 +774,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
Datum old_value;
Datum new_value;
+ Form_pg_attribute att;
+
/*
* Search for the biggest yet unprocessed internal attribute
*/
@@ -770,10 +805,11 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
* Attempt to compress it inline, if it has attstorage 'x'
*/
i = biggest_attno;
- if (TupleDescAttr(tupleDesc, i)->attstorage == 'x')
+ att = TupleDescAttr(tupleDesc, i);
+ if (att->attstorage == 'x')
{
old_value = toast_values[i];
- new_value = toast_compress_datum(old_value);
+ new_value = toast_compress_datum(old_value, att->attcompression);
if (DatumGetPointer(new_value) != NULL)
{
@@ -914,7 +950,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
*/
i = biggest_attno;
old_value = toast_values[i];
- new_value = toast_compress_datum(old_value);
+ new_value = toast_compress_datum(old_value,
+ TupleDescAttr(tupleDesc, i)->attcompression);
if (DatumGetPointer(new_value) != NULL)
{
@@ -1229,7 +1266,9 @@ toast_flatten_tuple_to_datum(HeapTupleHeader tup,
if (VARATT_IS_EXTERNAL(new_value) ||
VARATT_IS_COMPRESSED(new_value))
{
- new_value = heap_tuple_untoast_attr(new_value);
+ struct varlena *untoasted_value = heap_tuple_untoast_attr(new_value);
+
+ new_value = untoasted_value;
toast_values[i] = PointerGetDatum(new_value);
toast_free[i] = true;
}
@@ -1353,7 +1392,6 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
return new_tuple;
}
-
/* ----------
* toast_compress_datum -
*
@@ -1368,25 +1406,43 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
* ----------
*/
Datum
-toast_compress_datum(Datum value)
+toast_compress_datum(Datum value, Oid cmoptoid)
{
- struct varlena *tmp;
- int32 valsize = VARSIZE_ANY_EXHDR(DatumGetPointer(value));
- int32 len;
+ struct varlena *tmp = NULL;
+ int32 valsize,
+ len = 0;
+ AttributeCompression *ac = NULL;
Assert(!VARATT_IS_EXTERNAL(DatumGetPointer(value)));
Assert(!VARATT_IS_COMPRESSED(DatumGetPointer(value)));
+ if (OidIsValid(cmoptoid))
+ ac = get_compression_options_info(cmoptoid);
+
/*
* No point in wasting a palloc cycle if value size is out of the allowed
* range for compression
*/
- if (valsize < PGLZ_strategy_default->min_input_size ||
- valsize > PGLZ_strategy_default->max_input_size)
+ valsize = VARSIZE_ANY_EXHDR(DatumGetPointer(value));
+ if (!ac && (valsize < PGLZ_strategy_default->min_input_size ||
+ valsize > PGLZ_strategy_default->max_input_size))
return PointerGetDatum(NULL);
- tmp = (struct varlena *) palloc(PGLZ_MAX_OUTPUT(valsize) +
- TOAST_COMPRESS_HDRSZ);
+ if (ac)
+ {
+ tmp = ac->routine->compress(ac, (const struct varlena *) value);
+ if (!tmp)
+ return PointerGetDatum(NULL);
+ }
+ else
+ {
+ tmp = (struct varlena *) palloc(PGLZ_MAX_OUTPUT(valsize) +
+ TOAST_COMPRESS_HDRSZ);
+ len = pglz_compress(VARDATA_ANY(DatumGetPointer(value)),
+ valsize,
+ TOAST_COMPRESS_RAWDATA(tmp),
+ PGLZ_strategy_default);
+ }
/*
* We recheck the actual size even if pglz_compress() reports success,
@@ -1398,11 +1454,7 @@ toast_compress_datum(Datum value)
* only one header byte and no padding if the value is short enough. So
* we insist on a savings of more than 2 bytes to ensure we have a gain.
*/
- len = pglz_compress(VARDATA_ANY(DatumGetPointer(value)),
- valsize,
- TOAST_COMPRESS_RAWDATA(tmp),
- PGLZ_strategy_default);
- if (len >= 0 &&
+ if (!ac && len >= 0 &&
len + TOAST_COMPRESS_HDRSZ < valsize - 2)
{
TOAST_COMPRESS_SET_RAWSIZE(tmp, valsize);
@@ -1410,10 +1462,20 @@ toast_compress_datum(Datum value)
/* successful compression */
return PointerGetDatum(tmp);
}
+ else if (ac && VARSIZE(tmp) < valsize - 2)
+ {
+ TOAST_COMPRESS_SET_CUSTOM(tmp);
+ TOAST_COMPRESS_SET_RAWSIZE(tmp, valsize);
+ TOAST_COMPRESS_SET_CMOPTOID(tmp, ac->cmoptoid);
+ /* successful compression */
+ return PointerGetDatum(tmp);
+ }
else
{
/* incompressible data */
- pfree(tmp);
+ if (tmp)
+ pfree(tmp);
+
return PointerGetDatum(NULL);
}
}
@@ -2280,15 +2342,26 @@ toast_decompress_datum(struct varlena *attr)
Assert(VARATT_IS_COMPRESSED(attr));
- result = (struct varlena *)
- palloc(TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
- SET_VARSIZE(result, TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
+ if (VARATT_IS_CUSTOM_COMPRESSED(attr))
+ {
+ AttributeCompression *ac;
+ toast_compress_header_custom *hdr;
- if (pglz_decompress(TOAST_COMPRESS_RAWDATA(attr),
- VARSIZE(attr) - TOAST_COMPRESS_HDRSZ,
- VARDATA(result),
- TOAST_COMPRESS_RAWSIZE(attr)) < 0)
- elog(ERROR, "compressed data is corrupted");
+ hdr = (toast_compress_header_custom *) attr;
+ ac = get_compression_options_info(hdr->cmoptoid);
+ result = ac->routine->decompress(ac, attr);
+ }
+ else
+ {
+ result = (struct varlena *)
+ palloc(TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
+ SET_VARSIZE(result, TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
+ if (pglz_decompress(TOAST_COMPRESS_RAWDATA(attr),
+ VARSIZE(attr) - TOAST_COMPRESS_HDRSZ,
+ VARDATA(result),
+ TOAST_COMPRESS_RAWSIZE(attr)) < 0)
+ elog(ERROR, "compressed data is corrupted");
+ }
return result;
}
@@ -2390,3 +2463,44 @@ init_toast_snapshot(Snapshot toast_snapshot)
InitToastSnapshot(*toast_snapshot, snapshot->lsn, snapshot->whenTaken);
}
+
+static void
+init_compression_options_htab(void)
+{
+ HASHCTL ctl;
+
+ compression_options_mcxt = AllocSetContextCreate(TopMemoryContext,
+ "compression options cache context",
+ ALLOCSET_DEFAULT_SIZES);
+ MemSet(&ctl, 0, sizeof(ctl));
+ ctl.keysize = sizeof(Oid);
+ ctl.entrysize = sizeof(AttributeCompression);
+ ctl.hcxt = compression_options_mcxt;
+ compression_options_htab = hash_create("compression options cache", 100, &ctl,
+ HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
+}
+
+static AttributeCompression *
+get_compression_options_info(Oid cmoptoid)
+{
+ bool found;
+ AttributeCompression *result;
+
+ Assert(OidIsValid(cmoptoid));
+ if (!compression_options_htab)
+ init_compression_options_htab();
+
+ result = hash_search(compression_options_htab, &cmoptoid, HASH_ENTER, &found);
+ if (!found)
+ {
+ MemoryContext oldcxt;
+
+ Assert(compression_options_mcxt);
+ oldcxt = MemoryContextSwitchTo(compression_options_mcxt);
+ result->cmoptoid = cmoptoid;
+ result->routine = GetCompressionRoutine(cmoptoid);
+ result->options = GetCompressionOptionsList(cmoptoid);
+ MemoryContextSwitchTo(oldcxt);
+ }
+ return result;
+}
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 8287de97a2..be6b460aee 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -731,6 +731,7 @@ DefineAttr(char *name, char *type, int attnum, int nullness)
attrtypes[attnum]->attcacheoff = -1;
attrtypes[attnum]->atttypmod = -1;
attrtypes[attnum]->attislocal = true;
+ attrtypes[attnum]->attcompression = InvalidOid;
if (nullness == BOOTCOL_NULL_FORCE_NOT_NULL)
{
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index fd33426bad..c7cea974b1 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -46,7 +46,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_collation.h pg_partitioned_table.h pg_range.h pg_transform.h \
pg_sequence.h pg_publication.h pg_publication_rel.h pg_subscription.h \
pg_subscription_rel.h toasting.h indexing.h \
- toasting.h indexing.h \
+ pg_compression.h pg_compression_opt.h \
)
# location of Catalog.pm
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index ccde66a7dd..fd733a34a0 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -3340,6 +3340,8 @@ static const char *const no_priv_msg[MAX_ACL_KIND] =
gettext_noop("permission denied for publication %s"),
/* ACL_KIND_SUBSCRIPTION */
gettext_noop("permission denied for subscription %s"),
+ /* ACL_KIND_COMPRESSION_METHOD */
+ gettext_noop("permission denied for compression method %s"),
};
static const char *const not_owner_msg[MAX_ACL_KIND] =
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 033c4358ea..e1bfc7c6bf 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -28,6 +28,8 @@
#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_collation_fn.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_conversion.h"
@@ -173,7 +175,9 @@ static const Oid object_classes[] = {
PublicationRelationId, /* OCLASS_PUBLICATION */
PublicationRelRelationId, /* OCLASS_PUBLICATION_REL */
SubscriptionRelationId, /* OCLASS_SUBSCRIPTION */
- TransformRelationId /* OCLASS_TRANSFORM */
+ TransformRelationId, /* OCLASS_TRANSFORM */
+ CompressionMethodRelationId, /* OCLASS_COMPRESSION_METHOD */
+ CompressionOptRelationId, /* OCLASS_COMPRESSION_OPTIONS */
};
@@ -1271,6 +1275,14 @@ doDeletion(const ObjectAddress *object, int flags)
DropTransformById(object->objectId);
break;
+ case OCLASS_COMPRESSION_METHOD:
+ RemoveCompressionMethodById(object->objectId);
+ break;
+
+ case OCLASS_COMPRESSION_OPTIONS:
+ RemoveCompressionOptionsById(object->objectId);
+ break;
+
/*
* These global object types are not supported here.
*/
@@ -2512,6 +2524,12 @@ getObjectClass(const ObjectAddress *object)
case TransformRelationId:
return OCLASS_TRANSFORM;
+
+ case CompressionMethodRelationId:
+ return OCLASS_COMPRESSION_METHOD;
+
+ case CompressionOptRelationId:
+ return OCLASS_COMPRESSION_OPTIONS;
}
/* shouldn't get here */
diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl
index 256a9c9c93..c5838fa779 100644
--- a/src/backend/catalog/genbki.pl
+++ b/src/backend/catalog/genbki.pl
@@ -451,6 +451,7 @@ sub emit_pgattr_row
attisdropped => 'f',
attislocal => 't',
attinhcount => '0',
+ attcompression=> '0',
attacl => '_null_',
attoptions => '_null_',
attfdwoptions => '_null_');
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 05e70818e7..b08e857e3a 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -29,8 +29,10 @@
*/
#include "postgres.h"
+#include "access/compression.h"
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/reloptions.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "access/xact.h"
@@ -44,6 +46,8 @@
#include "catalog/partition.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_foreign_table.h"
@@ -628,6 +632,7 @@ InsertPgAttributeTuple(Relation pg_attribute_rel,
values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal);
values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount);
values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(new_attribute->attcollation);
+ values[Anum_pg_attribute_attcompression - 1] = ObjectIdGetDatum(new_attribute->attcompression);
/* start out with empty permissions and empty options */
nulls[Anum_pg_attribute_attacl - 1] = true;
@@ -707,6 +712,13 @@ AddNewAttributeTuples(Oid new_rel_oid,
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+
+ if (OidIsValid(attr->attcompression))
+ {
+ ObjectAddressSet(referenced, CompressionOptRelationId,
+ attr->attcompression);
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
}
/*
@@ -1453,6 +1465,22 @@ DeleteRelationTuple(Oid relid)
heap_close(pg_class_desc, RowExclusiveLock);
}
+static void
+DropAttributeCompression(Form_pg_attribute att)
+{
+ CompressionMethodRoutine *cmr = GetCompressionRoutine(att->attcompression);
+
+ if (cmr->drop)
+ {
+ bool attisdropped = att->attisdropped;
+ List *options = GetCompressionOptionsList(att->attcompression);
+
+ att->attisdropped = true;
+ cmr->drop(att, options);
+ att->attisdropped = attisdropped;
+ }
+}
+
/*
* DeleteAttributeTuples
*
@@ -1483,7 +1511,14 @@ DeleteAttributeTuples(Oid relid)
/* Delete all the matching tuples */
while ((atttup = systable_getnext(scan)) != NULL)
+ {
+ Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(atttup);
+
+ if (OidIsValid(att->attcompression))
+ DropAttributeCompression(att);
+
CatalogTupleDelete(attrel, &atttup->t_self);
+ }
/* Clean up after the scan */
systable_endscan(scan);
@@ -1576,6 +1611,8 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
else
{
/* Dropping user attributes is lots harder */
+ if (OidIsValid(attStruct->attcompression))
+ DropAttributeCompression(attStruct);
/* Mark the attribute as dropped */
attStruct->attisdropped = true;
@@ -1597,6 +1634,8 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
/* We don't want to keep stats for it anymore */
attStruct->attstattarget = 0;
+ attStruct->attcompression = InvalidOid;
+
/*
* Change the column name to something that isn't likely to conflict
*/
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index c7b2f031f0..11dc107ab7 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -393,6 +393,7 @@ ConstructTupleDescriptor(Relation heapRelation,
to->atttypmod = exprTypmod(indexkey);
to->attislocal = true;
to->attcollation = collationObjectId[i];
+ to->attcompression = InvalidOid;
ReleaseSysCache(tuple);
@@ -471,6 +472,7 @@ ConstructTupleDescriptor(Relation heapRelation,
to->attbyval = typeTup->typbyval;
to->attalign = typeTup->typalign;
to->attstorage = typeTup->typstorage;
+ to->attcompression = InvalidOid;
ReleaseSysCache(tuple);
}
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c2ad7c675e..0e1497cf7c 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -29,6 +29,8 @@
#include "catalog/pg_default_acl.h"
#include "catalog/pg_event_trigger.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_conversion.h"
@@ -490,6 +492,30 @@ static const ObjectPropertyType ObjectProperty[] =
InvalidAttrNumber, /* no ACL (same as relation) */
ACL_KIND_STATISTICS,
true
+ },
+ {
+ CompressionMethodRelationId,
+ CompressionMethodOidIndexId,
+ COMPRESSIONMETHODOID,
+ COMPRESSIONMETHODNAME,
+ Anum_pg_compression_cmname,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ true
+ },
+ {
+ CompressionOptRelationId,
+ CompressionOptionsOidIndexId,
+ COMPRESSIONOPTIONSOID,
+ -1,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ false,
}
};
@@ -712,6 +738,10 @@ static const struct object_type_map
/* OBJECT_STATISTIC_EXT */
{
"statistics object", OBJECT_STATISTIC_EXT
+ },
+ /* OCLASS_COMPRESSION_METHOD */
+ {
+ "compression method", OBJECT_COMPRESSION_METHOD
}
};
@@ -876,6 +906,7 @@ get_object_address(ObjectType objtype, Node *object,
case OBJECT_ACCESS_METHOD:
case OBJECT_PUBLICATION:
case OBJECT_SUBSCRIPTION:
+ case OBJECT_COMPRESSION_METHOD:
address = get_object_address_unqualified(objtype,
(Value *) object, missing_ok);
break;
@@ -1182,6 +1213,11 @@ get_object_address_unqualified(ObjectType objtype,
address.objectId = get_subscription_oid(name, missing_ok);
address.objectSubId = 0;
break;
+ case OBJECT_COMPRESSION_METHOD:
+ address.classId = CompressionMethodRelationId;
+ address.objectId = get_compression_method_oid(name, missing_ok);
+ address.objectSubId = 0;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
/* placate compiler, which doesn't know elog won't return */
@@ -2139,6 +2175,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_SCHEMA:
case OBJECT_SUBSCRIPTION:
case OBJECT_TABLESPACE:
+ case OBJECT_COMPRESSION_METHOD:
if (list_length(name) != 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -2395,12 +2432,14 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
case OBJECT_ACCESS_METHOD:
+ case OBJECT_COMPRESSION_METHOD:
/* We treat these object types as being owned by superusers */
if (!superuser_arg(roleid))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser")));
break;
+
case OBJECT_STATISTIC_EXT:
if (!pg_statistics_object_ownercheck(address.objectId, roleid))
aclcheck_error_type(ACLCHECK_NOT_OWNER, address.objectId);
@@ -3398,6 +3437,27 @@ getObjectDescription(const ObjectAddress *object)
break;
}
+ case OCLASS_COMPRESSION_METHOD:
+ {
+ char *name = get_compression_method_name(object->objectId);
+
+ if (!name)
+ elog(ERROR, "cache lookup failed for compression method %u",
+ object->objectId);
+ appendStringInfo(&buffer, _("compression method %s"), name);
+ pfree(name);
+ break;
+ }
+
+ case OCLASS_COMPRESSION_OPTIONS:
+ {
+ char *name = get_compression_method_name_for_opt(object->objectId);
+
+ appendStringInfo(&buffer, _("compression options for %s"), name);
+ pfree(name);
+ break;
+ }
+
case OCLASS_TRANSFORM:
{
HeapTuple trfTup;
@@ -3919,6 +3979,14 @@ getObjectTypeDescription(const ObjectAddress *object)
appendStringInfoString(&buffer, "transform");
break;
+ case OCLASS_COMPRESSION_METHOD:
+ appendStringInfoString(&buffer, "compression method");
+ break;
+
+ case OCLASS_COMPRESSION_OPTIONS:
+ appendStringInfoString(&buffer, "compression options");
+ break;
+
/*
* There's intentionally no default: case here; we want the
* compiler to warn if a new OCLASS hasn't been handled above.
@@ -4160,6 +4228,30 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case OCLASS_COMPRESSION_METHOD:
+ {
+ char *cmname = get_compression_method_name(object->objectId);
+
+ if (!cmname)
+ elog(ERROR, "cache lookup failed for compression method %u",
+ object->objectId);
+ appendStringInfoString(&buffer, quote_identifier(cmname));
+ if (objname)
+ *objname = list_make1(cmname);
+ else
+ pfree(cmname);
+ break;
+ }
+
+ case OCLASS_COMPRESSION_OPTIONS:
+ {
+ appendStringInfo(&buffer, "%u",
+ object->objectId);
+ if (objname)
+ *objname = list_make1(psprintf("%u", object->objectId));
+ break;
+ }
+
case OCLASS_CONSTRAINT:
{
HeapTuple conTup;
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 59ffd2104d..8d65c5fbe8 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -22,6 +22,7 @@
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 4a6c99e090..e23abf64f1 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -13,8 +13,8 @@ top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
- collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
- dbcommands.o define.o discard.o dropcmds.o \
+ collationcmds.o compressioncmds.o constraint.o conversioncmds.o copy.o \
+ createas.o dbcommands.o define.o discard.o dropcmds.o \
event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o matview.o operatorcmds.o opclasscmds.o \
policy.o portalcmds.o prepare.o proclang.o publicationcmds.o \
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 4f8147907c..4f18e4083f 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -385,6 +385,7 @@ ExecRenameStmt(RenameStmt *stmt)
case OBJECT_TSTEMPLATE:
case OBJECT_PUBLICATION:
case OBJECT_SUBSCRIPTION:
+ case OBJECT_COMPRESSION_METHOD:
{
ObjectAddress address;
Relation catalog;
@@ -500,6 +501,7 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
case OBJECT_TSDICTIONARY:
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
+ case OBJECT_COMPRESSION_METHOD:
{
Relation catalog;
Relation relation;
@@ -627,6 +629,8 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_COMPRESSION_METHOD:
+ case OCLASS_COMPRESSION_OPTIONS:
/* ignore object types that don't have schema-qualified names */
break;
diff --git a/src/backend/commands/compressioncmds.c b/src/backend/commands/compressioncmds.c
new file mode 100644
index 0000000000..32cbcc3efe
--- /dev/null
+++ b/src/backend/commands/compressioncmds.c
@@ -0,0 +1,485 @@
+/*-------------------------------------------------------------------------
+ *
+ * compressioncmds.c
+ * Routines for SQL commands that manipulate compression methods.
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/commands/compressioncmds.c
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "miscadmin.h"
+
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/compression.h"
+#include "access/reloptions.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "parser/parse_func.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+static CompressionMethodRoutine *get_compression_method_routine(Oid cmhandler, Oid typeid);
+
+/*
+ * Convert a handler function name to an Oid. If the return type of the
+ * function doesn't match the given AM type, an error is raised.
+ *
+ * This function either return valid function Oid or throw an error.
+ */
+static Oid
+LookupCompressionHandlerFunc(List *handlerName)
+{
+ static const Oid funcargtypes[1] = {INTERNALOID};
+ Oid handlerOid;
+
+ if (handlerName == NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("handler function is not specified")));
+
+ /* handlers have one argument of type internal */
+ handlerOid = LookupFuncName(handlerName, 1, funcargtypes, false);
+
+ /* check that handler has the correct return type */
+ if (get_func_rettype(handlerOid) != COMPRESSION_HANDLEROID)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("function %s must return type %s",
+ NameListToString(handlerName),
+ "compression_handler")));
+
+ return handlerOid;
+}
+
+static ObjectAddress
+CreateCompressionMethod(char *cmName, List *handlerName)
+{
+ Relation rel;
+ ObjectAddress myself;
+ ObjectAddress referenced;
+ Oid cmoid;
+ Oid cmhandler;
+ bool nulls[Natts_pg_compression];
+ Datum values[Natts_pg_compression];
+ HeapTuple tup;
+
+ rel = heap_open(CompressionMethodRelationId, RowExclusiveLock);
+
+ /* Check if name is used */
+ cmoid = GetSysCacheOid1(COMPRESSIONMETHODNAME, CStringGetDatum(cmName));
+ if (OidIsValid(cmoid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("compression method \"%s\" already exists",
+ cmName)));
+
+ /*
+ * Get the handler function oid and compression method routine
+ */
+ cmhandler = LookupCompressionHandlerFunc(handlerName);
+
+ /*
+ * Insert tuple into pg_compression.
+ */
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+
+ values[Anum_pg_compression_cmname - 1] =
+ DirectFunctionCall1(namein, CStringGetDatum(cmName));
+ values[Anum_pg_compression_cmhandler - 1] = ObjectIdGetDatum(cmhandler);
+
+ tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+
+ cmoid = CatalogTupleInsert(rel, tup);
+ heap_freetuple(tup);
+
+ ObjectAddressSet(myself, CompressionMethodRelationId, cmoid);
+
+ /* Record dependency on handler function */
+ ObjectAddressSet(referenced, ProcedureRelationId, cmhandler);
+
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ recordDependencyOnCurrentExtension(&myself, false);
+
+ heap_close(rel, RowExclusiveLock);
+
+ return myself;
+}
+
+ObjectAddress
+DefineCompressionMethod(List *names, List *parameters)
+{
+ char *cmName;
+ ListCell *pl;
+ DefElem *handlerEl = NULL;
+
+ if (list_length(names) != 1)
+ elog(ERROR, "compression method name cannot be qualified");
+
+ cmName = strVal(linitial(names));
+
+ /* Must be super user */
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to create compression method \"%s\"",
+ cmName),
+ errhint("Must be superuser to create an compression method.")));
+
+ foreach(pl, parameters)
+ {
+ DefElem *defel = (DefElem *) lfirst(pl);
+ DefElem **defelp;
+
+ if (pg_strcasecmp(defel->defname, "handler") == 0)
+ defelp = &handlerEl;
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("compression method attribute \"%s\" not recognized",
+ defel->defname)));
+ break;
+ }
+
+ *defelp = defel;
+ }
+
+ if (!handlerEl)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("compression method handler is not specified")));
+
+ return CreateCompressionMethod(cmName, (List *) handlerEl->arg);
+}
+
+void
+RemoveCompressionMethodById(Oid cmOid)
+{
+ Relation relation;
+ HeapTuple tup;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to drop compression methods")));
+
+ relation = heap_open(CompressionMethodRelationId, RowExclusiveLock);
+
+ tup = SearchSysCache1(COMPRESSIONMETHODOID, ObjectIdGetDatum(cmOid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for compression method %u", cmOid);
+
+ CatalogTupleDelete(relation, &tup->t_self);
+
+ ReleaseSysCache(tup);
+
+ heap_close(relation, RowExclusiveLock);
+}
+
+/*
+ * Create new record in pg_compression_opt
+ */
+Oid
+CreateCompressionOptions(Form_pg_attribute attr, Oid cmid, List *options)
+{
+ Relation rel;
+ HeapTuple tuple;
+ Oid result,
+ cmhandler;
+ Datum values[Natts_pg_compression_opt];
+ bool nulls[Natts_pg_compression_opt];
+ ObjectAddress myself,
+ ref1,
+ ref2,
+ ref3;
+ CompressionMethodRoutine *routine;
+
+ /* Initialize buffers for new tuple values */
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+
+ /* Get handler function OID for the compression method */
+ tuple = SearchSysCache1(COMPRESSIONMETHODOID, ObjectIdGetDatum(cmid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for compression method %u", cmid);
+ cmhandler = ((Form_pg_compression) GETSTRUCT(tuple))->cmhandler;
+ ReleaseSysCache(tuple);
+
+ routine = get_compression_method_routine(cmhandler, attr->atttypid);
+
+ if (routine->configure && options != NIL)
+ routine->configure(attr, options);
+
+ values[Anum_pg_compression_opt_cmid - 1] = ObjectIdGetDatum(cmid);
+ values[Anum_pg_compression_opt_cmhandler - 1] = ObjectIdGetDatum(cmhandler);
+
+ if (options)
+ values[Anum_pg_compression_opt_cmoptions - 1] = optionListToArray(options);
+ else
+ nulls[Anum_pg_compression_opt_cmoptions - 1] = true;
+
+ rel = heap_open(CompressionOptRelationId, RowExclusiveLock);
+ tuple = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+ result = CatalogTupleInsert(rel, tuple);
+ heap_freetuple(tuple);
+
+ ObjectAddressSet(myself, CompressionOptRelationId, result);
+ ObjectAddressSet(ref1, ProcedureRelationId, cmhandler);
+ ObjectAddressSubSet(ref2, RelationRelationId, attr->attrelid, attr->attnum);
+ ObjectAddressSet(ref3, CompressionMethodRelationId, cmid);
+
+ recordDependencyOn(&myself, &ref1, DEPENDENCY_NORMAL);
+ recordDependencyOn(&ref2, &myself, DEPENDENCY_NORMAL);
+ recordDependencyOn(&myself, &ref3, DEPENDENCY_NORMAL);
+ recordDependencyOnCurrentExtension(&myself, false);
+ heap_close(rel, RowExclusiveLock);
+
+ return result;
+}
+
+void
+RemoveCompressionOptionsById(Oid cmoptoid)
+{
+ Relation relation;
+ HeapTuple tup;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to drop compression options")));
+
+ relation = heap_open(CompressionOptRelationId, RowExclusiveLock);
+
+ tup = SearchSysCache1(COMPRESSIONOPTIONSOID, ObjectIdGetDatum(cmoptoid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+
+ CatalogTupleDelete(relation, &tup->t_self);
+ ReleaseSysCache(tup);
+ heap_close(relation, RowExclusiveLock);
+}
+
+ColumnCompression *
+GetColumnCompressionForAttribute(Form_pg_attribute att)
+{
+ HeapTuple tuple;
+ Form_pg_compression_opt cmopt;
+ ColumnCompression *compression = makeNode(ColumnCompression);
+
+ /* Get handler function OID for the compression method */
+ tuple = SearchSysCache1(COMPRESSIONOPTIONSOID,
+ ObjectIdGetDatum(att->attcompression));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for compression options %u",
+ att->attcompression);
+
+ cmopt = (Form_pg_compression_opt) GETSTRUCT(tuple);
+ compression->methodName = NULL;
+ compression->methodOid = cmopt->cmid;
+ compression->options = GetCompressionOptionsList(att->attcompression);
+ ReleaseSysCache(tuple);
+
+ return compression;
+}
+
+void
+CheckCompressionMismatch(ColumnCompression * c1, ColumnCompression * c2,
+ const char *attributeName)
+{
+ char *cmname1 = c1->methodName ? c1->methodName :
+ get_compression_method_name(c1->methodOid);
+ char *cmname2 = c2->methodName ? c2->methodName :
+ get_compression_method_name(c2->methodOid);
+
+ if (strcmp(cmname1, cmname2))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("column \"%s\" has a compression method conflict",
+ attributeName),
+ errdetail("%s versus %s", cmname1, cmname2)));
+
+ if (!equal(c1->options, c2->options))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("column \"%s\" has a compression options conflict",
+ attributeName),
+ errdetail("(%s) versus (%s)",
+ formatRelOptions(c1->options),
+ formatRelOptions(c2->options))));
+}
+
+/*
+ * get_compression_method_oid
+ *
+ * If missing_ok is false, throw an error if compression method not found.
+ * If missing_ok is true, just return InvalidOid.
+ */
+Oid
+get_compression_method_oid(const char *cmname, bool missing_ok)
+{
+ HeapTuple tup;
+ Oid oid = InvalidOid;
+
+ tup = SearchSysCache1(COMPRESSIONMETHODNAME, CStringGetDatum(cmname));
+ if (HeapTupleIsValid(tup))
+ {
+ oid = HeapTupleGetOid(tup);
+ ReleaseSysCache(tup);
+ }
+
+ if (!OidIsValid(oid) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("compression method \"%s\" does not exist", cmname)));
+
+ return oid;
+}
+
+/*
+ * get_compression_method_name
+ *
+ * given an compression method OID, look up its name.
+ */
+char *
+get_compression_method_name(Oid cmOid)
+{
+ HeapTuple tup;
+ char *result = NULL;
+
+ tup = SearchSysCache1(COMPRESSIONMETHODOID, ObjectIdGetDatum(cmOid));
+ if (HeapTupleIsValid(tup))
+ {
+ Form_pg_compression cmform = (Form_pg_compression) GETSTRUCT(tup);
+
+ result = pstrdup(NameStr(cmform->cmname));
+ ReleaseSysCache(tup);
+ }
+ return result;
+}
+
+/*
+ * get_compression_method_name_for_opt
+ *
+ * given an compression options OID, look up a name for used compression method.
+ */
+char *
+get_compression_method_name_for_opt(Oid cmoptoid)
+{
+ HeapTuple tup;
+ Oid cmid;
+ char *result = NULL;
+ Form_pg_compression cmform;
+
+ tup = SearchSysCache1(COMPRESSIONOPTIONSOID, ObjectIdGetDatum(cmoptoid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+
+ cmid = ((Form_pg_compression_opt) GETSTRUCT(tup))->cmid;
+ ReleaseSysCache(tup);
+
+ /* now we can get the name */
+ tup = SearchSysCache1(COMPRESSIONMETHODOID, ObjectIdGetDatum(cmid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for compression method %u", cmid);
+
+ cmform = (Form_pg_compression) GETSTRUCT(tup);
+ result = pstrdup(NameStr(cmform->cmname));
+ ReleaseSysCache(tup);
+ return result;
+}
+
+/* get_compression_options */
+List *
+GetCompressionOptionsList(Oid cmoptoid)
+{
+ HeapTuple tuple;
+ List *result = NULL;
+ bool isnull;
+ Datum cmoptions;
+
+ /* Get handler function OID for the compression method */
+ tuple = SearchSysCache1(COMPRESSIONOPTIONSOID, ObjectIdGetDatum(cmoptoid));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+
+ cmoptions = SysCacheGetAttr(COMPRESSIONOPTIONSOID, tuple,
+ Anum_pg_compression_opt_cmoptions, &isnull);
+
+ if (!isnull)
+ result = untransformRelOptions(cmoptions);
+
+ ReleaseSysCache(tuple);
+ return result;
+}
+
+CompressionMethodRoutine *
+GetCompressionRoutine(Oid cmoptoid)
+{
+ HeapTuple tuple;
+ Form_pg_compression_opt cmopt;
+ regproc cmhandler;
+
+ /* Get handler function OID for the compression method */
+ tuple = SearchSysCache1(COMPRESSIONOPTIONSOID, ObjectIdGetDatum(cmoptoid));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+
+ cmopt = (Form_pg_compression_opt) GETSTRUCT(tuple);
+ cmhandler = cmopt->cmhandler;
+
+ /* Complain if handler OID is invalid */
+ if (!RegProcedureIsValid(cmhandler))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("could not find compression method handler for compression options %u",
+ cmoptoid)));
+
+ ReleaseSysCache(tuple);
+
+ /* And finally, call the handler function to get the API struct. */
+ return get_compression_method_routine(cmhandler, InvalidOid);
+}
+
+/*
+ * Call the specified compression method handler
+ * routine to get its CompressionMethodRoutine struct,
+ * which will be palloc'd in the caller's context.
+ */
+static CompressionMethodRoutine *
+get_compression_method_routine(Oid cmhandler, Oid typeid)
+{
+ Datum datum;
+ CompressionMethodRoutine *routine;
+ CompressionMethodOpArgs opargs;
+
+ opargs.typeid = typeid;
+ opargs.cmhanderid = cmhandler;
+
+ datum = OidFunctionCall1(cmhandler, PointerGetDatum(&opargs));
+ routine = (CompressionMethodRoutine *) DatumGetPointer(datum);
+
+ if (routine == NULL || !IsA(routine, CompressionMethodRoutine))
+ elog(ERROR, "compression method handler function %u "
+ "did not return an CompressionMethodRoutine struct",
+ cmhandler);
+
+ return routine;
+}
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 05232ea505..4f71a240c6 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2768,8 +2768,8 @@ CopyFrom(CopyState cstate)
List *recheckIndexes = NIL;
/* OK, store the tuple and create index entries for it */
- heap_insert(resultRelInfo->ri_RelationDesc, tuple, mycid,
- hi_options, bistate);
+ heap_insert(resultRelInfo->ri_RelationDesc, tuple,
+ mycid, hi_options, bistate);
if (resultRelInfo->ri_NumIndices > 0)
recheckIndexes = ExecInsertIndexTuples(slot,
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 4d77411a68..aba2087839 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -112,7 +112,8 @@ create_ctas_internal(List *attrList, IntoClause *into)
* Create the relation. (This will error out if there's an existing view,
* so we don't need more code to complain if "replace" is false.)
*/
- intoRelationAddr = DefineRelation(create, relkind, InvalidOid, NULL, NULL);
+ intoRelationAddr = DefineRelation(create, relkind, InvalidOid, NULL, NULL,
+ NULL);
/*
* If necessary, create a TOAST table for the target table. Note that
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index 2b30677d6f..8611db22ec 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -275,6 +275,10 @@ does_not_exist_skipping(ObjectType objtype, Node *object)
name = NameListToString(castNode(List, object));
}
break;
+ case OBJECT_COMPRESSION_METHOD:
+ msg = gettext_noop("compression method \"%s\" does not exist, skipping");
+ name = strVal((Value *) object);
+ break;
case OBJECT_CONVERSION:
if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
{
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 938133bbe4..f520c7fe7b 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -91,6 +91,7 @@ static event_trigger_support_data event_trigger_support[] = {
{"CAST", true},
{"CONSTRAINT", true},
{"COLLATION", true},
+ {"COMPRESSION METHOD", true},
{"CONVERSION", true},
{"DATABASE", false},
{"DOMAIN", true},
@@ -1085,6 +1086,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
case OBJECT_CAST:
case OBJECT_COLUMN:
case OBJECT_COLLATION:
+ case OBJECT_COMPRESSION_METHOD:
case OBJECT_CONVERSION:
case OBJECT_DEFACL:
case OBJECT_DEFAULT:
@@ -1144,6 +1146,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
case OCLASS_ROLE:
+ case OCLASS_COMPRESSION_OPTIONS:
/* no support for global objects */
return false;
case OCLASS_EVENT_TRIGGER:
@@ -1183,6 +1186,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_COMPRESSION_METHOD:
return true;
/*
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index 9ad991507f..b840309d03 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -61,7 +61,7 @@ static void import_error_callback(void *arg);
* processing, hence any validation should be done before this
* conversion.
*/
-static Datum
+Datum
optionListToArray(List *options)
{
ArrayBuildState *astate = NULL;
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 5e1b0fe289..eb9f72dcba 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -212,7 +212,8 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
stmt->tablespacename = NULL;
stmt->if_not_exists = seq->if_not_exists;
- address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL, NULL);
+ address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL, NULL,
+ NULL);
seqoid = address.objectId;
Assert(seqoid != InvalidOid);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index c902293741..cbc80d282e 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -14,6 +14,7 @@
*/
#include "postgres.h"
+#include "access/compression.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "access/multixact.h"
@@ -33,6 +34,8 @@
#include "catalog/partition.h"
#include "catalog/pg_am.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_depend.h"
@@ -41,6 +44,7 @@
#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
+#include "catalog/pg_proc.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
@@ -90,6 +94,7 @@
#include "storage/smgr.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+#include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
@@ -459,6 +464,8 @@ static void ATExecGenericOptions(Relation rel, List *options);
static void ATExecEnableRowSecurity(Relation rel);
static void ATExecDisableRowSecurity(Relation rel);
static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
+static void ATExecAlterColumnCompression(AlteredTableInfo *tab, Relation rel,
+ const char *column, ColumnCompression * compression, LOCKMODE lockmode);
static void copy_relation_data(SMgrRelation rel, SMgrRelation dst,
ForkNumber forkNum, char relpersistence);
@@ -503,7 +510,8 @@ static ObjectAddress ATExecDetachPartition(Relation rel, RangeVar *name);
*/
ObjectAddress
DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
- ObjectAddress *typaddress, const char *queryString)
+ ObjectAddress *typaddress, const char *queryString,
+ Node **pAlterStmt)
{
char relname[NAMEDATALEN];
Oid namespaceId;
@@ -523,6 +531,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
Oid ofTypeId;
ObjectAddress address;
+ AlterTableStmt *alterStmt = NULL;
/*
* Truncate relname to appropriate length (probably a waste of time, as
@@ -724,6 +733,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
if (colDef->identity)
attr->attidentity = colDef->identity;
+
+ transformColumnCompression(colDef, stmt->relation, &alterStmt);
}
/*
@@ -921,6 +932,11 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
*/
relation_close(rel, NoLock);
+ if (pAlterStmt)
+ *pAlterStmt = (Node *) alterStmt;
+ else
+ Assert(!alterStmt);
+
return address;
}
@@ -1610,6 +1626,7 @@ storage_name(char c)
}
}
+
/*----------
* MergeAttributes
* Returns new schema given initial schema and superclasses.
@@ -1944,6 +1961,19 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
storage_name(def->storage),
storage_name(attribute->attstorage))));
+ if (OidIsValid(attribute->attcompression))
+ {
+ ColumnCompression *compression =
+ GetColumnCompressionForAttribute(attribute);
+
+ if (!def->compression)
+ def->compression = compression;
+ else
+ CheckCompressionMismatch(def->compression,
+ compression,
+ attributeName);
+ }
+
def->inhcount++;
/* Merge of NOT NULL constraints = OR 'em together */
def->is_not_null |= attribute->attnotnull;
@@ -1971,6 +2001,9 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
def->collOid = attribute->attcollation;
def->constraints = NIL;
def->location = -1;
+ if (OidIsValid(attribute->attcompression))
+ def->compression =
+ GetColumnCompressionForAttribute(attribute);
inhSchema = lappend(inhSchema, def);
newattno[parent_attno - 1] = ++child_attno;
}
@@ -2180,6 +2213,13 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
storage_name(def->storage),
storage_name(newdef->storage))));
+ if (!def->compression)
+ def->compression = newdef->compression;
+ else if (newdef->compression)
+ CheckCompressionMismatch(def->compression,
+ newdef->compression,
+ attributeName);
+
/* Mark the column as locally defined */
def->is_local = true;
/* Merge of NOT NULL constraints = OR 'em together */
@@ -3279,6 +3319,7 @@ AlterTableGetLockLevel(List *cmds)
*/
case AT_GenericOptions:
case AT_AlterColumnGenericOptions:
+ case AT_AlterColumnCompression:
cmd_lockmode = AccessExclusiveLock;
break;
@@ -3770,6 +3811,12 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* No command-specific prep needed */
pass = AT_PASS_MISC;
break;
+ case AT_AlterColumnCompression:
+ ATSimplePermissions(rel, ATT_TABLE);
+ /* FIXME This command never recurses */
+ /* No command-specific prep needed */
+ pass = AT_PASS_MISC;
+ break;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
@@ -4118,6 +4165,11 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
case AT_DetachPartition:
ATExecDetachPartition(rel, ((PartitionCmd *) cmd->def)->name);
break;
+ case AT_AlterColumnCompression:
+ ATExecAlterColumnCompression(tab, rel, cmd->name,
+ (ColumnCompression *) cmd->def,
+ lockmode);
+ break;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
@@ -5338,6 +5390,8 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
attribute.attislocal = colDef->is_local;
attribute.attinhcount = colDef->inhcount;
attribute.attcollation = collOid;
+ attribute.attcompression = InvalidOid;
+
/* attribute.attacl is handled by InsertPgAttributeTuple */
ReleaseSysCache(typeTuple);
@@ -6438,6 +6492,11 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE loc
errmsg("cannot alter system column \"%s\"",
colName)));
+ if (attrtuple->attcompression && newstorage != 'x' && newstorage != 'm')
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("compressed columns can only have storage MAIN or EXTENDED")));
+
/*
* safety check: do not allow toasted storage modes unless column datatype
* is TOAST-aware.
@@ -9358,6 +9417,8 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_COMPRESSION_METHOD:
+ case OCLASS_COMPRESSION_OPTIONS:
/*
* We don't expect any of these sorts of objects to depend on
@@ -9407,7 +9468,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
if (!(foundDep->refclassid == TypeRelationId &&
foundDep->refobjid == attTup->atttypid) &&
!(foundDep->refclassid == CollationRelationId &&
- foundDep->refobjid == attTup->attcollation))
+ foundDep->refobjid == attTup->attcollation) &&
+ !(foundDep->refclassid == CompressionMethodRelationId &&
+ foundDep->refobjid == attTup->attcompression))
elog(ERROR, "found unexpected dependency for column");
CatalogTupleDelete(depRel, &depTup->t_self);
@@ -9429,6 +9492,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
attTup->attbyval = tform->typbyval;
attTup->attalign = tform->typalign;
attTup->attstorage = tform->typstorage;
+ attTup->attcompression = InvalidOid;
ReleaseSysCache(typeTuple);
@@ -12481,6 +12545,86 @@ ATExecGenericOptions(Relation rel, List *options)
heap_freetuple(tuple);
}
+static void
+ATExecAlterColumnCompression(AlteredTableInfo *tab,
+ Relation rel,
+ const char *column,
+ ColumnCompression * compression,
+ LOCKMODE lockmode)
+{
+ Relation attrel;
+ HeapTuple atttuple;
+ Form_pg_attribute atttableform;
+ AttrNumber attnum;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_attribute];
+ bool nulls[Natts_pg_attribute];
+ bool replace[Natts_pg_attribute];
+
+ attrel = heap_open(AttributeRelationId, RowExclusiveLock);
+
+ atttuple = SearchSysCacheAttName(RelationGetRelid(rel), column);
+ if (!HeapTupleIsValid(atttuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("column \"%s\" of relation \"%s\" does not exist",
+ column, RelationGetRelationName(rel))));
+
+ /* Prevent them from altering a system attribute */
+ atttableform = (Form_pg_attribute) GETSTRUCT(atttuple);
+ attnum = atttableform->attnum;
+ if (attnum <= 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot alter system column \"%s\"", column)));
+
+ if (atttableform->attstorage != 'x' && atttableform->attstorage != 'm')
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("storage for \"%s\" should be MAIN or EXTENDED", column)));
+
+ /* Initialize buffers for new tuple values */
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+ memset(replace, false, sizeof(replace));
+
+ if (compression->methodName || OidIsValid(compression->methodOid))
+ {
+ /* SET COMPRESSED */
+ Oid cmid,
+ cmoptoid;
+
+ cmid = compression->methodName
+ ? get_compression_method_oid(compression->methodName, false)
+ : compression->methodOid;
+
+ cmoptoid = CreateCompressionOptions(atttableform, cmid, compression->options);
+
+ values[Anum_pg_attribute_attcompression - 1] = ObjectIdGetDatum(cmoptoid);
+ replace[Anum_pg_attribute_attcompression - 1] = true;
+
+ }
+ else
+ {
+ /* SET NOT COMPRESSED */
+ values[Anum_pg_attribute_attcompression - 1] = ObjectIdGetDatum(InvalidOid);
+ replace[Anum_pg_attribute_attcompression - 1] = true;
+ }
+
+ newtuple = heap_modify_tuple(atttuple, RelationGetDescr(attrel),
+ values, nulls, replace);
+ CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
+ heap_freetuple(newtuple);
+
+ InvokeObjectPostAlterHook(RelationRelationId,
+ RelationGetRelid(rel),
+ atttableform->attnum);
+
+ ReleaseSysCache(atttuple);
+ heap_close(attrel, RowExclusiveLock);
+}
+
+
/*
* Preparation phase for SET LOGGED/UNLOGGED
*
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 7df942b18b..462ed577ae 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -40,6 +40,7 @@
#include "catalog/pg_am.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_depend.h"
@@ -2182,7 +2183,7 @@ DefineCompositeType(RangeVar *typevar, List *coldeflist)
* Finally create the relation. This also creates the type.
*/
DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, &address,
- NULL);
+ NULL, NULL);
return address;
}
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index 076e2a3a40..9af0f11856 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -251,7 +251,7 @@ DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
* false).
*/
address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid, NULL,
- NULL);
+ NULL, NULL);
Assert(address.objectId != InvalidOid);
/* Make the new view relation visible */
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index 5dfc49deb9..3a80e997a9 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -302,6 +302,9 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc
var->vartypmod != -1))
return false; /* type mismatch */
+ if (OidIsValid(att_tup->attcompression))
+ return false;
+
tlist_item = lnext(tlist_item);
}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index e819188acc..417aaabe66 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2804,6 +2804,7 @@ _copyColumnDef(const ColumnDef *from)
COPY_STRING_FIELD(colname);
COPY_NODE_FIELD(typeName);
+ COPY_NODE_FIELD(compression);
COPY_SCALAR_FIELD(inhcount);
COPY_SCALAR_FIELD(is_local);
COPY_SCALAR_FIELD(is_not_null);
@@ -2822,6 +2823,19 @@ _copyColumnDef(const ColumnDef *from)
return newnode;
}
+static ColumnCompression *
+_copyColumnCompression(const ColumnCompression * from)
+{
+ ColumnCompression *newnode = makeNode(ColumnCompression);
+
+ COPY_STRING_FIELD(methodName);
+ COPY_SCALAR_FIELD(methodOid);
+ COPY_NODE_FIELD(options);
+
+ return newnode;
+}
+
+
static Constraint *
_copyConstraint(const Constraint *from)
{
@@ -5469,6 +5483,9 @@ copyObjectImpl(const void *from)
case T_ColumnDef:
retval = _copyColumnDef(from);
break;
+ case T_ColumnCompression:
+ retval = _copyColumnCompression(from);
+ break;
case T_Constraint:
retval = _copyConstraint(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index ccdcfa2d60..c9ee23d843 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2544,6 +2544,7 @@ _equalColumnDef(const ColumnDef *a, const ColumnDef *b)
{
COMPARE_STRING_FIELD(colname);
COMPARE_NODE_FIELD(typeName);
+ COMPARE_NODE_FIELD(compression);
COMPARE_SCALAR_FIELD(inhcount);
COMPARE_SCALAR_FIELD(is_local);
COMPARE_SCALAR_FIELD(is_not_null);
@@ -2562,6 +2563,16 @@ _equalColumnDef(const ColumnDef *a, const ColumnDef *b)
return true;
}
+static bool
+_equalColumnCompression(const ColumnCompression * a, const ColumnCompression * b)
+{
+ COMPARE_STRING_FIELD(methodName);
+ COMPARE_SCALAR_FIELD(methodOid);
+ COMPARE_NODE_FIELD(options);
+
+ return true;
+}
+
static bool
_equalConstraint(const Constraint *a, const Constraint *b)
{
@@ -3613,6 +3624,9 @@ equal(const void *a, const void *b)
case T_ColumnDef:
retval = _equalColumnDef(a, b);
break;
+ case T_ColumnCompression:
+ retval = _equalColumnCompression(a, b);
+ break;
case T_Constraint:
retval = _equalConstraint(a, b);
break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 8e6f27e153..d22c2d8fbd 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -3623,6 +3623,8 @@ raw_expression_tree_walker(Node *node,
if (walker(coldef->typeName, context))
return true;
+ if (walker(coldef->compression, context))
+ return true;
if (walker(coldef->raw_default, context))
return true;
if (walker(coldef->collClause, context))
@@ -3630,6 +3632,14 @@ raw_expression_tree_walker(Node *node,
/* for now, constraints are ignored */
}
break;
+ case T_ColumnCompression:
+ {
+ ColumnCompression *colcmp = (ColumnCompression *) node;
+
+ if (walker(colcmp->options, context))
+ return true;
+ }
+ break;
case T_IndexElem:
{
IndexElem *indelem = (IndexElem *) node;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 43d62062bc..a182978df8 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2796,6 +2796,7 @@ _outColumnDef(StringInfo str, const ColumnDef *node)
WRITE_STRING_FIELD(colname);
WRITE_NODE_FIELD(typeName);
+ WRITE_NODE_FIELD(compression);
WRITE_INT_FIELD(inhcount);
WRITE_BOOL_FIELD(is_local);
WRITE_BOOL_FIELD(is_not_null);
@@ -2812,6 +2813,16 @@ _outColumnDef(StringInfo str, const ColumnDef *node)
WRITE_LOCATION_FIELD(location);
}
+static void
+_outColumnCompression(StringInfo str, const ColumnCompression * node)
+{
+ WRITE_NODE_TYPE("COLUMNCOMPRESSION");
+
+ WRITE_STRING_FIELD(methodName);
+ WRITE_OID_FIELD(methodOid);
+ WRITE_NODE_FIELD(options);
+}
+
static void
_outTypeName(StringInfo str, const TypeName *node)
{
@@ -4101,6 +4112,9 @@ outNode(StringInfo str, const void *obj)
case T_ColumnDef:
_outColumnDef(str, obj);
break;
+ case T_ColumnCompression:
+ _outColumnCompression(str, obj);
+ break;
case T_TypeName:
_outTypeName(str, obj);
break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4c83a63f7d..02011e3cf4 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -397,6 +397,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
transform_element_list transform_type_list
TriggerTransitions TriggerReferencing
publication_name_list
+ optCompressionParameters
vacuum_relation_list opt_vacuum_relation_list
%type <list> group_by_list
@@ -581,6 +582,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <node> partbound_datum PartitionRangeDatum
%type <list> partbound_datum_list range_datum_list
+%type <node> columnCompression optColumnCompression compressedClause
+
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
* They must be listed first so that their numeric codes do not depend on
@@ -613,9 +616,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
CLUSTER COALESCE COLLATE COLLATION COLUMN COLUMNS COMMENT COMMENTS COMMIT
- COMMITTED CONCURRENTLY CONFIGURATION CONFLICT CONNECTION CONSTRAINT
- CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE
- CROSS CSV CUBE CURRENT_P
+ COMMITTED COMPRESSED COMPRESSION CONCURRENTLY CONFIGURATION CONFLICT
+ CONNECTION CONSTRAINT CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY
+ COST CREATE CROSS CSV CUBE CURRENT_P
CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA
CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
@@ -2167,6 +2170,15 @@ alter_table_cmd:
n->missing_ok = true;
$$ = (Node *)n;
}
+ /* ALTER TABLE <name> ALTER [COLUMN] <colname> SET (NOT COMPRESSED | COMPRESSED <cm> [WITH (<options>)]) */
+ | ALTER opt_column ColId SET columnCompression
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_AlterColumnCompression;
+ n->name = $3;
+ n->def = $5;
+ $$ = (Node *)n;
+ }
/* ALTER TABLE <name> DROP [COLUMN] IF EXISTS <colname> [RESTRICT|CASCADE] */
| DROP opt_column IF_P EXISTS ColId opt_drop_behavior
{
@@ -3260,11 +3272,12 @@ TypedTableElement:
| TableConstraint { $$ = $1; }
;
-columnDef: ColId Typename create_generic_options ColQualList
+columnDef: ColId Typename optColumnCompression create_generic_options ColQualList
{
ColumnDef *n = makeNode(ColumnDef);
n->colname = $1;
n->typeName = $2;
+ n->compression = (ColumnCompression *) $3;
n->inhcount = 0;
n->is_local = true;
n->is_not_null = false;
@@ -3274,8 +3287,8 @@ columnDef: ColId Typename create_generic_options ColQualList
n->raw_default = NULL;
n->cooked_default = NULL;
n->collOid = InvalidOid;
- n->fdwoptions = $3;
- SplitColQualList($4, &n->constraints, &n->collClause,
+ n->fdwoptions = $4;
+ SplitColQualList($5, &n->constraints, &n->collClause,
yyscanner);
n->location = @1;
$$ = (Node *)n;
@@ -3322,6 +3335,39 @@ columnOptions: ColId ColQualList
}
;
+compressedClause:
+ COMPRESSED name optCompressionParameters
+ {
+ ColumnCompression *n = makeNode(ColumnCompression);
+ n->methodName = $2;
+ n->methodOid = InvalidOid;
+ n->options = (List *) $3;
+ $$ = (Node *) n;
+ }
+ ;
+
+columnCompression:
+ compressedClause |
+ NOT COMPRESSED
+ {
+ ColumnCompression *n = makeNode(ColumnCompression);
+ n->methodName = NULL;
+ n->methodOid = InvalidOid;
+ n->options = NIL;
+ $$ = (Node *) n;
+ }
+ ;
+
+optColumnCompression:
+ compressedClause /* FIXME shift/reduce conflict on NOT COMPRESSED/NOT NULL */
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
+optCompressionParameters:
+ WITH '(' generic_option_list ')' { $$ = $3; }
+ | /*EMPTY*/ { $$ = NIL; }
+ ;
+
ColQualList:
ColQualList ColConstraint { $$ = lappend($1, $2); }
| /*EMPTY*/ { $$ = NIL; }
@@ -5682,6 +5728,15 @@ DefineStmt:
n->if_not_exists = true;
$$ = (Node *)n;
}
+ | CREATE COMPRESSION METHOD any_name HANDLER handler_name
+ {
+ DefineStmt *n = makeNode(DefineStmt);
+ n->kind = OBJECT_COMPRESSION_METHOD;
+ n->args = NIL;
+ n->defnames = $4;
+ n->definition = list_make1(makeDefElem("handler", (Node *) $6, @6));
+ $$ = (Node *) n;
+ }
;
definition: '(' def_list ')' { $$ = $2; }
@@ -6190,6 +6245,7 @@ drop_type_any_name:
/* object types taking name_list */
drop_type_name:
ACCESS METHOD { $$ = OBJECT_ACCESS_METHOD; }
+ | COMPRESSION METHOD { $$ = OBJECT_COMPRESSION_METHOD; }
| EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; }
| EXTENSION { $$ = OBJECT_EXTENSION; }
| FOREIGN DATA_P WRAPPER { $$ = OBJECT_FDW; }
@@ -6253,7 +6309,7 @@ opt_restart_seqs:
* The COMMENT ON statement can take different forms based upon the type of
* the object associated with the comment. The form of the statement is:
*
- * COMMENT ON [ [ ACCESS METHOD | CONVERSION | COLLATION |
+ * COMMENT ON [ [ ACCESS METHOD | COMPRESSION METHOD | CONVERSION | COLLATION |
* DATABASE | DOMAIN |
* EXTENSION | EVENT TRIGGER | FOREIGN DATA WRAPPER |
* FOREIGN TABLE | INDEX | [PROCEDURAL] LANGUAGE |
@@ -6443,6 +6499,7 @@ comment_type_any_name:
/* object types taking name */
comment_type_name:
ACCESS METHOD { $$ = OBJECT_ACCESS_METHOD; }
+ | COMPRESSION METHOD { $$ = OBJECT_COMPRESSION_METHOD; }
| DATABASE { $$ = OBJECT_DATABASE; }
| EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; }
| EXTENSION { $$ = OBJECT_EXTENSION; }
@@ -14632,6 +14689,8 @@ unreserved_keyword:
| COMMENTS
| COMMIT
| COMMITTED
+ | COMPRESSED
+ | COMPRESSION
| CONFIGURATION
| CONFLICT
| CONNECTION
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index decf4e3830..1a6621b438 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -27,6 +27,7 @@
#include "postgres.h"
#include "access/amapi.h"
+#include "access/compression.h"
#include "access/htup_details.h"
#include "access/reloptions.h"
#include "catalog/dependency.h"
@@ -494,6 +495,33 @@ generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column,
*sname_p = sname;
}
+void
+transformColumnCompression(ColumnDef *column, RangeVar *relation,
+ AlterTableStmt **alterStmt)
+{
+ if (column->compression)
+ {
+ AlterTableCmd *cmd;
+
+ cmd = makeNode(AlterTableCmd);
+ cmd->subtype = AT_AlterColumnCompression;
+ cmd->name = column->colname;
+ cmd->def = (Node *) column->compression;
+ cmd->behavior = DROP_RESTRICT;
+ cmd->missing_ok = false;
+
+ if (!*alterStmt)
+ {
+ *alterStmt = makeNode(AlterTableStmt);
+ (*alterStmt)->relation = relation;
+ (*alterStmt)->relkind = OBJECT_TABLE;
+ (*alterStmt)->cmds = NIL;
+ }
+
+ (*alterStmt)->cmds = lappend((*alterStmt)->cmds, cmd);
+ }
+}
+
/*
* transformColumnDefinition -
* transform a single ColumnDef within CREATE TABLE
@@ -794,6 +822,16 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
cxt->alist = lappend(cxt->alist, stmt);
}
+
+ if (cxt->isalter)
+ {
+ AlterTableStmt *stmt = NULL;
+
+ transformColumnCompression(column, cxt->relation, &stmt);
+
+ if (stmt)
+ cxt->alist = lappend(cxt->alist, stmt);
+ }
}
/*
@@ -1003,6 +1041,10 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
def->collOid = attribute->attcollation;
def->constraints = NIL;
def->location = -1;
+ if (attribute->attcompression)
+ def->compression = GetColumnCompressionForAttribute(attribute);
+ else
+ def->compression = NULL;
/*
* Add to column list
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 82a707af7b..331d133660 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -998,6 +998,7 @@ ProcessUtilitySlow(ParseState *pstate,
foreach(l, stmts)
{
Node *stmt = (Node *) lfirst(l);
+ Node *alterStmt = NULL;
if (IsA(stmt, CreateStmt))
{
@@ -1008,7 +1009,9 @@ ProcessUtilitySlow(ParseState *pstate,
address = DefineRelation((CreateStmt *) stmt,
RELKIND_RELATION,
InvalidOid, NULL,
- queryString);
+ queryString,
+ &alterStmt);
+
EventTriggerCollectSimpleCommand(address,
secondaryObject,
stmt);
@@ -1042,7 +1045,8 @@ ProcessUtilitySlow(ParseState *pstate,
address = DefineRelation((CreateStmt *) stmt,
RELKIND_FOREIGN_TABLE,
InvalidOid, NULL,
- queryString);
+ queryString,
+ &alterStmt);
CreateForeignTable((CreateForeignTableStmt *) stmt,
address.objectId);
EventTriggerCollectSimpleCommand(address,
@@ -1074,6 +1078,9 @@ ProcessUtilitySlow(ParseState *pstate,
NULL);
}
+ if (alterStmt)
+ lappend(stmts, alterStmt);
+
/* Need CCI between commands */
if (lnext(l) != NULL)
CommandCounterIncrement();
@@ -1283,6 +1290,11 @@ ProcessUtilitySlow(ParseState *pstate,
stmt->definition,
stmt->if_not_exists);
break;
+ case OBJECT_COMPRESSION_METHOD:
+ Assert(stmt->args == NIL);
+ address = DefineCompressionMethod(stmt->defnames,
+ stmt->definition);
+ break;
default:
elog(ERROR, "unrecognized define stmt type: %d",
(int) stmt->kind);
@@ -1696,6 +1708,11 @@ ExecDropStmt(DropStmt *stmt, bool isTopLevel)
case OBJECT_FOREIGN_TABLE:
RemoveRelations(stmt);
break;
+ case OBJECT_COMPRESSION_METHOD:
+ if (stmt->behavior == DROP_CASCADE)
+ {
+ /* TODO decompress columns instead of their deletion */
+ }
default:
RemoveObjects(stmt);
break;
@@ -2309,6 +2326,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_STATISTIC_EXT:
tag = "DROP STATISTICS";
break;
+ case OBJECT_COMPRESSION_METHOD:
+ tag = "DROP COMPRESSION METHOD";
+ break;
default:
tag = "???";
}
@@ -2412,6 +2432,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_ACCESS_METHOD:
tag = "CREATE ACCESS METHOD";
break;
+ case OBJECT_COMPRESSION_METHOD:
+ tag = "CREATE COMPRESSION METHOD";
+ break;
default:
tag = "???";
}
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index be793539a3..a5cfbe3d4c 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -418,3 +418,4 @@ PSEUDOTYPE_DUMMY_IO_FUNCS(internal);
PSEUDOTYPE_DUMMY_IO_FUNCS(opaque);
PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement);
PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray);
+PSEUDOTYPE_DUMMY_IO_FUNCS(compression_handler);
diff --git a/src/backend/utils/adt/tsvector.c b/src/backend/utils/adt/tsvector.c
index b0a9217d1e..c8f9004a38 100644
--- a/src/backend/utils/adt/tsvector.c
+++ b/src/backend/utils/adt/tsvector.c
@@ -14,11 +14,14 @@
#include "postgres.h"
+#include "access/compression.h"
+#include "catalog/pg_type.h"
#include "libpq/pqformat.h"
#include "tsearch/ts_locale.h"
#include "tsearch/ts_utils.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
+#include "common/pg_lzcompress.h"
typedef struct
{
@@ -548,3 +551,92 @@ tsvectorrecv(PG_FUNCTION_ARGS)
PG_RETURN_TSVECTOR(vec);
}
+
+/*
+ * Compress tsvector using LZ compression.
+ * Instead of trying to compress whole tsvector we compress only text part
+ * here. This approach gives more compressibility for tsvectors.
+ */
+static struct varlena *
+tsvector_compress(AttributeCompression *ac, const struct varlena *data)
+{
+ char *tmp;
+ int32 valsize = VARSIZE_ANY_EXHDR(data);
+ int32 len = valsize + VARHDRSZ_CUSTOM_COMPRESSED,
+ lenc;
+
+ char *arr = VARDATA(data),
+ *str = STRPTR((TSVector) data);
+ int32 arrsize = str - arr;
+
+ Assert(!VARATT_IS_COMPRESSED(data));
+ tmp = palloc0(len);
+
+ /* we try to compress string part of tsvector first */
+ lenc = pglz_compress(str,
+ valsize - arrsize,
+ tmp + VARHDRSZ_CUSTOM_COMPRESSED + arrsize,
+ PGLZ_strategy_default);
+
+ if (lenc >= 0)
+ {
+ /* tsvector is compressible, copy size and entries to its beginning */
+ memcpy(tmp + VARHDRSZ_CUSTOM_COMPRESSED, arr, arrsize);
+ SET_VARSIZE_COMPRESSED(tmp, arrsize + lenc + VARHDRSZ_CUSTOM_COMPRESSED);
+ return (struct varlena *) tmp;
+ }
+
+ pfree(tmp);
+ return NULL;
+}
+
+static void
+tsvector_configure(Form_pg_attribute attr, List *options)
+{
+ if (options != NIL)
+ elog(ERROR, "the compression method for tsvector doesn't take any options");
+}
+
+static struct varlena *
+tsvector_decompress(AttributeCompression *ac, const struct varlena *data)
+{
+ char *tmp,
+ *raw_data = (char *) data + VARHDRSZ_CUSTOM_COMPRESSED;
+ int32 count,
+ arrsize,
+ len = VARRAWSIZE_4B_C(data) + VARHDRSZ;
+
+ Assert(VARATT_IS_CUSTOM_COMPRESSED(data));
+ tmp = palloc0(len);
+ SET_VARSIZE(tmp, len);
+ count = *((uint32 *) raw_data);
+ arrsize = sizeof(uint32) + count * sizeof(WordEntry);
+ memcpy(VARDATA(tmp), raw_data, arrsize);
+
+ if (pglz_decompress(raw_data + arrsize,
+ VARSIZE(data) - VARHDRSZ_CUSTOM_COMPRESSED - arrsize,
+ VARDATA(tmp) + arrsize,
+ VARRAWSIZE_4B_C(data) - arrsize) < 0)
+ elog(ERROR, "compressed tsvector is corrupted");
+
+ return (struct varlena *) tmp;
+}
+
+Datum
+tsvector_compression_handler(PG_FUNCTION_ARGS)
+{
+ CompressionMethodOpArgs *opargs = (CompressionMethodOpArgs *)
+ PG_GETARG_POINTER(0);
+ CompressionMethodRoutine *cmr = makeNode(CompressionMethodRoutine);
+ Oid typeid = opargs->typeid;
+
+ if (OidIsValid(typeid) && typeid != TSVECTOROID)
+ elog(ERROR, "unexpected type %d for tsvector compression handler", typeid);
+
+ cmr->configure = tsvector_configure;
+ cmr->drop = NULL;
+ cmr->compress = tsvector_compress;
+ cmr->decompress = tsvector_decompress;
+
+ PG_RETURN_POINTER(cmr);
+}
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 48961e31aa..92004b9512 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -2276,16 +2276,12 @@ getBaseType(Oid typid)
}
/*
- * getBaseTypeAndTypmod
- * If the given type is a domain, return its base type and typmod;
- * otherwise return the type's own OID, and leave *typmod unchanged.
- *
* Note that the "applied typmod" should be -1 for every domain level
* above the bottommost; therefore, if the passed-in typid is indeed
* a domain, *typmod should be -1.
*/
-Oid
-getBaseTypeAndTypmod(Oid typid, int32 *typmod)
+static inline HeapTuple
+getBaseTypeTuple(Oid *typid, int32 *typmod)
{
/*
* We loop to find the bottom base type in a stack of domains.
@@ -2295,24 +2291,33 @@ getBaseTypeAndTypmod(Oid typid, int32 *typmod)
HeapTuple tup;
Form_pg_type typTup;
- tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*typid));
if (!HeapTupleIsValid(tup))
- elog(ERROR, "cache lookup failed for type %u", typid);
+ elog(ERROR, "cache lookup failed for type %u", *typid);
typTup = (Form_pg_type) GETSTRUCT(tup);
if (typTup->typtype != TYPTYPE_DOMAIN)
- {
/* Not a domain, so done */
- ReleaseSysCache(tup);
- break;
- }
+ return tup;
Assert(*typmod == -1);
- typid = typTup->typbasetype;
+ *typid = typTup->typbasetype;
*typmod = typTup->typtypmod;
ReleaseSysCache(tup);
}
+}
+
+/*
+ * getBaseTypeAndTypmod
+ * If the given type is a domain, return its base type and typmod;
+ * otherwise return the type's own OID, and leave *typmod unchanged.
+ */
+Oid
+getBaseTypeAndTypmod(Oid typid, int32 *typmod)
+{
+ HeapTuple tup = getBaseTypeTuple(&typid, typmod);
+ ReleaseSysCache(tup);
return typid;
}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index b8e37809b0..f8849c89e7 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -30,6 +30,7 @@
#include <fcntl.h>
#include <unistd.h>
+#include "access/compression.h"
#include "access/htup_details.h"
#include "access/multixact.h"
#include "access/nbtree.h"
@@ -76,6 +77,7 @@
#include "storage/smgr.h"
#include "utils/array.h"
#include "utils/builtins.h"
+#include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
@@ -565,6 +567,7 @@ RelationBuildTupleDesc(Relation relation)
attrdef[ndef].adbin = NULL;
ndef++;
}
+
need--;
if (need == 0)
break;
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 888edbb325..c80e31fd9b 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -31,6 +31,8 @@
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
@@ -309,6 +311,39 @@ static const struct cachedesc cacheinfo[] = {
},
8
},
+ {CompressionMethodRelationId, /* COMPRESSIONMETHODOID */
+ CompressionMethodOidIndexId,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 8
+ },
+ {CompressionMethodRelationId, /* COMPRESSIONMETHODNAME */
+ CompressionMethodNameIndexId,
+ 1,
+ {
+ Anum_pg_compression_cmname,
+ 0,
+ 0,
+ 0
+ },
+ 8
+ },
+ {CompressionOptRelationId, /* COMPRESSIONOPTIONSOID */
+ CompressionOptionsOidIndexId,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 8,
+ },
{ConversionRelationId, /* CONDEFAULT */
ConversionDefaultIndexId,
4,
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 4b47951de1..2d6827f1f3 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -54,6 +54,7 @@ static DumpableObject **oprinfoindex;
static DumpableObject **collinfoindex;
static DumpableObject **nspinfoindex;
static DumpableObject **extinfoindex;
+static DumpableObject **cminfoindex;
static int numTables;
static int numTypes;
static int numFuncs;
@@ -61,6 +62,7 @@ static int numOperators;
static int numCollations;
static int numNamespaces;
static int numExtensions;
+static int numCompressionMethods;
/* This is an array of object identities, not actual DumpableObjects */
static ExtensionMemberId *extmembers;
@@ -93,6 +95,8 @@ getSchemaData(Archive *fout, int *numTablesPtr)
NamespaceInfo *nspinfo;
ExtensionInfo *extinfo;
InhInfo *inhinfo;
+ CompressionMethodInfo *cminfo;
+
int numAggregates;
int numInherits;
int numRules;
@@ -289,6 +293,11 @@ getSchemaData(Archive *fout, int *numTablesPtr)
write_msg(NULL, "reading subscriptions\n");
getSubscriptions(fout);
+ if (g_verbose)
+ write_msg(NULL, "reading compression methods\n");
+ cminfo = getCompressionMethods(fout, &numCompressionMethods);
+ cminfoindex = buildIndexArray(cminfo, numCompressionMethods, sizeof(CompressionMethodInfo));
+
*numTablesPtr = numTables;
return tblinfo;
}
@@ -827,6 +836,17 @@ findExtensionByOid(Oid oid)
return (ExtensionInfo *) findObjectByOid(oid, extinfoindex, numExtensions);
}
+/*
+ * findCompressionMethodByOid
+ * finds the entry (in cminfo) of the compression method with the given oid
+ * returns NULL if not found
+ */
+CompressionMethodInfo *
+findCompressionMethodByOid(Oid oid)
+{
+ return (CompressionMethodInfo *) findObjectByOid(oid, cminfoindex,
+ numCompressionMethods);
+}
/*
* setExtensionMembership
diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index ce3100f09d..6b962a0ba0 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -77,6 +77,7 @@ typedef struct _restoreOptions
int no_publications; /* Skip publication entries */
int no_security_labels; /* Skip security label entries */
int no_subscriptions; /* Skip subscription entries */
+ int no_compression_methods; /* Skip compression methods */
int strict_names;
const char *filename;
@@ -149,6 +150,7 @@ typedef struct _dumpOptions
int no_security_labels;
int no_publications;
int no_subscriptions;
+ int no_compression_methods;
int no_synchronized_snapshots;
int no_unlogged_table_data;
int serializable_deferrable;
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 6d4c28852c..03a80c5fe5 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -361,6 +361,7 @@ main(int argc, char **argv)
{"no-synchronized-snapshots", no_argument, &dopt.no_synchronized_snapshots, 1},
{"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
{"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
+ {"no-compression-methods", no_argument, &dopt.no_compression_methods, 1},
{"no-sync", no_argument, NULL, 7},
{NULL, 0, NULL, 0}
@@ -3957,6 +3958,99 @@ dumpSubscription(Archive *fout, SubscriptionInfo *subinfo)
destroyPQExpBuffer(query);
}
+/*
+ * getCompressionMethods
+ * get information about compression methods
+ */
+CompressionMethodInfo *
+getCompressionMethods(Archive *fout, int *numMethods)
+{
+ DumpOptions *dopt = fout->dopt;
+ PQExpBuffer query;
+ PGresult *res;
+ CompressionMethodInfo *cminfo;
+ int i_tableoid;
+ int i_oid;
+ int i_handler;
+ int i_name;
+ int i,
+ ntups;
+
+ if (dopt->no_compression_methods || fout->remoteVersion < 110000)
+ return NULL;
+
+ query = createPQExpBuffer();
+ resetPQExpBuffer(query);
+
+ /* Get the compression methods in current database. */
+ appendPQExpBuffer(query,
+ "SELECT c.tableoid, c.oid, c.cmname, c.cmhandler "
+ "FROM pg_catalog.pg_compression c");
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_name = PQfnumber(res, "cmname");
+ i_handler = PQfnumber(res, "cmhandler");
+
+ cminfo = pg_malloc(ntups * sizeof(CompressionMethodInfo));
+
+ for (i = 0; i < ntups; i++)
+ {
+ cminfo[i].dobj.objType = DO_COMPRESSION_METHOD;
+ cminfo[i].dobj.catId.tableoid =
+ atooid(PQgetvalue(res, i, i_tableoid));
+ cminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&cminfo[i].dobj);
+ cminfo[i].cmhandler = pg_strdup(PQgetvalue(res, i, i_handler));
+ cminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_name));
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(cminfo[i].dobj), fout);
+ }
+ if (numMethods)
+ *numMethods = ntups;
+
+ PQclear(res);
+ destroyPQExpBuffer(query);
+
+ return cminfo;
+}
+
+/*
+ * dumpCompressionMethod
+ * dump the definition of the given compression method
+ */
+static void
+dumpCompressionMethod(Archive *fout, CompressionMethodInfo *cminfo)
+{
+ PQExpBuffer query;
+
+ if (!(cminfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
+ return;
+
+ query = createPQExpBuffer();
+ appendPQExpBuffer(query, "CREATE COMPRESSION METHOD %s HANDLER",
+ fmtId(cminfo->dobj.name));
+ appendPQExpBuffer(query, " %s;\n", fmtId(cminfo->cmhandler));
+
+ ArchiveEntry(fout,
+ cminfo->dobj.catId,
+ cminfo->dobj.dumpId,
+ cminfo->dobj.name,
+ NULL,
+ NULL,
+ "", false,
+ "COMPRESSION METHOD", SECTION_PRE_DATA,
+ query->data, "", NULL,
+ NULL, 0,
+ NULL, NULL);
+
+ destroyPQExpBuffer(query);
+}
+
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
@@ -4484,7 +4578,47 @@ getTypes(Archive *fout, int *numTypes)
/* Make sure we are in proper schema */
selectSourceSchema(fout, "pg_catalog");
- if (fout->remoteVersion >= 90600)
+ if (fout->remoteVersion >= 110000)
+ {
+ PQExpBuffer acl_subquery = createPQExpBuffer();
+ PQExpBuffer racl_subquery = createPQExpBuffer();
+ PQExpBuffer initacl_subquery = createPQExpBuffer();
+ PQExpBuffer initracl_subquery = createPQExpBuffer();
+
+ buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
+ initracl_subquery, "t.typacl", "t.typowner", "'T'",
+ dopt->binary_upgrade);
+
+ appendPQExpBuffer(query, "SELECT t.tableoid, t.oid, t.typname, "
+ "t.typnamespace, "
+ "%s AS typacl, "
+ "%s AS rtypacl, "
+ "%s AS inittypacl, "
+ "%s AS initrtypacl, "
+ "(%s t.typowner) AS rolname, "
+ "t.typelem, t.typrelid, "
+ "CASE WHEN t.typrelid = 0 THEN ' '::\"char\" "
+ "ELSE (SELECT relkind FROM pg_class WHERE oid = t.typrelid) END AS typrelkind, "
+ "t.typtype, t.typisdefined, "
+ "t.typname[0] = '_' AND t.typelem != 0 AND "
+ "(SELECT typarray FROM pg_type te WHERE oid = t.typelem) = t.oid AS isarray "
+ "FROM pg_type t "
+ "LEFT JOIN pg_init_privs pip ON "
+ "(t.oid = pip.objoid "
+ "AND pip.classoid = 'pg_type'::regclass "
+ "AND pip.objsubid = 0) ",
+ acl_subquery->data,
+ racl_subquery->data,
+ initacl_subquery->data,
+ initracl_subquery->data,
+ username_subquery);
+
+ destroyPQExpBuffer(acl_subquery);
+ destroyPQExpBuffer(racl_subquery);
+ destroyPQExpBuffer(initacl_subquery);
+ destroyPQExpBuffer(initracl_subquery);
+ }
+ else if (fout->remoteVersion >= 90600)
{
PQExpBuffer acl_subquery = createPQExpBuffer();
PQExpBuffer racl_subquery = createPQExpBuffer();
@@ -7895,6 +8029,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
int i_attoptions;
int i_attcollation;
int i_attfdwoptions;
+ int i_attcmoptions;
+ int i_attcmname;
PGresult *res;
int ntups;
bool hasdefaults;
@@ -7930,7 +8066,48 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
resetPQExpBuffer(q);
- if (fout->remoteVersion >= 100000)
+ if (fout->remoteVersion >= 110000)
+ {
+ /*
+ * attcompression is new in version 11
+ */
+ appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, "
+ "a.attstattarget, a.attstorage, t.typstorage, "
+ "a.attnotnull, a.atthasdef, a.attisdropped, "
+ "a.attlen, a.attalign, a.attislocal, "
+ "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
+ "array_to_string(a.attoptions, ', ') AS attoptions, "
+ "CASE WHEN a.attcollation <> t.typcollation "
+ "THEN a.attcollation ELSE 0 END AS attcollation, "
+ "a.attidentity, "
+ /* fdw options */
+ "pg_catalog.array_to_string(ARRAY("
+ "SELECT pg_catalog.quote_ident(option_name) || "
+ "' ' || pg_catalog.quote_literal(option_value) "
+ "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
+ "ORDER BY option_name"
+ "), E',\n ') AS attfdwoptions, "
+ /* compression options */
+ "pg_catalog.array_to_string(ARRAY("
+ "SELECT pg_catalog.quote_ident(option_name) || "
+ "' ' || pg_catalog.quote_literal(option_value) "
+ "FROM pg_catalog.pg_options_to_table(c.cmoptions) "
+ "ORDER BY option_name"
+ "), E',\n ') AS attcmoptions, "
+ "cm.cmname AS attcmname "
+ /* FROM */
+ "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
+ "ON a.atttypid = t.oid "
+ "LEFT JOIN pg_catalog.pg_compression_opt c "
+ "ON a.attcompression = c.oid "
+ "LEFT JOIN pg_catalog.pg_compression cm "
+ "ON c.cmid = cm.oid "
+ "WHERE a.attrelid = '%u'::pg_catalog.oid "
+ "AND a.attnum > 0::pg_catalog.int2 "
+ "ORDER BY a.attnum",
+ tbinfo->dobj.catId.oid);
+ }
+ else if (fout->remoteVersion >= 100000)
{
/*
* attidentity is new in version 10.
@@ -7949,9 +8126,13 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"' ' || pg_catalog.quote_literal(option_value) "
"FROM pg_catalog.pg_options_to_table(attfdwoptions) "
"ORDER BY option_name"
- "), E',\n ') AS attfdwoptions "
+ "), E',\n ') AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
+ "LEFT JOIN pg_catalog.pg_compression_opt c "
+ "ON a.attcompression = c.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
"AND a.attnum > 0::pg_catalog.int2 "
"ORDER BY a.attnum",
@@ -7975,7 +8156,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"' ' || pg_catalog.quote_literal(option_value) "
"FROM pg_catalog.pg_options_to_table(attfdwoptions) "
"ORDER BY option_name"
- "), E',\n ') AS attfdwoptions "
+ "), E',\n ') AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -7999,7 +8182,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"array_to_string(a.attoptions, ', ') AS attoptions, "
"CASE WHEN a.attcollation <> t.typcollation "
"THEN a.attcollation ELSE 0 END AS attcollation, "
- "NULL AS attfdwoptions "
+ "NULL AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -8017,7 +8202,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
"array_to_string(a.attoptions, ', ') AS attoptions, "
"0 AS attcollation, "
- "NULL AS attfdwoptions "
+ "NULL AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -8034,7 +8221,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"a.attlen, a.attalign, a.attislocal, "
"pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
"'' AS attoptions, 0 AS attcollation, "
- "NULL AS attfdwoptions "
+ "NULL AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -8064,6 +8253,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
i_attoptions = PQfnumber(res, "attoptions");
i_attcollation = PQfnumber(res, "attcollation");
i_attfdwoptions = PQfnumber(res, "attfdwoptions");
+ i_attcmname = PQfnumber(res, "attcmname");
+ i_attcmoptions = PQfnumber(res, "attcmoptions");
tbinfo->numatts = ntups;
tbinfo->attnames = (char **) pg_malloc(ntups * sizeof(char *));
@@ -8080,6 +8271,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
tbinfo->attoptions = (char **) pg_malloc(ntups * sizeof(char *));
tbinfo->attcollation = (Oid *) pg_malloc(ntups * sizeof(Oid));
tbinfo->attfdwoptions = (char **) pg_malloc(ntups * sizeof(char *));
+ tbinfo->attcmoptions = (char **) pg_malloc(ntups * sizeof(char *));
+ tbinfo->attcmnames = (char **) pg_malloc(ntups * sizeof(char *));
tbinfo->notnull = (bool *) pg_malloc(ntups * sizeof(bool));
tbinfo->inhNotNull = (bool *) pg_malloc(ntups * sizeof(bool));
tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(ntups * sizeof(AttrDefInfo *));
@@ -8107,6 +8300,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, j, i_attoptions));
tbinfo->attcollation[j] = atooid(PQgetvalue(res, j, i_attcollation));
tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, j, i_attfdwoptions));
+ tbinfo->attcmoptions[j] = pg_strdup(PQgetvalue(res, j, i_attcmoptions));
+ tbinfo->attcmnames[j] = pg_strdup(PQgetvalue(res, j, i_attcmname));
tbinfo->attrdefs[j] = NULL; /* fix below */
if (PQgetvalue(res, j, i_atthasdef)[0] == 't')
hasdefaults = true;
@@ -9596,6 +9791,9 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
case DO_SUBSCRIPTION:
dumpSubscription(fout, (SubscriptionInfo *) dobj);
break;
+ case DO_COMPRESSION_METHOD:
+ dumpCompressionMethod(fout, (CompressionMethodInfo *) dobj);
+ break;
case DO_PRE_DATA_BOUNDARY:
case DO_POST_DATA_BOUNDARY:
/* never dumped, nothing to do */
@@ -15513,6 +15711,15 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
}
}
+ if (tbinfo->attcmnames[j] && strlen(tbinfo->attcmnames[j]))
+ {
+ appendPQExpBuffer(q, " COMPRESSED %s",
+ fmtId(tbinfo->attcmnames[j]));
+ if (nonemptyReloptions(tbinfo->attcmoptions[j]))
+ appendPQExpBuffer(q, " WITH %s",
+ fmtId(tbinfo->attcmoptions[j]));
+ }
+
if (has_default)
appendPQExpBuffer(q, " DEFAULT %s",
tbinfo->attrdefs[j]->adef_expr);
@@ -17778,6 +17985,7 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
case DO_FOREIGN_SERVER:
case DO_TRANSFORM:
case DO_BLOB:
+ case DO_COMPRESSION_METHOD:
/* Pre-data objects: must come before the pre-data boundary */
addObjectDependency(preDataBound, dobj->dumpId);
break;
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index e7593e6da7..cd12f8055a 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -83,7 +83,8 @@ typedef enum
DO_POLICY,
DO_PUBLICATION,
DO_PUBLICATION_REL,
- DO_SUBSCRIPTION
+ DO_SUBSCRIPTION,
+ DO_COMPRESSION_METHOD
} DumpableObjectType;
/* component types of an object which can be selected for dumping */
@@ -315,6 +316,8 @@ typedef struct _tableInfo
char **attoptions; /* per-attribute options */
Oid *attcollation; /* per-attribute collation selection */
char **attfdwoptions; /* per-attribute fdw options */
+ char **attcmoptions; /* per-attribute compression options */
+ char **attcmnames; /* per-attribute compression method names */
bool *notnull; /* NOT NULL constraints on attributes */
bool *inhNotNull; /* true if NOT NULL is inherited */
struct _attrDefInfo **attrdefs; /* DEFAULT expressions */
@@ -611,6 +614,13 @@ typedef struct _SubscriptionInfo
char *subpublications;
} SubscriptionInfo;
+/* The CompressionMethodInfo struct is used to represent compression method */
+typedef struct _CompressionMethodInfo
+{
+ DumpableObject dobj;
+ char *cmhandler;
+} CompressionMethodInfo;
+
/*
* We build an array of these with an entry for each object that is an
* extension member according to pg_depend.
@@ -654,6 +664,7 @@ extern OprInfo *findOprByOid(Oid oid);
extern CollInfo *findCollationByOid(Oid oid);
extern NamespaceInfo *findNamespaceByOid(Oid oid);
extern ExtensionInfo *findExtensionByOid(Oid oid);
+extern CompressionMethodInfo *findCompressionMethodByOid(Oid oid);
extern void setExtensionMembership(ExtensionMemberId *extmems, int nextmems);
extern ExtensionInfo *findOwningExtension(CatalogId catalogId);
@@ -711,5 +722,7 @@ extern void getPublications(Archive *fout);
extern void getPublicationTables(Archive *fout, TableInfo tblinfo[],
int numTables);
extern void getSubscriptions(Archive *fout);
+extern CompressionMethodInfo *getCompressionMethods(Archive *fout,
+ int *numMethods);
#endif /* PG_DUMP_H */
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index 5044a76787..7195f54cdc 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -40,47 +40,48 @@ static const int dbObjectTypePriority[] =
{
1, /* DO_NAMESPACE */
4, /* DO_EXTENSION */
- 5, /* DO_TYPE */
- 5, /* DO_SHELL_TYPE */
- 6, /* DO_FUNC */
- 7, /* DO_AGG */
- 8, /* DO_OPERATOR */
- 8, /* DO_ACCESS_METHOD */
- 9, /* DO_OPCLASS */
- 9, /* DO_OPFAMILY */
- 3, /* DO_COLLATION */
- 11, /* DO_CONVERSION */
- 18, /* DO_TABLE */
- 20, /* DO_ATTRDEF */
- 28, /* DO_INDEX */
- 29, /* DO_STATSEXT */
- 30, /* DO_RULE */
- 31, /* DO_TRIGGER */
- 27, /* DO_CONSTRAINT */
- 32, /* DO_FK_CONSTRAINT */
- 2, /* DO_PROCLANG */
- 10, /* DO_CAST */
- 23, /* DO_TABLE_DATA */
- 24, /* DO_SEQUENCE_SET */
- 19, /* DO_DUMMY_TYPE */
- 12, /* DO_TSPARSER */
- 14, /* DO_TSDICT */
- 13, /* DO_TSTEMPLATE */
- 15, /* DO_TSCONFIG */
- 16, /* DO_FDW */
- 17, /* DO_FOREIGN_SERVER */
- 32, /* DO_DEFAULT_ACL */
- 3, /* DO_TRANSFORM */
- 21, /* DO_BLOB */
- 25, /* DO_BLOB_DATA */
- 22, /* DO_PRE_DATA_BOUNDARY */
- 26, /* DO_POST_DATA_BOUNDARY */
- 33, /* DO_EVENT_TRIGGER */
- 38, /* DO_REFRESH_MATVIEW */
- 34, /* DO_POLICY */
- 35, /* DO_PUBLICATION */
- 36, /* DO_PUBLICATION_REL */
- 37 /* DO_SUBSCRIPTION */
+ 6, /* DO_TYPE */
+ 6, /* DO_SHELL_TYPE */
+ 7, /* DO_FUNC */
+ 8, /* DO_AGG */
+ 9, /* DO_OPERATOR */
+ 9, /* DO_ACCESS_METHOD */
+ 10, /* DO_OPCLASS */
+ 10, /* DO_OPFAMILY */
+ 4, /* DO_COLLATION */
+ 12, /* DO_CONVERSION */
+ 19, /* DO_TABLE */
+ 21, /* DO_ATTRDEF */
+ 29, /* DO_INDEX */
+ 30, /* DO_STATSEXT */
+ 31, /* DO_RULE */
+ 32, /* DO_TRIGGER */
+ 28, /* DO_CONSTRAINT */
+ 33, /* DO_FK_CONSTRAINT */
+ 3, /* DO_PROCLANG */
+ 11, /* DO_CAST */
+ 24, /* DO_TABLE_DATA */
+ 25, /* DO_SEQUENCE_SET */
+ 20, /* DO_DUMMY_TYPE */
+ 13, /* DO_TSPARSER */
+ 15, /* DO_TSDICT */
+ 14, /* DO_TSTEMPLATE */
+ 16, /* DO_TSCONFIG */
+ 17, /* DO_FDW */
+ 18, /* DO_FOREIGN_SERVER */
+ 33, /* DO_DEFAULT_ACL */
+ 4, /* DO_TRANSFORM */
+ 22, /* DO_BLOB */
+ 26, /* DO_BLOB_DATA */
+ 23, /* DO_PRE_DATA_BOUNDARY */
+ 27, /* DO_POST_DATA_BOUNDARY */
+ 34, /* DO_EVENT_TRIGGER */
+ 39, /* DO_REFRESH_MATVIEW */
+ 35, /* DO_POLICY */
+ 36, /* DO_PUBLICATION */
+ 37, /* DO_PUBLICATION_REL */
+ 38, /* DO_SUBSCRIPTION */
+ 5 /* DO_COMPRESSION_METHOD */
};
static DumpId preDataBoundId;
@@ -1436,6 +1437,11 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
"POST-DATA BOUNDARY (ID %d)",
obj->dumpId);
return;
+ case DO_COMPRESSION_METHOD:
+ snprintf(buf, bufsize,
+ "COMPRESSION METHOD %s (ID %d OID %u)",
+ obj->name, obj->dumpId, obj->catId.oid);
+ return;
}
/* shouldn't get here */
snprintf(buf, bufsize,
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 41c5ff89b7..6b72ccf9ea 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -77,6 +77,7 @@ static int use_setsessauth = 0;
static int no_publications = 0;
static int no_security_labels = 0;
static int no_subscriptions = 0;
+static int no_compression_methods = 0;
static int no_unlogged_table_data = 0;
static int no_role_passwords = 0;
static int server_version;
@@ -137,6 +138,7 @@ main(int argc, char *argv[])
{"no-role-passwords", no_argument, &no_role_passwords, 1},
{"no-security-labels", no_argument, &no_security_labels, 1},
{"no-subscriptions", no_argument, &no_subscriptions, 1},
+ {"no-compression-methods", no_argument, &no_compression_methods, 1},
{"no-sync", no_argument, NULL, 4},
{"no-unlogged-table-data", no_argument, &no_unlogged_table_data, 1},
@@ -405,6 +407,8 @@ main(int argc, char *argv[])
appendPQExpBufferStr(pgdumpopts, " --no-security-labels");
if (no_subscriptions)
appendPQExpBufferStr(pgdumpopts, " --no-subscriptions");
+ if (no_compression_methods)
+ appendPQExpBufferStr(pgdumpopts, " --no-compression-methods");
if (no_unlogged_table_data)
appendPQExpBufferStr(pgdumpopts, " --no-unlogged-table-data");
@@ -628,6 +632,7 @@ help(void)
printf(_(" --no-role-passwords do not dump passwords for roles\n"));
printf(_(" --no-security-labels do not dump security label assignments\n"));
printf(_(" --no-subscriptions do not dump subscriptions\n"));
+ printf(_(" --no-compression-methods do not dump compression methods\n"));
printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c
index 860a211a3c..810403ec9c 100644
--- a/src/bin/pg_dump/pg_restore.c
+++ b/src/bin/pg_dump/pg_restore.c
@@ -74,6 +74,7 @@ main(int argc, char **argv)
static int no_publications = 0;
static int no_security_labels = 0;
static int no_subscriptions = 0;
+ static int no_compression_methods = 0;
static int strict_names = 0;
struct option cmdopts[] = {
@@ -122,6 +123,7 @@ main(int argc, char **argv)
{"no-publications", no_argument, &no_publications, 1},
{"no-security-labels", no_argument, &no_security_labels, 1},
{"no-subscriptions", no_argument, &no_subscriptions, 1},
+ {"no-compression-methods", no_argument, &no_compression_methods, 1},
{NULL, 0, NULL, 0}
};
@@ -361,6 +363,7 @@ main(int argc, char **argv)
opts->no_publications = no_publications;
opts->no_security_labels = no_security_labels;
opts->no_subscriptions = no_subscriptions;
+ opts->no_compression_methods = no_compression_methods;
if (if_exists && !opts->dropSchema)
{
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 041b5e0c87..0ded023858 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -735,7 +735,10 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
success = listConversions(pattern, show_verbose, show_system);
break;
case 'C':
- success = listCasts(pattern, show_verbose);
+ if (cmd[2] == 'M')
+ success = describeCompressionMethods(pattern, show_verbose);
+ else
+ success = listCasts(pattern, show_verbose);
break;
case 'd':
if (strncmp(cmd, "ddp", 3) == 0)
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index b7b978a361..eb6d29e4e9 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -200,6 +200,69 @@ describeAccessMethods(const char *pattern, bool verbose)
return true;
}
+/*
+ * \dCM
+ * Takes an optional regexp to select particular compression methods
+ */
+bool
+describeCompressionMethods(const char *pattern, bool verbose)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+ static const bool translate_columns[] = {false, false, false};
+
+ if (pset.sversion < 100000)
+ {
+ char sverbuf[32];
+
+ psql_error("The server (version %s) does not support compression methods.\n",
+ formatPGVersionNumber(pset.sversion, false,
+ sverbuf, sizeof(sverbuf)));
+ return true;
+ }
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT cmname AS \"%s\"",
+ gettext_noop("Name"));
+
+ if (verbose)
+ {
+ appendPQExpBuffer(&buf,
+ ",\n cmhandler AS \"%s\",\n"
+ " pg_catalog.obj_description(oid, 'pg_compression') AS \"%s\"",
+ gettext_noop("Handler"),
+ gettext_noop("Description"));
+ }
+
+ appendPQExpBufferStr(&buf,
+ "\nFROM pg_catalog.pg_compression\n");
+
+ processSQLNamePattern(pset.db, &buf, pattern, false, false,
+ NULL, "cmname", NULL,
+ NULL);
+
+ appendPQExpBufferStr(&buf, "ORDER BY 1;");
+
+ res = PSQLexec(buf.data);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of compression methods");
+ myopt.translate_header = true;
+ myopt.translate_columns = translate_columns;
+ myopt.n_translate_columns = lengthof(translate_columns);
+
+ printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+ PQclear(res);
+ return true;
+}
+
/*
* \db
* Takes an optional regexp to select particular tablespaces
@@ -1716,6 +1779,22 @@ describeOneTableDetails(const char *schemaname,
if (verbose)
{
appendPQExpBufferStr(&buf, ",\n a.attstorage");
+
+ if (pset.sversion >= 100000)
+ appendPQExpBufferStr(&buf, ",\n CASE WHEN attcompression = 0 THEN NULL ELSE "
+ " (SELECT cm.cmname || "
+ " (CASE WHEN cmoptions IS NULL "
+ " THEN '' "
+ " ELSE '(' || array_to_string(ARRAY(SELECT quote_ident(option_name) || ' ' || quote_literal(option_value)"
+ " FROM pg_options_to_table(cmoptions)), ', ') || ')'"
+ " END) "
+ " FROM pg_catalog.pg_compression_opt c "
+ " JOIN pg_catalog.pg_compression cm ON (cm.oid = c.cmid) "
+ " WHERE c.oid = a.attcompression) "
+ " END AS attcmname");
+ else
+ appendPQExpBufferStr(&buf, "\n NULL AS attcmname");
+
appendPQExpBufferStr(&buf, ",\n CASE WHEN a.attstattarget=-1 THEN NULL ELSE a.attstattarget END AS attstattarget");
/*
@@ -1830,6 +1909,10 @@ describeOneTableDetails(const char *schemaname,
if (verbose)
{
headers[cols++] = gettext_noop("Storage");
+
+ if (tableinfo.relkind == RELKIND_RELATION)
+ headers[cols++] = gettext_noop("Compression");
+
if (tableinfo.relkind == RELKIND_RELATION ||
tableinfo.relkind == RELKIND_INDEX ||
tableinfo.relkind == RELKIND_MATVIEW ||
@@ -1925,6 +2008,11 @@ describeOneTableDetails(const char *schemaname,
"???")))),
false, false);
+ /* Column compression. */
+ if (tableinfo.relkind == RELKIND_RELATION)
+ printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1),
+ false, false);
+
/* Statistics target, if the relkind supports this feature */
if (tableinfo.relkind == RELKIND_RELATION ||
tableinfo.relkind == RELKIND_INDEX ||
@@ -1932,7 +2020,7 @@ describeOneTableDetails(const char *schemaname,
tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
{
- printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1),
+ printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2),
false, false);
}
@@ -1943,7 +2031,7 @@ describeOneTableDetails(const char *schemaname,
tableinfo.relkind == RELKIND_COMPOSITE_TYPE ||
tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
- printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2),
+ printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 3),
false, false);
}
}
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 14a5667f3e..0ab8518a36 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -18,6 +18,9 @@ extern bool describeAccessMethods(const char *pattern, bool verbose);
/* \db */
extern bool describeTablespaces(const char *pattern, bool verbose);
+/* \dCM */
+extern bool describeCompressionMethods(const char *pattern, bool verbose);
+
/* \df, \dfa, \dfn, \dft, \dfw, etc. */
extern bool describeFunctions(const char *functypes, const char *pattern, bool verbose, bool showSystem);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index a926c40b9b..25d2fbc125 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -227,6 +227,7 @@ slashUsage(unsigned short int pager)
fprintf(output, _(" \\db[+] [PATTERN] list tablespaces\n"));
fprintf(output, _(" \\dc[S+] [PATTERN] list conversions\n"));
fprintf(output, _(" \\dC[+] [PATTERN] list casts\n"));
+ fprintf(output, _(" \\dCM[+] [PATTERN] list compression methods\n"));
fprintf(output, _(" \\dd[S] [PATTERN] show object descriptions not displayed elsewhere\n"));
fprintf(output, _(" \\dD[S+] [PATTERN] list domains\n"));
fprintf(output, _(" \\ddp [PATTERN] list default privileges\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index a09c49d6cf..1327abdfef 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -889,6 +889,11 @@ static const SchemaQuery Query_for_list_of_statistics = {
" AND d.datname = pg_catalog.current_database() "\
" AND s.subdbid = d.oid"
+#define Query_for_list_of_compression_methods \
+" SELECT pg_catalog.quote_ident(cmname) "\
+" FROM pg_catalog.pg_compression "\
+" WHERE substring(pg_catalog.quote_ident(cmname),1,%d)='%s'"
+
/* the silly-looking length condition is just to eat up the current word */
#define Query_for_list_of_arguments \
"SELECT pg_catalog.oidvectortypes(proargtypes)||')' "\
@@ -1011,6 +1016,7 @@ static const pgsql_thing_t words_after_create[] = {
* CREATE CONSTRAINT TRIGGER is not supported here because it is designed
* to be used only by pg_dump.
*/
+ {"COMPRESSION METHOD", NULL, NULL},
{"CONFIGURATION", Query_for_list_of_ts_configurations, NULL, THING_NO_SHOW},
{"CONVERSION", "SELECT pg_catalog.quote_ident(conname) FROM pg_catalog.pg_conversion WHERE substring(pg_catalog.quote_ident(conname),1,%d)='%s'"},
{"DATABASE", Query_for_list_of_databases},
@@ -1424,8 +1430,8 @@ psql_completion(const char *text, int start, int end)
"\\a",
"\\connect", "\\conninfo", "\\C", "\\cd", "\\copy",
"\\copyright", "\\crosstabview",
- "\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
- "\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
+ "\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dCM", "\\dd", "\\ddp",
+ "\\dD", "\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
"\\dm", "\\dn", "\\do", "\\dO", "\\dp",
"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
@@ -1954,11 +1960,17 @@ psql_completion(const char *text, int start, int end)
/* ALTER TABLE ALTER [COLUMN] <foo> SET */
else if (Matches7("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET") ||
Matches6("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET"))
- COMPLETE_WITH_LIST5("(", "DEFAULT", "NOT NULL", "STATISTICS", "STORAGE");
+ COMPLETE_WITH_LIST6("(", "COMPRESSED", "DEFAULT", "NOT NULL", "STATISTICS", "STORAGE");
/* ALTER TABLE ALTER [COLUMN] <foo> SET ( */
else if (Matches8("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "(") ||
Matches7("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "("))
COMPLETE_WITH_LIST2("n_distinct", "n_distinct_inherited");
+ else if (Matches8("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "COMPRESSED") ||
+ Matches7("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "COMPRESSED"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_compression_methods);
+ else if (Matches9("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "COMPRESSED", MatchAny) ||
+ Matches8("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "COMPRESSED", MatchAny))
+ COMPLETE_WITH_CONST("WITH (");
/* ALTER TABLE ALTER [COLUMN] <foo> SET STORAGE */
else if (Matches8("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "STORAGE") ||
Matches7("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "STORAGE"))
@@ -2177,12 +2189,14 @@ psql_completion(const char *text, int start, int end)
"SCHEMA", "SEQUENCE", "STATISTICS", "SUBSCRIPTION",
"TABLE", "TYPE", "VIEW", "MATERIALIZED VIEW", "COLUMN", "AGGREGATE", "FUNCTION",
"OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", "LARGE OBJECT",
- "TABLESPACE", "TEXT SEARCH", "ROLE", NULL};
+ "TABLESPACE", "TEXT SEARCH", "ROLE", "COMPRESSION METHOD", NULL};
COMPLETE_WITH_LIST(list_COMMENT);
}
else if (Matches4("COMMENT", "ON", "ACCESS", "METHOD"))
COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
+ else if (Matches4("COMMENT", "ON", "COMPRESSION", "METHOD"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_compression_methods);
else if (Matches3("COMMENT", "ON", "FOREIGN"))
COMPLETE_WITH_LIST2("DATA WRAPPER", "TABLE");
else if (Matches4("COMMENT", "ON", "TEXT", "SEARCH"))
@@ -2255,6 +2269,14 @@ psql_completion(const char *text, int start, int end)
else if (Matches6("CREATE", "ACCESS", "METHOD", MatchAny, "TYPE", MatchAny))
COMPLETE_WITH_CONST("HANDLER");
+ /* CREATE COMPRESSION METHOD */
+ /* Complete "CREATE COMPRESSION METHOD <name>" */
+ else if (Matches4("CREATE", "COMPRESSION", "METHOD", MatchAny))
+ COMPLETE_WITH_CONST("HANDLER");
+ /* Complete "CREATE COMPRESSION METHOD <name> HANDLER" */
+ else if (Matches5("CREATE", "COMPRESSION", "METHOD", MatchAny, "HANDLER"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
+
/* CREATE DATABASE */
else if (Matches3("CREATE", "DATABASE", MatchAny))
COMPLETE_WITH_LIST9("OWNER", "TEMPLATE", "ENCODING", "TABLESPACE",
@@ -2687,6 +2709,7 @@ psql_completion(const char *text, int start, int end)
Matches4("DROP", "ACCESS", "METHOD", MatchAny) ||
(Matches4("DROP", "AGGREGATE|FUNCTION", MatchAny, MatchAny) &&
ends_with(prev_wd, ')')) ||
+ Matches4("DROP", "COMPRESSION", "METHOD", MatchAny) ||
Matches4("DROP", "EVENT", "TRIGGER", MatchAny) ||
Matches5("DROP", "FOREIGN", "DATA", "WRAPPER", MatchAny) ||
Matches4("DROP", "FOREIGN", "TABLE", MatchAny) ||
@@ -2775,6 +2798,12 @@ psql_completion(const char *text, int start, int end)
else if (Matches5("DROP", "RULE", MatchAny, "ON", MatchAny))
COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
+ /* DROP COMPRESSION METHOD */
+ else if (Matches2("DROP", "COMPRESSION"))
+ COMPLETE_WITH_CONST("METHOD");
+ else if (Matches3("DROP", "COMPRESSION", "METHOD"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_compression_methods);
+
/* EXECUTE */
else if (Matches1("EXECUTE"))
COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements);
@@ -3407,6 +3436,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
else if (TailMatchesCS1("\\db*"))
COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
+ else if (TailMatchesCS1("\\dCM*"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_compression_methods);
else if (TailMatchesCS1("\\dD*"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL);
else if (TailMatchesCS1("\\des*"))
diff --git a/src/include/access/compression.h b/src/include/access/compression.h
new file mode 100644
index 0000000000..1320d8f882
--- /dev/null
+++ b/src/include/access/compression.h
@@ -0,0 +1,65 @@
+/*-------------------------------------------------------------------------
+ *
+ * compression.h
+ * API for Postgres compression methods.
+ *
+ * Copyright (c) 2015-2016, PostgreSQL Global Development Group
+ *
+ * src/include/access/compression.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef COMPRESSION_H
+#define COMPRESSION_H
+
+#include "postgres.h"
+#include "catalog/pg_attribute.h"
+#include "nodes/nodes.h"
+#include "nodes/pg_list.h"
+
+/* parsenodes.h */
+typedef struct ColumnCompression ColumnCompression;
+typedef struct CompressionMethodRoutine CompressionMethodRoutine;
+
+typedef struct
+{
+ CompressionMethodRoutine *routine;
+ List *options;
+ Oid cmoptoid;
+} AttributeCompression;
+
+typedef void (*CompressionConfigureRoutine)
+ (Form_pg_attribute attr, List *options);
+typedef struct varlena *(*CompressionRoutine)
+ (AttributeCompression * ac, const struct varlena *data);
+
+/*
+ * API struct for an compression method.
+ * Note this must be stored in a single palloc'd chunk of memory.
+ */
+typedef struct CompressionMethodRoutine
+{
+ NodeTag type;
+
+ CompressionConfigureRoutine configure;
+ CompressionConfigureRoutine drop;
+ CompressionRoutine compress;
+ CompressionRoutine decompress;
+} CompressionMethodRoutine;
+
+typedef struct CompressionMethodOpArgs
+{
+ Oid cmhanderid;
+ Oid typeid;
+} CompressionMethodOpArgs;
+
+extern CompressionMethodRoutine * GetCompressionRoutine(Oid cmoptoid);
+extern List *GetCompressionOptionsList(Oid cmoptoid);
+extern Oid CreateCompressionOptions(Form_pg_attribute attr, Oid cmid,
+ List *options);
+extern ColumnCompression * GetColumnCompressionForAttribute(Form_pg_attribute att);
+extern void CheckCompressionMismatch(ColumnCompression * c1,
+ ColumnCompression * c2, const char *attributeName);
+
+#endif /* COMPRESSION_H */
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 5cdaa3bff1..573512367a 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -258,7 +258,6 @@ extern void add_string_reloption(bits32 kinds, char *name, char *desc,
extern Datum transformRelOptions(Datum oldOptions, List *defList,
char *namspace, char *validnsps[],
bool ignoreOids, bool isReset);
-extern List *untransformRelOptions(Datum options);
extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
amoptions_function amoptions);
extern relopt_value *parseRelOptions(Datum options, bool validate,
@@ -269,6 +268,9 @@ extern void fillRelOptions(void *rdopts, Size basesize,
relopt_value *options, int numoptions,
bool validate,
const relopt_parse_elt *elems, int nelems);
+extern char *formatRelOptions(List *options);
+extern void freeRelOptions(List *options);
+extern List *untransformRelOptions(Datum options);
extern bytea *default_reloptions(Datum reloptions, bool validate,
relopt_kind kind);
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index 2be5af1d3e..867adc1bd8 100644
--- a/src/include/access/tupdesc.h
+++ b/src/include/access/tupdesc.h
@@ -14,7 +14,9 @@
#ifndef TUPDESC_H
#define TUPDESC_H
+#include "postgres.h"
#include "access/attnum.h"
+#include "access/compression.h"
#include "catalog/pg_attribute.h"
#include "nodes/pg_list.h"
@@ -88,6 +90,9 @@ typedef struct tupleDesc
/* Accessor for the i'th attribute of tupdesc. */
#define TupleDescAttr(tupdesc, i) (&(tupdesc)->attrs[(i)])
+#define TupleDescAttrCompression(tupdesc, i) \
+ ((tupdesc)->tdcompression? &((tupdesc)->tdcompression[i]) : NULL)
+
extern TupleDesc CreateTemplateTupleDesc(int natts, bool hasoid);
diff --git a/src/include/access/tuptoaster.h b/src/include/access/tuptoaster.h
index fd9f83ac44..11092e5e9d 100644
--- a/src/include/access/tuptoaster.h
+++ b/src/include/access/tuptoaster.h
@@ -210,7 +210,7 @@ extern HeapTuple toast_build_flattened_tuple(TupleDesc tupleDesc,
* Create a compressed version of a varlena datum, if possible
* ----------
*/
-extern Datum toast_compress_datum(Datum value);
+extern Datum toast_compress_datum(Datum value, Oid cmoptoid);
/* ----------
* toast_raw_datum_size -
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index b9f98423cc..36cadd409e 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -165,10 +165,12 @@ typedef enum ObjectClass
OCLASS_PUBLICATION, /* pg_publication */
OCLASS_PUBLICATION_REL, /* pg_publication_rel */
OCLASS_SUBSCRIPTION, /* pg_subscription */
- OCLASS_TRANSFORM /* pg_transform */
+ OCLASS_TRANSFORM, /* pg_transform */
+ OCLASS_COMPRESSION_METHOD, /* pg_compression */
+ OCLASS_COMPRESSION_OPTIONS /* pg_compression_opt */
} ObjectClass;
-#define LAST_OCLASS OCLASS_TRANSFORM
+#define LAST_OCLASS OCLASS_COMPRESSION_OPTIONS
/* flag bits for performDeletion/performMultipleDeletions: */
#define PERFORM_DELETION_INTERNAL 0x0001 /* internal action */
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index ef8493674c..b580f1971a 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -120,6 +120,14 @@ DECLARE_UNIQUE_INDEX(pg_collation_name_enc_nsp_index, 3164, on pg_collation usin
DECLARE_UNIQUE_INDEX(pg_collation_oid_index, 3085, on pg_collation using btree(oid oid_ops));
#define CollationOidIndexId 3085
+DECLARE_UNIQUE_INDEX(pg_compression_oid_index, 3422, on pg_compression using btree(oid oid_ops));
+#define CompressionMethodOidIndexId 3422
+DECLARE_UNIQUE_INDEX(pg_compression_name_index, 3423, on pg_compression using btree(cmname name_ops));
+#define CompressionMethodNameIndexId 3423
+
+DECLARE_UNIQUE_INDEX(pg_compression_opt_oid_index, 3424, on pg_compression_opt using btree(oid oid_ops));
+#define CompressionOptionsOidIndexId 3424
+
DECLARE_INDEX(pg_constraint_conname_nsp_index, 2664, on pg_constraint using btree(conname name_ops, connamespace oid_ops));
#define ConstraintNameNspIndexId 2664
DECLARE_INDEX(pg_constraint_conrelid_index, 2665, on pg_constraint using btree(conrelid oid_ops));
diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h
index bcf28e8f04..caadd61031 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -156,6 +156,9 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
/* attribute's collation */
Oid attcollation;
+ /* attribute's compression options or InvalidOid */
+ Oid attcompression;
+
#ifdef CATALOG_VARLEN /* variable-length fields start here */
/* NOTE: The following fields are not present in tuple descriptors. */
@@ -174,10 +177,10 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
* ATTRIBUTE_FIXED_PART_SIZE is the size of the fixed-layout,
* guaranteed-not-null part of a pg_attribute row. This is in fact as much
* of the row as gets copied into tuple descriptors, so don't expect you
- * can access fields beyond attcollation except in a real tuple!
+ * can access fields beyond attcompression except in a real tuple!
*/
#define ATTRIBUTE_FIXED_PART_SIZE \
- (offsetof(FormData_pg_attribute,attcollation) + sizeof(Oid))
+ (offsetof(FormData_pg_attribute,attcompression) + sizeof(Oid))
/* ----------------
* Form_pg_attribute corresponds to a pointer to a tuple with
@@ -191,29 +194,30 @@ typedef FormData_pg_attribute *Form_pg_attribute;
* ----------------
*/
-#define Natts_pg_attribute 22
-#define Anum_pg_attribute_attrelid 1
-#define Anum_pg_attribute_attname 2
-#define Anum_pg_attribute_atttypid 3
-#define Anum_pg_attribute_attstattarget 4
-#define Anum_pg_attribute_attlen 5
-#define Anum_pg_attribute_attnum 6
-#define Anum_pg_attribute_attndims 7
-#define Anum_pg_attribute_attcacheoff 8
-#define Anum_pg_attribute_atttypmod 9
-#define Anum_pg_attribute_attbyval 10
-#define Anum_pg_attribute_attstorage 11
-#define Anum_pg_attribute_attalign 12
-#define Anum_pg_attribute_attnotnull 13
-#define Anum_pg_attribute_atthasdef 14
-#define Anum_pg_attribute_attidentity 15
-#define Anum_pg_attribute_attisdropped 16
-#define Anum_pg_attribute_attislocal 17
-#define Anum_pg_attribute_attinhcount 18
-#define Anum_pg_attribute_attcollation 19
-#define Anum_pg_attribute_attacl 20
-#define Anum_pg_attribute_attoptions 21
-#define Anum_pg_attribute_attfdwoptions 22
+#define Natts_pg_attribute 23
+#define Anum_pg_attribute_attrelid 1
+#define Anum_pg_attribute_attname 2
+#define Anum_pg_attribute_atttypid 3
+#define Anum_pg_attribute_attstattarget 4
+#define Anum_pg_attribute_attlen 5
+#define Anum_pg_attribute_attnum 6
+#define Anum_pg_attribute_attndims 7
+#define Anum_pg_attribute_attcacheoff 8
+#define Anum_pg_attribute_atttypmod 9
+#define Anum_pg_attribute_attbyval 10
+#define Anum_pg_attribute_attstorage 11
+#define Anum_pg_attribute_attalign 12
+#define Anum_pg_attribute_attnotnull 13
+#define Anum_pg_attribute_atthasdef 14
+#define Anum_pg_attribute_attidentity 15
+#define Anum_pg_attribute_attisdropped 16
+#define Anum_pg_attribute_attislocal 17
+#define Anum_pg_attribute_attinhcount 18
+#define Anum_pg_attribute_attcollation 19
+#define Anum_pg_attribute_attcompression 20
+#define Anum_pg_attribute_attacl 21
+#define Anum_pg_attribute_attoptions 22
+#define Anum_pg_attribute_attfdwoptions 23
/* ----------------
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index b256657bda..40f5cc4f18 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -149,7 +149,7 @@ typedef FormData_pg_class *Form_pg_class;
*/
DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 30 0 t f f f f f f t n f 3 1 _null_ _null_ _null_));
DESCR("");
-DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 22 0 f f f f f f f t n f 3 1 _null_ _null_ _null_));
+DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 23 0 f f f f f f f t n f 3 1 _null_ _null_ _null_));
DESCR("");
DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f p r 29 0 t f f f f f f t n f 3 1 _null_ _null_ _null_));
DESCR("");
diff --git a/src/include/catalog/pg_compression.h b/src/include/catalog/pg_compression.h
new file mode 100644
index 0000000000..1d5f9ac479
--- /dev/null
+++ b/src/include/catalog/pg_compression.h
@@ -0,0 +1,55 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_compression.h
+ * definition of the system "compression method" relation (pg_compression)
+ * along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_compression.h
+ *
+ * NOTES
+ * the genbki.pl script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ * XXX do NOT break up DATA() statements into multiple lines!
+ * the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_COMPRESSION_H
+#define PG_COMPRESSION_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ * pg_compression definition. cpp turns this into
+ * typedef struct FormData_pg_compression
+ * ----------------
+ */
+#define CompressionMethodRelationId 3419
+
+CATALOG(pg_compression,3419)
+{
+ NameData cmname; /* compression method name */
+ regproc cmhandler; /* compression handler */
+} FormData_pg_compression;
+
+/* ----------------
+ * Form_pg_compression corresponds to a pointer to a tuple with
+ * the format of pg_compression relation.
+ * ----------------
+ */
+typedef FormData_pg_compression * Form_pg_compression;
+
+/* ----------------
+ * compiler constants for pg_compression
+ * ----------------
+ */
+#define Natts_pg_compression 2
+#define Anum_pg_compression_cmname 1
+#define Anum_pg_compression_cmhandler 2
+
+#endif /* PG_COMPRESSION_H */
diff --git a/src/include/catalog/pg_compression_opt.h b/src/include/catalog/pg_compression_opt.h
new file mode 100644
index 0000000000..343d3355d9
--- /dev/null
+++ b/src/include/catalog/pg_compression_opt.h
@@ -0,0 +1,54 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_compression_opt.h
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_compression_opt.h
+ *
+ * NOTES
+ * the genbki.pl script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ * XXX do NOT break up DATA() statements into multiple lines!
+ * the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_COMPRESSION_OPT_H
+#define PG_COMPRESSION_OPT_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ * pg_compression_opt definition. cpp turns this into
+ * typedef struct FormData_pg_compression_opt
+ * ----------------
+ */
+#define CompressionOptRelationId 3420
+
+CATALOG(pg_compression_opt,3420)
+{
+ Oid cmid; /* compression method oid */
+ regproc cmhandler; /* compression handler */
+ text cmoptions[1]; /* specific options from WITH */
+} FormData_pg_compression_opt;
+
+/* ----------------
+ * Form_pg_compression_opt corresponds to a pointer to a tuple with
+ * the format of pg_compression_opt relation.
+ * ----------------
+ */
+typedef FormData_pg_compression_opt * Form_pg_compression_opt;
+
+/* ----------------
+ * compiler constants for pg_compression_opt
+ * ----------------
+ */
+#define Natts_pg_compression_opt 3
+#define Anum_pg_compression_opt_cmid 1
+#define Anum_pg_compression_opt_cmhandler 2
+#define Anum_pg_compression_opt_cmoptions 3
+
+#endif /* PG_COMPRESSION_OPT_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 93c031aad7..f30dd7fbcb 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3881,6 +3881,10 @@ DATA(insert OID = 3311 ( tsm_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s
DESCR("I/O");
DATA(insert OID = 3312 ( tsm_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3310" _null_ _null_ _null_ _null_ _null_ tsm_handler_out _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 3425 ( compression_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3421 "2275" _null_ _null_ _null_ _null_ _null_ compression_handler_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 3426 ( compression_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3421" _null_ _null_ _null_ _null_ _null_ compression_handler_out _null_ _null_ _null_ ));
+DESCR("I/O");
/* tablesample method handlers */
DATA(insert OID = 3313 ( bernoulli PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3310 "2281" _null_ _null_ _null_ _null_ _null_ tsm_bernoulli_handler _null_ _null_ _null_ ));
@@ -4677,6 +4681,8 @@ DATA(insert OID = 3646 ( gtsvectorin PGNSP PGUID 12 1 0 0 0 f f f f t f i s
DESCR("I/O");
DATA(insert OID = 3647 ( gtsvectorout PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3642" _null_ _null_ _null_ _null_ _null_ gtsvectorout _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 3453 ( tsvector_compression_handler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3421 "2281" _null_ _null_ _null_ _null_ _null_ tsvector_compression_handler _null_ _null_ _null_ ));
+DESCR("tsvector compression handler");
DATA(insert OID = 3616 ( tsvector_lt PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "3614 3614" _null_ _null_ _null_ _null_ _null_ tsvector_lt _null_ _null_ _null_ ));
DATA(insert OID = 3617 ( tsvector_le PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "3614 3614" _null_ _null_ _null_ _null_ _null_ tsvector_le _null_ _null_ _null_ ));
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index ffdb452b02..be68bdfbc3 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -712,6 +712,8 @@ DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_han
#define TSM_HANDLEROID 3310
DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
#define ANYRANGEOID 3831
+DATA(insert OID = 3421 ( compression_handler PGNSP PGUID 4 t p P f t \054 0 0 0 compression_handler_in compression_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define COMPRESSION_HANDLEROID 3421
/*
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index f7bb4a54f7..01c542e829 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -140,6 +140,7 @@ extern Oid RemoveUserMapping(DropUserMappingStmt *stmt);
extern void RemoveUserMappingById(Oid umId);
extern void CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid);
extern void ImportForeignSchema(ImportForeignSchemaStmt *stmt);
+extern Datum optionListToArray(List *options);
extern Datum transformGenericOptions(Oid catalogId,
Datum oldOptions,
List *options,
@@ -152,6 +153,14 @@ extern Oid get_index_am_oid(const char *amname, bool missing_ok);
extern Oid get_am_oid(const char *amname, bool missing_ok);
extern char *get_am_name(Oid amOid);
+/* commands/compressioncmds.c */
+extern ObjectAddress DefineCompressionMethod(List *names, List *parameters);
+extern void RemoveCompressionMethodById(Oid cmOid);
+extern void RemoveCompressionOptionsById(Oid cmoptoid);
+extern Oid get_compression_method_oid(const char *cmname, bool missing_ok);
+extern char *get_compression_method_name(Oid cmOid);
+extern char *get_compression_method_name_for_opt(Oid cmoptoid);
+
/* support routines in commands/define.c */
extern char *defGetString(DefElem *def);
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index da3ff5dbee..c50d9525d0 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -24,7 +24,8 @@
extern ObjectAddress DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
- ObjectAddress *typaddress, const char *queryString);
+ ObjectAddress *typaddress, const char *queryString,
+ Node **pAlterStmt);
extern void RemoveRelations(DropStmt *drop);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index ffeeb4919b..6dc49a73b6 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -468,6 +468,7 @@ typedef enum NodeTag
T_PartitionBoundSpec,
T_PartitionRangeDatum,
T_PartitionCmd,
+ T_ColumnCompression,
T_VacuumRelation,
/*
@@ -498,7 +499,8 @@ typedef enum NodeTag
T_FdwRoutine, /* in foreign/fdwapi.h */
T_IndexAmRoutine, /* in access/amapi.h */
T_TsmRoutine, /* in access/tsmapi.h */
- T_ForeignKeyCacheInfo /* in utils/rel.h */
+ T_ForeignKeyCacheInfo, /* in utils/rel.h */
+ T_CompressionMethodRoutine, /* in access/compression.h */
} NodeTag;
/*
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 60a6cc4b26..083416d155 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -615,6 +615,14 @@ typedef struct RangeTableSample
int location; /* method name location, or -1 if unknown */
} RangeTableSample;
+typedef struct ColumnCompression
+{
+ NodeTag type;
+ char *methodName;
+ Oid methodOid;
+ List *options;
+} ColumnCompression;
+
/*
* ColumnDef - column definition (used in various creates)
*
@@ -638,6 +646,7 @@ typedef struct ColumnDef
NodeTag type;
char *colname; /* name of column */
TypeName *typeName; /* type of column */
+ ColumnCompression *compression;
int inhcount; /* number of times column is inherited */
bool is_local; /* column has local (non-inherited) def'n */
bool is_not_null; /* NOT NULL constraint specified? */
@@ -1616,6 +1625,7 @@ typedef enum ObjectType
OBJECT_CAST,
OBJECT_COLUMN,
OBJECT_COLLATION,
+ OBJECT_COMPRESSION_METHOD,
OBJECT_CONVERSION,
OBJECT_DATABASE,
OBJECT_DEFAULT,
@@ -1763,7 +1773,8 @@ typedef enum AlterTableType
AT_DetachPartition, /* DETACH PARTITION */
AT_AddIdentity, /* ADD IDENTITY */
AT_SetIdentity, /* SET identity column options */
- AT_DropIdentity /* DROP IDENTITY */
+ AT_DropIdentity, /* DROP IDENTITY */
+ AT_AlterColumnCompression /* ALTER COLUMN name COMPRESSED cm WITH (...) */
} AlterTableType;
typedef struct ReplicaIdentityStmt
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index f50e45e886..7bfc6e6be4 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -87,6 +87,8 @@ PG_KEYWORD("comment", COMMENT, UNRESERVED_KEYWORD)
PG_KEYWORD("comments", COMMENTS, UNRESERVED_KEYWORD)
PG_KEYWORD("commit", COMMIT, UNRESERVED_KEYWORD)
PG_KEYWORD("committed", COMMITTED, UNRESERVED_KEYWORD)
+PG_KEYWORD("compressed", COMPRESSED, UNRESERVED_KEYWORD)
+PG_KEYWORD("compression", COMPRESSION, UNRESERVED_KEYWORD)
PG_KEYWORD("concurrently", CONCURRENTLY, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("configuration", CONFIGURATION, UNRESERVED_KEYWORD)
PG_KEYWORD("conflict", CONFLICT, UNRESERVED_KEYWORD)
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index e749432ef0..5cab77457a 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -22,10 +22,12 @@ extern List *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
const char *queryString);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
+void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
extern List *transformCreateSchemaStmt(CreateSchemaStmt *stmt);
extern PartitionBoundSpec *transformPartitionBound(ParseState *pstate, Relation parent,
PartitionBoundSpec *spec);
+extern void transformColumnCompression(ColumnDef *column, RangeVar *relation,
+ AlterTableStmt **alterStmt);
#endif /* PARSE_UTILCMD_H */
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 1ca9b60ea1..f5c879ae60 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -146,9 +146,18 @@ typedef union
struct /* Compressed-in-line format */
{
uint32 va_header;
- uint32 va_rawsize; /* Original data size (excludes header) */
+ uint32 va_info; /* Original data size (excludes header) and
+ * flags */
char va_data[FLEXIBLE_ARRAY_MEMBER]; /* Compressed data */
} va_compressed;
+ struct /* Compressed-in-line format */
+ {
+ uint32 va_header;
+ uint32 va_info; /* Original data size (excludes header) and
+ * flags */
+ Oid va_cmoptoid; /* Oid of compression options */
+ char va_data[FLEXIBLE_ARRAY_MEMBER]; /* Compressed data */
+ } va_custom_compressed;
} varattrib_4b;
typedef struct
@@ -282,7 +291,12 @@ typedef struct
#define VARDATA_1B_E(PTR) (((varattrib_1b_e *) (PTR))->va_data)
#define VARRAWSIZE_4B_C(PTR) \
- (((varattrib_4b *) (PTR))->va_compressed.va_rawsize)
+ (((varattrib_4b *) (PTR))->va_compressed.va_info & 0x3FFFFFFF)
+#define VARFLAGS_4B_C(PTR) \
+ (((varattrib_4b *) (PTR))->va_compressed.va_info >> 30)
+
+#define VARHDRSZ_CUSTOM_COMPRESSED \
+ (offsetof(varattrib_4b, va_custom_compressed.va_data))
/* Externally visible macros */
@@ -311,6 +325,8 @@ typedef struct
#define VARDATA_EXTERNAL(PTR) VARDATA_1B_E(PTR)
#define VARATT_IS_COMPRESSED(PTR) VARATT_IS_4B_C(PTR)
+#define VARATT_IS_CUSTOM_COMPRESSED(PTR) (VARATT_IS_4B_C(PTR) && \
+ (VARFLAGS_4B_C(PTR) == 0x02))
#define VARATT_IS_EXTERNAL(PTR) VARATT_IS_1B_E(PTR)
#define VARATT_IS_EXTERNAL_ONDISK(PTR) \
(VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_ONDISK)
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 254a811aff..6ad889af7a 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -210,6 +210,7 @@ typedef enum AclObjectKind
ACL_KIND_EXTENSION, /* pg_extension */
ACL_KIND_PUBLICATION, /* pg_publication */
ACL_KIND_SUBSCRIPTION, /* pg_subscription */
+ ACL_KIND_COMPRESSION_METHOD, /* pg_compression */
MAX_ACL_KIND /* MUST BE LAST */
} AclObjectKind;
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 8a0be41929..ff7cb530fd 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -48,6 +48,9 @@ enum SysCacheIdentifier
CLAOID,
COLLNAMEENCNSP,
COLLOID,
+ COMPRESSIONMETHODOID,
+ COMPRESSIONMETHODNAME,
+ COMPRESSIONOPTIONSOID,
CONDEFAULT,
CONNAMENSP,
CONSTROID,
diff --git a/src/test/regress/expected/copy2.out b/src/test/regress/expected/copy2.out
index 65e9c626b3..112f0eda47 100644
--- a/src/test/regress/expected/copy2.out
+++ b/src/test/regress/expected/copy2.out
@@ -438,10 +438,10 @@ begin
end $$ language plpgsql immutable;
alter table check_con_tbl add check (check_con_function(check_con_tbl.*));
\d+ check_con_tbl
- Table "public.check_con_tbl"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- f1 | integer | | | | plain | |
+ Table "public.check_con_tbl"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ f1 | integer | | | | plain | | |
Check constraints:
"check_con_tbl_check" CHECK (check_con_function(check_con_tbl.*))
diff --git a/src/test/regress/expected/create_cm.out b/src/test/regress/expected/create_cm.out
new file mode 100644
index 0000000000..693e5a5e8c
--- /dev/null
+++ b/src/test/regress/expected/create_cm.out
@@ -0,0 +1,108 @@
+CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
+DROP COMPRESSION METHOD ts1;
+CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
+CREATE TABLE cmtest(fts tsvector COMPRESSED ts1);
+DROP COMPRESSION METHOD ts1;
+ERROR: cannot drop compression method ts1 because other objects depend on it
+DETAIL: compression options for ts1 depends on compression method ts1
+table cmtest column fts depends on compression options for ts1
+HINT: Use DROP ... CASCADE to drop the dependent objects too.
+SELECT * FROM pg_compression;
+ cmname | cmhandler
+--------+------------------------------
+ ts1 | tsvector_compression_handler
+(1 row)
+
+SELECT cmhandler, cmoptions FROM pg_compression_opt;
+ cmhandler | cmoptions
+------------------------------+-----------
+ tsvector_compression_handler |
+(1 row)
+
+\dCM
+List of compression methods
+ Name
+------
+ ts1
+(1 row)
+
+\d+ cmtest
+ Table "public.cmtest"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+
+INSERT INTO cmtest
+ SELECT to_tsvector(string_agg(repeat(substr(i::text,1,1), i), ' '))
+ FROM generate_series(1,200) i;
+SELECT length(fts) FROM cmtest;
+ length
+--------
+ 200
+(1 row)
+
+ALTER TABLE cmtest ALTER COLUMN fts SET NOT COMPRESSED;
+\d+ cmtest
+ Table "public.cmtest"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | | |
+
+ALTER TABLE cmtest ALTER COLUMN fts SET COMPRESSED ts1 WITH (format 'lz');
+ERROR: the compression method for tsvector doesn't take any options
+ALTER TABLE cmtest ALTER COLUMN fts SET COMPRESSED ts1;
+\d+ cmtest
+ Table "public.cmtest"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+
+SELECT * INTO cmtest2 FROM cmtest;
+CREATE TABLE cmtest3 (LIKE cmtest);
+CREATE TABLE cmtest4(fts tsvector, a int) inherits (cmtest);
+NOTICE: merging column "fts" with inherited definition
+\d+ cmtest3
+ Table "public.cmtest3"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+
+\d+ cmtest4
+ Table "public.cmtest4"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+ a | integer | | | | plain | | |
+Inherits: cmtest
+
+DROP TABLE cmtest CASCADE;
+NOTICE: drop cascades to table cmtest4
+SELECT length(fts) FROM cmtest2;
+ length
+--------
+ 200
+(1 row)
+
+SELECT * FROM pg_compression;
+ cmname | cmhandler
+--------+------------------------------
+ ts1 | tsvector_compression_handler
+(1 row)
+
+SELECT cmhandler, cmoptions FROM pg_compression_opt;
+ cmhandler | cmoptions
+------------------------------+-----------
+ tsvector_compression_handler |
+ tsvector_compression_handler |
+ tsvector_compression_handler |
+ tsvector_compression_handler |
+(4 rows)
+
+DROP TABLE cmtest2;
+DROP TABLE cmtest3;
+DROP COMPRESSION METHOD ts1 CASCADE;
+NOTICE: drop cascades to 4 other objects
+DETAIL: drop cascades to compression options for ts1
+drop cascades to compression options for ts1
+drop cascades to compression options for ts1
+drop cascades to compression options for ts1
diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out
index 60ab28a96a..093d6074a7 100644
--- a/src/test/regress/expected/create_table.out
+++ b/src/test/regress/expected/create_table.out
@@ -547,10 +547,10 @@ CREATE TABLE oids_parted (
) PARTITION BY RANGE (a) WITH OIDS;
CREATE TABLE part_forced_oids PARTITION OF oids_parted FOR VALUES FROM (1) TO (10) WITHOUT OIDS;
\d+ part_forced_oids
- Table "public.part_forced_oids"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
+ Table "public.part_forced_oids"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
Partition of: oids_parted FOR VALUES FROM (1) TO (10)
Partition constraint: ((a IS NOT NULL) AND (a >= 1) AND (a < 10))
Has OIDs: yes
@@ -669,11 +669,11 @@ CREATE TABLE part_c PARTITION OF parted (b WITH OPTIONS NOT NULL DEFAULT 0) FOR
CREATE TABLE part_c_1_10 PARTITION OF part_c FOR VALUES FROM (1) TO (10);
-- Partition bound in describe output
\d+ part_b
- Table "public.part_b"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | not null | 1 | plain | |
+ Table "public.part_b"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | not null | 1 | plain | | |
Partition of: parted FOR VALUES IN ('b')
Partition constraint: ((a IS NOT NULL) AND (a = ANY (ARRAY['b'::text])))
Check constraints:
@@ -696,11 +696,11 @@ Partitions: part_c_1_10 FOR VALUES FROM (1) TO (10)
-- a level-2 partition's constraint will include the parent's expressions
\d+ part_c_1_10
- Table "public.part_c_1_10"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | not null | 0 | plain | |
+ Table "public.part_c_1_10"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | not null | 0 | plain | | |
Partition of: part_c FOR VALUES FROM (1) TO (10)
Partition constraint: ((a IS NOT NULL) AND (a = ANY (ARRAY['c'::text])) AND (b IS NOT NULL) AND (b >= 1) AND (b < 10))
Check constraints:
@@ -725,46 +725,46 @@ Number of partitions: 3 (Use \d+ to list them.)
CREATE TABLE range_parted4 (a int, b int, c int) PARTITION BY RANGE (abs(a), abs(b), c);
CREATE TABLE unbounded_range_part PARTITION OF range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (MAXVALUE, MAXVALUE, MAXVALUE);
\d+ unbounded_range_part
- Table "public.unbounded_range_part"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
+ Table "public.unbounded_range_part"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
Partition of: range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (MAXVALUE, MAXVALUE, MAXVALUE)
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL))
DROP TABLE unbounded_range_part;
CREATE TABLE range_parted4_1 PARTITION OF range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (1, MAXVALUE, MAXVALUE);
\d+ range_parted4_1
- Table "public.range_parted4_1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
+ Table "public.range_parted4_1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
Partition of: range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (1, MAXVALUE, MAXVALUE)
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND (abs(a) <= 1))
CREATE TABLE range_parted4_2 PARTITION OF range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, MAXVALUE);
\d+ range_parted4_2
- Table "public.range_parted4_2"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
+ Table "public.range_parted4_2"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
Partition of: range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, MAXVALUE)
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND ((abs(a) > 3) OR ((abs(a) = 3) AND (abs(b) > 4)) OR ((abs(a) = 3) AND (abs(b) = 4) AND (c >= 5))) AND ((abs(a) < 6) OR ((abs(a) = 6) AND (abs(b) <= 7))))
CREATE TABLE range_parted4_3 PARTITION OF range_parted4 FOR VALUES FROM (6, 8, MINVALUE) TO (9, MAXVALUE, MAXVALUE);
\d+ range_parted4_3
- Table "public.range_parted4_3"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
+ Table "public.range_parted4_3"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
Partition of: range_parted4 FOR VALUES FROM (6, 8, MINVALUE) TO (9, MAXVALUE, MAXVALUE)
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND ((abs(a) > 6) OR ((abs(a) = 6) AND (abs(b) >= 8))) AND (abs(a) <= 9))
diff --git a/src/test/regress/expected/create_table_like.out b/src/test/regress/expected/create_table_like.out
index 3f405c94ce..b5ca8b820b 100644
--- a/src/test/regress/expected/create_table_like.out
+++ b/src/test/regress/expected/create_table_like.out
@@ -156,32 +156,32 @@ CREATE TABLE ctlt4 (a text, c text);
ALTER TABLE ctlt4 ALTER COLUMN c SET STORAGE EXTERNAL;
CREATE TABLE ctlt12_storage (LIKE ctlt1 INCLUDING STORAGE, LIKE ctlt2 INCLUDING STORAGE);
\d+ ctlt12_storage
- Table "public.ctlt12_storage"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | |
- b | text | | | | extended | |
- c | text | | | | external | |
+ Table "public.ctlt12_storage"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | |
+ b | text | | | | extended | | |
+ c | text | | | | external | | |
CREATE TABLE ctlt12_comments (LIKE ctlt1 INCLUDING COMMENTS, LIKE ctlt2 INCLUDING COMMENTS);
\d+ ctlt12_comments
- Table "public.ctlt12_comments"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | extended | | A
- b | text | | | | extended | | B
- c | text | | | | extended | | C
+ Table "public.ctlt12_comments"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | extended | | | A
+ b | text | | | | extended | | | B
+ c | text | | | | extended | | | C
CREATE TABLE ctlt1_inh (LIKE ctlt1 INCLUDING CONSTRAINTS INCLUDING COMMENTS) INHERITS (ctlt1);
NOTICE: merging column "a" with inherited definition
NOTICE: merging column "b" with inherited definition
NOTICE: merging constraint "ctlt1_a_check" with inherited definition
\d+ ctlt1_inh
- Table "public.ctlt1_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | | A
- b | text | | | | extended | | B
+ Table "public.ctlt1_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | | A
+ b | text | | | | extended | | | B
Check constraints:
"ctlt1_a_check" CHECK (length(a) > 2)
Inherits: ctlt1
@@ -195,12 +195,12 @@ SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_con
CREATE TABLE ctlt13_inh () INHERITS (ctlt1, ctlt3);
NOTICE: merging multiple inherited definitions of column "a"
\d+ ctlt13_inh
- Table "public.ctlt13_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | |
- b | text | | | | extended | |
- c | text | | | | external | |
+ Table "public.ctlt13_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | |
+ b | text | | | | extended | | |
+ c | text | | | | external | | |
Check constraints:
"ctlt1_a_check" CHECK (length(a) > 2)
"ctlt3_a_check" CHECK (length(a) < 5)
@@ -210,12 +210,12 @@ Inherits: ctlt1,
CREATE TABLE ctlt13_like (LIKE ctlt3 INCLUDING CONSTRAINTS INCLUDING COMMENTS INCLUDING STORAGE) INHERITS (ctlt1);
NOTICE: merging column "a" with inherited definition
\d+ ctlt13_like
- Table "public.ctlt13_like"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | | A3
- b | text | | | | extended | |
- c | text | | | | external | | C
+ Table "public.ctlt13_like"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | | A3
+ b | text | | | | extended | | |
+ c | text | | | | external | | | C
Check constraints:
"ctlt1_a_check" CHECK (length(a) > 2)
"ctlt3_a_check" CHECK (length(a) < 5)
@@ -229,11 +229,11 @@ SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_con
CREATE TABLE ctlt_all (LIKE ctlt1 INCLUDING ALL);
\d+ ctlt_all
- Table "public.ctlt_all"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | | A
- b | text | | | | extended | | B
+ Table "public.ctlt_all"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | | A
+ b | text | | | | extended | | | B
Indexes:
"ctlt_all_pkey" PRIMARY KEY, btree (a)
"ctlt_all_b_idx" btree (b)
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index f4eebb75cf..2ac75c44b5 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -272,10 +272,10 @@ explain (verbose, costs off)
create rule silly as on delete to dcomptable do instead
update dcomptable set d1.r = (d1).r - 1, d1.i = (d1).i + 1 where (d1).i > 0;
\d+ dcomptable
- Table "public.dcomptable"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+-----------+-----------+----------+---------+----------+--------------+-------------
- d1 | dcomptype | | | | extended | |
+ Table "public.dcomptable"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+-----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ d1 | dcomptype | | | | extended | | |
Indexes:
"dcomptable_d1_key" UNIQUE CONSTRAINT, btree (d1)
Rules:
@@ -409,10 +409,10 @@ create rule silly as on delete to dcomptable do instead
update dcomptable set d1[1].r = d1[1].r - 1, d1[1].i = d1[1].i + 1
where d1[1].i > 0;
\d+ dcomptable
- Table "public.dcomptable"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------------+-----------+----------+---------+----------+--------------+-------------
- d1 | dcomptypea | | | | extended | |
+ Table "public.dcomptable"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------------+-----------+----------+---------+----------+-------------+--------------+-------------
+ d1 | dcomptypea | | | | extended | | |
Indexes:
"dcomptable_d1_key" UNIQUE CONSTRAINT, btree (d1)
Rules:
diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out
index 331f7a911f..530ce1b1d0 100644
--- a/src/test/regress/expected/foreign_data.out
+++ b/src/test/regress/expected/foreign_data.out
@@ -1330,12 +1330,12 @@ CREATE TABLE pt1 (
CREATE FOREIGN TABLE ft2 () INHERITS (pt1)
SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value');
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Child tables: ft2
\d+ ft2
@@ -1351,12 +1351,12 @@ Inherits: pt1
DROP FOREIGN TABLE ft2;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
CREATE FOREIGN TABLE ft2 (
c1 integer NOT NULL,
@@ -1375,12 +1375,12 @@ FDW options: (delimiter ',', quote '"', "be quoted" 'value')
ALTER FOREIGN TABLE ft2 INHERIT pt1;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Child tables: ft2
\d+ ft2
@@ -1418,12 +1418,12 @@ Child tables: ct3,
ft3
\d+ ct3
- Table "public.ct3"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.ct3"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Inherits: ft2
\d+ ft3
@@ -1443,17 +1443,17 @@ ALTER TABLE pt1 ADD COLUMN c6 integer;
ALTER TABLE pt1 ADD COLUMN c7 integer NOT NULL;
ALTER TABLE pt1 ADD COLUMN c8 integer;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
- c4 | integer | | | | plain | |
- c5 | integer | | | 0 | plain | |
- c6 | integer | | | | plain | |
- c7 | integer | | not null | | plain | |
- c8 | integer | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
+ c4 | integer | | | | plain | | |
+ c5 | integer | | | 0 | plain | | |
+ c6 | integer | | | | plain | | |
+ c7 | integer | | not null | | plain | | |
+ c8 | integer | | | | plain | | |
Child tables: ft2
\d+ ft2
@@ -1475,17 +1475,17 @@ Child tables: ct3,
ft3
\d+ ct3
- Table "public.ct3"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
- c4 | integer | | | | plain | |
- c5 | integer | | | 0 | plain | |
- c6 | integer | | | | plain | |
- c7 | integer | | not null | | plain | |
- c8 | integer | | | | plain | |
+ Table "public.ct3"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
+ c4 | integer | | | | plain | | |
+ c5 | integer | | | 0 | plain | | |
+ c6 | integer | | | | plain | | |
+ c7 | integer | | not null | | plain | | |
+ c8 | integer | | | | plain | | |
Inherits: ft2
\d+ ft3
@@ -1517,17 +1517,17 @@ ALTER TABLE pt1 ALTER COLUMN c1 SET (n_distinct = 100);
ALTER TABLE pt1 ALTER COLUMN c8 SET STATISTICS -1;
ALTER TABLE pt1 ALTER COLUMN c8 SET STORAGE EXTERNAL;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
- c4 | integer | | | 0 | plain | |
- c5 | integer | | | | plain | |
- c6 | integer | | not null | | plain | |
- c7 | integer | | | | plain | |
- c8 | text | | | | external | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
+ c4 | integer | | | 0 | plain | | |
+ c5 | integer | | | | plain | | |
+ c6 | integer | | not null | | plain | | |
+ c7 | integer | | | | plain | | |
+ c8 | text | | | | external | | |
Child tables: ft2
\d+ ft2
@@ -1555,12 +1555,12 @@ ALTER TABLE pt1 DROP COLUMN c6;
ALTER TABLE pt1 DROP COLUMN c7;
ALTER TABLE pt1 DROP COLUMN c8;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Child tables: ft2
\d+ ft2
@@ -1592,12 +1592,12 @@ SELECT relname, conname, contype, conislocal, coninhcount, connoinherit
-- child does not inherit NO INHERIT constraints
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk1" CHECK (c1 > 0) NO INHERIT
"pt1chk2" CHECK (c2 <> ''::text)
@@ -1636,12 +1636,12 @@ ALTER FOREIGN TABLE ft2 ADD CONSTRAINT pt1chk2 CHECK (c2 <> '');
ALTER FOREIGN TABLE ft2 INHERIT pt1;
-- child does not inherit NO INHERIT constraints
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk1" CHECK (c1 > 0) NO INHERIT
"pt1chk2" CHECK (c2 <> ''::text)
@@ -1667,12 +1667,12 @@ ALTER TABLE pt1 DROP CONSTRAINT pt1chk2 CASCADE;
INSERT INTO pt1 VALUES (1, 'pt1'::text, '1994-01-01'::date);
ALTER TABLE pt1 ADD CONSTRAINT pt1chk3 CHECK (c2 <> '') NOT VALID;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk3" CHECK (c2 <> ''::text) NOT VALID
Child tables: ft2
@@ -1694,12 +1694,12 @@ Inherits: pt1
-- VALIDATE CONSTRAINT need do nothing on foreign tables
ALTER TABLE pt1 VALIDATE CONSTRAINT pt1chk3;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk3" CHECK (c2 <> ''::text)
Child tables: ft2
@@ -1721,12 +1721,12 @@ Inherits: pt1
-- OID system column
ALTER TABLE pt1 SET WITH OIDS;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk3" CHECK (c2 <> ''::text)
Child tables: ft2
@@ -1751,12 +1751,12 @@ ALTER TABLE ft2 SET WITHOUT OIDS; -- ERROR
ERROR: cannot drop inherited column "oid"
ALTER TABLE pt1 SET WITHOUT OIDS;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk3" CHECK (c2 <> ''::text)
Child tables: ft2
@@ -1782,12 +1782,12 @@ ALTER TABLE pt1 RENAME COLUMN c3 TO f3;
-- changes name of a constraint recursively
ALTER TABLE pt1 RENAME CONSTRAINT pt1chk3 TO f2_check;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- f1 | integer | | not null | | plain | 10000 |
- f2 | text | | | | extended | |
- f3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ f1 | integer | | not null | | plain | | 10000 |
+ f2 | text | | | | extended | | |
+ f3 | date | | | | plain | | |
Check constraints:
"f2_check" CHECK (f2 <> ''::text)
Child tables: ft2
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index c698faff2f..a147b8217f 100644
--- a/src/test/regress/expected/inherit.out
+++ b/src/test/regress/expected/inherit.out
@@ -1023,13 +1023,13 @@ ALTER TABLE inhts RENAME aa TO aaa; -- to be failed
ERROR: cannot rename inherited column "aa"
ALTER TABLE inhts RENAME d TO dd;
\d+ inhts
- Table "public.inhts"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- aa | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
- dd | integer | | | | plain | |
+ Table "public.inhts"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ aa | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
+ dd | integer | | | | plain | | |
Inherits: inht1,
inhs1
@@ -1042,14 +1042,14 @@ NOTICE: merging multiple inherited definitions of column "aa"
NOTICE: merging multiple inherited definitions of column "b"
ALTER TABLE inht1 RENAME aa TO aaa;
\d+ inht4
- Table "public.inht4"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- aaa | integer | | | | plain | |
- b | integer | | | | plain | |
- x | integer | | | | plain | |
- y | integer | | | | plain | |
- z | integer | | | | plain | |
+ Table "public.inht4"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ aaa | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ x | integer | | | | plain | | |
+ y | integer | | | | plain | | |
+ z | integer | | | | plain | | |
Inherits: inht2,
inht3
@@ -1059,14 +1059,14 @@ ALTER TABLE inht1 RENAME aaa TO aaaa;
ALTER TABLE inht1 RENAME b TO bb; -- to be failed
ERROR: cannot rename inherited column "b"
\d+ inhts
- Table "public.inhts"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- aaaa | integer | | | | plain | |
- b | integer | | | | plain | |
- x | integer | | | | plain | |
- c | integer | | | | plain | |
- d | integer | | | | plain | |
+ Table "public.inhts"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ aaaa | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ x | integer | | | | plain | | |
+ c | integer | | | | plain | | |
+ d | integer | | | | plain | | |
Inherits: inht2,
inhs1
@@ -1106,33 +1106,33 @@ drop cascades to table inht4
CREATE TABLE test_constraints (id int, val1 varchar, val2 int, UNIQUE(val1, val2));
CREATE TABLE test_constraints_inh () INHERITS (test_constraints);
\d+ test_constraints
- Table "public.test_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+-------------------+-----------+----------+---------+----------+--------------+-------------
- id | integer | | | | plain | |
- val1 | character varying | | | | extended | |
- val2 | integer | | | | plain | |
+ Table "public.test_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+-------------------+-----------+----------+---------+----------+-------------+--------------+-------------
+ id | integer | | | | plain | | |
+ val1 | character varying | | | | extended | | |
+ val2 | integer | | | | plain | | |
Indexes:
"test_constraints_val1_val2_key" UNIQUE CONSTRAINT, btree (val1, val2)
Child tables: test_constraints_inh
ALTER TABLE ONLY test_constraints DROP CONSTRAINT test_constraints_val1_val2_key;
\d+ test_constraints
- Table "public.test_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+-------------------+-----------+----------+---------+----------+--------------+-------------
- id | integer | | | | plain | |
- val1 | character varying | | | | extended | |
- val2 | integer | | | | plain | |
+ Table "public.test_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+-------------------+-----------+----------+---------+----------+-------------+--------------+-------------
+ id | integer | | | | plain | | |
+ val1 | character varying | | | | extended | | |
+ val2 | integer | | | | plain | | |
Child tables: test_constraints_inh
\d+ test_constraints_inh
- Table "public.test_constraints_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+-------------------+-----------+----------+---------+----------+--------------+-------------
- id | integer | | | | plain | |
- val1 | character varying | | | | extended | |
- val2 | integer | | | | plain | |
+ Table "public.test_constraints_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+-------------------+-----------+----------+---------+----------+-------------+--------------+-------------
+ id | integer | | | | plain | | |
+ val1 | character varying | | | | extended | | |
+ val2 | integer | | | | plain | | |
Inherits: test_constraints
DROP TABLE test_constraints_inh;
@@ -1143,27 +1143,27 @@ CREATE TABLE test_ex_constraints (
);
CREATE TABLE test_ex_constraints_inh () INHERITS (test_ex_constraints);
\d+ test_ex_constraints
- Table "public.test_ex_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+--------+-----------+----------+---------+---------+--------------+-------------
- c | circle | | | | plain | |
+ Table "public.test_ex_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+--------+-----------+----------+---------+---------+-------------+--------------+-------------
+ c | circle | | | | plain | | |
Indexes:
"test_ex_constraints_c_excl" EXCLUDE USING gist (c WITH &&)
Child tables: test_ex_constraints_inh
ALTER TABLE test_ex_constraints DROP CONSTRAINT test_ex_constraints_c_excl;
\d+ test_ex_constraints
- Table "public.test_ex_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+--------+-----------+----------+---------+---------+--------------+-------------
- c | circle | | | | plain | |
+ Table "public.test_ex_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+--------+-----------+----------+---------+---------+-------------+--------------+-------------
+ c | circle | | | | plain | | |
Child tables: test_ex_constraints_inh
\d+ test_ex_constraints_inh
- Table "public.test_ex_constraints_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+--------+-----------+----------+---------+---------+--------------+-------------
- c | circle | | | | plain | |
+ Table "public.test_ex_constraints_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+--------+-----------+----------+---------+---------+-------------+--------------+-------------
+ c | circle | | | | plain | | |
Inherits: test_ex_constraints
DROP TABLE test_ex_constraints_inh;
@@ -1173,37 +1173,37 @@ CREATE TABLE test_primary_constraints(id int PRIMARY KEY);
CREATE TABLE test_foreign_constraints(id1 int REFERENCES test_primary_constraints(id));
CREATE TABLE test_foreign_constraints_inh () INHERITS (test_foreign_constraints);
\d+ test_primary_constraints
- Table "public.test_primary_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- id | integer | | not null | | plain | |
+ Table "public.test_primary_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ id | integer | | not null | | plain | | |
Indexes:
"test_primary_constraints_pkey" PRIMARY KEY, btree (id)
Referenced by:
TABLE "test_foreign_constraints" CONSTRAINT "test_foreign_constraints_id1_fkey" FOREIGN KEY (id1) REFERENCES test_primary_constraints(id)
\d+ test_foreign_constraints
- Table "public.test_foreign_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- id1 | integer | | | | plain | |
+ Table "public.test_foreign_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ id1 | integer | | | | plain | | |
Foreign-key constraints:
"test_foreign_constraints_id1_fkey" FOREIGN KEY (id1) REFERENCES test_primary_constraints(id)
Child tables: test_foreign_constraints_inh
ALTER TABLE test_foreign_constraints DROP CONSTRAINT test_foreign_constraints_id1_fkey;
\d+ test_foreign_constraints
- Table "public.test_foreign_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- id1 | integer | | | | plain | |
+ Table "public.test_foreign_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ id1 | integer | | | | plain | | |
Child tables: test_foreign_constraints_inh
\d+ test_foreign_constraints_inh
- Table "public.test_foreign_constraints_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- id1 | integer | | | | plain | |
+ Table "public.test_foreign_constraints_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ id1 | integer | | | | plain | | |
Inherits: test_foreign_constraints
DROP TABLE test_foreign_constraints_inh;
diff --git a/src/test/regress/expected/insert.out b/src/test/regress/expected/insert.out
index b715619313..bbcc5e8680 100644
--- a/src/test/regress/expected/insert.out
+++ b/src/test/regress/expected/insert.out
@@ -142,11 +142,11 @@ create rule irule3 as on insert to inserttest2 do also
insert into inserttest (f4[1].if1, f4[1].if2[2])
select new.f1, new.f2;
\d+ inserttest2
- Table "public.inserttest2"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+--------+-----------+----------+---------+----------+--------------+-------------
- f1 | bigint | | | | plain | |
- f2 | text | | | | extended | |
+ Table "public.inserttest2"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+--------+-----------+----------+---------+----------+-------------+--------------+-------------
+ f1 | bigint | | | | plain | | |
+ f2 | text | | | | extended | | |
Rules:
irule1 AS
ON INSERT TO inserttest2 DO INSERT INTO inserttest (f3.if2[1], f3.if2[2])
@@ -389,10 +389,10 @@ drop table range_parted, list_parted;
create table list_parted (a int) partition by list (a);
create table part_default partition of list_parted default;
\d+ part_default
- Table "public.part_default"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
+ Table "public.part_default"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
Partition of: list_parted DEFAULT
No partition constraint
@@ -704,74 +704,74 @@ Partitions: mcrparted1_lt_b FOR VALUES FROM (MINVALUE, MINVALUE) TO ('b', MINVAL
mcrparted8_ge_d FOR VALUES FROM ('d', MINVALUE) TO (MAXVALUE, MAXVALUE)
\d+ mcrparted1_lt_b
- Table "public.mcrparted1_lt_b"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted1_lt_b"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM (MINVALUE, MINVALUE) TO ('b', MINVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a < 'b'::text))
\d+ mcrparted2_b
- Table "public.mcrparted2_b"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted2_b"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('b', MINVALUE) TO ('c', MINVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'b'::text) AND (a < 'c'::text))
\d+ mcrparted3_c_to_common
- Table "public.mcrparted3_c_to_common"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted3_c_to_common"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('c', MINVALUE) TO ('common', MINVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'c'::text) AND (a < 'common'::text))
\d+ mcrparted4_common_lt_0
- Table "public.mcrparted4_common_lt_0"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted4_common_lt_0"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('common', MINVALUE) TO ('common', 0)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::text) AND (b < 0))
\d+ mcrparted5_common_0_to_10
- Table "public.mcrparted5_common_0_to_10"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted5_common_0_to_10"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('common', 0) TO ('common', 10)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::text) AND (b >= 0) AND (b < 10))
\d+ mcrparted6_common_ge_10
- Table "public.mcrparted6_common_ge_10"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted6_common_ge_10"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('common', 10) TO ('common', MAXVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::text) AND (b >= 10))
\d+ mcrparted7_gt_common_lt_d
- Table "public.mcrparted7_gt_common_lt_d"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted7_gt_common_lt_d"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('common', MAXVALUE) TO ('d', MINVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a > 'common'::text) AND (a < 'd'::text))
\d+ mcrparted8_ge_d
- Table "public.mcrparted8_ge_d"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted8_ge_d"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('d', MINVALUE) TO (MAXVALUE, MAXVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'd'::text))
diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out
index b101331d69..f805e13d75 100644
--- a/src/test/regress/expected/publication.out
+++ b/src/test/regress/expected/publication.out
@@ -65,11 +65,11 @@ SELECT pubname, puballtables FROM pg_publication WHERE pubname = 'testpub_forall
(1 row)
\d+ testpub_tbl2
- Table "public.testpub_tbl2"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('testpub_tbl2_id_seq'::regclass) | plain | |
- data | text | | | | extended | |
+ Table "public.testpub_tbl2"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('testpub_tbl2_id_seq'::regclass) | plain | | |
+ data | text | | | | extended | | |
Indexes:
"testpub_tbl2_pkey" PRIMARY KEY, btree (id)
Publications:
@@ -141,22 +141,22 @@ ALTER PUBLICATION testpub_default SET TABLE testpub_tbl1;
ALTER PUBLICATION testpub_default ADD TABLE pub_test.testpub_nopk;
ALTER PUBLICATION testpib_ins_trunct ADD TABLE pub_test.testpub_nopk, testpub_tbl1;
\d+ pub_test.testpub_nopk
- Table "pub_test.testpub_nopk"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- foo | integer | | | | plain | |
- bar | integer | | | | plain | |
+ Table "pub_test.testpub_nopk"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ foo | integer | | | | plain | | |
+ bar | integer | | | | plain | | |
Publications:
"testpib_ins_trunct"
"testpub_default"
"testpub_fortbl"
\d+ testpub_tbl1
- Table "public.testpub_tbl1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | |
- data | text | | | | extended | |
+ Table "public.testpub_tbl1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | | |
+ data | text | | | | extended | | |
Indexes:
"testpub_tbl1_pkey" PRIMARY KEY, btree (id)
Publications:
@@ -178,11 +178,11 @@ ALTER PUBLICATION testpub_default DROP TABLE testpub_tbl1, pub_test.testpub_nopk
ALTER PUBLICATION testpub_default DROP TABLE pub_test.testpub_nopk;
ERROR: relation "testpub_nopk" is not part of the publication
\d+ testpub_tbl1
- Table "public.testpub_tbl1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | |
- data | text | | | | extended | |
+ Table "public.testpub_tbl1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | | |
+ data | text | | | | extended | | |
Indexes:
"testpub_tbl1_pkey" PRIMARY KEY, btree (id)
Publications:
diff --git a/src/test/regress/expected/replica_identity.out b/src/test/regress/expected/replica_identity.out
index 67c34a92a4..1526437bf8 100644
--- a/src/test/regress/expected/replica_identity.out
+++ b/src/test/regress/expected/replica_identity.out
@@ -158,13 +158,13 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass;
(1 row)
\d+ test_replica_identity
- Table "public.test_replica_identity"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('test_replica_identity_id_seq'::regclass) | plain | |
- keya | text | | not null | | extended | |
- keyb | text | | not null | | extended | |
- nonkey | text | | | | extended | |
+ Table "public.test_replica_identity"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('test_replica_identity_id_seq'::regclass) | plain | | |
+ keya | text | | not null | | extended | | |
+ keyb | text | | not null | | extended | | |
+ nonkey | text | | | | extended | | |
Indexes:
"test_replica_identity_pkey" PRIMARY KEY, btree (id)
"test_replica_identity_expr" UNIQUE, btree (keya, keyb, (3))
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index f1c1b44d6f..e358ff539c 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2799,11 +2799,11 @@ select * from rules_log;
create rule r3 as on delete to rules_src do notify rules_src_deletion;
\d+ rules_src
- Table "public.rules_src"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- f1 | integer | | | | plain | |
- f2 | integer | | | | plain | |
+ Table "public.rules_src"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ f1 | integer | | | | plain | | |
+ f2 | integer | | | | plain | | |
Rules:
r1 AS
ON UPDATE TO rules_src DO INSERT INTO rules_log (f1, f2, tag) VALUES (old.f1,old.f2,'old'::text), (new.f1,new.f2,'new'::text)
@@ -2819,11 +2819,11 @@ Rules:
create rule r4 as on insert to rules_src do instead insert into rules_log AS trgt SELECT NEW.* RETURNING trgt.f1, trgt.f2;
create rule r5 as on update to rules_src do instead UPDATE rules_log AS trgt SET tag = 'updated' WHERE trgt.f1 = new.f1;
\d+ rules_src
- Table "public.rules_src"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- f1 | integer | | | | plain | |
- f2 | integer | | | | plain | |
+ Table "public.rules_src"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ f1 | integer | | | | plain | | |
+ f2 | integer | | | | plain | | |
Rules:
r1 AS
ON UPDATE TO rules_src DO INSERT INTO rules_log (f1, f2, tag) VALUES (old.f1,old.f2,'old'::text), (new.f1,new.f2,'new'::text)
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index e996640593..62b06da011 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -111,6 +111,8 @@ pg_authid|t
pg_cast|t
pg_class|t
pg_collation|t
+pg_compression|t
+pg_compression_opt|t
pg_constraint|t
pg_conversion|t
pg_database|t
diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out
index cef70b1a1e..a652883172 100644
--- a/src/test/regress/expected/update.out
+++ b/src/test/regress/expected/update.out
@@ -221,11 +221,11 @@ update range_parted set b = b + 1 where b = 10;
-- Creating default partition for range
create table part_def partition of range_parted default;
\d+ part_def
- Table "public.part_def"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.part_def"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: range_parted DEFAULT
Partition constraint: (NOT (((a = 'a'::text) AND (b >= 1) AND (b < 10)) OR ((a = 'a'::text) AND (b >= 10) AND (b < 20)) OR ((a = 'b'::text) AND (b >= 1) AND (b < 10)) OR ((a = 'b'::text) AND (b >= 10) AND (b < 20))))
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index aa5e6af621..a44cf1c910 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -30,7 +30,7 @@ test: point lseg line box path polygon circle date time timetz timestamp timesta
# geometry depends on point, lseg, box, path, polygon and circle
# horology depends on interval, timetz, timestamp, timestamptz, reltime and abstime
# ----------
-test: geometry horology regex oidjoins type_sanity opr_sanity misc_sanity comments expressions
+test: geometry horology regex oidjoins type_sanity opr_sanity misc_sanity comments expressions create_cm
# ----------
# These four each depend on the previous one
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 3866314a92..5c72cb16f5 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -43,6 +43,7 @@ test: inet
test: macaddr
test: macaddr8
test: tstypes
+test: create_cm
test: geometry
test: horology
test: regex
diff --git a/src/test/regress/sql/create_cm.sql b/src/test/regress/sql/create_cm.sql
new file mode 100644
index 0000000000..83307597e7
--- /dev/null
+++ b/src/test/regress/sql/create_cm.sql
@@ -0,0 +1,37 @@
+CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
+DROP COMPRESSION METHOD ts1;
+CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
+CREATE TABLE cmtest(fts tsvector COMPRESSED ts1);
+DROP COMPRESSION METHOD ts1;
+SELECT * FROM pg_compression;
+SELECT cmhandler, cmoptions FROM pg_compression_opt;
+
+\dCM
+\d+ cmtest
+
+INSERT INTO cmtest
+ SELECT to_tsvector(string_agg(repeat(substr(i::text,1,1), i), ' '))
+ FROM generate_series(1,200) i;
+SELECT length(fts) FROM cmtest;
+
+ALTER TABLE cmtest ALTER COLUMN fts SET NOT COMPRESSED;
+\d+ cmtest
+ALTER TABLE cmtest ALTER COLUMN fts SET COMPRESSED ts1 WITH (format 'lz');
+ALTER TABLE cmtest ALTER COLUMN fts SET COMPRESSED ts1;
+\d+ cmtest
+
+SELECT * INTO cmtest2 FROM cmtest;
+CREATE TABLE cmtest3 (LIKE cmtest);
+CREATE TABLE cmtest4(fts tsvector, a int) inherits (cmtest);
+\d+ cmtest3
+\d+ cmtest4
+DROP TABLE cmtest CASCADE;
+
+SELECT length(fts) FROM cmtest2;
+SELECT * FROM pg_compression;
+SELECT cmhandler, cmoptions FROM pg_compression_opt;
+
+DROP TABLE cmtest2;
+DROP TABLE cmtest3;
+
+DROP COMPRESSION METHOD ts1 CASCADE;
diff --git a/src/tools/pgindent/exclude_file_patterns b/src/tools/pgindent/exclude_file_patterns
index cb2f902a90..95b9f0160f 100644
--- a/src/tools/pgindent/exclude_file_patterns
+++ b/src/tools/pgindent/exclude_file_patterns
@@ -5,3 +5,4 @@
/ecpg/test/expected/
/snowball/libstemmer/
/pl/plperl/ppport\.h$
+/tmp_install.*$
On 2 November 2017 at 17:41, Ildus Kurbangaliev
<i.kurbangaliev@postgrespro.ru> wrote:
In this patch compression methods is suitable for MAIN and EXTENDED
storages like in current implementation in postgres. Just instead only
of LZ4 you can specify any other compression method.
We've had this discussion before.
Please read the "pluggable compression support" thread. See you in a
few days ;) sorry, it's kinda long.
/messages/by-id/20130621000900.GA12425@alap2.anarazel.de
IIRC there were some concerns about what happened with pg_upgrade,
with consuming precious toast bits, and a few other things.
--
Craig Ringer http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Thu, Nov 2, 2017 at 6:02 PM, Craig Ringer <craig@2ndquadrant.com> wrote:
On 2 November 2017 at 17:41, Ildus Kurbangaliev
<i.kurbangaliev@postgrespro.ru> wrote:In this patch compression methods is suitable for MAIN and EXTENDED
storages like in current implementation in postgres. Just instead only
of LZ4 you can specify any other compression method.We've had this discussion before.
Please read the "pluggable compression support" thread. See you in a
few days ;) sorry, it's kinda long.
the proposed patch provides "pluggable" compression and let's user
decide by their own which algorithm to use.
The postgres core doesn't responsible for any patent problem.
IIRC there were some concerns about what happened with pg_upgrade,
with consuming precious toast bits, and a few other things.
yes, pg_upgrade may be a problem.
--
Craig Ringer http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Sun, Nov 5, 2017 at 2:22 PM, Oleg Bartunov <obartunov@gmail.com> wrote:
IIRC there were some concerns about what happened with pg_upgrade,
with consuming precious toast bits, and a few other things.yes, pg_upgrade may be a problem.
A basic problem here is that, as proposed, DROP COMPRESSION METHOD may
break your database irretrievably. If there's no data compressed
using the compression method you dropped, everything is cool -
otherwise everything is broken and there's no way to recover. The
only obvious alternative is to disallow DROP altogether (or make it
not really DROP).
Both of those alternatives sound fairly unpleasant to me, but I'm not
exactly sure what to recommend in terms of how to make it better.
Ideally anything we expose as an SQL command should have a DROP
command that undoes whatever CREATE did and leaves the database in an
intact state, but that seems hard to achieve in this case.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
If there's no data compressed
using the compression method you dropped, everything is cool -
otherwise everything is broken and there's no way to recover.
The only obvious alternative is to disallow DROP altogether (or make it
not really DROP).
Wouldn't whatever was using the compression method have something
marking which method was used? If so, couldn't we just scan if there is
any data using it, and if so disallow the drop, or possibly an option to allow
the drop and rewrite the table either uncompressed, or with the default
compression method?
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Robert Haas <robertmhaas@gmail.com> writes:
A basic problem here is that, as proposed, DROP COMPRESSION METHOD may
break your database irretrievably. If there's no data compressed
using the compression method you dropped, everything is cool -
otherwise everything is broken and there's no way to recover. The
only obvious alternative is to disallow DROP altogether (or make it
not really DROP).
Both of those alternatives sound fairly unpleasant to me, but I'm not
exactly sure what to recommend in terms of how to make it better.
Ideally anything we expose as an SQL command should have a DROP
command that undoes whatever CREATE did and leaves the database in an
intact state, but that seems hard to achieve in this case.
If the use of a compression method is tied to specific data types and/or
columns, then each of those could have a dependency on the compression
method, forcing a type or column drop if you did DROP COMPRESSION METHOD.
That would leave no reachable data using the removed compression method.
So that part doesn't seem unworkable on its face.
IIRC, the bigger concerns in the last discussion had to do with
replication, ie, can downstream servers make sense of the data.
Maybe that's not any worse than the issues you get with non-core
index AMs, but I'm not sure.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Thu, 2 Nov 2017 23:02:34 +0800
Craig Ringer <craig@2ndquadrant.com> wrote:
On 2 November 2017 at 17:41, Ildus Kurbangaliev
<i.kurbangaliev@postgrespro.ru> wrote:In this patch compression methods is suitable for MAIN and EXTENDED
storages like in current implementation in postgres. Just instead
only of LZ4 you can specify any other compression method.We've had this discussion before.
Please read the "pluggable compression support" thread. See you in a
few days ;) sorry, it's kinda long./messages/by-id/20130621000900.GA12425@alap2.anarazel.de
IIRC there were some concerns about what happened with pg_upgrade,
with consuming precious toast bits, and a few other things.
Thank you for the link, I didn't see that thread when I looked over
mailing lists. I read it briefly, and I can address few things
relating to my patch.
Most concerns have been related with legal issues.
Actually that was the reason I did not include any new compression
algorithms to my patch. Unlike that patch mine only provides syntax
and is just a way to give the users use their own compression algorithms
and deal with any legal issues themselves.
I use only one unused bit in header (there's still one free ;), that's
enough to determine that data is compressed or not.
I did found out that pg_upgrade doesn't work properly with my patch,
soon I will send fix for it.
--
---
Ildus Kurbangaliev
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Thu, 2 Nov 2017 15:28:36 +0300
Ildus Kurbangaliev <i.kurbangaliev@postgrespro.ru> wrote:
On Tue, 12 Sep 2017 17:55:05 +0300
Ildus Kurbangaliev <i.kurbangaliev@postgrespro.ru> wrote:Attached rebased version of the patch. Added support of pg_dump, the
code was simplified, and a separate cache for compression options
was added.Attached version 3 of the patch. Rebased to the current master,
removed ALTER TYPE .. SET COMPRESSED syntax, fixed bug in compression
options cache.
Attached version 4 of the patch. Fixed pg_upgrade and few other bugs.
--
---
Ildus Kurbangaliev
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company
Attachments:
custom_compression_methods_v4.patchtext/x-patchDownload
diff --git a/contrib/test_decoding/expected/ddl.out b/contrib/test_decoding/expected/ddl.out
index 1e22c1eefc..766ced401f 100644
--- a/contrib/test_decoding/expected/ddl.out
+++ b/contrib/test_decoding/expected/ddl.out
@@ -416,12 +416,12 @@ CREATE TABLE replication_metadata (
WITH (user_catalog_table = true)
;
\d+ replication_metadata
- Table "public.replication_metadata"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------+---------+-----------+----------+--------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | |
- relation | name | | not null | | plain | |
- options | text[] | | | | extended | |
+ Table "public.replication_metadata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+----------+---------+-----------+----------+--------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | |
+ relation | name | | not null | | plain | | |
+ options | text[] | | | | extended | | |
Indexes:
"replication_metadata_pkey" PRIMARY KEY, btree (id)
Options: user_catalog_table=true
@@ -430,12 +430,12 @@ INSERT INTO replication_metadata(relation, options)
VALUES ('foo', ARRAY['a', 'b']);
ALTER TABLE replication_metadata RESET (user_catalog_table);
\d+ replication_metadata
- Table "public.replication_metadata"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------+---------+-----------+----------+--------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | |
- relation | name | | not null | | plain | |
- options | text[] | | | | extended | |
+ Table "public.replication_metadata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+----------+---------+-----------+----------+--------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | |
+ relation | name | | not null | | plain | | |
+ options | text[] | | | | extended | | |
Indexes:
"replication_metadata_pkey" PRIMARY KEY, btree (id)
@@ -443,12 +443,12 @@ INSERT INTO replication_metadata(relation, options)
VALUES ('bar', ARRAY['a', 'b']);
ALTER TABLE replication_metadata SET (user_catalog_table = true);
\d+ replication_metadata
- Table "public.replication_metadata"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------+---------+-----------+----------+--------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | |
- relation | name | | not null | | plain | |
- options | text[] | | | | extended | |
+ Table "public.replication_metadata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+----------+---------+-----------+----------+--------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | |
+ relation | name | | not null | | plain | | |
+ options | text[] | | | | extended | | |
Indexes:
"replication_metadata_pkey" PRIMARY KEY, btree (id)
Options: user_catalog_table=true
@@ -461,13 +461,13 @@ ALTER TABLE replication_metadata ALTER COLUMN rewritemeornot TYPE text;
ERROR: cannot rewrite table "replication_metadata" used as a catalog table
ALTER TABLE replication_metadata SET (user_catalog_table = false);
\d+ replication_metadata
- Table "public.replication_metadata"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------------+---------+-----------+----------+--------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | |
- relation | name | | not null | | plain | |
- options | text[] | | | | extended | |
- rewritemeornot | integer | | | | plain | |
+ Table "public.replication_metadata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+----------------+---------+-----------+----------+--------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | |
+ relation | name | | not null | | plain | | |
+ options | text[] | | | | extended | | |
+ rewritemeornot | integer | | | | plain | | |
Indexes:
"replication_metadata_pkey" PRIMARY KEY, btree (id)
Options: user_catalog_table=false
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index 01acc2ef9d..f43f09cc19 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -59,6 +59,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY createAggregate SYSTEM "create_aggregate.sgml">
<!ENTITY createCast SYSTEM "create_cast.sgml">
<!ENTITY createCollation SYSTEM "create_collation.sgml">
+<!ENTITY createCompressionMethod SYSTEM "create_compression_method.sgml">
<!ENTITY createConversion SYSTEM "create_conversion.sgml">
<!ENTITY createDatabase SYSTEM "create_database.sgml">
<!ENTITY createDomain SYSTEM "create_domain.sgml">
diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index 3b19ea7131..3dc8eb2952 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -53,6 +53,8 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET ( <replaceable class="parameter">attribute_option</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] )
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> RESET ( <replaceable class="parameter">attribute_option</replaceable> [, ... ] )
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN }
+ ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET COMPRESSED <replaceable class="parameter">compression_method_name</replaceable> [ WITH (<replaceable class="parameter">compression_method_options</replaceable>) ]
+ ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET NOT COMPRESSED
ADD <replaceable class="parameter">table_constraint</replaceable> [ NOT VALID ]
ADD <replaceable class="parameter">table_constraint_using_index</replaceable>
ALTER CONSTRAINT <replaceable class="parameter">constraint_name</replaceable> [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -320,6 +322,34 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>
+ <literal>SET COMPRESSED <replaceable class="parameter">compression_method_name</replaceable></literal>
+ </term>
+ <listitem>
+ <para>
+ This form adds compression to a column. Compression method should be
+ created with <xref linkend="SQL-CREATECOMPRESSIONMETHOD">. If compression
+ method has options they could be specified with <literal>WITH</literal>
+ parameter. Setting a compression method doesn't change anything in the
+ table and affects only future table updates.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <literal>SET NOT COMPRESSED</literal>
+ </term>
+ <listitem>
+ <para>
+ This form removes compression from a column. Removing compresssion from
+ a column doesn't change already compressed tuples and affects only future
+ table updates.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>ADD <replaceable class="parameter">table_constraint</replaceable> [ NOT VALID ]</literal></term>
<listitem>
diff --git a/doc/src/sgml/ref/create_compression_method.sgml b/doc/src/sgml/ref/create_compression_method.sgml
new file mode 100644
index 0000000000..663010ecd9
--- /dev/null
+++ b/doc/src/sgml/ref/create_compression_method.sgml
@@ -0,0 +1,50 @@
+<!--
+doc/src/sgml/ref/create_compression_method.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-CREATECOMPRESSIONMETHOD">
+ <indexterm zone="sql-createcompressionmethod">
+ <primary>CREATE COMPRESSION METHOD</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>CREATE COMPRESSION METHOD</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>CREATE COMPRESSION METHOD</refname>
+ <refpurpose>define a new compression method</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+CREATE COMPRESSION METHOD <replaceable class="parameter">compression_method_name</replaceable>
+ HANDLER <replaceable class="parameter">compression_method_handler</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>CREATE COMPRESSION METHOD</command> creates a new compression method
+ with <replaceable class="parameter">compression_method_name</replaceable>.
+ </para>
+
+ <para>
+ A compression method links a name with a compression handler. And the
+ handler is a special function that returns collection of methods that
+ can be used for compression.
+ </para>
+
+ <para>
+ After a compression method is created, you can specify it in
+ <xref linkend="SQL-CREATETABLE"> or <xref linkend="SQL-ALTERTABLE">
+ statements.
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index bbb3a51def..70de6352a5 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -65,6 +65,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( <replaceable>sequence_options</replaceable> ) ] |
UNIQUE <replaceable class="parameter">index_parameters</replaceable> |
PRIMARY KEY <replaceable class="parameter">index_parameters</replaceable> |
+ COMPRESSED <replaceable class="parameter">compression_method_name</replaceable> [ WITH (<replaceable class="parameter">compression_method_options</replaceable>) ] |
REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -817,6 +818,18 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>COMPRESSED <replaceable class="parameter">compression_method_name</replaceable> [ WITH (<replaceable class="parameter">compression_method_options</replaceable>) ]</literal></term>
+ <listitem>
+ <para>
+ This clause adds compression to a column. Compression method should be
+ created with <xref linkend="SQL-CREATECOMPRESSIONMETHOD">. If compression
+ method has options they could be specified with <literal>WITH</literal>
+ parameter.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="sql-createtable-exclude">
<term><literal>EXCLUDE [ USING <replaceable class="parameter">index_method</replaceable> ] ( <replaceable class="parameter">exclude_element</replaceable> WITH <replaceable class="parameter">operator</replaceable> [, ... ] ) <replaceable class="parameter">index_parameters</replaceable> [ WHERE ( <replaceable class="parameter">predicate</replaceable> ) ]</literal></term>
<listitem>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index 9000b3aaaa..cc0bd70be3 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -87,6 +87,7 @@
&createAggregate;
&createCast;
&createCollation;
+ &createCompressionMethod;
&createConversion;
&createDatabase;
&createDomain;
diff --git a/src/backend/access/common/indextuple.c b/src/backend/access/common/indextuple.c
index 138671410a..629f3575a9 100644
--- a/src/backend/access/common/indextuple.c
+++ b/src/backend/access/common/indextuple.c
@@ -92,7 +92,8 @@ index_form_tuple(TupleDesc tupleDescriptor,
VARSIZE(DatumGetPointer(untoasted_values[i])) > TOAST_INDEX_TARGET &&
(att->attstorage == 'x' || att->attstorage == 'm'))
{
- Datum cvalue = toast_compress_datum(untoasted_values[i]);
+ Datum cvalue = toast_compress_datum(untoasted_values[i],
+ att->attcompression);
if (DatumGetPointer(cvalue) != NULL)
{
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 3d0ce9af6f..e96b77a629 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -935,11 +935,48 @@ untransformRelOptions(Datum options)
val = (Node *) makeString(pstrdup(p));
}
result = lappend(result, makeDefElem(pstrdup(s), val, -1));
+ pfree(s);
}
return result;
}
+char *
+formatRelOptions(List *options)
+{
+ StringInfoData buf;
+ ListCell *cell;
+
+ initStringInfo(&buf);
+
+ foreach(cell, options)
+ {
+ DefElem *def = (DefElem *) lfirst(cell);
+
+ appendStringInfo(&buf, "%s%s=%s", buf.len > 0 ? ", " : "",
+ def->defname, defGetString(def));
+ }
+
+ return buf.data;
+}
+
+void
+freeRelOptions(List *options)
+{
+ ListCell *cell;
+
+ Assert(options != NIL);
+ foreach(cell, options)
+ {
+ DefElem *def = (DefElem *) lfirst(cell);
+
+ pfree(def->defname);
+ pfree(defGetString(def));
+ pfree(def->arg);
+ }
+ list_free_deep(options);
+}
+
/*
* Extract and parse reloptions from a pg_class tuple.
*
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index 9e37ca73a8..d206cce18e 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -19,8 +19,10 @@
#include "postgres.h"
+#include "access/compression.h"
#include "access/hash.h"
#include "access/htup_details.h"
+#include "access/reloptions.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
@@ -242,6 +244,7 @@ TupleDescCopyEntry(TupleDesc dst, AttrNumber dstAttno,
dstAtt->attnotnull = false;
dstAtt->atthasdef = false;
dstAtt->attidentity = '\0';
+ dstAtt->attcompression = InvalidOid;
}
/*
@@ -396,6 +399,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
return false;
if (attr1->attcollation != attr2->attcollation)
return false;
+ if (attr1->attcompression != attr2->attcompression)
+ return false;
/* attacl, attoptions and attfdwoptions are not even present... */
}
@@ -458,6 +463,7 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
}
else if (tupdesc2->constr != NULL)
return false;
+
return true;
}
@@ -563,6 +569,7 @@ TupleDescInitEntry(TupleDesc desc,
att->attalign = typeForm->typalign;
att->attstorage = typeForm->typstorage;
att->attcollation = typeForm->typcollation;
+ att->attcompression = InvalidOid;
ReleaseSysCache(tuple);
}
@@ -675,7 +682,6 @@ TupleDescInitEntryCollation(TupleDesc desc,
TupleDescAttr(desc, attributeNumber - 1)->attcollation = collationid;
}
-
/*
* BuildDescForRelation
*
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index 5a8f1dab83..a255fee3fb 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -30,8 +30,10 @@
#include <unistd.h>
#include <fcntl.h>
+#include "access/compression.h"
#include "access/genam.h"
#include "access/heapam.h"
+#include "access/reloptions.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -39,6 +41,8 @@
#include "miscadmin.h"
#include "utils/expandeddatum.h"
#include "utils/fmgroids.h"
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/typcache.h"
@@ -53,19 +57,46 @@
typedef struct toast_compress_header
{
int32 vl_len_; /* varlena header (do not touch directly!) */
- int32 rawsize;
+ uint32 info; /* flags (2 bits) and rawsize */
} toast_compress_header;
+/*
+ * If the compression method were used, then data also contains
+ * Oid of compression options
+ */
+typedef struct toast_compress_header_custom
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ uint32 info; /* flags (2 high bits) and rawsize */
+ Oid cmoptoid; /* Oid from pg_compression_opt */
+} toast_compress_header_custom;
+
+static HTAB *compression_options_htab = NULL;
+static MemoryContext compression_options_mcxt = NULL;
+
+#define RAWSIZEMASK (0x3FFFFFFFU)
+
/*
* Utilities for manipulation of header information for compressed
* toast entries.
*/
-#define TOAST_COMPRESS_HDRSZ ((int32) sizeof(toast_compress_header))
-#define TOAST_COMPRESS_RAWSIZE(ptr) (((toast_compress_header *) (ptr))->rawsize)
+#define TOAST_COMPRESS_HDRSZ ((int32) sizeof(toast_compress_header))
+#define TOAST_COMPRESS_HDRSZ_CUSTOM ((int32) sizeof(toast_compress_header_custom))
+#define TOAST_COMPRESS_RAWSIZE(ptr) (((toast_compress_header *) (ptr))->info & RAWSIZEMASK)
#define TOAST_COMPRESS_RAWDATA(ptr) \
(((char *) (ptr)) + TOAST_COMPRESS_HDRSZ)
#define TOAST_COMPRESS_SET_RAWSIZE(ptr, len) \
- (((toast_compress_header *) (ptr))->rawsize = (len))
+do { \
+ ((toast_compress_header *) (ptr))->info &= 0xC0000000; \
+ ((toast_compress_header *) (ptr))->info |= ((uint32)(len) & RAWSIZEMASK); \
+} while (0)
+#define TOAST_COMPRESS_SET_CMOPTOID(ptr, oid) \
+ (((toast_compress_header_custom *) (ptr))->cmoptoid = (oid))
+#define TOAST_COMPRESS_SET_CUSTOM(ptr) \
+do { \
+ (((toast_compress_header *) (ptr))->info |= (1 << 31)); \
+ (((toast_compress_header *) (ptr))->info &= ~(1 << 30)); \
+} while (0)
static void toast_delete_datum(Relation rel, Datum value, bool is_speculative);
static Datum toast_save_datum(Relation rel, Datum value,
@@ -83,6 +114,8 @@ static int toast_open_indexes(Relation toastrel,
static void toast_close_indexes(Relation *toastidxs, int num_indexes,
LOCKMODE lock);
static void init_toast_snapshot(Snapshot toast_snapshot);
+static void init_compression_options_htab(void);
+static AttributeCompression * get_compression_options_info(Oid cmoptoid);
/* ----------
@@ -741,6 +774,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
Datum old_value;
Datum new_value;
+ Form_pg_attribute att;
+
/*
* Search for the biggest yet unprocessed internal attribute
*/
@@ -770,10 +805,11 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
* Attempt to compress it inline, if it has attstorage 'x'
*/
i = biggest_attno;
- if (TupleDescAttr(tupleDesc, i)->attstorage == 'x')
+ att = TupleDescAttr(tupleDesc, i);
+ if (att->attstorage == 'x')
{
old_value = toast_values[i];
- new_value = toast_compress_datum(old_value);
+ new_value = toast_compress_datum(old_value, att->attcompression);
if (DatumGetPointer(new_value) != NULL)
{
@@ -914,7 +950,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
*/
i = biggest_attno;
old_value = toast_values[i];
- new_value = toast_compress_datum(old_value);
+ new_value = toast_compress_datum(old_value,
+ TupleDescAttr(tupleDesc, i)->attcompression);
if (DatumGetPointer(new_value) != NULL)
{
@@ -1229,7 +1266,9 @@ toast_flatten_tuple_to_datum(HeapTupleHeader tup,
if (VARATT_IS_EXTERNAL(new_value) ||
VARATT_IS_COMPRESSED(new_value))
{
- new_value = heap_tuple_untoast_attr(new_value);
+ struct varlena *untoasted_value = heap_tuple_untoast_attr(new_value);
+
+ new_value = untoasted_value;
toast_values[i] = PointerGetDatum(new_value);
toast_free[i] = true;
}
@@ -1353,7 +1392,6 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
return new_tuple;
}
-
/* ----------
* toast_compress_datum -
*
@@ -1368,25 +1406,43 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
* ----------
*/
Datum
-toast_compress_datum(Datum value)
+toast_compress_datum(Datum value, Oid cmoptoid)
{
- struct varlena *tmp;
- int32 valsize = VARSIZE_ANY_EXHDR(DatumGetPointer(value));
- int32 len;
+ struct varlena *tmp = NULL;
+ int32 valsize,
+ len = 0;
+ AttributeCompression *ac = NULL;
Assert(!VARATT_IS_EXTERNAL(DatumGetPointer(value)));
Assert(!VARATT_IS_COMPRESSED(DatumGetPointer(value)));
+ if (OidIsValid(cmoptoid))
+ ac = get_compression_options_info(cmoptoid);
+
/*
* No point in wasting a palloc cycle if value size is out of the allowed
* range for compression
*/
- if (valsize < PGLZ_strategy_default->min_input_size ||
- valsize > PGLZ_strategy_default->max_input_size)
+ valsize = VARSIZE_ANY_EXHDR(DatumGetPointer(value));
+ if (!ac && (valsize < PGLZ_strategy_default->min_input_size ||
+ valsize > PGLZ_strategy_default->max_input_size))
return PointerGetDatum(NULL);
- tmp = (struct varlena *) palloc(PGLZ_MAX_OUTPUT(valsize) +
- TOAST_COMPRESS_HDRSZ);
+ if (ac)
+ {
+ tmp = ac->routine->compress(ac, (const struct varlena *) value);
+ if (!tmp)
+ return PointerGetDatum(NULL);
+ }
+ else
+ {
+ tmp = (struct varlena *) palloc(PGLZ_MAX_OUTPUT(valsize) +
+ TOAST_COMPRESS_HDRSZ);
+ len = pglz_compress(VARDATA_ANY(DatumGetPointer(value)),
+ valsize,
+ TOAST_COMPRESS_RAWDATA(tmp),
+ PGLZ_strategy_default);
+ }
/*
* We recheck the actual size even if pglz_compress() reports success,
@@ -1398,11 +1454,7 @@ toast_compress_datum(Datum value)
* only one header byte and no padding if the value is short enough. So
* we insist on a savings of more than 2 bytes to ensure we have a gain.
*/
- len = pglz_compress(VARDATA_ANY(DatumGetPointer(value)),
- valsize,
- TOAST_COMPRESS_RAWDATA(tmp),
- PGLZ_strategy_default);
- if (len >= 0 &&
+ if (!ac && len >= 0 &&
len + TOAST_COMPRESS_HDRSZ < valsize - 2)
{
TOAST_COMPRESS_SET_RAWSIZE(tmp, valsize);
@@ -1410,10 +1462,20 @@ toast_compress_datum(Datum value)
/* successful compression */
return PointerGetDatum(tmp);
}
+ else if (ac && VARSIZE(tmp) < valsize - 2)
+ {
+ TOAST_COMPRESS_SET_CUSTOM(tmp);
+ TOAST_COMPRESS_SET_RAWSIZE(tmp, valsize);
+ TOAST_COMPRESS_SET_CMOPTOID(tmp, ac->cmoptoid);
+ /* successful compression */
+ return PointerGetDatum(tmp);
+ }
else
{
/* incompressible data */
- pfree(tmp);
+ if (tmp)
+ pfree(tmp);
+
return PointerGetDatum(NULL);
}
}
@@ -2280,15 +2342,26 @@ toast_decompress_datum(struct varlena *attr)
Assert(VARATT_IS_COMPRESSED(attr));
- result = (struct varlena *)
- palloc(TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
- SET_VARSIZE(result, TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
+ if (VARATT_IS_CUSTOM_COMPRESSED(attr))
+ {
+ AttributeCompression *ac;
+ toast_compress_header_custom *hdr;
- if (pglz_decompress(TOAST_COMPRESS_RAWDATA(attr),
- VARSIZE(attr) - TOAST_COMPRESS_HDRSZ,
- VARDATA(result),
- TOAST_COMPRESS_RAWSIZE(attr)) < 0)
- elog(ERROR, "compressed data is corrupted");
+ hdr = (toast_compress_header_custom *) attr;
+ ac = get_compression_options_info(hdr->cmoptoid);
+ result = ac->routine->decompress(ac, attr);
+ }
+ else
+ {
+ result = (struct varlena *)
+ palloc(TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
+ SET_VARSIZE(result, TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
+ if (pglz_decompress(TOAST_COMPRESS_RAWDATA(attr),
+ VARSIZE(attr) - TOAST_COMPRESS_HDRSZ,
+ VARDATA(result),
+ TOAST_COMPRESS_RAWSIZE(attr)) < 0)
+ elog(ERROR, "compressed data is corrupted");
+ }
return result;
}
@@ -2390,3 +2463,44 @@ init_toast_snapshot(Snapshot toast_snapshot)
InitToastSnapshot(*toast_snapshot, snapshot->lsn, snapshot->whenTaken);
}
+
+static void
+init_compression_options_htab(void)
+{
+ HASHCTL ctl;
+
+ compression_options_mcxt = AllocSetContextCreate(TopMemoryContext,
+ "compression options cache context",
+ ALLOCSET_DEFAULT_SIZES);
+ MemSet(&ctl, 0, sizeof(ctl));
+ ctl.keysize = sizeof(Oid);
+ ctl.entrysize = sizeof(AttributeCompression);
+ ctl.hcxt = compression_options_mcxt;
+ compression_options_htab = hash_create("compression options cache", 100, &ctl,
+ HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
+}
+
+static AttributeCompression *
+get_compression_options_info(Oid cmoptoid)
+{
+ bool found;
+ AttributeCompression *result;
+
+ Assert(OidIsValid(cmoptoid));
+ if (!compression_options_htab)
+ init_compression_options_htab();
+
+ result = hash_search(compression_options_htab, &cmoptoid, HASH_ENTER, &found);
+ if (!found)
+ {
+ MemoryContext oldcxt;
+
+ Assert(compression_options_mcxt);
+ oldcxt = MemoryContextSwitchTo(compression_options_mcxt);
+ result->cmoptoid = cmoptoid;
+ result->routine = GetCompressionRoutine(cmoptoid);
+ result->options = GetCompressionOptionsList(cmoptoid);
+ MemoryContextSwitchTo(oldcxt);
+ }
+ return result;
+}
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 8287de97a2..be6b460aee 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -731,6 +731,7 @@ DefineAttr(char *name, char *type, int attnum, int nullness)
attrtypes[attnum]->attcacheoff = -1;
attrtypes[attnum]->atttypmod = -1;
attrtypes[attnum]->attislocal = true;
+ attrtypes[attnum]->attcompression = InvalidOid;
if (nullness == BOOTCOL_NULL_FORCE_NOT_NULL)
{
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index fd33426bad..c7cea974b1 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -46,7 +46,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_collation.h pg_partitioned_table.h pg_range.h pg_transform.h \
pg_sequence.h pg_publication.h pg_publication_rel.h pg_subscription.h \
pg_subscription_rel.h toasting.h indexing.h \
- toasting.h indexing.h \
+ pg_compression.h pg_compression_opt.h \
)
# location of Catalog.pm
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index ccde66a7dd..fd733a34a0 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -3340,6 +3340,8 @@ static const char *const no_priv_msg[MAX_ACL_KIND] =
gettext_noop("permission denied for publication %s"),
/* ACL_KIND_SUBSCRIPTION */
gettext_noop("permission denied for subscription %s"),
+ /* ACL_KIND_COMPRESSION_METHOD */
+ gettext_noop("permission denied for compression method %s"),
};
static const char *const not_owner_msg[MAX_ACL_KIND] =
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 033c4358ea..e1bfc7c6bf 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -28,6 +28,8 @@
#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_collation_fn.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_conversion.h"
@@ -173,7 +175,9 @@ static const Oid object_classes[] = {
PublicationRelationId, /* OCLASS_PUBLICATION */
PublicationRelRelationId, /* OCLASS_PUBLICATION_REL */
SubscriptionRelationId, /* OCLASS_SUBSCRIPTION */
- TransformRelationId /* OCLASS_TRANSFORM */
+ TransformRelationId, /* OCLASS_TRANSFORM */
+ CompressionMethodRelationId, /* OCLASS_COMPRESSION_METHOD */
+ CompressionOptRelationId, /* OCLASS_COMPRESSION_OPTIONS */
};
@@ -1271,6 +1275,14 @@ doDeletion(const ObjectAddress *object, int flags)
DropTransformById(object->objectId);
break;
+ case OCLASS_COMPRESSION_METHOD:
+ RemoveCompressionMethodById(object->objectId);
+ break;
+
+ case OCLASS_COMPRESSION_OPTIONS:
+ RemoveCompressionOptionsById(object->objectId);
+ break;
+
/*
* These global object types are not supported here.
*/
@@ -2512,6 +2524,12 @@ getObjectClass(const ObjectAddress *object)
case TransformRelationId:
return OCLASS_TRANSFORM;
+
+ case CompressionMethodRelationId:
+ return OCLASS_COMPRESSION_METHOD;
+
+ case CompressionOptRelationId:
+ return OCLASS_COMPRESSION_OPTIONS;
}
/* shouldn't get here */
diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl
index 256a9c9c93..c5838fa779 100644
--- a/src/backend/catalog/genbki.pl
+++ b/src/backend/catalog/genbki.pl
@@ -451,6 +451,7 @@ sub emit_pgattr_row
attisdropped => 'f',
attislocal => 't',
attinhcount => '0',
+ attcompression=> '0',
attacl => '_null_',
attoptions => '_null_',
attfdwoptions => '_null_');
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 9e14880b99..0a7ee5961e 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -29,8 +29,10 @@
*/
#include "postgres.h"
+#include "access/compression.h"
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/reloptions.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "access/xact.h"
@@ -44,6 +46,8 @@
#include "catalog/partition.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_foreign_table.h"
@@ -628,6 +632,7 @@ InsertPgAttributeTuple(Relation pg_attribute_rel,
values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal);
values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount);
values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(new_attribute->attcollation);
+ values[Anum_pg_attribute_attcompression - 1] = ObjectIdGetDatum(new_attribute->attcompression);
/* start out with empty permissions and empty options */
nulls[Anum_pg_attribute_attacl - 1] = true;
@@ -707,6 +712,13 @@ AddNewAttributeTuples(Oid new_rel_oid,
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+
+ if (OidIsValid(attr->attcompression))
+ {
+ ObjectAddressSet(referenced, CompressionOptRelationId,
+ attr->attcompression);
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
}
/*
@@ -1453,6 +1465,22 @@ DeleteRelationTuple(Oid relid)
heap_close(pg_class_desc, RowExclusiveLock);
}
+static void
+DropAttributeCompression(Form_pg_attribute att)
+{
+ CompressionMethodRoutine *cmr = GetCompressionRoutine(att->attcompression);
+
+ if (cmr->drop)
+ {
+ bool attisdropped = att->attisdropped;
+ List *options = GetCompressionOptionsList(att->attcompression);
+
+ att->attisdropped = true;
+ cmr->drop(att, options);
+ att->attisdropped = attisdropped;
+ }
+}
+
/*
* DeleteAttributeTuples
*
@@ -1483,7 +1511,14 @@ DeleteAttributeTuples(Oid relid)
/* Delete all the matching tuples */
while ((atttup = systable_getnext(scan)) != NULL)
+ {
+ Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(atttup);
+
+ if (OidIsValid(att->attcompression))
+ DropAttributeCompression(att);
+
CatalogTupleDelete(attrel, &atttup->t_self);
+ }
/* Clean up after the scan */
systable_endscan(scan);
@@ -1576,6 +1611,8 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
else
{
/* Dropping user attributes is lots harder */
+ if (OidIsValid(attStruct->attcompression))
+ DropAttributeCompression(attStruct);
/* Mark the attribute as dropped */
attStruct->attisdropped = true;
@@ -1597,6 +1634,8 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
/* We don't want to keep stats for it anymore */
attStruct->attstattarget = 0;
+ attStruct->attcompression = InvalidOid;
+
/*
* Change the column name to something that isn't likely to conflict
*/
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index c7b2f031f0..11dc107ab7 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -393,6 +393,7 @@ ConstructTupleDescriptor(Relation heapRelation,
to->atttypmod = exprTypmod(indexkey);
to->attislocal = true;
to->attcollation = collationObjectId[i];
+ to->attcompression = InvalidOid;
ReleaseSysCache(tuple);
@@ -471,6 +472,7 @@ ConstructTupleDescriptor(Relation heapRelation,
to->attbyval = typeTup->typbyval;
to->attalign = typeTup->typalign;
to->attstorage = typeTup->typstorage;
+ to->attcompression = InvalidOid;
ReleaseSysCache(tuple);
}
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 8d55c76fc4..9fd1cb763d 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -29,6 +29,8 @@
#include "catalog/pg_default_acl.h"
#include "catalog/pg_event_trigger.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_conversion.h"
@@ -490,6 +492,30 @@ static const ObjectPropertyType ObjectProperty[] =
InvalidAttrNumber, /* no ACL (same as relation) */
ACL_KIND_STATISTICS,
true
+ },
+ {
+ CompressionMethodRelationId,
+ CompressionMethodOidIndexId,
+ COMPRESSIONMETHODOID,
+ COMPRESSIONMETHODNAME,
+ Anum_pg_compression_cmname,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ true
+ },
+ {
+ CompressionOptRelationId,
+ CompressionOptionsOidIndexId,
+ COMPRESSIONOPTIONSOID,
+ -1,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ false,
}
};
@@ -712,6 +738,10 @@ static const struct object_type_map
/* OBJECT_STATISTIC_EXT */
{
"statistics object", OBJECT_STATISTIC_EXT
+ },
+ /* OCLASS_COMPRESSION_METHOD */
+ {
+ "compression method", OBJECT_COMPRESSION_METHOD
}
};
@@ -876,6 +906,7 @@ get_object_address(ObjectType objtype, Node *object,
case OBJECT_ACCESS_METHOD:
case OBJECT_PUBLICATION:
case OBJECT_SUBSCRIPTION:
+ case OBJECT_COMPRESSION_METHOD:
address = get_object_address_unqualified(objtype,
(Value *) object, missing_ok);
break;
@@ -1182,6 +1213,11 @@ get_object_address_unqualified(ObjectType objtype,
address.objectId = get_subscription_oid(name, missing_ok);
address.objectSubId = 0;
break;
+ case OBJECT_COMPRESSION_METHOD:
+ address.classId = CompressionMethodRelationId;
+ address.objectId = get_compression_method_oid(name, missing_ok);
+ address.objectSubId = 0;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
/* placate compiler, which doesn't know elog won't return */
@@ -2139,6 +2175,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_SCHEMA:
case OBJECT_SUBSCRIPTION:
case OBJECT_TABLESPACE:
+ case OBJECT_COMPRESSION_METHOD:
if (list_length(name) != 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -2395,12 +2432,14 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
case OBJECT_ACCESS_METHOD:
+ case OBJECT_COMPRESSION_METHOD:
/* We treat these object types as being owned by superusers */
if (!superuser_arg(roleid))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser")));
break;
+
case OBJECT_STATISTIC_EXT:
if (!pg_statistics_object_ownercheck(address.objectId, roleid))
aclcheck_error_type(ACLCHECK_NOT_OWNER, address.objectId);
@@ -3398,6 +3437,27 @@ getObjectDescription(const ObjectAddress *object)
break;
}
+ case OCLASS_COMPRESSION_METHOD:
+ {
+ char *name = get_compression_method_name(object->objectId);
+
+ if (!name)
+ elog(ERROR, "cache lookup failed for compression method %u",
+ object->objectId);
+ appendStringInfo(&buffer, _("compression method %s"), name);
+ pfree(name);
+ break;
+ }
+
+ case OCLASS_COMPRESSION_OPTIONS:
+ {
+ char *name = get_compression_method_name_for_opt(object->objectId);
+
+ appendStringInfo(&buffer, _("compression options for %s"), name);
+ pfree(name);
+ break;
+ }
+
case OCLASS_TRANSFORM:
{
HeapTuple trfTup;
@@ -3919,6 +3979,14 @@ getObjectTypeDescription(const ObjectAddress *object)
appendStringInfoString(&buffer, "transform");
break;
+ case OCLASS_COMPRESSION_METHOD:
+ appendStringInfoString(&buffer, "compression method");
+ break;
+
+ case OCLASS_COMPRESSION_OPTIONS:
+ appendStringInfoString(&buffer, "compression options");
+ break;
+
/*
* There's intentionally no default: case here; we want the
* compiler to warn if a new OCLASS hasn't been handled above.
@@ -4160,6 +4228,30 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case OCLASS_COMPRESSION_METHOD:
+ {
+ char *cmname = get_compression_method_name(object->objectId);
+
+ if (!cmname)
+ elog(ERROR, "cache lookup failed for compression method %u",
+ object->objectId);
+ appendStringInfoString(&buffer, quote_identifier(cmname));
+ if (objname)
+ *objname = list_make1(cmname);
+ else
+ pfree(cmname);
+ break;
+ }
+
+ case OCLASS_COMPRESSION_OPTIONS:
+ {
+ appendStringInfo(&buffer, "%u",
+ object->objectId);
+ if (objname)
+ *objname = list_make1(psprintf("%u", object->objectId));
+ break;
+ }
+
case OCLASS_CONSTRAINT:
{
HeapTuple conTup;
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index e02d312008..531a820464 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -22,6 +22,7 @@
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 4a6c99e090..e23abf64f1 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -13,8 +13,8 @@ top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
- collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
- dbcommands.o define.o discard.o dropcmds.o \
+ collationcmds.o compressioncmds.o constraint.o conversioncmds.o copy.o \
+ createas.o dbcommands.o define.o discard.o dropcmds.o \
event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o matview.o operatorcmds.o opclasscmds.o \
policy.o portalcmds.o prepare.o proclang.o publicationcmds.o \
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 4f8147907c..4f18e4083f 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -385,6 +385,7 @@ ExecRenameStmt(RenameStmt *stmt)
case OBJECT_TSTEMPLATE:
case OBJECT_PUBLICATION:
case OBJECT_SUBSCRIPTION:
+ case OBJECT_COMPRESSION_METHOD:
{
ObjectAddress address;
Relation catalog;
@@ -500,6 +501,7 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
case OBJECT_TSDICTIONARY:
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
+ case OBJECT_COMPRESSION_METHOD:
{
Relation catalog;
Relation relation;
@@ -627,6 +629,8 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_COMPRESSION_METHOD:
+ case OCLASS_COMPRESSION_OPTIONS:
/* ignore object types that don't have schema-qualified names */
break;
diff --git a/src/backend/commands/compressioncmds.c b/src/backend/commands/compressioncmds.c
new file mode 100644
index 0000000000..ad51ee759f
--- /dev/null
+++ b/src/backend/commands/compressioncmds.c
@@ -0,0 +1,479 @@
+/*-------------------------------------------------------------------------
+ *
+ * compressioncmds.c
+ * Routines for SQL commands that manipulate compression methods.
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/commands/compressioncmds.c
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "miscadmin.h"
+
+#include "access/compression.h"
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/reloptions.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "parser/parse_func.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+static CompressionMethodRoutine * get_compression_method_routine(Oid cmhandler, Oid typeid);
+
+/*
+ * Convert a handler function name to an Oid. If the return type of the
+ * function doesn't match the given AM type, an error is raised.
+ *
+ * This function either return valid function Oid or throw an error.
+ */
+static Oid
+LookupCompressionHandlerFunc(List *handlerName)
+{
+ static const Oid funcargtypes[1] = {INTERNALOID};
+ Oid handlerOid;
+
+ if (handlerName == NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("handler function is not specified")));
+
+ /* handlers have one argument of type internal */
+ handlerOid = LookupFuncName(handlerName, 1, funcargtypes, false);
+
+ /* check that handler has the correct return type */
+ if (get_func_rettype(handlerOid) != COMPRESSION_HANDLEROID)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("function %s must return type %s",
+ NameListToString(handlerName),
+ "compression_handler")));
+
+ return handlerOid;
+}
+
+static ObjectAddress
+CreateCompressionMethod(char *cmName, List *handlerName)
+{
+ Relation rel;
+ ObjectAddress myself;
+ ObjectAddress referenced;
+ Oid cmoid;
+ Oid cmhandler;
+ bool nulls[Natts_pg_compression];
+ Datum values[Natts_pg_compression];
+ HeapTuple tup;
+
+ rel = heap_open(CompressionMethodRelationId, RowExclusiveLock);
+
+ /* Check if name is used */
+ cmoid = GetSysCacheOid1(COMPRESSIONMETHODNAME, CStringGetDatum(cmName));
+ if (OidIsValid(cmoid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("compression method \"%s\" already exists",
+ cmName)));
+
+ /*
+ * Get the handler function oid and compression method routine
+ */
+ cmhandler = LookupCompressionHandlerFunc(handlerName);
+
+ /*
+ * Insert tuple into pg_compression.
+ */
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+
+ values[Anum_pg_compression_cmname - 1] =
+ DirectFunctionCall1(namein, CStringGetDatum(cmName));
+ values[Anum_pg_compression_cmhandler - 1] = ObjectIdGetDatum(cmhandler);
+
+ tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+
+ cmoid = CatalogTupleInsert(rel, tup);
+ heap_freetuple(tup);
+
+ ObjectAddressSet(myself, CompressionMethodRelationId, cmoid);
+
+ /* Record dependency on handler function */
+ ObjectAddressSet(referenced, ProcedureRelationId, cmhandler);
+
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ recordDependencyOnCurrentExtension(&myself, false);
+
+ heap_close(rel, RowExclusiveLock);
+
+ return myself;
+}
+
+ObjectAddress
+DefineCompressionMethod(List *names, List *parameters)
+{
+ char *cmName;
+ ListCell *pl;
+ DefElem *handlerEl = NULL;
+
+ if (list_length(names) != 1)
+ elog(ERROR, "compression method name cannot be qualified");
+
+ cmName = strVal(linitial(names));
+
+ /* Must be super user */
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to create compression method \"%s\"",
+ cmName),
+ errhint("Must be superuser to create an compression method.")));
+
+ foreach(pl, parameters)
+ {
+ DefElem *defel = (DefElem *) lfirst(pl);
+ DefElem **defelp;
+
+ if (pg_strcasecmp(defel->defname, "handler") == 0)
+ defelp = &handlerEl;
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("compression method attribute \"%s\" not recognized",
+ defel->defname)));
+ break;
+ }
+
+ *defelp = defel;
+ }
+
+ if (!handlerEl)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("compression method handler is not specified")));
+
+ return CreateCompressionMethod(cmName, (List *) handlerEl->arg);
+}
+
+void
+RemoveCompressionMethodById(Oid cmOid)
+{
+ Relation relation;
+ HeapTuple tup;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to drop compression methods")));
+
+ relation = heap_open(CompressionMethodRelationId, RowExclusiveLock);
+
+ tup = SearchSysCache1(COMPRESSIONMETHODOID, ObjectIdGetDatum(cmOid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for compression method %u", cmOid);
+
+ CatalogTupleDelete(relation, &tup->t_self);
+
+ ReleaseSysCache(tup);
+
+ heap_close(relation, RowExclusiveLock);
+}
+
+/*
+ * Create new record in pg_compression_opt
+ */
+Oid
+CreateCompressionOptions(Form_pg_attribute attr, Oid cmid, List *options)
+{
+ Relation rel;
+ HeapTuple tup,
+ newtup;
+ Oid cmoptoid;
+ Datum values[Natts_pg_compression_opt];
+ bool nulls[Natts_pg_compression_opt];
+
+ ObjectAddress myself,
+ ref1,
+ ref2,
+ ref3;
+
+ CompressionMethodRoutine *routine;
+ Form_pg_compression cmform;
+
+ /* Initialize buffers for new tuple values */
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+
+ /* Get handler function OID for the compression method */
+ tup = SearchSysCache1(COMPRESSIONMETHODOID, ObjectIdGetDatum(cmid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for compression method %u", cmid);
+ cmform = (Form_pg_compression) GETSTRUCT(tup);
+ routine = get_compression_method_routine(cmform->cmhandler, attr->atttypid);
+
+ if (routine->configure && options != NIL)
+ routine->configure(attr, options);
+
+ rel = heap_open(CompressionOptRelationId, RowExclusiveLock);
+
+ cmoptoid = GetNewOidWithIndex(rel, CompressionOptionsOidIndexId,
+ Anum_pg_compression_opt_cmoptoid);
+ values[Anum_pg_compression_opt_cmoptoid - 1] = ObjectIdGetDatum(cmoptoid);
+ values[Anum_pg_compression_opt_cmname - 1] = NameGetDatum(&cmform->cmname);
+ values[Anum_pg_compression_opt_cmhandler - 1] = ObjectIdGetDatum(cmform->cmhandler);
+
+ if (options)
+ values[Anum_pg_compression_opt_cmoptions - 1] = optionListToArray(options);
+ else
+ nulls[Anum_pg_compression_opt_cmoptions - 1] = true;
+
+ newtup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+ CatalogTupleInsert(rel, newtup);
+ heap_freetuple(newtup);
+
+ ReleaseSysCache(tup);
+
+ ObjectAddressSet(myself, CompressionOptRelationId, cmoptoid);
+ ObjectAddressSet(ref1, ProcedureRelationId, cmform->cmhandler);
+ ObjectAddressSubSet(ref2, RelationRelationId, attr->attrelid, attr->attnum);
+ ObjectAddressSet(ref3, CompressionMethodRelationId, cmid);
+
+ recordDependencyOn(&myself, &ref1, DEPENDENCY_NORMAL);
+ recordDependencyOn(&ref2, &myself, DEPENDENCY_NORMAL);
+ recordDependencyOn(&myself, &ref3, DEPENDENCY_NORMAL);
+ recordDependencyOnCurrentExtension(&myself, false);
+ heap_close(rel, RowExclusiveLock);
+
+ return cmoptoid;
+}
+
+void
+RemoveCompressionOptionsById(Oid cmoptoid)
+{
+ Relation relation;
+ HeapTuple tup;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to drop compression options")));
+
+ relation = heap_open(CompressionOptRelationId, RowExclusiveLock);
+
+ tup = SearchSysCache1(COMPRESSIONOPTIONSOID, ObjectIdGetDatum(cmoptoid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+
+ CatalogTupleDelete(relation, &tup->t_self);
+ ReleaseSysCache(tup);
+ heap_close(relation, RowExclusiveLock);
+}
+
+ColumnCompression *
+GetColumnCompressionForAttribute(Form_pg_attribute att)
+{
+ HeapTuple tuple;
+ Form_pg_compression_opt cmoptform;
+ ColumnCompression *compression = makeNode(ColumnCompression);
+
+ /* Get handler function OID for the compression method */
+ tuple = SearchSysCache1(COMPRESSIONOPTIONSOID,
+ ObjectIdGetDatum(att->attcompression));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for compression options %u",
+ att->attcompression);
+
+ cmoptform = (Form_pg_compression_opt) GETSTRUCT(tuple);
+ compression->methodName = pstrdup(NameStr(cmoptform->cmname));
+ compression->options = GetCompressionOptionsList(att->attcompression);
+ ReleaseSysCache(tuple);
+
+ return compression;
+}
+
+void
+CheckCompressionMismatch(ColumnCompression * c1, ColumnCompression * c2,
+ const char *attributeName)
+{
+ if (strcmp(c1->methodName, c2->methodName))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("column \"%s\" has a compression method conflict",
+ attributeName),
+ errdetail("%s versus %s", c1->methodName, c2->methodName)));
+
+ if (!equal(c1->options, c2->options))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("column \"%s\" has a compression options conflict",
+ attributeName),
+ errdetail("(%s) versus (%s)",
+ formatRelOptions(c1->options),
+ formatRelOptions(c2->options))));
+}
+
+/*
+ * get_compression_method_oid
+ *
+ * If missing_ok is false, throw an error if compression method not found.
+ * If missing_ok is true, just return InvalidOid.
+ */
+Oid
+get_compression_method_oid(const char *cmname, bool missing_ok)
+{
+ HeapTuple tup;
+ Oid oid = InvalidOid;
+
+ tup = SearchSysCache1(COMPRESSIONMETHODNAME, CStringGetDatum(cmname));
+ if (HeapTupleIsValid(tup))
+ {
+ oid = HeapTupleGetOid(tup);
+ ReleaseSysCache(tup);
+ }
+
+ if (!OidIsValid(oid) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("compression method \"%s\" does not exist", cmname)));
+
+ return oid;
+}
+
+/*
+ * get_compression_method_name
+ *
+ * given an compression method OID, look up its name.
+ */
+char *
+get_compression_method_name(Oid cmOid)
+{
+ HeapTuple tup;
+ char *result = NULL;
+
+ tup = SearchSysCache1(COMPRESSIONMETHODOID, ObjectIdGetDatum(cmOid));
+ if (HeapTupleIsValid(tup))
+ {
+ Form_pg_compression cmform = (Form_pg_compression) GETSTRUCT(tup);
+
+ result = pstrdup(NameStr(cmform->cmname));
+ ReleaseSysCache(tup);
+ }
+ return result;
+}
+
+/*
+ * get_compression_method_name_for_opt
+ *
+ * given an compression options OID, look up a name for used compression method.
+ */
+char *
+get_compression_method_name_for_opt(Oid cmoptoid)
+{
+ HeapTuple tup;
+ char *result = NULL;
+ Form_pg_compression_opt cmoptform;
+
+ tup = SearchSysCache1(COMPRESSIONOPTIONSOID, ObjectIdGetDatum(cmoptoid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+
+ cmoptform = (Form_pg_compression_opt) GETSTRUCT(tup);
+ result = pstrdup(NameStr(cmoptform->cmname));
+ ReleaseSysCache(tup);
+
+ return result;
+}
+
+/* get_compression_options */
+List *
+GetCompressionOptionsList(Oid cmoptoid)
+{
+ HeapTuple tuple;
+ List *result = NULL;
+ bool isnull;
+ Datum cmoptions;
+
+ /* Get handler function OID for the compression method */
+ tuple = SearchSysCache1(COMPRESSIONOPTIONSOID, ObjectIdGetDatum(cmoptoid));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+
+ cmoptions = SysCacheGetAttr(COMPRESSIONOPTIONSOID, tuple,
+ Anum_pg_compression_opt_cmoptions, &isnull);
+
+ if (!isnull)
+ result = untransformRelOptions(cmoptions);
+
+ ReleaseSysCache(tuple);
+ return result;
+}
+
+CompressionMethodRoutine *
+GetCompressionRoutine(Oid cmoptoid)
+{
+ HeapTuple tuple;
+ Form_pg_compression_opt cmopt;
+ regproc cmhandler;
+
+ /* Get handler function OID for the compression method */
+ tuple = SearchSysCache1(COMPRESSIONOPTIONSOID, ObjectIdGetDatum(cmoptoid));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+
+ cmopt = (Form_pg_compression_opt) GETSTRUCT(tuple);
+ cmhandler = cmopt->cmhandler;
+
+ /* Complain if handler OID is invalid */
+ if (!RegProcedureIsValid(cmhandler))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("could not find compression method handler for compression options %u",
+ cmoptoid)));
+
+ ReleaseSysCache(tuple);
+
+ /* And finally, call the handler function to get the API struct. */
+ return get_compression_method_routine(cmhandler, InvalidOid);
+}
+
+/*
+ * Call the specified compression method handler
+ * routine to get its CompressionMethodRoutine struct,
+ * which will be palloc'd in the caller's context.
+ */
+static CompressionMethodRoutine *
+get_compression_method_routine(Oid cmhandler, Oid typeid)
+{
+ Datum datum;
+ CompressionMethodRoutine *routine;
+ CompressionMethodOpArgs opargs;
+
+ opargs.typeid = typeid;
+ opargs.cmhanderid = cmhandler;
+
+ datum = OidFunctionCall1(cmhandler, PointerGetDatum(&opargs));
+ routine = (CompressionMethodRoutine *) DatumGetPointer(datum);
+
+ if (routine == NULL || !IsA(routine, CompressionMethodRoutine))
+ elog(ERROR, "compression method handler function %u "
+ "did not return an CompressionMethodRoutine struct",
+ cmhandler);
+
+ return routine;
+}
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 99ad7a9ccf..8ed9c7f454 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2780,8 +2780,8 @@ CopyFrom(CopyState cstate)
List *recheckIndexes = NIL;
/* OK, store the tuple and create index entries for it */
- heap_insert(resultRelInfo->ri_RelationDesc, tuple, mycid,
- hi_options, bistate);
+ heap_insert(resultRelInfo->ri_RelationDesc, tuple,
+ mycid, hi_options, bistate);
if (resultRelInfo->ri_NumIndices > 0)
recheckIndexes = ExecInsertIndexTuples(slot,
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 4d77411a68..aba2087839 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -112,7 +112,8 @@ create_ctas_internal(List *attrList, IntoClause *into)
* Create the relation. (This will error out if there's an existing view,
* so we don't need more code to complain if "replace" is false.)
*/
- intoRelationAddr = DefineRelation(create, relkind, InvalidOid, NULL, NULL);
+ intoRelationAddr = DefineRelation(create, relkind, InvalidOid, NULL, NULL,
+ NULL);
/*
* If necessary, create a TOAST table for the target table. Note that
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index 2b30677d6f..8611db22ec 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -275,6 +275,10 @@ does_not_exist_skipping(ObjectType objtype, Node *object)
name = NameListToString(castNode(List, object));
}
break;
+ case OBJECT_COMPRESSION_METHOD:
+ msg = gettext_noop("compression method \"%s\" does not exist, skipping");
+ name = strVal((Value *) object);
+ break;
case OBJECT_CONVERSION:
if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
{
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index fa7d0d015a..a2eee78a64 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -91,6 +91,7 @@ static event_trigger_support_data event_trigger_support[] = {
{"CAST", true},
{"CONSTRAINT", true},
{"COLLATION", true},
+ {"COMPRESSION METHOD", true},
{"CONVERSION", true},
{"DATABASE", false},
{"DOMAIN", true},
@@ -1085,6 +1086,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
case OBJECT_CAST:
case OBJECT_COLUMN:
case OBJECT_COLLATION:
+ case OBJECT_COMPRESSION_METHOD:
case OBJECT_CONVERSION:
case OBJECT_DEFACL:
case OBJECT_DEFAULT:
@@ -1144,6 +1146,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
case OCLASS_ROLE:
+ case OCLASS_COMPRESSION_OPTIONS:
/* no support for global objects */
return false;
case OCLASS_EVENT_TRIGGER:
@@ -1183,6 +1186,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_COMPRESSION_METHOD:
return true;
/*
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index 9ad991507f..b840309d03 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -61,7 +61,7 @@ static void import_error_callback(void *arg);
* processing, hence any validation should be done before this
* conversion.
*/
-static Datum
+Datum
optionListToArray(List *options)
{
ArrayBuildState *astate = NULL;
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 5e1b0fe289..eb9f72dcba 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -212,7 +212,8 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
stmt->tablespacename = NULL;
stmt->if_not_exists = seq->if_not_exists;
- address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL, NULL);
+ address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL, NULL,
+ NULL);
seqoid = address.objectId;
Assert(seqoid != InvalidOid);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 9c66aa75ed..67cb187180 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -14,6 +14,7 @@
*/
#include "postgres.h"
+#include "access/compression.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "access/multixact.h"
@@ -33,6 +34,8 @@
#include "catalog/partition.h"
#include "catalog/pg_am.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_depend.h"
@@ -41,6 +44,7 @@
#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
+#include "catalog/pg_proc.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
@@ -90,6 +94,7 @@
#include "storage/smgr.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+#include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
@@ -459,6 +464,8 @@ static void ATExecGenericOptions(Relation rel, List *options);
static void ATExecEnableRowSecurity(Relation rel);
static void ATExecDisableRowSecurity(Relation rel);
static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
+static void ATExecAlterColumnCompression(AlteredTableInfo *tab, Relation rel,
+ const char *column, ColumnCompression * compression, LOCKMODE lockmode);
static void copy_relation_data(SMgrRelation rel, SMgrRelation dst,
ForkNumber forkNum, char relpersistence);
@@ -503,7 +510,8 @@ static ObjectAddress ATExecDetachPartition(Relation rel, RangeVar *name);
*/
ObjectAddress
DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
- ObjectAddress *typaddress, const char *queryString)
+ ObjectAddress *typaddress, const char *queryString,
+ Node **pAlterStmt)
{
char relname[NAMEDATALEN];
Oid namespaceId;
@@ -523,6 +531,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
Oid ofTypeId;
ObjectAddress address;
+ AlterTableStmt *alterStmt = NULL;
/*
* Truncate relname to appropriate length (probably a waste of time, as
@@ -724,6 +733,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
if (colDef->identity)
attr->attidentity = colDef->identity;
+
+ transformColumnCompression(colDef, stmt->relation, &alterStmt);
}
/*
@@ -921,6 +932,11 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
*/
relation_close(rel, NoLock);
+ if (pAlterStmt)
+ *pAlterStmt = (Node *) alterStmt;
+ else
+ Assert(!alterStmt);
+
return address;
}
@@ -1610,6 +1626,7 @@ storage_name(char c)
}
}
+
/*----------
* MergeAttributes
* Returns new schema given initial schema and superclasses.
@@ -1944,6 +1961,19 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
storage_name(def->storage),
storage_name(attribute->attstorage))));
+ if (OidIsValid(attribute->attcompression))
+ {
+ ColumnCompression *compression =
+ GetColumnCompressionForAttribute(attribute);
+
+ if (!def->compression)
+ def->compression = compression;
+ else
+ CheckCompressionMismatch(def->compression,
+ compression,
+ attributeName);
+ }
+
def->inhcount++;
/* Merge of NOT NULL constraints = OR 'em together */
def->is_not_null |= attribute->attnotnull;
@@ -1971,6 +2001,9 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
def->collOid = attribute->attcollation;
def->constraints = NIL;
def->location = -1;
+ if (OidIsValid(attribute->attcompression))
+ def->compression =
+ GetColumnCompressionForAttribute(attribute);
inhSchema = lappend(inhSchema, def);
newattno[parent_attno - 1] = ++child_attno;
}
@@ -2180,6 +2213,13 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
storage_name(def->storage),
storage_name(newdef->storage))));
+ if (!def->compression)
+ def->compression = newdef->compression;
+ else if (newdef->compression)
+ CheckCompressionMismatch(def->compression,
+ newdef->compression,
+ attributeName);
+
/* Mark the column as locally defined */
def->is_local = true;
/* Merge of NOT NULL constraints = OR 'em together */
@@ -3279,6 +3319,7 @@ AlterTableGetLockLevel(List *cmds)
*/
case AT_GenericOptions:
case AT_AlterColumnGenericOptions:
+ case AT_AlterColumnCompression:
cmd_lockmode = AccessExclusiveLock;
break;
@@ -3770,6 +3811,12 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* No command-specific prep needed */
pass = AT_PASS_MISC;
break;
+ case AT_AlterColumnCompression:
+ ATSimplePermissions(rel, ATT_TABLE);
+ /* FIXME This command never recurses */
+ /* No command-specific prep needed */
+ pass = AT_PASS_MISC;
+ break;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
@@ -4118,6 +4165,11 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
case AT_DetachPartition:
ATExecDetachPartition(rel, ((PartitionCmd *) cmd->def)->name);
break;
+ case AT_AlterColumnCompression:
+ ATExecAlterColumnCompression(tab, rel, cmd->name,
+ (ColumnCompression *) cmd->def,
+ lockmode);
+ break;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
@@ -5338,6 +5390,8 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
attribute.attislocal = colDef->is_local;
attribute.attinhcount = colDef->inhcount;
attribute.attcollation = collOid;
+ attribute.attcompression = InvalidOid;
+
/* attribute.attacl is handled by InsertPgAttributeTuple */
ReleaseSysCache(typeTuple);
@@ -6438,6 +6492,11 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE loc
errmsg("cannot alter system column \"%s\"",
colName)));
+ if (attrtuple->attcompression && newstorage != 'x' && newstorage != 'm')
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("compressed columns can only have storage MAIN or EXTENDED")));
+
/*
* safety check: do not allow toasted storage modes unless column datatype
* is TOAST-aware.
@@ -9358,6 +9417,8 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_COMPRESSION_METHOD:
+ case OCLASS_COMPRESSION_OPTIONS:
/*
* We don't expect any of these sorts of objects to depend on
@@ -9407,7 +9468,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
if (!(foundDep->refclassid == TypeRelationId &&
foundDep->refobjid == attTup->atttypid) &&
!(foundDep->refclassid == CollationRelationId &&
- foundDep->refobjid == attTup->attcollation))
+ foundDep->refobjid == attTup->attcollation) &&
+ !(foundDep->refclassid == CompressionMethodRelationId &&
+ foundDep->refobjid == attTup->attcompression))
elog(ERROR, "found unexpected dependency for column");
CatalogTupleDelete(depRel, &depTup->t_self);
@@ -9429,6 +9492,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
attTup->attbyval = tform->typbyval;
attTup->attalign = tform->typalign;
attTup->attstorage = tform->typstorage;
+ attTup->attcompression = InvalidOid;
ReleaseSysCache(typeTuple);
@@ -12481,6 +12545,83 @@ ATExecGenericOptions(Relation rel, List *options)
heap_freetuple(tuple);
}
+static void
+ATExecAlterColumnCompression(AlteredTableInfo *tab,
+ Relation rel,
+ const char *column,
+ ColumnCompression * compression,
+ LOCKMODE lockmode)
+{
+ Relation attrel;
+ HeapTuple atttuple;
+ Form_pg_attribute atttableform;
+ AttrNumber attnum;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_attribute];
+ bool nulls[Natts_pg_attribute];
+ bool replace[Natts_pg_attribute];
+
+ attrel = heap_open(AttributeRelationId, RowExclusiveLock);
+
+ atttuple = SearchSysCacheAttName(RelationGetRelid(rel), column);
+ if (!HeapTupleIsValid(atttuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("column \"%s\" of relation \"%s\" does not exist",
+ column, RelationGetRelationName(rel))));
+
+ /* Prevent them from altering a system attribute */
+ atttableform = (Form_pg_attribute) GETSTRUCT(atttuple);
+ attnum = atttableform->attnum;
+ if (attnum <= 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot alter system column \"%s\"", column)));
+
+ if (atttableform->attstorage != 'x' && atttableform->attstorage != 'm')
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("storage for \"%s\" should be MAIN or EXTENDED", column)));
+
+ /* Initialize buffers for new tuple values */
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+ memset(replace, false, sizeof(replace));
+
+ if (compression->methodName)
+ {
+ /* SET COMPRESSED */
+ Oid cmid,
+ cmoptoid;
+
+ cmid = get_compression_method_oid(compression->methodName, false);
+ cmoptoid = CreateCompressionOptions(atttableform, cmid, compression->options);
+
+ values[Anum_pg_attribute_attcompression - 1] = ObjectIdGetDatum(cmoptoid);
+ replace[Anum_pg_attribute_attcompression - 1] = true;
+
+ }
+ else
+ {
+ /* SET NOT COMPRESSED */
+ values[Anum_pg_attribute_attcompression - 1] = ObjectIdGetDatum(InvalidOid);
+ replace[Anum_pg_attribute_attcompression - 1] = true;
+ }
+
+ newtuple = heap_modify_tuple(atttuple, RelationGetDescr(attrel),
+ values, nulls, replace);
+ CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
+ heap_freetuple(newtuple);
+
+ InvokeObjectPostAlterHook(RelationRelationId,
+ RelationGetRelid(rel),
+ atttableform->attnum);
+
+ ReleaseSysCache(atttuple);
+ heap_close(attrel, RowExclusiveLock);
+}
+
+
/*
* Preparation phase for SET LOGGED/UNLOGGED
*
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index f86af4c054..be4a6c5f38 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -40,6 +40,7 @@
#include "catalog/pg_am.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_depend.h"
@@ -2182,7 +2183,7 @@ DefineCompositeType(RangeVar *typevar, List *coldeflist)
* Finally create the relation. This also creates the type.
*/
DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, &address,
- NULL);
+ NULL, NULL);
return address;
}
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index c1e80e61d4..cc0182075e 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -251,7 +251,7 @@ DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
* false).
*/
address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid, NULL,
- NULL);
+ NULL, NULL);
Assert(address.objectId != InvalidOid);
/* Make the new view relation visible */
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index 5dfc49deb9..3a80e997a9 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -302,6 +302,9 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc
var->vartypmod != -1))
return false; /* type mismatch */
+ if (OidIsValid(att_tup->attcompression))
+ return false;
+
tlist_item = lnext(tlist_item);
}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index b8bd4004e1..95cdb8029b 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2804,6 +2804,7 @@ _copyColumnDef(const ColumnDef *from)
COPY_STRING_FIELD(colname);
COPY_NODE_FIELD(typeName);
+ COPY_NODE_FIELD(compression);
COPY_SCALAR_FIELD(inhcount);
COPY_SCALAR_FIELD(is_local);
COPY_SCALAR_FIELD(is_not_null);
@@ -2822,6 +2823,18 @@ _copyColumnDef(const ColumnDef *from)
return newnode;
}
+static ColumnCompression *
+_copyColumnCompression(const ColumnCompression * from)
+{
+ ColumnCompression *newnode = makeNode(ColumnCompression);
+
+ COPY_STRING_FIELD(methodName);
+ COPY_NODE_FIELD(options);
+
+ return newnode;
+}
+
+
static Constraint *
_copyConstraint(const Constraint *from)
{
@@ -5471,6 +5484,9 @@ copyObjectImpl(const void *from)
case T_ColumnDef:
retval = _copyColumnDef(from);
break;
+ case T_ColumnCompression:
+ retval = _copyColumnCompression(from);
+ break;
case T_Constraint:
retval = _copyConstraint(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 2a83da9aa9..d618cc637e 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2544,6 +2544,7 @@ _equalColumnDef(const ColumnDef *a, const ColumnDef *b)
{
COMPARE_STRING_FIELD(colname);
COMPARE_NODE_FIELD(typeName);
+ COMPARE_NODE_FIELD(compression);
COMPARE_SCALAR_FIELD(inhcount);
COMPARE_SCALAR_FIELD(is_local);
COMPARE_SCALAR_FIELD(is_not_null);
@@ -2562,6 +2563,15 @@ _equalColumnDef(const ColumnDef *a, const ColumnDef *b)
return true;
}
+static bool
+_equalColumnCompression(const ColumnCompression * a, const ColumnCompression * b)
+{
+ COMPARE_STRING_FIELD(methodName);
+ COMPARE_NODE_FIELD(options);
+
+ return true;
+}
+
static bool
_equalConstraint(const Constraint *a, const Constraint *b)
{
@@ -3615,6 +3625,9 @@ equal(const void *a, const void *b)
case T_ColumnDef:
retval = _equalColumnDef(a, b);
break;
+ case T_ColumnCompression:
+ retval = _equalColumnCompression(a, b);
+ break;
case T_Constraint:
retval = _equalConstraint(a, b);
break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index c2a93b2d4c..81e334a1d3 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -3623,6 +3623,8 @@ raw_expression_tree_walker(Node *node,
if (walker(coldef->typeName, context))
return true;
+ if (walker(coldef->compression, context))
+ return true;
if (walker(coldef->raw_default, context))
return true;
if (walker(coldef->collClause, context))
@@ -3630,6 +3632,14 @@ raw_expression_tree_walker(Node *node,
/* for now, constraints are ignored */
}
break;
+ case T_ColumnCompression:
+ {
+ ColumnCompression *colcmp = (ColumnCompression *) node;
+
+ if (walker(colcmp->options, context))
+ return true;
+ }
+ break;
case T_IndexElem:
{
IndexElem *indelem = (IndexElem *) node;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index dc35df9e4f..6bfd7ed0ee 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2796,6 +2796,7 @@ _outColumnDef(StringInfo str, const ColumnDef *node)
WRITE_STRING_FIELD(colname);
WRITE_NODE_FIELD(typeName);
+ WRITE_NODE_FIELD(compression);
WRITE_INT_FIELD(inhcount);
WRITE_BOOL_FIELD(is_local);
WRITE_BOOL_FIELD(is_not_null);
@@ -2812,6 +2813,15 @@ _outColumnDef(StringInfo str, const ColumnDef *node)
WRITE_LOCATION_FIELD(location);
}
+static void
+_outColumnCompression(StringInfo str, const ColumnCompression * node)
+{
+ WRITE_NODE_TYPE("COLUMNCOMPRESSION");
+
+ WRITE_STRING_FIELD(methodName);
+ WRITE_NODE_FIELD(options);
+}
+
static void
_outTypeName(StringInfo str, const TypeName *node)
{
@@ -4103,6 +4113,9 @@ outNode(StringInfo str, const void *obj)
case T_ColumnDef:
_outColumnDef(str, obj);
break;
+ case T_ColumnCompression:
+ _outColumnCompression(str, obj);
+ break;
case T_TypeName:
_outTypeName(str, obj);
break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c301ca465d..122d3de4d6 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -397,6 +397,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
transform_element_list transform_type_list
TriggerTransitions TriggerReferencing
publication_name_list
+ optCompressionParameters
vacuum_relation_list opt_vacuum_relation_list
%type <list> group_by_list
@@ -582,6 +583,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <list> hash_partbound partbound_datum_list range_datum_list
%type <defelt> hash_partbound_elem
+%type <node> columnCompression optColumnCompression compressedClause
+
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
* They must be listed first so that their numeric codes do not depend on
@@ -614,9 +617,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
CLUSTER COALESCE COLLATE COLLATION COLUMN COLUMNS COMMENT COMMENTS COMMIT
- COMMITTED CONCURRENTLY CONFIGURATION CONFLICT CONNECTION CONSTRAINT
- CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE
- CROSS CSV CUBE CURRENT_P
+ COMMITTED COMPRESSED COMPRESSION CONCURRENTLY CONFIGURATION CONFLICT
+ CONNECTION CONSTRAINT CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY
+ COST CREATE CROSS CSV CUBE CURRENT_P
CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA
CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
@@ -2168,6 +2171,15 @@ alter_table_cmd:
n->missing_ok = true;
$$ = (Node *)n;
}
+ /* ALTER TABLE <name> ALTER [COLUMN] <colname> SET (NOT COMPRESSED | COMPRESSED <cm> [WITH (<options>)]) */
+ | ALTER opt_column ColId SET columnCompression
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_AlterColumnCompression;
+ n->name = $3;
+ n->def = $5;
+ $$ = (Node *)n;
+ }
/* ALTER TABLE <name> DROP [COLUMN] IF EXISTS <colname> [RESTRICT|CASCADE] */
| DROP opt_column IF_P EXISTS ColId opt_drop_behavior
{
@@ -3332,11 +3344,12 @@ TypedTableElement:
| TableConstraint { $$ = $1; }
;
-columnDef: ColId Typename create_generic_options ColQualList
+columnDef: ColId Typename optColumnCompression create_generic_options ColQualList
{
ColumnDef *n = makeNode(ColumnDef);
n->colname = $1;
n->typeName = $2;
+ n->compression = (ColumnCompression *) $3;
n->inhcount = 0;
n->is_local = true;
n->is_not_null = false;
@@ -3346,8 +3359,8 @@ columnDef: ColId Typename create_generic_options ColQualList
n->raw_default = NULL;
n->cooked_default = NULL;
n->collOid = InvalidOid;
- n->fdwoptions = $3;
- SplitColQualList($4, &n->constraints, &n->collClause,
+ n->fdwoptions = $4;
+ SplitColQualList($5, &n->constraints, &n->collClause,
yyscanner);
n->location = @1;
$$ = (Node *)n;
@@ -3394,6 +3407,37 @@ columnOptions: ColId ColQualList
}
;
+compressedClause:
+ COMPRESSED name optCompressionParameters
+ {
+ ColumnCompression *n = makeNode(ColumnCompression);
+ n->methodName = $2;
+ n->options = (List *) $3;
+ $$ = (Node *) n;
+ }
+ ;
+
+columnCompression:
+ compressedClause |
+ NOT COMPRESSED
+ {
+ ColumnCompression *n = makeNode(ColumnCompression);
+ n->methodName = NULL;
+ n->options = NIL;
+ $$ = (Node *) n;
+ }
+ ;
+
+optColumnCompression:
+ compressedClause /* FIXME shift/reduce conflict on NOT COMPRESSED/NOT NULL */
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
+optCompressionParameters:
+ WITH '(' generic_option_list ')' { $$ = $3; }
+ | /*EMPTY*/ { $$ = NIL; }
+ ;
+
ColQualList:
ColQualList ColConstraint { $$ = lappend($1, $2); }
| /*EMPTY*/ { $$ = NIL; }
@@ -5754,6 +5798,15 @@ DefineStmt:
n->if_not_exists = true;
$$ = (Node *)n;
}
+ | CREATE COMPRESSION METHOD any_name HANDLER handler_name
+ {
+ DefineStmt *n = makeNode(DefineStmt);
+ n->kind = OBJECT_COMPRESSION_METHOD;
+ n->args = NIL;
+ n->defnames = $4;
+ n->definition = list_make1(makeDefElem("handler", (Node *) $6, @6));
+ $$ = (Node *) n;
+ }
;
definition: '(' def_list ')' { $$ = $2; }
@@ -6262,6 +6315,7 @@ drop_type_any_name:
/* object types taking name_list */
drop_type_name:
ACCESS METHOD { $$ = OBJECT_ACCESS_METHOD; }
+ | COMPRESSION METHOD { $$ = OBJECT_COMPRESSION_METHOD; }
| EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; }
| EXTENSION { $$ = OBJECT_EXTENSION; }
| FOREIGN DATA_P WRAPPER { $$ = OBJECT_FDW; }
@@ -6325,7 +6379,7 @@ opt_restart_seqs:
* The COMMENT ON statement can take different forms based upon the type of
* the object associated with the comment. The form of the statement is:
*
- * COMMENT ON [ [ ACCESS METHOD | CONVERSION | COLLATION |
+ * COMMENT ON [ [ ACCESS METHOD | COMPRESSION METHOD | CONVERSION | COLLATION |
* DATABASE | DOMAIN |
* EXTENSION | EVENT TRIGGER | FOREIGN DATA WRAPPER |
* FOREIGN TABLE | INDEX | [PROCEDURAL] LANGUAGE |
@@ -6515,6 +6569,7 @@ comment_type_any_name:
/* object types taking name */
comment_type_name:
ACCESS METHOD { $$ = OBJECT_ACCESS_METHOD; }
+ | COMPRESSION METHOD { $$ = OBJECT_COMPRESSION_METHOD; }
| DATABASE { $$ = OBJECT_DATABASE; }
| EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; }
| EXTENSION { $$ = OBJECT_EXTENSION; }
@@ -14704,6 +14759,8 @@ unreserved_keyword:
| COMMENTS
| COMMIT
| COMMITTED
+ | COMPRESSED
+ | COMPRESSION
| CONFIGURATION
| CONFLICT
| CONNECTION
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 8461da490a..167f70965a 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -27,6 +27,7 @@
#include "postgres.h"
#include "access/amapi.h"
+#include "access/compression.h"
#include "access/htup_details.h"
#include "access/reloptions.h"
#include "catalog/dependency.h"
@@ -494,6 +495,33 @@ generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column,
*sname_p = sname;
}
+void
+transformColumnCompression(ColumnDef *column, RangeVar *relation,
+ AlterTableStmt **alterStmt)
+{
+ if (column->compression)
+ {
+ AlterTableCmd *cmd;
+
+ cmd = makeNode(AlterTableCmd);
+ cmd->subtype = AT_AlterColumnCompression;
+ cmd->name = column->colname;
+ cmd->def = (Node *) column->compression;
+ cmd->behavior = DROP_RESTRICT;
+ cmd->missing_ok = false;
+
+ if (!*alterStmt)
+ {
+ *alterStmt = makeNode(AlterTableStmt);
+ (*alterStmt)->relation = relation;
+ (*alterStmt)->relkind = OBJECT_TABLE;
+ (*alterStmt)->cmds = NIL;
+ }
+
+ (*alterStmt)->cmds = lappend((*alterStmt)->cmds, cmd);
+ }
+}
+
/*
* transformColumnDefinition -
* transform a single ColumnDef within CREATE TABLE
@@ -794,6 +822,16 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
cxt->alist = lappend(cxt->alist, stmt);
}
+
+ if (cxt->isalter)
+ {
+ AlterTableStmt *stmt = NULL;
+
+ transformColumnCompression(column, cxt->relation, &stmt);
+
+ if (stmt)
+ cxt->alist = lappend(cxt->alist, stmt);
+ }
}
/*
@@ -1003,6 +1041,10 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
def->collOid = attribute->attcollation;
def->constraints = NIL;
def->location = -1;
+ if (attribute->attcompression)
+ def->compression = GetColumnCompressionForAttribute(attribute);
+ else
+ def->compression = NULL;
/*
* Add to column list
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 82a707af7b..331d133660 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -998,6 +998,7 @@ ProcessUtilitySlow(ParseState *pstate,
foreach(l, stmts)
{
Node *stmt = (Node *) lfirst(l);
+ Node *alterStmt = NULL;
if (IsA(stmt, CreateStmt))
{
@@ -1008,7 +1009,9 @@ ProcessUtilitySlow(ParseState *pstate,
address = DefineRelation((CreateStmt *) stmt,
RELKIND_RELATION,
InvalidOid, NULL,
- queryString);
+ queryString,
+ &alterStmt);
+
EventTriggerCollectSimpleCommand(address,
secondaryObject,
stmt);
@@ -1042,7 +1045,8 @@ ProcessUtilitySlow(ParseState *pstate,
address = DefineRelation((CreateStmt *) stmt,
RELKIND_FOREIGN_TABLE,
InvalidOid, NULL,
- queryString);
+ queryString,
+ &alterStmt);
CreateForeignTable((CreateForeignTableStmt *) stmt,
address.objectId);
EventTriggerCollectSimpleCommand(address,
@@ -1074,6 +1078,9 @@ ProcessUtilitySlow(ParseState *pstate,
NULL);
}
+ if (alterStmt)
+ lappend(stmts, alterStmt);
+
/* Need CCI between commands */
if (lnext(l) != NULL)
CommandCounterIncrement();
@@ -1283,6 +1290,11 @@ ProcessUtilitySlow(ParseState *pstate,
stmt->definition,
stmt->if_not_exists);
break;
+ case OBJECT_COMPRESSION_METHOD:
+ Assert(stmt->args == NIL);
+ address = DefineCompressionMethod(stmt->defnames,
+ stmt->definition);
+ break;
default:
elog(ERROR, "unrecognized define stmt type: %d",
(int) stmt->kind);
@@ -1696,6 +1708,11 @@ ExecDropStmt(DropStmt *stmt, bool isTopLevel)
case OBJECT_FOREIGN_TABLE:
RemoveRelations(stmt);
break;
+ case OBJECT_COMPRESSION_METHOD:
+ if (stmt->behavior == DROP_CASCADE)
+ {
+ /* TODO decompress columns instead of their deletion */
+ }
default:
RemoveObjects(stmt);
break;
@@ -2309,6 +2326,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_STATISTIC_EXT:
tag = "DROP STATISTICS";
break;
+ case OBJECT_COMPRESSION_METHOD:
+ tag = "DROP COMPRESSION METHOD";
+ break;
default:
tag = "???";
}
@@ -2412,6 +2432,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_ACCESS_METHOD:
tag = "CREATE ACCESS METHOD";
break;
+ case OBJECT_COMPRESSION_METHOD:
+ tag = "CREATE COMPRESSION METHOD";
+ break;
default:
tag = "???";
}
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index be793539a3..a5cfbe3d4c 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -418,3 +418,4 @@ PSEUDOTYPE_DUMMY_IO_FUNCS(internal);
PSEUDOTYPE_DUMMY_IO_FUNCS(opaque);
PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement);
PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray);
+PSEUDOTYPE_DUMMY_IO_FUNCS(compression_handler);
diff --git a/src/backend/utils/adt/tsvector.c b/src/backend/utils/adt/tsvector.c
index b0a9217d1e..b6d42399c6 100644
--- a/src/backend/utils/adt/tsvector.c
+++ b/src/backend/utils/adt/tsvector.c
@@ -14,11 +14,14 @@
#include "postgres.h"
+#include "access/compression.h"
+#include "catalog/pg_type.h"
#include "libpq/pqformat.h"
#include "tsearch/ts_locale.h"
#include "tsearch/ts_utils.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
+#include "common/pg_lzcompress.h"
typedef struct
{
@@ -548,3 +551,92 @@ tsvectorrecv(PG_FUNCTION_ARGS)
PG_RETURN_TSVECTOR(vec);
}
+
+/*
+ * Compress tsvector using LZ compression.
+ * Instead of trying to compress whole tsvector we compress only text part
+ * here. This approach gives more compressibility for tsvectors.
+ */
+static struct varlena *
+tsvector_compress(AttributeCompression * ac, const struct varlena *data)
+{
+ char *tmp;
+ int32 valsize = VARSIZE_ANY_EXHDR(data);
+ int32 len = valsize + VARHDRSZ_CUSTOM_COMPRESSED,
+ lenc;
+
+ char *arr = VARDATA(data),
+ *str = STRPTR((TSVector) data);
+ int32 arrsize = str - arr;
+
+ Assert(!VARATT_IS_COMPRESSED(data));
+ tmp = palloc0(len);
+
+ /* we try to compress string part of tsvector first */
+ lenc = pglz_compress(str,
+ valsize - arrsize,
+ tmp + VARHDRSZ_CUSTOM_COMPRESSED + arrsize,
+ PGLZ_strategy_default);
+
+ if (lenc >= 0)
+ {
+ /* tsvector is compressible, copy size and entries to its beginning */
+ memcpy(tmp + VARHDRSZ_CUSTOM_COMPRESSED, arr, arrsize);
+ SET_VARSIZE_COMPRESSED(tmp, arrsize + lenc + VARHDRSZ_CUSTOM_COMPRESSED);
+ return (struct varlena *) tmp;
+ }
+
+ pfree(tmp);
+ return NULL;
+}
+
+static void
+tsvector_configure(Form_pg_attribute attr, List *options)
+{
+ if (options != NIL)
+ elog(ERROR, "the compression method for tsvector doesn't take any options");
+}
+
+static struct varlena *
+tsvector_decompress(AttributeCompression * ac, const struct varlena *data)
+{
+ char *tmp,
+ *raw_data = (char *) data + VARHDRSZ_CUSTOM_COMPRESSED;
+ int32 count,
+ arrsize,
+ len = VARRAWSIZE_4B_C(data) + VARHDRSZ;
+
+ Assert(VARATT_IS_CUSTOM_COMPRESSED(data));
+ tmp = palloc0(len);
+ SET_VARSIZE(tmp, len);
+ count = *((uint32 *) raw_data);
+ arrsize = sizeof(uint32) + count * sizeof(WordEntry);
+ memcpy(VARDATA(tmp), raw_data, arrsize);
+
+ if (pglz_decompress(raw_data + arrsize,
+ VARSIZE(data) - VARHDRSZ_CUSTOM_COMPRESSED - arrsize,
+ VARDATA(tmp) + arrsize,
+ VARRAWSIZE_4B_C(data) - arrsize) < 0)
+ elog(ERROR, "compressed tsvector is corrupted");
+
+ return (struct varlena *) tmp;
+}
+
+Datum
+tsvector_compression_handler(PG_FUNCTION_ARGS)
+{
+ CompressionMethodOpArgs *opargs = (CompressionMethodOpArgs *)
+ PG_GETARG_POINTER(0);
+ CompressionMethodRoutine *cmr = makeNode(CompressionMethodRoutine);
+ Oid typeid = opargs->typeid;
+
+ if (OidIsValid(typeid) && typeid != TSVECTOROID)
+ elog(ERROR, "unexpected type %d for tsvector compression handler", typeid);
+
+ cmr->configure = tsvector_configure;
+ cmr->drop = NULL;
+ cmr->compress = tsvector_compress;
+ cmr->decompress = tsvector_decompress;
+
+ PG_RETURN_POINTER(cmr);
+}
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 0ea2f2bc54..894e3148b9 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -2276,16 +2276,12 @@ getBaseType(Oid typid)
}
/*
- * getBaseTypeAndTypmod
- * If the given type is a domain, return its base type and typmod;
- * otherwise return the type's own OID, and leave *typmod unchanged.
- *
* Note that the "applied typmod" should be -1 for every domain level
* above the bottommost; therefore, if the passed-in typid is indeed
* a domain, *typmod should be -1.
*/
-Oid
-getBaseTypeAndTypmod(Oid typid, int32 *typmod)
+static inline HeapTuple
+getBaseTypeTuple(Oid *typid, int32 *typmod)
{
/*
* We loop to find the bottom base type in a stack of domains.
@@ -2295,24 +2291,33 @@ getBaseTypeAndTypmod(Oid typid, int32 *typmod)
HeapTuple tup;
Form_pg_type typTup;
- tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*typid));
if (!HeapTupleIsValid(tup))
- elog(ERROR, "cache lookup failed for type %u", typid);
+ elog(ERROR, "cache lookup failed for type %u", *typid);
typTup = (Form_pg_type) GETSTRUCT(tup);
if (typTup->typtype != TYPTYPE_DOMAIN)
- {
/* Not a domain, so done */
- ReleaseSysCache(tup);
- break;
- }
+ return tup;
Assert(*typmod == -1);
- typid = typTup->typbasetype;
+ *typid = typTup->typbasetype;
*typmod = typTup->typtypmod;
ReleaseSysCache(tup);
}
+}
+
+/*
+ * getBaseTypeAndTypmod
+ * If the given type is a domain, return its base type and typmod;
+ * otherwise return the type's own OID, and leave *typmod unchanged.
+ */
+Oid
+getBaseTypeAndTypmod(Oid typid, int32 *typmod)
+{
+ HeapTuple tup = getBaseTypeTuple(&typid, typmod);
+ ReleaseSysCache(tup);
return typid;
}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 1908420d82..61f7ccef61 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -30,6 +30,7 @@
#include <fcntl.h>
#include <unistd.h>
+#include "access/compression.h"
#include "access/hash.h"
#include "access/htup_details.h"
#include "access/multixact.h"
@@ -77,6 +78,7 @@
#include "storage/smgr.h"
#include "utils/array.h"
#include "utils/builtins.h"
+#include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
@@ -561,6 +563,7 @@ RelationBuildTupleDesc(Relation relation)
attrdef[ndef].adbin = NULL;
ndef++;
}
+
need--;
if (need == 0)
break;
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 888edbb325..c689f60e47 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -31,6 +31,8 @@
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
@@ -309,6 +311,39 @@ static const struct cachedesc cacheinfo[] = {
},
8
},
+ {CompressionMethodRelationId, /* COMPRESSIONMETHODOID */
+ CompressionMethodOidIndexId,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 8
+ },
+ {CompressionMethodRelationId, /* COMPRESSIONMETHODNAME */
+ CompressionMethodNameIndexId,
+ 1,
+ {
+ Anum_pg_compression_cmname,
+ 0,
+ 0,
+ 0
+ },
+ 8
+ },
+ {CompressionOptRelationId, /* COMPRESSIONOPTIONSOID */
+ CompressionOptionsOidIndexId,
+ 1,
+ {
+ Anum_pg_compression_opt_cmoptoid,
+ 0,
+ 0,
+ 0
+ },
+ 8,
+ },
{ConversionRelationId, /* CONDEFAULT */
ConversionDefaultIndexId,
4,
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 4b47951de1..7ba6d31251 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -54,6 +54,7 @@ static DumpableObject **oprinfoindex;
static DumpableObject **collinfoindex;
static DumpableObject **nspinfoindex;
static DumpableObject **extinfoindex;
+static DumpableObject **cminfoindex;
static int numTables;
static int numTypes;
static int numFuncs;
@@ -61,6 +62,7 @@ static int numOperators;
static int numCollations;
static int numNamespaces;
static int numExtensions;
+static int numCompressionMethods;
/* This is an array of object identities, not actual DumpableObjects */
static ExtensionMemberId *extmembers;
@@ -93,6 +95,8 @@ getSchemaData(Archive *fout, int *numTablesPtr)
NamespaceInfo *nspinfo;
ExtensionInfo *extinfo;
InhInfo *inhinfo;
+ CompressionMethodInfo *cminfo;
+
int numAggregates;
int numInherits;
int numRules;
@@ -289,6 +293,11 @@ getSchemaData(Archive *fout, int *numTablesPtr)
write_msg(NULL, "reading subscriptions\n");
getSubscriptions(fout);
+ if (g_verbose)
+ write_msg(NULL, "reading compression methods\n");
+ cminfo = getCompressionMethods(fout, &numCompressionMethods);
+ cminfoindex = buildIndexArray(cminfo, numCompressionMethods, sizeof(CompressionMethodInfo));
+
*numTablesPtr = numTables;
return tblinfo;
}
@@ -827,6 +836,17 @@ findExtensionByOid(Oid oid)
return (ExtensionInfo *) findObjectByOid(oid, extinfoindex, numExtensions);
}
+/*
+ * findCompressionMethodByOid
+ * finds the entry (in cminfo) of the compression method with the given oid
+ * returns NULL if not found
+ */
+CompressionMethodInfo *
+findCompressionMethodByOid(Oid oid)
+{
+ return (CompressionMethodInfo *) findObjectByOid(oid, cminfoindex,
+ numCompressionMethods);
+}
/*
* setExtensionMembership
diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index ce3100f09d..5c1aaad48e 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -77,6 +77,7 @@ typedef struct _restoreOptions
int no_publications; /* Skip publication entries */
int no_security_labels; /* Skip security label entries */
int no_subscriptions; /* Skip subscription entries */
+ int no_compression_methods; /* Skip compression methods */
int strict_names;
const char *filename;
@@ -121,6 +122,8 @@ typedef struct _restoreOptions
bool *idWanted; /* array showing which dump IDs to emit */
int enable_row_security;
int sequence_data; /* dump sequence data even in schema-only mode */
+ int compression_options_data; /* dump compression options even
+ * in schema-only mode */
int binary_upgrade;
} RestoreOptions;
@@ -149,6 +152,7 @@ typedef struct _dumpOptions
int no_security_labels;
int no_publications;
int no_subscriptions;
+ int no_compression_methods;
int no_synchronized_snapshots;
int no_unlogged_table_data;
int serializable_deferrable;
@@ -170,6 +174,8 @@ typedef struct _dumpOptions
char *outputSuperuser;
int sequence_data; /* dump sequence data even in schema-only mode */
+ int compression_options_data; /* dump compression options even
+ * in schema-only mode */
} DumpOptions;
/*
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index ec2fa8b9b9..77eb55eb69 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -179,6 +179,7 @@ dumpOptionsFromRestoreOptions(RestoreOptions *ropt)
dopt->include_everything = ropt->include_everything;
dopt->enable_row_security = ropt->enable_row_security;
dopt->sequence_data = ropt->sequence_data;
+ dopt->compression_options_data = ropt->compression_options_data;
return dopt;
}
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index d8fb356130..6df688adb8 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -361,6 +361,7 @@ main(int argc, char **argv)
{"no-synchronized-snapshots", no_argument, &dopt.no_synchronized_snapshots, 1},
{"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
{"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
+ {"no-compression-methods", no_argument, &dopt.no_compression_methods, 1},
{"no-sync", no_argument, NULL, 7},
{NULL, 0, NULL, 0}
@@ -581,6 +582,9 @@ main(int argc, char **argv)
if (dopt.binary_upgrade)
dopt.sequence_data = 1;
+ if (dopt.binary_upgrade)
+ dopt.compression_options_data = 1;
+
if (dopt.dataOnly && dopt.schemaOnly)
{
write_msg(NULL, "options -s/--schema-only and -a/--data-only cannot be used together\n");
@@ -785,6 +789,9 @@ main(int argc, char **argv)
if (dopt.schemaOnly && dopt.sequence_data)
getTableData(&dopt, tblinfo, numTables, dopt.oids, RELKIND_SEQUENCE);
+ if (dopt.schemaOnly && dopt.compression_options_data)
+ getCompressionOptions(fout);
+
/*
* In binary-upgrade mode, we do not have to worry about the actual blob
* data or the associated metadata that resides in the pg_largeobject and
@@ -3957,6 +3964,205 @@ dumpSubscription(Archive *fout, SubscriptionInfo *subinfo)
destroyPQExpBuffer(query);
}
+/*
+ * getCompressionMethods
+ * get information about compression methods
+ */
+CompressionMethodInfo *
+getCompressionMethods(Archive *fout, int *numMethods)
+{
+ DumpOptions *dopt = fout->dopt;
+ PQExpBuffer query;
+ PGresult *res;
+ CompressionMethodInfo *cminfo;
+ int i_tableoid;
+ int i_oid;
+ int i_handler;
+ int i_name;
+ int i,
+ ntups;
+
+ if (dopt->no_compression_methods || fout->remoteVersion < 110000)
+ return NULL;
+
+ query = createPQExpBuffer();
+ resetPQExpBuffer(query);
+
+ /* Get the compression methods in current database. */
+ appendPQExpBuffer(query, "SELECT c.tableoid, c.oid, c.cmname, "
+ "c.cmhandler::pg_catalog.regproc as cmhandler "
+ "FROM pg_catalog.pg_compression c");
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_name = PQfnumber(res, "cmname");
+ i_handler = PQfnumber(res, "cmhandler");
+
+ cminfo = pg_malloc(ntups * sizeof(CompressionMethodInfo));
+
+ for (i = 0; i < ntups; i++)
+ {
+ cminfo[i].dobj.objType = DO_COMPRESSION_METHOD;
+ cminfo[i].dobj.catId.tableoid =
+ atooid(PQgetvalue(res, i, i_tableoid));
+ cminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&cminfo[i].dobj);
+ cminfo[i].cmhandler = pg_strdup(PQgetvalue(res, i, i_handler));
+ cminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_name));
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(cminfo[i].dobj), fout);
+ }
+ if (numMethods)
+ *numMethods = ntups;
+
+ PQclear(res);
+ destroyPQExpBuffer(query);
+
+ return cminfo;
+}
+
+/*
+ * dumpCompressionMethod
+ * dump the definition of the given compression method
+ */
+static void
+dumpCompressionMethod(Archive *fout, CompressionMethodInfo * cminfo)
+{
+ PQExpBuffer query;
+
+ if (!(cminfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
+ return;
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema(fout, "pg_catalog");
+
+ query = createPQExpBuffer();
+ appendPQExpBuffer(query, "CREATE COMPRESSION METHOD %s HANDLER",
+ fmtId(cminfo->dobj.name));
+ appendPQExpBuffer(query, " %s;\n", cminfo->cmhandler);
+
+ ArchiveEntry(fout,
+ cminfo->dobj.catId,
+ cminfo->dobj.dumpId,
+ cminfo->dobj.name,
+ NULL,
+ NULL,
+ "", false,
+ "COMPRESSION METHOD", SECTION_PRE_DATA,
+ query->data, "", NULL,
+ NULL, 0,
+ NULL, NULL);
+
+ destroyPQExpBuffer(query);
+}
+
+/*
+ * getCompressionOptions
+ * get information about compression options.
+ * this information should be migrated at binary upgrade
+ */
+void
+getCompressionOptions(Archive *fout)
+{
+ PQExpBuffer query;
+ PGresult *res;
+ CompressionOptionsInfo *cminfo;
+ int i_tableoid;
+ int i_oid;
+ int i_handler;
+ int i_name;
+ int i_options;
+ int i,
+ ntups;
+
+ if (fout->remoteVersion < 110000)
+ return;
+
+ query = createPQExpBuffer();
+ resetPQExpBuffer(query);
+
+ appendPQExpBuffer(query,
+ "SELECT c.tableoid, c.cmoptoid, c.cmname,"
+ " c.cmhandler::pg_catalog.regproc as cmhandler, c.cmoptions "
+ "FROM pg_catalog.pg_compression_opt c");
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "cmoptoid");
+ i_name = PQfnumber(res, "cmname");
+ i_handler = PQfnumber(res, "cmhandler");
+ i_options = PQfnumber(res, "cmoptions");
+
+ cminfo = pg_malloc(ntups * sizeof(CompressionOptionsInfo));
+
+ for (i = 0; i < ntups; i++)
+ {
+ cminfo[i].dobj.objType = DO_COMPRESSION_OPTIONS;
+ cminfo[i].dobj.catId.tableoid =
+ atooid(PQgetvalue(res, i, i_tableoid));
+ cminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&cminfo[i].dobj);
+ cminfo[i].cmhandler = pg_strdup(PQgetvalue(res, i, i_handler));
+ cminfo[i].cmoptions = pg_strdup(PQgetvalue(res, i, i_options));
+ cminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_name));
+ cminfo[i].dobj.dump = DUMP_COMPONENT_DEFINITION;
+ }
+
+ PQclear(res);
+ destroyPQExpBuffer(query);
+}
+
+/*
+ * dumpCompressionOptions
+ * dump the given compression options
+ */
+static void
+dumpCompressionOptions(Archive *fout, CompressionOptionsInfo * cminfo)
+{
+ PQExpBuffer query;
+
+ if (!(cminfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
+ return;
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema(fout, "pg_catalog");
+
+ query = createPQExpBuffer();
+ if (strlen(cminfo->cmoptions) > 0)
+ appendPQExpBuffer(query, "INSERT INTO pg_compression_opt (cmoptoid, cmname, cmhandler,"
+ " cmoptions) VALUES (%d, '%s', CAST('%s' AS REGPROC), '%s');\n",
+ cminfo->dobj.catId.oid,
+ cminfo->dobj.name,
+ cminfo->cmhandler,
+ cminfo->cmoptions);
+ else
+ appendPQExpBuffer(query, "INSERT INTO pg_compression_opt (cmoptoid, cmname, cmhandler,"
+ " cmoptions) VALUES (%d, '%s', CAST('%s' AS REGPROC), NULL);\n",
+ cminfo->dobj.catId.oid,
+ cminfo->dobj.name,
+ cminfo->cmhandler);
+
+ ArchiveEntry(fout,
+ cminfo->dobj.catId,
+ cminfo->dobj.dumpId,
+ cminfo->dobj.name,
+ NULL,
+ NULL,
+ "", false,
+ "COMPRESSION OPTIONS", SECTION_PRE_DATA,
+ query->data, "", NULL,
+ NULL, 0,
+ NULL, NULL);
+
+ destroyPQExpBuffer(query);
+}
+
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
@@ -4484,7 +4690,47 @@ getTypes(Archive *fout, int *numTypes)
/* Make sure we are in proper schema */
selectSourceSchema(fout, "pg_catalog");
- if (fout->remoteVersion >= 90600)
+ if (fout->remoteVersion >= 110000)
+ {
+ PQExpBuffer acl_subquery = createPQExpBuffer();
+ PQExpBuffer racl_subquery = createPQExpBuffer();
+ PQExpBuffer initacl_subquery = createPQExpBuffer();
+ PQExpBuffer initracl_subquery = createPQExpBuffer();
+
+ buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
+ initracl_subquery, "t.typacl", "t.typowner", "'T'",
+ dopt->binary_upgrade);
+
+ appendPQExpBuffer(query, "SELECT t.tableoid, t.oid, t.typname, "
+ "t.typnamespace, "
+ "%s AS typacl, "
+ "%s AS rtypacl, "
+ "%s AS inittypacl, "
+ "%s AS initrtypacl, "
+ "(%s t.typowner) AS rolname, "
+ "t.typelem, t.typrelid, "
+ "CASE WHEN t.typrelid = 0 THEN ' '::\"char\" "
+ "ELSE (SELECT relkind FROM pg_class WHERE oid = t.typrelid) END AS typrelkind, "
+ "t.typtype, t.typisdefined, "
+ "t.typname[0] = '_' AND t.typelem != 0 AND "
+ "(SELECT typarray FROM pg_type te WHERE oid = t.typelem) = t.oid AS isarray "
+ "FROM pg_type t "
+ "LEFT JOIN pg_init_privs pip ON "
+ "(t.oid = pip.objoid "
+ "AND pip.classoid = 'pg_type'::regclass "
+ "AND pip.objsubid = 0) ",
+ acl_subquery->data,
+ racl_subquery->data,
+ initacl_subquery->data,
+ initracl_subquery->data,
+ username_subquery);
+
+ destroyPQExpBuffer(acl_subquery);
+ destroyPQExpBuffer(racl_subquery);
+ destroyPQExpBuffer(initacl_subquery);
+ destroyPQExpBuffer(initracl_subquery);
+ }
+ else if (fout->remoteVersion >= 90600)
{
PQExpBuffer acl_subquery = createPQExpBuffer();
PQExpBuffer racl_subquery = createPQExpBuffer();
@@ -7895,6 +8141,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
int i_attoptions;
int i_attcollation;
int i_attfdwoptions;
+ int i_attcmoptions;
+ int i_attcmname;
PGresult *res;
int ntups;
bool hasdefaults;
@@ -7930,7 +8178,46 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
resetPQExpBuffer(q);
- if (fout->remoteVersion >= 100000)
+ if (fout->remoteVersion >= 110000)
+ {
+ /*
+ * attcompression is new in version 11
+ */
+ appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, "
+ "a.attstattarget, a.attstorage, t.typstorage, "
+ "a.attnotnull, a.atthasdef, a.attisdropped, "
+ "a.attlen, a.attalign, a.attislocal, "
+ "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
+ "array_to_string(a.attoptions, ', ') AS attoptions, "
+ "CASE WHEN a.attcollation <> t.typcollation "
+ "THEN a.attcollation ELSE 0 END AS attcollation, "
+ "a.attidentity, "
+ /* fdw options */
+ "pg_catalog.array_to_string(ARRAY("
+ "SELECT pg_catalog.quote_ident(option_name) || "
+ "' ' || pg_catalog.quote_literal(option_value) "
+ "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
+ "ORDER BY option_name"
+ "), E',\n ') AS attfdwoptions, "
+ /* compression options */
+ "pg_catalog.array_to_string(ARRAY("
+ "SELECT pg_catalog.quote_ident(option_name) || "
+ "' ' || pg_catalog.quote_literal(option_value) "
+ "FROM pg_catalog.pg_options_to_table(c.cmoptions) "
+ "ORDER BY option_name"
+ "), E',\n ') AS attcmoptions, "
+ "c.cmname AS attcmname "
+ /* FROM */
+ "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
+ "ON a.atttypid = t.oid "
+ "LEFT JOIN pg_catalog.pg_compression_opt c "
+ "ON a.attcompression = c.cmoptoid "
+ "WHERE a.attrelid = '%u'::pg_catalog.oid "
+ "AND a.attnum > 0::pg_catalog.int2 "
+ "ORDER BY a.attnum",
+ tbinfo->dobj.catId.oid);
+ }
+ else if (fout->remoteVersion >= 100000)
{
/*
* attidentity is new in version 10.
@@ -7949,9 +8236,13 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"' ' || pg_catalog.quote_literal(option_value) "
"FROM pg_catalog.pg_options_to_table(attfdwoptions) "
"ORDER BY option_name"
- "), E',\n ') AS attfdwoptions "
+ "), E',\n ') AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
+ "LEFT JOIN pg_catalog.pg_compression_opt c "
+ "ON a.attcompression = c.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
"AND a.attnum > 0::pg_catalog.int2 "
"ORDER BY a.attnum",
@@ -7975,7 +8266,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"' ' || pg_catalog.quote_literal(option_value) "
"FROM pg_catalog.pg_options_to_table(attfdwoptions) "
"ORDER BY option_name"
- "), E',\n ') AS attfdwoptions "
+ "), E',\n ') AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -7999,7 +8292,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"array_to_string(a.attoptions, ', ') AS attoptions, "
"CASE WHEN a.attcollation <> t.typcollation "
"THEN a.attcollation ELSE 0 END AS attcollation, "
- "NULL AS attfdwoptions "
+ "NULL AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -8017,7 +8312,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
"array_to_string(a.attoptions, ', ') AS attoptions, "
"0 AS attcollation, "
- "NULL AS attfdwoptions "
+ "NULL AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -8034,7 +8331,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"a.attlen, a.attalign, a.attislocal, "
"pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
"'' AS attoptions, 0 AS attcollation, "
- "NULL AS attfdwoptions "
+ "NULL AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -8064,6 +8363,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
i_attoptions = PQfnumber(res, "attoptions");
i_attcollation = PQfnumber(res, "attcollation");
i_attfdwoptions = PQfnumber(res, "attfdwoptions");
+ i_attcmname = PQfnumber(res, "attcmname");
+ i_attcmoptions = PQfnumber(res, "attcmoptions");
tbinfo->numatts = ntups;
tbinfo->attnames = (char **) pg_malloc(ntups * sizeof(char *));
@@ -8080,6 +8381,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
tbinfo->attoptions = (char **) pg_malloc(ntups * sizeof(char *));
tbinfo->attcollation = (Oid *) pg_malloc(ntups * sizeof(Oid));
tbinfo->attfdwoptions = (char **) pg_malloc(ntups * sizeof(char *));
+ tbinfo->attcmoptions = (char **) pg_malloc(ntups * sizeof(char *));
+ tbinfo->attcmnames = (char **) pg_malloc(ntups * sizeof(char *));
tbinfo->notnull = (bool *) pg_malloc(ntups * sizeof(bool));
tbinfo->inhNotNull = (bool *) pg_malloc(ntups * sizeof(bool));
tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(ntups * sizeof(AttrDefInfo *));
@@ -8107,6 +8410,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, j, i_attoptions));
tbinfo->attcollation[j] = atooid(PQgetvalue(res, j, i_attcollation));
tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, j, i_attfdwoptions));
+ tbinfo->attcmoptions[j] = pg_strdup(PQgetvalue(res, j, i_attcmoptions));
+ tbinfo->attcmnames[j] = pg_strdup(PQgetvalue(res, j, i_attcmname));
tbinfo->attrdefs[j] = NULL; /* fix below */
if (PQgetvalue(res, j, i_atthasdef)[0] == 't')
hasdefaults = true;
@@ -9596,6 +9901,12 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
case DO_SUBSCRIPTION:
dumpSubscription(fout, (SubscriptionInfo *) dobj);
break;
+ case DO_COMPRESSION_METHOD:
+ dumpCompressionMethod(fout, (CompressionMethodInfo *) dobj);
+ break;
+ case DO_COMPRESSION_OPTIONS:
+ dumpCompressionOptions(fout, (CompressionOptionsInfo *) dobj);
+ break;
case DO_PRE_DATA_BOUNDARY:
case DO_POST_DATA_BOUNDARY:
/* never dumped, nothing to do */
@@ -15513,6 +15824,15 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
}
}
+ if (tbinfo->attcmnames[j] && strlen(tbinfo->attcmnames[j]))
+ {
+ appendPQExpBuffer(q, " COMPRESSED %s",
+ tbinfo->attcmnames[j]);
+ if (nonemptyReloptions(tbinfo->attcmoptions[j]))
+ appendPQExpBuffer(q, " WITH (%s)",
+ tbinfo->attcmoptions[j]);
+ }
+
if (has_default)
appendPQExpBuffer(q, " DEFAULT %s",
tbinfo->attrdefs[j]->adef_expr);
@@ -17778,6 +18098,8 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
case DO_FOREIGN_SERVER:
case DO_TRANSFORM:
case DO_BLOB:
+ case DO_COMPRESSION_METHOD:
+ case DO_COMPRESSION_OPTIONS:
/* Pre-data objects: must come before the pre-data boundary */
addObjectDependency(preDataBound, dobj->dumpId);
break;
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index da884ffd09..88b18ba15c 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -83,7 +83,9 @@ typedef enum
DO_POLICY,
DO_PUBLICATION,
DO_PUBLICATION_REL,
- DO_SUBSCRIPTION
+ DO_SUBSCRIPTION,
+ DO_COMPRESSION_METHOD,
+ DO_COMPRESSION_OPTIONS
} DumpableObjectType;
/* component types of an object which can be selected for dumping */
@@ -315,6 +317,8 @@ typedef struct _tableInfo
char **attoptions; /* per-attribute options */
Oid *attcollation; /* per-attribute collation selection */
char **attfdwoptions; /* per-attribute fdw options */
+ char **attcmoptions; /* per-attribute compression options */
+ char **attcmnames; /* per-attribute compression method names */
bool *notnull; /* NOT NULL constraints on attributes */
bool *inhNotNull; /* true if NOT NULL is inherited */
struct _attrDefInfo **attrdefs; /* DEFAULT expressions */
@@ -611,6 +615,21 @@ typedef struct _SubscriptionInfo
char *subpublications;
} SubscriptionInfo;
+/* The CompressionMethodInfo struct is used to represent compression method */
+typedef struct _CompressionMethodInfo
+{
+ DumpableObject dobj;
+ char *cmhandler;
+} CompressionMethodInfo;
+
+/* The CompressionOptionsInfo struct is used to represent compression options */
+typedef struct _CompressionOptionsInfo
+{
+ DumpableObject dobj;
+ char *cmhandler;
+ char *cmoptions;
+} CompressionOptionsInfo;
+
/*
* We build an array of these with an entry for each object that is an
* extension member according to pg_depend.
@@ -654,6 +673,7 @@ extern OprInfo *findOprByOid(Oid oid);
extern CollInfo *findCollationByOid(Oid oid);
extern NamespaceInfo *findNamespaceByOid(Oid oid);
extern ExtensionInfo *findExtensionByOid(Oid oid);
+extern CompressionMethodInfo * findCompressionMethodByOid(Oid oid);
extern void setExtensionMembership(ExtensionMemberId *extmems, int nextmems);
extern ExtensionInfo *findOwningExtension(CatalogId catalogId);
@@ -711,5 +731,8 @@ extern void getPublications(Archive *fout);
extern void getPublicationTables(Archive *fout, TableInfo tblinfo[],
int numTables);
extern void getSubscriptions(Archive *fout);
+extern CompressionMethodInfo * getCompressionMethods(Archive *fout,
+ int *numMethods);
+void getCompressionOptions(Archive *fout);
#endif /* PG_DUMP_H */
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index 48b6dd594c..f5db0280e2 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -80,7 +80,9 @@ static const int dbObjectTypePriority[] =
34, /* DO_POLICY */
35, /* DO_PUBLICATION */
36, /* DO_PUBLICATION_REL */
- 37 /* DO_SUBSCRIPTION */
+ 37, /* DO_SUBSCRIPTION */
+ 17, /* DO_COMPRESSION_METHOD */
+ 17 /* DO_COMPRESSION_OPTIONS */
};
static DumpId preDataBoundId;
@@ -1436,6 +1438,16 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
"POST-DATA BOUNDARY (ID %d)",
obj->dumpId);
return;
+ case DO_COMPRESSION_METHOD:
+ snprintf(buf, bufsize,
+ "COMPRESSION METHOD %s (ID %d OID %u)",
+ obj->name, obj->dumpId, obj->catId.oid);
+ return;
+ case DO_COMPRESSION_OPTIONS:
+ snprintf(buf, bufsize,
+ "COMPRESSION OPTIONS %s (ID %d OID %u)",
+ obj->name, obj->dumpId, obj->catId.oid);
+ return;
}
/* shouldn't get here */
snprintf(buf, bufsize,
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 41c5ff89b7..6b72ccf9ea 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -77,6 +77,7 @@ static int use_setsessauth = 0;
static int no_publications = 0;
static int no_security_labels = 0;
static int no_subscriptions = 0;
+static int no_compression_methods = 0;
static int no_unlogged_table_data = 0;
static int no_role_passwords = 0;
static int server_version;
@@ -137,6 +138,7 @@ main(int argc, char *argv[])
{"no-role-passwords", no_argument, &no_role_passwords, 1},
{"no-security-labels", no_argument, &no_security_labels, 1},
{"no-subscriptions", no_argument, &no_subscriptions, 1},
+ {"no-compression-methods", no_argument, &no_compression_methods, 1},
{"no-sync", no_argument, NULL, 4},
{"no-unlogged-table-data", no_argument, &no_unlogged_table_data, 1},
@@ -405,6 +407,8 @@ main(int argc, char *argv[])
appendPQExpBufferStr(pgdumpopts, " --no-security-labels");
if (no_subscriptions)
appendPQExpBufferStr(pgdumpopts, " --no-subscriptions");
+ if (no_compression_methods)
+ appendPQExpBufferStr(pgdumpopts, " --no-compression-methods");
if (no_unlogged_table_data)
appendPQExpBufferStr(pgdumpopts, " --no-unlogged-table-data");
@@ -628,6 +632,7 @@ help(void)
printf(_(" --no-role-passwords do not dump passwords for roles\n"));
printf(_(" --no-security-labels do not dump security label assignments\n"));
printf(_(" --no-subscriptions do not dump subscriptions\n"));
+ printf(_(" --no-compression-methods do not dump compression methods\n"));
printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c
index 860a211a3c..810403ec9c 100644
--- a/src/bin/pg_dump/pg_restore.c
+++ b/src/bin/pg_dump/pg_restore.c
@@ -74,6 +74,7 @@ main(int argc, char **argv)
static int no_publications = 0;
static int no_security_labels = 0;
static int no_subscriptions = 0;
+ static int no_compression_methods = 0;
static int strict_names = 0;
struct option cmdopts[] = {
@@ -122,6 +123,7 @@ main(int argc, char **argv)
{"no-publications", no_argument, &no_publications, 1},
{"no-security-labels", no_argument, &no_security_labels, 1},
{"no-subscriptions", no_argument, &no_subscriptions, 1},
+ {"no-compression-methods", no_argument, &no_compression_methods, 1},
{NULL, 0, NULL, 0}
};
@@ -361,6 +363,7 @@ main(int argc, char **argv)
opts->no_publications = no_publications;
opts->no_security_labels = no_security_labels;
opts->no_subscriptions = no_subscriptions;
+ opts->no_compression_methods = no_compression_methods;
if (if_exists && !opts->dropSchema)
{
diff --git a/src/bin/pg_upgrade/info.c b/src/bin/pg_upgrade/info.c
index 0b14998f4b..4689b72e88 100644
--- a/src/bin/pg_upgrade/info.c
+++ b/src/bin/pg_upgrade/info.c
@@ -66,6 +66,7 @@ gen_db_file_maps(DbInfo *old_db, DbInfo *new_db,
RelInfo *new_rel = (new_relnum < new_db->rel_arr.nrels) ?
&new_db->rel_arr.rels[new_relnum] : NULL;
+ fprintf(stderr, "mapping: %s\n", old_rel->relname);
/* handle running off one array before the other */
if (!new_rel)
{
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 8cc4de3878..8b0dbfa45c 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -735,7 +735,10 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
success = listConversions(pattern, show_verbose, show_system);
break;
case 'C':
- success = listCasts(pattern, show_verbose);
+ if (cmd[2] == 'M')
+ success = describeCompressionMethods(pattern, show_verbose);
+ else
+ success = listCasts(pattern, show_verbose);
break;
case 'd':
if (strncmp(cmd, "ddp", 3) == 0)
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index b7b978a361..49160ded0c 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -200,6 +200,69 @@ describeAccessMethods(const char *pattern, bool verbose)
return true;
}
+/*
+ * \dCM
+ * Takes an optional regexp to select particular compression methods
+ */
+bool
+describeCompressionMethods(const char *pattern, bool verbose)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+ static const bool translate_columns[] = {false, false, false};
+
+ if (pset.sversion < 100000)
+ {
+ char sverbuf[32];
+
+ psql_error("The server (version %s) does not support compression methods.\n",
+ formatPGVersionNumber(pset.sversion, false,
+ sverbuf, sizeof(sverbuf)));
+ return true;
+ }
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT cmname AS \"%s\"",
+ gettext_noop("Name"));
+
+ if (verbose)
+ {
+ appendPQExpBuffer(&buf,
+ ",\n cmhandler AS \"%s\",\n"
+ " pg_catalog.obj_description(oid, 'pg_compression') AS \"%s\"",
+ gettext_noop("Handler"),
+ gettext_noop("Description"));
+ }
+
+ appendPQExpBufferStr(&buf,
+ "\nFROM pg_catalog.pg_compression\n");
+
+ processSQLNamePattern(pset.db, &buf, pattern, false, false,
+ NULL, "cmname", NULL,
+ NULL);
+
+ appendPQExpBufferStr(&buf, "ORDER BY 1;");
+
+ res = PSQLexec(buf.data);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of compression methods");
+ myopt.translate_header = true;
+ myopt.translate_columns = translate_columns;
+ myopt.n_translate_columns = lengthof(translate_columns);
+
+ printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+ PQclear(res);
+ return true;
+}
+
/*
* \db
* Takes an optional regexp to select particular tablespaces
@@ -1716,6 +1779,22 @@ describeOneTableDetails(const char *schemaname,
if (verbose)
{
appendPQExpBufferStr(&buf, ",\n a.attstorage");
+
+ /* compresssion info */
+ if (pset.sversion >= 110000)
+ appendPQExpBufferStr(&buf, ",\n CASE WHEN attcompression = 0 THEN NULL ELSE "
+ " (SELECT c.cmname || "
+ " (CASE WHEN cmoptions IS NULL "
+ " THEN '' "
+ " ELSE '(' || array_to_string(ARRAY(SELECT quote_ident(option_name) || ' ' || quote_literal(option_value)"
+ " FROM pg_options_to_table(cmoptions)), ', ') || ')'"
+ " END) "
+ " FROM pg_catalog.pg_compression_opt c "
+ " WHERE c.cmoptoid = a.attcompression) "
+ " END AS attcmname");
+ else
+ appendPQExpBufferStr(&buf, "\n NULL AS attcmname");
+
appendPQExpBufferStr(&buf, ",\n CASE WHEN a.attstattarget=-1 THEN NULL ELSE a.attstattarget END AS attstattarget");
/*
@@ -1830,6 +1909,10 @@ describeOneTableDetails(const char *schemaname,
if (verbose)
{
headers[cols++] = gettext_noop("Storage");
+
+ if (tableinfo.relkind == RELKIND_RELATION)
+ headers[cols++] = gettext_noop("Compression");
+
if (tableinfo.relkind == RELKIND_RELATION ||
tableinfo.relkind == RELKIND_INDEX ||
tableinfo.relkind == RELKIND_MATVIEW ||
@@ -1925,6 +2008,11 @@ describeOneTableDetails(const char *schemaname,
"???")))),
false, false);
+ /* Column compression. */
+ if (tableinfo.relkind == RELKIND_RELATION)
+ printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1),
+ false, false);
+
/* Statistics target, if the relkind supports this feature */
if (tableinfo.relkind == RELKIND_RELATION ||
tableinfo.relkind == RELKIND_INDEX ||
@@ -1932,7 +2020,7 @@ describeOneTableDetails(const char *schemaname,
tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
{
- printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1),
+ printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2),
false, false);
}
@@ -1943,7 +2031,7 @@ describeOneTableDetails(const char *schemaname,
tableinfo.relkind == RELKIND_COMPOSITE_TYPE ||
tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
- printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2),
+ printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 3),
false, false);
}
}
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 14a5667f3e..0ab8518a36 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -18,6 +18,9 @@ extern bool describeAccessMethods(const char *pattern, bool verbose);
/* \db */
extern bool describeTablespaces(const char *pattern, bool verbose);
+/* \dCM */
+extern bool describeCompressionMethods(const char *pattern, bool verbose);
+
/* \df, \dfa, \dfn, \dft, \dfw, etc. */
extern bool describeFunctions(const char *functypes, const char *pattern, bool verbose, bool showSystem);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index a926c40b9b..25d2fbc125 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -227,6 +227,7 @@ slashUsage(unsigned short int pager)
fprintf(output, _(" \\db[+] [PATTERN] list tablespaces\n"));
fprintf(output, _(" \\dc[S+] [PATTERN] list conversions\n"));
fprintf(output, _(" \\dC[+] [PATTERN] list casts\n"));
+ fprintf(output, _(" \\dCM[+] [PATTERN] list compression methods\n"));
fprintf(output, _(" \\dd[S] [PATTERN] show object descriptions not displayed elsewhere\n"));
fprintf(output, _(" \\dD[S+] [PATTERN] list domains\n"));
fprintf(output, _(" \\ddp [PATTERN] list default privileges\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index b3e3799c13..a323ca80c6 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -889,6 +889,11 @@ static const SchemaQuery Query_for_list_of_statistics = {
" AND d.datname = pg_catalog.current_database() "\
" AND s.subdbid = d.oid"
+#define Query_for_list_of_compression_methods \
+" SELECT pg_catalog.quote_ident(cmname) "\
+" FROM pg_catalog.pg_compression "\
+" WHERE substring(pg_catalog.quote_ident(cmname),1,%d)='%s'"
+
/* the silly-looking length condition is just to eat up the current word */
#define Query_for_list_of_arguments \
"SELECT pg_catalog.oidvectortypes(proargtypes)||')' "\
@@ -1011,6 +1016,7 @@ static const pgsql_thing_t words_after_create[] = {
* CREATE CONSTRAINT TRIGGER is not supported here because it is designed
* to be used only by pg_dump.
*/
+ {"COMPRESSION METHOD", NULL, NULL},
{"CONFIGURATION", Query_for_list_of_ts_configurations, NULL, THING_NO_SHOW},
{"CONVERSION", "SELECT pg_catalog.quote_ident(conname) FROM pg_catalog.pg_conversion WHERE substring(pg_catalog.quote_ident(conname),1,%d)='%s'"},
{"DATABASE", Query_for_list_of_databases},
@@ -1424,8 +1430,8 @@ psql_completion(const char *text, int start, int end)
"\\a",
"\\connect", "\\conninfo", "\\C", "\\cd", "\\copy",
"\\copyright", "\\crosstabview",
- "\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
- "\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
+ "\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dCM", "\\dd", "\\ddp",
+ "\\dD", "\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
"\\dm", "\\dn", "\\do", "\\dO", "\\dp",
"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
@@ -1954,11 +1960,17 @@ psql_completion(const char *text, int start, int end)
/* ALTER TABLE ALTER [COLUMN] <foo> SET */
else if (Matches7("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET") ||
Matches6("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET"))
- COMPLETE_WITH_LIST5("(", "DEFAULT", "NOT NULL", "STATISTICS", "STORAGE");
+ COMPLETE_WITH_LIST6("(", "COMPRESSED", "DEFAULT", "NOT NULL", "STATISTICS", "STORAGE");
/* ALTER TABLE ALTER [COLUMN] <foo> SET ( */
else if (Matches8("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "(") ||
Matches7("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "("))
COMPLETE_WITH_LIST2("n_distinct", "n_distinct_inherited");
+ else if (Matches8("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "COMPRESSED") ||
+ Matches7("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "COMPRESSED"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_compression_methods);
+ else if (Matches9("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "COMPRESSED", MatchAny) ||
+ Matches8("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "COMPRESSED", MatchAny))
+ COMPLETE_WITH_CONST("WITH (");
/* ALTER TABLE ALTER [COLUMN] <foo> SET STORAGE */
else if (Matches8("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "STORAGE") ||
Matches7("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "STORAGE"))
@@ -2177,12 +2189,14 @@ psql_completion(const char *text, int start, int end)
"SCHEMA", "SEQUENCE", "STATISTICS", "SUBSCRIPTION",
"TABLE", "TYPE", "VIEW", "MATERIALIZED VIEW", "COLUMN", "AGGREGATE", "FUNCTION",
"OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", "LARGE OBJECT",
- "TABLESPACE", "TEXT SEARCH", "ROLE", NULL};
+ "TABLESPACE", "TEXT SEARCH", "ROLE", "COMPRESSION METHOD", NULL};
COMPLETE_WITH_LIST(list_COMMENT);
}
else if (Matches4("COMMENT", "ON", "ACCESS", "METHOD"))
COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
+ else if (Matches4("COMMENT", "ON", "COMPRESSION", "METHOD"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_compression_methods);
else if (Matches3("COMMENT", "ON", "FOREIGN"))
COMPLETE_WITH_LIST2("DATA WRAPPER", "TABLE");
else if (Matches4("COMMENT", "ON", "TEXT", "SEARCH"))
@@ -2255,6 +2269,14 @@ psql_completion(const char *text, int start, int end)
else if (Matches6("CREATE", "ACCESS", "METHOD", MatchAny, "TYPE", MatchAny))
COMPLETE_WITH_CONST("HANDLER");
+ /* CREATE COMPRESSION METHOD */
+ /* Complete "CREATE COMPRESSION METHOD <name>" */
+ else if (Matches4("CREATE", "COMPRESSION", "METHOD", MatchAny))
+ COMPLETE_WITH_CONST("HANDLER");
+ /* Complete "CREATE COMPRESSION METHOD <name> HANDLER" */
+ else if (Matches5("CREATE", "COMPRESSION", "METHOD", MatchAny, "HANDLER"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
+
/* CREATE DATABASE */
else if (Matches3("CREATE", "DATABASE", MatchAny))
COMPLETE_WITH_LIST9("OWNER", "TEMPLATE", "ENCODING", "TABLESPACE",
@@ -2687,6 +2709,7 @@ psql_completion(const char *text, int start, int end)
Matches4("DROP", "ACCESS", "METHOD", MatchAny) ||
(Matches4("DROP", "AGGREGATE|FUNCTION", MatchAny, MatchAny) &&
ends_with(prev_wd, ')')) ||
+ Matches4("DROP", "COMPRESSION", "METHOD", MatchAny) ||
Matches4("DROP", "EVENT", "TRIGGER", MatchAny) ||
Matches5("DROP", "FOREIGN", "DATA", "WRAPPER", MatchAny) ||
Matches4("DROP", "FOREIGN", "TABLE", MatchAny) ||
@@ -2775,6 +2798,12 @@ psql_completion(const char *text, int start, int end)
else if (Matches5("DROP", "RULE", MatchAny, "ON", MatchAny))
COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
+ /* DROP COMPRESSION METHOD */
+ else if (Matches2("DROP", "COMPRESSION"))
+ COMPLETE_WITH_CONST("METHOD");
+ else if (Matches3("DROP", "COMPRESSION", "METHOD"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_compression_methods);
+
/* EXECUTE */
else if (Matches1("EXECUTE"))
COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements);
@@ -3407,6 +3436,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
else if (TailMatchesCS1("\\db*"))
COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
+ else if (TailMatchesCS1("\\dCM*"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_compression_methods);
else if (TailMatchesCS1("\\dD*"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL);
else if (TailMatchesCS1("\\des*"))
diff --git a/src/include/access/compression.h b/src/include/access/compression.h
new file mode 100644
index 0000000000..1320d8f882
--- /dev/null
+++ b/src/include/access/compression.h
@@ -0,0 +1,65 @@
+/*-------------------------------------------------------------------------
+ *
+ * compression.h
+ * API for Postgres compression methods.
+ *
+ * Copyright (c) 2015-2016, PostgreSQL Global Development Group
+ *
+ * src/include/access/compression.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef COMPRESSION_H
+#define COMPRESSION_H
+
+#include "postgres.h"
+#include "catalog/pg_attribute.h"
+#include "nodes/nodes.h"
+#include "nodes/pg_list.h"
+
+/* parsenodes.h */
+typedef struct ColumnCompression ColumnCompression;
+typedef struct CompressionMethodRoutine CompressionMethodRoutine;
+
+typedef struct
+{
+ CompressionMethodRoutine *routine;
+ List *options;
+ Oid cmoptoid;
+} AttributeCompression;
+
+typedef void (*CompressionConfigureRoutine)
+ (Form_pg_attribute attr, List *options);
+typedef struct varlena *(*CompressionRoutine)
+ (AttributeCompression * ac, const struct varlena *data);
+
+/*
+ * API struct for an compression method.
+ * Note this must be stored in a single palloc'd chunk of memory.
+ */
+typedef struct CompressionMethodRoutine
+{
+ NodeTag type;
+
+ CompressionConfigureRoutine configure;
+ CompressionConfigureRoutine drop;
+ CompressionRoutine compress;
+ CompressionRoutine decompress;
+} CompressionMethodRoutine;
+
+typedef struct CompressionMethodOpArgs
+{
+ Oid cmhanderid;
+ Oid typeid;
+} CompressionMethodOpArgs;
+
+extern CompressionMethodRoutine * GetCompressionRoutine(Oid cmoptoid);
+extern List *GetCompressionOptionsList(Oid cmoptoid);
+extern Oid CreateCompressionOptions(Form_pg_attribute attr, Oid cmid,
+ List *options);
+extern ColumnCompression * GetColumnCompressionForAttribute(Form_pg_attribute att);
+extern void CheckCompressionMismatch(ColumnCompression * c1,
+ ColumnCompression * c2, const char *attributeName);
+
+#endif /* COMPRESSION_H */
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index cd43e3a52e..ad133c9b90 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -258,7 +258,6 @@ extern void add_string_reloption(bits32 kinds, const char *name, const char *des
extern Datum transformRelOptions(Datum oldOptions, List *defList,
const char *namspace, char *validnsps[],
bool ignoreOids, bool isReset);
-extern List *untransformRelOptions(Datum options);
extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
amoptions_function amoptions);
extern relopt_value *parseRelOptions(Datum options, bool validate,
@@ -269,6 +268,9 @@ extern void fillRelOptions(void *rdopts, Size basesize,
relopt_value *options, int numoptions,
bool validate,
const relopt_parse_elt *elems, int nelems);
+extern char *formatRelOptions(List *options);
+extern void freeRelOptions(List *options);
+extern List *untransformRelOptions(Datum options);
extern bytea *default_reloptions(Datum reloptions, bool validate,
relopt_kind kind);
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index 2be5af1d3e..867adc1bd8 100644
--- a/src/include/access/tupdesc.h
+++ b/src/include/access/tupdesc.h
@@ -14,7 +14,9 @@
#ifndef TUPDESC_H
#define TUPDESC_H
+#include "postgres.h"
#include "access/attnum.h"
+#include "access/compression.h"
#include "catalog/pg_attribute.h"
#include "nodes/pg_list.h"
@@ -88,6 +90,9 @@ typedef struct tupleDesc
/* Accessor for the i'th attribute of tupdesc. */
#define TupleDescAttr(tupdesc, i) (&(tupdesc)->attrs[(i)])
+#define TupleDescAttrCompression(tupdesc, i) \
+ ((tupdesc)->tdcompression? &((tupdesc)->tdcompression[i]) : NULL)
+
extern TupleDesc CreateTemplateTupleDesc(int natts, bool hasoid);
diff --git a/src/include/access/tuptoaster.h b/src/include/access/tuptoaster.h
index fd9f83ac44..11092e5e9d 100644
--- a/src/include/access/tuptoaster.h
+++ b/src/include/access/tuptoaster.h
@@ -210,7 +210,7 @@ extern HeapTuple toast_build_flattened_tuple(TupleDesc tupleDesc,
* Create a compressed version of a varlena datum, if possible
* ----------
*/
-extern Datum toast_compress_datum(Datum value);
+extern Datum toast_compress_datum(Datum value, Oid cmoptoid);
/* ----------
* toast_raw_datum_size -
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index b9f98423cc..36cadd409e 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -165,10 +165,12 @@ typedef enum ObjectClass
OCLASS_PUBLICATION, /* pg_publication */
OCLASS_PUBLICATION_REL, /* pg_publication_rel */
OCLASS_SUBSCRIPTION, /* pg_subscription */
- OCLASS_TRANSFORM /* pg_transform */
+ OCLASS_TRANSFORM, /* pg_transform */
+ OCLASS_COMPRESSION_METHOD, /* pg_compression */
+ OCLASS_COMPRESSION_OPTIONS /* pg_compression_opt */
} ObjectClass;
-#define LAST_OCLASS OCLASS_TRANSFORM
+#define LAST_OCLASS OCLASS_COMPRESSION_OPTIONS
/* flag bits for performDeletion/performMultipleDeletions: */
#define PERFORM_DELETION_INTERNAL 0x0001 /* internal action */
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index ef8493674c..7fe9f1f059 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -120,6 +120,14 @@ DECLARE_UNIQUE_INDEX(pg_collation_name_enc_nsp_index, 3164, on pg_collation usin
DECLARE_UNIQUE_INDEX(pg_collation_oid_index, 3085, on pg_collation using btree(oid oid_ops));
#define CollationOidIndexId 3085
+DECLARE_UNIQUE_INDEX(pg_compression_oid_index, 3422, on pg_compression using btree(oid oid_ops));
+#define CompressionMethodOidIndexId 3422
+DECLARE_UNIQUE_INDEX(pg_compression_name_index, 3423, on pg_compression using btree(cmname name_ops));
+#define CompressionMethodNameIndexId 3423
+
+DECLARE_UNIQUE_INDEX(pg_compression_opt_oid_index, 3424, on pg_compression_opt using btree(cmoptoid oid_ops));
+#define CompressionOptionsOidIndexId 3424
+
DECLARE_INDEX(pg_constraint_conname_nsp_index, 2664, on pg_constraint using btree(conname name_ops, connamespace oid_ops));
#define ConstraintNameNspIndexId 2664
DECLARE_INDEX(pg_constraint_conrelid_index, 2665, on pg_constraint using btree(conrelid oid_ops));
diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h
index bcf28e8f04..caadd61031 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -156,6 +156,9 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
/* attribute's collation */
Oid attcollation;
+ /* attribute's compression options or InvalidOid */
+ Oid attcompression;
+
#ifdef CATALOG_VARLEN /* variable-length fields start here */
/* NOTE: The following fields are not present in tuple descriptors. */
@@ -174,10 +177,10 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
* ATTRIBUTE_FIXED_PART_SIZE is the size of the fixed-layout,
* guaranteed-not-null part of a pg_attribute row. This is in fact as much
* of the row as gets copied into tuple descriptors, so don't expect you
- * can access fields beyond attcollation except in a real tuple!
+ * can access fields beyond attcompression except in a real tuple!
*/
#define ATTRIBUTE_FIXED_PART_SIZE \
- (offsetof(FormData_pg_attribute,attcollation) + sizeof(Oid))
+ (offsetof(FormData_pg_attribute,attcompression) + sizeof(Oid))
/* ----------------
* Form_pg_attribute corresponds to a pointer to a tuple with
@@ -191,29 +194,30 @@ typedef FormData_pg_attribute *Form_pg_attribute;
* ----------------
*/
-#define Natts_pg_attribute 22
-#define Anum_pg_attribute_attrelid 1
-#define Anum_pg_attribute_attname 2
-#define Anum_pg_attribute_atttypid 3
-#define Anum_pg_attribute_attstattarget 4
-#define Anum_pg_attribute_attlen 5
-#define Anum_pg_attribute_attnum 6
-#define Anum_pg_attribute_attndims 7
-#define Anum_pg_attribute_attcacheoff 8
-#define Anum_pg_attribute_atttypmod 9
-#define Anum_pg_attribute_attbyval 10
-#define Anum_pg_attribute_attstorage 11
-#define Anum_pg_attribute_attalign 12
-#define Anum_pg_attribute_attnotnull 13
-#define Anum_pg_attribute_atthasdef 14
-#define Anum_pg_attribute_attidentity 15
-#define Anum_pg_attribute_attisdropped 16
-#define Anum_pg_attribute_attislocal 17
-#define Anum_pg_attribute_attinhcount 18
-#define Anum_pg_attribute_attcollation 19
-#define Anum_pg_attribute_attacl 20
-#define Anum_pg_attribute_attoptions 21
-#define Anum_pg_attribute_attfdwoptions 22
+#define Natts_pg_attribute 23
+#define Anum_pg_attribute_attrelid 1
+#define Anum_pg_attribute_attname 2
+#define Anum_pg_attribute_atttypid 3
+#define Anum_pg_attribute_attstattarget 4
+#define Anum_pg_attribute_attlen 5
+#define Anum_pg_attribute_attnum 6
+#define Anum_pg_attribute_attndims 7
+#define Anum_pg_attribute_attcacheoff 8
+#define Anum_pg_attribute_atttypmod 9
+#define Anum_pg_attribute_attbyval 10
+#define Anum_pg_attribute_attstorage 11
+#define Anum_pg_attribute_attalign 12
+#define Anum_pg_attribute_attnotnull 13
+#define Anum_pg_attribute_atthasdef 14
+#define Anum_pg_attribute_attidentity 15
+#define Anum_pg_attribute_attisdropped 16
+#define Anum_pg_attribute_attislocal 17
+#define Anum_pg_attribute_attinhcount 18
+#define Anum_pg_attribute_attcollation 19
+#define Anum_pg_attribute_attcompression 20
+#define Anum_pg_attribute_attacl 21
+#define Anum_pg_attribute_attoptions 22
+#define Anum_pg_attribute_attfdwoptions 23
/* ----------------
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index b256657bda..40f5cc4f18 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -149,7 +149,7 @@ typedef FormData_pg_class *Form_pg_class;
*/
DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 30 0 t f f f f f f t n f 3 1 _null_ _null_ _null_));
DESCR("");
-DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 22 0 f f f f f f f t n f 3 1 _null_ _null_ _null_));
+DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 23 0 f f f f f f f t n f 3 1 _null_ _null_ _null_));
DESCR("");
DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f p r 29 0 t f f f f f f t n f 3 1 _null_ _null_ _null_));
DESCR("");
diff --git a/src/include/catalog/pg_compression.h b/src/include/catalog/pg_compression.h
new file mode 100644
index 0000000000..1d5f9ac479
--- /dev/null
+++ b/src/include/catalog/pg_compression.h
@@ -0,0 +1,55 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_compression.h
+ * definition of the system "compression method" relation (pg_compression)
+ * along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_compression.h
+ *
+ * NOTES
+ * the genbki.pl script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ * XXX do NOT break up DATA() statements into multiple lines!
+ * the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_COMPRESSION_H
+#define PG_COMPRESSION_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ * pg_compression definition. cpp turns this into
+ * typedef struct FormData_pg_compression
+ * ----------------
+ */
+#define CompressionMethodRelationId 3419
+
+CATALOG(pg_compression,3419)
+{
+ NameData cmname; /* compression method name */
+ regproc cmhandler; /* compression handler */
+} FormData_pg_compression;
+
+/* ----------------
+ * Form_pg_compression corresponds to a pointer to a tuple with
+ * the format of pg_compression relation.
+ * ----------------
+ */
+typedef FormData_pg_compression * Form_pg_compression;
+
+/* ----------------
+ * compiler constants for pg_compression
+ * ----------------
+ */
+#define Natts_pg_compression 2
+#define Anum_pg_compression_cmname 1
+#define Anum_pg_compression_cmhandler 2
+
+#endif /* PG_COMPRESSION_H */
diff --git a/src/include/catalog/pg_compression_opt.h b/src/include/catalog/pg_compression_opt.h
new file mode 100644
index 0000000000..ddcef814a7
--- /dev/null
+++ b/src/include/catalog/pg_compression_opt.h
@@ -0,0 +1,56 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_compression_opt.h
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_compression_opt.h
+ *
+ * NOTES
+ * the genbki.pl script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ * XXX do NOT break up DATA() statements into multiple lines!
+ * the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_COMPRESSION_OPT_H
+#define PG_COMPRESSION_OPT_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ * pg_compression_opt definition. cpp turns this into
+ * typedef struct FormData_pg_compression_opt
+ * ----------------
+ */
+#define CompressionOptRelationId 3420
+
+CATALOG(pg_compression_opt,3420) BKI_WITHOUT_OIDS
+{
+ Oid cmoptoid; /* compression options oid */
+ NameData cmname; /* name of compression method */
+ regproc cmhandler; /* compression handler */
+ text cmoptions[1]; /* specific options from WITH */
+} FormData_pg_compression_opt;
+
+/* ----------------
+ * Form_pg_compression_opt corresponds to a pointer to a tuple with
+ * the format of pg_compression_opt relation.
+ * ----------------
+ */
+typedef FormData_pg_compression_opt * Form_pg_compression_opt;
+
+/* ----------------
+ * compiler constants for pg_compression_opt
+ * ----------------
+ */
+#define Natts_pg_compression_opt 4
+#define Anum_pg_compression_opt_cmoptoid 1
+#define Anum_pg_compression_opt_cmname 2
+#define Anum_pg_compression_opt_cmhandler 3
+#define Anum_pg_compression_opt_cmoptions 4
+
+#endif /* PG_COMPRESSION_OPT_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 0330c04f16..25c2d5c8f2 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3881,6 +3881,10 @@ DATA(insert OID = 3311 ( tsm_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s
DESCR("I/O");
DATA(insert OID = 3312 ( tsm_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3310" _null_ _null_ _null_ _null_ _null_ tsm_handler_out _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 3425 ( compression_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3421 "2275" _null_ _null_ _null_ _null_ _null_ compression_handler_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 3426 ( compression_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3421" _null_ _null_ _null_ _null_ _null_ compression_handler_out _null_ _null_ _null_ ));
+DESCR("I/O");
/* tablesample method handlers */
DATA(insert OID = 3313 ( bernoulli PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3310 "2281" _null_ _null_ _null_ _null_ _null_ tsm_bernoulli_handler _null_ _null_ _null_ ));
@@ -4677,6 +4681,8 @@ DATA(insert OID = 3646 ( gtsvectorin PGNSP PGUID 12 1 0 0 0 f f f f t f i s
DESCR("I/O");
DATA(insert OID = 3647 ( gtsvectorout PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3642" _null_ _null_ _null_ _null_ _null_ gtsvectorout _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 3453 ( tsvector_compression_handler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3421 "2281" _null_ _null_ _null_ _null_ _null_ tsvector_compression_handler _null_ _null_ _null_ ));
+DESCR("tsvector compression handler");
DATA(insert OID = 3616 ( tsvector_lt PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "3614 3614" _null_ _null_ _null_ _null_ _null_ tsvector_lt _null_ _null_ _null_ ));
DATA(insert OID = 3617 ( tsvector_le PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "3614 3614" _null_ _null_ _null_ _null_ _null_ tsvector_le _null_ _null_ _null_ ));
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index e3551440a0..ec8c3df953 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -712,6 +712,8 @@ DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_han
#define TSM_HANDLEROID 3310
DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
#define ANYRANGEOID 3831
+DATA(insert OID = 3421 ( compression_handler PGNSP PGUID 4 t p P f t \054 0 0 0 compression_handler_in compression_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define COMPRESSION_HANDLEROID 3421
/*
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index bfead9af3d..a98ecc12ce 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -140,6 +140,7 @@ extern Oid RemoveUserMapping(DropUserMappingStmt *stmt);
extern void RemoveUserMappingById(Oid umId);
extern void CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid);
extern void ImportForeignSchema(ImportForeignSchemaStmt *stmt);
+extern Datum optionListToArray(List *options);
extern Datum transformGenericOptions(Oid catalogId,
Datum oldOptions,
List *options,
@@ -152,6 +153,14 @@ extern Oid get_index_am_oid(const char *amname, bool missing_ok);
extern Oid get_am_oid(const char *amname, bool missing_ok);
extern char *get_am_name(Oid amOid);
+/* commands/compressioncmds.c */
+extern ObjectAddress DefineCompressionMethod(List *names, List *parameters);
+extern void RemoveCompressionMethodById(Oid cmOid);
+extern void RemoveCompressionOptionsById(Oid cmoptoid);
+extern Oid get_compression_method_oid(const char *cmname, bool missing_ok);
+extern char *get_compression_method_name(Oid cmOid);
+extern char *get_compression_method_name_for_opt(Oid cmoptoid);
+
/* support routines in commands/define.c */
extern char *defGetString(DefElem *def);
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index da3ff5dbee..c50d9525d0 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -24,7 +24,8 @@
extern ObjectAddress DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
- ObjectAddress *typaddress, const char *queryString);
+ ObjectAddress *typaddress, const char *queryString,
+ Node **pAlterStmt);
extern void RemoveRelations(DropStmt *drop);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index ffeeb4919b..6dc49a73b6 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -468,6 +468,7 @@ typedef enum NodeTag
T_PartitionBoundSpec,
T_PartitionRangeDatum,
T_PartitionCmd,
+ T_ColumnCompression,
T_VacuumRelation,
/*
@@ -498,7 +499,8 @@ typedef enum NodeTag
T_FdwRoutine, /* in foreign/fdwapi.h */
T_IndexAmRoutine, /* in access/amapi.h */
T_TsmRoutine, /* in access/tsmapi.h */
- T_ForeignKeyCacheInfo /* in utils/rel.h */
+ T_ForeignKeyCacheInfo, /* in utils/rel.h */
+ T_CompressionMethodRoutine, /* in access/compression.h */
} NodeTag;
/*
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 80c19b2a55..6b25ba3eb0 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -615,6 +615,13 @@ typedef struct RangeTableSample
int location; /* method name location, or -1 if unknown */
} RangeTableSample;
+typedef struct ColumnCompression
+{
+ NodeTag type;
+ char *methodName;
+ List *options;
+} ColumnCompression;
+
/*
* ColumnDef - column definition (used in various creates)
*
@@ -638,6 +645,7 @@ typedef struct ColumnDef
NodeTag type;
char *colname; /* name of column */
TypeName *typeName; /* type of column */
+ ColumnCompression *compression;
int inhcount; /* number of times column is inherited */
bool is_local; /* column has local (non-inherited) def'n */
bool is_not_null; /* NOT NULL constraint specified? */
@@ -1622,6 +1630,7 @@ typedef enum ObjectType
OBJECT_CAST,
OBJECT_COLUMN,
OBJECT_COLLATION,
+ OBJECT_COMPRESSION_METHOD,
OBJECT_CONVERSION,
OBJECT_DATABASE,
OBJECT_DEFAULT,
@@ -1769,7 +1778,8 @@ typedef enum AlterTableType
AT_DetachPartition, /* DETACH PARTITION */
AT_AddIdentity, /* ADD IDENTITY */
AT_SetIdentity, /* SET identity column options */
- AT_DropIdentity /* DROP IDENTITY */
+ AT_DropIdentity, /* DROP IDENTITY */
+ AT_AlterColumnCompression /* ALTER COLUMN name COMPRESSED cm WITH (...) */
} AlterTableType;
typedef struct ReplicaIdentityStmt
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index f50e45e886..7bfc6e6be4 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -87,6 +87,8 @@ PG_KEYWORD("comment", COMMENT, UNRESERVED_KEYWORD)
PG_KEYWORD("comments", COMMENTS, UNRESERVED_KEYWORD)
PG_KEYWORD("commit", COMMIT, UNRESERVED_KEYWORD)
PG_KEYWORD("committed", COMMITTED, UNRESERVED_KEYWORD)
+PG_KEYWORD("compressed", COMPRESSED, UNRESERVED_KEYWORD)
+PG_KEYWORD("compression", COMPRESSION, UNRESERVED_KEYWORD)
PG_KEYWORD("concurrently", CONCURRENTLY, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("configuration", CONFIGURATION, UNRESERVED_KEYWORD)
PG_KEYWORD("conflict", CONFLICT, UNRESERVED_KEYWORD)
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index e749432ef0..5cab77457a 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -22,10 +22,12 @@ extern List *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
const char *queryString);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
+void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
extern List *transformCreateSchemaStmt(CreateSchemaStmt *stmt);
extern PartitionBoundSpec *transformPartitionBound(ParseState *pstate, Relation parent,
PartitionBoundSpec *spec);
+extern void transformColumnCompression(ColumnDef *column, RangeVar *relation,
+ AlterTableStmt **alterStmt);
#endif /* PARSE_UTILCMD_H */
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 1ca9b60ea1..f5c879ae60 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -146,9 +146,18 @@ typedef union
struct /* Compressed-in-line format */
{
uint32 va_header;
- uint32 va_rawsize; /* Original data size (excludes header) */
+ uint32 va_info; /* Original data size (excludes header) and
+ * flags */
char va_data[FLEXIBLE_ARRAY_MEMBER]; /* Compressed data */
} va_compressed;
+ struct /* Compressed-in-line format */
+ {
+ uint32 va_header;
+ uint32 va_info; /* Original data size (excludes header) and
+ * flags */
+ Oid va_cmoptoid; /* Oid of compression options */
+ char va_data[FLEXIBLE_ARRAY_MEMBER]; /* Compressed data */
+ } va_custom_compressed;
} varattrib_4b;
typedef struct
@@ -282,7 +291,12 @@ typedef struct
#define VARDATA_1B_E(PTR) (((varattrib_1b_e *) (PTR))->va_data)
#define VARRAWSIZE_4B_C(PTR) \
- (((varattrib_4b *) (PTR))->va_compressed.va_rawsize)
+ (((varattrib_4b *) (PTR))->va_compressed.va_info & 0x3FFFFFFF)
+#define VARFLAGS_4B_C(PTR) \
+ (((varattrib_4b *) (PTR))->va_compressed.va_info >> 30)
+
+#define VARHDRSZ_CUSTOM_COMPRESSED \
+ (offsetof(varattrib_4b, va_custom_compressed.va_data))
/* Externally visible macros */
@@ -311,6 +325,8 @@ typedef struct
#define VARDATA_EXTERNAL(PTR) VARDATA_1B_E(PTR)
#define VARATT_IS_COMPRESSED(PTR) VARATT_IS_4B_C(PTR)
+#define VARATT_IS_CUSTOM_COMPRESSED(PTR) (VARATT_IS_4B_C(PTR) && \
+ (VARFLAGS_4B_C(PTR) == 0x02))
#define VARATT_IS_EXTERNAL(PTR) VARATT_IS_1B_E(PTR)
#define VARATT_IS_EXTERNAL_ONDISK(PTR) \
(VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_ONDISK)
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 254a811aff..6ad889af7a 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -210,6 +210,7 @@ typedef enum AclObjectKind
ACL_KIND_EXTENSION, /* pg_extension */
ACL_KIND_PUBLICATION, /* pg_publication */
ACL_KIND_SUBSCRIPTION, /* pg_subscription */
+ ACL_KIND_COMPRESSION_METHOD, /* pg_compression */
MAX_ACL_KIND /* MUST BE LAST */
} AclObjectKind;
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 8a0be41929..ff7cb530fd 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -48,6 +48,9 @@ enum SysCacheIdentifier
CLAOID,
COLLNAMEENCNSP,
COLLOID,
+ COMPRESSIONMETHODOID,
+ COMPRESSIONMETHODNAME,
+ COMPRESSIONOPTIONSOID,
CONDEFAULT,
CONNAMENSP,
CONSTROID,
diff --git a/src/test/regress/expected/copy2.out b/src/test/regress/expected/copy2.out
index 65e9c626b3..112f0eda47 100644
--- a/src/test/regress/expected/copy2.out
+++ b/src/test/regress/expected/copy2.out
@@ -438,10 +438,10 @@ begin
end $$ language plpgsql immutable;
alter table check_con_tbl add check (check_con_function(check_con_tbl.*));
\d+ check_con_tbl
- Table "public.check_con_tbl"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- f1 | integer | | | | plain | |
+ Table "public.check_con_tbl"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ f1 | integer | | | | plain | | |
Check constraints:
"check_con_tbl_check" CHECK (check_con_function(check_con_tbl.*))
diff --git a/src/test/regress/expected/create_cm.out b/src/test/regress/expected/create_cm.out
new file mode 100644
index 0000000000..693e5a5e8c
--- /dev/null
+++ b/src/test/regress/expected/create_cm.out
@@ -0,0 +1,108 @@
+CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
+DROP COMPRESSION METHOD ts1;
+CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
+CREATE TABLE cmtest(fts tsvector COMPRESSED ts1);
+DROP COMPRESSION METHOD ts1;
+ERROR: cannot drop compression method ts1 because other objects depend on it
+DETAIL: compression options for ts1 depends on compression method ts1
+table cmtest column fts depends on compression options for ts1
+HINT: Use DROP ... CASCADE to drop the dependent objects too.
+SELECT * FROM pg_compression;
+ cmname | cmhandler
+--------+------------------------------
+ ts1 | tsvector_compression_handler
+(1 row)
+
+SELECT cmhandler, cmoptions FROM pg_compression_opt;
+ cmhandler | cmoptions
+------------------------------+-----------
+ tsvector_compression_handler |
+(1 row)
+
+\dCM
+List of compression methods
+ Name
+------
+ ts1
+(1 row)
+
+\d+ cmtest
+ Table "public.cmtest"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+
+INSERT INTO cmtest
+ SELECT to_tsvector(string_agg(repeat(substr(i::text,1,1), i), ' '))
+ FROM generate_series(1,200) i;
+SELECT length(fts) FROM cmtest;
+ length
+--------
+ 200
+(1 row)
+
+ALTER TABLE cmtest ALTER COLUMN fts SET NOT COMPRESSED;
+\d+ cmtest
+ Table "public.cmtest"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | | |
+
+ALTER TABLE cmtest ALTER COLUMN fts SET COMPRESSED ts1 WITH (format 'lz');
+ERROR: the compression method for tsvector doesn't take any options
+ALTER TABLE cmtest ALTER COLUMN fts SET COMPRESSED ts1;
+\d+ cmtest
+ Table "public.cmtest"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+
+SELECT * INTO cmtest2 FROM cmtest;
+CREATE TABLE cmtest3 (LIKE cmtest);
+CREATE TABLE cmtest4(fts tsvector, a int) inherits (cmtest);
+NOTICE: merging column "fts" with inherited definition
+\d+ cmtest3
+ Table "public.cmtest3"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+
+\d+ cmtest4
+ Table "public.cmtest4"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+ a | integer | | | | plain | | |
+Inherits: cmtest
+
+DROP TABLE cmtest CASCADE;
+NOTICE: drop cascades to table cmtest4
+SELECT length(fts) FROM cmtest2;
+ length
+--------
+ 200
+(1 row)
+
+SELECT * FROM pg_compression;
+ cmname | cmhandler
+--------+------------------------------
+ ts1 | tsvector_compression_handler
+(1 row)
+
+SELECT cmhandler, cmoptions FROM pg_compression_opt;
+ cmhandler | cmoptions
+------------------------------+-----------
+ tsvector_compression_handler |
+ tsvector_compression_handler |
+ tsvector_compression_handler |
+ tsvector_compression_handler |
+(4 rows)
+
+DROP TABLE cmtest2;
+DROP TABLE cmtest3;
+DROP COMPRESSION METHOD ts1 CASCADE;
+NOTICE: drop cascades to 4 other objects
+DETAIL: drop cascades to compression options for ts1
+drop cascades to compression options for ts1
+drop cascades to compression options for ts1
+drop cascades to compression options for ts1
diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out
index 335cd37e18..b511daf9fa 100644
--- a/src/test/regress/expected/create_table.out
+++ b/src/test/regress/expected/create_table.out
@@ -590,10 +590,10 @@ CREATE TABLE oids_parted (
) PARTITION BY RANGE (a) WITH OIDS;
CREATE TABLE part_forced_oids PARTITION OF oids_parted FOR VALUES FROM (1) TO (10) WITHOUT OIDS;
\d+ part_forced_oids
- Table "public.part_forced_oids"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
+ Table "public.part_forced_oids"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
Partition of: oids_parted FOR VALUES FROM (1) TO (10)
Partition constraint: ((a IS NOT NULL) AND (a >= 1) AND (a < 10))
Has OIDs: yes
@@ -729,11 +729,11 @@ CREATE TABLE part_c PARTITION OF parted (b WITH OPTIONS NOT NULL DEFAULT 0) FOR
CREATE TABLE part_c_1_10 PARTITION OF part_c FOR VALUES FROM (1) TO (10);
-- Partition bound in describe output
\d+ part_b
- Table "public.part_b"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | not null | 1 | plain | |
+ Table "public.part_b"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | not null | 1 | plain | | |
Partition of: parted FOR VALUES IN ('b')
Partition constraint: ((a IS NOT NULL) AND (a = ANY (ARRAY['b'::text])))
Check constraints:
@@ -756,11 +756,11 @@ Partitions: part_c_1_10 FOR VALUES FROM (1) TO (10)
-- a level-2 partition's constraint will include the parent's expressions
\d+ part_c_1_10
- Table "public.part_c_1_10"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | not null | 0 | plain | |
+ Table "public.part_c_1_10"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | not null | 0 | plain | | |
Partition of: part_c FOR VALUES FROM (1) TO (10)
Partition constraint: ((a IS NOT NULL) AND (a = ANY (ARRAY['c'::text])) AND (b IS NOT NULL) AND (b >= 1) AND (b < 10))
Check constraints:
@@ -793,46 +793,46 @@ Number of partitions: 3 (Use \d+ to list them.)
CREATE TABLE range_parted4 (a int, b int, c int) PARTITION BY RANGE (abs(a), abs(b), c);
CREATE TABLE unbounded_range_part PARTITION OF range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (MAXVALUE, MAXVALUE, MAXVALUE);
\d+ unbounded_range_part
- Table "public.unbounded_range_part"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
+ Table "public.unbounded_range_part"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
Partition of: range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (MAXVALUE, MAXVALUE, MAXVALUE)
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL))
DROP TABLE unbounded_range_part;
CREATE TABLE range_parted4_1 PARTITION OF range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (1, MAXVALUE, MAXVALUE);
\d+ range_parted4_1
- Table "public.range_parted4_1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
+ Table "public.range_parted4_1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
Partition of: range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (1, MAXVALUE, MAXVALUE)
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND (abs(a) <= 1))
CREATE TABLE range_parted4_2 PARTITION OF range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, MAXVALUE);
\d+ range_parted4_2
- Table "public.range_parted4_2"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
+ Table "public.range_parted4_2"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
Partition of: range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, MAXVALUE)
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND ((abs(a) > 3) OR ((abs(a) = 3) AND (abs(b) > 4)) OR ((abs(a) = 3) AND (abs(b) = 4) AND (c >= 5))) AND ((abs(a) < 6) OR ((abs(a) = 6) AND (abs(b) <= 7))))
CREATE TABLE range_parted4_3 PARTITION OF range_parted4 FOR VALUES FROM (6, 8, MINVALUE) TO (9, MAXVALUE, MAXVALUE);
\d+ range_parted4_3
- Table "public.range_parted4_3"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
+ Table "public.range_parted4_3"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
Partition of: range_parted4 FOR VALUES FROM (6, 8, MINVALUE) TO (9, MAXVALUE, MAXVALUE)
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND ((abs(a) > 6) OR ((abs(a) = 6) AND (abs(b) >= 8))) AND (abs(a) <= 9))
diff --git a/src/test/regress/expected/create_table_like.out b/src/test/regress/expected/create_table_like.out
index 3f405c94ce..b5ca8b820b 100644
--- a/src/test/regress/expected/create_table_like.out
+++ b/src/test/regress/expected/create_table_like.out
@@ -156,32 +156,32 @@ CREATE TABLE ctlt4 (a text, c text);
ALTER TABLE ctlt4 ALTER COLUMN c SET STORAGE EXTERNAL;
CREATE TABLE ctlt12_storage (LIKE ctlt1 INCLUDING STORAGE, LIKE ctlt2 INCLUDING STORAGE);
\d+ ctlt12_storage
- Table "public.ctlt12_storage"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | |
- b | text | | | | extended | |
- c | text | | | | external | |
+ Table "public.ctlt12_storage"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | |
+ b | text | | | | extended | | |
+ c | text | | | | external | | |
CREATE TABLE ctlt12_comments (LIKE ctlt1 INCLUDING COMMENTS, LIKE ctlt2 INCLUDING COMMENTS);
\d+ ctlt12_comments
- Table "public.ctlt12_comments"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | extended | | A
- b | text | | | | extended | | B
- c | text | | | | extended | | C
+ Table "public.ctlt12_comments"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | extended | | | A
+ b | text | | | | extended | | | B
+ c | text | | | | extended | | | C
CREATE TABLE ctlt1_inh (LIKE ctlt1 INCLUDING CONSTRAINTS INCLUDING COMMENTS) INHERITS (ctlt1);
NOTICE: merging column "a" with inherited definition
NOTICE: merging column "b" with inherited definition
NOTICE: merging constraint "ctlt1_a_check" with inherited definition
\d+ ctlt1_inh
- Table "public.ctlt1_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | | A
- b | text | | | | extended | | B
+ Table "public.ctlt1_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | | A
+ b | text | | | | extended | | | B
Check constraints:
"ctlt1_a_check" CHECK (length(a) > 2)
Inherits: ctlt1
@@ -195,12 +195,12 @@ SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_con
CREATE TABLE ctlt13_inh () INHERITS (ctlt1, ctlt3);
NOTICE: merging multiple inherited definitions of column "a"
\d+ ctlt13_inh
- Table "public.ctlt13_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | |
- b | text | | | | extended | |
- c | text | | | | external | |
+ Table "public.ctlt13_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | |
+ b | text | | | | extended | | |
+ c | text | | | | external | | |
Check constraints:
"ctlt1_a_check" CHECK (length(a) > 2)
"ctlt3_a_check" CHECK (length(a) < 5)
@@ -210,12 +210,12 @@ Inherits: ctlt1,
CREATE TABLE ctlt13_like (LIKE ctlt3 INCLUDING CONSTRAINTS INCLUDING COMMENTS INCLUDING STORAGE) INHERITS (ctlt1);
NOTICE: merging column "a" with inherited definition
\d+ ctlt13_like
- Table "public.ctlt13_like"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | | A3
- b | text | | | | extended | |
- c | text | | | | external | | C
+ Table "public.ctlt13_like"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | | A3
+ b | text | | | | extended | | |
+ c | text | | | | external | | | C
Check constraints:
"ctlt1_a_check" CHECK (length(a) > 2)
"ctlt3_a_check" CHECK (length(a) < 5)
@@ -229,11 +229,11 @@ SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_con
CREATE TABLE ctlt_all (LIKE ctlt1 INCLUDING ALL);
\d+ ctlt_all
- Table "public.ctlt_all"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | | A
- b | text | | | | extended | | B
+ Table "public.ctlt_all"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | | A
+ b | text | | | | extended | | | B
Indexes:
"ctlt_all_pkey" PRIMARY KEY, btree (a)
"ctlt_all_b_idx" btree (b)
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index f4eebb75cf..2ac75c44b5 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -272,10 +272,10 @@ explain (verbose, costs off)
create rule silly as on delete to dcomptable do instead
update dcomptable set d1.r = (d1).r - 1, d1.i = (d1).i + 1 where (d1).i > 0;
\d+ dcomptable
- Table "public.dcomptable"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+-----------+-----------+----------+---------+----------+--------------+-------------
- d1 | dcomptype | | | | extended | |
+ Table "public.dcomptable"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+-----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ d1 | dcomptype | | | | extended | | |
Indexes:
"dcomptable_d1_key" UNIQUE CONSTRAINT, btree (d1)
Rules:
@@ -409,10 +409,10 @@ create rule silly as on delete to dcomptable do instead
update dcomptable set d1[1].r = d1[1].r - 1, d1[1].i = d1[1].i + 1
where d1[1].i > 0;
\d+ dcomptable
- Table "public.dcomptable"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------------+-----------+----------+---------+----------+--------------+-------------
- d1 | dcomptypea | | | | extended | |
+ Table "public.dcomptable"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------------+-----------+----------+---------+----------+-------------+--------------+-------------
+ d1 | dcomptypea | | | | extended | | |
Indexes:
"dcomptable_d1_key" UNIQUE CONSTRAINT, btree (d1)
Rules:
diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out
index 331f7a911f..530ce1b1d0 100644
--- a/src/test/regress/expected/foreign_data.out
+++ b/src/test/regress/expected/foreign_data.out
@@ -1330,12 +1330,12 @@ CREATE TABLE pt1 (
CREATE FOREIGN TABLE ft2 () INHERITS (pt1)
SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value');
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Child tables: ft2
\d+ ft2
@@ -1351,12 +1351,12 @@ Inherits: pt1
DROP FOREIGN TABLE ft2;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
CREATE FOREIGN TABLE ft2 (
c1 integer NOT NULL,
@@ -1375,12 +1375,12 @@ FDW options: (delimiter ',', quote '"', "be quoted" 'value')
ALTER FOREIGN TABLE ft2 INHERIT pt1;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Child tables: ft2
\d+ ft2
@@ -1418,12 +1418,12 @@ Child tables: ct3,
ft3
\d+ ct3
- Table "public.ct3"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.ct3"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Inherits: ft2
\d+ ft3
@@ -1443,17 +1443,17 @@ ALTER TABLE pt1 ADD COLUMN c6 integer;
ALTER TABLE pt1 ADD COLUMN c7 integer NOT NULL;
ALTER TABLE pt1 ADD COLUMN c8 integer;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
- c4 | integer | | | | plain | |
- c5 | integer | | | 0 | plain | |
- c6 | integer | | | | plain | |
- c7 | integer | | not null | | plain | |
- c8 | integer | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
+ c4 | integer | | | | plain | | |
+ c5 | integer | | | 0 | plain | | |
+ c6 | integer | | | | plain | | |
+ c7 | integer | | not null | | plain | | |
+ c8 | integer | | | | plain | | |
Child tables: ft2
\d+ ft2
@@ -1475,17 +1475,17 @@ Child tables: ct3,
ft3
\d+ ct3
- Table "public.ct3"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
- c4 | integer | | | | plain | |
- c5 | integer | | | 0 | plain | |
- c6 | integer | | | | plain | |
- c7 | integer | | not null | | plain | |
- c8 | integer | | | | plain | |
+ Table "public.ct3"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
+ c4 | integer | | | | plain | | |
+ c5 | integer | | | 0 | plain | | |
+ c6 | integer | | | | plain | | |
+ c7 | integer | | not null | | plain | | |
+ c8 | integer | | | | plain | | |
Inherits: ft2
\d+ ft3
@@ -1517,17 +1517,17 @@ ALTER TABLE pt1 ALTER COLUMN c1 SET (n_distinct = 100);
ALTER TABLE pt1 ALTER COLUMN c8 SET STATISTICS -1;
ALTER TABLE pt1 ALTER COLUMN c8 SET STORAGE EXTERNAL;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
- c4 | integer | | | 0 | plain | |
- c5 | integer | | | | plain | |
- c6 | integer | | not null | | plain | |
- c7 | integer | | | | plain | |
- c8 | text | | | | external | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
+ c4 | integer | | | 0 | plain | | |
+ c5 | integer | | | | plain | | |
+ c6 | integer | | not null | | plain | | |
+ c7 | integer | | | | plain | | |
+ c8 | text | | | | external | | |
Child tables: ft2
\d+ ft2
@@ -1555,12 +1555,12 @@ ALTER TABLE pt1 DROP COLUMN c6;
ALTER TABLE pt1 DROP COLUMN c7;
ALTER TABLE pt1 DROP COLUMN c8;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Child tables: ft2
\d+ ft2
@@ -1592,12 +1592,12 @@ SELECT relname, conname, contype, conislocal, coninhcount, connoinherit
-- child does not inherit NO INHERIT constraints
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk1" CHECK (c1 > 0) NO INHERIT
"pt1chk2" CHECK (c2 <> ''::text)
@@ -1636,12 +1636,12 @@ ALTER FOREIGN TABLE ft2 ADD CONSTRAINT pt1chk2 CHECK (c2 <> '');
ALTER FOREIGN TABLE ft2 INHERIT pt1;
-- child does not inherit NO INHERIT constraints
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk1" CHECK (c1 > 0) NO INHERIT
"pt1chk2" CHECK (c2 <> ''::text)
@@ -1667,12 +1667,12 @@ ALTER TABLE pt1 DROP CONSTRAINT pt1chk2 CASCADE;
INSERT INTO pt1 VALUES (1, 'pt1'::text, '1994-01-01'::date);
ALTER TABLE pt1 ADD CONSTRAINT pt1chk3 CHECK (c2 <> '') NOT VALID;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk3" CHECK (c2 <> ''::text) NOT VALID
Child tables: ft2
@@ -1694,12 +1694,12 @@ Inherits: pt1
-- VALIDATE CONSTRAINT need do nothing on foreign tables
ALTER TABLE pt1 VALIDATE CONSTRAINT pt1chk3;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk3" CHECK (c2 <> ''::text)
Child tables: ft2
@@ -1721,12 +1721,12 @@ Inherits: pt1
-- OID system column
ALTER TABLE pt1 SET WITH OIDS;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk3" CHECK (c2 <> ''::text)
Child tables: ft2
@@ -1751,12 +1751,12 @@ ALTER TABLE ft2 SET WITHOUT OIDS; -- ERROR
ERROR: cannot drop inherited column "oid"
ALTER TABLE pt1 SET WITHOUT OIDS;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk3" CHECK (c2 <> ''::text)
Child tables: ft2
@@ -1782,12 +1782,12 @@ ALTER TABLE pt1 RENAME COLUMN c3 TO f3;
-- changes name of a constraint recursively
ALTER TABLE pt1 RENAME CONSTRAINT pt1chk3 TO f2_check;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- f1 | integer | | not null | | plain | 10000 |
- f2 | text | | | | extended | |
- f3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ f1 | integer | | not null | | plain | | 10000 |
+ f2 | text | | | | extended | | |
+ f3 | date | | | | plain | | |
Check constraints:
"f2_check" CHECK (f2 <> ''::text)
Child tables: ft2
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index c698faff2f..a147b8217f 100644
--- a/src/test/regress/expected/inherit.out
+++ b/src/test/regress/expected/inherit.out
@@ -1023,13 +1023,13 @@ ALTER TABLE inhts RENAME aa TO aaa; -- to be failed
ERROR: cannot rename inherited column "aa"
ALTER TABLE inhts RENAME d TO dd;
\d+ inhts
- Table "public.inhts"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- aa | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
- dd | integer | | | | plain | |
+ Table "public.inhts"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ aa | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
+ dd | integer | | | | plain | | |
Inherits: inht1,
inhs1
@@ -1042,14 +1042,14 @@ NOTICE: merging multiple inherited definitions of column "aa"
NOTICE: merging multiple inherited definitions of column "b"
ALTER TABLE inht1 RENAME aa TO aaa;
\d+ inht4
- Table "public.inht4"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- aaa | integer | | | | plain | |
- b | integer | | | | plain | |
- x | integer | | | | plain | |
- y | integer | | | | plain | |
- z | integer | | | | plain | |
+ Table "public.inht4"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ aaa | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ x | integer | | | | plain | | |
+ y | integer | | | | plain | | |
+ z | integer | | | | plain | | |
Inherits: inht2,
inht3
@@ -1059,14 +1059,14 @@ ALTER TABLE inht1 RENAME aaa TO aaaa;
ALTER TABLE inht1 RENAME b TO bb; -- to be failed
ERROR: cannot rename inherited column "b"
\d+ inhts
- Table "public.inhts"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- aaaa | integer | | | | plain | |
- b | integer | | | | plain | |
- x | integer | | | | plain | |
- c | integer | | | | plain | |
- d | integer | | | | plain | |
+ Table "public.inhts"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ aaaa | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ x | integer | | | | plain | | |
+ c | integer | | | | plain | | |
+ d | integer | | | | plain | | |
Inherits: inht2,
inhs1
@@ -1106,33 +1106,33 @@ drop cascades to table inht4
CREATE TABLE test_constraints (id int, val1 varchar, val2 int, UNIQUE(val1, val2));
CREATE TABLE test_constraints_inh () INHERITS (test_constraints);
\d+ test_constraints
- Table "public.test_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+-------------------+-----------+----------+---------+----------+--------------+-------------
- id | integer | | | | plain | |
- val1 | character varying | | | | extended | |
- val2 | integer | | | | plain | |
+ Table "public.test_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+-------------------+-----------+----------+---------+----------+-------------+--------------+-------------
+ id | integer | | | | plain | | |
+ val1 | character varying | | | | extended | | |
+ val2 | integer | | | | plain | | |
Indexes:
"test_constraints_val1_val2_key" UNIQUE CONSTRAINT, btree (val1, val2)
Child tables: test_constraints_inh
ALTER TABLE ONLY test_constraints DROP CONSTRAINT test_constraints_val1_val2_key;
\d+ test_constraints
- Table "public.test_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+-------------------+-----------+----------+---------+----------+--------------+-------------
- id | integer | | | | plain | |
- val1 | character varying | | | | extended | |
- val2 | integer | | | | plain | |
+ Table "public.test_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+-------------------+-----------+----------+---------+----------+-------------+--------------+-------------
+ id | integer | | | | plain | | |
+ val1 | character varying | | | | extended | | |
+ val2 | integer | | | | plain | | |
Child tables: test_constraints_inh
\d+ test_constraints_inh
- Table "public.test_constraints_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+-------------------+-----------+----------+---------+----------+--------------+-------------
- id | integer | | | | plain | |
- val1 | character varying | | | | extended | |
- val2 | integer | | | | plain | |
+ Table "public.test_constraints_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+-------------------+-----------+----------+---------+----------+-------------+--------------+-------------
+ id | integer | | | | plain | | |
+ val1 | character varying | | | | extended | | |
+ val2 | integer | | | | plain | | |
Inherits: test_constraints
DROP TABLE test_constraints_inh;
@@ -1143,27 +1143,27 @@ CREATE TABLE test_ex_constraints (
);
CREATE TABLE test_ex_constraints_inh () INHERITS (test_ex_constraints);
\d+ test_ex_constraints
- Table "public.test_ex_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+--------+-----------+----------+---------+---------+--------------+-------------
- c | circle | | | | plain | |
+ Table "public.test_ex_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+--------+-----------+----------+---------+---------+-------------+--------------+-------------
+ c | circle | | | | plain | | |
Indexes:
"test_ex_constraints_c_excl" EXCLUDE USING gist (c WITH &&)
Child tables: test_ex_constraints_inh
ALTER TABLE test_ex_constraints DROP CONSTRAINT test_ex_constraints_c_excl;
\d+ test_ex_constraints
- Table "public.test_ex_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+--------+-----------+----------+---------+---------+--------------+-------------
- c | circle | | | | plain | |
+ Table "public.test_ex_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+--------+-----------+----------+---------+---------+-------------+--------------+-------------
+ c | circle | | | | plain | | |
Child tables: test_ex_constraints_inh
\d+ test_ex_constraints_inh
- Table "public.test_ex_constraints_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+--------+-----------+----------+---------+---------+--------------+-------------
- c | circle | | | | plain | |
+ Table "public.test_ex_constraints_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+--------+-----------+----------+---------+---------+-------------+--------------+-------------
+ c | circle | | | | plain | | |
Inherits: test_ex_constraints
DROP TABLE test_ex_constraints_inh;
@@ -1173,37 +1173,37 @@ CREATE TABLE test_primary_constraints(id int PRIMARY KEY);
CREATE TABLE test_foreign_constraints(id1 int REFERENCES test_primary_constraints(id));
CREATE TABLE test_foreign_constraints_inh () INHERITS (test_foreign_constraints);
\d+ test_primary_constraints
- Table "public.test_primary_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- id | integer | | not null | | plain | |
+ Table "public.test_primary_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ id | integer | | not null | | plain | | |
Indexes:
"test_primary_constraints_pkey" PRIMARY KEY, btree (id)
Referenced by:
TABLE "test_foreign_constraints" CONSTRAINT "test_foreign_constraints_id1_fkey" FOREIGN KEY (id1) REFERENCES test_primary_constraints(id)
\d+ test_foreign_constraints
- Table "public.test_foreign_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- id1 | integer | | | | plain | |
+ Table "public.test_foreign_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ id1 | integer | | | | plain | | |
Foreign-key constraints:
"test_foreign_constraints_id1_fkey" FOREIGN KEY (id1) REFERENCES test_primary_constraints(id)
Child tables: test_foreign_constraints_inh
ALTER TABLE test_foreign_constraints DROP CONSTRAINT test_foreign_constraints_id1_fkey;
\d+ test_foreign_constraints
- Table "public.test_foreign_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- id1 | integer | | | | plain | |
+ Table "public.test_foreign_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ id1 | integer | | | | plain | | |
Child tables: test_foreign_constraints_inh
\d+ test_foreign_constraints_inh
- Table "public.test_foreign_constraints_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- id1 | integer | | | | plain | |
+ Table "public.test_foreign_constraints_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ id1 | integer | | | | plain | | |
Inherits: test_foreign_constraints
DROP TABLE test_foreign_constraints_inh;
diff --git a/src/test/regress/expected/insert.out b/src/test/regress/expected/insert.out
index 9d84ba4658..bbab453247 100644
--- a/src/test/regress/expected/insert.out
+++ b/src/test/regress/expected/insert.out
@@ -142,11 +142,11 @@ create rule irule3 as on insert to inserttest2 do also
insert into inserttest (f4[1].if1, f4[1].if2[2])
select new.f1, new.f2;
\d+ inserttest2
- Table "public.inserttest2"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+--------+-----------+----------+---------+----------+--------------+-------------
- f1 | bigint | | | | plain | |
- f2 | text | | | | extended | |
+ Table "public.inserttest2"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+--------+-----------+----------+---------+----------+-------------+--------------+-------------
+ f1 | bigint | | | | plain | | |
+ f2 | text | | | | extended | | |
Rules:
irule1 AS
ON INSERT TO inserttest2 DO INSERT INTO inserttest (f3.if2[1], f3.if2[2])
@@ -435,10 +435,10 @@ drop function dummy_hashint4(a int4, seed int8);
create table list_parted (a int) partition by list (a);
create table part_default partition of list_parted default;
\d+ part_default
- Table "public.part_default"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
+ Table "public.part_default"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
Partition of: list_parted DEFAULT
No partition constraint
@@ -750,74 +750,74 @@ Partitions: mcrparted1_lt_b FOR VALUES FROM (MINVALUE, MINVALUE) TO ('b', MINVAL
mcrparted8_ge_d FOR VALUES FROM ('d', MINVALUE) TO (MAXVALUE, MAXVALUE)
\d+ mcrparted1_lt_b
- Table "public.mcrparted1_lt_b"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted1_lt_b"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM (MINVALUE, MINVALUE) TO ('b', MINVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a < 'b'::text))
\d+ mcrparted2_b
- Table "public.mcrparted2_b"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted2_b"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('b', MINVALUE) TO ('c', MINVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'b'::text) AND (a < 'c'::text))
\d+ mcrparted3_c_to_common
- Table "public.mcrparted3_c_to_common"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted3_c_to_common"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('c', MINVALUE) TO ('common', MINVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'c'::text) AND (a < 'common'::text))
\d+ mcrparted4_common_lt_0
- Table "public.mcrparted4_common_lt_0"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted4_common_lt_0"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('common', MINVALUE) TO ('common', 0)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::text) AND (b < 0))
\d+ mcrparted5_common_0_to_10
- Table "public.mcrparted5_common_0_to_10"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted5_common_0_to_10"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('common', 0) TO ('common', 10)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::text) AND (b >= 0) AND (b < 10))
\d+ mcrparted6_common_ge_10
- Table "public.mcrparted6_common_ge_10"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted6_common_ge_10"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('common', 10) TO ('common', MAXVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::text) AND (b >= 10))
\d+ mcrparted7_gt_common_lt_d
- Table "public.mcrparted7_gt_common_lt_d"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted7_gt_common_lt_d"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('common', MAXVALUE) TO ('d', MINVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a > 'common'::text) AND (a < 'd'::text))
\d+ mcrparted8_ge_d
- Table "public.mcrparted8_ge_d"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted8_ge_d"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('d', MINVALUE) TO (MAXVALUE, MAXVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'd'::text))
diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out
index b101331d69..f805e13d75 100644
--- a/src/test/regress/expected/publication.out
+++ b/src/test/regress/expected/publication.out
@@ -65,11 +65,11 @@ SELECT pubname, puballtables FROM pg_publication WHERE pubname = 'testpub_forall
(1 row)
\d+ testpub_tbl2
- Table "public.testpub_tbl2"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('testpub_tbl2_id_seq'::regclass) | plain | |
- data | text | | | | extended | |
+ Table "public.testpub_tbl2"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('testpub_tbl2_id_seq'::regclass) | plain | | |
+ data | text | | | | extended | | |
Indexes:
"testpub_tbl2_pkey" PRIMARY KEY, btree (id)
Publications:
@@ -141,22 +141,22 @@ ALTER PUBLICATION testpub_default SET TABLE testpub_tbl1;
ALTER PUBLICATION testpub_default ADD TABLE pub_test.testpub_nopk;
ALTER PUBLICATION testpib_ins_trunct ADD TABLE pub_test.testpub_nopk, testpub_tbl1;
\d+ pub_test.testpub_nopk
- Table "pub_test.testpub_nopk"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- foo | integer | | | | plain | |
- bar | integer | | | | plain | |
+ Table "pub_test.testpub_nopk"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ foo | integer | | | | plain | | |
+ bar | integer | | | | plain | | |
Publications:
"testpib_ins_trunct"
"testpub_default"
"testpub_fortbl"
\d+ testpub_tbl1
- Table "public.testpub_tbl1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | |
- data | text | | | | extended | |
+ Table "public.testpub_tbl1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | | |
+ data | text | | | | extended | | |
Indexes:
"testpub_tbl1_pkey" PRIMARY KEY, btree (id)
Publications:
@@ -178,11 +178,11 @@ ALTER PUBLICATION testpub_default DROP TABLE testpub_tbl1, pub_test.testpub_nopk
ALTER PUBLICATION testpub_default DROP TABLE pub_test.testpub_nopk;
ERROR: relation "testpub_nopk" is not part of the publication
\d+ testpub_tbl1
- Table "public.testpub_tbl1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | |
- data | text | | | | extended | |
+ Table "public.testpub_tbl1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | | |
+ data | text | | | | extended | | |
Indexes:
"testpub_tbl1_pkey" PRIMARY KEY, btree (id)
Publications:
diff --git a/src/test/regress/expected/replica_identity.out b/src/test/regress/expected/replica_identity.out
index 67c34a92a4..1526437bf8 100644
--- a/src/test/regress/expected/replica_identity.out
+++ b/src/test/regress/expected/replica_identity.out
@@ -158,13 +158,13 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass;
(1 row)
\d+ test_replica_identity
- Table "public.test_replica_identity"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('test_replica_identity_id_seq'::regclass) | plain | |
- keya | text | | not null | | extended | |
- keyb | text | | not null | | extended | |
- nonkey | text | | | | extended | |
+ Table "public.test_replica_identity"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('test_replica_identity_id_seq'::regclass) | plain | | |
+ keya | text | | not null | | extended | | |
+ keyb | text | | not null | | extended | | |
+ nonkey | text | | | | extended | | |
Indexes:
"test_replica_identity_pkey" PRIMARY KEY, btree (id)
"test_replica_identity_expr" UNIQUE, btree (keya, keyb, (3))
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index f1c1b44d6f..e358ff539c 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2799,11 +2799,11 @@ select * from rules_log;
create rule r3 as on delete to rules_src do notify rules_src_deletion;
\d+ rules_src
- Table "public.rules_src"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- f1 | integer | | | | plain | |
- f2 | integer | | | | plain | |
+ Table "public.rules_src"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ f1 | integer | | | | plain | | |
+ f2 | integer | | | | plain | | |
Rules:
r1 AS
ON UPDATE TO rules_src DO INSERT INTO rules_log (f1, f2, tag) VALUES (old.f1,old.f2,'old'::text), (new.f1,new.f2,'new'::text)
@@ -2819,11 +2819,11 @@ Rules:
create rule r4 as on insert to rules_src do instead insert into rules_log AS trgt SELECT NEW.* RETURNING trgt.f1, trgt.f2;
create rule r5 as on update to rules_src do instead UPDATE rules_log AS trgt SET tag = 'updated' WHERE trgt.f1 = new.f1;
\d+ rules_src
- Table "public.rules_src"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- f1 | integer | | | | plain | |
- f2 | integer | | | | plain | |
+ Table "public.rules_src"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ f1 | integer | | | | plain | | |
+ f2 | integer | | | | plain | | |
Rules:
r1 AS
ON UPDATE TO rules_src DO INSERT INTO rules_log (f1, f2, tag) VALUES (old.f1,old.f2,'old'::text), (new.f1,new.f2,'new'::text)
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index e996640593..62b06da011 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -111,6 +111,8 @@ pg_authid|t
pg_cast|t
pg_class|t
pg_collation|t
+pg_compression|t
+pg_compression_opt|t
pg_constraint|t
pg_conversion|t
pg_database|t
diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out
index a4fe96112e..adbe764196 100644
--- a/src/test/regress/expected/update.out
+++ b/src/test/regress/expected/update.out
@@ -221,11 +221,11 @@ update range_parted set b = b + 1 where b = 10;
-- Creating default partition for range
create table part_def partition of range_parted default;
\d+ part_def
- Table "public.part_def"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.part_def"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: range_parted DEFAULT
Partition constraint: (NOT (((a = 'a'::text) AND (b >= 1) AND (b < 10)) OR ((a = 'a'::text) AND (b >= 10) AND (b < 20)) OR ((a = 'b'::text) AND (b >= 1) AND (b < 10)) OR ((a = 'b'::text) AND (b >= 10) AND (b < 20))))
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index aa5e6af621..a44cf1c910 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -30,7 +30,7 @@ test: point lseg line box path polygon circle date time timetz timestamp timesta
# geometry depends on point, lseg, box, path, polygon and circle
# horology depends on interval, timetz, timestamp, timestamptz, reltime and abstime
# ----------
-test: geometry horology regex oidjoins type_sanity opr_sanity misc_sanity comments expressions
+test: geometry horology regex oidjoins type_sanity opr_sanity misc_sanity comments expressions create_cm
# ----------
# These four each depend on the previous one
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 3866314a92..5c72cb16f5 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -43,6 +43,7 @@ test: inet
test: macaddr
test: macaddr8
test: tstypes
+test: create_cm
test: geometry
test: horology
test: regex
diff --git a/src/test/regress/sql/create_cm.sql b/src/test/regress/sql/create_cm.sql
new file mode 100644
index 0000000000..83307597e7
--- /dev/null
+++ b/src/test/regress/sql/create_cm.sql
@@ -0,0 +1,37 @@
+CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
+DROP COMPRESSION METHOD ts1;
+CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
+CREATE TABLE cmtest(fts tsvector COMPRESSED ts1);
+DROP COMPRESSION METHOD ts1;
+SELECT * FROM pg_compression;
+SELECT cmhandler, cmoptions FROM pg_compression_opt;
+
+\dCM
+\d+ cmtest
+
+INSERT INTO cmtest
+ SELECT to_tsvector(string_agg(repeat(substr(i::text,1,1), i), ' '))
+ FROM generate_series(1,200) i;
+SELECT length(fts) FROM cmtest;
+
+ALTER TABLE cmtest ALTER COLUMN fts SET NOT COMPRESSED;
+\d+ cmtest
+ALTER TABLE cmtest ALTER COLUMN fts SET COMPRESSED ts1 WITH (format 'lz');
+ALTER TABLE cmtest ALTER COLUMN fts SET COMPRESSED ts1;
+\d+ cmtest
+
+SELECT * INTO cmtest2 FROM cmtest;
+CREATE TABLE cmtest3 (LIKE cmtest);
+CREATE TABLE cmtest4(fts tsvector, a int) inherits (cmtest);
+\d+ cmtest3
+\d+ cmtest4
+DROP TABLE cmtest CASCADE;
+
+SELECT length(fts) FROM cmtest2;
+SELECT * FROM pg_compression;
+SELECT cmhandler, cmoptions FROM pg_compression_opt;
+
+DROP TABLE cmtest2;
+DROP TABLE cmtest3;
+
+DROP COMPRESSION METHOD ts1 CASCADE;
diff --git a/src/tools/pgindent/exclude_file_patterns b/src/tools/pgindent/exclude_file_patterns
index cb2f902a90..95b9f0160f 100644
--- a/src/tools/pgindent/exclude_file_patterns
+++ b/src/tools/pgindent/exclude_file_patterns
@@ -5,3 +5,4 @@
/ecpg/test/expected/
/snowball/libstemmer/
/pl/plperl/ppport\.h$
+/tmp_install.*$
On Sun, 5 Nov 2017 17:34:23 -0500
Robert Haas <robertmhaas@gmail.com> wrote:
On Sun, Nov 5, 2017 at 2:22 PM, Oleg Bartunov <obartunov@gmail.com>
wrote:IIRC there were some concerns about what happened with pg_upgrade,
with consuming precious toast bits, and a few other things.yes, pg_upgrade may be a problem.
A basic problem here is that, as proposed, DROP COMPRESSION METHOD may
break your database irretrievably. If there's no data compressed
using the compression method you dropped, everything is cool -
otherwise everything is broken and there's no way to recover. The
only obvious alternative is to disallow DROP altogether (or make it
not really DROP).
In the patch I use separate table for compresssion options (because
each attribute can have additional options for compression). So basicly
compressed attribute linked to compression options, not the compression
method and this method can be safely dropped.
So in the next version of the patch I can just unlink the options from
compression methods and dropping compression method will not affect
already compressed tuples. They still could be decompressed.
--
---
Ildus Kurbangaliev
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company
On Wed, Nov 15, 2017 at 4:09 AM, Ildus Kurbangaliev
<i.kurbangaliev@postgrespro.ru> wrote:
So in the next version of the patch I can just unlink the options from
compression methods and dropping compression method will not affect
already compressed tuples. They still could be decompressed.
I guess I don't understand how that can work. I mean, if somebody
removes a compression method - i.e. uninstalls the library - and you
don't have a way to make sure there are no tuples that can only be
uncompressed by that library - then you've broken the database.
Ideally, there should be a way to add a new compression method via an
extension ... and then get rid of it and all dependencies thereupon.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Hi Ildus,
On 14.11.2017 16:23, Ildus Kurbangaliev wrote:
On Thu, 2 Nov 2017 15:28:36 +0300 Ildus Kurbangaliev
<i.kurbangaliev@postgrespro.ru> wrote:On Tue, 12 Sep 2017 17:55:05 +0300 Ildus Kurbangaliev
<i.kurbangaliev@postgrespro.ru> wrote:Attached rebased version of the patch. Added support of pg_dump,
the code was simplified, and a separate cache for compression
options was added.Attached version 3 of the patch. Rebased to the current master,
removed ALTER TYPE .. SET COMPRESSED syntax, fixed bug in
compression options cache.Attached version 4 of the patch. Fixed pg_upgrade and few other
bugs.
I've started to review your code. And even though it's fine overall I
have few questions and comments (aside from DROP COMPRESSION METHOD
discussion).
1. I'm not sure about proposed syntax for ALTER TABLE command:
ALTER TABLE t ALTER COLUMN a SET COMPRESSED <cmname> WITH
(<options>); ALTER TABLE t ALTER COLUMN a SET NOT COMPRESSED;
ISTM it is more common for Postgres to use syntax like SET/DROP for
column options (SET/DROP NOT NULL, DEFAULT etc). My suggestion would be:
ALTER TABLE t ALTER COLUMN a SET COMPRESSED USING <compression_method>
WITH (<options>);
ALTER TABLE t ALTER COLUMN a DROP COMPRESSED;
(keyword USING here is similar to "CREATE INDEX ... USING <method>" syntax)
2. The way you changed DefineRelation() implies that caller is
responsible for creation of compression options. Probably it would be
better to create them within DefineRelation().
3. Few minor issues which seem like obsolete code:
Function freeRelOptions() is defined but never used.
Function getBaseTypeTuple() has been extracted from
getBaseTypeAndTypmod() but never used separately.
In toast_flatten_tuple_to_datum() there is untoasted_value variable
which is only used for meaningless assignment.
(Should I send a patch for that kind of issues?)
--
Ildar Musin
i.musin@postgrespro.ru
Hi,
On 11/14/2017 02:23 PM, Ildus Kurbangaliev wrote:
...
Attached version 4 of the patch. Fixed pg_upgrade and few other bugs.
I did a review of this today, and I think there are some things that
need improvement / fixing.
Firstly, some basic comments from just eye-balling the diff, then some
bugs I discovered after writing an extension adding lz4.
1) formatRelOptions/freeRelOptions are no longer needed (I see Ildar
already pointer that out)
2) There's unnecessary whitespace (extra newlines) on a couple of
places, which is needlessly increasing the size of the patch. Small
difference, but annoying.
3) tuptoaster.c
Why do you change 'info' from int32 to uint32? Seems unnecessary.
Adding new 'att' variable in toast_insert_or_update is confusing, as
there already is 'att' in the very next loop. Technically it's correct,
but I'd bet it'll lead to some WTF?! moments later. I propose to just
use TupleDescAttr(tupleDesc,i) on the two places where it matters,
around line 808.
There are no comments for init_compression_options_htab and
get_compression_options_info, so that needs to be fixed. Moreover, the
names are confusing because what we really get is not just 'options' but
the compression routines too.
4) gen_db_file_maps probably shouldn't do the fprints, right?
5) not sure why you modify src/tools/pgindent/exclude_file_patterns
6) I'm rather confused by AttributeCompression vs. ColumnCompression. I
mean, attribute==column, right? Of course, one is for data from parser,
the other one is for internal info. But can we make the naming clearer?
7) The docs in general are somewhat unsatisfactory, TBH. For example the
ColumnCompression has no comments, unlike everything else in parsenodes.
Similarly for the SGML docs - I suggest to expand them to resemble FDW
docs (https://www.postgresql.org/docs/10/static/fdwhandler.html) which
also follows the handler/routines pattern.
8) One of the unclear things if why we even need 'drop' routing. It
seems that if it's defined DropAttributeCompression does something. But
what should it do? I suppose dropping the options should be done using
dependencies (just like we drop columns in this case).
BTW why does DropAttributeCompression mess with att->attisdropped in
this way? That seems a bit odd.
9) configure routines that only check if (options != NIL) and then error
out (like tsvector_configure) seem a bit unnecessary. Just allow it to
be NULL in CompressionMethodRoutine, and throw an error if options is
not NIL for such compression method.
10) toast_compress_datum still does this:
if (!ac && (valsize < PGLZ_strategy_default->min_input_size ||
valsize > PGLZ_strategy_default->max_input_size))
which seems rather pglz-specific (the naming is a hint). Why shouldn't
this be specific to compression, exposed either as min/max constants, or
wrapped in another routine - size_is_valid() or something like that?
11) The comments in toast_compress_datum probably need updating, as it
still references to pglz specifically. I guess the new compression
methods do matter too.
12) get_compression_options_info organizes the compression info into a
hash table by OID. The hash table implementation assumes the hash key is
at the beginning of the entry, but AttributeCompression is defined like
this:
typedef struct
{
CompressionMethodRoutine *routine;
List *options;
Oid cmoptoid;
} AttributeCompression;
Which means get_compression_options_info is busted, will never lookup
anything, and the hash table will grow by adding more and more entries
into the same bucket. Of course, this has extremely negative impact on
performance (pretty much arbitrarily bad, depending on how many entries
you've already added to the hash table).
Moving the OID to the beginning of the struct fixes the issue.
13) When writing the experimental extension, I was extremely confused
about the regular varlena headers, custom compression headers, etc. In
the end I stole the code from tsvector.c and whacked it a bit until it
worked, but I wouldn't dare to claim I understand how it works.
This needs to be documented somewhere. For example postgres.h has a
bunch of paragraphs about varlena headers, so perhaps it should be
there? I see the patch tweaks some of the constants, but does not update
the comment at all.
Perhaps it would be useful to provide some additional macros making
access to custom-compressed varlena values easier. Or perhaps the
VARSIZE_ANY / VARSIZE_ANY_EXHDR / VARDATA_ANY already support that? This
part is not very clear to me.
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
On 11/15/2017 02:13 PM, Robert Haas wrote:
On Wed, Nov 15, 2017 at 4:09 AM, Ildus Kurbangaliev
<i.kurbangaliev@postgrespro.ru> wrote:So in the next version of the patch I can just unlink the options from
compression methods and dropping compression method will not affect
already compressed tuples. They still could be decompressed.I guess I don't understand how that can work. I mean, if somebody
removes a compression method - i.e. uninstalls the library - and you
don't have a way to make sure there are no tuples that can only be
uncompressed by that library - then you've broken the database.
Ideally, there should be a way to add a new compression method via an
extension ... and then get rid of it and all dependencies thereupon.
I share your confusion. Once you do DROP COMPRESSION METHOD, there must
be no remaining data compressed with it. But that's what the patch is
doing already - it enforces this using dependencies, as usual.
Ildus, can you explain what you meant? How could the data still be
decompressed after DROP COMPRESSION METHOD, and possibly after removing
the .so library?
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Mon, 20 Nov 2017 00:23:23 +0100
Tomas Vondra <tomas.vondra@2ndquadrant.com> wrote:
On 11/15/2017 02:13 PM, Robert Haas wrote:
On Wed, Nov 15, 2017 at 4:09 AM, Ildus Kurbangaliev
<i.kurbangaliev@postgrespro.ru> wrote:So in the next version of the patch I can just unlink the options
from compression methods and dropping compression method will not
affect already compressed tuples. They still could be
decompressed.I guess I don't understand how that can work. I mean, if somebody
removes a compression method - i.e. uninstalls the library - and you
don't have a way to make sure there are no tuples that can only be
uncompressed by that library - then you've broken the database.
Ideally, there should be a way to add a new compression method via
an extension ... and then get rid of it and all dependencies
thereupon.I share your confusion. Once you do DROP COMPRESSION METHOD, there
must be no remaining data compressed with it. But that's what the
patch is doing already - it enforces this using dependencies, as
usual.Ildus, can you explain what you meant? How could the data still be
decompressed after DROP COMPRESSION METHOD, and possibly after
removing the .so library?
The removal of the .so library will broke all compressed tuples. I
don't see a way to avoid it. I meant that DROP COMPRESSION METHOD could
remove the record from 'pg_compression' table, but actually the
compressed tuple needs only a record from 'pg_compression_opt' where
its options are located. And there is dependency between an extension
and the options so you can't just remove the extension without CASCADE,
postgres will complain.
Still it's a problem if the user used for example `SELECT
<compressed_column> INTO * FROM *` because postgres will copy compressed
tuples, and there will not be any dependencies between destination and
the options.
Also thank you for review. I will look into it today.
--
---
Ildus Kurbangaliev
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company
On 11/20/2017 10:44 AM, Ildus Kurbangaliev wrote:
On Mon, 20 Nov 2017 00:23:23 +0100
Tomas Vondra <tomas.vondra@2ndquadrant.com> wrote:On 11/15/2017 02:13 PM, Robert Haas wrote:
On Wed, Nov 15, 2017 at 4:09 AM, Ildus Kurbangaliev
<i.kurbangaliev@postgrespro.ru> wrote:So in the next version of the patch I can just unlink the options
from compression methods and dropping compression method will not
affect already compressed tuples. They still could be
decompressed.I guess I don't understand how that can work. I mean, if somebody
removes a compression method - i.e. uninstalls the library - and you
don't have a way to make sure there are no tuples that can only be
uncompressed by that library - then you've broken the database.
Ideally, there should be a way to add a new compression method via
an extension ... and then get rid of it and all dependencies
thereupon.I share your confusion. Once you do DROP COMPRESSION METHOD, there
must be no remaining data compressed with it. But that's what the
patch is doing already - it enforces this using dependencies, as
usual.Ildus, can you explain what you meant? How could the data still be
decompressed after DROP COMPRESSION METHOD, and possibly after
removing the .so library?The removal of the .so library will broke all compressed tuples. I
don't see a way to avoid it. I meant that DROP COMPRESSION METHOD could
remove the record from 'pg_compression' table, but actually the
compressed tuple needs only a record from 'pg_compression_opt' where
its options are located. And there is dependency between an extension
and the options so you can't just remove the extension without CASCADE,
postgres will complain.
I don't think we need to do anything smart here - it should behave just
like dropping a data type, for example. That is, error out if there are
columns using the compression method (without CASCADE), and drop all the
columns (with CASCADE).
Leaving around the pg_compression_opt is not a solution. Not only it's
confusing and I'm not aware about any extension because the user is
likely to remove the .so file (perhaps not directly, but e.g. by
removing the rpm package providing it).
Still it's a problem if the user used for example `SELECT
<compressed_column> INTO * FROM *` because postgres will copy compressed
tuples, and there will not be any dependencies between destination and
the options.
This seems like a rather fatal design flaw, though. I'd say we need to
force recompression of the data, in such cases. Otherwise all the
dependency tracking is rather pointless.
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Nov 20, 2017, at 18:18, Tomas Vondra <tomas.vondra@2ndquadrant.com> wrote:
I don't think we need to do anything smart here - it should behave just
like dropping a data type, for example. That is, error out if there are
columns using the compression method (without CASCADE), and drop all the
columns (with CASCADE).
What about instead of dropping column we leave data uncompressed?
On 11/20/2017 04:21 PM, Евгений Шишкин wrote:
On Nov 20, 2017, at 18:18, Tomas Vondra <tomas.vondra@2ndquadrant.com
<mailto:tomas.vondra@2ndquadrant.com>> wrote:I don't think we need to do anything smart here - it should behave just
like dropping a data type, for example. That is, error out if there are
columns using the compression method (without CASCADE), and drop all the
columns (with CASCADE).What about instead of dropping column we leave data uncompressed?
That requires you to go through the data and rewrite the whole table.
And I'm not aware of a DROP command doing that, instead they just drop
the dependent objects (e.g. DROP TYPE, ...). So per PLOS the DROP
COMPRESSION METHOD command should do that too.
But I'm wondering if ALTER COLUMN ... SET NOT COMPRESSED should do that
(currently it only disables compression for new data).
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Nov 20, 2017, at 18:29, Tomas Vondra <tomas.vondra@2ndquadrant.com> wrote:
What about instead of dropping column we leave data uncompressed?
That requires you to go through the data and rewrite the whole table.
And I'm not aware of a DROP command doing that, instead they just drop
the dependent objects (e.g. DROP TYPE, ...). So per PLOS the DROP
COMPRESSION METHOD command should do that too.
Well, there is no much you can do with DROP TYPE. But i'd argue that compression
is different. We do not drop data in case of DROP STATISTICS or DROP INDEX.
At least there should be a way to easily alter compression method then.
On Mon, 20 Nov 2017 16:29:11 +0100
Tomas Vondra <tomas.vondra@2ndquadrant.com> wrote:
On 11/20/2017 04:21 PM, Евгений Шишкин wrote:
On Nov 20, 2017, at 18:18, Tomas Vondra
<tomas.vondra@2ndquadrant.com
<mailto:tomas.vondra@2ndquadrant.com>> wrote:I don't think we need to do anything smart here - it should behave
just like dropping a data type, for example. That is, error out if
there are columns using the compression method (without CASCADE),
and drop all the columns (with CASCADE).What about instead of dropping column we leave data uncompressed?
That requires you to go through the data and rewrite the whole table.
And I'm not aware of a DROP command doing that, instead they just drop
the dependent objects (e.g. DROP TYPE, ...). So per PLOS the DROP
COMPRESSION METHOD command should do that too.But I'm wondering if ALTER COLUMN ... SET NOT COMPRESSED should do
that (currently it only disables compression for new data).
If the table is big, decompression could take an eternity. That's why i
decided to only to disable it and the data could be decompressed using
compression options.
My idea was to keep compression options forever, since there will not
be much of them in one database. Still that requires that extension is
not removed.
I will try to find a way how to recompress data first in case it moves
to another table.
--
---
Ildus Kurbangaliev
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company
On 11/20/2017 04:43 PM, Евгений Шишкин wrote:
On Nov 20, 2017, at 18:29, Tomas Vondra <tomas.vondra@2ndquadrant.com> wrote:
What about instead of dropping column we leave data uncompressed?
That requires you to go through the data and rewrite the whole table.
And I'm not aware of a DROP command doing that, instead they just drop
the dependent objects (e.g. DROP TYPE, ...). So per PLOS the DROP
COMPRESSION METHOD command should do that too.Well, there is no much you can do with DROP TYPE. But i'd argue that compression
is different. We do not drop data in case of DROP STATISTICS or DROP INDEX.
But those DROP commands do not 'invalidate' data in the heap, so there's
no reason to drop the columns.
At least there should be a way to easily alter compression method then.
+1
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Mon, 20 Nov 2017 00:04:53 +0100
Tomas Vondra <tomas.vondra@2ndquadrant.com> wrote:
I did a review of this today, and I think there are some things that
need improvement / fixing.Firstly, some basic comments from just eye-balling the diff, then some
bugs I discovered after writing an extension adding lz4.1) formatRelOptions/freeRelOptions are no longer needed (I see Ildar
already pointer that out)
I removed freeRelOptions, but formatRelOptions is used in other place.
2) There's unnecessary whitespace (extra newlines) on a couple of
places, which is needlessly increasing the size of the patch. Small
difference, but annoying.
Cleaned up.
3) tuptoaster.c
Why do you change 'info' from int32 to uint32? Seems unnecessary.
That's because I use highest bit, and it makes number negative for
int32. I use right shifting to get that bit and right shift on negative
gives negative value too.
Adding new 'att' variable in toast_insert_or_update is confusing, as
there already is 'att' in the very next loop. Technically it's
correct, but I'd bet it'll lead to some WTF?! moments later. I
propose to just use TupleDescAttr(tupleDesc,i) on the two places
where it matters, around line 808.There are no comments for init_compression_options_htab and
get_compression_options_info, so that needs to be fixed. Moreover, the
names are confusing because what we really get is not just 'options'
but the compression routines too.
Removed extra 'att', and added comments.
4) gen_db_file_maps probably shouldn't do the fprints, right?
5) not sure why you modify src/tools/pgindent/exclude_file_patterns
My bad, removed these lines.
6) I'm rather confused by AttributeCompression vs. ColumnCompression.
I mean, attribute==column, right? Of course, one is for data from
parser, the other one is for internal info. But can we make the
naming clearer?
For now I have renamed AttributeCompression to CompressionOptions, not
sure that's a good name but at least it gives less confusion.
7) The docs in general are somewhat unsatisfactory, TBH. For example
the ColumnCompression has no comments, unlike everything else in
parsenodes. Similarly for the SGML docs - I suggest to expand them to
resemble FDW docs
(https://www.postgresql.org/docs/10/static/fdwhandler.html) which
also follows the handler/routines pattern.
I've added more comments. I think I'll add more documentation if the
committers will approve current syntax.
8) One of the unclear things if why we even need 'drop' routing. It
seems that if it's defined DropAttributeCompression does something.
But what should it do? I suppose dropping the options should be done
using dependencies (just like we drop columns in this case).BTW why does DropAttributeCompression mess with att->attisdropped in
this way? That seems a bit odd.
'drop' routine could be useful. An extension could do
something related with the attribute, like remove extra tables or
something else. The compression options will not be removed after
unlinking compression method from a column because there is still be
stored compressed data in that column.
That 'attisdropped' part has been removed.
9) configure routines that only check if (options != NIL) and then
error out (like tsvector_configure) seem a bit unnecessary. Just
allow it to be NULL in CompressionMethodRoutine, and throw an error
if options is not NIL for such compression method.
Good idea, done.
10) toast_compress_datum still does this:
if (!ac && (valsize < PGLZ_strategy_default->min_input_size ||
valsize > PGLZ_strategy_default->max_input_size))which seems rather pglz-specific (the naming is a hint). Why shouldn't
this be specific to compression, exposed either as min/max constants,
or wrapped in another routine - size_is_valid() or something like
that?
I agree, moved to the next block related with pglz.
11) The comments in toast_compress_datum probably need updating, as it
still references to pglz specifically. I guess the new compression
methods do matter too.
Done.
12) get_compression_options_info organizes the compression info into a
hash table by OID. The hash table implementation assumes the hash key
is at the beginning of the entry, but AttributeCompression is defined
like this:typedef struct
{
CompressionMethodRoutine *routine;
List *options;
Oid cmoptoid;
} AttributeCompression;Which means get_compression_options_info is busted, will never lookup
anything, and the hash table will grow by adding more and more entries
into the same bucket. Of course, this has extremely negative impact on
performance (pretty much arbitrarily bad, depending on how many
entries you've already added to the hash table).Moving the OID to the beginning of the struct fixes the issue.
Yeah, I fixed it before, but somehow managed to do not include it to the
patch.
13) When writing the experimental extension, I was extremely confused
about the regular varlena headers, custom compression headers, etc. In
the end I stole the code from tsvector.c and whacked it a bit until it
worked, but I wouldn't dare to claim I understand how it works.This needs to be documented somewhere. For example postgres.h has a
bunch of paragraphs about varlena headers, so perhaps it should be
there? I see the patch tweaks some of the constants, but does not
update the comment at all.
This point is good, I'm not sure how this documentation should look
like. I've just assumed that people should have deep undestanding of
varlenas if they're going to compress them. But now it's easy to make
mistake there. Maybe I should add some functions that help to construct
varlena, with different headers. I like the way is how jsonb is
constructed. It uses StringInfo and there are few helper functions
(reserveFromBuffer, appendToBuffer and others). Maybe
they should be not static.
Perhaps it would be useful to provide some additional macros making
access to custom-compressed varlena values easier. Or perhaps the
VARSIZE_ANY / VARSIZE_ANY_EXHDR / VARDATA_ANY already support that?
This part is not very clear to me.
These macros will work, custom compressed varlenas behave like old
compressed varlenas.
Still it's a problem if the user used for example `SELECT
<compressed_column> INTO * FROM *` because postgres will copy
compressed tuples, and there will not be any dependencies between
destination and the options.This seems like a rather fatal design flaw, though. I'd say we need to
force recompression of the data, in such cases. Otherwise all the
dependency tracking is rather pointless.
Fixed this problem too. I've added recompression for datum that use
custom compression.
--
---
Ildus Kurbangaliev
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company
Attachments:
custom_compression_methods_v5.patchtext/x-patchDownload
diff --git a/contrib/test_decoding/expected/ddl.out b/contrib/test_decoding/expected/ddl.out
index 1e22c1eefc..766ced401f 100644
--- a/contrib/test_decoding/expected/ddl.out
+++ b/contrib/test_decoding/expected/ddl.out
@@ -416,12 +416,12 @@ CREATE TABLE replication_metadata (
WITH (user_catalog_table = true)
;
\d+ replication_metadata
- Table "public.replication_metadata"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------+---------+-----------+----------+--------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | |
- relation | name | | not null | | plain | |
- options | text[] | | | | extended | |
+ Table "public.replication_metadata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+----------+---------+-----------+----------+--------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | |
+ relation | name | | not null | | plain | | |
+ options | text[] | | | | extended | | |
Indexes:
"replication_metadata_pkey" PRIMARY KEY, btree (id)
Options: user_catalog_table=true
@@ -430,12 +430,12 @@ INSERT INTO replication_metadata(relation, options)
VALUES ('foo', ARRAY['a', 'b']);
ALTER TABLE replication_metadata RESET (user_catalog_table);
\d+ replication_metadata
- Table "public.replication_metadata"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------+---------+-----------+----------+--------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | |
- relation | name | | not null | | plain | |
- options | text[] | | | | extended | |
+ Table "public.replication_metadata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+----------+---------+-----------+----------+--------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | |
+ relation | name | | not null | | plain | | |
+ options | text[] | | | | extended | | |
Indexes:
"replication_metadata_pkey" PRIMARY KEY, btree (id)
@@ -443,12 +443,12 @@ INSERT INTO replication_metadata(relation, options)
VALUES ('bar', ARRAY['a', 'b']);
ALTER TABLE replication_metadata SET (user_catalog_table = true);
\d+ replication_metadata
- Table "public.replication_metadata"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------+---------+-----------+----------+--------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | |
- relation | name | | not null | | plain | |
- options | text[] | | | | extended | |
+ Table "public.replication_metadata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+----------+---------+-----------+----------+--------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | |
+ relation | name | | not null | | plain | | |
+ options | text[] | | | | extended | | |
Indexes:
"replication_metadata_pkey" PRIMARY KEY, btree (id)
Options: user_catalog_table=true
@@ -461,13 +461,13 @@ ALTER TABLE replication_metadata ALTER COLUMN rewritemeornot TYPE text;
ERROR: cannot rewrite table "replication_metadata" used as a catalog table
ALTER TABLE replication_metadata SET (user_catalog_table = false);
\d+ replication_metadata
- Table "public.replication_metadata"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------------+---------+-----------+----------+--------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | |
- relation | name | | not null | | plain | |
- options | text[] | | | | extended | |
- rewritemeornot | integer | | | | plain | |
+ Table "public.replication_metadata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+----------------+---------+-----------+----------+--------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | |
+ relation | name | | not null | | plain | | |
+ options | text[] | | | | extended | | |
+ rewritemeornot | integer | | | | plain | | |
Indexes:
"replication_metadata_pkey" PRIMARY KEY, btree (id)
Options: user_catalog_table=false
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index 01acc2ef9d..f43f09cc19 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -59,6 +59,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY createAggregate SYSTEM "create_aggregate.sgml">
<!ENTITY createCast SYSTEM "create_cast.sgml">
<!ENTITY createCollation SYSTEM "create_collation.sgml">
+<!ENTITY createCompressionMethod SYSTEM "create_compression_method.sgml">
<!ENTITY createConversion SYSTEM "create_conversion.sgml">
<!ENTITY createDatabase SYSTEM "create_database.sgml">
<!ENTITY createDomain SYSTEM "create_domain.sgml">
diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index 92db00f52d..207d6cf26e 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -53,6 +53,8 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET ( <replaceable class="parameter">attribute_option</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] )
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> RESET ( <replaceable class="parameter">attribute_option</replaceable> [, ... ] )
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN }
+ ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET COMPRESSED <replaceable class="parameter">compression_method_name</replaceable> [ WITH (<replaceable class="parameter">compression_method_options</replaceable>) ]
+ ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET NOT COMPRESSED
ADD <replaceable class="parameter">table_constraint</replaceable> [ NOT VALID ]
ADD <replaceable class="parameter">table_constraint_using_index</replaceable>
ALTER CONSTRAINT <replaceable class="parameter">constraint_name</replaceable> [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -320,6 +322,34 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>
+ <literal>SET COMPRESSED <replaceable class="parameter">compression_method_name</replaceable></literal>
+ </term>
+ <listitem>
+ <para>
+ This form adds compression to a column. Compression method should be
+ created with <xref linkend="SQL-CREATECOMPRESSIONMETHOD">. If compression
+ method has options they could be specified with <literal>WITH</literal>
+ parameter. Setting a compression method doesn't change anything in the
+ table and affects only future table updates.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <literal>SET NOT COMPRESSED</literal>
+ </term>
+ <listitem>
+ <para>
+ This form removes compression from a column. Removing compresssion from
+ a column doesn't change already compressed tuples and affects only future
+ table updates.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>ADD <replaceable class="parameter">table_constraint</replaceable> [ NOT VALID ]</literal></term>
<listitem>
diff --git a/doc/src/sgml/ref/create_compression_method.sgml b/doc/src/sgml/ref/create_compression_method.sgml
new file mode 100644
index 0000000000..663010ecd9
--- /dev/null
+++ b/doc/src/sgml/ref/create_compression_method.sgml
@@ -0,0 +1,50 @@
+<!--
+doc/src/sgml/ref/create_compression_method.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-CREATECOMPRESSIONMETHOD">
+ <indexterm zone="sql-createcompressionmethod">
+ <primary>CREATE COMPRESSION METHOD</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>CREATE COMPRESSION METHOD</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>CREATE COMPRESSION METHOD</refname>
+ <refpurpose>define a new compression method</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+CREATE COMPRESSION METHOD <replaceable class="parameter">compression_method_name</replaceable>
+ HANDLER <replaceable class="parameter">compression_method_handler</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>CREATE COMPRESSION METHOD</command> creates a new compression method
+ with <replaceable class="parameter">compression_method_name</replaceable>.
+ </para>
+
+ <para>
+ A compression method links a name with a compression handler. And the
+ handler is a special function that returns collection of methods that
+ can be used for compression.
+ </para>
+
+ <para>
+ After a compression method is created, you can specify it in
+ <xref linkend="SQL-CREATETABLE"> or <xref linkend="SQL-ALTERTABLE">
+ statements.
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 83eef7f10d..9631279cb0 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -65,6 +65,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( <replaceable>sequence_options</replaceable> ) ] |
UNIQUE <replaceable class="parameter">index_parameters</replaceable> |
PRIMARY KEY <replaceable class="parameter">index_parameters</replaceable> |
+ COMPRESSED <replaceable class="parameter">compression_method_name</replaceable> [ WITH (<replaceable class="parameter">compression_method_options</replaceable>) ] |
REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -817,6 +818,18 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>COMPRESSED <replaceable class="parameter">compression_method_name</replaceable> [ WITH (<replaceable class="parameter">compression_method_options</replaceable>) ]</literal></term>
+ <listitem>
+ <para>
+ This clause adds compression to a column. Compression method should be
+ created with <xref linkend="SQL-CREATECOMPRESSIONMETHOD">. If compression
+ method has options they could be specified with <literal>WITH</literal>
+ parameter.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="sql-createtable-exclude">
<term><literal>EXCLUDE [ USING <replaceable class="parameter">index_method</replaceable> ] ( <replaceable class="parameter">exclude_element</replaceable> WITH <replaceable class="parameter">operator</replaceable> [, ... ] ) <replaceable class="parameter">index_parameters</replaceable> [ WHERE ( <replaceable class="parameter">predicate</replaceable> ) ]</literal></term>
<listitem>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index 9000b3aaaa..cc0bd70be3 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -87,6 +87,7 @@
&createAggregate;
&createCast;
&createCollation;
+ &createCompressionMethod;
&createConversion;
&createDatabase;
&createDomain;
diff --git a/src/backend/access/common/indextuple.c b/src/backend/access/common/indextuple.c
index 138671410a..629f3575a9 100644
--- a/src/backend/access/common/indextuple.c
+++ b/src/backend/access/common/indextuple.c
@@ -92,7 +92,8 @@ index_form_tuple(TupleDesc tupleDescriptor,
VARSIZE(DatumGetPointer(untoasted_values[i])) > TOAST_INDEX_TARGET &&
(att->attstorage == 'x' || att->attstorage == 'm'))
{
- Datum cvalue = toast_compress_datum(untoasted_values[i]);
+ Datum cvalue = toast_compress_datum(untoasted_values[i],
+ att->attcompression);
if (DatumGetPointer(cvalue) != NULL)
{
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index aa9c0f1bb9..f740ce4304 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -945,11 +945,31 @@ untransformRelOptions(Datum options)
val = (Node *) makeString(pstrdup(p));
}
result = lappend(result, makeDefElem(pstrdup(s), val, -1));
+ pfree(s);
}
return result;
}
+char *
+formatRelOptions(List *options)
+{
+ StringInfoData buf;
+ ListCell *cell;
+
+ initStringInfo(&buf);
+
+ foreach(cell, options)
+ {
+ DefElem *def = (DefElem *) lfirst(cell);
+
+ appendStringInfo(&buf, "%s%s=%s", buf.len > 0 ? ", " : "",
+ def->defname, defGetString(def));
+ }
+
+ return buf.data;
+}
+
/*
* Extract and parse reloptions from a pg_class tuple.
*
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index 9e37ca73a8..d206cce18e 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -19,8 +19,10 @@
#include "postgres.h"
+#include "access/compression.h"
#include "access/hash.h"
#include "access/htup_details.h"
+#include "access/reloptions.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
@@ -242,6 +244,7 @@ TupleDescCopyEntry(TupleDesc dst, AttrNumber dstAttno,
dstAtt->attnotnull = false;
dstAtt->atthasdef = false;
dstAtt->attidentity = '\0';
+ dstAtt->attcompression = InvalidOid;
}
/*
@@ -396,6 +399,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
return false;
if (attr1->attcollation != attr2->attcollation)
return false;
+ if (attr1->attcompression != attr2->attcompression)
+ return false;
/* attacl, attoptions and attfdwoptions are not even present... */
}
@@ -458,6 +463,7 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
}
else if (tupdesc2->constr != NULL)
return false;
+
return true;
}
@@ -563,6 +569,7 @@ TupleDescInitEntry(TupleDesc desc,
att->attalign = typeForm->typalign;
att->attstorage = typeForm->typstorage;
att->attcollation = typeForm->typcollation;
+ att->attcompression = InvalidOid;
ReleaseSysCache(tuple);
}
@@ -675,7 +682,6 @@ TupleDescInitEntryCollation(TupleDesc desc,
TupleDescAttr(desc, attributeNumber - 1)->attcollation = collationid;
}
-
/*
* BuildDescForRelation
*
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index c74945a52a..99307cb5e6 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -30,8 +30,10 @@
#include <unistd.h>
#include <fcntl.h>
+#include "access/compression.h"
#include "access/genam.h"
#include "access/heapam.h"
+#include "access/reloptions.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -39,6 +41,8 @@
#include "miscadmin.h"
#include "utils/expandeddatum.h"
#include "utils/fmgroids.h"
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/typcache.h"
@@ -53,19 +57,52 @@
typedef struct toast_compress_header
{
int32 vl_len_; /* varlena header (do not touch directly!) */
- int32 rawsize;
+ uint32 info; /* flags (2 bits) and rawsize */
} toast_compress_header;
+/*
+ * If the compression method were used, then data also contains
+ * Oid of compression options
+ */
+typedef struct toast_compress_header_custom
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ uint32 info; /* flags (2 high bits) and rawsize */
+ Oid cmoptoid; /* Oid from pg_compression_opt */
+} toast_compress_header_custom;
+
+static HTAB *compression_options_cache = NULL;
+static MemoryContext compression_options_mcxt = NULL;
+
+#define RAWSIZEMASK (0x3FFFFFFFU)
+
/*
* Utilities for manipulation of header information for compressed
* toast entries.
*/
-#define TOAST_COMPRESS_HDRSZ ((int32) sizeof(toast_compress_header))
-#define TOAST_COMPRESS_RAWSIZE(ptr) (((toast_compress_header *) (ptr))->rawsize)
+#define TOAST_COMPRESS_HDRSZ ((int32) sizeof(toast_compress_header))
+#define TOAST_COMPRESS_HDRSZ_CUSTOM ((int32) sizeof(toast_compress_header_custom))
+#define TOAST_COMPRESS_RAWSIZE(ptr) (((toast_compress_header *) (ptr))->info & RAWSIZEMASK)
#define TOAST_COMPRESS_RAWDATA(ptr) \
(((char *) (ptr)) + TOAST_COMPRESS_HDRSZ)
#define TOAST_COMPRESS_SET_RAWSIZE(ptr, len) \
- (((toast_compress_header *) (ptr))->rawsize = (len))
+do { \
+ ((toast_compress_header *) (ptr))->info &= 0xC0000000; \
+ ((toast_compress_header *) (ptr))->info |= ((uint32)(len) & RAWSIZEMASK); \
+} while (0)
+#define TOAST_COMPRESS_SET_CMOPTOID(ptr, oid) \
+ (((toast_compress_header_custom *) (ptr))->cmoptoid = (oid))
+#define TOAST_COMPRESS_SET_CUSTOM(ptr) \
+do { \
+ (((toast_compress_header *) (ptr))->info |= (1 << 31)); \
+ (((toast_compress_header *) (ptr))->info &= ~(1 << 30)); \
+} while (0)
+
+#define VARATT_EXTERNAL_SET_CUSTOM(toast_pointer) \
+do { \
+ ((toast_pointer).va_extinfo |= (1 << 31)); \
+ ((toast_pointer).va_extinfo &= ~(1 << 30)); \
+} while (0)
static void toast_delete_datum(Relation rel, Datum value, bool is_speculative);
static Datum toast_save_datum(Relation rel, Datum value,
@@ -83,6 +120,8 @@ static int toast_open_indexes(Relation toastrel,
static void toast_close_indexes(Relation *toastidxs, int num_indexes,
LOCKMODE lock);
static void init_toast_snapshot(Snapshot toast_snapshot);
+static void init_compression_options_cache(void);
+static CompressionOptions * get_cached_compression_options(Oid cmoptoid);
/* ----------
@@ -421,7 +460,7 @@ toast_datum_size(Datum value)
struct varatt_external toast_pointer;
VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
- result = toast_pointer.va_extsize;
+ result = VARATT_EXTERNAL_GET_SIZE(toast_pointer);
}
else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
{
@@ -686,8 +725,24 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
toast_oldexternal[i] = new_value;
if (att->attstorage == 'p')
new_value = heap_tuple_untoast_attr(new_value);
+ else if (VARATT_IS_EXTERNAL_ONDISK(new_value))
+ {
+ struct varatt_external toast_pointer;
+
+ VARATT_EXTERNAL_GET_POINTER(toast_pointer, new_value);
+
+ /*
+ * If we're trying to insert a custom compressed datum we
+ * should decompress it first.
+ */
+ if (VARATT_EXTERNAL_IS_CUSTOM_COMPRESSED(toast_pointer))
+ new_value = heap_tuple_untoast_attr(new_value);
+ else
+ new_value = heap_tuple_fetch_attr(new_value);
+ }
else
new_value = heap_tuple_fetch_attr(new_value);
+
toast_values[i] = PointerGetDatum(new_value);
toast_free[i] = true;
need_change = true;
@@ -741,12 +796,14 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
Datum old_value;
Datum new_value;
+ Form_pg_attribute att;
+
/*
* Search for the biggest yet unprocessed internal attribute
*/
for (i = 0; i < numAttrs; i++)
{
- Form_pg_attribute att = TupleDescAttr(tupleDesc, i);
+ char attstorage;
if (toast_action[i] != ' ')
continue;
@@ -754,7 +811,9 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
continue; /* can't happen, toast_action would be 'p' */
if (VARATT_IS_COMPRESSED(DatumGetPointer(toast_values[i])))
continue;
- if (att->attstorage != 'x' && att->attstorage != 'e')
+
+ attstorage = (TupleDescAttr(tupleDesc, i))->attstorage;
+ if (attstorage != 'x' && attstorage != 'e')
continue;
if (toast_sizes[i] > biggest_size)
{
@@ -770,10 +829,11 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
* Attempt to compress it inline, if it has attstorage 'x'
*/
i = biggest_attno;
- if (TupleDescAttr(tupleDesc, i)->attstorage == 'x')
+ att = TupleDescAttr(tupleDesc, i);
+ if (att->attstorage == 'x')
{
old_value = toast_values[i];
- new_value = toast_compress_datum(old_value);
+ new_value = toast_compress_datum(old_value, att->attcompression);
if (DatumGetPointer(new_value) != NULL)
{
@@ -914,7 +974,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
*/
i = biggest_attno;
old_value = toast_values[i];
- new_value = toast_compress_datum(old_value);
+ new_value = toast_compress_datum(old_value,
+ TupleDescAttr(tupleDesc, i)->attcompression);
if (DatumGetPointer(new_value) != NULL)
{
@@ -1353,7 +1414,6 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
return new_tuple;
}
-
/* ----------
* toast_compress_datum -
*
@@ -1368,41 +1428,57 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
* ----------
*/
Datum
-toast_compress_datum(Datum value)
+toast_compress_datum(Datum value, Oid cmoptoid)
{
- struct varlena *tmp;
- int32 valsize = VARSIZE_ANY_EXHDR(DatumGetPointer(value));
- int32 len;
+ struct varlena *tmp = NULL;
+ int32 valsize,
+ len = 0;
+ CompressionOptions *cmoptions = NULL;
Assert(!VARATT_IS_EXTERNAL(DatumGetPointer(value)));
Assert(!VARATT_IS_COMPRESSED(DatumGetPointer(value)));
- /*
- * No point in wasting a palloc cycle if value size is out of the allowed
- * range for compression
- */
- if (valsize < PGLZ_strategy_default->min_input_size ||
- valsize > PGLZ_strategy_default->max_input_size)
- return PointerGetDatum(NULL);
+ if (OidIsValid(cmoptoid))
+ cmoptions = get_cached_compression_options(cmoptoid);
- tmp = (struct varlena *) palloc(PGLZ_MAX_OUTPUT(valsize) +
- TOAST_COMPRESS_HDRSZ);
+ valsize = VARSIZE_ANY_EXHDR(DatumGetPointer(value));
+
+ if (cmoptions)
+ {
+ tmp = cmoptions->routine->compress(cmoptions, (const struct varlena *) value);
+ if (!tmp)
+ return PointerGetDatum(NULL);
+ }
+ else
+ {
+ /*
+ * No point in wasting a palloc cycle if value size is out of the
+ * allowed range for compression
+ */
+ if (valsize < PGLZ_strategy_default->min_input_size ||
+ valsize > PGLZ_strategy_default->max_input_size)
+ return PointerGetDatum(NULL);
+
+ tmp = (struct varlena *) palloc(PGLZ_MAX_OUTPUT(valsize) +
+ TOAST_COMPRESS_HDRSZ);
+ len = pglz_compress(VARDATA_ANY(DatumGetPointer(value)),
+ valsize,
+ TOAST_COMPRESS_RAWDATA(tmp),
+ PGLZ_strategy_default);
+ }
/*
- * We recheck the actual size even if pglz_compress() reports success,
- * because it might be satisfied with having saved as little as one byte
- * in the compressed data --- which could turn into a net loss once you
- * consider header and alignment padding. Worst case, the compressed
- * format might require three padding bytes (plus header, which is
- * included in VARSIZE(tmp)), whereas the uncompressed format would take
- * only one header byte and no padding if the value is short enough. So
- * we insist on a savings of more than 2 bytes to ensure we have a gain.
+ * We recheck the actual size even if compression function reports
+ * success, because it might be satisfied with having saved as little as
+ * one byte in the compressed data --- which could turn into a net loss
+ * once you consider header and alignment padding. Worst case, the
+ * compressed format might require three padding bytes (plus header, which
+ * is included in VARSIZE(tmp)), whereas the uncompressed format would
+ * take only one header byte and no padding if the value is short enough.
+ * So we insist on a savings of more than 2 bytes to ensure we have a
+ * gain.
*/
- len = pglz_compress(VARDATA_ANY(DatumGetPointer(value)),
- valsize,
- TOAST_COMPRESS_RAWDATA(tmp),
- PGLZ_strategy_default);
- if (len >= 0 &&
+ if (!cmoptions && len >= 0 &&
len + TOAST_COMPRESS_HDRSZ < valsize - 2)
{
TOAST_COMPRESS_SET_RAWSIZE(tmp, valsize);
@@ -1410,10 +1486,20 @@ toast_compress_datum(Datum value)
/* successful compression */
return PointerGetDatum(tmp);
}
+ else if (cmoptions && VARSIZE(tmp) < valsize - 2)
+ {
+ TOAST_COMPRESS_SET_CUSTOM(tmp);
+ TOAST_COMPRESS_SET_RAWSIZE(tmp, valsize);
+ TOAST_COMPRESS_SET_CMOPTOID(tmp, cmoptions->cmoptoid);
+ /* successful compression */
+ return PointerGetDatum(tmp);
+ }
else
{
/* incompressible data */
- pfree(tmp);
+ if (tmp)
+ pfree(tmp);
+
return PointerGetDatum(NULL);
}
}
@@ -1510,19 +1596,20 @@ toast_save_datum(Relation rel, Datum value,
&num_indexes);
/*
- * Get the data pointer and length, and compute va_rawsize and va_extsize.
+ * Get the data pointer and length, and compute va_rawsize and va_extinfo.
*
* va_rawsize is the size of the equivalent fully uncompressed datum, so
* we have to adjust for short headers.
*
- * va_extsize is the actual size of the data payload in the toast records.
+ * va_extinfo contains the actual size of the data payload in the toast
+ * records.
*/
if (VARATT_IS_SHORT(dval))
{
data_p = VARDATA_SHORT(dval);
data_todo = VARSIZE_SHORT(dval) - VARHDRSZ_SHORT;
toast_pointer.va_rawsize = data_todo + VARHDRSZ; /* as if not short */
- toast_pointer.va_extsize = data_todo;
+ toast_pointer.va_extinfo = data_todo; /* no flags */
}
else if (VARATT_IS_COMPRESSED(dval))
{
@@ -1530,7 +1617,10 @@ toast_save_datum(Relation rel, Datum value,
data_todo = VARSIZE(dval) - VARHDRSZ;
/* rawsize in a compressed datum is just the size of the payload */
toast_pointer.va_rawsize = VARRAWSIZE_4B_C(dval) + VARHDRSZ;
- toast_pointer.va_extsize = data_todo;
+ toast_pointer.va_extinfo = data_todo;
+ if (VARATT_IS_CUSTOM_COMPRESSED(dval))
+ VARATT_EXTERNAL_SET_CUSTOM(toast_pointer);
+
/* Assert that the numbers look like it's compressed */
Assert(VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer));
}
@@ -1539,7 +1629,7 @@ toast_save_datum(Relation rel, Datum value,
data_p = VARDATA(dval);
data_todo = VARSIZE(dval) - VARHDRSZ;
toast_pointer.va_rawsize = VARSIZE(dval);
- toast_pointer.va_extsize = data_todo;
+ toast_pointer.va_extinfo = data_todo; /* no flags */
}
/*
@@ -1899,7 +1989,7 @@ toast_fetch_datum(struct varlena *attr)
/* Must copy to access aligned fields */
VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
- ressize = toast_pointer.va_extsize;
+ ressize = VARATT_EXTERNAL_GET_SIZE(toast_pointer);
numchunks = ((ressize - 1) / TOAST_MAX_CHUNK_SIZE) + 1;
result = (struct varlena *) palloc(ressize + VARHDRSZ);
@@ -2084,7 +2174,7 @@ toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset, int32 length)
*/
Assert(!VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer));
- attrsize = toast_pointer.va_extsize;
+ attrsize = VARATT_EXTERNAL_GET_SIZE(toast_pointer);
totalchunks = ((attrsize - 1) / TOAST_MAX_CHUNK_SIZE) + 1;
if (sliceoffset >= attrsize)
@@ -2280,15 +2370,26 @@ toast_decompress_datum(struct varlena *attr)
Assert(VARATT_IS_COMPRESSED(attr));
- result = (struct varlena *)
- palloc(TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
- SET_VARSIZE(result, TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
+ if (VARATT_IS_CUSTOM_COMPRESSED(attr))
+ {
+ CompressionOptions *cmoptions;
+ toast_compress_header_custom *hdr;
- if (pglz_decompress(TOAST_COMPRESS_RAWDATA(attr),
- VARSIZE(attr) - TOAST_COMPRESS_HDRSZ,
- VARDATA(result),
- TOAST_COMPRESS_RAWSIZE(attr)) < 0)
- elog(ERROR, "compressed data is corrupted");
+ hdr = (toast_compress_header_custom *) attr;
+ cmoptions = get_cached_compression_options(hdr->cmoptoid);
+ result = cmoptions->routine->decompress(cmoptions, attr);
+ }
+ else
+ {
+ result = (struct varlena *)
+ palloc(TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
+ SET_VARSIZE(result, TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
+ if (pglz_decompress(TOAST_COMPRESS_RAWDATA(attr),
+ VARSIZE(attr) - TOAST_COMPRESS_HDRSZ,
+ VARDATA(result),
+ TOAST_COMPRESS_RAWSIZE(attr)) < 0)
+ elog(ERROR, "compressed data is corrupted");
+ }
return result;
}
@@ -2390,3 +2491,65 @@ init_toast_snapshot(Snapshot toast_snapshot)
InitToastSnapshot(*toast_snapshot, snapshot->lsn, snapshot->whenTaken);
}
+
+/* ----------
+ * init_compression_options_cache
+ *
+ * Initialize a local cache for compression options.
+ */
+static void
+init_compression_options_cache(void)
+{
+ HASHCTL ctl;
+
+ compression_options_mcxt = AllocSetContextCreate(TopMemoryContext,
+ "compression options cache context",
+ ALLOCSET_DEFAULT_SIZES);
+ MemSet(&ctl, 0, sizeof(ctl));
+ ctl.keysize = sizeof(Oid);
+ ctl.entrysize = sizeof(CompressionOptions);
+ ctl.hcxt = compression_options_mcxt;
+ compression_options_cache = hash_create("compression options cache", 100, &ctl,
+ HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
+}
+
+/* ----------
+ * get_cached_compression_options
+ *
+ * Get cached compression options structure or create it if it's not in cache.
+ * Cache is required because we can't afford for each tuple create
+ * CompressionMethodRoutine and parse its options.
+ */
+static CompressionOptions *
+get_cached_compression_options(Oid cmoptoid)
+{
+ bool found;
+ CompressionOptions *result;
+
+ Assert(OidIsValid(cmoptoid));
+ if (!compression_options_cache)
+ init_compression_options_cache();
+
+ result = hash_search(compression_options_cache, &cmoptoid, HASH_ENTER, &found);
+ if (!found)
+ {
+ MemoryContext oldcxt;
+
+ Assert(compression_options_mcxt);
+ oldcxt = MemoryContextSwitchTo(compression_options_mcxt);
+ result->cmoptoid = cmoptoid;
+ PG_TRY();
+ {
+ result->routine = GetCompressionRoutine(cmoptoid);
+ result->options = GetCompressionOptionsList(cmoptoid);
+ }
+ PG_CATCH();
+ {
+ hash_search(compression_options_cache, &cmoptoid, HASH_REMOVE, &found);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ MemoryContextSwitchTo(oldcxt);
+ }
+ return result;
+}
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 8287de97a2..be6b460aee 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -731,6 +731,7 @@ DefineAttr(char *name, char *type, int attnum, int nullness)
attrtypes[attnum]->attcacheoff = -1;
attrtypes[attnum]->atttypmod = -1;
attrtypes[attnum]->attislocal = true;
+ attrtypes[attnum]->attcompression = InvalidOid;
if (nullness == BOOTCOL_NULL_FORCE_NOT_NULL)
{
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index fd33426bad..c7cea974b1 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -46,7 +46,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_collation.h pg_partitioned_table.h pg_range.h pg_transform.h \
pg_sequence.h pg_publication.h pg_publication_rel.h pg_subscription.h \
pg_subscription_rel.h toasting.h indexing.h \
- toasting.h indexing.h \
+ pg_compression.h pg_compression_opt.h \
)
# location of Catalog.pm
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index ccde66a7dd..fd733a34a0 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -3340,6 +3340,8 @@ static const char *const no_priv_msg[MAX_ACL_KIND] =
gettext_noop("permission denied for publication %s"),
/* ACL_KIND_SUBSCRIPTION */
gettext_noop("permission denied for subscription %s"),
+ /* ACL_KIND_COMPRESSION_METHOD */
+ gettext_noop("permission denied for compression method %s"),
};
static const char *const not_owner_msg[MAX_ACL_KIND] =
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 033c4358ea..e1bfc7c6bf 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -28,6 +28,8 @@
#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_collation_fn.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_conversion.h"
@@ -173,7 +175,9 @@ static const Oid object_classes[] = {
PublicationRelationId, /* OCLASS_PUBLICATION */
PublicationRelRelationId, /* OCLASS_PUBLICATION_REL */
SubscriptionRelationId, /* OCLASS_SUBSCRIPTION */
- TransformRelationId /* OCLASS_TRANSFORM */
+ TransformRelationId, /* OCLASS_TRANSFORM */
+ CompressionMethodRelationId, /* OCLASS_COMPRESSION_METHOD */
+ CompressionOptRelationId, /* OCLASS_COMPRESSION_OPTIONS */
};
@@ -1271,6 +1275,14 @@ doDeletion(const ObjectAddress *object, int flags)
DropTransformById(object->objectId);
break;
+ case OCLASS_COMPRESSION_METHOD:
+ RemoveCompressionMethodById(object->objectId);
+ break;
+
+ case OCLASS_COMPRESSION_OPTIONS:
+ RemoveCompressionOptionsById(object->objectId);
+ break;
+
/*
* These global object types are not supported here.
*/
@@ -2512,6 +2524,12 @@ getObjectClass(const ObjectAddress *object)
case TransformRelationId:
return OCLASS_TRANSFORM;
+
+ case CompressionMethodRelationId:
+ return OCLASS_COMPRESSION_METHOD;
+
+ case CompressionOptRelationId:
+ return OCLASS_COMPRESSION_OPTIONS;
}
/* shouldn't get here */
diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl
index 256a9c9c93..c5838fa779 100644
--- a/src/backend/catalog/genbki.pl
+++ b/src/backend/catalog/genbki.pl
@@ -451,6 +451,7 @@ sub emit_pgattr_row
attisdropped => 'f',
attislocal => 't',
attinhcount => '0',
+ attcompression=> '0',
attacl => '_null_',
attoptions => '_null_',
attfdwoptions => '_null_');
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 9e14880b99..4f958be51f 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -29,8 +29,10 @@
*/
#include "postgres.h"
+#include "access/compression.h"
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/reloptions.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "access/xact.h"
@@ -44,6 +46,8 @@
#include "catalog/partition.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_foreign_table.h"
@@ -628,6 +632,7 @@ InsertPgAttributeTuple(Relation pg_attribute_rel,
values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal);
values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount);
values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(new_attribute->attcollation);
+ values[Anum_pg_attribute_attcompression - 1] = ObjectIdGetDatum(new_attribute->attcompression);
/* start out with empty permissions and empty options */
nulls[Anum_pg_attribute_attacl - 1] = true;
@@ -707,6 +712,13 @@ AddNewAttributeTuples(Oid new_rel_oid,
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+
+ if (OidIsValid(attr->attcompression))
+ {
+ ObjectAddressSet(referenced, CompressionOptRelationId,
+ attr->attcompression);
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
}
/*
@@ -1453,6 +1465,24 @@ DeleteRelationTuple(Oid relid)
heap_close(pg_class_desc, RowExclusiveLock);
}
+/*
+ * CallCompressionDropCallback
+ *
+ * Call drop callback from compression routine.
+ */
+static void
+CallCompressionDropCallback(Form_pg_attribute att)
+{
+ CompressionMethodRoutine *cmr = GetCompressionRoutine(att->attcompression);
+
+ if (cmr->drop)
+ {
+ List *options = GetCompressionOptionsList(att->attcompression);
+
+ cmr->drop(att, options);
+ }
+}
+
/*
* DeleteAttributeTuples
*
@@ -1483,7 +1513,14 @@ DeleteAttributeTuples(Oid relid)
/* Delete all the matching tuples */
while ((atttup = systable_getnext(scan)) != NULL)
+ {
+ Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(atttup);
+
+ if (OidIsValid(att->attcompression))
+ CallCompressionDropCallback(att);
+
CatalogTupleDelete(attrel, &atttup->t_self);
+ }
/* Clean up after the scan */
systable_endscan(scan);
@@ -1576,6 +1613,8 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
else
{
/* Dropping user attributes is lots harder */
+ if (OidIsValid(attStruct->attcompression))
+ CallCompressionDropCallback(attStruct);
/* Mark the attribute as dropped */
attStruct->attisdropped = true;
@@ -1597,6 +1636,8 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
/* We don't want to keep stats for it anymore */
attStruct->attstattarget = 0;
+ attStruct->attcompression = InvalidOid;
+
/*
* Change the column name to something that isn't likely to conflict
*/
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 0125c18bc1..15942564aa 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -393,6 +393,7 @@ ConstructTupleDescriptor(Relation heapRelation,
to->atttypmod = exprTypmod(indexkey);
to->attislocal = true;
to->attcollation = collationObjectId[i];
+ to->attcompression = InvalidOid;
ReleaseSysCache(tuple);
@@ -471,6 +472,7 @@ ConstructTupleDescriptor(Relation heapRelation,
to->attbyval = typeTup->typbyval;
to->attalign = typeTup->typalign;
to->attstorage = typeTup->typstorage;
+ to->attcompression = InvalidOid;
ReleaseSysCache(tuple);
}
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 8d55c76fc4..9fd1cb763d 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -29,6 +29,8 @@
#include "catalog/pg_default_acl.h"
#include "catalog/pg_event_trigger.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_conversion.h"
@@ -490,6 +492,30 @@ static const ObjectPropertyType ObjectProperty[] =
InvalidAttrNumber, /* no ACL (same as relation) */
ACL_KIND_STATISTICS,
true
+ },
+ {
+ CompressionMethodRelationId,
+ CompressionMethodOidIndexId,
+ COMPRESSIONMETHODOID,
+ COMPRESSIONMETHODNAME,
+ Anum_pg_compression_cmname,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ true
+ },
+ {
+ CompressionOptRelationId,
+ CompressionOptionsOidIndexId,
+ COMPRESSIONOPTIONSOID,
+ -1,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ false,
}
};
@@ -712,6 +738,10 @@ static const struct object_type_map
/* OBJECT_STATISTIC_EXT */
{
"statistics object", OBJECT_STATISTIC_EXT
+ },
+ /* OCLASS_COMPRESSION_METHOD */
+ {
+ "compression method", OBJECT_COMPRESSION_METHOD
}
};
@@ -876,6 +906,7 @@ get_object_address(ObjectType objtype, Node *object,
case OBJECT_ACCESS_METHOD:
case OBJECT_PUBLICATION:
case OBJECT_SUBSCRIPTION:
+ case OBJECT_COMPRESSION_METHOD:
address = get_object_address_unqualified(objtype,
(Value *) object, missing_ok);
break;
@@ -1182,6 +1213,11 @@ get_object_address_unqualified(ObjectType objtype,
address.objectId = get_subscription_oid(name, missing_ok);
address.objectSubId = 0;
break;
+ case OBJECT_COMPRESSION_METHOD:
+ address.classId = CompressionMethodRelationId;
+ address.objectId = get_compression_method_oid(name, missing_ok);
+ address.objectSubId = 0;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
/* placate compiler, which doesn't know elog won't return */
@@ -2139,6 +2175,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_SCHEMA:
case OBJECT_SUBSCRIPTION:
case OBJECT_TABLESPACE:
+ case OBJECT_COMPRESSION_METHOD:
if (list_length(name) != 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -2395,12 +2432,14 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
case OBJECT_ACCESS_METHOD:
+ case OBJECT_COMPRESSION_METHOD:
/* We treat these object types as being owned by superusers */
if (!superuser_arg(roleid))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser")));
break;
+
case OBJECT_STATISTIC_EXT:
if (!pg_statistics_object_ownercheck(address.objectId, roleid))
aclcheck_error_type(ACLCHECK_NOT_OWNER, address.objectId);
@@ -3398,6 +3437,27 @@ getObjectDescription(const ObjectAddress *object)
break;
}
+ case OCLASS_COMPRESSION_METHOD:
+ {
+ char *name = get_compression_method_name(object->objectId);
+
+ if (!name)
+ elog(ERROR, "cache lookup failed for compression method %u",
+ object->objectId);
+ appendStringInfo(&buffer, _("compression method %s"), name);
+ pfree(name);
+ break;
+ }
+
+ case OCLASS_COMPRESSION_OPTIONS:
+ {
+ char *name = get_compression_method_name_for_opt(object->objectId);
+
+ appendStringInfo(&buffer, _("compression options for %s"), name);
+ pfree(name);
+ break;
+ }
+
case OCLASS_TRANSFORM:
{
HeapTuple trfTup;
@@ -3919,6 +3979,14 @@ getObjectTypeDescription(const ObjectAddress *object)
appendStringInfoString(&buffer, "transform");
break;
+ case OCLASS_COMPRESSION_METHOD:
+ appendStringInfoString(&buffer, "compression method");
+ break;
+
+ case OCLASS_COMPRESSION_OPTIONS:
+ appendStringInfoString(&buffer, "compression options");
+ break;
+
/*
* There's intentionally no default: case here; we want the
* compiler to warn if a new OCLASS hasn't been handled above.
@@ -4160,6 +4228,30 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case OCLASS_COMPRESSION_METHOD:
+ {
+ char *cmname = get_compression_method_name(object->objectId);
+
+ if (!cmname)
+ elog(ERROR, "cache lookup failed for compression method %u",
+ object->objectId);
+ appendStringInfoString(&buffer, quote_identifier(cmname));
+ if (objname)
+ *objname = list_make1(cmname);
+ else
+ pfree(cmname);
+ break;
+ }
+
+ case OCLASS_COMPRESSION_OPTIONS:
+ {
+ appendStringInfo(&buffer, "%u",
+ object->objectId);
+ if (objname)
+ *objname = list_make1(psprintf("%u", object->objectId));
+ break;
+ }
+
case OCLASS_CONSTRAINT:
{
HeapTuple conTup;
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index e02d312008..531a820464 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -22,6 +22,7 @@
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 4a6c99e090..e23abf64f1 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -13,8 +13,8 @@ top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
- collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
- dbcommands.o define.o discard.o dropcmds.o \
+ collationcmds.o compressioncmds.o constraint.o conversioncmds.o copy.o \
+ createas.o dbcommands.o define.o discard.o dropcmds.o \
event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o matview.o operatorcmds.o opclasscmds.o \
policy.o portalcmds.o prepare.o proclang.o publicationcmds.o \
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 4f8147907c..4f18e4083f 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -385,6 +385,7 @@ ExecRenameStmt(RenameStmt *stmt)
case OBJECT_TSTEMPLATE:
case OBJECT_PUBLICATION:
case OBJECT_SUBSCRIPTION:
+ case OBJECT_COMPRESSION_METHOD:
{
ObjectAddress address;
Relation catalog;
@@ -500,6 +501,7 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
case OBJECT_TSDICTIONARY:
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
+ case OBJECT_COMPRESSION_METHOD:
{
Relation catalog;
Relation relation;
@@ -627,6 +629,8 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_COMPRESSION_METHOD:
+ case OCLASS_COMPRESSION_OPTIONS:
/* ignore object types that don't have schema-qualified names */
break;
diff --git a/src/backend/commands/compressioncmds.c b/src/backend/commands/compressioncmds.c
new file mode 100644
index 0000000000..fa2ad8a864
--- /dev/null
+++ b/src/backend/commands/compressioncmds.c
@@ -0,0 +1,489 @@
+/*-------------------------------------------------------------------------
+ *
+ * compressioncmds.c
+ * Routines for SQL commands that manipulate compression methods.
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/commands/compressioncmds.c
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "miscadmin.h"
+
+#include "access/compression.h"
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/reloptions.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "parser/parse_func.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+static CompressionMethodRoutine * get_compression_method_routine(Oid cmhandler, Oid typeid);
+
+/*
+ * Convert a handler function name to an Oid. If the return type of the
+ * function doesn't match the given AM type, an error is raised.
+ *
+ * This function either return valid function Oid or throw an error.
+ */
+static Oid
+LookupCompressionHandlerFunc(List *handlerName)
+{
+ static const Oid funcargtypes[1] = {INTERNALOID};
+ Oid handlerOid;
+
+ if (handlerName == NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("handler function is not specified")));
+
+ /* handlers have one argument of type internal */
+ handlerOid = LookupFuncName(handlerName, 1, funcargtypes, false);
+
+ /* check that handler has the correct return type */
+ if (get_func_rettype(handlerOid) != COMPRESSION_HANDLEROID)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("function %s must return type %s",
+ NameListToString(handlerName),
+ "compression_handler")));
+
+ return handlerOid;
+}
+
+static ObjectAddress
+CreateCompressionMethod(char *cmName, List *handlerName)
+{
+ Relation rel;
+ ObjectAddress myself;
+ ObjectAddress referenced;
+ Oid cmoid;
+ Oid cmhandler;
+ bool nulls[Natts_pg_compression];
+ Datum values[Natts_pg_compression];
+ HeapTuple tup;
+
+ rel = heap_open(CompressionMethodRelationId, RowExclusiveLock);
+
+ /* Check if name is used */
+ cmoid = GetSysCacheOid1(COMPRESSIONMETHODNAME, CStringGetDatum(cmName));
+ if (OidIsValid(cmoid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("compression method \"%s\" already exists",
+ cmName)));
+
+ /*
+ * Get the handler function oid and compression method routine
+ */
+ cmhandler = LookupCompressionHandlerFunc(handlerName);
+
+ /*
+ * Insert tuple into pg_compression.
+ */
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+
+ values[Anum_pg_compression_cmname - 1] =
+ DirectFunctionCall1(namein, CStringGetDatum(cmName));
+ values[Anum_pg_compression_cmhandler - 1] = ObjectIdGetDatum(cmhandler);
+
+ tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+
+ cmoid = CatalogTupleInsert(rel, tup);
+ heap_freetuple(tup);
+
+ ObjectAddressSet(myself, CompressionMethodRelationId, cmoid);
+
+ /* Record dependency on handler function */
+ ObjectAddressSet(referenced, ProcedureRelationId, cmhandler);
+
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ recordDependencyOnCurrentExtension(&myself, false);
+
+ heap_close(rel, RowExclusiveLock);
+
+ return myself;
+}
+
+/*
+ * CREATE COMPRESSION METHOD .. HANDLER ..
+ */
+ObjectAddress
+DefineCompressionMethod(List *names, List *parameters)
+{
+ char *cmName;
+ ListCell *pl;
+ DefElem *handlerEl = NULL;
+
+ if (list_length(names) != 1)
+ elog(ERROR, "compression method name cannot be qualified");
+
+ cmName = strVal(linitial(names));
+
+ /* Must be super user */
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to create compression method \"%s\"",
+ cmName),
+ errhint("Must be superuser to create an compression method.")));
+
+ foreach(pl, parameters)
+ {
+ DefElem *defel = (DefElem *) lfirst(pl);
+ DefElem **defelp;
+
+ if (pg_strcasecmp(defel->defname, "handler") == 0)
+ defelp = &handlerEl;
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("compression method attribute \"%s\" not recognized",
+ defel->defname)));
+ break;
+ }
+
+ *defelp = defel;
+ }
+
+ if (!handlerEl)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("compression method handler is not specified")));
+
+ return CreateCompressionMethod(cmName, (List *) handlerEl->arg);
+}
+
+void
+RemoveCompressionMethodById(Oid cmOid)
+{
+ Relation relation;
+ HeapTuple tup;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to drop compression methods")));
+
+ relation = heap_open(CompressionMethodRelationId, RowExclusiveLock);
+
+ tup = SearchSysCache1(COMPRESSIONMETHODOID, ObjectIdGetDatum(cmOid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for compression method %u", cmOid);
+
+ CatalogTupleDelete(relation, &tup->t_self);
+
+ ReleaseSysCache(tup);
+
+ heap_close(relation, RowExclusiveLock);
+}
+
+/*
+ * Create new record in pg_compression_opt
+ */
+Oid
+CreateCompressionOptions(Form_pg_attribute attr, Oid cmid, List *options)
+{
+ Relation rel;
+ HeapTuple tup,
+ newtup;
+ Oid cmoptoid;
+ Datum values[Natts_pg_compression_opt];
+ bool nulls[Natts_pg_compression_opt];
+
+ ObjectAddress myself,
+ ref1,
+ ref2,
+ ref3;
+
+ CompressionMethodRoutine *routine;
+ Form_pg_compression cmform;
+
+ /* Initialize buffers for new tuple values */
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+
+ /* Get handler function OID for the compression method */
+ tup = SearchSysCache1(COMPRESSIONMETHODOID, ObjectIdGetDatum(cmid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for compression method %u", cmid);
+ cmform = (Form_pg_compression) GETSTRUCT(tup);
+ routine = get_compression_method_routine(cmform->cmhandler, attr->atttypid);
+
+ if (routine->configure == NULL && options != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("the compression method \"%s\" does not take any options",
+ NameStr(cmform->cmname))));
+ else if (routine->configure && options != NIL)
+ routine->configure(attr, options);
+
+ rel = heap_open(CompressionOptRelationId, RowExclusiveLock);
+
+ cmoptoid = GetNewOidWithIndex(rel, CompressionOptionsOidIndexId,
+ Anum_pg_compression_opt_cmoptoid);
+ values[Anum_pg_compression_opt_cmoptoid - 1] = ObjectIdGetDatum(cmoptoid);
+ values[Anum_pg_compression_opt_cmname - 1] = NameGetDatum(&cmform->cmname);
+ values[Anum_pg_compression_opt_cmhandler - 1] = ObjectIdGetDatum(cmform->cmhandler);
+
+ if (options)
+ values[Anum_pg_compression_opt_cmoptions - 1] = optionListToArray(options);
+ else
+ nulls[Anum_pg_compression_opt_cmoptions - 1] = true;
+
+ newtup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+ CatalogTupleInsert(rel, newtup);
+ heap_freetuple(newtup);
+
+ ReleaseSysCache(tup);
+
+ ObjectAddressSet(myself, CompressionOptRelationId, cmoptoid);
+ ObjectAddressSet(ref1, ProcedureRelationId, cmform->cmhandler);
+ ObjectAddressSubSet(ref2, RelationRelationId, attr->attrelid, attr->attnum);
+ ObjectAddressSet(ref3, CompressionMethodRelationId, cmid);
+
+ recordDependencyOn(&myself, &ref1, DEPENDENCY_NORMAL);
+ recordDependencyOn(&ref2, &myself, DEPENDENCY_NORMAL);
+ recordDependencyOn(&myself, &ref3, DEPENDENCY_NORMAL);
+ recordDependencyOnCurrentExtension(&myself, false);
+ heap_close(rel, RowExclusiveLock);
+
+ return cmoptoid;
+}
+
+/*
+ * Remove the compression options record from pg_compression_opt
+ */
+void
+RemoveCompressionOptionsById(Oid cmoptoid)
+{
+ Relation relation;
+ HeapTuple tup;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to drop compression options")));
+
+ tup = SearchSysCache1(COMPRESSIONOPTIONSOID, ObjectIdGetDatum(cmoptoid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+
+ relation = heap_open(CompressionOptRelationId, RowExclusiveLock);
+ CatalogTupleDelete(relation, &tup->t_self);
+ heap_close(relation, RowExclusiveLock);
+ ReleaseSysCache(tup);
+}
+
+ColumnCompression *
+GetColumnCompressionForAttribute(Form_pg_attribute att)
+{
+ HeapTuple tuple;
+ Form_pg_compression_opt cmoptform;
+ ColumnCompression *compression = makeNode(ColumnCompression);
+
+ /* Get handler function OID for the compression method */
+ tuple = SearchSysCache1(COMPRESSIONOPTIONSOID,
+ ObjectIdGetDatum(att->attcompression));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for compression options %u",
+ att->attcompression);
+
+ cmoptform = (Form_pg_compression_opt) GETSTRUCT(tuple);
+ compression->methodName = pstrdup(NameStr(cmoptform->cmname));
+ compression->options = GetCompressionOptionsList(att->attcompression);
+ ReleaseSysCache(tuple);
+
+ return compression;
+}
+
+void
+CheckCompressionMismatch(ColumnCompression * c1, ColumnCompression * c2,
+ const char *attributeName)
+{
+ if (strcmp(c1->methodName, c2->methodName))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("column \"%s\" has a compression method conflict",
+ attributeName),
+ errdetail("%s versus %s", c1->methodName, c2->methodName)));
+
+ if (!equal(c1->options, c2->options))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("column \"%s\" has a compression options conflict",
+ attributeName),
+ errdetail("(%s) versus (%s)",
+ formatRelOptions(c1->options),
+ formatRelOptions(c2->options))));
+}
+
+/*
+ * get_compression_method_oid
+ *
+ * If missing_ok is false, throw an error if compression method not found.
+ * If missing_ok is true, just return InvalidOid.
+ */
+Oid
+get_compression_method_oid(const char *cmname, bool missing_ok)
+{
+ HeapTuple tup;
+ Oid oid = InvalidOid;
+
+ tup = SearchSysCache1(COMPRESSIONMETHODNAME, CStringGetDatum(cmname));
+ if (HeapTupleIsValid(tup))
+ {
+ oid = HeapTupleGetOid(tup);
+ ReleaseSysCache(tup);
+ }
+
+ if (!OidIsValid(oid) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("compression method \"%s\" does not exist", cmname)));
+
+ return oid;
+}
+
+/*
+ * get_compression_method_name
+ *
+ * given an compression method OID, look up its name.
+ */
+char *
+get_compression_method_name(Oid cmOid)
+{
+ HeapTuple tup;
+ char *result = NULL;
+
+ tup = SearchSysCache1(COMPRESSIONMETHODOID, ObjectIdGetDatum(cmOid));
+ if (HeapTupleIsValid(tup))
+ {
+ Form_pg_compression cmform = (Form_pg_compression) GETSTRUCT(tup);
+
+ result = pstrdup(NameStr(cmform->cmname));
+ ReleaseSysCache(tup);
+ }
+ return result;
+}
+
+/*
+ * get_compression_method_name_for_opt
+ *
+ * given an compression options OID, look up a name for used compression method.
+ */
+char *
+get_compression_method_name_for_opt(Oid cmoptoid)
+{
+ HeapTuple tup;
+ char *result = NULL;
+ Form_pg_compression_opt cmoptform;
+
+ tup = SearchSysCache1(COMPRESSIONOPTIONSOID, ObjectIdGetDatum(cmoptoid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+
+ cmoptform = (Form_pg_compression_opt) GETSTRUCT(tup);
+ result = pstrdup(NameStr(cmoptform->cmname));
+ ReleaseSysCache(tup);
+
+ return result;
+}
+
+/* get_compression_options */
+List *
+GetCompressionOptionsList(Oid cmoptoid)
+{
+ HeapTuple tuple;
+ List *result = NULL;
+ bool isnull;
+ Datum cmoptions;
+
+ /* Get handler function OID for the compression method */
+ tuple = SearchSysCache1(COMPRESSIONOPTIONSOID, ObjectIdGetDatum(cmoptoid));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+
+ cmoptions = SysCacheGetAttr(COMPRESSIONOPTIONSOID, tuple,
+ Anum_pg_compression_opt_cmoptions, &isnull);
+
+ if (!isnull)
+ result = untransformRelOptions(cmoptions);
+
+ ReleaseSysCache(tuple);
+ return result;
+}
+
+CompressionMethodRoutine *
+GetCompressionRoutine(Oid cmoptoid)
+{
+ HeapTuple tuple;
+ Form_pg_compression_opt cmopt;
+ regproc cmhandler;
+
+ /* Get handler function OID for the compression method */
+ tuple = SearchSysCache1(COMPRESSIONOPTIONSOID, ObjectIdGetDatum(cmoptoid));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+
+ cmopt = (Form_pg_compression_opt) GETSTRUCT(tuple);
+ cmhandler = cmopt->cmhandler;
+
+ /* Complain if handler OID is invalid */
+ if (!RegProcedureIsValid(cmhandler))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("could not find compression method handler for compression options %u",
+ cmoptoid)));
+
+ ReleaseSysCache(tuple);
+
+ /* And finally, call the handler function to get the API struct. */
+ return get_compression_method_routine(cmhandler, InvalidOid);
+}
+
+/*
+ * Call the specified compression method handler
+ * routine to get its CompressionMethodRoutine struct,
+ * which will be palloc'd in the caller's context.
+ */
+static CompressionMethodRoutine *
+get_compression_method_routine(Oid cmhandler, Oid typeid)
+{
+ Datum datum;
+ CompressionMethodRoutine *routine;
+ CompressionMethodOpArgs opargs;
+
+ opargs.typeid = typeid;
+ opargs.cmhanderid = cmhandler;
+
+ datum = OidFunctionCall1(cmhandler, PointerGetDatum(&opargs));
+ routine = (CompressionMethodRoutine *) DatumGetPointer(datum);
+
+ if (routine == NULL || !IsA(routine, CompressionMethodRoutine))
+ elog(ERROR, "compression method handler function %u "
+ "did not return an CompressionMethodRoutine struct",
+ cmhandler);
+
+ return routine;
+}
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 13eb9e34ba..f84588ed31 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2781,8 +2781,8 @@ CopyFrom(CopyState cstate)
List *recheckIndexes = NIL;
/* OK, store the tuple and create index entries for it */
- heap_insert(resultRelInfo->ri_RelationDesc, tuple, mycid,
- hi_options, bistate);
+ heap_insert(resultRelInfo->ri_RelationDesc, tuple,
+ mycid, hi_options, bistate);
if (resultRelInfo->ri_NumIndices > 0)
recheckIndexes = ExecInsertIndexTuples(slot,
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 4d77411a68..aba2087839 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -112,7 +112,8 @@ create_ctas_internal(List *attrList, IntoClause *into)
* Create the relation. (This will error out if there's an existing view,
* so we don't need more code to complain if "replace" is false.)
*/
- intoRelationAddr = DefineRelation(create, relkind, InvalidOid, NULL, NULL);
+ intoRelationAddr = DefineRelation(create, relkind, InvalidOid, NULL, NULL,
+ NULL);
/*
* If necessary, create a TOAST table for the target table. Note that
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index 2b30677d6f..8611db22ec 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -275,6 +275,10 @@ does_not_exist_skipping(ObjectType objtype, Node *object)
name = NameListToString(castNode(List, object));
}
break;
+ case OBJECT_COMPRESSION_METHOD:
+ msg = gettext_noop("compression method \"%s\" does not exist, skipping");
+ name = strVal((Value *) object);
+ break;
case OBJECT_CONVERSION:
if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
{
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index fa7d0d015a..a2eee78a64 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -91,6 +91,7 @@ static event_trigger_support_data event_trigger_support[] = {
{"CAST", true},
{"CONSTRAINT", true},
{"COLLATION", true},
+ {"COMPRESSION METHOD", true},
{"CONVERSION", true},
{"DATABASE", false},
{"DOMAIN", true},
@@ -1085,6 +1086,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
case OBJECT_CAST:
case OBJECT_COLUMN:
case OBJECT_COLLATION:
+ case OBJECT_COMPRESSION_METHOD:
case OBJECT_CONVERSION:
case OBJECT_DEFACL:
case OBJECT_DEFAULT:
@@ -1144,6 +1146,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
case OCLASS_ROLE:
+ case OCLASS_COMPRESSION_OPTIONS:
/* no support for global objects */
return false;
case OCLASS_EVENT_TRIGGER:
@@ -1183,6 +1186,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_COMPRESSION_METHOD:
return true;
/*
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index 9ad991507f..b840309d03 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -61,7 +61,7 @@ static void import_error_callback(void *arg);
* processing, hence any validation should be done before this
* conversion.
*/
-static Datum
+Datum
optionListToArray(List *options)
{
ArrayBuildState *astate = NULL;
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 5e1b0fe289..eb9f72dcba 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -212,7 +212,8 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
stmt->tablespacename = NULL;
stmt->if_not_exists = seq->if_not_exists;
- address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL, NULL);
+ address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL, NULL,
+ NULL);
seqoid = address.objectId;
Assert(seqoid != InvalidOid);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index d19846d005..f31be71e65 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -14,6 +14,7 @@
*/
#include "postgres.h"
+#include "access/compression.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "access/multixact.h"
@@ -33,6 +34,8 @@
#include "catalog/partition.h"
#include "catalog/pg_am.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_depend.h"
@@ -41,6 +44,7 @@
#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
+#include "catalog/pg_proc.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
@@ -90,6 +94,7 @@
#include "storage/smgr.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+#include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
@@ -459,6 +464,8 @@ static void ATExecGenericOptions(Relation rel, List *options);
static void ATExecEnableRowSecurity(Relation rel);
static void ATExecDisableRowSecurity(Relation rel);
static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
+static void ATExecAlterColumnCompression(AlteredTableInfo *tab, Relation rel,
+ const char *column, ColumnCompression * compression, LOCKMODE lockmode);
static void copy_relation_data(SMgrRelation rel, SMgrRelation dst,
ForkNumber forkNum, char relpersistence);
@@ -503,7 +510,8 @@ static ObjectAddress ATExecDetachPartition(Relation rel, RangeVar *name);
*/
ObjectAddress
DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
- ObjectAddress *typaddress, const char *queryString)
+ ObjectAddress *typaddress, const char *queryString,
+ Node **pAlterStmt)
{
char relname[NAMEDATALEN];
Oid namespaceId;
@@ -523,6 +531,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
Oid ofTypeId;
ObjectAddress address;
+ AlterTableStmt *alterStmt = NULL;
/*
* Truncate relname to appropriate length (probably a waste of time, as
@@ -724,6 +733,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
if (colDef->identity)
attr->attidentity = colDef->identity;
+
+ transformColumnCompression(colDef, stmt->relation, &alterStmt);
}
/*
@@ -921,6 +932,11 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
*/
relation_close(rel, NoLock);
+ if (pAlterStmt)
+ *pAlterStmt = (Node *) alterStmt;
+ else
+ Assert(!alterStmt);
+
return address;
}
@@ -1610,6 +1626,7 @@ storage_name(char c)
}
}
+
/*----------
* MergeAttributes
* Returns new schema given initial schema and superclasses.
@@ -1944,6 +1961,19 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
storage_name(def->storage),
storage_name(attribute->attstorage))));
+ if (OidIsValid(attribute->attcompression))
+ {
+ ColumnCompression *compression =
+ GetColumnCompressionForAttribute(attribute);
+
+ if (!def->compression)
+ def->compression = compression;
+ else
+ CheckCompressionMismatch(def->compression,
+ compression,
+ attributeName);
+ }
+
def->inhcount++;
/* Merge of NOT NULL constraints = OR 'em together */
def->is_not_null |= attribute->attnotnull;
@@ -1971,6 +2001,9 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
def->collOid = attribute->attcollation;
def->constraints = NIL;
def->location = -1;
+ if (OidIsValid(attribute->attcompression))
+ def->compression =
+ GetColumnCompressionForAttribute(attribute);
inhSchema = lappend(inhSchema, def);
newattno[parent_attno - 1] = ++child_attno;
}
@@ -2180,6 +2213,13 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
storage_name(def->storage),
storage_name(newdef->storage))));
+ if (!def->compression)
+ def->compression = newdef->compression;
+ else if (newdef->compression)
+ CheckCompressionMismatch(def->compression,
+ newdef->compression,
+ attributeName);
+
/* Mark the column as locally defined */
def->is_local = true;
/* Merge of NOT NULL constraints = OR 'em together */
@@ -3279,6 +3319,7 @@ AlterTableGetLockLevel(List *cmds)
*/
case AT_GenericOptions:
case AT_AlterColumnGenericOptions:
+ case AT_AlterColumnCompression:
cmd_lockmode = AccessExclusiveLock;
break;
@@ -3770,6 +3811,12 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* No command-specific prep needed */
pass = AT_PASS_MISC;
break;
+ case AT_AlterColumnCompression:
+ ATSimplePermissions(rel, ATT_TABLE);
+ /* FIXME This command never recurses */
+ /* No command-specific prep needed */
+ pass = AT_PASS_MISC;
+ break;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
@@ -4118,6 +4165,11 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
case AT_DetachPartition:
ATExecDetachPartition(rel, ((PartitionCmd *) cmd->def)->name);
break;
+ case AT_AlterColumnCompression:
+ ATExecAlterColumnCompression(tab, rel, cmd->name,
+ (ColumnCompression *) cmd->def,
+ lockmode);
+ break;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
@@ -5338,6 +5390,8 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
attribute.attislocal = colDef->is_local;
attribute.attinhcount = colDef->inhcount;
attribute.attcollation = collOid;
+ attribute.attcompression = InvalidOid;
+
/* attribute.attacl is handled by InsertPgAttributeTuple */
ReleaseSysCache(typeTuple);
@@ -6438,6 +6492,11 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE loc
errmsg("cannot alter system column \"%s\"",
colName)));
+ if (attrtuple->attcompression && newstorage != 'x' && newstorage != 'm')
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("compressed columns can only have storage MAIN or EXTENDED")));
+
/*
* safety check: do not allow toasted storage modes unless column datatype
* is TOAST-aware.
@@ -9361,6 +9420,8 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_COMPRESSION_METHOD:
+ case OCLASS_COMPRESSION_OPTIONS:
/*
* We don't expect any of these sorts of objects to depend on
@@ -9410,7 +9471,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
if (!(foundDep->refclassid == TypeRelationId &&
foundDep->refobjid == attTup->atttypid) &&
!(foundDep->refclassid == CollationRelationId &&
- foundDep->refobjid == attTup->attcollation))
+ foundDep->refobjid == attTup->attcollation) &&
+ !(foundDep->refclassid == CompressionMethodRelationId &&
+ foundDep->refobjid == attTup->attcompression))
elog(ERROR, "found unexpected dependency for column");
CatalogTupleDelete(depRel, &depTup->t_self);
@@ -9432,6 +9495,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
attTup->attbyval = tform->typbyval;
attTup->attalign = tform->typalign;
attTup->attstorage = tform->typstorage;
+ attTup->attcompression = InvalidOid;
ReleaseSysCache(typeTuple);
@@ -12484,6 +12548,83 @@ ATExecGenericOptions(Relation rel, List *options)
heap_freetuple(tuple);
}
+static void
+ATExecAlterColumnCompression(AlteredTableInfo *tab,
+ Relation rel,
+ const char *column,
+ ColumnCompression * compression,
+ LOCKMODE lockmode)
+{
+ Relation attrel;
+ HeapTuple atttuple;
+ Form_pg_attribute atttableform;
+ AttrNumber attnum;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_attribute];
+ bool nulls[Natts_pg_attribute];
+ bool replace[Natts_pg_attribute];
+
+ attrel = heap_open(AttributeRelationId, RowExclusiveLock);
+
+ atttuple = SearchSysCacheAttName(RelationGetRelid(rel), column);
+ if (!HeapTupleIsValid(atttuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("column \"%s\" of relation \"%s\" does not exist",
+ column, RelationGetRelationName(rel))));
+
+ /* Prevent them from altering a system attribute */
+ atttableform = (Form_pg_attribute) GETSTRUCT(atttuple);
+ attnum = atttableform->attnum;
+ if (attnum <= 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot alter system column \"%s\"", column)));
+
+ if (atttableform->attstorage != 'x' && atttableform->attstorage != 'm')
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("storage for \"%s\" should be MAIN or EXTENDED", column)));
+
+ /* Initialize buffers for new tuple values */
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+ memset(replace, false, sizeof(replace));
+
+ if (compression->methodName)
+ {
+ /* SET COMPRESSED */
+ Oid cmid,
+ cmoptoid;
+
+ cmid = get_compression_method_oid(compression->methodName, false);
+ cmoptoid = CreateCompressionOptions(atttableform, cmid, compression->options);
+
+ values[Anum_pg_attribute_attcompression - 1] = ObjectIdGetDatum(cmoptoid);
+ replace[Anum_pg_attribute_attcompression - 1] = true;
+
+ }
+ else
+ {
+ /* SET NOT COMPRESSED */
+ values[Anum_pg_attribute_attcompression - 1] = ObjectIdGetDatum(InvalidOid);
+ replace[Anum_pg_attribute_attcompression - 1] = true;
+ }
+
+ newtuple = heap_modify_tuple(atttuple, RelationGetDescr(attrel),
+ values, nulls, replace);
+ CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
+ heap_freetuple(newtuple);
+
+ InvokeObjectPostAlterHook(RelationRelationId,
+ RelationGetRelid(rel),
+ atttableform->attnum);
+
+ ReleaseSysCache(atttuple);
+ heap_close(attrel, RowExclusiveLock);
+}
+
+
/*
* Preparation phase for SET LOGGED/UNLOGGED
*
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index f86af4c054..be4a6c5f38 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -40,6 +40,7 @@
#include "catalog/pg_am.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_depend.h"
@@ -2182,7 +2183,7 @@ DefineCompositeType(RangeVar *typevar, List *coldeflist)
* Finally create the relation. This also creates the type.
*/
DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, &address,
- NULL);
+ NULL, NULL);
return address;
}
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index c1e80e61d4..cc0182075e 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -251,7 +251,7 @@ DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
* false).
*/
address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid, NULL,
- NULL);
+ NULL, NULL);
Assert(address.objectId != InvalidOid);
/* Make the new view relation visible */
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index 5dfc49deb9..3a80e997a9 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -302,6 +302,9 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc
var->vartypmod != -1))
return false; /* type mismatch */
+ if (OidIsValid(att_tup->attcompression))
+ return false;
+
tlist_item = lnext(tlist_item);
}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index e48921fff1..c9de2344fe 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2806,6 +2806,7 @@ _copyColumnDef(const ColumnDef *from)
COPY_STRING_FIELD(colname);
COPY_NODE_FIELD(typeName);
+ COPY_NODE_FIELD(compression);
COPY_SCALAR_FIELD(inhcount);
COPY_SCALAR_FIELD(is_local);
COPY_SCALAR_FIELD(is_not_null);
@@ -2824,6 +2825,17 @@ _copyColumnDef(const ColumnDef *from)
return newnode;
}
+static ColumnCompression *
+_copyColumnCompression(const ColumnCompression * from)
+{
+ ColumnCompression *newnode = makeNode(ColumnCompression);
+
+ COPY_STRING_FIELD(methodName);
+ COPY_NODE_FIELD(options);
+
+ return newnode;
+}
+
static Constraint *
_copyConstraint(const Constraint *from)
{
@@ -5473,6 +5485,9 @@ copyObjectImpl(const void *from)
case T_ColumnDef:
retval = _copyColumnDef(from);
break;
+ case T_ColumnCompression:
+ retval = _copyColumnCompression(from);
+ break;
case T_Constraint:
retval = _copyConstraint(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 2a83da9aa9..d618cc637e 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2544,6 +2544,7 @@ _equalColumnDef(const ColumnDef *a, const ColumnDef *b)
{
COMPARE_STRING_FIELD(colname);
COMPARE_NODE_FIELD(typeName);
+ COMPARE_NODE_FIELD(compression);
COMPARE_SCALAR_FIELD(inhcount);
COMPARE_SCALAR_FIELD(is_local);
COMPARE_SCALAR_FIELD(is_not_null);
@@ -2562,6 +2563,15 @@ _equalColumnDef(const ColumnDef *a, const ColumnDef *b)
return true;
}
+static bool
+_equalColumnCompression(const ColumnCompression * a, const ColumnCompression * b)
+{
+ COMPARE_STRING_FIELD(methodName);
+ COMPARE_NODE_FIELD(options);
+
+ return true;
+}
+
static bool
_equalConstraint(const Constraint *a, const Constraint *b)
{
@@ -3615,6 +3625,9 @@ equal(const void *a, const void *b)
case T_ColumnDef:
retval = _equalColumnDef(a, b);
break;
+ case T_ColumnCompression:
+ retval = _equalColumnCompression(a, b);
+ break;
case T_Constraint:
retval = _equalConstraint(a, b);
break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index c2a93b2d4c..81e334a1d3 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -3623,6 +3623,8 @@ raw_expression_tree_walker(Node *node,
if (walker(coldef->typeName, context))
return true;
+ if (walker(coldef->compression, context))
+ return true;
if (walker(coldef->raw_default, context))
return true;
if (walker(coldef->collClause, context))
@@ -3630,6 +3632,14 @@ raw_expression_tree_walker(Node *node,
/* for now, constraints are ignored */
}
break;
+ case T_ColumnCompression:
+ {
+ ColumnCompression *colcmp = (ColumnCompression *) node;
+
+ if (walker(colcmp->options, context))
+ return true;
+ }
+ break;
case T_IndexElem:
{
IndexElem *indelem = (IndexElem *) node;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index c97ee24ade..5ae1db146e 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2799,6 +2799,7 @@ _outColumnDef(StringInfo str, const ColumnDef *node)
WRITE_STRING_FIELD(colname);
WRITE_NODE_FIELD(typeName);
+ WRITE_NODE_FIELD(compression);
WRITE_INT_FIELD(inhcount);
WRITE_BOOL_FIELD(is_local);
WRITE_BOOL_FIELD(is_not_null);
@@ -2815,6 +2816,15 @@ _outColumnDef(StringInfo str, const ColumnDef *node)
WRITE_LOCATION_FIELD(location);
}
+static void
+_outColumnCompression(StringInfo str, const ColumnCompression * node)
+{
+ WRITE_NODE_TYPE("COLUMNCOMPRESSION");
+
+ WRITE_STRING_FIELD(methodName);
+ WRITE_NODE_FIELD(options);
+}
+
static void
_outTypeName(StringInfo str, const TypeName *node)
{
@@ -4106,6 +4116,9 @@ outNode(StringInfo str, const void *obj)
case T_ColumnDef:
_outColumnDef(str, obj);
break;
+ case T_ColumnCompression:
+ _outColumnCompression(str, obj);
+ break;
case T_TypeName:
_outTypeName(str, obj);
break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c301ca465d..122d3de4d6 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -397,6 +397,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
transform_element_list transform_type_list
TriggerTransitions TriggerReferencing
publication_name_list
+ optCompressionParameters
vacuum_relation_list opt_vacuum_relation_list
%type <list> group_by_list
@@ -582,6 +583,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <list> hash_partbound partbound_datum_list range_datum_list
%type <defelt> hash_partbound_elem
+%type <node> columnCompression optColumnCompression compressedClause
+
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
* They must be listed first so that their numeric codes do not depend on
@@ -614,9 +617,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
CLUSTER COALESCE COLLATE COLLATION COLUMN COLUMNS COMMENT COMMENTS COMMIT
- COMMITTED CONCURRENTLY CONFIGURATION CONFLICT CONNECTION CONSTRAINT
- CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE
- CROSS CSV CUBE CURRENT_P
+ COMMITTED COMPRESSED COMPRESSION CONCURRENTLY CONFIGURATION CONFLICT
+ CONNECTION CONSTRAINT CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY
+ COST CREATE CROSS CSV CUBE CURRENT_P
CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA
CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
@@ -2168,6 +2171,15 @@ alter_table_cmd:
n->missing_ok = true;
$$ = (Node *)n;
}
+ /* ALTER TABLE <name> ALTER [COLUMN] <colname> SET (NOT COMPRESSED | COMPRESSED <cm> [WITH (<options>)]) */
+ | ALTER opt_column ColId SET columnCompression
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_AlterColumnCompression;
+ n->name = $3;
+ n->def = $5;
+ $$ = (Node *)n;
+ }
/* ALTER TABLE <name> DROP [COLUMN] IF EXISTS <colname> [RESTRICT|CASCADE] */
| DROP opt_column IF_P EXISTS ColId opt_drop_behavior
{
@@ -3332,11 +3344,12 @@ TypedTableElement:
| TableConstraint { $$ = $1; }
;
-columnDef: ColId Typename create_generic_options ColQualList
+columnDef: ColId Typename optColumnCompression create_generic_options ColQualList
{
ColumnDef *n = makeNode(ColumnDef);
n->colname = $1;
n->typeName = $2;
+ n->compression = (ColumnCompression *) $3;
n->inhcount = 0;
n->is_local = true;
n->is_not_null = false;
@@ -3346,8 +3359,8 @@ columnDef: ColId Typename create_generic_options ColQualList
n->raw_default = NULL;
n->cooked_default = NULL;
n->collOid = InvalidOid;
- n->fdwoptions = $3;
- SplitColQualList($4, &n->constraints, &n->collClause,
+ n->fdwoptions = $4;
+ SplitColQualList($5, &n->constraints, &n->collClause,
yyscanner);
n->location = @1;
$$ = (Node *)n;
@@ -3394,6 +3407,37 @@ columnOptions: ColId ColQualList
}
;
+compressedClause:
+ COMPRESSED name optCompressionParameters
+ {
+ ColumnCompression *n = makeNode(ColumnCompression);
+ n->methodName = $2;
+ n->options = (List *) $3;
+ $$ = (Node *) n;
+ }
+ ;
+
+columnCompression:
+ compressedClause |
+ NOT COMPRESSED
+ {
+ ColumnCompression *n = makeNode(ColumnCompression);
+ n->methodName = NULL;
+ n->options = NIL;
+ $$ = (Node *) n;
+ }
+ ;
+
+optColumnCompression:
+ compressedClause /* FIXME shift/reduce conflict on NOT COMPRESSED/NOT NULL */
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
+optCompressionParameters:
+ WITH '(' generic_option_list ')' { $$ = $3; }
+ | /*EMPTY*/ { $$ = NIL; }
+ ;
+
ColQualList:
ColQualList ColConstraint { $$ = lappend($1, $2); }
| /*EMPTY*/ { $$ = NIL; }
@@ -5754,6 +5798,15 @@ DefineStmt:
n->if_not_exists = true;
$$ = (Node *)n;
}
+ | CREATE COMPRESSION METHOD any_name HANDLER handler_name
+ {
+ DefineStmt *n = makeNode(DefineStmt);
+ n->kind = OBJECT_COMPRESSION_METHOD;
+ n->args = NIL;
+ n->defnames = $4;
+ n->definition = list_make1(makeDefElem("handler", (Node *) $6, @6));
+ $$ = (Node *) n;
+ }
;
definition: '(' def_list ')' { $$ = $2; }
@@ -6262,6 +6315,7 @@ drop_type_any_name:
/* object types taking name_list */
drop_type_name:
ACCESS METHOD { $$ = OBJECT_ACCESS_METHOD; }
+ | COMPRESSION METHOD { $$ = OBJECT_COMPRESSION_METHOD; }
| EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; }
| EXTENSION { $$ = OBJECT_EXTENSION; }
| FOREIGN DATA_P WRAPPER { $$ = OBJECT_FDW; }
@@ -6325,7 +6379,7 @@ opt_restart_seqs:
* The COMMENT ON statement can take different forms based upon the type of
* the object associated with the comment. The form of the statement is:
*
- * COMMENT ON [ [ ACCESS METHOD | CONVERSION | COLLATION |
+ * COMMENT ON [ [ ACCESS METHOD | COMPRESSION METHOD | CONVERSION | COLLATION |
* DATABASE | DOMAIN |
* EXTENSION | EVENT TRIGGER | FOREIGN DATA WRAPPER |
* FOREIGN TABLE | INDEX | [PROCEDURAL] LANGUAGE |
@@ -6515,6 +6569,7 @@ comment_type_any_name:
/* object types taking name */
comment_type_name:
ACCESS METHOD { $$ = OBJECT_ACCESS_METHOD; }
+ | COMPRESSION METHOD { $$ = OBJECT_COMPRESSION_METHOD; }
| DATABASE { $$ = OBJECT_DATABASE; }
| EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; }
| EXTENSION { $$ = OBJECT_EXTENSION; }
@@ -14704,6 +14759,8 @@ unreserved_keyword:
| COMMENTS
| COMMIT
| COMMITTED
+ | COMPRESSED
+ | COMPRESSION
| CONFIGURATION
| CONFLICT
| CONNECTION
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 8461da490a..39f21d4da7 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -27,6 +27,7 @@
#include "postgres.h"
#include "access/amapi.h"
+#include "access/compression.h"
#include "access/htup_details.h"
#include "access/reloptions.h"
#include "catalog/dependency.h"
@@ -494,6 +495,39 @@ generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column,
*sname_p = sname;
}
+/*
+ * transformColumnCompression
+ *
+ * Build ALTER TABLE .. SET COMPRESSED command if a compression method was
+ * specified for the column
+ */
+void
+transformColumnCompression(ColumnDef *column, RangeVar *relation,
+ AlterTableStmt **alterStmt)
+{
+ if (column->compression)
+ {
+ AlterTableCmd *cmd;
+
+ cmd = makeNode(AlterTableCmd);
+ cmd->subtype = AT_AlterColumnCompression;
+ cmd->name = column->colname;
+ cmd->def = (Node *) column->compression;
+ cmd->behavior = DROP_RESTRICT;
+ cmd->missing_ok = false;
+
+ if (!*alterStmt)
+ {
+ *alterStmt = makeNode(AlterTableStmt);
+ (*alterStmt)->relation = relation;
+ (*alterStmt)->relkind = OBJECT_TABLE;
+ (*alterStmt)->cmds = NIL;
+ }
+
+ (*alterStmt)->cmds = lappend((*alterStmt)->cmds, cmd);
+ }
+}
+
/*
* transformColumnDefinition -
* transform a single ColumnDef within CREATE TABLE
@@ -794,6 +828,16 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
cxt->alist = lappend(cxt->alist, stmt);
}
+
+ if (cxt->isalter)
+ {
+ AlterTableStmt *stmt = NULL;
+
+ transformColumnCompression(column, cxt->relation, &stmt);
+
+ if (stmt)
+ cxt->alist = lappend(cxt->alist, stmt);
+ }
}
/*
@@ -1003,6 +1047,10 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
def->collOid = attribute->attcollation;
def->constraints = NIL;
def->location = -1;
+ if (attribute->attcompression)
+ def->compression = GetColumnCompressionForAttribute(attribute);
+ else
+ def->compression = NULL;
/*
* Add to column list
diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c
index 0f607bab70..0ff169e200 100644
--- a/src/backend/replication/logical/reorderbuffer.c
+++ b/src/backend/replication/logical/reorderbuffer.c
@@ -2878,7 +2878,7 @@ ReorderBufferToastReplace(ReorderBuffer *rb, ReorderBufferTXN *txn,
VARSIZE(chunk) - VARHDRSZ);
data_done += VARSIZE(chunk) - VARHDRSZ;
}
- Assert(data_done == toast_pointer.va_extsize);
+ Assert(data_done == VARATT_EXTERNAL_GET_SIZE(toast_pointer));
/* make sure its marked as compressed or not */
if (VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer))
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 82a707af7b..331d133660 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -998,6 +998,7 @@ ProcessUtilitySlow(ParseState *pstate,
foreach(l, stmts)
{
Node *stmt = (Node *) lfirst(l);
+ Node *alterStmt = NULL;
if (IsA(stmt, CreateStmt))
{
@@ -1008,7 +1009,9 @@ ProcessUtilitySlow(ParseState *pstate,
address = DefineRelation((CreateStmt *) stmt,
RELKIND_RELATION,
InvalidOid, NULL,
- queryString);
+ queryString,
+ &alterStmt);
+
EventTriggerCollectSimpleCommand(address,
secondaryObject,
stmt);
@@ -1042,7 +1045,8 @@ ProcessUtilitySlow(ParseState *pstate,
address = DefineRelation((CreateStmt *) stmt,
RELKIND_FOREIGN_TABLE,
InvalidOid, NULL,
- queryString);
+ queryString,
+ &alterStmt);
CreateForeignTable((CreateForeignTableStmt *) stmt,
address.objectId);
EventTriggerCollectSimpleCommand(address,
@@ -1074,6 +1078,9 @@ ProcessUtilitySlow(ParseState *pstate,
NULL);
}
+ if (alterStmt)
+ lappend(stmts, alterStmt);
+
/* Need CCI between commands */
if (lnext(l) != NULL)
CommandCounterIncrement();
@@ -1283,6 +1290,11 @@ ProcessUtilitySlow(ParseState *pstate,
stmt->definition,
stmt->if_not_exists);
break;
+ case OBJECT_COMPRESSION_METHOD:
+ Assert(stmt->args == NIL);
+ address = DefineCompressionMethod(stmt->defnames,
+ stmt->definition);
+ break;
default:
elog(ERROR, "unrecognized define stmt type: %d",
(int) stmt->kind);
@@ -1696,6 +1708,11 @@ ExecDropStmt(DropStmt *stmt, bool isTopLevel)
case OBJECT_FOREIGN_TABLE:
RemoveRelations(stmt);
break;
+ case OBJECT_COMPRESSION_METHOD:
+ if (stmt->behavior == DROP_CASCADE)
+ {
+ /* TODO decompress columns instead of their deletion */
+ }
default:
RemoveObjects(stmt);
break;
@@ -2309,6 +2326,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_STATISTIC_EXT:
tag = "DROP STATISTICS";
break;
+ case OBJECT_COMPRESSION_METHOD:
+ tag = "DROP COMPRESSION METHOD";
+ break;
default:
tag = "???";
}
@@ -2412,6 +2432,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_ACCESS_METHOD:
tag = "CREATE ACCESS METHOD";
break;
+ case OBJECT_COMPRESSION_METHOD:
+ tag = "CREATE COMPRESSION METHOD";
+ break;
default:
tag = "???";
}
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index be793539a3..a5cfbe3d4c 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -418,3 +418,4 @@ PSEUDOTYPE_DUMMY_IO_FUNCS(internal);
PSEUDOTYPE_DUMMY_IO_FUNCS(opaque);
PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement);
PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray);
+PSEUDOTYPE_DUMMY_IO_FUNCS(compression_handler);
diff --git a/src/backend/utils/adt/tsvector.c b/src/backend/utils/adt/tsvector.c
index b0a9217d1e..e1314e1f2e 100644
--- a/src/backend/utils/adt/tsvector.c
+++ b/src/backend/utils/adt/tsvector.c
@@ -14,11 +14,14 @@
#include "postgres.h"
+#include "access/compression.h"
+#include "catalog/pg_type.h"
#include "libpq/pqformat.h"
#include "tsearch/ts_locale.h"
#include "tsearch/ts_utils.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
+#include "common/pg_lzcompress.h"
typedef struct
{
@@ -548,3 +551,85 @@ tsvectorrecv(PG_FUNCTION_ARGS)
PG_RETURN_TSVECTOR(vec);
}
+
+/*
+ * Compress tsvector using LZ compression.
+ * Instead of trying to compress whole tsvector we compress only text part
+ * here. This approach gives more compressibility for tsvectors.
+ */
+static struct varlena *
+tsvector_compress(CompressionOptions * cmoptions, const struct varlena *data)
+{
+ char *tmp;
+ int32 valsize = VARSIZE_ANY_EXHDR(data);
+ int32 len = valsize + VARHDRSZ_CUSTOM_COMPRESSED,
+ lenc;
+
+ char *arr = VARDATA(data),
+ *str = STRPTR((TSVector) data);
+ int32 arrsize = str - arr;
+
+ Assert(!VARATT_IS_COMPRESSED(data));
+ tmp = palloc0(len);
+
+ /* we try to compress string part of tsvector first */
+ lenc = pglz_compress(str,
+ valsize - arrsize,
+ tmp + VARHDRSZ_CUSTOM_COMPRESSED + arrsize,
+ PGLZ_strategy_default);
+
+ if (lenc >= 0)
+ {
+ /* tsvector is compressible, copy size and entries to its beginning */
+ memcpy(tmp + VARHDRSZ_CUSTOM_COMPRESSED, arr, arrsize);
+ SET_VARSIZE_COMPRESSED(tmp, arrsize + lenc + VARHDRSZ_CUSTOM_COMPRESSED);
+ return (struct varlena *) tmp;
+ }
+
+ pfree(tmp);
+ return NULL;
+}
+
+static struct varlena *
+tsvector_decompress(CompressionOptions * cmoptions, const struct varlena *data)
+{
+ char *tmp,
+ *raw_data = (char *) data + VARHDRSZ_CUSTOM_COMPRESSED;
+ int32 count,
+ arrsize,
+ len = VARRAWSIZE_4B_C(data) + VARHDRSZ;
+
+ Assert(VARATT_IS_CUSTOM_COMPRESSED(data));
+ tmp = palloc0(len);
+ SET_VARSIZE(tmp, len);
+ count = *((uint32 *) raw_data);
+ arrsize = sizeof(uint32) + count * sizeof(WordEntry);
+ memcpy(VARDATA(tmp), raw_data, arrsize);
+
+ if (pglz_decompress(raw_data + arrsize,
+ VARSIZE(data) - VARHDRSZ_CUSTOM_COMPRESSED - arrsize,
+ VARDATA(tmp) + arrsize,
+ VARRAWSIZE_4B_C(data) - arrsize) < 0)
+ elog(ERROR, "compressed tsvector is corrupted");
+
+ return (struct varlena *) tmp;
+}
+
+Datum
+tsvector_compression_handler(PG_FUNCTION_ARGS)
+{
+ CompressionMethodOpArgs *opargs = (CompressionMethodOpArgs *)
+ PG_GETARG_POINTER(0);
+ CompressionMethodRoutine *cmr = makeNode(CompressionMethodRoutine);
+ Oid typeid = opargs->typeid;
+
+ if (OidIsValid(typeid) && typeid != TSVECTOROID)
+ elog(ERROR, "unexpected type %d for tsvector compression handler", typeid);
+
+ cmr->configure = NULL;
+ cmr->drop = NULL;
+ cmr->compress = tsvector_compress;
+ cmr->decompress = tsvector_decompress;
+
+ PG_RETURN_POINTER(cmr);
+}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 1908420d82..de834c5593 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -30,6 +30,7 @@
#include <fcntl.h>
#include <unistd.h>
+#include "access/compression.h"
#include "access/hash.h"
#include "access/htup_details.h"
#include "access/multixact.h"
@@ -77,6 +78,7 @@
#include "storage/smgr.h"
#include "utils/array.h"
#include "utils/builtins.h"
+#include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 888edbb325..c689f60e47 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -31,6 +31,8 @@
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
@@ -309,6 +311,39 @@ static const struct cachedesc cacheinfo[] = {
},
8
},
+ {CompressionMethodRelationId, /* COMPRESSIONMETHODOID */
+ CompressionMethodOidIndexId,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 8
+ },
+ {CompressionMethodRelationId, /* COMPRESSIONMETHODNAME */
+ CompressionMethodNameIndexId,
+ 1,
+ {
+ Anum_pg_compression_cmname,
+ 0,
+ 0,
+ 0
+ },
+ 8
+ },
+ {CompressionOptRelationId, /* COMPRESSIONOPTIONSOID */
+ CompressionOptionsOidIndexId,
+ 1,
+ {
+ Anum_pg_compression_opt_cmoptoid,
+ 0,
+ 0,
+ 0
+ },
+ 8,
+ },
{ConversionRelationId, /* CONDEFAULT */
ConversionDefaultIndexId,
4,
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 4b47951de1..7ba6d31251 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -54,6 +54,7 @@ static DumpableObject **oprinfoindex;
static DumpableObject **collinfoindex;
static DumpableObject **nspinfoindex;
static DumpableObject **extinfoindex;
+static DumpableObject **cminfoindex;
static int numTables;
static int numTypes;
static int numFuncs;
@@ -61,6 +62,7 @@ static int numOperators;
static int numCollations;
static int numNamespaces;
static int numExtensions;
+static int numCompressionMethods;
/* This is an array of object identities, not actual DumpableObjects */
static ExtensionMemberId *extmembers;
@@ -93,6 +95,8 @@ getSchemaData(Archive *fout, int *numTablesPtr)
NamespaceInfo *nspinfo;
ExtensionInfo *extinfo;
InhInfo *inhinfo;
+ CompressionMethodInfo *cminfo;
+
int numAggregates;
int numInherits;
int numRules;
@@ -289,6 +293,11 @@ getSchemaData(Archive *fout, int *numTablesPtr)
write_msg(NULL, "reading subscriptions\n");
getSubscriptions(fout);
+ if (g_verbose)
+ write_msg(NULL, "reading compression methods\n");
+ cminfo = getCompressionMethods(fout, &numCompressionMethods);
+ cminfoindex = buildIndexArray(cminfo, numCompressionMethods, sizeof(CompressionMethodInfo));
+
*numTablesPtr = numTables;
return tblinfo;
}
@@ -827,6 +836,17 @@ findExtensionByOid(Oid oid)
return (ExtensionInfo *) findObjectByOid(oid, extinfoindex, numExtensions);
}
+/*
+ * findCompressionMethodByOid
+ * finds the entry (in cminfo) of the compression method with the given oid
+ * returns NULL if not found
+ */
+CompressionMethodInfo *
+findCompressionMethodByOid(Oid oid)
+{
+ return (CompressionMethodInfo *) findObjectByOid(oid, cminfoindex,
+ numCompressionMethods);
+}
/*
* setExtensionMembership
diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index ce3100f09d..5c1aaad48e 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -77,6 +77,7 @@ typedef struct _restoreOptions
int no_publications; /* Skip publication entries */
int no_security_labels; /* Skip security label entries */
int no_subscriptions; /* Skip subscription entries */
+ int no_compression_methods; /* Skip compression methods */
int strict_names;
const char *filename;
@@ -121,6 +122,8 @@ typedef struct _restoreOptions
bool *idWanted; /* array showing which dump IDs to emit */
int enable_row_security;
int sequence_data; /* dump sequence data even in schema-only mode */
+ int compression_options_data; /* dump compression options even
+ * in schema-only mode */
int binary_upgrade;
} RestoreOptions;
@@ -149,6 +152,7 @@ typedef struct _dumpOptions
int no_security_labels;
int no_publications;
int no_subscriptions;
+ int no_compression_methods;
int no_synchronized_snapshots;
int no_unlogged_table_data;
int serializable_deferrable;
@@ -170,6 +174,8 @@ typedef struct _dumpOptions
char *outputSuperuser;
int sequence_data; /* dump sequence data even in schema-only mode */
+ int compression_options_data; /* dump compression options even
+ * in schema-only mode */
} DumpOptions;
/*
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index ec2fa8b9b9..77eb55eb69 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -179,6 +179,7 @@ dumpOptionsFromRestoreOptions(RestoreOptions *ropt)
dopt->include_everything = ropt->include_everything;
dopt->enable_row_security = ropt->enable_row_security;
dopt->sequence_data = ropt->sequence_data;
+ dopt->compression_options_data = ropt->compression_options_data;
return dopt;
}
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index d8fb356130..6df688adb8 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -361,6 +361,7 @@ main(int argc, char **argv)
{"no-synchronized-snapshots", no_argument, &dopt.no_synchronized_snapshots, 1},
{"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
{"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
+ {"no-compression-methods", no_argument, &dopt.no_compression_methods, 1},
{"no-sync", no_argument, NULL, 7},
{NULL, 0, NULL, 0}
@@ -581,6 +582,9 @@ main(int argc, char **argv)
if (dopt.binary_upgrade)
dopt.sequence_data = 1;
+ if (dopt.binary_upgrade)
+ dopt.compression_options_data = 1;
+
if (dopt.dataOnly && dopt.schemaOnly)
{
write_msg(NULL, "options -s/--schema-only and -a/--data-only cannot be used together\n");
@@ -785,6 +789,9 @@ main(int argc, char **argv)
if (dopt.schemaOnly && dopt.sequence_data)
getTableData(&dopt, tblinfo, numTables, dopt.oids, RELKIND_SEQUENCE);
+ if (dopt.schemaOnly && dopt.compression_options_data)
+ getCompressionOptions(fout);
+
/*
* In binary-upgrade mode, we do not have to worry about the actual blob
* data or the associated metadata that resides in the pg_largeobject and
@@ -3957,6 +3964,205 @@ dumpSubscription(Archive *fout, SubscriptionInfo *subinfo)
destroyPQExpBuffer(query);
}
+/*
+ * getCompressionMethods
+ * get information about compression methods
+ */
+CompressionMethodInfo *
+getCompressionMethods(Archive *fout, int *numMethods)
+{
+ DumpOptions *dopt = fout->dopt;
+ PQExpBuffer query;
+ PGresult *res;
+ CompressionMethodInfo *cminfo;
+ int i_tableoid;
+ int i_oid;
+ int i_handler;
+ int i_name;
+ int i,
+ ntups;
+
+ if (dopt->no_compression_methods || fout->remoteVersion < 110000)
+ return NULL;
+
+ query = createPQExpBuffer();
+ resetPQExpBuffer(query);
+
+ /* Get the compression methods in current database. */
+ appendPQExpBuffer(query, "SELECT c.tableoid, c.oid, c.cmname, "
+ "c.cmhandler::pg_catalog.regproc as cmhandler "
+ "FROM pg_catalog.pg_compression c");
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_name = PQfnumber(res, "cmname");
+ i_handler = PQfnumber(res, "cmhandler");
+
+ cminfo = pg_malloc(ntups * sizeof(CompressionMethodInfo));
+
+ for (i = 0; i < ntups; i++)
+ {
+ cminfo[i].dobj.objType = DO_COMPRESSION_METHOD;
+ cminfo[i].dobj.catId.tableoid =
+ atooid(PQgetvalue(res, i, i_tableoid));
+ cminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&cminfo[i].dobj);
+ cminfo[i].cmhandler = pg_strdup(PQgetvalue(res, i, i_handler));
+ cminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_name));
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(cminfo[i].dobj), fout);
+ }
+ if (numMethods)
+ *numMethods = ntups;
+
+ PQclear(res);
+ destroyPQExpBuffer(query);
+
+ return cminfo;
+}
+
+/*
+ * dumpCompressionMethod
+ * dump the definition of the given compression method
+ */
+static void
+dumpCompressionMethod(Archive *fout, CompressionMethodInfo * cminfo)
+{
+ PQExpBuffer query;
+
+ if (!(cminfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
+ return;
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema(fout, "pg_catalog");
+
+ query = createPQExpBuffer();
+ appendPQExpBuffer(query, "CREATE COMPRESSION METHOD %s HANDLER",
+ fmtId(cminfo->dobj.name));
+ appendPQExpBuffer(query, " %s;\n", cminfo->cmhandler);
+
+ ArchiveEntry(fout,
+ cminfo->dobj.catId,
+ cminfo->dobj.dumpId,
+ cminfo->dobj.name,
+ NULL,
+ NULL,
+ "", false,
+ "COMPRESSION METHOD", SECTION_PRE_DATA,
+ query->data, "", NULL,
+ NULL, 0,
+ NULL, NULL);
+
+ destroyPQExpBuffer(query);
+}
+
+/*
+ * getCompressionOptions
+ * get information about compression options.
+ * this information should be migrated at binary upgrade
+ */
+void
+getCompressionOptions(Archive *fout)
+{
+ PQExpBuffer query;
+ PGresult *res;
+ CompressionOptionsInfo *cminfo;
+ int i_tableoid;
+ int i_oid;
+ int i_handler;
+ int i_name;
+ int i_options;
+ int i,
+ ntups;
+
+ if (fout->remoteVersion < 110000)
+ return;
+
+ query = createPQExpBuffer();
+ resetPQExpBuffer(query);
+
+ appendPQExpBuffer(query,
+ "SELECT c.tableoid, c.cmoptoid, c.cmname,"
+ " c.cmhandler::pg_catalog.regproc as cmhandler, c.cmoptions "
+ "FROM pg_catalog.pg_compression_opt c");
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "cmoptoid");
+ i_name = PQfnumber(res, "cmname");
+ i_handler = PQfnumber(res, "cmhandler");
+ i_options = PQfnumber(res, "cmoptions");
+
+ cminfo = pg_malloc(ntups * sizeof(CompressionOptionsInfo));
+
+ for (i = 0; i < ntups; i++)
+ {
+ cminfo[i].dobj.objType = DO_COMPRESSION_OPTIONS;
+ cminfo[i].dobj.catId.tableoid =
+ atooid(PQgetvalue(res, i, i_tableoid));
+ cminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&cminfo[i].dobj);
+ cminfo[i].cmhandler = pg_strdup(PQgetvalue(res, i, i_handler));
+ cminfo[i].cmoptions = pg_strdup(PQgetvalue(res, i, i_options));
+ cminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_name));
+ cminfo[i].dobj.dump = DUMP_COMPONENT_DEFINITION;
+ }
+
+ PQclear(res);
+ destroyPQExpBuffer(query);
+}
+
+/*
+ * dumpCompressionOptions
+ * dump the given compression options
+ */
+static void
+dumpCompressionOptions(Archive *fout, CompressionOptionsInfo * cminfo)
+{
+ PQExpBuffer query;
+
+ if (!(cminfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
+ return;
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema(fout, "pg_catalog");
+
+ query = createPQExpBuffer();
+ if (strlen(cminfo->cmoptions) > 0)
+ appendPQExpBuffer(query, "INSERT INTO pg_compression_opt (cmoptoid, cmname, cmhandler,"
+ " cmoptions) VALUES (%d, '%s', CAST('%s' AS REGPROC), '%s');\n",
+ cminfo->dobj.catId.oid,
+ cminfo->dobj.name,
+ cminfo->cmhandler,
+ cminfo->cmoptions);
+ else
+ appendPQExpBuffer(query, "INSERT INTO pg_compression_opt (cmoptoid, cmname, cmhandler,"
+ " cmoptions) VALUES (%d, '%s', CAST('%s' AS REGPROC), NULL);\n",
+ cminfo->dobj.catId.oid,
+ cminfo->dobj.name,
+ cminfo->cmhandler);
+
+ ArchiveEntry(fout,
+ cminfo->dobj.catId,
+ cminfo->dobj.dumpId,
+ cminfo->dobj.name,
+ NULL,
+ NULL,
+ "", false,
+ "COMPRESSION OPTIONS", SECTION_PRE_DATA,
+ query->data, "", NULL,
+ NULL, 0,
+ NULL, NULL);
+
+ destroyPQExpBuffer(query);
+}
+
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
@@ -4484,7 +4690,47 @@ getTypes(Archive *fout, int *numTypes)
/* Make sure we are in proper schema */
selectSourceSchema(fout, "pg_catalog");
- if (fout->remoteVersion >= 90600)
+ if (fout->remoteVersion >= 110000)
+ {
+ PQExpBuffer acl_subquery = createPQExpBuffer();
+ PQExpBuffer racl_subquery = createPQExpBuffer();
+ PQExpBuffer initacl_subquery = createPQExpBuffer();
+ PQExpBuffer initracl_subquery = createPQExpBuffer();
+
+ buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
+ initracl_subquery, "t.typacl", "t.typowner", "'T'",
+ dopt->binary_upgrade);
+
+ appendPQExpBuffer(query, "SELECT t.tableoid, t.oid, t.typname, "
+ "t.typnamespace, "
+ "%s AS typacl, "
+ "%s AS rtypacl, "
+ "%s AS inittypacl, "
+ "%s AS initrtypacl, "
+ "(%s t.typowner) AS rolname, "
+ "t.typelem, t.typrelid, "
+ "CASE WHEN t.typrelid = 0 THEN ' '::\"char\" "
+ "ELSE (SELECT relkind FROM pg_class WHERE oid = t.typrelid) END AS typrelkind, "
+ "t.typtype, t.typisdefined, "
+ "t.typname[0] = '_' AND t.typelem != 0 AND "
+ "(SELECT typarray FROM pg_type te WHERE oid = t.typelem) = t.oid AS isarray "
+ "FROM pg_type t "
+ "LEFT JOIN pg_init_privs pip ON "
+ "(t.oid = pip.objoid "
+ "AND pip.classoid = 'pg_type'::regclass "
+ "AND pip.objsubid = 0) ",
+ acl_subquery->data,
+ racl_subquery->data,
+ initacl_subquery->data,
+ initracl_subquery->data,
+ username_subquery);
+
+ destroyPQExpBuffer(acl_subquery);
+ destroyPQExpBuffer(racl_subquery);
+ destroyPQExpBuffer(initacl_subquery);
+ destroyPQExpBuffer(initracl_subquery);
+ }
+ else if (fout->remoteVersion >= 90600)
{
PQExpBuffer acl_subquery = createPQExpBuffer();
PQExpBuffer racl_subquery = createPQExpBuffer();
@@ -7895,6 +8141,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
int i_attoptions;
int i_attcollation;
int i_attfdwoptions;
+ int i_attcmoptions;
+ int i_attcmname;
PGresult *res;
int ntups;
bool hasdefaults;
@@ -7930,7 +8178,46 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
resetPQExpBuffer(q);
- if (fout->remoteVersion >= 100000)
+ if (fout->remoteVersion >= 110000)
+ {
+ /*
+ * attcompression is new in version 11
+ */
+ appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, "
+ "a.attstattarget, a.attstorage, t.typstorage, "
+ "a.attnotnull, a.atthasdef, a.attisdropped, "
+ "a.attlen, a.attalign, a.attislocal, "
+ "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
+ "array_to_string(a.attoptions, ', ') AS attoptions, "
+ "CASE WHEN a.attcollation <> t.typcollation "
+ "THEN a.attcollation ELSE 0 END AS attcollation, "
+ "a.attidentity, "
+ /* fdw options */
+ "pg_catalog.array_to_string(ARRAY("
+ "SELECT pg_catalog.quote_ident(option_name) || "
+ "' ' || pg_catalog.quote_literal(option_value) "
+ "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
+ "ORDER BY option_name"
+ "), E',\n ') AS attfdwoptions, "
+ /* compression options */
+ "pg_catalog.array_to_string(ARRAY("
+ "SELECT pg_catalog.quote_ident(option_name) || "
+ "' ' || pg_catalog.quote_literal(option_value) "
+ "FROM pg_catalog.pg_options_to_table(c.cmoptions) "
+ "ORDER BY option_name"
+ "), E',\n ') AS attcmoptions, "
+ "c.cmname AS attcmname "
+ /* FROM */
+ "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
+ "ON a.atttypid = t.oid "
+ "LEFT JOIN pg_catalog.pg_compression_opt c "
+ "ON a.attcompression = c.cmoptoid "
+ "WHERE a.attrelid = '%u'::pg_catalog.oid "
+ "AND a.attnum > 0::pg_catalog.int2 "
+ "ORDER BY a.attnum",
+ tbinfo->dobj.catId.oid);
+ }
+ else if (fout->remoteVersion >= 100000)
{
/*
* attidentity is new in version 10.
@@ -7949,9 +8236,13 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"' ' || pg_catalog.quote_literal(option_value) "
"FROM pg_catalog.pg_options_to_table(attfdwoptions) "
"ORDER BY option_name"
- "), E',\n ') AS attfdwoptions "
+ "), E',\n ') AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
+ "LEFT JOIN pg_catalog.pg_compression_opt c "
+ "ON a.attcompression = c.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
"AND a.attnum > 0::pg_catalog.int2 "
"ORDER BY a.attnum",
@@ -7975,7 +8266,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"' ' || pg_catalog.quote_literal(option_value) "
"FROM pg_catalog.pg_options_to_table(attfdwoptions) "
"ORDER BY option_name"
- "), E',\n ') AS attfdwoptions "
+ "), E',\n ') AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -7999,7 +8292,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"array_to_string(a.attoptions, ', ') AS attoptions, "
"CASE WHEN a.attcollation <> t.typcollation "
"THEN a.attcollation ELSE 0 END AS attcollation, "
- "NULL AS attfdwoptions "
+ "NULL AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -8017,7 +8312,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
"array_to_string(a.attoptions, ', ') AS attoptions, "
"0 AS attcollation, "
- "NULL AS attfdwoptions "
+ "NULL AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -8034,7 +8331,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"a.attlen, a.attalign, a.attislocal, "
"pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
"'' AS attoptions, 0 AS attcollation, "
- "NULL AS attfdwoptions "
+ "NULL AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -8064,6 +8363,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
i_attoptions = PQfnumber(res, "attoptions");
i_attcollation = PQfnumber(res, "attcollation");
i_attfdwoptions = PQfnumber(res, "attfdwoptions");
+ i_attcmname = PQfnumber(res, "attcmname");
+ i_attcmoptions = PQfnumber(res, "attcmoptions");
tbinfo->numatts = ntups;
tbinfo->attnames = (char **) pg_malloc(ntups * sizeof(char *));
@@ -8080,6 +8381,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
tbinfo->attoptions = (char **) pg_malloc(ntups * sizeof(char *));
tbinfo->attcollation = (Oid *) pg_malloc(ntups * sizeof(Oid));
tbinfo->attfdwoptions = (char **) pg_malloc(ntups * sizeof(char *));
+ tbinfo->attcmoptions = (char **) pg_malloc(ntups * sizeof(char *));
+ tbinfo->attcmnames = (char **) pg_malloc(ntups * sizeof(char *));
tbinfo->notnull = (bool *) pg_malloc(ntups * sizeof(bool));
tbinfo->inhNotNull = (bool *) pg_malloc(ntups * sizeof(bool));
tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(ntups * sizeof(AttrDefInfo *));
@@ -8107,6 +8410,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, j, i_attoptions));
tbinfo->attcollation[j] = atooid(PQgetvalue(res, j, i_attcollation));
tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, j, i_attfdwoptions));
+ tbinfo->attcmoptions[j] = pg_strdup(PQgetvalue(res, j, i_attcmoptions));
+ tbinfo->attcmnames[j] = pg_strdup(PQgetvalue(res, j, i_attcmname));
tbinfo->attrdefs[j] = NULL; /* fix below */
if (PQgetvalue(res, j, i_atthasdef)[0] == 't')
hasdefaults = true;
@@ -9596,6 +9901,12 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
case DO_SUBSCRIPTION:
dumpSubscription(fout, (SubscriptionInfo *) dobj);
break;
+ case DO_COMPRESSION_METHOD:
+ dumpCompressionMethod(fout, (CompressionMethodInfo *) dobj);
+ break;
+ case DO_COMPRESSION_OPTIONS:
+ dumpCompressionOptions(fout, (CompressionOptionsInfo *) dobj);
+ break;
case DO_PRE_DATA_BOUNDARY:
case DO_POST_DATA_BOUNDARY:
/* never dumped, nothing to do */
@@ -15513,6 +15824,15 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
}
}
+ if (tbinfo->attcmnames[j] && strlen(tbinfo->attcmnames[j]))
+ {
+ appendPQExpBuffer(q, " COMPRESSED %s",
+ tbinfo->attcmnames[j]);
+ if (nonemptyReloptions(tbinfo->attcmoptions[j]))
+ appendPQExpBuffer(q, " WITH (%s)",
+ tbinfo->attcmoptions[j]);
+ }
+
if (has_default)
appendPQExpBuffer(q, " DEFAULT %s",
tbinfo->attrdefs[j]->adef_expr);
@@ -17778,6 +18098,8 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
case DO_FOREIGN_SERVER:
case DO_TRANSFORM:
case DO_BLOB:
+ case DO_COMPRESSION_METHOD:
+ case DO_COMPRESSION_OPTIONS:
/* Pre-data objects: must come before the pre-data boundary */
addObjectDependency(preDataBound, dobj->dumpId);
break;
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index da884ffd09..88b18ba15c 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -83,7 +83,9 @@ typedef enum
DO_POLICY,
DO_PUBLICATION,
DO_PUBLICATION_REL,
- DO_SUBSCRIPTION
+ DO_SUBSCRIPTION,
+ DO_COMPRESSION_METHOD,
+ DO_COMPRESSION_OPTIONS
} DumpableObjectType;
/* component types of an object which can be selected for dumping */
@@ -315,6 +317,8 @@ typedef struct _tableInfo
char **attoptions; /* per-attribute options */
Oid *attcollation; /* per-attribute collation selection */
char **attfdwoptions; /* per-attribute fdw options */
+ char **attcmoptions; /* per-attribute compression options */
+ char **attcmnames; /* per-attribute compression method names */
bool *notnull; /* NOT NULL constraints on attributes */
bool *inhNotNull; /* true if NOT NULL is inherited */
struct _attrDefInfo **attrdefs; /* DEFAULT expressions */
@@ -611,6 +615,21 @@ typedef struct _SubscriptionInfo
char *subpublications;
} SubscriptionInfo;
+/* The CompressionMethodInfo struct is used to represent compression method */
+typedef struct _CompressionMethodInfo
+{
+ DumpableObject dobj;
+ char *cmhandler;
+} CompressionMethodInfo;
+
+/* The CompressionOptionsInfo struct is used to represent compression options */
+typedef struct _CompressionOptionsInfo
+{
+ DumpableObject dobj;
+ char *cmhandler;
+ char *cmoptions;
+} CompressionOptionsInfo;
+
/*
* We build an array of these with an entry for each object that is an
* extension member according to pg_depend.
@@ -654,6 +673,7 @@ extern OprInfo *findOprByOid(Oid oid);
extern CollInfo *findCollationByOid(Oid oid);
extern NamespaceInfo *findNamespaceByOid(Oid oid);
extern ExtensionInfo *findExtensionByOid(Oid oid);
+extern CompressionMethodInfo * findCompressionMethodByOid(Oid oid);
extern void setExtensionMembership(ExtensionMemberId *extmems, int nextmems);
extern ExtensionInfo *findOwningExtension(CatalogId catalogId);
@@ -711,5 +731,8 @@ extern void getPublications(Archive *fout);
extern void getPublicationTables(Archive *fout, TableInfo tblinfo[],
int numTables);
extern void getSubscriptions(Archive *fout);
+extern CompressionMethodInfo * getCompressionMethods(Archive *fout,
+ int *numMethods);
+void getCompressionOptions(Archive *fout);
#endif /* PG_DUMP_H */
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index 48b6dd594c..f5db0280e2 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -80,7 +80,9 @@ static const int dbObjectTypePriority[] =
34, /* DO_POLICY */
35, /* DO_PUBLICATION */
36, /* DO_PUBLICATION_REL */
- 37 /* DO_SUBSCRIPTION */
+ 37, /* DO_SUBSCRIPTION */
+ 17, /* DO_COMPRESSION_METHOD */
+ 17 /* DO_COMPRESSION_OPTIONS */
};
static DumpId preDataBoundId;
@@ -1436,6 +1438,16 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
"POST-DATA BOUNDARY (ID %d)",
obj->dumpId);
return;
+ case DO_COMPRESSION_METHOD:
+ snprintf(buf, bufsize,
+ "COMPRESSION METHOD %s (ID %d OID %u)",
+ obj->name, obj->dumpId, obj->catId.oid);
+ return;
+ case DO_COMPRESSION_OPTIONS:
+ snprintf(buf, bufsize,
+ "COMPRESSION OPTIONS %s (ID %d OID %u)",
+ obj->name, obj->dumpId, obj->catId.oid);
+ return;
}
/* shouldn't get here */
snprintf(buf, bufsize,
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 41c5ff89b7..6b72ccf9ea 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -77,6 +77,7 @@ static int use_setsessauth = 0;
static int no_publications = 0;
static int no_security_labels = 0;
static int no_subscriptions = 0;
+static int no_compression_methods = 0;
static int no_unlogged_table_data = 0;
static int no_role_passwords = 0;
static int server_version;
@@ -137,6 +138,7 @@ main(int argc, char *argv[])
{"no-role-passwords", no_argument, &no_role_passwords, 1},
{"no-security-labels", no_argument, &no_security_labels, 1},
{"no-subscriptions", no_argument, &no_subscriptions, 1},
+ {"no-compression-methods", no_argument, &no_compression_methods, 1},
{"no-sync", no_argument, NULL, 4},
{"no-unlogged-table-data", no_argument, &no_unlogged_table_data, 1},
@@ -405,6 +407,8 @@ main(int argc, char *argv[])
appendPQExpBufferStr(pgdumpopts, " --no-security-labels");
if (no_subscriptions)
appendPQExpBufferStr(pgdumpopts, " --no-subscriptions");
+ if (no_compression_methods)
+ appendPQExpBufferStr(pgdumpopts, " --no-compression-methods");
if (no_unlogged_table_data)
appendPQExpBufferStr(pgdumpopts, " --no-unlogged-table-data");
@@ -628,6 +632,7 @@ help(void)
printf(_(" --no-role-passwords do not dump passwords for roles\n"));
printf(_(" --no-security-labels do not dump security label assignments\n"));
printf(_(" --no-subscriptions do not dump subscriptions\n"));
+ printf(_(" --no-compression-methods do not dump compression methods\n"));
printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c
index 860a211a3c..810403ec9c 100644
--- a/src/bin/pg_dump/pg_restore.c
+++ b/src/bin/pg_dump/pg_restore.c
@@ -74,6 +74,7 @@ main(int argc, char **argv)
static int no_publications = 0;
static int no_security_labels = 0;
static int no_subscriptions = 0;
+ static int no_compression_methods = 0;
static int strict_names = 0;
struct option cmdopts[] = {
@@ -122,6 +123,7 @@ main(int argc, char **argv)
{"no-publications", no_argument, &no_publications, 1},
{"no-security-labels", no_argument, &no_security_labels, 1},
{"no-subscriptions", no_argument, &no_subscriptions, 1},
+ {"no-compression-methods", no_argument, &no_compression_methods, 1},
{NULL, 0, NULL, 0}
};
@@ -361,6 +363,7 @@ main(int argc, char **argv)
opts->no_publications = no_publications;
opts->no_security_labels = no_security_labels;
opts->no_subscriptions = no_subscriptions;
+ opts->no_compression_methods = no_compression_methods;
if (if_exists && !opts->dropSchema)
{
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 8cc4de3878..8b0dbfa45c 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -735,7 +735,10 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
success = listConversions(pattern, show_verbose, show_system);
break;
case 'C':
- success = listCasts(pattern, show_verbose);
+ if (cmd[2] == 'M')
+ success = describeCompressionMethods(pattern, show_verbose);
+ else
+ success = listCasts(pattern, show_verbose);
break;
case 'd':
if (strncmp(cmd, "ddp", 3) == 0)
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index b7b978a361..49160ded0c 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -200,6 +200,69 @@ describeAccessMethods(const char *pattern, bool verbose)
return true;
}
+/*
+ * \dCM
+ * Takes an optional regexp to select particular compression methods
+ */
+bool
+describeCompressionMethods(const char *pattern, bool verbose)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+ static const bool translate_columns[] = {false, false, false};
+
+ if (pset.sversion < 100000)
+ {
+ char sverbuf[32];
+
+ psql_error("The server (version %s) does not support compression methods.\n",
+ formatPGVersionNumber(pset.sversion, false,
+ sverbuf, sizeof(sverbuf)));
+ return true;
+ }
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT cmname AS \"%s\"",
+ gettext_noop("Name"));
+
+ if (verbose)
+ {
+ appendPQExpBuffer(&buf,
+ ",\n cmhandler AS \"%s\",\n"
+ " pg_catalog.obj_description(oid, 'pg_compression') AS \"%s\"",
+ gettext_noop("Handler"),
+ gettext_noop("Description"));
+ }
+
+ appendPQExpBufferStr(&buf,
+ "\nFROM pg_catalog.pg_compression\n");
+
+ processSQLNamePattern(pset.db, &buf, pattern, false, false,
+ NULL, "cmname", NULL,
+ NULL);
+
+ appendPQExpBufferStr(&buf, "ORDER BY 1;");
+
+ res = PSQLexec(buf.data);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of compression methods");
+ myopt.translate_header = true;
+ myopt.translate_columns = translate_columns;
+ myopt.n_translate_columns = lengthof(translate_columns);
+
+ printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+ PQclear(res);
+ return true;
+}
+
/*
* \db
* Takes an optional regexp to select particular tablespaces
@@ -1716,6 +1779,22 @@ describeOneTableDetails(const char *schemaname,
if (verbose)
{
appendPQExpBufferStr(&buf, ",\n a.attstorage");
+
+ /* compresssion info */
+ if (pset.sversion >= 110000)
+ appendPQExpBufferStr(&buf, ",\n CASE WHEN attcompression = 0 THEN NULL ELSE "
+ " (SELECT c.cmname || "
+ " (CASE WHEN cmoptions IS NULL "
+ " THEN '' "
+ " ELSE '(' || array_to_string(ARRAY(SELECT quote_ident(option_name) || ' ' || quote_literal(option_value)"
+ " FROM pg_options_to_table(cmoptions)), ', ') || ')'"
+ " END) "
+ " FROM pg_catalog.pg_compression_opt c "
+ " WHERE c.cmoptoid = a.attcompression) "
+ " END AS attcmname");
+ else
+ appendPQExpBufferStr(&buf, "\n NULL AS attcmname");
+
appendPQExpBufferStr(&buf, ",\n CASE WHEN a.attstattarget=-1 THEN NULL ELSE a.attstattarget END AS attstattarget");
/*
@@ -1830,6 +1909,10 @@ describeOneTableDetails(const char *schemaname,
if (verbose)
{
headers[cols++] = gettext_noop("Storage");
+
+ if (tableinfo.relkind == RELKIND_RELATION)
+ headers[cols++] = gettext_noop("Compression");
+
if (tableinfo.relkind == RELKIND_RELATION ||
tableinfo.relkind == RELKIND_INDEX ||
tableinfo.relkind == RELKIND_MATVIEW ||
@@ -1925,6 +2008,11 @@ describeOneTableDetails(const char *schemaname,
"???")))),
false, false);
+ /* Column compression. */
+ if (tableinfo.relkind == RELKIND_RELATION)
+ printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1),
+ false, false);
+
/* Statistics target, if the relkind supports this feature */
if (tableinfo.relkind == RELKIND_RELATION ||
tableinfo.relkind == RELKIND_INDEX ||
@@ -1932,7 +2020,7 @@ describeOneTableDetails(const char *schemaname,
tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
{
- printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1),
+ printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2),
false, false);
}
@@ -1943,7 +2031,7 @@ describeOneTableDetails(const char *schemaname,
tableinfo.relkind == RELKIND_COMPOSITE_TYPE ||
tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
- printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2),
+ printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 3),
false, false);
}
}
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 14a5667f3e..0ab8518a36 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -18,6 +18,9 @@ extern bool describeAccessMethods(const char *pattern, bool verbose);
/* \db */
extern bool describeTablespaces(const char *pattern, bool verbose);
+/* \dCM */
+extern bool describeCompressionMethods(const char *pattern, bool verbose);
+
/* \df, \dfa, \dfn, \dft, \dfw, etc. */
extern bool describeFunctions(const char *functypes, const char *pattern, bool verbose, bool showSystem);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index a926c40b9b..25d2fbc125 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -227,6 +227,7 @@ slashUsage(unsigned short int pager)
fprintf(output, _(" \\db[+] [PATTERN] list tablespaces\n"));
fprintf(output, _(" \\dc[S+] [PATTERN] list conversions\n"));
fprintf(output, _(" \\dC[+] [PATTERN] list casts\n"));
+ fprintf(output, _(" \\dCM[+] [PATTERN] list compression methods\n"));
fprintf(output, _(" \\dd[S] [PATTERN] show object descriptions not displayed elsewhere\n"));
fprintf(output, _(" \\dD[S+] [PATTERN] list domains\n"));
fprintf(output, _(" \\ddp [PATTERN] list default privileges\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index b3e3799c13..a323ca80c6 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -889,6 +889,11 @@ static const SchemaQuery Query_for_list_of_statistics = {
" AND d.datname = pg_catalog.current_database() "\
" AND s.subdbid = d.oid"
+#define Query_for_list_of_compression_methods \
+" SELECT pg_catalog.quote_ident(cmname) "\
+" FROM pg_catalog.pg_compression "\
+" WHERE substring(pg_catalog.quote_ident(cmname),1,%d)='%s'"
+
/* the silly-looking length condition is just to eat up the current word */
#define Query_for_list_of_arguments \
"SELECT pg_catalog.oidvectortypes(proargtypes)||')' "\
@@ -1011,6 +1016,7 @@ static const pgsql_thing_t words_after_create[] = {
* CREATE CONSTRAINT TRIGGER is not supported here because it is designed
* to be used only by pg_dump.
*/
+ {"COMPRESSION METHOD", NULL, NULL},
{"CONFIGURATION", Query_for_list_of_ts_configurations, NULL, THING_NO_SHOW},
{"CONVERSION", "SELECT pg_catalog.quote_ident(conname) FROM pg_catalog.pg_conversion WHERE substring(pg_catalog.quote_ident(conname),1,%d)='%s'"},
{"DATABASE", Query_for_list_of_databases},
@@ -1424,8 +1430,8 @@ psql_completion(const char *text, int start, int end)
"\\a",
"\\connect", "\\conninfo", "\\C", "\\cd", "\\copy",
"\\copyright", "\\crosstabview",
- "\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
- "\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
+ "\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dCM", "\\dd", "\\ddp",
+ "\\dD", "\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
"\\dm", "\\dn", "\\do", "\\dO", "\\dp",
"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
@@ -1954,11 +1960,17 @@ psql_completion(const char *text, int start, int end)
/* ALTER TABLE ALTER [COLUMN] <foo> SET */
else if (Matches7("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET") ||
Matches6("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET"))
- COMPLETE_WITH_LIST5("(", "DEFAULT", "NOT NULL", "STATISTICS", "STORAGE");
+ COMPLETE_WITH_LIST6("(", "COMPRESSED", "DEFAULT", "NOT NULL", "STATISTICS", "STORAGE");
/* ALTER TABLE ALTER [COLUMN] <foo> SET ( */
else if (Matches8("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "(") ||
Matches7("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "("))
COMPLETE_WITH_LIST2("n_distinct", "n_distinct_inherited");
+ else if (Matches8("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "COMPRESSED") ||
+ Matches7("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "COMPRESSED"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_compression_methods);
+ else if (Matches9("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "COMPRESSED", MatchAny) ||
+ Matches8("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "COMPRESSED", MatchAny))
+ COMPLETE_WITH_CONST("WITH (");
/* ALTER TABLE ALTER [COLUMN] <foo> SET STORAGE */
else if (Matches8("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "STORAGE") ||
Matches7("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "STORAGE"))
@@ -2177,12 +2189,14 @@ psql_completion(const char *text, int start, int end)
"SCHEMA", "SEQUENCE", "STATISTICS", "SUBSCRIPTION",
"TABLE", "TYPE", "VIEW", "MATERIALIZED VIEW", "COLUMN", "AGGREGATE", "FUNCTION",
"OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", "LARGE OBJECT",
- "TABLESPACE", "TEXT SEARCH", "ROLE", NULL};
+ "TABLESPACE", "TEXT SEARCH", "ROLE", "COMPRESSION METHOD", NULL};
COMPLETE_WITH_LIST(list_COMMENT);
}
else if (Matches4("COMMENT", "ON", "ACCESS", "METHOD"))
COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
+ else if (Matches4("COMMENT", "ON", "COMPRESSION", "METHOD"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_compression_methods);
else if (Matches3("COMMENT", "ON", "FOREIGN"))
COMPLETE_WITH_LIST2("DATA WRAPPER", "TABLE");
else if (Matches4("COMMENT", "ON", "TEXT", "SEARCH"))
@@ -2255,6 +2269,14 @@ psql_completion(const char *text, int start, int end)
else if (Matches6("CREATE", "ACCESS", "METHOD", MatchAny, "TYPE", MatchAny))
COMPLETE_WITH_CONST("HANDLER");
+ /* CREATE COMPRESSION METHOD */
+ /* Complete "CREATE COMPRESSION METHOD <name>" */
+ else if (Matches4("CREATE", "COMPRESSION", "METHOD", MatchAny))
+ COMPLETE_WITH_CONST("HANDLER");
+ /* Complete "CREATE COMPRESSION METHOD <name> HANDLER" */
+ else if (Matches5("CREATE", "COMPRESSION", "METHOD", MatchAny, "HANDLER"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
+
/* CREATE DATABASE */
else if (Matches3("CREATE", "DATABASE", MatchAny))
COMPLETE_WITH_LIST9("OWNER", "TEMPLATE", "ENCODING", "TABLESPACE",
@@ -2687,6 +2709,7 @@ psql_completion(const char *text, int start, int end)
Matches4("DROP", "ACCESS", "METHOD", MatchAny) ||
(Matches4("DROP", "AGGREGATE|FUNCTION", MatchAny, MatchAny) &&
ends_with(prev_wd, ')')) ||
+ Matches4("DROP", "COMPRESSION", "METHOD", MatchAny) ||
Matches4("DROP", "EVENT", "TRIGGER", MatchAny) ||
Matches5("DROP", "FOREIGN", "DATA", "WRAPPER", MatchAny) ||
Matches4("DROP", "FOREIGN", "TABLE", MatchAny) ||
@@ -2775,6 +2798,12 @@ psql_completion(const char *text, int start, int end)
else if (Matches5("DROP", "RULE", MatchAny, "ON", MatchAny))
COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
+ /* DROP COMPRESSION METHOD */
+ else if (Matches2("DROP", "COMPRESSION"))
+ COMPLETE_WITH_CONST("METHOD");
+ else if (Matches3("DROP", "COMPRESSION", "METHOD"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_compression_methods);
+
/* EXECUTE */
else if (Matches1("EXECUTE"))
COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements);
@@ -3407,6 +3436,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
else if (TailMatchesCS1("\\db*"))
COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
+ else if (TailMatchesCS1("\\dCM*"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_compression_methods);
else if (TailMatchesCS1("\\dD*"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL);
else if (TailMatchesCS1("\\des*"))
diff --git a/src/include/access/compression.h b/src/include/access/compression.h
new file mode 100644
index 0000000000..6c0e5fda5f
--- /dev/null
+++ b/src/include/access/compression.h
@@ -0,0 +1,66 @@
+/*-------------------------------------------------------------------------
+ *
+ * compression.h
+ * API for Postgres compression methods.
+ *
+ * Copyright (c) 2015-2016, PostgreSQL Global Development Group
+ *
+ * src/include/access/compression.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef COMPRESSION_H
+#define COMPRESSION_H
+
+#include "postgres.h"
+#include "catalog/pg_attribute.h"
+#include "nodes/nodes.h"
+#include "nodes/pg_list.h"
+
+/* parsenodes.h */
+typedef struct ColumnCompression ColumnCompression;
+typedef struct CompressionMethodRoutine CompressionMethodRoutine;
+
+typedef struct
+{
+ Oid cmoptoid;
+ CompressionMethodRoutine *routine;
+ List *options;
+} CompressionOptions;
+
+typedef void (*CompressionConfigureRoutine)
+ (Form_pg_attribute attr, List *options);
+typedef struct varlena *(*CompressionRoutine)
+ (CompressionOptions * cmoptions, const struct varlena *data);
+
+/*
+ * API struct for a compression method.
+ * Note this must be stored in a single palloc'd chunk of memory.
+ */
+typedef struct CompressionMethodRoutine
+{
+ NodeTag type;
+
+ CompressionConfigureRoutine configure;
+ CompressionConfigureRoutine drop;
+ CompressionRoutine compress;
+ CompressionRoutine decompress;
+} CompressionMethodRoutine;
+
+/* Compression method handler parameters */
+typedef struct CompressionMethodOpArgs
+{
+ Oid cmhanderid;
+ Oid typeid;
+} CompressionMethodOpArgs;
+
+extern CompressionMethodRoutine * GetCompressionRoutine(Oid cmoptoid);
+extern List *GetCompressionOptionsList(Oid cmoptoid);
+extern Oid CreateCompressionOptions(Form_pg_attribute attr, Oid cmid,
+ List *options);
+extern ColumnCompression * GetColumnCompressionForAttribute(Form_pg_attribute att);
+extern void CheckCompressionMismatch(ColumnCompression * c1,
+ ColumnCompression * c2, const char *attributeName);
+
+#endif /* COMPRESSION_H */
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index cd43e3a52e..2e1c92123e 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -258,7 +258,6 @@ extern void add_string_reloption(bits32 kinds, const char *name, const char *des
extern Datum transformRelOptions(Datum oldOptions, List *defList,
const char *namspace, char *validnsps[],
bool ignoreOids, bool isReset);
-extern List *untransformRelOptions(Datum options);
extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
amoptions_function amoptions);
extern relopt_value *parseRelOptions(Datum options, bool validate,
@@ -269,6 +268,8 @@ extern void fillRelOptions(void *rdopts, Size basesize,
relopt_value *options, int numoptions,
bool validate,
const relopt_parse_elt *elems, int nelems);
+extern char *formatRelOptions(List *options);
+extern List *untransformRelOptions(Datum options);
extern bytea *default_reloptions(Datum reloptions, bool validate,
relopt_kind kind);
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index 2be5af1d3e..867adc1bd8 100644
--- a/src/include/access/tupdesc.h
+++ b/src/include/access/tupdesc.h
@@ -14,7 +14,9 @@
#ifndef TUPDESC_H
#define TUPDESC_H
+#include "postgres.h"
#include "access/attnum.h"
+#include "access/compression.h"
#include "catalog/pg_attribute.h"
#include "nodes/pg_list.h"
@@ -88,6 +90,9 @@ typedef struct tupleDesc
/* Accessor for the i'th attribute of tupdesc. */
#define TupleDescAttr(tupdesc, i) (&(tupdesc)->attrs[(i)])
+#define TupleDescAttrCompression(tupdesc, i) \
+ ((tupdesc)->tdcompression? &((tupdesc)->tdcompression[i]) : NULL)
+
extern TupleDesc CreateTemplateTupleDesc(int natts, bool hasoid);
diff --git a/src/include/access/tuptoaster.h b/src/include/access/tuptoaster.h
index fd9f83ac44..0632588e5d 100644
--- a/src/include/access/tuptoaster.h
+++ b/src/include/access/tuptoaster.h
@@ -101,6 +101,15 @@
/* Size of an EXTERNAL datum that contains an indirection pointer */
#define INDIRECT_POINTER_SIZE (VARHDRSZ_EXTERNAL + sizeof(varatt_indirect))
+/*
+ * va_extinfo in varatt_external contains actual length of the external data
+ * and optional flags
+ */
+#define VARATT_EXTERNAL_GET_SIZE(toast_pointer) \
+ ((toast_pointer).va_extinfo & 0x3FFFFFFF)
+#define VARATT_EXTERNAL_IS_CUSTOM_COMPRESSED(toast_pointer) \
+ (((toast_pointer).va_extinfo >> 30) == 0x02)
+
/*
* Testing whether an externally-stored value is compressed now requires
* comparing extsize (the actual length of the external data) to rawsize
@@ -109,7 +118,7 @@
* saves space, so we expect either equality or less-than.
*/
#define VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer) \
- ((toast_pointer).va_extsize < (toast_pointer).va_rawsize - VARHDRSZ)
+ (VARATT_EXTERNAL_GET_SIZE(toast_pointer) < (toast_pointer).va_rawsize - VARHDRSZ)
/*
* Macro to fetch the possibly-unaligned contents of an EXTERNAL datum
@@ -210,7 +219,7 @@ extern HeapTuple toast_build_flattened_tuple(TupleDesc tupleDesc,
* Create a compressed version of a varlena datum, if possible
* ----------
*/
-extern Datum toast_compress_datum(Datum value);
+extern Datum toast_compress_datum(Datum value, Oid cmoptoid);
/* ----------
* toast_raw_datum_size -
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index b9f98423cc..36cadd409e 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -165,10 +165,12 @@ typedef enum ObjectClass
OCLASS_PUBLICATION, /* pg_publication */
OCLASS_PUBLICATION_REL, /* pg_publication_rel */
OCLASS_SUBSCRIPTION, /* pg_subscription */
- OCLASS_TRANSFORM /* pg_transform */
+ OCLASS_TRANSFORM, /* pg_transform */
+ OCLASS_COMPRESSION_METHOD, /* pg_compression */
+ OCLASS_COMPRESSION_OPTIONS /* pg_compression_opt */
} ObjectClass;
-#define LAST_OCLASS OCLASS_TRANSFORM
+#define LAST_OCLASS OCLASS_COMPRESSION_OPTIONS
/* flag bits for performDeletion/performMultipleDeletions: */
#define PERFORM_DELETION_INTERNAL 0x0001 /* internal action */
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index ef8493674c..7fe9f1f059 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -120,6 +120,14 @@ DECLARE_UNIQUE_INDEX(pg_collation_name_enc_nsp_index, 3164, on pg_collation usin
DECLARE_UNIQUE_INDEX(pg_collation_oid_index, 3085, on pg_collation using btree(oid oid_ops));
#define CollationOidIndexId 3085
+DECLARE_UNIQUE_INDEX(pg_compression_oid_index, 3422, on pg_compression using btree(oid oid_ops));
+#define CompressionMethodOidIndexId 3422
+DECLARE_UNIQUE_INDEX(pg_compression_name_index, 3423, on pg_compression using btree(cmname name_ops));
+#define CompressionMethodNameIndexId 3423
+
+DECLARE_UNIQUE_INDEX(pg_compression_opt_oid_index, 3424, on pg_compression_opt using btree(cmoptoid oid_ops));
+#define CompressionOptionsOidIndexId 3424
+
DECLARE_INDEX(pg_constraint_conname_nsp_index, 2664, on pg_constraint using btree(conname name_ops, connamespace oid_ops));
#define ConstraintNameNspIndexId 2664
DECLARE_INDEX(pg_constraint_conrelid_index, 2665, on pg_constraint using btree(conrelid oid_ops));
diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h
index bcf28e8f04..caadd61031 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -156,6 +156,9 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
/* attribute's collation */
Oid attcollation;
+ /* attribute's compression options or InvalidOid */
+ Oid attcompression;
+
#ifdef CATALOG_VARLEN /* variable-length fields start here */
/* NOTE: The following fields are not present in tuple descriptors. */
@@ -174,10 +177,10 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
* ATTRIBUTE_FIXED_PART_SIZE is the size of the fixed-layout,
* guaranteed-not-null part of a pg_attribute row. This is in fact as much
* of the row as gets copied into tuple descriptors, so don't expect you
- * can access fields beyond attcollation except in a real tuple!
+ * can access fields beyond attcompression except in a real tuple!
*/
#define ATTRIBUTE_FIXED_PART_SIZE \
- (offsetof(FormData_pg_attribute,attcollation) + sizeof(Oid))
+ (offsetof(FormData_pg_attribute,attcompression) + sizeof(Oid))
/* ----------------
* Form_pg_attribute corresponds to a pointer to a tuple with
@@ -191,29 +194,30 @@ typedef FormData_pg_attribute *Form_pg_attribute;
* ----------------
*/
-#define Natts_pg_attribute 22
-#define Anum_pg_attribute_attrelid 1
-#define Anum_pg_attribute_attname 2
-#define Anum_pg_attribute_atttypid 3
-#define Anum_pg_attribute_attstattarget 4
-#define Anum_pg_attribute_attlen 5
-#define Anum_pg_attribute_attnum 6
-#define Anum_pg_attribute_attndims 7
-#define Anum_pg_attribute_attcacheoff 8
-#define Anum_pg_attribute_atttypmod 9
-#define Anum_pg_attribute_attbyval 10
-#define Anum_pg_attribute_attstorage 11
-#define Anum_pg_attribute_attalign 12
-#define Anum_pg_attribute_attnotnull 13
-#define Anum_pg_attribute_atthasdef 14
-#define Anum_pg_attribute_attidentity 15
-#define Anum_pg_attribute_attisdropped 16
-#define Anum_pg_attribute_attislocal 17
-#define Anum_pg_attribute_attinhcount 18
-#define Anum_pg_attribute_attcollation 19
-#define Anum_pg_attribute_attacl 20
-#define Anum_pg_attribute_attoptions 21
-#define Anum_pg_attribute_attfdwoptions 22
+#define Natts_pg_attribute 23
+#define Anum_pg_attribute_attrelid 1
+#define Anum_pg_attribute_attname 2
+#define Anum_pg_attribute_atttypid 3
+#define Anum_pg_attribute_attstattarget 4
+#define Anum_pg_attribute_attlen 5
+#define Anum_pg_attribute_attnum 6
+#define Anum_pg_attribute_attndims 7
+#define Anum_pg_attribute_attcacheoff 8
+#define Anum_pg_attribute_atttypmod 9
+#define Anum_pg_attribute_attbyval 10
+#define Anum_pg_attribute_attstorage 11
+#define Anum_pg_attribute_attalign 12
+#define Anum_pg_attribute_attnotnull 13
+#define Anum_pg_attribute_atthasdef 14
+#define Anum_pg_attribute_attidentity 15
+#define Anum_pg_attribute_attisdropped 16
+#define Anum_pg_attribute_attislocal 17
+#define Anum_pg_attribute_attinhcount 18
+#define Anum_pg_attribute_attcollation 19
+#define Anum_pg_attribute_attcompression 20
+#define Anum_pg_attribute_attacl 21
+#define Anum_pg_attribute_attoptions 22
+#define Anum_pg_attribute_attfdwoptions 23
/* ----------------
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index b256657bda..40f5cc4f18 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -149,7 +149,7 @@ typedef FormData_pg_class *Form_pg_class;
*/
DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 30 0 t f f f f f f t n f 3 1 _null_ _null_ _null_));
DESCR("");
-DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 22 0 f f f f f f f t n f 3 1 _null_ _null_ _null_));
+DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 23 0 f f f f f f f t n f 3 1 _null_ _null_ _null_));
DESCR("");
DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f p r 29 0 t f f f f f f t n f 3 1 _null_ _null_ _null_));
DESCR("");
diff --git a/src/include/catalog/pg_compression.h b/src/include/catalog/pg_compression.h
new file mode 100644
index 0000000000..1d5f9ac479
--- /dev/null
+++ b/src/include/catalog/pg_compression.h
@@ -0,0 +1,55 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_compression.h
+ * definition of the system "compression method" relation (pg_compression)
+ * along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_compression.h
+ *
+ * NOTES
+ * the genbki.pl script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ * XXX do NOT break up DATA() statements into multiple lines!
+ * the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_COMPRESSION_H
+#define PG_COMPRESSION_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ * pg_compression definition. cpp turns this into
+ * typedef struct FormData_pg_compression
+ * ----------------
+ */
+#define CompressionMethodRelationId 3419
+
+CATALOG(pg_compression,3419)
+{
+ NameData cmname; /* compression method name */
+ regproc cmhandler; /* compression handler */
+} FormData_pg_compression;
+
+/* ----------------
+ * Form_pg_compression corresponds to a pointer to a tuple with
+ * the format of pg_compression relation.
+ * ----------------
+ */
+typedef FormData_pg_compression * Form_pg_compression;
+
+/* ----------------
+ * compiler constants for pg_compression
+ * ----------------
+ */
+#define Natts_pg_compression 2
+#define Anum_pg_compression_cmname 1
+#define Anum_pg_compression_cmhandler 2
+
+#endif /* PG_COMPRESSION_H */
diff --git a/src/include/catalog/pg_compression_opt.h b/src/include/catalog/pg_compression_opt.h
new file mode 100644
index 0000000000..ddcef814a7
--- /dev/null
+++ b/src/include/catalog/pg_compression_opt.h
@@ -0,0 +1,56 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_compression_opt.h
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_compression_opt.h
+ *
+ * NOTES
+ * the genbki.pl script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ * XXX do NOT break up DATA() statements into multiple lines!
+ * the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_COMPRESSION_OPT_H
+#define PG_COMPRESSION_OPT_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ * pg_compression_opt definition. cpp turns this into
+ * typedef struct FormData_pg_compression_opt
+ * ----------------
+ */
+#define CompressionOptRelationId 3420
+
+CATALOG(pg_compression_opt,3420) BKI_WITHOUT_OIDS
+{
+ Oid cmoptoid; /* compression options oid */
+ NameData cmname; /* name of compression method */
+ regproc cmhandler; /* compression handler */
+ text cmoptions[1]; /* specific options from WITH */
+} FormData_pg_compression_opt;
+
+/* ----------------
+ * Form_pg_compression_opt corresponds to a pointer to a tuple with
+ * the format of pg_compression_opt relation.
+ * ----------------
+ */
+typedef FormData_pg_compression_opt * Form_pg_compression_opt;
+
+/* ----------------
+ * compiler constants for pg_compression_opt
+ * ----------------
+ */
+#define Natts_pg_compression_opt 4
+#define Anum_pg_compression_opt_cmoptoid 1
+#define Anum_pg_compression_opt_cmname 2
+#define Anum_pg_compression_opt_cmhandler 3
+#define Anum_pg_compression_opt_cmoptions 4
+
+#endif /* PG_COMPRESSION_OPT_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index c969375981..dc3fa66f2e 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3881,6 +3881,10 @@ DATA(insert OID = 3311 ( tsm_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s
DESCR("I/O");
DATA(insert OID = 3312 ( tsm_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3310" _null_ _null_ _null_ _null_ _null_ tsm_handler_out _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 3425 ( compression_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3421 "2275" _null_ _null_ _null_ _null_ _null_ compression_handler_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 3426 ( compression_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3421" _null_ _null_ _null_ _null_ _null_ compression_handler_out _null_ _null_ _null_ ));
+DESCR("I/O");
/* tablesample method handlers */
DATA(insert OID = 3313 ( bernoulli PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3310 "2281" _null_ _null_ _null_ _null_ _null_ tsm_bernoulli_handler _null_ _null_ _null_ ));
@@ -4677,6 +4681,8 @@ DATA(insert OID = 3646 ( gtsvectorin PGNSP PGUID 12 1 0 0 0 f f f f t f i s
DESCR("I/O");
DATA(insert OID = 3647 ( gtsvectorout PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3642" _null_ _null_ _null_ _null_ _null_ gtsvectorout _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 3453 ( tsvector_compression_handler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3421 "2281" _null_ _null_ _null_ _null_ _null_ tsvector_compression_handler _null_ _null_ _null_ ));
+DESCR("tsvector compression handler");
DATA(insert OID = 3616 ( tsvector_lt PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "3614 3614" _null_ _null_ _null_ _null_ _null_ tsvector_lt _null_ _null_ _null_ ));
DATA(insert OID = 3617 ( tsvector_le PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "3614 3614" _null_ _null_ _null_ _null_ _null_ tsvector_le _null_ _null_ _null_ ));
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index e3551440a0..ec8c3df953 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -712,6 +712,8 @@ DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_han
#define TSM_HANDLEROID 3310
DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
#define ANYRANGEOID 3831
+DATA(insert OID = 3421 ( compression_handler PGNSP PGUID 4 t p P f t \054 0 0 0 compression_handler_in compression_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define COMPRESSION_HANDLEROID 3421
/*
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index bfead9af3d..a98ecc12ce 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -140,6 +140,7 @@ extern Oid RemoveUserMapping(DropUserMappingStmt *stmt);
extern void RemoveUserMappingById(Oid umId);
extern void CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid);
extern void ImportForeignSchema(ImportForeignSchemaStmt *stmt);
+extern Datum optionListToArray(List *options);
extern Datum transformGenericOptions(Oid catalogId,
Datum oldOptions,
List *options,
@@ -152,6 +153,14 @@ extern Oid get_index_am_oid(const char *amname, bool missing_ok);
extern Oid get_am_oid(const char *amname, bool missing_ok);
extern char *get_am_name(Oid amOid);
+/* commands/compressioncmds.c */
+extern ObjectAddress DefineCompressionMethod(List *names, List *parameters);
+extern void RemoveCompressionMethodById(Oid cmOid);
+extern void RemoveCompressionOptionsById(Oid cmoptoid);
+extern Oid get_compression_method_oid(const char *cmname, bool missing_ok);
+extern char *get_compression_method_name(Oid cmOid);
+extern char *get_compression_method_name_for_opt(Oid cmoptoid);
+
/* support routines in commands/define.c */
extern char *defGetString(DefElem *def);
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index da3ff5dbee..c50d9525d0 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -24,7 +24,8 @@
extern ObjectAddress DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
- ObjectAddress *typaddress, const char *queryString);
+ ObjectAddress *typaddress, const char *queryString,
+ Node **pAlterStmt);
extern void RemoveRelations(DropStmt *drop);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index ffeeb4919b..6dc49a73b6 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -468,6 +468,7 @@ typedef enum NodeTag
T_PartitionBoundSpec,
T_PartitionRangeDatum,
T_PartitionCmd,
+ T_ColumnCompression,
T_VacuumRelation,
/*
@@ -498,7 +499,8 @@ typedef enum NodeTag
T_FdwRoutine, /* in foreign/fdwapi.h */
T_IndexAmRoutine, /* in access/amapi.h */
T_TsmRoutine, /* in access/tsmapi.h */
- T_ForeignKeyCacheInfo /* in utils/rel.h */
+ T_ForeignKeyCacheInfo, /* in utils/rel.h */
+ T_CompressionMethodRoutine, /* in access/compression.h */
} NodeTag;
/*
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 80c19b2a55..9fa905ae6e 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -615,6 +615,19 @@ typedef struct RangeTableSample
int location; /* method name location, or -1 if unknown */
} RangeTableSample;
+/*
+ * ColumnCompression - compression parameters for some attribute
+ *
+ * This represents compression information defined using clause:
+ * .. COMPRESSED <compression_method_name> WITH (<params>)
+ */
+typedef struct ColumnCompression
+{
+ NodeTag type;
+ char *methodName;
+ List *options;
+} ColumnCompression;
+
/*
* ColumnDef - column definition (used in various creates)
*
@@ -638,6 +651,7 @@ typedef struct ColumnDef
NodeTag type;
char *colname; /* name of column */
TypeName *typeName; /* type of column */
+ ColumnCompression *compression;
int inhcount; /* number of times column is inherited */
bool is_local; /* column has local (non-inherited) def'n */
bool is_not_null; /* NOT NULL constraint specified? */
@@ -1622,6 +1636,7 @@ typedef enum ObjectType
OBJECT_CAST,
OBJECT_COLUMN,
OBJECT_COLLATION,
+ OBJECT_COMPRESSION_METHOD,
OBJECT_CONVERSION,
OBJECT_DATABASE,
OBJECT_DEFAULT,
@@ -1769,7 +1784,8 @@ typedef enum AlterTableType
AT_DetachPartition, /* DETACH PARTITION */
AT_AddIdentity, /* ADD IDENTITY */
AT_SetIdentity, /* SET identity column options */
- AT_DropIdentity /* DROP IDENTITY */
+ AT_DropIdentity, /* DROP IDENTITY */
+ AT_AlterColumnCompression /* ALTER COLUMN name COMPRESSED cm WITH (...) */
} AlterTableType;
typedef struct ReplicaIdentityStmt
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index f50e45e886..7bfc6e6be4 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -87,6 +87,8 @@ PG_KEYWORD("comment", COMMENT, UNRESERVED_KEYWORD)
PG_KEYWORD("comments", COMMENTS, UNRESERVED_KEYWORD)
PG_KEYWORD("commit", COMMIT, UNRESERVED_KEYWORD)
PG_KEYWORD("committed", COMMITTED, UNRESERVED_KEYWORD)
+PG_KEYWORD("compressed", COMPRESSED, UNRESERVED_KEYWORD)
+PG_KEYWORD("compression", COMPRESSION, UNRESERVED_KEYWORD)
PG_KEYWORD("concurrently", CONCURRENTLY, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("configuration", CONFIGURATION, UNRESERVED_KEYWORD)
PG_KEYWORD("conflict", CONFLICT, UNRESERVED_KEYWORD)
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index e749432ef0..5cab77457a 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -22,10 +22,12 @@ extern List *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
const char *queryString);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
+void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
extern List *transformCreateSchemaStmt(CreateSchemaStmt *stmt);
extern PartitionBoundSpec *transformPartitionBound(ParseState *pstate, Relation parent,
PartitionBoundSpec *spec);
+extern void transformColumnCompression(ColumnDef *column, RangeVar *relation,
+ AlterTableStmt **alterStmt);
#endif /* PARSE_UTILCMD_H */
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 1ca9b60ea1..d21974696f 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -56,7 +56,7 @@
/*
* struct varatt_external is a traditional "TOAST pointer", that is, the
* information needed to fetch a Datum stored out-of-line in a TOAST table.
- * The data is compressed if and only if va_extsize < va_rawsize - VARHDRSZ.
+ * The data is compressed if and only if size in va_extinfo < va_rawsize - VARHDRSZ.
* This struct must not contain any padding, because we sometimes compare
* these pointers using memcmp.
*
@@ -68,7 +68,8 @@
typedef struct varatt_external
{
int32 va_rawsize; /* Original data size (includes header) */
- int32 va_extsize; /* External saved size (doesn't) */
+ uint32 va_extinfo; /* External saved size (without header) and
+ * flags */
Oid va_valueid; /* Unique ID of value within TOAST table */
Oid va_toastrelid; /* RelID of TOAST table containing it */
} varatt_external;
@@ -146,9 +147,18 @@ typedef union
struct /* Compressed-in-line format */
{
uint32 va_header;
- uint32 va_rawsize; /* Original data size (excludes header) */
+ uint32 va_info; /* Original data size (excludes header) and
+ * flags */
char va_data[FLEXIBLE_ARRAY_MEMBER]; /* Compressed data */
} va_compressed;
+ struct /* Compressed-in-line format */
+ {
+ uint32 va_header;
+ uint32 va_info; /* Original data size (excludes header) and
+ * flags */
+ Oid va_cmoptoid; /* Oid of compression options */
+ char va_data[FLEXIBLE_ARRAY_MEMBER]; /* Compressed data */
+ } va_custom_compressed;
} varattrib_4b;
typedef struct
@@ -281,8 +291,14 @@ typedef struct
#define VARDATA_1B(PTR) (((varattrib_1b *) (PTR))->va_data)
#define VARDATA_1B_E(PTR) (((varattrib_1b_e *) (PTR))->va_data)
+/* va_info in va_compress contains raw size of datum and optional flags */
#define VARRAWSIZE_4B_C(PTR) \
- (((varattrib_4b *) (PTR))->va_compressed.va_rawsize)
+ (((varattrib_4b *) (PTR))->va_compressed.va_info & 0x3FFFFFFF)
+#define VARFLAGS_4B_C(PTR) \
+ (((varattrib_4b *) (PTR))->va_compressed.va_info >> 30)
+
+#define VARHDRSZ_CUSTOM_COMPRESSED \
+ (offsetof(varattrib_4b, va_custom_compressed.va_data))
/* Externally visible macros */
@@ -311,6 +327,8 @@ typedef struct
#define VARDATA_EXTERNAL(PTR) VARDATA_1B_E(PTR)
#define VARATT_IS_COMPRESSED(PTR) VARATT_IS_4B_C(PTR)
+#define VARATT_IS_CUSTOM_COMPRESSED(PTR) (VARATT_IS_4B_C(PTR) && \
+ (VARFLAGS_4B_C(PTR) == 0x02))
#define VARATT_IS_EXTERNAL(PTR) VARATT_IS_1B_E(PTR)
#define VARATT_IS_EXTERNAL_ONDISK(PTR) \
(VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_ONDISK)
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 254a811aff..6ad889af7a 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -210,6 +210,7 @@ typedef enum AclObjectKind
ACL_KIND_EXTENSION, /* pg_extension */
ACL_KIND_PUBLICATION, /* pg_publication */
ACL_KIND_SUBSCRIPTION, /* pg_subscription */
+ ACL_KIND_COMPRESSION_METHOD, /* pg_compression */
MAX_ACL_KIND /* MUST BE LAST */
} AclObjectKind;
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 8a0be41929..ff7cb530fd 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -48,6 +48,9 @@ enum SysCacheIdentifier
CLAOID,
COLLNAMEENCNSP,
COLLOID,
+ COMPRESSIONMETHODOID,
+ COMPRESSIONMETHODNAME,
+ COMPRESSIONOPTIONSOID,
CONDEFAULT,
CONNAMENSP,
CONSTROID,
diff --git a/src/test/regress/expected/copy2.out b/src/test/regress/expected/copy2.out
index 65e9c626b3..112f0eda47 100644
--- a/src/test/regress/expected/copy2.out
+++ b/src/test/regress/expected/copy2.out
@@ -438,10 +438,10 @@ begin
end $$ language plpgsql immutable;
alter table check_con_tbl add check (check_con_function(check_con_tbl.*));
\d+ check_con_tbl
- Table "public.check_con_tbl"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- f1 | integer | | | | plain | |
+ Table "public.check_con_tbl"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ f1 | integer | | | | plain | | |
Check constraints:
"check_con_tbl_check" CHECK (check_con_function(check_con_tbl.*))
diff --git a/src/test/regress/expected/create_cm.out b/src/test/regress/expected/create_cm.out
new file mode 100644
index 0000000000..16d66a0263
--- /dev/null
+++ b/src/test/regress/expected/create_cm.out
@@ -0,0 +1,145 @@
+-- test drop
+CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
+CREATE TABLE droptest(fts tsvector COMPRESSED ts1);
+DROP COMPRESSION METHOD ts1;
+ERROR: cannot drop compression method ts1 because other objects depend on it
+DETAIL: compression options for ts1 depends on compression method ts1
+table droptest column fts depends on compression options for ts1
+HINT: Use DROP ... CASCADE to drop the dependent objects too.
+DROP COMPRESSION METHOD ts1 CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to compression options for ts1
+drop cascades to table droptest column fts
+\d+ droptest
+ Table "public.droptest"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+---------+-------------+--------------+-------------
+
+DROP TABLE droptest;
+CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
+CREATE TABLE cmtest(fts tsvector COMPRESSED ts1);
+SELECT * FROM pg_compression;
+ cmname | cmhandler
+--------+------------------------------
+ ts1 | tsvector_compression_handler
+(1 row)
+
+SELECT cmhandler, cmoptions FROM pg_compression_opt;
+ cmhandler | cmoptions
+------------------------------+-----------
+ tsvector_compression_handler |
+(1 row)
+
+\dCM
+List of compression methods
+ Name
+------
+ ts1
+(1 row)
+
+\d+ cmtest
+ Table "public.cmtest"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+
+INSERT INTO cmtest
+ SELECT to_tsvector(string_agg(repeat(substr(i::text,1,1), i), ' '))
+ FROM generate_series(1,200) i;
+SELECT length(fts) FROM cmtest;
+ length
+--------
+ 200
+(1 row)
+
+SELECT length(fts) FROM cmtest;
+ length
+--------
+ 200
+(1 row)
+
+-- check ALTER commands
+ALTER TABLE cmtest ALTER COLUMN fts SET NOT COMPRESSED;
+\d+ cmtest
+ Table "public.cmtest"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | | |
+
+ALTER TABLE cmtest ALTER COLUMN fts SET COMPRESSED ts1 WITH (format 'lz');
+ERROR: the compression method "ts1" does not take any options
+ALTER TABLE cmtest ALTER COLUMN fts SET COMPRESSED ts1;
+\d+ cmtest
+ Table "public.cmtest"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+
+-- create different types of tables
+SELECT * INTO cmtest2 FROM cmtest;
+CREATE TABLE cmtest3 (LIKE cmtest);
+CREATE TABLE cmtest4(fts tsvector, a int) INHERITS (cmtest);
+NOTICE: merging column "fts" with inherited definition
+CREATE TABLE cmtest5(fts tsvector);
+-- we update usual datum with compressed datum
+INSERT INTO cmtest5
+ SELECT to_tsvector(string_agg(repeat(substr(i::text,1,1), i), ' '))
+ FROM generate_series(1,200) i;
+UPDATE cmtest5 SET fts = cmtest.fts FROM cmtest;
+\d+ cmtest3
+ Table "public.cmtest3"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+
+\d+ cmtest4
+ Table "public.cmtest4"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+ a | integer | | | | plain | | |
+Inherits: cmtest
+
+DROP TABLE cmtest CASCADE;
+NOTICE: drop cascades to table cmtest4
+DROP TABLE cmtest3;
+SELECT cmhandler, cmoptions FROM pg_compression_opt;
+ cmhandler | cmoptions
+------------------------------+-----------
+ tsvector_compression_handler |
+ tsvector_compression_handler |
+ tsvector_compression_handler |
+ tsvector_compression_handler |
+(4 rows)
+
+DROP COMPRESSION METHOD ts1 CASCADE;
+NOTICE: drop cascades to 4 other objects
+DETAIL: drop cascades to compression options for ts1
+drop cascades to compression options for ts1
+drop cascades to compression options for ts1
+drop cascades to compression options for ts1
+SELECT * FROM pg_compression;
+ cmname | cmhandler
+--------+-----------
+(0 rows)
+
+SELECT * FROM pg_compression_opt;
+ cmoptoid | cmname | cmhandler | cmoptions
+----------+--------+-----------+-----------
+(0 rows)
+
+-- check that moved tuples still can be decompressed
+SELECT length(fts) FROM cmtest2;
+ length
+--------
+ 200
+(1 row)
+
+SELECT length(fts) FROM cmtest5;
+ length
+--------
+ 200
+(1 row)
+
+DROP TABLE cmtest2;
+DROP TABLE cmtest5;
diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out
index 335cd37e18..b511daf9fa 100644
--- a/src/test/regress/expected/create_table.out
+++ b/src/test/regress/expected/create_table.out
@@ -590,10 +590,10 @@ CREATE TABLE oids_parted (
) PARTITION BY RANGE (a) WITH OIDS;
CREATE TABLE part_forced_oids PARTITION OF oids_parted FOR VALUES FROM (1) TO (10) WITHOUT OIDS;
\d+ part_forced_oids
- Table "public.part_forced_oids"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
+ Table "public.part_forced_oids"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
Partition of: oids_parted FOR VALUES FROM (1) TO (10)
Partition constraint: ((a IS NOT NULL) AND (a >= 1) AND (a < 10))
Has OIDs: yes
@@ -729,11 +729,11 @@ CREATE TABLE part_c PARTITION OF parted (b WITH OPTIONS NOT NULL DEFAULT 0) FOR
CREATE TABLE part_c_1_10 PARTITION OF part_c FOR VALUES FROM (1) TO (10);
-- Partition bound in describe output
\d+ part_b
- Table "public.part_b"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | not null | 1 | plain | |
+ Table "public.part_b"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | not null | 1 | plain | | |
Partition of: parted FOR VALUES IN ('b')
Partition constraint: ((a IS NOT NULL) AND (a = ANY (ARRAY['b'::text])))
Check constraints:
@@ -756,11 +756,11 @@ Partitions: part_c_1_10 FOR VALUES FROM (1) TO (10)
-- a level-2 partition's constraint will include the parent's expressions
\d+ part_c_1_10
- Table "public.part_c_1_10"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | not null | 0 | plain | |
+ Table "public.part_c_1_10"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | not null | 0 | plain | | |
Partition of: part_c FOR VALUES FROM (1) TO (10)
Partition constraint: ((a IS NOT NULL) AND (a = ANY (ARRAY['c'::text])) AND (b IS NOT NULL) AND (b >= 1) AND (b < 10))
Check constraints:
@@ -793,46 +793,46 @@ Number of partitions: 3 (Use \d+ to list them.)
CREATE TABLE range_parted4 (a int, b int, c int) PARTITION BY RANGE (abs(a), abs(b), c);
CREATE TABLE unbounded_range_part PARTITION OF range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (MAXVALUE, MAXVALUE, MAXVALUE);
\d+ unbounded_range_part
- Table "public.unbounded_range_part"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
+ Table "public.unbounded_range_part"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
Partition of: range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (MAXVALUE, MAXVALUE, MAXVALUE)
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL))
DROP TABLE unbounded_range_part;
CREATE TABLE range_parted4_1 PARTITION OF range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (1, MAXVALUE, MAXVALUE);
\d+ range_parted4_1
- Table "public.range_parted4_1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
+ Table "public.range_parted4_1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
Partition of: range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (1, MAXVALUE, MAXVALUE)
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND (abs(a) <= 1))
CREATE TABLE range_parted4_2 PARTITION OF range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, MAXVALUE);
\d+ range_parted4_2
- Table "public.range_parted4_2"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
+ Table "public.range_parted4_2"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
Partition of: range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, MAXVALUE)
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND ((abs(a) > 3) OR ((abs(a) = 3) AND (abs(b) > 4)) OR ((abs(a) = 3) AND (abs(b) = 4) AND (c >= 5))) AND ((abs(a) < 6) OR ((abs(a) = 6) AND (abs(b) <= 7))))
CREATE TABLE range_parted4_3 PARTITION OF range_parted4 FOR VALUES FROM (6, 8, MINVALUE) TO (9, MAXVALUE, MAXVALUE);
\d+ range_parted4_3
- Table "public.range_parted4_3"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
+ Table "public.range_parted4_3"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
Partition of: range_parted4 FOR VALUES FROM (6, 8, MINVALUE) TO (9, MAXVALUE, MAXVALUE)
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND ((abs(a) > 6) OR ((abs(a) = 6) AND (abs(b) >= 8))) AND (abs(a) <= 9))
diff --git a/src/test/regress/expected/create_table_like.out b/src/test/regress/expected/create_table_like.out
index 3f405c94ce..b5ca8b820b 100644
--- a/src/test/regress/expected/create_table_like.out
+++ b/src/test/regress/expected/create_table_like.out
@@ -156,32 +156,32 @@ CREATE TABLE ctlt4 (a text, c text);
ALTER TABLE ctlt4 ALTER COLUMN c SET STORAGE EXTERNAL;
CREATE TABLE ctlt12_storage (LIKE ctlt1 INCLUDING STORAGE, LIKE ctlt2 INCLUDING STORAGE);
\d+ ctlt12_storage
- Table "public.ctlt12_storage"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | |
- b | text | | | | extended | |
- c | text | | | | external | |
+ Table "public.ctlt12_storage"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | |
+ b | text | | | | extended | | |
+ c | text | | | | external | | |
CREATE TABLE ctlt12_comments (LIKE ctlt1 INCLUDING COMMENTS, LIKE ctlt2 INCLUDING COMMENTS);
\d+ ctlt12_comments
- Table "public.ctlt12_comments"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | extended | | A
- b | text | | | | extended | | B
- c | text | | | | extended | | C
+ Table "public.ctlt12_comments"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | extended | | | A
+ b | text | | | | extended | | | B
+ c | text | | | | extended | | | C
CREATE TABLE ctlt1_inh (LIKE ctlt1 INCLUDING CONSTRAINTS INCLUDING COMMENTS) INHERITS (ctlt1);
NOTICE: merging column "a" with inherited definition
NOTICE: merging column "b" with inherited definition
NOTICE: merging constraint "ctlt1_a_check" with inherited definition
\d+ ctlt1_inh
- Table "public.ctlt1_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | | A
- b | text | | | | extended | | B
+ Table "public.ctlt1_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | | A
+ b | text | | | | extended | | | B
Check constraints:
"ctlt1_a_check" CHECK (length(a) > 2)
Inherits: ctlt1
@@ -195,12 +195,12 @@ SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_con
CREATE TABLE ctlt13_inh () INHERITS (ctlt1, ctlt3);
NOTICE: merging multiple inherited definitions of column "a"
\d+ ctlt13_inh
- Table "public.ctlt13_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | |
- b | text | | | | extended | |
- c | text | | | | external | |
+ Table "public.ctlt13_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | |
+ b | text | | | | extended | | |
+ c | text | | | | external | | |
Check constraints:
"ctlt1_a_check" CHECK (length(a) > 2)
"ctlt3_a_check" CHECK (length(a) < 5)
@@ -210,12 +210,12 @@ Inherits: ctlt1,
CREATE TABLE ctlt13_like (LIKE ctlt3 INCLUDING CONSTRAINTS INCLUDING COMMENTS INCLUDING STORAGE) INHERITS (ctlt1);
NOTICE: merging column "a" with inherited definition
\d+ ctlt13_like
- Table "public.ctlt13_like"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | | A3
- b | text | | | | extended | |
- c | text | | | | external | | C
+ Table "public.ctlt13_like"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | | A3
+ b | text | | | | extended | | |
+ c | text | | | | external | | | C
Check constraints:
"ctlt1_a_check" CHECK (length(a) > 2)
"ctlt3_a_check" CHECK (length(a) < 5)
@@ -229,11 +229,11 @@ SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_con
CREATE TABLE ctlt_all (LIKE ctlt1 INCLUDING ALL);
\d+ ctlt_all
- Table "public.ctlt_all"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | | A
- b | text | | | | extended | | B
+ Table "public.ctlt_all"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | | A
+ b | text | | | | extended | | | B
Indexes:
"ctlt_all_pkey" PRIMARY KEY, btree (a)
"ctlt_all_b_idx" btree (b)
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index f4eebb75cf..2ac75c44b5 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -272,10 +272,10 @@ explain (verbose, costs off)
create rule silly as on delete to dcomptable do instead
update dcomptable set d1.r = (d1).r - 1, d1.i = (d1).i + 1 where (d1).i > 0;
\d+ dcomptable
- Table "public.dcomptable"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+-----------+-----------+----------+---------+----------+--------------+-------------
- d1 | dcomptype | | | | extended | |
+ Table "public.dcomptable"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+-----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ d1 | dcomptype | | | | extended | | |
Indexes:
"dcomptable_d1_key" UNIQUE CONSTRAINT, btree (d1)
Rules:
@@ -409,10 +409,10 @@ create rule silly as on delete to dcomptable do instead
update dcomptable set d1[1].r = d1[1].r - 1, d1[1].i = d1[1].i + 1
where d1[1].i > 0;
\d+ dcomptable
- Table "public.dcomptable"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------------+-----------+----------+---------+----------+--------------+-------------
- d1 | dcomptypea | | | | extended | |
+ Table "public.dcomptable"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------------+-----------+----------+---------+----------+-------------+--------------+-------------
+ d1 | dcomptypea | | | | extended | | |
Indexes:
"dcomptable_d1_key" UNIQUE CONSTRAINT, btree (d1)
Rules:
diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out
index 331f7a911f..530ce1b1d0 100644
--- a/src/test/regress/expected/foreign_data.out
+++ b/src/test/regress/expected/foreign_data.out
@@ -1330,12 +1330,12 @@ CREATE TABLE pt1 (
CREATE FOREIGN TABLE ft2 () INHERITS (pt1)
SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value');
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Child tables: ft2
\d+ ft2
@@ -1351,12 +1351,12 @@ Inherits: pt1
DROP FOREIGN TABLE ft2;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
CREATE FOREIGN TABLE ft2 (
c1 integer NOT NULL,
@@ -1375,12 +1375,12 @@ FDW options: (delimiter ',', quote '"', "be quoted" 'value')
ALTER FOREIGN TABLE ft2 INHERIT pt1;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Child tables: ft2
\d+ ft2
@@ -1418,12 +1418,12 @@ Child tables: ct3,
ft3
\d+ ct3
- Table "public.ct3"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.ct3"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Inherits: ft2
\d+ ft3
@@ -1443,17 +1443,17 @@ ALTER TABLE pt1 ADD COLUMN c6 integer;
ALTER TABLE pt1 ADD COLUMN c7 integer NOT NULL;
ALTER TABLE pt1 ADD COLUMN c8 integer;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
- c4 | integer | | | | plain | |
- c5 | integer | | | 0 | plain | |
- c6 | integer | | | | plain | |
- c7 | integer | | not null | | plain | |
- c8 | integer | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
+ c4 | integer | | | | plain | | |
+ c5 | integer | | | 0 | plain | | |
+ c6 | integer | | | | plain | | |
+ c7 | integer | | not null | | plain | | |
+ c8 | integer | | | | plain | | |
Child tables: ft2
\d+ ft2
@@ -1475,17 +1475,17 @@ Child tables: ct3,
ft3
\d+ ct3
- Table "public.ct3"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
- c4 | integer | | | | plain | |
- c5 | integer | | | 0 | plain | |
- c6 | integer | | | | plain | |
- c7 | integer | | not null | | plain | |
- c8 | integer | | | | plain | |
+ Table "public.ct3"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
+ c4 | integer | | | | plain | | |
+ c5 | integer | | | 0 | plain | | |
+ c6 | integer | | | | plain | | |
+ c7 | integer | | not null | | plain | | |
+ c8 | integer | | | | plain | | |
Inherits: ft2
\d+ ft3
@@ -1517,17 +1517,17 @@ ALTER TABLE pt1 ALTER COLUMN c1 SET (n_distinct = 100);
ALTER TABLE pt1 ALTER COLUMN c8 SET STATISTICS -1;
ALTER TABLE pt1 ALTER COLUMN c8 SET STORAGE EXTERNAL;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
- c4 | integer | | | 0 | plain | |
- c5 | integer | | | | plain | |
- c6 | integer | | not null | | plain | |
- c7 | integer | | | | plain | |
- c8 | text | | | | external | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
+ c4 | integer | | | 0 | plain | | |
+ c5 | integer | | | | plain | | |
+ c6 | integer | | not null | | plain | | |
+ c7 | integer | | | | plain | | |
+ c8 | text | | | | external | | |
Child tables: ft2
\d+ ft2
@@ -1555,12 +1555,12 @@ ALTER TABLE pt1 DROP COLUMN c6;
ALTER TABLE pt1 DROP COLUMN c7;
ALTER TABLE pt1 DROP COLUMN c8;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Child tables: ft2
\d+ ft2
@@ -1592,12 +1592,12 @@ SELECT relname, conname, contype, conislocal, coninhcount, connoinherit
-- child does not inherit NO INHERIT constraints
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk1" CHECK (c1 > 0) NO INHERIT
"pt1chk2" CHECK (c2 <> ''::text)
@@ -1636,12 +1636,12 @@ ALTER FOREIGN TABLE ft2 ADD CONSTRAINT pt1chk2 CHECK (c2 <> '');
ALTER FOREIGN TABLE ft2 INHERIT pt1;
-- child does not inherit NO INHERIT constraints
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk1" CHECK (c1 > 0) NO INHERIT
"pt1chk2" CHECK (c2 <> ''::text)
@@ -1667,12 +1667,12 @@ ALTER TABLE pt1 DROP CONSTRAINT pt1chk2 CASCADE;
INSERT INTO pt1 VALUES (1, 'pt1'::text, '1994-01-01'::date);
ALTER TABLE pt1 ADD CONSTRAINT pt1chk3 CHECK (c2 <> '') NOT VALID;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk3" CHECK (c2 <> ''::text) NOT VALID
Child tables: ft2
@@ -1694,12 +1694,12 @@ Inherits: pt1
-- VALIDATE CONSTRAINT need do nothing on foreign tables
ALTER TABLE pt1 VALIDATE CONSTRAINT pt1chk3;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk3" CHECK (c2 <> ''::text)
Child tables: ft2
@@ -1721,12 +1721,12 @@ Inherits: pt1
-- OID system column
ALTER TABLE pt1 SET WITH OIDS;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk3" CHECK (c2 <> ''::text)
Child tables: ft2
@@ -1751,12 +1751,12 @@ ALTER TABLE ft2 SET WITHOUT OIDS; -- ERROR
ERROR: cannot drop inherited column "oid"
ALTER TABLE pt1 SET WITHOUT OIDS;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk3" CHECK (c2 <> ''::text)
Child tables: ft2
@@ -1782,12 +1782,12 @@ ALTER TABLE pt1 RENAME COLUMN c3 TO f3;
-- changes name of a constraint recursively
ALTER TABLE pt1 RENAME CONSTRAINT pt1chk3 TO f2_check;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- f1 | integer | | not null | | plain | 10000 |
- f2 | text | | | | extended | |
- f3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ f1 | integer | | not null | | plain | | 10000 |
+ f2 | text | | | | extended | | |
+ f3 | date | | | | plain | | |
Check constraints:
"f2_check" CHECK (f2 <> ''::text)
Child tables: ft2
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index c698faff2f..a147b8217f 100644
--- a/src/test/regress/expected/inherit.out
+++ b/src/test/regress/expected/inherit.out
@@ -1023,13 +1023,13 @@ ALTER TABLE inhts RENAME aa TO aaa; -- to be failed
ERROR: cannot rename inherited column "aa"
ALTER TABLE inhts RENAME d TO dd;
\d+ inhts
- Table "public.inhts"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- aa | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
- dd | integer | | | | plain | |
+ Table "public.inhts"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ aa | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
+ dd | integer | | | | plain | | |
Inherits: inht1,
inhs1
@@ -1042,14 +1042,14 @@ NOTICE: merging multiple inherited definitions of column "aa"
NOTICE: merging multiple inherited definitions of column "b"
ALTER TABLE inht1 RENAME aa TO aaa;
\d+ inht4
- Table "public.inht4"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- aaa | integer | | | | plain | |
- b | integer | | | | plain | |
- x | integer | | | | plain | |
- y | integer | | | | plain | |
- z | integer | | | | plain | |
+ Table "public.inht4"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ aaa | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ x | integer | | | | plain | | |
+ y | integer | | | | plain | | |
+ z | integer | | | | plain | | |
Inherits: inht2,
inht3
@@ -1059,14 +1059,14 @@ ALTER TABLE inht1 RENAME aaa TO aaaa;
ALTER TABLE inht1 RENAME b TO bb; -- to be failed
ERROR: cannot rename inherited column "b"
\d+ inhts
- Table "public.inhts"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- aaaa | integer | | | | plain | |
- b | integer | | | | plain | |
- x | integer | | | | plain | |
- c | integer | | | | plain | |
- d | integer | | | | plain | |
+ Table "public.inhts"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ aaaa | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ x | integer | | | | plain | | |
+ c | integer | | | | plain | | |
+ d | integer | | | | plain | | |
Inherits: inht2,
inhs1
@@ -1106,33 +1106,33 @@ drop cascades to table inht4
CREATE TABLE test_constraints (id int, val1 varchar, val2 int, UNIQUE(val1, val2));
CREATE TABLE test_constraints_inh () INHERITS (test_constraints);
\d+ test_constraints
- Table "public.test_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+-------------------+-----------+----------+---------+----------+--------------+-------------
- id | integer | | | | plain | |
- val1 | character varying | | | | extended | |
- val2 | integer | | | | plain | |
+ Table "public.test_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+-------------------+-----------+----------+---------+----------+-------------+--------------+-------------
+ id | integer | | | | plain | | |
+ val1 | character varying | | | | extended | | |
+ val2 | integer | | | | plain | | |
Indexes:
"test_constraints_val1_val2_key" UNIQUE CONSTRAINT, btree (val1, val2)
Child tables: test_constraints_inh
ALTER TABLE ONLY test_constraints DROP CONSTRAINT test_constraints_val1_val2_key;
\d+ test_constraints
- Table "public.test_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+-------------------+-----------+----------+---------+----------+--------------+-------------
- id | integer | | | | plain | |
- val1 | character varying | | | | extended | |
- val2 | integer | | | | plain | |
+ Table "public.test_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+-------------------+-----------+----------+---------+----------+-------------+--------------+-------------
+ id | integer | | | | plain | | |
+ val1 | character varying | | | | extended | | |
+ val2 | integer | | | | plain | | |
Child tables: test_constraints_inh
\d+ test_constraints_inh
- Table "public.test_constraints_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+-------------------+-----------+----------+---------+----------+--------------+-------------
- id | integer | | | | plain | |
- val1 | character varying | | | | extended | |
- val2 | integer | | | | plain | |
+ Table "public.test_constraints_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+-------------------+-----------+----------+---------+----------+-------------+--------------+-------------
+ id | integer | | | | plain | | |
+ val1 | character varying | | | | extended | | |
+ val2 | integer | | | | plain | | |
Inherits: test_constraints
DROP TABLE test_constraints_inh;
@@ -1143,27 +1143,27 @@ CREATE TABLE test_ex_constraints (
);
CREATE TABLE test_ex_constraints_inh () INHERITS (test_ex_constraints);
\d+ test_ex_constraints
- Table "public.test_ex_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+--------+-----------+----------+---------+---------+--------------+-------------
- c | circle | | | | plain | |
+ Table "public.test_ex_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+--------+-----------+----------+---------+---------+-------------+--------------+-------------
+ c | circle | | | | plain | | |
Indexes:
"test_ex_constraints_c_excl" EXCLUDE USING gist (c WITH &&)
Child tables: test_ex_constraints_inh
ALTER TABLE test_ex_constraints DROP CONSTRAINT test_ex_constraints_c_excl;
\d+ test_ex_constraints
- Table "public.test_ex_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+--------+-----------+----------+---------+---------+--------------+-------------
- c | circle | | | | plain | |
+ Table "public.test_ex_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+--------+-----------+----------+---------+---------+-------------+--------------+-------------
+ c | circle | | | | plain | | |
Child tables: test_ex_constraints_inh
\d+ test_ex_constraints_inh
- Table "public.test_ex_constraints_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+--------+-----------+----------+---------+---------+--------------+-------------
- c | circle | | | | plain | |
+ Table "public.test_ex_constraints_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+--------+-----------+----------+---------+---------+-------------+--------------+-------------
+ c | circle | | | | plain | | |
Inherits: test_ex_constraints
DROP TABLE test_ex_constraints_inh;
@@ -1173,37 +1173,37 @@ CREATE TABLE test_primary_constraints(id int PRIMARY KEY);
CREATE TABLE test_foreign_constraints(id1 int REFERENCES test_primary_constraints(id));
CREATE TABLE test_foreign_constraints_inh () INHERITS (test_foreign_constraints);
\d+ test_primary_constraints
- Table "public.test_primary_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- id | integer | | not null | | plain | |
+ Table "public.test_primary_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ id | integer | | not null | | plain | | |
Indexes:
"test_primary_constraints_pkey" PRIMARY KEY, btree (id)
Referenced by:
TABLE "test_foreign_constraints" CONSTRAINT "test_foreign_constraints_id1_fkey" FOREIGN KEY (id1) REFERENCES test_primary_constraints(id)
\d+ test_foreign_constraints
- Table "public.test_foreign_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- id1 | integer | | | | plain | |
+ Table "public.test_foreign_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ id1 | integer | | | | plain | | |
Foreign-key constraints:
"test_foreign_constraints_id1_fkey" FOREIGN KEY (id1) REFERENCES test_primary_constraints(id)
Child tables: test_foreign_constraints_inh
ALTER TABLE test_foreign_constraints DROP CONSTRAINT test_foreign_constraints_id1_fkey;
\d+ test_foreign_constraints
- Table "public.test_foreign_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- id1 | integer | | | | plain | |
+ Table "public.test_foreign_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ id1 | integer | | | | plain | | |
Child tables: test_foreign_constraints_inh
\d+ test_foreign_constraints_inh
- Table "public.test_foreign_constraints_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- id1 | integer | | | | plain | |
+ Table "public.test_foreign_constraints_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ id1 | integer | | | | plain | | |
Inherits: test_foreign_constraints
DROP TABLE test_foreign_constraints_inh;
diff --git a/src/test/regress/expected/insert.out b/src/test/regress/expected/insert.out
index 9d84ba4658..bbab453247 100644
--- a/src/test/regress/expected/insert.out
+++ b/src/test/regress/expected/insert.out
@@ -142,11 +142,11 @@ create rule irule3 as on insert to inserttest2 do also
insert into inserttest (f4[1].if1, f4[1].if2[2])
select new.f1, new.f2;
\d+ inserttest2
- Table "public.inserttest2"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+--------+-----------+----------+---------+----------+--------------+-------------
- f1 | bigint | | | | plain | |
- f2 | text | | | | extended | |
+ Table "public.inserttest2"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+--------+-----------+----------+---------+----------+-------------+--------------+-------------
+ f1 | bigint | | | | plain | | |
+ f2 | text | | | | extended | | |
Rules:
irule1 AS
ON INSERT TO inserttest2 DO INSERT INTO inserttest (f3.if2[1], f3.if2[2])
@@ -435,10 +435,10 @@ drop function dummy_hashint4(a int4, seed int8);
create table list_parted (a int) partition by list (a);
create table part_default partition of list_parted default;
\d+ part_default
- Table "public.part_default"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
+ Table "public.part_default"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
Partition of: list_parted DEFAULT
No partition constraint
@@ -750,74 +750,74 @@ Partitions: mcrparted1_lt_b FOR VALUES FROM (MINVALUE, MINVALUE) TO ('b', MINVAL
mcrparted8_ge_d FOR VALUES FROM ('d', MINVALUE) TO (MAXVALUE, MAXVALUE)
\d+ mcrparted1_lt_b
- Table "public.mcrparted1_lt_b"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted1_lt_b"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM (MINVALUE, MINVALUE) TO ('b', MINVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a < 'b'::text))
\d+ mcrparted2_b
- Table "public.mcrparted2_b"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted2_b"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('b', MINVALUE) TO ('c', MINVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'b'::text) AND (a < 'c'::text))
\d+ mcrparted3_c_to_common
- Table "public.mcrparted3_c_to_common"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted3_c_to_common"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('c', MINVALUE) TO ('common', MINVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'c'::text) AND (a < 'common'::text))
\d+ mcrparted4_common_lt_0
- Table "public.mcrparted4_common_lt_0"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted4_common_lt_0"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('common', MINVALUE) TO ('common', 0)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::text) AND (b < 0))
\d+ mcrparted5_common_0_to_10
- Table "public.mcrparted5_common_0_to_10"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted5_common_0_to_10"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('common', 0) TO ('common', 10)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::text) AND (b >= 0) AND (b < 10))
\d+ mcrparted6_common_ge_10
- Table "public.mcrparted6_common_ge_10"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted6_common_ge_10"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('common', 10) TO ('common', MAXVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::text) AND (b >= 10))
\d+ mcrparted7_gt_common_lt_d
- Table "public.mcrparted7_gt_common_lt_d"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted7_gt_common_lt_d"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('common', MAXVALUE) TO ('d', MINVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a > 'common'::text) AND (a < 'd'::text))
\d+ mcrparted8_ge_d
- Table "public.mcrparted8_ge_d"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted8_ge_d"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('d', MINVALUE) TO (MAXVALUE, MAXVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'd'::text))
diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out
index b101331d69..f805e13d75 100644
--- a/src/test/regress/expected/publication.out
+++ b/src/test/regress/expected/publication.out
@@ -65,11 +65,11 @@ SELECT pubname, puballtables FROM pg_publication WHERE pubname = 'testpub_forall
(1 row)
\d+ testpub_tbl2
- Table "public.testpub_tbl2"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('testpub_tbl2_id_seq'::regclass) | plain | |
- data | text | | | | extended | |
+ Table "public.testpub_tbl2"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('testpub_tbl2_id_seq'::regclass) | plain | | |
+ data | text | | | | extended | | |
Indexes:
"testpub_tbl2_pkey" PRIMARY KEY, btree (id)
Publications:
@@ -141,22 +141,22 @@ ALTER PUBLICATION testpub_default SET TABLE testpub_tbl1;
ALTER PUBLICATION testpub_default ADD TABLE pub_test.testpub_nopk;
ALTER PUBLICATION testpib_ins_trunct ADD TABLE pub_test.testpub_nopk, testpub_tbl1;
\d+ pub_test.testpub_nopk
- Table "pub_test.testpub_nopk"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- foo | integer | | | | plain | |
- bar | integer | | | | plain | |
+ Table "pub_test.testpub_nopk"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ foo | integer | | | | plain | | |
+ bar | integer | | | | plain | | |
Publications:
"testpib_ins_trunct"
"testpub_default"
"testpub_fortbl"
\d+ testpub_tbl1
- Table "public.testpub_tbl1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | |
- data | text | | | | extended | |
+ Table "public.testpub_tbl1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | | |
+ data | text | | | | extended | | |
Indexes:
"testpub_tbl1_pkey" PRIMARY KEY, btree (id)
Publications:
@@ -178,11 +178,11 @@ ALTER PUBLICATION testpub_default DROP TABLE testpub_tbl1, pub_test.testpub_nopk
ALTER PUBLICATION testpub_default DROP TABLE pub_test.testpub_nopk;
ERROR: relation "testpub_nopk" is not part of the publication
\d+ testpub_tbl1
- Table "public.testpub_tbl1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | |
- data | text | | | | extended | |
+ Table "public.testpub_tbl1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | | |
+ data | text | | | | extended | | |
Indexes:
"testpub_tbl1_pkey" PRIMARY KEY, btree (id)
Publications:
diff --git a/src/test/regress/expected/replica_identity.out b/src/test/regress/expected/replica_identity.out
index 67c34a92a4..1526437bf8 100644
--- a/src/test/regress/expected/replica_identity.out
+++ b/src/test/regress/expected/replica_identity.out
@@ -158,13 +158,13 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass;
(1 row)
\d+ test_replica_identity
- Table "public.test_replica_identity"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('test_replica_identity_id_seq'::regclass) | plain | |
- keya | text | | not null | | extended | |
- keyb | text | | not null | | extended | |
- nonkey | text | | | | extended | |
+ Table "public.test_replica_identity"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('test_replica_identity_id_seq'::regclass) | plain | | |
+ keya | text | | not null | | extended | | |
+ keyb | text | | not null | | extended | | |
+ nonkey | text | | | | extended | | |
Indexes:
"test_replica_identity_pkey" PRIMARY KEY, btree (id)
"test_replica_identity_expr" UNIQUE, btree (keya, keyb, (3))
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index f1c1b44d6f..e358ff539c 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2799,11 +2799,11 @@ select * from rules_log;
create rule r3 as on delete to rules_src do notify rules_src_deletion;
\d+ rules_src
- Table "public.rules_src"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- f1 | integer | | | | plain | |
- f2 | integer | | | | plain | |
+ Table "public.rules_src"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ f1 | integer | | | | plain | | |
+ f2 | integer | | | | plain | | |
Rules:
r1 AS
ON UPDATE TO rules_src DO INSERT INTO rules_log (f1, f2, tag) VALUES (old.f1,old.f2,'old'::text), (new.f1,new.f2,'new'::text)
@@ -2819,11 +2819,11 @@ Rules:
create rule r4 as on insert to rules_src do instead insert into rules_log AS trgt SELECT NEW.* RETURNING trgt.f1, trgt.f2;
create rule r5 as on update to rules_src do instead UPDATE rules_log AS trgt SET tag = 'updated' WHERE trgt.f1 = new.f1;
\d+ rules_src
- Table "public.rules_src"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- f1 | integer | | | | plain | |
- f2 | integer | | | | plain | |
+ Table "public.rules_src"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ f1 | integer | | | | plain | | |
+ f2 | integer | | | | plain | | |
Rules:
r1 AS
ON UPDATE TO rules_src DO INSERT INTO rules_log (f1, f2, tag) VALUES (old.f1,old.f2,'old'::text), (new.f1,new.f2,'new'::text)
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index e996640593..62b06da011 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -111,6 +111,8 @@ pg_authid|t
pg_cast|t
pg_class|t
pg_collation|t
+pg_compression|t
+pg_compression_opt|t
pg_constraint|t
pg_conversion|t
pg_database|t
diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out
index a4fe96112e..adbe764196 100644
--- a/src/test/regress/expected/update.out
+++ b/src/test/regress/expected/update.out
@@ -221,11 +221,11 @@ update range_parted set b = b + 1 where b = 10;
-- Creating default partition for range
create table part_def partition of range_parted default;
\d+ part_def
- Table "public.part_def"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.part_def"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: range_parted DEFAULT
Partition constraint: (NOT (((a = 'a'::text) AND (b >= 1) AND (b < 10)) OR ((a = 'a'::text) AND (b >= 10) AND (b < 20)) OR ((a = 'b'::text) AND (b >= 1) AND (b < 10)) OR ((a = 'b'::text) AND (b >= 10) AND (b < 20))))
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index aa5e6af621..a44cf1c910 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -30,7 +30,7 @@ test: point lseg line box path polygon circle date time timetz timestamp timesta
# geometry depends on point, lseg, box, path, polygon and circle
# horology depends on interval, timetz, timestamp, timestamptz, reltime and abstime
# ----------
-test: geometry horology regex oidjoins type_sanity opr_sanity misc_sanity comments expressions
+test: geometry horology regex oidjoins type_sanity opr_sanity misc_sanity comments expressions create_cm
# ----------
# These four each depend on the previous one
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 3866314a92..5c72cb16f5 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -43,6 +43,7 @@ test: inet
test: macaddr
test: macaddr8
test: tstypes
+test: create_cm
test: geometry
test: horology
test: regex
diff --git a/src/test/regress/sql/create_cm.sql b/src/test/regress/sql/create_cm.sql
new file mode 100644
index 0000000000..f4701a1dbf
--- /dev/null
+++ b/src/test/regress/sql/create_cm.sql
@@ -0,0 +1,58 @@
+-- test drop
+CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
+CREATE TABLE droptest(fts tsvector COMPRESSED ts1);
+DROP COMPRESSION METHOD ts1;
+DROP COMPRESSION METHOD ts1 CASCADE;
+\d+ droptest
+DROP TABLE droptest;
+
+CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
+CREATE TABLE cmtest(fts tsvector COMPRESSED ts1);
+SELECT * FROM pg_compression;
+SELECT cmhandler, cmoptions FROM pg_compression_opt;
+
+\dCM
+\d+ cmtest
+
+INSERT INTO cmtest
+ SELECT to_tsvector(string_agg(repeat(substr(i::text,1,1), i), ' '))
+ FROM generate_series(1,200) i;
+
+SELECT length(fts) FROM cmtest;
+SELECT length(fts) FROM cmtest;
+
+-- check ALTER commands
+ALTER TABLE cmtest ALTER COLUMN fts SET NOT COMPRESSED;
+\d+ cmtest
+ALTER TABLE cmtest ALTER COLUMN fts SET COMPRESSED ts1 WITH (format 'lz');
+ALTER TABLE cmtest ALTER COLUMN fts SET COMPRESSED ts1;
+\d+ cmtest
+
+-- create different types of tables
+SELECT * INTO cmtest2 FROM cmtest;
+CREATE TABLE cmtest3 (LIKE cmtest);
+CREATE TABLE cmtest4(fts tsvector, a int) INHERITS (cmtest);
+CREATE TABLE cmtest5(fts tsvector);
+
+-- we update usual datum with compressed datum
+INSERT INTO cmtest5
+ SELECT to_tsvector(string_agg(repeat(substr(i::text,1,1), i), ' '))
+ FROM generate_series(1,200) i;
+UPDATE cmtest5 SET fts = cmtest.fts FROM cmtest;
+
+\d+ cmtest3
+\d+ cmtest4
+DROP TABLE cmtest CASCADE;
+DROP TABLE cmtest3;
+
+SELECT cmhandler, cmoptions FROM pg_compression_opt;
+
+DROP COMPRESSION METHOD ts1 CASCADE;
+SELECT * FROM pg_compression;
+SELECT * FROM pg_compression_opt;
+
+-- check that moved tuples still can be decompressed
+SELECT length(fts) FROM cmtest2;
+SELECT length(fts) FROM cmtest5;
+DROP TABLE cmtest2;
+DROP TABLE cmtest5;
Hi,
On 11/21/2017 03:47 PM, Ildus Kurbangaliev wrote:
On Mon, 20 Nov 2017 00:04:53 +0100
Tomas Vondra <tomas.vondra@2ndquadrant.com> wrote:...
6) I'm rather confused by AttributeCompression vs.
ColumnCompression. I mean, attribute==column, right? Of course, one
is for data from parser, the other one is for internal info. But
can we make the naming clearer?For now I have renamed AttributeCompression to CompressionOptions,
not sure that's a good name but at least it gives less confusion.
I propose to use either
CompressionMethodOptions (and CompressionMethodRoutine)
or
CompressionOptions (and CompressionRoutine)
7) The docs in general are somewhat unsatisfactory, TBH. For example
the ColumnCompression has no comments, unlike everything else in
parsenodes. Similarly for the SGML docs - I suggest to expand them to
resemble FDW docs
(https://www.postgresql.org/docs/10/static/fdwhandler.html) which
also follows the handler/routines pattern.I've added more comments. I think I'll add more documentation if the
committers will approve current syntax.
OK. Haven't reviewed this yet.
8) One of the unclear things if why we even need 'drop' routing. It
seems that if it's defined DropAttributeCompression does something.
But what should it do? I suppose dropping the options should be done
using dependencies (just like we drop columns in this case).BTW why does DropAttributeCompression mess with att->attisdropped in
this way? That seems a bit odd.'drop' routine could be useful. An extension could do something
related with the attribute, like remove extra tables or something
else. The compression options will not be removed after unlinking
compression method from a column because there is still be stored
compressed data in that column.
OK. So something like a "global" dictionary used for the column, or
something like that? Sure, seems useful and I've been thinking about
that, but I think we badly need some extension using that, even if in a
very simple way. Firstly, we need a "how to" example, secondly we need
some way to test it.
13) When writing the experimental extension, I was extremely
confused about the regular varlena headers, custom compression
headers, etc. In the end I stole the code from tsvector.c and
whacked it a bit until it worked, but I wouldn't dare to claim I
understand how it works.This needs to be documented somewhere. For example postgres.h has
a bunch of paragraphs about varlena headers, so perhaps it should
be there? I see the patch tweaks some of the constants, but does
not update the comment at all.This point is good, I'm not sure how this documentation should look
like. I've just assumed that people should have deep undestanding of
varlenas if they're going to compress them. But now it's easy to
make mistake there. Maybe I should add some functions that help to
construct varlena, with different headers. I like the way is how
jsonb is constructed. It uses StringInfo and there are few helper
functions (reserveFromBuffer, appendToBuffer and others). Maybe they
should be not static.
Not sure. My main problem was not understanding how this affects the
varlena header, etc. And I had no idea where to look.
Perhaps it would be useful to provide some additional macros
making access to custom-compressed varlena values easier. Or
perhaps the VARSIZE_ANY / VARSIZE_ANY_EXHDR / VARDATA_ANY already
support that? This part is not very clear to me.These macros will work, custom compressed varlenas behave like old
compressed varlenas.
OK. But then I don't understand why tsvector.c does things like
VARSIZE(data) - VARHDRSZ_CUSTOM_COMPRESSED - arrsize
VARRAWSIZE_4B_C(data) - arrsize
instead of
VARSIZE_ANY_EXHDR(data) - arrsize
VARSIZE_ANY(data) - arrsize
Seems somewhat confusing.
Still it's a problem if the user used for example `SELECT
<compressed_column> INTO * FROM *` because postgres will copy
compressed tuples, and there will not be any dependencies
between destination and the options.This seems like a rather fatal design flaw, though. I'd say we need
to force recompression of the data, in such cases. Otherwise all
the dependency tracking is rather pointless.Fixed this problem too. I've added recompression for datum that use
custom compression.
Hmmm, it still doesn't work for me. See this:
test=# create extension pg_lz4 ;
CREATE EXTENSION
test=# create table t_lz4 (v text compressed lz4);
CREATE TABLE
test=# create table t_pglz (v text);
CREATE TABLE
test=# insert into t_lz4 select repeat(md5(1::text),300);
INSERT 0 1
test=# insert into t_pglz select * from t_lz4;
INSERT 0 1
test=# drop extension pg_lz4 cascade;
NOTICE: drop cascades to 2 other objects
DETAIL: drop cascades to compression options for lz4
drop cascades to table t_lz4 column v
DROP EXTENSION
test=# \c test
You are now connected to database "test" as user "user".
test=# insert into t_lz4 select repeat(md5(1::text),300);^C
test=# select * from t_pglz ;
ERROR: cache lookup failed for compression options 16419
That suggests no recompression happened.
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Tue, 21 Nov 2017 18:47:49 +0100
Tomas Vondra <tomas.vondra@2ndquadrant.com> wrote:
I propose to use either
CompressionMethodOptions (and CompressionMethodRoutine)
or
CompressionOptions (and CompressionRoutine)
Sounds good, thanks.
OK. But then I don't understand why tsvector.c does things like
VARSIZE(data) - VARHDRSZ_CUSTOM_COMPRESSED - arrsize
VARRAWSIZE_4B_C(data) - arrsizeinstead of
VARSIZE_ANY_EXHDR(data) - arrsize
VARSIZE_ANY(data) - arrsizeSeems somewhat confusing.
VARRAWSIZE_4B_C returns original size of data, before compression (from
va_rawsize in current postgres, and from va_info in my patch), not size
of the already compressed data, so you can't use VARSIZE_ANY here.
VARSIZE_ANY_EXHDR in current postgres returns VARSIZE-VARHDRSZ, despite
the varlena is compressed or not, so I just kept this behavior for
custom compressed varlenas too. If you look into tuptoaster.c you will
also see lines like 'VARSIZE(attr) - TOAST_COMPRESS_HDRSZ'. So I think
if VARSIZE_ANY_EXHDR will subtract different header sizes then it
should subtract them for usual compressed varlenas too.
Hmmm, it still doesn't work for me. See this:
test=# create extension pg_lz4 ;
CREATE EXTENSION
test=# create table t_lz4 (v text compressed lz4);
CREATE TABLE
test=# create table t_pglz (v text);
CREATE TABLE
test=# insert into t_lz4 select repeat(md5(1::text),300);
INSERT 0 1
test=# insert into t_pglz select * from t_lz4;
INSERT 0 1
test=# drop extension pg_lz4 cascade;
NOTICE: drop cascades to 2 other objects
DETAIL: drop cascades to compression options for lz4
drop cascades to table t_lz4 column v
DROP EXTENSION
test=# \c test
You are now connected to database "test" as user "user".
test=# insert into t_lz4 select repeat(md5(1::text),300);^C
test=# select * from t_pglz ;
ERROR: cache lookup failed for compression options 16419That suggests no recompression happened.
I will check that. Is your extension published somewhere?
On 11/21/2017 09:28 PM, Ildus K wrote:
Hmmm, it still doesn't work for me. See this:
test=# create extension pg_lz4 ;
CREATE EXTENSION
test=# create table t_lz4 (v text compressed lz4);
CREATE TABLE
test=# create table t_pglz (v text);
CREATE TABLE
test=# insert into t_lz4 select repeat(md5(1::text),300);
INSERT 0 1
test=# insert into t_pglz select * from t_lz4;
INSERT 0 1
test=# drop extension pg_lz4 cascade;
NOTICE: drop cascades to 2 other objects
DETAIL: drop cascades to compression options for lz4
drop cascades to table t_lz4 column v
DROP EXTENSION
test=# \c test
You are now connected to database "test" as user "user".
test=# insert into t_lz4 select repeat(md5(1::text),300);^C
test=# select * from t_pglz ;
ERROR: cache lookup failed for compression options 16419That suggests no recompression happened.
I will check that. Is your extension published somewhere?
No, it was just an experiment, so I've only attached it to the initial
review. Attached is an updated version, with a fix or two.
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
On Tue, 21 Nov 2017 18:47:49 +0100
Tomas Vondra <tomas.vondra@2ndquadrant.com> wrote:
Hmmm, it still doesn't work for me. See this:
test=# create extension pg_lz4 ;
CREATE EXTENSION
test=# create table t_lz4 (v text compressed lz4);
CREATE TABLE
test=# create table t_pglz (v text);
CREATE TABLE
test=# insert into t_lz4 select repeat(md5(1::text),300);
INSERT 0 1
test=# insert into t_pglz select * from t_lz4;
INSERT 0 1
test=# drop extension pg_lz4 cascade;
NOTICE: drop cascades to 2 other objects
DETAIL: drop cascades to compression options for lz4
drop cascades to table t_lz4 column v
DROP EXTENSION
test=# \c test
You are now connected to database "test" as user "user".
test=# insert into t_lz4 select repeat(md5(1::text),300);^C
test=# select * from t_pglz ;
ERROR: cache lookup failed for compression options 16419That suggests no recompression happened.
Should be fixed in the attached patch. I've changed your extension a
little bit according changes in the new patch (also in attachments).
Also I renamed few functions, added more comments and simplified the
code related with DefineRelation (thanks to Ildar Musin suggestion).
--
---
Ildus Kurbangaliev
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company
Attachments:
custom_compression_methods_v6.patchtext/x-patchDownload
diff --git a/contrib/test_decoding/expected/ddl.out b/contrib/test_decoding/expected/ddl.out
index 1e22c1eefc..766ced401f 100644
--- a/contrib/test_decoding/expected/ddl.out
+++ b/contrib/test_decoding/expected/ddl.out
@@ -416,12 +416,12 @@ CREATE TABLE replication_metadata (
WITH (user_catalog_table = true)
;
\d+ replication_metadata
- Table "public.replication_metadata"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------+---------+-----------+----------+--------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | |
- relation | name | | not null | | plain | |
- options | text[] | | | | extended | |
+ Table "public.replication_metadata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+----------+---------+-----------+----------+--------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | |
+ relation | name | | not null | | plain | | |
+ options | text[] | | | | extended | | |
Indexes:
"replication_metadata_pkey" PRIMARY KEY, btree (id)
Options: user_catalog_table=true
@@ -430,12 +430,12 @@ INSERT INTO replication_metadata(relation, options)
VALUES ('foo', ARRAY['a', 'b']);
ALTER TABLE replication_metadata RESET (user_catalog_table);
\d+ replication_metadata
- Table "public.replication_metadata"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------+---------+-----------+----------+--------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | |
- relation | name | | not null | | plain | |
- options | text[] | | | | extended | |
+ Table "public.replication_metadata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+----------+---------+-----------+----------+--------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | |
+ relation | name | | not null | | plain | | |
+ options | text[] | | | | extended | | |
Indexes:
"replication_metadata_pkey" PRIMARY KEY, btree (id)
@@ -443,12 +443,12 @@ INSERT INTO replication_metadata(relation, options)
VALUES ('bar', ARRAY['a', 'b']);
ALTER TABLE replication_metadata SET (user_catalog_table = true);
\d+ replication_metadata
- Table "public.replication_metadata"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------+---------+-----------+----------+--------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | |
- relation | name | | not null | | plain | |
- options | text[] | | | | extended | |
+ Table "public.replication_metadata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+----------+---------+-----------+----------+--------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | |
+ relation | name | | not null | | plain | | |
+ options | text[] | | | | extended | | |
Indexes:
"replication_metadata_pkey" PRIMARY KEY, btree (id)
Options: user_catalog_table=true
@@ -461,13 +461,13 @@ ALTER TABLE replication_metadata ALTER COLUMN rewritemeornot TYPE text;
ERROR: cannot rewrite table "replication_metadata" used as a catalog table
ALTER TABLE replication_metadata SET (user_catalog_table = false);
\d+ replication_metadata
- Table "public.replication_metadata"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------------+---------+-----------+----------+--------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | |
- relation | name | | not null | | plain | |
- options | text[] | | | | extended | |
- rewritemeornot | integer | | | | plain | |
+ Table "public.replication_metadata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+----------------+---------+-----------+----------+--------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | |
+ relation | name | | not null | | plain | | |
+ options | text[] | | | | extended | | |
+ rewritemeornot | integer | | | | plain | | |
Indexes:
"replication_metadata_pkey" PRIMARY KEY, btree (id)
Options: user_catalog_table=false
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index 01acc2ef9d..f43f09cc19 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -59,6 +59,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY createAggregate SYSTEM "create_aggregate.sgml">
<!ENTITY createCast SYSTEM "create_cast.sgml">
<!ENTITY createCollation SYSTEM "create_collation.sgml">
+<!ENTITY createCompressionMethod SYSTEM "create_compression_method.sgml">
<!ENTITY createConversion SYSTEM "create_conversion.sgml">
<!ENTITY createDatabase SYSTEM "create_database.sgml">
<!ENTITY createDomain SYSTEM "create_domain.sgml">
diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index 92db00f52d..207d6cf26e 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -53,6 +53,8 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET ( <replaceable class="parameter">attribute_option</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] )
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> RESET ( <replaceable class="parameter">attribute_option</replaceable> [, ... ] )
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN }
+ ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET COMPRESSED <replaceable class="parameter">compression_method_name</replaceable> [ WITH (<replaceable class="parameter">compression_method_options</replaceable>) ]
+ ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET NOT COMPRESSED
ADD <replaceable class="parameter">table_constraint</replaceable> [ NOT VALID ]
ADD <replaceable class="parameter">table_constraint_using_index</replaceable>
ALTER CONSTRAINT <replaceable class="parameter">constraint_name</replaceable> [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -320,6 +322,34 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>
+ <literal>SET COMPRESSED <replaceable class="parameter">compression_method_name</replaceable></literal>
+ </term>
+ <listitem>
+ <para>
+ This form adds compression to a column. Compression method should be
+ created with <xref linkend="SQL-CREATECOMPRESSIONMETHOD">. If compression
+ method has options they could be specified with <literal>WITH</literal>
+ parameter. Setting a compression method doesn't change anything in the
+ table and affects only future table updates.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <literal>SET NOT COMPRESSED</literal>
+ </term>
+ <listitem>
+ <para>
+ This form removes compression from a column. Removing compresssion from
+ a column doesn't change already compressed tuples and affects only future
+ table updates.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>ADD <replaceable class="parameter">table_constraint</replaceable> [ NOT VALID ]</literal></term>
<listitem>
diff --git a/doc/src/sgml/ref/create_compression_method.sgml b/doc/src/sgml/ref/create_compression_method.sgml
new file mode 100644
index 0000000000..663010ecd9
--- /dev/null
+++ b/doc/src/sgml/ref/create_compression_method.sgml
@@ -0,0 +1,50 @@
+<!--
+doc/src/sgml/ref/create_compression_method.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-CREATECOMPRESSIONMETHOD">
+ <indexterm zone="sql-createcompressionmethod">
+ <primary>CREATE COMPRESSION METHOD</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>CREATE COMPRESSION METHOD</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>CREATE COMPRESSION METHOD</refname>
+ <refpurpose>define a new compression method</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+CREATE COMPRESSION METHOD <replaceable class="parameter">compression_method_name</replaceable>
+ HANDLER <replaceable class="parameter">compression_method_handler</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>CREATE COMPRESSION METHOD</command> creates a new compression method
+ with <replaceable class="parameter">compression_method_name</replaceable>.
+ </para>
+
+ <para>
+ A compression method links a name with a compression handler. And the
+ handler is a special function that returns collection of methods that
+ can be used for compression.
+ </para>
+
+ <para>
+ After a compression method is created, you can specify it in
+ <xref linkend="SQL-CREATETABLE"> or <xref linkend="SQL-ALTERTABLE">
+ statements.
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 3bc155a775..5e0c06421a 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -65,6 +65,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( <replaceable>sequence_options</replaceable> ) ] |
UNIQUE <replaceable class="parameter">index_parameters</replaceable> |
PRIMARY KEY <replaceable class="parameter">index_parameters</replaceable> |
+ COMPRESSED <replaceable class="parameter">compression_method_name</replaceable> [ WITH (<replaceable class="parameter">compression_method_options</replaceable>) ] |
REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -817,6 +818,18 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>COMPRESSED <replaceable class="parameter">compression_method_name</replaceable> [ WITH (<replaceable class="parameter">compression_method_options</replaceable>) ]</literal></term>
+ <listitem>
+ <para>
+ This clause adds compression to a column. Compression method should be
+ created with <xref linkend="SQL-CREATECOMPRESSIONMETHOD">. If compression
+ method has options they could be specified with <literal>WITH</literal>
+ parameter.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="sql-createtable-exclude">
<term><literal>EXCLUDE [ USING <replaceable class="parameter">index_method</replaceable> ] ( <replaceable class="parameter">exclude_element</replaceable> WITH <replaceable class="parameter">operator</replaceable> [, ... ] ) <replaceable class="parameter">index_parameters</replaceable> [ WHERE ( <replaceable class="parameter">predicate</replaceable> ) ]</literal></term>
<listitem>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index 9000b3aaaa..cc0bd70be3 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -87,6 +87,7 @@
&createAggregate;
&createCast;
&createCollation;
+ &createCompressionMethod;
&createConversion;
&createDatabase;
&createDomain;
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index 5c035fb203..0c6cf937f8 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -96,6 +96,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
int keyno;
int idxattno;
uint16 phony_infomask = 0;
+ uint16 phony_infomask2 = 0;
bits8 *phony_nullbitmap;
Size len,
hoff,
@@ -187,6 +188,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
(char *) rettuple + hoff,
data_len,
&phony_infomask,
+ &phony_infomask2,
phony_nullbitmap);
/* done with these */
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index a1a9d9905b..534c11edaa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -145,7 +145,7 @@ void
heap_fill_tuple(TupleDesc tupleDesc,
Datum *values, bool *isnull,
char *data, Size data_size,
- uint16 *infomask, bits8 *bit)
+ uint16 *infomask, uint16 *infomask2, bits8 *bit)
{
bits8 *bitP;
int bitmask;
@@ -230,10 +230,20 @@ heap_fill_tuple(TupleDesc tupleDesc,
}
else
{
+
*infomask |= HEAP_HASEXTERNAL;
/* no alignment, since it's short by definition */
data_length = VARSIZE_EXTERNAL(val);
memcpy(data, val, data_length);
+
+ if (VARATT_IS_EXTERNAL_ONDISK(val))
+ {
+ struct varatt_external toast_pointer;
+
+ VARATT_EXTERNAL_GET_POINTER(toast_pointer, val);
+ if (VARATT_EXTERNAL_IS_CUSTOM_COMPRESSED(toast_pointer))
+ *infomask2 |= HEAP_HASCUSTOMCOMPRESSED;
+ }
}
}
else if (VARATT_IS_SHORT(val))
@@ -257,6 +267,9 @@ heap_fill_tuple(TupleDesc tupleDesc,
att->attalign);
data_length = VARSIZE(val);
memcpy(data, val, data_length);
+
+ if (VARATT_IS_CUSTOM_COMPRESSED(val))
+ *infomask2 |= HEAP_HASCUSTOMCOMPRESSED;
}
}
else if (att->attlen == -2)
@@ -774,6 +787,7 @@ heap_form_tuple(TupleDesc tupleDescriptor,
(char *) td + hoff,
data_len,
&td->t_infomask,
+ &td->t_infomask2,
(hasnull ? td->t_bits : NULL));
return tuple;
@@ -1456,6 +1470,7 @@ heap_form_minimal_tuple(TupleDesc tupleDescriptor,
(char *) tuple + hoff,
data_len,
&tuple->t_infomask,
+ &tuple->t_infomask2,
(hasnull ? tuple->t_bits : NULL));
return tuple;
diff --git a/src/backend/access/common/indextuple.c b/src/backend/access/common/indextuple.c
index 138671410a..e5299d3094 100644
--- a/src/backend/access/common/indextuple.c
+++ b/src/backend/access/common/indextuple.c
@@ -47,6 +47,7 @@ index_form_tuple(TupleDesc tupleDescriptor,
unsigned short infomask = 0;
bool hasnull = false;
uint16 tupmask = 0;
+ uint16 tupmask2 = 0;
int numberOfAttributes = tupleDescriptor->natts;
#ifdef TOAST_INDEX_HACK
@@ -74,13 +75,30 @@ index_form_tuple(TupleDesc tupleDescriptor,
/*
* If value is stored EXTERNAL, must fetch it so we are not depending
- * on outside storage. This should be improved someday.
+ * on outside storage. This should be improved someday. If value also
+ * was compressed by custom compression method then we should
+ * decompress it too.
*/
if (VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+ {
+ struct varatt_external toast_pointer;
+
+ VARATT_EXTERNAL_GET_POINTER(toast_pointer, DatumGetPointer(values[i]));
+ if (VARATT_EXTERNAL_IS_CUSTOM_COMPRESSED(toast_pointer))
+ untoasted_values[i] =
+ PointerGetDatum(heap_tuple_untoast_attr((struct varlena *)
+ DatumGetPointer(values[i])));
+ else
+ untoasted_values[i] =
+ PointerGetDatum(heap_tuple_fetch_attr((struct varlena *)
+ DatumGetPointer(values[i])));
+ untoasted_free[i] = true;
+ }
+ else if (VARATT_IS_CUSTOM_COMPRESSED(DatumGetPointer(values[i])))
{
untoasted_values[i] =
- PointerGetDatum(heap_tuple_fetch_attr((struct varlena *)
- DatumGetPointer(values[i])));
+ PointerGetDatum(heap_tuple_untoast_attr((struct varlena *)
+ DatumGetPointer(values[i])));
untoasted_free[i] = true;
}
@@ -92,7 +110,7 @@ index_form_tuple(TupleDesc tupleDescriptor,
VARSIZE(DatumGetPointer(untoasted_values[i])) > TOAST_INDEX_TARGET &&
(att->attstorage == 'x' || att->attstorage == 'm'))
{
- Datum cvalue = toast_compress_datum(untoasted_values[i]);
+ Datum cvalue = toast_compress_datum(untoasted_values[i], InvalidOid);
if (DatumGetPointer(cvalue) != NULL)
{
@@ -142,6 +160,7 @@ index_form_tuple(TupleDesc tupleDescriptor,
(char *) tp + hoff,
data_size,
&tupmask,
+ &tupmask2,
(hasnull ? (bits8 *) tp + sizeof(IndexTupleData) : NULL));
#ifdef TOAST_INDEX_HACK
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index aa9c0f1bb9..f740ce4304 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -945,11 +945,31 @@ untransformRelOptions(Datum options)
val = (Node *) makeString(pstrdup(p));
}
result = lappend(result, makeDefElem(pstrdup(s), val, -1));
+ pfree(s);
}
return result;
}
+char *
+formatRelOptions(List *options)
+{
+ StringInfoData buf;
+ ListCell *cell;
+
+ initStringInfo(&buf);
+
+ foreach(cell, options)
+ {
+ DefElem *def = (DefElem *) lfirst(cell);
+
+ appendStringInfo(&buf, "%s%s=%s", buf.len > 0 ? ", " : "",
+ def->defname, defGetString(def));
+ }
+
+ return buf.data;
+}
+
/*
* Extract and parse reloptions from a pg_class tuple.
*
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index 9e37ca73a8..d206cce18e 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -19,8 +19,10 @@
#include "postgres.h"
+#include "access/compression.h"
#include "access/hash.h"
#include "access/htup_details.h"
+#include "access/reloptions.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
@@ -242,6 +244,7 @@ TupleDescCopyEntry(TupleDesc dst, AttrNumber dstAttno,
dstAtt->attnotnull = false;
dstAtt->atthasdef = false;
dstAtt->attidentity = '\0';
+ dstAtt->attcompression = InvalidOid;
}
/*
@@ -396,6 +399,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
return false;
if (attr1->attcollation != attr2->attcollation)
return false;
+ if (attr1->attcompression != attr2->attcompression)
+ return false;
/* attacl, attoptions and attfdwoptions are not even present... */
}
@@ -458,6 +463,7 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
}
else if (tupdesc2->constr != NULL)
return false;
+
return true;
}
@@ -563,6 +569,7 @@ TupleDescInitEntry(TupleDesc desc,
att->attalign = typeForm->typalign;
att->attstorage = typeForm->typstorage;
att->attcollation = typeForm->typcollation;
+ att->attcompression = InvalidOid;
ReleaseSysCache(tuple);
}
@@ -675,7 +682,6 @@ TupleDescInitEntryCollation(TupleDesc desc,
TupleDescAttr(desc, attributeNumber - 1)->attcollation = collationid;
}
-
/*
* BuildDescForRelation
*
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 3acef279f4..af4c7b2e13 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2656,7 +2656,8 @@ heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
Assert(!HeapTupleHasExternal(tup));
return tup;
}
- else if (HeapTupleHasExternal(tup) || tup->t_len > TOAST_TUPLE_THRESHOLD)
+ else if (HeapTupleHasExternal(tup) || HeapTupleHasCustomCompressed(tup)
+ || tup->t_len > TOAST_TUPLE_THRESHOLD)
return toast_insert_or_update(relation, tup, NULL, options);
else
return tup;
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index c74945a52a..4dd87a5d8f 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -30,8 +30,10 @@
#include <unistd.h>
#include <fcntl.h>
+#include "access/compression.h"
#include "access/genam.h"
#include "access/heapam.h"
+#include "access/reloptions.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -39,8 +41,11 @@
#include "miscadmin.h"
#include "utils/expandeddatum.h"
#include "utils/fmgroids.h"
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
+#include "utils/syscache.h"
#include "utils/typcache.h"
#include "utils/tqual.h"
@@ -53,19 +58,52 @@
typedef struct toast_compress_header
{
int32 vl_len_; /* varlena header (do not touch directly!) */
- int32 rawsize;
+ uint32 info; /* flags (2 bits) and rawsize */
} toast_compress_header;
+/*
+ * If the compression method were used, then data also contains
+ * Oid of compression options
+ */
+typedef struct toast_compress_header_custom
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ uint32 info; /* flags (2 high bits) and rawsize */
+ Oid cmoptoid; /* Oid from pg_compression_opt */
+} toast_compress_header_custom;
+
+static HTAB *compression_options_cache = NULL;
+static MemoryContext compression_options_mcxt = NULL;
+
+#define RAWSIZEMASK (0x3FFFFFFFU)
+
/*
* Utilities for manipulation of header information for compressed
* toast entries.
*/
-#define TOAST_COMPRESS_HDRSZ ((int32) sizeof(toast_compress_header))
-#define TOAST_COMPRESS_RAWSIZE(ptr) (((toast_compress_header *) (ptr))->rawsize)
+#define TOAST_COMPRESS_HDRSZ ((int32) sizeof(toast_compress_header))
+#define TOAST_COMPRESS_HDRSZ_CUSTOM ((int32) sizeof(toast_compress_header_custom))
+#define TOAST_COMPRESS_RAWSIZE(ptr) (((toast_compress_header *) (ptr))->info & RAWSIZEMASK)
#define TOAST_COMPRESS_RAWDATA(ptr) \
(((char *) (ptr)) + TOAST_COMPRESS_HDRSZ)
#define TOAST_COMPRESS_SET_RAWSIZE(ptr, len) \
- (((toast_compress_header *) (ptr))->rawsize = (len))
+do { \
+ ((toast_compress_header *) (ptr))->info &= 0xC0000000; \
+ ((toast_compress_header *) (ptr))->info |= ((uint32)(len) & RAWSIZEMASK); \
+} while (0)
+#define TOAST_COMPRESS_SET_CMOPTOID(ptr, oid) \
+ (((toast_compress_header_custom *) (ptr))->cmoptoid = (oid))
+#define TOAST_COMPRESS_SET_CUSTOM(ptr) \
+do { \
+ (((toast_compress_header *) (ptr))->info |= (1 << 31)); \
+ (((toast_compress_header *) (ptr))->info &= ~(1 << 30)); \
+} while (0)
+
+#define VARATT_EXTERNAL_SET_CUSTOM(toast_pointer) \
+do { \
+ ((toast_pointer).va_extinfo |= (1 << 31)); \
+ ((toast_pointer).va_extinfo &= ~(1 << 30)); \
+} while (0)
static void toast_delete_datum(Relation rel, Datum value, bool is_speculative);
static Datum toast_save_datum(Relation rel, Datum value,
@@ -83,6 +121,8 @@ static int toast_open_indexes(Relation toastrel,
static void toast_close_indexes(Relation *toastidxs, int num_indexes,
LOCKMODE lock);
static void init_toast_snapshot(Snapshot toast_snapshot);
+static void init_compression_options_cache(void);
+static CompressionMethodOptions *get_cached_compression_options(Oid cmoptoid);
/* ----------
@@ -421,7 +461,7 @@ toast_datum_size(Datum value)
struct varatt_external toast_pointer;
VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
- result = toast_pointer.va_extsize;
+ result = VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer);
}
else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
{
@@ -686,8 +726,32 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
toast_oldexternal[i] = new_value;
if (att->attstorage == 'p')
new_value = heap_tuple_untoast_attr(new_value);
+ else if (VARATT_IS_EXTERNAL_ONDISK(new_value))
+ {
+ struct varatt_external toast_pointer;
+
+ VARATT_EXTERNAL_GET_POINTER(toast_pointer, new_value);
+
+ /*
+ * If we're trying to insert a custom compressed datum we
+ * should decompress it first.
+ */
+ if (VARATT_EXTERNAL_IS_CUSTOM_COMPRESSED(toast_pointer))
+ new_value = heap_tuple_untoast_attr(new_value);
+ else
+ new_value = heap_tuple_fetch_attr(new_value);
+ }
else
new_value = heap_tuple_fetch_attr(new_value);
+
+ toast_values[i] = PointerGetDatum(new_value);
+ toast_free[i] = true;
+ need_change = true;
+ need_free = true;
+ }
+ else if (VARATT_IS_CUSTOM_COMPRESSED(new_value))
+ {
+ new_value = heap_tuple_untoast_attr(new_value);
toast_values[i] = PointerGetDatum(new_value);
toast_free[i] = true;
need_change = true;
@@ -741,12 +805,14 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
Datum old_value;
Datum new_value;
+ Form_pg_attribute att;
+
/*
* Search for the biggest yet unprocessed internal attribute
*/
for (i = 0; i < numAttrs; i++)
{
- Form_pg_attribute att = TupleDescAttr(tupleDesc, i);
+ char attstorage;
if (toast_action[i] != ' ')
continue;
@@ -754,7 +820,9 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
continue; /* can't happen, toast_action would be 'p' */
if (VARATT_IS_COMPRESSED(DatumGetPointer(toast_values[i])))
continue;
- if (att->attstorage != 'x' && att->attstorage != 'e')
+
+ attstorage = (TupleDescAttr(tupleDesc, i))->attstorage;
+ if (attstorage != 'x' && attstorage != 'e')
continue;
if (toast_sizes[i] > biggest_size)
{
@@ -770,10 +838,11 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
* Attempt to compress it inline, if it has attstorage 'x'
*/
i = biggest_attno;
- if (TupleDescAttr(tupleDesc, i)->attstorage == 'x')
+ att = TupleDescAttr(tupleDesc, i);
+ if (att->attstorage == 'x')
{
old_value = toast_values[i];
- new_value = toast_compress_datum(old_value);
+ new_value = toast_compress_datum(old_value, att->attcompression);
if (DatumGetPointer(new_value) != NULL)
{
@@ -914,7 +983,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
*/
i = biggest_attno;
old_value = toast_values[i];
- new_value = toast_compress_datum(old_value);
+ new_value = toast_compress_datum(old_value,
+ TupleDescAttr(tupleDesc, i)->attcompression);
if (DatumGetPointer(new_value) != NULL)
{
@@ -1046,6 +1116,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
(char *) new_data + new_header_len,
new_data_len,
&(new_data->t_infomask),
+ &(new_data->t_infomask2),
has_nulls ? new_data->t_bits : NULL);
}
else
@@ -1274,6 +1345,7 @@ toast_flatten_tuple_to_datum(HeapTupleHeader tup,
(char *) new_data + new_header_len,
new_data_len,
&(new_data->t_infomask),
+ &(new_data->t_infomask2),
has_nulls ? new_data->t_bits : NULL);
/*
@@ -1353,7 +1425,6 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
return new_tuple;
}
-
/* ----------
* toast_compress_datum -
*
@@ -1368,41 +1439,57 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
* ----------
*/
Datum
-toast_compress_datum(Datum value)
+toast_compress_datum(Datum value, Oid cmoptoid)
{
- struct varlena *tmp;
- int32 valsize = VARSIZE_ANY_EXHDR(DatumGetPointer(value));
- int32 len;
+ struct varlena *tmp = NULL;
+ int32 valsize,
+ len = 0;
+ CompressionMethodOptions *cmoptions = NULL;
Assert(!VARATT_IS_EXTERNAL(DatumGetPointer(value)));
Assert(!VARATT_IS_COMPRESSED(DatumGetPointer(value)));
- /*
- * No point in wasting a palloc cycle if value size is out of the allowed
- * range for compression
- */
- if (valsize < PGLZ_strategy_default->min_input_size ||
- valsize > PGLZ_strategy_default->max_input_size)
- return PointerGetDatum(NULL);
+ if (OidIsValid(cmoptoid))
+ cmoptions = get_cached_compression_options(cmoptoid);
+
+ valsize = VARSIZE_ANY_EXHDR(DatumGetPointer(value));
- tmp = (struct varlena *) palloc(PGLZ_MAX_OUTPUT(valsize) +
- TOAST_COMPRESS_HDRSZ);
+ if (cmoptions)
+ {
+ tmp = cmoptions->routine->compress(cmoptions, (const struct varlena *) value);
+ if (!tmp)
+ return PointerGetDatum(NULL);
+ }
+ else
+ {
+ /*
+ * No point in wasting a palloc cycle if value size is out of the
+ * allowed range for compression
+ */
+ if (valsize < PGLZ_strategy_default->min_input_size ||
+ valsize > PGLZ_strategy_default->max_input_size)
+ return PointerGetDatum(NULL);
+
+ tmp = (struct varlena *) palloc(PGLZ_MAX_OUTPUT(valsize) +
+ TOAST_COMPRESS_HDRSZ);
+ len = pglz_compress(VARDATA_ANY(DatumGetPointer(value)),
+ valsize,
+ TOAST_COMPRESS_RAWDATA(tmp),
+ PGLZ_strategy_default);
+ }
/*
- * We recheck the actual size even if pglz_compress() reports success,
- * because it might be satisfied with having saved as little as one byte
- * in the compressed data --- which could turn into a net loss once you
- * consider header and alignment padding. Worst case, the compressed
- * format might require three padding bytes (plus header, which is
- * included in VARSIZE(tmp)), whereas the uncompressed format would take
- * only one header byte and no padding if the value is short enough. So
- * we insist on a savings of more than 2 bytes to ensure we have a gain.
+ * We recheck the actual size even if compression function reports
+ * success, because it might be satisfied with having saved as little as
+ * one byte in the compressed data --- which could turn into a net loss
+ * once you consider header and alignment padding. Worst case, the
+ * compressed format might require three padding bytes (plus header, which
+ * is included in VARSIZE(tmp)), whereas the uncompressed format would
+ * take only one header byte and no padding if the value is short enough.
+ * So we insist on a savings of more than 2 bytes to ensure we have a
+ * gain.
*/
- len = pglz_compress(VARDATA_ANY(DatumGetPointer(value)),
- valsize,
- TOAST_COMPRESS_RAWDATA(tmp),
- PGLZ_strategy_default);
- if (len >= 0 &&
+ if (!cmoptions && len >= 0 &&
len + TOAST_COMPRESS_HDRSZ < valsize - 2)
{
TOAST_COMPRESS_SET_RAWSIZE(tmp, valsize);
@@ -1410,10 +1497,20 @@ toast_compress_datum(Datum value)
/* successful compression */
return PointerGetDatum(tmp);
}
+ else if (cmoptions && VARSIZE(tmp) < valsize - 2)
+ {
+ TOAST_COMPRESS_SET_CUSTOM(tmp);
+ TOAST_COMPRESS_SET_RAWSIZE(tmp, valsize);
+ TOAST_COMPRESS_SET_CMOPTOID(tmp, cmoptions->cmoptoid);
+ /* successful compression */
+ return PointerGetDatum(tmp);
+ }
else
{
/* incompressible data */
- pfree(tmp);
+ if (tmp)
+ pfree(tmp);
+
return PointerGetDatum(NULL);
}
}
@@ -1510,19 +1607,20 @@ toast_save_datum(Relation rel, Datum value,
&num_indexes);
/*
- * Get the data pointer and length, and compute va_rawsize and va_extsize.
+ * Get the data pointer and length, and compute va_rawsize and va_extinfo.
*
* va_rawsize is the size of the equivalent fully uncompressed datum, so
* we have to adjust for short headers.
*
- * va_extsize is the actual size of the data payload in the toast records.
+ * va_extinfo contains the actual size of the data payload in the toast
+ * records.
*/
if (VARATT_IS_SHORT(dval))
{
data_p = VARDATA_SHORT(dval);
data_todo = VARSIZE_SHORT(dval) - VARHDRSZ_SHORT;
toast_pointer.va_rawsize = data_todo + VARHDRSZ; /* as if not short */
- toast_pointer.va_extsize = data_todo;
+ toast_pointer.va_extinfo = data_todo; /* no flags */
}
else if (VARATT_IS_COMPRESSED(dval))
{
@@ -1530,7 +1628,10 @@ toast_save_datum(Relation rel, Datum value,
data_todo = VARSIZE(dval) - VARHDRSZ;
/* rawsize in a compressed datum is just the size of the payload */
toast_pointer.va_rawsize = VARRAWSIZE_4B_C(dval) + VARHDRSZ;
- toast_pointer.va_extsize = data_todo;
+ toast_pointer.va_extinfo = data_todo;
+ if (VARATT_IS_CUSTOM_COMPRESSED(dval))
+ VARATT_EXTERNAL_SET_CUSTOM(toast_pointer);
+
/* Assert that the numbers look like it's compressed */
Assert(VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer));
}
@@ -1539,7 +1640,7 @@ toast_save_datum(Relation rel, Datum value,
data_p = VARDATA(dval);
data_todo = VARSIZE(dval) - VARHDRSZ;
toast_pointer.va_rawsize = VARSIZE(dval);
- toast_pointer.va_extsize = data_todo;
+ toast_pointer.va_extinfo = data_todo; /* no flags */
}
/*
@@ -1899,7 +2000,7 @@ toast_fetch_datum(struct varlena *attr)
/* Must copy to access aligned fields */
VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
- ressize = toast_pointer.va_extsize;
+ ressize = VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer);
numchunks = ((ressize - 1) / TOAST_MAX_CHUNK_SIZE) + 1;
result = (struct varlena *) palloc(ressize + VARHDRSZ);
@@ -2084,7 +2185,7 @@ toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset, int32 length)
*/
Assert(!VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer));
- attrsize = toast_pointer.va_extsize;
+ attrsize = VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer);
totalchunks = ((attrsize - 1) / TOAST_MAX_CHUNK_SIZE) + 1;
if (sliceoffset >= attrsize)
@@ -2280,15 +2381,26 @@ toast_decompress_datum(struct varlena *attr)
Assert(VARATT_IS_COMPRESSED(attr));
- result = (struct varlena *)
- palloc(TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
- SET_VARSIZE(result, TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
+ if (VARATT_IS_CUSTOM_COMPRESSED(attr))
+ {
+ CompressionMethodOptions *cmoptions;
+ toast_compress_header_custom *hdr;
- if (pglz_decompress(TOAST_COMPRESS_RAWDATA(attr),
- VARSIZE(attr) - TOAST_COMPRESS_HDRSZ,
- VARDATA(result),
- TOAST_COMPRESS_RAWSIZE(attr)) < 0)
- elog(ERROR, "compressed data is corrupted");
+ hdr = (toast_compress_header_custom *) attr;
+ cmoptions = get_cached_compression_options(hdr->cmoptoid);
+ result = cmoptions->routine->decompress(cmoptions, attr);
+ }
+ else
+ {
+ result = (struct varlena *)
+ palloc(TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
+ SET_VARSIZE(result, TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
+ if (pglz_decompress(TOAST_COMPRESS_RAWDATA(attr),
+ VARSIZE(attr) - TOAST_COMPRESS_HDRSZ,
+ VARDATA(result),
+ TOAST_COMPRESS_RAWSIZE(attr)) < 0)
+ elog(ERROR, "compressed data is corrupted");
+ }
return result;
}
@@ -2390,3 +2502,78 @@ init_toast_snapshot(Snapshot toast_snapshot)
InitToastSnapshot(*toast_snapshot, snapshot->lsn, snapshot->whenTaken);
}
+
+/* ----------
+ * init_compression_options_cache
+ *
+ * Initialize a local cache for compression options.
+ */
+static void
+init_compression_options_cache(void)
+{
+ HASHCTL ctl;
+
+ compression_options_mcxt = AllocSetContextCreate(TopMemoryContext,
+ "compression options cache context",
+ ALLOCSET_DEFAULT_SIZES);
+ MemSet(&ctl, 0, sizeof(ctl));
+ ctl.keysize = sizeof(Oid);
+ ctl.entrysize = sizeof(CompressionMethodOptions);
+ ctl.hcxt = compression_options_mcxt;
+ compression_options_cache = hash_create("compression options cache", 100, &ctl,
+ HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
+}
+
+/* ----------
+ * get_cached_compression_options
+ *
+ * Remove cached compression options from the local cache
+ */
+static inline void
+remove_cached_compression_options(Oid cmoptoid)
+{
+ bool found;
+
+ hash_search(compression_options_cache, &cmoptoid, HASH_REMOVE, &found);
+}
+
+/* ----------
+ * get_cached_compression_options
+ *
+ * Get cached compression options structure or create it if it's not in cache.
+ * Cache is required because we can't afford for each tuple create
+ * CompressionMethodRoutine and parse its options.
+ */
+static CompressionMethodOptions *
+get_cached_compression_options(Oid cmoptoid)
+{
+ bool found;
+ CompressionMethodOptions *result;
+
+ Assert(OidIsValid(cmoptoid));
+ if (!compression_options_cache)
+ init_compression_options_cache();
+
+ /* check if the compression options wasn't removed from the last check */
+ found = SearchSysCacheExists(COMPRESSIONOPTIONSOID,
+ ObjectIdGetDatum(cmoptoid), 0, 0, 0);
+ if (!found)
+ {
+ remove_cached_compression_options(cmoptoid);
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+ }
+
+ result = hash_search(compression_options_cache, &cmoptoid, HASH_ENTER, &found);
+ if (!found)
+ {
+ MemoryContext oldcxt;
+
+ Assert(compression_options_mcxt);
+ oldcxt = MemoryContextSwitchTo(compression_options_mcxt);
+ result->cmoptoid = cmoptoid;
+ result->routine = GetCompressionMethodRoutine(cmoptoid);
+ result->options = GetCompressionOptionsList(cmoptoid);
+ MemoryContextSwitchTo(oldcxt);
+ }
+ return result;
+}
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 8287de97a2..be6b460aee 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -731,6 +731,7 @@ DefineAttr(char *name, char *type, int attnum, int nullness)
attrtypes[attnum]->attcacheoff = -1;
attrtypes[attnum]->atttypmod = -1;
attrtypes[attnum]->attislocal = true;
+ attrtypes[attnum]->attcompression = InvalidOid;
if (nullness == BOOTCOL_NULL_FORCE_NOT_NULL)
{
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index fd33426bad..c7cea974b1 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -46,7 +46,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_collation.h pg_partitioned_table.h pg_range.h pg_transform.h \
pg_sequence.h pg_publication.h pg_publication_rel.h pg_subscription.h \
pg_subscription_rel.h toasting.h indexing.h \
- toasting.h indexing.h \
+ pg_compression.h pg_compression_opt.h \
)
# location of Catalog.pm
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index ccde66a7dd..fd733a34a0 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -3340,6 +3340,8 @@ static const char *const no_priv_msg[MAX_ACL_KIND] =
gettext_noop("permission denied for publication %s"),
/* ACL_KIND_SUBSCRIPTION */
gettext_noop("permission denied for subscription %s"),
+ /* ACL_KIND_COMPRESSION_METHOD */
+ gettext_noop("permission denied for compression method %s"),
};
static const char *const not_owner_msg[MAX_ACL_KIND] =
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 033c4358ea..e1bfc7c6bf 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -28,6 +28,8 @@
#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_collation_fn.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_conversion.h"
@@ -173,7 +175,9 @@ static const Oid object_classes[] = {
PublicationRelationId, /* OCLASS_PUBLICATION */
PublicationRelRelationId, /* OCLASS_PUBLICATION_REL */
SubscriptionRelationId, /* OCLASS_SUBSCRIPTION */
- TransformRelationId /* OCLASS_TRANSFORM */
+ TransformRelationId, /* OCLASS_TRANSFORM */
+ CompressionMethodRelationId, /* OCLASS_COMPRESSION_METHOD */
+ CompressionOptRelationId, /* OCLASS_COMPRESSION_OPTIONS */
};
@@ -1271,6 +1275,14 @@ doDeletion(const ObjectAddress *object, int flags)
DropTransformById(object->objectId);
break;
+ case OCLASS_COMPRESSION_METHOD:
+ RemoveCompressionMethodById(object->objectId);
+ break;
+
+ case OCLASS_COMPRESSION_OPTIONS:
+ RemoveCompressionOptionsById(object->objectId);
+ break;
+
/*
* These global object types are not supported here.
*/
@@ -2512,6 +2524,12 @@ getObjectClass(const ObjectAddress *object)
case TransformRelationId:
return OCLASS_TRANSFORM;
+
+ case CompressionMethodRelationId:
+ return OCLASS_COMPRESSION_METHOD;
+
+ case CompressionOptRelationId:
+ return OCLASS_COMPRESSION_OPTIONS;
}
/* shouldn't get here */
diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl
index 256a9c9c93..c5838fa779 100644
--- a/src/backend/catalog/genbki.pl
+++ b/src/backend/catalog/genbki.pl
@@ -451,6 +451,7 @@ sub emit_pgattr_row
attisdropped => 'f',
attislocal => 't',
attinhcount => '0',
+ attcompression=> '0',
attacl => '_null_',
attoptions => '_null_',
attfdwoptions => '_null_');
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 9e14880b99..4439f5df03 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -29,8 +29,10 @@
*/
#include "postgres.h"
+#include "access/compression.h"
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/reloptions.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "access/xact.h"
@@ -44,6 +46,8 @@
#include "catalog/partition.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_foreign_table.h"
@@ -628,6 +632,7 @@ InsertPgAttributeTuple(Relation pg_attribute_rel,
values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal);
values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount);
values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(new_attribute->attcollation);
+ values[Anum_pg_attribute_attcompression - 1] = ObjectIdGetDatum(new_attribute->attcompression);
/* start out with empty permissions and empty options */
nulls[Anum_pg_attribute_attacl - 1] = true;
@@ -1453,6 +1458,24 @@ DeleteRelationTuple(Oid relid)
heap_close(pg_class_desc, RowExclusiveLock);
}
+/*
+ * CallCompressionDropCallback
+ *
+ * Call drop callback from compression routine.
+ */
+static void
+CallCompressionDropCallback(Form_pg_attribute att)
+{
+ CompressionMethodRoutine *cmr = GetCompressionMethodRoutine(att->attcompression);
+
+ if (cmr->drop)
+ {
+ List *options = GetCompressionOptionsList(att->attcompression);
+
+ cmr->drop(att, options);
+ }
+}
+
/*
* DeleteAttributeTuples
*
@@ -1483,7 +1506,14 @@ DeleteAttributeTuples(Oid relid)
/* Delete all the matching tuples */
while ((atttup = systable_getnext(scan)) != NULL)
+ {
+ Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(atttup);
+
+ if (OidIsValid(att->attcompression))
+ CallCompressionDropCallback(att);
+
CatalogTupleDelete(attrel, &atttup->t_self);
+ }
/* Clean up after the scan */
systable_endscan(scan);
@@ -1576,6 +1606,8 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
else
{
/* Dropping user attributes is lots harder */
+ if (OidIsValid(attStruct->attcompression))
+ CallCompressionDropCallback(attStruct);
/* Mark the attribute as dropped */
attStruct->attisdropped = true;
@@ -1597,6 +1629,8 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
/* We don't want to keep stats for it anymore */
attStruct->attstattarget = 0;
+ attStruct->attcompression = InvalidOid;
+
/*
* Change the column name to something that isn't likely to conflict
*/
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 0125c18bc1..15942564aa 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -393,6 +393,7 @@ ConstructTupleDescriptor(Relation heapRelation,
to->atttypmod = exprTypmod(indexkey);
to->attislocal = true;
to->attcollation = collationObjectId[i];
+ to->attcompression = InvalidOid;
ReleaseSysCache(tuple);
@@ -471,6 +472,7 @@ ConstructTupleDescriptor(Relation heapRelation,
to->attbyval = typeTup->typbyval;
to->attalign = typeTup->typalign;
to->attstorage = typeTup->typstorage;
+ to->attcompression = InvalidOid;
ReleaseSysCache(tuple);
}
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 8d55c76fc4..9fd1cb763d 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -29,6 +29,8 @@
#include "catalog/pg_default_acl.h"
#include "catalog/pg_event_trigger.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_conversion.h"
@@ -490,6 +492,30 @@ static const ObjectPropertyType ObjectProperty[] =
InvalidAttrNumber, /* no ACL (same as relation) */
ACL_KIND_STATISTICS,
true
+ },
+ {
+ CompressionMethodRelationId,
+ CompressionMethodOidIndexId,
+ COMPRESSIONMETHODOID,
+ COMPRESSIONMETHODNAME,
+ Anum_pg_compression_cmname,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ true
+ },
+ {
+ CompressionOptRelationId,
+ CompressionOptionsOidIndexId,
+ COMPRESSIONOPTIONSOID,
+ -1,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ false,
}
};
@@ -712,6 +738,10 @@ static const struct object_type_map
/* OBJECT_STATISTIC_EXT */
{
"statistics object", OBJECT_STATISTIC_EXT
+ },
+ /* OCLASS_COMPRESSION_METHOD */
+ {
+ "compression method", OBJECT_COMPRESSION_METHOD
}
};
@@ -876,6 +906,7 @@ get_object_address(ObjectType objtype, Node *object,
case OBJECT_ACCESS_METHOD:
case OBJECT_PUBLICATION:
case OBJECT_SUBSCRIPTION:
+ case OBJECT_COMPRESSION_METHOD:
address = get_object_address_unqualified(objtype,
(Value *) object, missing_ok);
break;
@@ -1182,6 +1213,11 @@ get_object_address_unqualified(ObjectType objtype,
address.objectId = get_subscription_oid(name, missing_ok);
address.objectSubId = 0;
break;
+ case OBJECT_COMPRESSION_METHOD:
+ address.classId = CompressionMethodRelationId;
+ address.objectId = get_compression_method_oid(name, missing_ok);
+ address.objectSubId = 0;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
/* placate compiler, which doesn't know elog won't return */
@@ -2139,6 +2175,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_SCHEMA:
case OBJECT_SUBSCRIPTION:
case OBJECT_TABLESPACE:
+ case OBJECT_COMPRESSION_METHOD:
if (list_length(name) != 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -2395,12 +2432,14 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
case OBJECT_ACCESS_METHOD:
+ case OBJECT_COMPRESSION_METHOD:
/* We treat these object types as being owned by superusers */
if (!superuser_arg(roleid))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser")));
break;
+
case OBJECT_STATISTIC_EXT:
if (!pg_statistics_object_ownercheck(address.objectId, roleid))
aclcheck_error_type(ACLCHECK_NOT_OWNER, address.objectId);
@@ -3398,6 +3437,27 @@ getObjectDescription(const ObjectAddress *object)
break;
}
+ case OCLASS_COMPRESSION_METHOD:
+ {
+ char *name = get_compression_method_name(object->objectId);
+
+ if (!name)
+ elog(ERROR, "cache lookup failed for compression method %u",
+ object->objectId);
+ appendStringInfo(&buffer, _("compression method %s"), name);
+ pfree(name);
+ break;
+ }
+
+ case OCLASS_COMPRESSION_OPTIONS:
+ {
+ char *name = get_compression_method_name_for_opt(object->objectId);
+
+ appendStringInfo(&buffer, _("compression options for %s"), name);
+ pfree(name);
+ break;
+ }
+
case OCLASS_TRANSFORM:
{
HeapTuple trfTup;
@@ -3919,6 +3979,14 @@ getObjectTypeDescription(const ObjectAddress *object)
appendStringInfoString(&buffer, "transform");
break;
+ case OCLASS_COMPRESSION_METHOD:
+ appendStringInfoString(&buffer, "compression method");
+ break;
+
+ case OCLASS_COMPRESSION_OPTIONS:
+ appendStringInfoString(&buffer, "compression options");
+ break;
+
/*
* There's intentionally no default: case here; we want the
* compiler to warn if a new OCLASS hasn't been handled above.
@@ -4160,6 +4228,30 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case OCLASS_COMPRESSION_METHOD:
+ {
+ char *cmname = get_compression_method_name(object->objectId);
+
+ if (!cmname)
+ elog(ERROR, "cache lookup failed for compression method %u",
+ object->objectId);
+ appendStringInfoString(&buffer, quote_identifier(cmname));
+ if (objname)
+ *objname = list_make1(cmname);
+ else
+ pfree(cmname);
+ break;
+ }
+
+ case OCLASS_COMPRESSION_OPTIONS:
+ {
+ appendStringInfo(&buffer, "%u",
+ object->objectId);
+ if (objname)
+ *objname = list_make1(psprintf("%u", object->objectId));
+ break;
+ }
+
case OCLASS_CONSTRAINT:
{
HeapTuple conTup;
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index e02d312008..531a820464 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -22,6 +22,7 @@
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 4a6c99e090..e23abf64f1 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -13,8 +13,8 @@ top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
- collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
- dbcommands.o define.o discard.o dropcmds.o \
+ collationcmds.o compressioncmds.o constraint.o conversioncmds.o copy.o \
+ createas.o dbcommands.o define.o discard.o dropcmds.o \
event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o matview.o operatorcmds.o opclasscmds.o \
policy.o portalcmds.o prepare.o proclang.o publicationcmds.o \
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 4f8147907c..4f18e4083f 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -385,6 +385,7 @@ ExecRenameStmt(RenameStmt *stmt)
case OBJECT_TSTEMPLATE:
case OBJECT_PUBLICATION:
case OBJECT_SUBSCRIPTION:
+ case OBJECT_COMPRESSION_METHOD:
{
ObjectAddress address;
Relation catalog;
@@ -500,6 +501,7 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
case OBJECT_TSDICTIONARY:
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
+ case OBJECT_COMPRESSION_METHOD:
{
Relation catalog;
Relation relation;
@@ -627,6 +629,8 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_COMPRESSION_METHOD:
+ case OCLASS_COMPRESSION_OPTIONS:
/* ignore object types that don't have schema-qualified names */
break;
diff --git a/src/backend/commands/compressioncmds.c b/src/backend/commands/compressioncmds.c
new file mode 100644
index 0000000000..5780926179
--- /dev/null
+++ b/src/backend/commands/compressioncmds.c
@@ -0,0 +1,522 @@
+/*-------------------------------------------------------------------------
+ *
+ * compressioncmds.c
+ * Routines for SQL commands that manipulate compression methods.
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/commands/compressioncmds.c
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "miscadmin.h"
+
+#include "access/compression.h"
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/reloptions.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "parser/parse_func.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+static CompressionMethodRoutine *invoke_compression_handler(Oid cmhandler, Oid typeid);
+
+/*
+ * Convert a handler function name to an Oid. If the return type of the
+ * function doesn't match the given AM type, an error is raised.
+ *
+ * This function either return valid function Oid or throw an error.
+ */
+static Oid
+lookup_compression_handler(List *handlerName)
+{
+ static const Oid funcargtypes[1] = {INTERNALOID};
+ Oid handlerOid;
+
+ if (handlerName == NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("handler function is not specified")));
+
+ /* handlers have one argument of type internal */
+ handlerOid = LookupFuncName(handlerName, 1, funcargtypes, false);
+
+ /* check that handler has the correct return type */
+ if (get_func_rettype(handlerOid) != COMPRESSION_HANDLEROID)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("function %s must return type %s",
+ NameListToString(handlerName),
+ "compression_handler")));
+
+ return handlerOid;
+}
+
+/* Creates a record in pg_compression */
+static ObjectAddress
+create_compression_method(char *cmName, List *handlerName)
+{
+ Relation rel;
+ ObjectAddress myself;
+ ObjectAddress referenced;
+ Oid cmoid;
+ Oid cmhandler;
+ bool nulls[Natts_pg_compression];
+ Datum values[Natts_pg_compression];
+ HeapTuple tup;
+
+ rel = heap_open(CompressionMethodRelationId, RowExclusiveLock);
+
+ /* Check if name is used */
+ cmoid = GetSysCacheOid1(COMPRESSIONMETHODNAME, CStringGetDatum(cmName));
+ if (OidIsValid(cmoid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("compression method \"%s\" already exists",
+ cmName)));
+
+ /*
+ * Get the handler function oid and compression method routine
+ */
+ cmhandler = lookup_compression_handler(handlerName);
+
+ /*
+ * Insert tuple into pg_compression.
+ */
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+
+ values[Anum_pg_compression_cmname - 1] =
+ DirectFunctionCall1(namein, CStringGetDatum(cmName));
+ values[Anum_pg_compression_cmhandler - 1] = ObjectIdGetDatum(cmhandler);
+
+ tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+
+ cmoid = CatalogTupleInsert(rel, tup);
+ heap_freetuple(tup);
+
+ ObjectAddressSet(myself, CompressionMethodRelationId, cmoid);
+
+ /* Record dependency on handler function */
+ ObjectAddressSet(referenced, ProcedureRelationId, cmhandler);
+
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ recordDependencyOnCurrentExtension(&myself, false);
+
+ heap_close(rel, RowExclusiveLock);
+
+ return myself;
+}
+
+/*
+ * Call the specified compression method handler
+ * routine to get its CompressionMethodRoutine struct,
+ * which will be palloc'd in the caller's context.
+ */
+static CompressionMethodRoutine *
+invoke_compression_handler(Oid cmhandler, Oid typeid)
+{
+ Datum datum;
+ CompressionMethodRoutine *routine;
+ CompressionMethodOpArgs opargs;
+
+ opargs.typeid = typeid;
+ opargs.cmhanderid = cmhandler;
+
+ datum = OidFunctionCall1(cmhandler, PointerGetDatum(&opargs));
+ routine = (CompressionMethodRoutine *) DatumGetPointer(datum);
+
+ if (routine == NULL || !IsA(routine, CompressionMethodRoutine))
+ elog(ERROR, "compression method handler function %u "
+ "did not return an CompressionMethodRoutine struct",
+ cmhandler);
+
+ return routine;
+}
+
+/*
+ * CREATE COMPRESSION METHOD .. HANDLER ..
+ */
+ObjectAddress
+DefineCompressionMethod(List *names, List *parameters)
+{
+ char *cmName;
+ ListCell *pl;
+ DefElem *handlerEl = NULL;
+
+ if (list_length(names) != 1)
+ elog(ERROR, "compression method name cannot be qualified");
+
+ cmName = strVal(linitial(names));
+
+ /* Must be super user */
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to create compression method \"%s\"",
+ cmName),
+ errhint("Must be superuser to create an compression method.")));
+
+ /* Extract the name of compression handler function */
+ foreach(pl, parameters)
+ {
+ DefElem *defel = (DefElem *) lfirst(pl);
+ DefElem **defelp;
+
+ if (pg_strcasecmp(defel->defname, "handler") == 0)
+ defelp = &handlerEl;
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("compression method attribute \"%s\" not recognized",
+ defel->defname)));
+ break;
+ }
+
+ *defelp = defel;
+ }
+
+ if (!handlerEl)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("compression method handler is not specified")));
+
+ /* Finally create a compression method */
+ return create_compression_method(cmName, (List *) handlerEl->arg);
+}
+
+/*
+ * RemoveCompressionMethodById
+ *
+ * Removes a compresssion method by its Oid.
+ */
+void
+RemoveCompressionMethodById(Oid cmOid)
+{
+ Relation relation;
+ HeapTuple tup;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to drop compression methods")));
+
+ relation = heap_open(CompressionMethodRelationId, RowExclusiveLock);
+
+ tup = SearchSysCache1(COMPRESSIONMETHODOID, ObjectIdGetDatum(cmOid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for compression method %u", cmOid);
+
+ CatalogTupleDelete(relation, &tup->t_self);
+
+ ReleaseSysCache(tup);
+
+ heap_close(relation, RowExclusiveLock);
+}
+
+/*
+ * Create new record in pg_compression_opt
+ */
+Oid
+CreateCompressionOptions(Form_pg_attribute attr, ColumnCompression *compression)
+{
+ Relation rel;
+ HeapTuple tup,
+ newtup;
+ Oid cmoptoid,
+ cmid;
+ Datum values[Natts_pg_compression_opt];
+ bool nulls[Natts_pg_compression_opt];
+
+ ObjectAddress myself,
+ ref1,
+ ref2;
+
+ CompressionMethodRoutine *routine;
+ Form_pg_compression cmform;
+
+ /* Initialize buffers for new tuple values */
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+
+ cmid = get_compression_method_oid(compression->methodName, false);
+
+ /* Get handler function OID for the compression method */
+ tup = SearchSysCache1(COMPRESSIONMETHODOID, ObjectIdGetDatum(cmid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for compression method %u", cmid);
+ cmform = (Form_pg_compression) GETSTRUCT(tup);
+ routine = invoke_compression_handler(cmform->cmhandler, attr->atttypid);
+
+ if (routine->configure == NULL && compression->options != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("the compression method \"%s\" does not take any options",
+ NameStr(cmform->cmname))));
+ else if (routine->configure && compression->options != NIL)
+ routine->configure(attr, compression->options);
+
+ rel = heap_open(CompressionOptRelationId, RowExclusiveLock);
+
+ cmoptoid = GetNewOidWithIndex(rel, CompressionOptionsOidIndexId,
+ Anum_pg_compression_opt_cmoptoid);
+ values[Anum_pg_compression_opt_cmoptoid - 1] = ObjectIdGetDatum(cmoptoid);
+ values[Anum_pg_compression_opt_cmname - 1] = NameGetDatum(&cmform->cmname);
+ values[Anum_pg_compression_opt_cmhandler - 1] = ObjectIdGetDatum(cmform->cmhandler);
+
+ if (compression->options)
+ values[Anum_pg_compression_opt_cmoptions - 1] =
+ optionListToArray(compression->options);
+ else
+ nulls[Anum_pg_compression_opt_cmoptions - 1] = true;
+
+ newtup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+ CatalogTupleInsert(rel, newtup);
+ heap_freetuple(newtup);
+
+ ReleaseSysCache(tup);
+
+ ObjectAddressSet(myself, CompressionOptRelationId, cmoptoid);
+ ObjectAddressSet(ref1, ProcedureRelationId, cmform->cmhandler);
+ ObjectAddressSet(ref2, CompressionMethodRelationId, cmid);
+
+ recordDependencyOn(&myself, &ref1, DEPENDENCY_NORMAL);
+ recordDependencyOn(&myself, &ref2, DEPENDENCY_NORMAL);
+ recordDependencyOnCurrentExtension(&myself, false);
+ heap_close(rel, RowExclusiveLock);
+
+ return cmoptoid;
+}
+
+/*
+ * Create pg_depend record between attribute and its compression options
+ */
+void
+CreateColumnCompressionDependency(Form_pg_attribute attr, Oid cmoptoid)
+{
+ ObjectAddress optref,
+ attrref;
+
+ ObjectAddressSet(optref, CompressionOptRelationId, cmoptoid);
+ ObjectAddressSubSet(attrref, RelationRelationId, attr->attrelid, attr->attnum);
+ recordDependencyOn(&attrref, &optref, DEPENDENCY_NORMAL);
+}
+
+/*
+ * Remove the compression options record from pg_compression_opt
+ */
+void
+RemoveCompressionOptionsById(Oid cmoptoid)
+{
+ Relation relation;
+ HeapTuple tup;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to drop compression options")));
+
+ tup = SearchSysCache1(COMPRESSIONOPTIONSOID, ObjectIdGetDatum(cmoptoid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+
+ relation = heap_open(CompressionOptRelationId, RowExclusiveLock);
+ CatalogTupleDelete(relation, &tup->t_self);
+ heap_close(relation, RowExclusiveLock);
+ ReleaseSysCache(tup);
+}
+
+ColumnCompression *
+GetColumnCompressionForAttribute(Form_pg_attribute att)
+{
+ HeapTuple tuple;
+ Form_pg_compression_opt cmoptform;
+ ColumnCompression *compression = makeNode(ColumnCompression);
+
+ /* Get handler function OID for the compression method */
+ tuple = SearchSysCache1(COMPRESSIONOPTIONSOID,
+ ObjectIdGetDatum(att->attcompression));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for compression options %u",
+ att->attcompression);
+
+ cmoptform = (Form_pg_compression_opt) GETSTRUCT(tuple);
+ compression->methodName = pstrdup(NameStr(cmoptform->cmname));
+ compression->options = GetCompressionOptionsList(att->attcompression);
+ ReleaseSysCache(tuple);
+
+ return compression;
+}
+
+void
+CheckCompressionMismatch(ColumnCompression *c1, ColumnCompression *c2,
+ const char *attributeName)
+{
+ if (strcmp(c1->methodName, c2->methodName))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("column \"%s\" has a compression method conflict",
+ attributeName),
+ errdetail("%s versus %s", c1->methodName, c2->methodName)));
+
+ if (!equal(c1->options, c2->options))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("column \"%s\" has a compression options conflict",
+ attributeName),
+ errdetail("(%s) versus (%s)",
+ formatRelOptions(c1->options),
+ formatRelOptions(c2->options))));
+}
+
+/*
+ * get_compression_method_oid
+ *
+ * If missing_ok is false, throw an error if compression method not found.
+ * If missing_ok is true, just return InvalidOid.
+ */
+Oid
+get_compression_method_oid(const char *cmname, bool missing_ok)
+{
+ HeapTuple tup;
+ Oid oid = InvalidOid;
+
+ tup = SearchSysCache1(COMPRESSIONMETHODNAME, CStringGetDatum(cmname));
+ if (HeapTupleIsValid(tup))
+ {
+ oid = HeapTupleGetOid(tup);
+ ReleaseSysCache(tup);
+ }
+
+ if (!OidIsValid(oid) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("compression method \"%s\" does not exist", cmname)));
+
+ return oid;
+}
+
+/*
+ * get_compression_method_name
+ *
+ * given an compression method OID, look up its name.
+ */
+char *
+get_compression_method_name(Oid cmOid)
+{
+ HeapTuple tup;
+ char *result = NULL;
+
+ tup = SearchSysCache1(COMPRESSIONMETHODOID, ObjectIdGetDatum(cmOid));
+ if (HeapTupleIsValid(tup))
+ {
+ Form_pg_compression cmform = (Form_pg_compression) GETSTRUCT(tup);
+
+ result = pstrdup(NameStr(cmform->cmname));
+ ReleaseSysCache(tup);
+ }
+ return result;
+}
+
+/*
+ * get_compression_method_name_for_opt
+ *
+ * given an compression options OID, look up a name for used compression method.
+ */
+char *
+get_compression_method_name_for_opt(Oid cmoptoid)
+{
+ HeapTuple tup;
+ char *result = NULL;
+ Form_pg_compression_opt cmoptform;
+
+ tup = SearchSysCache1(COMPRESSIONOPTIONSOID, ObjectIdGetDatum(cmoptoid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+
+ cmoptform = (Form_pg_compression_opt) GETSTRUCT(tup);
+ result = pstrdup(NameStr(cmoptform->cmname));
+ ReleaseSysCache(tup);
+
+ return result;
+}
+
+/*
+ * GetCompressionOptionsList
+ *
+ * Parse array with compression options and return it as list.
+ */
+List *
+GetCompressionOptionsList(Oid cmoptoid)
+{
+ HeapTuple tuple;
+ List *result = NULL;
+ bool isnull;
+ Datum cmoptions;
+
+ /* Get handler function OID for the compression method */
+ tuple = SearchSysCache1(COMPRESSIONOPTIONSOID, ObjectIdGetDatum(cmoptoid));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+
+ cmoptions = SysCacheGetAttr(COMPRESSIONOPTIONSOID, tuple,
+ Anum_pg_compression_opt_cmoptions, &isnull);
+
+ if (!isnull)
+ result = untransformRelOptions(cmoptions);
+
+ ReleaseSysCache(tuple);
+ return result;
+}
+
+/*
+ * GetCompressionMethodRoutine
+ *
+ * Determine compression handler by compression options Oid and return
+ * structure with compression methods
+ */
+CompressionMethodRoutine *
+GetCompressionMethodRoutine(Oid cmoptoid)
+{
+ HeapTuple tuple;
+ Form_pg_compression_opt cmopt;
+ regproc cmhandler;
+
+ /* Get handler function OID for the compression method */
+ tuple = SearchSysCache1(COMPRESSIONOPTIONSOID, ObjectIdGetDatum(cmoptoid));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+
+ cmopt = (Form_pg_compression_opt) GETSTRUCT(tuple);
+ cmhandler = cmopt->cmhandler;
+
+ /* Complain if handler OID is invalid */
+ if (!RegProcedureIsValid(cmhandler))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("could not find compression method handler for compression options %u",
+ cmoptoid)));
+
+ ReleaseSysCache(tuple);
+
+ /* And finally, call the handler function to get the API struct. */
+ return invoke_compression_handler(cmhandler, InvalidOid);
+}
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 13eb9e34ba..f84588ed31 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2781,8 +2781,8 @@ CopyFrom(CopyState cstate)
List *recheckIndexes = NIL;
/* OK, store the tuple and create index entries for it */
- heap_insert(resultRelInfo->ri_RelationDesc, tuple, mycid,
- hi_options, bistate);
+ heap_insert(resultRelInfo->ri_RelationDesc, tuple,
+ mycid, hi_options, bistate);
if (resultRelInfo->ri_NumIndices > 0)
recheckIndexes = ExecInsertIndexTuples(slot,
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index 2b30677d6f..8611db22ec 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -275,6 +275,10 @@ does_not_exist_skipping(ObjectType objtype, Node *object)
name = NameListToString(castNode(List, object));
}
break;
+ case OBJECT_COMPRESSION_METHOD:
+ msg = gettext_noop("compression method \"%s\" does not exist, skipping");
+ name = strVal((Value *) object);
+ break;
case OBJECT_CONVERSION:
if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
{
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index fa7d0d015a..a2eee78a64 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -91,6 +91,7 @@ static event_trigger_support_data event_trigger_support[] = {
{"CAST", true},
{"CONSTRAINT", true},
{"COLLATION", true},
+ {"COMPRESSION METHOD", true},
{"CONVERSION", true},
{"DATABASE", false},
{"DOMAIN", true},
@@ -1085,6 +1086,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
case OBJECT_CAST:
case OBJECT_COLUMN:
case OBJECT_COLLATION:
+ case OBJECT_COMPRESSION_METHOD:
case OBJECT_CONVERSION:
case OBJECT_DEFACL:
case OBJECT_DEFAULT:
@@ -1144,6 +1146,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
case OCLASS_ROLE:
+ case OCLASS_COMPRESSION_OPTIONS:
/* no support for global objects */
return false;
case OCLASS_EVENT_TRIGGER:
@@ -1183,6 +1186,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_COMPRESSION_METHOD:
return true;
/*
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index 9ad991507f..b840309d03 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -61,7 +61,7 @@ static void import_error_callback(void *arg);
* processing, hence any validation should be done before this
* conversion.
*/
-static Datum
+Datum
optionListToArray(List *options)
{
ArrayBuildState *astate = NULL;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index d19846d005..f5e3522551 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -14,6 +14,7 @@
*/
#include "postgres.h"
+#include "access/compression.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "access/multixact.h"
@@ -33,6 +34,8 @@
#include "catalog/partition.h"
#include "catalog/pg_am.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_depend.h"
@@ -41,6 +44,7 @@
#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
+#include "catalog/pg_proc.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
@@ -90,6 +94,7 @@
#include "storage/smgr.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+#include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
@@ -459,6 +464,8 @@ static void ATExecGenericOptions(Relation rel, List *options);
static void ATExecEnableRowSecurity(Relation rel);
static void ATExecDisableRowSecurity(Relation rel);
static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
+static void ATExecAlterColumnCompression(AlteredTableInfo *tab, Relation rel,
+ const char *column, ColumnCompression *compression, LOCKMODE lockmode);
static void copy_relation_data(SMgrRelation rel, SMgrRelation dst,
ForkNumber forkNum, char relpersistence);
@@ -724,6 +731,12 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
if (colDef->identity)
attr->attidentity = colDef->identity;
+
+ /* Create compression options */
+ if (colDef->compression)
+ attr->attcompression = CreateCompressionOptions(attr, colDef->compression);
+ else
+ attr->attcompression = InvalidOid;
}
/*
@@ -771,6 +784,19 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
*/
rel = relation_open(relationId, AccessExclusiveLock);
+ /*
+ * We should also create dependencies between attributes and their
+ * compression options
+ */
+ for (attnum = 0; attnum < (RelationGetDescr(rel))->natts; attnum++)
+ {
+ Form_pg_attribute attr;
+
+ attr = TupleDescAttr(RelationGetDescr(rel), attnum);
+ if (OidIsValid(attr->attcompression))
+ CreateColumnCompressionDependency(attr, attr->attcompression);
+ }
+
/* Process and store partition bound, if any. */
if (stmt->partbound)
{
@@ -1610,6 +1636,7 @@ storage_name(char c)
}
}
+
/*----------
* MergeAttributes
* Returns new schema given initial schema and superclasses.
@@ -1944,6 +1971,19 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
storage_name(def->storage),
storage_name(attribute->attstorage))));
+ if (OidIsValid(attribute->attcompression))
+ {
+ ColumnCompression *compression =
+ GetColumnCompressionForAttribute(attribute);
+
+ if (!def->compression)
+ def->compression = compression;
+ else
+ CheckCompressionMismatch(def->compression,
+ compression,
+ attributeName);
+ }
+
def->inhcount++;
/* Merge of NOT NULL constraints = OR 'em together */
def->is_not_null |= attribute->attnotnull;
@@ -1971,6 +2011,9 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
def->collOid = attribute->attcollation;
def->constraints = NIL;
def->location = -1;
+ if (OidIsValid(attribute->attcompression))
+ def->compression =
+ GetColumnCompressionForAttribute(attribute);
inhSchema = lappend(inhSchema, def);
newattno[parent_attno - 1] = ++child_attno;
}
@@ -2180,6 +2223,13 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
storage_name(def->storage),
storage_name(newdef->storage))));
+ if (!def->compression)
+ def->compression = newdef->compression;
+ else if (newdef->compression)
+ CheckCompressionMismatch(def->compression,
+ newdef->compression,
+ attributeName);
+
/* Mark the column as locally defined */
def->is_local = true;
/* Merge of NOT NULL constraints = OR 'em together */
@@ -3279,6 +3329,7 @@ AlterTableGetLockLevel(List *cmds)
*/
case AT_GenericOptions:
case AT_AlterColumnGenericOptions:
+ case AT_AlterColumnCompression:
cmd_lockmode = AccessExclusiveLock;
break;
@@ -3770,6 +3821,12 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* No command-specific prep needed */
pass = AT_PASS_MISC;
break;
+ case AT_AlterColumnCompression:
+ ATSimplePermissions(rel, ATT_TABLE);
+ /* FIXME This command never recurses */
+ /* No command-specific prep needed */
+ pass = AT_PASS_MISC;
+ break;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
@@ -4118,6 +4175,11 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
case AT_DetachPartition:
ATExecDetachPartition(rel, ((PartitionCmd *) cmd->def)->name);
break;
+ case AT_AlterColumnCompression:
+ ATExecAlterColumnCompression(tab, rel, cmd->name,
+ (ColumnCompression *) cmd->def,
+ lockmode);
+ break;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
@@ -5338,6 +5400,8 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
attribute.attislocal = colDef->is_local;
attribute.attinhcount = colDef->inhcount;
attribute.attcollation = collOid;
+ attribute.attcompression = InvalidOid;
+
/* attribute.attacl is handled by InsertPgAttributeTuple */
ReleaseSysCache(typeTuple);
@@ -6438,6 +6502,11 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE loc
errmsg("cannot alter system column \"%s\"",
colName)));
+ if (attrtuple->attcompression && newstorage != 'x' && newstorage != 'm')
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("compressed columns can only have storage MAIN or EXTENDED")));
+
/*
* safety check: do not allow toasted storage modes unless column datatype
* is TOAST-aware.
@@ -9361,6 +9430,8 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_COMPRESSION_METHOD:
+ case OCLASS_COMPRESSION_OPTIONS:
/*
* We don't expect any of these sorts of objects to depend on
@@ -9410,7 +9481,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
if (!(foundDep->refclassid == TypeRelationId &&
foundDep->refobjid == attTup->atttypid) &&
!(foundDep->refclassid == CollationRelationId &&
- foundDep->refobjid == attTup->attcollation))
+ foundDep->refobjid == attTup->attcollation) &&
+ !(foundDep->refclassid == CompressionMethodRelationId &&
+ foundDep->refobjid == attTup->attcompression))
elog(ERROR, "found unexpected dependency for column");
CatalogTupleDelete(depRel, &depTup->t_self);
@@ -9432,6 +9505,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
attTup->attbyval = tform->typbyval;
attTup->attalign = tform->typalign;
attTup->attstorage = tform->typstorage;
+ attTup->attcompression = InvalidOid;
ReleaseSysCache(typeTuple);
@@ -12484,6 +12558,82 @@ ATExecGenericOptions(Relation rel, List *options)
heap_freetuple(tuple);
}
+static void
+ATExecAlterColumnCompression(AlteredTableInfo *tab,
+ Relation rel,
+ const char *column,
+ ColumnCompression *compression,
+ LOCKMODE lockmode)
+{
+ Relation attrel;
+ HeapTuple atttuple;
+ Form_pg_attribute atttableform;
+ AttrNumber attnum;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_attribute];
+ bool nulls[Natts_pg_attribute];
+ bool replace[Natts_pg_attribute];
+
+ attrel = heap_open(AttributeRelationId, RowExclusiveLock);
+
+ atttuple = SearchSysCacheAttName(RelationGetRelid(rel), column);
+ if (!HeapTupleIsValid(atttuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("column \"%s\" of relation \"%s\" does not exist",
+ column, RelationGetRelationName(rel))));
+
+ /* Prevent them from altering a system attribute */
+ atttableform = (Form_pg_attribute) GETSTRUCT(atttuple);
+ attnum = atttableform->attnum;
+ if (attnum <= 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot alter system column \"%s\"", column)));
+
+ if (atttableform->attstorage != 'x' && atttableform->attstorage != 'm')
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("storage for \"%s\" should be MAIN or EXTENDED", column)));
+
+ /* Initialize buffers for new tuple values */
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+ memset(replace, false, sizeof(replace));
+
+ if (compression->methodName)
+ {
+ /* SET COMPRESSED */
+ Oid cmoptoid;
+
+ cmoptoid = CreateCompressionOptions(atttableform, compression);
+ CreateColumnCompressionDependency(atttableform, cmoptoid);
+
+ values[Anum_pg_attribute_attcompression - 1] = ObjectIdGetDatum(cmoptoid);
+ replace[Anum_pg_attribute_attcompression - 1] = true;
+
+ }
+ else
+ {
+ /* SET NOT COMPRESSED */
+ values[Anum_pg_attribute_attcompression - 1] = ObjectIdGetDatum(InvalidOid);
+ replace[Anum_pg_attribute_attcompression - 1] = true;
+ }
+
+ newtuple = heap_modify_tuple(atttuple, RelationGetDescr(attrel),
+ values, nulls, replace);
+ CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
+ heap_freetuple(newtuple);
+
+ InvokeObjectPostAlterHook(RelationRelationId,
+ RelationGetRelid(rel),
+ atttableform->attnum);
+
+ ReleaseSysCache(atttuple);
+ heap_close(attrel, RowExclusiveLock);
+}
+
+
/*
* Preparation phase for SET LOGGED/UNLOGGED
*
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index f86af4c054..a7e9111c8d 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -40,6 +40,7 @@
#include "catalog/pg_am.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_depend.h"
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index 5dfc49deb9..3a80e997a9 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -302,6 +302,9 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc
var->vartypmod != -1))
return false; /* type mismatch */
+ if (OidIsValid(att_tup->attcompression))
+ return false;
+
tlist_item = lnext(tlist_item);
}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index e48921fff1..1f14818d32 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2806,6 +2806,7 @@ _copyColumnDef(const ColumnDef *from)
COPY_STRING_FIELD(colname);
COPY_NODE_FIELD(typeName);
+ COPY_NODE_FIELD(compression);
COPY_SCALAR_FIELD(inhcount);
COPY_SCALAR_FIELD(is_local);
COPY_SCALAR_FIELD(is_not_null);
@@ -2824,6 +2825,17 @@ _copyColumnDef(const ColumnDef *from)
return newnode;
}
+static ColumnCompression *
+_copyColumnCompression(const ColumnCompression *from)
+{
+ ColumnCompression *newnode = makeNode(ColumnCompression);
+
+ COPY_STRING_FIELD(methodName);
+ COPY_NODE_FIELD(options);
+
+ return newnode;
+}
+
static Constraint *
_copyConstraint(const Constraint *from)
{
@@ -5473,6 +5485,9 @@ copyObjectImpl(const void *from)
case T_ColumnDef:
retval = _copyColumnDef(from);
break;
+ case T_ColumnCompression:
+ retval = _copyColumnCompression(from);
+ break;
case T_Constraint:
retval = _copyConstraint(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 2a83da9aa9..4eddc69602 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2544,6 +2544,7 @@ _equalColumnDef(const ColumnDef *a, const ColumnDef *b)
{
COMPARE_STRING_FIELD(colname);
COMPARE_NODE_FIELD(typeName);
+ COMPARE_NODE_FIELD(compression);
COMPARE_SCALAR_FIELD(inhcount);
COMPARE_SCALAR_FIELD(is_local);
COMPARE_SCALAR_FIELD(is_not_null);
@@ -2562,6 +2563,15 @@ _equalColumnDef(const ColumnDef *a, const ColumnDef *b)
return true;
}
+static bool
+_equalColumnCompression(const ColumnCompression *a, const ColumnCompression *b)
+{
+ COMPARE_STRING_FIELD(methodName);
+ COMPARE_NODE_FIELD(options);
+
+ return true;
+}
+
static bool
_equalConstraint(const Constraint *a, const Constraint *b)
{
@@ -3615,6 +3625,9 @@ equal(const void *a, const void *b)
case T_ColumnDef:
retval = _equalColumnDef(a, b);
break;
+ case T_ColumnCompression:
+ retval = _equalColumnCompression(a, b);
+ break;
case T_Constraint:
retval = _equalConstraint(a, b);
break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index c2a93b2d4c..81e334a1d3 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -3623,6 +3623,8 @@ raw_expression_tree_walker(Node *node,
if (walker(coldef->typeName, context))
return true;
+ if (walker(coldef->compression, context))
+ return true;
if (walker(coldef->raw_default, context))
return true;
if (walker(coldef->collClause, context))
@@ -3630,6 +3632,14 @@ raw_expression_tree_walker(Node *node,
/* for now, constraints are ignored */
}
break;
+ case T_ColumnCompression:
+ {
+ ColumnCompression *colcmp = (ColumnCompression *) node;
+
+ if (walker(colcmp->options, context))
+ return true;
+ }
+ break;
case T_IndexElem:
{
IndexElem *indelem = (IndexElem *) node;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index c97ee24ade..72e88de08f 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2799,6 +2799,7 @@ _outColumnDef(StringInfo str, const ColumnDef *node)
WRITE_STRING_FIELD(colname);
WRITE_NODE_FIELD(typeName);
+ WRITE_NODE_FIELD(compression);
WRITE_INT_FIELD(inhcount);
WRITE_BOOL_FIELD(is_local);
WRITE_BOOL_FIELD(is_not_null);
@@ -2815,6 +2816,15 @@ _outColumnDef(StringInfo str, const ColumnDef *node)
WRITE_LOCATION_FIELD(location);
}
+static void
+_outColumnCompression(StringInfo str, const ColumnCompression *node)
+{
+ WRITE_NODE_TYPE("COLUMNCOMPRESSION");
+
+ WRITE_STRING_FIELD(methodName);
+ WRITE_NODE_FIELD(options);
+}
+
static void
_outTypeName(StringInfo str, const TypeName *node)
{
@@ -4106,6 +4116,9 @@ outNode(StringInfo str, const void *obj)
case T_ColumnDef:
_outColumnDef(str, obj);
break;
+ case T_ColumnCompression:
+ _outColumnCompression(str, obj);
+ break;
case T_TypeName:
_outTypeName(str, obj);
break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c301ca465d..122d3de4d6 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -397,6 +397,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
transform_element_list transform_type_list
TriggerTransitions TriggerReferencing
publication_name_list
+ optCompressionParameters
vacuum_relation_list opt_vacuum_relation_list
%type <list> group_by_list
@@ -582,6 +583,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <list> hash_partbound partbound_datum_list range_datum_list
%type <defelt> hash_partbound_elem
+%type <node> columnCompression optColumnCompression compressedClause
+
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
* They must be listed first so that their numeric codes do not depend on
@@ -614,9 +617,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
CLUSTER COALESCE COLLATE COLLATION COLUMN COLUMNS COMMENT COMMENTS COMMIT
- COMMITTED CONCURRENTLY CONFIGURATION CONFLICT CONNECTION CONSTRAINT
- CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE
- CROSS CSV CUBE CURRENT_P
+ COMMITTED COMPRESSED COMPRESSION CONCURRENTLY CONFIGURATION CONFLICT
+ CONNECTION CONSTRAINT CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY
+ COST CREATE CROSS CSV CUBE CURRENT_P
CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA
CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
@@ -2168,6 +2171,15 @@ alter_table_cmd:
n->missing_ok = true;
$$ = (Node *)n;
}
+ /* ALTER TABLE <name> ALTER [COLUMN] <colname> SET (NOT COMPRESSED | COMPRESSED <cm> [WITH (<options>)]) */
+ | ALTER opt_column ColId SET columnCompression
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_AlterColumnCompression;
+ n->name = $3;
+ n->def = $5;
+ $$ = (Node *)n;
+ }
/* ALTER TABLE <name> DROP [COLUMN] IF EXISTS <colname> [RESTRICT|CASCADE] */
| DROP opt_column IF_P EXISTS ColId opt_drop_behavior
{
@@ -3332,11 +3344,12 @@ TypedTableElement:
| TableConstraint { $$ = $1; }
;
-columnDef: ColId Typename create_generic_options ColQualList
+columnDef: ColId Typename optColumnCompression create_generic_options ColQualList
{
ColumnDef *n = makeNode(ColumnDef);
n->colname = $1;
n->typeName = $2;
+ n->compression = (ColumnCompression *) $3;
n->inhcount = 0;
n->is_local = true;
n->is_not_null = false;
@@ -3346,8 +3359,8 @@ columnDef: ColId Typename create_generic_options ColQualList
n->raw_default = NULL;
n->cooked_default = NULL;
n->collOid = InvalidOid;
- n->fdwoptions = $3;
- SplitColQualList($4, &n->constraints, &n->collClause,
+ n->fdwoptions = $4;
+ SplitColQualList($5, &n->constraints, &n->collClause,
yyscanner);
n->location = @1;
$$ = (Node *)n;
@@ -3394,6 +3407,37 @@ columnOptions: ColId ColQualList
}
;
+compressedClause:
+ COMPRESSED name optCompressionParameters
+ {
+ ColumnCompression *n = makeNode(ColumnCompression);
+ n->methodName = $2;
+ n->options = (List *) $3;
+ $$ = (Node *) n;
+ }
+ ;
+
+columnCompression:
+ compressedClause |
+ NOT COMPRESSED
+ {
+ ColumnCompression *n = makeNode(ColumnCompression);
+ n->methodName = NULL;
+ n->options = NIL;
+ $$ = (Node *) n;
+ }
+ ;
+
+optColumnCompression:
+ compressedClause /* FIXME shift/reduce conflict on NOT COMPRESSED/NOT NULL */
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
+optCompressionParameters:
+ WITH '(' generic_option_list ')' { $$ = $3; }
+ | /*EMPTY*/ { $$ = NIL; }
+ ;
+
ColQualList:
ColQualList ColConstraint { $$ = lappend($1, $2); }
| /*EMPTY*/ { $$ = NIL; }
@@ -5754,6 +5798,15 @@ DefineStmt:
n->if_not_exists = true;
$$ = (Node *)n;
}
+ | CREATE COMPRESSION METHOD any_name HANDLER handler_name
+ {
+ DefineStmt *n = makeNode(DefineStmt);
+ n->kind = OBJECT_COMPRESSION_METHOD;
+ n->args = NIL;
+ n->defnames = $4;
+ n->definition = list_make1(makeDefElem("handler", (Node *) $6, @6));
+ $$ = (Node *) n;
+ }
;
definition: '(' def_list ')' { $$ = $2; }
@@ -6262,6 +6315,7 @@ drop_type_any_name:
/* object types taking name_list */
drop_type_name:
ACCESS METHOD { $$ = OBJECT_ACCESS_METHOD; }
+ | COMPRESSION METHOD { $$ = OBJECT_COMPRESSION_METHOD; }
| EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; }
| EXTENSION { $$ = OBJECT_EXTENSION; }
| FOREIGN DATA_P WRAPPER { $$ = OBJECT_FDW; }
@@ -6325,7 +6379,7 @@ opt_restart_seqs:
* The COMMENT ON statement can take different forms based upon the type of
* the object associated with the comment. The form of the statement is:
*
- * COMMENT ON [ [ ACCESS METHOD | CONVERSION | COLLATION |
+ * COMMENT ON [ [ ACCESS METHOD | COMPRESSION METHOD | CONVERSION | COLLATION |
* DATABASE | DOMAIN |
* EXTENSION | EVENT TRIGGER | FOREIGN DATA WRAPPER |
* FOREIGN TABLE | INDEX | [PROCEDURAL] LANGUAGE |
@@ -6515,6 +6569,7 @@ comment_type_any_name:
/* object types taking name */
comment_type_name:
ACCESS METHOD { $$ = OBJECT_ACCESS_METHOD; }
+ | COMPRESSION METHOD { $$ = OBJECT_COMPRESSION_METHOD; }
| DATABASE { $$ = OBJECT_DATABASE; }
| EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; }
| EXTENSION { $$ = OBJECT_EXTENSION; }
@@ -14704,6 +14759,8 @@ unreserved_keyword:
| COMMENTS
| COMMIT
| COMMITTED
+ | COMPRESSED
+ | COMPRESSION
| CONFIGURATION
| CONFLICT
| CONNECTION
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 8461da490a..39f21d4da7 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -27,6 +27,7 @@
#include "postgres.h"
#include "access/amapi.h"
+#include "access/compression.h"
#include "access/htup_details.h"
#include "access/reloptions.h"
#include "catalog/dependency.h"
@@ -494,6 +495,39 @@ generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column,
*sname_p = sname;
}
+/*
+ * transformColumnCompression
+ *
+ * Build ALTER TABLE .. SET COMPRESSED command if a compression method was
+ * specified for the column
+ */
+void
+transformColumnCompression(ColumnDef *column, RangeVar *relation,
+ AlterTableStmt **alterStmt)
+{
+ if (column->compression)
+ {
+ AlterTableCmd *cmd;
+
+ cmd = makeNode(AlterTableCmd);
+ cmd->subtype = AT_AlterColumnCompression;
+ cmd->name = column->colname;
+ cmd->def = (Node *) column->compression;
+ cmd->behavior = DROP_RESTRICT;
+ cmd->missing_ok = false;
+
+ if (!*alterStmt)
+ {
+ *alterStmt = makeNode(AlterTableStmt);
+ (*alterStmt)->relation = relation;
+ (*alterStmt)->relkind = OBJECT_TABLE;
+ (*alterStmt)->cmds = NIL;
+ }
+
+ (*alterStmt)->cmds = lappend((*alterStmt)->cmds, cmd);
+ }
+}
+
/*
* transformColumnDefinition -
* transform a single ColumnDef within CREATE TABLE
@@ -794,6 +828,16 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
cxt->alist = lappend(cxt->alist, stmt);
}
+
+ if (cxt->isalter)
+ {
+ AlterTableStmt *stmt = NULL;
+
+ transformColumnCompression(column, cxt->relation, &stmt);
+
+ if (stmt)
+ cxt->alist = lappend(cxt->alist, stmt);
+ }
}
/*
@@ -1003,6 +1047,10 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
def->collOid = attribute->attcollation;
def->constraints = NIL;
def->location = -1;
+ if (attribute->attcompression)
+ def->compression = GetColumnCompressionForAttribute(attribute);
+ else
+ def->compression = NULL;
/*
* Add to column list
diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c
index 0f607bab70..f9eecf041b 100644
--- a/src/backend/replication/logical/reorderbuffer.c
+++ b/src/backend/replication/logical/reorderbuffer.c
@@ -2878,7 +2878,7 @@ ReorderBufferToastReplace(ReorderBuffer *rb, ReorderBufferTXN *txn,
VARSIZE(chunk) - VARHDRSZ);
data_done += VARSIZE(chunk) - VARHDRSZ;
}
- Assert(data_done == toast_pointer.va_extsize);
+ Assert(data_done == VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer));
/* make sure its marked as compressed or not */
if (VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer))
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 82a707af7b..8c8c51dada 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1009,6 +1009,7 @@ ProcessUtilitySlow(ParseState *pstate,
RELKIND_RELATION,
InvalidOid, NULL,
queryString);
+
EventTriggerCollectSimpleCommand(address,
secondaryObject,
stmt);
@@ -1283,6 +1284,11 @@ ProcessUtilitySlow(ParseState *pstate,
stmt->definition,
stmt->if_not_exists);
break;
+ case OBJECT_COMPRESSION_METHOD:
+ Assert(stmt->args == NIL);
+ address = DefineCompressionMethod(stmt->defnames,
+ stmt->definition);
+ break;
default:
elog(ERROR, "unrecognized define stmt type: %d",
(int) stmt->kind);
@@ -2309,6 +2315,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_STATISTIC_EXT:
tag = "DROP STATISTICS";
break;
+ case OBJECT_COMPRESSION_METHOD:
+ tag = "DROP COMPRESSION METHOD";
+ break;
default:
tag = "???";
}
@@ -2412,6 +2421,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_ACCESS_METHOD:
tag = "CREATE ACCESS METHOD";
break;
+ case OBJECT_COMPRESSION_METHOD:
+ tag = "CREATE COMPRESSION METHOD";
+ break;
default:
tag = "???";
}
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index be793539a3..a5cfbe3d4c 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -418,3 +418,4 @@ PSEUDOTYPE_DUMMY_IO_FUNCS(internal);
PSEUDOTYPE_DUMMY_IO_FUNCS(opaque);
PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement);
PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray);
+PSEUDOTYPE_DUMMY_IO_FUNCS(compression_handler);
diff --git a/src/backend/utils/adt/tsvector.c b/src/backend/utils/adt/tsvector.c
index b0a9217d1e..fcff9d3fd6 100644
--- a/src/backend/utils/adt/tsvector.c
+++ b/src/backend/utils/adt/tsvector.c
@@ -14,11 +14,14 @@
#include "postgres.h"
+#include "access/compression.h"
+#include "catalog/pg_type.h"
#include "libpq/pqformat.h"
#include "tsearch/ts_locale.h"
#include "tsearch/ts_utils.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
+#include "common/pg_lzcompress.h"
typedef struct
{
@@ -548,3 +551,85 @@ tsvectorrecv(PG_FUNCTION_ARGS)
PG_RETURN_TSVECTOR(vec);
}
+
+/*
+ * Compress tsvector using LZ compression.
+ * Instead of trying to compress whole tsvector we compress only text part
+ * here. This approach gives more compressibility for tsvectors.
+ */
+static struct varlena *
+tsvector_compress(CompressionMethodOptions *cmoptions, const struct varlena *data)
+{
+ char *tmp;
+ int32 valsize = VARSIZE_ANY_EXHDR(data);
+ int32 len = valsize + VARHDRSZ_CUSTOM_COMPRESSED,
+ lenc;
+
+ char *arr = VARDATA(data),
+ *str = STRPTR((TSVector) data);
+ int32 arrsize = str - arr;
+
+ Assert(!VARATT_IS_COMPRESSED(data));
+ tmp = palloc0(len);
+
+ /* we try to compress string part of tsvector first */
+ lenc = pglz_compress(str,
+ valsize - arrsize,
+ tmp + VARHDRSZ_CUSTOM_COMPRESSED + arrsize,
+ PGLZ_strategy_default);
+
+ if (lenc >= 0)
+ {
+ /* tsvector is compressible, copy size and entries to its beginning */
+ memcpy(tmp + VARHDRSZ_CUSTOM_COMPRESSED, arr, arrsize);
+ SET_VARSIZE_COMPRESSED(tmp, arrsize + lenc + VARHDRSZ_CUSTOM_COMPRESSED);
+ return (struct varlena *) tmp;
+ }
+
+ pfree(tmp);
+ return NULL;
+}
+
+static struct varlena *
+tsvector_decompress(CompressionMethodOptions *cmoptions, const struct varlena *data)
+{
+ char *tmp,
+ *raw_data = (char *) data + VARHDRSZ_CUSTOM_COMPRESSED;
+ int32 count,
+ arrsize,
+ len = VARRAWSIZE_4B_C(data) + VARHDRSZ;
+
+ Assert(VARATT_IS_CUSTOM_COMPRESSED(data));
+ tmp = palloc0(len);
+ SET_VARSIZE(tmp, len);
+ count = *((uint32 *) raw_data);
+ arrsize = sizeof(uint32) + count * sizeof(WordEntry);
+ memcpy(VARDATA(tmp), raw_data, arrsize);
+
+ if (pglz_decompress(raw_data + arrsize,
+ VARSIZE(data) - VARHDRSZ_CUSTOM_COMPRESSED - arrsize,
+ VARDATA(tmp) + arrsize,
+ VARRAWSIZE_4B_C(data) - arrsize) < 0)
+ elog(ERROR, "compressed tsvector is corrupted");
+
+ return (struct varlena *) tmp;
+}
+
+Datum
+tsvector_compression_handler(PG_FUNCTION_ARGS)
+{
+ CompressionMethodOpArgs *opargs = (CompressionMethodOpArgs *)
+ PG_GETARG_POINTER(0);
+ CompressionMethodRoutine *cmr = makeNode(CompressionMethodRoutine);
+ Oid typeid = opargs->typeid;
+
+ if (OidIsValid(typeid) && typeid != TSVECTOROID)
+ elog(ERROR, "unexpected type %d for tsvector compression handler", typeid);
+
+ cmr->configure = NULL;
+ cmr->drop = NULL;
+ cmr->compress = tsvector_compress;
+ cmr->decompress = tsvector_decompress;
+
+ PG_RETURN_POINTER(cmr);
+}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 1908420d82..de834c5593 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -30,6 +30,7 @@
#include <fcntl.h>
#include <unistd.h>
+#include "access/compression.h"
#include "access/hash.h"
#include "access/htup_details.h"
#include "access/multixact.h"
@@ -77,6 +78,7 @@
#include "storage/smgr.h"
#include "utils/array.h"
#include "utils/builtins.h"
+#include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 888edbb325..c689f60e47 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -31,6 +31,8 @@
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
@@ -309,6 +311,39 @@ static const struct cachedesc cacheinfo[] = {
},
8
},
+ {CompressionMethodRelationId, /* COMPRESSIONMETHODOID */
+ CompressionMethodOidIndexId,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 8
+ },
+ {CompressionMethodRelationId, /* COMPRESSIONMETHODNAME */
+ CompressionMethodNameIndexId,
+ 1,
+ {
+ Anum_pg_compression_cmname,
+ 0,
+ 0,
+ 0
+ },
+ 8
+ },
+ {CompressionOptRelationId, /* COMPRESSIONOPTIONSOID */
+ CompressionOptionsOidIndexId,
+ 1,
+ {
+ Anum_pg_compression_opt_cmoptoid,
+ 0,
+ 0,
+ 0
+ },
+ 8,
+ },
{ConversionRelationId, /* CONDEFAULT */
ConversionDefaultIndexId,
4,
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 4b47951de1..7ba6d31251 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -54,6 +54,7 @@ static DumpableObject **oprinfoindex;
static DumpableObject **collinfoindex;
static DumpableObject **nspinfoindex;
static DumpableObject **extinfoindex;
+static DumpableObject **cminfoindex;
static int numTables;
static int numTypes;
static int numFuncs;
@@ -61,6 +62,7 @@ static int numOperators;
static int numCollations;
static int numNamespaces;
static int numExtensions;
+static int numCompressionMethods;
/* This is an array of object identities, not actual DumpableObjects */
static ExtensionMemberId *extmembers;
@@ -93,6 +95,8 @@ getSchemaData(Archive *fout, int *numTablesPtr)
NamespaceInfo *nspinfo;
ExtensionInfo *extinfo;
InhInfo *inhinfo;
+ CompressionMethodInfo *cminfo;
+
int numAggregates;
int numInherits;
int numRules;
@@ -289,6 +293,11 @@ getSchemaData(Archive *fout, int *numTablesPtr)
write_msg(NULL, "reading subscriptions\n");
getSubscriptions(fout);
+ if (g_verbose)
+ write_msg(NULL, "reading compression methods\n");
+ cminfo = getCompressionMethods(fout, &numCompressionMethods);
+ cminfoindex = buildIndexArray(cminfo, numCompressionMethods, sizeof(CompressionMethodInfo));
+
*numTablesPtr = numTables;
return tblinfo;
}
@@ -827,6 +836,17 @@ findExtensionByOid(Oid oid)
return (ExtensionInfo *) findObjectByOid(oid, extinfoindex, numExtensions);
}
+/*
+ * findCompressionMethodByOid
+ * finds the entry (in cminfo) of the compression method with the given oid
+ * returns NULL if not found
+ */
+CompressionMethodInfo *
+findCompressionMethodByOid(Oid oid)
+{
+ return (CompressionMethodInfo *) findObjectByOid(oid, cminfoindex,
+ numCompressionMethods);
+}
/*
* setExtensionMembership
diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index ce3100f09d..5c1aaad48e 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -77,6 +77,7 @@ typedef struct _restoreOptions
int no_publications; /* Skip publication entries */
int no_security_labels; /* Skip security label entries */
int no_subscriptions; /* Skip subscription entries */
+ int no_compression_methods; /* Skip compression methods */
int strict_names;
const char *filename;
@@ -121,6 +122,8 @@ typedef struct _restoreOptions
bool *idWanted; /* array showing which dump IDs to emit */
int enable_row_security;
int sequence_data; /* dump sequence data even in schema-only mode */
+ int compression_options_data; /* dump compression options even
+ * in schema-only mode */
int binary_upgrade;
} RestoreOptions;
@@ -149,6 +152,7 @@ typedef struct _dumpOptions
int no_security_labels;
int no_publications;
int no_subscriptions;
+ int no_compression_methods;
int no_synchronized_snapshots;
int no_unlogged_table_data;
int serializable_deferrable;
@@ -170,6 +174,8 @@ typedef struct _dumpOptions
char *outputSuperuser;
int sequence_data; /* dump sequence data even in schema-only mode */
+ int compression_options_data; /* dump compression options even
+ * in schema-only mode */
} DumpOptions;
/*
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index ec2fa8b9b9..77eb55eb69 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -179,6 +179,7 @@ dumpOptionsFromRestoreOptions(RestoreOptions *ropt)
dopt->include_everything = ropt->include_everything;
dopt->enable_row_security = ropt->enable_row_security;
dopt->sequence_data = ropt->sequence_data;
+ dopt->compression_options_data = ropt->compression_options_data;
return dopt;
}
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index d8fb356130..6df688adb8 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -361,6 +361,7 @@ main(int argc, char **argv)
{"no-synchronized-snapshots", no_argument, &dopt.no_synchronized_snapshots, 1},
{"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
{"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
+ {"no-compression-methods", no_argument, &dopt.no_compression_methods, 1},
{"no-sync", no_argument, NULL, 7},
{NULL, 0, NULL, 0}
@@ -581,6 +582,9 @@ main(int argc, char **argv)
if (dopt.binary_upgrade)
dopt.sequence_data = 1;
+ if (dopt.binary_upgrade)
+ dopt.compression_options_data = 1;
+
if (dopt.dataOnly && dopt.schemaOnly)
{
write_msg(NULL, "options -s/--schema-only and -a/--data-only cannot be used together\n");
@@ -785,6 +789,9 @@ main(int argc, char **argv)
if (dopt.schemaOnly && dopt.sequence_data)
getTableData(&dopt, tblinfo, numTables, dopt.oids, RELKIND_SEQUENCE);
+ if (dopt.schemaOnly && dopt.compression_options_data)
+ getCompressionOptions(fout);
+
/*
* In binary-upgrade mode, we do not have to worry about the actual blob
* data or the associated metadata that resides in the pg_largeobject and
@@ -3957,6 +3964,205 @@ dumpSubscription(Archive *fout, SubscriptionInfo *subinfo)
destroyPQExpBuffer(query);
}
+/*
+ * getCompressionMethods
+ * get information about compression methods
+ */
+CompressionMethodInfo *
+getCompressionMethods(Archive *fout, int *numMethods)
+{
+ DumpOptions *dopt = fout->dopt;
+ PQExpBuffer query;
+ PGresult *res;
+ CompressionMethodInfo *cminfo;
+ int i_tableoid;
+ int i_oid;
+ int i_handler;
+ int i_name;
+ int i,
+ ntups;
+
+ if (dopt->no_compression_methods || fout->remoteVersion < 110000)
+ return NULL;
+
+ query = createPQExpBuffer();
+ resetPQExpBuffer(query);
+
+ /* Get the compression methods in current database. */
+ appendPQExpBuffer(query, "SELECT c.tableoid, c.oid, c.cmname, "
+ "c.cmhandler::pg_catalog.regproc as cmhandler "
+ "FROM pg_catalog.pg_compression c");
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_name = PQfnumber(res, "cmname");
+ i_handler = PQfnumber(res, "cmhandler");
+
+ cminfo = pg_malloc(ntups * sizeof(CompressionMethodInfo));
+
+ for (i = 0; i < ntups; i++)
+ {
+ cminfo[i].dobj.objType = DO_COMPRESSION_METHOD;
+ cminfo[i].dobj.catId.tableoid =
+ atooid(PQgetvalue(res, i, i_tableoid));
+ cminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&cminfo[i].dobj);
+ cminfo[i].cmhandler = pg_strdup(PQgetvalue(res, i, i_handler));
+ cminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_name));
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(cminfo[i].dobj), fout);
+ }
+ if (numMethods)
+ *numMethods = ntups;
+
+ PQclear(res);
+ destroyPQExpBuffer(query);
+
+ return cminfo;
+}
+
+/*
+ * dumpCompressionMethod
+ * dump the definition of the given compression method
+ */
+static void
+dumpCompressionMethod(Archive *fout, CompressionMethodInfo * cminfo)
+{
+ PQExpBuffer query;
+
+ if (!(cminfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
+ return;
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema(fout, "pg_catalog");
+
+ query = createPQExpBuffer();
+ appendPQExpBuffer(query, "CREATE COMPRESSION METHOD %s HANDLER",
+ fmtId(cminfo->dobj.name));
+ appendPQExpBuffer(query, " %s;\n", cminfo->cmhandler);
+
+ ArchiveEntry(fout,
+ cminfo->dobj.catId,
+ cminfo->dobj.dumpId,
+ cminfo->dobj.name,
+ NULL,
+ NULL,
+ "", false,
+ "COMPRESSION METHOD", SECTION_PRE_DATA,
+ query->data, "", NULL,
+ NULL, 0,
+ NULL, NULL);
+
+ destroyPQExpBuffer(query);
+}
+
+/*
+ * getCompressionOptions
+ * get information about compression options.
+ * this information should be migrated at binary upgrade
+ */
+void
+getCompressionOptions(Archive *fout)
+{
+ PQExpBuffer query;
+ PGresult *res;
+ CompressionOptionsInfo *cminfo;
+ int i_tableoid;
+ int i_oid;
+ int i_handler;
+ int i_name;
+ int i_options;
+ int i,
+ ntups;
+
+ if (fout->remoteVersion < 110000)
+ return;
+
+ query = createPQExpBuffer();
+ resetPQExpBuffer(query);
+
+ appendPQExpBuffer(query,
+ "SELECT c.tableoid, c.cmoptoid, c.cmname,"
+ " c.cmhandler::pg_catalog.regproc as cmhandler, c.cmoptions "
+ "FROM pg_catalog.pg_compression_opt c");
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "cmoptoid");
+ i_name = PQfnumber(res, "cmname");
+ i_handler = PQfnumber(res, "cmhandler");
+ i_options = PQfnumber(res, "cmoptions");
+
+ cminfo = pg_malloc(ntups * sizeof(CompressionOptionsInfo));
+
+ for (i = 0; i < ntups; i++)
+ {
+ cminfo[i].dobj.objType = DO_COMPRESSION_OPTIONS;
+ cminfo[i].dobj.catId.tableoid =
+ atooid(PQgetvalue(res, i, i_tableoid));
+ cminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&cminfo[i].dobj);
+ cminfo[i].cmhandler = pg_strdup(PQgetvalue(res, i, i_handler));
+ cminfo[i].cmoptions = pg_strdup(PQgetvalue(res, i, i_options));
+ cminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_name));
+ cminfo[i].dobj.dump = DUMP_COMPONENT_DEFINITION;
+ }
+
+ PQclear(res);
+ destroyPQExpBuffer(query);
+}
+
+/*
+ * dumpCompressionOptions
+ * dump the given compression options
+ */
+static void
+dumpCompressionOptions(Archive *fout, CompressionOptionsInfo * cminfo)
+{
+ PQExpBuffer query;
+
+ if (!(cminfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
+ return;
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema(fout, "pg_catalog");
+
+ query = createPQExpBuffer();
+ if (strlen(cminfo->cmoptions) > 0)
+ appendPQExpBuffer(query, "INSERT INTO pg_compression_opt (cmoptoid, cmname, cmhandler,"
+ " cmoptions) VALUES (%d, '%s', CAST('%s' AS REGPROC), '%s');\n",
+ cminfo->dobj.catId.oid,
+ cminfo->dobj.name,
+ cminfo->cmhandler,
+ cminfo->cmoptions);
+ else
+ appendPQExpBuffer(query, "INSERT INTO pg_compression_opt (cmoptoid, cmname, cmhandler,"
+ " cmoptions) VALUES (%d, '%s', CAST('%s' AS REGPROC), NULL);\n",
+ cminfo->dobj.catId.oid,
+ cminfo->dobj.name,
+ cminfo->cmhandler);
+
+ ArchiveEntry(fout,
+ cminfo->dobj.catId,
+ cminfo->dobj.dumpId,
+ cminfo->dobj.name,
+ NULL,
+ NULL,
+ "", false,
+ "COMPRESSION OPTIONS", SECTION_PRE_DATA,
+ query->data, "", NULL,
+ NULL, 0,
+ NULL, NULL);
+
+ destroyPQExpBuffer(query);
+}
+
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
@@ -4484,7 +4690,47 @@ getTypes(Archive *fout, int *numTypes)
/* Make sure we are in proper schema */
selectSourceSchema(fout, "pg_catalog");
- if (fout->remoteVersion >= 90600)
+ if (fout->remoteVersion >= 110000)
+ {
+ PQExpBuffer acl_subquery = createPQExpBuffer();
+ PQExpBuffer racl_subquery = createPQExpBuffer();
+ PQExpBuffer initacl_subquery = createPQExpBuffer();
+ PQExpBuffer initracl_subquery = createPQExpBuffer();
+
+ buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
+ initracl_subquery, "t.typacl", "t.typowner", "'T'",
+ dopt->binary_upgrade);
+
+ appendPQExpBuffer(query, "SELECT t.tableoid, t.oid, t.typname, "
+ "t.typnamespace, "
+ "%s AS typacl, "
+ "%s AS rtypacl, "
+ "%s AS inittypacl, "
+ "%s AS initrtypacl, "
+ "(%s t.typowner) AS rolname, "
+ "t.typelem, t.typrelid, "
+ "CASE WHEN t.typrelid = 0 THEN ' '::\"char\" "
+ "ELSE (SELECT relkind FROM pg_class WHERE oid = t.typrelid) END AS typrelkind, "
+ "t.typtype, t.typisdefined, "
+ "t.typname[0] = '_' AND t.typelem != 0 AND "
+ "(SELECT typarray FROM pg_type te WHERE oid = t.typelem) = t.oid AS isarray "
+ "FROM pg_type t "
+ "LEFT JOIN pg_init_privs pip ON "
+ "(t.oid = pip.objoid "
+ "AND pip.classoid = 'pg_type'::regclass "
+ "AND pip.objsubid = 0) ",
+ acl_subquery->data,
+ racl_subquery->data,
+ initacl_subquery->data,
+ initracl_subquery->data,
+ username_subquery);
+
+ destroyPQExpBuffer(acl_subquery);
+ destroyPQExpBuffer(racl_subquery);
+ destroyPQExpBuffer(initacl_subquery);
+ destroyPQExpBuffer(initracl_subquery);
+ }
+ else if (fout->remoteVersion >= 90600)
{
PQExpBuffer acl_subquery = createPQExpBuffer();
PQExpBuffer racl_subquery = createPQExpBuffer();
@@ -7895,6 +8141,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
int i_attoptions;
int i_attcollation;
int i_attfdwoptions;
+ int i_attcmoptions;
+ int i_attcmname;
PGresult *res;
int ntups;
bool hasdefaults;
@@ -7930,7 +8178,46 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
resetPQExpBuffer(q);
- if (fout->remoteVersion >= 100000)
+ if (fout->remoteVersion >= 110000)
+ {
+ /*
+ * attcompression is new in version 11
+ */
+ appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, "
+ "a.attstattarget, a.attstorage, t.typstorage, "
+ "a.attnotnull, a.atthasdef, a.attisdropped, "
+ "a.attlen, a.attalign, a.attislocal, "
+ "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
+ "array_to_string(a.attoptions, ', ') AS attoptions, "
+ "CASE WHEN a.attcollation <> t.typcollation "
+ "THEN a.attcollation ELSE 0 END AS attcollation, "
+ "a.attidentity, "
+ /* fdw options */
+ "pg_catalog.array_to_string(ARRAY("
+ "SELECT pg_catalog.quote_ident(option_name) || "
+ "' ' || pg_catalog.quote_literal(option_value) "
+ "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
+ "ORDER BY option_name"
+ "), E',\n ') AS attfdwoptions, "
+ /* compression options */
+ "pg_catalog.array_to_string(ARRAY("
+ "SELECT pg_catalog.quote_ident(option_name) || "
+ "' ' || pg_catalog.quote_literal(option_value) "
+ "FROM pg_catalog.pg_options_to_table(c.cmoptions) "
+ "ORDER BY option_name"
+ "), E',\n ') AS attcmoptions, "
+ "c.cmname AS attcmname "
+ /* FROM */
+ "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
+ "ON a.atttypid = t.oid "
+ "LEFT JOIN pg_catalog.pg_compression_opt c "
+ "ON a.attcompression = c.cmoptoid "
+ "WHERE a.attrelid = '%u'::pg_catalog.oid "
+ "AND a.attnum > 0::pg_catalog.int2 "
+ "ORDER BY a.attnum",
+ tbinfo->dobj.catId.oid);
+ }
+ else if (fout->remoteVersion >= 100000)
{
/*
* attidentity is new in version 10.
@@ -7949,9 +8236,13 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"' ' || pg_catalog.quote_literal(option_value) "
"FROM pg_catalog.pg_options_to_table(attfdwoptions) "
"ORDER BY option_name"
- "), E',\n ') AS attfdwoptions "
+ "), E',\n ') AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
+ "LEFT JOIN pg_catalog.pg_compression_opt c "
+ "ON a.attcompression = c.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
"AND a.attnum > 0::pg_catalog.int2 "
"ORDER BY a.attnum",
@@ -7975,7 +8266,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"' ' || pg_catalog.quote_literal(option_value) "
"FROM pg_catalog.pg_options_to_table(attfdwoptions) "
"ORDER BY option_name"
- "), E',\n ') AS attfdwoptions "
+ "), E',\n ') AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -7999,7 +8292,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"array_to_string(a.attoptions, ', ') AS attoptions, "
"CASE WHEN a.attcollation <> t.typcollation "
"THEN a.attcollation ELSE 0 END AS attcollation, "
- "NULL AS attfdwoptions "
+ "NULL AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -8017,7 +8312,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
"array_to_string(a.attoptions, ', ') AS attoptions, "
"0 AS attcollation, "
- "NULL AS attfdwoptions "
+ "NULL AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -8034,7 +8331,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"a.attlen, a.attalign, a.attislocal, "
"pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
"'' AS attoptions, 0 AS attcollation, "
- "NULL AS attfdwoptions "
+ "NULL AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -8064,6 +8363,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
i_attoptions = PQfnumber(res, "attoptions");
i_attcollation = PQfnumber(res, "attcollation");
i_attfdwoptions = PQfnumber(res, "attfdwoptions");
+ i_attcmname = PQfnumber(res, "attcmname");
+ i_attcmoptions = PQfnumber(res, "attcmoptions");
tbinfo->numatts = ntups;
tbinfo->attnames = (char **) pg_malloc(ntups * sizeof(char *));
@@ -8080,6 +8381,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
tbinfo->attoptions = (char **) pg_malloc(ntups * sizeof(char *));
tbinfo->attcollation = (Oid *) pg_malloc(ntups * sizeof(Oid));
tbinfo->attfdwoptions = (char **) pg_malloc(ntups * sizeof(char *));
+ tbinfo->attcmoptions = (char **) pg_malloc(ntups * sizeof(char *));
+ tbinfo->attcmnames = (char **) pg_malloc(ntups * sizeof(char *));
tbinfo->notnull = (bool *) pg_malloc(ntups * sizeof(bool));
tbinfo->inhNotNull = (bool *) pg_malloc(ntups * sizeof(bool));
tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(ntups * sizeof(AttrDefInfo *));
@@ -8107,6 +8410,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, j, i_attoptions));
tbinfo->attcollation[j] = atooid(PQgetvalue(res, j, i_attcollation));
tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, j, i_attfdwoptions));
+ tbinfo->attcmoptions[j] = pg_strdup(PQgetvalue(res, j, i_attcmoptions));
+ tbinfo->attcmnames[j] = pg_strdup(PQgetvalue(res, j, i_attcmname));
tbinfo->attrdefs[j] = NULL; /* fix below */
if (PQgetvalue(res, j, i_atthasdef)[0] == 't')
hasdefaults = true;
@@ -9596,6 +9901,12 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
case DO_SUBSCRIPTION:
dumpSubscription(fout, (SubscriptionInfo *) dobj);
break;
+ case DO_COMPRESSION_METHOD:
+ dumpCompressionMethod(fout, (CompressionMethodInfo *) dobj);
+ break;
+ case DO_COMPRESSION_OPTIONS:
+ dumpCompressionOptions(fout, (CompressionOptionsInfo *) dobj);
+ break;
case DO_PRE_DATA_BOUNDARY:
case DO_POST_DATA_BOUNDARY:
/* never dumped, nothing to do */
@@ -15513,6 +15824,15 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
}
}
+ if (tbinfo->attcmnames[j] && strlen(tbinfo->attcmnames[j]))
+ {
+ appendPQExpBuffer(q, " COMPRESSED %s",
+ tbinfo->attcmnames[j]);
+ if (nonemptyReloptions(tbinfo->attcmoptions[j]))
+ appendPQExpBuffer(q, " WITH (%s)",
+ tbinfo->attcmoptions[j]);
+ }
+
if (has_default)
appendPQExpBuffer(q, " DEFAULT %s",
tbinfo->attrdefs[j]->adef_expr);
@@ -17778,6 +18098,8 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
case DO_FOREIGN_SERVER:
case DO_TRANSFORM:
case DO_BLOB:
+ case DO_COMPRESSION_METHOD:
+ case DO_COMPRESSION_OPTIONS:
/* Pre-data objects: must come before the pre-data boundary */
addObjectDependency(preDataBound, dobj->dumpId);
break;
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index da884ffd09..88b18ba15c 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -83,7 +83,9 @@ typedef enum
DO_POLICY,
DO_PUBLICATION,
DO_PUBLICATION_REL,
- DO_SUBSCRIPTION
+ DO_SUBSCRIPTION,
+ DO_COMPRESSION_METHOD,
+ DO_COMPRESSION_OPTIONS
} DumpableObjectType;
/* component types of an object which can be selected for dumping */
@@ -315,6 +317,8 @@ typedef struct _tableInfo
char **attoptions; /* per-attribute options */
Oid *attcollation; /* per-attribute collation selection */
char **attfdwoptions; /* per-attribute fdw options */
+ char **attcmoptions; /* per-attribute compression options */
+ char **attcmnames; /* per-attribute compression method names */
bool *notnull; /* NOT NULL constraints on attributes */
bool *inhNotNull; /* true if NOT NULL is inherited */
struct _attrDefInfo **attrdefs; /* DEFAULT expressions */
@@ -611,6 +615,21 @@ typedef struct _SubscriptionInfo
char *subpublications;
} SubscriptionInfo;
+/* The CompressionMethodInfo struct is used to represent compression method */
+typedef struct _CompressionMethodInfo
+{
+ DumpableObject dobj;
+ char *cmhandler;
+} CompressionMethodInfo;
+
+/* The CompressionOptionsInfo struct is used to represent compression options */
+typedef struct _CompressionOptionsInfo
+{
+ DumpableObject dobj;
+ char *cmhandler;
+ char *cmoptions;
+} CompressionOptionsInfo;
+
/*
* We build an array of these with an entry for each object that is an
* extension member according to pg_depend.
@@ -654,6 +673,7 @@ extern OprInfo *findOprByOid(Oid oid);
extern CollInfo *findCollationByOid(Oid oid);
extern NamespaceInfo *findNamespaceByOid(Oid oid);
extern ExtensionInfo *findExtensionByOid(Oid oid);
+extern CompressionMethodInfo * findCompressionMethodByOid(Oid oid);
extern void setExtensionMembership(ExtensionMemberId *extmems, int nextmems);
extern ExtensionInfo *findOwningExtension(CatalogId catalogId);
@@ -711,5 +731,8 @@ extern void getPublications(Archive *fout);
extern void getPublicationTables(Archive *fout, TableInfo tblinfo[],
int numTables);
extern void getSubscriptions(Archive *fout);
+extern CompressionMethodInfo * getCompressionMethods(Archive *fout,
+ int *numMethods);
+void getCompressionOptions(Archive *fout);
#endif /* PG_DUMP_H */
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index 48b6dd594c..f5db0280e2 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -80,7 +80,9 @@ static const int dbObjectTypePriority[] =
34, /* DO_POLICY */
35, /* DO_PUBLICATION */
36, /* DO_PUBLICATION_REL */
- 37 /* DO_SUBSCRIPTION */
+ 37, /* DO_SUBSCRIPTION */
+ 17, /* DO_COMPRESSION_METHOD */
+ 17 /* DO_COMPRESSION_OPTIONS */
};
static DumpId preDataBoundId;
@@ -1436,6 +1438,16 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
"POST-DATA BOUNDARY (ID %d)",
obj->dumpId);
return;
+ case DO_COMPRESSION_METHOD:
+ snprintf(buf, bufsize,
+ "COMPRESSION METHOD %s (ID %d OID %u)",
+ obj->name, obj->dumpId, obj->catId.oid);
+ return;
+ case DO_COMPRESSION_OPTIONS:
+ snprintf(buf, bufsize,
+ "COMPRESSION OPTIONS %s (ID %d OID %u)",
+ obj->name, obj->dumpId, obj->catId.oid);
+ return;
}
/* shouldn't get here */
snprintf(buf, bufsize,
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 41c5ff89b7..6b72ccf9ea 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -77,6 +77,7 @@ static int use_setsessauth = 0;
static int no_publications = 0;
static int no_security_labels = 0;
static int no_subscriptions = 0;
+static int no_compression_methods = 0;
static int no_unlogged_table_data = 0;
static int no_role_passwords = 0;
static int server_version;
@@ -137,6 +138,7 @@ main(int argc, char *argv[])
{"no-role-passwords", no_argument, &no_role_passwords, 1},
{"no-security-labels", no_argument, &no_security_labels, 1},
{"no-subscriptions", no_argument, &no_subscriptions, 1},
+ {"no-compression-methods", no_argument, &no_compression_methods, 1},
{"no-sync", no_argument, NULL, 4},
{"no-unlogged-table-data", no_argument, &no_unlogged_table_data, 1},
@@ -405,6 +407,8 @@ main(int argc, char *argv[])
appendPQExpBufferStr(pgdumpopts, " --no-security-labels");
if (no_subscriptions)
appendPQExpBufferStr(pgdumpopts, " --no-subscriptions");
+ if (no_compression_methods)
+ appendPQExpBufferStr(pgdumpopts, " --no-compression-methods");
if (no_unlogged_table_data)
appendPQExpBufferStr(pgdumpopts, " --no-unlogged-table-data");
@@ -628,6 +632,7 @@ help(void)
printf(_(" --no-role-passwords do not dump passwords for roles\n"));
printf(_(" --no-security-labels do not dump security label assignments\n"));
printf(_(" --no-subscriptions do not dump subscriptions\n"));
+ printf(_(" --no-compression-methods do not dump compression methods\n"));
printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c
index 860a211a3c..810403ec9c 100644
--- a/src/bin/pg_dump/pg_restore.c
+++ b/src/bin/pg_dump/pg_restore.c
@@ -74,6 +74,7 @@ main(int argc, char **argv)
static int no_publications = 0;
static int no_security_labels = 0;
static int no_subscriptions = 0;
+ static int no_compression_methods = 0;
static int strict_names = 0;
struct option cmdopts[] = {
@@ -122,6 +123,7 @@ main(int argc, char **argv)
{"no-publications", no_argument, &no_publications, 1},
{"no-security-labels", no_argument, &no_security_labels, 1},
{"no-subscriptions", no_argument, &no_subscriptions, 1},
+ {"no-compression-methods", no_argument, &no_compression_methods, 1},
{NULL, 0, NULL, 0}
};
@@ -361,6 +363,7 @@ main(int argc, char **argv)
opts->no_publications = no_publications;
opts->no_security_labels = no_security_labels;
opts->no_subscriptions = no_subscriptions;
+ opts->no_compression_methods = no_compression_methods;
if (if_exists && !opts->dropSchema)
{
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 8cc4de3878..8b0dbfa45c 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -735,7 +735,10 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
success = listConversions(pattern, show_verbose, show_system);
break;
case 'C':
- success = listCasts(pattern, show_verbose);
+ if (cmd[2] == 'M')
+ success = describeCompressionMethods(pattern, show_verbose);
+ else
+ success = listCasts(pattern, show_verbose);
break;
case 'd':
if (strncmp(cmd, "ddp", 3) == 0)
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index b7b978a361..49160ded0c 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -200,6 +200,69 @@ describeAccessMethods(const char *pattern, bool verbose)
return true;
}
+/*
+ * \dCM
+ * Takes an optional regexp to select particular compression methods
+ */
+bool
+describeCompressionMethods(const char *pattern, bool verbose)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+ static const bool translate_columns[] = {false, false, false};
+
+ if (pset.sversion < 100000)
+ {
+ char sverbuf[32];
+
+ psql_error("The server (version %s) does not support compression methods.\n",
+ formatPGVersionNumber(pset.sversion, false,
+ sverbuf, sizeof(sverbuf)));
+ return true;
+ }
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT cmname AS \"%s\"",
+ gettext_noop("Name"));
+
+ if (verbose)
+ {
+ appendPQExpBuffer(&buf,
+ ",\n cmhandler AS \"%s\",\n"
+ " pg_catalog.obj_description(oid, 'pg_compression') AS \"%s\"",
+ gettext_noop("Handler"),
+ gettext_noop("Description"));
+ }
+
+ appendPQExpBufferStr(&buf,
+ "\nFROM pg_catalog.pg_compression\n");
+
+ processSQLNamePattern(pset.db, &buf, pattern, false, false,
+ NULL, "cmname", NULL,
+ NULL);
+
+ appendPQExpBufferStr(&buf, "ORDER BY 1;");
+
+ res = PSQLexec(buf.data);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of compression methods");
+ myopt.translate_header = true;
+ myopt.translate_columns = translate_columns;
+ myopt.n_translate_columns = lengthof(translate_columns);
+
+ printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+ PQclear(res);
+ return true;
+}
+
/*
* \db
* Takes an optional regexp to select particular tablespaces
@@ -1716,6 +1779,22 @@ describeOneTableDetails(const char *schemaname,
if (verbose)
{
appendPQExpBufferStr(&buf, ",\n a.attstorage");
+
+ /* compresssion info */
+ if (pset.sversion >= 110000)
+ appendPQExpBufferStr(&buf, ",\n CASE WHEN attcompression = 0 THEN NULL ELSE "
+ " (SELECT c.cmname || "
+ " (CASE WHEN cmoptions IS NULL "
+ " THEN '' "
+ " ELSE '(' || array_to_string(ARRAY(SELECT quote_ident(option_name) || ' ' || quote_literal(option_value)"
+ " FROM pg_options_to_table(cmoptions)), ', ') || ')'"
+ " END) "
+ " FROM pg_catalog.pg_compression_opt c "
+ " WHERE c.cmoptoid = a.attcompression) "
+ " END AS attcmname");
+ else
+ appendPQExpBufferStr(&buf, "\n NULL AS attcmname");
+
appendPQExpBufferStr(&buf, ",\n CASE WHEN a.attstattarget=-1 THEN NULL ELSE a.attstattarget END AS attstattarget");
/*
@@ -1830,6 +1909,10 @@ describeOneTableDetails(const char *schemaname,
if (verbose)
{
headers[cols++] = gettext_noop("Storage");
+
+ if (tableinfo.relkind == RELKIND_RELATION)
+ headers[cols++] = gettext_noop("Compression");
+
if (tableinfo.relkind == RELKIND_RELATION ||
tableinfo.relkind == RELKIND_INDEX ||
tableinfo.relkind == RELKIND_MATVIEW ||
@@ -1925,6 +2008,11 @@ describeOneTableDetails(const char *schemaname,
"???")))),
false, false);
+ /* Column compression. */
+ if (tableinfo.relkind == RELKIND_RELATION)
+ printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1),
+ false, false);
+
/* Statistics target, if the relkind supports this feature */
if (tableinfo.relkind == RELKIND_RELATION ||
tableinfo.relkind == RELKIND_INDEX ||
@@ -1932,7 +2020,7 @@ describeOneTableDetails(const char *schemaname,
tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
{
- printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1),
+ printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2),
false, false);
}
@@ -1943,7 +2031,7 @@ describeOneTableDetails(const char *schemaname,
tableinfo.relkind == RELKIND_COMPOSITE_TYPE ||
tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
- printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2),
+ printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 3),
false, false);
}
}
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 14a5667f3e..0ab8518a36 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -18,6 +18,9 @@ extern bool describeAccessMethods(const char *pattern, bool verbose);
/* \db */
extern bool describeTablespaces(const char *pattern, bool verbose);
+/* \dCM */
+extern bool describeCompressionMethods(const char *pattern, bool verbose);
+
/* \df, \dfa, \dfn, \dft, \dfw, etc. */
extern bool describeFunctions(const char *functypes, const char *pattern, bool verbose, bool showSystem);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index a926c40b9b..25d2fbc125 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -227,6 +227,7 @@ slashUsage(unsigned short int pager)
fprintf(output, _(" \\db[+] [PATTERN] list tablespaces\n"));
fprintf(output, _(" \\dc[S+] [PATTERN] list conversions\n"));
fprintf(output, _(" \\dC[+] [PATTERN] list casts\n"));
+ fprintf(output, _(" \\dCM[+] [PATTERN] list compression methods\n"));
fprintf(output, _(" \\dd[S] [PATTERN] show object descriptions not displayed elsewhere\n"));
fprintf(output, _(" \\dD[S+] [PATTERN] list domains\n"));
fprintf(output, _(" \\ddp [PATTERN] list default privileges\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index b3e3799c13..a323ca80c6 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -889,6 +889,11 @@ static const SchemaQuery Query_for_list_of_statistics = {
" AND d.datname = pg_catalog.current_database() "\
" AND s.subdbid = d.oid"
+#define Query_for_list_of_compression_methods \
+" SELECT pg_catalog.quote_ident(cmname) "\
+" FROM pg_catalog.pg_compression "\
+" WHERE substring(pg_catalog.quote_ident(cmname),1,%d)='%s'"
+
/* the silly-looking length condition is just to eat up the current word */
#define Query_for_list_of_arguments \
"SELECT pg_catalog.oidvectortypes(proargtypes)||')' "\
@@ -1011,6 +1016,7 @@ static const pgsql_thing_t words_after_create[] = {
* CREATE CONSTRAINT TRIGGER is not supported here because it is designed
* to be used only by pg_dump.
*/
+ {"COMPRESSION METHOD", NULL, NULL},
{"CONFIGURATION", Query_for_list_of_ts_configurations, NULL, THING_NO_SHOW},
{"CONVERSION", "SELECT pg_catalog.quote_ident(conname) FROM pg_catalog.pg_conversion WHERE substring(pg_catalog.quote_ident(conname),1,%d)='%s'"},
{"DATABASE", Query_for_list_of_databases},
@@ -1424,8 +1430,8 @@ psql_completion(const char *text, int start, int end)
"\\a",
"\\connect", "\\conninfo", "\\C", "\\cd", "\\copy",
"\\copyright", "\\crosstabview",
- "\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
- "\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
+ "\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dCM", "\\dd", "\\ddp",
+ "\\dD", "\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
"\\dm", "\\dn", "\\do", "\\dO", "\\dp",
"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
@@ -1954,11 +1960,17 @@ psql_completion(const char *text, int start, int end)
/* ALTER TABLE ALTER [COLUMN] <foo> SET */
else if (Matches7("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET") ||
Matches6("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET"))
- COMPLETE_WITH_LIST5("(", "DEFAULT", "NOT NULL", "STATISTICS", "STORAGE");
+ COMPLETE_WITH_LIST6("(", "COMPRESSED", "DEFAULT", "NOT NULL", "STATISTICS", "STORAGE");
/* ALTER TABLE ALTER [COLUMN] <foo> SET ( */
else if (Matches8("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "(") ||
Matches7("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "("))
COMPLETE_WITH_LIST2("n_distinct", "n_distinct_inherited");
+ else if (Matches8("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "COMPRESSED") ||
+ Matches7("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "COMPRESSED"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_compression_methods);
+ else if (Matches9("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "COMPRESSED", MatchAny) ||
+ Matches8("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "COMPRESSED", MatchAny))
+ COMPLETE_WITH_CONST("WITH (");
/* ALTER TABLE ALTER [COLUMN] <foo> SET STORAGE */
else if (Matches8("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "STORAGE") ||
Matches7("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "STORAGE"))
@@ -2177,12 +2189,14 @@ psql_completion(const char *text, int start, int end)
"SCHEMA", "SEQUENCE", "STATISTICS", "SUBSCRIPTION",
"TABLE", "TYPE", "VIEW", "MATERIALIZED VIEW", "COLUMN", "AGGREGATE", "FUNCTION",
"OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", "LARGE OBJECT",
- "TABLESPACE", "TEXT SEARCH", "ROLE", NULL};
+ "TABLESPACE", "TEXT SEARCH", "ROLE", "COMPRESSION METHOD", NULL};
COMPLETE_WITH_LIST(list_COMMENT);
}
else if (Matches4("COMMENT", "ON", "ACCESS", "METHOD"))
COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
+ else if (Matches4("COMMENT", "ON", "COMPRESSION", "METHOD"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_compression_methods);
else if (Matches3("COMMENT", "ON", "FOREIGN"))
COMPLETE_WITH_LIST2("DATA WRAPPER", "TABLE");
else if (Matches4("COMMENT", "ON", "TEXT", "SEARCH"))
@@ -2255,6 +2269,14 @@ psql_completion(const char *text, int start, int end)
else if (Matches6("CREATE", "ACCESS", "METHOD", MatchAny, "TYPE", MatchAny))
COMPLETE_WITH_CONST("HANDLER");
+ /* CREATE COMPRESSION METHOD */
+ /* Complete "CREATE COMPRESSION METHOD <name>" */
+ else if (Matches4("CREATE", "COMPRESSION", "METHOD", MatchAny))
+ COMPLETE_WITH_CONST("HANDLER");
+ /* Complete "CREATE COMPRESSION METHOD <name> HANDLER" */
+ else if (Matches5("CREATE", "COMPRESSION", "METHOD", MatchAny, "HANDLER"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
+
/* CREATE DATABASE */
else if (Matches3("CREATE", "DATABASE", MatchAny))
COMPLETE_WITH_LIST9("OWNER", "TEMPLATE", "ENCODING", "TABLESPACE",
@@ -2687,6 +2709,7 @@ psql_completion(const char *text, int start, int end)
Matches4("DROP", "ACCESS", "METHOD", MatchAny) ||
(Matches4("DROP", "AGGREGATE|FUNCTION", MatchAny, MatchAny) &&
ends_with(prev_wd, ')')) ||
+ Matches4("DROP", "COMPRESSION", "METHOD", MatchAny) ||
Matches4("DROP", "EVENT", "TRIGGER", MatchAny) ||
Matches5("DROP", "FOREIGN", "DATA", "WRAPPER", MatchAny) ||
Matches4("DROP", "FOREIGN", "TABLE", MatchAny) ||
@@ -2775,6 +2798,12 @@ psql_completion(const char *text, int start, int end)
else if (Matches5("DROP", "RULE", MatchAny, "ON", MatchAny))
COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
+ /* DROP COMPRESSION METHOD */
+ else if (Matches2("DROP", "COMPRESSION"))
+ COMPLETE_WITH_CONST("METHOD");
+ else if (Matches3("DROP", "COMPRESSION", "METHOD"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_compression_methods);
+
/* EXECUTE */
else if (Matches1("EXECUTE"))
COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements);
@@ -3407,6 +3436,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
else if (TailMatchesCS1("\\db*"))
COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
+ else if (TailMatchesCS1("\\dCM*"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_compression_methods);
else if (TailMatchesCS1("\\dD*"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL);
else if (TailMatchesCS1("\\des*"))
diff --git a/src/include/access/compression.h b/src/include/access/compression.h
new file mode 100644
index 0000000000..bb4e299447
--- /dev/null
+++ b/src/include/access/compression.h
@@ -0,0 +1,68 @@
+/*-------------------------------------------------------------------------
+ *
+ * compression.h
+ * API for Postgres compression methods.
+ *
+ * Copyright (c) 2015-2016, PostgreSQL Global Development Group
+ *
+ * src/include/access/compression.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef COMPRESSION_H
+#define COMPRESSION_H
+
+#include "postgres.h"
+#include "catalog/pg_attribute.h"
+#include "nodes/nodes.h"
+#include "nodes/pg_list.h"
+
+/* parsenodes.h */
+typedef struct ColumnCompression ColumnCompression;
+typedef struct CompressionMethodRoutine CompressionMethodRoutine;
+
+typedef struct
+{
+ Oid cmoptoid;
+ CompressionMethodRoutine *routine;
+ List *options;
+} CompressionMethodOptions;
+
+typedef void (*CompressionConfigureRoutine)
+ (Form_pg_attribute attr, List *options);
+typedef struct varlena *(*CompressionRoutine)
+ (CompressionMethodOptions *cmoptions, const struct varlena *data);
+
+/*
+ * API struct for a compression method.
+ * Note this must be stored in a single palloc'd chunk of memory.
+ */
+typedef struct CompressionMethodRoutine
+{
+ NodeTag type;
+
+ CompressionConfigureRoutine configure;
+ CompressionConfigureRoutine drop;
+ CompressionRoutine compress;
+ CompressionRoutine decompress;
+} CompressionMethodRoutine;
+
+/* Compression method handler parameters */
+typedef struct CompressionMethodOpArgs
+{
+ Oid cmhanderid;
+ Oid typeid;
+} CompressionMethodOpArgs;
+
+extern CompressionMethodRoutine *GetCompressionMethodRoutine(Oid cmoptoid);
+extern List *GetCompressionOptionsList(Oid cmoptoid);
+extern Oid CreateCompressionOptions(Form_pg_attribute attr,
+ ColumnCompression *compression);
+extern ColumnCompression *GetColumnCompressionForAttribute(Form_pg_attribute att);
+extern void CheckCompressionMismatch(ColumnCompression *c1,
+ ColumnCompression *c2, const char *attributeName);
+extern void CreateColumnCompressionDependency(Form_pg_attribute attr,
+ Oid cmoptoid);
+
+#endif /* COMPRESSION_H */
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index b0d4c54121..7ff4cafacc 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -265,7 +265,9 @@ struct HeapTupleHeaderData
* information stored in t_infomask2:
*/
#define HEAP_NATTS_MASK 0x07FF /* 11 bits for number of attributes */
-/* bits 0x1800 are available */
+/* bit 0x800 is available */
+#define HEAP_HASCUSTOMCOMPRESSED 0x1000 /* tuple contain custom compressed
+ * varlenas */
#define HEAP_KEYS_UPDATED 0x2000 /* tuple was updated and key cols
* modified, or tuple deleted */
#define HEAP_HOT_UPDATED 0x4000 /* tuple was HOT-updated */
@@ -679,6 +681,9 @@ struct MinimalTupleData
#define HeapTupleHasExternal(tuple) \
(((tuple)->t_data->t_infomask & HEAP_HASEXTERNAL) != 0)
+#define HeapTupleHasCustomCompressed(tuple) \
+ (((tuple)->t_data->t_infomask2 & HEAP_HASCUSTOMCOMPRESSED) != 0)
+
#define HeapTupleIsHotUpdated(tuple) \
HeapTupleHeaderIsHotUpdated((tuple)->t_data)
@@ -794,7 +799,7 @@ extern Size heap_compute_data_size(TupleDesc tupleDesc,
extern void heap_fill_tuple(TupleDesc tupleDesc,
Datum *values, bool *isnull,
char *data, Size data_size,
- uint16 *infomask, bits8 *bit);
+ uint16 *infomask, uint16 *infomask2, bits8 *bit);
extern bool heap_attisnull(HeapTuple tup, int attnum);
extern Datum nocachegetattr(HeapTuple tup, int attnum,
TupleDesc att);
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index cd43e3a52e..2e1c92123e 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -258,7 +258,6 @@ extern void add_string_reloption(bits32 kinds, const char *name, const char *des
extern Datum transformRelOptions(Datum oldOptions, List *defList,
const char *namspace, char *validnsps[],
bool ignoreOids, bool isReset);
-extern List *untransformRelOptions(Datum options);
extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
amoptions_function amoptions);
extern relopt_value *parseRelOptions(Datum options, bool validate,
@@ -269,6 +268,8 @@ extern void fillRelOptions(void *rdopts, Size basesize,
relopt_value *options, int numoptions,
bool validate,
const relopt_parse_elt *elems, int nelems);
+extern char *formatRelOptions(List *options);
+extern List *untransformRelOptions(Datum options);
extern bytea *default_reloptions(Datum reloptions, bool validate,
relopt_kind kind);
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index 2be5af1d3e..1a8c526ca4 100644
--- a/src/include/access/tupdesc.h
+++ b/src/include/access/tupdesc.h
@@ -14,7 +14,9 @@
#ifndef TUPDESC_H
#define TUPDESC_H
+#include "postgres.h"
#include "access/attnum.h"
+#include "access/compression.h"
#include "catalog/pg_attribute.h"
#include "nodes/pg_list.h"
diff --git a/src/include/access/tuptoaster.h b/src/include/access/tuptoaster.h
index fd9f83ac44..115889c8f5 100644
--- a/src/include/access/tuptoaster.h
+++ b/src/include/access/tuptoaster.h
@@ -101,6 +101,15 @@
/* Size of an EXTERNAL datum that contains an indirection pointer */
#define INDIRECT_POINTER_SIZE (VARHDRSZ_EXTERNAL + sizeof(varatt_indirect))
+/*
+ * va_extinfo in varatt_external contains actual length of the external data
+ * and optional flags
+ */
+#define VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer) \
+ ((toast_pointer).va_extinfo & 0x3FFFFFFF)
+#define VARATT_EXTERNAL_IS_CUSTOM_COMPRESSED(toast_pointer) \
+ (((toast_pointer).va_extinfo >> 30) == 0x02)
+
/*
* Testing whether an externally-stored value is compressed now requires
* comparing extsize (the actual length of the external data) to rawsize
@@ -109,7 +118,7 @@
* saves space, so we expect either equality or less-than.
*/
#define VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer) \
- ((toast_pointer).va_extsize < (toast_pointer).va_rawsize - VARHDRSZ)
+ (VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer) < (toast_pointer).va_rawsize - VARHDRSZ)
/*
* Macro to fetch the possibly-unaligned contents of an EXTERNAL datum
@@ -210,7 +219,7 @@ extern HeapTuple toast_build_flattened_tuple(TupleDesc tupleDesc,
* Create a compressed version of a varlena datum, if possible
* ----------
*/
-extern Datum toast_compress_datum(Datum value);
+extern Datum toast_compress_datum(Datum value, Oid cmoptoid);
/* ----------
* toast_raw_datum_size -
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index b9f98423cc..36cadd409e 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -165,10 +165,12 @@ typedef enum ObjectClass
OCLASS_PUBLICATION, /* pg_publication */
OCLASS_PUBLICATION_REL, /* pg_publication_rel */
OCLASS_SUBSCRIPTION, /* pg_subscription */
- OCLASS_TRANSFORM /* pg_transform */
+ OCLASS_TRANSFORM, /* pg_transform */
+ OCLASS_COMPRESSION_METHOD, /* pg_compression */
+ OCLASS_COMPRESSION_OPTIONS /* pg_compression_opt */
} ObjectClass;
-#define LAST_OCLASS OCLASS_TRANSFORM
+#define LAST_OCLASS OCLASS_COMPRESSION_OPTIONS
/* flag bits for performDeletion/performMultipleDeletions: */
#define PERFORM_DELETION_INTERNAL 0x0001 /* internal action */
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index ef8493674c..7fe9f1f059 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -120,6 +120,14 @@ DECLARE_UNIQUE_INDEX(pg_collation_name_enc_nsp_index, 3164, on pg_collation usin
DECLARE_UNIQUE_INDEX(pg_collation_oid_index, 3085, on pg_collation using btree(oid oid_ops));
#define CollationOidIndexId 3085
+DECLARE_UNIQUE_INDEX(pg_compression_oid_index, 3422, on pg_compression using btree(oid oid_ops));
+#define CompressionMethodOidIndexId 3422
+DECLARE_UNIQUE_INDEX(pg_compression_name_index, 3423, on pg_compression using btree(cmname name_ops));
+#define CompressionMethodNameIndexId 3423
+
+DECLARE_UNIQUE_INDEX(pg_compression_opt_oid_index, 3424, on pg_compression_opt using btree(cmoptoid oid_ops));
+#define CompressionOptionsOidIndexId 3424
+
DECLARE_INDEX(pg_constraint_conname_nsp_index, 2664, on pg_constraint using btree(conname name_ops, connamespace oid_ops));
#define ConstraintNameNspIndexId 2664
DECLARE_INDEX(pg_constraint_conrelid_index, 2665, on pg_constraint using btree(conrelid oid_ops));
diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h
index bcf28e8f04..caadd61031 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -156,6 +156,9 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
/* attribute's collation */
Oid attcollation;
+ /* attribute's compression options or InvalidOid */
+ Oid attcompression;
+
#ifdef CATALOG_VARLEN /* variable-length fields start here */
/* NOTE: The following fields are not present in tuple descriptors. */
@@ -174,10 +177,10 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
* ATTRIBUTE_FIXED_PART_SIZE is the size of the fixed-layout,
* guaranteed-not-null part of a pg_attribute row. This is in fact as much
* of the row as gets copied into tuple descriptors, so don't expect you
- * can access fields beyond attcollation except in a real tuple!
+ * can access fields beyond attcompression except in a real tuple!
*/
#define ATTRIBUTE_FIXED_PART_SIZE \
- (offsetof(FormData_pg_attribute,attcollation) + sizeof(Oid))
+ (offsetof(FormData_pg_attribute,attcompression) + sizeof(Oid))
/* ----------------
* Form_pg_attribute corresponds to a pointer to a tuple with
@@ -191,29 +194,30 @@ typedef FormData_pg_attribute *Form_pg_attribute;
* ----------------
*/
-#define Natts_pg_attribute 22
-#define Anum_pg_attribute_attrelid 1
-#define Anum_pg_attribute_attname 2
-#define Anum_pg_attribute_atttypid 3
-#define Anum_pg_attribute_attstattarget 4
-#define Anum_pg_attribute_attlen 5
-#define Anum_pg_attribute_attnum 6
-#define Anum_pg_attribute_attndims 7
-#define Anum_pg_attribute_attcacheoff 8
-#define Anum_pg_attribute_atttypmod 9
-#define Anum_pg_attribute_attbyval 10
-#define Anum_pg_attribute_attstorage 11
-#define Anum_pg_attribute_attalign 12
-#define Anum_pg_attribute_attnotnull 13
-#define Anum_pg_attribute_atthasdef 14
-#define Anum_pg_attribute_attidentity 15
-#define Anum_pg_attribute_attisdropped 16
-#define Anum_pg_attribute_attislocal 17
-#define Anum_pg_attribute_attinhcount 18
-#define Anum_pg_attribute_attcollation 19
-#define Anum_pg_attribute_attacl 20
-#define Anum_pg_attribute_attoptions 21
-#define Anum_pg_attribute_attfdwoptions 22
+#define Natts_pg_attribute 23
+#define Anum_pg_attribute_attrelid 1
+#define Anum_pg_attribute_attname 2
+#define Anum_pg_attribute_atttypid 3
+#define Anum_pg_attribute_attstattarget 4
+#define Anum_pg_attribute_attlen 5
+#define Anum_pg_attribute_attnum 6
+#define Anum_pg_attribute_attndims 7
+#define Anum_pg_attribute_attcacheoff 8
+#define Anum_pg_attribute_atttypmod 9
+#define Anum_pg_attribute_attbyval 10
+#define Anum_pg_attribute_attstorage 11
+#define Anum_pg_attribute_attalign 12
+#define Anum_pg_attribute_attnotnull 13
+#define Anum_pg_attribute_atthasdef 14
+#define Anum_pg_attribute_attidentity 15
+#define Anum_pg_attribute_attisdropped 16
+#define Anum_pg_attribute_attislocal 17
+#define Anum_pg_attribute_attinhcount 18
+#define Anum_pg_attribute_attcollation 19
+#define Anum_pg_attribute_attcompression 20
+#define Anum_pg_attribute_attacl 21
+#define Anum_pg_attribute_attoptions 22
+#define Anum_pg_attribute_attfdwoptions 23
/* ----------------
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index b256657bda..40f5cc4f18 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -149,7 +149,7 @@ typedef FormData_pg_class *Form_pg_class;
*/
DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 30 0 t f f f f f f t n f 3 1 _null_ _null_ _null_));
DESCR("");
-DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 22 0 f f f f f f f t n f 3 1 _null_ _null_ _null_));
+DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 23 0 f f f f f f f t n f 3 1 _null_ _null_ _null_));
DESCR("");
DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f p r 29 0 t f f f f f f t n f 3 1 _null_ _null_ _null_));
DESCR("");
diff --git a/src/include/catalog/pg_compression.h b/src/include/catalog/pg_compression.h
new file mode 100644
index 0000000000..1d5f9ac479
--- /dev/null
+++ b/src/include/catalog/pg_compression.h
@@ -0,0 +1,55 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_compression.h
+ * definition of the system "compression method" relation (pg_compression)
+ * along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_compression.h
+ *
+ * NOTES
+ * the genbki.pl script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ * XXX do NOT break up DATA() statements into multiple lines!
+ * the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_COMPRESSION_H
+#define PG_COMPRESSION_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ * pg_compression definition. cpp turns this into
+ * typedef struct FormData_pg_compression
+ * ----------------
+ */
+#define CompressionMethodRelationId 3419
+
+CATALOG(pg_compression,3419)
+{
+ NameData cmname; /* compression method name */
+ regproc cmhandler; /* compression handler */
+} FormData_pg_compression;
+
+/* ----------------
+ * Form_pg_compression corresponds to a pointer to a tuple with
+ * the format of pg_compression relation.
+ * ----------------
+ */
+typedef FormData_pg_compression * Form_pg_compression;
+
+/* ----------------
+ * compiler constants for pg_compression
+ * ----------------
+ */
+#define Natts_pg_compression 2
+#define Anum_pg_compression_cmname 1
+#define Anum_pg_compression_cmhandler 2
+
+#endif /* PG_COMPRESSION_H */
diff --git a/src/include/catalog/pg_compression_opt.h b/src/include/catalog/pg_compression_opt.h
new file mode 100644
index 0000000000..ddcef814a7
--- /dev/null
+++ b/src/include/catalog/pg_compression_opt.h
@@ -0,0 +1,56 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_compression_opt.h
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_compression_opt.h
+ *
+ * NOTES
+ * the genbki.pl script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ * XXX do NOT break up DATA() statements into multiple lines!
+ * the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_COMPRESSION_OPT_H
+#define PG_COMPRESSION_OPT_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ * pg_compression_opt definition. cpp turns this into
+ * typedef struct FormData_pg_compression_opt
+ * ----------------
+ */
+#define CompressionOptRelationId 3420
+
+CATALOG(pg_compression_opt,3420) BKI_WITHOUT_OIDS
+{
+ Oid cmoptoid; /* compression options oid */
+ NameData cmname; /* name of compression method */
+ regproc cmhandler; /* compression handler */
+ text cmoptions[1]; /* specific options from WITH */
+} FormData_pg_compression_opt;
+
+/* ----------------
+ * Form_pg_compression_opt corresponds to a pointer to a tuple with
+ * the format of pg_compression_opt relation.
+ * ----------------
+ */
+typedef FormData_pg_compression_opt * Form_pg_compression_opt;
+
+/* ----------------
+ * compiler constants for pg_compression_opt
+ * ----------------
+ */
+#define Natts_pg_compression_opt 4
+#define Anum_pg_compression_opt_cmoptoid 1
+#define Anum_pg_compression_opt_cmname 2
+#define Anum_pg_compression_opt_cmhandler 3
+#define Anum_pg_compression_opt_cmoptions 4
+
+#endif /* PG_COMPRESSION_OPT_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index c969375981..dc3fa66f2e 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3881,6 +3881,10 @@ DATA(insert OID = 3311 ( tsm_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s
DESCR("I/O");
DATA(insert OID = 3312 ( tsm_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3310" _null_ _null_ _null_ _null_ _null_ tsm_handler_out _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 3425 ( compression_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3421 "2275" _null_ _null_ _null_ _null_ _null_ compression_handler_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 3426 ( compression_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3421" _null_ _null_ _null_ _null_ _null_ compression_handler_out _null_ _null_ _null_ ));
+DESCR("I/O");
/* tablesample method handlers */
DATA(insert OID = 3313 ( bernoulli PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3310 "2281" _null_ _null_ _null_ _null_ _null_ tsm_bernoulli_handler _null_ _null_ _null_ ));
@@ -4677,6 +4681,8 @@ DATA(insert OID = 3646 ( gtsvectorin PGNSP PGUID 12 1 0 0 0 f f f f t f i s
DESCR("I/O");
DATA(insert OID = 3647 ( gtsvectorout PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3642" _null_ _null_ _null_ _null_ _null_ gtsvectorout _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 3453 ( tsvector_compression_handler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3421 "2281" _null_ _null_ _null_ _null_ _null_ tsvector_compression_handler _null_ _null_ _null_ ));
+DESCR("tsvector compression handler");
DATA(insert OID = 3616 ( tsvector_lt PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "3614 3614" _null_ _null_ _null_ _null_ _null_ tsvector_lt _null_ _null_ _null_ ));
DATA(insert OID = 3617 ( tsvector_le PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "3614 3614" _null_ _null_ _null_ _null_ _null_ tsvector_le _null_ _null_ _null_ ));
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index e3551440a0..ec8c3df953 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -712,6 +712,8 @@ DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_han
#define TSM_HANDLEROID 3310
DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
#define ANYRANGEOID 3831
+DATA(insert OID = 3421 ( compression_handler PGNSP PGUID 4 t p P f t \054 0 0 0 compression_handler_in compression_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define COMPRESSION_HANDLEROID 3421
/*
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index bfead9af3d..a98ecc12ce 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -140,6 +140,7 @@ extern Oid RemoveUserMapping(DropUserMappingStmt *stmt);
extern void RemoveUserMappingById(Oid umId);
extern void CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid);
extern void ImportForeignSchema(ImportForeignSchemaStmt *stmt);
+extern Datum optionListToArray(List *options);
extern Datum transformGenericOptions(Oid catalogId,
Datum oldOptions,
List *options,
@@ -152,6 +153,14 @@ extern Oid get_index_am_oid(const char *amname, bool missing_ok);
extern Oid get_am_oid(const char *amname, bool missing_ok);
extern char *get_am_name(Oid amOid);
+/* commands/compressioncmds.c */
+extern ObjectAddress DefineCompressionMethod(List *names, List *parameters);
+extern void RemoveCompressionMethodById(Oid cmOid);
+extern void RemoveCompressionOptionsById(Oid cmoptoid);
+extern Oid get_compression_method_oid(const char *cmname, bool missing_ok);
+extern char *get_compression_method_name(Oid cmOid);
+extern char *get_compression_method_name_for_opt(Oid cmoptoid);
+
/* support routines in commands/define.c */
extern char *defGetString(DefElem *def);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index ffeeb4919b..6dc49a73b6 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -468,6 +468,7 @@ typedef enum NodeTag
T_PartitionBoundSpec,
T_PartitionRangeDatum,
T_PartitionCmd,
+ T_ColumnCompression,
T_VacuumRelation,
/*
@@ -498,7 +499,8 @@ typedef enum NodeTag
T_FdwRoutine, /* in foreign/fdwapi.h */
T_IndexAmRoutine, /* in access/amapi.h */
T_TsmRoutine, /* in access/tsmapi.h */
- T_ForeignKeyCacheInfo /* in utils/rel.h */
+ T_ForeignKeyCacheInfo, /* in utils/rel.h */
+ T_CompressionMethodRoutine, /* in access/compression.h */
} NodeTag;
/*
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 80c19b2a55..56e42ce1f1 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -615,6 +615,19 @@ typedef struct RangeTableSample
int location; /* method name location, or -1 if unknown */
} RangeTableSample;
+/*
+ * ColumnCompression - compression parameters for some attribute
+ *
+ * This represents compression information defined using clause:
+ * .. COMPRESSED <compression_method_name> WITH (<params>)
+ */
+typedef struct ColumnCompression
+{
+ NodeTag type;
+ char *methodName;
+ List *options;
+} ColumnCompression;
+
/*
* ColumnDef - column definition (used in various creates)
*
@@ -638,6 +651,7 @@ typedef struct ColumnDef
NodeTag type;
char *colname; /* name of column */
TypeName *typeName; /* type of column */
+ ColumnCompression *compression;
int inhcount; /* number of times column is inherited */
bool is_local; /* column has local (non-inherited) def'n */
bool is_not_null; /* NOT NULL constraint specified? */
@@ -1622,6 +1636,7 @@ typedef enum ObjectType
OBJECT_CAST,
OBJECT_COLUMN,
OBJECT_COLLATION,
+ OBJECT_COMPRESSION_METHOD,
OBJECT_CONVERSION,
OBJECT_DATABASE,
OBJECT_DEFAULT,
@@ -1769,7 +1784,8 @@ typedef enum AlterTableType
AT_DetachPartition, /* DETACH PARTITION */
AT_AddIdentity, /* ADD IDENTITY */
AT_SetIdentity, /* SET identity column options */
- AT_DropIdentity /* DROP IDENTITY */
+ AT_DropIdentity, /* DROP IDENTITY */
+ AT_AlterColumnCompression /* ALTER COLUMN name COMPRESSED cm WITH (...) */
} AlterTableType;
typedef struct ReplicaIdentityStmt
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index f50e45e886..7bfc6e6be4 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -87,6 +87,8 @@ PG_KEYWORD("comment", COMMENT, UNRESERVED_KEYWORD)
PG_KEYWORD("comments", COMMENTS, UNRESERVED_KEYWORD)
PG_KEYWORD("commit", COMMIT, UNRESERVED_KEYWORD)
PG_KEYWORD("committed", COMMITTED, UNRESERVED_KEYWORD)
+PG_KEYWORD("compressed", COMPRESSED, UNRESERVED_KEYWORD)
+PG_KEYWORD("compression", COMPRESSION, UNRESERVED_KEYWORD)
PG_KEYWORD("concurrently", CONCURRENTLY, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("configuration", CONFIGURATION, UNRESERVED_KEYWORD)
PG_KEYWORD("conflict", CONFLICT, UNRESERVED_KEYWORD)
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index e749432ef0..5cab77457a 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -22,10 +22,12 @@ extern List *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
const char *queryString);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
+void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
extern List *transformCreateSchemaStmt(CreateSchemaStmt *stmt);
extern PartitionBoundSpec *transformPartitionBound(ParseState *pstate, Relation parent,
PartitionBoundSpec *spec);
+extern void transformColumnCompression(ColumnDef *column, RangeVar *relation,
+ AlterTableStmt **alterStmt);
#endif /* PARSE_UTILCMD_H */
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 1ca9b60ea1..d21974696f 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -56,7 +56,7 @@
/*
* struct varatt_external is a traditional "TOAST pointer", that is, the
* information needed to fetch a Datum stored out-of-line in a TOAST table.
- * The data is compressed if and only if va_extsize < va_rawsize - VARHDRSZ.
+ * The data is compressed if and only if size in va_extinfo < va_rawsize - VARHDRSZ.
* This struct must not contain any padding, because we sometimes compare
* these pointers using memcmp.
*
@@ -68,7 +68,8 @@
typedef struct varatt_external
{
int32 va_rawsize; /* Original data size (includes header) */
- int32 va_extsize; /* External saved size (doesn't) */
+ uint32 va_extinfo; /* External saved size (without header) and
+ * flags */
Oid va_valueid; /* Unique ID of value within TOAST table */
Oid va_toastrelid; /* RelID of TOAST table containing it */
} varatt_external;
@@ -146,9 +147,18 @@ typedef union
struct /* Compressed-in-line format */
{
uint32 va_header;
- uint32 va_rawsize; /* Original data size (excludes header) */
+ uint32 va_info; /* Original data size (excludes header) and
+ * flags */
char va_data[FLEXIBLE_ARRAY_MEMBER]; /* Compressed data */
} va_compressed;
+ struct /* Compressed-in-line format */
+ {
+ uint32 va_header;
+ uint32 va_info; /* Original data size (excludes header) and
+ * flags */
+ Oid va_cmoptoid; /* Oid of compression options */
+ char va_data[FLEXIBLE_ARRAY_MEMBER]; /* Compressed data */
+ } va_custom_compressed;
} varattrib_4b;
typedef struct
@@ -281,8 +291,14 @@ typedef struct
#define VARDATA_1B(PTR) (((varattrib_1b *) (PTR))->va_data)
#define VARDATA_1B_E(PTR) (((varattrib_1b_e *) (PTR))->va_data)
+/* va_info in va_compress contains raw size of datum and optional flags */
#define VARRAWSIZE_4B_C(PTR) \
- (((varattrib_4b *) (PTR))->va_compressed.va_rawsize)
+ (((varattrib_4b *) (PTR))->va_compressed.va_info & 0x3FFFFFFF)
+#define VARFLAGS_4B_C(PTR) \
+ (((varattrib_4b *) (PTR))->va_compressed.va_info >> 30)
+
+#define VARHDRSZ_CUSTOM_COMPRESSED \
+ (offsetof(varattrib_4b, va_custom_compressed.va_data))
/* Externally visible macros */
@@ -311,6 +327,8 @@ typedef struct
#define VARDATA_EXTERNAL(PTR) VARDATA_1B_E(PTR)
#define VARATT_IS_COMPRESSED(PTR) VARATT_IS_4B_C(PTR)
+#define VARATT_IS_CUSTOM_COMPRESSED(PTR) (VARATT_IS_4B_C(PTR) && \
+ (VARFLAGS_4B_C(PTR) == 0x02))
#define VARATT_IS_EXTERNAL(PTR) VARATT_IS_1B_E(PTR)
#define VARATT_IS_EXTERNAL_ONDISK(PTR) \
(VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_ONDISK)
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 254a811aff..6ad889af7a 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -210,6 +210,7 @@ typedef enum AclObjectKind
ACL_KIND_EXTENSION, /* pg_extension */
ACL_KIND_PUBLICATION, /* pg_publication */
ACL_KIND_SUBSCRIPTION, /* pg_subscription */
+ ACL_KIND_COMPRESSION_METHOD, /* pg_compression */
MAX_ACL_KIND /* MUST BE LAST */
} AclObjectKind;
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 8a0be41929..ff7cb530fd 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -48,6 +48,9 @@ enum SysCacheIdentifier
CLAOID,
COLLNAMEENCNSP,
COLLOID,
+ COMPRESSIONMETHODOID,
+ COMPRESSIONMETHODNAME,
+ COMPRESSIONOPTIONSOID,
CONDEFAULT,
CONNAMENSP,
CONSTROID,
diff --git a/src/test/regress/expected/copy2.out b/src/test/regress/expected/copy2.out
index 65e9c626b3..112f0eda47 100644
--- a/src/test/regress/expected/copy2.out
+++ b/src/test/regress/expected/copy2.out
@@ -438,10 +438,10 @@ begin
end $$ language plpgsql immutable;
alter table check_con_tbl add check (check_con_function(check_con_tbl.*));
\d+ check_con_tbl
- Table "public.check_con_tbl"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- f1 | integer | | | | plain | |
+ Table "public.check_con_tbl"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ f1 | integer | | | | plain | | |
Check constraints:
"check_con_tbl_check" CHECK (check_con_function(check_con_tbl.*))
diff --git a/src/test/regress/expected/create_cm.out b/src/test/regress/expected/create_cm.out
new file mode 100644
index 0000000000..119fac595a
--- /dev/null
+++ b/src/test/regress/expected/create_cm.out
@@ -0,0 +1,154 @@
+-- test drop
+CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
+CREATE TABLE droptest(fts tsvector COMPRESSED ts1);
+DROP COMPRESSION METHOD ts1;
+ERROR: cannot drop compression method ts1 because other objects depend on it
+DETAIL: compression options for ts1 depends on compression method ts1
+table droptest column fts depends on compression options for ts1
+HINT: Use DROP ... CASCADE to drop the dependent objects too.
+DROP COMPRESSION METHOD ts1 CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to compression options for ts1
+drop cascades to table droptest column fts
+\d+ droptest
+ Table "public.droptest"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+---------+-------------+--------------+-------------
+
+DROP TABLE droptest;
+CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
+CREATE TABLE cmtest(fts tsvector COMPRESSED ts1);
+SELECT * FROM pg_compression;
+ cmname | cmhandler
+--------+------------------------------
+ ts1 | tsvector_compression_handler
+(1 row)
+
+SELECT cmhandler, cmoptions FROM pg_compression_opt;
+ cmhandler | cmoptions
+------------------------------+-----------
+ tsvector_compression_handler |
+(1 row)
+
+\dCM
+List of compression methods
+ Name
+------
+ ts1
+(1 row)
+
+\d+ cmtest
+ Table "public.cmtest"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+
+INSERT INTO cmtest
+ SELECT to_tsvector(string_agg(repeat(substr(i::text,1,1), i), ' '))
+ FROM generate_series(1,200) i;
+SELECT length(fts) FROM cmtest;
+ length
+--------
+ 200
+(1 row)
+
+SELECT length(fts) FROM cmtest;
+ length
+--------
+ 200
+(1 row)
+
+-- check ALTER commands
+ALTER TABLE cmtest ALTER COLUMN fts SET NOT COMPRESSED;
+\d+ cmtest
+ Table "public.cmtest"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | | |
+
+ALTER TABLE cmtest ALTER COLUMN fts SET COMPRESSED ts1 WITH (format 'lz');
+ERROR: the compression method "ts1" does not take any options
+ALTER TABLE cmtest ALTER COLUMN fts SET COMPRESSED ts1;
+\d+ cmtest
+ Table "public.cmtest"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+
+-- create different types of tables
+SELECT * INTO cmtest2 FROM cmtest;
+CREATE TABLE cmtest3 (LIKE cmtest);
+CREATE TABLE cmtest4(fts tsvector, a int) INHERITS (cmtest);
+NOTICE: merging column "fts" with inherited definition
+CREATE TABLE cmtest5(fts tsvector);
+CREATE TABLE cmtest6(fts tsvector);
+INSERT INTO cmtest6 SELECT * FROM cmtest;
+-- we update usual datum with compressed datum
+INSERT INTO cmtest5
+ SELECT to_tsvector(string_agg(repeat(substr(i::text,1,1), i), ' '))
+ FROM generate_series(1,200) i;
+UPDATE cmtest5 SET fts = cmtest.fts FROM cmtest;
+\d+ cmtest3
+ Table "public.cmtest3"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+
+\d+ cmtest4
+ Table "public.cmtest4"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+ a | integer | | | | plain | | |
+Inherits: cmtest
+
+DROP TABLE cmtest CASCADE;
+NOTICE: drop cascades to table cmtest4
+DROP TABLE cmtest3;
+SELECT cmhandler, cmoptions FROM pg_compression_opt;
+ cmhandler | cmoptions
+------------------------------+-----------
+ tsvector_compression_handler |
+ tsvector_compression_handler |
+ tsvector_compression_handler |
+ tsvector_compression_handler |
+(4 rows)
+
+DROP COMPRESSION METHOD ts1 CASCADE;
+NOTICE: drop cascades to 4 other objects
+DETAIL: drop cascades to compression options for ts1
+drop cascades to compression options for ts1
+drop cascades to compression options for ts1
+drop cascades to compression options for ts1
+SELECT * FROM pg_compression;
+ cmname | cmhandler
+--------+-----------
+(0 rows)
+
+SELECT * FROM pg_compression_opt;
+ cmoptoid | cmname | cmhandler | cmoptions
+----------+--------+-----------+-----------
+(0 rows)
+
+-- check that moved tuples still can be decompressed
+SELECT length(fts) FROM cmtest2;
+ length
+--------
+ 200
+(1 row)
+
+SELECT length(fts) FROM cmtest5;
+ length
+--------
+ 200
+(1 row)
+
+SELECT length(fts) FROM cmtest6;
+ length
+--------
+ 200
+(1 row)
+
+DROP TABLE cmtest2;
+DROP TABLE cmtest5;
+DROP TABLE cmtest6;
diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out
index 335cd37e18..b511daf9fa 100644
--- a/src/test/regress/expected/create_table.out
+++ b/src/test/regress/expected/create_table.out
@@ -590,10 +590,10 @@ CREATE TABLE oids_parted (
) PARTITION BY RANGE (a) WITH OIDS;
CREATE TABLE part_forced_oids PARTITION OF oids_parted FOR VALUES FROM (1) TO (10) WITHOUT OIDS;
\d+ part_forced_oids
- Table "public.part_forced_oids"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
+ Table "public.part_forced_oids"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
Partition of: oids_parted FOR VALUES FROM (1) TO (10)
Partition constraint: ((a IS NOT NULL) AND (a >= 1) AND (a < 10))
Has OIDs: yes
@@ -729,11 +729,11 @@ CREATE TABLE part_c PARTITION OF parted (b WITH OPTIONS NOT NULL DEFAULT 0) FOR
CREATE TABLE part_c_1_10 PARTITION OF part_c FOR VALUES FROM (1) TO (10);
-- Partition bound in describe output
\d+ part_b
- Table "public.part_b"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | not null | 1 | plain | |
+ Table "public.part_b"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | not null | 1 | plain | | |
Partition of: parted FOR VALUES IN ('b')
Partition constraint: ((a IS NOT NULL) AND (a = ANY (ARRAY['b'::text])))
Check constraints:
@@ -756,11 +756,11 @@ Partitions: part_c_1_10 FOR VALUES FROM (1) TO (10)
-- a level-2 partition's constraint will include the parent's expressions
\d+ part_c_1_10
- Table "public.part_c_1_10"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | not null | 0 | plain | |
+ Table "public.part_c_1_10"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | not null | 0 | plain | | |
Partition of: part_c FOR VALUES FROM (1) TO (10)
Partition constraint: ((a IS NOT NULL) AND (a = ANY (ARRAY['c'::text])) AND (b IS NOT NULL) AND (b >= 1) AND (b < 10))
Check constraints:
@@ -793,46 +793,46 @@ Number of partitions: 3 (Use \d+ to list them.)
CREATE TABLE range_parted4 (a int, b int, c int) PARTITION BY RANGE (abs(a), abs(b), c);
CREATE TABLE unbounded_range_part PARTITION OF range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (MAXVALUE, MAXVALUE, MAXVALUE);
\d+ unbounded_range_part
- Table "public.unbounded_range_part"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
+ Table "public.unbounded_range_part"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
Partition of: range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (MAXVALUE, MAXVALUE, MAXVALUE)
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL))
DROP TABLE unbounded_range_part;
CREATE TABLE range_parted4_1 PARTITION OF range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (1, MAXVALUE, MAXVALUE);
\d+ range_parted4_1
- Table "public.range_parted4_1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
+ Table "public.range_parted4_1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
Partition of: range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (1, MAXVALUE, MAXVALUE)
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND (abs(a) <= 1))
CREATE TABLE range_parted4_2 PARTITION OF range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, MAXVALUE);
\d+ range_parted4_2
- Table "public.range_parted4_2"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
+ Table "public.range_parted4_2"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
Partition of: range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, MAXVALUE)
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND ((abs(a) > 3) OR ((abs(a) = 3) AND (abs(b) > 4)) OR ((abs(a) = 3) AND (abs(b) = 4) AND (c >= 5))) AND ((abs(a) < 6) OR ((abs(a) = 6) AND (abs(b) <= 7))))
CREATE TABLE range_parted4_3 PARTITION OF range_parted4 FOR VALUES FROM (6, 8, MINVALUE) TO (9, MAXVALUE, MAXVALUE);
\d+ range_parted4_3
- Table "public.range_parted4_3"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
+ Table "public.range_parted4_3"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
Partition of: range_parted4 FOR VALUES FROM (6, 8, MINVALUE) TO (9, MAXVALUE, MAXVALUE)
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND ((abs(a) > 6) OR ((abs(a) = 6) AND (abs(b) >= 8))) AND (abs(a) <= 9))
diff --git a/src/test/regress/expected/create_table_like.out b/src/test/regress/expected/create_table_like.out
index 3f405c94ce..b5ca8b820b 100644
--- a/src/test/regress/expected/create_table_like.out
+++ b/src/test/regress/expected/create_table_like.out
@@ -156,32 +156,32 @@ CREATE TABLE ctlt4 (a text, c text);
ALTER TABLE ctlt4 ALTER COLUMN c SET STORAGE EXTERNAL;
CREATE TABLE ctlt12_storage (LIKE ctlt1 INCLUDING STORAGE, LIKE ctlt2 INCLUDING STORAGE);
\d+ ctlt12_storage
- Table "public.ctlt12_storage"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | |
- b | text | | | | extended | |
- c | text | | | | external | |
+ Table "public.ctlt12_storage"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | |
+ b | text | | | | extended | | |
+ c | text | | | | external | | |
CREATE TABLE ctlt12_comments (LIKE ctlt1 INCLUDING COMMENTS, LIKE ctlt2 INCLUDING COMMENTS);
\d+ ctlt12_comments
- Table "public.ctlt12_comments"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | extended | | A
- b | text | | | | extended | | B
- c | text | | | | extended | | C
+ Table "public.ctlt12_comments"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | extended | | | A
+ b | text | | | | extended | | | B
+ c | text | | | | extended | | | C
CREATE TABLE ctlt1_inh (LIKE ctlt1 INCLUDING CONSTRAINTS INCLUDING COMMENTS) INHERITS (ctlt1);
NOTICE: merging column "a" with inherited definition
NOTICE: merging column "b" with inherited definition
NOTICE: merging constraint "ctlt1_a_check" with inherited definition
\d+ ctlt1_inh
- Table "public.ctlt1_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | | A
- b | text | | | | extended | | B
+ Table "public.ctlt1_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | | A
+ b | text | | | | extended | | | B
Check constraints:
"ctlt1_a_check" CHECK (length(a) > 2)
Inherits: ctlt1
@@ -195,12 +195,12 @@ SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_con
CREATE TABLE ctlt13_inh () INHERITS (ctlt1, ctlt3);
NOTICE: merging multiple inherited definitions of column "a"
\d+ ctlt13_inh
- Table "public.ctlt13_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | |
- b | text | | | | extended | |
- c | text | | | | external | |
+ Table "public.ctlt13_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | |
+ b | text | | | | extended | | |
+ c | text | | | | external | | |
Check constraints:
"ctlt1_a_check" CHECK (length(a) > 2)
"ctlt3_a_check" CHECK (length(a) < 5)
@@ -210,12 +210,12 @@ Inherits: ctlt1,
CREATE TABLE ctlt13_like (LIKE ctlt3 INCLUDING CONSTRAINTS INCLUDING COMMENTS INCLUDING STORAGE) INHERITS (ctlt1);
NOTICE: merging column "a" with inherited definition
\d+ ctlt13_like
- Table "public.ctlt13_like"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | | A3
- b | text | | | | extended | |
- c | text | | | | external | | C
+ Table "public.ctlt13_like"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | | A3
+ b | text | | | | extended | | |
+ c | text | | | | external | | | C
Check constraints:
"ctlt1_a_check" CHECK (length(a) > 2)
"ctlt3_a_check" CHECK (length(a) < 5)
@@ -229,11 +229,11 @@ SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_con
CREATE TABLE ctlt_all (LIKE ctlt1 INCLUDING ALL);
\d+ ctlt_all
- Table "public.ctlt_all"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | | A
- b | text | | | | extended | | B
+ Table "public.ctlt_all"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | | A
+ b | text | | | | extended | | | B
Indexes:
"ctlt_all_pkey" PRIMARY KEY, btree (a)
"ctlt_all_b_idx" btree (b)
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index f4eebb75cf..2ac75c44b5 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -272,10 +272,10 @@ explain (verbose, costs off)
create rule silly as on delete to dcomptable do instead
update dcomptable set d1.r = (d1).r - 1, d1.i = (d1).i + 1 where (d1).i > 0;
\d+ dcomptable
- Table "public.dcomptable"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+-----------+-----------+----------+---------+----------+--------------+-------------
- d1 | dcomptype | | | | extended | |
+ Table "public.dcomptable"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+-----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ d1 | dcomptype | | | | extended | | |
Indexes:
"dcomptable_d1_key" UNIQUE CONSTRAINT, btree (d1)
Rules:
@@ -409,10 +409,10 @@ create rule silly as on delete to dcomptable do instead
update dcomptable set d1[1].r = d1[1].r - 1, d1[1].i = d1[1].i + 1
where d1[1].i > 0;
\d+ dcomptable
- Table "public.dcomptable"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------------+-----------+----------+---------+----------+--------------+-------------
- d1 | dcomptypea | | | | extended | |
+ Table "public.dcomptable"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------------+-----------+----------+---------+----------+-------------+--------------+-------------
+ d1 | dcomptypea | | | | extended | | |
Indexes:
"dcomptable_d1_key" UNIQUE CONSTRAINT, btree (d1)
Rules:
diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out
index 331f7a911f..530ce1b1d0 100644
--- a/src/test/regress/expected/foreign_data.out
+++ b/src/test/regress/expected/foreign_data.out
@@ -1330,12 +1330,12 @@ CREATE TABLE pt1 (
CREATE FOREIGN TABLE ft2 () INHERITS (pt1)
SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value');
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Child tables: ft2
\d+ ft2
@@ -1351,12 +1351,12 @@ Inherits: pt1
DROP FOREIGN TABLE ft2;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
CREATE FOREIGN TABLE ft2 (
c1 integer NOT NULL,
@@ -1375,12 +1375,12 @@ FDW options: (delimiter ',', quote '"', "be quoted" 'value')
ALTER FOREIGN TABLE ft2 INHERIT pt1;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Child tables: ft2
\d+ ft2
@@ -1418,12 +1418,12 @@ Child tables: ct3,
ft3
\d+ ct3
- Table "public.ct3"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.ct3"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Inherits: ft2
\d+ ft3
@@ -1443,17 +1443,17 @@ ALTER TABLE pt1 ADD COLUMN c6 integer;
ALTER TABLE pt1 ADD COLUMN c7 integer NOT NULL;
ALTER TABLE pt1 ADD COLUMN c8 integer;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
- c4 | integer | | | | plain | |
- c5 | integer | | | 0 | plain | |
- c6 | integer | | | | plain | |
- c7 | integer | | not null | | plain | |
- c8 | integer | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
+ c4 | integer | | | | plain | | |
+ c5 | integer | | | 0 | plain | | |
+ c6 | integer | | | | plain | | |
+ c7 | integer | | not null | | plain | | |
+ c8 | integer | | | | plain | | |
Child tables: ft2
\d+ ft2
@@ -1475,17 +1475,17 @@ Child tables: ct3,
ft3
\d+ ct3
- Table "public.ct3"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
- c4 | integer | | | | plain | |
- c5 | integer | | | 0 | plain | |
- c6 | integer | | | | plain | |
- c7 | integer | | not null | | plain | |
- c8 | integer | | | | plain | |
+ Table "public.ct3"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
+ c4 | integer | | | | plain | | |
+ c5 | integer | | | 0 | plain | | |
+ c6 | integer | | | | plain | | |
+ c7 | integer | | not null | | plain | | |
+ c8 | integer | | | | plain | | |
Inherits: ft2
\d+ ft3
@@ -1517,17 +1517,17 @@ ALTER TABLE pt1 ALTER COLUMN c1 SET (n_distinct = 100);
ALTER TABLE pt1 ALTER COLUMN c8 SET STATISTICS -1;
ALTER TABLE pt1 ALTER COLUMN c8 SET STORAGE EXTERNAL;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
- c4 | integer | | | 0 | plain | |
- c5 | integer | | | | plain | |
- c6 | integer | | not null | | plain | |
- c7 | integer | | | | plain | |
- c8 | text | | | | external | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
+ c4 | integer | | | 0 | plain | | |
+ c5 | integer | | | | plain | | |
+ c6 | integer | | not null | | plain | | |
+ c7 | integer | | | | plain | | |
+ c8 | text | | | | external | | |
Child tables: ft2
\d+ ft2
@@ -1555,12 +1555,12 @@ ALTER TABLE pt1 DROP COLUMN c6;
ALTER TABLE pt1 DROP COLUMN c7;
ALTER TABLE pt1 DROP COLUMN c8;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Child tables: ft2
\d+ ft2
@@ -1592,12 +1592,12 @@ SELECT relname, conname, contype, conislocal, coninhcount, connoinherit
-- child does not inherit NO INHERIT constraints
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk1" CHECK (c1 > 0) NO INHERIT
"pt1chk2" CHECK (c2 <> ''::text)
@@ -1636,12 +1636,12 @@ ALTER FOREIGN TABLE ft2 ADD CONSTRAINT pt1chk2 CHECK (c2 <> '');
ALTER FOREIGN TABLE ft2 INHERIT pt1;
-- child does not inherit NO INHERIT constraints
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk1" CHECK (c1 > 0) NO INHERIT
"pt1chk2" CHECK (c2 <> ''::text)
@@ -1667,12 +1667,12 @@ ALTER TABLE pt1 DROP CONSTRAINT pt1chk2 CASCADE;
INSERT INTO pt1 VALUES (1, 'pt1'::text, '1994-01-01'::date);
ALTER TABLE pt1 ADD CONSTRAINT pt1chk3 CHECK (c2 <> '') NOT VALID;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk3" CHECK (c2 <> ''::text) NOT VALID
Child tables: ft2
@@ -1694,12 +1694,12 @@ Inherits: pt1
-- VALIDATE CONSTRAINT need do nothing on foreign tables
ALTER TABLE pt1 VALIDATE CONSTRAINT pt1chk3;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk3" CHECK (c2 <> ''::text)
Child tables: ft2
@@ -1721,12 +1721,12 @@ Inherits: pt1
-- OID system column
ALTER TABLE pt1 SET WITH OIDS;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk3" CHECK (c2 <> ''::text)
Child tables: ft2
@@ -1751,12 +1751,12 @@ ALTER TABLE ft2 SET WITHOUT OIDS; -- ERROR
ERROR: cannot drop inherited column "oid"
ALTER TABLE pt1 SET WITHOUT OIDS;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk3" CHECK (c2 <> ''::text)
Child tables: ft2
@@ -1782,12 +1782,12 @@ ALTER TABLE pt1 RENAME COLUMN c3 TO f3;
-- changes name of a constraint recursively
ALTER TABLE pt1 RENAME CONSTRAINT pt1chk3 TO f2_check;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- f1 | integer | | not null | | plain | 10000 |
- f2 | text | | | | extended | |
- f3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ f1 | integer | | not null | | plain | | 10000 |
+ f2 | text | | | | extended | | |
+ f3 | date | | | | plain | | |
Check constraints:
"f2_check" CHECK (f2 <> ''::text)
Child tables: ft2
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index c698faff2f..a147b8217f 100644
--- a/src/test/regress/expected/inherit.out
+++ b/src/test/regress/expected/inherit.out
@@ -1023,13 +1023,13 @@ ALTER TABLE inhts RENAME aa TO aaa; -- to be failed
ERROR: cannot rename inherited column "aa"
ALTER TABLE inhts RENAME d TO dd;
\d+ inhts
- Table "public.inhts"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- aa | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
- dd | integer | | | | plain | |
+ Table "public.inhts"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ aa | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
+ dd | integer | | | | plain | | |
Inherits: inht1,
inhs1
@@ -1042,14 +1042,14 @@ NOTICE: merging multiple inherited definitions of column "aa"
NOTICE: merging multiple inherited definitions of column "b"
ALTER TABLE inht1 RENAME aa TO aaa;
\d+ inht4
- Table "public.inht4"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- aaa | integer | | | | plain | |
- b | integer | | | | plain | |
- x | integer | | | | plain | |
- y | integer | | | | plain | |
- z | integer | | | | plain | |
+ Table "public.inht4"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ aaa | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ x | integer | | | | plain | | |
+ y | integer | | | | plain | | |
+ z | integer | | | | plain | | |
Inherits: inht2,
inht3
@@ -1059,14 +1059,14 @@ ALTER TABLE inht1 RENAME aaa TO aaaa;
ALTER TABLE inht1 RENAME b TO bb; -- to be failed
ERROR: cannot rename inherited column "b"
\d+ inhts
- Table "public.inhts"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- aaaa | integer | | | | plain | |
- b | integer | | | | plain | |
- x | integer | | | | plain | |
- c | integer | | | | plain | |
- d | integer | | | | plain | |
+ Table "public.inhts"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ aaaa | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ x | integer | | | | plain | | |
+ c | integer | | | | plain | | |
+ d | integer | | | | plain | | |
Inherits: inht2,
inhs1
@@ -1106,33 +1106,33 @@ drop cascades to table inht4
CREATE TABLE test_constraints (id int, val1 varchar, val2 int, UNIQUE(val1, val2));
CREATE TABLE test_constraints_inh () INHERITS (test_constraints);
\d+ test_constraints
- Table "public.test_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+-------------------+-----------+----------+---------+----------+--------------+-------------
- id | integer | | | | plain | |
- val1 | character varying | | | | extended | |
- val2 | integer | | | | plain | |
+ Table "public.test_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+-------------------+-----------+----------+---------+----------+-------------+--------------+-------------
+ id | integer | | | | plain | | |
+ val1 | character varying | | | | extended | | |
+ val2 | integer | | | | plain | | |
Indexes:
"test_constraints_val1_val2_key" UNIQUE CONSTRAINT, btree (val1, val2)
Child tables: test_constraints_inh
ALTER TABLE ONLY test_constraints DROP CONSTRAINT test_constraints_val1_val2_key;
\d+ test_constraints
- Table "public.test_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+-------------------+-----------+----------+---------+----------+--------------+-------------
- id | integer | | | | plain | |
- val1 | character varying | | | | extended | |
- val2 | integer | | | | plain | |
+ Table "public.test_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+-------------------+-----------+----------+---------+----------+-------------+--------------+-------------
+ id | integer | | | | plain | | |
+ val1 | character varying | | | | extended | | |
+ val2 | integer | | | | plain | | |
Child tables: test_constraints_inh
\d+ test_constraints_inh
- Table "public.test_constraints_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+-------------------+-----------+----------+---------+----------+--------------+-------------
- id | integer | | | | plain | |
- val1 | character varying | | | | extended | |
- val2 | integer | | | | plain | |
+ Table "public.test_constraints_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+-------------------+-----------+----------+---------+----------+-------------+--------------+-------------
+ id | integer | | | | plain | | |
+ val1 | character varying | | | | extended | | |
+ val2 | integer | | | | plain | | |
Inherits: test_constraints
DROP TABLE test_constraints_inh;
@@ -1143,27 +1143,27 @@ CREATE TABLE test_ex_constraints (
);
CREATE TABLE test_ex_constraints_inh () INHERITS (test_ex_constraints);
\d+ test_ex_constraints
- Table "public.test_ex_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+--------+-----------+----------+---------+---------+--------------+-------------
- c | circle | | | | plain | |
+ Table "public.test_ex_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+--------+-----------+----------+---------+---------+-------------+--------------+-------------
+ c | circle | | | | plain | | |
Indexes:
"test_ex_constraints_c_excl" EXCLUDE USING gist (c WITH &&)
Child tables: test_ex_constraints_inh
ALTER TABLE test_ex_constraints DROP CONSTRAINT test_ex_constraints_c_excl;
\d+ test_ex_constraints
- Table "public.test_ex_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+--------+-----------+----------+---------+---------+--------------+-------------
- c | circle | | | | plain | |
+ Table "public.test_ex_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+--------+-----------+----------+---------+---------+-------------+--------------+-------------
+ c | circle | | | | plain | | |
Child tables: test_ex_constraints_inh
\d+ test_ex_constraints_inh
- Table "public.test_ex_constraints_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+--------+-----------+----------+---------+---------+--------------+-------------
- c | circle | | | | plain | |
+ Table "public.test_ex_constraints_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+--------+-----------+----------+---------+---------+-------------+--------------+-------------
+ c | circle | | | | plain | | |
Inherits: test_ex_constraints
DROP TABLE test_ex_constraints_inh;
@@ -1173,37 +1173,37 @@ CREATE TABLE test_primary_constraints(id int PRIMARY KEY);
CREATE TABLE test_foreign_constraints(id1 int REFERENCES test_primary_constraints(id));
CREATE TABLE test_foreign_constraints_inh () INHERITS (test_foreign_constraints);
\d+ test_primary_constraints
- Table "public.test_primary_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- id | integer | | not null | | plain | |
+ Table "public.test_primary_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ id | integer | | not null | | plain | | |
Indexes:
"test_primary_constraints_pkey" PRIMARY KEY, btree (id)
Referenced by:
TABLE "test_foreign_constraints" CONSTRAINT "test_foreign_constraints_id1_fkey" FOREIGN KEY (id1) REFERENCES test_primary_constraints(id)
\d+ test_foreign_constraints
- Table "public.test_foreign_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- id1 | integer | | | | plain | |
+ Table "public.test_foreign_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ id1 | integer | | | | plain | | |
Foreign-key constraints:
"test_foreign_constraints_id1_fkey" FOREIGN KEY (id1) REFERENCES test_primary_constraints(id)
Child tables: test_foreign_constraints_inh
ALTER TABLE test_foreign_constraints DROP CONSTRAINT test_foreign_constraints_id1_fkey;
\d+ test_foreign_constraints
- Table "public.test_foreign_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- id1 | integer | | | | plain | |
+ Table "public.test_foreign_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ id1 | integer | | | | plain | | |
Child tables: test_foreign_constraints_inh
\d+ test_foreign_constraints_inh
- Table "public.test_foreign_constraints_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- id1 | integer | | | | plain | |
+ Table "public.test_foreign_constraints_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ id1 | integer | | | | plain | | |
Inherits: test_foreign_constraints
DROP TABLE test_foreign_constraints_inh;
diff --git a/src/test/regress/expected/insert.out b/src/test/regress/expected/insert.out
index 9d84ba4658..bbab453247 100644
--- a/src/test/regress/expected/insert.out
+++ b/src/test/regress/expected/insert.out
@@ -142,11 +142,11 @@ create rule irule3 as on insert to inserttest2 do also
insert into inserttest (f4[1].if1, f4[1].if2[2])
select new.f1, new.f2;
\d+ inserttest2
- Table "public.inserttest2"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+--------+-----------+----------+---------+----------+--------------+-------------
- f1 | bigint | | | | plain | |
- f2 | text | | | | extended | |
+ Table "public.inserttest2"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+--------+-----------+----------+---------+----------+-------------+--------------+-------------
+ f1 | bigint | | | | plain | | |
+ f2 | text | | | | extended | | |
Rules:
irule1 AS
ON INSERT TO inserttest2 DO INSERT INTO inserttest (f3.if2[1], f3.if2[2])
@@ -435,10 +435,10 @@ drop function dummy_hashint4(a int4, seed int8);
create table list_parted (a int) partition by list (a);
create table part_default partition of list_parted default;
\d+ part_default
- Table "public.part_default"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
+ Table "public.part_default"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
Partition of: list_parted DEFAULT
No partition constraint
@@ -750,74 +750,74 @@ Partitions: mcrparted1_lt_b FOR VALUES FROM (MINVALUE, MINVALUE) TO ('b', MINVAL
mcrparted8_ge_d FOR VALUES FROM ('d', MINVALUE) TO (MAXVALUE, MAXVALUE)
\d+ mcrparted1_lt_b
- Table "public.mcrparted1_lt_b"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted1_lt_b"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM (MINVALUE, MINVALUE) TO ('b', MINVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a < 'b'::text))
\d+ mcrparted2_b
- Table "public.mcrparted2_b"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted2_b"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('b', MINVALUE) TO ('c', MINVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'b'::text) AND (a < 'c'::text))
\d+ mcrparted3_c_to_common
- Table "public.mcrparted3_c_to_common"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted3_c_to_common"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('c', MINVALUE) TO ('common', MINVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'c'::text) AND (a < 'common'::text))
\d+ mcrparted4_common_lt_0
- Table "public.mcrparted4_common_lt_0"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted4_common_lt_0"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('common', MINVALUE) TO ('common', 0)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::text) AND (b < 0))
\d+ mcrparted5_common_0_to_10
- Table "public.mcrparted5_common_0_to_10"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted5_common_0_to_10"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('common', 0) TO ('common', 10)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::text) AND (b >= 0) AND (b < 10))
\d+ mcrparted6_common_ge_10
- Table "public.mcrparted6_common_ge_10"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted6_common_ge_10"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('common', 10) TO ('common', MAXVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::text) AND (b >= 10))
\d+ mcrparted7_gt_common_lt_d
- Table "public.mcrparted7_gt_common_lt_d"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted7_gt_common_lt_d"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('common', MAXVALUE) TO ('d', MINVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a > 'common'::text) AND (a < 'd'::text))
\d+ mcrparted8_ge_d
- Table "public.mcrparted8_ge_d"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted8_ge_d"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('d', MINVALUE) TO (MAXVALUE, MAXVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'd'::text))
diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out
index b101331d69..f805e13d75 100644
--- a/src/test/regress/expected/publication.out
+++ b/src/test/regress/expected/publication.out
@@ -65,11 +65,11 @@ SELECT pubname, puballtables FROM pg_publication WHERE pubname = 'testpub_forall
(1 row)
\d+ testpub_tbl2
- Table "public.testpub_tbl2"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('testpub_tbl2_id_seq'::regclass) | plain | |
- data | text | | | | extended | |
+ Table "public.testpub_tbl2"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('testpub_tbl2_id_seq'::regclass) | plain | | |
+ data | text | | | | extended | | |
Indexes:
"testpub_tbl2_pkey" PRIMARY KEY, btree (id)
Publications:
@@ -141,22 +141,22 @@ ALTER PUBLICATION testpub_default SET TABLE testpub_tbl1;
ALTER PUBLICATION testpub_default ADD TABLE pub_test.testpub_nopk;
ALTER PUBLICATION testpib_ins_trunct ADD TABLE pub_test.testpub_nopk, testpub_tbl1;
\d+ pub_test.testpub_nopk
- Table "pub_test.testpub_nopk"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- foo | integer | | | | plain | |
- bar | integer | | | | plain | |
+ Table "pub_test.testpub_nopk"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ foo | integer | | | | plain | | |
+ bar | integer | | | | plain | | |
Publications:
"testpib_ins_trunct"
"testpub_default"
"testpub_fortbl"
\d+ testpub_tbl1
- Table "public.testpub_tbl1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | |
- data | text | | | | extended | |
+ Table "public.testpub_tbl1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | | |
+ data | text | | | | extended | | |
Indexes:
"testpub_tbl1_pkey" PRIMARY KEY, btree (id)
Publications:
@@ -178,11 +178,11 @@ ALTER PUBLICATION testpub_default DROP TABLE testpub_tbl1, pub_test.testpub_nopk
ALTER PUBLICATION testpub_default DROP TABLE pub_test.testpub_nopk;
ERROR: relation "testpub_nopk" is not part of the publication
\d+ testpub_tbl1
- Table "public.testpub_tbl1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | |
- data | text | | | | extended | |
+ Table "public.testpub_tbl1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | | |
+ data | text | | | | extended | | |
Indexes:
"testpub_tbl1_pkey" PRIMARY KEY, btree (id)
Publications:
diff --git a/src/test/regress/expected/replica_identity.out b/src/test/regress/expected/replica_identity.out
index 67c34a92a4..1526437bf8 100644
--- a/src/test/regress/expected/replica_identity.out
+++ b/src/test/regress/expected/replica_identity.out
@@ -158,13 +158,13 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass;
(1 row)
\d+ test_replica_identity
- Table "public.test_replica_identity"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('test_replica_identity_id_seq'::regclass) | plain | |
- keya | text | | not null | | extended | |
- keyb | text | | not null | | extended | |
- nonkey | text | | | | extended | |
+ Table "public.test_replica_identity"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('test_replica_identity_id_seq'::regclass) | plain | | |
+ keya | text | | not null | | extended | | |
+ keyb | text | | not null | | extended | | |
+ nonkey | text | | | | extended | | |
Indexes:
"test_replica_identity_pkey" PRIMARY KEY, btree (id)
"test_replica_identity_expr" UNIQUE, btree (keya, keyb, (3))
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index f1c1b44d6f..e358ff539c 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2799,11 +2799,11 @@ select * from rules_log;
create rule r3 as on delete to rules_src do notify rules_src_deletion;
\d+ rules_src
- Table "public.rules_src"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- f1 | integer | | | | plain | |
- f2 | integer | | | | plain | |
+ Table "public.rules_src"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ f1 | integer | | | | plain | | |
+ f2 | integer | | | | plain | | |
Rules:
r1 AS
ON UPDATE TO rules_src DO INSERT INTO rules_log (f1, f2, tag) VALUES (old.f1,old.f2,'old'::text), (new.f1,new.f2,'new'::text)
@@ -2819,11 +2819,11 @@ Rules:
create rule r4 as on insert to rules_src do instead insert into rules_log AS trgt SELECT NEW.* RETURNING trgt.f1, trgt.f2;
create rule r5 as on update to rules_src do instead UPDATE rules_log AS trgt SET tag = 'updated' WHERE trgt.f1 = new.f1;
\d+ rules_src
- Table "public.rules_src"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- f1 | integer | | | | plain | |
- f2 | integer | | | | plain | |
+ Table "public.rules_src"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ f1 | integer | | | | plain | | |
+ f2 | integer | | | | plain | | |
Rules:
r1 AS
ON UPDATE TO rules_src DO INSERT INTO rules_log (f1, f2, tag) VALUES (old.f1,old.f2,'old'::text), (new.f1,new.f2,'new'::text)
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index e996640593..62b06da011 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -111,6 +111,8 @@ pg_authid|t
pg_cast|t
pg_class|t
pg_collation|t
+pg_compression|t
+pg_compression_opt|t
pg_constraint|t
pg_conversion|t
pg_database|t
diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out
index a4fe96112e..adbe764196 100644
--- a/src/test/regress/expected/update.out
+++ b/src/test/regress/expected/update.out
@@ -221,11 +221,11 @@ update range_parted set b = b + 1 where b = 10;
-- Creating default partition for range
create table part_def partition of range_parted default;
\d+ part_def
- Table "public.part_def"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.part_def"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: range_parted DEFAULT
Partition constraint: (NOT (((a = 'a'::text) AND (b >= 1) AND (b < 10)) OR ((a = 'a'::text) AND (b >= 10) AND (b < 20)) OR ((a = 'b'::text) AND (b >= 1) AND (b < 10)) OR ((a = 'b'::text) AND (b >= 10) AND (b < 20))))
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 1a3ac4c1f9..a69c485e8c 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -30,7 +30,7 @@ test: point lseg line box path polygon circle date time timetz timestamp timesta
# geometry depends on point, lseg, box, path, polygon and circle
# horology depends on interval, timetz, timestamp, timestamptz, reltime and abstime
# ----------
-test: geometry horology regex oidjoins type_sanity opr_sanity misc_sanity comments expressions
+test: geometry horology regex oidjoins type_sanity opr_sanity misc_sanity comments expressions create_cm
# ----------
# These four each depend on the previous one
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index a205e5d05c..e72015a391 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -43,6 +43,7 @@ test: inet
test: macaddr
test: macaddr8
test: tstypes
+test: create_cm
test: geometry
test: horology
test: regex
diff --git a/src/test/regress/sql/create_cm.sql b/src/test/regress/sql/create_cm.sql
new file mode 100644
index 0000000000..6bf0ea8267
--- /dev/null
+++ b/src/test/regress/sql/create_cm.sql
@@ -0,0 +1,62 @@
+-- test drop
+CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
+CREATE TABLE droptest(fts tsvector COMPRESSED ts1);
+DROP COMPRESSION METHOD ts1;
+DROP COMPRESSION METHOD ts1 CASCADE;
+\d+ droptest
+DROP TABLE droptest;
+
+CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
+CREATE TABLE cmtest(fts tsvector COMPRESSED ts1);
+SELECT * FROM pg_compression;
+SELECT cmhandler, cmoptions FROM pg_compression_opt;
+
+\dCM
+\d+ cmtest
+
+INSERT INTO cmtest
+ SELECT to_tsvector(string_agg(repeat(substr(i::text,1,1), i), ' '))
+ FROM generate_series(1,200) i;
+
+SELECT length(fts) FROM cmtest;
+SELECT length(fts) FROM cmtest;
+
+-- check ALTER commands
+ALTER TABLE cmtest ALTER COLUMN fts SET NOT COMPRESSED;
+\d+ cmtest
+ALTER TABLE cmtest ALTER COLUMN fts SET COMPRESSED ts1 WITH (format 'lz');
+ALTER TABLE cmtest ALTER COLUMN fts SET COMPRESSED ts1;
+\d+ cmtest
+
+-- create different types of tables
+SELECT * INTO cmtest2 FROM cmtest;
+CREATE TABLE cmtest3 (LIKE cmtest);
+CREATE TABLE cmtest4(fts tsvector, a int) INHERITS (cmtest);
+CREATE TABLE cmtest5(fts tsvector);
+CREATE TABLE cmtest6(fts tsvector);
+INSERT INTO cmtest6 SELECT * FROM cmtest;
+
+-- we update usual datum with compressed datum
+INSERT INTO cmtest5
+ SELECT to_tsvector(string_agg(repeat(substr(i::text,1,1), i), ' '))
+ FROM generate_series(1,200) i;
+UPDATE cmtest5 SET fts = cmtest.fts FROM cmtest;
+
+\d+ cmtest3
+\d+ cmtest4
+DROP TABLE cmtest CASCADE;
+DROP TABLE cmtest3;
+
+SELECT cmhandler, cmoptions FROM pg_compression_opt;
+
+DROP COMPRESSION METHOD ts1 CASCADE;
+SELECT * FROM pg_compression;
+SELECT * FROM pg_compression_opt;
+
+-- check that moved tuples still can be decompressed
+SELECT length(fts) FROM cmtest2;
+SELECT length(fts) FROM cmtest5;
+SELECT length(fts) FROM cmtest6;
+DROP TABLE cmtest2;
+DROP TABLE cmtest5;
+DROP TABLE cmtest6;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index b422050a92..2fe9d7d434 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -340,6 +340,7 @@ CollectedCommand
CollectedCommandType
ColorTrgm
ColorTrgmInfo
+ColumnCompression
ColumnCompareData
ColumnDef
ColumnIOData
@@ -363,6 +364,8 @@ CompositeIOData
CompositeTypeStmt
CompoundAffixFlag
CompressionAlgorithm
+CompressionMethodOptions
+CompressionMethodRoutine
CompressorState
ConditionVariable
ConditionalStack
Hi,
On 11/23/2017 10:38 AM, Ildus Kurbangaliev wrote:
On Tue, 21 Nov 2017 18:47:49 +0100
Tomas Vondra <tomas.vondra@2ndquadrant.com> wrote:Hmmm, it still doesn't work for me. See this:
test=# create extension pg_lz4 ;
CREATE EXTENSION
test=# create table t_lz4 (v text compressed lz4);
CREATE TABLE
test=# create table t_pglz (v text);
CREATE TABLE
test=# insert into t_lz4 select repeat(md5(1::text),300);
INSERT 0 1
test=# insert into t_pglz select * from t_lz4;
INSERT 0 1
test=# drop extension pg_lz4 cascade;
NOTICE: drop cascades to 2 other objects
DETAIL: drop cascades to compression options for lz4
drop cascades to table t_lz4 column v
DROP EXTENSION
test=# \c test
You are now connected to database "test" as user "user".
test=# insert into t_lz4 select repeat(md5(1::text),300);^C
test=# select * from t_pglz ;
ERROR: cache lookup failed for compression options 16419That suggests no recompression happened.
Should be fixed in the attached patch. I've changed your extension a
little bit according changes in the new patch (also in attachments).
Hmm, this seems to have fixed it, but only in one direction. Consider this:
create table t_pglz (v text);
create table t_lz4 (v text compressed lz4);
insert into t_pglz select repeat(md5(i::text),300)
from generate_series(1,100000) s(i);
insert into t_lz4 select repeat(md5(i::text),300)
from generate_series(1,100000) s(i);
\d+
Schema | Name | Type | Owner | Size | Description
--------+--------+-------+-------+-------+-------------
public | t_lz4 | table | user | 12 MB |
public | t_pglz | table | user | 18 MB |
(2 rows)
truncate t_pglz;
insert into t_pglz select * from t_lz4;
\d+
Schema | Name | Type | Owner | Size | Description
--------+--------+-------+-------+-------+-------------
public | t_lz4 | table | user | 12 MB |
public | t_pglz | table | user | 18 MB |
(2 rows)
which is fine. But in the other direction, this happens
truncate t_lz4;
insert into t_lz4 select * from t_pglz;
\d+
List of relations
Schema | Name | Type | Owner | Size | Description
--------+--------+-------+-------+-------+-------------
public | t_lz4 | table | user | 18 MB |
public | t_pglz | table | user | 18 MB |
(2 rows)
which means the data is still pglz-compressed. That's rather strange, I
guess, and it should compress the data using the compression method set
for the target table instead.
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Thu, 23 Nov 2017 21:54:32 +0100
Tomas Vondra <tomas.vondra@2ndquadrant.com> wrote:
Hmm, this seems to have fixed it, but only in one direction. Consider
this:create table t_pglz (v text);
create table t_lz4 (v text compressed lz4);insert into t_pglz select repeat(md5(i::text),300)
from generate_series(1,100000) s(i);insert into t_lz4 select repeat(md5(i::text),300)
from generate_series(1,100000) s(i);\d+
Schema | Name | Type | Owner | Size | Description
--------+--------+-------+-------+-------+-------------
public | t_lz4 | table | user | 12 MB |
public | t_pglz | table | user | 18 MB |
(2 rows)truncate t_pglz;
insert into t_pglz select * from t_lz4;\d+
Schema | Name | Type | Owner | Size | Description
--------+--------+-------+-------+-------+-------------
public | t_lz4 | table | user | 12 MB |
public | t_pglz | table | user | 18 MB |
(2 rows)which is fine. But in the other direction, this happens
truncate t_lz4;
insert into t_lz4 select * from t_pglz;\d+
List of relations
Schema | Name | Type | Owner | Size | Description
--------+--------+-------+-------+-------+-------------
public | t_lz4 | table | user | 18 MB |
public | t_pglz | table | user | 18 MB |
(2 rows)which means the data is still pglz-compressed. That's rather strange,
I guess, and it should compress the data using the compression method
set for the target table instead.
That's actually an interesting issue. It happens because if tuple fits
to page then postgres just moves it as is. I've just added
recompression if it has custom compressed datums to keep dependencies
right. But look:
create table t1(a text);
create table t2(a text);
alter table t2 alter column a set storage external;
insert into t1 select repeat(md5(i::text),300) from
generate_series(1,100000) s(i);
\d+
List of relations
Schema | Name | Type | Owner | Size | Description
--------+------+-------+-------+------------+-------------
public | t1 | table | ildus | 18 MB |
public | t2 | table | ildus | 8192 bytes |
(2 rows)
insert into t2 select * from t1;
\d+
List of relations
Schema | Name | Type | Owner | Size | Description
--------+------+-------+-------+-------+-------------
public | t1 | table | ildus | 18 MB |
public | t2 | table | ildus | 18 MB |
(2 rows)
That means compressed datums now in the column with storage specified as
external. I'm not sure that's a bug or a feature. Lets insert them
usual way:
delete from t2;
insert into t2 select repeat(md5(i::text),300) from
generate_series(1,100000) s(i);
\d+
List of relations
Schema | Name | Type | Owner | Size | Description
--------+------+-------+-------+---------+-------------
public | t1 | table | ildus | 18 MB |
public | t2 | table | ildus | 1011 MB |
Maybe there should be more common solution like comparison of attribute
properties?
--
---
Ildus Kurbangaliev
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company
Hi,
On 11/24/2017 10:38 AM, Ildus Kurbangaliev wrote:
...
That means compressed datums now in the column with storage
specified as external. I'm not sure that's a bug or a feature.
Interesting. Never realized it behaves like this. Not sure if it's
intentional or not (i.e. bug vs. feature).
Lets insert them usual way:
delete from t2;
insert into t2 select repeat(md5(i::text),300) from
generate_series(1,100000) s(i);
\d+List of relations
Schema | Name | Type | Owner | Size | Description
--------+------+-------+-------+---------+-------------
public | t1 | table | ildus | 18 MB |
public | t2 | table | ildus | 1011 MB |Maybe there should be more common solution like comparison of
attribute properties?
Maybe, not sure what the right solution is. I just know that if we allow
inserting data into arbitrary tables without recompression, we may end
up with data that can't be decompressed.
I agree that the behavior with extended storage is somewhat similar, but
the important distinction is that while that is surprising the data is
still accessible.
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Hi,
I ran into another issue - after inserting some data into a table with a
tsvector column (without any compression defined), I can no longer read
the data.
This is what I get in the console:
db=# select max(md5(body_tsvector::text)) from messages;
ERROR: cache lookup failed for compression options 6432
and the stack trace looks like this:
Breakpoint 1, get_cached_compression_options (cmoptoid=6432) at
tuptoaster.c:2563
2563 elog(ERROR, "cache lookup failed for compression options %u",
cmoptoid);
(gdb) bt
#0 get_cached_compression_options (cmoptoid=6432) at tuptoaster.c:2563
#1 0x00000000004bf3da in toast_decompress_datum (attr=0x2b44148) at
tuptoaster.c:2390
#2 0x00000000004c0c1e in heap_tuple_untoast_attr (attr=0x2b44148) at
tuptoaster.c:225
#3 0x000000000083f976 in pg_detoast_datum (datum=<optimized out>) at
fmgr.c:1829
#4 0x00000000008072de in tsvectorout (fcinfo=0x2b41e00) at tsvector.c:315
#5 0x00000000005fae00 in ExecInterpExpr (state=0x2b414b8,
econtext=0x2b25ab0, isnull=<optimized out>) at execExprInterp.c:1131
#6 0x000000000060bdf4 in ExecEvalExprSwitchContext
(isNull=0x7fffffe9bd37 "", econtext=0x2b25ab0, state=0x2b414b8) at
../../../src/include/executor/executor.h:299
It seems the VARATT_IS_CUSTOM_COMPRESSED incorrectly identifies the
value as custom-compressed for some reason.
Not sure why, but the tsvector column is populated by a trigger that
simply does
NEW.body_tsvector
:= to_tsvector('english', strip_replies(NEW.body_plain));
If needed, the complete tool is here:
https://bitbucket.org/tvondra/archie
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Sat, 25 Nov 2017 06:40:00 +0100
Tomas Vondra <tomas.vondra@2ndquadrant.com> wrote:
Hi,
I ran into another issue - after inserting some data into a table
with a tsvector column (without any compression defined), I can no
longer read the data.This is what I get in the console:
db=# select max(md5(body_tsvector::text)) from messages;
ERROR: cache lookup failed for compression options 6432and the stack trace looks like this:
Breakpoint 1, get_cached_compression_options (cmoptoid=6432) at
tuptoaster.c:2563
2563 elog(ERROR, "cache lookup failed for
compression options %u", cmoptoid);
(gdb) bt
#0 get_cached_compression_options (cmoptoid=6432) at
tuptoaster.c:2563 #1 0x00000000004bf3da in toast_decompress_datum
(attr=0x2b44148) at tuptoaster.c:2390
#2 0x00000000004c0c1e in heap_tuple_untoast_attr (attr=0x2b44148) at
tuptoaster.c:225
#3 0x000000000083f976 in pg_detoast_datum (datum=<optimized out>) at
fmgr.c:1829
#4 0x00000000008072de in tsvectorout (fcinfo=0x2b41e00) at
tsvector.c:315 #5 0x00000000005fae00 in ExecInterpExpr
(state=0x2b414b8, econtext=0x2b25ab0, isnull=<optimized out>) at
execExprInterp.c:1131 #6 0x000000000060bdf4 in
ExecEvalExprSwitchContext (isNull=0x7fffffe9bd37 "",
econtext=0x2b25ab0, state=0x2b414b8)
at ../../../src/include/executor/executor.h:299It seems the VARATT_IS_CUSTOM_COMPRESSED incorrectly identifies the
value as custom-compressed for some reason.Not sure why, but the tsvector column is populated by a trigger that
simply doesNEW.body_tsvector
:= to_tsvector('english', strip_replies(NEW.body_plain));If needed, the complete tool is here:
Hi. This looks like a serious bug, but I couldn't reproduce
it yet. Did you upgrade some old database or this bug happened after
insertion of all data to new database? I tried using your 'archie'
tool to download mailing lists and insert them to database, but couldn't
catch any errors.
--
---
Ildus Kurbangaliev
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company
Hi,
On 11/27/2017 04:52 PM, Ildus Kurbangaliev wrote:
...
Hi. This looks like a serious bug, but I couldn't reproduce it yet.
Did you upgrade some old database or this bug happened after
insertion of all data to new database? I tried using your 'archie'
tool to download mailing lists and insert them to database, but
couldn't catch any errors.
I can trigger it pretty reliably with these steps:
git checkout f65d21b258085bdc8ef2cc282ab1ff12da9c595c
patch -p1 < ~/custom_compression_methods_v6.patch
./configure --enable-debug --enable-cassert \
CFLAGS="-fno-omit-frame-pointer -O0 -DRANDOMIZE_ALLOCATED_MEMORY" \
--prefix=/home/postgres/pg-compress
make -s clean && make -s -j4 install
cd contrib/
make -s clean && make -s -j4 install
export PATH=/home/postgres/pg-compress/bin:$PATH
pg_ctl -D /mnt/raid/pg-compress init
pg_ctl -D /mnt/raid/pg-compress -l compress.log start
createdb archie
cd ~/archie/sql/
psql archie < create.sql
~/archie/bin/load.py --workers 4 --db archie */* > load.log 2>&1
I guess the trick might be -DRANDOMIZE_ALLOCATED_MEMORY (I first tried
without it, and it seemed working fine). If that's the case, I bet there
is a palloc that should have been palloc0, or something like that.
If you still can't reproduce that, I may give you access to this machine
so that you can debug it there.
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Mon, 27 Nov 2017 18:20:12 +0100
Tomas Vondra <tomas.vondra@2ndquadrant.com> wrote:
I guess the trick might be -DRANDOMIZE_ALLOCATED_MEMORY (I first tried
without it, and it seemed working fine). If that's the case, I bet
there is a palloc that should have been palloc0, or something like
that.
Thanks, that was it. I've been able to reproduce this bug. The attached
patch should fix this bug and I've also added recompression when
tuples moved to the relation with the compressed attribute.
--
---
Ildus Kurbangaliev
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company
Attachments:
custom_compression_methods_v7.patchtext/x-patchDownload
diff --git a/contrib/test_decoding/expected/ddl.out b/contrib/test_decoding/expected/ddl.out
index 1e22c1eefc..766ced401f 100644
--- a/contrib/test_decoding/expected/ddl.out
+++ b/contrib/test_decoding/expected/ddl.out
@@ -416,12 +416,12 @@ CREATE TABLE replication_metadata (
WITH (user_catalog_table = true)
;
\d+ replication_metadata
- Table "public.replication_metadata"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------+---------+-----------+----------+--------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | |
- relation | name | | not null | | plain | |
- options | text[] | | | | extended | |
+ Table "public.replication_metadata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+----------+---------+-----------+----------+--------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | |
+ relation | name | | not null | | plain | | |
+ options | text[] | | | | extended | | |
Indexes:
"replication_metadata_pkey" PRIMARY KEY, btree (id)
Options: user_catalog_table=true
@@ -430,12 +430,12 @@ INSERT INTO replication_metadata(relation, options)
VALUES ('foo', ARRAY['a', 'b']);
ALTER TABLE replication_metadata RESET (user_catalog_table);
\d+ replication_metadata
- Table "public.replication_metadata"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------+---------+-----------+----------+--------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | |
- relation | name | | not null | | plain | |
- options | text[] | | | | extended | |
+ Table "public.replication_metadata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+----------+---------+-----------+----------+--------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | |
+ relation | name | | not null | | plain | | |
+ options | text[] | | | | extended | | |
Indexes:
"replication_metadata_pkey" PRIMARY KEY, btree (id)
@@ -443,12 +443,12 @@ INSERT INTO replication_metadata(relation, options)
VALUES ('bar', ARRAY['a', 'b']);
ALTER TABLE replication_metadata SET (user_catalog_table = true);
\d+ replication_metadata
- Table "public.replication_metadata"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------+---------+-----------+----------+--------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | |
- relation | name | | not null | | plain | |
- options | text[] | | | | extended | |
+ Table "public.replication_metadata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+----------+---------+-----------+----------+--------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | |
+ relation | name | | not null | | plain | | |
+ options | text[] | | | | extended | | |
Indexes:
"replication_metadata_pkey" PRIMARY KEY, btree (id)
Options: user_catalog_table=true
@@ -461,13 +461,13 @@ ALTER TABLE replication_metadata ALTER COLUMN rewritemeornot TYPE text;
ERROR: cannot rewrite table "replication_metadata" used as a catalog table
ALTER TABLE replication_metadata SET (user_catalog_table = false);
\d+ replication_metadata
- Table "public.replication_metadata"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------------+---------+-----------+----------+--------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | |
- relation | name | | not null | | plain | |
- options | text[] | | | | extended | |
- rewritemeornot | integer | | | | plain | |
+ Table "public.replication_metadata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+----------------+---------+-----------+----------+--------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | |
+ relation | name | | not null | | plain | | |
+ options | text[] | | | | extended | | |
+ rewritemeornot | integer | | | | plain | | |
Indexes:
"replication_metadata_pkey" PRIMARY KEY, btree (id)
Options: user_catalog_table=false
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index 01acc2ef9d..f43f09cc19 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -59,6 +59,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY createAggregate SYSTEM "create_aggregate.sgml">
<!ENTITY createCast SYSTEM "create_cast.sgml">
<!ENTITY createCollation SYSTEM "create_collation.sgml">
+<!ENTITY createCompressionMethod SYSTEM "create_compression_method.sgml">
<!ENTITY createConversion SYSTEM "create_conversion.sgml">
<!ENTITY createDatabase SYSTEM "create_database.sgml">
<!ENTITY createDomain SYSTEM "create_domain.sgml">
diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index 7bcf242846..e44f9eb94b 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -53,6 +53,8 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET ( <replaceable class="parameter">attribute_option</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] )
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> RESET ( <replaceable class="parameter">attribute_option</replaceable> [, ... ] )
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN }
+ ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET COMPRESSED <replaceable class="parameter">compression_method_name</replaceable> [ WITH (<replaceable class="parameter">compression_method_options</replaceable>) ]
+ ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET NOT COMPRESSED
ADD <replaceable class="parameter">table_constraint</replaceable> [ NOT VALID ]
ADD <replaceable class="parameter">table_constraint_using_index</replaceable>
ALTER CONSTRAINT <replaceable class="parameter">constraint_name</replaceable> [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -320,6 +322,34 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>
+ <literal>SET COMPRESSED <replaceable class="parameter">compression_method_name</replaceable></literal>
+ </term>
+ <listitem>
+ <para>
+ This form adds compression to a column. Compression method should be
+ created with <xref linkend="SQL-CREATECOMPRESSIONMETHOD">. If compression
+ method has options they could be specified with <literal>WITH</literal>
+ parameter. Setting a compression method doesn't change anything in the
+ table and affects only future table updates.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <literal>SET NOT COMPRESSED</literal>
+ </term>
+ <listitem>
+ <para>
+ This form removes compression from a column. Removing compresssion from
+ a column doesn't change already compressed tuples and affects only future
+ table updates.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>ADD <replaceable class="parameter">table_constraint</replaceable> [ NOT VALID ]</literal></term>
<listitem>
diff --git a/doc/src/sgml/ref/create_compression_method.sgml b/doc/src/sgml/ref/create_compression_method.sgml
new file mode 100644
index 0000000000..663010ecd9
--- /dev/null
+++ b/doc/src/sgml/ref/create_compression_method.sgml
@@ -0,0 +1,50 @@
+<!--
+doc/src/sgml/ref/create_compression_method.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-CREATECOMPRESSIONMETHOD">
+ <indexterm zone="sql-createcompressionmethod">
+ <primary>CREATE COMPRESSION METHOD</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>CREATE COMPRESSION METHOD</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>CREATE COMPRESSION METHOD</refname>
+ <refpurpose>define a new compression method</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+CREATE COMPRESSION METHOD <replaceable class="parameter">compression_method_name</replaceable>
+ HANDLER <replaceable class="parameter">compression_method_handler</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>CREATE COMPRESSION METHOD</command> creates a new compression method
+ with <replaceable class="parameter">compression_method_name</replaceable>.
+ </para>
+
+ <para>
+ A compression method links a name with a compression handler. And the
+ handler is a special function that returns collection of methods that
+ can be used for compression.
+ </para>
+
+ <para>
+ After a compression method is created, you can specify it in
+ <xref linkend="SQL-CREATETABLE"> or <xref linkend="SQL-ALTERTABLE">
+ statements.
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index a0c9a6d257..1eae358c8a 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -65,6 +65,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( <replaceable>sequence_options</replaceable> ) ] |
UNIQUE <replaceable class="parameter">index_parameters</replaceable> |
PRIMARY KEY <replaceable class="parameter">index_parameters</replaceable> |
+ COMPRESSED <replaceable class="parameter">compression_method_name</replaceable> [ WITH (<replaceable class="parameter">compression_method_options</replaceable>) ] |
REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -817,6 +818,18 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>COMPRESSED <replaceable class="parameter">compression_method_name</replaceable> [ WITH (<replaceable class="parameter">compression_method_options</replaceable>) ]</literal></term>
+ <listitem>
+ <para>
+ This clause adds compression to a column. Compression method should be
+ created with <xref linkend="SQL-CREATECOMPRESSIONMETHOD">. If compression
+ method has options they could be specified with <literal>WITH</literal>
+ parameter.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="sql-createtable-exclude">
<term><literal>EXCLUDE [ USING <replaceable class="parameter">index_method</replaceable> ] ( <replaceable class="parameter">exclude_element</replaceable> WITH <replaceable class="parameter">operator</replaceable> [, ... ] ) <replaceable class="parameter">index_parameters</replaceable> [ WHERE ( <replaceable class="parameter">predicate</replaceable> ) ]</literal></term>
<listitem>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index d20eaa87e7..167090639f 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -87,6 +87,7 @@
&createAggregate;
&createCast;
&createCollation;
+ &createCompressionMethod;
&createConversion;
&createDatabase;
&createDomain;
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index 5c035fb203..0c6cf937f8 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -96,6 +96,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
int keyno;
int idxattno;
uint16 phony_infomask = 0;
+ uint16 phony_infomask2 = 0;
bits8 *phony_nullbitmap;
Size len,
hoff,
@@ -187,6 +188,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
(char *) rettuple + hoff,
data_len,
&phony_infomask,
+ &phony_infomask2,
phony_nullbitmap);
/* done with these */
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index a1a9d9905b..aa764b7b59 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -145,7 +145,7 @@ void
heap_fill_tuple(TupleDesc tupleDesc,
Datum *values, bool *isnull,
char *data, Size data_size,
- uint16 *infomask, bits8 *bit)
+ uint16 *infomask, uint16 *infomask2, bits8 *bit)
{
bits8 *bitP;
int bitmask;
@@ -169,6 +169,7 @@ heap_fill_tuple(TupleDesc tupleDesc,
}
*infomask &= ~(HEAP_HASNULL | HEAP_HASVARWIDTH | HEAP_HASEXTERNAL);
+ *infomask2 &= ~HEAP_HASCUSTOMCOMPRESSED;
for (i = 0; i < numberOfAttributes; i++)
{
@@ -195,6 +196,9 @@ heap_fill_tuple(TupleDesc tupleDesc,
*bitP |= bitmask;
}
+ if (OidIsValid(att->attcompression))
+ *infomask2 |= HEAP_HASCUSTOMCOMPRESSED;
+
/*
* XXX we use the att_align macros on the pointer value itself, not on
* an offset. This is a bit of a hack.
@@ -213,6 +217,7 @@ heap_fill_tuple(TupleDesc tupleDesc,
Pointer val = DatumGetPointer(values[i]);
*infomask |= HEAP_HASVARWIDTH;
+
if (VARATT_IS_EXTERNAL(val))
{
if (VARATT_IS_EXTERNAL_EXPANDED(val))
@@ -230,10 +235,20 @@ heap_fill_tuple(TupleDesc tupleDesc,
}
else
{
+
*infomask |= HEAP_HASEXTERNAL;
/* no alignment, since it's short by definition */
data_length = VARSIZE_EXTERNAL(val);
memcpy(data, val, data_length);
+
+ if (VARATT_IS_EXTERNAL_ONDISK(val))
+ {
+ struct varatt_external toast_pointer;
+
+ VARATT_EXTERNAL_GET_POINTER(toast_pointer, val);
+ if (VARATT_EXTERNAL_IS_CUSTOM_COMPRESSED(toast_pointer))
+ *infomask2 |= HEAP_HASCUSTOMCOMPRESSED;
+ }
}
}
else if (VARATT_IS_SHORT(val))
@@ -257,6 +272,9 @@ heap_fill_tuple(TupleDesc tupleDesc,
att->attalign);
data_length = VARSIZE(val);
memcpy(data, val, data_length);
+
+ if (VARATT_IS_CUSTOM_COMPRESSED(val))
+ *infomask2 |= HEAP_HASCUSTOMCOMPRESSED;
}
}
else if (att->attlen == -2)
@@ -774,6 +792,7 @@ heap_form_tuple(TupleDesc tupleDescriptor,
(char *) td + hoff,
data_len,
&td->t_infomask,
+ &td->t_infomask2,
(hasnull ? td->t_bits : NULL));
return tuple;
@@ -1456,6 +1475,7 @@ heap_form_minimal_tuple(TupleDesc tupleDescriptor,
(char *) tuple + hoff,
data_len,
&tuple->t_infomask,
+ &tuple->t_infomask2,
(hasnull ? tuple->t_bits : NULL));
return tuple;
diff --git a/src/backend/access/common/indextuple.c b/src/backend/access/common/indextuple.c
index 138671410a..e5299d3094 100644
--- a/src/backend/access/common/indextuple.c
+++ b/src/backend/access/common/indextuple.c
@@ -47,6 +47,7 @@ index_form_tuple(TupleDesc tupleDescriptor,
unsigned short infomask = 0;
bool hasnull = false;
uint16 tupmask = 0;
+ uint16 tupmask2 = 0;
int numberOfAttributes = tupleDescriptor->natts;
#ifdef TOAST_INDEX_HACK
@@ -74,13 +75,30 @@ index_form_tuple(TupleDesc tupleDescriptor,
/*
* If value is stored EXTERNAL, must fetch it so we are not depending
- * on outside storage. This should be improved someday.
+ * on outside storage. This should be improved someday. If value also
+ * was compressed by custom compression method then we should
+ * decompress it too.
*/
if (VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+ {
+ struct varatt_external toast_pointer;
+
+ VARATT_EXTERNAL_GET_POINTER(toast_pointer, DatumGetPointer(values[i]));
+ if (VARATT_EXTERNAL_IS_CUSTOM_COMPRESSED(toast_pointer))
+ untoasted_values[i] =
+ PointerGetDatum(heap_tuple_untoast_attr((struct varlena *)
+ DatumGetPointer(values[i])));
+ else
+ untoasted_values[i] =
+ PointerGetDatum(heap_tuple_fetch_attr((struct varlena *)
+ DatumGetPointer(values[i])));
+ untoasted_free[i] = true;
+ }
+ else if (VARATT_IS_CUSTOM_COMPRESSED(DatumGetPointer(values[i])))
{
untoasted_values[i] =
- PointerGetDatum(heap_tuple_fetch_attr((struct varlena *)
- DatumGetPointer(values[i])));
+ PointerGetDatum(heap_tuple_untoast_attr((struct varlena *)
+ DatumGetPointer(values[i])));
untoasted_free[i] = true;
}
@@ -92,7 +110,7 @@ index_form_tuple(TupleDesc tupleDescriptor,
VARSIZE(DatumGetPointer(untoasted_values[i])) > TOAST_INDEX_TARGET &&
(att->attstorage == 'x' || att->attstorage == 'm'))
{
- Datum cvalue = toast_compress_datum(untoasted_values[i]);
+ Datum cvalue = toast_compress_datum(untoasted_values[i], InvalidOid);
if (DatumGetPointer(cvalue) != NULL)
{
@@ -142,6 +160,7 @@ index_form_tuple(TupleDesc tupleDescriptor,
(char *) tp + hoff,
data_size,
&tupmask,
+ &tupmask2,
(hasnull ? (bits8 *) tp + sizeof(IndexTupleData) : NULL));
#ifdef TOAST_INDEX_HACK
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index aa9c0f1bb9..f740ce4304 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -945,11 +945,31 @@ untransformRelOptions(Datum options)
val = (Node *) makeString(pstrdup(p));
}
result = lappend(result, makeDefElem(pstrdup(s), val, -1));
+ pfree(s);
}
return result;
}
+char *
+formatRelOptions(List *options)
+{
+ StringInfoData buf;
+ ListCell *cell;
+
+ initStringInfo(&buf);
+
+ foreach(cell, options)
+ {
+ DefElem *def = (DefElem *) lfirst(cell);
+
+ appendStringInfo(&buf, "%s%s=%s", buf.len > 0 ? ", " : "",
+ def->defname, defGetString(def));
+ }
+
+ return buf.data;
+}
+
/*
* Extract and parse reloptions from a pg_class tuple.
*
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index 9e37ca73a8..06dcf219bf 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -19,8 +19,10 @@
#include "postgres.h"
+#include "access/compression.h"
#include "access/hash.h"
#include "access/htup_details.h"
+#include "access/reloptions.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
@@ -64,6 +66,7 @@ CreateTemplateTupleDesc(int natts, bool hasoid)
desc->tdtypeid = RECORDOID;
desc->tdtypmod = -1;
desc->tdhasoid = hasoid;
+ desc->tdflags = 0;
desc->tdrefcount = -1; /* assume not reference-counted */
return desc;
@@ -86,8 +89,17 @@ CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs)
desc = CreateTemplateTupleDesc(natts, hasoid);
for (i = 0; i < natts; ++i)
+ {
memcpy(TupleDescAttr(desc, i), attrs[i], ATTRIBUTE_FIXED_PART_SIZE);
+ /*
+ * If even one of attributes is compressed we save information about it
+ * to TupleDesc flags
+ */
+ if (OidIsValid(attrs[i]->attcompression))
+ desc->tdflags |= TD_ATTR_CUSTOM_COMPRESSED;
+ }
+
return desc;
}
@@ -118,6 +130,7 @@ CreateTupleDescCopy(TupleDesc tupdesc)
desc->tdtypeid = tupdesc->tdtypeid;
desc->tdtypmod = tupdesc->tdtypmod;
+ desc->tdflags = tupdesc->tdflags;
return desc;
}
@@ -180,6 +193,7 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc)
desc->tdtypeid = tupdesc->tdtypeid;
desc->tdtypmod = tupdesc->tdtypmod;
+ desc->tdflags = tupdesc->tdflags;
return desc;
}
@@ -242,6 +256,7 @@ TupleDescCopyEntry(TupleDesc dst, AttrNumber dstAttno,
dstAtt->attnotnull = false;
dstAtt->atthasdef = false;
dstAtt->attidentity = '\0';
+ dstAtt->attcompression = InvalidOid;
}
/*
@@ -346,6 +361,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
return false;
if (tupdesc1->tdhasoid != tupdesc2->tdhasoid)
return false;
+ if (tupdesc1->tdflags != tupdesc2->tdflags)
+ return false;
for (i = 0; i < tupdesc1->natts; i++)
{
@@ -396,6 +413,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
return false;
if (attr1->attcollation != attr2->attcollation)
return false;
+ if (attr1->attcompression != attr2->attcompression)
+ return false;
/* attacl, attoptions and attfdwoptions are not even present... */
}
@@ -458,6 +477,7 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
}
else if (tupdesc2->constr != NULL)
return false;
+
return true;
}
@@ -563,6 +583,7 @@ TupleDescInitEntry(TupleDesc desc,
att->attalign = typeForm->typalign;
att->attstorage = typeForm->typstorage;
att->attcollation = typeForm->typcollation;
+ att->attcompression = InvalidOid;
ReleaseSysCache(tuple);
}
@@ -675,7 +696,6 @@ TupleDescInitEntryCollation(TupleDesc desc,
TupleDescAttr(desc, attributeNumber - 1)->attcollation = collationid;
}
-
/*
* BuildDescForRelation
*
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 3acef279f4..7615ad310f 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2656,7 +2656,10 @@ heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
Assert(!HeapTupleHasExternal(tup));
return tup;
}
- else if (HeapTupleHasExternal(tup) || tup->t_len > TOAST_TUPLE_THRESHOLD)
+ else if (HeapTupleHasExternal(tup)
+ || RelationGetDescr(relation)->tdflags & TD_ATTR_CUSTOM_COMPRESSED
+ || HeapTupleHasCustomCompressed(tup)
+ || tup->t_len > TOAST_TUPLE_THRESHOLD)
return toast_insert_or_update(relation, tup, NULL, options);
else
return tup;
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index c74945a52a..b9a610fb6e 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -30,8 +30,10 @@
#include <unistd.h>
#include <fcntl.h>
+#include "access/compression.h"
#include "access/genam.h"
#include "access/heapam.h"
+#include "access/reloptions.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -39,8 +41,11 @@
#include "miscadmin.h"
#include "utils/expandeddatum.h"
#include "utils/fmgroids.h"
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
+#include "utils/syscache.h"
#include "utils/typcache.h"
#include "utils/tqual.h"
@@ -53,19 +58,57 @@
typedef struct toast_compress_header
{
int32 vl_len_; /* varlena header (do not touch directly!) */
- int32 rawsize;
+ uint32 info; /* flags (2 bits) and rawsize */
} toast_compress_header;
+/*
+ * If the compression method were used, then data also contains
+ * Oid of compression options
+ */
+typedef struct toast_compress_header_custom
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ uint32 info; /* flags (2 high bits) and rawsize */
+ Oid cmoptoid; /* Oid from pg_compression_opt */
+} toast_compress_header_custom;
+
+static HTAB *compression_options_cache = NULL;
+static MemoryContext compression_options_mcxt = NULL;
+
+#define RAWSIZEMASK (0x3FFFFFFFU)
+
/*
* Utilities for manipulation of header information for compressed
* toast entries.
*/
-#define TOAST_COMPRESS_HDRSZ ((int32) sizeof(toast_compress_header))
-#define TOAST_COMPRESS_RAWSIZE(ptr) (((toast_compress_header *) (ptr))->rawsize)
+#define TOAST_COMPRESS_HDRSZ ((int32) sizeof(toast_compress_header))
+#define TOAST_COMPRESS_HDRSZ_CUSTOM ((int32) sizeof(toast_compress_header_custom))
+#define TOAST_COMPRESS_RAWSIZE(ptr) (((toast_compress_header *) (ptr))->info & RAWSIZEMASK)
#define TOAST_COMPRESS_RAWDATA(ptr) \
(((char *) (ptr)) + TOAST_COMPRESS_HDRSZ)
#define TOAST_COMPRESS_SET_RAWSIZE(ptr, len) \
- (((toast_compress_header *) (ptr))->rawsize = (len))
+do { \
+ Assert(len > 0 && len <= RAWSIZEMASK); \
+ ((toast_compress_header *) (ptr))->info = (len); \
+} while (0)
+#define TOAST_COMPRESS_SET_CMOPTOID(ptr, oid) \
+ (((toast_compress_header_custom *) (ptr))->cmoptoid = (oid))
+
+/*
+ * Marks varlena as custom compressed. Notice that this macro should be called
+ * after TOAST_COMPRESS_SET_RAWSIZE because it will clean flags.
+ */
+#define TOAST_COMPRESS_SET_CUSTOM(ptr) \
+do { \
+ (((toast_compress_header *) (ptr))->info |= (1 << 31)); \
+ (((toast_compress_header *) (ptr))->info &= ~(1 << 30)); \
+} while (0)
+
+#define VARATT_EXTERNAL_SET_CUSTOM(toast_pointer) \
+do { \
+ ((toast_pointer).va_extinfo |= (1 << 31)); \
+ ((toast_pointer).va_extinfo &= ~(1 << 30)); \
+} while (0)
static void toast_delete_datum(Relation rel, Datum value, bool is_speculative);
static Datum toast_save_datum(Relation rel, Datum value,
@@ -83,6 +126,8 @@ static int toast_open_indexes(Relation toastrel,
static void toast_close_indexes(Relation *toastidxs, int num_indexes,
LOCKMODE lock);
static void init_toast_snapshot(Snapshot toast_snapshot);
+static void init_compression_options_cache(void);
+static CompressionMethodOptions *get_cached_compression_options(Oid cmoptoid);
/* ----------
@@ -421,7 +466,7 @@ toast_datum_size(Datum value)
struct varatt_external toast_pointer;
VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
- result = toast_pointer.va_extsize;
+ result = VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer);
}
else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
{
@@ -678,21 +723,47 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
* still in the tuple must be someone else's that we cannot reuse
* (this includes the case of an out-of-line in-memory datum).
* Fetch it back (without decompression, unless we are forcing
- * PLAIN storage). If necessary, we'll push it out as a new
- * external value below.
+ * PLAIN storage or it has a custom compression). If necessary,
+ * we'll push it out as a new external value below.
*/
if (VARATT_IS_EXTERNAL(new_value))
{
toast_oldexternal[i] = new_value;
if (att->attstorage == 'p')
new_value = heap_tuple_untoast_attr(new_value);
+ else if (VARATT_IS_EXTERNAL_ONDISK(new_value))
+ {
+ struct varatt_external toast_pointer;
+
+ VARATT_EXTERNAL_GET_POINTER(toast_pointer, new_value);
+
+ /*
+ * If we're trying to insert a custom compressed datum we
+ * should decompress it first.
+ */
+ if (VARATT_EXTERNAL_IS_CUSTOM_COMPRESSED(toast_pointer))
+ new_value = heap_tuple_untoast_attr(new_value);
+ else
+ new_value = heap_tuple_fetch_attr(new_value);
+ }
else
new_value = heap_tuple_fetch_attr(new_value);
+
toast_values[i] = PointerGetDatum(new_value);
toast_free[i] = true;
need_change = true;
need_free = true;
}
+ else if (VARATT_IS_CUSTOM_COMPRESSED(new_value)
+ || OidIsValid(att->attcompression))
+ {
+ struct varlena *untoasted_value = heap_tuple_untoast_attr(new_value);
+
+ toast_values[i] = PointerGetDatum(untoasted_value);
+ need_free = (untoasted_value != new_value);
+ toast_free[i] = need_free;
+ need_change = need_free;
+ }
/*
* Remember the size of this attribute
@@ -741,12 +812,14 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
Datum old_value;
Datum new_value;
+ Form_pg_attribute att;
+
/*
* Search for the biggest yet unprocessed internal attribute
*/
for (i = 0; i < numAttrs; i++)
{
- Form_pg_attribute att = TupleDescAttr(tupleDesc, i);
+ char attstorage;
if (toast_action[i] != ' ')
continue;
@@ -754,7 +827,9 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
continue; /* can't happen, toast_action would be 'p' */
if (VARATT_IS_COMPRESSED(DatumGetPointer(toast_values[i])))
continue;
- if (att->attstorage != 'x' && att->attstorage != 'e')
+
+ attstorage = (TupleDescAttr(tupleDesc, i))->attstorage;
+ if (attstorage != 'x' && attstorage != 'e')
continue;
if (toast_sizes[i] > biggest_size)
{
@@ -770,10 +845,11 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
* Attempt to compress it inline, if it has attstorage 'x'
*/
i = biggest_attno;
- if (TupleDescAttr(tupleDesc, i)->attstorage == 'x')
+ att = TupleDescAttr(tupleDesc, i);
+ if (att->attstorage == 'x')
{
old_value = toast_values[i];
- new_value = toast_compress_datum(old_value);
+ new_value = toast_compress_datum(old_value, att->attcompression);
if (DatumGetPointer(new_value) != NULL)
{
@@ -914,7 +990,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
*/
i = biggest_attno;
old_value = toast_values[i];
- new_value = toast_compress_datum(old_value);
+ new_value = toast_compress_datum(old_value,
+ TupleDescAttr(tupleDesc, i)->attcompression);
if (DatumGetPointer(new_value) != NULL)
{
@@ -1046,6 +1123,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
(char *) new_data + new_header_len,
new_data_len,
&(new_data->t_infomask),
+ &(new_data->t_infomask2),
has_nulls ? new_data->t_bits : NULL);
}
else
@@ -1274,6 +1352,7 @@ toast_flatten_tuple_to_datum(HeapTupleHeader tup,
(char *) new_data + new_header_len,
new_data_len,
&(new_data->t_infomask),
+ &(new_data->t_infomask2),
has_nulls ? new_data->t_bits : NULL);
/*
@@ -1353,7 +1432,6 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
return new_tuple;
}
-
/* ----------
* toast_compress_datum -
*
@@ -1368,41 +1446,57 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
* ----------
*/
Datum
-toast_compress_datum(Datum value)
+toast_compress_datum(Datum value, Oid cmoptoid)
{
- struct varlena *tmp;
- int32 valsize = VARSIZE_ANY_EXHDR(DatumGetPointer(value));
- int32 len;
+ struct varlena *tmp = NULL;
+ int32 valsize,
+ len = 0;
+ CompressionMethodOptions *cmoptions = NULL;
Assert(!VARATT_IS_EXTERNAL(DatumGetPointer(value)));
Assert(!VARATT_IS_COMPRESSED(DatumGetPointer(value)));
- /*
- * No point in wasting a palloc cycle if value size is out of the allowed
- * range for compression
- */
- if (valsize < PGLZ_strategy_default->min_input_size ||
- valsize > PGLZ_strategy_default->max_input_size)
- return PointerGetDatum(NULL);
+ if (OidIsValid(cmoptoid))
+ cmoptions = get_cached_compression_options(cmoptoid);
- tmp = (struct varlena *) palloc(PGLZ_MAX_OUTPUT(valsize) +
- TOAST_COMPRESS_HDRSZ);
+ valsize = VARSIZE_ANY_EXHDR(DatumGetPointer(value));
+
+ if (cmoptions)
+ {
+ tmp = cmoptions->routine->compress(cmoptions, (const struct varlena *) value);
+ if (!tmp)
+ return PointerGetDatum(NULL);
+ }
+ else
+ {
+ /*
+ * No point in wasting a palloc cycle if value size is out of the
+ * allowed range for compression
+ */
+ if (valsize < PGLZ_strategy_default->min_input_size ||
+ valsize > PGLZ_strategy_default->max_input_size)
+ return PointerGetDatum(NULL);
+
+ tmp = (struct varlena *) palloc(PGLZ_MAX_OUTPUT(valsize) +
+ TOAST_COMPRESS_HDRSZ);
+ len = pglz_compress(VARDATA_ANY(DatumGetPointer(value)),
+ valsize,
+ TOAST_COMPRESS_RAWDATA(tmp),
+ PGLZ_strategy_default);
+ }
/*
- * We recheck the actual size even if pglz_compress() reports success,
- * because it might be satisfied with having saved as little as one byte
- * in the compressed data --- which could turn into a net loss once you
- * consider header and alignment padding. Worst case, the compressed
- * format might require three padding bytes (plus header, which is
- * included in VARSIZE(tmp)), whereas the uncompressed format would take
- * only one header byte and no padding if the value is short enough. So
- * we insist on a savings of more than 2 bytes to ensure we have a gain.
+ * We recheck the actual size even if compression function reports
+ * success, because it might be satisfied with having saved as little as
+ * one byte in the compressed data --- which could turn into a net loss
+ * once you consider header and alignment padding. Worst case, the
+ * compressed format might require three padding bytes (plus header, which
+ * is included in VARSIZE(tmp)), whereas the uncompressed format would
+ * take only one header byte and no padding if the value is short enough.
+ * So we insist on a savings of more than 2 bytes to ensure we have a
+ * gain.
*/
- len = pglz_compress(VARDATA_ANY(DatumGetPointer(value)),
- valsize,
- TOAST_COMPRESS_RAWDATA(tmp),
- PGLZ_strategy_default);
- if (len >= 0 &&
+ if (!cmoptions && len >= 0 &&
len + TOAST_COMPRESS_HDRSZ < valsize - 2)
{
TOAST_COMPRESS_SET_RAWSIZE(tmp, valsize);
@@ -1410,10 +1504,20 @@ toast_compress_datum(Datum value)
/* successful compression */
return PointerGetDatum(tmp);
}
+ else if (cmoptions && VARSIZE(tmp) < valsize - 2)
+ {
+ TOAST_COMPRESS_SET_RAWSIZE(tmp, valsize);
+ TOAST_COMPRESS_SET_CUSTOM(tmp);
+ TOAST_COMPRESS_SET_CMOPTOID(tmp, cmoptions->cmoptoid);
+ /* successful compression */
+ return PointerGetDatum(tmp);
+ }
else
{
/* incompressible data */
- pfree(tmp);
+ if (tmp)
+ pfree(tmp);
+
return PointerGetDatum(NULL);
}
}
@@ -1510,19 +1614,20 @@ toast_save_datum(Relation rel, Datum value,
&num_indexes);
/*
- * Get the data pointer and length, and compute va_rawsize and va_extsize.
+ * Get the data pointer and length, and compute va_rawsize and va_extinfo.
*
* va_rawsize is the size of the equivalent fully uncompressed datum, so
* we have to adjust for short headers.
*
- * va_extsize is the actual size of the data payload in the toast records.
+ * va_extinfo contains the actual size of the data payload in the toast
+ * records.
*/
if (VARATT_IS_SHORT(dval))
{
data_p = VARDATA_SHORT(dval);
data_todo = VARSIZE_SHORT(dval) - VARHDRSZ_SHORT;
toast_pointer.va_rawsize = data_todo + VARHDRSZ; /* as if not short */
- toast_pointer.va_extsize = data_todo;
+ toast_pointer.va_extinfo = data_todo; /* no flags */
}
else if (VARATT_IS_COMPRESSED(dval))
{
@@ -1530,7 +1635,10 @@ toast_save_datum(Relation rel, Datum value,
data_todo = VARSIZE(dval) - VARHDRSZ;
/* rawsize in a compressed datum is just the size of the payload */
toast_pointer.va_rawsize = VARRAWSIZE_4B_C(dval) + VARHDRSZ;
- toast_pointer.va_extsize = data_todo;
+ toast_pointer.va_extinfo = data_todo;
+ if (VARATT_IS_CUSTOM_COMPRESSED(dval))
+ VARATT_EXTERNAL_SET_CUSTOM(toast_pointer);
+
/* Assert that the numbers look like it's compressed */
Assert(VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer));
}
@@ -1539,7 +1647,7 @@ toast_save_datum(Relation rel, Datum value,
data_p = VARDATA(dval);
data_todo = VARSIZE(dval) - VARHDRSZ;
toast_pointer.va_rawsize = VARSIZE(dval);
- toast_pointer.va_extsize = data_todo;
+ toast_pointer.va_extinfo = data_todo; /* no flags */
}
/*
@@ -1899,7 +2007,7 @@ toast_fetch_datum(struct varlena *attr)
/* Must copy to access aligned fields */
VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
- ressize = toast_pointer.va_extsize;
+ ressize = VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer);
numchunks = ((ressize - 1) / TOAST_MAX_CHUNK_SIZE) + 1;
result = (struct varlena *) palloc(ressize + VARHDRSZ);
@@ -2084,7 +2192,7 @@ toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset, int32 length)
*/
Assert(!VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer));
- attrsize = toast_pointer.va_extsize;
+ attrsize = VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer);
totalchunks = ((attrsize - 1) / TOAST_MAX_CHUNK_SIZE) + 1;
if (sliceoffset >= attrsize)
@@ -2280,15 +2388,26 @@ toast_decompress_datum(struct varlena *attr)
Assert(VARATT_IS_COMPRESSED(attr));
- result = (struct varlena *)
- palloc(TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
- SET_VARSIZE(result, TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
+ if (VARATT_IS_CUSTOM_COMPRESSED(attr))
+ {
+ CompressionMethodOptions *cmoptions;
+ toast_compress_header_custom *hdr;
- if (pglz_decompress(TOAST_COMPRESS_RAWDATA(attr),
- VARSIZE(attr) - TOAST_COMPRESS_HDRSZ,
- VARDATA(result),
- TOAST_COMPRESS_RAWSIZE(attr)) < 0)
- elog(ERROR, "compressed data is corrupted");
+ hdr = (toast_compress_header_custom *) attr;
+ cmoptions = get_cached_compression_options(hdr->cmoptoid);
+ result = cmoptions->routine->decompress(cmoptions, attr);
+ }
+ else
+ {
+ result = (struct varlena *)
+ palloc(TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
+ SET_VARSIZE(result, TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
+ if (pglz_decompress(TOAST_COMPRESS_RAWDATA(attr),
+ VARSIZE(attr) - TOAST_COMPRESS_HDRSZ,
+ VARDATA(result),
+ TOAST_COMPRESS_RAWSIZE(attr)) < 0)
+ elog(ERROR, "compressed data is corrupted");
+ }
return result;
}
@@ -2390,3 +2509,78 @@ init_toast_snapshot(Snapshot toast_snapshot)
InitToastSnapshot(*toast_snapshot, snapshot->lsn, snapshot->whenTaken);
}
+
+/* ----------
+ * init_compression_options_cache
+ *
+ * Initialize a local cache for compression options.
+ */
+static void
+init_compression_options_cache(void)
+{
+ HASHCTL ctl;
+
+ compression_options_mcxt = AllocSetContextCreate(TopMemoryContext,
+ "compression options cache context",
+ ALLOCSET_DEFAULT_SIZES);
+ MemSet(&ctl, 0, sizeof(ctl));
+ ctl.keysize = sizeof(Oid);
+ ctl.entrysize = sizeof(CompressionMethodOptions);
+ ctl.hcxt = compression_options_mcxt;
+ compression_options_cache = hash_create("compression options cache", 100, &ctl,
+ HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
+}
+
+/* ----------
+ * get_cached_compression_options
+ *
+ * Remove cached compression options from the local cache
+ */
+static inline void
+remove_cached_compression_options(Oid cmoptoid)
+{
+ bool found;
+
+ hash_search(compression_options_cache, &cmoptoid, HASH_REMOVE, &found);
+}
+
+/* ----------
+ * get_cached_compression_options
+ *
+ * Get cached compression options structure or create it if it's not in cache.
+ * Cache is required because we can't afford for each tuple create
+ * CompressionMethodRoutine and parse its options.
+ */
+static CompressionMethodOptions *
+get_cached_compression_options(Oid cmoptoid)
+{
+ bool found;
+ CompressionMethodOptions *result;
+
+ Assert(OidIsValid(cmoptoid));
+ if (!compression_options_cache)
+ init_compression_options_cache();
+
+ /* check if the compression options wasn't removed from the last check */
+ found = SearchSysCacheExists(COMPRESSIONOPTIONSOID,
+ ObjectIdGetDatum(cmoptoid), 0, 0, 0);
+ if (!found)
+ {
+ remove_cached_compression_options(cmoptoid);
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+ }
+
+ result = hash_search(compression_options_cache, &cmoptoid, HASH_ENTER, &found);
+ if (!found)
+ {
+ MemoryContext oldcxt;
+
+ Assert(compression_options_mcxt);
+ oldcxt = MemoryContextSwitchTo(compression_options_mcxt);
+ result->cmoptoid = cmoptoid;
+ result->routine = GetCompressionMethodRoutine(cmoptoid);
+ result->options = GetCompressionOptionsList(cmoptoid);
+ MemoryContextSwitchTo(oldcxt);
+ }
+ return result;
+}
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 8287de97a2..be6b460aee 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -731,6 +731,7 @@ DefineAttr(char *name, char *type, int attnum, int nullness)
attrtypes[attnum]->attcacheoff = -1;
attrtypes[attnum]->atttypmod = -1;
attrtypes[attnum]->attislocal = true;
+ attrtypes[attnum]->attcompression = InvalidOid;
if (nullness == BOOTCOL_NULL_FORCE_NOT_NULL)
{
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index fd33426bad..c7cea974b1 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -46,7 +46,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_collation.h pg_partitioned_table.h pg_range.h pg_transform.h \
pg_sequence.h pg_publication.h pg_publication_rel.h pg_subscription.h \
pg_subscription_rel.h toasting.h indexing.h \
- toasting.h indexing.h \
+ pg_compression.h pg_compression_opt.h \
)
# location of Catalog.pm
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index ccde66a7dd..fd733a34a0 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -3340,6 +3340,8 @@ static const char *const no_priv_msg[MAX_ACL_KIND] =
gettext_noop("permission denied for publication %s"),
/* ACL_KIND_SUBSCRIPTION */
gettext_noop("permission denied for subscription %s"),
+ /* ACL_KIND_COMPRESSION_METHOD */
+ gettext_noop("permission denied for compression method %s"),
};
static const char *const not_owner_msg[MAX_ACL_KIND] =
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 033c4358ea..e1bfc7c6bf 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -28,6 +28,8 @@
#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_collation_fn.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_conversion.h"
@@ -173,7 +175,9 @@ static const Oid object_classes[] = {
PublicationRelationId, /* OCLASS_PUBLICATION */
PublicationRelRelationId, /* OCLASS_PUBLICATION_REL */
SubscriptionRelationId, /* OCLASS_SUBSCRIPTION */
- TransformRelationId /* OCLASS_TRANSFORM */
+ TransformRelationId, /* OCLASS_TRANSFORM */
+ CompressionMethodRelationId, /* OCLASS_COMPRESSION_METHOD */
+ CompressionOptRelationId, /* OCLASS_COMPRESSION_OPTIONS */
};
@@ -1271,6 +1275,14 @@ doDeletion(const ObjectAddress *object, int flags)
DropTransformById(object->objectId);
break;
+ case OCLASS_COMPRESSION_METHOD:
+ RemoveCompressionMethodById(object->objectId);
+ break;
+
+ case OCLASS_COMPRESSION_OPTIONS:
+ RemoveCompressionOptionsById(object->objectId);
+ break;
+
/*
* These global object types are not supported here.
*/
@@ -2512,6 +2524,12 @@ getObjectClass(const ObjectAddress *object)
case TransformRelationId:
return OCLASS_TRANSFORM;
+
+ case CompressionMethodRelationId:
+ return OCLASS_COMPRESSION_METHOD;
+
+ case CompressionOptRelationId:
+ return OCLASS_COMPRESSION_OPTIONS;
}
/* shouldn't get here */
diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl
index 256a9c9c93..c5838fa779 100644
--- a/src/backend/catalog/genbki.pl
+++ b/src/backend/catalog/genbki.pl
@@ -451,6 +451,7 @@ sub emit_pgattr_row
attisdropped => 'f',
attislocal => 't',
attinhcount => '0',
+ attcompression=> '0',
attacl => '_null_',
attoptions => '_null_',
attfdwoptions => '_null_');
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 4319fc6b8c..8c1cc48060 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -29,8 +29,10 @@
*/
#include "postgres.h"
+#include "access/compression.h"
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/reloptions.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "access/xact.h"
@@ -44,6 +46,8 @@
#include "catalog/partition.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_foreign_table.h"
@@ -628,6 +632,7 @@ InsertPgAttributeTuple(Relation pg_attribute_rel,
values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal);
values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount);
values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(new_attribute->attcollation);
+ values[Anum_pg_attribute_attcompression - 1] = ObjectIdGetDatum(new_attribute->attcompression);
/* start out with empty permissions and empty options */
nulls[Anum_pg_attribute_attacl - 1] = true;
@@ -1453,6 +1458,24 @@ DeleteRelationTuple(Oid relid)
heap_close(pg_class_desc, RowExclusiveLock);
}
+/*
+ * CallCompressionDropCallback
+ *
+ * Call drop callback from compression routine.
+ */
+static void
+CallCompressionDropCallback(Form_pg_attribute att)
+{
+ CompressionMethodRoutine *cmr = GetCompressionMethodRoutine(att->attcompression);
+
+ if (cmr->drop)
+ {
+ List *options = GetCompressionOptionsList(att->attcompression);
+
+ cmr->drop(att, options);
+ }
+}
+
/*
* DeleteAttributeTuples
*
@@ -1483,7 +1506,14 @@ DeleteAttributeTuples(Oid relid)
/* Delete all the matching tuples */
while ((atttup = systable_getnext(scan)) != NULL)
+ {
+ Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(atttup);
+
+ if (OidIsValid(att->attcompression))
+ CallCompressionDropCallback(att);
+
CatalogTupleDelete(attrel, &atttup->t_self);
+ }
/* Clean up after the scan */
systable_endscan(scan);
@@ -1576,6 +1606,8 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
else
{
/* Dropping user attributes is lots harder */
+ if (OidIsValid(attStruct->attcompression))
+ CallCompressionDropCallback(attStruct);
/* Mark the attribute as dropped */
attStruct->attisdropped = true;
@@ -1597,6 +1629,8 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
/* We don't want to keep stats for it anymore */
attStruct->attstattarget = 0;
+ attStruct->attcompression = InvalidOid;
+
/*
* Change the column name to something that isn't likely to conflict
*/
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 0125c18bc1..15942564aa 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -393,6 +393,7 @@ ConstructTupleDescriptor(Relation heapRelation,
to->atttypmod = exprTypmod(indexkey);
to->attislocal = true;
to->attcollation = collationObjectId[i];
+ to->attcompression = InvalidOid;
ReleaseSysCache(tuple);
@@ -471,6 +472,7 @@ ConstructTupleDescriptor(Relation heapRelation,
to->attbyval = typeTup->typbyval;
to->attalign = typeTup->typalign;
to->attstorage = typeTup->typstorage;
+ to->attcompression = InvalidOid;
ReleaseSysCache(tuple);
}
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 8d55c76fc4..9fd1cb763d 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -29,6 +29,8 @@
#include "catalog/pg_default_acl.h"
#include "catalog/pg_event_trigger.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_conversion.h"
@@ -490,6 +492,30 @@ static const ObjectPropertyType ObjectProperty[] =
InvalidAttrNumber, /* no ACL (same as relation) */
ACL_KIND_STATISTICS,
true
+ },
+ {
+ CompressionMethodRelationId,
+ CompressionMethodOidIndexId,
+ COMPRESSIONMETHODOID,
+ COMPRESSIONMETHODNAME,
+ Anum_pg_compression_cmname,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ true
+ },
+ {
+ CompressionOptRelationId,
+ CompressionOptionsOidIndexId,
+ COMPRESSIONOPTIONSOID,
+ -1,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ false,
}
};
@@ -712,6 +738,10 @@ static const struct object_type_map
/* OBJECT_STATISTIC_EXT */
{
"statistics object", OBJECT_STATISTIC_EXT
+ },
+ /* OCLASS_COMPRESSION_METHOD */
+ {
+ "compression method", OBJECT_COMPRESSION_METHOD
}
};
@@ -876,6 +906,7 @@ get_object_address(ObjectType objtype, Node *object,
case OBJECT_ACCESS_METHOD:
case OBJECT_PUBLICATION:
case OBJECT_SUBSCRIPTION:
+ case OBJECT_COMPRESSION_METHOD:
address = get_object_address_unqualified(objtype,
(Value *) object, missing_ok);
break;
@@ -1182,6 +1213,11 @@ get_object_address_unqualified(ObjectType objtype,
address.objectId = get_subscription_oid(name, missing_ok);
address.objectSubId = 0;
break;
+ case OBJECT_COMPRESSION_METHOD:
+ address.classId = CompressionMethodRelationId;
+ address.objectId = get_compression_method_oid(name, missing_ok);
+ address.objectSubId = 0;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
/* placate compiler, which doesn't know elog won't return */
@@ -2139,6 +2175,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_SCHEMA:
case OBJECT_SUBSCRIPTION:
case OBJECT_TABLESPACE:
+ case OBJECT_COMPRESSION_METHOD:
if (list_length(name) != 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -2395,12 +2432,14 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
case OBJECT_ACCESS_METHOD:
+ case OBJECT_COMPRESSION_METHOD:
/* We treat these object types as being owned by superusers */
if (!superuser_arg(roleid))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser")));
break;
+
case OBJECT_STATISTIC_EXT:
if (!pg_statistics_object_ownercheck(address.objectId, roleid))
aclcheck_error_type(ACLCHECK_NOT_OWNER, address.objectId);
@@ -3398,6 +3437,27 @@ getObjectDescription(const ObjectAddress *object)
break;
}
+ case OCLASS_COMPRESSION_METHOD:
+ {
+ char *name = get_compression_method_name(object->objectId);
+
+ if (!name)
+ elog(ERROR, "cache lookup failed for compression method %u",
+ object->objectId);
+ appendStringInfo(&buffer, _("compression method %s"), name);
+ pfree(name);
+ break;
+ }
+
+ case OCLASS_COMPRESSION_OPTIONS:
+ {
+ char *name = get_compression_method_name_for_opt(object->objectId);
+
+ appendStringInfo(&buffer, _("compression options for %s"), name);
+ pfree(name);
+ break;
+ }
+
case OCLASS_TRANSFORM:
{
HeapTuple trfTup;
@@ -3919,6 +3979,14 @@ getObjectTypeDescription(const ObjectAddress *object)
appendStringInfoString(&buffer, "transform");
break;
+ case OCLASS_COMPRESSION_METHOD:
+ appendStringInfoString(&buffer, "compression method");
+ break;
+
+ case OCLASS_COMPRESSION_OPTIONS:
+ appendStringInfoString(&buffer, "compression options");
+ break;
+
/*
* There's intentionally no default: case here; we want the
* compiler to warn if a new OCLASS hasn't been handled above.
@@ -4160,6 +4228,30 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case OCLASS_COMPRESSION_METHOD:
+ {
+ char *cmname = get_compression_method_name(object->objectId);
+
+ if (!cmname)
+ elog(ERROR, "cache lookup failed for compression method %u",
+ object->objectId);
+ appendStringInfoString(&buffer, quote_identifier(cmname));
+ if (objname)
+ *objname = list_make1(cmname);
+ else
+ pfree(cmname);
+ break;
+ }
+
+ case OCLASS_COMPRESSION_OPTIONS:
+ {
+ appendStringInfo(&buffer, "%u",
+ object->objectId);
+ if (objname)
+ *objname = list_make1(psprintf("%u", object->objectId));
+ break;
+ }
+
case OCLASS_CONSTRAINT:
{
HeapTuple conTup;
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index e02d312008..531a820464 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -22,6 +22,7 @@
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 4a6c99e090..e23abf64f1 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -13,8 +13,8 @@ top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
- collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
- dbcommands.o define.o discard.o dropcmds.o \
+ collationcmds.o compressioncmds.o constraint.o conversioncmds.o copy.o \
+ createas.o dbcommands.o define.o discard.o dropcmds.o \
event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o matview.o operatorcmds.o opclasscmds.o \
policy.o portalcmds.o prepare.o proclang.o publicationcmds.o \
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 4f8147907c..4f18e4083f 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -385,6 +385,7 @@ ExecRenameStmt(RenameStmt *stmt)
case OBJECT_TSTEMPLATE:
case OBJECT_PUBLICATION:
case OBJECT_SUBSCRIPTION:
+ case OBJECT_COMPRESSION_METHOD:
{
ObjectAddress address;
Relation catalog;
@@ -500,6 +501,7 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
case OBJECT_TSDICTIONARY:
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
+ case OBJECT_COMPRESSION_METHOD:
{
Relation catalog;
Relation relation;
@@ -627,6 +629,8 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_COMPRESSION_METHOD:
+ case OCLASS_COMPRESSION_OPTIONS:
/* ignore object types that don't have schema-qualified names */
break;
diff --git a/src/backend/commands/compressioncmds.c b/src/backend/commands/compressioncmds.c
new file mode 100644
index 0000000000..5780926179
--- /dev/null
+++ b/src/backend/commands/compressioncmds.c
@@ -0,0 +1,522 @@
+/*-------------------------------------------------------------------------
+ *
+ * compressioncmds.c
+ * Routines for SQL commands that manipulate compression methods.
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/commands/compressioncmds.c
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "miscadmin.h"
+
+#include "access/compression.h"
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/reloptions.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "parser/parse_func.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+static CompressionMethodRoutine *invoke_compression_handler(Oid cmhandler, Oid typeid);
+
+/*
+ * Convert a handler function name to an Oid. If the return type of the
+ * function doesn't match the given AM type, an error is raised.
+ *
+ * This function either return valid function Oid or throw an error.
+ */
+static Oid
+lookup_compression_handler(List *handlerName)
+{
+ static const Oid funcargtypes[1] = {INTERNALOID};
+ Oid handlerOid;
+
+ if (handlerName == NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("handler function is not specified")));
+
+ /* handlers have one argument of type internal */
+ handlerOid = LookupFuncName(handlerName, 1, funcargtypes, false);
+
+ /* check that handler has the correct return type */
+ if (get_func_rettype(handlerOid) != COMPRESSION_HANDLEROID)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("function %s must return type %s",
+ NameListToString(handlerName),
+ "compression_handler")));
+
+ return handlerOid;
+}
+
+/* Creates a record in pg_compression */
+static ObjectAddress
+create_compression_method(char *cmName, List *handlerName)
+{
+ Relation rel;
+ ObjectAddress myself;
+ ObjectAddress referenced;
+ Oid cmoid;
+ Oid cmhandler;
+ bool nulls[Natts_pg_compression];
+ Datum values[Natts_pg_compression];
+ HeapTuple tup;
+
+ rel = heap_open(CompressionMethodRelationId, RowExclusiveLock);
+
+ /* Check if name is used */
+ cmoid = GetSysCacheOid1(COMPRESSIONMETHODNAME, CStringGetDatum(cmName));
+ if (OidIsValid(cmoid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("compression method \"%s\" already exists",
+ cmName)));
+
+ /*
+ * Get the handler function oid and compression method routine
+ */
+ cmhandler = lookup_compression_handler(handlerName);
+
+ /*
+ * Insert tuple into pg_compression.
+ */
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+
+ values[Anum_pg_compression_cmname - 1] =
+ DirectFunctionCall1(namein, CStringGetDatum(cmName));
+ values[Anum_pg_compression_cmhandler - 1] = ObjectIdGetDatum(cmhandler);
+
+ tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+
+ cmoid = CatalogTupleInsert(rel, tup);
+ heap_freetuple(tup);
+
+ ObjectAddressSet(myself, CompressionMethodRelationId, cmoid);
+
+ /* Record dependency on handler function */
+ ObjectAddressSet(referenced, ProcedureRelationId, cmhandler);
+
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ recordDependencyOnCurrentExtension(&myself, false);
+
+ heap_close(rel, RowExclusiveLock);
+
+ return myself;
+}
+
+/*
+ * Call the specified compression method handler
+ * routine to get its CompressionMethodRoutine struct,
+ * which will be palloc'd in the caller's context.
+ */
+static CompressionMethodRoutine *
+invoke_compression_handler(Oid cmhandler, Oid typeid)
+{
+ Datum datum;
+ CompressionMethodRoutine *routine;
+ CompressionMethodOpArgs opargs;
+
+ opargs.typeid = typeid;
+ opargs.cmhanderid = cmhandler;
+
+ datum = OidFunctionCall1(cmhandler, PointerGetDatum(&opargs));
+ routine = (CompressionMethodRoutine *) DatumGetPointer(datum);
+
+ if (routine == NULL || !IsA(routine, CompressionMethodRoutine))
+ elog(ERROR, "compression method handler function %u "
+ "did not return an CompressionMethodRoutine struct",
+ cmhandler);
+
+ return routine;
+}
+
+/*
+ * CREATE COMPRESSION METHOD .. HANDLER ..
+ */
+ObjectAddress
+DefineCompressionMethod(List *names, List *parameters)
+{
+ char *cmName;
+ ListCell *pl;
+ DefElem *handlerEl = NULL;
+
+ if (list_length(names) != 1)
+ elog(ERROR, "compression method name cannot be qualified");
+
+ cmName = strVal(linitial(names));
+
+ /* Must be super user */
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to create compression method \"%s\"",
+ cmName),
+ errhint("Must be superuser to create an compression method.")));
+
+ /* Extract the name of compression handler function */
+ foreach(pl, parameters)
+ {
+ DefElem *defel = (DefElem *) lfirst(pl);
+ DefElem **defelp;
+
+ if (pg_strcasecmp(defel->defname, "handler") == 0)
+ defelp = &handlerEl;
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("compression method attribute \"%s\" not recognized",
+ defel->defname)));
+ break;
+ }
+
+ *defelp = defel;
+ }
+
+ if (!handlerEl)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("compression method handler is not specified")));
+
+ /* Finally create a compression method */
+ return create_compression_method(cmName, (List *) handlerEl->arg);
+}
+
+/*
+ * RemoveCompressionMethodById
+ *
+ * Removes a compresssion method by its Oid.
+ */
+void
+RemoveCompressionMethodById(Oid cmOid)
+{
+ Relation relation;
+ HeapTuple tup;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to drop compression methods")));
+
+ relation = heap_open(CompressionMethodRelationId, RowExclusiveLock);
+
+ tup = SearchSysCache1(COMPRESSIONMETHODOID, ObjectIdGetDatum(cmOid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for compression method %u", cmOid);
+
+ CatalogTupleDelete(relation, &tup->t_self);
+
+ ReleaseSysCache(tup);
+
+ heap_close(relation, RowExclusiveLock);
+}
+
+/*
+ * Create new record in pg_compression_opt
+ */
+Oid
+CreateCompressionOptions(Form_pg_attribute attr, ColumnCompression *compression)
+{
+ Relation rel;
+ HeapTuple tup,
+ newtup;
+ Oid cmoptoid,
+ cmid;
+ Datum values[Natts_pg_compression_opt];
+ bool nulls[Natts_pg_compression_opt];
+
+ ObjectAddress myself,
+ ref1,
+ ref2;
+
+ CompressionMethodRoutine *routine;
+ Form_pg_compression cmform;
+
+ /* Initialize buffers for new tuple values */
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+
+ cmid = get_compression_method_oid(compression->methodName, false);
+
+ /* Get handler function OID for the compression method */
+ tup = SearchSysCache1(COMPRESSIONMETHODOID, ObjectIdGetDatum(cmid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for compression method %u", cmid);
+ cmform = (Form_pg_compression) GETSTRUCT(tup);
+ routine = invoke_compression_handler(cmform->cmhandler, attr->atttypid);
+
+ if (routine->configure == NULL && compression->options != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("the compression method \"%s\" does not take any options",
+ NameStr(cmform->cmname))));
+ else if (routine->configure && compression->options != NIL)
+ routine->configure(attr, compression->options);
+
+ rel = heap_open(CompressionOptRelationId, RowExclusiveLock);
+
+ cmoptoid = GetNewOidWithIndex(rel, CompressionOptionsOidIndexId,
+ Anum_pg_compression_opt_cmoptoid);
+ values[Anum_pg_compression_opt_cmoptoid - 1] = ObjectIdGetDatum(cmoptoid);
+ values[Anum_pg_compression_opt_cmname - 1] = NameGetDatum(&cmform->cmname);
+ values[Anum_pg_compression_opt_cmhandler - 1] = ObjectIdGetDatum(cmform->cmhandler);
+
+ if (compression->options)
+ values[Anum_pg_compression_opt_cmoptions - 1] =
+ optionListToArray(compression->options);
+ else
+ nulls[Anum_pg_compression_opt_cmoptions - 1] = true;
+
+ newtup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+ CatalogTupleInsert(rel, newtup);
+ heap_freetuple(newtup);
+
+ ReleaseSysCache(tup);
+
+ ObjectAddressSet(myself, CompressionOptRelationId, cmoptoid);
+ ObjectAddressSet(ref1, ProcedureRelationId, cmform->cmhandler);
+ ObjectAddressSet(ref2, CompressionMethodRelationId, cmid);
+
+ recordDependencyOn(&myself, &ref1, DEPENDENCY_NORMAL);
+ recordDependencyOn(&myself, &ref2, DEPENDENCY_NORMAL);
+ recordDependencyOnCurrentExtension(&myself, false);
+ heap_close(rel, RowExclusiveLock);
+
+ return cmoptoid;
+}
+
+/*
+ * Create pg_depend record between attribute and its compression options
+ */
+void
+CreateColumnCompressionDependency(Form_pg_attribute attr, Oid cmoptoid)
+{
+ ObjectAddress optref,
+ attrref;
+
+ ObjectAddressSet(optref, CompressionOptRelationId, cmoptoid);
+ ObjectAddressSubSet(attrref, RelationRelationId, attr->attrelid, attr->attnum);
+ recordDependencyOn(&attrref, &optref, DEPENDENCY_NORMAL);
+}
+
+/*
+ * Remove the compression options record from pg_compression_opt
+ */
+void
+RemoveCompressionOptionsById(Oid cmoptoid)
+{
+ Relation relation;
+ HeapTuple tup;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to drop compression options")));
+
+ tup = SearchSysCache1(COMPRESSIONOPTIONSOID, ObjectIdGetDatum(cmoptoid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+
+ relation = heap_open(CompressionOptRelationId, RowExclusiveLock);
+ CatalogTupleDelete(relation, &tup->t_self);
+ heap_close(relation, RowExclusiveLock);
+ ReleaseSysCache(tup);
+}
+
+ColumnCompression *
+GetColumnCompressionForAttribute(Form_pg_attribute att)
+{
+ HeapTuple tuple;
+ Form_pg_compression_opt cmoptform;
+ ColumnCompression *compression = makeNode(ColumnCompression);
+
+ /* Get handler function OID for the compression method */
+ tuple = SearchSysCache1(COMPRESSIONOPTIONSOID,
+ ObjectIdGetDatum(att->attcompression));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for compression options %u",
+ att->attcompression);
+
+ cmoptform = (Form_pg_compression_opt) GETSTRUCT(tuple);
+ compression->methodName = pstrdup(NameStr(cmoptform->cmname));
+ compression->options = GetCompressionOptionsList(att->attcompression);
+ ReleaseSysCache(tuple);
+
+ return compression;
+}
+
+void
+CheckCompressionMismatch(ColumnCompression *c1, ColumnCompression *c2,
+ const char *attributeName)
+{
+ if (strcmp(c1->methodName, c2->methodName))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("column \"%s\" has a compression method conflict",
+ attributeName),
+ errdetail("%s versus %s", c1->methodName, c2->methodName)));
+
+ if (!equal(c1->options, c2->options))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("column \"%s\" has a compression options conflict",
+ attributeName),
+ errdetail("(%s) versus (%s)",
+ formatRelOptions(c1->options),
+ formatRelOptions(c2->options))));
+}
+
+/*
+ * get_compression_method_oid
+ *
+ * If missing_ok is false, throw an error if compression method not found.
+ * If missing_ok is true, just return InvalidOid.
+ */
+Oid
+get_compression_method_oid(const char *cmname, bool missing_ok)
+{
+ HeapTuple tup;
+ Oid oid = InvalidOid;
+
+ tup = SearchSysCache1(COMPRESSIONMETHODNAME, CStringGetDatum(cmname));
+ if (HeapTupleIsValid(tup))
+ {
+ oid = HeapTupleGetOid(tup);
+ ReleaseSysCache(tup);
+ }
+
+ if (!OidIsValid(oid) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("compression method \"%s\" does not exist", cmname)));
+
+ return oid;
+}
+
+/*
+ * get_compression_method_name
+ *
+ * given an compression method OID, look up its name.
+ */
+char *
+get_compression_method_name(Oid cmOid)
+{
+ HeapTuple tup;
+ char *result = NULL;
+
+ tup = SearchSysCache1(COMPRESSIONMETHODOID, ObjectIdGetDatum(cmOid));
+ if (HeapTupleIsValid(tup))
+ {
+ Form_pg_compression cmform = (Form_pg_compression) GETSTRUCT(tup);
+
+ result = pstrdup(NameStr(cmform->cmname));
+ ReleaseSysCache(tup);
+ }
+ return result;
+}
+
+/*
+ * get_compression_method_name_for_opt
+ *
+ * given an compression options OID, look up a name for used compression method.
+ */
+char *
+get_compression_method_name_for_opt(Oid cmoptoid)
+{
+ HeapTuple tup;
+ char *result = NULL;
+ Form_pg_compression_opt cmoptform;
+
+ tup = SearchSysCache1(COMPRESSIONOPTIONSOID, ObjectIdGetDatum(cmoptoid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+
+ cmoptform = (Form_pg_compression_opt) GETSTRUCT(tup);
+ result = pstrdup(NameStr(cmoptform->cmname));
+ ReleaseSysCache(tup);
+
+ return result;
+}
+
+/*
+ * GetCompressionOptionsList
+ *
+ * Parse array with compression options and return it as list.
+ */
+List *
+GetCompressionOptionsList(Oid cmoptoid)
+{
+ HeapTuple tuple;
+ List *result = NULL;
+ bool isnull;
+ Datum cmoptions;
+
+ /* Get handler function OID for the compression method */
+ tuple = SearchSysCache1(COMPRESSIONOPTIONSOID, ObjectIdGetDatum(cmoptoid));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+
+ cmoptions = SysCacheGetAttr(COMPRESSIONOPTIONSOID, tuple,
+ Anum_pg_compression_opt_cmoptions, &isnull);
+
+ if (!isnull)
+ result = untransformRelOptions(cmoptions);
+
+ ReleaseSysCache(tuple);
+ return result;
+}
+
+/*
+ * GetCompressionMethodRoutine
+ *
+ * Determine compression handler by compression options Oid and return
+ * structure with compression methods
+ */
+CompressionMethodRoutine *
+GetCompressionMethodRoutine(Oid cmoptoid)
+{
+ HeapTuple tuple;
+ Form_pg_compression_opt cmopt;
+ regproc cmhandler;
+
+ /* Get handler function OID for the compression method */
+ tuple = SearchSysCache1(COMPRESSIONOPTIONSOID, ObjectIdGetDatum(cmoptoid));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for compression options %u", cmoptoid);
+
+ cmopt = (Form_pg_compression_opt) GETSTRUCT(tuple);
+ cmhandler = cmopt->cmhandler;
+
+ /* Complain if handler OID is invalid */
+ if (!RegProcedureIsValid(cmhandler))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("could not find compression method handler for compression options %u",
+ cmoptoid)));
+
+ ReleaseSysCache(tuple);
+
+ /* And finally, call the handler function to get the API struct. */
+ return invoke_compression_handler(cmhandler, InvalidOid);
+}
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 13eb9e34ba..f84588ed31 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2781,8 +2781,8 @@ CopyFrom(CopyState cstate)
List *recheckIndexes = NIL;
/* OK, store the tuple and create index entries for it */
- heap_insert(resultRelInfo->ri_RelationDesc, tuple, mycid,
- hi_options, bistate);
+ heap_insert(resultRelInfo->ri_RelationDesc, tuple,
+ mycid, hi_options, bistate);
if (resultRelInfo->ri_NumIndices > 0)
recheckIndexes = ExecInsertIndexTuples(slot,
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index 2b30677d6f..8611db22ec 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -275,6 +275,10 @@ does_not_exist_skipping(ObjectType objtype, Node *object)
name = NameListToString(castNode(List, object));
}
break;
+ case OBJECT_COMPRESSION_METHOD:
+ msg = gettext_noop("compression method \"%s\" does not exist, skipping");
+ name = strVal((Value *) object);
+ break;
case OBJECT_CONVERSION:
if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
{
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index fa7d0d015a..a2eee78a64 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -91,6 +91,7 @@ static event_trigger_support_data event_trigger_support[] = {
{"CAST", true},
{"CONSTRAINT", true},
{"COLLATION", true},
+ {"COMPRESSION METHOD", true},
{"CONVERSION", true},
{"DATABASE", false},
{"DOMAIN", true},
@@ -1085,6 +1086,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
case OBJECT_CAST:
case OBJECT_COLUMN:
case OBJECT_COLLATION:
+ case OBJECT_COMPRESSION_METHOD:
case OBJECT_CONVERSION:
case OBJECT_DEFACL:
case OBJECT_DEFAULT:
@@ -1144,6 +1146,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
case OCLASS_ROLE:
+ case OCLASS_COMPRESSION_OPTIONS:
/* no support for global objects */
return false;
case OCLASS_EVENT_TRIGGER:
@@ -1183,6 +1186,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_COMPRESSION_METHOD:
return true;
/*
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index 9ad991507f..b840309d03 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -61,7 +61,7 @@ static void import_error_callback(void *arg);
* processing, hence any validation should be done before this
* conversion.
*/
-static Datum
+Datum
optionListToArray(List *options)
{
ArrayBuildState *astate = NULL;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index d979ce266d..ed1c5aa577 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -14,6 +14,7 @@
*/
#include "postgres.h"
+#include "access/compression.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "access/multixact.h"
@@ -33,6 +34,8 @@
#include "catalog/partition.h"
#include "catalog/pg_am.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_depend.h"
@@ -41,6 +44,7 @@
#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
+#include "catalog/pg_proc.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
@@ -90,6 +94,7 @@
#include "storage/smgr.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+#include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
@@ -459,6 +464,8 @@ static void ATExecGenericOptions(Relation rel, List *options);
static void ATExecEnableRowSecurity(Relation rel);
static void ATExecDisableRowSecurity(Relation rel);
static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
+static void ATExecAlterColumnCompression(AlteredTableInfo *tab, Relation rel,
+ const char *column, ColumnCompression *compression, LOCKMODE lockmode);
static void copy_relation_data(SMgrRelation rel, SMgrRelation dst,
ForkNumber forkNum, char relpersistence);
@@ -724,6 +731,12 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
if (colDef->identity)
attr->attidentity = colDef->identity;
+
+ /* Create compression options */
+ if (colDef->compression)
+ attr->attcompression = CreateCompressionOptions(attr, colDef->compression);
+ else
+ attr->attcompression = InvalidOid;
}
/*
@@ -771,6 +784,19 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
*/
rel = relation_open(relationId, AccessExclusiveLock);
+ /*
+ * We should also create dependencies between attributes and their
+ * compression options
+ */
+ for (attnum = 0; attnum < (RelationGetDescr(rel))->natts; attnum++)
+ {
+ Form_pg_attribute attr;
+
+ attr = TupleDescAttr(RelationGetDescr(rel), attnum);
+ if (OidIsValid(attr->attcompression))
+ CreateColumnCompressionDependency(attr, attr->attcompression);
+ }
+
/* Process and store partition bound, if any. */
if (stmt->partbound)
{
@@ -1610,6 +1636,7 @@ storage_name(char c)
}
}
+
/*----------
* MergeAttributes
* Returns new schema given initial schema and superclasses.
@@ -1944,6 +1971,19 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
storage_name(def->storage),
storage_name(attribute->attstorage))));
+ if (OidIsValid(attribute->attcompression))
+ {
+ ColumnCompression *compression =
+ GetColumnCompressionForAttribute(attribute);
+
+ if (!def->compression)
+ def->compression = compression;
+ else
+ CheckCompressionMismatch(def->compression,
+ compression,
+ attributeName);
+ }
+
def->inhcount++;
/* Merge of NOT NULL constraints = OR 'em together */
def->is_not_null |= attribute->attnotnull;
@@ -1971,6 +2011,9 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
def->collOid = attribute->attcollation;
def->constraints = NIL;
def->location = -1;
+ if (OidIsValid(attribute->attcompression))
+ def->compression =
+ GetColumnCompressionForAttribute(attribute);
inhSchema = lappend(inhSchema, def);
newattno[parent_attno - 1] = ++child_attno;
}
@@ -2180,6 +2223,13 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
storage_name(def->storage),
storage_name(newdef->storage))));
+ if (!def->compression)
+ def->compression = newdef->compression;
+ else if (newdef->compression)
+ CheckCompressionMismatch(def->compression,
+ newdef->compression,
+ attributeName);
+
/* Mark the column as locally defined */
def->is_local = true;
/* Merge of NOT NULL constraints = OR 'em together */
@@ -3279,6 +3329,7 @@ AlterTableGetLockLevel(List *cmds)
*/
case AT_GenericOptions:
case AT_AlterColumnGenericOptions:
+ case AT_AlterColumnCompression:
cmd_lockmode = AccessExclusiveLock;
break;
@@ -3770,6 +3821,12 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* No command-specific prep needed */
pass = AT_PASS_MISC;
break;
+ case AT_AlterColumnCompression:
+ ATSimplePermissions(rel, ATT_TABLE);
+ /* FIXME This command never recurses */
+ /* No command-specific prep needed */
+ pass = AT_PASS_MISC;
+ break;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
@@ -4118,6 +4175,11 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
case AT_DetachPartition:
ATExecDetachPartition(rel, ((PartitionCmd *) cmd->def)->name);
break;
+ case AT_AlterColumnCompression:
+ ATExecAlterColumnCompression(tab, rel, cmd->name,
+ (ColumnCompression *) cmd->def,
+ lockmode);
+ break;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
@@ -5338,6 +5400,8 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
attribute.attislocal = colDef->is_local;
attribute.attinhcount = colDef->inhcount;
attribute.attcollation = collOid;
+ attribute.attcompression = InvalidOid;
+
/* attribute.attacl is handled by InsertPgAttributeTuple */
ReleaseSysCache(typeTuple);
@@ -6438,6 +6502,11 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE loc
errmsg("cannot alter system column \"%s\"",
colName)));
+ if (attrtuple->attcompression && newstorage != 'x' && newstorage != 'm')
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("compressed columns can only have storage MAIN or EXTENDED")));
+
/*
* safety check: do not allow toasted storage modes unless column datatype
* is TOAST-aware.
@@ -9361,6 +9430,8 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_COMPRESSION_METHOD:
+ case OCLASS_COMPRESSION_OPTIONS:
/*
* We don't expect any of these sorts of objects to depend on
@@ -9410,7 +9481,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
if (!(foundDep->refclassid == TypeRelationId &&
foundDep->refobjid == attTup->atttypid) &&
!(foundDep->refclassid == CollationRelationId &&
- foundDep->refobjid == attTup->attcollation))
+ foundDep->refobjid == attTup->attcollation) &&
+ !(foundDep->refclassid == CompressionMethodRelationId &&
+ foundDep->refobjid == attTup->attcompression))
elog(ERROR, "found unexpected dependency for column");
CatalogTupleDelete(depRel, &depTup->t_self);
@@ -9432,6 +9505,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
attTup->attbyval = tform->typbyval;
attTup->attalign = tform->typalign;
attTup->attstorage = tform->typstorage;
+ attTup->attcompression = InvalidOid;
ReleaseSysCache(typeTuple);
@@ -12484,6 +12558,82 @@ ATExecGenericOptions(Relation rel, List *options)
heap_freetuple(tuple);
}
+static void
+ATExecAlterColumnCompression(AlteredTableInfo *tab,
+ Relation rel,
+ const char *column,
+ ColumnCompression *compression,
+ LOCKMODE lockmode)
+{
+ Relation attrel;
+ HeapTuple atttuple;
+ Form_pg_attribute atttableform;
+ AttrNumber attnum;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_attribute];
+ bool nulls[Natts_pg_attribute];
+ bool replace[Natts_pg_attribute];
+
+ attrel = heap_open(AttributeRelationId, RowExclusiveLock);
+
+ atttuple = SearchSysCacheAttName(RelationGetRelid(rel), column);
+ if (!HeapTupleIsValid(atttuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("column \"%s\" of relation \"%s\" does not exist",
+ column, RelationGetRelationName(rel))));
+
+ /* Prevent them from altering a system attribute */
+ atttableform = (Form_pg_attribute) GETSTRUCT(atttuple);
+ attnum = atttableform->attnum;
+ if (attnum <= 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot alter system column \"%s\"", column)));
+
+ if (atttableform->attstorage != 'x' && atttableform->attstorage != 'm')
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("storage for \"%s\" should be MAIN or EXTENDED", column)));
+
+ /* Initialize buffers for new tuple values */
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+ memset(replace, false, sizeof(replace));
+
+ if (compression->methodName)
+ {
+ /* SET COMPRESSED */
+ Oid cmoptoid;
+
+ cmoptoid = CreateCompressionOptions(atttableform, compression);
+ CreateColumnCompressionDependency(atttableform, cmoptoid);
+
+ values[Anum_pg_attribute_attcompression - 1] = ObjectIdGetDatum(cmoptoid);
+ replace[Anum_pg_attribute_attcompression - 1] = true;
+
+ }
+ else
+ {
+ /* SET NOT COMPRESSED */
+ values[Anum_pg_attribute_attcompression - 1] = ObjectIdGetDatum(InvalidOid);
+ replace[Anum_pg_attribute_attcompression - 1] = true;
+ }
+
+ newtuple = heap_modify_tuple(atttuple, RelationGetDescr(attrel),
+ values, nulls, replace);
+ CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
+ heap_freetuple(newtuple);
+
+ InvokeObjectPostAlterHook(RelationRelationId,
+ RelationGetRelid(rel),
+ atttableform->attnum);
+
+ ReleaseSysCache(atttuple);
+ heap_close(attrel, RowExclusiveLock);
+}
+
+
/*
* Preparation phase for SET LOGGED/UNLOGGED
*
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index f86af4c054..a7e9111c8d 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -40,6 +40,7 @@
#include "catalog/pg_am.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_depend.h"
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index e48921fff1..1f14818d32 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2806,6 +2806,7 @@ _copyColumnDef(const ColumnDef *from)
COPY_STRING_FIELD(colname);
COPY_NODE_FIELD(typeName);
+ COPY_NODE_FIELD(compression);
COPY_SCALAR_FIELD(inhcount);
COPY_SCALAR_FIELD(is_local);
COPY_SCALAR_FIELD(is_not_null);
@@ -2824,6 +2825,17 @@ _copyColumnDef(const ColumnDef *from)
return newnode;
}
+static ColumnCompression *
+_copyColumnCompression(const ColumnCompression *from)
+{
+ ColumnCompression *newnode = makeNode(ColumnCompression);
+
+ COPY_STRING_FIELD(methodName);
+ COPY_NODE_FIELD(options);
+
+ return newnode;
+}
+
static Constraint *
_copyConstraint(const Constraint *from)
{
@@ -5473,6 +5485,9 @@ copyObjectImpl(const void *from)
case T_ColumnDef:
retval = _copyColumnDef(from);
break;
+ case T_ColumnCompression:
+ retval = _copyColumnCompression(from);
+ break;
case T_Constraint:
retval = _copyConstraint(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 2a83da9aa9..4eddc69602 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2544,6 +2544,7 @@ _equalColumnDef(const ColumnDef *a, const ColumnDef *b)
{
COMPARE_STRING_FIELD(colname);
COMPARE_NODE_FIELD(typeName);
+ COMPARE_NODE_FIELD(compression);
COMPARE_SCALAR_FIELD(inhcount);
COMPARE_SCALAR_FIELD(is_local);
COMPARE_SCALAR_FIELD(is_not_null);
@@ -2562,6 +2563,15 @@ _equalColumnDef(const ColumnDef *a, const ColumnDef *b)
return true;
}
+static bool
+_equalColumnCompression(const ColumnCompression *a, const ColumnCompression *b)
+{
+ COMPARE_STRING_FIELD(methodName);
+ COMPARE_NODE_FIELD(options);
+
+ return true;
+}
+
static bool
_equalConstraint(const Constraint *a, const Constraint *b)
{
@@ -3615,6 +3625,9 @@ equal(const void *a, const void *b)
case T_ColumnDef:
retval = _equalColumnDef(a, b);
break;
+ case T_ColumnCompression:
+ retval = _equalColumnCompression(a, b);
+ break;
case T_Constraint:
retval = _equalConstraint(a, b);
break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index c2a93b2d4c..81e334a1d3 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -3623,6 +3623,8 @@ raw_expression_tree_walker(Node *node,
if (walker(coldef->typeName, context))
return true;
+ if (walker(coldef->compression, context))
+ return true;
if (walker(coldef->raw_default, context))
return true;
if (walker(coldef->collClause, context))
@@ -3630,6 +3632,14 @@ raw_expression_tree_walker(Node *node,
/* for now, constraints are ignored */
}
break;
+ case T_ColumnCompression:
+ {
+ ColumnCompression *colcmp = (ColumnCompression *) node;
+
+ if (walker(colcmp->options, context))
+ return true;
+ }
+ break;
case T_IndexElem:
{
IndexElem *indelem = (IndexElem *) node;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index c97ee24ade..72e88de08f 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2799,6 +2799,7 @@ _outColumnDef(StringInfo str, const ColumnDef *node)
WRITE_STRING_FIELD(colname);
WRITE_NODE_FIELD(typeName);
+ WRITE_NODE_FIELD(compression);
WRITE_INT_FIELD(inhcount);
WRITE_BOOL_FIELD(is_local);
WRITE_BOOL_FIELD(is_not_null);
@@ -2815,6 +2816,15 @@ _outColumnDef(StringInfo str, const ColumnDef *node)
WRITE_LOCATION_FIELD(location);
}
+static void
+_outColumnCompression(StringInfo str, const ColumnCompression *node)
+{
+ WRITE_NODE_TYPE("COLUMNCOMPRESSION");
+
+ WRITE_STRING_FIELD(methodName);
+ WRITE_NODE_FIELD(options);
+}
+
static void
_outTypeName(StringInfo str, const TypeName *node)
{
@@ -4106,6 +4116,9 @@ outNode(StringInfo str, const void *obj)
case T_ColumnDef:
_outColumnDef(str, obj);
break;
+ case T_ColumnCompression:
+ _outColumnCompression(str, obj);
+ break;
case T_TypeName:
_outTypeName(str, obj);
break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c301ca465d..122d3de4d6 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -397,6 +397,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
transform_element_list transform_type_list
TriggerTransitions TriggerReferencing
publication_name_list
+ optCompressionParameters
vacuum_relation_list opt_vacuum_relation_list
%type <list> group_by_list
@@ -582,6 +583,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <list> hash_partbound partbound_datum_list range_datum_list
%type <defelt> hash_partbound_elem
+%type <node> columnCompression optColumnCompression compressedClause
+
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
* They must be listed first so that their numeric codes do not depend on
@@ -614,9 +617,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
CLUSTER COALESCE COLLATE COLLATION COLUMN COLUMNS COMMENT COMMENTS COMMIT
- COMMITTED CONCURRENTLY CONFIGURATION CONFLICT CONNECTION CONSTRAINT
- CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE
- CROSS CSV CUBE CURRENT_P
+ COMMITTED COMPRESSED COMPRESSION CONCURRENTLY CONFIGURATION CONFLICT
+ CONNECTION CONSTRAINT CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY
+ COST CREATE CROSS CSV CUBE CURRENT_P
CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA
CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
@@ -2168,6 +2171,15 @@ alter_table_cmd:
n->missing_ok = true;
$$ = (Node *)n;
}
+ /* ALTER TABLE <name> ALTER [COLUMN] <colname> SET (NOT COMPRESSED | COMPRESSED <cm> [WITH (<options>)]) */
+ | ALTER opt_column ColId SET columnCompression
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_AlterColumnCompression;
+ n->name = $3;
+ n->def = $5;
+ $$ = (Node *)n;
+ }
/* ALTER TABLE <name> DROP [COLUMN] IF EXISTS <colname> [RESTRICT|CASCADE] */
| DROP opt_column IF_P EXISTS ColId opt_drop_behavior
{
@@ -3332,11 +3344,12 @@ TypedTableElement:
| TableConstraint { $$ = $1; }
;
-columnDef: ColId Typename create_generic_options ColQualList
+columnDef: ColId Typename optColumnCompression create_generic_options ColQualList
{
ColumnDef *n = makeNode(ColumnDef);
n->colname = $1;
n->typeName = $2;
+ n->compression = (ColumnCompression *) $3;
n->inhcount = 0;
n->is_local = true;
n->is_not_null = false;
@@ -3346,8 +3359,8 @@ columnDef: ColId Typename create_generic_options ColQualList
n->raw_default = NULL;
n->cooked_default = NULL;
n->collOid = InvalidOid;
- n->fdwoptions = $3;
- SplitColQualList($4, &n->constraints, &n->collClause,
+ n->fdwoptions = $4;
+ SplitColQualList($5, &n->constraints, &n->collClause,
yyscanner);
n->location = @1;
$$ = (Node *)n;
@@ -3394,6 +3407,37 @@ columnOptions: ColId ColQualList
}
;
+compressedClause:
+ COMPRESSED name optCompressionParameters
+ {
+ ColumnCompression *n = makeNode(ColumnCompression);
+ n->methodName = $2;
+ n->options = (List *) $3;
+ $$ = (Node *) n;
+ }
+ ;
+
+columnCompression:
+ compressedClause |
+ NOT COMPRESSED
+ {
+ ColumnCompression *n = makeNode(ColumnCompression);
+ n->methodName = NULL;
+ n->options = NIL;
+ $$ = (Node *) n;
+ }
+ ;
+
+optColumnCompression:
+ compressedClause /* FIXME shift/reduce conflict on NOT COMPRESSED/NOT NULL */
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
+optCompressionParameters:
+ WITH '(' generic_option_list ')' { $$ = $3; }
+ | /*EMPTY*/ { $$ = NIL; }
+ ;
+
ColQualList:
ColQualList ColConstraint { $$ = lappend($1, $2); }
| /*EMPTY*/ { $$ = NIL; }
@@ -5754,6 +5798,15 @@ DefineStmt:
n->if_not_exists = true;
$$ = (Node *)n;
}
+ | CREATE COMPRESSION METHOD any_name HANDLER handler_name
+ {
+ DefineStmt *n = makeNode(DefineStmt);
+ n->kind = OBJECT_COMPRESSION_METHOD;
+ n->args = NIL;
+ n->defnames = $4;
+ n->definition = list_make1(makeDefElem("handler", (Node *) $6, @6));
+ $$ = (Node *) n;
+ }
;
definition: '(' def_list ')' { $$ = $2; }
@@ -6262,6 +6315,7 @@ drop_type_any_name:
/* object types taking name_list */
drop_type_name:
ACCESS METHOD { $$ = OBJECT_ACCESS_METHOD; }
+ | COMPRESSION METHOD { $$ = OBJECT_COMPRESSION_METHOD; }
| EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; }
| EXTENSION { $$ = OBJECT_EXTENSION; }
| FOREIGN DATA_P WRAPPER { $$ = OBJECT_FDW; }
@@ -6325,7 +6379,7 @@ opt_restart_seqs:
* The COMMENT ON statement can take different forms based upon the type of
* the object associated with the comment. The form of the statement is:
*
- * COMMENT ON [ [ ACCESS METHOD | CONVERSION | COLLATION |
+ * COMMENT ON [ [ ACCESS METHOD | COMPRESSION METHOD | CONVERSION | COLLATION |
* DATABASE | DOMAIN |
* EXTENSION | EVENT TRIGGER | FOREIGN DATA WRAPPER |
* FOREIGN TABLE | INDEX | [PROCEDURAL] LANGUAGE |
@@ -6515,6 +6569,7 @@ comment_type_any_name:
/* object types taking name */
comment_type_name:
ACCESS METHOD { $$ = OBJECT_ACCESS_METHOD; }
+ | COMPRESSION METHOD { $$ = OBJECT_COMPRESSION_METHOD; }
| DATABASE { $$ = OBJECT_DATABASE; }
| EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; }
| EXTENSION { $$ = OBJECT_EXTENSION; }
@@ -14704,6 +14759,8 @@ unreserved_keyword:
| COMMENTS
| COMMIT
| COMMITTED
+ | COMPRESSED
+ | COMPRESSION
| CONFIGURATION
| CONFLICT
| CONNECTION
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 8461da490a..39f21d4da7 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -27,6 +27,7 @@
#include "postgres.h"
#include "access/amapi.h"
+#include "access/compression.h"
#include "access/htup_details.h"
#include "access/reloptions.h"
#include "catalog/dependency.h"
@@ -494,6 +495,39 @@ generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column,
*sname_p = sname;
}
+/*
+ * transformColumnCompression
+ *
+ * Build ALTER TABLE .. SET COMPRESSED command if a compression method was
+ * specified for the column
+ */
+void
+transformColumnCompression(ColumnDef *column, RangeVar *relation,
+ AlterTableStmt **alterStmt)
+{
+ if (column->compression)
+ {
+ AlterTableCmd *cmd;
+
+ cmd = makeNode(AlterTableCmd);
+ cmd->subtype = AT_AlterColumnCompression;
+ cmd->name = column->colname;
+ cmd->def = (Node *) column->compression;
+ cmd->behavior = DROP_RESTRICT;
+ cmd->missing_ok = false;
+
+ if (!*alterStmt)
+ {
+ *alterStmt = makeNode(AlterTableStmt);
+ (*alterStmt)->relation = relation;
+ (*alterStmt)->relkind = OBJECT_TABLE;
+ (*alterStmt)->cmds = NIL;
+ }
+
+ (*alterStmt)->cmds = lappend((*alterStmt)->cmds, cmd);
+ }
+}
+
/*
* transformColumnDefinition -
* transform a single ColumnDef within CREATE TABLE
@@ -794,6 +828,16 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
cxt->alist = lappend(cxt->alist, stmt);
}
+
+ if (cxt->isalter)
+ {
+ AlterTableStmt *stmt = NULL;
+
+ transformColumnCompression(column, cxt->relation, &stmt);
+
+ if (stmt)
+ cxt->alist = lappend(cxt->alist, stmt);
+ }
}
/*
@@ -1003,6 +1047,10 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
def->collOid = attribute->attcollation;
def->constraints = NIL;
def->location = -1;
+ if (attribute->attcompression)
+ def->compression = GetColumnCompressionForAttribute(attribute);
+ else
+ def->compression = NULL;
/*
* Add to column list
diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c
index fa95bab58e..28680be49c 100644
--- a/src/backend/replication/logical/reorderbuffer.c
+++ b/src/backend/replication/logical/reorderbuffer.c
@@ -2832,7 +2832,7 @@ ReorderBufferToastReplace(ReorderBuffer *rb, ReorderBufferTXN *txn,
VARSIZE(chunk) - VARHDRSZ);
data_done += VARSIZE(chunk) - VARHDRSZ;
}
- Assert(data_done == toast_pointer.va_extsize);
+ Assert(data_done == VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer));
/* make sure its marked as compressed or not */
if (VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer))
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 82a707af7b..8c8c51dada 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1009,6 +1009,7 @@ ProcessUtilitySlow(ParseState *pstate,
RELKIND_RELATION,
InvalidOid, NULL,
queryString);
+
EventTriggerCollectSimpleCommand(address,
secondaryObject,
stmt);
@@ -1283,6 +1284,11 @@ ProcessUtilitySlow(ParseState *pstate,
stmt->definition,
stmt->if_not_exists);
break;
+ case OBJECT_COMPRESSION_METHOD:
+ Assert(stmt->args == NIL);
+ address = DefineCompressionMethod(stmt->defnames,
+ stmt->definition);
+ break;
default:
elog(ERROR, "unrecognized define stmt type: %d",
(int) stmt->kind);
@@ -2309,6 +2315,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_STATISTIC_EXT:
tag = "DROP STATISTICS";
break;
+ case OBJECT_COMPRESSION_METHOD:
+ tag = "DROP COMPRESSION METHOD";
+ break;
default:
tag = "???";
}
@@ -2412,6 +2421,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_ACCESS_METHOD:
tag = "CREATE ACCESS METHOD";
break;
+ case OBJECT_COMPRESSION_METHOD:
+ tag = "CREATE COMPRESSION METHOD";
+ break;
default:
tag = "???";
}
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index be793539a3..a5cfbe3d4c 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -418,3 +418,4 @@ PSEUDOTYPE_DUMMY_IO_FUNCS(internal);
PSEUDOTYPE_DUMMY_IO_FUNCS(opaque);
PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement);
PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray);
+PSEUDOTYPE_DUMMY_IO_FUNCS(compression_handler);
diff --git a/src/backend/utils/adt/tsvector.c b/src/backend/utils/adt/tsvector.c
index b0a9217d1e..fcff9d3fd6 100644
--- a/src/backend/utils/adt/tsvector.c
+++ b/src/backend/utils/adt/tsvector.c
@@ -14,11 +14,14 @@
#include "postgres.h"
+#include "access/compression.h"
+#include "catalog/pg_type.h"
#include "libpq/pqformat.h"
#include "tsearch/ts_locale.h"
#include "tsearch/ts_utils.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
+#include "common/pg_lzcompress.h"
typedef struct
{
@@ -548,3 +551,85 @@ tsvectorrecv(PG_FUNCTION_ARGS)
PG_RETURN_TSVECTOR(vec);
}
+
+/*
+ * Compress tsvector using LZ compression.
+ * Instead of trying to compress whole tsvector we compress only text part
+ * here. This approach gives more compressibility for tsvectors.
+ */
+static struct varlena *
+tsvector_compress(CompressionMethodOptions *cmoptions, const struct varlena *data)
+{
+ char *tmp;
+ int32 valsize = VARSIZE_ANY_EXHDR(data);
+ int32 len = valsize + VARHDRSZ_CUSTOM_COMPRESSED,
+ lenc;
+
+ char *arr = VARDATA(data),
+ *str = STRPTR((TSVector) data);
+ int32 arrsize = str - arr;
+
+ Assert(!VARATT_IS_COMPRESSED(data));
+ tmp = palloc0(len);
+
+ /* we try to compress string part of tsvector first */
+ lenc = pglz_compress(str,
+ valsize - arrsize,
+ tmp + VARHDRSZ_CUSTOM_COMPRESSED + arrsize,
+ PGLZ_strategy_default);
+
+ if (lenc >= 0)
+ {
+ /* tsvector is compressible, copy size and entries to its beginning */
+ memcpy(tmp + VARHDRSZ_CUSTOM_COMPRESSED, arr, arrsize);
+ SET_VARSIZE_COMPRESSED(tmp, arrsize + lenc + VARHDRSZ_CUSTOM_COMPRESSED);
+ return (struct varlena *) tmp;
+ }
+
+ pfree(tmp);
+ return NULL;
+}
+
+static struct varlena *
+tsvector_decompress(CompressionMethodOptions *cmoptions, const struct varlena *data)
+{
+ char *tmp,
+ *raw_data = (char *) data + VARHDRSZ_CUSTOM_COMPRESSED;
+ int32 count,
+ arrsize,
+ len = VARRAWSIZE_4B_C(data) + VARHDRSZ;
+
+ Assert(VARATT_IS_CUSTOM_COMPRESSED(data));
+ tmp = palloc0(len);
+ SET_VARSIZE(tmp, len);
+ count = *((uint32 *) raw_data);
+ arrsize = sizeof(uint32) + count * sizeof(WordEntry);
+ memcpy(VARDATA(tmp), raw_data, arrsize);
+
+ if (pglz_decompress(raw_data + arrsize,
+ VARSIZE(data) - VARHDRSZ_CUSTOM_COMPRESSED - arrsize,
+ VARDATA(tmp) + arrsize,
+ VARRAWSIZE_4B_C(data) - arrsize) < 0)
+ elog(ERROR, "compressed tsvector is corrupted");
+
+ return (struct varlena *) tmp;
+}
+
+Datum
+tsvector_compression_handler(PG_FUNCTION_ARGS)
+{
+ CompressionMethodOpArgs *opargs = (CompressionMethodOpArgs *)
+ PG_GETARG_POINTER(0);
+ CompressionMethodRoutine *cmr = makeNode(CompressionMethodRoutine);
+ Oid typeid = opargs->typeid;
+
+ if (OidIsValid(typeid) && typeid != TSVECTOROID)
+ elog(ERROR, "unexpected type %d for tsvector compression handler", typeid);
+
+ cmr->configure = NULL;
+ cmr->drop = NULL;
+ cmr->compress = tsvector_compress;
+ cmr->decompress = tsvector_decompress;
+
+ PG_RETURN_POINTER(cmr);
+}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 1908420d82..9ebfb909ca 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -30,6 +30,7 @@
#include <fcntl.h>
#include <unistd.h>
+#include "access/compression.h"
#include "access/hash.h"
#include "access/htup_details.h"
#include "access/multixact.h"
@@ -77,6 +78,7 @@
#include "storage/smgr.h"
#include "utils/array.h"
#include "utils/builtins.h"
+#include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
@@ -561,6 +563,11 @@ RelationBuildTupleDesc(Relation relation)
attrdef[ndef].adbin = NULL;
ndef++;
}
+
+ /* mark tupledesc as it contains attributes with custom compression */
+ if (attp->attcompression)
+ relation->rd_att->tdflags |= TD_ATTR_CUSTOM_COMPRESSED;
+
need--;
if (need == 0)
break;
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 888edbb325..c689f60e47 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -31,6 +31,8 @@
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_compression.h"
+#include "catalog/pg_compression_opt.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
@@ -309,6 +311,39 @@ static const struct cachedesc cacheinfo[] = {
},
8
},
+ {CompressionMethodRelationId, /* COMPRESSIONMETHODOID */
+ CompressionMethodOidIndexId,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 8
+ },
+ {CompressionMethodRelationId, /* COMPRESSIONMETHODNAME */
+ CompressionMethodNameIndexId,
+ 1,
+ {
+ Anum_pg_compression_cmname,
+ 0,
+ 0,
+ 0
+ },
+ 8
+ },
+ {CompressionOptRelationId, /* COMPRESSIONOPTIONSOID */
+ CompressionOptionsOidIndexId,
+ 1,
+ {
+ Anum_pg_compression_opt_cmoptoid,
+ 0,
+ 0,
+ 0
+ },
+ 8,
+ },
{ConversionRelationId, /* CONDEFAULT */
ConversionDefaultIndexId,
4,
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 4b47951de1..7ba6d31251 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -54,6 +54,7 @@ static DumpableObject **oprinfoindex;
static DumpableObject **collinfoindex;
static DumpableObject **nspinfoindex;
static DumpableObject **extinfoindex;
+static DumpableObject **cminfoindex;
static int numTables;
static int numTypes;
static int numFuncs;
@@ -61,6 +62,7 @@ static int numOperators;
static int numCollations;
static int numNamespaces;
static int numExtensions;
+static int numCompressionMethods;
/* This is an array of object identities, not actual DumpableObjects */
static ExtensionMemberId *extmembers;
@@ -93,6 +95,8 @@ getSchemaData(Archive *fout, int *numTablesPtr)
NamespaceInfo *nspinfo;
ExtensionInfo *extinfo;
InhInfo *inhinfo;
+ CompressionMethodInfo *cminfo;
+
int numAggregates;
int numInherits;
int numRules;
@@ -289,6 +293,11 @@ getSchemaData(Archive *fout, int *numTablesPtr)
write_msg(NULL, "reading subscriptions\n");
getSubscriptions(fout);
+ if (g_verbose)
+ write_msg(NULL, "reading compression methods\n");
+ cminfo = getCompressionMethods(fout, &numCompressionMethods);
+ cminfoindex = buildIndexArray(cminfo, numCompressionMethods, sizeof(CompressionMethodInfo));
+
*numTablesPtr = numTables;
return tblinfo;
}
@@ -827,6 +836,17 @@ findExtensionByOid(Oid oid)
return (ExtensionInfo *) findObjectByOid(oid, extinfoindex, numExtensions);
}
+/*
+ * findCompressionMethodByOid
+ * finds the entry (in cminfo) of the compression method with the given oid
+ * returns NULL if not found
+ */
+CompressionMethodInfo *
+findCompressionMethodByOid(Oid oid)
+{
+ return (CompressionMethodInfo *) findObjectByOid(oid, cminfoindex,
+ numCompressionMethods);
+}
/*
* setExtensionMembership
diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index ce3100f09d..5c1aaad48e 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -77,6 +77,7 @@ typedef struct _restoreOptions
int no_publications; /* Skip publication entries */
int no_security_labels; /* Skip security label entries */
int no_subscriptions; /* Skip subscription entries */
+ int no_compression_methods; /* Skip compression methods */
int strict_names;
const char *filename;
@@ -121,6 +122,8 @@ typedef struct _restoreOptions
bool *idWanted; /* array showing which dump IDs to emit */
int enable_row_security;
int sequence_data; /* dump sequence data even in schema-only mode */
+ int compression_options_data; /* dump compression options even
+ * in schema-only mode */
int binary_upgrade;
} RestoreOptions;
@@ -149,6 +152,7 @@ typedef struct _dumpOptions
int no_security_labels;
int no_publications;
int no_subscriptions;
+ int no_compression_methods;
int no_synchronized_snapshots;
int no_unlogged_table_data;
int serializable_deferrable;
@@ -170,6 +174,8 @@ typedef struct _dumpOptions
char *outputSuperuser;
int sequence_data; /* dump sequence data even in schema-only mode */
+ int compression_options_data; /* dump compression options even
+ * in schema-only mode */
} DumpOptions;
/*
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index ec2fa8b9b9..77eb55eb69 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -179,6 +179,7 @@ dumpOptionsFromRestoreOptions(RestoreOptions *ropt)
dopt->include_everything = ropt->include_everything;
dopt->enable_row_security = ropt->enable_row_security;
dopt->sequence_data = ropt->sequence_data;
+ dopt->compression_options_data = ropt->compression_options_data;
return dopt;
}
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index d8fb356130..6df688adb8 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -361,6 +361,7 @@ main(int argc, char **argv)
{"no-synchronized-snapshots", no_argument, &dopt.no_synchronized_snapshots, 1},
{"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
{"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
+ {"no-compression-methods", no_argument, &dopt.no_compression_methods, 1},
{"no-sync", no_argument, NULL, 7},
{NULL, 0, NULL, 0}
@@ -581,6 +582,9 @@ main(int argc, char **argv)
if (dopt.binary_upgrade)
dopt.sequence_data = 1;
+ if (dopt.binary_upgrade)
+ dopt.compression_options_data = 1;
+
if (dopt.dataOnly && dopt.schemaOnly)
{
write_msg(NULL, "options -s/--schema-only and -a/--data-only cannot be used together\n");
@@ -785,6 +789,9 @@ main(int argc, char **argv)
if (dopt.schemaOnly && dopt.sequence_data)
getTableData(&dopt, tblinfo, numTables, dopt.oids, RELKIND_SEQUENCE);
+ if (dopt.schemaOnly && dopt.compression_options_data)
+ getCompressionOptions(fout);
+
/*
* In binary-upgrade mode, we do not have to worry about the actual blob
* data or the associated metadata that resides in the pg_largeobject and
@@ -3957,6 +3964,205 @@ dumpSubscription(Archive *fout, SubscriptionInfo *subinfo)
destroyPQExpBuffer(query);
}
+/*
+ * getCompressionMethods
+ * get information about compression methods
+ */
+CompressionMethodInfo *
+getCompressionMethods(Archive *fout, int *numMethods)
+{
+ DumpOptions *dopt = fout->dopt;
+ PQExpBuffer query;
+ PGresult *res;
+ CompressionMethodInfo *cminfo;
+ int i_tableoid;
+ int i_oid;
+ int i_handler;
+ int i_name;
+ int i,
+ ntups;
+
+ if (dopt->no_compression_methods || fout->remoteVersion < 110000)
+ return NULL;
+
+ query = createPQExpBuffer();
+ resetPQExpBuffer(query);
+
+ /* Get the compression methods in current database. */
+ appendPQExpBuffer(query, "SELECT c.tableoid, c.oid, c.cmname, "
+ "c.cmhandler::pg_catalog.regproc as cmhandler "
+ "FROM pg_catalog.pg_compression c");
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_name = PQfnumber(res, "cmname");
+ i_handler = PQfnumber(res, "cmhandler");
+
+ cminfo = pg_malloc(ntups * sizeof(CompressionMethodInfo));
+
+ for (i = 0; i < ntups; i++)
+ {
+ cminfo[i].dobj.objType = DO_COMPRESSION_METHOD;
+ cminfo[i].dobj.catId.tableoid =
+ atooid(PQgetvalue(res, i, i_tableoid));
+ cminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&cminfo[i].dobj);
+ cminfo[i].cmhandler = pg_strdup(PQgetvalue(res, i, i_handler));
+ cminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_name));
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(cminfo[i].dobj), fout);
+ }
+ if (numMethods)
+ *numMethods = ntups;
+
+ PQclear(res);
+ destroyPQExpBuffer(query);
+
+ return cminfo;
+}
+
+/*
+ * dumpCompressionMethod
+ * dump the definition of the given compression method
+ */
+static void
+dumpCompressionMethod(Archive *fout, CompressionMethodInfo * cminfo)
+{
+ PQExpBuffer query;
+
+ if (!(cminfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
+ return;
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema(fout, "pg_catalog");
+
+ query = createPQExpBuffer();
+ appendPQExpBuffer(query, "CREATE COMPRESSION METHOD %s HANDLER",
+ fmtId(cminfo->dobj.name));
+ appendPQExpBuffer(query, " %s;\n", cminfo->cmhandler);
+
+ ArchiveEntry(fout,
+ cminfo->dobj.catId,
+ cminfo->dobj.dumpId,
+ cminfo->dobj.name,
+ NULL,
+ NULL,
+ "", false,
+ "COMPRESSION METHOD", SECTION_PRE_DATA,
+ query->data, "", NULL,
+ NULL, 0,
+ NULL, NULL);
+
+ destroyPQExpBuffer(query);
+}
+
+/*
+ * getCompressionOptions
+ * get information about compression options.
+ * this information should be migrated at binary upgrade
+ */
+void
+getCompressionOptions(Archive *fout)
+{
+ PQExpBuffer query;
+ PGresult *res;
+ CompressionOptionsInfo *cminfo;
+ int i_tableoid;
+ int i_oid;
+ int i_handler;
+ int i_name;
+ int i_options;
+ int i,
+ ntups;
+
+ if (fout->remoteVersion < 110000)
+ return;
+
+ query = createPQExpBuffer();
+ resetPQExpBuffer(query);
+
+ appendPQExpBuffer(query,
+ "SELECT c.tableoid, c.cmoptoid, c.cmname,"
+ " c.cmhandler::pg_catalog.regproc as cmhandler, c.cmoptions "
+ "FROM pg_catalog.pg_compression_opt c");
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "cmoptoid");
+ i_name = PQfnumber(res, "cmname");
+ i_handler = PQfnumber(res, "cmhandler");
+ i_options = PQfnumber(res, "cmoptions");
+
+ cminfo = pg_malloc(ntups * sizeof(CompressionOptionsInfo));
+
+ for (i = 0; i < ntups; i++)
+ {
+ cminfo[i].dobj.objType = DO_COMPRESSION_OPTIONS;
+ cminfo[i].dobj.catId.tableoid =
+ atooid(PQgetvalue(res, i, i_tableoid));
+ cminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&cminfo[i].dobj);
+ cminfo[i].cmhandler = pg_strdup(PQgetvalue(res, i, i_handler));
+ cminfo[i].cmoptions = pg_strdup(PQgetvalue(res, i, i_options));
+ cminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_name));
+ cminfo[i].dobj.dump = DUMP_COMPONENT_DEFINITION;
+ }
+
+ PQclear(res);
+ destroyPQExpBuffer(query);
+}
+
+/*
+ * dumpCompressionOptions
+ * dump the given compression options
+ */
+static void
+dumpCompressionOptions(Archive *fout, CompressionOptionsInfo * cminfo)
+{
+ PQExpBuffer query;
+
+ if (!(cminfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
+ return;
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema(fout, "pg_catalog");
+
+ query = createPQExpBuffer();
+ if (strlen(cminfo->cmoptions) > 0)
+ appendPQExpBuffer(query, "INSERT INTO pg_compression_opt (cmoptoid, cmname, cmhandler,"
+ " cmoptions) VALUES (%d, '%s', CAST('%s' AS REGPROC), '%s');\n",
+ cminfo->dobj.catId.oid,
+ cminfo->dobj.name,
+ cminfo->cmhandler,
+ cminfo->cmoptions);
+ else
+ appendPQExpBuffer(query, "INSERT INTO pg_compression_opt (cmoptoid, cmname, cmhandler,"
+ " cmoptions) VALUES (%d, '%s', CAST('%s' AS REGPROC), NULL);\n",
+ cminfo->dobj.catId.oid,
+ cminfo->dobj.name,
+ cminfo->cmhandler);
+
+ ArchiveEntry(fout,
+ cminfo->dobj.catId,
+ cminfo->dobj.dumpId,
+ cminfo->dobj.name,
+ NULL,
+ NULL,
+ "", false,
+ "COMPRESSION OPTIONS", SECTION_PRE_DATA,
+ query->data, "", NULL,
+ NULL, 0,
+ NULL, NULL);
+
+ destroyPQExpBuffer(query);
+}
+
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
@@ -4484,7 +4690,47 @@ getTypes(Archive *fout, int *numTypes)
/* Make sure we are in proper schema */
selectSourceSchema(fout, "pg_catalog");
- if (fout->remoteVersion >= 90600)
+ if (fout->remoteVersion >= 110000)
+ {
+ PQExpBuffer acl_subquery = createPQExpBuffer();
+ PQExpBuffer racl_subquery = createPQExpBuffer();
+ PQExpBuffer initacl_subquery = createPQExpBuffer();
+ PQExpBuffer initracl_subquery = createPQExpBuffer();
+
+ buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
+ initracl_subquery, "t.typacl", "t.typowner", "'T'",
+ dopt->binary_upgrade);
+
+ appendPQExpBuffer(query, "SELECT t.tableoid, t.oid, t.typname, "
+ "t.typnamespace, "
+ "%s AS typacl, "
+ "%s AS rtypacl, "
+ "%s AS inittypacl, "
+ "%s AS initrtypacl, "
+ "(%s t.typowner) AS rolname, "
+ "t.typelem, t.typrelid, "
+ "CASE WHEN t.typrelid = 0 THEN ' '::\"char\" "
+ "ELSE (SELECT relkind FROM pg_class WHERE oid = t.typrelid) END AS typrelkind, "
+ "t.typtype, t.typisdefined, "
+ "t.typname[0] = '_' AND t.typelem != 0 AND "
+ "(SELECT typarray FROM pg_type te WHERE oid = t.typelem) = t.oid AS isarray "
+ "FROM pg_type t "
+ "LEFT JOIN pg_init_privs pip ON "
+ "(t.oid = pip.objoid "
+ "AND pip.classoid = 'pg_type'::regclass "
+ "AND pip.objsubid = 0) ",
+ acl_subquery->data,
+ racl_subquery->data,
+ initacl_subquery->data,
+ initracl_subquery->data,
+ username_subquery);
+
+ destroyPQExpBuffer(acl_subquery);
+ destroyPQExpBuffer(racl_subquery);
+ destroyPQExpBuffer(initacl_subquery);
+ destroyPQExpBuffer(initracl_subquery);
+ }
+ else if (fout->remoteVersion >= 90600)
{
PQExpBuffer acl_subquery = createPQExpBuffer();
PQExpBuffer racl_subquery = createPQExpBuffer();
@@ -7895,6 +8141,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
int i_attoptions;
int i_attcollation;
int i_attfdwoptions;
+ int i_attcmoptions;
+ int i_attcmname;
PGresult *res;
int ntups;
bool hasdefaults;
@@ -7930,7 +8178,46 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
resetPQExpBuffer(q);
- if (fout->remoteVersion >= 100000)
+ if (fout->remoteVersion >= 110000)
+ {
+ /*
+ * attcompression is new in version 11
+ */
+ appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, "
+ "a.attstattarget, a.attstorage, t.typstorage, "
+ "a.attnotnull, a.atthasdef, a.attisdropped, "
+ "a.attlen, a.attalign, a.attislocal, "
+ "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
+ "array_to_string(a.attoptions, ', ') AS attoptions, "
+ "CASE WHEN a.attcollation <> t.typcollation "
+ "THEN a.attcollation ELSE 0 END AS attcollation, "
+ "a.attidentity, "
+ /* fdw options */
+ "pg_catalog.array_to_string(ARRAY("
+ "SELECT pg_catalog.quote_ident(option_name) || "
+ "' ' || pg_catalog.quote_literal(option_value) "
+ "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
+ "ORDER BY option_name"
+ "), E',\n ') AS attfdwoptions, "
+ /* compression options */
+ "pg_catalog.array_to_string(ARRAY("
+ "SELECT pg_catalog.quote_ident(option_name) || "
+ "' ' || pg_catalog.quote_literal(option_value) "
+ "FROM pg_catalog.pg_options_to_table(c.cmoptions) "
+ "ORDER BY option_name"
+ "), E',\n ') AS attcmoptions, "
+ "c.cmname AS attcmname "
+ /* FROM */
+ "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
+ "ON a.atttypid = t.oid "
+ "LEFT JOIN pg_catalog.pg_compression_opt c "
+ "ON a.attcompression = c.cmoptoid "
+ "WHERE a.attrelid = '%u'::pg_catalog.oid "
+ "AND a.attnum > 0::pg_catalog.int2 "
+ "ORDER BY a.attnum",
+ tbinfo->dobj.catId.oid);
+ }
+ else if (fout->remoteVersion >= 100000)
{
/*
* attidentity is new in version 10.
@@ -7949,9 +8236,13 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"' ' || pg_catalog.quote_literal(option_value) "
"FROM pg_catalog.pg_options_to_table(attfdwoptions) "
"ORDER BY option_name"
- "), E',\n ') AS attfdwoptions "
+ "), E',\n ') AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
+ "LEFT JOIN pg_catalog.pg_compression_opt c "
+ "ON a.attcompression = c.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
"AND a.attnum > 0::pg_catalog.int2 "
"ORDER BY a.attnum",
@@ -7975,7 +8266,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"' ' || pg_catalog.quote_literal(option_value) "
"FROM pg_catalog.pg_options_to_table(attfdwoptions) "
"ORDER BY option_name"
- "), E',\n ') AS attfdwoptions "
+ "), E',\n ') AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -7999,7 +8292,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"array_to_string(a.attoptions, ', ') AS attoptions, "
"CASE WHEN a.attcollation <> t.typcollation "
"THEN a.attcollation ELSE 0 END AS attcollation, "
- "NULL AS attfdwoptions "
+ "NULL AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -8017,7 +8312,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
"array_to_string(a.attoptions, ', ') AS attoptions, "
"0 AS attcollation, "
- "NULL AS attfdwoptions "
+ "NULL AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -8034,7 +8331,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"a.attlen, a.attalign, a.attislocal, "
"pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
"'' AS attoptions, 0 AS attcollation, "
- "NULL AS attfdwoptions "
+ "NULL AS attfdwoptions, "
+ "NULL AS attcmoptions, "
+ "NULL AS attcmname "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -8064,6 +8363,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
i_attoptions = PQfnumber(res, "attoptions");
i_attcollation = PQfnumber(res, "attcollation");
i_attfdwoptions = PQfnumber(res, "attfdwoptions");
+ i_attcmname = PQfnumber(res, "attcmname");
+ i_attcmoptions = PQfnumber(res, "attcmoptions");
tbinfo->numatts = ntups;
tbinfo->attnames = (char **) pg_malloc(ntups * sizeof(char *));
@@ -8080,6 +8381,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
tbinfo->attoptions = (char **) pg_malloc(ntups * sizeof(char *));
tbinfo->attcollation = (Oid *) pg_malloc(ntups * sizeof(Oid));
tbinfo->attfdwoptions = (char **) pg_malloc(ntups * sizeof(char *));
+ tbinfo->attcmoptions = (char **) pg_malloc(ntups * sizeof(char *));
+ tbinfo->attcmnames = (char **) pg_malloc(ntups * sizeof(char *));
tbinfo->notnull = (bool *) pg_malloc(ntups * sizeof(bool));
tbinfo->inhNotNull = (bool *) pg_malloc(ntups * sizeof(bool));
tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(ntups * sizeof(AttrDefInfo *));
@@ -8107,6 +8410,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, j, i_attoptions));
tbinfo->attcollation[j] = atooid(PQgetvalue(res, j, i_attcollation));
tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, j, i_attfdwoptions));
+ tbinfo->attcmoptions[j] = pg_strdup(PQgetvalue(res, j, i_attcmoptions));
+ tbinfo->attcmnames[j] = pg_strdup(PQgetvalue(res, j, i_attcmname));
tbinfo->attrdefs[j] = NULL; /* fix below */
if (PQgetvalue(res, j, i_atthasdef)[0] == 't')
hasdefaults = true;
@@ -9596,6 +9901,12 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
case DO_SUBSCRIPTION:
dumpSubscription(fout, (SubscriptionInfo *) dobj);
break;
+ case DO_COMPRESSION_METHOD:
+ dumpCompressionMethod(fout, (CompressionMethodInfo *) dobj);
+ break;
+ case DO_COMPRESSION_OPTIONS:
+ dumpCompressionOptions(fout, (CompressionOptionsInfo *) dobj);
+ break;
case DO_PRE_DATA_BOUNDARY:
case DO_POST_DATA_BOUNDARY:
/* never dumped, nothing to do */
@@ -15513,6 +15824,15 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
}
}
+ if (tbinfo->attcmnames[j] && strlen(tbinfo->attcmnames[j]))
+ {
+ appendPQExpBuffer(q, " COMPRESSED %s",
+ tbinfo->attcmnames[j]);
+ if (nonemptyReloptions(tbinfo->attcmoptions[j]))
+ appendPQExpBuffer(q, " WITH (%s)",
+ tbinfo->attcmoptions[j]);
+ }
+
if (has_default)
appendPQExpBuffer(q, " DEFAULT %s",
tbinfo->attrdefs[j]->adef_expr);
@@ -17778,6 +18098,8 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
case DO_FOREIGN_SERVER:
case DO_TRANSFORM:
case DO_BLOB:
+ case DO_COMPRESSION_METHOD:
+ case DO_COMPRESSION_OPTIONS:
/* Pre-data objects: must come before the pre-data boundary */
addObjectDependency(preDataBound, dobj->dumpId);
break;
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index da884ffd09..88b18ba15c 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -83,7 +83,9 @@ typedef enum
DO_POLICY,
DO_PUBLICATION,
DO_PUBLICATION_REL,
- DO_SUBSCRIPTION
+ DO_SUBSCRIPTION,
+ DO_COMPRESSION_METHOD,
+ DO_COMPRESSION_OPTIONS
} DumpableObjectType;
/* component types of an object which can be selected for dumping */
@@ -315,6 +317,8 @@ typedef struct _tableInfo
char **attoptions; /* per-attribute options */
Oid *attcollation; /* per-attribute collation selection */
char **attfdwoptions; /* per-attribute fdw options */
+ char **attcmoptions; /* per-attribute compression options */
+ char **attcmnames; /* per-attribute compression method names */
bool *notnull; /* NOT NULL constraints on attributes */
bool *inhNotNull; /* true if NOT NULL is inherited */
struct _attrDefInfo **attrdefs; /* DEFAULT expressions */
@@ -611,6 +615,21 @@ typedef struct _SubscriptionInfo
char *subpublications;
} SubscriptionInfo;
+/* The CompressionMethodInfo struct is used to represent compression method */
+typedef struct _CompressionMethodInfo
+{
+ DumpableObject dobj;
+ char *cmhandler;
+} CompressionMethodInfo;
+
+/* The CompressionOptionsInfo struct is used to represent compression options */
+typedef struct _CompressionOptionsInfo
+{
+ DumpableObject dobj;
+ char *cmhandler;
+ char *cmoptions;
+} CompressionOptionsInfo;
+
/*
* We build an array of these with an entry for each object that is an
* extension member according to pg_depend.
@@ -654,6 +673,7 @@ extern OprInfo *findOprByOid(Oid oid);
extern CollInfo *findCollationByOid(Oid oid);
extern NamespaceInfo *findNamespaceByOid(Oid oid);
extern ExtensionInfo *findExtensionByOid(Oid oid);
+extern CompressionMethodInfo * findCompressionMethodByOid(Oid oid);
extern void setExtensionMembership(ExtensionMemberId *extmems, int nextmems);
extern ExtensionInfo *findOwningExtension(CatalogId catalogId);
@@ -711,5 +731,8 @@ extern void getPublications(Archive *fout);
extern void getPublicationTables(Archive *fout, TableInfo tblinfo[],
int numTables);
extern void getSubscriptions(Archive *fout);
+extern CompressionMethodInfo * getCompressionMethods(Archive *fout,
+ int *numMethods);
+void getCompressionOptions(Archive *fout);
#endif /* PG_DUMP_H */
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index 48b6dd594c..f5db0280e2 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -80,7 +80,9 @@ static const int dbObjectTypePriority[] =
34, /* DO_POLICY */
35, /* DO_PUBLICATION */
36, /* DO_PUBLICATION_REL */
- 37 /* DO_SUBSCRIPTION */
+ 37, /* DO_SUBSCRIPTION */
+ 17, /* DO_COMPRESSION_METHOD */
+ 17 /* DO_COMPRESSION_OPTIONS */
};
static DumpId preDataBoundId;
@@ -1436,6 +1438,16 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
"POST-DATA BOUNDARY (ID %d)",
obj->dumpId);
return;
+ case DO_COMPRESSION_METHOD:
+ snprintf(buf, bufsize,
+ "COMPRESSION METHOD %s (ID %d OID %u)",
+ obj->name, obj->dumpId, obj->catId.oid);
+ return;
+ case DO_COMPRESSION_OPTIONS:
+ snprintf(buf, bufsize,
+ "COMPRESSION OPTIONS %s (ID %d OID %u)",
+ obj->name, obj->dumpId, obj->catId.oid);
+ return;
}
/* shouldn't get here */
snprintf(buf, bufsize,
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 41c5ff89b7..6b72ccf9ea 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -77,6 +77,7 @@ static int use_setsessauth = 0;
static int no_publications = 0;
static int no_security_labels = 0;
static int no_subscriptions = 0;
+static int no_compression_methods = 0;
static int no_unlogged_table_data = 0;
static int no_role_passwords = 0;
static int server_version;
@@ -137,6 +138,7 @@ main(int argc, char *argv[])
{"no-role-passwords", no_argument, &no_role_passwords, 1},
{"no-security-labels", no_argument, &no_security_labels, 1},
{"no-subscriptions", no_argument, &no_subscriptions, 1},
+ {"no-compression-methods", no_argument, &no_compression_methods, 1},
{"no-sync", no_argument, NULL, 4},
{"no-unlogged-table-data", no_argument, &no_unlogged_table_data, 1},
@@ -405,6 +407,8 @@ main(int argc, char *argv[])
appendPQExpBufferStr(pgdumpopts, " --no-security-labels");
if (no_subscriptions)
appendPQExpBufferStr(pgdumpopts, " --no-subscriptions");
+ if (no_compression_methods)
+ appendPQExpBufferStr(pgdumpopts, " --no-compression-methods");
if (no_unlogged_table_data)
appendPQExpBufferStr(pgdumpopts, " --no-unlogged-table-data");
@@ -628,6 +632,7 @@ help(void)
printf(_(" --no-role-passwords do not dump passwords for roles\n"));
printf(_(" --no-security-labels do not dump security label assignments\n"));
printf(_(" --no-subscriptions do not dump subscriptions\n"));
+ printf(_(" --no-compression-methods do not dump compression methods\n"));
printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c
index 860a211a3c..810403ec9c 100644
--- a/src/bin/pg_dump/pg_restore.c
+++ b/src/bin/pg_dump/pg_restore.c
@@ -74,6 +74,7 @@ main(int argc, char **argv)
static int no_publications = 0;
static int no_security_labels = 0;
static int no_subscriptions = 0;
+ static int no_compression_methods = 0;
static int strict_names = 0;
struct option cmdopts[] = {
@@ -122,6 +123,7 @@ main(int argc, char **argv)
{"no-publications", no_argument, &no_publications, 1},
{"no-security-labels", no_argument, &no_security_labels, 1},
{"no-subscriptions", no_argument, &no_subscriptions, 1},
+ {"no-compression-methods", no_argument, &no_compression_methods, 1},
{NULL, 0, NULL, 0}
};
@@ -361,6 +363,7 @@ main(int argc, char **argv)
opts->no_publications = no_publications;
opts->no_security_labels = no_security_labels;
opts->no_subscriptions = no_subscriptions;
+ opts->no_compression_methods = no_compression_methods;
if (if_exists && !opts->dropSchema)
{
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 8cc4de3878..8b0dbfa45c 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -735,7 +735,10 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
success = listConversions(pattern, show_verbose, show_system);
break;
case 'C':
- success = listCasts(pattern, show_verbose);
+ if (cmd[2] == 'M')
+ success = describeCompressionMethods(pattern, show_verbose);
+ else
+ success = listCasts(pattern, show_verbose);
break;
case 'd':
if (strncmp(cmd, "ddp", 3) == 0)
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 804a84a0c9..4e077aff06 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -200,6 +200,69 @@ describeAccessMethods(const char *pattern, bool verbose)
return true;
}
+/*
+ * \dCM
+ * Takes an optional regexp to select particular compression methods
+ */
+bool
+describeCompressionMethods(const char *pattern, bool verbose)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+ static const bool translate_columns[] = {false, false, false};
+
+ if (pset.sversion < 100000)
+ {
+ char sverbuf[32];
+
+ psql_error("The server (version %s) does not support compression methods.\n",
+ formatPGVersionNumber(pset.sversion, false,
+ sverbuf, sizeof(sverbuf)));
+ return true;
+ }
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT cmname AS \"%s\"",
+ gettext_noop("Name"));
+
+ if (verbose)
+ {
+ appendPQExpBuffer(&buf,
+ ",\n cmhandler AS \"%s\",\n"
+ " pg_catalog.obj_description(oid, 'pg_compression') AS \"%s\"",
+ gettext_noop("Handler"),
+ gettext_noop("Description"));
+ }
+
+ appendPQExpBufferStr(&buf,
+ "\nFROM pg_catalog.pg_compression\n");
+
+ processSQLNamePattern(pset.db, &buf, pattern, false, false,
+ NULL, "cmname", NULL,
+ NULL);
+
+ appendPQExpBufferStr(&buf, "ORDER BY 1;");
+
+ res = PSQLexec(buf.data);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of compression methods");
+ myopt.translate_header = true;
+ myopt.translate_columns = translate_columns;
+ myopt.n_translate_columns = lengthof(translate_columns);
+
+ printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+ PQclear(res);
+ return true;
+}
+
/*
* \db
* Takes an optional regexp to select particular tablespaces
@@ -1716,6 +1779,22 @@ describeOneTableDetails(const char *schemaname,
if (verbose)
{
appendPQExpBufferStr(&buf, ",\n a.attstorage");
+
+ /* compresssion info */
+ if (pset.sversion >= 110000)
+ appendPQExpBufferStr(&buf, ",\n CASE WHEN attcompression = 0 THEN NULL ELSE "
+ " (SELECT c.cmname || "
+ " (CASE WHEN cmoptions IS NULL "
+ " THEN '' "
+ " ELSE '(' || array_to_string(ARRAY(SELECT quote_ident(option_name) || ' ' || quote_literal(option_value)"
+ " FROM pg_options_to_table(cmoptions)), ', ') || ')'"
+ " END) "
+ " FROM pg_catalog.pg_compression_opt c "
+ " WHERE c.cmoptoid = a.attcompression) "
+ " END AS attcmname");
+ else
+ appendPQExpBufferStr(&buf, "\n NULL AS attcmname");
+
appendPQExpBufferStr(&buf, ",\n CASE WHEN a.attstattarget=-1 THEN NULL ELSE a.attstattarget END AS attstattarget");
/*
@@ -1830,6 +1909,10 @@ describeOneTableDetails(const char *schemaname,
if (verbose)
{
headers[cols++] = gettext_noop("Storage");
+
+ if (tableinfo.relkind == RELKIND_RELATION)
+ headers[cols++] = gettext_noop("Compression");
+
if (tableinfo.relkind == RELKIND_RELATION ||
tableinfo.relkind == RELKIND_INDEX ||
tableinfo.relkind == RELKIND_MATVIEW ||
@@ -1925,6 +2008,11 @@ describeOneTableDetails(const char *schemaname,
"???")))),
false, false);
+ /* Column compression. */
+ if (tableinfo.relkind == RELKIND_RELATION)
+ printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1),
+ false, false);
+
/* Statistics target, if the relkind supports this feature */
if (tableinfo.relkind == RELKIND_RELATION ||
tableinfo.relkind == RELKIND_INDEX ||
@@ -1932,7 +2020,7 @@ describeOneTableDetails(const char *schemaname,
tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
{
- printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1),
+ printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2),
false, false);
}
@@ -1943,7 +2031,7 @@ describeOneTableDetails(const char *schemaname,
tableinfo.relkind == RELKIND_COMPOSITE_TYPE ||
tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
- printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2),
+ printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 3),
false, false);
}
}
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 14a5667f3e..0ab8518a36 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -18,6 +18,9 @@ extern bool describeAccessMethods(const char *pattern, bool verbose);
/* \db */
extern bool describeTablespaces(const char *pattern, bool verbose);
+/* \dCM */
+extern bool describeCompressionMethods(const char *pattern, bool verbose);
+
/* \df, \dfa, \dfn, \dft, \dfw, etc. */
extern bool describeFunctions(const char *functypes, const char *pattern, bool verbose, bool showSystem);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index a926c40b9b..25d2fbc125 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -227,6 +227,7 @@ slashUsage(unsigned short int pager)
fprintf(output, _(" \\db[+] [PATTERN] list tablespaces\n"));
fprintf(output, _(" \\dc[S+] [PATTERN] list conversions\n"));
fprintf(output, _(" \\dC[+] [PATTERN] list casts\n"));
+ fprintf(output, _(" \\dCM[+] [PATTERN] list compression methods\n"));
fprintf(output, _(" \\dd[S] [PATTERN] show object descriptions not displayed elsewhere\n"));
fprintf(output, _(" \\dD[S+] [PATTERN] list domains\n"));
fprintf(output, _(" \\ddp [PATTERN] list default privileges\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index b3e3799c13..a323ca80c6 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -889,6 +889,11 @@ static const SchemaQuery Query_for_list_of_statistics = {
" AND d.datname = pg_catalog.current_database() "\
" AND s.subdbid = d.oid"
+#define Query_for_list_of_compression_methods \
+" SELECT pg_catalog.quote_ident(cmname) "\
+" FROM pg_catalog.pg_compression "\
+" WHERE substring(pg_catalog.quote_ident(cmname),1,%d)='%s'"
+
/* the silly-looking length condition is just to eat up the current word */
#define Query_for_list_of_arguments \
"SELECT pg_catalog.oidvectortypes(proargtypes)||')' "\
@@ -1011,6 +1016,7 @@ static const pgsql_thing_t words_after_create[] = {
* CREATE CONSTRAINT TRIGGER is not supported here because it is designed
* to be used only by pg_dump.
*/
+ {"COMPRESSION METHOD", NULL, NULL},
{"CONFIGURATION", Query_for_list_of_ts_configurations, NULL, THING_NO_SHOW},
{"CONVERSION", "SELECT pg_catalog.quote_ident(conname) FROM pg_catalog.pg_conversion WHERE substring(pg_catalog.quote_ident(conname),1,%d)='%s'"},
{"DATABASE", Query_for_list_of_databases},
@@ -1424,8 +1430,8 @@ psql_completion(const char *text, int start, int end)
"\\a",
"\\connect", "\\conninfo", "\\C", "\\cd", "\\copy",
"\\copyright", "\\crosstabview",
- "\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
- "\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
+ "\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dCM", "\\dd", "\\ddp",
+ "\\dD", "\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
"\\dm", "\\dn", "\\do", "\\dO", "\\dp",
"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
@@ -1954,11 +1960,17 @@ psql_completion(const char *text, int start, int end)
/* ALTER TABLE ALTER [COLUMN] <foo> SET */
else if (Matches7("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET") ||
Matches6("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET"))
- COMPLETE_WITH_LIST5("(", "DEFAULT", "NOT NULL", "STATISTICS", "STORAGE");
+ COMPLETE_WITH_LIST6("(", "COMPRESSED", "DEFAULT", "NOT NULL", "STATISTICS", "STORAGE");
/* ALTER TABLE ALTER [COLUMN] <foo> SET ( */
else if (Matches8("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "(") ||
Matches7("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "("))
COMPLETE_WITH_LIST2("n_distinct", "n_distinct_inherited");
+ else if (Matches8("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "COMPRESSED") ||
+ Matches7("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "COMPRESSED"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_compression_methods);
+ else if (Matches9("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "COMPRESSED", MatchAny) ||
+ Matches8("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "COMPRESSED", MatchAny))
+ COMPLETE_WITH_CONST("WITH (");
/* ALTER TABLE ALTER [COLUMN] <foo> SET STORAGE */
else if (Matches8("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "STORAGE") ||
Matches7("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "STORAGE"))
@@ -2177,12 +2189,14 @@ psql_completion(const char *text, int start, int end)
"SCHEMA", "SEQUENCE", "STATISTICS", "SUBSCRIPTION",
"TABLE", "TYPE", "VIEW", "MATERIALIZED VIEW", "COLUMN", "AGGREGATE", "FUNCTION",
"OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", "LARGE OBJECT",
- "TABLESPACE", "TEXT SEARCH", "ROLE", NULL};
+ "TABLESPACE", "TEXT SEARCH", "ROLE", "COMPRESSION METHOD", NULL};
COMPLETE_WITH_LIST(list_COMMENT);
}
else if (Matches4("COMMENT", "ON", "ACCESS", "METHOD"))
COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
+ else if (Matches4("COMMENT", "ON", "COMPRESSION", "METHOD"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_compression_methods);
else if (Matches3("COMMENT", "ON", "FOREIGN"))
COMPLETE_WITH_LIST2("DATA WRAPPER", "TABLE");
else if (Matches4("COMMENT", "ON", "TEXT", "SEARCH"))
@@ -2255,6 +2269,14 @@ psql_completion(const char *text, int start, int end)
else if (Matches6("CREATE", "ACCESS", "METHOD", MatchAny, "TYPE", MatchAny))
COMPLETE_WITH_CONST("HANDLER");
+ /* CREATE COMPRESSION METHOD */
+ /* Complete "CREATE COMPRESSION METHOD <name>" */
+ else if (Matches4("CREATE", "COMPRESSION", "METHOD", MatchAny))
+ COMPLETE_WITH_CONST("HANDLER");
+ /* Complete "CREATE COMPRESSION METHOD <name> HANDLER" */
+ else if (Matches5("CREATE", "COMPRESSION", "METHOD", MatchAny, "HANDLER"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
+
/* CREATE DATABASE */
else if (Matches3("CREATE", "DATABASE", MatchAny))
COMPLETE_WITH_LIST9("OWNER", "TEMPLATE", "ENCODING", "TABLESPACE",
@@ -2687,6 +2709,7 @@ psql_completion(const char *text, int start, int end)
Matches4("DROP", "ACCESS", "METHOD", MatchAny) ||
(Matches4("DROP", "AGGREGATE|FUNCTION", MatchAny, MatchAny) &&
ends_with(prev_wd, ')')) ||
+ Matches4("DROP", "COMPRESSION", "METHOD", MatchAny) ||
Matches4("DROP", "EVENT", "TRIGGER", MatchAny) ||
Matches5("DROP", "FOREIGN", "DATA", "WRAPPER", MatchAny) ||
Matches4("DROP", "FOREIGN", "TABLE", MatchAny) ||
@@ -2775,6 +2798,12 @@ psql_completion(const char *text, int start, int end)
else if (Matches5("DROP", "RULE", MatchAny, "ON", MatchAny))
COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
+ /* DROP COMPRESSION METHOD */
+ else if (Matches2("DROP", "COMPRESSION"))
+ COMPLETE_WITH_CONST("METHOD");
+ else if (Matches3("DROP", "COMPRESSION", "METHOD"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_compression_methods);
+
/* EXECUTE */
else if (Matches1("EXECUTE"))
COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements);
@@ -3407,6 +3436,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
else if (TailMatchesCS1("\\db*"))
COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
+ else if (TailMatchesCS1("\\dCM*"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_compression_methods);
else if (TailMatchesCS1("\\dD*"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL);
else if (TailMatchesCS1("\\des*"))
diff --git a/src/include/access/compression.h b/src/include/access/compression.h
new file mode 100644
index 0000000000..bb4e299447
--- /dev/null
+++ b/src/include/access/compression.h
@@ -0,0 +1,68 @@
+/*-------------------------------------------------------------------------
+ *
+ * compression.h
+ * API for Postgres compression methods.
+ *
+ * Copyright (c) 2015-2016, PostgreSQL Global Development Group
+ *
+ * src/include/access/compression.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef COMPRESSION_H
+#define COMPRESSION_H
+
+#include "postgres.h"
+#include "catalog/pg_attribute.h"
+#include "nodes/nodes.h"
+#include "nodes/pg_list.h"
+
+/* parsenodes.h */
+typedef struct ColumnCompression ColumnCompression;
+typedef struct CompressionMethodRoutine CompressionMethodRoutine;
+
+typedef struct
+{
+ Oid cmoptoid;
+ CompressionMethodRoutine *routine;
+ List *options;
+} CompressionMethodOptions;
+
+typedef void (*CompressionConfigureRoutine)
+ (Form_pg_attribute attr, List *options);
+typedef struct varlena *(*CompressionRoutine)
+ (CompressionMethodOptions *cmoptions, const struct varlena *data);
+
+/*
+ * API struct for a compression method.
+ * Note this must be stored in a single palloc'd chunk of memory.
+ */
+typedef struct CompressionMethodRoutine
+{
+ NodeTag type;
+
+ CompressionConfigureRoutine configure;
+ CompressionConfigureRoutine drop;
+ CompressionRoutine compress;
+ CompressionRoutine decompress;
+} CompressionMethodRoutine;
+
+/* Compression method handler parameters */
+typedef struct CompressionMethodOpArgs
+{
+ Oid cmhanderid;
+ Oid typeid;
+} CompressionMethodOpArgs;
+
+extern CompressionMethodRoutine *GetCompressionMethodRoutine(Oid cmoptoid);
+extern List *GetCompressionOptionsList(Oid cmoptoid);
+extern Oid CreateCompressionOptions(Form_pg_attribute attr,
+ ColumnCompression *compression);
+extern ColumnCompression *GetColumnCompressionForAttribute(Form_pg_attribute att);
+extern void CheckCompressionMismatch(ColumnCompression *c1,
+ ColumnCompression *c2, const char *attributeName);
+extern void CreateColumnCompressionDependency(Form_pg_attribute attr,
+ Oid cmoptoid);
+
+#endif /* COMPRESSION_H */
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index b0d4c54121..7ff4cafacc 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -265,7 +265,9 @@ struct HeapTupleHeaderData
* information stored in t_infomask2:
*/
#define HEAP_NATTS_MASK 0x07FF /* 11 bits for number of attributes */
-/* bits 0x1800 are available */
+/* bit 0x800 is available */
+#define HEAP_HASCUSTOMCOMPRESSED 0x1000 /* tuple contain custom compressed
+ * varlenas */
#define HEAP_KEYS_UPDATED 0x2000 /* tuple was updated and key cols
* modified, or tuple deleted */
#define HEAP_HOT_UPDATED 0x4000 /* tuple was HOT-updated */
@@ -679,6 +681,9 @@ struct MinimalTupleData
#define HeapTupleHasExternal(tuple) \
(((tuple)->t_data->t_infomask & HEAP_HASEXTERNAL) != 0)
+#define HeapTupleHasCustomCompressed(tuple) \
+ (((tuple)->t_data->t_infomask2 & HEAP_HASCUSTOMCOMPRESSED) != 0)
+
#define HeapTupleIsHotUpdated(tuple) \
HeapTupleHeaderIsHotUpdated((tuple)->t_data)
@@ -794,7 +799,7 @@ extern Size heap_compute_data_size(TupleDesc tupleDesc,
extern void heap_fill_tuple(TupleDesc tupleDesc,
Datum *values, bool *isnull,
char *data, Size data_size,
- uint16 *infomask, bits8 *bit);
+ uint16 *infomask, uint16 *infomask2, bits8 *bit);
extern bool heap_attisnull(HeapTuple tup, int attnum);
extern Datum nocachegetattr(HeapTuple tup, int attnum,
TupleDesc att);
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index cd43e3a52e..2e1c92123e 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -258,7 +258,6 @@ extern void add_string_reloption(bits32 kinds, const char *name, const char *des
extern Datum transformRelOptions(Datum oldOptions, List *defList,
const char *namspace, char *validnsps[],
bool ignoreOids, bool isReset);
-extern List *untransformRelOptions(Datum options);
extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
amoptions_function amoptions);
extern relopt_value *parseRelOptions(Datum options, bool validate,
@@ -269,6 +268,8 @@ extern void fillRelOptions(void *rdopts, Size basesize,
relopt_value *options, int numoptions,
bool validate,
const relopt_parse_elt *elems, int nelems);
+extern char *formatRelOptions(List *options);
+extern List *untransformRelOptions(Datum options);
extern bytea *default_reloptions(Datum reloptions, bool validate,
relopt_kind kind);
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index 2be5af1d3e..9356a1f13e 100644
--- a/src/include/access/tupdesc.h
+++ b/src/include/access/tupdesc.h
@@ -14,7 +14,9 @@
#ifndef TUPDESC_H
#define TUPDESC_H
+#include "postgres.h"
#include "access/attnum.h"
+#include "access/compression.h"
#include "catalog/pg_attribute.h"
#include "nodes/pg_list.h"
@@ -43,6 +45,10 @@ typedef struct tupleConstr
bool has_not_null;
} TupleConstr;
+/* tupledesc flags */
+#define TD_ATTR_CUSTOM_COMPRESSED 0x01 /* is TupleDesc contain attributes
+ with custom compression? */
+
/*
* This struct is passed around within the backend to describe the structure
* of tuples. For tuples coming from on-disk relations, the information is
@@ -80,6 +86,7 @@ typedef struct tupleDesc
Oid tdtypeid; /* composite type ID for tuple type */
int32 tdtypmod; /* typmod for tuple type */
bool tdhasoid; /* tuple has oid attribute in its header */
+ bool tdflags; /* tuple additional flags */
int tdrefcount; /* reference count, or -1 if not counting */
TupleConstr *constr; /* constraints, or NULL if none */
/* attrs[N] is the description of Attribute Number N+1 */
diff --git a/src/include/access/tuptoaster.h b/src/include/access/tuptoaster.h
index fd9f83ac44..115889c8f5 100644
--- a/src/include/access/tuptoaster.h
+++ b/src/include/access/tuptoaster.h
@@ -101,6 +101,15 @@
/* Size of an EXTERNAL datum that contains an indirection pointer */
#define INDIRECT_POINTER_SIZE (VARHDRSZ_EXTERNAL + sizeof(varatt_indirect))
+/*
+ * va_extinfo in varatt_external contains actual length of the external data
+ * and optional flags
+ */
+#define VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer) \
+ ((toast_pointer).va_extinfo & 0x3FFFFFFF)
+#define VARATT_EXTERNAL_IS_CUSTOM_COMPRESSED(toast_pointer) \
+ (((toast_pointer).va_extinfo >> 30) == 0x02)
+
/*
* Testing whether an externally-stored value is compressed now requires
* comparing extsize (the actual length of the external data) to rawsize
@@ -109,7 +118,7 @@
* saves space, so we expect either equality or less-than.
*/
#define VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer) \
- ((toast_pointer).va_extsize < (toast_pointer).va_rawsize - VARHDRSZ)
+ (VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer) < (toast_pointer).va_rawsize - VARHDRSZ)
/*
* Macro to fetch the possibly-unaligned contents of an EXTERNAL datum
@@ -210,7 +219,7 @@ extern HeapTuple toast_build_flattened_tuple(TupleDesc tupleDesc,
* Create a compressed version of a varlena datum, if possible
* ----------
*/
-extern Datum toast_compress_datum(Datum value);
+extern Datum toast_compress_datum(Datum value, Oid cmoptoid);
/* ----------
* toast_raw_datum_size -
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index b9f98423cc..36cadd409e 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -165,10 +165,12 @@ typedef enum ObjectClass
OCLASS_PUBLICATION, /* pg_publication */
OCLASS_PUBLICATION_REL, /* pg_publication_rel */
OCLASS_SUBSCRIPTION, /* pg_subscription */
- OCLASS_TRANSFORM /* pg_transform */
+ OCLASS_TRANSFORM, /* pg_transform */
+ OCLASS_COMPRESSION_METHOD, /* pg_compression */
+ OCLASS_COMPRESSION_OPTIONS /* pg_compression_opt */
} ObjectClass;
-#define LAST_OCLASS OCLASS_TRANSFORM
+#define LAST_OCLASS OCLASS_COMPRESSION_OPTIONS
/* flag bits for performDeletion/performMultipleDeletions: */
#define PERFORM_DELETION_INTERNAL 0x0001 /* internal action */
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index ef8493674c..7fe9f1f059 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -120,6 +120,14 @@ DECLARE_UNIQUE_INDEX(pg_collation_name_enc_nsp_index, 3164, on pg_collation usin
DECLARE_UNIQUE_INDEX(pg_collation_oid_index, 3085, on pg_collation using btree(oid oid_ops));
#define CollationOidIndexId 3085
+DECLARE_UNIQUE_INDEX(pg_compression_oid_index, 3422, on pg_compression using btree(oid oid_ops));
+#define CompressionMethodOidIndexId 3422
+DECLARE_UNIQUE_INDEX(pg_compression_name_index, 3423, on pg_compression using btree(cmname name_ops));
+#define CompressionMethodNameIndexId 3423
+
+DECLARE_UNIQUE_INDEX(pg_compression_opt_oid_index, 3424, on pg_compression_opt using btree(cmoptoid oid_ops));
+#define CompressionOptionsOidIndexId 3424
+
DECLARE_INDEX(pg_constraint_conname_nsp_index, 2664, on pg_constraint using btree(conname name_ops, connamespace oid_ops));
#define ConstraintNameNspIndexId 2664
DECLARE_INDEX(pg_constraint_conrelid_index, 2665, on pg_constraint using btree(conrelid oid_ops));
diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h
index bcf28e8f04..caadd61031 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -156,6 +156,9 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
/* attribute's collation */
Oid attcollation;
+ /* attribute's compression options or InvalidOid */
+ Oid attcompression;
+
#ifdef CATALOG_VARLEN /* variable-length fields start here */
/* NOTE: The following fields are not present in tuple descriptors. */
@@ -174,10 +177,10 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
* ATTRIBUTE_FIXED_PART_SIZE is the size of the fixed-layout,
* guaranteed-not-null part of a pg_attribute row. This is in fact as much
* of the row as gets copied into tuple descriptors, so don't expect you
- * can access fields beyond attcollation except in a real tuple!
+ * can access fields beyond attcompression except in a real tuple!
*/
#define ATTRIBUTE_FIXED_PART_SIZE \
- (offsetof(FormData_pg_attribute,attcollation) + sizeof(Oid))
+ (offsetof(FormData_pg_attribute,attcompression) + sizeof(Oid))
/* ----------------
* Form_pg_attribute corresponds to a pointer to a tuple with
@@ -191,29 +194,30 @@ typedef FormData_pg_attribute *Form_pg_attribute;
* ----------------
*/
-#define Natts_pg_attribute 22
-#define Anum_pg_attribute_attrelid 1
-#define Anum_pg_attribute_attname 2
-#define Anum_pg_attribute_atttypid 3
-#define Anum_pg_attribute_attstattarget 4
-#define Anum_pg_attribute_attlen 5
-#define Anum_pg_attribute_attnum 6
-#define Anum_pg_attribute_attndims 7
-#define Anum_pg_attribute_attcacheoff 8
-#define Anum_pg_attribute_atttypmod 9
-#define Anum_pg_attribute_attbyval 10
-#define Anum_pg_attribute_attstorage 11
-#define Anum_pg_attribute_attalign 12
-#define Anum_pg_attribute_attnotnull 13
-#define Anum_pg_attribute_atthasdef 14
-#define Anum_pg_attribute_attidentity 15
-#define Anum_pg_attribute_attisdropped 16
-#define Anum_pg_attribute_attislocal 17
-#define Anum_pg_attribute_attinhcount 18
-#define Anum_pg_attribute_attcollation 19
-#define Anum_pg_attribute_attacl 20
-#define Anum_pg_attribute_attoptions 21
-#define Anum_pg_attribute_attfdwoptions 22
+#define Natts_pg_attribute 23
+#define Anum_pg_attribute_attrelid 1
+#define Anum_pg_attribute_attname 2
+#define Anum_pg_attribute_atttypid 3
+#define Anum_pg_attribute_attstattarget 4
+#define Anum_pg_attribute_attlen 5
+#define Anum_pg_attribute_attnum 6
+#define Anum_pg_attribute_attndims 7
+#define Anum_pg_attribute_attcacheoff 8
+#define Anum_pg_attribute_atttypmod 9
+#define Anum_pg_attribute_attbyval 10
+#define Anum_pg_attribute_attstorage 11
+#define Anum_pg_attribute_attalign 12
+#define Anum_pg_attribute_attnotnull 13
+#define Anum_pg_attribute_atthasdef 14
+#define Anum_pg_attribute_attidentity 15
+#define Anum_pg_attribute_attisdropped 16
+#define Anum_pg_attribute_attislocal 17
+#define Anum_pg_attribute_attinhcount 18
+#define Anum_pg_attribute_attcollation 19
+#define Anum_pg_attribute_attcompression 20
+#define Anum_pg_attribute_attacl 21
+#define Anum_pg_attribute_attoptions 22
+#define Anum_pg_attribute_attfdwoptions 23
/* ----------------
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index b256657bda..40f5cc4f18 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -149,7 +149,7 @@ typedef FormData_pg_class *Form_pg_class;
*/
DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 30 0 t f f f f f f t n f 3 1 _null_ _null_ _null_));
DESCR("");
-DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 22 0 f f f f f f f t n f 3 1 _null_ _null_ _null_));
+DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 23 0 f f f f f f f t n f 3 1 _null_ _null_ _null_));
DESCR("");
DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f p r 29 0 t f f f f f f t n f 3 1 _null_ _null_ _null_));
DESCR("");
diff --git a/src/include/catalog/pg_compression.h b/src/include/catalog/pg_compression.h
new file mode 100644
index 0000000000..1d5f9ac479
--- /dev/null
+++ b/src/include/catalog/pg_compression.h
@@ -0,0 +1,55 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_compression.h
+ * definition of the system "compression method" relation (pg_compression)
+ * along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_compression.h
+ *
+ * NOTES
+ * the genbki.pl script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ * XXX do NOT break up DATA() statements into multiple lines!
+ * the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_COMPRESSION_H
+#define PG_COMPRESSION_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ * pg_compression definition. cpp turns this into
+ * typedef struct FormData_pg_compression
+ * ----------------
+ */
+#define CompressionMethodRelationId 3419
+
+CATALOG(pg_compression,3419)
+{
+ NameData cmname; /* compression method name */
+ regproc cmhandler; /* compression handler */
+} FormData_pg_compression;
+
+/* ----------------
+ * Form_pg_compression corresponds to a pointer to a tuple with
+ * the format of pg_compression relation.
+ * ----------------
+ */
+typedef FormData_pg_compression * Form_pg_compression;
+
+/* ----------------
+ * compiler constants for pg_compression
+ * ----------------
+ */
+#define Natts_pg_compression 2
+#define Anum_pg_compression_cmname 1
+#define Anum_pg_compression_cmhandler 2
+
+#endif /* PG_COMPRESSION_H */
diff --git a/src/include/catalog/pg_compression_opt.h b/src/include/catalog/pg_compression_opt.h
new file mode 100644
index 0000000000..ddcef814a7
--- /dev/null
+++ b/src/include/catalog/pg_compression_opt.h
@@ -0,0 +1,56 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_compression_opt.h
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_compression_opt.h
+ *
+ * NOTES
+ * the genbki.pl script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ * XXX do NOT break up DATA() statements into multiple lines!
+ * the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_COMPRESSION_OPT_H
+#define PG_COMPRESSION_OPT_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ * pg_compression_opt definition. cpp turns this into
+ * typedef struct FormData_pg_compression_opt
+ * ----------------
+ */
+#define CompressionOptRelationId 3420
+
+CATALOG(pg_compression_opt,3420) BKI_WITHOUT_OIDS
+{
+ Oid cmoptoid; /* compression options oid */
+ NameData cmname; /* name of compression method */
+ regproc cmhandler; /* compression handler */
+ text cmoptions[1]; /* specific options from WITH */
+} FormData_pg_compression_opt;
+
+/* ----------------
+ * Form_pg_compression_opt corresponds to a pointer to a tuple with
+ * the format of pg_compression_opt relation.
+ * ----------------
+ */
+typedef FormData_pg_compression_opt * Form_pg_compression_opt;
+
+/* ----------------
+ * compiler constants for pg_compression_opt
+ * ----------------
+ */
+#define Natts_pg_compression_opt 4
+#define Anum_pg_compression_opt_cmoptoid 1
+#define Anum_pg_compression_opt_cmname 2
+#define Anum_pg_compression_opt_cmhandler 3
+#define Anum_pg_compression_opt_cmoptions 4
+
+#endif /* PG_COMPRESSION_OPT_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index c969375981..dc3fa66f2e 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3881,6 +3881,10 @@ DATA(insert OID = 3311 ( tsm_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s
DESCR("I/O");
DATA(insert OID = 3312 ( tsm_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3310" _null_ _null_ _null_ _null_ _null_ tsm_handler_out _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 3425 ( compression_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3421 "2275" _null_ _null_ _null_ _null_ _null_ compression_handler_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 3426 ( compression_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3421" _null_ _null_ _null_ _null_ _null_ compression_handler_out _null_ _null_ _null_ ));
+DESCR("I/O");
/* tablesample method handlers */
DATA(insert OID = 3313 ( bernoulli PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3310 "2281" _null_ _null_ _null_ _null_ _null_ tsm_bernoulli_handler _null_ _null_ _null_ ));
@@ -4677,6 +4681,8 @@ DATA(insert OID = 3646 ( gtsvectorin PGNSP PGUID 12 1 0 0 0 f f f f t f i s
DESCR("I/O");
DATA(insert OID = 3647 ( gtsvectorout PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3642" _null_ _null_ _null_ _null_ _null_ gtsvectorout _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 3453 ( tsvector_compression_handler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3421 "2281" _null_ _null_ _null_ _null_ _null_ tsvector_compression_handler _null_ _null_ _null_ ));
+DESCR("tsvector compression handler");
DATA(insert OID = 3616 ( tsvector_lt PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "3614 3614" _null_ _null_ _null_ _null_ _null_ tsvector_lt _null_ _null_ _null_ ));
DATA(insert OID = 3617 ( tsvector_le PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "3614 3614" _null_ _null_ _null_ _null_ _null_ tsvector_le _null_ _null_ _null_ ));
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index e3551440a0..ec8c3df953 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -712,6 +712,8 @@ DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_han
#define TSM_HANDLEROID 3310
DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
#define ANYRANGEOID 3831
+DATA(insert OID = 3421 ( compression_handler PGNSP PGUID 4 t p P f t \054 0 0 0 compression_handler_in compression_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define COMPRESSION_HANDLEROID 3421
/*
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index bfead9af3d..a98ecc12ce 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -140,6 +140,7 @@ extern Oid RemoveUserMapping(DropUserMappingStmt *stmt);
extern void RemoveUserMappingById(Oid umId);
extern void CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid);
extern void ImportForeignSchema(ImportForeignSchemaStmt *stmt);
+extern Datum optionListToArray(List *options);
extern Datum transformGenericOptions(Oid catalogId,
Datum oldOptions,
List *options,
@@ -152,6 +153,14 @@ extern Oid get_index_am_oid(const char *amname, bool missing_ok);
extern Oid get_am_oid(const char *amname, bool missing_ok);
extern char *get_am_name(Oid amOid);
+/* commands/compressioncmds.c */
+extern ObjectAddress DefineCompressionMethod(List *names, List *parameters);
+extern void RemoveCompressionMethodById(Oid cmOid);
+extern void RemoveCompressionOptionsById(Oid cmoptoid);
+extern Oid get_compression_method_oid(const char *cmname, bool missing_ok);
+extern char *get_compression_method_name(Oid cmOid);
+extern char *get_compression_method_name_for_opt(Oid cmoptoid);
+
/* support routines in commands/define.c */
extern char *defGetString(DefElem *def);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 03dc5307e8..367d6b189b 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -469,6 +469,7 @@ typedef enum NodeTag
T_PartitionBoundSpec,
T_PartitionRangeDatum,
T_PartitionCmd,
+ T_ColumnCompression,
T_VacuumRelation,
/*
@@ -499,7 +500,8 @@ typedef enum NodeTag
T_FdwRoutine, /* in foreign/fdwapi.h */
T_IndexAmRoutine, /* in access/amapi.h */
T_TsmRoutine, /* in access/tsmapi.h */
- T_ForeignKeyCacheInfo /* in utils/rel.h */
+ T_ForeignKeyCacheInfo, /* in utils/rel.h */
+ T_CompressionMethodRoutine, /* in access/compression.h */
} NodeTag;
/*
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 80c19b2a55..56e42ce1f1 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -615,6 +615,19 @@ typedef struct RangeTableSample
int location; /* method name location, or -1 if unknown */
} RangeTableSample;
+/*
+ * ColumnCompression - compression parameters for some attribute
+ *
+ * This represents compression information defined using clause:
+ * .. COMPRESSED <compression_method_name> WITH (<params>)
+ */
+typedef struct ColumnCompression
+{
+ NodeTag type;
+ char *methodName;
+ List *options;
+} ColumnCompression;
+
/*
* ColumnDef - column definition (used in various creates)
*
@@ -638,6 +651,7 @@ typedef struct ColumnDef
NodeTag type;
char *colname; /* name of column */
TypeName *typeName; /* type of column */
+ ColumnCompression *compression;
int inhcount; /* number of times column is inherited */
bool is_local; /* column has local (non-inherited) def'n */
bool is_not_null; /* NOT NULL constraint specified? */
@@ -1622,6 +1636,7 @@ typedef enum ObjectType
OBJECT_CAST,
OBJECT_COLUMN,
OBJECT_COLLATION,
+ OBJECT_COMPRESSION_METHOD,
OBJECT_CONVERSION,
OBJECT_DATABASE,
OBJECT_DEFAULT,
@@ -1769,7 +1784,8 @@ typedef enum AlterTableType
AT_DetachPartition, /* DETACH PARTITION */
AT_AddIdentity, /* ADD IDENTITY */
AT_SetIdentity, /* SET identity column options */
- AT_DropIdentity /* DROP IDENTITY */
+ AT_DropIdentity, /* DROP IDENTITY */
+ AT_AlterColumnCompression /* ALTER COLUMN name COMPRESSED cm WITH (...) */
} AlterTableType;
typedef struct ReplicaIdentityStmt
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index f50e45e886..7bfc6e6be4 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -87,6 +87,8 @@ PG_KEYWORD("comment", COMMENT, UNRESERVED_KEYWORD)
PG_KEYWORD("comments", COMMENTS, UNRESERVED_KEYWORD)
PG_KEYWORD("commit", COMMIT, UNRESERVED_KEYWORD)
PG_KEYWORD("committed", COMMITTED, UNRESERVED_KEYWORD)
+PG_KEYWORD("compressed", COMPRESSED, UNRESERVED_KEYWORD)
+PG_KEYWORD("compression", COMPRESSION, UNRESERVED_KEYWORD)
PG_KEYWORD("concurrently", CONCURRENTLY, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("configuration", CONFIGURATION, UNRESERVED_KEYWORD)
PG_KEYWORD("conflict", CONFLICT, UNRESERVED_KEYWORD)
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index e749432ef0..5cab77457a 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -22,10 +22,12 @@ extern List *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
const char *queryString);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
+void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
extern List *transformCreateSchemaStmt(CreateSchemaStmt *stmt);
extern PartitionBoundSpec *transformPartitionBound(ParseState *pstate, Relation parent,
PartitionBoundSpec *spec);
+extern void transformColumnCompression(ColumnDef *column, RangeVar *relation,
+ AlterTableStmt **alterStmt);
#endif /* PARSE_UTILCMD_H */
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 1ca9b60ea1..d21974696f 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -56,7 +56,7 @@
/*
* struct varatt_external is a traditional "TOAST pointer", that is, the
* information needed to fetch a Datum stored out-of-line in a TOAST table.
- * The data is compressed if and only if va_extsize < va_rawsize - VARHDRSZ.
+ * The data is compressed if and only if size in va_extinfo < va_rawsize - VARHDRSZ.
* This struct must not contain any padding, because we sometimes compare
* these pointers using memcmp.
*
@@ -68,7 +68,8 @@
typedef struct varatt_external
{
int32 va_rawsize; /* Original data size (includes header) */
- int32 va_extsize; /* External saved size (doesn't) */
+ uint32 va_extinfo; /* External saved size (without header) and
+ * flags */
Oid va_valueid; /* Unique ID of value within TOAST table */
Oid va_toastrelid; /* RelID of TOAST table containing it */
} varatt_external;
@@ -146,9 +147,18 @@ typedef union
struct /* Compressed-in-line format */
{
uint32 va_header;
- uint32 va_rawsize; /* Original data size (excludes header) */
+ uint32 va_info; /* Original data size (excludes header) and
+ * flags */
char va_data[FLEXIBLE_ARRAY_MEMBER]; /* Compressed data */
} va_compressed;
+ struct /* Compressed-in-line format */
+ {
+ uint32 va_header;
+ uint32 va_info; /* Original data size (excludes header) and
+ * flags */
+ Oid va_cmoptoid; /* Oid of compression options */
+ char va_data[FLEXIBLE_ARRAY_MEMBER]; /* Compressed data */
+ } va_custom_compressed;
} varattrib_4b;
typedef struct
@@ -281,8 +291,14 @@ typedef struct
#define VARDATA_1B(PTR) (((varattrib_1b *) (PTR))->va_data)
#define VARDATA_1B_E(PTR) (((varattrib_1b_e *) (PTR))->va_data)
+/* va_info in va_compress contains raw size of datum and optional flags */
#define VARRAWSIZE_4B_C(PTR) \
- (((varattrib_4b *) (PTR))->va_compressed.va_rawsize)
+ (((varattrib_4b *) (PTR))->va_compressed.va_info & 0x3FFFFFFF)
+#define VARFLAGS_4B_C(PTR) \
+ (((varattrib_4b *) (PTR))->va_compressed.va_info >> 30)
+
+#define VARHDRSZ_CUSTOM_COMPRESSED \
+ (offsetof(varattrib_4b, va_custom_compressed.va_data))
/* Externally visible macros */
@@ -311,6 +327,8 @@ typedef struct
#define VARDATA_EXTERNAL(PTR) VARDATA_1B_E(PTR)
#define VARATT_IS_COMPRESSED(PTR) VARATT_IS_4B_C(PTR)
+#define VARATT_IS_CUSTOM_COMPRESSED(PTR) (VARATT_IS_4B_C(PTR) && \
+ (VARFLAGS_4B_C(PTR) == 0x02))
#define VARATT_IS_EXTERNAL(PTR) VARATT_IS_1B_E(PTR)
#define VARATT_IS_EXTERNAL_ONDISK(PTR) \
(VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_ONDISK)
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 254a811aff..6ad889af7a 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -210,6 +210,7 @@ typedef enum AclObjectKind
ACL_KIND_EXTENSION, /* pg_extension */
ACL_KIND_PUBLICATION, /* pg_publication */
ACL_KIND_SUBSCRIPTION, /* pg_subscription */
+ ACL_KIND_COMPRESSION_METHOD, /* pg_compression */
MAX_ACL_KIND /* MUST BE LAST */
} AclObjectKind;
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 8a0be41929..ff7cb530fd 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -48,6 +48,9 @@ enum SysCacheIdentifier
CLAOID,
COLLNAMEENCNSP,
COLLOID,
+ COMPRESSIONMETHODOID,
+ COMPRESSIONMETHODNAME,
+ COMPRESSIONOPTIONSOID,
CONDEFAULT,
CONNAMENSP,
CONSTROID,
diff --git a/src/test/regress/expected/copy2.out b/src/test/regress/expected/copy2.out
index 65e9c626b3..112f0eda47 100644
--- a/src/test/regress/expected/copy2.out
+++ b/src/test/regress/expected/copy2.out
@@ -438,10 +438,10 @@ begin
end $$ language plpgsql immutable;
alter table check_con_tbl add check (check_con_function(check_con_tbl.*));
\d+ check_con_tbl
- Table "public.check_con_tbl"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- f1 | integer | | | | plain | |
+ Table "public.check_con_tbl"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ f1 | integer | | | | plain | | |
Check constraints:
"check_con_tbl_check" CHECK (check_con_function(check_con_tbl.*))
diff --git a/src/test/regress/expected/create_cm.out b/src/test/regress/expected/create_cm.out
new file mode 100644
index 0000000000..119fac595a
--- /dev/null
+++ b/src/test/regress/expected/create_cm.out
@@ -0,0 +1,154 @@
+-- test drop
+CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
+CREATE TABLE droptest(fts tsvector COMPRESSED ts1);
+DROP COMPRESSION METHOD ts1;
+ERROR: cannot drop compression method ts1 because other objects depend on it
+DETAIL: compression options for ts1 depends on compression method ts1
+table droptest column fts depends on compression options for ts1
+HINT: Use DROP ... CASCADE to drop the dependent objects too.
+DROP COMPRESSION METHOD ts1 CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to compression options for ts1
+drop cascades to table droptest column fts
+\d+ droptest
+ Table "public.droptest"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+---------+-------------+--------------+-------------
+
+DROP TABLE droptest;
+CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
+CREATE TABLE cmtest(fts tsvector COMPRESSED ts1);
+SELECT * FROM pg_compression;
+ cmname | cmhandler
+--------+------------------------------
+ ts1 | tsvector_compression_handler
+(1 row)
+
+SELECT cmhandler, cmoptions FROM pg_compression_opt;
+ cmhandler | cmoptions
+------------------------------+-----------
+ tsvector_compression_handler |
+(1 row)
+
+\dCM
+List of compression methods
+ Name
+------
+ ts1
+(1 row)
+
+\d+ cmtest
+ Table "public.cmtest"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+
+INSERT INTO cmtest
+ SELECT to_tsvector(string_agg(repeat(substr(i::text,1,1), i), ' '))
+ FROM generate_series(1,200) i;
+SELECT length(fts) FROM cmtest;
+ length
+--------
+ 200
+(1 row)
+
+SELECT length(fts) FROM cmtest;
+ length
+--------
+ 200
+(1 row)
+
+-- check ALTER commands
+ALTER TABLE cmtest ALTER COLUMN fts SET NOT COMPRESSED;
+\d+ cmtest
+ Table "public.cmtest"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | | |
+
+ALTER TABLE cmtest ALTER COLUMN fts SET COMPRESSED ts1 WITH (format 'lz');
+ERROR: the compression method "ts1" does not take any options
+ALTER TABLE cmtest ALTER COLUMN fts SET COMPRESSED ts1;
+\d+ cmtest
+ Table "public.cmtest"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+
+-- create different types of tables
+SELECT * INTO cmtest2 FROM cmtest;
+CREATE TABLE cmtest3 (LIKE cmtest);
+CREATE TABLE cmtest4(fts tsvector, a int) INHERITS (cmtest);
+NOTICE: merging column "fts" with inherited definition
+CREATE TABLE cmtest5(fts tsvector);
+CREATE TABLE cmtest6(fts tsvector);
+INSERT INTO cmtest6 SELECT * FROM cmtest;
+-- we update usual datum with compressed datum
+INSERT INTO cmtest5
+ SELECT to_tsvector(string_agg(repeat(substr(i::text,1,1), i), ' '))
+ FROM generate_series(1,200) i;
+UPDATE cmtest5 SET fts = cmtest.fts FROM cmtest;
+\d+ cmtest3
+ Table "public.cmtest3"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+
+\d+ cmtest4
+ Table "public.cmtest4"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ fts | tsvector | | | | extended | ts1 | |
+ a | integer | | | | plain | | |
+Inherits: cmtest
+
+DROP TABLE cmtest CASCADE;
+NOTICE: drop cascades to table cmtest4
+DROP TABLE cmtest3;
+SELECT cmhandler, cmoptions FROM pg_compression_opt;
+ cmhandler | cmoptions
+------------------------------+-----------
+ tsvector_compression_handler |
+ tsvector_compression_handler |
+ tsvector_compression_handler |
+ tsvector_compression_handler |
+(4 rows)
+
+DROP COMPRESSION METHOD ts1 CASCADE;
+NOTICE: drop cascades to 4 other objects
+DETAIL: drop cascades to compression options for ts1
+drop cascades to compression options for ts1
+drop cascades to compression options for ts1
+drop cascades to compression options for ts1
+SELECT * FROM pg_compression;
+ cmname | cmhandler
+--------+-----------
+(0 rows)
+
+SELECT * FROM pg_compression_opt;
+ cmoptoid | cmname | cmhandler | cmoptions
+----------+--------+-----------+-----------
+(0 rows)
+
+-- check that moved tuples still can be decompressed
+SELECT length(fts) FROM cmtest2;
+ length
+--------
+ 200
+(1 row)
+
+SELECT length(fts) FROM cmtest5;
+ length
+--------
+ 200
+(1 row)
+
+SELECT length(fts) FROM cmtest6;
+ length
+--------
+ 200
+(1 row)
+
+DROP TABLE cmtest2;
+DROP TABLE cmtest5;
+DROP TABLE cmtest6;
diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out
index 8e745402ae..0e9b9a36fb 100644
--- a/src/test/regress/expected/create_table.out
+++ b/src/test/regress/expected/create_table.out
@@ -592,10 +592,10 @@ CREATE TABLE oids_parted (
) PARTITION BY RANGE (a) WITH OIDS;
CREATE TABLE part_forced_oids PARTITION OF oids_parted FOR VALUES FROM (1) TO (10) WITHOUT OIDS;
\d+ part_forced_oids
- Table "public.part_forced_oids"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
+ Table "public.part_forced_oids"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
Partition of: oids_parted FOR VALUES FROM (1) TO (10)
Partition constraint: ((a IS NOT NULL) AND (a >= 1) AND (a < 10))
Has OIDs: yes
@@ -731,11 +731,11 @@ CREATE TABLE part_c PARTITION OF parted (b WITH OPTIONS NOT NULL DEFAULT 0) FOR
CREATE TABLE part_c_1_10 PARTITION OF part_c FOR VALUES FROM (1) TO (10);
-- Partition bound in describe output
\d+ part_b
- Table "public.part_b"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | not null | 1 | plain | |
+ Table "public.part_b"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | not null | 1 | plain | | |
Partition of: parted FOR VALUES IN ('b')
Partition constraint: ((a IS NOT NULL) AND (a = ANY (ARRAY['b'::text])))
Check constraints:
@@ -758,11 +758,11 @@ Partitions: part_c_1_10 FOR VALUES FROM (1) TO (10)
-- a level-2 partition's constraint will include the parent's expressions
\d+ part_c_1_10
- Table "public.part_c_1_10"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | not null | 0 | plain | |
+ Table "public.part_c_1_10"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | not null | 0 | plain | | |
Partition of: part_c FOR VALUES FROM (1) TO (10)
Partition constraint: ((a IS NOT NULL) AND (a = ANY (ARRAY['c'::text])) AND (b IS NOT NULL) AND (b >= 1) AND (b < 10))
Check constraints:
@@ -795,46 +795,46 @@ Number of partitions: 3 (Use \d+ to list them.)
CREATE TABLE range_parted4 (a int, b int, c int) PARTITION BY RANGE (abs(a), abs(b), c);
CREATE TABLE unbounded_range_part PARTITION OF range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (MAXVALUE, MAXVALUE, MAXVALUE);
\d+ unbounded_range_part
- Table "public.unbounded_range_part"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
+ Table "public.unbounded_range_part"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
Partition of: range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (MAXVALUE, MAXVALUE, MAXVALUE)
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL))
DROP TABLE unbounded_range_part;
CREATE TABLE range_parted4_1 PARTITION OF range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (1, MAXVALUE, MAXVALUE);
\d+ range_parted4_1
- Table "public.range_parted4_1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
+ Table "public.range_parted4_1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
Partition of: range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (1, MAXVALUE, MAXVALUE)
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND (abs(a) <= 1))
CREATE TABLE range_parted4_2 PARTITION OF range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, MAXVALUE);
\d+ range_parted4_2
- Table "public.range_parted4_2"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
+ Table "public.range_parted4_2"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
Partition of: range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, MAXVALUE)
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND ((abs(a) > 3) OR ((abs(a) = 3) AND (abs(b) > 4)) OR ((abs(a) = 3) AND (abs(b) = 4) AND (c >= 5))) AND ((abs(a) < 6) OR ((abs(a) = 6) AND (abs(b) <= 7))))
CREATE TABLE range_parted4_3 PARTITION OF range_parted4 FOR VALUES FROM (6, 8, MINVALUE) TO (9, MAXVALUE, MAXVALUE);
\d+ range_parted4_3
- Table "public.range_parted4_3"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
+ Table "public.range_parted4_3"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
Partition of: range_parted4 FOR VALUES FROM (6, 8, MINVALUE) TO (9, MAXVALUE, MAXVALUE)
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND ((abs(a) > 6) OR ((abs(a) = 6) AND (abs(b) >= 8))) AND (abs(a) <= 9))
diff --git a/src/test/regress/expected/create_table_like.out b/src/test/regress/expected/create_table_like.out
index 3f405c94ce..b5ca8b820b 100644
--- a/src/test/regress/expected/create_table_like.out
+++ b/src/test/regress/expected/create_table_like.out
@@ -156,32 +156,32 @@ CREATE TABLE ctlt4 (a text, c text);
ALTER TABLE ctlt4 ALTER COLUMN c SET STORAGE EXTERNAL;
CREATE TABLE ctlt12_storage (LIKE ctlt1 INCLUDING STORAGE, LIKE ctlt2 INCLUDING STORAGE);
\d+ ctlt12_storage
- Table "public.ctlt12_storage"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | |
- b | text | | | | extended | |
- c | text | | | | external | |
+ Table "public.ctlt12_storage"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | |
+ b | text | | | | extended | | |
+ c | text | | | | external | | |
CREATE TABLE ctlt12_comments (LIKE ctlt1 INCLUDING COMMENTS, LIKE ctlt2 INCLUDING COMMENTS);
\d+ ctlt12_comments
- Table "public.ctlt12_comments"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | extended | | A
- b | text | | | | extended | | B
- c | text | | | | extended | | C
+ Table "public.ctlt12_comments"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | extended | | | A
+ b | text | | | | extended | | | B
+ c | text | | | | extended | | | C
CREATE TABLE ctlt1_inh (LIKE ctlt1 INCLUDING CONSTRAINTS INCLUDING COMMENTS) INHERITS (ctlt1);
NOTICE: merging column "a" with inherited definition
NOTICE: merging column "b" with inherited definition
NOTICE: merging constraint "ctlt1_a_check" with inherited definition
\d+ ctlt1_inh
- Table "public.ctlt1_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | | A
- b | text | | | | extended | | B
+ Table "public.ctlt1_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | | A
+ b | text | | | | extended | | | B
Check constraints:
"ctlt1_a_check" CHECK (length(a) > 2)
Inherits: ctlt1
@@ -195,12 +195,12 @@ SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_con
CREATE TABLE ctlt13_inh () INHERITS (ctlt1, ctlt3);
NOTICE: merging multiple inherited definitions of column "a"
\d+ ctlt13_inh
- Table "public.ctlt13_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | |
- b | text | | | | extended | |
- c | text | | | | external | |
+ Table "public.ctlt13_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | |
+ b | text | | | | extended | | |
+ c | text | | | | external | | |
Check constraints:
"ctlt1_a_check" CHECK (length(a) > 2)
"ctlt3_a_check" CHECK (length(a) < 5)
@@ -210,12 +210,12 @@ Inherits: ctlt1,
CREATE TABLE ctlt13_like (LIKE ctlt3 INCLUDING CONSTRAINTS INCLUDING COMMENTS INCLUDING STORAGE) INHERITS (ctlt1);
NOTICE: merging column "a" with inherited definition
\d+ ctlt13_like
- Table "public.ctlt13_like"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | | A3
- b | text | | | | extended | |
- c | text | | | | external | | C
+ Table "public.ctlt13_like"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | | A3
+ b | text | | | | extended | | |
+ c | text | | | | external | | | C
Check constraints:
"ctlt1_a_check" CHECK (length(a) > 2)
"ctlt3_a_check" CHECK (length(a) < 5)
@@ -229,11 +229,11 @@ SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_con
CREATE TABLE ctlt_all (LIKE ctlt1 INCLUDING ALL);
\d+ ctlt_all
- Table "public.ctlt_all"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
- a | text | | not null | | main | | A
- b | text | | | | extended | | B
+ Table "public.ctlt_all"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | not null | | main | | | A
+ b | text | | | | extended | | | B
Indexes:
"ctlt_all_pkey" PRIMARY KEY, btree (a)
"ctlt_all_b_idx" btree (b)
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index f4eebb75cf..2ac75c44b5 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -272,10 +272,10 @@ explain (verbose, costs off)
create rule silly as on delete to dcomptable do instead
update dcomptable set d1.r = (d1).r - 1, d1.i = (d1).i + 1 where (d1).i > 0;
\d+ dcomptable
- Table "public.dcomptable"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+-----------+-----------+----------+---------+----------+--------------+-------------
- d1 | dcomptype | | | | extended | |
+ Table "public.dcomptable"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+-----------+-----------+----------+---------+----------+-------------+--------------+-------------
+ d1 | dcomptype | | | | extended | | |
Indexes:
"dcomptable_d1_key" UNIQUE CONSTRAINT, btree (d1)
Rules:
@@ -409,10 +409,10 @@ create rule silly as on delete to dcomptable do instead
update dcomptable set d1[1].r = d1[1].r - 1, d1[1].i = d1[1].i + 1
where d1[1].i > 0;
\d+ dcomptable
- Table "public.dcomptable"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------------+-----------+----------+---------+----------+--------------+-------------
- d1 | dcomptypea | | | | extended | |
+ Table "public.dcomptable"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------------+-----------+----------+---------+----------+-------------+--------------+-------------
+ d1 | dcomptypea | | | | extended | | |
Indexes:
"dcomptable_d1_key" UNIQUE CONSTRAINT, btree (d1)
Rules:
diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out
index d2c184f2cf..ab21907a49 100644
--- a/src/test/regress/expected/foreign_data.out
+++ b/src/test/regress/expected/foreign_data.out
@@ -1330,12 +1330,12 @@ CREATE TABLE pt1 (
CREATE FOREIGN TABLE ft2 () INHERITS (pt1)
SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value');
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Child tables: ft2
\d+ ft2
@@ -1351,12 +1351,12 @@ Inherits: pt1
DROP FOREIGN TABLE ft2;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
CREATE FOREIGN TABLE ft2 (
c1 integer NOT NULL,
@@ -1375,12 +1375,12 @@ FDW options: (delimiter ',', quote '"', "be quoted" 'value')
ALTER FOREIGN TABLE ft2 INHERIT pt1;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Child tables: ft2
\d+ ft2
@@ -1418,12 +1418,12 @@ Child tables: ct3,
ft3
\d+ ct3
- Table "public.ct3"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.ct3"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Inherits: ft2
\d+ ft3
@@ -1443,17 +1443,17 @@ ALTER TABLE pt1 ADD COLUMN c6 integer;
ALTER TABLE pt1 ADD COLUMN c7 integer NOT NULL;
ALTER TABLE pt1 ADD COLUMN c8 integer;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
- c4 | integer | | | | plain | |
- c5 | integer | | | 0 | plain | |
- c6 | integer | | | | plain | |
- c7 | integer | | not null | | plain | |
- c8 | integer | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
+ c4 | integer | | | | plain | | |
+ c5 | integer | | | 0 | plain | | |
+ c6 | integer | | | | plain | | |
+ c7 | integer | | not null | | plain | | |
+ c8 | integer | | | | plain | | |
Child tables: ft2
\d+ ft2
@@ -1475,17 +1475,17 @@ Child tables: ct3,
ft3
\d+ ct3
- Table "public.ct3"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
- c4 | integer | | | | plain | |
- c5 | integer | | | 0 | plain | |
- c6 | integer | | | | plain | |
- c7 | integer | | not null | | plain | |
- c8 | integer | | | | plain | |
+ Table "public.ct3"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
+ c4 | integer | | | | plain | | |
+ c5 | integer | | | 0 | plain | | |
+ c6 | integer | | | | plain | | |
+ c7 | integer | | not null | | plain | | |
+ c8 | integer | | | | plain | | |
Inherits: ft2
\d+ ft3
@@ -1517,17 +1517,17 @@ ALTER TABLE pt1 ALTER COLUMN c1 SET (n_distinct = 100);
ALTER TABLE pt1 ALTER COLUMN c8 SET STATISTICS -1;
ALTER TABLE pt1 ALTER COLUMN c8 SET STORAGE EXTERNAL;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
- c4 | integer | | | 0 | plain | |
- c5 | integer | | | | plain | |
- c6 | integer | | not null | | plain | |
- c7 | integer | | | | plain | |
- c8 | text | | | | external | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
+ c4 | integer | | | 0 | plain | | |
+ c5 | integer | | | | plain | | |
+ c6 | integer | | not null | | plain | | |
+ c7 | integer | | | | plain | | |
+ c8 | text | | | | external | | |
Child tables: ft2
\d+ ft2
@@ -1555,12 +1555,12 @@ ALTER TABLE pt1 DROP COLUMN c6;
ALTER TABLE pt1 DROP COLUMN c7;
ALTER TABLE pt1 DROP COLUMN c8;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Child tables: ft2
\d+ ft2
@@ -1592,12 +1592,12 @@ SELECT relname, conname, contype, conislocal, coninhcount, connoinherit
-- child does not inherit NO INHERIT constraints
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk1" CHECK (c1 > 0) NO INHERIT
"pt1chk2" CHECK (c2 <> ''::text)
@@ -1636,12 +1636,12 @@ ALTER FOREIGN TABLE ft2 ADD CONSTRAINT pt1chk2 CHECK (c2 <> '');
ALTER FOREIGN TABLE ft2 INHERIT pt1;
-- child does not inherit NO INHERIT constraints
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk1" CHECK (c1 > 0) NO INHERIT
"pt1chk2" CHECK (c2 <> ''::text)
@@ -1667,12 +1667,12 @@ ALTER TABLE pt1 DROP CONSTRAINT pt1chk2 CASCADE;
INSERT INTO pt1 VALUES (1, 'pt1'::text, '1994-01-01'::date);
ALTER TABLE pt1 ADD CONSTRAINT pt1chk3 CHECK (c2 <> '') NOT VALID;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk3" CHECK (c2 <> ''::text) NOT VALID
Child tables: ft2
@@ -1694,12 +1694,12 @@ Inherits: pt1
-- VALIDATE CONSTRAINT need do nothing on foreign tables
ALTER TABLE pt1 VALIDATE CONSTRAINT pt1chk3;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk3" CHECK (c2 <> ''::text)
Child tables: ft2
@@ -1721,12 +1721,12 @@ Inherits: pt1
-- OID system column
ALTER TABLE pt1 SET WITH OIDS;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk3" CHECK (c2 <> ''::text)
Child tables: ft2
@@ -1751,12 +1751,12 @@ ALTER TABLE ft2 SET WITHOUT OIDS; -- ERROR
ERROR: cannot drop inherited column "oid"
ALTER TABLE pt1 SET WITHOUT OIDS;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- c1 | integer | | not null | | plain | 10000 |
- c2 | text | | | | extended | |
- c3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ c1 | integer | | not null | | plain | | 10000 |
+ c2 | text | | | | extended | | |
+ c3 | date | | | | plain | | |
Check constraints:
"pt1chk3" CHECK (c2 <> ''::text)
Child tables: ft2
@@ -1782,12 +1782,12 @@ ALTER TABLE pt1 RENAME COLUMN c3 TO f3;
-- changes name of a constraint recursively
ALTER TABLE pt1 RENAME CONSTRAINT pt1chk3 TO f2_check;
\d+ pt1
- Table "public.pt1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- f1 | integer | | not null | | plain | 10000 |
- f2 | text | | | | extended | |
- f3 | date | | | | plain | |
+ Table "public.pt1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ f1 | integer | | not null | | plain | | 10000 |
+ f2 | text | | | | extended | | |
+ f3 | date | | | | plain | | |
Check constraints:
"f2_check" CHECK (f2 <> ''::text)
Child tables: ft2
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index c698faff2f..a147b8217f 100644
--- a/src/test/regress/expected/inherit.out
+++ b/src/test/regress/expected/inherit.out
@@ -1023,13 +1023,13 @@ ALTER TABLE inhts RENAME aa TO aaa; -- to be failed
ERROR: cannot rename inherited column "aa"
ALTER TABLE inhts RENAME d TO dd;
\d+ inhts
- Table "public.inhts"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- aa | integer | | | | plain | |
- b | integer | | | | plain | |
- c | integer | | | | plain | |
- dd | integer | | | | plain | |
+ Table "public.inhts"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ aa | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ c | integer | | | | plain | | |
+ dd | integer | | | | plain | | |
Inherits: inht1,
inhs1
@@ -1042,14 +1042,14 @@ NOTICE: merging multiple inherited definitions of column "aa"
NOTICE: merging multiple inherited definitions of column "b"
ALTER TABLE inht1 RENAME aa TO aaa;
\d+ inht4
- Table "public.inht4"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- aaa | integer | | | | plain | |
- b | integer | | | | plain | |
- x | integer | | | | plain | |
- y | integer | | | | plain | |
- z | integer | | | | plain | |
+ Table "public.inht4"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ aaa | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ x | integer | | | | plain | | |
+ y | integer | | | | plain | | |
+ z | integer | | | | plain | | |
Inherits: inht2,
inht3
@@ -1059,14 +1059,14 @@ ALTER TABLE inht1 RENAME aaa TO aaaa;
ALTER TABLE inht1 RENAME b TO bb; -- to be failed
ERROR: cannot rename inherited column "b"
\d+ inhts
- Table "public.inhts"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- aaaa | integer | | | | plain | |
- b | integer | | | | plain | |
- x | integer | | | | plain | |
- c | integer | | | | plain | |
- d | integer | | | | plain | |
+ Table "public.inhts"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ aaaa | integer | | | | plain | | |
+ b | integer | | | | plain | | |
+ x | integer | | | | plain | | |
+ c | integer | | | | plain | | |
+ d | integer | | | | plain | | |
Inherits: inht2,
inhs1
@@ -1106,33 +1106,33 @@ drop cascades to table inht4
CREATE TABLE test_constraints (id int, val1 varchar, val2 int, UNIQUE(val1, val2));
CREATE TABLE test_constraints_inh () INHERITS (test_constraints);
\d+ test_constraints
- Table "public.test_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+-------------------+-----------+----------+---------+----------+--------------+-------------
- id | integer | | | | plain | |
- val1 | character varying | | | | extended | |
- val2 | integer | | | | plain | |
+ Table "public.test_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+-------------------+-----------+----------+---------+----------+-------------+--------------+-------------
+ id | integer | | | | plain | | |
+ val1 | character varying | | | | extended | | |
+ val2 | integer | | | | plain | | |
Indexes:
"test_constraints_val1_val2_key" UNIQUE CONSTRAINT, btree (val1, val2)
Child tables: test_constraints_inh
ALTER TABLE ONLY test_constraints DROP CONSTRAINT test_constraints_val1_val2_key;
\d+ test_constraints
- Table "public.test_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+-------------------+-----------+----------+---------+----------+--------------+-------------
- id | integer | | | | plain | |
- val1 | character varying | | | | extended | |
- val2 | integer | | | | plain | |
+ Table "public.test_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+-------------------+-----------+----------+---------+----------+-------------+--------------+-------------
+ id | integer | | | | plain | | |
+ val1 | character varying | | | | extended | | |
+ val2 | integer | | | | plain | | |
Child tables: test_constraints_inh
\d+ test_constraints_inh
- Table "public.test_constraints_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+-------------------+-----------+----------+---------+----------+--------------+-------------
- id | integer | | | | plain | |
- val1 | character varying | | | | extended | |
- val2 | integer | | | | plain | |
+ Table "public.test_constraints_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+-------------------+-----------+----------+---------+----------+-------------+--------------+-------------
+ id | integer | | | | plain | | |
+ val1 | character varying | | | | extended | | |
+ val2 | integer | | | | plain | | |
Inherits: test_constraints
DROP TABLE test_constraints_inh;
@@ -1143,27 +1143,27 @@ CREATE TABLE test_ex_constraints (
);
CREATE TABLE test_ex_constraints_inh () INHERITS (test_ex_constraints);
\d+ test_ex_constraints
- Table "public.test_ex_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+--------+-----------+----------+---------+---------+--------------+-------------
- c | circle | | | | plain | |
+ Table "public.test_ex_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+--------+-----------+----------+---------+---------+-------------+--------------+-------------
+ c | circle | | | | plain | | |
Indexes:
"test_ex_constraints_c_excl" EXCLUDE USING gist (c WITH &&)
Child tables: test_ex_constraints_inh
ALTER TABLE test_ex_constraints DROP CONSTRAINT test_ex_constraints_c_excl;
\d+ test_ex_constraints
- Table "public.test_ex_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+--------+-----------+----------+---------+---------+--------------+-------------
- c | circle | | | | plain | |
+ Table "public.test_ex_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+--------+-----------+----------+---------+---------+-------------+--------------+-------------
+ c | circle | | | | plain | | |
Child tables: test_ex_constraints_inh
\d+ test_ex_constraints_inh
- Table "public.test_ex_constraints_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+--------+-----------+----------+---------+---------+--------------+-------------
- c | circle | | | | plain | |
+ Table "public.test_ex_constraints_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+--------+-----------+----------+---------+---------+-------------+--------------+-------------
+ c | circle | | | | plain | | |
Inherits: test_ex_constraints
DROP TABLE test_ex_constraints_inh;
@@ -1173,37 +1173,37 @@ CREATE TABLE test_primary_constraints(id int PRIMARY KEY);
CREATE TABLE test_foreign_constraints(id1 int REFERENCES test_primary_constraints(id));
CREATE TABLE test_foreign_constraints_inh () INHERITS (test_foreign_constraints);
\d+ test_primary_constraints
- Table "public.test_primary_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- id | integer | | not null | | plain | |
+ Table "public.test_primary_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ id | integer | | not null | | plain | | |
Indexes:
"test_primary_constraints_pkey" PRIMARY KEY, btree (id)
Referenced by:
TABLE "test_foreign_constraints" CONSTRAINT "test_foreign_constraints_id1_fkey" FOREIGN KEY (id1) REFERENCES test_primary_constraints(id)
\d+ test_foreign_constraints
- Table "public.test_foreign_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- id1 | integer | | | | plain | |
+ Table "public.test_foreign_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ id1 | integer | | | | plain | | |
Foreign-key constraints:
"test_foreign_constraints_id1_fkey" FOREIGN KEY (id1) REFERENCES test_primary_constraints(id)
Child tables: test_foreign_constraints_inh
ALTER TABLE test_foreign_constraints DROP CONSTRAINT test_foreign_constraints_id1_fkey;
\d+ test_foreign_constraints
- Table "public.test_foreign_constraints"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- id1 | integer | | | | plain | |
+ Table "public.test_foreign_constraints"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ id1 | integer | | | | plain | | |
Child tables: test_foreign_constraints_inh
\d+ test_foreign_constraints_inh
- Table "public.test_foreign_constraints_inh"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- id1 | integer | | | | plain | |
+ Table "public.test_foreign_constraints_inh"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ id1 | integer | | | | plain | | |
Inherits: test_foreign_constraints
DROP TABLE test_foreign_constraints_inh;
diff --git a/src/test/regress/expected/insert.out b/src/test/regress/expected/insert.out
index 7481bebd83..98de0c4cff 100644
--- a/src/test/regress/expected/insert.out
+++ b/src/test/regress/expected/insert.out
@@ -142,11 +142,11 @@ create rule irule3 as on insert to inserttest2 do also
insert into inserttest (f4[1].if1, f4[1].if2[2])
select new.f1, new.f2;
\d+ inserttest2
- Table "public.inserttest2"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+--------+-----------+----------+---------+----------+--------------+-------------
- f1 | bigint | | | | plain | |
- f2 | text | | | | extended | |
+ Table "public.inserttest2"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+--------+-----------+----------+---------+----------+-------------+--------------+-------------
+ f1 | bigint | | | | plain | | |
+ f2 | text | | | | extended | | |
Rules:
irule1 AS
ON INSERT TO inserttest2 DO INSERT INTO inserttest (f3.if2[1], f3.if2[2])
@@ -452,10 +452,10 @@ drop function dummy_hashint4(a int4, seed int8);
create table list_parted (a int) partition by list (a);
create table part_default partition of list_parted default;
\d+ part_default
- Table "public.part_default"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a | integer | | | | plain | |
+ Table "public.part_default"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ a | integer | | | | plain | | |
Partition of: list_parted DEFAULT
No partition constraint
@@ -767,74 +767,74 @@ Partitions: mcrparted1_lt_b FOR VALUES FROM (MINVALUE, MINVALUE) TO ('b', MINVAL
mcrparted8_ge_d FOR VALUES FROM ('d', MINVALUE) TO (MAXVALUE, MAXVALUE)
\d+ mcrparted1_lt_b
- Table "public.mcrparted1_lt_b"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted1_lt_b"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM (MINVALUE, MINVALUE) TO ('b', MINVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a < 'b'::text))
\d+ mcrparted2_b
- Table "public.mcrparted2_b"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted2_b"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('b', MINVALUE) TO ('c', MINVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'b'::text) AND (a < 'c'::text))
\d+ mcrparted3_c_to_common
- Table "public.mcrparted3_c_to_common"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted3_c_to_common"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('c', MINVALUE) TO ('common', MINVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'c'::text) AND (a < 'common'::text))
\d+ mcrparted4_common_lt_0
- Table "public.mcrparted4_common_lt_0"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted4_common_lt_0"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('common', MINVALUE) TO ('common', 0)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::text) AND (b < 0))
\d+ mcrparted5_common_0_to_10
- Table "public.mcrparted5_common_0_to_10"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted5_common_0_to_10"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('common', 0) TO ('common', 10)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::text) AND (b >= 0) AND (b < 10))
\d+ mcrparted6_common_ge_10
- Table "public.mcrparted6_common_ge_10"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted6_common_ge_10"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('common', 10) TO ('common', MAXVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::text) AND (b >= 10))
\d+ mcrparted7_gt_common_lt_d
- Table "public.mcrparted7_gt_common_lt_d"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted7_gt_common_lt_d"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('common', MAXVALUE) TO ('d', MINVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a > 'common'::text) AND (a < 'd'::text))
\d+ mcrparted8_ge_d
- Table "public.mcrparted8_ge_d"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.mcrparted8_ge_d"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: mcrparted FOR VALUES FROM ('d', MINVALUE) TO (MAXVALUE, MAXVALUE)
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'd'::text))
diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out
index b101331d69..f805e13d75 100644
--- a/src/test/regress/expected/publication.out
+++ b/src/test/regress/expected/publication.out
@@ -65,11 +65,11 @@ SELECT pubname, puballtables FROM pg_publication WHERE pubname = 'testpub_forall
(1 row)
\d+ testpub_tbl2
- Table "public.testpub_tbl2"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('testpub_tbl2_id_seq'::regclass) | plain | |
- data | text | | | | extended | |
+ Table "public.testpub_tbl2"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('testpub_tbl2_id_seq'::regclass) | plain | | |
+ data | text | | | | extended | | |
Indexes:
"testpub_tbl2_pkey" PRIMARY KEY, btree (id)
Publications:
@@ -141,22 +141,22 @@ ALTER PUBLICATION testpub_default SET TABLE testpub_tbl1;
ALTER PUBLICATION testpub_default ADD TABLE pub_test.testpub_nopk;
ALTER PUBLICATION testpib_ins_trunct ADD TABLE pub_test.testpub_nopk, testpub_tbl1;
\d+ pub_test.testpub_nopk
- Table "pub_test.testpub_nopk"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- foo | integer | | | | plain | |
- bar | integer | | | | plain | |
+ Table "pub_test.testpub_nopk"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ foo | integer | | | | plain | | |
+ bar | integer | | | | plain | | |
Publications:
"testpib_ins_trunct"
"testpub_default"
"testpub_fortbl"
\d+ testpub_tbl1
- Table "public.testpub_tbl1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | |
- data | text | | | | extended | |
+ Table "public.testpub_tbl1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | | |
+ data | text | | | | extended | | |
Indexes:
"testpub_tbl1_pkey" PRIMARY KEY, btree (id)
Publications:
@@ -178,11 +178,11 @@ ALTER PUBLICATION testpub_default DROP TABLE testpub_tbl1, pub_test.testpub_nopk
ALTER PUBLICATION testpub_default DROP TABLE pub_test.testpub_nopk;
ERROR: relation "testpub_nopk" is not part of the publication
\d+ testpub_tbl1
- Table "public.testpub_tbl1"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | |
- data | text | | | | extended | |
+ Table "public.testpub_tbl1"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | | |
+ data | text | | | | extended | | |
Indexes:
"testpub_tbl1_pkey" PRIMARY KEY, btree (id)
Publications:
diff --git a/src/test/regress/expected/replica_identity.out b/src/test/regress/expected/replica_identity.out
index 67c34a92a4..1526437bf8 100644
--- a/src/test/regress/expected/replica_identity.out
+++ b/src/test/regress/expected/replica_identity.out
@@ -158,13 +158,13 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass;
(1 row)
\d+ test_replica_identity
- Table "public.test_replica_identity"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------------------------------------------------+----------+--------------+-------------
- id | integer | | not null | nextval('test_replica_identity_id_seq'::regclass) | plain | |
- keya | text | | not null | | extended | |
- keyb | text | | not null | | extended | |
- nonkey | text | | | | extended | |
+ Table "public.test_replica_identity"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------------------------------------------------+----------+-------------+--------------+-------------
+ id | integer | | not null | nextval('test_replica_identity_id_seq'::regclass) | plain | | |
+ keya | text | | not null | | extended | | |
+ keyb | text | | not null | | extended | | |
+ nonkey | text | | | | extended | | |
Indexes:
"test_replica_identity_pkey" PRIMARY KEY, btree (id)
"test_replica_identity_expr" UNIQUE, btree (keya, keyb, (3))
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index f1c1b44d6f..e358ff539c 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2799,11 +2799,11 @@ select * from rules_log;
create rule r3 as on delete to rules_src do notify rules_src_deletion;
\d+ rules_src
- Table "public.rules_src"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- f1 | integer | | | | plain | |
- f2 | integer | | | | plain | |
+ Table "public.rules_src"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ f1 | integer | | | | plain | | |
+ f2 | integer | | | | plain | | |
Rules:
r1 AS
ON UPDATE TO rules_src DO INSERT INTO rules_log (f1, f2, tag) VALUES (old.f1,old.f2,'old'::text), (new.f1,new.f2,'new'::text)
@@ -2819,11 +2819,11 @@ Rules:
create rule r4 as on insert to rules_src do instead insert into rules_log AS trgt SELECT NEW.* RETURNING trgt.f1, trgt.f2;
create rule r5 as on update to rules_src do instead UPDATE rules_log AS trgt SET tag = 'updated' WHERE trgt.f1 = new.f1;
\d+ rules_src
- Table "public.rules_src"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- f1 | integer | | | | plain | |
- f2 | integer | | | | plain | |
+ Table "public.rules_src"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
+ f1 | integer | | | | plain | | |
+ f2 | integer | | | | plain | | |
Rules:
r1 AS
ON UPDATE TO rules_src DO INSERT INTO rules_log (f1, f2, tag) VALUES (old.f1,old.f2,'old'::text), (new.f1,new.f2,'new'::text)
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index e996640593..62b06da011 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -111,6 +111,8 @@ pg_authid|t
pg_cast|t
pg_class|t
pg_collation|t
+pg_compression|t
+pg_compression_opt|t
pg_constraint|t
pg_conversion|t
pg_database|t
diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out
index a4fe96112e..adbe764196 100644
--- a/src/test/regress/expected/update.out
+++ b/src/test/regress/expected/update.out
@@ -221,11 +221,11 @@ update range_parted set b = b + 1 where b = 10;
-- Creating default partition for range
create table part_def partition of range_parted default;
\d+ part_def
- Table "public.part_def"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+----------+--------------+-------------
- a | text | | | | extended | |
- b | integer | | | | plain | |
+ Table "public.part_def"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
+ a | text | | | | extended | | |
+ b | integer | | | | plain | | |
Partition of: range_parted DEFAULT
Partition constraint: (NOT (((a = 'a'::text) AND (b >= 1) AND (b < 10)) OR ((a = 'a'::text) AND (b >= 10) AND (b < 20)) OR ((a = 'b'::text) AND (b >= 1) AND (b < 10)) OR ((a = 'b'::text) AND (b >= 10) AND (b < 20))))
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 1a3ac4c1f9..a69c485e8c 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -30,7 +30,7 @@ test: point lseg line box path polygon circle date time timetz timestamp timesta
# geometry depends on point, lseg, box, path, polygon and circle
# horology depends on interval, timetz, timestamp, timestamptz, reltime and abstime
# ----------
-test: geometry horology regex oidjoins type_sanity opr_sanity misc_sanity comments expressions
+test: geometry horology regex oidjoins type_sanity opr_sanity misc_sanity comments expressions create_cm
# ----------
# These four each depend on the previous one
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index a205e5d05c..e72015a391 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -43,6 +43,7 @@ test: inet
test: macaddr
test: macaddr8
test: tstypes
+test: create_cm
test: geometry
test: horology
test: regex
diff --git a/src/test/regress/sql/create_cm.sql b/src/test/regress/sql/create_cm.sql
new file mode 100644
index 0000000000..6bf0ea8267
--- /dev/null
+++ b/src/test/regress/sql/create_cm.sql
@@ -0,0 +1,62 @@
+-- test drop
+CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
+CREATE TABLE droptest(fts tsvector COMPRESSED ts1);
+DROP COMPRESSION METHOD ts1;
+DROP COMPRESSION METHOD ts1 CASCADE;
+\d+ droptest
+DROP TABLE droptest;
+
+CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
+CREATE TABLE cmtest(fts tsvector COMPRESSED ts1);
+SELECT * FROM pg_compression;
+SELECT cmhandler, cmoptions FROM pg_compression_opt;
+
+\dCM
+\d+ cmtest
+
+INSERT INTO cmtest
+ SELECT to_tsvector(string_agg(repeat(substr(i::text,1,1), i), ' '))
+ FROM generate_series(1,200) i;
+
+SELECT length(fts) FROM cmtest;
+SELECT length(fts) FROM cmtest;
+
+-- check ALTER commands
+ALTER TABLE cmtest ALTER COLUMN fts SET NOT COMPRESSED;
+\d+ cmtest
+ALTER TABLE cmtest ALTER COLUMN fts SET COMPRESSED ts1 WITH (format 'lz');
+ALTER TABLE cmtest ALTER COLUMN fts SET COMPRESSED ts1;
+\d+ cmtest
+
+-- create different types of tables
+SELECT * INTO cmtest2 FROM cmtest;
+CREATE TABLE cmtest3 (LIKE cmtest);
+CREATE TABLE cmtest4(fts tsvector, a int) INHERITS (cmtest);
+CREATE TABLE cmtest5(fts tsvector);
+CREATE TABLE cmtest6(fts tsvector);
+INSERT INTO cmtest6 SELECT * FROM cmtest;
+
+-- we update usual datum with compressed datum
+INSERT INTO cmtest5
+ SELECT to_tsvector(string_agg(repeat(substr(i::text,1,1), i), ' '))
+ FROM generate_series(1,200) i;
+UPDATE cmtest5 SET fts = cmtest.fts FROM cmtest;
+
+\d+ cmtest3
+\d+ cmtest4
+DROP TABLE cmtest CASCADE;
+DROP TABLE cmtest3;
+
+SELECT cmhandler, cmoptions FROM pg_compression_opt;
+
+DROP COMPRESSION METHOD ts1 CASCADE;
+SELECT * FROM pg_compression;
+SELECT * FROM pg_compression_opt;
+
+-- check that moved tuples still can be decompressed
+SELECT length(fts) FROM cmtest2;
+SELECT length(fts) FROM cmtest5;
+SELECT length(fts) FROM cmtest6;
+DROP TABLE cmtest2;
+DROP TABLE cmtest5;
+DROP TABLE cmtest6;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index b422050a92..2fe9d7d434 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -340,6 +340,7 @@ CollectedCommand
CollectedCommandType
ColorTrgm
ColorTrgmInfo
+ColumnCompression
ColumnCompareData
ColumnDef
ColumnIOData
@@ -363,6 +364,8 @@ CompositeIOData
CompositeTypeStmt
CompoundAffixFlag
CompressionAlgorithm
+CompressionMethodOptions
+CompressionMethodRoutine
CompressorState
ConditionVariable
ConditionalStack
On 11/28/2017 02:29 PM, Ildus Kurbangaliev wrote:
On Mon, 27 Nov 2017 18:20:12 +0100
Tomas Vondra <tomas.vondra@2ndquadrant.com> wrote:I guess the trick might be -DRANDOMIZE_ALLOCATED_MEMORY (I first
tried without it, and it seemed working fine). If that's the case,
I bet there is a palloc that should have been palloc0, or something
like that.Thanks, that was it. I've been able to reproduce this bug. The
attached patch should fix this bug and I've also added recompression
when tuples moved to the relation with the compressed attribute.
I've done many tests with fulltext search on the mail archive, using
different compression algorithm, and this time it worked fine. So I can
confirm v7 fixes the issue.
Let me elaborate a bit about the benchmarking I did. I realize the patch
is meant to provide only an "API" for custom compression methods, and so
benchmarking of existing general-purpose algorithms (replacing the
built-in pglz) may seem a bit irrelevant. But I'll draw some conclusions
from that, so please bear with me. Or just skip the next section.
------------------ benchmark / start ------------------
I was curious how much better we could do than the built-in compression,
so I've whipped together a bunch of extensions for a few common
general-purpose compression algorithms (lz4, gz, bz2, zstd, brotli and
snappy), loaded the community mailing list archives using "archie" [1]
and ran a bunch of real-world full-text queries on it. I've used
"default" (or "medium") compression levels for all algorithms.
For the loads, the results look like this:
seconds size
-------------------------
pglz 1631 9786
zstd 1844 7102
lz4 1582 9537
bz2 2382 7670
gz 1703 7067
snappy 1587 12288
brotli 10973 6180
According to those results the algorithms seem quite comparable, with
the exception of snappy and brotli. Snappy supposedly aims for fast
compression and not compression ratio, but it's about as fast as the
other algorithms and compression ratio is almost 2x worse. Brotli is
much slower, although it gets better compression ratio.
For the queries, I ran about 33k of real-world queries (executed on the
community mailing lists in the past). Firstly, a simple
-- unsorted
SELECT COUNT(id) FROM messages WHERE body_tsvector @@ $1::tsquery
and then
-- sorted
SELECT id FROM messages WHERE body_tsvector @@ $1::tsquery
ORDER BY ts_rank(body_tsvector, $1::tsquery) DESC LIMIT 100;
Attached are 4 different charts, plotting pglz on x-axis and the other
algorithms on y-axis (so below diagonal => new algorithm is faster,
above diagonal => pglz is faster). I did this on two different machines,
one with only 8GB of RAM (so the dataset does not fit) and one much
larger (so everything fits into RAM).
I'm actually surprised how well the built-in pglz compression fares,
both on compression ratio and (de)compression speed. There is a bit of
noise for the fastest queries, when the alternative algorithms perform
better in non-trivial number of cases.
I suspect those cases may be due to not implementing anything like
PGLZ_strategy_default->min_comp_rate (requiring 25% size reduction), but
I'm not sure about it.
For more expensive queries, pglz pretty much wins. Of course, increasing
compression level might change the results a bit, but it will also make
the data loads slower.
------------------ benchmark / end ------------------
While the results may look differently for other datasets, my conclusion
is that it's unlikely we'll find another general-purpose algorithm
beating pglz (enough for people to switch to it, as they'll need to
worry about testing, deployment of extensions etc).
That doesn't necessarily mean supporting custom compression algorithms
is pointless, of course, but I think people will be much more interested
in exploiting known features of the data (instead of treating the values
as opaque arrays of bytes).
For example, I see the patch implements a special compression method for
tsvector values (used in the tests), exploiting from knowledge of
internal structure. I haven't tested if that is an improvement (either
in compression/decompression speed or compression ratio), though.
I can imagine other interesting use cases - for example values in JSONB
columns often use the same "schema" (keys, nesting, ...), so can I
imagine building a "dictionary" of JSON keys for the whole column ...
Ildus, is this a use case you've been aiming for, or were you aiming to
use the new API in a different way?
I wonder if the patch can be improved to handle this use case better.
For example, it requires knowledge the actual data type, instead of
treating it as opaque varlena / byte array. I see tsvector compression
does that by checking typeid in the handler.
But that fails for example with this example
db=# create domain x as tsvector;
CREATE DOMAIN
db=# create table t (a x compressed ts1);
ERROR: unexpected type 28198672 for tsvector compression handler
which means it's a few brick shy to properly support domains. But I
wonder if this should be instead specified in CREATE COMPRESSION METHOD
instead. I mean, something like
CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler
TYPE tsvector;
When type is no specified, it applies to all varlena values. Otherwise
only to that type. Also, why not to allow setting the compression as the
default method for a data type, e.g.
CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler
TYPE tsvector DEFAULT;
would automatically add 'COMPRESSED ts1' to all tsvector columns in new
CREATE TABLE commands.
BTW do you expect the tsvector compression to be generally useful, or is
it meant to be used only by the tests? If generally useful, perhaps it
should be created in pg_compression by default. If only for tests, maybe
it should be implemented in an extension in contrib (thus also serving
as example how to implement new methods).
I haven't thought about the JSONB use case very much, but I suppose that
could be done using the configure/drop methods. I mean, allocating the
dictionary somewhere (e.g. in a table created by an extension?). The
configure method gets the Form_pg_attribute record, so that should be
enough I guess.
But the patch is not testing those two methods at all, which seems like
something that needs to be addresses before commit. I don't expect a
full-fledged JSONB compression extension, but something simple that
actually exercises those methods in a meaningful way.
Similarly for the compression options - we need to test that the WITH
part is handled correctly (tsvector does not provide configure method).
Which reminds me I'm confused by pg_compression_opt. Consider this:
CREATE COMPRESSION METHOD ts1 HANDLER tsvector_compression_handler;
CREATE TABLE t (a tsvector COMPRESSED ts1);
db=# select * from pg_compression_opt ;
cmoptoid | cmname | cmhandler | cmoptions
----------+--------+------------------------------+-----------
28198689 | ts1 | tsvector_compression_handler |
(1 row)
DROP TABLE t;
db=# select * from pg_compression_opt ;
cmoptoid | cmname | cmhandler | cmoptions
----------+--------+------------------------------+-----------
28198689 | ts1 | tsvector_compression_handler |
(1 row)
db=# DROP COMPRESSION METHOD ts1;
ERROR: cannot drop compression method ts1 because other objects
depend on it
DETAIL: compression options for ts1 depends on compression method
ts1
HINT: Use DROP ... CASCADE to drop the dependent objects too.
I believe the pg_compression_opt is actually linked to pg_attribute, in
which case it should include (attrelid,attnum), and should be dropped
when the table is dropped.
I suppose it was done this way to work around the lack of recompression
(i.e. the compressed value might have ended in other table), but that is
no longer true.
A few more comments:
1) The patch makes optionListToArray (in foreigncmds.c) non-static, but
it's not used anywhere. So this seems like change that is no longer
necessary.
2) I see we add two un-reserved keywords in gram.y - COMPRESSION and
COMPRESSED. Perhaps COMPRESSION would be enough? I mean, we could do
CREATE TABLE t (c TEXT COMPRESSION cm1);
ALTER ... SET COMPRESSION name ...
ALTER ... SET COMPRESSION none;
Although I agree the "SET COMPRESSION none" is a bit strange.
3) heap_prepare_insert uses this chunk of code
+ else if (HeapTupleHasExternal(tup)
+ || RelationGetDescr(relation)->tdflags & TD_ATTR_CUSTOM_COMPRESSED
+ || HeapTupleHasCustomCompressed(tup)
+ || tup->t_len > TOAST_TUPLE_THRESHOLD)
Shouldn't that be rather
+ else if (HeapTupleHasExternal(tup)
+ || (RelationGetDescr(relation)->tdflags & TD_ATTR_CUSTOM_COMPRESSED
+ && HeapTupleHasCustomCompressed(tup))
+ || tup->t_len > TOAST_TUPLE_THRESHOLD)
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
data-sorted-exceeds-ram.pngimage/png; name=data-sorted-exceeds-ram.pngDownload
�PNG
IHDR � � �W�3 bKGD � � ����� IDATx���w`SU����H��HAJK�dXVKYe��C�"�RE��>��dIJ�(����a���U�@i�����5�t��I�M���EOO��e���g�DQ `]Nr ��H� ��� �H� ��� �H� ��� �H� ��� �H� ��� �H� ��� �H� ��� �H� ��� �H� ��� �H� ��� �H� ��� �H� ��� �H� ��� BnnnXX��Q ����
������qc�5N�:%�~�~��U���[7�F���[�����)S���������(�g��9�����.��(���dgg<xp�� 3 ��d�__�J�*���3g�DDD�����D�TmExx�����2���n���e�5jt�����~*e5�������������[�V�_|q���{���'#���7n��SO�/_��IU�V:t����g�}�������V�Z���F����Nzz��9s�7o^�V�&M�L�<����,����U������u-��YSwiOO�#G������y���[����o��=!!��W)��}��w�^a������o<|�0�>�>}z��Mk����]�%K�h�Z�o��[W�b�2e�l��������n�2e�T�����,��[���U�J__�3f����o���[�B���~���!11����.W�\@@��K�A����j�����^�f�����;���[I�s�x��������E+�i����^���e���{�_�~c�����c�f�������n��6lNJJ�tHzm������I�7�u�2e���QC_�k��qqq��2o?�������_~��s������W�2e�~���k������������������c|,�O�~���r�����i���k�N��=�-%��jC��m��[o����-����;��H��Hg������}��2e����?����B�����`����r��/�����ks��0`�a`%��B��)�?n��������EQ����)S<==���K�j_~���9s�{���!C����5h����Jl�������}���;wDQ������
�X�bjj�����?��3��333������#���h�=*Q�s��u����������I������edd,\��i�����Q���{����.]z��������/�?>$$�����:.�2eJ�-���������?"""&&��g�3g������3�����q���o���Q��={�>}��Db��y�~s��Q�F��m!&�S�Z�j�����<v�X�J���h4���l1����j��i�k�
)7UQr�1��������O-[����4��y��DF����%&&��[������>����C��h��6m��<y�������4���<y�j��YYYF��q�����
�w��������K��y���<RF%"ed������Q�e�W��[3� Jb�������{�������;v4���L�s�����Y�f����P�R�e���h�B������#F�Z�j���U�TA�V8p����=�W+W�\���l�R�J���������4�477�Y�f���K�7o�j��a��}�����M{���U*������~n�u���\������%111]�t�X����$����������w�����c��III��W�N~����G������]��K���w��mT�l������
��~S5"�����4m�411����'N�(}����*��]�vo����
�_{�������X}�'�|��Y�������0�p��q���)SF�@d�M��x{{8p����m��wK)C��������P;��}���iS�re}I�v��_�~��M��$el��+W6lhX�R�f����/��{��%''�P�^�a���2��Z���o���o8_W�����?��,��7h��[^�ti��MK��G�4h�����q�"j������w�U�V���{��������Z�w�����d�oo����l��pn��
�����nS�N�����7�����/Y�D�oQW�Z5y�����oT�Xq��5#G��Q�Fxx����������Q��O>1���������^�z�>�lPP���+����7{���>}������Q�Q�F�~���E����R-##�?������T�z�
|��G�K��9��S��5k6n�x������9rd��U����)���G�GAX�~���c�3TK��)iw6������>|8$$���' ���.\�_���;������m���;�������Z���W�^*��f��|���p���5j�(S�����[�����������kX��M���cH�a��U�Pa����U������MJJ2��]�n��K�����O=z�������w||�o�����V�V���D����s��2~�x___�c���&�g��?�E�-Z��})��{w�N��W��������%���Wt��)[����K������rss=<<K���>���7�x�������+�����wJ���>�RH� ��||��'U�T�>}��+Wz��U�~}??�������9r���������w����U�r�Jll��1c
�`�wK�����Mu������7ae�Js��2��8���Y��@It��%�D�J�j������M��#�;;g��h��eFFF��gFGG/X����%]�TXk3g�,�"�NX-b��(�_~�e����?������{����m��X������xy�����������M��%���/l��e�V�\���n�b^^^�f�t��:��gO��W~���>>>�6m��������4i���|��M�?��C_s�����5;r�HNNN^^^RR����CCC333
�~�z�5�o�~��}]5��.��^-%%e����O�����������&O�����5k���N�<�������+V��W/88�h�y��?�����E�M�6+W�3f���
�b5a�R)?o�[��Q�F�V������s��ZmVV���{���gX����-]NKKE�������
6�w��QlR�i������c�_|��[o��,v��Q�F��_~y����n�*�����;wta���KYM�L{����?��q���}�]NNNNN���G_x���C���N,2d���G
K$�����[�n
6l��=�'O�������jW�^���_����=�xS-���e���������S��;���Jd��V����m�������W���O���j��o����������c)����?�w�n��]������OQ���.\l��=�������;p�@�q�^��N�:��>��qS5����jNN������j���n)e�c�w�b�q�RI��=��3� ���^����X����z�h��j:���!)c{V�ZU�~��S�n�������C�����_V�P����������z���
kMbR���t�9���X-)s���^x�h����������������������w/!!!,,l��Z�V_�A��/^�%$$�����111���E�[�n�o��%e�.]��wFFF���D�O��u�V��/^���q��+r���g��"66V���;v���R���KII
2,�r�J���>|hX�f�A�2f
R��u�i���(>x��i�����~~~yyyZ�����7��{������?���=R�\�7��I��Q�v�����^�z�^�z<�����x�����_��_�~������i����>�V��I�zr�<~�8..n���-Z�x��
�)���������������|z/_���s����G���U��������j�����^��D���o���.S�0C�^x����6l���T"����UK�V;;;6��j�!!!����0a� �2d��eI�A�/��Qw����m[��npp���j��illl��
����{���G�����_���R"n�z6qSm������[���3��R�0�1a�Q,�&e��|uW`Rf���F�cQ����~�z��I����K�g���?��C��O�:��+�n��1��_�����/_^�pa�/Zlk*�*//������w�=�h�ww��S���������?������;h���3g�_�����~'tH����KVV�Qa��}������o�n������|���U�T������X�lY}H�������P���cg���i�&��;$$$##c����V�JHH�M_�z�����%VEq���l��Y�f��
�t�R����v��8q��������� �?)�T���O��v���B�
�<��������g��Vg�������C��m���V��$z������
Kj����cG�Y 'O�
j����G�q�����t������/�X�r�����Kv����M�zr�xxxt����?<q���M�&M�t���[a���������Y�M��w��]������t//��3g�pE�=Ko���s��U�T6h���D7U�]F"����^������g\\��U��M������STT��W4X�o���� I�����iS�ZmX���k��F���?�9s��C����Wlx�o�.b�}�-%��Z"��T�z�)��+�x�,v�������������9�!V999F�2�����O�I������s�>����G��r��W_}UX�����������_�c��EEE�����{��?�������~{�����7h����� �M�8����W�^�q�F�^����+�o�
4(�u���_�3sqq��������)S
�Kl.����7o�o>���� ddd�kzzz&$$�������?x���#
��RJ5QG��n��q�����/11q���/��b�����cT���l�F��)H���������.B���������o�����[�Z�����;�[�?�����`U*U�z��[*�����7O�k� ���l��7�x���>|���kXR�N����_~����O���������?�� W�����n�\�i�n�%�2zyyyiii�GP:���999U�V�C�{������:��N��5+U�d�M��xS5T�I���y����'<==���'�~��m�n)7U�(�M�r����-���<V}12*�5���]���o��mTx���g�}��j(I[���3x�`����z�O�>�v�*�Q���/�����aaaRv#OJJ���?K�Z��mw��]�Q��V�Z������_�x���� E�P����k�j��_����u������j7m�T`�g���,�����1K8g������}���??b�������7...::���e���;����c���m�>��S�=�\���N�:g��5*E������S(�3�<s���~�Aw���2�sa�7�Z+Z���0kV�w��/��][�����-[zzz>|X����������� s��1Jj����/���7��?����+�]����M�VI{��zOM��
����O?����_��k����rvv����Y�*=s�T�����D��+�y���������J6R�J�����[[�[J�M��k�~S�w�^��UM�J�L�[J�Xm�Q��H
�}u7���k����%�(������&TCH����������{����{��Nff�i�Z�6m<==?��s�:�f������H���W�^�?�X'33s��U�C�R-^����G���5��4,�Y�|yPPP��Iyyy���kV8n���_��aC����4(--���|��]�t1�j"��o����Y3�?�F�Az�������h��e��m���$������o������ AAA����K��{v������������7��������?$$D�c�-~������l��% @���������������|�M"�����={���~������s���h����m
��|����;�������uUt#R>��z��������j�=����Z���0j����F}Pz�*���&�Tu�w����I�&��7OJ<����?��_����4/�>J���I�RU�T)>>^J^�I�&E��������PPP�ON*n�F�)�������X�����<J}��Ww�OLL4�5=z�Z�jF_h%V�7n����5W�)I�������������gdd�X���O>�3g����T���Wo�����^�gp������
*4i�D�����;i��:����yyyyyy�O�����+��b�H�jutt��1c�3Q]\\6n�8v���?�Xw/�v��� ����h�"+�g��g�}��w���cccu&u��x���={������h�"����������[����>)���i�q�F��>�������S�N���/�b����:ujhh�nO�����>�l����;w��S����'**J�vI''���[����7)S���7K
����n�����������/[�L?W���}��u�����e��`�|���-�����S�~��U+''����09�:���/))�{������2e�n����1�{���i�����L�R�j������������s������o������O�����o�z�� �V��ju��t�����-Z������?|JJ���[���k4����J�,�iin��vQ���v��u��)88��Kz�������/�9���/������ci^�|�F��Y�jU�y�:\�x���[�V�w�7n\�r%""����8�T���������sss[�je���+��R��G������1s���_~�����M~5j��L��w���N�:=�����Xta�Wnn�G}t���q��5i��v���=��� ����
�R��~csCe������t�R�y
���F��T�2s��������n���;���������IG���������e�����h���-[�Z���5|"�}�
�<y�k��~~~5j������3g��;���V�vm�C����y������aTm������<y�3�<�q�F}Ijj���S��������m�t�R�f����������
��>�����|��U�T1<��h��O�T�R���?��C]���K``���9s��v�����G7j������g�
_�f����F�:����c{��Q�N�z���=���KF��X�����Z��]�v�z��������������m���������=z���wG����4�S�H7��}�����/���_~���1����._�|�z�t����j�����^�z���g��;���[I�s��n�����������o�������������������ys��1�?������M���_�!,%�����NNN^�E@�?�F�
(����7h��r��-Z���+J�����)S���[7 ((h��AQQQF��H�������u�]�v��U[�n�o��s���?�n����7���k����o����1d��j��u�b{��[�4h������U�jU�����>*����=-��Z�.S�L___]�u����'�a}��S�O���������
:4�!�c���U�z/^,��4b��e������>|�������+���������\��b��������g�yF�R��cd�[�nF�����r������Fg �7U=��TG���ks���n)J��I}K��H'**JWn8����uJ�������5�K�7m���Y��5k�o����Vb���$A���oZ��J%*r 0A��=���S�9���}��g�}c��4M����;n:��~�m��Y������������4h���W�T�������
��HipSU�b�����������3~~~�k��-s�����?M>v�.�� ���N�>]���K.\�x��Zsvv����(�y�S(VNN������/_���+i��������;-�P����#G���l,#�T����>::�{��dd`5G�3f#� l^^^������W�3f�vHl�/���V�u������k���j���B�����V�Fb�F9T*��e����/w �*�g=z����Gw\�qS�����������o���7nX[O�c� [u���.]�����T��������}�c��� IDAT�,k��)�i����NNN�*U��c�x�������J�Z�n��+�=,��ee�T��� �X� �2 2 ) �2 2 ) �2 2 ) �2 2 ) �2 2 ) �2 2 ) �2 2 ) �2 2 ) �2 2 ) �2 2 ) %&e���w��1h��y��Xa���!!!���'N�����o �EqIQ'O�|����#G�����p�����7'$$>|�U�V�&M��o �HqI�J=m���5kXa�������� �y�������[ 3R\R�XYYY>>>����N�:e�� �����LVVV�2eK�}��k��Y��Epuu���-�� V%���!���LNN�Q�D�VgeeY��E���U���X*���^.y����{ ��fr#&<������l�����n��K�`iZ�t��O��l���[�A��������;��/as_��?�����:u���7M��ll�����Q�D����)��- �9;v��:ujBB������2e�����1,IJJ�o l�� ��s��I�&���7h�@�X
fcIA\\\n����1!!��^��oa����;���7`+\���0Kk&7b�m�i[l�����n��K�`iZ��+�����w+7H�6���wg7�%l��|����k��������y����� <�����W�XaT��w�}���[�nuvv��k������]k�����U0�ptv�q��{�o��Q�F����I�&�e�w�m����7t��G�=~����k�/_a���/����B�6mn����cGgg���k������o ������5��o��gdK��"���d K����8�;`���?r��o����_4,Wf��=e ����9rdll�QFF�H� �T6�� ����A�IBB��#�����Y3�c�J��wl�2g@ �880x���;w�i���
��+1&���� q����}�~��Wm��-��2�,_ ����c}����m[�") l��������u��v����)H� ��������;&&&$$D�XLDR�T�����; ���'z���e�������DFF����M:%�sc[��W �������w��aC��]%>D��wf� �q����_~y����32�ER ����Ow��5**�[�nr�b$e �
8s�Lxx��5k�w�.w,��"w �t��f���TM^E���������6��_�������{��!w,fCR `~��5���4ZQ��m��4�(��!/S�={�S�N+W���������� ���R�.#�����/��l�������W�X1`� �c13�2 ��;SkTr?_ P�������-^�x���r�b~$e ����lTR)_ P�.t��a��E��;� ) 0�~jg��G��j@������\�x1,,l���C��;Ka�_ ����Q�*����`N_�d�.]
{����*w,�E��Z(�J�k ��\�|9$$d��y�jS��w%�d[��� `��]����o���kflV��w�� �p������3f�7#�X�) @�����H��I ����L�>}��qr�b%J��c[�9
�u��5k��i���e[�������`�n��2e�� &X�}e�Y� @��]J�gdA�h����e���$%%���L�<�B�")c�DFF� `'������W��22c���8q�y[���������(q��mQ�( ��M>r�v�Y��=�W���+{��=���;wBBB�>k�,�^H��wf� P�~j��s[���!c<v�x�&�lZJ�67OL��F�M;���;(8�;w����:�����% J��G-��I��6�C��P
�����aw��m������g��-w,�!) @�ZWS+0�!��dM�?�G�&��DAi�6��\RRR��o?`��9s����X� �g�Gy�9�T�W��.#��_�y�����H� ����$6������v���K�.o��������K `~�n�F�G)p�l������:���.Z�H�XA�'B�e�� @^��5Q��)#��Y���=e k�ed��m�d��_]��wf� ��1 0�����;�n�Z���b)1Qd[��l @!t��^zi���r����;� Ky��Q�N�Z�l)cFF���(�-�L� ��������u��Y�F�R� �Q����2 ������v�Z�N�32�ER �YFFF�n�j��MF�$e �9�22~~~k��ur"�P(^ `6�����w�Y�fTT��)q������ �������{W�\y��u���(s����l�2�W �,;;�w���7ovqq�;�'(s���� �Q���}��qwwW`FF�H�������r� ��egg���W�Vo��E9���H��]�X
���;�E�3� �����~�����n��U9#��3S �(''g��...111���(� 0�V�>|xvv��]�\]]����� %��j�
������w�-[V�plI P2�92���ddJ�� (�V;r��{�����G�V��
#) ���M�������j�u=ZWc�3�j������;wbcc����O��-�<U ��;��Y{.M������Y5&���Ch�J^^�+��r�����X777��)e�9 ���K���� ���R������8~�������������b�| p\,o�cgj�J��+��ed~�����8����� ��dM����mn�����>�v,Y#wP0o7g��J�J �DQ�0a��s����<==��~�� �[�[� ��J���Y5 ��
0�(�'N<s��c� ��X�b�Z��E�`�<-�*��6��<7�|�#�O9��.��4���Ea9�(N�4���S���^^^r�coH� ����v�Y�������V�'(�T������rA���1v�Y�JT��1��f��u�������/ ��-�t��fB��!q)���v<� ��/�22:�Y���/�Ji��Y<p�@�����>1S pPr-o���5���t����IV�B+G�'�n�+���={vBB�����XI �qYy��(p�d�25*�/��0,�^��_5g������V�TI�X��� ���&�@�t��Q�����(��^��2����7o�������;;GR J�;���l�,����k�4�������[�}M��������������dd�C%�b��P8��� p8��5Q��)#��Y�����`�"##�m����X�jU�c13e��S J�m�a>����[�9r��22���D�mQ��=Dp�������� �)�,Y�����3����DFF���;�� !)SZ�� �D��-[�fMbbb�j����R�9xg� �k����W�>r��gd��� pP+V������{���1S G�j��+V9r���G�X3e p8QQQ��-KLL����;�ER �����'&&��UK�X�� p ���{�����(3e p�����w>���'w,`� �a��
s��=t�������@A%���1�6��� �t��m�6m����{�9�c��2�,_ ��}��W��M;p��cfd�� �l���S�NMHH�_����� �) ����c����4h w,0�L ���]�&M������
�L E9zS��Bz�&���i@]����rG@���w���kqqq���r���)q�a���
� �8��Y{.M���o�jg��@�V>�e ����{��W�����M���"(s���% ��v)]��A��_J�1 R�����W^���o��(I ��;SkTr?_ E���9rdll��/�(w,({� (������'�0����
&cc ����0b��={�4k�L�XP<�2 �5����_�G��{���7$���dM�?obJ�6�l�J��.8p`���_�u�������� ,�vG��|��J0L'�H: znd?�����s��-Z��") X�M��[WS���������G��}��6m��J�� `qG�6��
g���2�l~|j�6#����3h����]:��������[��k'w,(N_ ,�;�8���x�&�lZJ�67O�-q:���V��g�<��*���(�J��cs_�rr_}xbl��[,t-�?~�O�>111!!!r��#) X��Qq�K���Z+���^���]�T���G7�
�����g��9�~n����g�[�Z���������-[BCC���`� `qR��e�X��6�MO2.��a��:�'N�����/�h������D��1�?"##��
�������]*�]��X�'@�\�k�xT�%�w���^�zm��!<<\�X�+22R?`�;���DQ,�
�R� ��%k��96[��*"���L K��s����L*W��-������9}�txx���k�w�.w,6C��w�/ � e��pn������Kdd����3���k��!#c��(�-�L� ���3g�t��z���={���Q���=e ����kxx�g�}FF�n�| �;{�l�N�V�X���/����2 (��������/_>p�@�c�9�� @�.\��h��A��5���� @�.^����2D�X`~�) �PGojO�n�)��]�t�}��|����C��AR @��'k���i�� )���i*Qh�C^��K�.�����7o��ar�Ka� ��m������h���K�2�8�k��u��q���r�") �`gj�J��+`v��] y��7��+w,�,�/ ���b���o�?�����l� Gt����������7N�X`q�� ��dM����mn���/�X���N� ��J���Y5 ���a����!!!��M?~����H� �H�~1�|��zUvwvqRUvw��+�]~��ed�L���������K �#��_L�jj��� )))$$$""b���r��a� ������~1�\�����c���G����r��") 8"�����;�����
�5k�����X� 8�V>jQ%���2%�����:x����zK�X �(���B�T*^C X[��YP>]Ff���s���;�����c�-�|_ `��'k��K����vV� �j��H�MIII
�����o�-w,A��w�� l����(��{���o��O22�� `c$�f
@�<x��s���;GFF�dFR �1�f
��t���]�v�/�;��� `c8��Q>���c�6m�,Y"w,P�� L!��G�f
�"]F&88x���r��P����E�8 ��8�@�<z��c��-Z�X�b���8(e�Y� ���.==�{���5Z�|���@YH� %��G $JOO���k��uW�^�R�� {� %���|;��,L���� yeddt���v��ddP f� %&��������i)��<1%C}6�X��Za��.#����v�Z''F�( ��Z��G7����������<��Wp!������222�w����EF���a�DFF� ��u5�'!���T�$���I�>8����=z��Q#::����"##�v�c)�O��-�<U
1��}��g��p^��[�x XZVVV�>}<==7o���\�nS�2e��� $}� v ;;�o���6m"#�bq� `A�|��J0<})��� ;��������7��0�F��8{��(s ������������u�V22
���;�� (����������������t$e fsq����.~V����.^��@�p�A��qrr��u���������� ������co��� ��v5;q�MA��3���qY�V�>|xVV���������) 0����edtr��~�G�x,M��y��������-+w8�=�� �������%vC���1����_�5��� `��75�g]�.�Y�fo�2��/g?QR�>W�h���#G������G����D$e SO�D�K�hEAR2��g�T��������
���r4{����d�
&�f�=-oH����7j��;w���A)�� `�m��u �V�~)��
O�D�MK������T��dMi� J����!k�y��qrUy�� YS�� {��W�q�����_�w�vss�;�6f� ��;SkTr?_�,0�S� ;@)�R���
rGa)�(�?��?��������;�<f� ��vs6*����
�=7�0�(��������������;��2 �)�x��U������os�4{n@�DQ�0a����JFf��% �9����G-����K��i
����g{`��! ���8i����O���{yy���J��k�p*�! �rO��}2g1&����\��%��DQ�<y�O?�_�\9�����9xWbL�E��+ t&�'��=V�z8�l�-W<