Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 302cf15

Browse files
committed
Add support for LIKE in CREATE FOREIGN TABLE
LIKE enables the creation of foreign tables based on the column definitions, constraints and objects of the defined source relation(s). This feature mirrors the behavior of CREATE TABLE LIKE, but ignores the INCLUDING sub-options that do not make sense for foreign tables: INDEXES, COMPRESSION, IDENTITY and STORAGE. The supported sub-options are COMMENTS, CONSTRAINTS, DEFAULTS, GENERATED and STATISTICS, mapping with the clauses already supported by the command. Note that the restriction with LIKE in CREATE FOREIGN TABLE was added in a0c6dfe. Author: Zhang Mingli Reviewed-by: Álvaro Herrera, Sami Imseih, Michael Paquier Discussion: https://postgr.es/m/42d3f855-2275-4361-a42a-826172ca2dc4@Spark
1 parent e7563e3 commit 302cf15

File tree

4 files changed

+271
-12
lines changed

4 files changed

+271
-12
lines changed

doc/src/sgml/ref/create_foreign_table.sgml

Lines changed: 113 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>
@@ -448,6 +558,8 @@ CREATE FOREIGN TABLE measurement_y2016m07
448558
The ability to specify column default values is also
449559
a <productname>PostgreSQL</productname> extension. Table inheritance, in the form
450560
defined by <productname>PostgreSQL</productname>, is nonstandard.
561+
The <literal>LIKE</literal> clause, as supported in this command, is
562+
nonstandard.
451563
</para>
452564

453565
</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 storage,
1136+
* these INCLUDING 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: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,3 +566,106 @@ 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,
575+
b varchar COMPRESSION pglz,
576+
c int GENERATED ALWAYS AS (a * 2) STORED,
577+
d bigint GENERATED ALWAYS AS IDENTITY,
578+
e int DEFAULT 1);
579+
CREATE INDEX ctl_table_a_key ON ctl_table(a);
580+
COMMENT ON COLUMN ctl_table.b IS 'Column b';
581+
CREATE STATISTICS ctl_table_stat ON a,b FROM ctl_table;
582+
ALTER TABLE ctl_table ADD CONSTRAINT foo CHECK (b = 'text');
583+
ALTER TABLE ctl_table ALTER COLUMN b SET STORAGE MAIN;
584+
\d+ ctl_table
585+
Table "public.ctl_table"
586+
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
587+
--------+-------------------+-----------+----------+------------------------------------+---------+--------------+-------------
588+
a | integer | | not null | | plain | |
589+
b | character varying | | | | main | | Column b
590+
c | integer | | | generated always as (a * 2) stored | plain | |
591+
d | bigint | | not null | generated always as identity | plain | |
592+
e | integer | | | 1 | plain | |
593+
Indexes:
594+
"ctl_table_pkey" PRIMARY KEY, btree (a)
595+
"ctl_table_a_key" btree (a)
596+
Check constraints:
597+
"foo" CHECK (b::text = 'text'::text)
598+
Statistics objects:
599+
"public.ctl_table_stat" ON a, b FROM ctl_table
600+
Not-null constraints:
601+
"ctl_table_a_not_null" NOT NULL "a"
602+
"ctl_table_d_not_null" NOT NULL "d"
603+
604+
-- Test EXCLUDING ALL
605+
CREATE FOREIGN TABLE ctl_foreign_table1(LIKE ctl_table EXCLUDING ALL) SERVER ctl_s0;
606+
\d+ ctl_foreign_table1
607+
Foreign table "public.ctl_foreign_table1"
608+
Column | Type | Collation | Nullable | Default | FDW options | Storage | Stats target | Description
609+
--------+-------------------+-----------+----------+---------+-------------+----------+--------------+-------------
610+
a | integer | | not null | | | plain | |
611+
b | character varying | | | | | extended | |
612+
c | integer | | | | | plain | |
613+
d | bigint | | not null | | | plain | |
614+
e | integer | | | | | plain | |
615+
Not-null constraints:
616+
"ctl_table_a_not_null" NOT NULL "a"
617+
"ctl_table_d_not_null" NOT NULL "d"
618+
Server: ctl_s0
619+
620+
-- \d+ does not report the value of attcompression for a foreign table, so
621+
-- check separately.
622+
SELECT attname, attcompression FROM pg_attribute
623+
WHERE attrelid = 'ctl_foreign_table1'::regclass and attnum > 0 ORDER BY attnum;
624+
attname | attcompression
625+
---------+----------------
626+
a |
627+
b |
628+
c |
629+
d |
630+
e |
631+
(5 rows)
632+
633+
-- Test INCLUDING ALL
634+
-- INDEXES, IDENTITY, COMPRESSION, STORAGE are not copied.
635+
CREATE FOREIGN TABLE ctl_foreign_table2(LIKE ctl_table INCLUDING ALL) SERVER ctl_s0;
636+
\d+ ctl_foreign_table2
637+
Foreign table "public.ctl_foreign_table2"
638+
Column | Type | Collation | Nullable | Default | FDW options | Storage | Stats target | Description
639+
--------+-------------------+-----------+----------+------------------------------------+-------------+----------+--------------+-------------
640+
a | integer | | not null | | | plain | |
641+
b | character varying | | | | | extended | | Column b
642+
c | integer | | | generated always as (a * 2) stored | | plain | |
643+
d | bigint | | not null | | | plain | |
644+
e | integer | | | 1 | | plain | |
645+
Check constraints:
646+
"foo" CHECK (b::text = 'text'::text)
647+
Statistics objects:
648+
"public.ctl_foreign_table2_a_b_stat" ON a, b FROM ctl_foreign_table2
649+
Not-null constraints:
650+
"ctl_table_a_not_null" NOT NULL "a"
651+
"ctl_table_d_not_null" NOT NULL "d"
652+
Server: ctl_s0
653+
654+
-- \d+ does not report the value of attcompression for a foreign table, so
655+
-- check separately.
656+
SELECT attname, attcompression FROM pg_attribute
657+
WHERE attrelid = 'ctl_foreign_table2'::regclass and attnum > 0 ORDER BY attnum;
658+
attname | attcompression
659+
---------+----------------
660+
a |
661+
b |
662+
c |
663+
d |
664+
e |
665+
(5 rows)
666+
667+
DROP TABLE ctl_table;
668+
DROP FOREIGN TABLE ctl_foreign_table1;
669+
DROP FOREIGN TABLE ctl_foreign_table2;
670+
DROP FOREIGN DATA WRAPPER ctl_dummy CASCADE;
671+
NOTICE: drop cascades to server ctl_s0

