@sixb/pg 0.1.15 → 0.1.17

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.
Files changed (92) hide show
  1. package/README.md +125 -0
  2. package/dist/index.d.ts +15 -2
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +3202 -1474
  5. package/dist/index.js.map +32 -25
  6. package/dist/migrations.d.ts.map +1 -1
  7. package/dist/objects/aggregate-admission.d.ts +10 -0
  8. package/dist/objects/aggregate-admission.d.ts.map +1 -0
  9. package/dist/objects/preparation-lock.d.ts +6 -0
  10. package/dist/objects/preparation-lock.d.ts.map +1 -0
  11. package/dist/objects/prepare-queries.d.ts +5 -0
  12. package/dist/objects/prepare-queries.d.ts.map +1 -0
  13. package/dist/objects/query-compiler.d.ts +19 -0
  14. package/dist/objects/query-compiler.d.ts.map +1 -1
  15. package/dist/objects/query-indexes.d.ts +43 -0
  16. package/dist/objects/query-indexes.d.ts.map +1 -0
  17. package/dist/objects/reader.d.ts +7 -1
  18. package/dist/objects/reader.d.ts.map +1 -1
  19. package/dist/objects/storage.d.ts +1 -1
  20. package/dist/objects/storage.d.ts.map +1 -1
  21. package/dist/objects/text-count-index.d.ts +24 -0
  22. package/dist/objects/text-count-index.d.ts.map +1 -0
  23. package/dist/ontology-storage/index.d.ts +2 -0
  24. package/dist/ontology-storage/index.d.ts.map +1 -1
  25. package/dist/ontology-storage/materialization-session.d.ts +18 -38
  26. package/dist/ontology-storage/materialization-session.d.ts.map +1 -1
  27. package/dist/ontology-storage/materialization-state.d.ts +3 -26
  28. package/dist/ontology-storage/materialization-state.d.ts.map +1 -1
  29. package/dist/ontology-storage/materialization-writer.d.ts +51 -9
  30. package/dist/ontology-storage/materialization-writer.d.ts.map +1 -1
  31. package/dist/ontology-storage/materializations.d.ts +5 -7
  32. package/dist/ontology-storage/materializations.d.ts.map +1 -1
  33. package/dist/ontology-storage/outbox.d.ts.map +1 -1
  34. package/dist/ontology-storage/replacement-plans.d.ts +36 -0
  35. package/dist/ontology-storage/replacement-plans.d.ts.map +1 -0
  36. package/dist/ontology-storage/shared.d.ts +11 -5
  37. package/dist/ontology-storage/shared.d.ts.map +1 -1
  38. package/dist/ontology-storage/source-cleanup.d.ts.map +1 -1
  39. package/dist/ontology-storage/source-roots.d.ts +14 -1
  40. package/dist/ontology-storage/source-roots.d.ts.map +1 -1
  41. package/dist/ontology-storage/sources.d.ts +1 -2
  42. package/dist/ontology-storage/sources.d.ts.map +1 -1
  43. package/dist/pg-ai-cost-storage.d.ts.map +1 -1
  44. package/dist/pg-ai-limit-storage.d.ts.map +1 -1
  45. package/dist/pg-ai-usage-storage.d.ts +2 -0
  46. package/dist/pg-ai-usage-storage.d.ts.map +1 -1
  47. package/dist/pg-execution-storage.d.ts +28 -0
  48. package/dist/pg-execution-storage.d.ts.map +1 -1
  49. package/dist/pg-file-download-grant-storage.d.ts +11 -0
  50. package/dist/pg-file-download-grant-storage.d.ts.map +1 -0
  51. package/dist/pg-projection-run-storage.d.ts.map +1 -1
  52. package/dist/pg-rules-storage.d.ts.map +1 -1
  53. package/dist/transactions.d.ts +6 -2
  54. package/dist/transactions.d.ts.map +1 -1
  55. package/package.json +3 -3
  56. package/src/index.ts +53 -3
  57. package/src/migrations/053-replacement-plans.sql +72 -0
  58. package/src/migrations/054-slim-ontology-outbox.sql +108 -0
  59. package/src/migrations/055-object-query-preparation.sql +11 -0
  60. package/src/migrations/056-commit-touches.sql +41 -0
  61. package/src/migrations/057-audio-transcription.sql +10 -0
  62. package/src/migrations/058-reranking-model-kind.sql +11 -0
  63. package/src/migrations/059-file-download-grants.sql +16 -0
  64. package/src/migrations/060-drained-source-versions.sql +11 -0
  65. package/src/migrations.ts +20 -0
  66. package/src/objects/aggregate-admission.ts +21 -0
  67. package/src/objects/preparation-lock.ts +15 -0
  68. package/src/objects/prepare-queries.ts +181 -0
  69. package/src/objects/query-compiler.ts +405 -115
  70. package/src/objects/query-indexes.ts +348 -0
  71. package/src/objects/reader.ts +140 -51
  72. package/src/objects/storage.ts +15 -3
  73. package/src/objects/text-count-index.ts +455 -0
  74. package/src/ontology-storage/index.ts +3 -0
  75. package/src/ontology-storage/materialization-session.ts +116 -275
  76. package/src/ontology-storage/materialization-state.ts +8 -259
  77. package/src/ontology-storage/materialization-writer.ts +618 -508
  78. package/src/ontology-storage/materializations.ts +106 -289
  79. package/src/ontology-storage/outbox.ts +46 -15
  80. package/src/ontology-storage/replacement-plans.ts +1110 -0
  81. package/src/ontology-storage/shared.ts +27 -15
  82. package/src/ontology-storage/source-cleanup.ts +22 -10
  83. package/src/ontology-storage/source-roots.ts +35 -8
  84. package/src/ontology-storage/sources.ts +0 -30
  85. package/src/pg-ai-cost-storage.ts +12 -0
  86. package/src/pg-ai-limit-storage.ts +13 -2
  87. package/src/pg-ai-usage-storage.ts +8 -0
  88. package/src/pg-execution-storage.ts +8 -3
  89. package/src/pg-file-download-grant-storage.ts +76 -0
  90. package/src/pg-projection-run-storage.ts +6 -1
  91. package/src/pg-rules-storage.ts +7 -3
  92. package/src/transactions.ts +10 -3
