PAYATHON 2026

Postgres.js 动态建表时如何安全插入列类型?

数据阿明

结论

Postgres.js 的 sql(value) 用来安全引用标识符,例如 schema、表名和列名。它不会将字符串作为 SQL 语法直接插入。因此,sql(column.type) 会被当作标识符处理,而普通模板插值会生成参数占位符。列类型属于 SQL 语法片段,不能作为普通值参数传入。

可以将允许的类型名映射到预先定义的 SQL 片段,再把对应片段插入查询。Postgres.js 文档说明,sql.unsafe 可以在安全模板中嵌入原始 SQL 片段,但动态内容必须受到严格限制。Postgres.js 文档:Unsafe raw string queries

示例

const typeFragments = new Map([
  ['uuid', sql.unsafe('uuid')],
  ['boolean', sql.unsafe('boolean')],
  ['text', sql.unsafe('text')],
]);

const columns = table.columns.map((column) => {
  const type = typeFragments.get(column.type);

  if (!type) {
    throw new Error(`Unsupported column type: ${column.type}`);
  }

  return sql`${sql(column.name)} ${type} ${
    column.pk ? sql`primary key` : sql``
  }`;
});

await sql`
  create table if not exists ${sql(table.schema)}.${sql(table.name)} (
    ${sql.join(columns, sql`, `)}
  )
`;

sql(column.name)、sql(table.schema) 和 sql(table.name) 会将动态内容作为标识符处理。类型片段从固定映射中选取,未列入映射的输入会被拒绝。示例使用 sql.join,并以分隔符组合各列定义。

注意事项

不要直接将来自请求、文件或其他不可信来源的 column.type 传给 sql.unsafe(column.type)。类型名不能作为参数绑定;如果允许任意输入直接进入 SQL,就可能引入 SQL 注入风险。需要支持更多类型时,应将明确允许的类型加入映射。若要支持带 schema 的自定义类型,也应先验证各部分,再构造对应片段。

备注:内容仅供参考。