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

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 (274) hide show
  1. package/README.md +76 -4
  2. package/dist/errors.d.ts +7 -0
  3. package/dist/errors.js +19 -0
  4. package/dist/index.d.ts +4 -5
  5. package/dist/index.js +3 -3
  6. package/dist/native.d.ts +1 -0
  7. package/dist/native.js +47 -0
  8. package/dist/open-lix.d.ts +38 -207
  9. package/dist/open-lix.js +59 -284
  10. package/dist/result.d.ts +18 -0
  11. package/dist/result.js +48 -0
  12. package/dist/types.d.ts +114 -1
  13. package/dist/value.d.ts +28 -0
  14. package/dist/value.js +245 -0
  15. package/package.json +38 -71
  16. package/SKILL.md +0 -507
  17. package/dist/builtin-schemas.d.ts +0 -1
  18. package/dist/builtin-schemas.js +0 -1
  19. package/dist/engine-wasm/index.d.ts +0 -87
  20. package/dist/engine-wasm/index.js +0 -339
  21. package/dist/engine-wasm/wasm/lix_engine.d.ts +0 -79
  22. package/dist/engine-wasm/wasm/lix_engine.js +0 -833
  23. package/dist/engine-wasm/wasm/lix_engine.wasm +0 -0
  24. package/dist/engine-wasm/wasm/lix_engine.wasm.d.ts +0 -27
  25. package/dist/generated/builtin-schemas.d.ts +0 -427
  26. package/dist/generated/builtin-schemas.js +0 -643
  27. package/dist/sqlite/index.d.ts +0 -12
  28. package/dist/sqlite/index.js +0 -359
  29. package/dist-engine-src/README.md +0 -18
  30. package/dist-engine-src/src/backend/capabilities.rs +0 -67
  31. package/dist-engine-src/src/backend/conformance/baseline.rs +0 -1127
  32. package/dist-engine-src/src/backend/conformance/factory.rs +0 -93
  33. package/dist-engine-src/src/backend/conformance/failure_tests.rs +0 -608
  34. package/dist-engine-src/src/backend/conformance/fixtures.rs +0 -26
  35. package/dist-engine-src/src/backend/conformance/mod.rs +0 -75
  36. package/dist-engine-src/src/backend/conformance/model.rs +0 -28
  37. package/dist-engine-src/src/backend/conformance/model_based.rs +0 -257
  38. package/dist-engine-src/src/backend/conformance/persistence.rs +0 -204
  39. package/dist-engine-src/src/backend/conformance/projection.rs +0 -21
  40. package/dist-engine-src/src/backend/conformance/pushdown.rs +0 -24
  41. package/dist-engine-src/src/backend/conformance/runner.rs +0 -90
  42. package/dist-engine-src/src/backend/conformance/scan.rs +0 -24
  43. package/dist-engine-src/src/backend/conformance/write.rs +0 -16
  44. package/dist-engine-src/src/backend/error.rs +0 -94
  45. package/dist-engine-src/src/backend/in_memory.rs +0 -670
  46. package/dist-engine-src/src/backend/mod.rs +0 -39
  47. package/dist-engine-src/src/backend/predicate.rs +0 -80
  48. package/dist-engine-src/src/backend/traits.rs +0 -260
  49. package/dist-engine-src/src/backend/types.rs +0 -239
  50. package/dist-engine-src/src/binary_cas/chunking.rs +0 -31
  51. package/dist-engine-src/src/binary_cas/codec.rs +0 -346
  52. package/dist-engine-src/src/binary_cas/context.rs +0 -139
  53. package/dist-engine-src/src/binary_cas/kv.rs +0 -1038
  54. package/dist-engine-src/src/binary_cas/mod.rs +0 -11
  55. package/dist-engine-src/src/binary_cas/types.rs +0 -121
  56. package/dist-engine-src/src/branch/context.rs +0 -40
  57. package/dist-engine-src/src/branch/lifecycle.rs +0 -221
  58. package/dist-engine-src/src/branch/mod.rs +0 -13
  59. package/dist-engine-src/src/branch/refs.rs +0 -321
  60. package/dist-engine-src/src/branch/stage_rows.rs +0 -67
  61. package/dist-engine-src/src/branch/types.rs +0 -21
  62. package/dist-engine-src/src/catalog/context.rs +0 -412
  63. package/dist-engine-src/src/catalog/mod.rs +0 -10
  64. package/dist-engine-src/src/catalog/schema.rs +0 -4
  65. package/dist-engine-src/src/catalog/snapshot.rs +0 -1114
  66. package/dist-engine-src/src/cel/context.rs +0 -86
  67. package/dist-engine-src/src/cel/error.rs +0 -19
  68. package/dist-engine-src/src/cel/mod.rs +0 -8
  69. package/dist-engine-src/src/cel/provider.rs +0 -9
  70. package/dist-engine-src/src/cel/runtime.rs +0 -167
  71. package/dist-engine-src/src/cel/value.rs +0 -50
  72. package/dist-engine-src/src/changelog/bench_support.rs +0 -785
  73. package/dist-engine-src/src/changelog/change.rs +0 -1
  74. package/dist-engine-src/src/changelog/codec.rs +0 -497
  75. package/dist-engine-src/src/changelog/commit.rs +0 -1
  76. package/dist-engine-src/src/changelog/context.rs +0 -1614
  77. package/dist-engine-src/src/changelog/mod.rs +0 -29
  78. package/dist-engine-src/src/changelog/store.rs +0 -163
  79. package/dist-engine-src/src/changelog/test_support.rs +0 -54
  80. package/dist-engine-src/src/changelog/types.rs +0 -213
  81. package/dist-engine-src/src/commit_graph/context.rs +0 -944
  82. package/dist-engine-src/src/commit_graph/mod.rs +0 -9
  83. package/dist-engine-src/src/commit_graph/types.rs +0 -89
  84. package/dist-engine-src/src/commit_graph/walker.rs +0 -786
  85. package/dist-engine-src/src/common/error.rs +0 -347
  86. package/dist-engine-src/src/common/fingerprint.rs +0 -3
  87. package/dist-engine-src/src/common/fs_path.rs +0 -1336
  88. package/dist-engine-src/src/common/identity.rs +0 -145
  89. package/dist-engine-src/src/common/json_pointer.rs +0 -67
  90. package/dist-engine-src/src/common/metadata.rs +0 -40
  91. package/dist-engine-src/src/common/mod.rs +0 -23
  92. package/dist-engine-src/src/common/types.rs +0 -105
  93. package/dist-engine-src/src/common/wire.rs +0 -222
  94. package/dist-engine-src/src/domain.rs +0 -320
  95. package/dist-engine-src/src/engine.rs +0 -203
  96. package/dist-engine-src/src/entity_pk.rs +0 -402
  97. package/dist-engine-src/src/functions/context.rs +0 -296
  98. package/dist-engine-src/src/functions/deterministic.rs +0 -113
  99. package/dist-engine-src/src/functions/mod.rs +0 -18
  100. package/dist-engine-src/src/functions/provider.rs +0 -130
  101. package/dist-engine-src/src/functions/state.rs +0 -335
  102. package/dist-engine-src/src/functions/types.rs +0 -37
  103. package/dist-engine-src/src/init.rs +0 -692
  104. package/dist-engine-src/src/json_store/compression.rs +0 -77
  105. package/dist-engine-src/src/json_store/context.rs +0 -172
  106. package/dist-engine-src/src/json_store/encoded.rs +0 -15
  107. package/dist-engine-src/src/json_store/mod.rs +0 -38
  108. package/dist-engine-src/src/json_store/store.rs +0 -494
  109. package/dist-engine-src/src/json_store/types.rs +0 -212
  110. package/dist-engine-src/src/lib.rs +0 -92
  111. package/dist-engine-src/src/live_state/context.rs +0 -1883
  112. package/dist-engine-src/src/live_state/mod.rs +0 -21
  113. package/dist-engine-src/src/live_state/overlay.rs +0 -75
  114. package/dist-engine-src/src/live_state/reader.rs +0 -23
  115. package/dist-engine-src/src/live_state/types.rs +0 -231
  116. package/dist-engine-src/src/live_state/visibility.rs +0 -666
  117. package/dist-engine-src/src/plugin/archive.rs +0 -438
  118. package/dist-engine-src/src/plugin/component.rs +0 -183
  119. package/dist-engine-src/src/plugin/install.rs +0 -619
  120. package/dist-engine-src/src/plugin/manifest.rs +0 -516
  121. package/dist-engine-src/src/plugin/materializer.rs +0 -202
  122. package/dist-engine-src/src/plugin/mod.rs +0 -33
  123. package/dist-engine-src/src/plugin/plugin_manifest.json +0 -119
  124. package/dist-engine-src/src/plugin/storage.rs +0 -74
  125. package/dist-engine-src/src/schema/annotations/defaults.rs +0 -275
  126. package/dist-engine-src/src/schema/annotations/mod.rs +0 -1
  127. package/dist-engine-src/src/schema/builtin/lix_account.json +0 -21
  128. package/dist-engine-src/src/schema/builtin/lix_active_account.json +0 -29
  129. package/dist-engine-src/src/schema/builtin/lix_binary_blob_ref.json +0 -29
  130. package/dist-engine-src/src/schema/builtin/lix_branch_descriptor.json +0 -34
  131. package/dist-engine-src/src/schema/builtin/lix_branch_ref.json +0 -48
  132. package/dist-engine-src/src/schema/builtin/lix_change.json +0 -63
  133. package/dist-engine-src/src/schema/builtin/lix_change_author.json +0 -45
  134. package/dist-engine-src/src/schema/builtin/lix_commit.json +0 -24
  135. package/dist-engine-src/src/schema/builtin/lix_commit_edge.json +0 -53
  136. package/dist-engine-src/src/schema/builtin/lix_directory_descriptor.json +0 -52
  137. package/dist-engine-src/src/schema/builtin/lix_file_descriptor.json +0 -52
  138. package/dist-engine-src/src/schema/builtin/lix_key_value.json +0 -40
  139. package/dist-engine-src/src/schema/builtin/lix_label.json +0 -29
  140. package/dist-engine-src/src/schema/builtin/lix_label_assignment.json +0 -74
  141. package/dist-engine-src/src/schema/builtin/lix_registered_schema.json +0 -25
  142. package/dist-engine-src/src/schema/builtin/mod.rs +0 -220
  143. package/dist-engine-src/src/schema/compatibility.rs +0 -787
  144. package/dist-engine-src/src/schema/definition.json +0 -187
  145. package/dist-engine-src/src/schema/definition.rs +0 -742
  146. package/dist-engine-src/src/schema/key.rs +0 -138
  147. package/dist-engine-src/src/schema/mod.rs +0 -20
  148. package/dist-engine-src/src/schema/seed.rs +0 -14
  149. package/dist-engine-src/src/schema/tests.rs +0 -780
  150. package/dist-engine-src/src/session/context.rs +0 -1059
  151. package/dist-engine-src/src/session/create_branch.rs +0 -94
  152. package/dist-engine-src/src/session/execute.rs +0 -681
  153. package/dist-engine-src/src/session/merge/analysis.rs +0 -108
  154. package/dist-engine-src/src/session/merge/branch.rs +0 -417
  155. package/dist-engine-src/src/session/merge/conflicts.rs +0 -63
  156. package/dist-engine-src/src/session/merge/mod.rs +0 -10
  157. package/dist-engine-src/src/session/merge/stats.rs +0 -61
  158. package/dist-engine-src/src/session/mod.rs +0 -30
  159. package/dist-engine-src/src/session/switch_branch.rs +0 -113
  160. package/dist-engine-src/src/session/transaction.rs +0 -557
  161. package/dist-engine-src/src/sql2/bind/classify.rs +0 -102
  162. package/dist-engine-src/src/sql2/bind/error.rs +0 -5
  163. package/dist-engine-src/src/sql2/bind/expr.rs +0 -29
  164. package/dist-engine-src/src/sql2/bind/mod.rs +0 -12
  165. package/dist-engine-src/src/sql2/bind/public_udf.rs +0 -306
  166. package/dist-engine-src/src/sql2/bind/read.rs +0 -65
  167. package/dist-engine-src/src/sql2/bind/statement.rs +0 -2236
  168. package/dist-engine-src/src/sql2/bind/table.rs +0 -273
  169. package/dist-engine-src/src/sql2/bind/write.rs +0 -86
  170. package/dist-engine-src/src/sql2/branch_scope.rs +0 -436
  171. package/dist-engine-src/src/sql2/catalog/capability.rs +0 -20
  172. package/dist-engine-src/src/sql2/catalog/entity_surface.rs +0 -296
  173. package/dist-engine-src/src/sql2/catalog/mod.rs +0 -15
  174. package/dist-engine-src/src/sql2/catalog/registry.rs +0 -556
  175. package/dist-engine-src/src/sql2/catalog/schema.rs +0 -88
  176. package/dist-engine-src/src/sql2/catalog/surface.rs +0 -41
  177. package/dist-engine-src/src/sql2/change_materialization.rs +0 -122
  178. package/dist-engine-src/src/sql2/context.rs +0 -317
  179. package/dist-engine-src/src/sql2/dml.rs +0 -148
  180. package/dist-engine-src/src/sql2/error.rs +0 -215
  181. package/dist-engine-src/src/sql2/exec/bound_public_write.rs +0 -1593
  182. package/dist-engine-src/src/sql2/exec/datafusion.rs +0 -5266
  183. package/dist-engine-src/src/sql2/exec/fast_write.rs +0 -82
  184. package/dist-engine-src/src/sql2/exec/mod.rs +0 -24
  185. package/dist-engine-src/src/sql2/exec/write.rs +0 -661
  186. package/dist-engine-src/src/sql2/filesystem_planner.rs +0 -1485
  187. package/dist-engine-src/src/sql2/filesystem_predicates.rs +0 -159
  188. package/dist-engine-src/src/sql2/filesystem_visibility.rs +0 -383
  189. package/dist-engine-src/src/sql2/history_projection.rs +0 -56
  190. package/dist-engine-src/src/sql2/history_route.rs +0 -661
  191. package/dist-engine-src/src/sql2/mod.rs +0 -52
  192. package/dist-engine-src/src/sql2/optimize/datafusion.rs +0 -1
  193. package/dist-engine-src/src/sql2/optimize/mod.rs +0 -2
  194. package/dist-engine-src/src/sql2/optimize/simple_write.rs +0 -116
  195. package/dist-engine-src/src/sql2/parse/mod.rs +0 -69
  196. package/dist-engine-src/src/sql2/parse/normalize.rs +0 -1
  197. package/dist-engine-src/src/sql2/plan/branch_scope.rs +0 -24
  198. package/dist-engine-src/src/sql2/plan/mod.rs +0 -5
  199. package/dist-engine-src/src/sql2/plan/predicate.rs +0 -22
  200. package/dist-engine-src/src/sql2/plan/write.rs +0 -147
  201. package/dist-engine-src/src/sql2/predicate_typecheck.rs +0 -504
  202. package/dist-engine-src/src/sql2/providers/branch.rs +0 -1206
  203. package/dist-engine-src/src/sql2/providers/change.rs +0 -445
  204. package/dist-engine-src/src/sql2/providers/directory.rs +0 -2422
  205. package/dist-engine-src/src/sql2/providers/directory_history.rs +0 -645
  206. package/dist-engine-src/src/sql2/providers/entity.rs +0 -1484
  207. package/dist-engine-src/src/sql2/providers/entity_history.rs +0 -452
  208. package/dist-engine-src/src/sql2/providers/file.rs +0 -3686
  209. package/dist-engine-src/src/sql2/providers/file_history.rs +0 -924
  210. package/dist-engine-src/src/sql2/providers/history.rs +0 -426
  211. package/dist-engine-src/src/sql2/providers/lix_state.rs +0 -2542
  212. package/dist-engine-src/src/sql2/providers/mod.rs +0 -508
  213. package/dist-engine-src/src/sql2/read_only.rs +0 -63
  214. package/dist-engine-src/src/sql2/record_batch.rs +0 -17
  215. package/dist-engine-src/src/sql2/result_metadata.rs +0 -29
  216. package/dist-engine-src/src/sql2/runtime.rs +0 -60
  217. package/dist-engine-src/src/sql2/session.rs +0 -83
  218. package/dist-engine-src/src/sql2/storage/constraints.rs +0 -1
  219. package/dist-engine-src/src/sql2/storage/mod.rs +0 -1
  220. package/dist-engine-src/src/sql2/test_support/differential.rs +0 -712
  221. package/dist-engine-src/src/sql2/test_support/generators.rs +0 -354
  222. package/dist-engine-src/src/sql2/test_support/mod.rs +0 -2
  223. package/dist-engine-src/src/sql2/udfs/common.rs +0 -295
  224. package/dist-engine-src/src/sql2/udfs/lix_active_branch_commit_id.rs +0 -53
  225. package/dist-engine-src/src/sql2/udfs/lix_empty_blob.rs +0 -47
  226. package/dist-engine-src/src/sql2/udfs/lix_json.rs +0 -100
  227. package/dist-engine-src/src/sql2/udfs/lix_json_get.rs +0 -99
  228. package/dist-engine-src/src/sql2/udfs/lix_json_get_text.rs +0 -99
  229. package/dist-engine-src/src/sql2/udfs/lix_text_decode.rs +0 -82
  230. package/dist-engine-src/src/sql2/udfs/lix_text_encode.rs +0 -85
  231. package/dist-engine-src/src/sql2/udfs/lix_timestamp.rs +0 -76
  232. package/dist-engine-src/src/sql2/udfs/lix_uuid_v7.rs +0 -76
  233. package/dist-engine-src/src/sql2/udfs/mod.rs +0 -86
  234. package/dist-engine-src/src/sql2/write_normalization.rs +0 -368
  235. package/dist-engine-src/src/storage/conformance.rs +0 -399
  236. package/dist-engine-src/src/storage/context.rs +0 -620
  237. package/dist-engine-src/src/storage/mod.rs +0 -52
  238. package/dist-engine-src/src/storage/point.rs +0 -440
  239. package/dist-engine-src/src/storage/read_scope.rs +0 -67
  240. package/dist-engine-src/src/storage/reader.rs +0 -867
  241. package/dist-engine-src/src/storage/scan.rs +0 -784
  242. package/dist-engine-src/src/storage/spaces.rs +0 -236
  243. package/dist-engine-src/src/storage/stats.rs +0 -80
  244. package/dist-engine-src/src/storage/write_set.rs +0 -962
  245. package/dist-engine-src/src/storage_bench.rs +0 -171
  246. package/dist-engine-src/src/test_support.rs +0 -450
  247. package/dist-engine-src/src/tracked_state/bench_support.rs +0 -394
  248. package/dist-engine-src/src/tracked_state/codec.rs +0 -1183
  249. package/dist-engine-src/src/tracked_state/commit_root_rebuild.rs +0 -358
  250. package/dist-engine-src/src/tracked_state/context.rs +0 -2801
  251. package/dist-engine-src/src/tracked_state/diff.rs +0 -2140
  252. package/dist-engine-src/src/tracked_state/merge.rs +0 -478
  253. package/dist-engine-src/src/tracked_state/mod.rs +0 -35
  254. package/dist-engine-src/src/tracked_state/row_materialization.rs +0 -275
  255. package/dist-engine-src/src/tracked_state/storage.rs +0 -427
  256. package/dist-engine-src/src/tracked_state/tree.rs +0 -3063
  257. package/dist-engine-src/src/tracked_state/types.rs +0 -238
  258. package/dist-engine-src/src/transaction/bench_support.rs +0 -407
  259. package/dist-engine-src/src/transaction/commit.rs +0 -1592
  260. package/dist-engine-src/src/transaction/context.rs +0 -1653
  261. package/dist-engine-src/src/transaction/mod.rs +0 -24
  262. package/dist-engine-src/src/transaction/normalization.rs +0 -877
  263. package/dist-engine-src/src/transaction/prep.rs +0 -37
  264. package/dist-engine-src/src/transaction/schema_resolver.rs +0 -163
  265. package/dist-engine-src/src/transaction/staging.rs +0 -1525
  266. package/dist-engine-src/src/transaction/types.rs +0 -403
  267. package/dist-engine-src/src/transaction/validation.rs +0 -5766
  268. package/dist-engine-src/src/untracked_state/codec.rs +0 -615
  269. package/dist-engine-src/src/untracked_state/context.rs +0 -98
  270. package/dist-engine-src/src/untracked_state/materialization.rs +0 -63
  271. package/dist-engine-src/src/untracked_state/mod.rs +0 -15
  272. package/dist-engine-src/src/untracked_state/storage.rs +0 -898
  273. package/dist-engine-src/src/untracked_state/types.rs +0 -146
  274. package/dist-engine-src/src/wasm/mod.rs +0 -60
