+ command.
+ schema. Otherwise it is created in the current schema.
+ or view in the same schema.
+ name as any existing data type in the same schema.
+ Do not throw an error if a relation with the same name already exists.
+ created.
+ The name (optionally schema-qualified) of the table to be created.
+ The name of a column to be created in the new table.
--table=table
- Dump only tables (or views or sequences) matching
- class="parameter">table. Multiple tables can be
- selected by writing multiple -t> switches. Also, the
+ Dump only tables (or views or sequences or foreign tables) matching
+ table . Multiple tables
+ can be selected by writing multiple -t> switches. Also, the
table parameter is
interpreted as a pattern according to the same rules used by
psql>'s \d> commands (see
- For each relation (table, view, index, or sequence) matching the
+ For each relation (table, view, index, sequence or foreign table)
+ matching the
pattern , show all
columns, their types, the tablespace (if not the default) and any
special attributes such as NOT NULL or defaults.
Associated indexes, constraints, rules, and triggers are
- also shown.
+ also shown. For foreign tables, the associated foreign
+ server is shown as well.
(Matching the pattern> is defined in
below.)
The command form \d+ is identical, except that
more information is displayed: any comments associated with the
columns of the table are shown, as is the presence of OIDs in the
- table, and the view definition if the relation is a view.
+ table, the view definition if the relation is a view, and the generic
+ options if the relation is a foreign table.
If \d is used without a
pattern argument, it is
- equivalent to \dtvs which will show a list of
- all visible tables, views, and sequences. This is purely a convenience
- measure.
+ equivalent to \dtvsE which will show a list of
+ all visible tables, views, sequences and foreign tables.
+ This is purely a convenience measure.
+
+ \det[+] [ pattern ]
+
+ Lists foreign tables (mnemonic: external tables
).
+ If pattern is
+ specified, only entries whose table name or schema name matches
+ the pattern are listed. If the form \det+
+ is used, generic options are also displayed.
+
+
+
+
+
\des[+] [ pattern ]
\ds[S+] [ pattern ]
\dt[S+] [ pattern ]
\dv[S+] [ pattern ]
+ \dE[S+] [ pattern ]
In this group of commands, the letters
i , s ,
- t , and v
- stand for index, sequence, table, and view, respectively.
+ t , v , and E
+ stand for index, sequence, table, view, and foreign table,
+ respectively.
You can specify any or all of
these letters, in any order, to obtain a listing of objects
of these types. For example, \dit> lists indexes
COLUMN table_name .column_name |
AGGREGATE agg_name (agg_type [, ...] ) |
DOMAIN object_name |
+ FOREIGN TABLE object_name
FUNCTION function_name ( [ [ argmode ] [ argname ] argtype [, ...] ] ) |
LARGE OBJECT large_object_oid |
[ PROCEDURAL ] LANGUAGE object_name |
The name of the object to be commented. Names of tables,
- aggregates, domains, functions, sequences, types, and views can
- be schema-qualified.
+ aggregates, domains, foreign tables, functions, sequences, types, and
+ views can be schema-qualified.
&alterDefaultPrivileges;
&alterDomain;
&alterForeignDataWrapper;
+ &alterForeignTable;
&alterFunction;
&alterGroup;
&alterIndex;
&createDatabase;
&createDomain;
&createForeignDataWrapper;
+ &createForeignTable;
&createFunction;
&createGroup;
&createIndex;
&dropDatabase;
&dropDomain;
&dropForeignDataWrapper;
+ &dropForeignTable;
&dropFunction;
&dropGroup;
&dropIndex;
case RELKIND_INDEX:
options = index_reloptions(amoptions, datum, false);
break;
+ case RELKIND_FOREIGN_TABLE:
+ options = NULL;
+ break;
default:
Assert(false); /* can't get here */
options = NULL; /* keep compiler quiet */
case RELKIND_RELATION:
return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
default:
- /* sequences, composite types and view s are not supported */
+ /* other relkind s are not supported */
return NULL;
}
}
*
* This is essentially relation_open plus check that the relation
* is not an index nor a composite type. (The caller should also
- * check that it's not a view before assuming it has storage.)
+ * check that it's not a view or foreign table before assuming it has
+ * storage.)
* ----------------
*/
Relation
pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
pg_ts_parser.h pg_ts_template.h \
pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
+ pg_foreign_table.h \
pg_default_acl.h pg_seclabel.h \
toasting.h indexing.h \
)
case ACL_KIND_FOREIGN_SERVER:
whole_mask = ACL_ALL_RIGHTS_FOREIGN_SERVER;
break;
+ case ACL_KIND_FOREIGN_TABLE:
+ whole_mask = ACL_ALL_RIGHTS_FOREIGN_TABLE;
+ break;
default:
elog(ERROR, "unrecognized object kind: %d", objkind);
/* not reached, but keep compiler quiet */
all_privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
errormsg = gettext_noop("invalid privilege type %s for foreign server");
break;
+ case ACL_OBJECT_FOREIGN_TABLE:
+ all_privileges = ACL_ALL_RIGHTS_FOREIGN_TABLE;
+ errormsg = gettext_noop("invalid privilege type %s for foreign table");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) stmt->objtype);
{
case ACL_OBJECT_RELATION:
case ACL_OBJECT_SEQUENCE:
+ case ACL_OBJECT_FOREIGN_TABLE:
ExecGrant_Relation(istmt);
break;
case ACL_OBJECT_DATABASE:
{
case ACL_OBJECT_RELATION:
case ACL_OBJECT_SEQUENCE:
+ case ACL_OBJECT_FOREIGN_TABLE:
foreach(cell, objnames)
{
RangeVar *relvar = (RangeVar *) lfirst(cell);
switch (objtype)
{
case ACL_OBJECT_RELATION:
- /* Process both regular tables and view s */
+ /* Process regular tables, views and foreign table s */
objs = getRelationsInNamespace(namespaceId, RELKIND_RELATION);
objects = list_concat(objects, objs);
objs = getRelationsInNamespace(namespaceId, RELKIND_VIEW);
objects = list_concat(objects, objs);
+ objs = getRelationsInNamespace(namespaceId, RELKIND_FOREIGN_TABLE);
+ objects = list_concat(objects, objs);
break;
case ACL_OBJECT_SEQUENCE:
objs = getRelationsInNamespace(namespaceId, RELKIND_SEQUENCE);
errmsg("\"%s\" is not a sequence",
NameStr(pg_class_tuple->relname))));
- /* Adjust the default permissions based on whether it is a sequence */
+ /* Used GRANT FOREIGN TABLE on a non-foreign-table? */
+ if (istmt->objtype == ACL_OBJECT_FOREIGN_TABLE &&
+ pg_class_tuple->relkind != RELKIND_FOREIGN_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a foreign table",
+ NameStr(pg_class_tuple->relname))));
+
+ /* Adjust the default permissions based on object type */
if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
{
if (pg_class_tuple->relkind == RELKIND_SEQUENCE)
this_privileges = ACL_ALL_RIGHTS_SEQUENCE;
+ else if (pg_class_tuple->relkind == RELKIND_FOREIGN_TABLE)
+ this_privileges = ACL_ALL_RIGHTS_FOREIGN_TABLE;
else
this_privileges = ACL_ALL_RIGHTS_RELATION;
}
this_privileges &= (AclMode) ACL_ALL_RIGHTS_SEQUENCE;
}
}
+ else if (pg_class_tuple->relkind == RELKIND_FOREIGN_TABLE)
+ {
+ if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_FOREIGN_TABLE))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_GRANT_OPERATION),
+ errmsg("foreign table \"%s\" only supports SELECT privileges",
+ NameStr(pg_class_tuple->relname))));
+ }
+ }
else
{
if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_RELATION))
&isNull);
if (isNull)
{
- old_acl = acldefault(pg_class_tuple->relkind == RELKIND_SEQUENCE ?
- ACL_OBJECT_SEQUENCE : ACL_OBJECT_RELATION,
- ownerId);
+ switch (pg_class_tuple->relkind)
+ {
+ case RELKIND_SEQUENCE:
+ old_acl = acldefault(ACL_OBJECT_SEQUENCE, ownerId);
+ break;
+ case RELKIND_FOREIGN_TABLE:
+ old_acl = acldefault(ACL_OBJECT_FOREIGN_TABLE, ownerId);
+ break;
+ default:
+ old_acl = acldefault(ACL_OBJECT_RELATION, ownerId);
+ break;
+ }
/* There are no old member roles according to the catalogs */
noldmembers = 0;
oldmembers = NULL;
bool replaces[Natts_pg_class];
int nnewmembers;
Oid *newmembers;
+ AclObjectKind aclkind;
/* Determine ID to do the grant as, and available grant options */
select_best_grantor(GetUserId(), this_privileges,
old_acl, ownerId,
&grantorId, &avail_goptions);
+ switch (pg_class_tuple->relkind)
+ {
+ case RELKIND_SEQUENCE:
+ aclkind = ACL_KIND_SEQUENCE;
+ break;
+ case RELKIND_FOREIGN_TABLE:
+ aclkind = ACL_KIND_FOREIGN_TABLE;
+ break;
+ default:
+ aclkind = ACL_KIND_CLASS;
+ break;
+ }
+
/*
* Restrict the privileges to what we can actually grant, and emit
* the standards-mandated warning and error messages.
this_privileges =
restrict_and_check_grant(istmt->is_grant, avail_goptions,
istmt->all_privs, this_privileges,
- relOid, grantorId,
- pg_class_tuple->relkind == RELKIND_SEQUENCE
- ? ACL_KIND_SEQUENCE : ACL_KIND_CLASS,
+ relOid, grantorId, aclkind,
NameStr(pg_class_tuple->relname),
0, NULL);
this_privileges &= (AclMode) ACL_SELECT;
}
+ else if (pg_class_tuple->relkind == RELKIND_FOREIGN_TABLE &&
+ this_privileges & ~((AclMode) ACL_SELECT))
+ {
+ /* Foreign tables have the same restriction as sequences. */
+ ereport(WARNING,
+ (errcode(ERRCODE_INVALID_GRANT_OPERATION),
+ errmsg("foreign table \"%s\" only supports SELECT column privileges",
+ NameStr(pg_class_tuple->relname))));
+ this_privileges &= (AclMode) ACL_SELECT;
+ }
expand_col_privileges(col_privs->cols, relOid,
this_privileges,
/* ACL_KIND_FDW */
gettext_noop("permission denied for foreign-data wrapper %s"),
/* ACL_KIND_FOREIGN_SERVER */
- gettext_noop("permission denied for foreign server %s")
+ gettext_noop("permission denied for foreign server %s"),
+ /* ACL_KIND_FOREIGN_TABLE */
+ gettext_noop("permission denied for foreign table %s"),
};
static const char *const not_owner_msg[MAX_ACL_KIND] =
/* ACL_KIND_FDW */
gettext_noop("must be owner of foreign-data wrapper %s"),
/* ACL_KIND_FOREIGN_SERVER */
- gettext_noop("must be owner of foreign server %s")
+ gettext_noop("must be owner of foreign server %s"),
+ /* ACL_KIND_FOREIGN_TABLE */
+ gettext_noop("must be owner of foreign table %s"),
};
if (isNull)
{
/* No ACL, so build default ACL */
- acl = acldefault(classForm->relkind == RELKIND_SEQUENCE ?
- ACL_OBJECT_SEQUENCE : ACL_OBJECT_RELATION,
- ownerId);
+ switch (classForm->relkind)
+ {
+ case RELKIND_SEQUENCE:
+ acl = acldefault(ACL_OBJECT_SEQUENCE, ownerId);
+ break;
+ case RELKIND_FOREIGN_TABLE:
+ acl = acldefault(ACL_OBJECT_FOREIGN_TABLE, ownerId);
+ break;
+ default:
+ acl = acldefault(ACL_OBJECT_RELATION, ownerId);
+ break;
+ }
aclDatum = (Datum) 0;
}
else
#include "catalog/pg_depend.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
+#include "catalog/pg_foreign_table.h"
#include "catalog/pg_language.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_namespace.h"
ForeignDataWrapperRelationId, /* OCLASS_FDW */
ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */
UserMappingRelationId, /* OCLASS_USER_MAPPING */
+ ForeignTableRelationId, /* OCLASS_FOREIGN_TABLE */
DefaultAclRelationId /* OCLASS_DEFACL */
};
case UserMappingRelationId:
return OCLASS_USER_MAPPING;
+ case ForeignTableRelationId:
+ Assert(object->objectSubId == 0);
+ return OCLASS_FOREIGN_TABLE;
+
case DefaultAclRelationId:
return OCLASS_DEFACL;
}
appendStringInfo(buffer, _("composite type %s"),
relname);
break;
+ case RELKIND_FOREIGN_TABLE:
+ appendStringInfo(buffer, _("foreign table %s"),
+ relname);
+ break;
default:
/* shouldn't get here */
appendStringInfo(buffer, _("relation %s"),
#include "catalog/objectaccess.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_constraint.h"
+#include "catalog/pg_foreign_table.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_statistic.h"
{
case RELKIND_VIEW:
case RELKIND_COMPOSITE_TYPE:
+ case RELKIND_FOREIGN_TABLE:
create_storage = false;
/*
/* Use binary-upgrade overrides if applicable */
if (OidIsValid(binary_upgrade_next_heap_relfilenode) &&
(relkind == RELKIND_RELATION || relkind == RELKIND_SEQUENCE ||
- relkind == RELKIND_VIEW || relkind == RELKIND_COMPOSITE_TYPE))
+ relkind == RELKIND_VIEW || relkind == RELKIND_COMPOSITE_TYPE ||
+ relkind == RELKIND_FOREIGN_TABLE))
{
relid = binary_upgrade_next_heap_relfilenode;
binary_upgrade_next_heap_relfilenode = InvalidOid;
{
case RELKIND_RELATION:
case RELKIND_VIEW:
+ case RELKIND_FOREIGN_TABLE:
relacl = get_user_default_acl(ACL_OBJECT_RELATION, ownerid,
relnamespace);
break;
* Decide whether to create an array type over the relation's rowtype. We
* do not create any array types for system catalogs (ie, those made
* during initdb). We create array types for regular relations, views,
- * and composite types ... but not, eg, for toast tables or sequences.
+ * composite types and foreign tables ... but not, eg, for toast tables or
+ * sequences.
*/
if (IsUnderPostmaster && (relkind == RELKIND_RELATION ||
relkind == RELKIND_VIEW ||
+ relkind == RELKIND_FOREIGN_TABLE ||
relkind == RELKIND_COMPOSITE_TYPE))
new_array_oid = AssignTypeArrayOid();
"it is being used by active queries in this session",
RelationGetRelationName(rel))));
+ /*
+ * Delete pg_foreign_table tuple first.
+ */
+ if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+ {
+ Relation rel;
+ HeapTuple tuple;
+
+ rel = heap_open(ForeignTableRelationId, RowExclusiveLock);
+
+ tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for foreign table %u", relid);
+
+ simple_heap_delete(rel, &tuple->t_self);
+
+ ReleaseSysCache(tuple);
+ heap_close(rel, RowExclusiveLock);
+ }
+
/*
* Schedule unlinking of the relation's physical files at commit.
*/
if (rel->rd_rel->relkind != RELKIND_VIEW &&
- rel->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
+ rel->rd_rel->relkind != RELKIND_COMPOSITE_TYPE &&
+ rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
{
RelationDropStorage(rel);
}
AND a.attrelid = c.oid
AND a.atttypid = t.oid
AND t.typtype = 'd'
- AND c.relkind IN ('r', 'v')
+ AND c.relkind IN ('r', 'v', 'f' )
AND a.attnum > 0
AND NOT a.attisdropped
AND pg_has_role(t.typowner, 'USAGE');
pr_c.relowner
FROM (SELECT oid, relname, relnamespace, relowner, (aclexplode(relacl)).*
FROM pg_class
- WHERE relkind IN ('r', 'v')
+ WHERE relkind IN ('r', 'v', 'f' )
) pr_c (oid, relname, relnamespace, relowner, grantor, grantee, prtype, grantable),
pg_attribute a
WHERE a.attrelid = pr_c.oid
) pr_a (attrelid, attname, grantor, grantee, prtype, grantable),
pg_class c
WHERE pr_a.attrelid = c.oid
- AND relkind IN ('r','v ')
+ AND relkind IN ('r', 'v', 'f ')
) x,
pg_namespace nc,
pg_authid u_grantor,
WHERE a.attrelid = c.oid
AND a.atttypid = t.oid
AND nc.oid = c.relnamespace
- AND a.attnum > 0 AND NOT a.attisdropped AND c.relkind in ('r', 'v')
+ AND a.attnum > 0 AND NOT a.attisdropped AND c.relkind in ('r', 'v', 'f' )
AND pg_has_role(coalesce(bt.typowner, t.typowner), 'USAGE');
GRANT SELECT ON column_udt_usage TO PUBLIC;
AND nc.oid = c.relnamespace
AND (NOT pg_is_other_temp_schema(nc.oid))
- AND a.attnum > 0 AND NOT a.attisdropped AND c.relkind in ('r', 'v')
+ AND a.attnum > 0 AND NOT a.attisdropped AND c.relkind in ('r', 'v', 'f' )
AND (pg_has_role(c.relowner, 'USAGE')
OR has_column_privilege(c.oid, a.attnum,
CASE WHEN nc.oid = pg_my_temp_schema() THEN 'LOCAL TEMPORARY'
WHEN c.relkind = 'r' THEN 'BASE TABLE'
WHEN c.relkind = 'v' THEN 'VIEW'
+ WHEN c.relkind = 'f' THEN 'FOREIGN TABLE'
ELSE null END
AS character_data) AS table_type,
FROM pg_namespace nc JOIN pg_class c ON (nc.oid = c.relnamespace)
LEFT JOIN (pg_type t JOIN pg_namespace nt ON (t.typnamespace = nt.oid)) ON (c.reloftype = t.oid)
- WHERE c.relkind IN ('r', 'v')
+ WHERE c.relkind IN ('r', 'v', 'f' )
AND (NOT pg_is_other_temp_schema(nc.oid))
AND (pg_has_role(c.relowner, 'USAGE')
OR has_table_privilege(c.oid, 'SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER')
AND dt.refclassid = 'pg_catalog.pg_class'::regclass
AND dt.refobjid = t.oid
AND t.relnamespace = nt.oid
- AND t.relkind IN ('r', 'v')
+ AND t.relkind IN ('r', 'v', 'f' )
AND t.oid = a.attrelid
AND dt.refobjsubid = a.attnum
AND pg_has_role(t.relowner, 'USAGE');
AND dt.refclassid = 'pg_catalog.pg_class'::regclass
AND dt.refobjid = t.oid
AND t.relnamespace = nt.oid
- AND t.relkind IN ('r', 'v')
+ AND t.relkind IN ('r', 'v', 'f' )
AND pg_has_role(t.relowner, 'USAGE');
GRANT SELECT ON view_table_usage TO PUBLIC;
'TABLE'::text, a.attnum, a.atttypid
FROM pg_class c, pg_attribute a
WHERE c.oid = a.attrelid
- AND c.relkind IN ('r', 'v')
+ AND c.relkind IN ('r', 'v', 'f' )
AND attnum > 0 AND NOT attisdropped
UNION ALL
GRANT SELECT ON foreign_servers TO PUBLIC;
+/* Base view for foreign tables */
+CREATE VIEW _pg_foreign_tables AS
+ SELECT
+ CAST(current_database() AS sql_identifier) AS foreign_table_catalog,
+ n.nspname AS foreign_table_schema,
+ c.relname AS foreign_table_name,
+ t.ftoptions AS ftoptions,
+ CAST(current_database() AS sql_identifier) AS foreign_server_catalog,
+ CAST(srvname AS sql_identifier) AS foreign_server_name,
+ CAST(u.rolname AS sql_identifier) AS authorization_identifier
+ FROM pg_foreign_table t, pg_foreign_server s, pg_foreign_data_wrapper w,
+ pg_authid u, pg_namespace n, pg_class c
+ WHERE w.oid = s.srvfdw
+ AND u.oid = c.relowner
+ AND (pg_has_role(c.relowner, 'USAGE')
+ OR has_table_privilege(c.oid, 'SELECT')
+ OR has_any_column_privilege(c.oid, 'SELECT'))
+ AND n.oid = c.relnamespace
+ AND c.oid = t.ftrelid
+ AND c.relkind = 'f'
+ AND s.oid = t.ftserver;
+
+
+/*
+ * 24.8
+ * FOREIGN_TABLE_OPTIONS view
+ */
+CREATE VIEW foreign_table_options AS
+ SELECT foreign_table_catalog,
+ foreign_table_schema,
+ foreign_table_name,
+ CAST((pg_options_to_table(t.ftoptions)).option_name AS sql_identifier) AS option_name,
+ CAST((pg_options_to_table(t.ftoptions)).option_value AS character_data) AS option_value
+ FROM _pg_foreign_tables t;
+
+GRANT SELECT ON TABLE foreign_table_options TO PUBLIC;
+
+
+/*
+ * 24.9
+ * FOREIGN_TABLES view
+ */
+CREATE VIEW foreign_tables AS
+ SELECT foreign_table_catalog,
+ foreign_table_schema,
+ foreign_table_name,
+ foreign_server_catalog,
+ foreign_server_name
+ FROM _pg_foreign_tables;
+
+GRANT SELECT ON foreign_tables TO PUBLIC;
+
+
+
/* Base view for user mappings */
CREATE VIEW _pg_user_mappings AS
SELECT um.oid,
case OBJECT_SEQUENCE:
case OBJECT_TABLE:
case OBJECT_VIEW:
+ case OBJECT_FOREIGN_TABLE:
relation =
get_relation_by_qualified_name(objtype, objname, lockmode);
address.classId = RelationRelationId;
errmsg("\"%s\" is not a view",
RelationGetRelationName(relation))));
break;
+ case OBJECT_FOREIGN_TABLE:
+ if (relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a foreign table",
+ RelationGetRelationName(relation))));
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
break;
SELECT
l.objoid, l.classoid, l.objsubid,
CASE WHEN rel.relkind = 'r' THEN 'table'::text
- WHEN rel.relkind = 'v' THEN 'view'::text
- WHEN rel.relkind = 'S' THEN 'sequence'::text END AS objtype,
+ WHEN rel.relkind = 'v' THEN 'view'::text
+ WHEN rel.relkind = 'S' THEN 'sequence'::text
+ WHEN rel.relkind = 'f' THEN 'foreign table'::text END AS objtype,
rel.relnamespace AS objnamespace,
CASE WHEN pg_table_is_visible(rel.oid)
THEN quote_ident(rel.relname)
case OBJECT_COLUMN:
case OBJECT_ATTRIBUTE:
case OBJECT_TRIGGER:
+ case OBJECT_FOREIGN_TABLE:
{
Oid relid;
case OBJECT_SEQUENCE:
case OBJECT_VIEW:
case OBJECT_INDEX:
+ case OBJECT_FOREIGN_TABLE:
{
/*
* RENAME TABLE requires that we (still) hold
case OBJECT_SEQUENCE:
case OBJECT_TABLE:
case OBJECT_VIEW:
+ case OBJECT_FOREIGN_TABLE:
CheckRelationOwnership(stmt->relation, true);
AlterTableNamespace(stmt->relation, stmt->newschema,
stmt->objectType, AccessExclusiveLock);
/* No need for a WARNING if we already complained during VACUUM */
if (!(vacstmt->options & VACOPT_VACUUM))
ereport(WARNING,
- (errmsg("skipping \"%s\" --- cannot analyze indexes, views, or special system tables",
+ (errmsg("skipping \"%s\" --- cannot analyze non-tables or special system tables",
RelationGetRelationName(onerel))));
relation_close(onerel, ShareUpdateExclusiveLock);
return;
case OBJECT_SEQUENCE:
case OBJECT_TABLE:
case OBJECT_VIEW:
+ case OBJECT_FOREIGN_TABLE:
if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
RelationGetRelationName(relation));
RelationGetRelationName(relation));
/*
- * Allow comments only on columns of tables, views, and composite types
- * (which are the only relkinds for which pg_dump will dump per-column
- * comments). In particular we wish to disallow comments on index
- * columns, because the naming of an index's columns may change across PG
- * versions, so dumping per-column comments could create reload failures.
+ * Allow comments only on columns of tables, views, composite types, and
+ * foreign tables (which are the only relkinds for which pg_dump will dump
+ * per-column comments). In particular we wish to disallow comments on
+ * index columns, because the naming of an index's columns may change
+ * across PG versions, so dumping per-column comments could create reload
+ * failures.
*/
if (relation->rd_rel->relkind != RELKIND_RELATION &&
relation->rd_rel->relkind != RELKIND_VIEW &&
- relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
+ relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE &&
+ relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table, view, or composite typ e",
+ errmsg("\"%s\" is not a table, view, composite type, or foreign tabl e",
RelationGetRelationName(relation))));
}
errmsg("cannot copy from view \"%s\"",
RelationGetRelationName(cstate->rel)),
errhint("Try the COPY (SELECT ...) TO variant.")));
+ else if (cstate->rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot copy from foreign table \"%s\"",
+ RelationGetRelationName(cstate->rel)),
+ errhint("Try the COPY (SELECT ...) TO variant.")));
else if (cstate->rel->rd_rel->relkind == RELKIND_SEQUENCE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot copy to view \"%s\"",
RelationGetRelationName(cstate->rel))));
+ else if (cstate->rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot copy to foreign table \"%s\"",
+ RelationGetRelationName(cstate->rel))));
else if (cstate->rel->rd_rel->relkind == RELKIND_SEQUENCE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
#include "postgres.h"
#include "access/heapam.h"
+#include "access/xact.h"
#include "access/reloptions.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
+#include "catalog/pg_foreign_table.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
*
* This is used by CREATE/ALTER of FOREIGN DATA WRAPPER/SERVER/USER MAPPING.
*/
-static Datum
+Datum
transformGenericOptions(Oid catalogId,
Datum oldOptions,
List *options,
heap_close(rel, RowExclusiveLock);
}
+
+/*
+ * Create a foreign table
+ * call after DefineRelation().
+ */
+void
+CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
+{
+ Relation ftrel;
+ Datum ftoptions;
+ Datum values[Natts_pg_foreign_table];
+ bool nulls[Natts_pg_foreign_table];
+ HeapTuple tuple;
+ Oid ftId;
+ AclResult aclresult;
+ ObjectAddress myself;
+ ObjectAddress referenced;
+ Oid ownerId;
+ ForeignDataWrapper *fdw;
+ ForeignServer *server;
+
+ /*
+ * Advance command counter to ensure the pg_attribute tuple visible; the
+ * tuple might be updated to add constraints in previous step.
+ */
+ CommandCounterIncrement();
+
+ /*
+ * For now the owner cannot be specified on create. Use effective user ID.
+ */
+ ownerId = GetUserId();
+
+ /*
+ * Check that the foreign server exists and that we have USAGE on it. Also
+ * get the actual FDW for option validation etc.
+ */
+ server = GetForeignServerByName(stmt->servername, false);
+ aclresult = pg_foreign_server_aclcheck(server->serverid, ownerId, ACL_USAGE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_FOREIGN_SERVER, server->servername);
+
+ fdw = GetForeignDataWrapper(server->fdwid);
+
+ aclresult = pg_foreign_data_wrapper_aclcheck(fdw->fdwid, ownerId, ACL_USAGE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname);
+
+ /*
+ * Insert tuple into pg_foreign_table.
+ */
+ ftrel = heap_open(ForeignTableRelationId, RowExclusiveLock);
+
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+
+ values[Anum_pg_foreign_table_ftrelid - 1] = ObjectIdGetDatum(relid);
+ values[Anum_pg_foreign_table_ftserver - 1] = ObjectIdGetDatum(server->serverid);
+ /* Add table generic options */
+ ftoptions = transformGenericOptions(ForeignTableRelationId,
+ PointerGetDatum(NULL),
+ stmt->options,
+ fdw->fdwvalidator);
+
+ if (PointerIsValid(DatumGetPointer(ftoptions)))
+ values[Anum_pg_foreign_table_ftoptions - 1] = ftoptions;
+ else
+ nulls[Anum_pg_foreign_table_ftoptions - 1] = true;
+
+ tuple = heap_form_tuple(ftrel->rd_att, values, nulls);
+
+ /* pg_foreign_table don't have OID */
+ ftId = simple_heap_insert(ftrel, tuple);
+
+ CatalogUpdateIndexes(ftrel, tuple);
+
+ heap_freetuple(tuple);
+
+ /* Add pg_class dependency on the server */
+ myself.classId = RelationRelationId;
+ myself.objectId = relid;
+ myself.objectSubId = 0;
+
+ referenced.classId = ForeignServerRelationId;
+ referenced.objectId = server->serverid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ heap_close(ftrel, NoLock);
+}
case OBJECT_SEQUENCE:
case OBJECT_TABLE:
case OBJECT_VIEW:
+ case OBJECT_FOREIGN_TABLE:
if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
RelationGetRelationName(relation));
*/
if (relation->rd_rel->relkind != RELKIND_RELATION &&
relation->rd_rel->relkind != RELKIND_VIEW &&
- relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
+ relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE &&
+ relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table, view, or composite typ e",
+ errmsg("\"%s\" is not a table, view, composite type, or foreign tabl e",
RelationGetRelationName(relation))));
}
#include "catalog/objectaccess.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
+#include "catalog/pg_foreign_table.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_namespace.h"
#include "commands/trigger.h"
#include "commands/typecmds.h"
#include "executor/executor.h"
+#include "foreign/foreign.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
gettext_noop("type \"%s\" does not exist, skipping"),
gettext_noop("\"%s\" is not a type"),
gettext_noop("Use DROP TYPE to remove a type.")},
+ {RELKIND_FOREIGN_TABLE,
+ ERRCODE_UNDEFINED_OBJECT,
+ gettext_noop("foreign table \"%s\" does not exist"),
+ gettext_noop("foreign table \"%s\" does not exist, skipping"),
+ gettext_noop("\"%s\" is not a foreign table"),
+ gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
{'\0', 0, NULL, NULL, NULL, NULL}
};
+/* Alter table target-type flags for ATSimplePermissions */
+#define ATT_TABLE 0x0001
+#define ATT_VIEW 0x0002
+#define ATT_INDEX 0x0004
+#define ATT_COMPOSITE_TYPE 0x0008
+#define ATT_FOREIGN_TABLE 0x0010
static void truncate_check_rel(Relation rel);
static List *MergeAttributes(List *schema, List *supers, char relpersistence,
static void ATRewriteTables(List **wqueue, LOCKMODE lockmode);
static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
-static void ATSimplePermissions(Relation rel, bool allowView, bool allowType );
-static void ATSimplePermissionsRelationOrIndex(Relation rel );
+static void ATSimplePermissions(Relation rel, int allowed_targets );
+static void ATWrongRelkindError(Relation rel, int allowed_targets );
static void ATSimpleRecursion(List **wqueue, Relation rel,
AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode);
static void ATOneLevelRecursion(List **wqueue, Relation rel,
static void ATPrepAddInherit(Relation child_rel);
static void ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
static void ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
+static void ATExecGenericOptions(Relation rel, List *options);
+
static void copy_relation_data(SMgrRelation rel, SMgrRelation dst,
ForkNumber forkNum, char relpersistence);
static const char *storage_name(char c);
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("ON COMMIT can only be used on temporary tables")));
+ if (stmt->constraints != NIL && relkind == RELKIND_FOREIGN_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("constraints on foreign tables are not supported")));
/*
* Security check: disallow creating temp tables from security-restricted
{
RawColumnDefault *rawEnt;
+ if (relkind == RELKIND_FOREIGN_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("default values on foreign tables are not supported")));
+
Assert(colDef->cooked_default == NULL);
rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
/*
* RemoveRelations
- * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW
+ * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
+ * DROP FOREIGN TABLE
*/
void
RemoveRelations(DropStmt *drop)
relkind = RELKIND_VIEW;
break;
+ case OBJECT_FOREIGN_TABLE:
+ relkind = RELKIND_FOREIGN_TABLE;
+ break;
+
default:
elog(ERROR, "unrecognized drop object type: %d",
(int) drop->removeType);
if (relkind != RELKIND_RELATION &&
relkind != RELKIND_VIEW &&
relkind != RELKIND_COMPOSITE_TYPE &&
- relkind != RELKIND_INDEX)
+ relkind != RELKIND_INDEX &&
+ relkind != RELKIND_FOREIGN_TABLE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table, view, composite type or index ",
+ errmsg("\"%s\" is not a table, view, composite type, index or foreign table ",
RelationGetRelationName(targetrelation))));
/*
/*
- * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW RENAME
+ * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE RENAME
*
* Caller has already done permissions checks.
*/
/*
* For compatibility with prior releases, we don't complain if ALTER TABLE
- * or ALTER INDEX is used to rename a sequence or view.
+ * or ALTER INDEX is used to rename some other type of relation. But
+ * ALTER SEQUENCE/VIEW/FOREIGN TABLE are only to be used with relations of
+ * that type.
*/
if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
ereport(ERROR,
errmsg("\"%s\" is not a view",
RelationGetRelationName(targetrelation))));
+ if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a foreign table",
+ RelationGetRelationName(targetrelation)),
+ errhint("Use ALTER FOREIGN TABLE instead.")));
+
/*
* Don't allow ALTER TABLE on composite types. We want people to use ALTER
* TYPE for that.
* For mostly-historical reasons, we allow ALTER TABLE to apply to
* almost all relation types.
*/
- if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+ if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE
+ || rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a table",
RelationGetRelationName(rel))));
break;
+ case OBJECT_FOREIGN_TABLE:
+ if (rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a foreign table",
+ RelationGetRelationName(rel))));
+ break;
+
default:
elog(ERROR, "unrecognized object type: %d", (int) stmt->relkind);
}
case AT_SetTableSpace: /* must rewrite heap */
case AT_DropNotNull: /* may change some SQL plans */
case AT_SetNotNull:
+ case AT_GenericOptions:
cmd_lockmode = AccessExclusiveLock;
break;
switch (cmd->subtype)
{
case AT_AddColumn: /* ADD COLUMN */
- ATSimplePermissions(rel, false, true);
+ ATSimplePermissions(rel,
+ ATT_TABLE|ATT_COMPOSITE_TYPE|ATT_FOREIGN_TABLE);
/* Performs own recursion */
ATPrepAddColumn(wqueue, rel, recurse, recursing, cmd, lockmode);
pass = AT_PASS_ADD_COL;
break;
case AT_AddColumnToView: /* add column via CREATE OR REPLACE
* VIEW */
- ATSimplePermissions(rel, true, false );
+ ATSimplePermissions(rel, ATT_VIEW );
/* Performs own recursion */
ATPrepAddColumn(wqueue, rel, recurse, recursing, cmd, lockmode);
pass = AT_PASS_ADD_COL;
* substitutes default values into INSERTs before it expands
* rules.
*/
- ATSimplePermissions(rel, true, false );
+ ATSimplePermissions(rel, ATT_TABLE|ATT_VIEW );
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
/* No command-specific prep needed */
pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP;
break;
case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
- ATSimplePermissions(rel, false, false );
+ ATSimplePermissions(rel, ATT_TABLE|ATT_FOREIGN_TABLE );
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
/* No command-specific prep needed */
pass = AT_PASS_DROP;
break;
case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
- ATSimplePermissions(rel, false, false );
+ ATSimplePermissions(rel, ATT_TABLE|ATT_FOREIGN_TABLE );
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
/* No command-specific prep needed */
pass = AT_PASS_ADD_CONSTR;
break;
case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
- ATSimplePermissionsRelationOrIndex(rel );
+ ATSimplePermissions(rel, ATT_TABLE|ATT_INDEX );
/* This command never recurses */
pass = AT_PASS_MISC;
break;
case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
- ATSimplePermissions(rel, false, false );
+ ATSimplePermissions(rel, ATT_TABLE );
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
/* No command-specific prep needed */
pass = AT_PASS_MISC;
break;
case AT_DropColumn: /* DROP COLUMN */
- ATSimplePermissions(rel, false, true);
+ ATSimplePermissions(rel,
+ ATT_TABLE|ATT_COMPOSITE_TYPE|ATT_FOREIGN_TABLE);
ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd, lockmode);
/* Recursion occurs during execution phase */
pass = AT_PASS_DROP;
break;
case AT_AddIndex: /* ADD INDEX */
- ATSimplePermissions(rel, false, false );
+ ATSimplePermissions(rel, ATT_TABLE );
/* This command never recurses */
/* No command-specific prep needed */
pass = AT_PASS_ADD_INDEX;
break;
case AT_AddConstraint: /* ADD CONSTRAINT */
- ATSimplePermissions(rel, false, false );
+ ATSimplePermissions(rel, ATT_TABLE );
/* Recursion occurs during execution phase */
/* No command-specific prep needed except saving recurse flag */
if (recurse)
pass = AT_PASS_ADD_CONSTR;
break;
case AT_DropConstraint: /* DROP CONSTRAINT */
- ATSimplePermissions(rel, false, false );
+ ATSimplePermissions(rel, ATT_TABLE );
/* Recursion occurs during execution phase */
/* No command-specific prep needed except saving recurse flag */
if (recurse)
pass = AT_PASS_DROP;
break;
case AT_AlterColumnType: /* ALTER COLUMN TYPE */
- ATSimplePermissions(rel, false, true);
+ ATSimplePermissions(rel,
+ ATT_TABLE|ATT_COMPOSITE_TYPE|ATT_FOREIGN_TABLE);
/* Performs own recursion */
ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd, lockmode);
pass = AT_PASS_ALTER_TYPE;
break;
case AT_ClusterOn: /* CLUSTER ON */
case AT_DropCluster: /* SET WITHOUT CLUSTER */
- ATSimplePermissions(rel, false, false );
+ ATSimplePermissions(rel, ATT_TABLE );
/* These commands never recurse */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
break;
case AT_AddOids: /* SET WITH OIDS */
- ATSimplePermissions(rel, false, false );
+ ATSimplePermissions(rel, ATT_TABLE );
/* Performs own recursion */
if (!rel->rd_rel->relhasoids || recursing)
ATPrepAddOids(wqueue, rel, recurse, cmd, lockmode);
pass = AT_PASS_ADD_COL;
break;
case AT_DropOids: /* SET WITHOUT OIDS */
- ATSimplePermissions(rel, false, false );
+ ATSimplePermissions(rel, ATT_TABLE );
/* Performs own recursion */
if (rel->rd_rel->relhasoids)
{
pass = AT_PASS_DROP;
break;
case AT_SetTableSpace: /* SET TABLESPACE */
- ATSimplePermissionsRelationOrIndex(rel );
+ ATSimplePermissions(rel, ATT_TABLE|ATT_INDEX );
/* This command never recurses */
ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
pass = AT_PASS_MISC; /* doesn't actually matter */
break;
case AT_SetRelOptions: /* SET (...) */
case AT_ResetRelOptions: /* RESET (...) */
- ATSimplePermissionsRelationOrIndex(rel );
+ ATSimplePermissions(rel, ATT_TABLE|ATT_INDEX );
/* This command never recurses */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
break;
case AT_AddInherit: /* INHERIT */
- ATSimplePermissions(rel, false, false );
+ ATSimplePermissions(rel, ATT_TABLE );
/* This command never recurses */
ATPrepAddInherit(rel);
pass = AT_PASS_MISC;
case AT_EnableAlwaysRule:
case AT_EnableReplicaRule:
case AT_DisableRule:
- case AT_DropInherit: /* NO INHERIT */
- ATSimplePermissions(rel, false, false);
+ ATSimplePermissions(rel, ATT_TABLE);
/* These commands never recurse */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
break;
+ case AT_DropInherit: /* NO INHERIT */
+ ATSimplePermissions(rel, ATT_TABLE);
+ /* No command-specific prep needed */
+ pass = AT_PASS_MISC;
+ break;
+ case AT_GenericOptions:
+ ATSimplePermissions(rel, ATT_FOREIGN_TABLE);
+ /* No command-specific prep needed */
+ pass = AT_PASS_MISC;
+ break;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
case AT_DropInherit:
ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
break;
+ case AT_GenericOptions:
+ ATExecGenericOptions(rel, (List *) cmd->def);
+ break;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
{
AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
+ /* Foreign tables have no storage. */
+ if (tab->relkind == RELKIND_FOREIGN_TABLE)
+ continue;
+
/*
* We only need to rewrite the table if at least one column needs to
* be recomputed, or we are adding/removing the OID column.
* - Ensure that it is not a system table
*/
static void
-ATSimplePermissions(Relation rel, bool allowView, bool allowType )
+ATSimplePermissions(Relation rel, int allowed_targets )
{
- if (rel->rd_rel->relkind != RELKIND_RELATION)
+ int actual_target;
+
+ switch (rel->rd_rel->relkind)
{
- if (allowView)
- {
- if (rel->rd_rel->relkind != RELKIND_VIEW)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table or view",
- RelationGetRelationName(rel))));
- }
- else if (allowType)
- {
- if (rel->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table or composite type",
- RelationGetRelationName(rel))));
- }
- else
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table",
- RelationGetRelationName(rel))));
+ case RELKIND_RELATION:
+ actual_target = ATT_TABLE;
+ break;
+ case RELKIND_VIEW:
+ actual_target = ATT_VIEW;
+ break;
+ case RELKIND_INDEX:
+ actual_target = ATT_INDEX;
+ break;
+ case RELKIND_COMPOSITE_TYPE:
+ actual_target = ATT_COMPOSITE_TYPE;
+ break;
+ case RELKIND_FOREIGN_TABLE:
+ actual_target = ATT_FOREIGN_TABLE;
+ break;
+ default:
+ actual_target = 0;
+ break;
}
+ /* Wrong target type? */
+ if ((actual_target & allowed_targets) == 0)
+ ATWrongRelkindError(rel, allowed_targets);
+
/* Permissions checks */
if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
}
/*
- * ATSimplePermissionsRelationOrIndex
+ * ATWrongRelkindError
*
- * - Ensure that it is a relation or an index
- * - Ensure this user is the owner
- * - Ensure that it is not a system table
+ * Throw an error when a relation has been determined to be of the wrong
+ * type.
*/
static void
-ATSimplePermissionsRelationOrIndex(Relation rel )
+ATWrongRelkindError(Relation rel, int allowed_targets )
{
- if (rel->rd_rel->relkind != RELKIND_RELATION &&
- rel->rd_rel->relkind != RELKIND_INDEX)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table or index",
- RelationGetRelationName(rel))));
+ char *msg;
- /* Permissions checks */
- if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
- RelationGetRelationName(rel));
+ switch (allowed_targets)
+ {
+ case ATT_TABLE:
+ msg = _("\"%s\" is not a table");
+ break;
+ case ATT_TABLE|ATT_INDEX:
+ msg = _("\"%s\" is not a table or index");
+ break;
+ case ATT_TABLE|ATT_VIEW:
+ msg = _("\"%s\" is not a table or view");
+ break;
+ case ATT_TABLE|ATT_FOREIGN_TABLE:
+ msg = _("\"%s\" is not a table or foreign table");
+ break;
+ case ATT_TABLE|ATT_COMPOSITE_TYPE|ATT_FOREIGN_TABLE:
+ msg = _("\"%s\" is not a table, composite type, or foreign table");
+ break;
+ case ATT_VIEW:
+ msg = _("\"%s\" is not a view");
+ break;
+ case ATT_FOREIGN_TABLE:
+ msg = _("\"%s\" is not a foreign table");
+ break;
+ default:
+ /* shouldn't get here, add all necessary cases above */
+ msg = _("\"%s\" is of the wrong type");
+ break;
+ }
- if (!allowSystemTableMods && IsSystemRelation(rel))
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("permission denied: \"%s\" is a system catalog",
- RelationGetRelationName(rel))));
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg(msg, RelationGetRelationName(rel))));
}
/*
{
RawColumnDefault *rawEnt;
+ if (relkind == RELKIND_FOREIGN_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("default values on foreign tables are not supported")));
+
rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
rawEnt->attnum = attribute.attnum;
rawEnt->raw_default = copyObject(colDef->raw_default);
* returned by AddRelationNewConstraints, so that the right thing happens
* when a datatype's default applies.
*
- * We skip this step completely for views. For a view, we can only get
- * here from CREATE OR REPLACE VIEW, which historically doesn't set up
- * defaults, not even for domain-typed columns. And in any case we
- * mustn't invoke Phase 3 on a view, since it has no storage.
+ * We skip this step completely for views and foreign tables. For a view,
+ * we can only get here from CREATE OR REPLACE VIEW, which historically
+ * doesn't set up defaults, not even for domain-typed columns. And in any
+ * case we mustn't invoke Phase 3 on a view or foreign table, since they
+ * have no storage.
*/
- if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE && attribute.attnum > 0)
+ if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE
+ && relkind != RELKIND_FOREIGN_TABLE && attribute.attnum > 0)
{
defval = (Expr *) build_column_default(rel, attribute.attnum);
/* At top level, permission check was done in ATPrepCmd, else do it */
if (recursing)
- ATSimplePermissions(rel, false, true );
+ ATSimplePermissions(rel, ATT_TABLE );
/*
* get the number of the attribute
/* At top level, permission check was done in ATPrepCmd, else do it */
if (recursing)
- ATSimplePermissions(rel, false, false );
+ ATSimplePermissions(rel, ATT_TABLE );
/*
* Call AddRelationNewConstraints to do the work, making sure it works on
/* At top level, permission check was done in ATPrepCmd, else do it */
if (recursing)
- ATSimplePermissions(rel, false, false );
+ ATSimplePermissions(rel, ATT_TABLE );
conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
tab->newvals = lappend(tab->newvals, newval);
}
+ else if (tab->relkind == RELKIND_FOREIGN_TABLE)
+ {
+ if (cmd->transform)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("ALTER TYPE USING is not supported on foreign tables")));
+ }
if (tab->relkind == RELKIND_COMPOSITE_TYPE)
{
{
case RELKIND_RELATION:
case RELKIND_VIEW:
+ case RELKIND_FOREIGN_TABLE:
/* ok to change owner */
break;
case RELKIND_INDEX:
default:
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table, view, or sequenc e",
+ errmsg("\"%s\" is not a table, view, sequence, or foreign tabl, or foreign table e",
NameStr(tuple_class->relname))));
}
* Must be owner of both parent and child -- child was checked by
* ATSimplePermissions call in ATPrepCmd
*/
- ATSimplePermissions(parent_rel, false, false );
+ ATSimplePermissions(parent_rel, ATT_TABLE );
/* Permanent rels cannot inherit from temporary ones */
if (RelationUsesTempNamespace(parent_rel)
heap_close(parent_rel, NoLock);
}
+/*
+ * ALTER FOREIGN TABLE OPTIONS (...)
+ */
+static void
+ATExecGenericOptions(Relation rel, List *options)
+{
+ Relation ftrel;
+ ForeignServer *server;
+ ForeignDataWrapper *fdw;
+ HeapTuple tuple;
+ bool isnull;
+ Datum repl_val[Natts_pg_foreign_table];
+ bool repl_null[Natts_pg_foreign_table];
+ bool repl_repl[Natts_pg_foreign_table];
+ Datum datum;
+ Form_pg_foreign_table tableform;
+
+ if (options == NIL)
+ return;
+
+ ftrel = heap_open(ForeignTableRelationId, RowExclusiveLock);
+
+ tuple = SearchSysCacheCopy1(FOREIGNTABLEREL, rel->rd_id);
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("foreign table \"%s\" does not exist",
+ RelationGetRelationName(rel))));
+ tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
+ server = GetForeignServer(tableform->ftserver);
+ fdw = GetForeignDataWrapper(server->fdwid);
+
+ memset(repl_val, 0, sizeof(repl_val));
+ memset(repl_null, false, sizeof(repl_null));
+ memset(repl_repl, false, sizeof(repl_repl));
+
+ /* Extract the current options */
+ datum = SysCacheGetAttr(FOREIGNTABLEREL,
+ tuple,
+ Anum_pg_foreign_table_ftoptions,
+ &isnull);
+ if (isnull)
+ datum = PointerGetDatum(NULL);
+
+ /* Transform the options */
+ datum = transformGenericOptions(ForeignTableRelationId,
+ datum,
+ options,
+ fdw->fdwvalidator);
+
+ if (PointerIsValid(DatumGetPointer(datum)))
+ repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
+ else
+ repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
+
+ repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
+
+ /* Everything looks good - update the tuple */
+
+ tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
+ repl_val, repl_null, repl_repl);
+
+ simple_heap_update(ftrel, &tuple->t_self, tuple);
+ CatalogUpdateIndexes(ftrel, tuple);
+
+ heap_close(ftrel, RowExclusiveLock);
+
+ heap_freetuple(tuple);
+}
+
/*
* Execute ALTER TABLE SET SCHEMA
RelationGetRelationName(rel))));
break;
+ case OBJECT_FOREIGN_TABLE:
+ if (rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a foreign table",
+ RelationGetRelationName(rel))));
+ break;
+
default:
elog(ERROR, "unrecognized object type: %d", (int) stmttype);
}
{
case RELKIND_RELATION:
case RELKIND_VIEW:
+ case RELKIND_FOREIGN_TABLE:
/* ok to change schema */
break;
case RELKIND_SEQUENCE:
default:
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table, view, or sequenc e",
+ errmsg("\"%s\" is not a table, view, sequence, or foreign tabl e",
RelationGetRelationName(rel))));
}
onerel->rd_rel->relkind != RELKIND_TOASTVALUE)
{
ereport(WARNING,
- (errmsg("skipping \"%s\" --- cannot vacuum indexes, views, or special system tables",
+ (errmsg("skipping \"%s\" --- cannot only non-tables or special system tables",
RelationGetRelationName(onerel))));
relation_close(onerel, lmode);
PopActiveSnapshot();
break;
}
break;
+ case RELKIND_FOREIGN_TABLE:
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot change foreign table \"%s\"",
+ RelationGetRelationName(resultRelationDesc))));
+ break;
default:
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
return newnode;
}
-static CreateStmt *
-_copyCreateStmt(CreateStmt *from)
+/*
+ * CopyCreateStmtFields
+ *
+ * This function copies the fields of the CreateStmt node. It is used by
+ * copy functions for classes which inherit from CreateStmt.
+ */
+static void
+CopyCreateStmtFields(CreateStmt *from, CreateStmt *newnode)
{
- CreateStmt *newnode = makeNode(CreateStmt);
-
COPY_NODE_FIELD(relation);
COPY_NODE_FIELD(tableElts);
COPY_NODE_FIELD(inhRelations);
COPY_SCALAR_FIELD(oncommit);
COPY_STRING_FIELD(tablespacename);
COPY_SCALAR_FIELD(if_not_exists);
+}
+
+static CreateStmt *
+_copyCreateStmt(CreateStmt *from)
+{
+ CreateStmt *newnode = makeNode(CreateStmt);
+
+ CopyCreateStmtFields(from, newnode);
return newnode;
}
return newnode;
}
+static CreateForeignTableStmt *
+_copyCreateForeignTableStmt(CreateForeignTableStmt *from)
+{
+ CreateForeignTableStmt *newnode = makeNode(CreateForeignTableStmt);
+
+ CopyCreateStmtFields((CreateStmt *) from, (CreateStmt *) newnode);
+
+ COPY_STRING_FIELD(servername);
+ COPY_NODE_FIELD(options);
+
+ return newnode;
+}
+
static CreateTrigStmt *
_copyCreateTrigStmt(CreateTrigStmt *from)
{
case T_DropUserMappingStmt:
retval = _copyDropUserMappingStmt(from);
break;
+ case T_CreateForeignTableStmt:
+ retval = _copyCreateForeignTableStmt(from);
+ break;
case T_CreateTrigStmt:
retval = _copyCreateTrigStmt(from);
break;
return true;
}
+static bool
+_equalCreateForeignTableStmt(CreateForeignTableStmt *a, CreateForeignTableStmt *b)
+{
+ if (!_equalCreateStmt(&a->base, &b->base))
+ return false;
+
+ COMPARE_STRING_FIELD(servername);
+ COMPARE_NODE_FIELD(options);
+
+ return true;
+}
+
static bool
_equalCreateTrigStmt(CreateTrigStmt *a, CreateTrigStmt *b)
{
case T_DropUserMappingStmt:
retval = _equalDropUserMappingStmt(a, b);
break;
+ case T_CreateForeignTableStmt:
+ retval = _equalCreateForeignTableStmt(a, b);
+ break;
case T_CreateTrigStmt:
retval = _equalCreateTrigStmt(a, b);
break;
WRITE_BOOL_FIELD(if_not_exists);
}
+static void
+_outCreateForeignTableStmt(StringInfo str, CreateForeignTableStmt *node)
+{
+ WRITE_NODE_TYPE("CREATEFOREIGNTABLESTMT");
+
+ _outCreateStmt(str, (CreateStmt *) &node->base);
+
+ WRITE_STRING_FIELD(servername);
+ WRITE_NODE_FIELD(options);
+}
+
static void
_outIndexStmt(StringInfo str, IndexStmt *node)
{
case T_CreateStmt:
_outCreateStmt(str, obj);
break;
+ case T_CreateForeignTableStmt:
+ _outCreateForeignTableStmt(str, obj);
+ break;
case T_IndexStmt:
_outIndexStmt(str, obj);
break;
*/
relation = heap_open(relationObjectId, NoLock);
+ /* Foreign table scans are not implemented yet. */
+ if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("foreign table scans are not yet supported")));
+
rel->min_attr = FirstLowInvalidHeapAttributeNumber + 1;
rel->max_attr = RelationGetNumberOfAttributes(relation);
rel->reltablespace = RelationGetForm(relation)->reltablespace;
AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
+ AlterForeignTableStmt
AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
AlterRoleStmt AlterRoleSetStmt
AlterDefaultPrivilegesStmt DefACLAction
CreateDomainStmt CreateGroupStmt CreateOpClassStmt
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
- CreateFdwStmt CreateForeignServerStmt CreateAssertStmt CreateTrigStmt
+ CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
+ CreateAssertStmt CreateTrigStmt
CreateUserStmt CreateUserMappingStmt CreateRoleStmt
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
%type stmtblock stmtmulti
OptTableElementList TableElementList OptInherit definition
OptTypedTableElementList TypedTableElementList
+ OptForeignTableElementList ForeignTableElementList
reloptions opt_reloptions
OptWith opt_distinct opt_definition func_args func_args_list
func_args_with_defaults func_args_with_defaults_list
%type set_rest SetResetClause
%type TableElement TypedTableElement ConstraintElem TableFuncElement
+ ForeignTableElement
%type columnDef columnOptions
%type def_elem reloption_elem old_aggr_elem
%type def_arg columnElem where_clause where_or_current_clause
| AlterEnumStmt
| AlterFdwStmt
| AlterForeignServerStmt
+ | AlterForeignTableStmt
| AlterFunctionStmt
| AlterGroupStmt
| AlterObjectSchemaStmt
| CreateDomainStmt
| CreateFdwStmt
| CreateForeignServerStmt
+ | CreateForeignTableStmt
| CreateFunctionStmt
| CreateGroupStmt
| CreateOpClassStmt
n->def = (Node *)$2;
$$ = (Node *)n;
}
+ | alter_generic_options
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_GenericOptions;
+ n->def = (Node *)$1;
+ $$ = (Node *) n;
+ }
;
alter_column_default:
}
;
+/*****************************************************************************
+ *
+ * QUERY:
+ * CREATE FOREIGN TABLE relname (...) SERVER name (...)
+ *
+ *****************************************************************************/
+
+CreateForeignTableStmt:
+ CREATE FOREIGN TABLE qualified_name
+ OptForeignTableElementList
+ SERVER name create_generic_options
+ {
+ CreateForeignTableStmt *n = makeNode(CreateForeignTableStmt);
+ $4->relpersistence = RELPERSISTENCE_PERMANENT;
+ n->base.relation = $4;
+ n->base.tableElts = $5;
+ n->base.inhRelations = NIL;
+ n->base.if_not_exists = false;
+ /* FDW-specific data */
+ n->servername = $7;
+ n->options = $8;
+ $$ = (Node *) n;
+ }
+ | CREATE FOREIGN TABLE IF_P NOT EXISTS qualified_name
+ OptForeignTableElementList
+ SERVER name create_generic_options
+ {
+ CreateForeignTableStmt *n = makeNode(CreateForeignTableStmt);
+ $7->relpersistence = RELPERSISTENCE_PERMANENT;
+ n->base.relation = $7;
+ n->base.tableElts = $8;
+ n->base.inhRelations = NIL;
+ n->base.if_not_exists = true;
+ /* FDW-specific data */
+ n->servername = $10;
+ n->options = $11;
+ $$ = (Node *) n;
+ }
+ ;
+
+OptForeignTableElementList:
+ '(' ForeignTableElementList ')' { $$ = $2; }
+ | '(' ')' { $$ = NIL; }
+ ;
+
+ForeignTableElementList:
+ ForeignTableElement
+ {
+ $$ = list_make1($1);
+ }
+ | ForeignTableElementList ',' ForeignTableElement
+ {
+ $$ = lappend($1, $3);
+ }
+ ;
+
+ForeignTableElement:
+ columnDef { $$ = $1; }
+ ;
+
+/*****************************************************************************
+ *
+ * QUERY:
+ * ALTER FOREIGN TABLE relname [...]
+ *
+ *****************************************************************************/
+
+AlterForeignTableStmt:
+ ALTER FOREIGN TABLE relation_expr alter_table_cmds
+ {
+ AlterTableStmt *n = makeNode(AlterTableStmt);
+ n->relation = $4;
+ n->cmds = $5;
+ n->relkind = OBJECT_FOREIGN_TABLE;
+ $$ = (Node *)n;
+ }
+ ;
+
/*****************************************************************************
*
* QUERY:
| VIEW { $$ = OBJECT_VIEW; }
| INDEX { $$ = OBJECT_INDEX; }
| TYPE_P { $$ = OBJECT_TYPE; }
+ | FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; }
| DOMAIN_P { $$ = OBJECT_DOMAIN; }
| CONVERSION_P { $$ = OBJECT_CONVERSION; }
| SCHEMA { $$ = OBJECT_SCHEMA; }
* CONVERSION | LANGUAGE | OPERATOR CLASS | LARGE OBJECT |
* CAST | COLUMN | SCHEMA | TABLESPACE | ROLE |
* TEXT SEARCH PARSER | TEXT SEARCH DICTIONARY |
- * TEXT SEARCH TEMPLATE |
- * TEXT SEARCH CONFIGURATION ] |
+ * TEXT SEARCH TEMPLATE | TEXT SEARCH CONFIGURATION |
+ * FOREIGN TABLE ] |
* AGGREGATE
(arg1, ...) |
* FUNCTION (arg1, arg2, ...) |
* OPERATOR (leftoperand_typ, rightoperand_typ) |
| CONVERSION_P { $$ = OBJECT_CONVERSION; }
| TABLESPACE { $$ = OBJECT_TABLESPACE; }
| ROLE { $$ = OBJECT_ROLE; }
+ | FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; }
;
comment_text:
security_label_type:
COLUMN { $$ = OBJECT_COLUMN; }
+ | FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; }
| SCHEMA { $$ = OBJECT_SCHEMA; }
| SEQUENCE { $$ = OBJECT_SEQUENCE; }
| TABLE { $$ = OBJECT_TABLE; }
n->objs = $3;
$$ = n;
}
+ | FOREIGN TABLE qualified_name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_OBJECT;
+ n->objtype = ACL_OBJECT_FOREIGN_TABLE;
+ n->objs = $3;
+ $$ = n;
+ }
| FUNCTION function_with_argtypes_list
{
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
n->newname = $6;
$$ = (Node *)n;
}
+ | ALTER FOREIGN TABLE relation_expr RENAME TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+ n->renameType = OBJECT_FOREIGN_TABLE;
+ n->relation = $4;
+ n->subname = NULL;
+ n->newname = $7;
+ $$ = (Node *)n;
+ }
| ALTER TABLE relation_expr RENAME opt_column name TO name
{
RenameStmt *n = makeNode(RenameStmt);
n->renameType = OBJECT_COLUMN;
+ n->relationType = OBJECT_TABLE;
n->relation = $3;
n->subname = $6;
n->newname = $8;
$$ = (Node *)n;
}
+ | ALTER FOREIGN TABLE relation_expr RENAME opt_column name TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+ n->renameType = OBJECT_COLUMN;
+ n->relationType = OBJECT_FOREIGN_TABLE;
+ n->relation = $4;
+ n->subname = $7;
+ n->newname = $9;
+ $$ = (Node *)n;
+ }
| ALTER TRIGGER name ON qualified_name RENAME TO name
{
RenameStmt *n = makeNode(RenameStmt);
{
RenameStmt *n = makeNode(RenameStmt);
n->renameType = OBJECT_ATTRIBUTE;
+ n->relationType = OBJECT_TYPE;
n->relation = makeRangeVarFromAnyName($3, @3, yyscanner);
n->subname = $6;
n->newname = $8;
n->newschema = $6;
$$ = (Node *)n;
}
+ | ALTER FOREIGN TABLE relation_expr SET SCHEMA name
+ {
+ AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ n->objectType = OBJECT_FOREIGN_TABLE;
+ n->relation = $4;
+ n->newschema = $7;
+ $$ = (Node *)n;
+ }
| ALTER TYPE_P any_name SET SCHEMA name
{
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
/* State shared by transformCreateStmt and its subroutines */
typedef struct
{
- const char *stmtType; /* "CREATE TABLE" or "ALTER TABLE" */
+ const char *stmtType; /* "CREATE [FOREIGN] TABLE" or "ALTER TABLE" */
RangeVar *relation; /* relation to create */
Relation rel; /* opened/locked rel, if ALTER */
List *inhRelations; /* relations to inherit from */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
- cxt.stmtType = "CREATE TABLE";
+ if (IsA(stmt, CreateForeignTableStmt))
+ cxt.stmtType = "CREATE FOREIGN TABLE";
+ else
+ cxt.stmtType = "CREATE TABLE";
cxt.relation = stmt->relation;
cxt.rel = NULL;
cxt.inhRelations = stmt->inhRelations;
case T_AlterUserMappingStmt:
case T_DropUserMappingStmt:
case T_AlterTableSpaceOptionsStmt:
+ case T_CreateForeignTableStmt:
case T_SecLabelStmt:
PreventCommandIfReadOnly(CreateCommandTag(parsetree));
break;
break;
case T_CreateStmt:
+ case T_CreateForeignTableStmt:
{
List *stmts;
ListCell *l;
AlterTableCreateToastTable(relOid, toast_options);
}
+ else if (IsA(stmt, CreateForeignTableStmt))
+ {
+ /* Create the table itself */
+ relOid = DefineRelation((CreateStmt *) stmt,
+ RELKIND_FOREIGN_TABLE,
+ InvalidOid);
+
+ /*
+ * Unless "IF NOT EXISTS" was specified and the
+ * relation already exists, create the pg_foreign_table
+ * entry.
+ */
+ if (relOid != InvalidOid)
+ CreateForeignTable((CreateForeignTableStmt *) stmt,
+ relOid);
+ }
else
{
/* Recurse for anything else */
case OBJECT_SEQUENCE:
case OBJECT_VIEW:
case OBJECT_INDEX:
+ case OBJECT_FOREIGN_TABLE:
RemoveRelations(stmt);
break;
tag = "DROP USER MAPPING";
break;
+ case T_CreateForeignTableStmt:
+ tag = "CREATE FOREIGN TABLE";
+ break;
+
case T_DropStmt:
switch (((DropStmt *) parsetree)->removeType)
{
case OBJECT_TSCONFIGURATION:
tag = "DROP TEXT SEARCH CONFIGURATION";
break;
+ case OBJECT_FOREIGN_TABLE:
+ tag = "DROP FOREIGN TABLE";
+ break;
default:
tag = "???";
}
tag = "ALTER SEQUENCE";
break;
case OBJECT_COLUMN:
+ {
+ RenameStmt *stmt = (RenameStmt *) parsetree;
+ if (stmt->relationType == OBJECT_FOREIGN_TABLE)
+ tag = "ALTER FOREIGN TABLE";
+ else
+ tag = "ALTER TABLE";
+ }
+ break;
case OBJECT_TABLE:
tag = "ALTER TABLE";
break;
case OBJECT_VIEW:
tag = "ALTER VIEW";
break;
+ case OBJECT_FOREIGN_TABLE:
+ tag = "ALTER FOREIGN TABLE";
+ break;
case OBJECT_TSPARSER:
tag = "ALTER TEXT SEARCH PARSER";
break;
case OBJECT_VIEW:
tag = "ALTER VIEW";
break;
+ case OBJECT_FOREIGN_TABLE:
+ tag = "ALTER FOREIGN TABLE";
+ break;
default:
tag = "???";
break;
case OBJECT_FOREIGN_SERVER:
tag = "ALTER SERVER";
break;
+ case OBJECT_FOREIGN_TABLE:
+ tag = "ALTER FOREIGN TABLE";
+ break;
default:
tag = "???";
break;
case OBJECT_VIEW:
tag = "ALTER VIEW";
break;
+ case OBJECT_FOREIGN_TABLE:
+ tag = "ALTER FOREIGN TABLE";
+ break;
default:
tag = "???";
break;
break;
case T_CreateStmt:
+ case T_CreateForeignTableStmt:
lev = LOGSTMT_DDL;
break;
world_default = ACL_NO_RIGHTS;
owner_default = ACL_ALL_RIGHTS_FOREIGN_SERVER;
break;
+ case ACL_OBJECT_FOREIGN_TABLE:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_ALL_RIGHTS_FOREIGN_TABLE;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
#include "catalog/pg_enum.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
+#include "catalog/pg_foreign_table.h"
#include "catalog/pg_language.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
},
32
},
+ {ForeignTableRelationId, /* FOREIGNTABLEREL */
+ ForeignTableRelidIndexId,
+ 1,
+ {
+ Anum_pg_foreign_table_ftrelid,
+ 0,
+ 0,
+ 0
+ },
+ 128
+ },
{IndexRelationId, /* INDEXRELID */
IndexRelidIndexId,
1,
CONVERT_PRIV('U', "USAGE");
else if (strcmp(type, "FOREIGN SERVER") == 0)
CONVERT_PRIV('U', "USAGE");
+ else if (strcmp(type, "FOREIGN TABLE") == 0)
+ CONVERT_PRIV('r', "SELECT");
else if (strcmp(type, "LARGE OBJECT") == 0)
{
CONVERT_PRIV('r', "SELECT");
strcmp(type, "DOMAIN") == 0 ||
strcmp(type, "TABLE") == 0 ||
strcmp(type, "TYPE") == 0 ||
+ strcmp(type, "FOREIGN TABLE") == 0 ||
strcmp(type, "TEXT SEARCH DICTIONARY") == 0 ||
strcmp(type, "TEXT SEARCH CONFIGURATION") == 0)
{
strcmp(te->desc, "TYPE") == 0 ||
strcmp(te->desc, "VIEW") == 0 ||
strcmp(te->desc, "SEQUENCE") == 0 ||
+ strcmp(te->desc, "FOREIGN TABLE") == 0 ||
strcmp(te->desc, "TEXT SEARCH DICTIONARY") == 0 ||
strcmp(te->desc, "TEXT SEARCH CONFIGURATION") == 0 ||
strcmp(te->desc, "FOREIGN DATA WRAPPER") == 0 ||
"SELECT c.oid"
"\nFROM pg_catalog.pg_class c"
"\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace"
- "\nWHERE c.relkind in ('%c', '%c', '%c')\n",
- RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW);
+ "\nWHERE c.relkind in ('%c', '%c', '%c', '%c')\n",
+ RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,
+ RELKIND_FOREIGN_TABLE);
processSQLNamePattern(g_conn, query, cell->val, true, false,
"n.nspname", "c.relname", NULL,
"pg_catalog.pg_table_is_visible(c.oid)");
/* Skip SEQUENCEs (handled elsewhere) */
if (tblinfo[i].relkind == RELKIND_SEQUENCE)
continue;
+ /* Skip FOREIGN TABLEs (no data to dump) */
+ if (tblinfo[i].relkind == RELKIND_FOREIGN_TABLE)
+ continue;
/* Skip unlogged tables if so requested */
if (tblinfo[i].relpersistence == RELPERSISTENCE_UNLOGGED
&& no_unlogged_table_data)
"d.objsubid = 0 AND "
"d.refclassid = c.tableoid AND d.deptype = 'a') "
"LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
- "WHERE c.relkind in ('%c', '%c', '%c', '%c') "
+ "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c' ) "
"ORDER BY c.oid",
username_subquery,
RELKIND_SEQUENCE,
RELKIND_RELATION, RELKIND_SEQUENCE,
- RELKIND_VIEW, RELKIND_COMPOSITE_TYPE);
+ RELKIND_VIEW, RELKIND_COMPOSITE_TYPE,
+ RELKIND_FOREIGN_TABLE);
}
else if (g_fout->remoteVersion >= 90000)
{
* assume our lock on the child is enough to prevent schema
* alterations to parent tables.
*
- * NOTE: it'd be kinda nice to lock views and sequence s too, not only
+ * NOTE: it'd be kinda nice to lock other relation s too, not only
* plain tables, but the backend doesn't presently allow that.
*/
if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION)
/* Handle the ACL here */
namecopy = strdup(fmtId(tbinfo->dobj.name));
dumpACL(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
- (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE",
+ (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" :
+ (tbinfo->relkind == RELKIND_FOREIGN_TABLE) ? "FOREIGN TABLE" :
+ "TABLE",
namecopy, NULL, tbinfo->dobj.name,
tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
tbinfo->relacl);
int j,
k;
bool toast_set = false;
+ char *srvname;
+ char *ftoptions = NULL;
/* Make sure we are in proper schema */
selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
}
else
{
- reltypename = "TABLE";
+ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
+ {
+ int i_srvname;
+ int i_ftoptions;
+
+ reltypename = "FOREIGN TABLE";
+
+ /* retrieve name of foreign server and generic options */
+ appendPQExpBuffer(query,
+ "SELECT fs.srvname, array_to_string(ARRAY("
+ " SELECT option_name || ' ' || quote_literal(option_value)"
+ " FROM pg_options_to_table(ftoptions)), ', ') AS ftoptions "
+ "FROM pg_foreign_table ft JOIN pg_foreign_server fs "
+ " ON (fs.oid = ft.ftserver) "
+ "WHERE ft.ftrelid = %u", tbinfo->dobj.catId.oid);
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+ i_srvname = PQfnumber(res, "srvname");
+ i_ftoptions = PQfnumber(res, "ftoptions");
+ srvname = strdup(PQgetvalue(res, 0, i_srvname));
+ ftoptions = strdup(PQgetvalue(res, 0, i_ftoptions));
+ PQclear(res);
+ }
+ else
+ {
+ reltypename = "TABLE";
+ srvname = NULL;
+ ftoptions = NULL;
+ }
numParents = tbinfo->numParents;
parents = tbinfo->parents;
* DROP must be fully qualified in case same name appears in
* pg_catalog
*/
- appendPQExpBuffer(delq, "DROP TABLE %s." ,
+ appendPQExpBuffer(delq, "DROP %s %s.", reltypename ,
fmtId(tbinfo->dobj.namespace->dobj.name));
appendPQExpBuffer(delq, "%s;\n",
fmtId(tbinfo->dobj.name));
if (binary_upgrade)
binary_upgrade_set_relfilenodes(q, tbinfo->dobj.catId.oid, false);
- if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED)
- appendPQExpBuffer(q, "CREATE UNLOGGED TABLE %s",
- fmtId(tbinfo->dobj.name));
- else
- appendPQExpBuffer(q, "CREATE TABLE %s",
- fmtId(tbinfo->dobj.name));
+ appendPQExpBuffer(q, "CREATE %s%s %s",
+ tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
+ "UNLOGGED " : "",
+ reltypename,
+ fmtId(tbinfo->dobj.name));
if (tbinfo->reloftype)
appendPQExpBuffer(q, " OF %s", tbinfo->reloftype);
actual_atts = 0;
appendPQExpBuffer(q, ")");
}
+ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
+ appendPQExpBuffer(q, "\nSERVER %s", srvname);
+
if ((tbinfo->reloptions && strlen(tbinfo->reloptions) > 0) ||
(tbinfo->toast_reloptions && strlen(tbinfo->toast_reloptions) > 0))
{
appendPQExpBuffer(q, ")");
}
+ /* Dump generic options if any */
+ if (ftoptions && ftoptions[0])
+ appendPQExpBuffer(q, "\nOPTIONS (%s)", ftoptions);
+
appendPQExpBuffer(q, ";\n");
/*
* order. That also means we have to take care about setting
* attislocal correctly, plus fix up any inherited CHECK constraints.
*/
- if (binary_upgrade)
+ if (binary_upgrade && tbinfo->relkind == RELKIND_RELATION )
{
for (j = 0; j < tbinfo->numatts; j++)
{
success = describeTableDetails(pattern, show_verbose, show_system);
else
/* standard listing of interesting things */
- success = listTables("tvs", NULL, show_verbose, show_system);
+ success = listTables("tvsE ", NULL, show_verbose, show_system);
break;
case 'a':
success = describeAggregates(pattern, show_verbose, show_system);
case 'v':
case 'i':
case 's':
+ case 'E':
success = listTables(&cmd[1], pattern, show_verbose, show_system);
break;
case 'r':
case 'w':
success = listForeignDataWrappers(pattern, show_verbose);
break;
+ case 't':
+ success = listForeignTables(pattern, show_verbose);
+ break;
default:
status = PSQL_CMD_UNKNOWN;
break;
printfPQExpBuffer(&buf,
"SELECT n.nspname as \"%s\",\n"
" c.relname as \"%s\",\n"
- " CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'S' THEN '%s' END as \"%s\",\n"
+ " CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'S' THEN '%s' WHEN 'f' THEN '%s' END as \"%s\",\n"
" ",
gettext_noop("Schema"),
gettext_noop("Name"),
gettext_noop("table"), gettext_noop("view"), gettext_noop("sequence"),
+ gettext_noop("foreign table"),
gettext_noop("Type"));
printACLColumn(&buf, "c.relacl");
appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_class c\n"
" LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
- "WHERE c.relkind IN ('r', 'v', 'S')\n");
+ "WHERE c.relkind IN ('r', 'v', 'S', 'f' )\n");
/*
* Unless a schema pattern is specified, we suppress system and temp
" n.nspname as nspname,\n"
" CAST(c.relname AS pg_catalog.text) as name,\n"
" CAST(\n"
- " CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' END"
+ " CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' WHEN 'f' THEN '%s' END"
" AS pg_catalog.text) as object\n"
" FROM pg_catalog.pg_class c\n"
" LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
- " WHERE c.relkind IN ('r', 'v', 'i', 'S')\n",
+ " WHERE c.relkind IN ('r', 'v', 'i', 'S', 'f' )\n",
gettext_noop("table"),
gettext_noop("view"),
gettext_noop("index"),
- gettext_noop("sequence"));
+ gettext_noop("sequence"),
+ gettext_noop("foreign table"));
if (!showSystem && !pattern)
appendPQExpBuffer(&buf, " AND n.nspname <> 'pg_catalog'\n"
printfPQExpBuffer(&title, _("Composite type \"%s.%s\""),
schemaname, relationname);
break;
+ case 'f':
+ printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""),
+ schemaname, relationname);
+ break;
default:
/* untranslated unknown relkind */
printfPQExpBuffer(&title, "?%c? \"%s.%s\"",
headers[1] = gettext_noop("Type");
cols = 2;
- if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v')
+ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' ||
+ tableinfo.relkind == 'f')
{
show_modifiers = true;
headers[cols++] = gettext_noop("Modifiers");
PQclear(result);
}
}
- else if (tableinfo.relkind == 'r')
+ else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'f' )
{
/* Footer information about a table */
PGresult *result = NULL;
/*
* Finish printing the footer information about a table.
*/
- if (tableinfo.relkind == 'r')
+ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'f' )
{
PGresult *result;
int tuples;
+ /* print foreign server name */
+ if (tableinfo.relkind == 'f')
+ {
+ /* Footer information about foreign table */
+ printfPQExpBuffer(&buf,
+ "SELECT s.srvname\n"
+ "FROM pg_catalog.pg_foreign_table f,\n"
+ " pg_catalog.pg_foreign_server s\n"
+ "WHERE f.ftrelid = %s AND s.oid = f.ftserver",
+ oid);
+ result = PSQLexec(buf.data, false);
+ if (!result)
+ goto error_return;
+ else if (PQntuples(result) != 1)
+ {
+ PQclear(result);
+ goto error_return;
+ }
+
+ printfPQExpBuffer(&buf, "Server: %s",
+ PQgetvalue(result, 0, 0));
+ printTableAddFooter(&cont, buf.data);
+ PQclear(result);
+ }
+
/* print inherited tables */
printfPQExpBuffer(&buf, "SELECT c.oid::pg_catalog.regclass FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i WHERE c.oid=i.inhparent AND i.inhrelid = '%s' ORDER BY inhseqno", oid);
* i - indexes
* v - views
* s - sequences
+ * E - foreign table (Note: different from 'f', the relkind value)
* (any order of the above is fine)
- * If tabtypes is empty, we default to \dtvs.
+ * If tabtypes is empty, we default to \dtvsE .
*/
bool
listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem)
bool showIndexes = strchr(tabtypes, 'i') != NULL;
bool showViews = strchr(tabtypes, 'v') != NULL;
bool showSeq = strchr(tabtypes, 's') != NULL;
+ bool showForeign = strchr(tabtypes, 'E') != NULL;
PQExpBufferData buf;
PGresult *res;
printQueryOpt myopt = pset.popt;
static const bool translate_columns[] = {false, false, true, false, false, false, false};
- if (!(showTables || showIndexes || showViews || showSeq))
- showTables = showViews = showSeq = true;
+ if (!(showTables || showIndexes || showViews || showSeq || showForeign ))
+ showTables = showViews = showSeq = showForeign = true;
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
"SELECT n.nspname as \"%s\",\n"
" c.relname as \"%s\",\n"
- " CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' WHEN 's' THEN '%s' END as \"%s\",\n"
+ " CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' WHEN 's' THEN '%s' WHEN 'f' THEN '%s' END as \"%s\",\n"
" pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
gettext_noop("Schema"),
gettext_noop("Name"),
gettext_noop("index"),
gettext_noop("sequence"),
gettext_noop("special"),
+ gettext_noop("foreign table"),
gettext_noop("Type"),
gettext_noop("Owner"));
if (showSystem || pattern)
appendPQExpBuffer(&buf, "'s',"); /* was RELKIND_SPECIAL in <=
* 8.1 */
+ if (showForeign)
+ appendPQExpBuffer(&buf, "'f',");
+
appendPQExpBuffer(&buf, "''"); /* dummy */
appendPQExpBuffer(&buf, ")\n");
return true;
}
+/*
+ * \det
+ *
+ * Describes foreign tables.
+ */
+bool
+listForeignTables(const char *pattern, bool verbose)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+
+ if (pset.sversion < 90100)
+ {
+ fprintf(stderr, _("The server (version %d.%d) does not support foreign table.\n"),
+ pset.sversion / 10000, (pset.sversion / 100) % 100);
+ return true;
+ }
+
+ initPQExpBuffer(&buf);
+ printfPQExpBuffer(&buf,
+ "SELECT n.nspname AS \"%s\",\n"
+ " c.relname AS \"%s\",\n"
+ " s.srvname AS \"%s\"",
+ gettext_noop("Schema"),
+ gettext_noop("Table"),
+ gettext_noop("Server"));
+
+ if (verbose)
+ appendPQExpBuffer(&buf,
+ ",\n ft.ftoptions AS \"%s\"",
+ gettext_noop("Options"));
+
+ appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_foreign_table ft,");
+ appendPQExpBuffer(&buf, "\n pg_catalog.pg_class c,");
+ appendPQExpBuffer(&buf, "\n pg_catalog.pg_namespace n,");
+ appendPQExpBuffer(&buf, "\n pg_catalog.pg_foreign_server s\n");
+ appendPQExpBuffer(&buf, "\nWHERE c.oid = ft.ftrelid");
+ appendPQExpBuffer(&buf, "\nAND s.oid = ft.ftserver\n");
+ appendPQExpBuffer(&buf, "\nAND n.oid = c.relnamespace\n");
+
+ processSQLNamePattern(pset.db, &buf, pattern, true, false,
+ NULL, "n.nspname", "c.relname", NULL);
+
+ appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
+
+ res = PSQLexec(buf.data, false);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of foreign tables");
+ myopt.translate_header = true;
+
+ printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+ PQclear(res);
+ return true;
+}
+
/*
* printACLColumn
*
/* \deu */
extern bool listUserMappings(const char *pattern, bool verbose);
+/* \det */
+extern bool listForeignTables(const char *pattern, bool verbose);
+
#endif /* DESCRIBE_H */
{
FILE *output;
- output = PageOutput(90 , pager);
+ output = PageOutput(92 , pager);
/* if you add/remove a line here, change the row count above */
fprintf(output, _(" \\dd[S] [PATTERN] show comments on objects\n"));
fprintf(output, _(" \\ddp [PATTERN] list default privileges\n"));
fprintf(output, _(" \\dD[S] [PATTERN] list domains\n"));
+ fprintf(output, _(" \\det[+] [PATTERN] list foreign tables\n"));
fprintf(output, _(" \\des[+] [PATTERN] list foreign servers\n"));
fprintf(output, _(" \\deu[+] [PATTERN] list user mappings\n"));
fprintf(output, _(" \\dew[+] [PATTERN] list foreign-data wrappers\n"));
fprintf(output, _(" \\dT[S+] [PATTERN] list data types\n"));
fprintf(output, _(" \\du[+] [PATTERN] list roles\n"));
fprintf(output, _(" \\dv[S+] [PATTERN] list views\n"));
+ fprintf(output, _(" \\dE[S+] [PATTERN] list foreign tables\n"));
fprintf(output, _(" \\l[+] list all databases\n"));
fprintf(output, _(" \\sf[+] FUNCNAME show a function's definition\n"));
fprintf(output, _(" \\z [PATTERN] same as \\dp\n"));
NULL
};
+static const SchemaQuery Query_for_list_of_foreign_tables = {
+ /* catname */
+ "pg_catalog.pg_class c",
+ /* selcondition */
+ "c.relkind IN ('f')",
+ /* viscondition */
+ "pg_catalog.pg_table_is_visible(c.oid)",
+ /* namespace */
+ "c.relnamespace",
+ /* result */
+ "pg_catalog.quote_ident(c.relname)",
+ /* qualresult */
+ NULL
+};
+
static const SchemaQuery Query_for_list_of_tables = {
/* catname */
"pg_catalog.pg_class c",
NULL
};
-static const SchemaQuery Query_for_list_of_tisv = {
+static const SchemaQuery Query_for_list_of_tisvf = {
/* catname */
"pg_catalog.pg_class c",
/* selcondition */
- "c.relkind IN ('r', 'i', 'S', 'v')",
+ "c.relkind IN ('r', 'i', 'S', 'v', 'f' )",
/* viscondition */
"pg_catalog.pg_table_is_visible(c.oid)",
/* namespace */
NULL
};
-static const SchemaQuery Query_for_list_of_tsv = {
+static const SchemaQuery Query_for_list_of_tsvf = {
/* catname */
"pg_catalog.pg_class c",
/* selcondition */
- "c.relkind IN ('r', 'S', 'v')",
+ "c.relkind IN ('r', 'S', 'v', 'f' )",
/* viscondition */
"pg_catalog.pg_table_is_visible(c.oid)",
/* namespace */
{"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, true},
{"DOMAIN", NULL, &Query_for_list_of_domains},
{"FOREIGN DATA WRAPPER", NULL, NULL},
+ {"FOREIGN TABLE", NULL, NULL},
{"FUNCTION", NULL, &Query_for_list_of_functions},
{"GROUP", Query_for_list_of_roles},
{"LANGUAGE", Query_for_list_of_languages},
static const char *const backslash_commands[] = {
"\\a", "\\connect", "\\conninfo", "\\C", "\\cd", "\\copy", "\\copyright",
- "\\d", "\\da", "\\db", "\\dc", "\\dC", "\\dd", "\\dD", "\\des", "\\deu", "\\dew", "\\df",
+ "\\d", "\\da", "\\db", "\\dc", "\\dC", "\\dd", "\\dD", "\\des", "\\det", "\\de u", "\\dew", "\\df",
"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl",
"\\dn", "\\do", "\\dp", "\\drds", "\\ds", "\\dS", "\\dt", "\\dT", "\\dv", "\\du",
"\\e", "\\echo", "\\ef", "\\encoding",
pg_strcasecmp(prev3_wd, "TABLE") != 0)
{
static const char *const list_ALTER[] =
- {"AGGREGATE", "CONVERSION", "DATABASE", "DEFAULT PRIVILEGES", "DOMAIN", "FOREIGN DATA WRAPPER", "FUNCTION",
+ {"AGGREGATE", "CONVERSION", "DATABASE", "DEFAULT PRIVILEGES", "DOMAIN", "FOREIGN DATA WRAPPER", "FOREIGN TABLE", "F UNCTION",
"GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "OPERATOR", "ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE",
"TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE", "USER", "USER MAPPING FOR", "VIEW", NULL};
COMPLETE_WITH_LIST(list_ALTERDATABASE);
}
+ /* ALTER FOREIGN */
+ else if (pg_strcasecmp(prev2_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev_wd, "FOREIGN") == 0)
+ {
+ static const char *const list_ALTER_FOREIGN[] =
+ {"DATA WRAPPER", "TABLE", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTER_FOREIGN);
+ }
+
/* ALTER FOREIGN DATA WRAPPER */
else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
pg_strcasecmp(prev4_wd, "FOREIGN") == 0 &&
COMPLETE_WITH_LIST(list_ALTER_FDW);
}
+ /* ALTER FOREIGN TABLE */
+ else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev3_wd, "FOREIGN") == 0 &&
+ pg_strcasecmp(prev2_wd, "TABLE") == 0)
+ {
+ static const char *const list_ALTER_FOREIGN_TABLE[] =
+ {"ALTER", "DROP", "RENAME", "OWNER TO", "SET SCHEMA", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTER_FOREIGN_TABLE);
+ }
+
/* ALTER INDEX */
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
pg_strcasecmp(prev2_wd, "INDEX") == 0)
pg_strcasecmp(prev_wd, "ON") == 0)
{
static const char *const list_COMMENT[] =
- {"CAST", "CONVERSION", "DATABASE", "INDEX", "LANGUAGE", "RULE", "SCHEMA",
+ {"CAST", "CONVERSION", "DATABASE", "FOREIGN TABLE", " INDEX", "LANGUAGE", "RULE", "SCHEMA",
"SEQUENCE", "TABLE", "TYPE", "VIEW", "COLUMN", "AGGREGATE", "FUNCTION",
"OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", "LARGE OBJECT",
"TABLESPACE", "TEXT SEARCH", "ROLE", NULL};
pg_strcasecmp(prev_wd, "TEMPLATE") == 0)
COMPLETE_WITH_QUERY(Query_for_list_of_template_databases);
+ /* CREATE FOREIGN */
+ else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
+ pg_strcasecmp(prev_wd, "FOREIGN") == 0)
+ {
+ static const char *const list_CREATE_FOREIGN[] =
+ {"DATA WRAPPER", "TABLE", NULL};
+
+ COMPLETE_WITH_LIST(list_CREATE_FOREIGN);
+ }
+
/* CREATE FOREIGN DATA WRAPPER */
else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 &&
pg_strcasecmp(prev4_wd, "FOREIGN") == 0 &&
COMPLETE_WITH_LIST(list_DROPCR);
}
}
+ else if (pg_strcasecmp(prev2_wd, "DROP") == 0 &&
+ pg_strcasecmp(prev_wd, "FOREIGN") == 0)
+ {
+ static const char *const drop_CREATE_FOREIGN[] =
+ {"DATA WRAPPER", "TABLE", NULL};
+
+ COMPLETE_WITH_LIST(drop_CREATE_FOREIGN);
+ }
else if (pg_strcasecmp(prev4_wd, "DROP") == 0 &&
(pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 ||
pg_strcasecmp(prev3_wd, "FUNCTION") == 0) &&
pg_strcasecmp(prev_wd, "WRAPPER") == 0)
COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
+/* FOREIGN TABLE */
+ else if (pg_strcasecmp(prev3_wd, "CREATE") != 0 &&
+ pg_strcasecmp(prev2_wd, "FOREIGN") == 0 &&
+ pg_strcasecmp(prev_wd, "TABLE") == 0)
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, NULL);
+
/* GRANT && REVOKE */
/* Complete GRANT/REVOKE with a list of privileges */
else if (pg_strcasecmp(prev_wd, "GRANT") == 0 ||
else if ((pg_strcasecmp(prev3_wd, "GRANT") == 0 ||
pg_strcasecmp(prev3_wd, "REVOKE") == 0) &&
pg_strcasecmp(prev_wd, "ON") == 0)
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsv,
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvf ,
" UNION SELECT 'DATABASE'"
" UNION SELECT 'FOREIGN DATA WRAPPER'"
" UNION SELECT 'FOREIGN SERVER'"
+ " UNION SELECT 'FOREIGN TABLE'"
" UNION SELECT 'FUNCTION'"
" UNION SELECT 'LANGUAGE'"
" UNION SELECT 'LARGE OBJECT'"
pg_strcasecmp(prev_wd, "FOREIGN") == 0)
{
static const char *const list_privilege_foreign[] =
- {"DATA WRAPPER", "SERVER", NULL};
+ {"DATA WRAPPER", "SERVER", "TABLE", NULL};
COMPLETE_WITH_LIST(list_privilege_foreign);
}
else if (pg_strcasecmp(prev_wd, "FROM") == 0 &&
pg_strcasecmp(prev3_wd, "COPY") != 0 &&
pg_strcasecmp(prev3_wd, "\\copy") != 0)
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsv, NULL);
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvf , NULL);
/* Backslash commands */
/* TODO: \dc \dd \dl */
COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
else if (strncmp(prev_wd, "\\dp", strlen("\\dp")) == 0
|| strncmp(prev_wd, "\\z", strlen("\\z")) == 0)
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsv, NULL);
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvf , NULL);
else if (strncmp(prev_wd, "\\ds", strlen("\\ds")) == 0)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
else if (strncmp(prev_wd, "\\dt", strlen("\\dt")) == 0)
/* must be at end of \d list */
else if (strncmp(prev_wd, "\\d", strlen("\\d")) == 0)
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tisv, NULL);
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tisvf , NULL);
else if (strcmp(prev_wd, "\\ef") == 0)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 20101229 1
+#define CATALOG_VERSION_NO 20110101 1
#endif
OCLASS_FDW, /* pg_foreign_data_wrapper */
OCLASS_FOREIGN_SERVER, /* pg_foreign_server */
OCLASS_USER_MAPPING, /* pg_user_mapping */
+ OCLASS_FOREIGN_TABLE, /* pg_foreign_table */
OCLASS_DEFACL, /* pg_default_acl */
MAX_OCLASS /* MUST BE LAST */
} ObjectClass;
DECLARE_UNIQUE_INDEX(pg_user_mapping_user_server_index, 175, on pg_user_mapping using btree(umuser oid_ops, umserver oid_ops));
#define UserMappingUserServerIndexId 175
+DECLARE_UNIQUE_INDEX(pg_foreign_table_relid_index, 3119, on pg_foreign_table using btree(ftrelid oid_ops));
+#define ForeignTableRelidIndexId 3119
+
DECLARE_UNIQUE_INDEX(pg_default_acl_role_nsp_obj_index, 827, on pg_default_acl using btree(defaclrole oid_ops, defaclnamespace oid_ops, defaclobjtype char_ops));
#define DefaultAclRoleNspObjIndexId 827
DECLARE_UNIQUE_INDEX(pg_default_acl_oid_index, 828, on pg_default_acl using btree(oid oid_ops));
#define RELKIND_TOASTVALUE 't' /* moved off huge values */
#define RELKIND_VIEW 'v' /* view */
#define RELKIND_COMPOSITE_TYPE 'c' /* composite type */
+#define RELKIND_FOREIGN_TABLE 'f' /* foreign table */
#define RELPERSISTENCE_PERMANENT 'p'
#define RELPERSISTENCE_UNLOGGED 'u'
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * pg_foreign_table.h
+ * definition of the system "foreign table" relation (pg_foreign_table)
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_foreign_table.h
+ *
+ * NOTES
+ * the genbki.sh script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_FOREIGN_TABLE_H
+#define PG_FOREIGN_TABLE_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ * pg_foreign_table definition. cpp turns this into
+ * typedef struct FormData_pg_foreign_table
+ * ----------------
+ */
+#define ForeignTableRelationId 3118
+
+CATALOG(pg_foreign_table,3118) BKI_WITHOUT_OIDS
+{
+ Oid ftrelid; /* OID of foreign table */
+ Oid ftserver; /* OID of foreign server */
+ text ftoptions[1]; /* FDW-specific options */
+} FormData_pg_foreign_table;
+
+/* ----------------
+ * Form_pg_foreign_table corresponds to a pointer to a tuple with
+ * the format of pg_foreign_table relation.
+ * ----------------
+ */
+typedef FormData_pg_foreign_table *Form_pg_foreign_table;
+
+/* ----------------
+ * compiler constants for pg_foreign_table
+ * ----------------
+ */
+
+#define Natts_pg_foreign_table 3
+#define Anum_pg_foreign_table_ftrelid 1
+#define Anum_pg_foreign_table_ftserver 2
+#define Anum_pg_foreign_table_ftoptions 3
+
+#endif /* PG_FOREIGN_TABLE_H */
extern void AlterUserMapping(AlterUserMappingStmt *stmt);
extern void RemoveUserMapping(DropUserMappingStmt *stmt);
extern void RemoveUserMappingById(Oid umId);
+extern void CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid);
+extern Datum transformGenericOptions(Oid catalogId,
+ Datum oldOptions,
+ List *options,
+ Oid fdwvalidator);
/* support routines in commands/define.c */
T_DropUserMappingStmt,
T_AlterTableSpaceOptionsStmt,
T_SecLabelStmt,
+ T_CreateForeignTableStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
OBJECT_DOMAIN,
OBJECT_FDW,
OBJECT_FOREIGN_SERVER,
+ OBJECT_FOREIGN_TABLE,
OBJECT_FUNCTION,
OBJECT_INDEX,
OBJECT_LANGUAGE,
AT_EnableReplicaRule, /* ENABLE REPLICA RULE name */
AT_DisableRule, /* DISABLE RULE name */
AT_AddInherit, /* INHERIT parent */
- AT_DropInherit /* NO INHERIT parent */
+ AT_DropInherit, /* NO INHERIT parent */
+ AT_GenericOptions, /* OPTIONS (...) */
} AlterTableType;
typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */
ACL_OBJECT_DATABASE, /* database */
ACL_OBJECT_FDW, /* foreign-data wrapper */
ACL_OBJECT_FOREIGN_SERVER, /* foreign server */
+ ACL_OBJECT_FOREIGN_TABLE, /* foreign table */
ACL_OBJECT_FUNCTION, /* function */
ACL_OBJECT_LANGUAGE, /* procedural language */
ACL_OBJECT_LARGEOBJECT, /* largeobject */
DropBehavior behavior; /* drop behavior - cascade/restrict */
} DropForeignServerStmt;
+/* ----------------------
+ * Create FOREIGN TABLE Statements
+ * ----------------------
+ */
+
+typedef struct CreateForeignTableStmt
+{
+ CreateStmt base;
+ char *servername;
+ List *options;
+} CreateForeignTableStmt;
+
/* ----------------------
* Create/Drop USER MAPPING Statements
* ----------------------
{
NodeTag type;
ObjectType renameType; /* OBJECT_TABLE, OBJECT_COLUMN, etc */
+ ObjectType relationType; /* if column name, associated relation type */
RangeVar *relation; /* in case it's a table */
List *object; /* in case it's some other object */
List *objarg; /* argument types, if applicable */
#define ACL_ALL_RIGHTS_DATABASE (ACL_CREATE|ACL_CREATE_TEMP|ACL_CONNECT)
#define ACL_ALL_RIGHTS_FDW (ACL_USAGE)
#define ACL_ALL_RIGHTS_FOREIGN_SERVER (ACL_USAGE)
+#define ACL_ALL_RIGHTS_FOREIGN_TABLE (ACL_SELECT)
#define ACL_ALL_RIGHTS_FUNCTION (ACL_EXECUTE)
#define ACL_ALL_RIGHTS_LANGUAGE (ACL_USAGE)
#define ACL_ALL_RIGHTS_LARGEOBJECT (ACL_SELECT|ACL_UPDATE)
ACL_KIND_TSCONFIGURATION, /* pg_ts_config */
ACL_KIND_FDW, /* pg_foreign_data_wrapper */
ACL_KIND_FOREIGN_SERVER, /* pg_foreign_server */
+ ACL_KIND_FOREIGN_TABLE, /* pg_foreign_table */
MAX_ACL_KIND /* MUST BE LAST */
} AclObjectKind;
FOREIGNDATAWRAPPEROID,
FOREIGNSERVERNAME,
FOREIGNSERVEROID,
+ FOREIGNTABLEREL,
INDEXRELID,
LANGNAME,
LANGOID,
-- try creating a view and altering that, should fail
create view myview as select * from atacc1;
alter table myview alter column test drop not null;
-ERROR: "myview" is not a table
+ERROR: "myview" is not a table or foreign table
alter table myview alter column test set not null;
-ERROR: "myview" is not a table
+ERROR: "myview" is not a table or foreign table
drop view myview;
drop table atacc1;
-- test inheritance
(0 rows)
alter table myview drop d;
-ERROR: "myview" is not a table or composite typ e
+ERROR: "myview" is not a table, composite type, or foreign tabl e
drop view myview;
-- test some commands to make sure they fail on the dropped column
analyze atacc1(a);
t1 | regress_test_role
(8 rows)
+-- CREATE FOREIGN TABLE
+CREATE SCHEMA foreign_schema;
+CREATE SERVER sc FOREIGN DATA WRAPPER dummy;
+CREATE FOREIGN TABLE ft1 (); -- ERROR
+ERROR: syntax error at or near ";"
+LINE 1: CREATE FOREIGN TABLE ft1 ();
+ ^
+CREATE FOREIGN TABLE ft1 () SERVER no_server; -- ERROR
+ERROR: server "no_server" does not exist
+CREATE FOREIGN TABLE ft1 (c1 serial) SERVER sc; -- ERROR
+NOTICE: CREATE FOREIGN TABLE will create implicit sequence "ft1_c1_seq" for serial column "ft1.c1"
+ERROR: default values on foreign tables are not supported
+CREATE FOREIGN TABLE ft1 () SERVER sc WITH OIDS; -- ERROR
+ERROR: syntax error at or near "WITH OIDS"
+LINE 1: CREATE FOREIGN TABLE ft1 () SERVER sc WITH OIDS;
+ ^
+CREATE FOREIGN TABLE ft1 (
+ c1 integer NOT NULL,
+ c2 text,
+ c3 date
+) SERVER sc OPTIONS (delimiter ',', quote '"');
+COMMENT ON FOREIGN TABLE ft1 IS 'ft1';
+COMMENT ON COLUMN ft1.c1 IS 'ft1.c1';
+\d+ ft1
+ Foreign table "public.ft1"
+ Column | Type | Modifiers | Storage | Description
+--------+---------+-----------+----------+-------------
+ c1 | integer | not null | plain | ft1.c1
+ c2 | text | | extended |
+ c3 | date | | plain |
+Server: sc
+Has OIDs: no
+
+\det+
+ List of foreign tables
+ Schema | Table | Server | Options
+--------+-------+--------+----------------------------
+ public | ft1 | sc | {"delimiter=,","quote=\""}
+(1 row)
+
+CREATE INDEX id_ft1_c2 ON ft1 (c2); -- ERROR
+ERROR: "ft1" is not a table
+-- ALTER FOREIGN TABLE
+COMMENT ON FOREIGN TABLE ft1 IS 'foreign table';
+COMMENT ON FOREIGN TABLE ft1 IS NULL;
+COMMENT ON COLUMN ft1.c1 IS 'foreign column';
+COMMENT ON COLUMN ft1.c1 IS NULL;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c4 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c5 integer DEFAULT 0; -- ERROR
+ERROR: default values on foreign tables are not supported
+ALTER FOREIGN TABLE ft1 ADD COLUMN c6 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c7 integer NOT NULL;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c8 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c9 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c10 integer;
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c4 SET DEFAULT 0; -- ERROR
+ERROR: "ft1" is not a table or view
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c5 DROP DEFAULT; -- ERROR
+ERROR: "ft1" is not a table or view
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c6 SET NOT NULL;
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c7 DROP NOT NULL;
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10) using '0'; -- ERROR
+ERROR: ALTER TYPE USING is not supported on foreign tables
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10);
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET DATA TYPE text;
+ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c9_check CHECK (c9 < 0);
+ERROR: "ft1" is not a table
+ALTER FOREIGN TABLE ft1 DROP CONSTRAINT no_const; -- ERROR
+ERROR: "ft1" is not a table
+ALTER FOREIGN TABLE ft1 DROP CONSTRAINT IF EXISTS no_const;
+ERROR: "ft1" is not a table
+ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c1_check;
+ERROR: "ft1" is not a table
+ALTER FOREIGN TABLE ft1 SET WITH OIDS; -- ERROR
+ERROR: "ft1" is not a table
+ALTER FOREIGN TABLE ft1 OWNER TO regress_test_role;
+ALTER FOREIGN TABLE ft1 OPTIONS (DROP delimiter, SET quote '~', ADD escape '@');
+ALTER FOREIGN TABLE ft1 DROP COLUMN no_column; -- ERROR
+ERROR: column "no_column" of relation "ft1" does not exist
+ALTER FOREIGN TABLE ft1 DROP COLUMN IF EXISTS no_column;
+NOTICE: column "no_column" of relation "ft1" does not exist, skipping
+ALTER FOREIGN TABLE ft1 DROP COLUMN c9;
+ALTER FOREIGN TABLE ft1 SET SCHEMA foreign_schema;
+ALTER FOREIGN TABLE ft1 SET TABLESPACE ts; -- ERROR
+ERROR: relation "ft1" does not exist
+ALTER FOREIGN TABLE foreign_schema.ft1 RENAME c1 TO foreign_column_1;
+ALTER FOREIGN TABLE foreign_schema.ft1 RENAME TO foreign_table_1;
+\d foreign_schema.foreign_table_1
+Foreign table "foreign_schema.foreign_table_1"
+ Column | Type | Modifiers
+------------------+---------+-----------
+ foreign_column_1 | integer | not null
+ c2 | text |
+ c3 | date |
+ c4 | integer |
+ c6 | integer | not null
+ c7 | integer |
+ c8 | text |
+ c10 | integer |
+Server: sc
+
-- Information schema
SELECT * FROM information_schema.foreign_data_wrappers ORDER BY 1, 2;
foreign_data_wrapper_catalog | foreign_data_wrapper_name | authorization_identifier | library_name | foreign_data_wrapper_language
regression | s5 | regression | foo | | 15.0 | regress_test_role
regression | s6 | regression | foo | | 16.0 | regress_test_indirect
regression | s8 | regression | postgresql | | | foreign_data_user
+ regression | sc | regression | dummy | | | foreign_data_user
regression | t1 | regression | foo | | | regress_test_indirect
regression | t2 | regression | foo | | | regress_test_role
-(6 rows)
+(7 rows)
SELECT * FROM information_schema.foreign_server_options ORDER BY 1, 2, 3;
foreign_server_catalog | foreign_server_name | option_name | option_value
foreign_data_user | regress_test_role2 | regression | | s6 | FOREIGN SERVER | USAGE | YES
(4 rows)
+SELECT * FROM information_schema.foreign_tables ORDER BY 1, 2, 3;
+ foreign_table_catalog | foreign_table_schema | foreign_table_name | foreign_server_catalog | foreign_server_name
+-----------------------+----------------------+--------------------+------------------------+---------------------
+ regression | foreign_schema | foreign_table_1 | regression | sc
+(1 row)
+
+SELECT * FROM information_schema.foreign_table_options ORDER BY 1, 2, 3, 4;
+ foreign_table_catalog | foreign_table_schema | foreign_table_name | option_name | option_value
+-----------------------+----------------------+--------------------+-------------+--------------
+ regression | foreign_schema | foreign_table_1 | escape | @
+ regression | foreign_schema | foreign_table_1 | quote | ~
+(2 rows)
+
SET ROLE regress_test_role;
SELECT * FROM information_schema.user_mapping_options ORDER BY 1, 2, 3, 4;
authorization_identifier | foreign_server_catalog | foreign_server_name | option_name | option_value
DROP SERVER s9 CASCADE; -- ERROR
ERROR: must be owner of foreign server s9
RESET ROLE;
+-- DROP FOREIGN TABLE
+DROP FOREIGN TABLE no_table; -- ERROR
+ERROR: foreign table "no_table" does not exist
+DROP FOREIGN TABLE IF EXISTS no_table;
+NOTICE: foreign table "no_table" does not exist, skipping
+DROP FOREIGN TABLE foreign_schema.foreign_table_1;
-- Cleanup
+DROP SCHEMA foreign_schema CASCADE;
DROP ROLE regress_test_role; -- ERROR
ERROR: role "regress_test_role" cannot be dropped because some objects depend on it
DETAIL: privileges for server s4
DROP ROLE regress_test_role2;
DROP FOREIGN DATA WRAPPER postgresql CASCADE;
DROP FOREIGN DATA WRAPPER dummy CASCADE;
+NOTICE: drop cascades to server sc
\c
DROP ROLE foreign_data_user;
-- At this point we should have no wrappers, no servers, and no mappings.
-- Check that ruleutils are working
--
SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schema' ORDER BY viewname;
- viewname | definition
------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ viewname | definition
+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
iexit | SELECT ih.name, ih.thepath, interpt_pp(ih.thepath, r.thepath) AS exit FROM ihighway ih, ramp r WHERE (ih.thepath ## r.thepath);
pg_cursors | SELECT c.name, c.statement, c.is_holdable, c.is_binary, c.is_scrollable, c.creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time);
pg_group | SELECT pg_authid.rolname AS groname, pg_authid.oid AS grosysid, ARRAY(SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid WHERE (NOT pg_authid.rolcanlogin);
pg_prepared_xacts | SELECT p.transaction, p.gid, p.prepared, u.rolname AS owner, d.datname AS database FROM ((pg_prepared_xact() p(transaction, gid, prepared, ownerid, dbid) LEFT JOIN pg_authid u ON ((p.ownerid = u.oid))) LEFT JOIN pg_database d ON ((p.dbid = d.oid)));
pg_roles | SELECT pg_authid.rolname, pg_authid.rolsuper, pg_authid.rolinherit, pg_authid.rolcreaterole, pg_authid.rolcreatedb, pg_authid.rolcatupdate, pg_authid.rolcanlogin, pg_authid.rolreplication, pg_authid.rolconnlimit, '********'::text AS rolpassword, pg_authid.rolvaliduntil, s.setconfig AS rolconfig, pg_authid.oid FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid))));
pg_rules | SELECT n.nspname AS schemaname, c.relname AS tablename, r.rulename, pg_get_ruledef(r.oid) AS definition FROM ((pg_rewrite r JOIN pg_class c ON ((c.oid = r.ev_class))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (r.rulename <> '_RETURN'::name);
- pg_seclabels | (((((SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (rel.relkind = 'r'::"char") THEN 'table'::text WHEN (rel.relkind = 'v'::"char") THEN 'view'::text WHEN (rel.relkind = 'S'::"char") THEN 'sequence'::text ELSE NULL::text END AS objtype, rel.relnamespace AS objnamespace, CASE WHEN pg_table_is_visible(rel.oid) THEN quote_ident((rel.relname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((rel.relname)::text)) END AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_class rel ON (((l.classoid = rel.tableoid) AND (l.objoid = rel.oid)))) JOIN pg_namespace nsp ON ((rel.relnamespace = nsp.oid))) WHERE (l.objsubid = 0) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'column'::text AS objtype, rel.relnamespace AS objnamespace, ((CASE WHEN pg_table_is_visible(rel.oid) THEN quote_ident((rel.relname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((rel.relname)::text)) END || '.'::text) || (att.attname)::text) AS objname, l.provider, l.label FROM (((pg_seclabel l JOIN pg_class rel ON (((l.classoid = rel.tableoid) AND (l.objoid = rel.oid)))) JOIN pg_attribute att ON (((rel.oid = att.attrelid) AND (l.objsubid = att.attnum)))) JOIN pg_namespace nsp ON ((rel.relnamespace = nsp.oid))) WHERE (l.objsubid <> 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (pro.proisagg = true) THEN 'aggregate'::text WHEN (pro.proisagg = false) THEN 'function'::text ELSE NULL::text END AS objtype, pro.pronamespace AS objnamespace, (((CASE WHEN pg_function_is_visible(pro.oid) THEN quote_ident((pro.proname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((pro.proname)::text)) END || '('::text) || pg_get_function_arguments(pro.oid)) || ')'::text) AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_proc pro ON (((l.classoid = pro.tableoid) AND (l.objoid = pro.oid)))) JOIN pg_namespace nsp ON ((pro.pronamespace = nsp.oid))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (typ.typtype = 'd'::"char") THEN 'domain'::text ELSE 'type'::text END AS objtype, typ.typnamespace AS objnamespace, CASE WHEN pg_type_is_visible(typ.oid) THEN quote_ident((typ.typname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((typ.typname)::text)) END AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_type typ ON (((l.classoid = typ.tableoid) AND (l.objoid = typ.oid)))) JOIN pg_namespace nsp ON ((typ.typnamespace = nsp.oid))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'large object'::text AS objtype, NULL::oid AS objnamespace, (l.objoid)::text AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_largeobject_metadata lom ON ((l.objoid = lom.oid))) WHERE ((l.classoid = ('pg_largeobject'::regclass)::oid) AND (l.objsubid = 0))) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'language'::text AS objtype, NULL::oid AS objnamespace, quote_ident((lan.lanname)::text) AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_language lan ON (((l.classoid = lan.tableoid) AND (l.objoid = lan.oid)))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'schema'::text AS objtype, nsp.oid AS objnamespace, quote_ident((nsp.nspname)::text) AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_namespace nsp ON (((l.classoid = nsp.tableoid) AND (l.objoid = nsp.oid)))) WHERE (l.objsubid = 0);
+ pg_seclabels | (((((SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (rel.relkind = 'r'::"char") THEN 'table'::text WHEN (rel.relkind = 'v'::"char") THEN 'view'::text WHEN (rel.relkind = 'S'::"char") THEN 'sequence'::text WHEN (rel.relkind = 'f'::"char") THEN 'foreign table'::text ELSE NULL::text END AS objtype, rel.relnamespace AS objnamespace, CASE WHEN pg_table_is_visible(rel.oid) THEN quote_ident((rel.relname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((rel.relname)::text)) END AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_class rel ON (((l.classoid = rel.tableoid) AND (l.objoid = rel.oid)))) JOIN pg_namespace nsp ON ((rel.relnamespace = nsp.oid))) WHERE (l.objsubid = 0) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'column'::text AS objtype, rel.relnamespace AS objnamespace, ((CASE WHEN pg_table_is_visible(rel.oid) THEN quote_ident((rel.relname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((rel.relname)::text)) END || '.'::text) || (att.attname)::text) AS objname, l.provider, l.label FROM (((pg_seclabel l JOIN pg_class rel ON (((l.classoid = rel.tableoid) AND (l.objoid = rel.oid)))) JOIN pg_attribute att ON (((rel.oid = att.attrelid) AND (l.objsubid = att.attnum)))) JOIN pg_namespace nsp ON ((rel.relnamespace = nsp.oid))) WHERE (l.objsubid <> 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (pro.proisagg = true) THEN 'aggregate'::text WHEN (pro.proisagg = false) THEN 'function'::text ELSE NULL::text END AS objtype, pro.pronamespace AS objnamespace, (((CASE WHEN pg_function_is_visible(pro.oid) THEN quote_ident((pro.proname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((pro.proname)::text)) END || '('::text) || pg_get_function_arguments(pro.oid)) || ')'::text) AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_proc pro ON (((l.classoid = pro.tableoid) AND (l.objoid = pro.oid)))) JOIN pg_namespace nsp ON ((pro.pronamespace = nsp.oid))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (typ.typtype = 'd'::"char") THEN 'domain'::text ELSE 'type'::text END AS objtype, typ.typnamespace AS objnamespace, CASE WHEN pg_type_is_visible(typ.oid) THEN quote_ident((typ.typname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((typ.typname)::text)) END AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_type typ ON (((l.classoid = typ.tableoid) AND (l.objoid = typ.oid)))) JOIN pg_namespace nsp ON ((typ.typnamespace = nsp.oid))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'large object'::text AS objtype, NULL::oid AS objnamespace, (l.objoid)::text AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_largeobject_metadata lom ON ((l.objoid = lom.oid))) WHERE ((l.classoid = ('pg_largeobject'::regclass)::oid) AND (l.objsubid = 0))) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'language'::text AS objtype, NULL::oid AS objnamespace, quote_ident((lan.lanname)::text) AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_language lan ON (((l.classoid = lan.tableoid) AND (l.objoid = lan.oid)))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'schema'::text AS objtype, nsp.oid AS objnamespace, quote_ident((nsp.nspname)::text) AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_namespace nsp ON (((l.classoid = nsp.tableoid) AND (l.objoid = nsp.oid)))) WHERE (l.objsubid = 0);
pg_settings | SELECT a.name, a.setting, a.unit, a.category, a.short_desc, a.extra_desc, a.context, a.vartype, a.source, a.min_val, a.max_val, a.enumvals, a.boot_val, a.reset_val, a.sourcefile, a.sourceline FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline);
pg_shadow | SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, pg_authid.rolcreatedb AS usecreatedb, pg_authid.rolsuper AS usesuper, pg_authid.rolcatupdate AS usecatupd, pg_authid.rolreplication AS userepl, pg_authid.rolpassword AS passwd, (pg_authid.rolvaliduntil)::abstime AS valuntil, s.setconfig AS useconfig FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin;
pg_stat_activity | SELECT s.datid, d.datname, s.procpid, s.usesysid, u.rolname AS usename, s.application_name, s.client_addr, s.client_port, s.backend_start, s.xact_start, s.query_start, s.waiting, s.current_query FROM pg_database d, pg_stat_get_activity(NULL::integer) s(datid, procpid, usesysid, application_name, current_query, waiting, xact_start, query_start, backend_start, client_addr, client_port), pg_authid u WHERE ((s.datid = d.oid) AND (s.usesysid = u.oid));
pg_enum | t
pg_foreign_data_wrapper | t
pg_foreign_server | t
+ pg_foreign_table | t
pg_index | t
pg_inherits | t
pg_language | t
timetz_tbl | f
tinterval_tbl | f
varchar_tbl | f
-(143 rows)
+(144 rows)
--
-- another sanity check: every system catalog that has OIDs should have
-- Look for illegal values in pg_class fields
SELECT p1.oid, p1.relname
FROM pg_class as p1
-WHERE p1.relkind NOT IN ('r', 'i', 's', 'S', 'c', 't', 'v');
+WHERE p1.relkind NOT IN ('r', 'i', 's', 'S', 'c', 't', 'v', 'f' );
oid | relname
-----+---------
(0 rows)
DROP SERVER s7;
\deu
+-- CREATE FOREIGN TABLE
+CREATE SCHEMA foreign_schema;
+CREATE SERVER sc FOREIGN DATA WRAPPER dummy;
+CREATE FOREIGN TABLE ft1 (); -- ERROR
+CREATE FOREIGN TABLE ft1 () SERVER no_server; -- ERROR
+CREATE FOREIGN TABLE ft1 (c1 serial) SERVER sc; -- ERROR
+CREATE FOREIGN TABLE ft1 () SERVER sc WITH OIDS; -- ERROR
+CREATE FOREIGN TABLE ft1 (
+ c1 integer NOT NULL,
+ c2 text,
+ c3 date
+) SERVER sc OPTIONS (delimiter ',', quote '"');
+COMMENT ON FOREIGN TABLE ft1 IS 'ft1';
+COMMENT ON COLUMN ft1.c1 IS 'ft1.c1';
+\d+ ft1
+\det+
+CREATE INDEX id_ft1_c2 ON ft1 (c2); -- ERROR
+
+-- ALTER FOREIGN TABLE
+COMMENT ON FOREIGN TABLE ft1 IS 'foreign table';
+COMMENT ON FOREIGN TABLE ft1 IS NULL;
+COMMENT ON COLUMN ft1.c1 IS 'foreign column';
+COMMENT ON COLUMN ft1.c1 IS NULL;
+
+ALTER FOREIGN TABLE ft1 ADD COLUMN c4 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c5 integer DEFAULT 0; -- ERROR
+ALTER FOREIGN TABLE ft1 ADD COLUMN c6 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c7 integer NOT NULL;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c8 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c9 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c10 integer;
+
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c4 SET DEFAULT 0; -- ERROR
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c5 DROP DEFAULT; -- ERROR
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c6 SET NOT NULL;
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c7 DROP NOT NULL;
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10) using '0'; -- ERROR
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10);
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET DATA TYPE text;
+ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c9_check CHECK (c9 < 0);
+ALTER FOREIGN TABLE ft1 DROP CONSTRAINT no_const; -- ERROR
+ALTER FOREIGN TABLE ft1 DROP CONSTRAINT IF EXISTS no_const;
+ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c1_check;
+ALTER FOREIGN TABLE ft1 SET WITH OIDS; -- ERROR
+ALTER FOREIGN TABLE ft1 OWNER TO regress_test_role;
+ALTER FOREIGN TABLE ft1 OPTIONS (DROP delimiter, SET quote '~', ADD escape '@');
+ALTER FOREIGN TABLE ft1 DROP COLUMN no_column; -- ERROR
+ALTER FOREIGN TABLE ft1 DROP COLUMN IF EXISTS no_column;
+ALTER FOREIGN TABLE ft1 DROP COLUMN c9;
+ALTER FOREIGN TABLE ft1 SET SCHEMA foreign_schema;
+ALTER FOREIGN TABLE ft1 SET TABLESPACE ts; -- ERROR
+ALTER FOREIGN TABLE foreign_schema.ft1 RENAME c1 TO foreign_column_1;
+ALTER FOREIGN TABLE foreign_schema.ft1 RENAME TO foreign_table_1;
+\d foreign_schema.foreign_table_1
+
-- Information schema
SELECT * FROM information_schema.foreign_data_wrappers ORDER BY 1, 2;
SELECT * FROM information_schema.user_mapping_options ORDER BY lower(authorization_identifier), 2, 3, 4;
SELECT * FROM information_schema.usage_privileges WHERE object_type LIKE 'FOREIGN%' ORDER BY 1, 2, 3, 4, 5;
SELECT * FROM information_schema.role_usage_grants WHERE object_type LIKE 'FOREIGN%' ORDER BY 1, 2, 3, 4, 5;
+SELECT * FROM information_schema.foreign_tables ORDER BY 1, 2, 3;
+SELECT * FROM information_schema.foreign_table_options ORDER BY 1, 2, 3, 4;
SET ROLE regress_test_role;
SELECT * FROM information_schema.user_mapping_options ORDER BY 1, 2, 3, 4;
SELECT * FROM information_schema.usage_privileges WHERE object_type LIKE 'FOREIGN%' ORDER BY 1, 2, 3, 4, 5;
DROP SERVER s9 CASCADE; -- ERROR
RESET ROLE;
+-- DROP FOREIGN TABLE
+DROP FOREIGN TABLE no_table; -- ERROR
+DROP FOREIGN TABLE IF EXISTS no_table;
+DROP FOREIGN TABLE foreign_schema.foreign_table_1;
+
-- Cleanup
+DROP SCHEMA foreign_schema CASCADE;
DROP ROLE regress_test_role; -- ERROR
DROP SERVER s5 CASCADE;
DROP SERVER t1 CASCADE;
SELECT p1.oid, p1.relname
FROM pg_class as p1
-WHERE p1.relkind NOT IN ('r', 'i', 's', 'S', 'c', 't', 'v');
+WHERE p1.relkind NOT IN ('r', 'i', 's', 'S', 'c', 't', 'v', 'f' );
-- Indexes should have an access method, others not.