@exposify/supabase 0.3.13 → 0.3.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -10
- package/dist/cli.js +19 -8
- package/dist/index.cjs +21 -3
- package/dist/index.d.cts +18 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +21 -3
- package/package.json +2 -2
- package/sql/exposify_meta.sql +14 -2
package/README.md
CHANGED
|
@@ -28,15 +28,18 @@ npx @exposify/supabase init
|
|
|
28
28
|
|
|
29
29
|
This is safe to re-run; see "Re-running `init`" below. It writes:
|
|
30
30
|
|
|
31
|
-
- `supabase/migrations/<timestamp>
|
|
31
|
+
- `supabase/migrations/<timestamp>_exposify_meta_v3.sql` — a SQL migration that
|
|
32
32
|
adds `exposify_meta()`, a `SECURITY DEFINER` function returning, per table,
|
|
33
33
|
whether RLS is enabled, what the `authenticated` role is granted on it
|
|
34
|
-
(select/insert/update/delete),
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
(select/insert/update/delete), whether each column is an identity or
|
|
35
|
+
generated column (so `insert_<table>` doesn't ask for a `generated always as
|
|
36
|
+
identity` key the database rejects), and any `COMMENT ON TABLE`/`COMMENT ON
|
|
37
|
+
COLUMN` text you've written. It only ever returns catalog metadata
|
|
38
|
+
(table/column names, flags, comments) — never row data — which is what makes
|
|
39
|
+
it safe to grant to `anon`/`authenticated` and call with a low-privilege key.
|
|
40
|
+
A project on an older migration (`_exposify_meta.sql` with no grants, or
|
|
41
|
+
`_exposify_meta_v2.sql` with no identity/generated columns) gets the `_v3`
|
|
42
|
+
one on the next `init`, which replaces the function.
|
|
40
43
|
- `supabase/functions/mcp/index.ts` — the edge function itself. `init` owns
|
|
41
44
|
the part between `// exposify:begin` and `// exposify:end` (imports and a
|
|
42
45
|
`generated` options object). Everything after that is yours, starting with
|
|
@@ -252,6 +255,12 @@ Deno.serve(aiReady({
|
|
|
252
255
|
```
|
|
253
256
|
|
|
254
257
|
Notes:
|
|
258
|
+
- `insert_<table>` takes a `body` with one field per column, but only the
|
|
259
|
+
columns the caller actually supplies: a column with a `DEFAULT` or a `BY
|
|
260
|
+
DEFAULT` identity is optional (the database fills it in), and a `GENERATED
|
|
261
|
+
ALWAYS AS IDENTITY` column or a generated column is left out entirely (the
|
|
262
|
+
database computes it and rejects any value). So a table whose only
|
|
263
|
+
caller-supplied column is `body` is inserted with just `{ "body": "..." }`.
|
|
255
264
|
- `update_<table>` and `delete_<table>` change one row: the filter must
|
|
256
265
|
include `eq` on every primary-key column (more filters only narrow it).
|
|
257
266
|
A filter that happens to match everything (`id neq -1`, `item like %`) is
|
|
@@ -357,9 +366,9 @@ exits 1 if anything failed, so you can run it in CI.
|
|
|
357
366
|
readable. It lists which tables became tools, and warns about tables that
|
|
358
367
|
never will: RLS off, or RLS on but no `SELECT` for `authenticated` (with the
|
|
359
368
|
`grant` to run). It warns about writes in the function's config the role
|
|
360
|
-
isn't granted, about
|
|
361
|
-
grants, and about tables whose missing
|
|
362
|
-
descriptions.
|
|
369
|
+
isn't granted, about an older `exposify_meta()` that doesn't report
|
|
370
|
+
grants or identity/generated columns, and about tables whose missing
|
|
371
|
+
comments leave tools with thin descriptions.
|
|
363
372
|
- **Deployed function:** speaks MCP to `<project>/functions/v1/mcp`
|
|
364
373
|
(`initialize`, then `tools/list`) and reports the tool count. If the
|
|
365
374
|
function registered `exposify://diagnostics`, which it only does when
|
package/dist/cli.js
CHANGED
|
@@ -76,6 +76,10 @@ function columnsFromOpenApi(doc, table) {
|
|
|
76
76
|
type: TYPE_MAP[prop.type ?? "string"] ?? "string",
|
|
77
77
|
format: prop.format,
|
|
78
78
|
required: required.has(name),
|
|
79
|
+
// A column with a DEFAULT doesn't have to be supplied on insert even when
|
|
80
|
+
// it's NOT NULL (the database fills it in). identity/generated aren't in
|
|
81
|
+
// PostgREST's OpenAPI -- introspect.ts adds those from exposify_meta().
|
|
82
|
+
hasDefault: prop.default !== void 0,
|
|
79
83
|
isPrimaryKey: isPrimaryKey(prop),
|
|
80
84
|
// Falls back to the OpenAPI-embedded comment (PostgREST mirrors `COMMENT
|
|
81
85
|
// ON COLUMN` into `description`, then appends its own "Note:\nThis is a
|
|
@@ -110,7 +114,13 @@ function introspectTables(openapi, metaRows) {
|
|
|
110
114
|
...fromOpenApi,
|
|
111
115
|
// exposify_meta()'s comment wins over OpenAPI's mirrored copy (spec:
|
|
112
116
|
// "Column comments → param descriptions" sourced from the migration).
|
|
113
|
-
description: row.column_comment ?? fromOpenApi.description
|
|
117
|
+
description: row.column_comment ?? fromOpenApi.description,
|
|
118
|
+
// identity/generated aren't in PostgREST's OpenAPI; they come from
|
|
119
|
+
// exposify_meta() (the `_v3` migration). Absent on the older function,
|
|
120
|
+
// leaving these unset -- the insert/update shapes then fall back to
|
|
121
|
+
// defaults alone, which is the pre-fix behaviour.
|
|
122
|
+
...row.identity_kind ? { identity: row.identity_kind } : {},
|
|
123
|
+
...row.is_generated === true ? { generated: true } : {}
|
|
114
124
|
});
|
|
115
125
|
}
|
|
116
126
|
if (columns.length === 0) continue;
|
|
@@ -741,14 +751,14 @@ async function runDoctor(deps) {
|
|
|
741
751
|
sections.push({ title: "Project", checks: project });
|
|
742
752
|
const files = [];
|
|
743
753
|
const migrations = await deps.listDir(MIGRATIONS_DIR);
|
|
744
|
-
const migration = findExistingMigration(migrations, "
|
|
745
|
-
const legacyMigration = findExistingMigration(migrations, "_exposify_meta.sql");
|
|
754
|
+
const migration = findExistingMigration(migrations, "_exposify_meta_v3.sql");
|
|
755
|
+
const legacyMigration = findExistingMigration(migrations, "_exposify_meta_v2.sql") || findExistingMigration(migrations, "_exposify_meta.sql");
|
|
746
756
|
files.push(
|
|
747
757
|
migration ? { status: "pass", message: `migration ${MIGRATIONS_DIR}/${migration}` } : legacyMigration ? {
|
|
748
758
|
status: "warn",
|
|
749
|
-
message: `migration ${MIGRATIONS_DIR}/${legacyMigration} is
|
|
750
|
-
fix: "npx @exposify/supabase init (it adds *
|
|
751
|
-
} : { status: "fail", message: "no *
|
|
759
|
+
message: `migration ${MIGRATIONS_DIR}/${legacyMigration} is an older exposify_meta(), which doesn't report identity/generated columns`,
|
|
760
|
+
fix: "npx @exposify/supabase init (it adds *_exposify_meta_v3.sql), then supabase db push"
|
|
761
|
+
} : { status: "fail", message: "no *_exposify_meta_v3.sql migration", fix: "npx @exposify/supabase init" }
|
|
752
762
|
);
|
|
753
763
|
const functionSource = await deps.readText(FUNCTION_PATH);
|
|
754
764
|
const authMode = functionSource === null ? null : detectAuthMode(functionSource);
|
|
@@ -1000,7 +1010,8 @@ var FUNCTION_DIR = "supabase/functions/mcp";
|
|
|
1000
1010
|
var FUNCTION_PATH2 = `${FUNCTION_DIR}/index.ts`;
|
|
1001
1011
|
var MIGRATIONS_DIR2 = "supabase/migrations";
|
|
1002
1012
|
var CONFIG_TOML2 = "supabase/config.toml";
|
|
1003
|
-
var META_SUFFIX = "
|
|
1013
|
+
var META_SUFFIX = "_exposify_meta_v3.sql";
|
|
1014
|
+
var META_SUFFIX_V2 = "_exposify_meta_v2.sql";
|
|
1004
1015
|
var LEGACY_META_SUFFIX = "_exposify_meta.sql";
|
|
1005
1016
|
var COMMENTS_SUFFIX = "_exposify_comments.sql";
|
|
1006
1017
|
function authLabel(config) {
|
|
@@ -1036,7 +1047,7 @@ function planInit(input) {
|
|
|
1036
1047
|
changes.push({
|
|
1037
1048
|
path: `${MIGRATIONS_DIR2}/${name}`,
|
|
1038
1049
|
action: "create",
|
|
1039
|
-
summary: findExistingMigration(input.migrations, LEGACY_META_SUFFIX) ? "exposify_meta() update: it also reports
|
|
1050
|
+
summary: findExistingMigration(input.migrations, LEGACY_META_SUFFIX) || findExistingMigration(input.migrations, META_SUFFIX_V2) ? "exposify_meta() update: it also reports identity and generated columns" : "exposify_meta() migration",
|
|
1040
1051
|
content: input.metaMigrationSql,
|
|
1041
1052
|
effect: "push"
|
|
1042
1053
|
});
|
package/dist/index.cjs
CHANGED
|
@@ -258,6 +258,10 @@ function columnsFromOpenApi(doc, table) {
|
|
|
258
258
|
type: TYPE_MAP[prop.type ?? "string"] ?? "string",
|
|
259
259
|
format: prop.format,
|
|
260
260
|
required: required.has(name),
|
|
261
|
+
// A column with a DEFAULT doesn't have to be supplied on insert even when
|
|
262
|
+
// it's NOT NULL (the database fills it in). identity/generated aren't in
|
|
263
|
+
// PostgREST's OpenAPI -- introspect.ts adds those from exposify_meta().
|
|
264
|
+
hasDefault: prop.default !== void 0,
|
|
261
265
|
isPrimaryKey: isPrimaryKey(prop),
|
|
262
266
|
// Falls back to the OpenAPI-embedded comment (PostgREST mirrors `COMMENT
|
|
263
267
|
// ON COLUMN` into `description`, then appends its own "Note:\nThis is a
|
|
@@ -302,7 +306,13 @@ function introspectTables(openapi, metaRows) {
|
|
|
302
306
|
...fromOpenApi,
|
|
303
307
|
// exposify_meta()'s comment wins over OpenAPI's mirrored copy (spec:
|
|
304
308
|
// "Column comments → param descriptions" sourced from the migration).
|
|
305
|
-
description: row.column_comment ?? fromOpenApi.description
|
|
309
|
+
description: row.column_comment ?? fromOpenApi.description,
|
|
310
|
+
// identity/generated aren't in PostgREST's OpenAPI; they come from
|
|
311
|
+
// exposify_meta() (the `_v3` migration). Absent on the older function,
|
|
312
|
+
// leaving these unset -- the insert/update shapes then fall back to
|
|
313
|
+
// defaults alone, which is the pre-fix behaviour.
|
|
314
|
+
...row.identity_kind ? { identity: row.identity_kind } : {},
|
|
315
|
+
...row.is_generated === true ? { generated: true } : {}
|
|
306
316
|
});
|
|
307
317
|
}
|
|
308
318
|
if (columns.length === 0) continue;
|
|
@@ -527,11 +537,16 @@ function registerQueryTool(server, table, client, cfg, taken) {
|
|
|
527
537
|
}
|
|
528
538
|
);
|
|
529
539
|
}
|
|
540
|
+
function isDatabaseGenerated(col) {
|
|
541
|
+
return col.identity === "always" || col.generated === true;
|
|
542
|
+
}
|
|
530
543
|
function insertBodyShape(columns) {
|
|
531
544
|
const shape = {};
|
|
532
545
|
for (const col of columns) {
|
|
546
|
+
if (isDatabaseGenerated(col)) continue;
|
|
533
547
|
let field = columnToZod(col);
|
|
534
|
-
|
|
548
|
+
const databaseFillsIn = col.hasDefault === true || col.identity === "default";
|
|
549
|
+
if (!col.required || databaseFillsIn) field = field.optional();
|
|
535
550
|
shape[col.name] = field;
|
|
536
551
|
}
|
|
537
552
|
return shape;
|
|
@@ -582,7 +597,10 @@ function keyFilterShape(table, verb) {
|
|
|
582
597
|
function registerUpdateTool(server, table, client, taken) {
|
|
583
598
|
const name = toolName("update", table.name, taken);
|
|
584
599
|
const setShape = {};
|
|
585
|
-
for (const col of table.columns)
|
|
600
|
+
for (const col of table.columns) {
|
|
601
|
+
if (isDatabaseGenerated(col)) continue;
|
|
602
|
+
setShape[col.name] = columnToZod(col).optional();
|
|
603
|
+
}
|
|
586
604
|
const keys = keyColumns(table);
|
|
587
605
|
server.registerTool(
|
|
588
606
|
name,
|
package/dist/index.d.cts
CHANGED
|
@@ -7,7 +7,19 @@ interface ColumnSchema {
|
|
|
7
7
|
type: ColumnType;
|
|
8
8
|
/** OpenAPI `format`, e.g. "uuid", "timestamp with time zone", "bigint". */
|
|
9
9
|
format?: string;
|
|
10
|
+
/** NOT NULL per PostgREST's OpenAPI `required`. Whether it must be *given* on insert is a separate question -- see `hasDefault`/`identity` and server.ts's `insertBodyShape`. */
|
|
10
11
|
required: boolean;
|
|
12
|
+
/** The OpenAPI property carried a `default`, so the database fills the column in when it's omitted -- optional on insert even when NOT NULL. */
|
|
13
|
+
hasDefault?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* An identity column (`GENERATED ... AS IDENTITY`), from `exposify_meta()`.
|
|
16
|
+
* `"always"` rejects any supplied value (left out of insert/update entirely);
|
|
17
|
+
* `"default"` is accepted but optional. Absent on a non-identity column, and
|
|
18
|
+
* on every column when the database still has the pre-identity `exposify_meta()`.
|
|
19
|
+
*/
|
|
20
|
+
identity?: "always" | "default";
|
|
21
|
+
/** A generated column (`GENERATED ALWAYS AS (...) STORED`), from `exposify_meta()`: the database computes it, so it's left out of insert/update. */
|
|
22
|
+
generated?: boolean;
|
|
11
23
|
/** From `exposify_meta()`'s `column_comment` (a `COMMENT ON COLUMN`), not OpenAPI. */
|
|
12
24
|
description?: string;
|
|
13
25
|
isPrimaryKey?: boolean;
|
|
@@ -43,6 +55,10 @@ interface MetaRow {
|
|
|
43
55
|
can_insert?: boolean;
|
|
44
56
|
can_update?: boolean;
|
|
45
57
|
can_delete?: boolean;
|
|
58
|
+
/** `pg_attribute.attidentity` mapped: `"always"` ('a'), `"default"` ('d'), or null/absent. Absent before the `_v3` migration. */
|
|
59
|
+
identity_kind?: "always" | "default" | null;
|
|
60
|
+
/** `pg_attribute.attgenerated <> ''` -- a generated (stored) column. Absent before the `_v3` migration. */
|
|
61
|
+
is_generated?: boolean;
|
|
46
62
|
}
|
|
47
63
|
/** Registered sibling edge function exposed as a tool. */
|
|
48
64
|
interface FunctionToolConfig {
|
|
@@ -189,6 +205,8 @@ interface PostgrestColumnProperty {
|
|
|
189
205
|
type?: string;
|
|
190
206
|
format?: string;
|
|
191
207
|
description?: string;
|
|
208
|
+
/** PostgREST emits the column's `DEFAULT` expression here (e.g. `"now()"`, `"auth.uid()"`) when it has one. We only care whether it's present. */
|
|
209
|
+
default?: unknown;
|
|
192
210
|
}
|
|
193
211
|
/**
|
|
194
212
|
* Extracts column schemas for one table from a PostgREST OpenAPI document.
|
package/dist/index.d.ts
CHANGED
|
@@ -7,7 +7,19 @@ interface ColumnSchema {
|
|
|
7
7
|
type: ColumnType;
|
|
8
8
|
/** OpenAPI `format`, e.g. "uuid", "timestamp with time zone", "bigint". */
|
|
9
9
|
format?: string;
|
|
10
|
+
/** NOT NULL per PostgREST's OpenAPI `required`. Whether it must be *given* on insert is a separate question -- see `hasDefault`/`identity` and server.ts's `insertBodyShape`. */
|
|
10
11
|
required: boolean;
|
|
12
|
+
/** The OpenAPI property carried a `default`, so the database fills the column in when it's omitted -- optional on insert even when NOT NULL. */
|
|
13
|
+
hasDefault?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* An identity column (`GENERATED ... AS IDENTITY`), from `exposify_meta()`.
|
|
16
|
+
* `"always"` rejects any supplied value (left out of insert/update entirely);
|
|
17
|
+
* `"default"` is accepted but optional. Absent on a non-identity column, and
|
|
18
|
+
* on every column when the database still has the pre-identity `exposify_meta()`.
|
|
19
|
+
*/
|
|
20
|
+
identity?: "always" | "default";
|
|
21
|
+
/** A generated column (`GENERATED ALWAYS AS (...) STORED`), from `exposify_meta()`: the database computes it, so it's left out of insert/update. */
|
|
22
|
+
generated?: boolean;
|
|
11
23
|
/** From `exposify_meta()`'s `column_comment` (a `COMMENT ON COLUMN`), not OpenAPI. */
|
|
12
24
|
description?: string;
|
|
13
25
|
isPrimaryKey?: boolean;
|
|
@@ -43,6 +55,10 @@ interface MetaRow {
|
|
|
43
55
|
can_insert?: boolean;
|
|
44
56
|
can_update?: boolean;
|
|
45
57
|
can_delete?: boolean;
|
|
58
|
+
/** `pg_attribute.attidentity` mapped: `"always"` ('a'), `"default"` ('d'), or null/absent. Absent before the `_v3` migration. */
|
|
59
|
+
identity_kind?: "always" | "default" | null;
|
|
60
|
+
/** `pg_attribute.attgenerated <> ''` -- a generated (stored) column. Absent before the `_v3` migration. */
|
|
61
|
+
is_generated?: boolean;
|
|
46
62
|
}
|
|
47
63
|
/** Registered sibling edge function exposed as a tool. */
|
|
48
64
|
interface FunctionToolConfig {
|
|
@@ -189,6 +205,8 @@ interface PostgrestColumnProperty {
|
|
|
189
205
|
type?: string;
|
|
190
206
|
format?: string;
|
|
191
207
|
description?: string;
|
|
208
|
+
/** PostgREST emits the column's `DEFAULT` expression here (e.g. `"now()"`, `"auth.uid()"`) when it has one. We only care whether it's present. */
|
|
209
|
+
default?: unknown;
|
|
192
210
|
}
|
|
193
211
|
/**
|
|
194
212
|
* Extracts column schemas for one table from a PostgREST OpenAPI document.
|
package/dist/index.js
CHANGED
|
@@ -221,6 +221,10 @@ function columnsFromOpenApi(doc, table) {
|
|
|
221
221
|
type: TYPE_MAP[prop.type ?? "string"] ?? "string",
|
|
222
222
|
format: prop.format,
|
|
223
223
|
required: required.has(name),
|
|
224
|
+
// A column with a DEFAULT doesn't have to be supplied on insert even when
|
|
225
|
+
// it's NOT NULL (the database fills it in). identity/generated aren't in
|
|
226
|
+
// PostgREST's OpenAPI -- introspect.ts adds those from exposify_meta().
|
|
227
|
+
hasDefault: prop.default !== void 0,
|
|
224
228
|
isPrimaryKey: isPrimaryKey(prop),
|
|
225
229
|
// Falls back to the OpenAPI-embedded comment (PostgREST mirrors `COMMENT
|
|
226
230
|
// ON COLUMN` into `description`, then appends its own "Note:\nThis is a
|
|
@@ -265,7 +269,13 @@ function introspectTables(openapi, metaRows) {
|
|
|
265
269
|
...fromOpenApi,
|
|
266
270
|
// exposify_meta()'s comment wins over OpenAPI's mirrored copy (spec:
|
|
267
271
|
// "Column comments → param descriptions" sourced from the migration).
|
|
268
|
-
description: row.column_comment ?? fromOpenApi.description
|
|
272
|
+
description: row.column_comment ?? fromOpenApi.description,
|
|
273
|
+
// identity/generated aren't in PostgREST's OpenAPI; they come from
|
|
274
|
+
// exposify_meta() (the `_v3` migration). Absent on the older function,
|
|
275
|
+
// leaving these unset -- the insert/update shapes then fall back to
|
|
276
|
+
// defaults alone, which is the pre-fix behaviour.
|
|
277
|
+
...row.identity_kind ? { identity: row.identity_kind } : {},
|
|
278
|
+
...row.is_generated === true ? { generated: true } : {}
|
|
269
279
|
});
|
|
270
280
|
}
|
|
271
281
|
if (columns.length === 0) continue;
|
|
@@ -494,11 +504,16 @@ function registerQueryTool(server, table, client, cfg, taken) {
|
|
|
494
504
|
}
|
|
495
505
|
);
|
|
496
506
|
}
|
|
507
|
+
function isDatabaseGenerated(col) {
|
|
508
|
+
return col.identity === "always" || col.generated === true;
|
|
509
|
+
}
|
|
497
510
|
function insertBodyShape(columns) {
|
|
498
511
|
const shape = {};
|
|
499
512
|
for (const col of columns) {
|
|
513
|
+
if (isDatabaseGenerated(col)) continue;
|
|
500
514
|
let field = columnToZod(col);
|
|
501
|
-
|
|
515
|
+
const databaseFillsIn = col.hasDefault === true || col.identity === "default";
|
|
516
|
+
if (!col.required || databaseFillsIn) field = field.optional();
|
|
502
517
|
shape[col.name] = field;
|
|
503
518
|
}
|
|
504
519
|
return shape;
|
|
@@ -549,7 +564,10 @@ function keyFilterShape(table, verb) {
|
|
|
549
564
|
function registerUpdateTool(server, table, client, taken) {
|
|
550
565
|
const name = toolName("update", table.name, taken);
|
|
551
566
|
const setShape = {};
|
|
552
|
-
for (const col of table.columns)
|
|
567
|
+
for (const col of table.columns) {
|
|
568
|
+
if (isDatabaseGenerated(col)) continue;
|
|
569
|
+
setShape[col.name] = columnToZod(col).optional();
|
|
570
|
+
}
|
|
553
571
|
const keys = keyColumns(table);
|
|
554
572
|
server.registerTool(
|
|
555
573
|
name,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exposify/supabase",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.14",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"prepack": "yarn build"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@exposify/core": "0.3.
|
|
30
|
+
"@exposify/core": "0.3.18",
|
|
31
31
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
32
32
|
"zod": "^3.24.0"
|
|
33
33
|
},
|
package/sql/exposify_meta.sql
CHANGED
|
@@ -28,6 +28,12 @@
|
|
|
28
28
|
-- The grant columns were added later, as a new migration
|
|
29
29
|
-- (`<timestamp>_exposify_meta_v2.sql`): adding output columns changes the
|
|
30
30
|
-- function's return type, which `create or replace` can't do, hence the drop.
|
|
31
|
+
-- The `identity_kind`/`is_generated` columns were added later still, as a
|
|
32
|
+
-- third migration (`<timestamp>_exposify_meta_v3.sql`), for the same reason:
|
|
33
|
+
-- they tell the insert/update tools which columns the database computes
|
|
34
|
+
-- itself (an identity or generated column) so they aren't demanded or even
|
|
35
|
+
-- offered -- otherwise `insert_<table>` asks for a `generated always as
|
|
36
|
+
-- identity` primary key and PostgREST rejects any value for it.
|
|
31
37
|
drop function if exists public.exposify_meta();
|
|
32
38
|
create function public.exposify_meta()
|
|
33
39
|
returns table (
|
|
@@ -39,7 +45,9 @@ returns table (
|
|
|
39
45
|
can_select boolean,
|
|
40
46
|
can_insert boolean,
|
|
41
47
|
can_update boolean,
|
|
42
|
-
can_delete boolean
|
|
48
|
+
can_delete boolean,
|
|
49
|
+
identity_kind text,
|
|
50
|
+
is_generated boolean
|
|
43
51
|
)
|
|
44
52
|
language sql
|
|
45
53
|
security definer
|
|
@@ -55,7 +63,11 @@ as $$
|
|
|
55
63
|
has_any_column_privilege('authenticated', c.oid, 'SELECT') as can_select,
|
|
56
64
|
has_any_column_privilege('authenticated', c.oid, 'INSERT') as can_insert,
|
|
57
65
|
has_any_column_privilege('authenticated', c.oid, 'UPDATE') as can_update,
|
|
58
|
-
has_table_privilege('authenticated', c.oid, 'DELETE') as can_delete
|
|
66
|
+
has_table_privilege('authenticated', c.oid, 'DELETE') as can_delete,
|
|
67
|
+
-- 'a' = GENERATED ALWAYS AS IDENTITY, 'd' = ... BY DEFAULT, '' = not an identity column.
|
|
68
|
+
case a.attidentity when 'a' then 'always' when 'd' then 'default' else null end as identity_kind,
|
|
69
|
+
-- 's' = GENERATED ALWAYS AS (...) STORED; '' = ordinary column.
|
|
70
|
+
(a.attgenerated <> '') as is_generated
|
|
59
71
|
from pg_catalog.pg_class c
|
|
60
72
|
join pg_catalog.pg_namespace n on n.oid = c.relnamespace
|
|
61
73
|
join pg_catalog.pg_attribute a on a.attrelid = c.oid
|