pg.ddx.io pgsql-hackers@postgresql.org mailing list archive
help / color / mirror / Atom feed From: Ildus Kurbangaliev <i.kurbangaliev@postgrespro.ru>
To: Tomas Vondra <tomas.vondra@2ndquadrant.com>
Cc: pgsql-hackers@postgresql.org, Ildar Musin <i.musin@postgrespro.ru>
Subject: Re: [HACKERS] Custom compression methods
Date: Tue, 28 Nov 2017 16:29:18 +0300
Message-ID: <20171128162918.7104d0f9@wp.localdomain> (raw )
In-Reply-To: <0158242e-af45-c0f0-4fbb-4d54ca219cfd@2ndquadrant.com >
References: <20170907194236.4cefce96@wp.localdomain >
<20170912175505.4afa11fd@wp.localdomain >
<20171102152836.60c041e4@wp.localdomain >
<20171114162356.52e3d388@wp.localdomain >
<62e46a47-08a8-6e06-5e4f-6f52e0a13202@2ndquadrant.com >
<20171121174717.69ecd8f4@wp.localdomain >
<bfb3d34e-ba74-02cb-ca2a-43344866d9fd@2ndquadrant.com >
<20171123123849.5a686d7b@wp.localdomain >
<57daf28d-ed76-c364-a9ca-65d0ff71a36f@2ndquadrant.com >
<20171124123800.034c9208@wp.localdomain >
<e3378c06-d805-a3f5-6ea7-af2a2417361e@2ndquadrant.com >
<b969fa5a-bc2a-c220-c992-e6e0de34df14@2ndquadrant.com >
<20171127185218.5cddad41@wp.localdomain >
<0158242e-af45-c0f0-4fbb-4d54ca219cfd@2ndquadrant.com >
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:
[text/x-patch] custom_compression_methods_v7.patch (260.0K, ../20171128162918.7104d0f9@wp.localdomain/2-custom_compression_methods_v7.patch)
download | inline diff:
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
view thread (430+ messages) latest in thread
Message-ID: <20171128162918.7104d0f9@wp.localdomain>
Permalink: ../20171128162918.7104d0f9@wp.localdomain/
Also on: postgresql.org/message-id/20171128162918.7104d0f9@wp.localdomain
copy link · copy postgr.es · copy git am
reply Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: i.kurbangaliev@postgrespro.ru, tomas.vondra@2ndquadrant.com, i.musin@postgrespro.ru
Subject: Re: [HACKERS] Custom compression methods
In-Reply-To: <20171128162918.7104d0f9@wp.localdomain>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox