Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 29 additions & 29 deletions bundle-size-baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,25 @@
{
"name": "@databricks/appkit",
"tarball": {
"packed": 1364290,
"unpacked": 4666959
"packed": 1448119,
"unpacked": 4951341
},
"dist": {
"total": {
"raw": 4651188,
"gzip": 1599394
"raw": 4935570,
"gzip": 1691147
},
"js": {
"raw": 1391995,
"gzip": 496182
"raw": 1486963,
"gzip": 527492
},
"types": {
"raw": 478827,
"gzip": 174184
"raw": 489843,
"gzip": 179005
},
"maps": {
"raw": 2769571,
"gzip": 925210
"raw": 2947969,
"gzip": 980832
},
"css": {
"raw": 0,
Expand All @@ -31,7 +31,7 @@
"raw": 10795,
"gzip": 3818
},
"fileCount": 935
"fileCount": 969
},
"entries": [
{
Expand Down Expand Up @@ -64,17 +64,17 @@
},
{
"id": "./beta",
"gzip": 110783,
"gzip": 110872,
"composition": {
"initialGzip": 110298,
"initialGzip": 110387,
"lazyGzip": 485,
"totalGzip": 110783,
"own": 331023,
"totalGzip": 110872,
"own": 331204,
"nodeModules": null,
"chunks": [
{
"label": "beta.js",
"gzip": 89747,
"gzip": 89836,
"kind": "initial"
},
{
Expand Down Expand Up @@ -127,29 +127,29 @@
},
{
"id": "./beta/database-tooling",
"gzip": 28557,
"gzip": 45940,
"composition": {
"initialGzip": 28557,
"initialGzip": 45940,
"lazyGzip": 0,
"totalGzip": 28557,
"own": 84002,
"totalGzip": 45940,
"own": 136903,
"nodeModules": null,
"chunks": [
{
"label": "index.js",
"gzip": 28557,
"gzip": 45940,
"kind": "initial"
}
]
}
},
{
"id": "./testing",
"gzip": 79050,
"gzip": 79052,
"composition": {
"initialGzip": 45995,
"lazyGzip": 33055,
"totalGzip": 79050,
"lazyGzip": 33057,
"totalGzip": 79052,
"own": 228781,
"nodeModules": null,
"chunks": [
Expand All @@ -170,7 +170,7 @@
},
{
"label": "index.js",
"gzip": 28624,
"gzip": 28626,
"kind": "lazy"
},
{
Expand Down Expand Up @@ -206,17 +206,17 @@
},
{
"id": "./type-generator",
"gzip": 26635,
"gzip": 26636,
"composition": {
"initialGzip": 26635,
"initialGzip": 26636,
"lazyGzip": 0,
"totalGzip": 26635,
"totalGzip": 26636,
"own": 76220,
"nodeModules": null,
"chunks": [
{
"label": "index.js",
"gzip": 26635,
"gzip": 26636,
"kind": "initial"
}
]
Expand Down
6 changes: 4 additions & 2 deletions docs/docs/plugins/database.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,12 @@ protected branches.

### One command at a time

While `migrate` or `introspect` runs, it holds
While `migrate`, `introspect`, `clean` or `init` runs, it holds
`config/database/.appkit-db.lock`. A second `appkit db` command in the same
checkout fails and names the process that holds the lock. The lock goes when
the command finishes or exits.
the command finishes or exits. `init` holds it for its whole run, including the
clean and migrate it starts. `init --schema none` changes no migration history,
so it takes no lock.

A command stopped by a signal, such as SIGKILL, can leave the file behind. The
next command then names the holder. When that process no longer runs, the
Expand Down
11 changes: 6 additions & 5 deletions packages/appkit/src/database/migrations/apply.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ function systemCodeOf(error: unknown): string | undefined {
return findCode(error, (code) => SYSTEM_CODE.test(code));
}

function quoteIdentifier(name: string): string {
export function quoteIdentifier(name: string): string {
return `"${name.replace(/"/g, '""')}"`;
}

Expand Down Expand Up @@ -367,14 +367,15 @@ async function acquireMigrationLock(
}
}

async function inTransaction(
export async function inTransaction<T>(
client: QueryClient,
work: () => Promise<void>,
): Promise<void> {
work: () => Promise<T>,
): Promise<T> {
await client.query("begin");
try {
await work();
const result = await work();
await client.query("commit");
return result;
} catch (error) {
await client.query("rollback").catch(() => undefined);
throw error;
Expand Down
3 changes: 2 additions & 1 deletion packages/appkit/src/database/migrations/ownership.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ export function ownershipHint(
`Schema "${ownership.schemaName}" is owned by "${ownership.owner}", but the app connects as "${ownership.currentUser}". ` +
"This happens when the app's database resource was removed and added again, or the app was recreated with a new service principal: " +
`Lakebase hands the objects to another role. Have "${ownership.owner}" transfer the schema and its tables to "${ownership.currentUser}" ` +
"(ALTER … OWNER TO), or set another schemaName in schema.ts."
"(ALTER … OWNER TO), or set another schemaName in schema.ts. " +
"On your dev or session branch, rerun `appkit db init`, which hands you the schema."
);
}
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ describe("ownership: one owner per branch", () => {
"the app's database resource was removed and added again, or the app was recreated",
);
expect(hint).toContain("set another schemaName in schema.ts");
expect(hint).toContain(
"On your dev or session branch, rerun `appkit db init`, which hands you the schema.",
);
});

test("a schema that does not exist yet points at CREATE on the database", () => {
Expand Down
24 changes: 24 additions & 0 deletions packages/appkit/src/database/tooling/branch-sql.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { PoolClient } from "pg";

import { inTransaction } from "../migrations/apply";
import { type DbContext, withBranchPool } from "./context";

/** Run work in one transaction on the `.env` branch. */
export function runInTransaction<T>(
ctx: DbContext,
work: (client: PoolClient) => Promise<T>,
): Promise<T> {
return withBranchPool(ctx, async (pool) => {
const client = await pool.connect();
let failed: Error | undefined;
try {
return await inTransaction(client, () => work(client));
} catch (error) {
failed = error instanceof Error ? error : new Error(String(error));
throw error;
} finally {
// A connection whose transaction state is unknown is not reused.
client.release(failed);
}
});
}
Loading
Loading