@lix-js/sdk 0.6.0-preview.4 → 0.6.0-preview.5

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 (234) hide show
  1. package/README.md +1 -1
  2. package/SKILL.md +65 -64
  3. package/dist/engine-wasm/index.js +4 -4
  4. package/dist/engine-wasm/wasm/lix_engine.d.ts +5 -5
  5. package/dist/engine-wasm/wasm/lix_engine.js +130 -118
  6. package/dist/engine-wasm/wasm/lix_engine.wasm +0 -0
  7. package/dist/engine-wasm/wasm/lix_engine.wasm.d.ts +9 -8
  8. package/dist/generated/builtin-schemas.d.ts +69 -69
  9. package/dist/generated/builtin-schemas.js +94 -94
  10. package/dist/open-lix.d.ts +33 -26
  11. package/dist/open-lix.js +10 -10
  12. package/dist/sqlite/index.js +86 -30
  13. package/dist-engine-src/README.md +3 -3
  14. package/dist-engine-src/src/backend/capabilities.rs +67 -0
  15. package/dist-engine-src/src/backend/conformance/baseline.rs +1127 -0
  16. package/dist-engine-src/src/backend/conformance/factory.rs +93 -0
  17. package/dist-engine-src/src/backend/conformance/failure_tests.rs +608 -0
  18. package/dist-engine-src/src/backend/conformance/fixtures.rs +26 -0
  19. package/dist-engine-src/src/backend/conformance/mod.rs +75 -0
  20. package/dist-engine-src/src/backend/conformance/model.rs +28 -0
  21. package/dist-engine-src/src/backend/conformance/model_based.rs +257 -0
  22. package/dist-engine-src/src/backend/conformance/persistence.rs +204 -0
  23. package/dist-engine-src/src/backend/conformance/projection.rs +21 -0
  24. package/dist-engine-src/src/backend/conformance/pushdown.rs +24 -0
  25. package/dist-engine-src/src/backend/conformance/runner.rs +90 -0
  26. package/dist-engine-src/src/backend/conformance/scan.rs +24 -0
  27. package/dist-engine-src/src/backend/conformance/write.rs +16 -0
  28. package/dist-engine-src/src/backend/error.rs +94 -0
  29. package/dist-engine-src/src/backend/in_memory.rs +670 -0
  30. package/dist-engine-src/src/backend/mod.rs +36 -9
  31. package/dist-engine-src/src/backend/predicate.rs +80 -0
  32. package/dist-engine-src/src/backend/traits.rs +260 -0
  33. package/dist-engine-src/src/backend/types.rs +224 -81
  34. package/dist-engine-src/src/binary_cas/context.rs +8 -8
  35. package/dist-engine-src/src/binary_cas/kv.rs +234 -259
  36. package/dist-engine-src/src/{version → branch}/context.rs +12 -12
  37. package/dist-engine-src/src/branch/lifecycle.rs +221 -0
  38. package/dist-engine-src/src/branch/mod.rs +13 -0
  39. package/dist-engine-src/src/branch/refs.rs +321 -0
  40. package/dist-engine-src/src/branch/stage_rows.rs +67 -0
  41. package/dist-engine-src/src/branch/types.rs +21 -0
  42. package/dist-engine-src/src/catalog/context.rs +18 -18
  43. package/dist-engine-src/src/catalog/snapshot.rs +8 -8
  44. package/dist-engine-src/src/changelog/bench_support.rs +785 -0
  45. package/dist-engine-src/src/changelog/change.rs +1 -0
  46. package/dist-engine-src/src/changelog/codec.rs +497 -0
  47. package/dist-engine-src/src/changelog/commit.rs +1 -0
  48. package/dist-engine-src/src/changelog/context.rs +1614 -0
  49. package/dist-engine-src/src/changelog/mod.rs +29 -0
  50. package/dist-engine-src/src/changelog/store.rs +163 -0
  51. package/dist-engine-src/src/changelog/test_support.rs +54 -0
  52. package/dist-engine-src/src/changelog/types.rs +213 -0
  53. package/dist-engine-src/src/commit_graph/context.rs +317 -274
  54. package/dist-engine-src/src/commit_graph/mod.rs +2 -4
  55. package/dist-engine-src/src/commit_graph/types.rs +22 -42
  56. package/dist-engine-src/src/commit_graph/walker.rs +133 -103
  57. package/dist-engine-src/src/common/error.rs +52 -18
  58. package/dist-engine-src/src/common/identity.rs +2 -2
  59. package/dist-engine-src/src/common/mod.rs +1 -1
  60. package/dist-engine-src/src/domain.rs +42 -46
  61. package/dist-engine-src/src/engine.rs +74 -96
  62. package/dist-engine-src/src/{entity_identity.rs → entity_pk.rs} +89 -92
  63. package/dist-engine-src/src/functions/context.rs +56 -52
  64. package/dist-engine-src/src/functions/state.rs +51 -52
  65. package/dist-engine-src/src/init.rs +288 -154
  66. package/dist-engine-src/src/json_store/context.rs +15 -266
  67. package/dist-engine-src/src/json_store/mod.rs +26 -0
  68. package/dist-engine-src/src/json_store/store.rs +103 -718
  69. package/dist-engine-src/src/json_store/types.rs +4 -9
  70. package/dist-engine-src/src/lib.rs +49 -19
  71. package/dist-engine-src/src/live_state/context.rs +654 -790
  72. package/dist-engine-src/src/live_state/mod.rs +9 -3
  73. package/dist-engine-src/src/live_state/overlay.rs +4 -4
  74. package/dist-engine-src/src/live_state/types.rs +30 -21
  75. package/dist-engine-src/src/live_state/visibility.rs +514 -71
  76. package/dist-engine-src/src/plugin/install.rs +48 -48
  77. package/dist-engine-src/src/plugin/manifest.rs +7 -7
  78. package/dist-engine-src/src/plugin/materializer.rs +0 -275
  79. package/dist-engine-src/src/plugin/plugin_manifest.json +4 -3
  80. package/dist-engine-src/src/schema/builtin/lix_binary_blob_ref.json +2 -2
  81. package/dist-engine-src/src/schema/builtin/lix_branch_descriptor.json +34 -0
  82. package/dist-engine-src/src/schema/builtin/lix_branch_ref.json +48 -0
  83. package/dist-engine-src/src/schema/builtin/lix_change.json +3 -3
  84. package/dist-engine-src/src/schema/builtin/lix_commit.json +1 -1
  85. package/dist-engine-src/src/schema/builtin/lix_label_assignment.json +6 -6
  86. package/dist-engine-src/src/schema/builtin/mod.rs +18 -20
  87. package/dist-engine-src/src/schema/compatibility.rs +11 -11
  88. package/dist-engine-src/src/schema/definition.json +2 -2
  89. package/dist-engine-src/src/schema/definition.rs +5 -5
  90. package/dist-engine-src/src/schema/key.rs +3 -3
  91. package/dist-engine-src/src/schema/mod.rs +1 -1
  92. package/dist-engine-src/src/schema/tests.rs +18 -18
  93. package/dist-engine-src/src/session/context.rs +803 -148
  94. package/dist-engine-src/src/session/create_branch.rs +94 -0
  95. package/dist-engine-src/src/session/execute.rs +223 -83
  96. package/dist-engine-src/src/session/merge/analysis.rs +9 -3
  97. package/dist-engine-src/src/session/merge/{version.rs → branch.rs} +119 -129
  98. package/dist-engine-src/src/session/merge/conflicts.rs +2 -2
  99. package/dist-engine-src/src/session/merge/mod.rs +5 -6
  100. package/dist-engine-src/src/session/merge/stats.rs +7 -11
  101. package/dist-engine-src/src/session/mod.rs +15 -12
  102. package/dist-engine-src/src/session/switch_branch.rs +113 -0
  103. package/dist-engine-src/src/session/transaction.rs +495 -14
  104. package/dist-engine-src/src/sql2/{classify.rs → bind/classify.rs} +3 -75
  105. package/dist-engine-src/src/sql2/bind/error.rs +5 -0
  106. package/dist-engine-src/src/sql2/bind/expr.rs +29 -0
  107. package/dist-engine-src/src/sql2/bind/mod.rs +12 -0
  108. package/dist-engine-src/src/sql2/{udfs/public_call.rs → bind/public_udf.rs} +71 -3
  109. package/dist-engine-src/src/sql2/bind/read.rs +65 -0
  110. package/dist-engine-src/src/sql2/bind/statement.rs +2236 -0
  111. package/dist-engine-src/src/sql2/bind/table.rs +273 -0
  112. package/dist-engine-src/src/sql2/bind/write.rs +86 -0
  113. package/dist-engine-src/src/sql2/branch_scope.rs +436 -0
  114. package/dist-engine-src/src/sql2/catalog/capability.rs +20 -0
  115. package/dist-engine-src/src/sql2/catalog/entity_surface.rs +296 -0
  116. package/dist-engine-src/src/sql2/catalog/mod.rs +15 -0
  117. package/dist-engine-src/src/sql2/catalog/registry.rs +556 -0
  118. package/dist-engine-src/src/sql2/catalog/schema.rs +88 -0
  119. package/dist-engine-src/src/sql2/catalog/surface.rs +41 -0
  120. package/dist-engine-src/src/sql2/change_materialization.rs +122 -0
  121. package/dist-engine-src/src/sql2/context.rs +36 -30
  122. package/dist-engine-src/src/sql2/error.rs +1 -1
  123. package/dist-engine-src/src/sql2/exec/bound_public_write.rs +1593 -0
  124. package/dist-engine-src/src/sql2/exec/datafusion.rs +5266 -0
  125. package/dist-engine-src/src/sql2/exec/fast_write.rs +82 -0
  126. package/dist-engine-src/src/sql2/exec/mod.rs +24 -0
  127. package/dist-engine-src/src/sql2/exec/write.rs +661 -0
  128. package/dist-engine-src/src/sql2/filesystem_planner.rs +72 -77
  129. package/dist-engine-src/src/sql2/filesystem_visibility.rs +21 -21
  130. package/dist-engine-src/src/sql2/history_projection.rs +8 -8
  131. package/dist-engine-src/src/sql2/history_route.rs +35 -31
  132. package/dist-engine-src/src/sql2/mod.rs +28 -23
  133. package/dist-engine-src/src/sql2/optimize/datafusion.rs +1 -0
  134. package/dist-engine-src/src/sql2/optimize/mod.rs +2 -0
  135. package/dist-engine-src/src/sql2/optimize/simple_write.rs +116 -0
  136. package/dist-engine-src/src/sql2/parse/mod.rs +69 -0
  137. package/dist-engine-src/src/sql2/parse/normalize.rs +1 -0
  138. package/dist-engine-src/src/sql2/plan/branch_scope.rs +24 -0
  139. package/dist-engine-src/src/sql2/plan/mod.rs +5 -0
  140. package/dist-engine-src/src/sql2/plan/predicate.rs +22 -0
  141. package/dist-engine-src/src/sql2/plan/write.rs +147 -0
  142. package/dist-engine-src/src/sql2/predicate_typecheck.rs +258 -0
  143. package/dist-engine-src/src/sql2/{version_provider.rs → providers/branch.rs} +218 -214
  144. package/dist-engine-src/src/sql2/{change_provider.rs → providers/change.rs} +156 -42
  145. package/dist-engine-src/src/sql2/{directory_provider.rs → providers/directory.rs} +291 -322
  146. package/dist-engine-src/src/sql2/{directory_history_provider.rs → providers/directory_history.rs} +56 -42
  147. package/dist-engine-src/src/sql2/providers/entity.rs +1484 -0
  148. package/dist-engine-src/src/sql2/{entity_history_provider.rs → providers/entity_history.rs} +43 -31
  149. package/dist-engine-src/src/sql2/{file_provider.rs → providers/file.rs} +323 -316
  150. package/dist-engine-src/src/sql2/{file_history_provider.rs → providers/file_history.rs} +60 -46
  151. package/dist-engine-src/src/sql2/{history_provider.rs → providers/history.rs} +46 -32
  152. package/dist-engine-src/src/sql2/{lix_state_provider.rs → providers/lix_state.rs} +359 -329
  153. package/dist-engine-src/src/sql2/providers/mod.rs +508 -0
  154. package/dist-engine-src/src/sql2/read_only.rs +2 -2
  155. package/dist-engine-src/src/sql2/session.rs +47 -96
  156. package/dist-engine-src/src/sql2/storage/constraints.rs +1 -0
  157. package/dist-engine-src/src/sql2/storage/mod.rs +1 -0
  158. package/dist-engine-src/src/sql2/test_support/differential.rs +712 -0
  159. package/dist-engine-src/src/sql2/test_support/generators.rs +354 -0
  160. package/dist-engine-src/src/sql2/test_support/mod.rs +2 -0
  161. package/dist-engine-src/src/sql2/udfs/{lix_active_version_commit_id.rs → lix_active_branch_commit_id.rs} +7 -7
  162. package/dist-engine-src/src/sql2/udfs/mod.rs +3 -6
  163. package/dist-engine-src/src/sql2/write_normalization.rs +45 -22
  164. package/dist-engine-src/src/storage/conformance.rs +399 -0
  165. package/dist-engine-src/src/storage/context.rs +552 -288
  166. package/dist-engine-src/src/storage/mod.rs +48 -10
  167. package/dist-engine-src/src/storage/point.rs +440 -0
  168. package/dist-engine-src/src/storage/read_scope.rs +43 -64
  169. package/dist-engine-src/src/storage/reader.rs +867 -0
  170. package/dist-engine-src/src/storage/scan.rs +784 -0
  171. package/dist-engine-src/src/storage/spaces.rs +236 -0
  172. package/dist-engine-src/src/storage/stats.rs +80 -0
  173. package/dist-engine-src/src/storage/write_set.rs +962 -0
  174. package/dist-engine-src/src/storage_bench.rs +136 -4828
  175. package/dist-engine-src/src/test_support.rs +360 -138
  176. package/dist-engine-src/src/tracked_state/bench_support.rs +394 -0
  177. package/dist-engine-src/src/tracked_state/codec.rs +155 -1057
  178. package/dist-engine-src/src/tracked_state/commit_root_rebuild.rs +358 -0
  179. package/dist-engine-src/src/tracked_state/context.rs +1927 -993
  180. package/dist-engine-src/src/tracked_state/diff.rs +1715 -261
  181. package/dist-engine-src/src/tracked_state/merge.rs +74 -88
  182. package/dist-engine-src/src/tracked_state/mod.rs +19 -16
  183. package/dist-engine-src/src/tracked_state/{materialization.rs → row_materialization.rs} +50 -178
  184. package/dist-engine-src/src/tracked_state/storage.rs +243 -191
  185. package/dist-engine-src/src/tracked_state/tree.rs +247 -371
  186. package/dist-engine-src/src/tracked_state/types.rs +49 -42
  187. package/dist-engine-src/src/transaction/bench_support.rs +407 -0
  188. package/dist-engine-src/src/transaction/commit.rs +821 -713
  189. package/dist-engine-src/src/transaction/context.rs +705 -600
  190. package/dist-engine-src/src/transaction/mod.rs +13 -2
  191. package/dist-engine-src/src/transaction/normalization.rs +63 -76
  192. package/dist-engine-src/src/transaction/prep.rs +13 -13
  193. package/dist-engine-src/src/transaction/schema_resolver.rs +19 -5
  194. package/dist-engine-src/src/transaction/staging.rs +228 -434
  195. package/dist-engine-src/src/transaction/types.rs +41 -98
  196. package/dist-engine-src/src/transaction/validation.rs +382 -446
  197. package/dist-engine-src/src/untracked_state/codec.rs +337 -29
  198. package/dist-engine-src/src/untracked_state/context.rs +7 -7
  199. package/dist-engine-src/src/untracked_state/materialization.rs +2 -2
  200. package/dist-engine-src/src/untracked_state/mod.rs +1 -1
  201. package/dist-engine-src/src/untracked_state/storage.rs +659 -157
  202. package/dist-engine-src/src/untracked_state/types.rs +21 -21
  203. package/package.json +71 -68
  204. package/dist-engine-src/src/backend/kv.rs +0 -358
  205. package/dist-engine-src/src/backend/testing.rs +0 -658
  206. package/dist-engine-src/src/commit_store/codec.rs +0 -887
  207. package/dist-engine-src/src/commit_store/context.rs +0 -944
  208. package/dist-engine-src/src/commit_store/materialization.rs +0 -84
  209. package/dist-engine-src/src/commit_store/mod.rs +0 -16
  210. package/dist-engine-src/src/commit_store/storage.rs +0 -600
  211. package/dist-engine-src/src/commit_store/types.rs +0 -215
  212. package/dist-engine-src/src/schema/builtin/lix_version_descriptor.json +0 -34
  213. package/dist-engine-src/src/schema/builtin/lix_version_ref.json +0 -48
  214. package/dist-engine-src/src/session/create_version.rs +0 -88
  215. package/dist-engine-src/src/session/merge/apply.rs +0 -23
  216. package/dist-engine-src/src/session/optimization9_sql2_bench.rs +0 -100
  217. package/dist-engine-src/src/session/switch_version.rs +0 -110
  218. package/dist-engine-src/src/sql2/entity_provider.rs +0 -3211
  219. package/dist-engine-src/src/sql2/execute.rs +0 -3533
  220. package/dist-engine-src/src/sql2/public_bind/assignment.rs +0 -46
  221. package/dist-engine-src/src/sql2/public_bind/capability.rs +0 -41
  222. package/dist-engine-src/src/sql2/public_bind/dml.rs +0 -172
  223. package/dist-engine-src/src/sql2/public_bind/mod.rs +0 -26
  224. package/dist-engine-src/src/sql2/public_bind/table.rs +0 -168
  225. package/dist-engine-src/src/sql2/version_scope.rs +0 -394
  226. package/dist-engine-src/src/storage/types.rs +0 -501
  227. package/dist-engine-src/src/tracked_state/by_file_index.rs +0 -98
  228. package/dist-engine-src/src/tracked_state/materializer.rs +0 -488
  229. package/dist-engine-src/src/transaction/live_state_overlay.rs +0 -35
  230. package/dist-engine-src/src/version/lifecycle.rs +0 -221
  231. package/dist-engine-src/src/version/mod.rs +0 -13
  232. package/dist-engine-src/src/version/refs.rs +0 -330
  233. package/dist-engine-src/src/version/stage_rows.rs +0 -67
  234. package/dist-engine-src/src/version/types.rs +0 -21
