使用Postgres.js创建PostgreSQL枚举类型时双引号引发报错,如何改为单引号?
Postgres.js创建PostgreSQL枚举类型时双引号引发报错,如何改为单引号?
我完全懂你遇到的这个坑——Postgres.js默认给枚举值套了双引号,但PostgreSQL的枚举类型要求值用单引号包裹,这就直接导致执行报错了。你在catch里替换引号的思路其实是事后补救,根本没法解决实际执行时的问题,咱们得从根源上让生成的SQL就用单引号才行!
问题根源
你之前直接用${sql(Enum)}把数组传给Postgres.js的模板字符串,它会把数组里的每个元素当作数据库标识符(比如表名、列名)来处理,所以自动加了双引号。但枚举值是字符串字面量,需要的是单引号包裹,这俩的处理逻辑完全不一样。
正确解决方案:用sql.literal()处理字面量
Postgres.js专门提供了sql.literal()方法来生成符合要求的SQL字面量,它会自动给字符串加单引号,还能防止SQL注入。咱们只需要遍历枚举数组,给每个元素用sql.literal()处理,再用sql.join()拼接成枚举值列表就行。
修改后的完整代码如下:
import { PostgresError } from 'postgres'; import sql from './database/index.js'; const RolesEnum = { user: 'user', moderator: 'moderator', admin: 'admin' } as const; const Enum = ['user', 'moderator', 'admin'] as const; async function Build() { try { await sql`DROP TYPE IF EXISTS F_that;`; // 用IF EXISTS更优雅,不用手动忽略不存在的错误 const { statement } = await sql` CREATE TYPE F_that AS ENUM (${sql.join(Enum.map(role => sql.literal(role)), ', ')}); `; console.log(statement); // 现在会输出带单引号的正确SQL } catch (error) { console.error( (error as PostgresError).query ?? (error as Error).message ?? error ); } } await Build();
效果验证
修改后生成的SQL会是这样的:
CREATE TYPE F_that AS ENUM ('user', 'moderator', 'admin');
完全符合PostgreSQL的语法要求,不会再因为双引号报错了。
额外优化点
我还把你原来的DROP TYPE F_that;改成了DROP TYPE IF EXISTS F_that;,这样PostgreSQL会自动忽略“类型不存在”的情况,不用再手动catch这个语句的错误,代码更简洁健壮。
为什么不推荐字符串替换的方式?
你之前在catch里替换引号的思路,一来是执行报错后才处理,根本没法解决实际执行的问题;二来手动替换字符串还可能引入SQL注入风险,而用sql.literal()是Postgres.js官方推荐的安全方式,能自动处理转义等细节,绝对比手动拼接靠谱。
内容来源于stack exchange




