leaves12138 commented on code in PR #499:
URL: https://github.com/apache/paimon-rust/pull/499#discussion_r3563090053
##########
crates/integrations/datafusion/src/sql_context.rs:
##########
@@ -1430,6 +1442,165 @@ impl SQLContext {
}
}
+ async fn handle_create_function(
+ &self,
+ create_function: &CreateFunction,
+ ) -> DFResult<DataFrame> {
+ validate_persistent_create_function(create_function)?;
+ if create_function
+ .language
+ .as_ref()
+ .is_some_and(|language|
!language.value.eq_ignore_ascii_case("sql"))
+ {
+ return Err(DataFusionError::Plan(
+ "CREATE FUNCTION only supports LANGUAGE SQL".to_string(),
+ ));
+ }
+ if matches!(
+ create_function.behavior,
+ Some(FunctionBehavior::Stable | FunctionBehavior::Volatile)
+ ) {
+ return Err(DataFusionError::Plan(
+ "CREATE FUNCTION only supports deterministic SQL
functions".to_string(),
+ ));
+ }
+ let FunctionReturnType::DataType(return_type) = create_function
+ .return_type
+ .as_ref()
+ .ok_or_else(|| DataFusionError::Plan("CREATE FUNCTION requires
RETURNS".to_string()))?
+ else {
+ return Err(DataFusionError::Plan(
+ "CREATE FUNCTION SETOF return types are not
supported".to_string(),
+ ));
+ };
+ let CreateFunctionBody::Return(body) = create_function
+ .function_body
+ .as_ref()
+ .ok_or_else(|| DataFusionError::Plan("CREATE FUNCTION requires
RETURN".to_string()))?
+ else {
+ return Err(DataFusionError::Plan(
+ "CREATE FUNCTION only supports a RETURN
expression".to_string(),
+ ));
+ };
+
+ let mut parameter_names = HashSet::new();
+ let input_params = create_function
+ .args
+ .as_deref()
+ .unwrap_or_default()
+ .iter()
+ .enumerate()
+ .map(|(id, argument)| {
+ if argument.mode.is_some() || argument.default_expr.is_some() {
+ return Err(DataFusionError::Plan(
+ "CREATE FUNCTION argument modes and defaults are not
supported".to_string(),
+ ));
+ }
+ let name = argument
+ .name
+ .clone()
+ .map(normalize_create_function_argument_name)
+ .ok_or_else(|| {
+ DataFusionError::Plan(
+ "CREATE FUNCTION arguments must have
names".to_string(),
+ )
+ })?;
+ if !parameter_names.insert(name.clone()) {
+ return Err(DataFusionError::Plan(format!(
+ "duplicate function argument name '{name}'"
+ )));
+ }
+ Ok(PaimonDataField::new(
+ id as i32,
+ name,
+ sql_data_type_to_paimon_type(&argument.data_type, true)?,
+ ))
+ })
+ .collect::<DFResult<Vec<_>>>()?;
+ let return_params = vec![PaimonDataField::new(
+ 0,
+ "result".to_string(),
+ sql_data_type_to_paimon_type(return_type, true)?,
+ )];
+ let (catalog, catalog_name, identifier) =
+ self.resolve_catalog_and_function(&create_function.name)?;
+ let function = paimon::catalog::Function::new(
+ identifier,
+ Some(input_params),
+ Some(return_params),
+ true,
+ HashMap::from([(
+ "datafusion".to_string(),
+ paimon::catalog::FunctionDefinition::Sql {
+ definition: body.to_string(),
+ },
+ )]),
+ None,
+ HashMap::new(),
+ );
+ self.validate_create_function(&function, &catalog_name)
+ .await?;
+ catalog
+ .create_function(&function, create_function.if_not_exists)
+ .await
+ .map_err(to_datafusion_error)?;
+ ok_result(&self.ctx)
+ }
+
+ async fn validate_create_function(
+ &self,
+ function: &paimon::catalog::Function,
+ catalog_name: &str,
+ ) -> DFResult<()> {
+ let arguments = function
+ .input_params()
+ .unwrap_or_default()
+ .iter()
+ .map(|field| {
+ let value =
serde_json::to_value(field.data_type()).map_err(|error| {
+ DataFusionError::Plan(format!(
+ "Invalid CREATE FUNCTION argument type '{:?}':
{error}",
+ field.data_type()
+ ))
+ })?;
+ let sql_type = value.as_str().ok_or_else(|| {
Review Comment:
Complex signature types are accepted by the DDL parser and converted into
Paimon `DataType`s, but this assumes every serialized type is a JSON string.
`ARRAY`, `MAP`, and `ROW` serialize as structured JSON, so, for example,
`CREATE FUNCTION array_answer(x ARRAY<BIGINT>) RETURNS BIGINT RETURN 42` fails
here before the REST request is sent. The same assumption in `sql_function.rs`
also makes complex return types unusable (for example, `RETURNS
ARRAY<BIGINT>`). Please reuse/extract the recursive SQL type renderer currently
in `table/mod.rs` for both validation placeholders and return casts, or
explicitly reject and document unsupported complex signature types, with
regression tests for both argument and return types.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]