src/test/regress/sql/create_table_like.sql

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,3 +225,45 @@ 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,
236+
b varchar COMPRESSION pglz,
237+
c int GENERATED ALWAYS AS (a * 2) STORED,
238+
d bigint GENERATED ALWAYS AS IDENTITY,
239+
e int DEFAULT 1);
240+
241+
CREATE INDEX ctl_table_a_key ON ctl_table(a);
242+
COMMENT ON COLUMN ctl_table.b IS 'Column b';
243+
CREATE STATISTICS ctl_table_stat ON a,b FROM ctl_table;
244+
ALTER TABLE ctl_table ADD CONSTRAINT foo CHECK (b = 'text');
245+
ALTER TABLE ctl_table ALTER COLUMN b SET STORAGE MAIN;
246+
247+
\d+ ctl_table
248+
249+
-- Test EXCLUDING ALL
250+
CREATE FOREIGN TABLE ctl_foreign_table1(LIKE ctl_table EXCLUDING ALL) SERVER ctl_s0;
251+
\d+ ctl_foreign_table1
252+
-- \d+ does not report the value of attcompression for a foreign table, so
253+
-- check separately.
254+
SELECT attname, attcompression FROM pg_attribute
255+
WHERE attrelid = 'ctl_foreign_table1'::regclass and attnum > 0 ORDER BY attnum;
256+
257+
-- Test INCLUDING ALL
258+
-- INDEXES, IDENTITY, COMPRESSION, STORAGE are not copied.
259+
CREATE FOREIGN TABLE ctl_foreign_table2(LIKE ctl_table INCLUDING ALL) SERVER ctl_s0;
260+
\d+ ctl_foreign_table2
261+
-- \d+ does not report the value of attcompression for a foreign table, so
262+
-- check separately.
263+
SELECT attname, attcompression FROM pg_attribute
264+
WHERE attrelid = 'ctl_foreign_table2'::regclass and attnum > 0 ORDER BY attnum;
265+
266+
DROP TABLE ctl_table;
267+
DROP FOREIGN TABLE ctl_foreign_table1;
268+
DROP FOREIGN TABLE ctl_foreign_table2;
269+
DROP FOREIGN DATA WRAPPER ctl_dummy CASCADE;

0 commit comments

Comments
 (0)