Repository: polarsignals/frostdb
Pull request #519 (2023-09-05 16:16:42)

Expr: Clone
Expands the Expr interface to include a Clone() method which returns a copy of the original expression


Patch:
diff --git a/query/logicalplan/builder.go b/query/logicalplan/builder.go
index 26b0b30..7aa0f97 100644
--- a/query/logicalplan/builder.go
+++ b/query/logicalplan/builder.go
@@ -81,6 +81,9 @@ type Expr interface {
 	// Computed returns whether the expression is computed as opposed to being
 	// a static value or unmodified physical column.
 	Computed() bool
+
+	// Clone returns a deep copy of the expression.
+	Clone() Expr
 }
 
 func (b Builder) Filter(expr Expr) Builder {
diff --git a/query/logicalplan/expr.go b/query/logicalplan/expr.go
index 972be00..185c6a7 100644
--- a/query/logicalplan/expr.go
+++ b/query/logicalplan/expr.go
@@ -61,6 +61,14 @@ type BinaryExpr struct {
 	Right Expr
 }
 
+func (e *BinaryExpr) Clone() Expr {
+	return &BinaryExpr{
+		Left:  e.Left.Clone(),
+		Op:    e.Op,
+		Right: e.Right.Clone(),
+	}
+}
+
 func (e *BinaryExpr) Accept(visitor Visitor) bool {
 	continu := visitor.PreVisit(e)
 	if !continu {
@@ -117,6 +125,10 @@ type Column struct {
 	ColumnName string
 }
 
+func (c *Column) Clone() Expr {
+	return &Column{ColumnName: c.ColumnName}
+}
+
 func (c *Column) Computed() bool {
 	return false
 }
@@ -315,6 +327,10 @@ type DynamicColumn struct {
 	ColumnName string
 }
 
+func (c *DynamicColumn) Clone() Expr {
+	return &DynamicColumn{ColumnName: c.ColumnName}
+}
+
 func (c *DynamicColumn) Computed() bool {
 	return false
 }
@@ -367,6 +383,12 @@ type LiteralExpr struct {
 	Value scalar.Scalar
 }
 
+func (e *LiteralExpr) Clone() Expr {
+	return &LiteralExpr{
+		Value: e.Value,
+	}
+}
+
 func (e *LiteralExpr) Computed() bool {
 	return false
 }
@@ -409,6 +431,13 @@ type AggregationFunction struct {
 	Expr Expr
 }
 
+func (f *AggregationFunction) Clone() Expr {
+	return &AggregationFunction{
+		Func: f.Func,
+		Expr: f.Expr.Clone(),
+	}
+}
+
 func (f *AggregationFunction) DataType(s *parquet.Schema) (arrow.DataType, error) {
 	return f.Expr.DataType(s)
 }
@@ -515,6 +544,13 @@ type AliasExpr struct {
 	Alias string
 }
 
+func (e *AliasExpr) Clone() Expr {
+	return &AliasExpr{
+		Expr:  e.Expr.Clone(),
+		Alias: e.Alias,
+	}
+}
+
 func (e *AliasExpr) DataType(s *parquet.Schema) (arrow.DataType, error) {
 	return e.Expr.DataType(s)
 }
@@ -568,6 +604,12 @@ type DurationExpr struct {
 	duration time.Duration
 }
 
+func (d *DurationExpr) Clone() Expr {
+	return &DurationExpr{
+		duration: d.duration,
+	}
+}
+
 func (d *DurationExpr) DataType(schema *parquet.Schema) (arrow.DataType, error) {
 	return &arrow.DurationType{}, nil
 }
@@ -610,6 +652,12 @@ type AverageExpr struct {
 	Expr Expr
 }
 
+func (a *AverageExpr) Clone() Expr {
+	return &AverageExpr{
+		Expr: a.Expr.Clone(),
+	}
+}
+
 func (a *AverageExpr) DataType(s *parquet.Schema) (arrow.DataType, error) {
 	return a.Expr.DataType(s)
 }


Test patch:
diff --git a/query/logicalplan/logicalplan_test.go b/query/logicalplan/logicalplan_test.go
index 0986381..ef44ef6 100644
--- a/query/logicalplan/logicalplan_test.go
+++ b/query/logicalplan/logicalplan_test.go
@@ -91,3 +91,13 @@ func TestInputSchemaGetter(t *testing.T) {
 		Build()
 	require.Nil(t, plan.InputSchema())
 }
+
+func Test_ExprClone(t *testing.T) {
+	expr := Col("labels.test").Eq(Literal("abc"))
+	expr2 := expr.Clone()
+	require.Equal(t, expr, expr2)
+
+	// Modify the original expr and make sure the clone is not affected
+	expr.Op = OpGt
+	require.NotEqual(t, expr, expr2)
+}