From 46f1fea845642aaf16ffabbf7b2e4ffd886aea34 Mon Sep 17 00:00:00 2001
From: qingxinhome <70939751+qingxinhome@users.noreply.github.com>
Date: Mon, 15 Aug 2022 12:48:21 +0800
Subject: [PATCH] Support UUID function#4019 (#4474)

Support uuid function

Approved by: @aressu1985, @ouyuanning
---
 pkg/sql/plan/base_binder.go                   |  5 ++
 pkg/sql/plan/function/builtin/multi/uuid.go   | 60 ++++++++++++++
 .../plan/function/builtin/multi/uuid_test.go  | 78 +++++++++++++++++++
 pkg/sql/plan/function/builtins.go             | 20 +++++
 pkg/sql/plan/function/function.go             | 12 +++
 pkg/sql/plan/function/function_id.go          |  2 +
 test/cases/function/func_string_uuid.test     |  5 ++
 test/result/function/func_string_uuid.result  |  9 +++
 8 files changed, 191 insertions(+)
 create mode 100644 pkg/sql/plan/function/builtin/multi/uuid.go
 create mode 100644 pkg/sql/plan/function/builtin/multi/uuid_test.go
 create mode 100644 test/cases/function/func_string_uuid.test
 create mode 100644 test/result/function/func_string_uuid.result

diff --git a/pkg/sql/plan/base_binder.go b/pkg/sql/plan/base_binder.go
index 3d3b2103e..d7b96790f 100644
--- a/pkg/sql/plan/base_binder.go
+++ b/pkg/sql/plan/base_binder.go
@@ -851,6 +851,11 @@ func bindFuncExprImplByPlanExpr(name string, args []*Expr) (*plan.Expr, error) {
 		}
 	}
 
+	if function.GetFunctionAppendHideArgByID(funcID) {
+		// Append a hidden parameter to the function. The default value is constant null
+		args = append(args, makePlan2NullConstExprWithType())
+	}
+
 	// return new expr
 	return &Expr{
 		Expr: &plan.Expr_F{
diff --git a/pkg/sql/plan/function/builtin/multi/uuid.go b/pkg/sql/plan/function/builtin/multi/uuid.go
new file mode 100644
index 000000000..bf7122728
--- /dev/null
+++ b/pkg/sql/plan/function/builtin/multi/uuid.go
@@ -0,0 +1,60 @@
+// Copyright 2021 - 2022 Matrix Origin
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package multi
+
+import (
+	"github.com/google/uuid"
+	"github.com/matrixorigin/matrixone/pkg/common/moerr"
+	"github.com/matrixorigin/matrixone/pkg/container/types"
+	"github.com/matrixorigin/matrixone/pkg/container/vector"
+	"github.com/matrixorigin/matrixone/pkg/vm/process"
+)
+
+const UUID_LENGTH uint32 = 36
+
+func UUID(inputVecs []*vector.Vector, proc *process.Process) (*vector.Vector, error) {
+	if len(inputVecs) != 1 {
+		return nil, moerr.NewError(moerr.INTERNAL_ERROR, "uuid function requires a hidden parameter")
+	}
+	rows := inputVecs[0].Length
+	resultType := types.T_varchar.ToType()
+	resultVector := vector.New(resultType)
+
+	results := &types.Bytes{
+		Data:    make([]byte, int(UUID_LENGTH)*rows),
+		Offsets: make([]uint32, rows),
+		Lengths: make([]uint32, rows),
+	}
+	var retCursor uint32 = 0
+	for i := 0; i < rows; i++ {
+		id, err := uuid.NewUUID()
+		if err != nil {
+			return nil, moerr.NewError(moerr.INTERNAL_ERROR, "generation uuid error")
+		}
+		slice := []byte(id.String())
+		for _, b := range slice {
+			results.Data[retCursor] = b
+			retCursor++
+		}
+		if i != 0 {
+			results.Offsets[i] = results.Offsets[i-1] + results.Lengths[i-1]
+		} else {
+			results.Offsets[i] = uint32(0)
+		}
+		results.Lengths[i] = UUID_LENGTH
+	}
+	vector.SetCol(resultVector, results)
+	return resultVector, nil
+}
diff --git a/pkg/sql/plan/function/builtin/multi/uuid_test.go b/pkg/sql/plan/function/builtin/multi/uuid_test.go
new file mode 100644
index 000000000..ae4a049b4
--- /dev/null
+++ b/pkg/sql/plan/function/builtin/multi/uuid_test.go
@@ -0,0 +1,78 @@
+// Copyright 2021 - 2022 Matrix Origin
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package multi
+
+import (
+	"github.com/matrixorigin/matrixone/pkg/container/types"
+	"github.com/matrixorigin/matrixone/pkg/container/vector"
+	"github.com/matrixorigin/matrixone/pkg/testutil"
+	"github.com/stretchr/testify/require"
+	"testing"
+)
+
+// test return multi line
+func TestUUID(t *testing.T) {
+	//scalar
+	vec := testutil.MakeScalarNull(5)
+	proc := testutil.NewProc()
+	res, err := UUID([]*vector.Vector{vec}, proc)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	bytes := res.Col.(*types.Bytes)
+	uuids := make([]string, 5)
+
+	for i := 0; i < 5; i++ {
+		offset := bytes.Offsets[i]
+		length := bytes.Lengths[i]
+		bytes := bytes.Data[offset : offset+length]
+		uuid := string(bytes)
+		uuids[i] = uuid
+		t.Log(uuid)
+	}
+
+	for i := 0; i < 5; i++ {
+		for j := 0; j < 5; j++ {
+			if i == j {
+				continue
+			}
+			require.NotEqual(t, uuids[i], uuids[j])
+		}
+	}
+}
+
+// test retuan one line
+func TestUUID2(t *testing.T) {
+	//scalar
+	vec := testutil.MakeScalarInt64(1, 1)
+	proc := testutil.NewProc()
+	res, err := UUID([]*vector.Vector{vec}, proc)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	bytes := res.Col.(*types.Bytes)
+	uuids := make([]string, 1)
+
+	for i := 0; i < 1; i++ {
+		offset := bytes.Offsets[i]
+		length := bytes.Lengths[i]
+		bytes := bytes.Data[offset : offset+length]
+		uuid := string(bytes)
+		uuids[i] = uuid
+		t.Log(uuid)
+	}
+}
diff --git a/pkg/sql/plan/function/builtins.go b/pkg/sql/plan/function/builtins.go
index 245d24864..8f1552fb8 100644
--- a/pkg/sql/plan/function/builtins.go
+++ b/pkg/sql/plan/function/builtins.go
@@ -174,6 +174,26 @@ var builtins = map[int]Functions{
 			},
 		},
 	},
