@sequenceholdings/orm 2.4.0 → 2.6.0
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 +23 -0
- package/dist/authoring.cjs +53 -7
- package/dist/authoring.cjs.map +1 -1
- package/dist/authoring.d.cts +10 -6
- package/dist/authoring.d.ts +10 -6
- package/dist/authoring.js +53 -7
- package/dist/authoring.js.map +1 -1
- package/dist/canonical.cjs +48 -2
- package/dist/canonical.cjs.map +1 -1
- package/dist/canonical.d.cts +1 -1
- package/dist/canonical.d.ts +1 -1
- package/dist/canonical.js +48 -2
- package/dist/canonical.js.map +1 -1
- package/dist/cli.cjs +260 -31
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +260 -31
- package/dist/cli.js.map +1 -1
- package/dist/client.cjs +11 -6
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +38 -31
- package/dist/client.d.ts +38 -31
- package/dist/client.js +11 -7
- package/dist/client.js.map +1 -1
- package/dist/compiler.cjs +513 -23
- package/dist/compiler.cjs.map +1 -1
- package/dist/compiler.d.cts +39 -3
- package/dist/compiler.d.ts +39 -3
- package/dist/compiler.js +510 -24
- package/dist/compiler.js.map +1 -1
- package/dist/{constants-C6EG6Wq7.d.cts → constants-Dou1RkTo.d.cts} +7 -2
- package/dist/{constants-C6EG6Wq7.d.ts → constants-Dou1RkTo.d.ts} +7 -2
- package/dist/dynamic-input.d.cts +1 -1
- package/dist/dynamic-input.d.ts +1 -1
- package/dist/index.cjs +461 -140
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -4
- package/dist/index.d.ts +34 -4
- package/dist/index.js +460 -141
- package/dist/index.js.map +1 -1
- package/dist/ir.cjs +55 -4
- package/dist/ir.cjs.map +1 -1
- package/dist/ir.d.cts +1 -1
- package/dist/ir.d.ts +1 -1
- package/dist/ir.js +55 -4
- package/dist/ir.js.map +1 -1
- package/dist/predicate-v2.cjs +55 -4
- package/dist/predicate-v2.cjs.map +1 -1
- package/dist/predicate-v2.d.cts +1 -1
- package/dist/predicate-v2.d.ts +1 -1
- package/dist/predicate-v2.js +55 -4
- package/dist/predicate-v2.js.map +1 -1
- package/dist/sdl.cjs +60 -4
- package/dist/sdl.cjs.map +1 -1
- package/dist/sdl.d.cts +3 -1
- package/dist/sdl.d.ts +3 -1
- package/dist/sdl.js +60 -4
- package/dist/sdl.js.map +1 -1
- package/package.json +1 -1
package/dist/compiler.js
CHANGED
|
@@ -6,12 +6,17 @@ import { PgParser } from '@supabase/pg-parser';
|
|
|
6
6
|
// src/compiler-gql/artifact.ts
|
|
7
7
|
|
|
8
8
|
// src/constants.ts
|
|
9
|
+
var NAMESPACE_PATTERN = /^(?!(?:events|native_sources)$)[a-z][a-z0-9_]{0,40}$/;
|
|
9
10
|
var COLUMN_IDENTIFIER_PATTERN = /^_?[a-z][a-z0-9_]*$/;
|
|
10
11
|
var MAX_IDENTIFIER_LENGTH = 63;
|
|
11
12
|
var CAPABILITY_KEY_PATTERN = /^(?:org\/|app\/[a-z][a-z0-9-]{0,63}\/)?[a-z0-9-]{1,64}$/;
|
|
13
|
+
var MAX_PERSISTED_READ_ROWS = 50001;
|
|
12
14
|
var QUALIFIED_TABLE_PATTERN = /^[a-z][a-z0-9_]{0,62}\.[a-z][a-z0-9_]{0,62}$/;
|
|
13
15
|
var ORM_SCHEMA_PREFIX = "orm_";
|
|
14
|
-
var ormSchemaName = (namespace) =>
|
|
16
|
+
var ormSchemaName = (namespace) => {
|
|
17
|
+
if (!NAMESPACE_PATTERN.test(namespace)) throw new Error(`invalid or reserved ORM namespace '${namespace}'`);
|
|
18
|
+
return `${ORM_SCHEMA_PREFIX}${namespace}`;
|
|
19
|
+
};
|
|
15
20
|
var CELL_PARTS = ["at", "by", "rev", "source", "conflicts"];
|
|
16
21
|
var CELL_PART_SUFFIX = {
|
|
17
22
|
at: "__at",
|
|
@@ -387,6 +392,7 @@ function validateResolvedView({ view, tables, views = [] }) {
|
|
|
387
392
|
function validateSource({ scope, source, table }) {
|
|
388
393
|
const { fail: fail4 } = scope;
|
|
389
394
|
if (source.kind === "etl" && table.source === void 0) fail4(`etl source '${source.name}' must reference a synced table`);
|
|
395
|
+
if (source.kind === "etl" && table.primaryKey?.length === 0) fail4(`etl source '${source.name}' cannot use an unkeyed synced table for resolved row identity`);
|
|
390
396
|
if (source.kind !== "etl" && (table.internal !== true || table.cellMetadata !== true)) {
|
|
391
397
|
fail4(`${source.kind} source '${source.name}' must reference an internal table with cell metadata`);
|
|
392
398
|
}
|
|
@@ -618,6 +624,163 @@ function isSingleViewQuerySql(value) {
|
|
|
618
624
|
}
|
|
619
625
|
return state === "normal" && !/\$[a-z_0-9]*\$/i.test(query);
|
|
620
626
|
}
|
|
627
|
+
var VIEW_QUERY_FUNCTION_ALLOWLIST = [
|
|
628
|
+
"abs",
|
|
629
|
+
"age",
|
|
630
|
+
"array_agg",
|
|
631
|
+
"array_append",
|
|
632
|
+
"array_cat",
|
|
633
|
+
"array_dims",
|
|
634
|
+
"array_length",
|
|
635
|
+
"array_lower",
|
|
636
|
+
"array_ndims",
|
|
637
|
+
"array_position",
|
|
638
|
+
"array_positions",
|
|
639
|
+
"array_prepend",
|
|
640
|
+
"array_remove",
|
|
641
|
+
"array_replace",
|
|
642
|
+
"array_to_string",
|
|
643
|
+
"array_trim",
|
|
644
|
+
"array_upper",
|
|
645
|
+
"ascii",
|
|
646
|
+
"avg",
|
|
647
|
+
"bit_and",
|
|
648
|
+
"bit_or",
|
|
649
|
+
"bit_xor",
|
|
650
|
+
"bool_and",
|
|
651
|
+
"bool_or",
|
|
652
|
+
"btrim",
|
|
653
|
+
"cardinality",
|
|
654
|
+
"cbrt",
|
|
655
|
+
"ceil",
|
|
656
|
+
"ceiling",
|
|
657
|
+
"char_length",
|
|
658
|
+
"character_length",
|
|
659
|
+
"chr",
|
|
660
|
+
"concat",
|
|
661
|
+
"concat_ws",
|
|
662
|
+
"count",
|
|
663
|
+
"cume_dist",
|
|
664
|
+
"date_bin",
|
|
665
|
+
"date_part",
|
|
666
|
+
"date_trunc",
|
|
667
|
+
"decode",
|
|
668
|
+
"degrees",
|
|
669
|
+
"dense_rank",
|
|
670
|
+
"encode",
|
|
671
|
+
"enum_first",
|
|
672
|
+
"enum_last",
|
|
673
|
+
"enum_range",
|
|
674
|
+
"every",
|
|
675
|
+
"exp",
|
|
676
|
+
"first_value",
|
|
677
|
+
"floor",
|
|
678
|
+
"format",
|
|
679
|
+
"grouping",
|
|
680
|
+
"initcap",
|
|
681
|
+
"json_agg",
|
|
682
|
+
"json_array_length",
|
|
683
|
+
"json_build_array",
|
|
684
|
+
"json_build_object",
|
|
685
|
+
"json_extract_path",
|
|
686
|
+
"json_extract_path_text",
|
|
687
|
+
"json_object_agg",
|
|
688
|
+
"json_object_keys",
|
|
689
|
+
"json_strip_nulls",
|
|
690
|
+
"json_typeof",
|
|
691
|
+
"jsonb_agg",
|
|
692
|
+
"jsonb_array_length",
|
|
693
|
+
"jsonb_build_array",
|
|
694
|
+
"jsonb_build_object",
|
|
695
|
+
"jsonb_extract_path",
|
|
696
|
+
"jsonb_extract_path_text",
|
|
697
|
+
"jsonb_insert",
|
|
698
|
+
"jsonb_object_agg",
|
|
699
|
+
"jsonb_object_keys",
|
|
700
|
+
"jsonb_path_exists",
|
|
701
|
+
"jsonb_path_match",
|
|
702
|
+
"jsonb_path_query",
|
|
703
|
+
"jsonb_path_query_array",
|
|
704
|
+
"jsonb_path_query_first",
|
|
705
|
+
"jsonb_pretty",
|
|
706
|
+
"jsonb_set",
|
|
707
|
+
"jsonb_set_lax",
|
|
708
|
+
"jsonb_strip_nulls",
|
|
709
|
+
"jsonb_typeof",
|
|
710
|
+
"justify_days",
|
|
711
|
+
"justify_hours",
|
|
712
|
+
"justify_interval",
|
|
713
|
+
"lag",
|
|
714
|
+
"last_value",
|
|
715
|
+
"lead",
|
|
716
|
+
"left",
|
|
717
|
+
"length",
|
|
718
|
+
"ln",
|
|
719
|
+
"log",
|
|
720
|
+
"lower",
|
|
721
|
+
"lpad",
|
|
722
|
+
"ltrim",
|
|
723
|
+
"make_date",
|
|
724
|
+
"make_interval",
|
|
725
|
+
"make_time",
|
|
726
|
+
"make_timestamp",
|
|
727
|
+
"make_timestamptz",
|
|
728
|
+
"max",
|
|
729
|
+
"md5",
|
|
730
|
+
"min",
|
|
731
|
+
"mod",
|
|
732
|
+
"nth_value",
|
|
733
|
+
"ntile",
|
|
734
|
+
"octet_length",
|
|
735
|
+
"percent_rank",
|
|
736
|
+
"pi",
|
|
737
|
+
"power",
|
|
738
|
+
"quote_ident",
|
|
739
|
+
"quote_literal",
|
|
740
|
+
"quote_nullable",
|
|
741
|
+
"radians",
|
|
742
|
+
"rank",
|
|
743
|
+
"regexp_count",
|
|
744
|
+
"regexp_instr",
|
|
745
|
+
"regexp_like",
|
|
746
|
+
"regexp_match",
|
|
747
|
+
"regexp_matches",
|
|
748
|
+
"regexp_replace",
|
|
749
|
+
"regexp_split_to_array",
|
|
750
|
+
"regexp_split_to_table",
|
|
751
|
+
"repeat",
|
|
752
|
+
"replace",
|
|
753
|
+
"reverse",
|
|
754
|
+
"right",
|
|
755
|
+
"round",
|
|
756
|
+
"row_number",
|
|
757
|
+
"rpad",
|
|
758
|
+
"rtrim",
|
|
759
|
+
"sign",
|
|
760
|
+
"split_part",
|
|
761
|
+
"sqrt",
|
|
762
|
+
"starts_with",
|
|
763
|
+
"string_agg",
|
|
764
|
+
"string_to_array",
|
|
765
|
+
"strpos",
|
|
766
|
+
"substr",
|
|
767
|
+
"substring",
|
|
768
|
+
"sum",
|
|
769
|
+
"timezone",
|
|
770
|
+
"to_ascii",
|
|
771
|
+
"to_char",
|
|
772
|
+
"to_date",
|
|
773
|
+
"to_hex",
|
|
774
|
+
"to_json",
|
|
775
|
+
"to_jsonb",
|
|
776
|
+
"to_number",
|
|
777
|
+
"to_timestamp",
|
|
778
|
+
"translate",
|
|
779
|
+
"trunc",
|
|
780
|
+
"unnest",
|
|
781
|
+
"upper",
|
|
782
|
+
"width_bucket"
|
|
783
|
+
];
|
|
621
784
|
var canonicalActionSchema = z.object({
|
|
622
785
|
name: identifier2,
|
|
623
786
|
requires: z.string().max(1e3).optional(),
|
|
@@ -678,7 +841,7 @@ var canonicalTableSchema = z.object({
|
|
|
678
841
|
/** Synced/reflected read-only tables (`source: schema.table`) — v1 semantics. */
|
|
679
842
|
source: z.string().regex(QUALIFIED_TABLE_PATTERN).optional(),
|
|
680
843
|
columns: z.array(canonicalColumnSchema).default([]),
|
|
681
|
-
primaryKey: z.array(columnIdentifier).
|
|
844
|
+
primaryKey: z.array(columnIdentifier).max(8).optional(),
|
|
682
845
|
links: z.array(canonicalLinkSchema).default([]),
|
|
683
846
|
indexes: z.array(canonicalIndexSchema).default([]),
|
|
684
847
|
/** Omitted by old definitions, preserving their existing content hashes. */
|
|
@@ -714,6 +877,29 @@ var canonicalTableSchema = z.object({
|
|
|
714
877
|
* data plane. Omitted otherwise.
|
|
715
878
|
*/
|
|
716
879
|
materializes: identifier2.optional()
|
|
880
|
+
}).superRefine((table, ctx) => {
|
|
881
|
+
if (table.primaryKey?.length !== 0) return;
|
|
882
|
+
const fail4 = (message, path = ["primaryKey"]) => ctx.addIssue({ code: "custom", path, message: `unkeyed table '${table.table}' ${message}` });
|
|
883
|
+
if (table.source === void 0 || table.columns.length === 0) {
|
|
884
|
+
fail4("requires a typed synced source");
|
|
885
|
+
}
|
|
886
|
+
if ([table.policies.write, table.policies.insert, table.policies.update].some((policy) => policy !== void 0)) {
|
|
887
|
+
fail4("is read-only and cannot declare write policies", ["policies"]);
|
|
888
|
+
}
|
|
889
|
+
const ownedCollections = [table.indexes, table.actions, table.checks, table.foreignKeys, table.cowMutations];
|
|
890
|
+
const ownedMetadata = [table.cellMetadata, table.internal, table.materializes, table.renamedFrom];
|
|
891
|
+
if (ownedCollections.some((items) => items !== void 0 && items.length > 0) || ownedMetadata.some((value) => value !== void 0)) {
|
|
892
|
+
fail4("cannot declare owned storage, constraints or identity operations");
|
|
893
|
+
}
|
|
894
|
+
for (const [index, column] of table.columns.entries()) {
|
|
895
|
+
const ownedColumnMetadata = [column.default, column.defaultFn, column.onUpdateFn, column.renamedFrom, column.cast, column.cell];
|
|
896
|
+
if (column.unique || ownedColumnMetadata.some((value) => value !== void 0)) {
|
|
897
|
+
fail4("cannot declare column uniqueness, mutation defaults or migration metadata", ["columns", index]);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
for (const [index, link] of table.links.entries()) {
|
|
901
|
+
if (link.enforce || link.kind === "toMany") fail4("cannot provide an identity for this link", ["links", index]);
|
|
902
|
+
}
|
|
717
903
|
});
|
|
718
904
|
var MAX_VIEW_QUERY = 1e5;
|
|
719
905
|
var MAX_RESOLVED_VIEW_QUERY = 5e6;
|
|
@@ -770,6 +956,26 @@ var canonicalViewSchema = z.object({
|
|
|
770
956
|
}
|
|
771
957
|
}
|
|
772
958
|
});
|
|
959
|
+
function validateUnkeyedLinkIdentities({ definition, ctx }) {
|
|
960
|
+
const tables = new Map(definition.tables.map((table) => [table.table, table]));
|
|
961
|
+
const relations = [
|
|
962
|
+
...definition.tables.map((table, index) => ({ kind: "tables", index, links: table.links })),
|
|
963
|
+
...(definition.views ?? []).map((view, index) => ({ kind: "views", index, links: view.links }))
|
|
964
|
+
];
|
|
965
|
+
for (const relation of relations) {
|
|
966
|
+
for (const [linkIndex, link] of relation.links.entries()) {
|
|
967
|
+
if (link.kind !== "toOne" || link.namespace !== void 0 && link.namespace !== definition.namespace) continue;
|
|
968
|
+
const target = link.target === void 0 ? void 0 : tables.get(link.target);
|
|
969
|
+
if (target?.source !== void 0 && target.primaryKey?.length === 0) {
|
|
970
|
+
ctx.addIssue({
|
|
971
|
+
code: "custom",
|
|
972
|
+
path: [relation.kind, relation.index, "links", linkIndex],
|
|
973
|
+
message: `link '${link.name}' cannot use unkeyed table '${target.table}' as a to-one identity`
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
}
|
|
773
979
|
var CELL_PART_TYPES = {
|
|
774
980
|
at: "timestamptz",
|
|
775
981
|
by: "text",
|
|
@@ -833,7 +1039,7 @@ var exportGateSchema = z.union([z.literal(true), z.object({ capability: z.string
|
|
|
833
1039
|
var relationExportSchema = z.object({ read: exportGateSchema, write: exportGateSchema.optional() }).strict();
|
|
834
1040
|
var localDefinitionSchema = z.object({
|
|
835
1041
|
definition_format: z.literal(CANONICAL_FORMAT),
|
|
836
|
-
namespace: identifier2.max(41),
|
|
1042
|
+
namespace: identifier2.max(41).regex(NAMESPACE_PATTERN, "invalid or reserved ORM namespace"),
|
|
837
1043
|
description: z.string().max(2e3).optional(),
|
|
838
1044
|
enums: z.array(canonicalEnumSchema).default([]),
|
|
839
1045
|
tables: z.array(canonicalTableSchema).min(0).max(500),
|
|
@@ -867,6 +1073,7 @@ var localDefinitionSchema = z.object({
|
|
|
867
1073
|
}
|
|
868
1074
|
}
|
|
869
1075
|
const fkTables = new Map(definition.tables.map((table) => [table.table, table]));
|
|
1076
|
+
validateUnkeyedLinkIdentities({ definition, ctx });
|
|
870
1077
|
const relations = /* @__PURE__ */ new Set([...fkTables.keys(), ...(definition.views ?? []).map((view) => view.view)]);
|
|
871
1078
|
for (const name2 of Object.keys(definition.exports ?? {})) {
|
|
872
1079
|
if (!relations.has(name2)) ctx.addIssue({ code: "custom", path: ["exports", name2], message: `export '${name2}' is not a local object` });
|
|
@@ -1101,6 +1308,9 @@ function normalizeCanonical(definition) {
|
|
|
1101
1308
|
return normalized;
|
|
1102
1309
|
}
|
|
1103
1310
|
|
|
1311
|
+
// src/client/sql.ts
|
|
1312
|
+
var BUILTIN_SQL_READ_HASH = "sha256:a50c477c952a5e0fe68ed158a4530e4aab573c09bcbb1a8ac454dc88a39a6258";
|
|
1313
|
+
|
|
1104
1314
|
// src/ir/index.ts
|
|
1105
1315
|
var IR_VERSION = 5;
|
|
1106
1316
|
var LEGACY_IR_VERSION = 1;
|
|
@@ -1189,7 +1399,7 @@ var limitIrSchema = z.object({
|
|
|
1189
1399
|
variable: z.string().optional(),
|
|
1190
1400
|
value: z.number().int().positive().optional(),
|
|
1191
1401
|
default: z.number().int().positive(),
|
|
1192
|
-
max: z.number().int().positive()
|
|
1402
|
+
max: z.number().int().positive().max(MAX_PERSISTED_READ_ROWS)
|
|
1193
1403
|
});
|
|
1194
1404
|
var selectionIrSchema = z.lazy(
|
|
1195
1405
|
() => z.object({
|
|
@@ -1434,7 +1644,8 @@ var compiledOperationSchema = z.object({
|
|
|
1434
1644
|
}).superRefine((operation, ctx) => {
|
|
1435
1645
|
if (operation.language === "sql") {
|
|
1436
1646
|
const validPlan = operation.sqlQuery ? !operation.sql && operation.irVersion === RUNTIME_SQL_IR_VERSION : !!operation.sql && operation.irVersion === SQL_IR_VERSION;
|
|
1437
|
-
|
|
1647
|
+
const builtin = operation.name === "BuiltinSqlRead" && operation.opHash === BUILTIN_SQL_READ_HASH && operation.requires === null;
|
|
1648
|
+
if (!validPlan || operation.kind !== "query" || operation.roots.length !== 0 || !builtin && !operation.requires?.capability) {
|
|
1438
1649
|
ctx.addIssue({ code: "custom", message: "SQL operations require a query-only v4 plan or v5 runtime contract, capability, and no GraphQL roots" });
|
|
1439
1650
|
}
|
|
1440
1651
|
} else if (operation.sql || operation.sqlQuery || operation.roots.length === 0) {
|
|
@@ -2826,6 +3037,7 @@ function buildCompileContext(rawCanonical) {
|
|
|
2826
3037
|
const platformMeta = usesPlatformUsers ? platformUserMeta() : void 0;
|
|
2827
3038
|
if (platformMeta) tables[PLATFORM_USER_TABLE] = platformMeta;
|
|
2828
3039
|
const syncedTableNames = new Set(canonical.tables.filter((t) => t.source !== void 0).map((t) => t.table));
|
|
3040
|
+
const unkeyedSyncedTableNames = new Set(relations.filter((table) => table.source !== void 0 && table.primaryKey?.length === 0).map((table) => table.table));
|
|
2829
3041
|
const viewDependencies = new Map((canonical.views ?? []).map((view) => [view.view, view.dependencies]));
|
|
2830
3042
|
const claimsGatedMemo = /* @__PURE__ */ new Map();
|
|
2831
3043
|
const relationIsClaimsGated = (name2) => {
|
|
@@ -2839,6 +3051,7 @@ function buildCompileContext(rawCanonical) {
|
|
|
2839
3051
|
};
|
|
2840
3052
|
for (const table of relations) {
|
|
2841
3053
|
const isView = viewNames.has(table.table);
|
|
3054
|
+
const readOnly = isView || unkeyedSyncedTableNames.has(table.table);
|
|
2842
3055
|
const owner = owners.get(table.table);
|
|
2843
3056
|
const entries = resolveColumnEntries(table, enums, diagnostics);
|
|
2844
3057
|
columnEntries.set(table.table, entries);
|
|
@@ -2886,13 +3099,13 @@ function buildCompileContext(rawCanonical) {
|
|
|
2886
3099
|
// are claims-gated — see tableMetaIrSchema.claimsGated.
|
|
2887
3100
|
claimsGated: relationIsClaimsGated(table.table),
|
|
2888
3101
|
columns: entries.map(({ deprecated: _deprecated, onUpdateFn: _onUpdateFn, ...column }) => column),
|
|
2889
|
-
primaryKey: isView ? primaryKey : primaryKey.length > 0 ? primaryKey : ["id"],
|
|
3102
|
+
primaryKey: isView || unkeyedSyncedTableNames.has(table.table) ? primaryKey : primaryKey.length > 0 ? primaryKey : ["id"],
|
|
2890
3103
|
links: [],
|
|
2891
3104
|
filterable: data.map((c) => c.name),
|
|
2892
3105
|
orderable: data.filter((c) => c.type !== "jsonb").map((c) => c.name),
|
|
2893
|
-
insertable: resolved ? resolvedWritable(data) :
|
|
2894
|
-
settable: resolved ? resolvedWritable(data).filter((name2) => !primaryKey.includes(name2)) :
|
|
2895
|
-
appendable:
|
|
3106
|
+
insertable: resolved ? resolvedWritable(data) : readOnly ? [] : active.map((c) => c.name),
|
|
3107
|
+
settable: resolved ? resolvedWritable(data).filter((name2) => !primaryKey.includes(name2)) : readOnly ? [] : active.filter((c) => !c.onUpdateFn).map((c) => c.name),
|
|
3108
|
+
appendable: readOnly ? [] : active.filter((c) => c.type === "jsonb").map((c) => c.name),
|
|
2896
3109
|
...touchOnUpdate.length > 0 ? { touchOnUpdate } : {},
|
|
2897
3110
|
...cells ? { cells } : {},
|
|
2898
3111
|
...resolved ? { resolved: resolvedMeta({ resolved, pk: primaryKey[0] }) } : {},
|
|
@@ -2916,6 +3129,16 @@ function buildCompileContext(rawCanonical) {
|
|
|
2916
3129
|
});
|
|
2917
3130
|
continue;
|
|
2918
3131
|
}
|
|
3132
|
+
const identityTable = link.kind === "toOne" ? targetName : link.kind === "toMany" ? table.table : void 0;
|
|
3133
|
+
if (identityTable !== void 0 && unkeyedSyncedTableNames.has(identityTable)) {
|
|
3134
|
+
diagnostics.push({
|
|
3135
|
+
severity: "error",
|
|
3136
|
+
code: "unkeyed-link-identity",
|
|
3137
|
+
table: table.table,
|
|
3138
|
+
message: `link '${link.name}' cannot use unkeyed table '${identityTable}' as a row identity`
|
|
3139
|
+
});
|
|
3140
|
+
continue;
|
|
3141
|
+
}
|
|
2919
3142
|
const targetEntries = targetName === PLATFORM_USER_TABLE ? target.columns.map((c) => ({ ...c, deprecated: false, onUpdateFn: false })) : columnEntries.get(targetName);
|
|
2920
3143
|
const onEntries = link.kind === "toMany" ? targetEntries : entries;
|
|
2921
3144
|
const referencesEntries = link.kind === "toMany" ? entries : targetEntries;
|
|
@@ -2981,7 +3204,7 @@ function buildCompileContext(rawCanonical) {
|
|
|
2981
3204
|
compilePolicyFor("write", writePolicies);
|
|
2982
3205
|
compilePolicyFor("insert", insertPolicies);
|
|
2983
3206
|
compilePolicyFor("update", updatePolicies);
|
|
2984
|
-
if (isView && table.resolved === void 0) {
|
|
3207
|
+
if (isView && table.resolved === void 0 || unkeyedSyncedTableNames.has(table.table)) {
|
|
2985
3208
|
writePolicies.set(table.table, CONST_FALSE);
|
|
2986
3209
|
insertPolicies.set(table.table, CONST_FALSE);
|
|
2987
3210
|
updatePolicies.set(table.table, CONST_FALSE);
|
|
@@ -3056,7 +3279,7 @@ function buildCompileContext(rawCanonical) {
|
|
|
3056
3279
|
const queryRoots = /* @__PURE__ */ new Map();
|
|
3057
3280
|
const mutationRoots = /* @__PURE__ */ new Map();
|
|
3058
3281
|
for (const table of relations) {
|
|
3059
|
-
if (table.internal === true) continue;
|
|
3282
|
+
if (table.internal === true || unkeyedSyncedTableNames.has(table.table)) continue;
|
|
3060
3283
|
const roots = queryRootNames(table.table);
|
|
3061
3284
|
queryRoots.set(roots.select, { kind: "select", table: table.table });
|
|
3062
3285
|
if ((tables[table.table]?.primaryKey.length ?? 0) > 0) {
|
|
@@ -3436,8 +3659,10 @@ var OperationLowerer = class {
|
|
|
3436
3659
|
constructor(ctx, restricted) {
|
|
3437
3660
|
this.ctx = ctx;
|
|
3438
3661
|
this.variables = new Map(restricted.variables.map((v) => [v.name, v]));
|
|
3662
|
+
this.maxRows = restricted.readBudget ?? MAX_LIMIT;
|
|
3439
3663
|
}
|
|
3440
3664
|
variables;
|
|
3665
|
+
maxRows;
|
|
3441
3666
|
meta(table) {
|
|
3442
3667
|
return this.ctx.tables[table];
|
|
3443
3668
|
}
|
|
@@ -3641,13 +3866,13 @@ var OperationLowerer = class {
|
|
|
3641
3866
|
});
|
|
3642
3867
|
}
|
|
3643
3868
|
lowerLimit(node) {
|
|
3644
|
-
const base2 = { default: DEFAULT_LIMIT, max:
|
|
3869
|
+
const base2 = { default: DEFAULT_LIMIT, max: this.maxRows };
|
|
3645
3870
|
if (!node) return base2;
|
|
3646
3871
|
if (node.kind === Kind.VARIABLE) return { ...base2, variable: node.name.value };
|
|
3647
3872
|
if (node.kind === Kind.INT) {
|
|
3648
3873
|
const value = Number(node.value);
|
|
3649
3874
|
if (value < 1) fail2("limit must be a positive integer", "invalid-limit");
|
|
3650
|
-
return { ...base2, value: Math.min(value,
|
|
3875
|
+
return { ...base2, value: Math.min(value, this.maxRows) };
|
|
3651
3876
|
}
|
|
3652
3877
|
fail2("limit must be an integer literal or a variable", "invalid-limit");
|
|
3653
3878
|
}
|
|
@@ -4446,7 +4671,13 @@ var OperationLowerer = class {
|
|
|
4446
4671
|
};
|
|
4447
4672
|
function lowerOperation({ restricted, ctx }) {
|
|
4448
4673
|
const lowerer = new OperationLowerer(ctx, restricted);
|
|
4449
|
-
|
|
4674
|
+
const roots = lowerer.lowerRoots(restricted.definition, restricted.kind);
|
|
4675
|
+
if (restricted.readBudget === void 0) return roots;
|
|
4676
|
+
const root = roots[0];
|
|
4677
|
+
if (restricted.kind !== "query" || restricted.definition.selectionSet.selections.length !== 1 || roots.length !== 1 || root?.kind !== "select" || root.selection.relations.length > 0 || root.selection.aggregates.length > 0 || root.selection.cells !== void 0 || root.prefetch?.length) {
|
|
4678
|
+
fail2("@readBudget requires one flat select root without related selections, aggregates, cells, or prefetch", "invalid-read-budget");
|
|
4679
|
+
}
|
|
4680
|
+
return roots;
|
|
4450
4681
|
}
|
|
4451
4682
|
function isCellColumn(meta, name2) {
|
|
4452
4683
|
if (!meta.cells) return false;
|
|
@@ -4505,6 +4736,19 @@ function parseDocuments({
|
|
|
4505
4736
|
}
|
|
4506
4737
|
return { documents: parsed, diagnostics };
|
|
4507
4738
|
}
|
|
4739
|
+
function parseReadBudget(operation) {
|
|
4740
|
+
const directives = operation.directives?.filter((directive) => directive.name.value === "readBudget") ?? [];
|
|
4741
|
+
if (directives.length === 0) return void 0;
|
|
4742
|
+
const value = directives[0]?.arguments?.find((argument) => argument.name.value === "maxRows")?.value;
|
|
4743
|
+
if (operation.operation !== "query" || directives.length !== 1 || value?.kind !== Kind.INT) {
|
|
4744
|
+
throw new DocumentCompileError("@readBudget requires a literal maxRows on a query", "invalid-read-budget");
|
|
4745
|
+
}
|
|
4746
|
+
const maxRows = Number(value.value);
|
|
4747
|
+
if (!Number.isSafeInteger(maxRows) || maxRows < 1 || maxRows > MAX_PERSISTED_READ_ROWS) {
|
|
4748
|
+
throw new DocumentCompileError(`@readBudget(maxRows:) must be between 1 and ${MAX_PERSISTED_READ_ROWS}`, "invalid-read-budget");
|
|
4749
|
+
}
|
|
4750
|
+
return maxRows;
|
|
4751
|
+
}
|
|
4508
4752
|
function unwrapType(node, variable) {
|
|
4509
4753
|
let nullable = true;
|
|
4510
4754
|
let list2 = false;
|
|
@@ -4665,6 +4909,7 @@ function restrictOperation({
|
|
|
4665
4909
|
name: name2,
|
|
4666
4910
|
kind: operation.operation === "mutation" ? "mutation" : "query",
|
|
4667
4911
|
requires: parseRequires(operation),
|
|
4912
|
+
readBudget: parseReadBudget(operation),
|
|
4668
4913
|
variables,
|
|
4669
4914
|
definition: operation
|
|
4670
4915
|
};
|
|
@@ -5178,9 +5423,9 @@ function compilePredIrToSql(pred, options = {}) {
|
|
|
5178
5423
|
case "cmp":
|
|
5179
5424
|
return compileCmp(pred.op, pred.column, pred.value, options);
|
|
5180
5425
|
case "capability":
|
|
5181
|
-
return `(${capabilityExpression(pred.key, pred.namespace ?? options.namespace)})`;
|
|
5426
|
+
return `(${options.statementClaimChecks ? "SELECT " : ""}${capabilityExpression(pred.key, pred.namespace ?? options.namespace)})`;
|
|
5182
5427
|
case "role":
|
|
5183
|
-
return `(${roleExpression(pred.key, pred.namespace ?? options.namespace)})`;
|
|
5428
|
+
return `(${options.statementClaimChecks ? "SELECT " : ""}${roleExpression(pred.key, pred.namespace ?? options.namespace)})`;
|
|
5184
5429
|
case "exists":
|
|
5185
5430
|
if (options.exists) return options.exists(pred);
|
|
5186
5431
|
throw new OrmDefinitionError("synced view read policy cannot use 'exists' without a relationship resolver");
|
|
@@ -5288,7 +5533,8 @@ function createSqlCatalog({ canonical, publishedObjects, executionTarget }) {
|
|
|
5288
5533
|
const { ctx, diagnostics } = buildCompileContext(canonical);
|
|
5289
5534
|
if (diagnostics.some((d) => d.severity === "error")) throw new Error(diagnostics.map((d) => d.message).join("; "));
|
|
5290
5535
|
const identity = sqlHash(`ontology-sql-v1\0${computeSchemaHash(canonical)}\0${executionTarget}`);
|
|
5291
|
-
const
|
|
5536
|
+
const logicalNames = new Map(Object.entries(ctx.tables).map(([key, meta]) => [meta, key]));
|
|
5537
|
+
const refOf = (meta) => `${meta.namespace ?? canonical.namespace}/${logicalNames.get(meta)}`;
|
|
5292
5538
|
const definitions = [canonical, ...canonical.imports ?? []];
|
|
5293
5539
|
const internal = new Set(definitions.flatMap((owner) => [
|
|
5294
5540
|
...owner.tables.filter((t) => t.internal).map((t) => `${owner.namespace}/${t.table}`),
|
|
@@ -5364,15 +5610,48 @@ function only(node, keys) {
|
|
|
5364
5610
|
var parser;
|
|
5365
5611
|
var SQL_TYPES = ["text", "boolean", "integer", "bigint", "numeric", "double", "uuid", "date", "timestamptz"];
|
|
5366
5612
|
var pgType = (type) => type === "double" ? "double precision" : SQL_TYPES.some((t) => t === type) ? type : fail3("unsupported SQL type");
|
|
5367
|
-
async function
|
|
5368
|
-
if (
|
|
5613
|
+
async function parseSelect(sql) {
|
|
5614
|
+
if (Buffer.byteLength(sql) > 65536) fail3("SQL limit exceeded");
|
|
5369
5615
|
parser ??= new PgParser({ version: 17 });
|
|
5370
5616
|
const parsed = await parser.parse(sql);
|
|
5371
5617
|
if (parsed.error || !parsed.tree) fail3("invalid SQL");
|
|
5372
5618
|
const statements = list(obj(parsed.tree).stmts);
|
|
5373
5619
|
if (statements.length !== 1) fail3("one SQL SELECT is required");
|
|
5620
|
+
const statement = obj(obj(statements[0]).stmt);
|
|
5621
|
+
if (!statement.SelectStmt) fail3("one SQL SELECT is required");
|
|
5622
|
+
return statement;
|
|
5623
|
+
}
|
|
5624
|
+
async function referencedSqlObjects({ sql }) {
|
|
5625
|
+
const objects = /* @__PURE__ */ new Set();
|
|
5626
|
+
let nodes = 0;
|
|
5627
|
+
function visit2(value, depth) {
|
|
5628
|
+
if (++nodes > 1e4 || depth > 96) fail3("SQL complexity limit exceeded");
|
|
5629
|
+
if (Array.isArray(value)) {
|
|
5630
|
+
for (const child of value) visit2(child, depth + 1);
|
|
5631
|
+
return;
|
|
5632
|
+
}
|
|
5633
|
+
if (!value || typeof value !== "object") return;
|
|
5634
|
+
const node = obj(value);
|
|
5635
|
+
if (node.RangeVar) {
|
|
5636
|
+
const range = obj(node.RangeVar);
|
|
5637
|
+
if (range.schemaname) {
|
|
5638
|
+
const namespace = str(range.schemaname), name2 = str(range.relname);
|
|
5639
|
+
if (!/^[a-z][a-z0-9_]{0,40}$/.test(namespace) || namespace === "pg_catalog" || namespace === "information_schema") fail3("invalid ontology namespace");
|
|
5640
|
+
objects.add(`${namespace}/${name2}`);
|
|
5641
|
+
if (objects.size > 256) fail3("too many SQL objects");
|
|
5642
|
+
}
|
|
5643
|
+
}
|
|
5644
|
+
for (const child of Object.values(node)) visit2(child, depth + 1);
|
|
5645
|
+
}
|
|
5646
|
+
visit2(await parseSelect(sql), 0);
|
|
5647
|
+
if (!objects.size) fail3("SQL operation must read an ontology object");
|
|
5648
|
+
return [...objects].sort();
|
|
5649
|
+
}
|
|
5650
|
+
async function compileOntologySql({ catalog, sql, parameterTypes = [], maxRows = 1e3 }) {
|
|
5651
|
+
if (!Number.isInteger(maxRows) || maxRows < 1 || maxRows > 1e4 || Buffer.byteLength(sql) > 65536) fail3("SQL limit exceeded");
|
|
5652
|
+
const statement = await parseSelect(sql);
|
|
5374
5653
|
const emitter = new SqlEmitter({ catalog, parameterTypes });
|
|
5375
|
-
const result = emitter.select(
|
|
5654
|
+
const result = emitter.select(statement, { relations: [], ctes: /* @__PURE__ */ new Map() });
|
|
5376
5655
|
if (emitter.used.size !== parameterTypes.length) fail3("SQL parameters must be used exactly as declared");
|
|
5377
5656
|
if (!emitter.read.size) fail3("SQL operation must read an ontology object");
|
|
5378
5657
|
return {
|
|
@@ -5705,6 +5984,118 @@ function bindNamedParameters({ sql, names }) {
|
|
|
5705
5984
|
}
|
|
5706
5985
|
return result;
|
|
5707
5986
|
}
|
|
5987
|
+
var parser2;
|
|
5988
|
+
var functions = new Set(VIEW_QUERY_FUNCTION_ALLOWLIST);
|
|
5989
|
+
var scalarTypes = /* @__PURE__ */ new Set([
|
|
5990
|
+
"bool",
|
|
5991
|
+
"boolean",
|
|
5992
|
+
"int2",
|
|
5993
|
+
"int4",
|
|
5994
|
+
"int8",
|
|
5995
|
+
"integer",
|
|
5996
|
+
"bigint",
|
|
5997
|
+
"smallint",
|
|
5998
|
+
"numeric",
|
|
5999
|
+
"decimal",
|
|
6000
|
+
"float4",
|
|
6001
|
+
"float8",
|
|
6002
|
+
"real",
|
|
6003
|
+
"text",
|
|
6004
|
+
"varchar",
|
|
6005
|
+
"bpchar",
|
|
6006
|
+
"uuid",
|
|
6007
|
+
"date",
|
|
6008
|
+
"timestamp",
|
|
6009
|
+
"timestamptz",
|
|
6010
|
+
"time",
|
|
6011
|
+
"timetz",
|
|
6012
|
+
"interval",
|
|
6013
|
+
"json",
|
|
6014
|
+
"jsonb",
|
|
6015
|
+
"bytea"
|
|
6016
|
+
]);
|
|
6017
|
+
var sqlValues = /* @__PURE__ */ new Set([
|
|
6018
|
+
"SVFOP_CURRENT_DATE",
|
|
6019
|
+
"SVFOP_CURRENT_TIME",
|
|
6020
|
+
"SVFOP_CURRENT_TIME_N",
|
|
6021
|
+
"SVFOP_CURRENT_TIMESTAMP",
|
|
6022
|
+
"SVFOP_CURRENT_TIMESTAMP_N",
|
|
6023
|
+
"SVFOP_LOCALTIME",
|
|
6024
|
+
"SVFOP_LOCALTIME_N",
|
|
6025
|
+
"SVFOP_LOCALTIMESTAMP",
|
|
6026
|
+
"SVFOP_LOCALTIMESTAMP_N",
|
|
6027
|
+
"SVFOP_CURRENT_ROLE",
|
|
6028
|
+
"SVFOP_CURRENT_USER",
|
|
6029
|
+
"SVFOP_USER",
|
|
6030
|
+
"SVFOP_SESSION_USER",
|
|
6031
|
+
"SVFOP_CURRENT_CATALOG",
|
|
6032
|
+
"SVFOP_CURRENT_SCHEMA"
|
|
6033
|
+
]);
|
|
6034
|
+
async function validateManagedViewQueries({ canonical }) {
|
|
6035
|
+
for (const definition of [canonical, ...canonical.imports ?? []]) {
|
|
6036
|
+
for (const view of definition.views ?? []) {
|
|
6037
|
+
const fail4 = (message) => {
|
|
6038
|
+
throw new OrmDefinitionError(`managed view '${definition.namespace}/${view.view}': ${message}`);
|
|
6039
|
+
};
|
|
6040
|
+
parser2 ??= new PgParser({ version: 17 });
|
|
6041
|
+
const parsed = await parser2.parse(view.query);
|
|
6042
|
+
if (parsed.error || !parsed.tree) fail4("invalid SELECT query");
|
|
6043
|
+
const statements = list(obj(parsed.tree).stmts);
|
|
6044
|
+
if (statements.length !== 1 || !obj(obj(statements[0]).stmt).SelectStmt) fail4("one SELECT query is required");
|
|
6045
|
+
validateTree({ tree: parsed.tree, schema: ormSchemaName(definition.namespace), enums: definition.enums.map((entry) => entry.name), fail: fail4 });
|
|
6046
|
+
}
|
|
6047
|
+
}
|
|
6048
|
+
}
|
|
6049
|
+
function pgCatalogName(parts) {
|
|
6050
|
+
if (parts.length === 1) return parts[0];
|
|
6051
|
+
if (parts.length === 2 && parts[0] === "pg_catalog") return parts[1];
|
|
6052
|
+
return void 0;
|
|
6053
|
+
}
|
|
6054
|
+
function reviewedFunctionName({ parts, format }) {
|
|
6055
|
+
return format === "COERCE_SQL_SYNTAX" && parts.length === 2 && parts[0] === "pg_catalog" && parts[1] === "position" ? "strpos" : pgCatalogName(parts);
|
|
6056
|
+
}
|
|
6057
|
+
function validateTree({ tree, ...scope }) {
|
|
6058
|
+
const pending = [tree];
|
|
6059
|
+
while (pending.length) {
|
|
6060
|
+
const value = pending.pop();
|
|
6061
|
+
if (Array.isArray(value)) {
|
|
6062
|
+
pending.push(...value);
|
|
6063
|
+
continue;
|
|
6064
|
+
}
|
|
6065
|
+
if (!value || typeof value !== "object") continue;
|
|
6066
|
+
const node = obj(value);
|
|
6067
|
+
for (const [kind, child] of Object.entries(node)) {
|
|
6068
|
+
validateNode({ kind, child, scope });
|
|
6069
|
+
pending.push(child);
|
|
6070
|
+
}
|
|
6071
|
+
}
|
|
6072
|
+
}
|
|
6073
|
+
function validateNode({ kind, child, scope }) {
|
|
6074
|
+
const { fail: fail4 } = scope;
|
|
6075
|
+
if (kind.endsWith("Stmt") && kind !== "SelectStmt") fail4("only SELECT statements are permitted");
|
|
6076
|
+
if (kind === "FuncCall") {
|
|
6077
|
+
const call = obj(child);
|
|
6078
|
+
const parts = strings(call.funcname);
|
|
6079
|
+
const name2 = reviewedFunctionName({ parts, format: call.funcformat });
|
|
6080
|
+
if (!name2 || !functions.has(name2)) fail4(`only explicitly allowlisted pg_catalog functions are permitted (found '${parts.join(".")}')`);
|
|
6081
|
+
}
|
|
6082
|
+
if (kind === "typeName" || kind === "TypeName") validateType({ node: obj(child), ...scope });
|
|
6083
|
+
if (kind === "A_Expr" && pgCatalogName(strings(obj(child).name)) === void 0) fail4("only pg_catalog operators are permitted");
|
|
6084
|
+
if (kind === "SortBy" && list(obj(child).useOp).length && pgCatalogName(strings(obj(child).useOp)) === void 0) fail4("only pg_catalog sort operators are permitted");
|
|
6085
|
+
if (kind === "SQLValueFunction" && !sqlValues.has(String(obj(child).op))) fail4("unsupported SQL value expression");
|
|
6086
|
+
if (kind === "ParamRef") fail4("view queries cannot contain parameters");
|
|
6087
|
+
if (kind === "A_Indirection" && list(obj(child).indirection).some((entry) => !obj(entry).A_Indices)) {
|
|
6088
|
+
fail4("composite field notation is not permitted in managed views");
|
|
6089
|
+
}
|
|
6090
|
+
}
|
|
6091
|
+
function validateType({ node, schema, enums, fail: fail4 }) {
|
|
6092
|
+
const parts = strings(node.names);
|
|
6093
|
+
const builtin = pgCatalogName(parts);
|
|
6094
|
+
const managedEnum = parts.length === 1 && enums.includes(parts[0]) || parts.length === 2 && parts[0] === schema && enums.includes(parts[1]);
|
|
6095
|
+
if (node.setof || node.pct_type || !managedEnum && (!builtin || !scalarTypes.has(builtin))) {
|
|
6096
|
+
fail4(`casts may only use supported pg_catalog scalar types or declared enums (found '${parts.join(".")}')`);
|
|
6097
|
+
}
|
|
6098
|
+
}
|
|
5708
6099
|
|
|
5709
6100
|
// src/sql/operations.ts
|
|
5710
6101
|
function isSqlOperationDocument(path, contents) {
|
|
@@ -5720,7 +6111,13 @@ function isSqlOperationDocument(path, contents) {
|
|
|
5720
6111
|
async function compileOperationDocuments(input) {
|
|
5721
6112
|
const sqlDocuments = input.documents.filter((d) => isSqlOperationDocument(d.path, d.contents));
|
|
5722
6113
|
const result = compileNamespace({ ...input, documents: input.documents.filter((d) => !isSqlOperationDocument(d.path, d.contents)) });
|
|
5723
|
-
if (!result.ok
|
|
6114
|
+
if (!result.ok) return result;
|
|
6115
|
+
const viewDiagnostics = await managedViewDiagnostics(input.canonical);
|
|
6116
|
+
if (viewDiagnostics.length) return { ok: false, diagnostics: [...result.diagnostics, ...viewDiagnostics] };
|
|
6117
|
+
if (!sqlDocuments.length) return result;
|
|
6118
|
+
return compileSqlDocuments({ input, result, sqlDocuments });
|
|
6119
|
+
}
|
|
6120
|
+
async function compileSqlDocuments({ input, result, sqlDocuments }) {
|
|
5724
6121
|
for (const document of sqlDocuments) {
|
|
5725
6122
|
try {
|
|
5726
6123
|
const definition = defineOperation(JSON.parse(document.contents));
|
|
@@ -5814,6 +6211,18 @@ async function compileOperationDocuments(input) {
|
|
|
5814
6211
|
result.manifest.opsHash = result.artifact.opsHash;
|
|
5815
6212
|
return { ...result, artifact: normalizeIr(result.artifact) };
|
|
5816
6213
|
}
|
|
6214
|
+
async function managedViewDiagnostics(canonical) {
|
|
6215
|
+
try {
|
|
6216
|
+
await validateManagedViewQueries({ canonical });
|
|
6217
|
+
return [];
|
|
6218
|
+
} catch (error) {
|
|
6219
|
+
return [{
|
|
6220
|
+
severity: "error",
|
|
6221
|
+
code: "invalid-view-query",
|
|
6222
|
+
message: error instanceof Error ? error.message : "invalid managed view query"
|
|
6223
|
+
}];
|
|
6224
|
+
}
|
|
6225
|
+
}
|
|
5817
6226
|
var parameterName = z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/).max(63).refine((value) => !["__proto__", "prototype", "constructor"].includes(value), "reserved parameter name");
|
|
5818
6227
|
var parameter = z.discriminatedUnion("type", [
|
|
5819
6228
|
z.object({ type: z.literal("text"), value: z.string().nullable() }).strict(),
|
|
@@ -5830,7 +6239,7 @@ var sqlQueryInputSchema = z.object({
|
|
|
5830
6239
|
sql: z.string().min(1).max(65536).refine((value) => Buffer.byteLength(value) <= 65536, "SQL exceeds 64 KiB"),
|
|
5831
6240
|
parameters: z.record(parameterName, parameter).refine((value) => Object.keys(value).length <= 128, "too many SQL parameters").default({})
|
|
5832
6241
|
}).strict();
|
|
5833
|
-
async function compileRuntimeSql({ operation, input }) {
|
|
6242
|
+
async function compileRuntimeSql({ operation, input, qualified = false }) {
|
|
5834
6243
|
const contract = operation.sqlQuery;
|
|
5835
6244
|
if (!contract) throw new Error("missing runtime SQL contract");
|
|
5836
6245
|
const names = Object.keys(input.parameters).sort();
|
|
@@ -5846,10 +6255,87 @@ async function compileRuntimeSql({ operation, input }) {
|
|
|
5846
6255
|
sqlQuery: void 0,
|
|
5847
6256
|
irVersion: SQL_IR_VERSION,
|
|
5848
6257
|
variables: names.map((name2) => ({ name: name2, type: { kind: "scalar", scalar: input.parameters[name2].type }, nullable: true, list: false })),
|
|
5849
|
-
tableSet: plan.read.map((ref) => ref.split("/")[1]).sort(),
|
|
6258
|
+
tableSet: qualified ? plan.read : plan.read.map((ref) => ref.split("/")[1]).sort(),
|
|
5850
6259
|
sql: { text: plan.text, columns: plan.columns, parameters: names, read: plan.read, maxRows: plan.maxRows }
|
|
5851
6260
|
});
|
|
5852
6261
|
}
|
|
6262
|
+
|
|
6263
|
+
// src/sql/builtin.ts
|
|
6264
|
+
function localHash(definition) {
|
|
6265
|
+
const { imports: _imports, ...local } = definition;
|
|
6266
|
+
return computeSchemaHash(normalizeCanonical(local));
|
|
6267
|
+
}
|
|
6268
|
+
function compileBuiltinSqlArtifact({ namespace, definitions, read, unbackedObjects = [], executionTarget = "default" }) {
|
|
6269
|
+
const anchor = definitions.find((definition) => definition.namespace === namespace);
|
|
6270
|
+
if (!anchor) throw new Error("missing SQL namespace definition");
|
|
6271
|
+
const tables = {};
|
|
6272
|
+
const policies = {};
|
|
6273
|
+
const objects = [];
|
|
6274
|
+
for (const definition of definitions) {
|
|
6275
|
+
const owner = definition.namespace;
|
|
6276
|
+
const { ctx, diagnostics } = buildCompileContext(definition);
|
|
6277
|
+
if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) throw new Error("SQL namespace definition is invalid");
|
|
6278
|
+
const ref = (key) => {
|
|
6279
|
+
const meta = ctx.tables[key];
|
|
6280
|
+
if (!meta) throw new Error("SQL dependency is unavailable");
|
|
6281
|
+
return `${meta.namespace ?? owner}/${key}`;
|
|
6282
|
+
};
|
|
6283
|
+
for (const [key, meta] of Object.entries(ctx.tables)) {
|
|
6284
|
+
if ((meta.namespace ?? owner) !== owner) continue;
|
|
6285
|
+
const identity = ref(key);
|
|
6286
|
+
tables[identity] = {
|
|
6287
|
+
...meta,
|
|
6288
|
+
namespace: owner,
|
|
6289
|
+
links: meta.links.map((link) => ({ ...link, ...link.target ? { target: ref(link.target) } : {} })),
|
|
6290
|
+
...meta.dependencies ? { dependencies: meta.dependencies.map(ref) } : {},
|
|
6291
|
+
...meta.foreignReferences ? { foreignReferences: meta.foreignReferences.map((link) => ({ ...link, target: ref(link.target) })) } : {}
|
|
6292
|
+
};
|
|
6293
|
+
const policy = ctx.readPolicies.get(key);
|
|
6294
|
+
if (policy) policies[identity] = policy;
|
|
6295
|
+
}
|
|
6296
|
+
const selected = read.filter((identity) => identity.startsWith(`${owner}/`));
|
|
6297
|
+
if (!selected.length) continue;
|
|
6298
|
+
if (owner !== namespace && selected.some((identity) => !definition.exports?.[identity.slice(owner.length + 1)]?.read)) {
|
|
6299
|
+
throw new Error("SQL object is not exported");
|
|
6300
|
+
}
|
|
6301
|
+
objects.push(...createSqlCatalog({ canonical: definition, publishedObjects: selected, executionTarget }).objects);
|
|
6302
|
+
}
|
|
6303
|
+
if (objects.length !== read.length) throw new Error("SQL object is unavailable");
|
|
6304
|
+
const actualRead = [...new Set(objects.flatMap((object) => object.read))].sort();
|
|
6305
|
+
const catalog = { objects, identity: sqlHash(JSON.stringify([
|
|
6306
|
+
"builtin-sql-catalog-v1",
|
|
6307
|
+
namespace,
|
|
6308
|
+
executionTarget,
|
|
6309
|
+
read,
|
|
6310
|
+
definitions.map((definition) => [definition.namespace, computeSchemaHash(definition)]).sort()
|
|
6311
|
+
])) };
|
|
6312
|
+
return normalizeIr({
|
|
6313
|
+
irVersion: RUNTIME_SQL_IR_VERSION,
|
|
6314
|
+
namespace,
|
|
6315
|
+
schemaHash: catalog.identity,
|
|
6316
|
+
ownSchemaHash: localHash(anchor),
|
|
6317
|
+
opsHash: BUILTIN_SQL_READ_HASH,
|
|
6318
|
+
dependencies: Object.fromEntries(definitions.filter((definition) => definition.namespace !== namespace).map((definition) => [definition.namespace, localHash(definition)])),
|
|
6319
|
+
tables,
|
|
6320
|
+
readPolicies: policies,
|
|
6321
|
+
unbackedTables: [...unbackedObjects],
|
|
6322
|
+
operations: { BuiltinSqlRead: {
|
|
6323
|
+
irVersion: RUNTIME_SQL_IR_VERSION,
|
|
6324
|
+
name: "BuiltinSqlRead",
|
|
6325
|
+
opHash: BUILTIN_SQL_READ_HASH,
|
|
6326
|
+
language: "sql",
|
|
6327
|
+
kind: "query",
|
|
6328
|
+
requires: null,
|
|
6329
|
+
roots: [],
|
|
6330
|
+
tableSet: actualRead,
|
|
6331
|
+
variables: [
|
|
6332
|
+
{ name: "sql", type: { kind: "scalar", scalar: "text" }, nullable: false, list: false },
|
|
6333
|
+
{ name: "parameters", type: { kind: "scalar", scalar: "jsonb" }, nullable: true, list: false }
|
|
6334
|
+
],
|
|
6335
|
+
sqlQuery: { catalog, read: actualRead, maxRows: 1e3 }
|
|
6336
|
+
} }
|
|
6337
|
+
});
|
|
6338
|
+
}
|
|
5853
6339
|
var DATASET_PREVIEW_LIMIT = 100;
|
|
5854
6340
|
function compileDatasetPreviews(input) {
|
|
5855
6341
|
const canonical = normalizeCanonical(input);
|
|
@@ -5895,6 +6381,6 @@ function compileDatasetPreviews(input) {
|
|
|
5895
6381
|
return operations;
|
|
5896
6382
|
}
|
|
5897
6383
|
|
|
5898
|
-
export { COMPILER_MAJOR, CONST_TRUE, DATASET_PREVIEW_LIMIT, DEFAULT_LIMIT, DocumentCompileError, MAX_LIMIT, NamespaceCompileError, ORDER_BY_DIRECTIONS, OperationLowerer, PLATFORM_USER_TABLE, PLATFORM_USER_TYPENAME, POLICY_COMPARE_OPS, POLICY_MAX_LINK_DEPTH, PolicyCompileError, PolicyParseError, allowedBoolExpOperators, allowedOps, andPred, assertClaimsOnlyWhen, buildCompileContext, camelCase, canonicalOperationText, classifyInputType, collectPinnedColumns, compileDatasetPreviews, compileNamespace, compileNamespaceOrThrow, compileOperationDocuments, compilePolicy, compilePredicateV2, compileRuntimeSql, computeOpHash, computeOpsHash, computePinnedColumnsByTable, computeSchemaHash, defineOperation, enumTypeName, flattenOperation, injectPolicies, inputTypeName, isSqlOperationDocument, lowerOperation, mutationRootNames, parseDocuments, parsePolicy, parseSdl, pascalCase, platformUserMeta, predContains, predContainsExists, queryRootNames, restrictOperation, sqlOperationDefinitionSchema, sqlQueryInputSchema, typeNameForTable, validateAppendInput, validateBoolExpInput, validateInsertInput, validateOnConflictInput, validateOrderByInput, validateSelectColumns, validateSetInput, walkPolicy, walkPred };
|
|
6384
|
+
export { COMPILER_MAJOR, CONST_TRUE, DATASET_PREVIEW_LIMIT, DEFAULT_LIMIT, DocumentCompileError, MAX_LIMIT, NamespaceCompileError, ORDER_BY_DIRECTIONS, OperationLowerer, PLATFORM_USER_TABLE, PLATFORM_USER_TYPENAME, POLICY_COMPARE_OPS, POLICY_MAX_LINK_DEPTH, PolicyCompileError, PolicyParseError, allowedBoolExpOperators, allowedOps, andPred, assertClaimsOnlyWhen, bindNamedParameters, buildCompileContext, camelCase, canonicalOperationText, classifyInputType, collectPinnedColumns, compileBuiltinSqlArtifact, compileDatasetPreviews, compileNamespace, compileNamespaceOrThrow, compileOperationDocuments, compilePolicy, compilePredicateV2, compileRuntimeSql, computeOpHash, computeOpsHash, computePinnedColumnsByTable, computeSchemaHash, defineOperation, enumTypeName, flattenOperation, injectPolicies, inputTypeName, isSqlOperationDocument, lowerOperation, mutationRootNames, parseDocuments, parsePolicy, parseSdl, pascalCase, platformUserMeta, predContains, predContainsExists, queryRootNames, referencedSqlObjects, restrictOperation, sqlOperationDefinitionSchema, sqlQueryInputSchema, typeNameForTable, validateAppendInput, validateBoolExpInput, validateInsertInput, validateManagedViewQueries, validateOnConflictInput, validateOrderByInput, validateSelectColumns, validateSetInput, walkPolicy, walkPred };
|
|
5899
6385
|
//# sourceMappingURL=compiler.js.map
|
|
5900
6386
|
//# sourceMappingURL=compiler.js.map
|