Skip to main content

linkrs_query/executor/expression/functions/
table.rs

1//! Table function definitions.
2//!
3//! Table functions return rows of data instead of a single scalar value.
4
5use crate::executor::expression::{ExpressionError, ExpressionErrorType};
6use linkrs_core::Value;
7
8/// Table function trait for functions that return rows of data.
9///
10/// Unlike scalar functions that return a single `Value`, table functions
11/// return multiple rows, each row being a `Vec<Value>`.
12pub trait TableFunction: Send + Sync + std::fmt::Debug {
13    /// Function name
14    fn name(&self) -> &str;
15
16    /// Execute the table function with the given arguments.
17    /// Returns a vector of rows, where each row is a vector of values.
18    fn execute(&self, args: &[Value]) -> Result<Vec<Vec<Value>>, ExpressionError>;
19
20    /// Function description
21    fn description(&self) -> &str;
22}
23
24/// Built-in table function implementations.
25#[derive(Debug, Clone)]
26pub enum BuiltinTableFunction {
27    /// Read CSV files
28    ReadCsv,
29}
30
31impl TableFunction for BuiltinTableFunction {
32    fn name(&self) -> &str {
33        match self {
34            BuiltinTableFunction::ReadCsv => "read_csv",
35        }
36    }
37
38    fn execute(&self, args: &[Value]) -> Result<Vec<Vec<Value>>, ExpressionError> {
39        match self {
40            BuiltinTableFunction::ReadCsv => execute_read_csv(args),
41        }
42    }
43
44    fn description(&self) -> &str {
45        match self {
46            BuiltinTableFunction::ReadCsv => "Read a CSV file and return its rows",
47        }
48    }
49}
50
51fn execute_read_csv(args: &[Value]) -> Result<Vec<Vec<Value>>, ExpressionError> {
52    if args.is_empty() {
53        return Err(ExpressionError::new(
54            ExpressionErrorType::InvalidArgumentCount,
55            "read_csv expects at least 1 argument (path)".to_string(),
56        ));
57    }
58
59    let path = match &args[0] {
60        Value::String(s) => s.clone(),
61        other => {
62            return Err(ExpressionError::new(
63                ExpressionErrorType::TypeError,
64                format!("read_csv path must be a string, got {:?}", other),
65            ))
66        }
67    };
68
69    let delimiter = if args.len() > 1 {
70        match &args[1] {
71            Value::String(s) => s.as_bytes().first().copied().unwrap_or(b','),
72            _ => b',',
73        }
74    } else {
75        b','
76    };
77
78    let file = std::fs::File::open(&path).map_err(|e| {
79        ExpressionError::new(
80            ExpressionErrorType::FunctionExecutionError,
81            format!("read_csv failed to open '{path}': {e}"),
82        )
83    })?;
84
85    let mut reader = csv::ReaderBuilder::new()
86        .has_headers(true)
87        .delimiter(delimiter)
88        .trim(csv::Trim::All)
89        .flexible(true)
90        .from_reader(std::io::BufReader::new(file));
91
92    let mut rows: Vec<Vec<Value>> = Vec::new();
93    for result in reader.records() {
94        let record = result.map_err(|e| {
95            ExpressionError::new(
96                ExpressionErrorType::FunctionExecutionError,
97                format!("read_csv row error: {e}"),
98            )
99        })?;
100        let row: Vec<Value> = record.iter().map(|s| Value::String(s.into())).collect();
101        rows.push(row);
102    }
103
104    Ok(rows)
105}