runlot

env.db

워커가 보는 데이터베이스 API 는 넷입니다. exec · tryExec · session · identity.

export interface Db {
  exec(sql: string, params?: unknown[]): Promise<unknown[]>;
  tryExec(sql: string, params?: unknown[]): Promise<TryExecResult>;
  session(): Promise<DbSession>;
  identity(): Promise<{ project: string; epoch: number }>;
}

exec

성공하면 행 배열을, 실패하면 던집니다.

const rows = await env.db.exec(
  "insert into posts (title) values ($1) returning id",
  ["hello"],
);

파라미터는 $1 부터입니다. 문자열을 이어 붙이지 마세요 — 두 번째 인자가 바인딩입니다.

tryExec

던지지 않고 결과를 돌려줍니다. SQLSTATE 를 코드로 읽는 유일한 방법입니다.

const r = await env.db.tryExec("insert into users (email) values ($1)", [email]);
if (!r.ok) {
  if (r.error.code === "23505") return new Response("이미 있는 이메일입니다", { status: 409 });
  throw new Error(r.error.message);
}

성공한 결과의 모양입니다.

{
  ok: true,
  columns: [{ name: "id", typeOid: 23 }],
  values: [[1]],                  // 위치 배열. 열 이름이 겹쳐도 셀을 잃지 않습니다
  rows: [{ id: 1 }],
  commandTag: "SELECT 1",
  transactionStatus: "I",
}

실패한 결과입니다.

{
  ok: false,
  error: { code: "23505", message: "…", detail: null, hint: null, position: null, severity: "ERROR" },
}
exec 이 던지는 Error 에는 .code 가 없습니다. 던진 객체의 커스텀 필드는 워커 경계를 넘지 못하기 때문입니다. SQLSTATE 는 메시지 앞에 [42P01] 처럼 붙여 두었지만, 프로그램이 읽을 계약은 tryExec(...).error.code 하나입니다. @runlot/pg 를 쓰시면 err.code 가 정상으로 옵니다 — 그쪽은 워커 안의 JS 라 경계를 넘지 않습니다.

session

BEGIN … COMMIT 처럼 여러 문장이 한 세션에 있어야 할 때 씁니다.

const s = await env.db.session();
try {
  await s.exec("begin");
  await s.exec("update accounts set balance = balance - $1 where id = $2", [100, 1]);
  await s.exec("update accounts set balance = balance + $1 where id = $2", [100, 2]);
  await s.exec("commit");
} finally {
  await s.close();
}

close() 는 멱등이고, 열려 있던 블록을 롤백합니다. 커밋은 여러분이 COMMIT 을 보내는 것이지 close 의 부수 효과가 아닙니다. 자세한 것은 세션 에 있습니다.

identity

지금 어느 프로젝트의 몇 번째 세대인지 알려 줍니다. 디버깅과 로그에 씁니다.

const { project, epoch } = await env.db.identity();

값이 옮겨지는 규칙

조용한 손실이 없는 것이 규칙입니다.

타입JS 로
int2 int4 float4 float8number
int8 numericstring — number 로 조용히 반올림하지 않습니다
boolboolean
text varchar uuidstring
byteaArrayBuffer
json jsonb파싱한 값. 실패하면 원문 문자열을 보존합니다
timestamp timestamptz datestring — 타임존과 정밀도를 잃지 않습니다
배열·도메인·확장 타입{ text, typeOid } 또는 문자열

표에 없는 타입은 문자열로 흘립니다. base 타입을 추측하지 않습니다.

@runlot/pg 는 이 위에 node-postgres 의 기본 파서를 얹습니다. 그래서 그쪽에서는 timestamptzdateDate 객체로 옵니다. 두 표면의 날짜 타입이 다릅니다.

이 페이지에서