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