@@ -0,0 +1,354 @@
1
+ //! Deterministic sql2 statement generators for differential tests.
2
+
3
+ #[cfg(test)]
4
+ use std::borrow::Cow;
5
+
6
+ #[cfg(test)]
7
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
8
+ pub(crate) enum DifferentialExpectation {
9
+ /// Semantic regression coverage. The candidate runs in normal auto mode,
10
+ /// so this proves sql2 behavior but does not prove fast execution.
11
+ SemanticParityMayFallback,
12
+ /// Physical fast-path coverage. The candidate must produce a fast write
13
+ /// plan, and the test fails if optimization declines the statement.
14
+ FastRequiredParity,
15
+ }
16
+
17
+ #[cfg(test)]
18
+ #[derive(Clone, Debug, PartialEq, Eq)]
19
+ pub(crate) struct DifferentialSqlCase {
20
+ pub(crate) seed: Cow<'static, str>,
21
+ pub(crate) setup_sql: &'static [&'static str],
22
+ pub(crate) transaction_setup_sql: &'static [&'static str],
23
+ pub(crate) sql: Cow<'static, str>,
24
+ pub(crate) params: &'static [DifferentialParam],
25
+ pub(crate) probes: &'static [DifferentialProbe],
26
+ pub(crate) expectation: DifferentialExpectation,
27
+ pub(crate) expected_execution: ExpectedExecution,
28
+ }
29
+
30
+ #[cfg(test)]
31
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
32
+ pub(crate) enum ExpectedExecution {
33
+ Ok,
34
+ Err { code: &'static str },
35
+ }
36
+
37
+ #[cfg(test)]
38
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
39
+ pub(crate) enum DifferentialParam {
40
+ Json(&'static str),
41
+ }
42
+
43
+ #[cfg(test)]
44
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
45
+ pub(crate) enum DifferentialProbe {
46
+ LixStateActive {
47
+ schema_key: &'static str,
48
+ entity_pks: &'static [&'static str],
49
+ },
50
+ LixStateByBranch {
51
+ schema_key: &'static str,
52
+ entity_pks: &'static [&'static str],
53
+ branch_ids: &'static [&'static str],
54
+ },
55
+ RegisteredSchemaActive,
56
+ RegisteredSchemaByBranch {
57
+ branch_ids: &'static [&'static str],
58
+ },
59
+ }
60
+
61
+ #[cfg(test)]
62
+ const SEED_LIX_STATE_ROW_SQL: &str = "INSERT INTO lix_state (entity_pk, schema_key, file_id, snapshot_content, global, untracked) VALUES (lix_json('[\"diff-key\"]'), 'lix_key_value', NULL, lix_json('{\"key\":\"diff-key\",\"value\":\"base\"}'), false, true)";
63
+
64
+ #[cfg(test)]
65
+ const SETUP_SEED_LIX_STATE_ROW: &[&str] = &[SEED_LIX_STATE_ROW_SQL];
66
+
67
+ #[cfg(test)]
68
+ const EMPTY_PARAMS: &[DifferentialParam] = &[];
69
+
70
+ #[cfg(test)]
71
+ pub(crate) const ACTIVE_BRANCH_PROBE_ID: &str = "__active_branch__";
72
+
73
+ #[cfg(test)]
74
+ const LIX_KEY_VALUE_PROBE: &[DifferentialProbe] = &[DifferentialProbe::LixStateActive {
75
+ schema_key: "lix_key_value",
76
+ entity_pks: &["diff-key", "global-diff", "tx-diff", "dup"],
77
+ }];
78
+
79
+ #[cfg(test)]
80
+ const LIX_KEY_VALUE_BRANCHED_PROBE: &[DifferentialProbe] = &[
81
+ DifferentialProbe::LixStateActive {
82
+ schema_key: "lix_key_value",
83
+ entity_pks: &["diff-key", "global-diff", "tx-diff", "dup"],
84
+ },
85
+ DifferentialProbe::LixStateByBranch {
86
+ schema_key: "lix_key_value",
87
+ entity_pks: &["diff-key", "global-diff", "tx-diff", "dup"],
88
+ branch_ids: &[ACTIVE_BRANCH_PROBE_ID, "global", "branch-a", "branch-b"],
89
+ },
90
+ ];
91
+
92
+ #[cfg(test)]
93
+ const REGISTERED_SCHEMA_PROBE: &[DifferentialProbe] = &[
94
+ DifferentialProbe::RegisteredSchemaActive,
95
+ DifferentialProbe::RegisteredSchemaByBranch {
96
+ branch_ids: &[ACTIVE_BRANCH_PROBE_ID, "global", "branch-a", "branch-b"],
97
+ },
98
+ ];
99
+
100
+ #[cfg(test)]
101
+ const LIX_KEY_VALUE_AND_REGISTERED_SCHEMA_PROBES: &[DifferentialProbe] = &[
102
+ DifferentialProbe::LixStateActive {
103
+ schema_key: "lix_key_value",
104
+ entity_pks: &["diff-key", "global-diff", "tx-diff", "dup"],
105
+ },
106
+ DifferentialProbe::RegisteredSchemaActive,
107
+ ];
108
+
109
+ #[cfg(test)]
110
+ const STAGED_TX_INSERT_SQL: &str = "INSERT INTO lix_state (entity_pk, schema_key, file_id, snapshot_content, global, untracked) VALUES (lix_json('[\"tx-diff\"]'), 'lix_key_value', NULL, lix_json('{\"key\":\"tx-diff\",\"value\":\"base\"}'), false, true)";
111
+
112
+ #[cfg(test)]
113
+ const TX_SETUP_STAGED_LIX_STATE_ROW: &[&str] = &[STAGED_TX_INSERT_SQL];
114
+
115
+ #[cfg(test)]
116
+ const PARAM_METADATA_JSON: &[DifferentialParam] =
117
+ &[DifferentialParam::Json("{\"seen\":\"param\"}")];
118
+
119
+ #[cfg(test)]
120
+ const PARAM_ENTITY_PK_AND_METADATA: &[DifferentialParam] = &[
121
+ DifferentialParam::Json("[\"diff-key\"]"),
122
+ DifferentialParam::Json("{\"seen\":\"param\"}"),
123
+ ];
124
+
125
+ #[cfg(test)]
126
+ pub(crate) fn deterministic_repro_cases() -> Vec<DifferentialSqlCase> {
127
+ vec![
128
+ DifferentialSqlCase {
129
+ seed: "known/unresolvable-assignment-target".into(),
130
+ setup_sql: &[],
131
+ transaction_setup_sql: &[],
132
+ sql: "UPDATE lix_state SET no_such_column = 'x' WHERE false".into(),
133
+ params: EMPTY_PARAMS,
134
+ probes: LIX_KEY_VALUE_AND_REGISTERED_SCHEMA_PROBES,
135
+ expectation: DifferentialExpectation::SemanticParityMayFallback,
136
+ expected_execution: ExpectedExecution::Err {
137
+ code: "LIX_COLUMN_NOT_FOUND",
138
+ },
139
+ },
140
+ DifferentialSqlCase {
141
+ seed: "known/base-entity-branch-override".into(),
142
+ setup_sql: &[],
143
+ transaction_setup_sql: &[],
144
+ sql: "UPDATE lix_registered_schema SET value = lix_json('{\"x-lix-key\":\"x\",\"type\":\"object\"}') WHERE lixcol_branch_id = 'branch-b'".into(),
145
+ params: EMPTY_PARAMS,
146
+ probes: REGISTERED_SCHEMA_PROBE,
147
+ expectation: DifferentialExpectation::SemanticParityMayFallback,
148
+ expected_execution: ExpectedExecution::Err {
149
+ code: "LIX_COLUMN_NOT_FOUND",
150
+ },
151
+ },
152
+ DifferentialSqlCase {
153
+ seed: "known/base-entity-insert-hidden-branch-column".into(),
154
+ setup_sql: &[],
155
+ transaction_setup_sql: &[],
156
+ sql: "INSERT INTO lix_registered_schema (value, lixcol_branch_id) VALUES (lix_json('{\"x-lix-key\":\"x\",\"type\":\"object\"}'), 'branch-b')".into(),
157
+ params: EMPTY_PARAMS,
158
+ probes: REGISTERED_SCHEMA_PROBE,
159
+ expectation: DifferentialExpectation::SemanticParityMayFallback,
160
+ expected_execution: ExpectedExecution::Err {
161
+ code: "LIX_COLUMN_NOT_FOUND",
162
+ },
163
+ },
164
+ DifferentialSqlCase {
165
+ seed: "known/unknown-typed-entity-insert-column".into(),
166
+ setup_sql: &[],
167
+ transaction_setup_sql: &[],
168
+ sql: "INSERT INTO lix_registered_schema (value, unknown_column) VALUES (lix_json('{\"x-lix-key\":\"x\",\"type\":\"object\"}'), 'x')".into(),
169
+ params: EMPTY_PARAMS,
170
+ probes: REGISTERED_SCHEMA_PROBE,
171
+ expectation: DifferentialExpectation::SemanticParityMayFallback,
172
+ expected_execution: ExpectedExecution::Err {
173
+ code: "LIX_COLUMN_NOT_FOUND",
174
+ },
175
+ },
176
+ DifferentialSqlCase {
177
+ seed: "known/by-branch-update-without-branch-predicate".into(),
178
+ setup_sql: &[],
179
+ transaction_setup_sql: &[],
180
+ sql: "UPDATE lix_registered_schema_by_branch SET value = lix_json('{\"x-lix-key\":\"x\",\"type\":\"object\"}')".into(),
181
+ params: EMPTY_PARAMS,
182
+ probes: REGISTERED_SCHEMA_PROBE,
183
+ expectation: DifferentialExpectation::SemanticParityMayFallback,
184
+ expected_execution: ExpectedExecution::Err {
185
+ code: "LIX_UNSUPPORTED_SQL",
186
+ },
187
+ },
188
+ DifferentialSqlCase {
189
+ seed: "known/by-branch-delete-without-branch-predicate".into(),
190
+ setup_sql: &[],
191
+ transaction_setup_sql: &[],
192
+ sql: "DELETE FROM lix_registered_schema_by_branch".into(),
193
+ params: EMPTY_PARAMS,
194
+ probes: REGISTERED_SCHEMA_PROBE,
195
+ expectation: DifferentialExpectation::SemanticParityMayFallback,
196
+ expected_execution: ExpectedExecution::Err {
197
+ code: "LIX_UNSUPPORTED_SQL",
198
+ },
199
+ },
200
+ DifferentialSqlCase {
201
+ seed: "known/repeated-contradictory-predicates".into(),
202
+ setup_sql: SETUP_SEED_LIX_STATE_ROW,
203
+ transaction_setup_sql: &[],
204
+ sql: "UPDATE lix_state SET metadata = lix_json('{\"seen\":true}') WHERE schema_key = 'lix_key_value' AND schema_key = 'other_schema'".into(),
205
+ params: EMPTY_PARAMS,
206
+ probes: LIX_KEY_VALUE_PROBE,
207
+ expectation: DifferentialExpectation::SemanticParityMayFallback,
208
+ expected_execution: ExpectedExecution::Ok,
209
+ },
210
+ DifferentialSqlCase {
211
+ seed: "known/duplicate-insert-target-columns".into(),
212
+ setup_sql: &[],
213
+ transaction_setup_sql: &[],
214
+ sql: "INSERT INTO lix_state (entity_pk, entity_pk, schema_key, file_id, snapshot_content) VALUES (lix_json('[\"dup\"]'), lix_json('[\"dup\"]'), 'lix_key_value', NULL, lix_json('{\"key\":\"dup\",\"value\":\"dup\"}'))".into(),
215
+ params: EMPTY_PARAMS,
216
+ probes: LIX_KEY_VALUE_PROBE,
217
+ expectation: DifferentialExpectation::SemanticParityMayFallback,
218
+ expected_execution: ExpectedExecution::Err {
219
+ code: "LIX_INVALID_PARAM",
220
+ },
221
+ },
222
+ DifferentialSqlCase {
223
+ seed: "known/duplicate-update-assignments".into(),
224
+ setup_sql: &[],
225
+ transaction_setup_sql: &[],
226
+ sql: "UPDATE lix_state SET metadata = NULL, metadata = NULL WHERE false".into(),
227
+ params: EMPTY_PARAMS,
228
+ probes: LIX_KEY_VALUE_PROBE,
229
+ expectation: DifferentialExpectation::SemanticParityMayFallback,
230
+ expected_execution: ExpectedExecution::Err {
231
+ code: "LIX_INVALID_PARAM",
232
+ },
233
+ },
234
+ DifferentialSqlCase {
235
+ seed: "known/qualified-target-table-name".into(),
236
+ setup_sql: &[],
237
+ transaction_setup_sql: &[],
238
+ sql: "UPDATE public.lix_state SET metadata = NULL WHERE false".into(),
239
+ params: EMPTY_PARAMS,
240
+ probes: LIX_KEY_VALUE_PROBE,
241
+ expectation: DifferentialExpectation::SemanticParityMayFallback,
242
+ expected_execution: ExpectedExecution::Err {
243
+ code: "LIX_UNSUPPORTED_SQL",
244
+ },
245
+ },
246
+ DifferentialSqlCase {
247
+ seed: "known/staged-overlay-global-row-read".into(),
248
+ setup_sql: &[],
249
+ transaction_setup_sql: &[],
250
+ sql: "INSERT INTO lix_state (entity_pk, schema_key, file_id, snapshot_content, global, untracked) VALUES (lix_json('[\"global-diff\"]'), 'lix_key_value', NULL, lix_json('{\"key\":\"global-diff\",\"value\":\"global\"}'), true, true)".into(),
251
+ params: EMPTY_PARAMS,
252
+ probes: LIX_KEY_VALUE_PROBE,
253
+ expectation: DifferentialExpectation::SemanticParityMayFallback,
254
+ expected_execution: ExpectedExecution::Ok,
255
+ },
256
+ DifferentialSqlCase {
257
+ seed: "known/empty-branch-filter-base-staged-dedupe".into(),
258
+ setup_sql: SETUP_SEED_LIX_STATE_ROW,
259
+ transaction_setup_sql: &[],
260
+ sql: "UPDATE lix_state SET snapshot_content = lix_json('{\"key\":\"diff-key\",\"value\":\"staged\"}') WHERE schema_key IN ('lix_key_value') AND entity_pk = lix_json('[\"diff-key\"]')".into(),
261
+ params: EMPTY_PARAMS,
262
+ probes: LIX_KEY_VALUE_PROBE,
263
+ expectation: DifferentialExpectation::SemanticParityMayFallback,
264
+ expected_execution: ExpectedExecution::Ok,
265
+ },
266
+ DifferentialSqlCase {
267
+ seed: "known/parameter-binding-after-contradiction".into(),
268
+ setup_sql: SETUP_SEED_LIX_STATE_ROW,
269
+ transaction_setup_sql: &[],
270
+ sql: "UPDATE lix_state SET metadata = $2 WHERE schema_key = 'lix_key_value' AND schema_key = 'other_schema' AND entity_pk = $1".into(),
271
+ params: PARAM_ENTITY_PK_AND_METADATA,
272
+ probes: LIX_KEY_VALUE_PROBE,
273
+ expectation: DifferentialExpectation::SemanticParityMayFallback,
274
+ expected_execution: ExpectedExecution::Ok,
275
+ },
276
+ DifferentialSqlCase {
277
+ seed: "known/staged-overlay-update-sees-prior-staged-row".into(),
278
+ setup_sql: &[],
279
+ transaction_setup_sql: TX_SETUP_STAGED_LIX_STATE_ROW,
280
+ sql: "UPDATE lix_state SET snapshot_content = lix_json('{\"key\":\"tx-diff\",\"value\":\"updated\"}') WHERE schema_key = 'lix_key_value' AND entity_pk = lix_json('[\"tx-diff\"]')".into(),
281
+ params: EMPTY_PARAMS,
282
+ probes: LIX_KEY_VALUE_PROBE,
283
+ expectation: DifferentialExpectation::SemanticParityMayFallback,
284
+ expected_execution: ExpectedExecution::Ok,
285
+ },
286
+ ]
287
+ }
288
+
289
+ #[cfg(test)]
290
+ pub(crate) fn generated_dml_cases() -> Vec<DifferentialSqlCase> {
291
+ let mut cases = Vec::new();
292
+
293
+ for target in ["lix_state", "lix_state_by_branch"] {
294
+ cases.push(DifferentialSqlCase {
295
+ seed: format!("generated/{target}/delete-false").into(),
296
+ setup_sql: SETUP_SEED_LIX_STATE_ROW,
297
+ transaction_setup_sql: &[],
298
+ sql: format!("DELETE FROM {target} WHERE false").into(),
299
+ params: EMPTY_PARAMS,
300
+ probes: LIX_KEY_VALUE_BRANCHED_PROBE,
301
+ expectation: DifferentialExpectation::FastRequiredParity,
302
+ expected_execution: ExpectedExecution::Ok,
303
+ });
304
+ cases.push(DifferentialSqlCase {
305
+ seed: format!("generated/{target}/update-false").into(),
306
+ setup_sql: SETUP_SEED_LIX_STATE_ROW,
307
+ transaction_setup_sql: &[],
308
+ sql: format!("UPDATE {target} SET metadata = NULL WHERE false").into(),
309
+ params: EMPTY_PARAMS,
310
+ probes: LIX_KEY_VALUE_BRANCHED_PROBE,
311
+ expectation: DifferentialExpectation::FastRequiredParity,
312
+ expected_execution: ExpectedExecution::Ok,
313
+ });
314
+ }
315
+
316
+ cases.extend([
317
+ DifferentialSqlCase {
318
+ seed: "generated/lix-state-by-branch/update-explicit-miss".into(),
319
+ setup_sql: SETUP_SEED_LIX_STATE_ROW,
320
+ transaction_setup_sql: &[],
321
+ sql: "UPDATE lix_state_by_branch SET metadata = NULL WHERE branch_id = 'branch-b' AND schema_key = 'lix_key_value'".into(),
322
+ params: EMPTY_PARAMS,
323
+ probes: LIX_KEY_VALUE_BRANCHED_PROBE,
324
+ expectation: DifferentialExpectation::SemanticParityMayFallback,
325
+ expected_execution: ExpectedExecution::Err {
326
+ code: "LIX_ERROR_INVALID_STORAGE_SCOPE",
327
+ },
328
+ },
329
+ DifferentialSqlCase {
330
+ seed: "generated/entity-base/reject-hidden-branch".into(),
331
+ setup_sql: &[],
332
+ transaction_setup_sql: &[],
333
+ sql: "DELETE FROM lix_registered_schema WHERE lixcol_branch_id = 'branch-a'".into(),
334
+ params: EMPTY_PARAMS,
335
+ probes: REGISTERED_SCHEMA_PROBE,
336
+ expectation: DifferentialExpectation::SemanticParityMayFallback,
337
+ expected_execution: ExpectedExecution::Err {
338
+ code: "LIX_COLUMN_NOT_FOUND",
339
+ },
340
+ },
341
+ DifferentialSqlCase {
342
+ seed: "generated/lix-state/update-param-metadata".into(),
343
+ setup_sql: SETUP_SEED_LIX_STATE_ROW,
344
+ transaction_setup_sql: &[],
345
+ sql: "UPDATE lix_state SET metadata = $1 WHERE schema_key = 'lix_key_value' AND entity_pk = lix_json('[\"diff-key\"]')".into(),
346
+ params: PARAM_METADATA_JSON,
347
+ probes: LIX_KEY_VALUE_PROBE,
348
+ expectation: DifferentialExpectation::SemanticParityMayFallback,
349
+ expected_execution: ExpectedExecution::Ok,
350
+ },
351
+ ]);
352
+
353
+ cases
354
+ }
@@ -0,0 +1,2 @@
1
+ pub(crate) mod differential;
2
+ pub(crate) mod generators;
@@ -7,29 +7,29 @@ use datafusion::logical_expr::{
7
7
  };
