Skip to main content

operator/statement/
admin.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15mod event;
16mod layer;
17
18use std::sync::Arc;
19
20use common_function::function::FunctionContext;
21use common_function::function_registry::{FUNCTION_REGISTRY, get_admin_function};
22use common_query::Output;
23use common_recordbatch::{RecordBatch, RecordBatches};
24use common_sql::convert::sql_value_to_value;
25use common_telemetry::tracing;
26use common_time::Timezone;
27use datafusion_expr::TypeSignature;
28use datatypes::arrow::datatypes::DataType as ArrowDataType;
29use datatypes::data_type::DataType;
30use datatypes::prelude::ConcreteDataType;
31use datatypes::schema::{ColumnSchema, Schema};
32use datatypes::value::Value;
33use datatypes::vectors::VectorRef;
34pub use layer::{
35    AdminEventRecorderHandle, AdminFunctionLayer, AdminFunctionLayerRef,
36    AdminFunctionRecordingLayer,
37};
38use session::context::QueryContextRef;
39use snafu::{OptionExt, ResultExt, ensure};
40use sql::ast::{Expr, FunctionArg, FunctionArgExpr, FunctionArguments, Value as SqlValue};
41use sql::statements::admin::Admin;
42
43use crate::error::{self, CastSnafu, ExecuteAdminFunctionSnafu, Result};
44use crate::statement::StatementExecutor;
45
46const DUMMY_COLUMN: &str = "<dummy>";
47
48/// A request to execute one ADMIN function statement.
49#[derive(Clone)]
50pub struct AdminFunctionRequest {
51    /// The parsed ADMIN statement to execute.
52    pub statement: Admin,
53    /// The query context of the request.
54    pub query_ctx: QueryContextRef,
55}
56
57/// The client output and immediate typed result of one ADMIN function execution.
58pub struct AdminFunctionResponse {
59    /// The output returned to the client.
60    pub output: Output,
61    /// The typed immediate result exposed to outer layers.
62    pub immediate_result: Option<Value>,
63}
64
65/// Executes an ADMIN function request.
66#[async_trait::async_trait]
67pub trait AdminFunctionService: Send + Sync {
68    /// Executes an ADMIN function request.
69    async fn call(&self, request: AdminFunctionRequest) -> Result<AdminFunctionResponse>;
70}
71
72/// A shared ADMIN function service.
73pub type AdminFunctionServiceRef = Arc<dyn AdminFunctionService>;
74
75#[derive(Clone)]
76struct CoreAdminFunctionService {
77    query_engine: query::QueryEngineRef,
78}
79
80impl CoreAdminFunctionService {
81    fn new(query_engine: query::QueryEngineRef) -> Self {
82        Self { query_engine }
83    }
84
85    async fn execute(&self, request: AdminFunctionRequest) -> Result<AdminFunctionResponse> {
86        let AdminFunctionRequest {
87            statement: stmt,
88            query_ctx,
89        } = request;
90
91        let Admin::Func(func) = &stmt;
92        // the function name should be in lower case.
93        let func_name = func.name.to_string().to_lowercase();
94        let factory = get_admin_function(&func_name)
95            .or_else(|| FUNCTION_REGISTRY.get_function(&func_name))
96            .context(error::AdminFunctionNotFoundSnafu {
97                name: func_name.clone(),
98            })?;
99
100        let func_ctx = FunctionContext {
101            query_ctx: query_ctx.clone(),
102            state: self.query_engine.engine_state().function_state(),
103        };
104
105        let admin_udf = factory.provide(func_ctx);
106        let admin_async_fn = admin_udf
107            .as_async()
108            .context(error::AdminFunctionNotFoundSnafu { name: func_name })?;
109
110        let fn_name = admin_udf.name();
111        let signature = admin_udf.signature();
112
113        // Parse function arguments
114        let FunctionArguments::List(args) = &func.args else {
115            return error::BuildAdminFunctionArgsSnafu {
116                msg: format!("unsupported function args {} for {}", func.args, fn_name),
117            }
118            .fail();
119        };
120        let arg_values = args
121            .args
122            .iter()
123            .map(|arg| {
124                let FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(value))) = arg else {
125                    return error::BuildAdminFunctionArgsSnafu {
126                        msg: format!("unsupported function arg {arg} for {}", fn_name),
127                    }
128                    .fail();
129                };
130                Ok(&value.value)
131            })
132            .collect::<Result<Vec<_>>>()?;
133
134        let args = args_to_vector(&signature.type_signature, &arg_values, &query_ctx)?;
135        let arg_types = args
136            .iter()
137            .map(|arg| arg.data_type().as_arrow_type())
138            .collect::<Vec<_>>();
139        let ret_type = admin_udf.return_type(&arg_types).map_err(|e| {
140            error::Error::BuildAdminFunctionArgs {
141                msg: format!(
142                    "Failed to get return type of admin function {}: {}",
143                    fn_name, e
144                ),
145            }
146        })?;
147
148        // Convert arguments to DataFusion ColumnarValue format
149        let columnar_args: Vec<datafusion_expr::ColumnarValue> = args
150            .iter()
151            .map(|vector| datafusion_expr::ColumnarValue::Array(vector.to_arrow_array()))
152            .collect();
153
154        // Create ScalarFunctionArgs following the same pattern as udf.rs
155        let func_args = datafusion::logical_expr::ScalarFunctionArgs {
156            args: columnar_args,
157            arg_fields: args
158                .iter()
159                .enumerate()
160                .map(|(i, vector)| {
161                    Arc::new(arrow::datatypes::Field::new(
162                        format!("arg_{}", i),
163                        arg_types[i].clone(),
164                        vector.null_count() > 0,
165                    ))
166                })
167                .collect(),
168            return_field: Arc::new(arrow::datatypes::Field::new("result", ret_type, true)),
169            number_rows: if args.is_empty() { 1 } else { args[0].len() },
170            config_options: Arc::new(query_ctx.create_config_options()),
171        };
172
173        // Execute the async UDF
174        let result_columnar = admin_async_fn
175            .invoke_async_with_args(func_args)
176            .await
177            .with_context(|_| ExecuteAdminFunctionSnafu {
178                msg: fn_name.to_string(),
179            })?;
180
181        // Convert result back to VectorRef
182        let result_columnar: common_query::prelude::ColumnarValue =
183            (&result_columnar).try_into().context(CastSnafu)?;
184
185        let result_vector: VectorRef = result_columnar.try_into_vector(1).context(CastSnafu)?;
186        let immediate_result = immediate_result(&result_vector);
187
188        let column_schemas = vec![ColumnSchema::new(
189            // Use statement as the result column name
190            stmt.to_string(),
191            result_vector.data_type(),
192            false,
193        )];
194        let schema = Arc::new(Schema::new(column_schemas));
195        let batch = RecordBatch::new(schema.clone(), vec![result_vector])
196            .context(error::BuildRecordBatchSnafu)?;
197        let batches =
198            RecordBatches::try_new(schema, vec![batch]).context(error::BuildRecordBatchSnafu)?;
199
200        Ok(AdminFunctionResponse {
201            output: Output::new_with_record_batches(batches),
202            immediate_result,
203        })
204    }
205}
206
207fn immediate_result(result_vector: &VectorRef) -> Option<Value> {
208    (!result_vector.is_empty()).then(|| result_vector.get(0))
209}
210
211#[async_trait::async_trait]
212impl AdminFunctionService for CoreAdminFunctionService {
213    async fn call(&self, request: AdminFunctionRequest) -> Result<AdminFunctionResponse> {
214        self.execute(request).await
215    }
216}
217
218/// Creates the core ADMIN function service.
219pub(crate) fn new_admin_function_service(
220    query_engine: query::QueryEngineRef,
221) -> AdminFunctionServiceRef {
222    Arc::new(CoreAdminFunctionService::new(query_engine))
223}
224
225impl StatementExecutor {
226    /// Executes the [`Admin`] statement and returns the output.
227    #[tracing::instrument(skip_all)]
228    pub(crate) async fn execute_admin_command(
229        &self,
230        stmt: Admin,
231        query_ctx: QueryContextRef,
232    ) -> Result<Output> {
233        self.admin_function_service
234            .call(AdminFunctionRequest {
235                statement: stmt,
236                query_ctx,
237            })
238            .await
239            .map(|response| response.output)
240    }
241}
242
243/// Try to cast the arguments to vectors by function's signature.
244fn args_to_vector(
245    type_signature: &TypeSignature,
246    args: &Vec<&SqlValue>,
247    query_ctx: &QueryContextRef,
248) -> Result<Vec<VectorRef>> {
249    let tz = query_ctx.timezone();
250
251    match type_signature {
252        TypeSignature::Variadic(valid_types) => {
253            values_to_vectors_by_valid_types(valid_types, args, Some(&tz))
254        }
255
256        TypeSignature::Uniform(arity, valid_types) => {
257            ensure!(
258                *arity == args.len(),
259                error::FunctionArityMismatchSnafu {
260                    actual: args.len(),
261                    expected: *arity,
262                }
263            );
264
265            values_to_vectors_by_valid_types(valid_types, args, Some(&tz))
266        }
267
268        TypeSignature::Exact(data_types) => {
269            values_to_vectors_by_exact_types(data_types, args, Some(&tz))
270        }
271
272        TypeSignature::VariadicAny => {
273            let data_types = args
274                .iter()
275                .map(|value| try_get_data_type_for_sql_value(value))
276                .collect::<Result<Vec<_>>>()?;
277
278            values_to_vectors_by_exact_types(&data_types, args, Some(&tz))
279        }
280
281        TypeSignature::Any(arity) => {
282            ensure!(
283                *arity == args.len(),
284                error::FunctionArityMismatchSnafu {
285                    actual: args.len(),
286                    expected: *arity,
287                }
288            );
289
290            let data_types = args
291                .iter()
292                .map(|value| try_get_data_type_for_sql_value(value))
293                .collect::<Result<Vec<_>>>()?;
294
295            values_to_vectors_by_exact_types(&data_types, args, Some(&tz))
296        }
297
298        TypeSignature::OneOf(type_sigs) => {
299            for type_sig in type_sigs {
300                if let Ok(vectors) = args_to_vector(type_sig, args, query_ctx) {
301                    return Ok(vectors);
302                }
303            }
304
305            error::BuildAdminFunctionArgsSnafu {
306                msg: "function signature not match",
307            }
308            .fail()
309        }
310
311        _ => error::BuildAdminFunctionArgsSnafu {
312            msg: format!("unknown function type signature: {type_signature:?}"),
313        }
314        .fail(),
315    }
316}
317
318/// Try to cast sql values to vectors by exact data types.
319fn values_to_vectors_by_exact_types(
320    exact_types: &[ArrowDataType],
321    args: &[&SqlValue],
322    tz: Option<&Timezone>,
323) -> Result<Vec<VectorRef>> {
324    args.iter()
325        .zip(exact_types.iter())
326        .map(|(value, data_type)| {
327            let schema = ColumnSchema::new(
328                DUMMY_COLUMN,
329                ConcreteDataType::from_arrow_type(data_type),
330                true,
331            );
332            let value = sql_value_to_value(&schema, value, tz, None, false)
333                .context(error::SqlCommonSnafu)?;
334
335            Ok(value_to_vector(value))
336        })
337        .collect()
338}
339
340/// Try to cast sql values to vectors by valid data types.
341fn values_to_vectors_by_valid_types(
342    valid_types: &[ArrowDataType],
343    args: &[&SqlValue],
344    tz: Option<&Timezone>,
345) -> Result<Vec<VectorRef>> {
346    args.iter()
347        .map(|value| {
348            for data_type in valid_types {
349                let schema = ColumnSchema::new(
350                    DUMMY_COLUMN,
351                    ConcreteDataType::from_arrow_type(data_type),
352                    true,
353                );
354                if let Ok(value) = sql_value_to_value(&schema, value, tz, None, false) {
355                    return Ok(value_to_vector(value));
356                }
357            }
358
359            error::BuildAdminFunctionArgsSnafu {
360                msg: format!("failed to cast {value}"),
361            }
362            .fail()
363        })
364        .collect::<Result<Vec<_>>>()
365}
366
367/// Build a [`VectorRef`] from [`Value`]
368fn value_to_vector(value: Value) -> VectorRef {
369    let data_type = value.data_type();
370    let mut mutable_vector = data_type.create_mutable_vector(1);
371    mutable_vector.push_value_ref(&value.as_value_ref());
372
373    mutable_vector.to_vector()
374}
375
376/// Try to infer the data type from sql value.
377fn try_get_data_type_for_sql_value(value: &SqlValue) -> Result<ArrowDataType> {
378    match value {
379        SqlValue::Number(_, _) => Ok(ArrowDataType::Float64),
380        SqlValue::Null => Ok(ArrowDataType::Null),
381        SqlValue::Boolean(_) => Ok(ArrowDataType::Boolean),
382        SqlValue::HexStringLiteral(_)
383        | SqlValue::DoubleQuotedString(_)
384        | SqlValue::SingleQuotedString(_) => Ok(ArrowDataType::Utf8),
385        _ => error::BuildAdminFunctionArgsSnafu {
386            msg: format!("unsupported sql value: {value}"),
387        }
388        .fail(),
389    }
390}
391
392#[cfg(test)]
393mod tests {
394    use std::sync::Arc;
395
396    use datatypes::vectors::{Int32Vector, VectorRef};
397
398    use crate::statement::admin::immediate_result;
399
400    #[test]
401    fn empty_admin_function_result_has_no_immediate_value() {
402        let result_vector: VectorRef = Arc::new(Int32Vector::from(vec![]));
403
404        assert_eq!(immediate_result(&result_vector), None);
405    }
406}