关系型数据库(PostgreSQL)
自 @cloudbase/manager-node@5.4.0 起新增此模块。通过 app.database 访问,提供在 PostgreSQL 架构的云开发环境上执行 SQL 语句及管理数据的能力。
初始化
import CloudBase from '@cloudbase/manager-node'
const app = CloudBase.init({
secretId: 'Your SecretId',
secretKey: 'Your SecretKey',
envId: 'Your envId'
})
const { database } = app
executePGSql
1. 接口描述
接口功能:在 PostgreSQL 环境上执行任意 SQL 语句(DDL / DML / DQL 等),并返回结果集与受影响行数。
接口声明:app.database.executePGSql(options): Promise<IExecutePGSqlResult>
2. 输入参数
IExecutePGSqlOptions
| 字段 | 必填 | 类型 | 说明 |
|---|---|---|---|
| Sql | 是 | String | 要执行的 SQL 语句 |
| Role | 否 | String | 指定 role 执行 SQL,可传任意有效的 role(包括用户自定义 role)。需要只读执行时请传应用角色 anon / authenticated 或你自建的只读角色;平台保留角色不对用户开放,直接传可能因缺少权限报 42501 |
| EnvId | 否 | String | 云开发环境 ID,不传则使用当前管理实例初始化时的 EnvId |
3. 返回结果
IExecutePGSqlResult
| 字段 | 类型 | 说明 |
|---|---|---|
| RequestId | String | 请求唯一标识 |
| AffectedRows | Number | 受影响行数(DML 类语句有效) |
| Columns | String[] / null | 字段名列表(SELECT 类语句返回);无结果集时为 null |
| Rows | String[] / null | 数据行列表,每一项是 JSON 串,反序列化后是 (string | null)[],按 Columns 顺序对齐;无结果集时为 null |
| ExecutionTimeMs | Number | SQL 执行耗时(毫秒) |
4. 示例代码
// 创建表
await database.executePGSql({
Sql: 'CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT NOT NULL, email TEXT UNIQUE)'
})
// 插入数据
const insertRes = await database.executePGSql({
Sql: "INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com')"
})
console.log('受影响行数:', insertRes.AffectedRows) // 1
// 查询并解析结果
const res = await database.executePGSql({
Sql: 'SELECT id, name, email FROM users WHERE id = 1'
})
console.log(res.Columns) // ['id', 'name', 'email']
const rows = (res.Rows || []).map(s => JSON.parse(s))
// rows: [['1', 'Alice', 'alice@example.com']]
// 用你自己创建的只读角色执行查询,避免非预期的写操作执行成功
const readonlyRes = await database.executePGSql({
Role: 'report_reader',
Sql: 'SELECT id, name, email FROM users'
})
console.log(readonlyRes.Rows)
// 指定其他环境 ID 执行
await database.executePGSql({
EnvId: 'other-env-id',
Sql: 'SELECT NOW()'
})
数据库迁移(Migrations)
自 @cloudbase/manager-node@5.6.5 起新增以下数据库迁移相关方法,用于在 PostgreSQL 架构的云开发环境中,以“版本化 SQL 脚本”的方式管理表结构与数据变更。
概念说明
- Migration(迁移):一次带有版本号的 SQL 变更,包含
Version、Name、Query。Version:14 位数字时间串,例如20260526000000。建议使用发生时间(YYYYMMDDHHMMSS)保证全局有序、唯一。Name:仅允许小写字母和下划线,如create_users_table。Query:本次要执行的 SQL。
cloudbase_migrations.schema_migrations:云开发在目标 PG 环境中维护的迁移历史表,记录已成功应用的 migration。- 典型流程:
- 用
previewPGUserMigrations预览计划、检查 checksum 冲突。 - 无冲突后调用
pushPGUserMigrations,得到TaskId。 - 通过
describeTaskResult轮询任务状态直至Succeed/Failed。 - 需要时用
listPGUserMigrations/listAllPGUserMigrations/describePGUserMigration查询已应用记录。 - 出现历史与实际不一致时,用
repairPGUserMigrationHistory仅修复 history 记录(不执行 SQL)。
- 用
previewPGUserMigrations
1. 接口描述
接口功能:预览一批用户 migration 的远端执行计划(不实际执行 SQL),并返回 Pending(待执行)、Applied(已应用)、Conflicts(checksum 冲突)、Executable(是否可直接执行)。
接口声明:app.database.previewPGUserMigrations(options): Promise<IPreviewPGUserMigrationsResult>
2. 输入参数
IPreviewPGUserMigrationsOptions
| 字段 | 必填 | 类型 | 说明 |
|---|---|---|---|
| EnvId | 否 | String | 云开发环境 ID,不传则使用管理实例初始化时的 EnvId |
| Migrations | 是 | IPGUserMigrationInput[] | migration 列表,至少 1 条;每条需通过下方入参校验 |
| IncludeAll | 否 | Boolean | 是否允许 out-of-order 本地 migration(在已应用版本之前插入新版本),默认 false |
IPGUserMigrationInput
| 字段 | 必填 | 类型 | 说明 |
|---|---|---|---|
| Version | 是 | String | migration 版本,14 位数字时间串(如 20260526000000) |
| Name | 是 | String | migration 名称,仅允许小写字母和下划线 |
| Query | 是 | String | 要执行的 SQL,不能为空字符串 |
3. 返回结果
IPreviewPGUserMigrationsResult
| 字段 | 类型 | 说明 |
|---|---|---|
| Pending | IPGUserMigrationPlanItem[] | null | 将要执行的 migration 列表 |
| Applied | IPGUserMigrationPlanItem[] | null | 已经应用过的 migration 列表 |
| Conflicts | IPGUserMigrationConflict[] | null | 冲突列表:版本号相同但 checksum 不一致的 migration,非空即表示本地 SQL 与已应用记录不一致 |
| Executable | Boolean | 是否可直接执行(当前主要表示无 checksum 冲突) |
| RequestId | String | 请求唯一标识 |
IPGUserMigrationPlanItem 主要字段:Version、Name、Status(如 applied / pending)、Reason(如 checksum_matched)、Checksum、Source。
IPGUserMigrationConflict 主要字段:Version、Name、RemoteName、LocalChecksum、RemoteChecksum、Reason(如 checksum_mismatch)、Message。
4. 示例代码
const preview = await database.previewPGUserMigrations({
Migrations: [
{
Version: '20260526000000',
Name: 'create_users_table',
Query:
'CREATE TABLE IF NOT EXISTS users (id SERIAL PRIMARY KEY, name TEXT NOT NULL);'
}
]
})
if (!preview.Executable || (preview.Conflicts || []).length > 0) {
console.error('存在 checksum 冲突,需要处理:', preview.Conflicts)
return
}
console.log('待执行:', preview.Pending)
console.log('已应用:', preview.Applied)
pushPGUserMigrations
1. 接口描述
接口功能:批量应用一组用户 migration。该接口是异步任务,成功调用后返回 TaskId,需要用 describeTaskResult 查询最终结果。
接口声明:app.database.pushPGUserMigrations(options): Promise<IPushPGUserMigrationsResult>
2. 输入参数
IPushPGUserMigrationsOptions
| 字段 | 必填 | 类型 | 说明 |
|---|---|---|---|
| EnvId | 否 | String | 云开发环境 ID,不传则使用管理实例初始化时的 EnvId |
| Migrations | 是 | IPGUserMigrationInput[] | migration 列表,至少 1 条;字段校验规则同 previewPGUserMigrations |
| LockTimeoutMs | 否 | Number | 获取数据库锁的最长等待时间(毫秒),不能小于 0,默认 5000 |
| StatementTimeoutMs | 否 | Number | 单条 SQL 最长执行时间(毫秒),不能小于 0,默认 300000 |
| IncludeAll | 否 | Boolean | 是否允许 out-of-order 本地 migration,默认 false |
3. 返回结果
IPushPGUserMigrationsResult
| 字段 | 类型 | 说明 |
|---|---|---|
| RequestId | String | 请求唯一标识 |
| TaskId | String | 异步任务 ID,可用于查询任务进度与状态 |
4. 示例代码
// 1. 提交异步任务
const { TaskId } = await database.pushPGUserMigrations({
Migrations: [
{
Version: '20260526000000',
Name: 'create_users_table',
Query:
'CREATE TABLE IF NOT EXISTS users (id SERIAL PRIMARY KEY, name TEXT NOT NULL);'
}
],
LockTimeoutMs: 5000,
StatementTimeoutMs: 60000
})
// 2. 轮询任务状态
async function waitTask(taskId) {
while (true) {
const task = await database.describeTaskResult({ TaskId: taskId })
if (task.Status === 'Succeed' || task.Status === 'Failed') {
return task
}
await new Promise(r => setTimeout(r, 1000))
}
}
const result = await waitTask(TaskId)
console.log('任务结束:', result.Status, result.Phase, result.Reason)
repairPGUserMigrationHistory
1. 接口描述
接口功能:仅维护 cloudbase_migrations.schema_migrations 表中的历史记录,不执行任何 SQL。适用于以下场景:
- 线下手动执行过 SQL,但历史表中没有对应记录(用
applied补录)。 - 需要将某条已记录的 migration 从历史中撤回(用
reverted删除记录)。
接口声明:app.database.repairPGUserMigrationHistory(options): Promise<IResponseInfo>
2. 输入参数
IRepairPGUserMigrationHistoryOptions
| 字段 | 必填 | 类型 | 说明 |
|---|---|---|---|
| EnvId | 否 | String | 云开发环境 ID,不传则使用管理实例初始化时的 EnvId |
| MigrationVersion | 是 | String | migration 版本,14 位数字时间串 |
| Name | 是 | String | migration 名称,仅允许小写字母和下划线 |
| Status | 是 | 'applied' | 'reverted' | 修复方式:applied 写入 history,reverted 从 history 删除 |
| Reason | 是 | String | 修复原因,不能为空 |
| Query | 否 | String | 当 Status=applied 时必填,建议填对应的 SQL;Status=reverted 时可不传 |
3. 返回结果
IResponseInfo:包含 RequestId 等通用字段。
4. 示例代码
// 补录一条已线下执行的 migration
await database.repairPGUserMigrationHistory({
MigrationVersion: '20260526000000',
Name: 'create_users_table',
Status: 'applied',
Reason: '线下手动执行完成,补录历史',
Query: 'CREATE TABLE IF NOT EXISTS users (id SERIAL PRIMARY KEY, name TEXT NOT NULL);'
})
// 将一条错误记录的 migration 从 history 中撤回
await database.repairPGUserMigrationHistory({
MigrationVersion: '20260526000000',
Name: 'create_users_table',
Status: 'reverted',
Reason: '误记录,需从 history 中删除'
})
listPGUserMigrations
1. 接口描述
接口功能:分页查询目标环境已应用的 migration 列表。
接口声明:app.database.listPGUserMigrations(options?): Promise<IListPGUserMigrationsResult>
2. 输入参数
IListPGUserMigrationsOptions(全部可选)
| 字段 | 必填 | 类型 | 说明 |
|---|---|---|---|
| EnvId | 否 | String | 云开发环境 ID,不传则使用管理实例初始化时的 EnvId |
| Limit | 否 | Number | 查询条数,取值范围 [1, 500],默认 100 |
| Offset | 否 | Number | 分页偏移,>= 0,默认 0 |
3. 返回结果
IListPGUserMigrationsResult
| 字段 | 类型 | 说明 |
|---|---|---|
| Total | Number | 总数量 |
| LatestVersion | String | 已应用的最新版本号(14 位数字时间串) |
| Migrations | IPGUserMigrationSummary[] | 已应用 migration 列表,每项包含 Version、Name |
| RequestId | String | 请求唯一标识 |
4. 示例代码
const page = await database.listPGUserMigrations({ Limit: 100, Offset: 0 })
console.log('总数:', page.Total, '最新版本:', page.LatestVersion)
page.Migrations.forEach(m => {
console.log(m.Version, m.Name)
})
listAllPGUserMigrations
1. 接口描述
接口功能:自动翻页查询目标环境全部已应用的 migration 列表。内部按 PageSize 反复调用 listPGUserMigrations 直到取完。
接口声明:app.database.listAllPGUserMigrations(options?): Promise<IListPGUserMigrationsResult>
2. 输入参数
IListAllPGUserMigrationsOptions(全部可选)
| 字段 | 必填 | 类型 | 说明 |
|---|---|---|---|
| EnvId | 否 | String | 云开发环境 ID,不传则使用管理实例初始化时的 EnvId |
| PageSize | 否 | Number | 内部翻页每页大小,取值范围 [1, 500],默认 500 |
3. 返回结果
同 listPGUserMigrations:IListPGUserMigrationsResult。返回的 Migrations 为全量列表。
4. 示例代码
const all = await database.listAllPGUserMigrations()
console.log('已应用总数:', all.Migrations.length)
console.log('最新版本:', all.LatestVersion)
describePGUserMigration
1. 接口描述
接口功能:查询目标环境指定 migration 的详情(包含 SQL 内容)。
接口声明:app.database.describePGUserMigration(options): Promise<IDescribePGUserMigrationResult>
2. 输入参数
IDescribePGUserMigrationOptions
| 字段 | 必填 | 类型 | 说明 |
|---|---|---|---|
| EnvId | 否 | String | 云开发环境 ID,不传则使用管理实例初始化时的 EnvId |
| MigrationVersion | 是 | String | migration 版本,14 位数字时间串 |
3. 返回结果
IDescribePGUserMigrationResult
| 字段 | 类型 | 说明 |
|---|---|---|
| Version | String | migration 版本号 |
| Name | String | migration 名称 |
| Query | String | migration 对应的 SQL |
| RequestId | String | 请求唯一标识 |
4. 示例代码
const detail = await database.describePGUserMigration({
MigrationVersion: '20260526000000'
})
console.log(detail.Version, detail.Name)
console.log(detail.Query)
describeTaskResult
1. 接口描述
接口功能:查询异步任务(如 pushPGUserMigrations 返回的任务)的执行状态。可用于轮询直至任务终态。
接口声明:app.database.describeTaskResult(options): Promise<IDescribeTaskResultResult>
2. 输入参数
IDescribeTaskResultOptions
| 字段 | 必填 | 类型 | 说明 |
|---|---|---|---|
| EnvId | 否 | String | 云开发环境 ID,不传则使用管理实例初始化时的 EnvId |
| TaskId | 是 | String | 任务 ID,不能为空 |
3. 返回结果
IDescribeTaskResultResult
| 字段 | 类型 | 说明 |
|---|---|---|
| TaskId | String | 任务 ID |
| TaskType | String | 任务类型(如 PGUserMigration) |
| Status | String | 任务状态:Accepted / Running / Succeed / Failed |
| Phase | String | 当前步骤 |
| Reason | String | 失败原因(成功时可能为空) |
| CreatedAt | String | 创建时间(ISO 8601) |
| UpdatedAt | String | 最后更新时间(ISO 8601) |
| RequestId | String | 请求唯一标识 |
4. 示例代码
const task = await database.describeTaskResult({ TaskId: 'task-xxxxxx' })
if (task.Status === 'Succeed') {
console.log('任务成功,耗时至', task.UpdatedAt)
} else if (task.Status === 'Failed') {
console.error('任务失败:', task.Phase, task.Reason)
} else {
console.log('任务进行中:', task.Status, task.Phase)
}