Skip to content

Commit 7ebc84d

Browse files
author
Commitfest Bot
committed
[CF 52/5557] v6 - CREATE FOREIGN TABLE LIKE
This commit was automatically generated by a robot at cfbot.cputube.org. It is based on patches submitted to the PostgreSQL mailing lists and registered in the PostgreSQL Commitfest application. This branch will be overwritten each time a new patch version is posted to the email thread, and also periodically to check for bitrot caused by changes on the master branch. Commitfest entry: https://commitfest.postgresql.org/52/5557 Patch(es): https://www.postgresql.org/message-id/9839f72c-8f1e-4a62-8eb5-7676933405d0@Spark Author(s):
2 parents 95ef3d9 + fb26dfc commit 7ebc84d

File tree

4 files changed

+244
-12
lines changed

4 files changed

+244
-12
lines changed

doc/src/sgml/ref/create_foreign_table.sgml

Lines changed: 120 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ PostgreSQL documentation
2323
<synopsis>
2424
CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="parameter">table_name</replaceable> ( [
2525
{ <replaceable class="parameter">column_name</replaceable> <replaceable class="parameter">data_type</replaceable> [ OPTIONS ( <replaceable class="parameter">option</replaceable> '<replaceable class="parameter">value</replaceable>' [, ... ] ) ] [ COLLATE <replaceable>collation</replaceable> ] [ <replaceable class="parameter">column_constraint</replaceable> [ ... ] ]
26-
| <replaceable>table_constraint</replaceable> }
26+
| <replaceable>table_constraint</replaceable>
27+
| LIKE <replaceable>source_table</replaceable> [ <replaceable>like_option</replaceable> ... ] }
2728
[, ... ]
2829
] )
2930
[ INHERITS ( <replaceable>parent_table</replaceable> [, ... ] ) ]
@@ -57,6 +58,10 @@ CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="parameter">table_name
5758
CHECK ( <replaceable class="parameter">expression</replaceable> ) [ NO INHERIT ] }
5859
[ ENFORCED | NOT ENFORCED ]
5960

61+
<phrase>and <replaceable class="parameter">like_option</replaceable> is:</phrase>
62+
63+
{ INCLUDING | EXCLUDING } { COMMENTS | CONSTRAINTS | DEFAULTS | GENERATED | STATISTICS | ALL }
64+
6065
<phrase>and <replaceable class="parameter">partition_bound_spec</replaceable> is:</phrase>
6166

6267
IN ( <replaceable class="parameter">partition_bound_expr</replaceable> [, ...] ) |
@@ -191,6 +196,111 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
191196
</listitem>
192197
</varlistentry>
193198

