This is an automated email from the ASF dual-hosted git repository.

comphead pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion.git


The following commit(s) were added to refs/heads/main by this push:
     new 40d51580d4 doc-gen: migrate scalar functions (other, conditional, and 
struct) documentation (#14163)
40d51580d4 is described below

commit 40d51580d432e6f8b5f0cb661870febe727889d3
Author: Ian Lai <[email protected]>
AuthorDate: Sat Jan 18 00:42:47 2025 +0800

    doc-gen: migrate scalar functions (other, conditional, and struct) 
documentation (#14163)
---
 datafusion/functions/src/core/arrow_cast.rs   | 58 ++++++++---------
 datafusion/functions/src/core/arrowtypeof.rs  | 48 ++++++--------
 datafusion/functions/src/core/coalesce.rs     | 47 ++++++--------
 datafusion/functions/src/core/getfield.rs     | 94 ++++++++++++---------------
 datafusion/functions/src/core/greatest.rs     | 46 ++++++-------
 datafusion/functions/src/core/least.rs        | 46 ++++++-------
 datafusion/functions/src/core/named_struct.rs | 75 ++++++++++-----------
 datafusion/functions/src/core/nullif.rs       | 70 +++++++++-----------
 datafusion/functions/src/core/nvl.rs          | 70 +++++++++-----------
 datafusion/functions/src/core/nvl2.rs         | 77 ++++++++++------------
 datafusion/functions/src/core/struct.rs       | 93 ++++++++++++--------------
 datafusion/functions/src/core/version.rs      | 42 +++++-------
 12 files changed, 336 insertions(+), 430 deletions(-)

diff --git a/datafusion/functions/src/core/arrow_cast.rs 
b/datafusion/functions/src/core/arrow_cast.rs
index 3853737d7b..8e4ae36c9a 100644
--- a/datafusion/functions/src/core/arrow_cast.rs
+++ b/datafusion/functions/src/core/arrow_cast.rs
@@ -23,14 +23,13 @@ use datafusion_common::{
     ExprSchema, Result, ScalarValue,
 };
 use std::any::Any;
-use std::sync::OnceLock;
 
-use datafusion_expr::scalar_doc_sections::DOC_SECTION_OTHER;
 use datafusion_expr::simplify::{ExprSimplifyResult, SimplifyInfo};
 use datafusion_expr::{
     ColumnarValue, Documentation, Expr, ExprSchemable, ScalarUDFImpl, 
Signature,
     Volatility,
 };
+use datafusion_macros::user_doc;
 
 /// Implements casting to arbitrary arrow types (rather than SQL types)
 ///
@@ -53,6 +52,31 @@ use datafusion_expr::{
 /// ```sql
 /// select arrow_cast(column_x, 'Float64')
 /// ```
+#[user_doc(
+    doc_section(label = "Other Functions"),
+    description = "Casts a value to a specific Arrow data type.",
+    syntax_example = "arrow_cast(expression, datatype)",
+    sql_example = r#"```sql
+> select arrow_cast(-5, 'Int8') as a,
+  arrow_cast('foo', 'Dictionary(Int32, Utf8)') as b,
+  arrow_cast('bar', 'LargeUtf8') as c,
+  arrow_cast('2023-01-02T12:53:02', 'Timestamp(Microsecond, Some("+08:00"))') 
as d
+  ;
++----+-----+-----+---------------------------+
+| a  | b   | c   | d                         |
++----+-----+-----+---------------------------+
+| -5 | foo | bar | 2023-01-02T12:53:02+08:00 |
++----+-----+-----+---------------------------+
+```"#,
+    argument(
+        name = "expression",
+        description = "Expression to cast. The expression can be a constant, 
column, or function, and any combination of operators."
+    ),
+    argument(
+        name = "datatype",
+        description = "[Arrow data 
type](https://docs.rs/arrow/latest/arrow/datatypes/enum.DataType.html) name to 
cast to, as a string. The format is the same as that returned by 
[`arrow_typeof`]"
+    )
+)]
 #[derive(Debug)]
 pub struct ArrowCastFunc {
     signature: Signature,
@@ -139,38 +163,10 @@ impl ScalarUDFImpl for ArrowCastFunc {
     }
 
     fn documentation(&self) -> Option<&Documentation> {
-        Some(get_arrow_cast_doc())
+        self.doc()
     }
 }
 
-static DOCUMENTATION: OnceLock<Documentation> = OnceLock::new();
-
-fn get_arrow_cast_doc() -> &'static Documentation {
-    DOCUMENTATION.get_or_init(|| {
-        Documentation::builder(
-            DOC_SECTION_OTHER,
-            "Casts a value to a specific Arrow data type.",
-            "arrow_cast(expression, datatype)")
-            .with_sql_example(
-                r#"```sql
-> select arrow_cast(-5, 'Int8') as a,
-  arrow_cast('foo', 'Dictionary(Int32, Utf8)') as b,
-  arrow_cast('bar', 'LargeUtf8') as c,
-  arrow_cast('2023-01-02T12:53:02', 'Timestamp(Microsecond, Some("+08:00"))') 
as d
-  ;
-+----+-----+-----+---------------------------+
-| a  | b   | c   | d                         |
-+----+-----+-----+---------------------------+
-| -5 | foo | bar | 2023-01-02T12:53:02+08:00 |
-+----+-----+-----+---------------------------+
-```"#,
-            )
-            .with_argument("expression", "Expression to cast. The expression 
can be a constant, column, or function, and any combination of operators.")
-            .with_argument("datatype", "[Arrow data 
type](https://docs.rs/arrow/latest/arrow/datatypes/enum.DataType.html) name to 
cast to, as a string. The format is the same as that returned by 
[`arrow_typeof`]")
-            .build()
-    })
-}
-
 /// Returns the requested type from the arguments
 fn data_type_from_args(args: &[Expr]) -> Result<DataType> {
     if args.len() != 2 {
diff --git a/datafusion/functions/src/core/arrowtypeof.rs 
b/datafusion/functions/src/core/arrowtypeof.rs
index 54684c6f0b..8a282beb1d 100644
--- a/datafusion/functions/src/core/arrowtypeof.rs
+++ b/datafusion/functions/src/core/arrowtypeof.rs
@@ -17,12 +17,29 @@
 
 use arrow::datatypes::DataType;
 use datafusion_common::{exec_err, Result, ScalarValue};
-use datafusion_expr::scalar_doc_sections::DOC_SECTION_OTHER;
 use datafusion_expr::{ColumnarValue, Documentation};
 use datafusion_expr::{ScalarUDFImpl, Signature, Volatility};
+use datafusion_macros::user_doc;
 use std::any::Any;
-use std::sync::OnceLock;
 
+#[user_doc(
+    doc_section(label = "Other Functions"),
+    description = "Returns the name of the underlying [Arrow data 
type](https://docs.rs/arrow/latest/arrow/datatypes/enum.DataType.html) of the 
expression.",
+    syntax_example = "arrow_typeof(expression)",
+    sql_example = r#"```sql
+> select arrow_typeof('foo'), arrow_typeof(1);
++---------------------------+------------------------+
+| arrow_typeof(Utf8("foo")) | arrow_typeof(Int64(1)) |
++---------------------------+------------------------+
+| Utf8                      | Int64                  |
++---------------------------+------------------------+
+```
+"#,
+    argument(
+        name = "expression",
+        description = "Expression to evaluate. The expression can be a 
constant, column, or function, and any combination of operators."
+    )
+)]
 #[derive(Debug)]
 pub struct ArrowTypeOfFunc {
     signature: Signature,
@@ -77,31 +94,6 @@ impl ScalarUDFImpl for ArrowTypeOfFunc {
     }
 
     fn documentation(&self) -> Option<&Documentation> {
-        Some(get_arrowtypeof_doc())
+        self.doc()
     }
 }
-
-static DOCUMENTATION: OnceLock<Documentation> = OnceLock::new();
-
-fn get_arrowtypeof_doc() -> &'static Documentation {
-    DOCUMENTATION.get_or_init(|| {
-        Documentation::builder(
-            DOC_SECTION_OTHER,
-                "Returns the name of the underlying [Arrow data 
type](https://docs.rs/arrow/latest/arrow/datatypes/enum.DataType.html) of the 
expression.",
-
-            "arrow_typeof(expression)")
-            .with_sql_example(
-                r#"```sql
-> select arrow_typeof('foo'), arrow_typeof(1);
-+---------------------------+------------------------+
-| arrow_typeof(Utf8("foo")) | arrow_typeof(Int64(1)) |
-+---------------------------+------------------------+
-| Utf8                      | Int64                  |
-+---------------------------+------------------------+
-```
-"#,
-            )
-            .with_argument("expression", "Expression to evaluate. The 
expression can be a constant, column, or function, and any combination of 
operators.")
-            .build()
-    })
-}
diff --git a/datafusion/functions/src/core/coalesce.rs 
b/datafusion/functions/src/core/coalesce.rs
index 4f9e83fbf0..bfd69bab66 100644
--- a/datafusion/functions/src/core/coalesce.rs
+++ b/datafusion/functions/src/core/coalesce.rs
@@ -21,13 +21,29 @@ use arrow::compute::{and, is_not_null, is_null};
 use arrow::datatypes::DataType;
 use datafusion_common::{exec_err, ExprSchema, Result};
 use datafusion_expr::binary::try_type_union_resolution;
-use datafusion_expr::scalar_doc_sections::DOC_SECTION_CONDITIONAL;
 use datafusion_expr::{ColumnarValue, Documentation, Expr, ExprSchemable};
 use datafusion_expr::{ScalarUDFImpl, Signature, Volatility};
+use datafusion_macros::user_doc;
 use itertools::Itertools;
 use std::any::Any;
-use std::sync::OnceLock;
 
+#[user_doc(
+    doc_section(label = "Conditional Functions"),
+    description = "Returns the first of its arguments that is not _null_. 
Returns _null_ if all arguments are _null_. This function is often used to 
substitute a default value for _null_ values.",
+    syntax_example = "coalesce(expression1[, ..., expression_n])",
+    sql_example = r#"```sql
+> select coalesce(null, null, 'datafusion');
++----------------------------------------+
+| coalesce(NULL,NULL,Utf8("datafusion")) |
++----------------------------------------+
+| datafusion                             |
++----------------------------------------+
+```"#,
+    argument(
+        name = "expression1, expression_n",
+        description = "Expression to use if previous expressions are _null_. 
Can be a constant, column, or function, and any combination of arithmetic 
operators. Pass as many expression arguments as necessary."
+    )
+)]
 #[derive(Debug)]
 pub struct CoalesceFunc {
     signature: Signature,
@@ -146,35 +162,10 @@ impl ScalarUDFImpl for CoalesceFunc {
     }
 
     fn documentation(&self) -> Option<&Documentation> {
-        Some(get_coalesce_doc())
+        self.doc()
     }
 }
 
-static DOCUMENTATION: OnceLock<Documentation> = OnceLock::new();
-
-fn get_coalesce_doc() -> &'static Documentation {
-    DOCUMENTATION.get_or_init(|| {
-        Documentation::builder(
-            DOC_SECTION_CONDITIONAL,
-            "Returns the first of its arguments that is not _null_. Returns 
_null_ if all arguments are _null_. This function is often used to substitute a 
default value for _null_ values.",
-            "coalesce(expression1[, ..., expression_n])")
-            .with_sql_example(r#"```sql
-> select coalesce(null, null, 'datafusion');
-+----------------------------------------+
-| coalesce(NULL,NULL,Utf8("datafusion")) |
-+----------------------------------------+
-| datafusion                             |
-+----------------------------------------+
-```"#,
-            )
-            .with_argument(
-                "expression1, expression_n",
-                "Expression to use if previous expressions are _null_. Can be 
a constant, column, or function, and any combination of arithmetic operators. 
Pass as many expression arguments as necessary."
-            )
-            .build()
-    })
-}
-
 #[cfg(test)]
 mod test {
     use arrow::datatypes::DataType;
diff --git a/datafusion/functions/src/core/getfield.rs 
b/datafusion/functions/src/core/getfield.rs
index 5c8e1e803e..f9e700b5db 100644
--- a/datafusion/functions/src/core/getfield.rs
+++ b/datafusion/functions/src/core/getfield.rs
@@ -23,12 +23,52 @@ use datafusion_common::cast::{as_map_array, 
as_struct_array};
 use datafusion_common::{
     exec_err, plan_datafusion_err, plan_err, ExprSchema, Result, ScalarValue,
 };
-use datafusion_expr::scalar_doc_sections::DOC_SECTION_OTHER;
 use datafusion_expr::{ColumnarValue, Documentation, Expr, ExprSchemable};
 use datafusion_expr::{ScalarUDFImpl, Signature, Volatility};
+use datafusion_macros::user_doc;
 use std::any::Any;
-use std::sync::{Arc, OnceLock};
+use std::sync::Arc;
 
+#[user_doc(
+    doc_section(label = "Other Functions"),
+    description = r#"Returns a field within a map or a struct with the given 
key.
+    Note: most users invoke `get_field` indirectly via field access
+    syntax such as `my_struct_col['field_name']` which results in a call to
+    `get_field(my_struct_col, 'field_name')`."#,
+    syntax_example = "get_field(expression1, expression2)",
+    sql_example = r#"```sql
+> create table t (idx varchar, v varchar) as values ('data','fusion'), 
('apache', 'arrow');
+> select struct(idx, v) from t as c;
++-------------------------+
+| struct(c.idx,c.v)       |
++-------------------------+
+| {c0: data, c1: fusion}  |
+| {c0: apache, c1: arrow} |
++-------------------------+
+> select get_field((select struct(idx, v) from t), 'c0');
++-----------------------+
+| struct(t.idx,t.v)[c0] |
++-----------------------+
+| data                  |
+| apache                |
++-----------------------+
+> select get_field((select struct(idx, v) from t), 'c1');
++-----------------------+
+| struct(t.idx,t.v)[c1] |
++-----------------------+
+| fusion                |
+| arrow                 |
++-----------------------+
+```"#,
+    argument(
+        name = "expression1",
+        description = "The map or struct to retrieve a field for."
+    ),
+    argument(
+        name = "expression2",
+        description = "The field name in the map or struct to retrieve data 
for. Must evaluate to a string."
+    )
+)]
 #[derive(Debug)]
 pub struct GetFieldFunc {
     signature: Signature,
@@ -241,54 +281,6 @@ impl ScalarUDFImpl for GetFieldFunc {
     }
 
     fn documentation(&self) -> Option<&Documentation> {
-        Some(get_getfield_doc())
+        self.doc()
     }
 }
-
-static DOCUMENTATION: OnceLock<Documentation> = OnceLock::new();
-
-fn get_getfield_doc() -> &'static Documentation {
-    DOCUMENTATION.get_or_init(|| {
-        Documentation::builder(
-            DOC_SECTION_OTHER,
-            r#"Returns a field within a map or a struct with the given key.
-Note: most users invoke `get_field` indirectly via field access
-syntax such as `my_struct_col['field_name']` which results in a call to
-`get_field(my_struct_col, 'field_name')`."#,
-            "get_field(expression1, expression2)")
-            .with_sql_example(r#"```sql
-> create table t (idx varchar, v varchar) as values ('data','fusion'), 
('apache', 'arrow');
-> select struct(idx, v) from t as c;
-+-------------------------+
-| struct(c.idx,c.v)       |
-+-------------------------+
-| {c0: data, c1: fusion}  |
-| {c0: apache, c1: arrow} |
-+-------------------------+
-> select get_field((select struct(idx, v) from t), 'c0');
-+-----------------------+
-| struct(t.idx,t.v)[c0] |
-+-----------------------+
-| data                  |
-| apache                |
-+-----------------------+
-> select get_field((select struct(idx, v) from t), 'c1');
-+-----------------------+
-| struct(t.idx,t.v)[c1] |
-+-----------------------+
-| fusion                |
-| arrow                 |
-+-----------------------+
-```
- "#)
-            .with_argument(
-                "expression1",
-                "The map or struct to retrieve a field for."
-            )
-            .with_argument(
-                "expression2",
-                "The field name in the map or struct to retrieve data for. 
Must evaluate to a string."
-            )
-            .build()
-    })
-}
diff --git a/datafusion/functions/src/core/greatest.rs 
b/datafusion/functions/src/core/greatest.rs
index 654b2a2987..7ad8c73728 100644
--- a/datafusion/functions/src/core/greatest.rs
+++ b/datafusion/functions/src/core/greatest.rs
@@ -23,11 +23,10 @@ use arrow::datatypes::DataType;
 use arrow_buffer::BooleanBuffer;
 use datafusion_common::{internal_err, Result, ScalarValue};
 use datafusion_doc::Documentation;
-use datafusion_expr::scalar_doc_sections::DOC_SECTION_CONDITIONAL;
 use datafusion_expr::ColumnarValue;
 use datafusion_expr::{ScalarUDFImpl, Signature, Volatility};
+use datafusion_macros::user_doc;
 use std::any::Any;
-use std::sync::OnceLock;
 
 const SORT_OPTIONS: SortOptions = SortOptions {
     // We want greatest first
@@ -37,6 +36,23 @@ const SORT_OPTIONS: SortOptions = SortOptions {
     nulls_first: true,
 };
 
+#[user_doc(
+    doc_section(label = "Conditional Functions"),
+    description = "Returns the greatest value in a list of expressions. 
Returns _null_ if all expressions are _null_.",
+    syntax_example = "greatest(expression1[, ..., expression_n])",
+    sql_example = r#"```sql
+> select greatest(4, 7, 5);
++---------------------------+
+| greatest(4,7,5)           |
++---------------------------+
+| 7                         |
++---------------------------+
+```"#,
+    argument(
+        name = "expression1, expression_n",
+        description = "Expressions to compare and return the greatest value.. 
Can be a constant, column, or function, and any combination of arithmetic 
operators. Pass as many expression arguments as necessary."
+    )
+)]
 #[derive(Debug)]
 pub struct GreatestFunc {
     signature: Signature,
@@ -139,33 +155,9 @@ impl ScalarUDFImpl for GreatestFunc {
     }
 
     fn documentation(&self) -> Option<&Documentation> {
-        Some(get_greatest_doc())
+        self.doc()
     }
 }
-static DOCUMENTATION: OnceLock<Documentation> = OnceLock::new();
-
-fn get_greatest_doc() -> &'static Documentation {
-    DOCUMENTATION.get_or_init(|| {
-        Documentation::builder(
-            DOC_SECTION_CONDITIONAL,
-            "Returns the greatest value in a list of expressions. Returns 
_null_ if all expressions are _null_.",
-            "greatest(expression1[, ..., expression_n])")
-            .with_sql_example(r#"```sql
-> select greatest(4, 7, 5);
-+---------------------------+
-| greatest(4,7,5)           |
-+---------------------------+
-| 7                         |
-+---------------------------+
-```"#,
-            )
-            .with_argument(
-                "expression1, expression_n",
-                "Expressions to compare and return the greatest value.. Can be 
a constant, column, or function, and any combination of arithmetic operators. 
Pass as many expression arguments as necessary."
-            )
-            .build()
-    })
-}
 
 #[cfg(test)]
 mod test {
diff --git a/datafusion/functions/src/core/least.rs 
b/datafusion/functions/src/core/least.rs
index 0850907277..02299feb9b 100644
--- a/datafusion/functions/src/core/least.rs
+++ b/datafusion/functions/src/core/least.rs
@@ -23,11 +23,10 @@ use arrow::datatypes::DataType;
 use arrow_buffer::BooleanBuffer;
 use datafusion_common::{internal_err, Result, ScalarValue};
 use datafusion_doc::Documentation;
-use datafusion_expr::scalar_doc_sections::DOC_SECTION_CONDITIONAL;
 use datafusion_expr::ColumnarValue;
 use datafusion_expr::{ScalarUDFImpl, Signature, Volatility};
+use datafusion_macros::user_doc;
 use std::any::Any;
-use std::sync::OnceLock;
 
 const SORT_OPTIONS: SortOptions = SortOptions {
     // Having the smallest result first
@@ -37,6 +36,23 @@ const SORT_OPTIONS: SortOptions = SortOptions {
     nulls_first: false,
 };
 
+#[user_doc(
+    doc_section(label = "Conditional Functions"),
+    description = "Returns the smallest value in a list of expressions. 
Returns _null_ if all expressions are _null_.",
+    syntax_example = "least(expression1[, ..., expression_n])",
+    sql_example = r#"```sql
+> select least(4, 7, 5);
++---------------------------+
+| least(4,7,5)              |
++---------------------------+
+| 4                         |
++---------------------------+
+```"#,
+    argument(
+        name = "expression1, expression_n",
+        description = "Expressions to compare and return the smallest value. 
Can be a constant, column, or function, and any combination of arithmetic 
operators. Pass as many expression arguments as necessary."
+    )
+)]
 #[derive(Debug)]
 pub struct LeastFunc {
     signature: Signature,
@@ -152,33 +168,9 @@ impl ScalarUDFImpl for LeastFunc {
     }
 
     fn documentation(&self) -> Option<&Documentation> {
-        Some(get_smallest_doc())
+        self.doc()
     }
 }
-static DOCUMENTATION: OnceLock<Documentation> = OnceLock::new();
-
-fn get_smallest_doc() -> &'static Documentation {
-    DOCUMENTATION.get_or_init(|| {
-        Documentation::builder(
-            DOC_SECTION_CONDITIONAL,
-            "Returns the smallest value in a list of expressions. Returns 
_null_ if all expressions are _null_.",
-            "least(expression1[, ..., expression_n])")
-            .with_sql_example(r#"```sql
-> select least(4, 7, 5);
-+---------------------------+
-| least(4,7,5)              |
-+---------------------------+
-| 4                         |
-+---------------------------+
-```"#,
-            )
-            .with_argument(
-                "expression1, expression_n",
-                "Expressions to compare and return the smallest value. Can be 
a constant, column, or function, and any combination of arithmetic operators. 
Pass as many expression arguments as necessary."
-            )
-            .build()
-    })
-}
 
 #[cfg(test)]
 mod test {
diff --git a/datafusion/functions/src/core/named_struct.rs 
b/datafusion/functions/src/core/named_struct.rs
index 556cad1de1..527ec48fcc 100644
--- a/datafusion/functions/src/core/named_struct.rs
+++ b/datafusion/functions/src/core/named_struct.rs
@@ -18,11 +18,11 @@
 use arrow::array::StructArray;
 use arrow::datatypes::{DataType, Field, Fields};
 use datafusion_common::{exec_err, internal_err, HashSet, Result, ScalarValue};
-use datafusion_expr::scalar_doc_sections::DOC_SECTION_STRUCT;
 use datafusion_expr::{ColumnarValue, Documentation, Expr, ExprSchemable};
 use datafusion_expr::{ScalarUDFImpl, Signature, Volatility};
+use datafusion_macros::user_doc;
 use std::any::Any;
-use std::sync::{Arc, OnceLock};
+use std::sync::Arc;
 
 /// Put values in a struct array.
 fn named_struct_expr(args: &[ColumnarValue]) -> Result<ColumnarValue> {
@@ -83,6 +83,38 @@ fn named_struct_expr(args: &[ColumnarValue]) -> 
Result<ColumnarValue> {
     Ok(ColumnarValue::Array(Arc::new(struct_array)))
 }
 
+#[user_doc(
+    doc_section(label = "Struct Functions"),
+    description = "Returns an Arrow struct using the specified name and input 
expressions pairs.",
+    syntax_example = "named_struct(expression1_name, expression1_input[, ..., 
expression_n_name, expression_n_input])",
+    sql_example = r#"
+For example, this query converts two columns `a` and `b` to a single column 
with
+a struct type of fields `field_a` and `field_b`:
+```sql
+> select * from t;
++---+---+
+| a | b |
++---+---+
+| 1 | 2 |
+| 3 | 4 |
++---+---+
+> select named_struct('field_a', a, 'field_b', b) from t;
++-------------------------------------------------------+
+| named_struct(Utf8("field_a"),t.a,Utf8("field_b"),t.b) |
++-------------------------------------------------------+
+| {field_a: 1, field_b: 2}                              |
+| {field_a: 3, field_b: 4}                              |
++-------------------------------------------------------+
+```"#,
+    argument(
+        name = "expression_n_name",
+        description = "Name of the column field. Must be a constant string."
+    ),
+    argument(
+        name = "expression_n_input",
+        description = "Expression to include in the output struct. Can be a 
constant, column, or function, and any combination of arithmetic or string 
operators."
+    )
+)]
 #[derive(Debug)]
 pub struct NamedStructFunc {
     signature: Signature,
@@ -167,43 +199,6 @@ impl ScalarUDFImpl for NamedStructFunc {
     }
 
     fn documentation(&self) -> Option<&Documentation> {
-        Some(get_named_struct_doc())
+        self.doc()
     }
 }
-
-static DOCUMENTATION: OnceLock<Documentation> = OnceLock::new();
-
-fn get_named_struct_doc() -> &'static Documentation {
-    DOCUMENTATION.get_or_init(|| {
-        Documentation::builder(
-            DOC_SECTION_STRUCT,
-            "Returns an Arrow struct using the specified name and input 
expressions pairs.",
-            "named_struct(expression1_name, expression1_input[, ..., 
expression_n_name, expression_n_input])")
-            .with_sql_example(r#"
-For example, this query converts two columns `a` and `b` to a single column 
with
-a struct type of fields `field_a` and `field_b`:
-```sql
-> select * from t;
-+---+---+
-| a | b |
-+---+---+
-| 1 | 2 |
-| 3 | 4 |
-+---+---+
-> select named_struct('field_a', a, 'field_b', b) from t;
-+-------------------------------------------------------+
-| named_struct(Utf8("field_a"),t.a,Utf8("field_b"),t.b) |
-+-------------------------------------------------------+
-| {field_a: 1, field_b: 2}                              |
-| {field_a: 3, field_b: 4}                              |
-+-------------------------------------------------------+
-```
-"#)
-            .with_argument(
-                "expression_n_name",
-                "Name of the column field. Must be a constant string."
-            )
-            .with_argument("expression_n_input", "Expression to include in the 
output struct. Can be a constant, column, or function, and any combination of 
arithmetic or string operators.")
-            .build()
-    })
-}
diff --git a/datafusion/functions/src/core/nullif.rs 
b/datafusion/functions/src/core/nullif.rs
index 7c86047a02..9dd4898267 100644
--- a/datafusion/functions/src/core/nullif.rs
+++ b/datafusion/functions/src/core/nullif.rs
@@ -22,11 +22,37 @@ use datafusion_expr::{ColumnarValue, Documentation};
 use arrow::compute::kernels::cmp::eq;
 use arrow::compute::kernels::nullif::nullif;
 use datafusion_common::ScalarValue;
-use datafusion_expr::scalar_doc_sections::DOC_SECTION_CONDITIONAL;
 use datafusion_expr::{ScalarUDFImpl, Signature, Volatility};
+use datafusion_macros::user_doc;
 use std::any::Any;
-use std::sync::OnceLock;
-
+#[user_doc(
+    doc_section(label = "Conditional Functions"),
+    description = "Returns _null_ if _expression1_ equals _expression2_; 
otherwise it returns _expression1_.
+This can be used to perform the inverse operation of [`coalesce`](#coalesce).",
+    syntax_example = "nullif(expression1, expression2)",
+    sql_example = r#"```sql
+> select nullif('datafusion', 'data');
++-----------------------------------------+
+| nullif(Utf8("datafusion"),Utf8("data")) |
++-----------------------------------------+
+| datafusion                              |
++-----------------------------------------+
+> select nullif('datafusion', 'datafusion');
++-----------------------------------------------+
+| nullif(Utf8("datafusion"),Utf8("datafusion")) |
++-----------------------------------------------+
+|                                               |
++-----------------------------------------------+
+```"#,
+    argument(
+        name = "expression1",
+        description = "Expression to compare and return if equal to 
expression2. Can be a constant, column, or function, and any combination of 
operators."
+    ),
+    argument(
+        name = "expression2",
+        description = "Expression to compare to expression1. Can be a 
constant, column, or function, and any combination of operators."
+    )
+)]
 #[derive(Debug)]
 pub struct NullIfFunc {
     signature: Signature,
@@ -84,46 +110,10 @@ impl ScalarUDFImpl for NullIfFunc {
     }
 
     fn documentation(&self) -> Option<&Documentation> {
-        Some(get_nullif_doc())
+        self.doc()
     }
 }
 
-static DOCUMENTATION: OnceLock<Documentation> = OnceLock::new();
-
-fn get_nullif_doc() -> &'static Documentation {
-    DOCUMENTATION.get_or_init(|| {
-        Documentation::builder(
-            DOC_SECTION_CONDITIONAL,
-            "Returns _null_ if _expression1_ equals _expression2_; otherwise 
it returns _expression1_.
-This can be used to perform the inverse operation of [`coalesce`](#coalesce).",
-            "nullif(expression1, expression2)")
-            .with_sql_example(r#"```sql
-> select nullif('datafusion', 'data');
-+-----------------------------------------+
-| nullif(Utf8("datafusion"),Utf8("data")) |
-+-----------------------------------------+
-| datafusion                              |
-+-----------------------------------------+
-> select nullif('datafusion', 'datafusion');
-+-----------------------------------------------+
-| nullif(Utf8("datafusion"),Utf8("datafusion")) |
-+-----------------------------------------------+
-|                                               |
-+-----------------------------------------------+
-```
-"#)
-            .with_argument(
-                "expression1",
-                "Expression to compare and return if equal to expression2. Can 
be a constant, column, or function, and any combination of operators."
-            )
-            .with_argument(
-                "expression2",
-                "Expression to compare to expression1. Can be a constant, 
column, or function, and any combination of operators."
-            )
-            .build()
-    })
-}
-
 /// Implements NULLIF(expr1, expr2)
 /// Args: 0 - left expr is any array
 ///       1 - if the left is equal to this expr2, then the result is NULL, 
otherwise left value is passed.
diff --git a/datafusion/functions/src/core/nvl.rs 
b/datafusion/functions/src/core/nvl.rs
index eb8c926060..bcd4b3908d 100644
--- a/datafusion/functions/src/core/nvl.rs
+++ b/datafusion/functions/src/core/nvl.rs
@@ -20,12 +20,39 @@ use arrow::compute::is_not_null;
 use arrow::compute::kernels::zip::zip;
 use arrow::datatypes::DataType;
 use datafusion_common::{internal_err, Result};
-use datafusion_expr::scalar_doc_sections::DOC_SECTION_CONDITIONAL;
 use datafusion_expr::{
     ColumnarValue, Documentation, ScalarUDFImpl, Signature, Volatility,
 };
-use std::sync::{Arc, OnceLock};
-
+use datafusion_macros::user_doc;
+use std::sync::Arc;
+#[user_doc(
+    doc_section(label = "Conditional Functions"),
+    description = "Returns _expression2_ if _expression1_ is NULL otherwise it 
returns _expression1_.",
+    syntax_example = "nvl(expression1, expression2)",
+    sql_example = r#"```sql
+> select nvl(null, 'a');
++---------------------+
+| nvl(NULL,Utf8("a")) |
++---------------------+
+| a                   |
++---------------------+\
+> select nvl('b', 'a');
++--------------------------+
+| nvl(Utf8("b"),Utf8("a")) |
++--------------------------+
+| b                        |
++--------------------------+
+```
+"#,
+    argument(
+        name = "expression1",
+        description = "Expression to return if not null. Can be a constant, 
column, or function, and any combination of operators."
+    ),
+    argument(
+        name = "expression2",
+        description = "Expression to return if expr1 is null. Can be a 
constant, column, or function, and any combination of operators."
+    )
+)]
 #[derive(Debug)]
 pub struct NVLFunc {
     signature: Signature,
@@ -101,45 +128,10 @@ impl ScalarUDFImpl for NVLFunc {
     }
 
     fn documentation(&self) -> Option<&Documentation> {
-        Some(get_nvl_doc())
+        self.doc()
     }
 }
 
-static DOCUMENTATION: OnceLock<Documentation> = OnceLock::new();
-
-fn get_nvl_doc() -> &'static Documentation {
-    DOCUMENTATION.get_or_init(|| {
-        Documentation::builder(
-            DOC_SECTION_CONDITIONAL,
-            "Returns _expression2_ if _expression1_ is NULL otherwise it 
returns _expression1_.",
-            "nvl(expression1, expression2)")
-            .with_sql_example(r#"```sql
-> select nvl(null, 'a');
-+---------------------+
-| nvl(NULL,Utf8("a")) |
-+---------------------+
-| a                   |
-+---------------------+\
-> select nvl('b', 'a');
-+--------------------------+
-| nvl(Utf8("b"),Utf8("a")) |
-+--------------------------+
-| b                        |
-+--------------------------+
-```
-"#)
-            .with_argument(
-                "expression1",
-                "Expression to return if not null. Can be a constant, column, 
or function, and any combination of operators."
-            )
-            .with_argument(
-                "expression2",
-                "Expression to return if expr1 is null. Can be a constant, 
column, or function, and any combination of operators."
-            )
-            .build()
-    })
-}
-
 fn nvl_func(args: &[ColumnarValue]) -> Result<ColumnarValue> {
     if args.len() != 2 {
         return internal_err!(
diff --git a/datafusion/functions/src/core/nvl2.rs 
b/datafusion/functions/src/core/nvl2.rs
index a372926832..70b762a49c 100644
--- a/datafusion/functions/src/core/nvl2.rs
+++ b/datafusion/functions/src/core/nvl2.rs
@@ -20,13 +20,45 @@ use arrow::compute::is_not_null;
 use arrow::compute::kernels::zip::zip;
 use arrow::datatypes::DataType;
 use datafusion_common::{exec_err, internal_err, Result};
-use datafusion_expr::scalar_doc_sections::DOC_SECTION_CONDITIONAL;
 use datafusion_expr::{
     type_coercion::binary::comparison_coercion, ColumnarValue, Documentation,
     ScalarUDFImpl, Signature, Volatility,
 };
-use std::sync::{Arc, OnceLock};
+use datafusion_macros::user_doc;
+use std::sync::Arc;
 
+#[user_doc(
+    doc_section(label = "Conditional Functions"),
+    description = "Returns _expression2_ if _expression1_ is not NULL; 
otherwise it returns _expression3_.",
+    syntax_example = "nvl2(expression1, expression2, expression3)",
+    sql_example = r#"```sql
+> select nvl2(null, 'a', 'b');
++--------------------------------+
+| nvl2(NULL,Utf8("a"),Utf8("b")) |
++--------------------------------+
+| b                              |
++--------------------------------+
+> select nvl2('data', 'a', 'b');
++----------------------------------------+
+| nvl2(Utf8("data"),Utf8("a"),Utf8("b")) |
++----------------------------------------+
+| a                                      |
++----------------------------------------+
+```
+"#,
+    argument(
+        name = "expression1",
+        description = "Expression to test for null. Can be a constant, column, 
or function, and any combination of operators."
+    ),
+    argument(
+        name = "expression2",
+        description = "Expression to return if expr1 is not null. Can be a 
constant, column, or function, and any combination of operators."
+    ),
+    argument(
+        name = "expression3",
+        description = "Expression to return if expr1 is null. Can be a 
constant, column, or function, and any combination of operators."
+    )
+)]
 #[derive(Debug)]
 pub struct NVL2Func {
     signature: Signature,
@@ -97,49 +129,10 @@ impl ScalarUDFImpl for NVL2Func {
     }
 
     fn documentation(&self) -> Option<&Documentation> {
-        Some(get_nvl2_doc())
+        self.doc()
     }
 }
 
-static DOCUMENTATION: OnceLock<Documentation> = OnceLock::new();
-
-fn get_nvl2_doc() -> &'static Documentation {
-    DOCUMENTATION.get_or_init(|| {
-        Documentation::builder(
-            DOC_SECTION_CONDITIONAL,
-            "Returns _expression2_ if _expression1_ is not NULL; otherwise it 
returns _expression3_.",
-            "nvl2(expression1, expression2, expression3)")
-            .with_sql_example(r#"```sql
-> select nvl2(null, 'a', 'b');
-+--------------------------------+
-| nvl2(NULL,Utf8("a"),Utf8("b")) |
-+--------------------------------+
-| b                              |
-+--------------------------------+
-> select nvl2('data', 'a', 'b');
-+----------------------------------------+
-| nvl2(Utf8("data"),Utf8("a"),Utf8("b")) |
-+----------------------------------------+
-| a                                      |
-+----------------------------------------+
-```
-"#)
-            .with_argument(
-                "expression1",
-                "Expression to test for null. Can be a constant, column, or 
function, and any combination of operators."
-            )
-            .with_argument(
-                "expression2",
-                "Expression to return if expr1 is not null. Can be a constant, 
column, or function, and any combination of operators."
-            )
-            .with_argument(
-                "expression3",
-                "Expression to return if expr1 is null. Can be a constant, 
column, or function, and any combination of operators."
-            )
-            .build()
-    })
-}
-
 fn nvl2_func(args: &[ColumnarValue]) -> Result<ColumnarValue> {
     if args.len() != 3 {
         return internal_err!(
diff --git a/datafusion/functions/src/core/struct.rs 
b/datafusion/functions/src/core/struct.rs
index b7579ba5e4..f5bff2cc72 100644
--- a/datafusion/functions/src/core/struct.rs
+++ b/datafusion/functions/src/core/struct.rs
@@ -18,11 +18,11 @@
 use arrow::array::{ArrayRef, StructArray};
 use arrow::datatypes::{DataType, Field, Fields};
 use datafusion_common::{exec_err, Result};
-use datafusion_expr::scalar_doc_sections::DOC_SECTION_STRUCT;
 use datafusion_expr::{ColumnarValue, Documentation};
 use datafusion_expr::{ScalarUDFImpl, Signature, Volatility};
+use datafusion_macros::user_doc;
 use std::any::Any;
-use std::sync::{Arc, OnceLock};
+use std::sync::Arc;
 
 fn array_struct(args: &[ArrayRef]) -> Result<ArrayRef> {
     // do not accept 0 arguments.
@@ -55,6 +55,46 @@ fn struct_expr(args: &[ColumnarValue]) -> 
Result<ColumnarValue> {
     Ok(ColumnarValue::Array(array_struct(arrays.as_slice())?))
 }
 
+#[user_doc(
+    doc_section(label = "Struct Functions"),
+    description = "Returns an Arrow struct using the specified input 
expressions optionally named.
+Fields in the returned struct use the optional name or the `cN` naming 
convention.
+For example: `c0`, `c1`, `c2`, etc.",
+    syntax_example = "struct(expression1[, ..., expression_n])",
+    sql_example = r#"For example, this query converts two columns `a` and `b` 
to a single column with
+a struct type of fields `field_a` and `c1`:
+```sql
+> select * from t;
++---+---+
+| a | b |
++---+---+
+| 1 | 2 |
+| 3 | 4 |
++---+---+
+
+-- use default names `c0`, `c1`
+> select struct(a, b) from t;
++-----------------+
+| struct(t.a,t.b) |
++-----------------+
+| {c0: 1, c1: 2}  |
+| {c0: 3, c1: 4}  |
++-----------------+
+
+-- name the first field `field_a`
+select struct(a as field_a, b) from t;
++--------------------------------------------------+
+| named_struct(Utf8("field_a"),t.a,Utf8("c1"),t.b) |
++--------------------------------------------------+
+| {field_a: 1, c1: 2}                              |
+| {field_a: 3, c1: 4}                              |
++--------------------------------------------------+
+```"#,
+    argument(
+        name = "expression1, expression_n",
+        description = "Expression to include in the output struct. Can be a 
constant, column, or function, any combination of arithmetic or string 
operators."
+    )
+)]
 #[derive(Debug)]
 pub struct StructFunc {
     signature: Signature,
@@ -110,53 +150,6 @@ impl ScalarUDFImpl for StructFunc {
     }
 
     fn documentation(&self) -> Option<&Documentation> {
-        Some(get_struct_doc())
+        self.doc()
     }
 }
-
-static DOCUMENTATION: OnceLock<Documentation> = OnceLock::new();
-
-fn get_struct_doc() -> &'static Documentation {
-    DOCUMENTATION.get_or_init(|| {
-        Documentation::builder(
-            DOC_SECTION_STRUCT,
-            "Returns an Arrow struct using the specified input expressions 
optionally named.
-Fields in the returned struct use the optional name or the `cN` naming 
convention.
-For example: `c0`, `c1`, `c2`, etc.",
-            "struct(expression1[, ..., expression_n])")
-            .with_sql_example(r#"For example, this query converts two columns 
`a` and `b` to a single column with
-a struct type of fields `field_a` and `c1`:
-```sql
-> select * from t;
-+---+---+
-| a | b |
-+---+---+
-| 1 | 2 |
-| 3 | 4 |
-+---+---+
-
--- use default names `c0`, `c1`
-> select struct(a, b) from t;
-+-----------------+
-| struct(t.a,t.b) |
-+-----------------+
-| {c0: 1, c1: 2}  |
-| {c0: 3, c1: 4}  |
-+-----------------+
-
--- name the first field `field_a`
-select struct(a as field_a, b) from t;
-+--------------------------------------------------+
-| named_struct(Utf8("field_a"),t.a,Utf8("c1"),t.b) |
-+--------------------------------------------------+
-| {field_a: 1, c1: 2}                              |
-| {field_a: 3, c1: 4}                              |
-+--------------------------------------------------+
-```
-"#)
-            .with_argument(
-                "expression1, expression_n",
-                "Expression to include in the output struct. Can be a 
constant, column, or function, any combination of arithmetic or string 
operators.")
-            .build()
-    })
-}
diff --git a/datafusion/functions/src/core/version.rs 
b/datafusion/functions/src/core/version.rs
index bfc87f28eb..9085407314 100644
--- a/datafusion/functions/src/core/version.rs
+++ b/datafusion/functions/src/core/version.rs
@@ -19,13 +19,24 @@
 
 use arrow::datatypes::DataType;
 use datafusion_common::{internal_err, plan_err, Result, ScalarValue};
-use datafusion_expr::scalar_doc_sections::DOC_SECTION_OTHER;
 use datafusion_expr::{
     ColumnarValue, Documentation, ScalarUDFImpl, Signature, Volatility,
 };
+use datafusion_macros::user_doc;
 use std::any::Any;
-use std::sync::OnceLock;
-
+#[user_doc(
+    doc_section(label = "Other Functions"),
+    description = "Returns the version of DataFusion.",
+    syntax_example = "version()",
+    sql_example = r#"```sql
+> select version();
++--------------------------------------------+
+| version()                                  |
++--------------------------------------------+
+| Apache DataFusion 42.0.0, aarch64 on macos |
++--------------------------------------------+
+```"#
+)]
 #[derive(Debug)]
 pub struct VersionFunc {
     signature: Signature,
@@ -86,33 +97,10 @@ impl ScalarUDFImpl for VersionFunc {
     }
 
     fn documentation(&self) -> Option<&Documentation> {
-        Some(get_version_doc())
+        self.doc()
     }
 }
 
-static DOCUMENTATION: OnceLock<Documentation> = OnceLock::new();
-
-fn get_version_doc() -> &'static Documentation {
-    DOCUMENTATION.get_or_init(|| {
-        Documentation::builder(
-            DOC_SECTION_OTHER,
-            "Returns the version of DataFusion.",
-            "version()",
-        )
-        .with_sql_example(
-            r#"```sql
-> select version();
-+--------------------------------------------+
-| version()                                  |
-+--------------------------------------------+
-| Apache DataFusion 42.0.0, aarch64 on macos |
-+--------------------------------------------+
-```"#,
-        )
-        .build()
-    })
-}
-
 #[cfg(test)]
 mod test {
     use super::*;


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to