+	UUID: {
+		// uuid function contains a hidden placeholder parameter
+		Id: UUID,
+		TypeCheckFn: func(_ []Function, inputs []types.T) (overloadIndex int32, ts []types.T) {
+			if len(inputs) == 0 {
+				return int32(0), nil
+			}
+			return wrongFunctionParameters, nil
+		},
+		Overloads: []Function{
+			{
+				Index:         0,
+				Flag:          plan.Function_STRICT,
+				Layout:        STANDARD_FUNCTION,
+				Volatile:      true,
+				AppendHideArg: true,
+				ReturnTyp:     types.T_varchar, Fn: multi.UUID,
+			},
+		},
+	},
 	DATE: {
 		Id: DATE,
 		Overloads: []Function{
diff --git a/pkg/sql/plan/function/function.go b/pkg/sql/plan/function/function.go
index c69c9b506..80482b2e4 100644
--- a/pkg/sql/plan/function/function.go
+++ b/pkg/sql/plan/function/function.go
@@ -115,6 +115,9 @@ type Function struct {
 	// Volatile function cannot be fold
 	Volatile bool
 
+	// whether the function needs to append a hidden parameter, such as 'uuid'
+	AppendHideArg bool
+
 	Flag plan.Function_FuncFlag
 
 	// Layout adapt to plan/function.go, used for `explain SQL`.
@@ -202,6 +205,15 @@ func GetFunctionIsAggregateByName(name string) bool {
 	return len(fs) > 0 && fs[0].IsAggregate()
 }
 
+// Check whether the function needs to append a hidden parameter
+func GetFunctionAppendHideArgByID(overloadID int64) bool {
+	function, err := GetFunctionByID(overloadID)
+	if err != nil {
+		return false
+	}
+	return function.AppendHideArg
+}
+
 // GetFunctionByName check a function exist or not according to input function name and arg types,
 // if matches,
 // return the encoded overload id and the overload's return type
diff --git a/pkg/sql/plan/function/function_id.go b/pkg/sql/plan/function/function_id.go
index 286bf94c6..a307d69e6 100644
--- a/pkg/sql/plan/function/function_id.go
+++ b/pkg/sql/plan/function/function_id.go
@@ -228,6 +228,7 @@ const (
 	ADD_FAULT_POINT     // Add a fault point
 	REMOVE_FAULT_POINT  // Remove
 	TRIGGER_FAULT_POINT // Trigger.
+	UUID
 
 	// FUNCTION_END_NUMBER is not a function, just a flag to record the max number of function.
 	// TODO: every one should put the new function id in front of this one if you want to make a new function.
@@ -373,6 +374,7 @@ var functionIdRegister = map[string]int32{
 	"add_fault_point":         ADD_FAULT_POINT,
 	"remove_fault_point":      REMOVE_FAULT_POINT,
 	"trigger_fault_point":     TRIGGER_FAULT_POINT,
+	"uuid":                    UUID,
 }
 
 func GetFunctionIsWinfunByName(name string) bool {
diff --git a/test/cases/function/func_string_uuid.test b/test/cases/function/func_string_uuid.test
new file mode 100644
index 000000000..4b6b85ff1
--- /dev/null
+++ b/test/cases/function/func_string_uuid.test
@@ -0,0 +1,5 @@
+create table t1(a INT,  b float);
+insert into t1 values(12124, -4213.413), (12124, -42413.409);
+SELECT length(uuid()) FROM t1;
+SELECT uuid(1) FROM t1;
+drop table t1;
\ No newline at end of file
diff --git a/test/result/function/func_string_uuid.result b/test/result/function/func_string_uuid.result
new file mode 100644
index 000000000..f972c3044
--- /dev/null
+++ b/test/result/function/func_string_uuid.result
@@ -0,0 +1,9 @@
+create table t1(a INT,  b float);
+insert into t1 values(12124, -4213.413), (12124, -42413.409);
+SELECT length(uuid()) FROM t1;
+length(uuid())
+36
+36
+SELECT uuid(1) FROM t1;
+Function 'uuid' with parameters [BIGINT] will be implemented in future version.
+drop table t1;
-- 
GitLab