ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

PostGraphile v4 数据库函数指南:用 PostgreSQL Functions 扩展 GraphQL Schema 的三种方式

2026/9/23 21:18:14 拓冰建站 浏览量
PostGraphile v4 数据库函数指南:用 PostgreSQL Functions 扩展 GraphQL Schema 的三种方式 后端API网关【免费下载链接】crystal Graphiles Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!项目地址https://gitcode.com/gh_mirrors/cry/crystal点击查看免费下载本文是 PostGraphile v4 的数据库函数Database Functions实战指南核心讲解如何利用 PostgreSQL 函数为 PostGraphile schema 添加能力以计算列Computed Columns、自定义查询Custom Queries和自定义变更Custom Mutations三种形态暴露到 GraphQL。读者将掌握函数语言选型、命名参数规则、命名冲突消解、VOLATILE/STABLE/IMMUTABLE 易变性语义对暴露形态的影响以及 SETOF 函数与 Relay Connection 分页的结合方式并看到这些机制在当前仓库测试库中的真实落地形态。三种暴露方式PostGraphile 中数据库函数的核心用法在 PostGraphile 中把业务逻辑下沉到 PostgreSQL 数据库是给 schema 增加能力的最便捷路径之一。根据函数签名与易变性声明PostGraphile 会自动把函数映射为三种 GraphQL 形态暴露形态说明对应文档计算列Computed Columns为某个表类型table type添加一个计算字段computed-columns自定义查询Custom Queries在根级Query上添加字段可返回标量、列表、自定义类型、表行甚至表连接connectioncustom-queries自定义变更Custom Mutations在根级Mutation上添加字段可修改数据库并返回void、标量、列表、自定义类型、表行或表行列表但不能返回连接因为无法对变更进行分页custom-mutations一个函数最终被暴露为哪种形态取决于它的易变性声明VOLATILE/STABLE/IMMUTABLE与返回类型详见后文。理解数据库函数为什么业务逻辑要下沉到数据库要构建最强大的 PostGraphile 服务理解 PostgreSQL 函数至关重要。函数允许你用 SQL 或多种其他脚本语言在数据库中定义业务逻辑。把业务逻辑放在数据库层往往比放在应用层性能更好——因为 PostgreSQL 针对数据密集型场景做了精细调优函数执行与数据访问发生在同一进程内避免了应用与数据库之间的往返开销。想要获取可运行的函数示例可以查看当前仓库 v4 测试库中的函数定义集合 kitchen-sink-schema.sql其中包含数十个用于验证查询、变更行为的真实函数覆盖了本文将要讲到的命名参数、默认值、STRICT、VOLATILE/STABLE/IMMUTABLE、SETOF等全部要点例如a.add_1_query(int, int)、a.add_4_mutation(int, b int default 2)、c.table_set_query()等。推荐阅读PostgreSQLCREATE FUNCTION文档——实际创建函数的语法依据。PostgreSQLCREATE TRIGGER文档。StackOverflow 上关于 PostgreSQL 计算列 的回答。过程语言选型SQL、PL/pgSQL 与脚本语言PostgreSQL 函数要求使用 SQL 或某种过程语言procedural language编写。最常见的 PostgreSQL 过程语言是 PL/pgSQL。SQL 可能是最容易上手的选择因为开发者大多已熟悉它PL/pgSQL 是 PostgreSQL 自带的过程语言上手不难且 StackOverflow 等社区资源丰富。如果打算写触发器trigger就必须学习 PL/pgSQL或其它过程语言因为 SQL 不能用于触发器。不过不必焦虑——没有深入的 PL/pgSQL 知识也能构建出色的应用。一个用LANGUAGE sql编写的简单函数CREATE FUNCTION add(a int, b int) RETURNS int AS $$ select a b; $$ LANGUAGE sql IMMUTABLE STRICT;同一个函数用LANGUAGE plpgsql编写CREATE FUNCTION add(a int, b int) RETURNS int AS $$ BEGIN RETURN a b; END; $$ LANGUAGE plpgsql IMMUTABLE STRICT;如果你不想用 PL/pgSQL 或 SQL许多流行的脚本语言也可以在 PostgreSQL内部编写函数JavaScriptplv8Rubyplruby例如用 JavaScript 定义的函数与 PL/pgSQL 版本外观几乎一致-- 这看起来与 PL/pgSQL 示例几乎相同…… CREATE FUNCTION add(a int, b int) RETURNS int AS $$ return a b; $$ LANGUAGE plv8 IMMUTABLE STRICT; -- 这是来自 plv8 仓库的更好示例…… CREATE FUNCTION plv8_test(keys text[], vals text[]) RETURNS text AS $$ var object {} for (var i 0; i keys.length; i) { object[keys[i]] vals[i] } return JSON.stringify(object) $$ LANGUAGE plv8 IMMUTABLE STRICT;命名参数GraphQL 只认名字匿名参数会被自动编号PostgreSQL 允许在函数中混用命名参数与位置参数匿名参数。但 GraphQL只允许命名参数。因此如果某个参数没有名字PostGraphile 会自动为它取名arg1、arg2、arg3……依此类推。一个使用匿名参数的函数示例CREATE FUNCTION add(int, int) RETURNS int AS $$ SELECT $1 $2; $$ LANGUAGE sql IMMUTABLE STRICT;而命名参数版本如下CREATE FUNCTION add(a int, b int) RETURNS int AS $$ select a b; $$ LANGUAGE sql IMMUTABLE STRICT;当前仓库的测试库对这两种形态都有真实覆盖见 kitchen-sink-schema.sqlcreate function a.add_1_query(int, int) returns int as $$ select $1 $2 $$ language sql immutable strict; create function a.add_2_query(a int, b int default 2) returns int as $$ select $1 $2 $$ language sql stable strict; create function a.add_3_query(a int, int) returns int as $$ select $1 $2 $$ language sql immutable; create function a.add_4_query(int, b int default 2) returns int as $$ select $1 $2 $$ language sql stable;对应的 GraphQL 调用在 procedure-mutation.test.graphql 中可见add1Mutation(input: { arg0: 1, arg1: 2 }) { clientMutationId integer } add2Mutation(input: { clientMutationId: hello, a: 2, b: 2 }) { clientMutationId integer } add3Mutation(input: { clientMutationId: world, arg1: 5 }) { clientMutationId integer } add4Mutation(input: { arg0: 1, b: 3 }) { integer }可以看到匿名参数被自动命名为arg0、arg1命名参数如a、b保持原名带default默认值的参数在 GraphQL 侧成为可选输入项如add3Mutation只传了arg1。注意自动编号从arg0开始而非文档早先提到的arg1风格——这是测试库实测呈现的行为以实际生成的 schema 为准。解决命名冲突参数名与列名的碰撞有时你为函数参数选的名字会与函数内部可访问的列名或其它标识符冲突。要避免这类冲突可以使用数字参数如$1表示第一个参数、$2表示第二个依此类推并用表名消除歧义create function get_user(id int) returns users as $$ select * from users where users.id $1; $$ language sql stable;如果更倾向于使用参数名而非数字$n参数可以用函数名本身来消歧create function get_user(id int) returns users as $$ select * from users where users.id get_user.id; $$ language sql stable;这种方法总体可行但某些场景仍不够用。例如在plpgsql函数中写 upsertINSERT...ON CONFLICT语句时create function upsert_value(id int, value text) returns void as $$ begin insert into my_table (id, value) values(id, value) on conflict (id) -- 这里会报错 do update set value excluded.value; end; $$ language plpgsql volatile;这里的on conflict (id)会引发问题PL/pgSQL 无法判断id指的是表列还是函数参数而在括号内加表名又是语法错误。解决办法之一是改用sql语言——SQL 会优先把标识符当作列来解析。另一个办法是显式告知函数用列来解决冲突create function upsert_value(id int, value text) returns void as $$ #variable_conflict use_column begin insert into my_table (id, value) values(id, value) on conflict (id) do update set value excluded.value; end; $$ language plpgsql volatile;要更深入理解这些冲突与解决方案请参阅 PostgreSQL 文档中关于变量替换的部分。VOLATILE易变函数PostGraphile 的自定义变更来源默认情况下函数是 volatile易变的。例如CREATE FUNCTION my_function(a int, b int) RETURNS int AS $$ … $$ LANGUAGE sql;等价于CREATE FUNCTION my_function(a int, b int) RETURNS int AS $$ … $$ LANGUAGE sql VOLATILE;PostgreSQL 文档中的定义VOLATILE表示函数值即使在单次表扫描中也可能改变因此无法做任何优化……但任何有副作用的函数都必须归类为 volatile即使其结果相当可预测也要防止调用被优化掉例如setval()。简单说VOLATILE基本意味着你在修改数据或存储状态。熟悉 HTTP 的开发者可以把VOLATILE函数类比为 不安全 的 HTTP 方法如POST、PUT、PATCH和DELETE。某些VOLATILE函数会被 PostGraphile 暴露为自定义变更custom mutations。仓库测试中的典型VOLATILE变更函数见 kitchen-sink-schema.sqlcreate function a.add_1_mutation(int, int) returns int as $$ select $1 $2 $$ language sql volatile strict; create function a.add_2_mutation(a int, b int default 2) returns int as $$ select $1 $2 $$ language sql strict; -- 默认即 volatile create function a.add_4_mutation_error(int, b int default 2) returns int as $$ begin raise exception Deliberate error; end $$ language plpgsql;注意add_4_mutation_error这类函数会故意抛出异常用于验证 PostGraphile 的错误处理路径同时命名参数a、b与匿名参数自动命名在同一个 mutation 中混用也是被支持的。STABLE / IMMUTABLE查询函数PostGraphile 的自定义查询与计算列来源如果你的函数不修改任何数据或状态应当声明为STABLE。如果函数只依赖其参数、不从表等其它来源取数则可以声明为IMMUTABLE——它是STABLE更严格的形式。将函数标记为STABLE或IMMUTABLE后PostgreSQL 知道可以应用多种优化包括记忆化memoization从而避免在同一个语句中对相同输入多次调用该函数。定义 STABLE/IMMUTABLE 函数的示例CREATE FUNCTION my_function(a int, b int) RETURNS int AS $$ … $$ LANGUAGE sql STABLE; -- 或者…… CREATE FUNCTION my_function(a int, b int) RETURNS int AS $$ … $$ LANGUAGE sql IMMUTABLE; -- 或者想返回表中的一行…… CREATE FUNCTION my_function(a int, b int) RETURNS my_table AS $$ … $$ LANGUAGE sql STABLE;PostgreSQL 文档中的定义IMMUTABLE表示函数不能修改数据库并且在给定相同参数值的情况下始终返回相同结果也就是说它不做数据库查找也不使用参数列表中未直接呈现的信息。若给出此选项任何使用全常量参数的函数调用都可以立即替换为函数值。以及……STABLE表示函数不能修改数据库并且在单次表扫描内对相同的参数值会一致地返回相同结果但其结果可能在不同 SQL 语句之间变化。这适合结果依赖数据库查找、参数变量如当前时区等的函数。不适合想要查询当前命令所修改行的 AFTER 触发器。继续用 HTTP 类比IMMUTABLE和STABLE相当于 安全 的 HTTP 方法如GET和HEAD。某些STABLE/IMMUTABLE函数会被 PostGraphile 暴露为自定义查询custom queries或计算列computed columns。仓库测试中的典型STABLE/IMMUTABLE查询函数见 kitchen-sink-schema.sqlcreate function a.add_1_query(int, int) returns int as $$ select $1 $2 $$ language sql immutable strict; create function a.add_2_query(a int, b int default 2) returns int as $$ select $1 $2 $$ language sql stable strict; create function c.table_query(id int) returns a.post as $$ select * from a.post where id $1 $$ language sql stable; create function c.person_exists(person c.person, email b.email) returns boolean as $$ … $$ language sql stable; create function c.edge_case_computed(edge_case c.edge_case) returns text as $$ select hello world::text $$ language sql stable;其中person_exists以表类型为参数并返回布尔值是计算列的典型形态edge_case_computed同样是标准的计算列函数。关于 STRICTNULL 处理在上面的示例中多次出现的STRICT也是影响函数暴露行为的关键修饰符STRICT等价于RETURNS NULL ON NULL INPUT表示当任一参数为NULL时函数不执行且直接返回NULL。PostGraphile 在生成 GraphQL 字段时会依据STRICT与否决定输入参数的nullable语义。仓库中对此有专门的对照测试见 kitchen-sink-schema.sqlcreate function b.mult_1(int, int) returns int as $$ select $1 * $2 $$ language sql; -- 默认 called on null input create function b.mult_2(int, int) returns int as $$ select $1 * $2 $$ language sql called on null input; create function b.mult_3(int, int) returns int as $$ select $1 * $2 $$ language sql returns null on null input; -- 即 strict create function b.mult_4(int, int) returns int as $$ select $1 * $2 $$ language sql strict;SETOF 函数与 Connections返回集合即可分页除了标量、复合类型及其数组之外PostgreSQL 函数还可以返回集合sets。集合模拟表因此 PostGraphile 很自然地把它们用连接connections暴露到 GraphQL。SETOF 函数是向用户暴露一次性给太多、需要分页处理的数据的有力方式。要创建返回连接的函数可以使用如下 SQL-- 假设我们已经有一张名为 person 的表…… CREATE FUNCTION my_function(a int, b int) RETURNS SETOF person AS $$ … $$ LANGUAGE sql STABLE;该函数会被识别为自定义查询custom query可以这样查询{ myFunction(a: 1, b: 2, first: 2) { pageInfo { hasNextPage hasPrevPage } edges { cursor node { id } } } }关于构造高级查询的更多信息可参见custom-queries 文档。仓库测试库中对 SETOF 连接的覆盖非常全面见 kitchen-sink-schema.sqlcreate function c.table_set_query() returns setof c.person as $$ select * from c.person order by id asc $$ language sql stable; create function c.table_set_query_plpgsql() returns setof c.person as $$ begin return query select * from c.person order by id asc; end $$ language plpgsql stable; create function c.table_set_query_volatile() returns setof c.person as $$ select * from c.person order by id asc $$ language sql volatile; create function b.type_function_connection() returns setof b.types as $$ select * from b.types order by id asc $$ language sql stable; create function c.person_type_function_connection(p c.person) returns setof b.types as $$ select * from b.types order by id asc $$ language sql stable;注意其中的规律同名同返回类型的函数仅因stable与volatile或省略的不同就被分别映射为自定义查询与自定义变更——这正是上文易变性决定暴露形态规则的直接体现。SETOF函数在变更侧同样被支持例如c.table_set_mutation() returns setof c.person它作为变更返回的是表行列表而非连接因为无法对变更结果分页。小结从函数签名预判 GraphQL 暴露形态综合全文可以形成一条判断规则编写一个数据库函数后PostGraphile 会结合易变性声明与返回类型决定其 GraphQL 暴露形态VOLATILE或省略易变性声明默认即 volatile→ 暴露为自定义变更Mutation 根字段STABLE/IMMUTABLE→ 暴露为自定义查询Query 根字段若首个参数为表类型则可能成为该表类型的计算列返回SETOF table时 → 查询侧暴露为可分页的ConnectionRelay 风格edges/pageInfo变更侧暴露为表行列表匿名参数会被自动命名为arg0、arg1……命名参数保持原名带default的参数在 GraphQL 侧为可选项STRICT声明影响参数的 null 语义与字段可空性。掌握这些规则后你便可以在数据库层自由组合 SQL、PL/pgSQL 甚至 plv8 等语言来定义业务逻辑并准确预判 PostGraphile 会为你的函数生成怎样的 GraphQL API——所有结论均可在当前仓库的 kitchen-sink-schema.sql 与 procedure-mutation.test.graphql 测试中逐一验证。赞分享后端API网关【免费下载链接】crystal Graphiles Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!项目地址https://gitcode.com/gh_mirrors/cry/crystal点击查看免费下载相关推荐HsMod完整指南55个功能轻松解锁炉石传说高级体验HsMod完整指南55个功能轻松解锁炉石传说高级体验 你是否厌倦了炉石传说中重复的等待时间想要更流畅的游戏体验和个性化定制HsMod正是为你准备的解决方案后端API网关PostGraphile v4 自定义查询Custom Queries实战指南用 PostgreSQL 函数自动生成 GraphQL 根字段PostGraphile v4 自定义查询Custom Queries实战指南用 PostgreSQL 函数自动生成 GraphQL 根字段 导读 本文围后端API网关PostGraphile 数据库自省Introspection机制解析从 PostgreSQL 系统目录到 GraphQL SchemaPostGraphile 数据库自省Introspection机制解析从 PostgreSQL 系统目录到 GraphQL Schema PostGrap后端API网关上一篇mmsegmentation 语义分割可视化实战指南从单图演示到训练监控的完整链路下一篇如何快速掌握NanoKVMRISC-V多媒体子系统的视频采集与处理核心技术创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考