199+
<varlistentry>
200+
<term><literal>LIKE <replaceable>source_table</replaceable> [ <replaceable>like_option</replaceable> ... ]</literal></term>
201+
<listitem>
202+
<para>
203+
The <literal>LIKE</literal> clause specifies a table from which
204+
the new table automatically copies all column names, their data types,
205+
and their not-null constraints.
206+
</para>
207+
<para>
208+
Unlike <literal>INHERITS</literal>, the new table and original table
209+
are completely decoupled after creation is complete. Changes to the
210+
original table will not be applied to the new table, and it is not
211+
possible to include data of the new table in scans of the original
212+
table.
213+
</para>
214+
<para>
215+
Also unlike <literal>INHERITS</literal>, columns and
216+
constraints copied by <literal>LIKE</literal> are not merged with similarly
217+
named columns and constraints.
218+
If the same name is specified explicitly or in another
219+
<literal>LIKE</literal> clause, an error is signaled.
220+
</para>
221+
<para>
222+
The optional <replaceable>like_option</replaceable> clauses specify
223+
which additional properties of the original table to copy. Specifying
224+
<literal>INCLUDING</literal> copies the property, specifying
225+
<literal>EXCLUDING</literal> omits the property.
226+
<literal>EXCLUDING</literal> is the default. If multiple specifications
227+
are made for the same kind of object, the last one is used. The
228+
available options are:
229+
230+
<variablelist>
231+
<varlistentry>
232+
<term><literal>INCLUDING COMMENTS</literal></term>
233+
<listitem>
234+
<para>
235+
Comments for the copied columns, constraints, and indexes will be
236+
copied. The default behavior is to exclude comments, resulting in
237+
the copied columns and constraints in the new table having no
238+
comments.
239+
</para>
240+
</listitem>
241+
</varlistentry>
242+
243+
<varlistentry>
244+
<term><literal>INCLUDING CONSTRAINTS</literal></term>
245+
<listitem>
246+
<para>
247+
<literal>CHECK</literal> constraints will be copied. No distinction
248+
is made between column constraints and table constraints. Not-null
249+
constraints are always copied to the new table.
250+
</para>
251+
</listitem>
252+
</varlistentry>
253+
254+
<varlistentry>
255+
<term><literal>INCLUDING DEFAULTS</literal></term>
256+
<listitem>
257+
<para>
258+
Default expressions for the copied column definitions will be
259+
copied. Otherwise, default expressions are not copied, resulting in
260+
the copied columns in the new table having null defaults. Note that
261+
copying defaults that call database-modification functions, such as
262+
<function>nextval</function>, may create a functional linkage
263+
between the original and new tables.
264+
</para>
265+
</listitem>
266+
</varlistentry>
267+
268+
<varlistentry>
269+
<term><literal>INCLUDING GENERATED</literal></term>
270+
<listitem>
271+
<para>
272+
Any generation expressions of copied column definitions will be
273+
copied. By default, new columns will be regular base columns.
274+
</para>
275+
</listitem>
276+
</varlistentry>
277+
278+
<varlistentry>
279+
<term><literal>INCLUDING STATISTICS</literal></term>
280+
<listitem>
281+
<para>
282+
Extended statistics are copied to the new table.
283+
</para>
284+
</listitem>
285+
</varlistentry>
286+
287+
<varlistentry>
288+
<term><literal>INCLUDING ALL</literal></term>
289+
<listitem>
290+
<para>
291+
<literal>INCLUDING ALL</literal> is an abbreviated form selecting
292+
all the available individual options. (It could be useful to write
293+
individual <literal>EXCLUDING</literal> clauses after
294+
<literal>INCLUDING ALL</literal> to select all but some specific
295+
options.)
296+
</para>
297+
</listitem>
298+
</varlistentry>
299+
</variablelist>
300+
</para>
301+
</listitem>
302+
</varlistentry>
303+
194304
<varlistentry>
195305
<term><literal>CONSTRAINT <replaceable class="parameter">constraint_name</replaceable></literal></term>
196306
<listitem>
@@ -450,6 +560,15 @@ CREATE FOREIGN TABLE measurement_y2016m07
450560
defined by <productname>PostgreSQL</productname>, is nonstandard.
451561
</para>
452562

563+
<refsect2>
564+
<title><literal>LIKE</literal> Clause</title>
565+
566+
<para>
567+
The <literal>LIKE</literal> clause is a
568+
<productname>PostgreSQL</productname> extension.
569+
</para>
570+
</refsect2>
571+
453572
</refsect1>
454573

455574
<refsect1>

src/backend/parser/parse_utilcmd.c

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1131,6 +1131,10 @@ transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
11311131
* process at this point, add the TableLikeClause to cxt->likeclauses, which
11321132
* will cause utility.c to call expandTableLikeClause() after the new
11331133
* table has been created.
1134+
*
1135+
* Some options are ignored. For example, as foreign tables have no
1136+
* storage, these options have no effect: storage, compression, identity
1137+
* and indexes. Similarly, INCLUDING INDEXES is ignored from a view.
11341138
*/
11351139
static void
11361140
transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_clause)
@@ -1145,12 +1149,6 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
11451149
setup_parser_errposition_callback(&pcbstate, cxt->pstate,
11461150
table_like_clause->relation->location);
11471151

1148-
/* we could support LIKE in many cases, but worry about it another day */
1149-
if (cxt->isforeign)
1150-
ereport(ERROR,
1151-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1152-
errmsg("LIKE is not supported for creating foreign tables")));
1153-
11541152
/* Open the relation referenced by the LIKE clause */
11551153
relation = relation_openrv(table_like_clause->relation, AccessShareLock);
11561154

@@ -1231,7 +1229,8 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
12311229
* Copy identity if requested
12321230
*/
12331231
if (attribute->attidentity &&
1234-
(table_like_clause->options & CREATE_TABLE_LIKE_IDENTITY))
1232+
(table_like_clause->options & CREATE_TABLE_LIKE_IDENTITY) &&
1233+
!cxt->isforeign)
12351234
{
12361235
Oid seq_relid;
12371236
List *seq_options;
@@ -1250,14 +1249,16 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
12501249
}
12511250

