1use crate::executor::expression::ExpressionError;
4use linkrs_core::value::list::List;
5use linkrs_core::value::NullType;
6use linkrs_core::Value;
7use serde_json::Value as JsonValue;
8use std::collections::HashMap;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum UtilityFunction {
13 Coalesce,
14 Hash,
15 JsonExtract,
16 JsonBuildObject,
17 JsonBuildArray,
18 JsonObjectKeys,
19 NullIf,
20 Greatest,
21 Least,
22 GenRandomUuid,
23 JsonEach,
24 JsonTypeOf,
25 JsonStripNulls,
26 IfNull,
27 TypeOf,
28 Version,
29 CurrentUser,
30 CurrentDatabase,
31 Corr,
32 CovarPop,
33 CovarSamp,
34 OctetLength,
35 Encode,
36 Decode,
37 UnionValue,
38 UnionTag,
39 UnionExtract,
40}
41
42impl UtilityFunction {
43 pub fn name(&self) -> &str {
44 match self {
45 UtilityFunction::Coalesce => "coalesce",
46 UtilityFunction::Hash => "hash",
47 UtilityFunction::JsonExtract => "json_extract",
48 UtilityFunction::JsonBuildObject => "json_build_object",
49 UtilityFunction::JsonBuildArray => "json_build_array",
50 UtilityFunction::JsonObjectKeys => "json_object_keys",
51 UtilityFunction::NullIf => "nullif",
52 UtilityFunction::Greatest => "greatest",
53 UtilityFunction::Least => "least",
54 UtilityFunction::GenRandomUuid => "gen_random_uuid",
55 UtilityFunction::JsonEach => "json_each",
56 UtilityFunction::JsonTypeOf => "json_typeof",
57 UtilityFunction::JsonStripNulls => "json_strip_nulls",
58 UtilityFunction::IfNull => "ifnull",
59 UtilityFunction::TypeOf => "typeof",
60 UtilityFunction::Version => "version",
61 UtilityFunction::CurrentUser => "current_user",
62 UtilityFunction::CurrentDatabase => "current_database",
63 UtilityFunction::Corr => "corr",
64 UtilityFunction::CovarPop => "covar_pop",
65 UtilityFunction::CovarSamp => "covar_samp",
66 UtilityFunction::OctetLength => "octet_length",
67 UtilityFunction::Encode => "encode",
68 UtilityFunction::Decode => "decode",
69 UtilityFunction::UnionValue => "union_value",
70 UtilityFunction::UnionTag => "union_tag",
71 UtilityFunction::UnionExtract => "union_extract",
72 }
73 }
74
75 pub fn arity(&self) -> usize {
76 match self {
77 UtilityFunction::Coalesce => 1,
78 UtilityFunction::Hash => 1,
79 UtilityFunction::JsonExtract => 2,
80 UtilityFunction::JsonBuildObject => 0,
81 UtilityFunction::JsonBuildArray => 0,
82 UtilityFunction::JsonObjectKeys => 1,
83 UtilityFunction::NullIf => 2,
84 UtilityFunction::Greatest => 2,
85 UtilityFunction::Least => 2,
86 UtilityFunction::GenRandomUuid => 0,
87 UtilityFunction::JsonEach => 1,
88 UtilityFunction::JsonTypeOf => 1,
89 UtilityFunction::JsonStripNulls => 1,
90 UtilityFunction::IfNull => 2,
91 UtilityFunction::TypeOf => 1,
92 UtilityFunction::Version => 0,
93 UtilityFunction::CurrentUser => 0,
94 UtilityFunction::CurrentDatabase => 0,
95 UtilityFunction::Corr => 2,
96 UtilityFunction::CovarPop => 2,
97 UtilityFunction::CovarSamp => 2,
98 UtilityFunction::OctetLength => 1,
99 UtilityFunction::Encode => 1,
100 UtilityFunction::Decode => 1,
101 UtilityFunction::UnionValue => 2,
102 UtilityFunction::UnionTag => 1,
103 UtilityFunction::UnionExtract => 1,
104 }
105 }
106
107 pub fn is_variadic(&self) -> bool {
108 matches!(
109 self,
110 UtilityFunction::Coalesce
111 | UtilityFunction::Greatest
112 | UtilityFunction::Least
113 | UtilityFunction::JsonBuildObject
114 | UtilityFunction::JsonBuildArray
115 )
116 }
117
118 pub fn description(&self) -> &str {
119 match self {
120 UtilityFunction::Coalesce => "Returns the first non-NULL value",
121 UtilityFunction::Hash => "Compute the hash",
122 UtilityFunction::JsonExtract => {
123 "Extract the value of a specified path from a JSON string"
124 }
125 UtilityFunction::JsonBuildObject => "Build a JSON object from key-value pairs",
126 UtilityFunction::JsonBuildArray => "Build a JSON array from a list of values",
127 UtilityFunction::JsonObjectKeys => "Get keys from a JSON object",
128 UtilityFunction::NullIf => "Return NULL if two values are equal",
129 UtilityFunction::Greatest => "Return the largest of the arguments",
130 UtilityFunction::Least => "Return the smallest of the arguments",
131 UtilityFunction::GenRandomUuid => "Generate a random UUID v4",
132 UtilityFunction::JsonEach => "Expand a JSON object into key-value pairs",
133 UtilityFunction::JsonTypeOf => "Return the type of a JSON value",
134 UtilityFunction::JsonStripNulls => "Strip null values from a JSON object or array",
135 UtilityFunction::IfNull => {
136 "Return first argument if not NULL, otherwise second argument"
137 }
138 UtilityFunction::TypeOf => "Return the type name of a value",
139 UtilityFunction::Version => "Return the Linkrs version string",
140 UtilityFunction::CurrentUser => "Return the current user name",
141 UtilityFunction::CurrentDatabase => "Return the current database name",
142 UtilityFunction::Corr => "Return the correlation coefficient of two lists",
143 UtilityFunction::CovarPop => "Return the population covariance of two lists",
144 UtilityFunction::CovarSamp => "Return the sample covariance of two lists",
145 UtilityFunction::OctetLength => "Return the number of bytes in a string or blob",
146 UtilityFunction::Encode => "Encode a string to blob using encoding (UTF8, HEX, BASE64)",
147 UtilityFunction::Decode => "Decode a blob to string using encoding (UTF8, HEX, BASE64)",
148 UtilityFunction::UnionValue => "Create a union value with a tag and a value",
149 UtilityFunction::UnionTag => "Get the tag of a union value",
150 UtilityFunction::UnionExtract => "Extract the value from a union value",
151 }
152 }
153
154 pub fn execute(&self, args: &[Value]) -> Result<Value, ExpressionError> {
155 if !self.is_variadic() && args.len() != self.arity() {
156 return Err(ExpressionError::invalid_arity(
157 self.name(),
158 self.arity(),
159 args.len(),
160 ));
161 }
162 match self {
163 UtilityFunction::Coalesce => execute_coalesce(args),
164 UtilityFunction::Hash => execute_hash(args),
165 UtilityFunction::JsonExtract => execute_json_extract(args),
166 UtilityFunction::JsonBuildObject => execute_json_build_object(args),
167 UtilityFunction::JsonBuildArray => execute_json_build_array(args),
168 UtilityFunction::JsonObjectKeys => execute_json_object_keys(args),
169 UtilityFunction::NullIf => execute_nullif(args),
170 UtilityFunction::Greatest => execute_greatest(args),
171 UtilityFunction::Least => execute_least(args),
172 UtilityFunction::GenRandomUuid => execute_gen_random_uuid(args),
173 UtilityFunction::JsonEach => execute_json_each(args),
174 UtilityFunction::JsonTypeOf => execute_json_typeof(args),
175 UtilityFunction::JsonStripNulls => execute_json_strip_nulls(args),
176 UtilityFunction::IfNull => execute_ifnull(args),
177 UtilityFunction::TypeOf => execute_typeof(args),
178 UtilityFunction::Version => execute_version(args),
179 UtilityFunction::CurrentUser => execute_current_user(args),
180 UtilityFunction::CurrentDatabase => execute_current_database(args),
181 UtilityFunction::Corr => execute_corr(args),
182 UtilityFunction::CovarPop => execute_covar_pop(args),
183 UtilityFunction::CovarSamp => execute_covar_samp(args),
184 UtilityFunction::OctetLength => execute_octet_length(args),
185 UtilityFunction::Encode => execute_encode(args),
186 UtilityFunction::Decode => execute_decode(args),
187 UtilityFunction::UnionValue => execute_union_value(args),
188 UtilityFunction::UnionTag => execute_union_tag(args),
189 UtilityFunction::UnionExtract => execute_union_extract(args),
190 }
191 }
192}
193
194fn execute_coalesce(args: &[Value]) -> Result<Value, ExpressionError> {
195 for arg in args {
196 match arg {
197 Value::Null(_) => continue,
198 other => return Ok(other.clone()),
199 }
200 }
201 Ok(Value::Null(NullType::Null))
202}
203
204fn execute_hash(args: &[Value]) -> Result<Value, ExpressionError> {
205 use std::collections::hash_map::DefaultHasher;
206 use std::hash::{Hash, Hasher};
207
208 match &args[0] {
209 Value::Null(_) => Ok(Value::Null(NullType::Null)),
210 Value::String(s) => {
211 let mut hasher = DefaultHasher::new();
212 s.hash(&mut hasher);
213 let hash_value = hasher.finish() as i64;
214 Ok(Value::BigInt(hash_value))
215 }
216 Value::Int(i) => {
217 let mut hasher = DefaultHasher::new();
218 i.hash(&mut hasher);
219 let hash_value = hasher.finish() as i64;
220 Ok(Value::BigInt(hash_value))
221 }
222 _ => Err(ExpressionError::type_error(
223 "The hash function requires a string or integer type",
224 )),
225 }
226}
227
228fn execute_json_extract(args: &[Value]) -> Result<Value, ExpressionError> {
229 if args[0].is_null() || args[1].is_null() {
230 return Ok(Value::Null(NullType::Null));
231 }
232 let json_value = arg_to_json_value(&args[0])?;
233 let path = match &args[1] {
234 Value::String(s) => s.as_str(),
235 _ => {
236 return Err(ExpressionError::type_error(
237 "json_extract path must be a string",
238 ))
239 }
240 };
241 let result = extract_json_value(&json_value, path);
242 Ok(json_to_value(result))
243}
244
245fn execute_json_build_object(args: &[Value]) -> Result<Value, ExpressionError> {
246 use serde_json::Map;
247
248 if !args.len().is_multiple_of(2) {
249 return Err(ExpressionError::type_error(
250 "json_build_object requires an even number of arguments (key-value pairs)",
251 ));
252 }
253
254 let mut map = Map::new();
255 for chunk in args.chunks(2) {
256 let key = match &chunk[0] {
257 Value::String(s) => s.to_string(),
258 Value::Null(_) => continue,
259 _ => {
260 return Err(ExpressionError::type_error(
261 "json_build_object keys must be strings",
262 ))
263 }
264 };
265 let value = value_to_json_value(&chunk[1]);
266 map.insert(key, value);
267 }
268
269 Ok(Value::string(
270 serde_json::to_string(&JsonValue::Object(map))
271 .map_err(|e| ExpressionError::type_error(format!("JSON serialization error: {}", e)))?,
272 ))
273}
274
275fn execute_json_build_array(args: &[Value]) -> Result<Value, ExpressionError> {
276 let arr: Vec<JsonValue> = args.iter().map(value_to_json_value).collect();
277 Ok(Value::string(
278 serde_json::to_string(&JsonValue::Array(arr))
279 .map_err(|e| ExpressionError::type_error(format!("JSON serialization error: {}", e)))?,
280 ))
281}
282
283fn execute_json_object_keys(args: &[Value]) -> Result<Value, ExpressionError> {
284 use linkrs_core::value::list::List;
285
286 if args[0].is_null() {
287 return Ok(Value::Null(NullType::Null));
288 }
289 let json_value = arg_to_json_value(&args[0])?;
290
291 match &json_value {
292 JsonValue::Object(map) => {
293 let keys: Vec<Value> = map.keys().map(|k| Value::string(k.clone())).collect();
294 Ok(Value::list(List { values: keys }))
295 }
296 _ => Err(ExpressionError::type_error(
297 "json_object_keys requires a JSON object",
298 )),
299 }
300}
301
302fn value_to_json_value(value: &Value) -> JsonValue {
303 match value {
304 Value::Null(_) => JsonValue::Null,
305 Value::Bool(b) => JsonValue::Bool(*b),
306 Value::SmallInt(i) => JsonValue::Number((*i).into()),
307 Value::Int(i) => JsonValue::Number((*i).into()),
308 Value::BigInt(i) => JsonValue::Number((*i).into()),
309 Value::Float(f) => JsonValue::Number(
310 serde_json::Number::from_f64(*f as f64).unwrap_or(serde_json::Number::from(0)),
311 ),
312 Value::Double(f) => JsonValue::Number(
313 serde_json::Number::from_f64(*f).unwrap_or(serde_json::Number::from(0)),
314 ),
315 Value::String(s) => JsonValue::String(s.to_string()),
316 Value::List(list) => {
317 JsonValue::Array(list.values.iter().map(value_to_json_value).collect())
318 }
319 Value::Map(map) => {
320 let obj: serde_json::Map<String, JsonValue> = map
321 .iter()
322 .map(|(k, v)| {
323 let key = match k {
326 Value::String(s) => s.to_string(),
327 other => format!("{}", other),
328 };
329 (key, value_to_json_value(v))
330 })
331 .collect();
332 JsonValue::Object(obj)
333 }
334 Value::Json(j) => j.to_value().unwrap_or(JsonValue::Null),
335 Value::JsonB(j) => j.as_value().clone(),
336 _ => JsonValue::Null,
337 }
338}
339
340fn arg_to_json_value(arg: &Value) -> Result<JsonValue, ExpressionError> {
342 match arg {
343 Value::Null(_) => Err(ExpressionError::type_error("Null JSON value")),
344 Value::String(s) => {
345 serde_json::from_str(s).map_err(|_| ExpressionError::type_error("Invalid JSON string"))
346 }
347 Value::Json(j) => j
348 .to_value()
349 .map_err(|e| ExpressionError::type_error(format!("Invalid JSON: {}", e))),
350 Value::JsonB(j) => Ok(j.as_value().clone()),
351 _ => Err(ExpressionError::type_error(
352 "Expected a JSON-compatible value (string, json, or jsonb)",
353 )),
354 }
355}
356
357fn extract_json_value<'a>(json: &'a JsonValue, path: &str) -> &'a JsonValue {
358 let parts: Vec<&str> = path.split('.').collect();
359 let mut current = json;
360
361 for part in parts {
362 if part.is_empty() {
363 continue;
364 }
365
366 current = match current {
367 JsonValue::Object(map) => map.get(part).unwrap_or(&JsonValue::Null),
368 JsonValue::Array(arr) => {
369 if let Ok(index) = part.parse::<usize>() {
370 arr.get(index).unwrap_or(&JsonValue::Null)
371 } else {
372 &JsonValue::Null
373 }
374 }
375 _ => &JsonValue::Null,
376 };
377 }
378
379 current
380}
381
382fn json_to_value(json: &JsonValue) -> Value {
383 match json {
384 JsonValue::Null => Value::Null(NullType::Null),
385 JsonValue::Bool(b) => Value::Bool(*b),
386 JsonValue::Number(n) => {
387 if let Some(i) = n.as_i64() {
388 Value::BigInt(i)
389 } else if let Some(f) = n.as_f64() {
390 Value::Double(f)
391 } else {
392 Value::Null(NullType::Null)
393 }
394 }
395 JsonValue::String(s) => Value::string(s.clone()),
396 JsonValue::Array(arr) => {
397 let values: Vec<Value> = arr.iter().map(json_to_value).collect();
398 Value::list(List { values })
399 }
400 JsonValue::Object(obj) => {
401 let map: std::collections::HashMap<Value, Value> = obj
402 .iter()
403 .map(|(k, v)| (Value::string(k.clone()), json_to_value(v)))
404 .collect();
405 Value::map(map)
406 }
407 }
408}
409
410fn execute_nullif(args: &[Value]) -> Result<Value, ExpressionError> {
411 match (&args[0], &args[1]) {
412 (Value::Null(_), _) | (_, Value::Null(_)) => Ok(Value::Null(NullType::Null)),
413 (a, b) => {
414 if values_equal(a, b) {
415 Ok(Value::Null(NullType::Null))
416 } else {
417 Ok(a.clone())
418 }
419 }
420 }
421}
422
423fn values_equal(a: &Value, b: &Value) -> bool {
424 match (a, b) {
425 (Value::Int(a), Value::BigInt(b)) => *a as i64 == *b,
426 (Value::BigInt(a), Value::Int(b)) => *a == *b as i64,
427 (Value::Float(a), Value::Double(b)) => *a as f64 == *b,
428 (Value::Double(a), Value::Float(b)) => *a == *b as f64,
429 _ => a == b,
430 }
431}
432
433fn values_less_than(a: &Value, b: &Value) -> bool {
434 match (a, b) {
435 (Value::Int(a), Value::Int(b)) => a < b,
436 (Value::BigInt(a), Value::BigInt(b)) => a < b,
437 (Value::Float(a), Value::Float(b)) => a < b,
438 (Value::Double(a), Value::Double(b)) => a < b,
439 (Value::String(a), Value::String(b)) => a < b,
440 (Value::Int(a), Value::BigInt(b)) => (*a as i64) < *b,
441 (Value::BigInt(a), Value::Int(b)) => *a < (*b as i64),
442 (Value::Float(a), Value::Double(b)) => (*a as f64) < *b,
443 (Value::Double(a), Value::Float(b)) => *a < (*b as f64),
444 _ => false,
445 }
446}
447
448fn execute_greatest(args: &[Value]) -> Result<Value, ExpressionError> {
449 if args.is_empty() {
450 return Err(ExpressionError::type_error(
451 "greatest requires at least 1 argument",
452 ));
453 }
454 let mut result = &args[0];
455 for arg in &args[1..] {
456 if values_less_than(result, arg) {
457 result = arg;
458 }
459 }
460 Ok(result.clone())
461}
462
463fn execute_least(args: &[Value]) -> Result<Value, ExpressionError> {
464 if args.is_empty() {
465 return Err(ExpressionError::type_error(
466 "least requires at least 1 argument",
467 ));
468 }
469 let mut result = &args[0];
470 for arg in &args[1..] {
471 if values_less_than(arg, result) {
472 result = arg;
473 }
474 }
475 Ok(result.clone())
476}
477
478fn execute_gen_random_uuid(_args: &[Value]) -> Result<Value, ExpressionError> {
479 use linkrs_core::value::uuid::UuidValue;
480 Ok(Value::Uuid(UuidValue::new_v4()))
481}
482
483fn execute_json_each(args: &[Value]) -> Result<Value, ExpressionError> {
484 if args[0].is_null() {
485 return Ok(Value::Null(NullType::Null));
486 }
487 let json_value = arg_to_json_value(&args[0])?;
488
489 match json_value {
490 JsonValue::Object(map) => {
491 let entries: Vec<Value> = map
492 .into_iter()
493 .map(|(k, v)| {
494 Value::list(List {
495 values: vec![Value::string(k), json_to_value(&v)],
496 })
497 })
498 .collect();
499 Ok(Value::list(List { values: entries }))
500 }
501 JsonValue::Array(arr) => {
502 let entries: Vec<Value> = arr
503 .into_iter()
504 .enumerate()
505 .map(|(i, v)| {
506 Value::list(List {
507 values: vec![Value::BigInt(i as i64), json_to_value(&v)],
508 })
509 })
510 .collect();
511 Ok(Value::list(List { values: entries }))
512 }
513 _ => Err(ExpressionError::type_error(
514 "json_each requires a JSON object or array",
515 )),
516 }
517}
518
519fn execute_json_typeof(args: &[Value]) -> Result<Value, ExpressionError> {
520 if args[0].is_null() {
521 return Ok(Value::Null(NullType::Null));
522 }
523 let json_value = arg_to_json_value(&args[0])?;
524
525 let type_str = match &json_value {
526 JsonValue::Null => "null",
527 JsonValue::Bool(_) => "boolean",
528 JsonValue::Number(_) => "number",
529 JsonValue::String(_) => "string",
530 JsonValue::Array(_) => "array",
531 JsonValue::Object(_) => "object",
532 };
533 Ok(Value::string(type_str))
534}
535
536fn execute_json_strip_nulls(args: &[Value]) -> Result<Value, ExpressionError> {
537 if args[0].is_null() {
538 return Ok(Value::Null(NullType::Null));
539 }
540 let json_value = arg_to_json_value(&args[0])?;
541 let stripped = strip_nulls_from_json(json_value);
542 let output = serde_json::to_string(&stripped)
543 .map_err(|e| ExpressionError::type_error(format!("JSON serialization error: {}", e)))?;
544 Ok(Value::string(output))
545}
546
547fn strip_nulls_from_json(value: JsonValue) -> JsonValue {
548 match value {
549 JsonValue::Object(map) => {
550 let cleaned: serde_json::Map<String, JsonValue> = map
551 .into_iter()
552 .filter(|(_, v)| !v.is_null())
553 .map(|(k, v)| (k, strip_nulls_from_json(v)))
554 .collect();
555 JsonValue::Object(cleaned)
556 }
557 JsonValue::Array(arr) => {
558 let cleaned: Vec<JsonValue> = arr
559 .into_iter()
560 .filter(|v| !v.is_null())
561 .map(strip_nulls_from_json)
562 .collect();
563 JsonValue::Array(cleaned)
564 }
565 other => other,
566 }
567}
568
569fn execute_ifnull(args: &[Value]) -> Result<Value, ExpressionError> {
570 match &args[0] {
571 Value::Null(_) => Ok(args[1].clone()),
572 other => Ok(other.clone()),
573 }
574}
575
576fn execute_typeof(args: &[Value]) -> Result<Value, ExpressionError> {
577 let type_name = match &args[0] {
578 Value::Null(_) => "null",
579 Value::Bool(_) => "bool",
580 Value::SmallInt(_) => "smallint",
581 Value::Int(_) => "int",
582 Value::BigInt(_) => "bigint",
583 Value::Float(_) => "float",
584 Value::Double(_) => "double",
585 Value::String(_) => "string",
586 Value::Date(_) => "date",
587 Value::Time(_) => "time",
588 Value::DateTime(_) => "datetime",
589 Value::List(_) => "list",
590 Value::Map(_) => "map",
591 Value::Set(_) => "set",
592 Value::Vertex(_) => "vertex",
593 Value::Edge(_) | Value::EdgeHeader(_) => "edge",
594 Value::Path(_) => "path",
595 Value::Uuid(_) => "uuid",
596 Value::Vector(_) => "vector",
597 Value::Geography(_) => "geography",
598 _ => "unknown",
599 };
600 Ok(Value::string(type_name))
601}
602
603fn execute_version(_args: &[Value]) -> Result<Value, ExpressionError> {
604 Ok(Value::string("Linkrs 0.1.0"))
605}
606
607fn execute_current_user(_args: &[Value]) -> Result<Value, ExpressionError> {
608 Ok(Value::string("root"))
609}
610
611fn execute_current_database(_args: &[Value]) -> Result<Value, ExpressionError> {
612 Ok(Value::string("default"))
613}
614
615fn extract_numeric_pairs(args: &[Value]) -> Result<(Vec<f64>, Vec<f64>), ExpressionError> {
616 let xs = match &args[0] {
617 Value::List(list) => list
618 .values
619 .iter()
620 .map(|v| match v {
621 Value::SmallInt(i) => Ok(*i as f64),
622 Value::Int(i) => Ok(*i as f64),
623 Value::BigInt(i) => Ok(*i as f64),
624 Value::Float(f) => Ok(*f as f64),
625 Value::Double(f) => Ok(*f),
626 Value::Null(_) => Ok(f64::NAN),
627 _ => Err(ExpressionError::type_error("Non-numeric value in list")),
628 })
629 .collect::<Result<Vec<f64>, _>>(),
630 Value::Null(_) => return Ok((vec![], vec![])),
631 _ => return Err(ExpressionError::type_error("First argument must be a list")),
632 }?;
633 let ys = match &args[1] {
634 Value::List(list) => list
635 .values
636 .iter()
637 .map(|v| match v {
638 Value::SmallInt(i) => Ok(*i as f64),
639 Value::Int(i) => Ok(*i as f64),
640 Value::BigInt(i) => Ok(*i as f64),
641 Value::Float(f) => Ok(*f as f64),
642 Value::Double(f) => Ok(*f),
643 Value::Null(_) => Ok(f64::NAN),
644 _ => Err(ExpressionError::type_error("Non-numeric value in list")),
645 })
646 .collect::<Result<Vec<f64>, _>>(),
647 Value::Null(_) => return Ok((vec![], vec![])),
648 _ => {
649 return Err(ExpressionError::type_error(
650 "Second argument must be a list",
651 ))
652 }
653 }?;
654 if xs.len() != ys.len() {
655 return Err(ExpressionError::type_error(
656 "Lists must have the same length",
657 ));
658 }
659 Ok((xs, ys))
660}
661
662fn execute_corr(args: &[Value]) -> Result<Value, ExpressionError> {
663 let (xs, ys) = extract_numeric_pairs(args)?;
664 if xs.is_empty() {
665 return Ok(Value::Null(NullType::Null));
666 }
667 let n = xs.len() as f64;
668 let sum_x: f64 = xs.iter().filter(|v| !v.is_nan()).sum();
669 let sum_y: f64 = ys.iter().filter(|v| !v.is_nan()).sum();
670 let sum_xy: f64 = xs
671 .iter()
672 .zip(ys.iter())
673 .filter(|(x, y)| !x.is_nan() && !y.is_nan())
674 .map(|(x, y)| x * y)
675 .sum();
676 let sum_x2: f64 = xs.iter().filter(|v| !v.is_nan()).map(|x| x * x).sum();
677 let sum_y2: f64 = ys.iter().filter(|v| !v.is_nan()).map(|y| y * y).sum();
678 let numerator = n * sum_xy - sum_x * sum_y;
679 let denom = (n * sum_x2 - sum_x * sum_x).sqrt() * (n * sum_y2 - sum_y * sum_y).sqrt();
680 if denom == 0.0 {
681 Ok(Value::Null(NullType::Null))
682 } else {
683 Ok(Value::Double(numerator / denom))
684 }
685}
686
687fn execute_covar_pop(args: &[Value]) -> Result<Value, ExpressionError> {
688 let (xs, ys) = extract_numeric_pairs(args)?;
689 if xs.is_empty() {
690 return Ok(Value::Null(NullType::Null));
691 }
692 let n = xs.len() as f64;
693 let mean_x: f64 = xs.iter().sum::<f64>() / n;
694 let mean_y: f64 = ys.iter().sum::<f64>() / n;
695 let covar: f64 = xs
696 .iter()
697 .zip(ys.iter())
698 .map(|(x, y)| (x - mean_x) * (y - mean_y))
699 .sum::<f64>()
700 / n;
701 Ok(Value::Double(covar))
702}
703
704fn execute_covar_samp(args: &[Value]) -> Result<Value, ExpressionError> {
705 let (xs, ys) = extract_numeric_pairs(args)?;
706 if xs.len() < 2 {
707 return Ok(Value::Null(NullType::Null));
708 }
709 let n = xs.len() as f64;
710 let mean_x: f64 = xs.iter().sum::<f64>() / n;
711 let mean_y: f64 = ys.iter().sum::<f64>() / n;
712 let covar: f64 = xs
713 .iter()
714 .zip(ys.iter())
715 .map(|(x, y)| (x - mean_x) * (y - mean_y))
716 .sum::<f64>()
717 / (n - 1.0);
718 Ok(Value::Double(covar))
719}
720
721fn execute_octet_length(args: &[Value]) -> Result<Value, ExpressionError> {
722 match &args[0] {
723 Value::String(s) => Ok(Value::BigInt(s.len() as i64)),
724 Value::Blob(b) => Ok(Value::BigInt(b.len() as i64)),
725 Value::Null(_) => Ok(Value::Null(NullType::Null)),
726 _ => Err(ExpressionError::type_error(
727 "octet_length requires a string or blob type",
728 )),
729 }
730}
731
732fn execute_encode(args: &[Value]) -> Result<Value, ExpressionError> {
733 match &args[0] {
734 Value::String(s) => Ok(Value::Blob(s.as_bytes().to_vec())),
735 Value::Null(_) => Ok(Value::Null(NullType::Null)),
736 _ => Err(ExpressionError::type_error("encode requires a string type")),
737 }
738}
739
740fn execute_decode(args: &[Value]) -> Result<Value, ExpressionError> {
741 match &args[0] {
742 Value::Blob(b) => {
743 let s = String::from_utf8(b.to_vec()).map_err(|_| {
744 ExpressionError::type_error("decode failed: invalid UTF-8 sequence")
745 })?;
746 Ok(Value::string(s))
747 }
748 Value::Null(_) => Ok(Value::Null(NullType::Null)),
749 _ => Err(ExpressionError::type_error("decode requires a blob type")),
750 }
751}
752
753fn execute_union_value(args: &[Value]) -> Result<Value, ExpressionError> {
754 let tag = match &args[0] {
755 Value::Int(i) => *i,
756 Value::SmallInt(i) => *i as i32,
757 Value::BigInt(i) => *i as i32,
758 Value::Null(_) => return Ok(Value::Null(NullType::Null)),
759 _ => {
760 return Err(ExpressionError::type_error(
761 "union_value first argument must be an integer tag",
762 ))
763 }
764 };
765 if tag < 0 {
766 return Err(ExpressionError::type_error(
767 "union tag must be non-negative",
768 ));
769 }
770 let value = args[1].clone();
771 let mut map = HashMap::new();
772 map.insert(Value::string("__tag"), Value::Int(tag));
773 map.insert(Value::string("__value"), value);
774 Ok(Value::map(map))
775}
776
777fn execute_union_tag(args: &[Value]) -> Result<Value, ExpressionError> {
778 match &args[0] {
779 Value::Map(m) => {
780 let tag_key = Value::string("__tag");
781 match m.get(&tag_key) {
782 Some(Value::Int(tag)) => Ok(Value::Int(*tag)),
783 Some(Value::SmallInt(tag)) => Ok(Value::Int(*tag as i32)),
784 Some(Value::BigInt(tag)) => Ok(Value::Int(*tag as i32)),
785 Some(_) => Err(ExpressionError::type_error("union tag must be an integer")),
786 None => Err(ExpressionError::type_error(
787 "not a union value: missing __tag",
788 )),
789 }
790 }
791 Value::Null(_) => Ok(Value::Null(NullType::Null)),
792 _ => Err(ExpressionError::type_error(
793 "union_tag requires a union value (map)",
794 )),
795 }
796}
797
798fn execute_union_extract(args: &[Value]) -> Result<Value, ExpressionError> {
799 match &args[0] {
800 Value::Map(m) => {
801 let value_key = Value::string("__value");
802 match m.get(&value_key) {
803 Some(v) => Ok(v.clone()),
804 None => Err(ExpressionError::type_error(
805 "not a union value: missing __value",
806 )),
807 }
808 }
809 Value::Null(_) => Ok(Value::Null(NullType::Null)),
810 _ => Err(ExpressionError::type_error(
811 "union_extract requires a union value (map)",
812 )),
813 }
814}
815
816#[cfg(test)]
817mod tests;