On 2025-07-15 12:31, jian he wrote:
On Mon, Jul 14, 2025 at 10:02 PM Álvaro Herrera <alvhe...@kurilemu.de>
wrote:
On 2025-Jul-02, jian he wrote:
> @@ -673,11 +680,34 @@ BeginCopyTo(ParseState *pstate,
> errmsg("cannot copy from sequence
\"%s\"",
>
RelationGetRelationName(rel))));
> else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
> - ereport(ERROR,
> - (errcode(ERRCODE_WRONG_OBJECT_TYPE),
> - errmsg("cannot copy from partitioned table
\"%s\"",
> -
RelationGetRelationName(rel)),
> - errhint("Try the COPY (SELECT ...) TO
variant.")));
> + {
> + children = find_all_inheritors(RelationGetRelid(rel),
> +
AccessShareLock,
> +
NULL);
> +
> + foreach_oid(childreloid, children)
> + {
> + char relkind =
get_rel_relkind(childreloid);
> +
> + if (relkind == RELKIND_FOREIGN_TABLE)
> + {
> + char *relation_name;
> +
> + relation_name =
get_rel_name(childreloid);
> + ereport(ERROR,
> +
errcode(ERRCODE_WRONG_OBJECT_TYPE),
> + errmsg("cannot copy from foreign table
\"%s\"", relation_name),
> + errdetail("Partition \"%s\" is a
foreign table in the partitioned table \"%s\"",
> +
relation_name, RelationGetRelationName(rel)),
> + errhint("Try the COPY
(SELECT ...) TO variant."));
> + }
This code looks like it's duplicating what you could obtain by using
RelationGetPartitionDesc and then observe the ->isleaf bits. Maybe
have
a look at the function RelationHasForeignPartition() in the patch at
https://postgr.es/m/canhcyew_s2ld6ridsmhtwqnpyb67ewxqf7n8mn7dornakmf...@mail.gmail.com
which looks very similar to what you need here. I think that would
also
have the (maybe dubious) advantage that the rows will be output in
partition bound order rather than breadth-first (partition hierarchy)
OID order.
hi.
else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
{
PartitionDesc pd = RelationGetPartitionDesc(rel, true);
for (int i = 0; i < pd->nparts; i++)
{
Relation partRel;
if (!pd->is_leaf[i])
continue;
partRel = table_open(pd->oids[i], AccessShareLock);
if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
ereport(ERROR,
errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot copy from foreign table
\"%s\"", RelationGetRelationName(partRel)),
errdetail("Partition \"%s\" is a foreign
table in the partitioned table \"%s\"",
RelationGetRelationName(partRel), RelationGetRelationName(rel)),
errhint("Try the COPY (SELECT ...) TO
variant."));
table_close(partRel, NoLock);
scan_oids = lappend_oid(scan_oids,
RelationGetRelid(partRel));
}
}
I tried the above code, but it doesn't work because
RelationGetPartitionDesc
only retrieves the immediate partition descriptor of a partitioned
relation, it
doesn't recurse to the lowest level.
Actually Melih Mutlu raised this question at
https://postgr.es/m/CAGPVpCQou3hWQYUqXNTLKdcuO6envsWJYSJqbZZQnRCjZA6nkQ%40mail.gmail.com
I kind of ignored it...
I guess we have to stick with find_all_inheritors here?
That might be the case.
I thought we could consider using RelationHasForeignPartition() instead,
if [1] gets committed.
However, since that function only tells us whether any foreign
partitions exist, whereas the current patch outputs the specific
problematic partitions or foreign tables in the log, I think the current
approach is more user-friendly.
> <command>COPY TO</command> can be used with plain
> - tables and populated materialized views.
> - For example,
> + tables, populated materialized views and partitioned tables.
> + For example, if <replaceable
class="parameter">table</replaceable> is not partitioned table,
> <literal>COPY <replaceable class="parameter">table</replaceable>
> TO</literal> copies the same rows as
> <literal>SELECT * FROM ONLY <replaceable
class="parameter">table</replaceable></literal>.
I believe "is not a partitioned table" here is intended to refer to both
plain tables and materialized views.
However, as far as I understand, using ONLY with a materialized view has
no effect.
So, wouldn’t it be better and clearer to say "if the table is a plain
table" instead?
I think the behavior for materialized views can be described along with
that for partitioned tables. For example:
<command>COPY TO</command> can be used with plain
tables, populated materialized views and partitioned tables.
For example, if <replaceable class="parameter">table</replaceable>
is a plain table,
<literal>COPY <replaceable class="parameter">table</replaceable>
TO</literal> copies the same rows as
<literal>SELECT * FROM ONLY <replaceable
class="parameter">table</replaceable></literal>.
If <replaceable class="parameter">table</replaceable> is a
partitioned table or a materialized view,
<literal>COPY <replaceable class="parameter">table</replaceable>
TO</literal>
copies the same rows as <literal>SELECT * FROM <replaceable
class="parameter">table</replaceable></literal>.
+ List *children = NIL;
...
@@ -673,11 +680,34 @@ BeginCopyTo(ParseState *pstate,
errmsg("cannot copy from sequence \"%s\"",
RelationGetRelationName(rel))));
else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("cannot copy from partitioned table
\"%s\"",
- RelationGetRelationName(rel)),
- errhint("Try the COPY (SELECT ...) TO
variant.")));
+ {
+ children = find_all_inheritors(RelationGetRelid(rel),
Since 'children' is only used inside the else if block, I think we don't
need the separate "List *children = NIL;" declaration.
Instead, it could just be "List *children = find_all_inheritors(...)".
[1]
https://www.postgresql.org/message-id/flat/CANhcyEW_s2LD6RiDSMHtWQnpYB67EWXqf7N8mn7dOrnaKMfROg%40mail.gmail.com#7bfbe70576e7a3f7162ca268f08bd395
--
Regards,
--
Atsushi Torikoshi
Seconded from NTT DATA Japan Corporation to SRA OSS K.K.