Skip to content

Commit

Permalink
[FLINK-34265][doc] Add the doc of named parameters (apache#24377)
Browse files Browse the repository at this point in the history
  • Loading branch information
hackergin authored Feb 26, 2024
1 parent dd77ee5 commit 0af2540
Show file tree
Hide file tree
Showing 4 changed files with 566 additions and 0 deletions.
139 changes: 139 additions & 0 deletions docs/content.zh/docs/dev/table/functions/udfs.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ env.from("MyTable").select(call("SubstringFunction", $("myField"), 5, 12));
// 在 SQL 里调用注册好的函数
env.sqlQuery("SELECT SubstringFunction(myField, 5, 12) FROM MyTable");

// 在 SQL 里使用命名参数调用注册好的函数
env.sqlQuery("SELECT SubstringFunction(param1 => myField, param2 => 5, param3 => 12) FROM MyTable");

```
{{< /tab >}}
{{< tab "Scala" >}}
Expand Down Expand Up @@ -599,6 +602,142 @@ public static class LiteralFunction extends ScalarFunction {
For more examples of custom type inference, see also the `flink-examples-table` module with
{{< gh_link file="flink-examples/flink-examples-table/src/main/java/org/apache/flink/table/examples/java/functions/AdvancedFunctionsExample.java" name="advanced function implementation" >}}.

### 命名参数

在调用函数时,可以使用参数名称来指定参数值。命名参数允许同时传递参数名和值给函数,避免了因为错误的参数顺序而导致混淆,并提高了代码的可读性和可维护性。 此外,命名参数还可以省略非必需的参数,默认情况下会使用 `null` 进行填充。
我们可以通过 `@ArgumentHint` 注解来指定参数的名称,类型,是否是必需的参数等。

**`@ArgumentHint`**

下面三个示例展示了如何在不同的范围内使用 `@ArgumentHint`。更多信息请参考注解类的文档。

1. 在 function 的 `eval` 方法的参数上使用 `@ArgumentHint` 注解。

{{< tabs "8064df87-eb42-4def-9bd2-0988fc246d37" >}}
{{< tab "Java" >}}

```java
import com.sun.tracing.dtrace.ArgsAttributes;
import org.apache.flink.table.annotation.ArgumentHint;
import org.apache.flink.table.functions.ScalarFunction;

public static class NamedParameterClass extends ScalarFunction {

// 使用 @ArgumentHint 注解指定参数的名称,参数类型,以及是否是必需的参数
public String eval(@ArgumentHint(name = "param1", isOptional = false, type = @DataTypeHint("STRING")) String s1,
@ArgumentHint(name = "param2", isOptional = true, type = @DataTypeHint("INT")) Integer s2) {
return s1 + ", " + s2;
}
}

```
{{< /tab >}}
{{< tab "Scala" >}}
```scala
import org.apache.flink.table.annotation.ArgumentHint;
import org.apache.flink.table.functions.ScalarFunction;

class NamedParameterClass extends ScalarFunction {

// 使用 @ArgumentHint 注解指定参数的名称,参数类型,以及是否是必需的参数
def eval(@ArgumentHint(name = "param1", isOptional = false, `type` = @DataTypeHint("STRING")) s1: String,
@ArgumentHint(name = "param2", isOptional = true, `type` = @DataTypeHint("INTEGER")) s2: Integer) = {
s1 + ", " + s2
}
}
```
{{< /tab >}}
{{< /tabs >}}

2. 在 function 的 `eval` 方法上使用 `@ArgumentHint` 注解。

{{< tabs "1356086c-189c-4932-a797-badf5b5e27ab" >}}
{{< tab "Java" >}}
```java
import org.apache.flink.table.annotation.ArgumentHint;
import org.apache.flink.table.functions.ScalarFunction;

public static class NamedParameterClass extends ScalarFunction {

// 使用 @ArgumentHint 注解指定参数的名称,参数类型,以及该参数是否是必需的参数
@FunctionHint(
argument = {@ArgumentHint(name = "param1", isOptional = false, type = @DataTypeHint("STRING")),
@ArgumentHint(name = "param2", isOptional = true, type = @DataTypeHint("INTEGER"))}
)
public String eval(String s1, Integer s2) {
return s1 + ", " + s2;
}
}
```
{{< /tab >}}
{{< tab "Scala" >}}
```scala
import org.apache.flink.table.annotation.ArgumentHint;
import org.apache.flink.table.functions.ScalarFunction;

class NamedParameterClass extends ScalarFunction {

// 使用 @ArgumentHint 注解指定参数的名称,参数类型,以及是否是必需的参数
@FunctionHint(
argument = Array(new ArgumentHint(name = "param1", isOptional = false, `type` = new DataTypeHint("STRING")),
new ArgumentHint(name = "param2", isOptional = true, `type` = new DataTypeHint("INTEGER")))
)
def eval(s1: String, s2: Int): String = {
s1 + ", " + s2
}
}
```
{{< /tab >}}
{{< /tabs >}}

3. 在 function 的 class 上使用 `@ArgumentHint` 注解。

{{< tabs "ba00146a-08bf-496c-89bc-8d5e333f04f7" >}}
{{< tab "Java" >}}
```java
import org.apache.flink.table.annotation.ArgumentHint;
import org.apache.flink.table.functions.ScalarFunction;

// 使用 @ArgumentHint 注解指定参数的名称,参数类型,以及是否是必需的参数
@FunctionHint(
argument = {@ArgumentHint(name = "param1", isOptional = false, type = @DataTypeHint("STRING")),
@ArgumentHint(name = "param2", isOptional = true, type = @DataTypeHint("INTEGER"))}
)
public static class NamedParameterClass extends ScalarFunction {

public String eval(String s1, Integer s2) {
return s1 + ", " + s2;
}
}
```
{{< /tab >}}
{{< tab "Scala" >}}
```scala
import org.apache.flink.table.annotation.ArgumentHint;
import org.apache.flink.table.functions.ScalarFunction;

// 使用 @ArgumentHint 注解指定参数的名称,参数类型,以及是否是必需的参数
@FunctionHint(
argument = Array(new ArgumentHint(name = "param1", isOptional = false, `type` = new DataTypeHint("STRING")),
new ArgumentHint(name = "param2", isOptional = true, `type` = new DataTypeHint("INTEGER")))
)
class NamedParameterClass extends ScalarFunction {

// 使用 @ArgumentHint 注解指定参数的名称,参数类型,以及是否是必需的参数
def eval(s1: String, s2: Int): String = {
s1 + ", " + s2
}
}
```
{{< /tab >}}
{{< /tabs >}}


{{< hint info >}}
* `@ArgumentHint` 内部包含了 `@DataTypeHint` 注解,因此在 `@FunctionHint` 中不能同时声明 `input``argument` ,当作用于函数的参数时 `@ArgumentHint` 也不能和 `@DataTypeHint` 同时使用,推荐使用 `@ArgumentHint`
* 命名参数只有在对应的类不包含重载函数和可变参函数才会生效,否则使用命名参数会导致报错。
{{< /hint >}}

### 确定性

每个用户自定义函数类都可以通过重写 `isDeterministic()` 方法来声明它是否产生确定性的结果。如果该函数不是纯粹函数式的(如`random()`, `date()`, 或`now()`),该方法必须返回 `false`。默认情况下,`isDeterministic()` 返回 `true`
Expand Down
146 changes: 146 additions & 0 deletions docs/content.zh/docs/dev/table/procedures.md
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,152 @@ class OverloadedProcedure extends Procedure {
{{< /tab >}}
{{< /tabs >}}

### 命名参数

在调用存储过程时,可以使用参数名称来指定参数值。命名参数允许同时传递参数名和值给存储过程,避免了因为错误的参数顺序而导致混淆,并提高了代码的可读性和可维护性。 此外,命名参数还可以省略非必需的参数,默认情况下会使用 `null` 进行填充。
我们可以通过 `@ArgumentHint` 注解来指定参数的名称,类型,是否是必需的参数等。

下面三个示例展示了如何在不同的范围内使用 `@ArgumentHint`。更多信息请参考注解类的文档。

1. 在存储过程的 `call` 方法的参数上使用 `@ArgumentHint` 注解。

{{< tabs "d2132879-26dc-45ba-8daa-365733a738e0" >}}
{{< tab "Java" >}}
```java
import org.apache.flink.table.annotation.DataTypeHint;
import org.apache.flink.table.annotation.ProcedureHint;
import org.apache.flink.table.procedure.ProcedureContext;
import org.apache.flink.table.procedures.Procedure;
import org.apache.flink.types.Row;

public static class NamedParameterProcedure extends Procedure {

public @DataTypeHint("INT") Integer[] call(ProcedureContext context, @ArgumentHint(name = "a", isOption = true) Integer a, @ArgumentHint(name = "b") Integer b) {
return new Integer[] {a + (b == null ? 0 : b)};
}
}
```
{{< /tab >}}
{{< tab "Scala" >}}
```scala

import org.apache.flink.table.annotation.DataTypeHint
import org.apache.flink.table.annotation.ProcedureHint
import org.apache.flink.table.procedure.ProcedureContext
import org.apache.flink.table.procedures.Procedure
import org.apache.flink.types.Row
import scala.annotation.varargs

class NamedParameterProcedure extends Procedure {

def call(context: ProcedureContext, @ArgumentHint(name = "param1", isOptional = true) a: Integer, @ArgumentHint(name = "param2") b: Integer): Array[Integer] = {
Array(a + (if (b == null) 0 else b))
}
}
```
{{< /tab >}}
{{< /tabs >}}

2. 在存储过程的 `call` 方法上使用 `@ArgumentHint` 注解。

{{< tabs "5c682030-fdbd-485c-9961-b402971e14d4" >}}
{{< tab "Java" >}}
```java
import org.apache.flink.table.annotation.DataTypeHint;
import org.apache.flink.table.annotation.ProcedureHint;
import org.apache.flink.table.procedure.ProcedureContext;
import org.apache.flink.table.procedures.Procedure;
import org.apache.flink.types.Row;

public static class NamedParameterProcedure extends Procedure {

@ProcedureHint(
argument = {@ArgumentHint(name = "param1", type = @DataTypeHint("INTEGER"), isOptional = false),
@ArgumentHint(name = "param2", type = @DataTypeHint("INTEGER"), isOptional = true)}
)
public @DataTypeHint("INT") Integer[] call(ProcedureContext context, Integer a, Integer b) {
return new Integer[] {a + (b == null ? 0 : b)};
}
}
```
{{< /tab >}}
{{< tab "Scala" >}}
```scala

import org.apache.flink.table.annotation.DataTypeHint
import org.apache.flink.table.annotation.ProcedureHint
import org.apache.flink.table.procedure.ProcedureContext
import org.apache.flink.table.procedures.Procedure
import org.apache.flink.types.Row
import scala.annotation.varargs

class NamedParameterProcedure extends Procedure {
@ProcedureHint(
argument = Array(
new ArgumentHint(name = "param1", `type` = new DataTypeHint("INTEGER"), isOptional = false),
new ArgumentHint(name = "param2", `type` = new DataTypeHint("INTEGER"), isOptional = true)
)
)
def call(context: ProcedureContext, a: Integer, b: Integer): Array[Integer] = {
Array(a + (if (b == null) 0 else b))
}
}
```
{{< /tab >}}
{{< /tabs >}}

3. 在存储过程的 class 上使用 `@ArgumentHint` 注解。

{{< tabs "64db1ce1-1251-4cc4-a7d8-13b5664f9019" >}}
{{< tab "Java" >}}
```java
import org.apache.flink.table.annotation.DataTypeHint;
import org.apache.flink.table.annotation.ProcedureHint;
import org.apache.flink.table.procedure.ProcedureContext;
import org.apache.flink.table.procedures.Procedure;
import org.apache.flink.types.Row;

@ProcedureHint(
argument = {@ArgumentHint(name = "param1", type = @DataTypeHint("INTEGER"), isOptional = false),
@ArgumentHint(name = "param2", type = @DataTypeHint("INTEGER"), isOptional = true)}
)
public static class NamedParameterProcedure extends Procedure {

public @DataTypeHint("INT") Integer[] call(ProcedureContext context, Integer a, Integer b) {
return new Integer[] {a + (b == null ? 0 : b)};
}
}
```
{{< /tab >}}
{{< tab "Scala" >}}
```scala

import org.apache.flink.table.annotation.DataTypeHint
import org.apache.flink.table.annotation.ProcedureHint
import org.apache.flink.table.procedure.ProcedureContext
import org.apache.flink.table.procedures.Procedure
import org.apache.flink.types.Row
import scala.annotation.varargs

@ProcedureHint(
argument = Array(
new ArgumentHint(name = "param1", `type` = new DataTypeHint("INTEGER"), isOptional = false),
new ArgumentHint(name = "param2", `type` = new DataTypeHint("INTEGER"), isOptional = true)
)
)
class NamedParameterProcedure extends Procedure {
def call(context: ProcedureContext, a: Integer, b: Integer): Array[Integer] = {
Array(a + (if (b == null) 0 else b))
}
}
```
{{< /tab >}}
{{< /tabs >}}

{{< hint info >}}
* `@ArgumentHint` 内部包含了 `@DataTypeHint` 注解,因此在 `@ProcedureHint` 中不能同时声明 `input``argument`, 当作用于函数的参数时 `@ArgumentHint` 也不能和 `@DataTypeHint` 同时使用,推荐使用 `@ArgumentHint`
* 命名参数只有在对应的存储过程类不包含重载函数和可变参函数才会生效, 否则使用命名参数会导致报错。
{{< /hint >}}

### 在 Catalog 中返回存储过程
在实现了一个存储过程后,Catalog 可以通过方法 `Catalog.getProcedure(ObjectPath procedurePath)` 来返回该存储过程,下面的例子展示了如何在 Catalog 中返回存储过程。
Expand Down
Loading

0 comments on commit 0af2540

Please sign in to comment.