@@ -1,504 +0,0 @@
1
- use std::collections::{BTreeMap, BTreeSet};
2
-
3
- use datafusion::arrow::datatypes::{Field, Schema};
4
- use datafusion::common::metadata::FieldMetadata;
5
- use datafusion::common::tree_node::{Transformed, TreeNode};
6
- use datafusion::common::{DFSchema, DataFusionError, ScalarValue};
7
- use datafusion::logical_expr::expr::{Between, InList};
8
- use datafusion::logical_expr::{BinaryExpr, Expr, Like, Operator};
9
-
10
- use crate::LixError;
11
-
12
- use super::error::lix_error_to_datafusion_error;
13
- use super::result_metadata::{field_is_json, LIX_VALUE_TYPE_JSON, LIX_VALUE_TYPE_METADATA_KEY};
14
-
15
- pub(crate) fn validate_json_predicate_filters(
16
- schema: &Schema,
17
- filters: &[Expr],
18
- ) -> Result<(), DataFusionError> {
19
- for filter in filters {
20
- validate_json_predicate_expr_with_arrow_schema(schema, filter)
21
- .map_err(lix_error_to_datafusion_error)?;
22
- }
23
- Ok(())
24
- }
25
-
26
- pub(crate) fn canonicalize_json_identity_text_filters(
27
- schema: &Schema,
28
- filters: &[Expr],
29
- ) -> Result<Vec<Expr>, DataFusionError> {
30
- filters
31
- .iter()
32
- .cloned()
33
- .map(|filter| canonicalize_json_identity_text_filter(schema, filter))
34
- .collect()
35
- }
36
-
37
- pub(crate) fn validate_json_predicate_expr_with_dfschema(
38
- schema: &DFSchema,
39
- expr: &Expr,
40
- ) -> Result<(), LixError> {
41
- validate_expr(expr, &|column| {
42
- schema
43
- .field_with_name(column.relation.as_ref(), &column.name)
44
- .ok()
45
- .map(|field| field.as_ref())
46
- })
47
- }
48
-
49
- pub(crate) fn json_predicate_placeholder_indexes_with_dfschema(
50
- schema: &DFSchema,
51
- expr: &Expr,
52
- ) -> BTreeSet<usize> {
53
- let mut indexes = BTreeSet::new();
54
- collect_json_predicate_placeholder_indexes(expr, &mut indexes, &|column| {
55
- schema
56
- .field_with_name(column.relation.as_ref(), &column.name)
57
- .ok()
58
- .map(|field| field.as_ref())
59
- });
60
- indexes
61
- }
62
-
63
- fn canonicalize_json_identity_text_filter(
64
- schema: &Schema,
65
- expr: Expr,
66
- ) -> Result<Expr, DataFusionError> {
67
- expr.transform(|expr| canonicalize_json_identity_text_expr(schema, expr))
68
- .map(|transformed| transformed.data)
69
- }
70
-
71
- fn canonicalize_json_identity_text_expr(
72
- schema: &Schema,
73
- expr: Expr,
74
- ) -> Result<Transformed<Expr>, DataFusionError> {
75
- match expr {
76
- Expr::BinaryExpr(binary) if is_comparison_operator(binary.op) => {
77
- canonicalize_json_identity_text_binary(schema, binary)
78
- }
79
- Expr::InList(in_list) => canonicalize_json_identity_text_in_list(schema, in_list),
80
- _ => Ok(Transformed::no(expr)),
81
- }
82
- }
83
-
84
- fn canonicalize_json_identity_text_binary(
85
- schema: &Schema,
86
- binary: BinaryExpr,
87
- ) -> Result<Transformed<Expr>, DataFusionError> {
88
- let BinaryExpr { left, op, right } = binary;
89
- let left_identity_json = is_identity_json_expr_for_arrow_schema(schema, &left);
90
- let right_identity_json = is_identity_json_expr_for_arrow_schema(schema, &right);
91
- let left = if right_identity_json {
92
- Box::new(canonicalize_json_text_literal(*left)?)
93
- } else {
94
- left
95
- };
96
- let right = if left_identity_json {
97
- Box::new(canonicalize_json_text_literal(*right)?)
98
- } else {
99
- right
100
- };
101
- Ok(Transformed::yes(Expr::BinaryExpr(BinaryExpr::new(
102
- left, op, right,
103
- ))))
104
- }
105
-
106
- fn canonicalize_json_identity_text_in_list(
107
- schema: &Schema,
108
- in_list: InList,
109
- ) -> Result<Transformed<Expr>, DataFusionError> {
110
- let expr_identity_json = is_identity_json_expr_for_arrow_schema(schema, &in_list.expr);
111
- let list_has_identity_json = in_list
112
- .list
113
- .iter()
114
- .any(|item| is_identity_json_expr_for_arrow_schema(schema, item));
115
-
116
- let expr = if list_has_identity_json {
117
- Box::new(canonicalize_json_text_literal(*in_list.expr)?)
118
- } else {
119
- in_list.expr
120
- };
121
- let list = if expr_identity_json {
122
- in_list
123
- .list
124
- .into_iter()
125
- .map(canonicalize_json_text_literal)
126
- .collect::<Result<Vec<_>, _>>()?
127
- } else {
128
- in_list.list
129
- };
130
-
131
- Ok(Transformed::yes(Expr::InList(InList::new(
132
- expr,
133
- list,
134
- in_list.negated,
135
- ))))
136
- }
137
-
138
- fn canonicalize_json_text_literal(expr: Expr) -> Result<Expr, DataFusionError> {
139
- let Expr::Literal(literal, metadata) = expr else {
140
- return Ok(expr);
141
- };
142
- let canonical = match &literal {
143
- ScalarValue::Utf8(Some(value))
144
- | ScalarValue::Utf8View(Some(value))
145
- | ScalarValue::LargeUtf8(Some(value)) => Some(canonical_json_text(value)?),
146
- _ => None,
147
- };
148
- Ok(match canonical {
149
- Some(value) => Expr::Literal(ScalarValue::Utf8(Some(value)), Some(json_field_metadata())),
150
- None => Expr::Literal(literal, metadata),
151
- })
152
- }
153
-
154
- fn canonical_json_text(raw: &str) -> Result<String, DataFusionError> {
155
- serde_json::from_str::<serde_json::Value>(raw)
156
- .map(|value| value.to_string())
157
- .map_err(|error| {
158
- lix_error_to_datafusion_error(LixError::new(
159
- LixError::CODE_TYPE_MISMATCH,
160
- format!("JSON comparison value is not valid JSON: {error}"),
161
- ))
162
- })
163
- }
164
-
165
- fn json_field_metadata() -> FieldMetadata {
166
- FieldMetadata::new(BTreeMap::from([(
167
- LIX_VALUE_TYPE_METADATA_KEY.to_string(),
168
- LIX_VALUE_TYPE_JSON.to_string(),
169
- )]))
170
- }
171
-
172
- fn is_identity_json_expr_for_arrow_schema(schema: &Schema, expr: &Expr) -> bool {
173
- is_identity_json_expr(expr)
174
- && is_json_expr(expr, &|column| {
175
- schema
176
- .fields()
177
- .iter()
178
- .find(|field| field.name() == &column.name)
179
- .map(|field| field.as_ref())
180
- })
181
- }
182
-
183
- fn validate_json_predicate_expr_with_arrow_schema(
184
- schema: &Schema,
185
- expr: &Expr,
186
- ) -> Result<(), LixError> {
187
- validate_expr(expr, &|column| {
188
- schema
189
- .fields()
190
- .iter()
191
- .find(|field| field.name() == &column.name)
192
- .map(|field| field.as_ref())
193
- })
194
- }
195
-
196
- fn validate_expr<'a>(
197
- expr: &'a Expr,
198
- lookup_field: &impl Fn(&datafusion::common::Column) -> Option<&'a Field>,
199
- ) -> Result<(), LixError> {
200
- match expr {
201
- Expr::BinaryExpr(binary) => validate_binary_expr(binary, lookup_field),
202
- Expr::InList(in_list) => validate_in_list(in_list, lookup_field),
203
- Expr::Between(between) => validate_between(between, lookup_field),
204
- Expr::Like(like) | Expr::SimilarTo(like) => validate_like(like, lookup_field),
205
- Expr::Alias(alias) => validate_expr(&alias.expr, lookup_field),
206
- Expr::Not(inner)
207
- | Expr::IsNotNull(inner)
208
- | Expr::IsNull(inner)
209
- | Expr::IsTrue(inner)
210
- | Expr::IsFalse(inner)
211
- | Expr::IsUnknown(inner)
212
- | Expr::IsNotTrue(inner)
213
- | Expr::IsNotFalse(inner)
214
- | Expr::IsNotUnknown(inner)
215
- | Expr::Negative(inner) => validate_expr(inner, lookup_field),
216
- Expr::Cast(cast) => validate_expr(&cast.expr, lookup_field),
217
- Expr::TryCast(cast) => validate_expr(&cast.expr, lookup_field),
218
- Expr::ScalarFunction(function) => {
219
- for arg in &function.args {
220
- validate_expr(arg, lookup_field)?;
221
- }
222
- Ok(())
223
- }
224
- Expr::Case(case) => {
225
- if let Some(expr) = &case.expr {
226
- validate_expr(expr, lookup_field)?;
227
- }
228
- for (when, then) in &case.when_then_expr {
229
- validate_expr(when, lookup_field)?;
230
- validate_expr(then, lookup_field)?;
231
- }
232
- if let Some(expr) = &case.else_expr {
233
- validate_expr(expr, lookup_field)?;
234
- }
235
- Ok(())
236
- }
237
- Expr::AggregateFunction(function) => {
238
- for arg in &function.params.args {
239
- validate_expr(arg, lookup_field)?;
240
- }
241
- Ok(())
242
- }
243
- Expr::WindowFunction(function) => {
244
- for arg in &function.params.args {
245
- validate_expr(arg, lookup_field)?;
246
- }
247
- Ok(())
248
- }
249
- _ => Ok(()),
250
- }
251
- }
252
-
253
- fn collect_json_predicate_placeholder_indexes<'a>(
254
- expr: &'a Expr,
255
- indexes: &mut BTreeSet<usize>,
256
- lookup_field: &impl Fn(&datafusion::common::Column) -> Option<&'a Field>,
257
- ) {
258
- match expr {
259
- Expr::BinaryExpr(binary) if is_comparison_operator(binary.op) => {
260
- collect_json_predicate_placeholder_indexes(&binary.left, indexes, lookup_field);
261
- collect_json_predicate_placeholder_indexes(&binary.right, indexes, lookup_field);
262
- if is_json_expr(&binary.left, lookup_field) {
263
- collect_placeholder_indexes(&binary.right, indexes);
264
- }
265
- if is_json_expr(&binary.right, lookup_field) {
266
- collect_placeholder_indexes(&binary.left, indexes);
267
- }
268
- }
269
- Expr::BinaryExpr(binary) => {
270
- collect_json_predicate_placeholder_indexes(&binary.left, indexes, lookup_field);
271
- collect_json_predicate_placeholder_indexes(&binary.right, indexes, lookup_field);
272
- }
273
- Expr::InList(in_list) => {
274
- collect_json_predicate_placeholder_indexes(&in_list.expr, indexes, lookup_field);
275
- for item in &in_list.list {
276
- collect_json_predicate_placeholder_indexes(item, indexes, lookup_field);
277
- }
278
- if is_json_expr(&in_list.expr, lookup_field) {
279
- for item in &in_list.list {
280
- collect_placeholder_indexes(item, indexes);
281
- }
282
- }
283
- for item in &in_list.list {
284
- if is_json_expr(item, lookup_field) {
285
- collect_placeholder_indexes(&in_list.expr, indexes);
286
- }
287
- }
288
- }
289
- Expr::Between(between) => {
290
- collect_json_predicate_placeholder_indexes(&between.expr, indexes, lookup_field);
291
- collect_json_predicate_placeholder_indexes(&between.low, indexes, lookup_field);
292
- collect_json_predicate_placeholder_indexes(&between.high, indexes, lookup_field);
293
- if is_json_expr(&between.expr, lookup_field) {
294
- collect_placeholder_indexes(&between.low, indexes);
295
- collect_placeholder_indexes(&between.high, indexes);
296
- }
297
- }
298
- Expr::Alias(alias) => {
299
- collect_json_predicate_placeholder_indexes(&alias.expr, indexes, lookup_field)
300
- }
301
- Expr::Not(inner)
302
- | Expr::IsNotNull(inner)
303
- | Expr::IsNull(inner)
304
- | Expr::IsTrue(inner)
305
- | Expr::IsFalse(inner)
306
- | Expr::IsUnknown(inner)
307
- | Expr::IsNotTrue(inner)
308
- | Expr::IsNotFalse(inner)
309
- | Expr::IsNotUnknown(inner)
310
- | Expr::Negative(inner) => {
311
- collect_json_predicate_placeholder_indexes(inner, indexes, lookup_field)
312
- }
313
- Expr::Cast(cast) => {
314
- collect_json_predicate_placeholder_indexes(&cast.expr, indexes, lookup_field)
315
- }
316
- Expr::TryCast(cast) => {
317
- collect_json_predicate_placeholder_indexes(&cast.expr, indexes, lookup_field)
318
- }
319
- Expr::ScalarFunction(function) => {
320
- for arg in &function.args {
321
- collect_json_predicate_placeholder_indexes(arg, indexes, lookup_field);
322
- }
323
- }
324
- Expr::Case(case) => {
325
- if let Some(expr) = &case.expr {
326
- collect_json_predicate_placeholder_indexes(expr, indexes, lookup_field);
327
- }
328
- for (when, then) in &case.when_then_expr {
329
- collect_json_predicate_placeholder_indexes(when, indexes, lookup_field);
330
- collect_json_predicate_placeholder_indexes(then, indexes, lookup_field);
331
- }
332
- if let Some(expr) = &case.else_expr {
333
- collect_json_predicate_placeholder_indexes(expr, indexes, lookup_field);
334
- }
335
- }
336
- _ => {}
337
- }
338
- }
339
-
340
- fn collect_placeholder_indexes(expr: &Expr, indexes: &mut BTreeSet<usize>) {
341
- if let Expr::Placeholder(placeholder) = expr {
342
- if let Some(index) = placeholder
343
- .id
344
- .strip_prefix('$')
345
- .and_then(|value| value.parse::<usize>().ok())
346
- {
347
- indexes.insert(index);
348
- }
349
- }
350
- }
351
-
352
- fn validate_binary_expr<'a>(
353
- binary: &'a BinaryExpr,
354
- lookup_field: &impl Fn(&datafusion::common::Column) -> Option<&'a Field>,
355
- ) -> Result<(), LixError> {
356
- validate_expr(&binary.left, lookup_field)?;
357
- validate_expr(&binary.right, lookup_field)?;
358
-
359
- if !is_comparison_operator(binary.op) {
360
- return Ok(());
361
- }
362
-
363
- validate_comparison_operands(&binary.left, &binary.right, lookup_field)
364
- }
365
-
366
- fn validate_in_list<'a>(
367
- in_list: &'a InList,
368
- lookup_field: &impl Fn(&datafusion::common::Column) -> Option<&'a Field>,
369
- ) -> Result<(), LixError> {
370
- validate_expr(&in_list.expr, lookup_field)?;
371
- for item in &in_list.list {
372
- validate_expr(item, lookup_field)?;
373
- }
374
-
375
- if is_json_expr(&in_list.expr, lookup_field) {
376
- for item in &in_list.list {
377
- require_json_comparison_operand(item, lookup_field)?;
378
- }
379
- }
380
-
381
- for item in &in_list.list {
382
- if is_json_expr(item, lookup_field) {
383
- require_json_comparison_operand(&in_list.expr, lookup_field)?;
384
- }
385
- }
386
-
387
- Ok(())
388
- }
389
-
390
- fn validate_between<'a>(
391
- between: &'a Between,
392
- lookup_field: &impl Fn(&datafusion::common::Column) -> Option<&'a Field>,
393
- ) -> Result<(), LixError> {
394
- validate_expr(&between.expr, lookup_field)?;
395
- validate_expr(&between.low, lookup_field)?;
396
- validate_expr(&between.high, lookup_field)?;
397
-
398
- if is_json_expr(&between.expr, lookup_field) {
399
- require_json_comparison_operand(&between.low, lookup_field)?;
400
- require_json_comparison_operand(&between.high, lookup_field)?;
401
- }
402
-
403
- Ok(())
404
- }
405
-
406
- fn validate_like<'a>(
407
- like: &'a Like,
408
- lookup_field: &impl Fn(&datafusion::common::Column) -> Option<&'a Field>,
409
- ) -> Result<(), LixError> {
410
- validate_expr(&like.expr, lookup_field)?;
411
- validate_expr(&like.pattern, lookup_field)?;
412
-
413
- if is_json_expr(&like.expr, lookup_field) {
414
- return Err(json_predicate_type_error(&like.expr));
415
- }
416
-
417
- Ok(())
418
- }
419
-
420
- fn validate_comparison_operands<'a>(
421
- left: &'a Expr,
422
- right: &'a Expr,
423
- lookup_field: &impl Fn(&datafusion::common::Column) -> Option<&'a Field>,
424
- ) -> Result<(), LixError> {
425
- let left_is_json = is_json_expr(left, lookup_field);
426
- let right_is_json = is_json_expr(right, lookup_field);
427
-
428
- if left_is_json {
429
- require_json_comparison_operand(right, lookup_field)?;
430
- }
431
- if right_is_json {
432
- require_json_comparison_operand(left, lookup_field)?;
433
- }
434
-
435
- Ok(())
436
- }
437
-
438
- fn require_json_comparison_operand<'a>(
439
- expr: &'a Expr,
440
- lookup_field: &impl Fn(&datafusion::common::Column) -> Option<&'a Field>,
441
- ) -> Result<(), LixError> {
442
- if is_json_expr(expr, lookup_field)
443
- || is_null_literal(expr)
444
- || matches!(expr, Expr::Placeholder(_))
445
- {
446
- return Ok(());
447
- }
448
-
449
- Err(json_predicate_type_error(expr))
450
- }
451
-
452
- fn is_json_expr<'a>(
453
- expr: &'a Expr,
454
- lookup_field: &impl Fn(&datafusion::common::Column) -> Option<&'a Field>,
455
- ) -> bool {
456
- match expr {
457
- Expr::Column(column) => lookup_field(column).is_some_and(field_is_json),
458
- Expr::Literal(_, Some(metadata)) => metadata
459
- .inner()
460
- .get(LIX_VALUE_TYPE_METADATA_KEY)
461
- .is_some_and(|value| value == LIX_VALUE_TYPE_JSON),
462
- Expr::ScalarFunction(function) => matches!(function.name(), "lix_json" | "lix_json_get"),
463
- Expr::Alias(alias) => is_json_expr(&alias.expr, lookup_field),
464
- Expr::Cast(cast) => is_json_expr(&cast.expr, lookup_field),
465
- Expr::TryCast(cast) => is_json_expr(&cast.expr, lookup_field),
466
- _ => false,
467
- }
468
- }
469
-
470
- fn is_identity_json_expr(expr: &Expr) -> bool {
471
- match expr {
472
- Expr::Column(column) => matches!(column.name.as_str(), "entity_pk" | "lixcol_entity_pk"),
473
- Expr::Alias(alias) => is_identity_json_expr(&alias.expr),
474
- Expr::Cast(cast) => is_identity_json_expr(&cast.expr),
475
- Expr::TryCast(cast) => is_identity_json_expr(&cast.expr),
476
- _ => false,
477
- }
478
- }
479
-
480
- fn is_null_literal(expr: &Expr) -> bool {
481
- matches!(expr, Expr::Literal(value, _) if matches!(value, ScalarValue::Null))
482
- }
483
-
484
- fn is_comparison_operator(op: Operator) -> bool {
485
- matches!(
486
- op,
487
- Operator::Eq
488
- | Operator::NotEq
489
- | Operator::Lt
490
- | Operator::LtEq
491
- | Operator::Gt
492
- | Operator::GtEq
493
- | Operator::IsDistinctFrom
494
- | Operator::IsNotDistinctFrom
495
- )
496
- }
497
-
498
- fn json_predicate_type_error(expr: &Expr) -> LixError {
499
- LixError::new(
500
- LixError::CODE_TYPE_MISMATCH,
501
- format!("JSON columns can only be compared with JSON expressions, got {expr}"),
502
- )
503
- .with_hint("Wrap JSON text with lix_json(...), use lix_json_get(...) for JSON values, or use IS NULL for null checks.")
504
- }