You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用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

火山引擎 最新活动