8
8
 
9
9
  #[derive(Clone, PartialEq, Eq, Hash)]
10
- pub(super) struct LixActiveVersionCommitId {
10
+ pub(super) struct LixActiveBranchCommitId {
11
11
  commit_id: Option<String>,
12
12
  }
13
13
 
14
- impl LixActiveVersionCommitId {
14
+ impl LixActiveBranchCommitId {
15
15
  pub(super) fn new(commit_id: Option<String>) -> Self {
16
16
  Self { commit_id }
17
17
  }
18
18
  }
19
19
 
20
- impl std::fmt::Debug for LixActiveVersionCommitId {
20
+ impl std::fmt::Debug for LixActiveBranchCommitId {
21
21
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22
- f.debug_struct("LixActiveVersionCommitId").finish()
22
+ f.debug_struct("LixActiveBranchCommitId").finish()
23
23
  }
24
24
  }
25
25
 
26
- impl ScalarUDFImpl for LixActiveVersionCommitId {
26
+ impl ScalarUDFImpl for LixActiveBranchCommitId {
27
27
  fn as_any(&self) -> &dyn Any {
28
28
  self
29
29
  }
30
30
 
31
31
  fn name(&self) -> &str {
32
- "lix_active_version_commit_id"
32
+ "lix_active_branch_commit_id"
33
33
  }
34
34
 
35
35
  fn signature(&self) -> &Signature {
@@ -44,7 +44,7 @@ impl ScalarUDFImpl for LixActiveVersionCommitId {
44
44
 
45
45
  fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
46
46
  if !args.args.is_empty() {
47
- return plan_err!("lix_active_version_commit_id requires no arguments");
47
+ return plan_err!("lix_active_branch_commit_id requires no arguments");
48
48
  }
49
49
  Ok(ColumnarValue::Scalar(ScalarValue::Utf8(
50
50
  self.commit_id.clone(),
@@ -1,5 +1,5 @@
1
1
  mod common;
2
- mod lix_active_version_commit_id;
2
+ mod lix_active_branch_commit_id;
3
3
  mod lix_empty_blob;
4
4
  mod lix_json;
5
5
  mod lix_json_get;
@@ -8,15 +8,12 @@ mod lix_text_decode;
8
8
  mod lix_text_encode;
9
9
  mod lix_timestamp;
10
10
  mod lix_uuid_v7;
11
- mod public_call;
12
11
 
13
12
  use datafusion::execution::context::SessionContext;
14
13
  use datafusion::logical_expr::ScalarUDF;
15
14
 
16
15
  use crate::functions::FunctionProviderHandle;
17
16
 
18
- pub(crate) use public_call::validate_public_udf_calls_in_datafusion_statement;
19
-
20
17
  #[cfg(test)]
21
18
  pub(crate) fn system_sql2_function_provider() -> FunctionProviderHandle {
22
19
  use crate::functions::{FunctionProvider, SharedFunctionProvider, SystemFunctionProvider};
@@ -27,10 +24,10 @@ pub(crate) fn system_sql2_function_provider() -> FunctionProviderHandle {
27
24
  pub(crate) fn register_sql2_functions(
28
25
  ctx: &SessionContext,
29
26
  functions: FunctionProviderHandle,
30
- active_version_commit_id: Option<String>,
27
+ active_branch_commit_id: Option<String>,
31
28
  ) {
32
29
  ctx.register_udf(ScalarUDF::from(
33
- lix_active_version_commit_id::LixActiveVersionCommitId::new(active_version_commit_id),
30
+ lix_active_branch_commit_id::LixActiveBranchCommitId::new(active_branch_commit_id),
34
31
  ));
35
32
  ctx.register_udf(ScalarUDF::from(lix_json_get::LixJsonGet::new()));
36
33
  ctx.register_udf(ScalarUDF::from(lix_json_get_text::LixJsonGetText::new()));
@@ -11,6 +11,7 @@ use datafusion::physical_expr::PhysicalExpr;
11
11
  use datafusion::physical_plan::projection::ProjectionExec;
12
12
  use datafusion::physical_plan::ExecutionPlan;
13
13
 
14
+ use crate::sql2::exec::datafusion::LIX_INSERT_COLUMN_OMITTED_METADATA_KEY;
14
15
  use crate::LixError;
15
16
 
16
17
  #[derive(Debug, Clone)]
@@ -47,24 +48,32 @@ pub(crate) struct InsertColumnIntents {
47
48
  }
48
49
 
49
50
  impl InsertColumnIntents {
50
- pub(crate) fn all_explicit() -> Self {
51
- Self {
52
- explicit_columns: None,
51
+ pub(crate) fn from_input(input: &Arc<dyn ExecutionPlan>) -> Self {
52
+ if let Some(explicit_columns) = Self::explicit_columns_from_schema(input) {
53
+ return Self {
54
+ explicit_columns: Some(explicit_columns),
55
+ };
53
56
  }
54
- }
55
57
 
56
- pub(crate) fn from_input(input: &Arc<dyn ExecutionPlan>) -> Self {
57
58
  let Some(projection) = input.as_any().downcast_ref::<ProjectionExec>() else {
58
59
  return Self {
59
60
  explicit_columns: None,
60
61
  };
61
62
  };
62
63
 
64
+ let child_schema = projection.children().first().map(|child| child.schema());
63
65
  let explicit_columns = projection
64
66
  .expr()
65
67
  .iter()
66
- .filter(|expr| !is_generated_null_default(expr.expr.as_ref()))
67
- .map(|expr| expr.alias.clone())
68
+ .enumerate()
69
+ .filter(|(index, expr)| {
70
+ !is_generated_null_default(expr.expr.as_ref())
71
+ && !child_schema
72
+ .as_ref()
73
+ .and_then(|schema| schema.fields().get(*index))
74
+ .is_some_and(|field| field_is_omitted_insert_default(field.as_ref()))
75
+ })
76
+ .map(|(_, expr)| expr.alias.clone())
68
77
  .collect();
69
78
 
70
79
  Self {
@@ -72,27 +81,41 @@ impl InsertColumnIntents {
72
81
  }
73
82
  }
74
83
 
84
+ fn explicit_columns_from_schema(input: &Arc<dyn ExecutionPlan>) -> Option<BTreeSet<String>> {
85
+ let omitted_columns = input
86
+ .schema()
87
+ .fields()
88
+ .iter()
89
+ .filter(|field| field_is_omitted_insert_default(field.as_ref()))
90
+ .map(|field| field.name().clone())
91
+ .collect::<BTreeSet<_>>();
92
+ if omitted_columns.is_empty() {
93
+ return None;
94
+ }
95
+
96
+ Some(
97
+ input
98
+ .schema()
99
+ .fields()
100
+ .iter()
101
+ .filter(|field| !omitted_columns.contains(field.name().as_str()))
102
+ .map(|field| field.name().clone())
103
+ .collect(),
104
+ )
105
+ }
106
+
75
107
  pub(crate) fn includes_column(&self, column_name: &str) -> bool {
76
108
  self.explicit_columns
77
109
  .as_ref()
78
110
  .is_none_or(|columns| columns.contains(column_name))
79
111
  }
112
+ }
80
113
 
81
- pub(crate) fn cell(
82
- &self,
83
- batch: &RecordBatch,
84
- row_index: usize,
85
- column_name: &str,
86
- ) -> Result<InsertCell> {
87
- if !self.includes_column(column_name) {
88
- return Ok(InsertCell::Omitted);
89
- }
90
-
91
- optional_scalar_value(batch, row_index, column_name).map(|value| match value {
92
- None => InsertCell::Omitted,
93
- Some(value) => InsertCell::Provided(SqlCell::from_scalar(value)),
94
- })
95
- }
114
+ fn field_is_omitted_insert_default(field: &datafusion::arrow::datatypes::Field) -> bool {
115
+ field
116
+ .metadata()
117
+ .get(LIX_INSERT_COLUMN_OMITTED_METADATA_KEY)
118
+ .is_some_and(|value| value == "true")
96
119
  }
97
120
 
98
121
  pub(crate) fn reject_non_binary_casts_for_insert_column(