12521251
/* Likewise, copy storage if requested */
1253-
if (table_like_clause->options & CREATE_TABLE_LIKE_STORAGE)
1252+
if ((table_like_clause->options & CREATE_TABLE_LIKE_STORAGE) &&
1253+
!cxt->isforeign)
12541254
def->storage = attribute->attstorage;
12551255
else
12561256
def->storage = 0;
12571257

12581258
/* Likewise, copy compression if requested */
1259-
if ((table_like_clause->options & CREATE_TABLE_LIKE_COMPRESSION) != 0
1260-
&& CompressionMethodIsValid(attribute->attcompression))
1259+
if ((table_like_clause->options & CREATE_TABLE_LIKE_COMPRESSION) != 0 &&
1260+
CompressionMethodIsValid(attribute->attcompression) &&
1261+
!cxt->isforeign)
12611262
def->compression =
12621263
pstrdup(GetCompressionMethodName(attribute->attcompression));
12631264
else
@@ -1536,7 +1537,8 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause)
15361537
* Process indexes if required.
15371538
*/
15381539
if ((table_like_clause->options & CREATE_TABLE_LIKE_INDEXES) &&
1539-
relation->rd_rel->relhasindex)
1540+
relation->rd_rel->relhasindex &&
1541+
childrel->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
15401542
{
15411543
List *parent_indexes;
15421544
ListCell *l;

src/test/regress/expected/create_table_like.out

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,3 +566,80 @@ DROP TYPE ctlty1;
566566
DROP VIEW ctlv1;
567567
DROP TABLE IF EXISTS ctlt4, ctlt10, ctlt11, ctlt11a, ctlt12;
568568
NOTICE: table "ctlt10" does not exist, skipping
569+
--
570+
-- CREATE FOREIGN TABLE LIKE
571+
--
572+
CREATE FOREIGN DATA WRAPPER ctl_dummy;
573+
CREATE SERVER ctl_s0 FOREIGN DATA WRAPPER ctl_dummy;
574+
CREATE TABLE ctl_table(a int primary key, b varchar COMPRESSION pglz,
575+
c int GENERATED ALWAYS AS (a * 2) STORED,
576+
d bigint GENERATED ALWAYS AS IDENTITY,
577+
e int default 1);
578+
CREATE INDEX ctl_table_a_key ON ctl_table(a);
579+
COMMENT ON COLUMN ctl_table.b IS 'Column b';
580+
CREATE STATISTICS ctl_table_stat ON a,b FROM ctl_table;
581+
ALTER TABLE ctl_table add constraint foo CHECK (b = 'text');
582+
\d+ ctl_table
583+
Table "public.ctl_table"
584+
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
585+
--------+-------------------+-----------+----------+------------------------------------+----------+--------------+-------------
586+
a | integer | | not null | | plain | |
587+
b | character varying | | | | extended | | Column b
588+
c | integer | | | generated always as (a * 2) stored | plain | |
589+
d | bigint | | not null | generated always as identity | plain | |
590+
e | integer | | | 1 | plain | |
591+
Indexes:
592+
"ctl_table_pkey" PRIMARY KEY, btree (a)
593+
"ctl_table_a_key" btree (a)
594+
Check constraints:
595+
"foo" CHECK (b::text = 'text'::text)
596+
Statistics objects:
597+
"public.ctl_table_stat" ON a, b FROM ctl_table
598+
Not-null constraints:
599+
"ctl_table_a_not_null" NOT NULL "a"
600+
"ctl_table_d_not_null" NOT NULL "d"
601+
602+
-- Test EXCLUDING ALL
603+
CREATE FOREIGN TABLE ctl_foreign_table1(LIKE ctl_table EXCLUDING ALL) SERVER ctl_s0;
604+
\d+ ctl_foreign_table1
605+
Foreign table "public.ctl_foreign_table1"
606+
Column | Type | Collation | Nullable | Default | FDW options | Storage | Stats target | Description
607+
--------+-------------------+-----------+----------+---------+-------------+----------+--------------+-------------
608+
a | integer | | not null | | | plain | |
609+
b | character varying | | | | | extended | |
610+
c | integer | | | | | plain | |
611+
d | bigint | | not null | | | plain | |
612+
e | integer | | | | | plain | |
613+
Not-null constraints:
614+
"ctl_table_a_not_null" NOT NULL "a"
615+
"ctl_table_d_not_null" NOT NULL "d"
616+
Server: ctl_s0
617+
618+
\set HIDE_TOAST_COMPRESSION false
619+
-- Test INCLUDING ALL
620+
-- INDEXES, IDENTITY, COMPRESSION, STORAGE are not copied.
621+
CREATE FOREIGN TABLE ctl_foreign_table2(LIKE ctl_table INCLUDING ALL) SERVER ctl_s0;
622+
\d+ ctl_foreign_table2
623+
Foreign table "public.ctl_foreign_table2"
624+
Column | Type | Collation | Nullable | Default | FDW options | Storage | Stats target | Description
625+
--------+-------------------+-----------+----------+------------------------------------+-------------+----------+--------------+-------------
626+
a | integer | | not null | | | plain | |
627+
b | character varying | | | | | extended | | Column b
628+
c | integer | | | generated always as (a * 2) stored | | plain | |
629+
d | bigint | | not null | | | plain | |
630+
e | integer | | | 1 | | plain | |
631+
Check constraints:
632+
"foo" CHECK (b::text = 'text'::text)
633+
Statistics objects:
634+
"public.ctl_foreign_table2_a_b_stat" ON a, b FROM ctl_foreign_table2
635+
Not-null constraints:
636+
"ctl_table_a_not_null" NOT NULL "a"
637+
"ctl_table_d_not_null" NOT NULL "d"
638+
Server: ctl_s0
639+
640+
\set HIDE_TOAST_COMPRESSION true
641+
DROP TABLE ctl_table;
642+
DROP FOREIGN TABLE ctl_foreign_table1;
643+
DROP FOREIGN TABLE ctl_foreign_table2;
644+
DROP FOREIGN DATA WRAPPER ctl_dummy CASCADE;
645+
NOTICE: drop cascades to server ctl_s0

src/test/regress/sql/create_table_like.sql

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,3 +225,37 @@ DROP SEQUENCE ctlseq1;
225225
DROP TYPE ctlty1;
226226
DROP VIEW ctlv1;
227227
DROP TABLE IF EXISTS ctlt4, ctlt10, ctlt11, ctlt11a, ctlt12;
228+
229+
--
230+
-- CREATE FOREIGN TABLE LIKE
231+
--
232+
CREATE FOREIGN DATA WRAPPER ctl_dummy;
233+
CREATE SERVER ctl_s0 FOREIGN DATA WRAPPER ctl_dummy;
234+
235+
CREATE TABLE ctl_table(a int primary key, b varchar COMPRESSION pglz,
236+
c int GENERATED ALWAYS AS (a * 2) STORED,
237+
d bigint GENERATED ALWAYS AS IDENTITY,
238+
e int default 1);
239+
240+
CREATE INDEX ctl_table_a_key ON ctl_table(a);
241+
COMMENT ON COLUMN ctl_table.b IS 'Column b';
242+
CREATE STATISTICS ctl_table_stat ON a,b FROM ctl_table;
243+
ALTER TABLE ctl_table add constraint foo CHECK (b = 'text');
244+
245+
\d+ ctl_table
246+
247+
-- Test EXCLUDING ALL
248+
CREATE FOREIGN TABLE ctl_foreign_table1(LIKE ctl_table EXCLUDING ALL) SERVER ctl_s0;
249+
\d+ ctl_foreign_table1
250+
251+
\set HIDE_TOAST_COMPRESSION false
252+
-- Test INCLUDING ALL
253+
-- INDEXES, IDENTITY, COMPRESSION, STORAGE are not copied.
254+
CREATE FOREIGN TABLE ctl_foreign_table2(LIKE ctl_table INCLUDING ALL) SERVER ctl_s0;
255+
\d+ ctl_foreign_table2
256+
\set HIDE_TOAST_COMPRESSION true
257+
258+
DROP TABLE ctl_table;
259+
DROP FOREIGN TABLE ctl_foreign_table1;
260+
DROP FOREIGN TABLE ctl_foreign_table2;
261+
DROP FOREIGN DATA WRAPPER ctl_dummy CASCADE;

0 commit comments

Comments
 (0)