PostgreSQL - 修改扩展中的 CREATE TABLE 语法

PostgreSQL - Modify CREATE TABLE syntax in an extension

是否可以编写修改 DDL 语法的 PostgreSQL 扩展?

我正在 PostgreSQL 和 PostGIS 之上创建一个扩展,以支持特定空间数据模型 (OMT-G) 的完整性约束。为此,我想修改 CREATE TABLE 语法,它接受 CONSTRAINTS 语法:

CONSTRAINT constraint_name CHECK ( expression )

但我想创建自己的语法,如下例中的语法,然后将调用我已经编写的函数或触发器。

CREATE TABLE school_district (
      id integer PRIMARY KEY,
      school_name varchar(120)
      geom geometry,

      SPATIAL_CONSTRAINT PLANAR_SUBDIVISION (geom),
      SPATIAL_CONSTRAINT CONTAINS school (geom)**
);

这可能吗?如果是,怎么做?

正如其他人评论的那样,不可能通过扩展来改变 Postgres 语法。在黑客的邮件列表上有一些与此相关的讨论,但没有人看到任何使 Bison 语法可扩展的可行方法。

您提议的另一个问题是 CHECK 约束(看起来是您在这里尝试做的)不能安全地引用其他 tables。

听起来您真正想要的是可扩展的外键支持。这也是社区实际上想要的东西,至少对于数组而言。这个想法是支持像 int[] 这样的东西,其中每个元素都应该被视为对另一个 table 的外键引用。您所描述的是相似的:您想要使用不同的运算符而不是不同的数据类型。

虽然现在,我认为您能做的最好的事情就是为您的用户提供一个功能,该功能将在 table 上放置适当的列和触发器。该列将是学校 table 的外键。触发器会找到合适的学校并在新列中填充它的主键。 (您可能希望在学校 table 上使用 BEFORE DELETE 触发器来处理删除学校的问题。)

您需要外键字段的原因是因为外键触发器使用与普通查询不同的可见性规则运行,因此您无法在用户 space 中完全模拟它们。如果您不是那么偏执,您可以只使用一个 AFTER INSERT 触发器来查找学校并在找不到学校时抛出错误。

就任何纯 DDL 解决方案而言,最有前途的方法是使用 CREATE TABLE ... CONSTRAINT ... EXCLUDE,它在单个 table 上运行 GiST,并且仅在边界框上运行的运算符相对有限(例如 &&):

CREATE TABLE polygons (
    geom geometry(Polygon,4326),
    EXCLUDE USING gist (geom WITH &&)
);
INSERT INTO polygons(geom)
VALUES('SRID=4326;POLYGON ((0 0, 0 1, 1 0, 0 0))');

但是,这会产生冲突(即使几何图形实际上并不重叠):

INSERT INTO polygons(geom)
VALUES('SRID=4326;POLYGON ((1 1, 1 2, 2 2, 2 1, 1 1))');

ERROR: conflicting key value violates exclusion constraint "polygons_geom_excl" DETAIL: Key (geom)=(0103000020E61000000100000005000000000000000000F03F000000000000F03F000000000000F03F0000000000000040000000000000004000000000000000400000000000000040000000000000F03F000000000000F03F000000000000F03F) conflicts with existing key (geom)=(0103000020E61000000100000004000000000000000000000000000000000000000000000000000000000000000000F03F000000000000F03F000000000000000000000000000000000000000000000000).


正如@Jim 上面提到的,让一个 table 对另一个 table 建立约束的最佳方法是制作一个好的 trigger function,并在两者上使用它table秒。它通常会写在 PL/pgSQL 中,您可以在其中嵌入有用的消息,例如:

RAISE EXCEPTION 'School % is not located in school district % (it is % away)',
                s.name, d.name, ST_Distance(s.geom, d.geom);

这样,如果您编辑 school_districtschools table,触发器将检查 UPDATEINSERTDELETE 查看条件是否仍然有效。