@@ -39,6 +39,7 @@ import type {
39
39
  import type { SQLClient } from "../pg-client"
40
40
  import {
41
41
  jsonParameter,
42
+ jsonTupleExpression,
42
43
  linkRefFromColumns,
43
44
  objectRefFromColumns,
44
45
  type PgOntologySourceAssertionRow,
@@ -50,12 +51,10 @@ import {
50
51
  activeSourceRows,
51
52
  liveSourceRowsJoin,
52
53
  replacementAssertionRows,
53
- replacementSourceRows,
54
54
  sourceAssertionColumns,
55
55
  } from "./source-roots"
56
56
 
57
57
  export const PG_MATERIALIZATION_WORK_TABLE = "ontology_materialization_work"
58
- export const PG_REPLACEMENT_WORK_TABLE = "ontology_replacement_work"
59
58
 
60
59
  interface EffectiveObjectRow {
61
60
  readonly object_type_id: string
@@ -128,42 +127,18 @@ interface LinkSlotOverrideRow {
128
127
  readonly updated_at: Date | string
129
128
  }
130
129
 
131
- export type ReplacementIdentity =
132
- | {
133
- readonly kind: "object"
134
- readonly ref: OntologyObjectRef
135
- readonly sortKey: string
136
- readonly diffRequired: true
137
- }
138
- | {
139
- readonly kind: "link"
140
- readonly ref: OntologyLinkRef
141
- readonly sortKey: string
142
- readonly diffRequired: boolean
143
- }
130
+ /** A link a replacement plan decides, and whether its plan needs a diff. */
131
+ export interface ReplacementLinkIdentity {
132
+ readonly ref: OntologyLinkRef
133
+ readonly sortKey: string
134
+ readonly diffRequired: boolean
135
+ }
144
136
 
145
137
  interface ReplacementSources {
146
138
  readonly owned: ReadonlySet<string>
147
139
  readonly byMaterialization: ReadonlyMap<string, ReadonlyMap<string, StoredSourceAssertion>>
148
140
  }
149
141
 
150
- interface ReplacementIdentityInput {
151
- readonly incremental?: boolean
152
- readonly sessionId: string
153
- readonly sourceId: string
154
- readonly candidateMaterializationId: string
155
- readonly previousMaterializationId: string | null
156
- readonly kind: "object" | "link"
157
- readonly pageRows: number
158
- }
159
-
160
- interface ReplacementWorkRow {
161
- readonly entity_kind: "object" | "link"
162
- readonly identity_key: string
163
- readonly sort_key: string
164
- readonly diff_required: boolean
165
- }
166
-
167
142
  export class PgMaterializationStateReader {
168
143
  constructor(
169
144
  private readonly sql: SQLClient,
@@ -612,32 +587,6 @@ export class PgMaterializationStateReader {
612
587
  }
613
588
  }
614
589
 
615
- async *replacementIdentities(
616
- input: ReplacementIdentityInput
617
- ): AsyncIterable<ReplacementIdentity[]> {
618
- await this.prepareReplacementIdentities(input)
619
- // Temporary tables are not auto-analyzed. Refresh after each identity population is
620
- // prepared so the planner can choose the ordered index for its bounded pages.
621
- await this.sql`ANALYZE ${this.sql(PG_REPLACEMENT_WORK_TABLE)}`
622
- let cursor: string | null = null
623
- while (true) {
624
- // Keep the next-page bound indexable in generic prepared plans. A nullable OR
625
- // makes PostgreSQL revisit the preceding pages before applying the cursor filter.
626
- const after = cursor === null ? this.sql`` : this.sql`AND sort_key > ${cursor}`
627
- const rows: ReplacementWorkRow[] = await this.sql<ReplacementWorkRow[]>`
628
- SELECT entity_kind, identity_key, sort_key, diff_required
629
- FROM ${this.sql(PG_REPLACEMENT_WORK_TABLE)}
630
- WHERE session_id = ${input.sessionId} AND entity_kind = ${input.kind}
631
- ${after}
632
- ORDER BY sort_key
633
- LIMIT ${input.pageRows}
634
- `
635
- if (rows.length === 0) break
636
- yield rows.map(replacementIdentity)
637
- cursor = rows[rows.length - 1]?.sort_key ?? null
638
- }
639
- }
640
-
641
590
  async replacementObjectStates(
642
591
  sourceId: string,
643
592
  candidateMaterializationId: string,
@@ -666,7 +615,7 @@ export class PgMaterializationStateReader {
666
615
  sourceId: string,
667
616
  candidateMaterializationId: string,
668
617
  materializationIds: readonly string[],
669
- identities: readonly Extract<ReplacementIdentity, { readonly kind: "link" }>[],
618
+ identities: readonly ReplacementLinkIdentity[],
670
619
  incremental = false
671
620
  ): Promise<readonly SourceReplacementLinkState[]> {
672
621
  const refs = identities.map((identity) => identity.ref)
@@ -808,175 +757,6 @@ export class PgMaterializationStateReader {
808
757
  }
809
758
  return { owned, byMaterialization }
810
759
  }
811
-
812
- private async prepareReplacementIdentities(input: ReplacementIdentityInput): Promise<void> {
813
- if (input.kind === "object") {
814
- await this.prepareReplacementObjects(input)
815
- return
816
- }
817
- await this.prepareReplacementLinks(input)
818
- }
819
-
820
- private async prepareReplacementObjects(input: ReplacementIdentityInput): Promise<void> {
821
- const rows = replacementSourceRows(this.sql, {
822
- projectId: this.projectId,
823
- sourceId: input.sourceId,
824
- materializationId: input.candidateMaterializationId,
825
- incremental: input.incremental ?? false,
826
- })
827
- await this.sql`
828
- WITH selected AS (
829
- SELECT DISTINCT object_type_id, primary_id,
830
- ${this.sql.unsafe(objectKeyExpression("rows"))} AS identity_key,
831
- ${this.sql.unsafe(objectSortExpression("rows"))} AS sort_key
832
- FROM (${rows}) AS rows
833
- WHERE entity_kind = 'object'
834
- )
835
- INSERT INTO ${this.sql(PG_REPLACEMENT_WORK_TABLE)} (
836
- session_id, entity_kind, identity_key, sort_key, diff_required
837
- )
838
- SELECT ${input.sessionId}, 'object', identity_key, sort_key, TRUE
839
- FROM selected
840
- ON CONFLICT (session_id, entity_kind, identity_key) DO UPDATE SET
841
- diff_required = ${this.sql(PG_REPLACEMENT_WORK_TABLE)}.diff_required
842
- OR EXCLUDED.diff_required
843
- `
844
- }
845
-
846
- private async prepareReplacementLinks(input: ReplacementIdentityInput): Promise<void> {
847
- await this.sql`ANALYZE ${this.sql(PG_MATERIALIZATION_WORK_TABLE)}`
848
- const rows = replacementSourceRows(this.sql, {
849
- projectId: this.projectId,
850
- sourceId: input.sourceId,
851
- materializationId: input.candidateMaterializationId,
852
- incremental: input.incremental ?? false,
853
- })
854
- await this.sql`
855
- WITH incident_objects AS (
856
- SELECT DISTINCT payload->'ref'->>'objectTypeId' AS object_type_id,
857
- payload->'ref'->>'primaryId' AS primary_id
858
- FROM ${this.sql(PG_MATERIALIZATION_WORK_TABLE)}
859
- WHERE session_id = ${input.sessionId} AND kind = 'incident-object'
860
- ), replacement_links AS (
861
- SELECT source_type_id, source_primary_id AS source_id, link_id,
862
- target_type_id, target_primary_id AS target_id
863
- FROM (${rows}) AS rows
864
- WHERE entity_kind = 'link'
865
- ), incident_links AS (
866
- SELECT links.source_type_id, links.source_id, links.link_id,
867
- links.target_type_id, links.target_id
868
- FROM links
869
- JOIN incident_objects
870
- ON incident_objects.object_type_id = links.source_type_id
871
- AND incident_objects.primary_id = links.source_id
872
- WHERE links.project_id = ${this.projectId}
873
- UNION
874
- SELECT links.source_type_id, links.source_id, links.link_id,
875
- links.target_type_id, links.target_id
876
- FROM links
877
- JOIN incident_objects
878
- ON incident_objects.object_type_id = links.target_type_id
879
- AND incident_objects.primary_id = links.target_id
880
- WHERE links.project_id = ${this.projectId}
881
- UNION
882
- SELECT overrides.source_type_id, overrides.source_primary_id AS source_id,
883
- overrides.link_id, overrides.target_type_id,
884
- overrides.target_primary_id AS target_id
885
- FROM ontology_link_overrides AS overrides
886
- JOIN incident_objects
887
- ON incident_objects.object_type_id = overrides.source_type_id
888
- AND incident_objects.primary_id = overrides.source_primary_id
889
- WHERE overrides.project_id = ${this.projectId}
890
- UNION
891
- SELECT overrides.source_type_id, overrides.source_primary_id AS source_id,
892
- overrides.link_id, overrides.target_type_id,
893
- overrides.target_primary_id AS target_id
894
- FROM ontology_link_overrides AS overrides
895
- JOIN incident_objects
896
- ON incident_objects.object_type_id = overrides.target_type_id
897
- AND incident_objects.primary_id = overrides.target_primary_id
898
- WHERE overrides.project_id = ${this.projectId}
899
- UNION
900
- SELECT rows.source_type_id, rows.source_primary_id AS source_id,
901
- rows.link_id, rows.target_type_id, rows.target_primary_id AS target_id
902
- FROM ontology_source_rows AS rows
903
- ${liveSourceRowsJoin(this.sql, this.projectId)}
904
- JOIN incident_objects
905
- ON incident_objects.object_type_id = rows.source_type_id
906
- AND incident_objects.primary_id = rows.source_primary_id
907
- WHERE rows.entity_kind = 'link'
908
- UNION
909
- SELECT rows.source_type_id, rows.source_primary_id AS source_id,
910
- rows.link_id, rows.target_type_id, rows.target_primary_id AS target_id
911
- FROM ontology_source_rows AS rows
912
- ${liveSourceRowsJoin(this.sql, this.projectId)}
913
- JOIN incident_objects
914
- ON incident_objects.object_type_id = rows.target_type_id
915
- AND incident_objects.primary_id = rows.target_primary_id
916
- WHERE rows.entity_kind = 'link'
917
- ), diff_links AS (
918
- SELECT * FROM replacement_links
919
- UNION SELECT * FROM incident_links
920
- ), affected_scopes AS (
921
- SELECT DISTINCT source_type_id, source_id, link_id FROM diff_links
922
- ), all_links AS (
923
- SELECT diff_links.*, TRUE AS diff_required FROM diff_links
924
- UNION ALL
925
- SELECT links.source_type_id, links.source_id, links.link_id,
926
- links.target_type_id, links.target_id, FALSE AS diff_required
927
- FROM links
928
- JOIN affected_scopes USING (source_type_id, source_id, link_id)
929
- WHERE links.project_id = ${this.projectId}
930
- UNION ALL
931
- SELECT overrides.source_type_id, overrides.source_primary_id AS source_id,
932
- overrides.link_id, overrides.target_type_id,
933
- overrides.target_primary_id AS target_id, FALSE AS diff_required
934
- FROM ontology_link_overrides AS overrides
935
- JOIN affected_scopes
936
- ON affected_scopes.source_type_id = overrides.source_type_id
937
- AND affected_scopes.source_id = overrides.source_primary_id
938
- AND affected_scopes.link_id = overrides.link_id
939
- WHERE overrides.project_id = ${this.projectId} AND overrides.identity_kind = 'slot'
940
- ), selected AS (
941
- SELECT source_type_id, source_id, link_id, target_type_id, target_id,
942
- ${this.sql.unsafe(linkKeyExpression("all_links"))} AS identity_key,
943
- ${this.sql.unsafe(linkSortExpression("all_links"))} AS sort_key,
944
- BOOL_OR(diff_required) AS diff_required
945
- FROM all_links
946
- GROUP BY source_type_id, source_id, link_id, target_type_id, target_id
947
- )
948
- INSERT INTO ${this.sql(PG_REPLACEMENT_WORK_TABLE)} (
949
- session_id, entity_kind, identity_key, sort_key, diff_required
950
- )
951
- SELECT ${input.sessionId}, 'link', identity_key, sort_key, diff_required
952
- FROM selected
953
- ON CONFLICT (session_id, entity_kind, identity_key) DO UPDATE SET
954
- diff_required = ${this.sql(PG_REPLACEMENT_WORK_TABLE)}.diff_required
955
- OR EXCLUDED.diff_required
956
- `
957
- }
958
- }
959
-
960
- function replacementIdentity(row: ReplacementWorkRow): ReplacementIdentity {
961
- const parts = JSON.parse(row.identity_key) as string[]
962
- if (row.entity_kind === "object") {
963
- return {
964
- kind: "object",
965
- ref: { objectTypeId: parts[0]!, primaryId: parts[1]! },
966
- sortKey: row.sort_key,
967
- diffRequired: true,
968
- }
969
- }
970
- return {
971
- kind: "link",
972
- ref: {
973
- source: { objectTypeId: parts[0]!, primaryId: parts[1]! },
974
- linkId: parts[2]!,
975
- target: { objectTypeId: parts[3]!, primaryId: parts[4]! },
976
- },
977
- sortKey: row.sort_key,
978
- diffRequired: row.diff_required,
979
- }
980
760
  }
981
761
 
982
762
  function activeSourceMap(
@@ -1149,22 +929,6 @@ function storedPoint(row: TelemetryRow): StoredTelemetryPoint {
1149
929
  }
1150
930
  }
1151
931
 
1152
- export function objectSortExpression(alias?: string): string {
1153
- const prefix = alias ? `${alias}.` : ""
1154
- return utf8SortExpression([
1155
- `to_jsonb(${prefix}object_type_id)::text`,
1156
- `to_jsonb(${prefix}primary_id)::text`,
1157
- ])
1158
- }
1159
-
1160
- function objectKeyExpression(alias?: string): string {
1161
- const prefix = alias ? `${alias}.` : ""
1162
- return jsonTupleExpression([
1163
- `to_jsonb(${prefix}object_type_id)::text`,
1164
- `to_jsonb(${prefix}primary_id)::text`,
1165
- ])
1166
- }
1167
-
1168
932
  export function linkSortExpression(alias?: string): string {
1169
933
  const prefix = alias ? `${alias}.` : ""
1170
934
  return utf8SortExpression([
@@ -1176,21 +940,6 @@ export function linkSortExpression(alias?: string): string {
1176
940
  ])
1177
941
  }
1178
942
 
1179
- function linkKeyExpression(alias?: string): string {
1180
- const prefix = alias ? `${alias}.` : ""
1181
- return jsonTupleExpression([
1182
- `to_jsonb(${prefix}source_type_id)::text`,
1183
- `to_jsonb(${prefix}source_id)::text`,
1184
- `to_jsonb(${prefix}link_id)::text`,
1185
- `to_jsonb(${prefix}target_type_id)::text`,
1186
- `to_jsonb(${prefix}target_id)::text`,
1187
- ])
1188
- }
1189
-
1190
943
  function utf8SortExpression(parts: readonly string[]): string {
1191
944
  return `encode(convert_to(${jsonTupleExpression(parts)}, 'UTF8'), 'hex')`
1192
945
  }
1193
-
1194
- function jsonTupleExpression(parts: readonly string[]): string {
1195
- return `concat('[', ${parts.join(", ',', ")}, ']')`
1196
- }