Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
f516013
feat:Implement single column aggregations, no support for group by yet
Nov 24, 2025
d4b5538
Feat: ground work for group bys, Consuming child record batching and …
Nov 25, 2025
be9c813
feat: Implement agrregations with dynamic group by clause
Nov 25, 2025
c1da3cb
feat: Implement having operator
Nov 26, 2025
825732b
fixed PR comments
Nov 26, 2025
3e20453
Merge pull request #23 from Rich-T-kid/feature/Basic-Aggr-expr
Rich-T-kid Nov 26, 2025
7352d28
fix:removed array memory leaks
Nov 26, 2025
139c88c
fix:added naming convention for child input record batch
Nov 26, 2025
1e48e9d
closes #25 and closes #24
Nov 26, 2025
0e5c22d
Merge pull request #26 from Rich-T-kid/fix/memory-leaks
Rich-T-kid Nov 26, 2025
413f206
feat:implement basic sort operator
Nov 27, 2025
eb30dec
feat:Full-Sort operator returns results in batches| TODO:Top K sort o…
Nov 27, 2025
72a6673
feat:Implement TopK sort operator
Nov 30, 2025
cb72c10
Fix:PR comments
Nov 30, 2025
89beff7
Merge pull request #29 from Rich-T-kid/feat/SortOperator
Rich-T-kid Nov 30, 2025
1b40587
feat:implement Distinct operator |test included
Nov 30, 2025
deb94a9
fix:PR comments
Nov 30, 2025
bf24562
Merge pull request #30 from Rich-T-kid/feature/Distinct-Exec
Rich-T-kid Nov 30, 2025
9d82ced
feat:Implement hash join
Dec 3, 2025
67f654a
fix: PR comments
Dec 3, 2025
646c2f1
Merge pull request #31 from Rich-T-kid/feat/Joins
Rich-T-kid Dec 3, 2025
c9a1277
implemented project and filter intergration test
Dec 4, 2025
7d75906
Documentation: added operator test,Need intergration test
Dec 4, 2025
158f351
Documentation: Implement first wave of intergration test
Dec 5, 2025
f623bc5
Documentation:Implement all intergration test
Dec 5, 2025
e12e1d7
Documentation: Update read me, remove print statments, work on TODO s…
Dec 5, 2025
488bf2b
Merge pull request #33 from Rich-T-kid/documentation/intergration-test
Rich-T-kid Dec 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ src/Backend/test_data/json
# Allow a specific CSV dataset that we want tracked despite the general csv ignores
!src/Backend/test_data/csv/
!src/Backend/test_data/csv/Mental_Health_and_Social_Media_Balance_Dataset.csv
!src/Backend/test_data/csv/intergration_test_data_1.csv
!src/Backend/test_data/csv/intergration_test_data_2.csv
# allow parquet file
!src/Backend/test_data/parquet/
!src/Backend/test_data/parquet/capitals_clean.parquet
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ Initial development is done in **Go** (`opti-sql-go`), which serves as the prima
- `/operators` - SQL operator implementations (filter, join, aggregation, project)
- `/physical-optimizer` - Query plan parsing and optimization
- `/substrait` - Substrait plan integration
- `/operators/OPERATORS.md` - concise reference for operator constructors, behavior and examples

## Branching Model

Expand Down
113 changes: 100 additions & 13 deletions src/Backend/opti-sql-go/Expr/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ func EvalExpression(expr Expression, batch *operators.RecordBatch) (arrow.Array,
return EvalScalarFunction(e, batch)
case *CastExpr:
return EvalCast(e, batch)
case *NullCheckExpr:
return EvalNullCheckMask(e.Expr, batch)
default:
return nil, ErrUnsupportedExpression(expr.String())
}
Expand Down Expand Up @@ -146,6 +148,8 @@ func ExprDataType(e Expression, inputSchema *arrow.Schema) (arrow.DataType, erro
return nil, err
}
return inferScalarFunctionType(ex.Function, argType), nil
case *NullCheckExpr:
return arrow.FixedWidthTypes.Boolean, nil

default:
return nil, ErrUnsupportedExpression(ex.String())
Expand Down Expand Up @@ -215,7 +219,50 @@ type LiteralResolve struct {
}

func NewLiteralResolve(Type arrow.DataType, Value any) *LiteralResolve {
return &LiteralResolve{Type: Type, Value: Value}
var castVal any

switch v := Value.(type) {

// ------------------------------------------------------
// INT → cast based on Arrow integer type
// ------------------------------------------------------
case int:
switch Type.ID() {
case arrow.INT8:
castVal = int8(v)
case arrow.INT16:
castVal = int16(v)
case arrow.INT32:
castVal = int32(v)
case arrow.INT64:
castVal = int64(v)
case arrow.UINT8:
castVal = uint8(v)
case arrow.UINT16:
castVal = uint16(v)
case arrow.UINT32:
castVal = uint32(v)
case arrow.UINT64:
castVal = uint64(v)
default:
// not an integer Arrow type → store original
castVal = v
}
case string:
castVal = string(v)
case bool:
castVal = bool(v)
case float64:
switch Type.ID() {
case arrow.FLOAT32:
castVal = float32(v)
case arrow.FLOAT64:
castVal = float64(v)
}
default:
castVal = Value
}
return &LiteralResolve{Type: Type, Value: castVal}
}
func EvalLiteral(l *LiteralResolve, batch *operators.RecordBatch) (arrow.Array, error) {
n := int(batch.RowCount)
Expand Down Expand Up @@ -355,6 +402,16 @@ func EvalLiteral(l *LiteralResolve, batch *operators.RecordBatch) (arrow.Array,
b.Append(v)
}
return b.NewArray(), nil
// ------------------------------
// Nulls
// ------------------------------
case arrow.NULL:
b := array.NewNullBuilder(memory.DefaultAllocator)
defer b.Release()
for i := 0; i < n; i++ {
b.AppendNull()
}
return b.NewArray(), nil

default:
return nil, fmt.Errorf("literal type %s not supported", l.Type)
Expand Down Expand Up @@ -389,37 +446,36 @@ func EvalBinary(b *BinaryExpr, batch *operators.RecordBatch) (arrow.Array, error
if err != nil {
return nil, err
}
ctx := context.Background()
opt := compute.ArithmeticOptions{}
switch b.Op {
// arithmetic
case Addition:
datum, err := compute.Add(context.TODO(), opt, compute.NewDatum(leftArr), compute.NewDatum(rightArr))
datum, err := compute.Add(ctx, opt, compute.NewDatum(leftArr), compute.NewDatum(rightArr))
if err != nil {
return nil, err
}
return unpackDatum(datum)
case Subtraction:
datum, err := compute.Subtract(context.TODO(), opt, compute.NewDatum(leftArr), compute.NewDatum(rightArr))
datum, err := compute.Subtract(ctx, opt, compute.NewDatum(leftArr), compute.NewDatum(rightArr))
if err != nil {
return nil, err
}
return unpackDatum(datum)

case Multiplication:
datum, err := compute.Multiply(context.TODO(), opt, compute.NewDatum(leftArr), compute.NewDatum(rightArr))
datum, err := compute.Multiply(ctx, opt, compute.NewDatum(leftArr), compute.NewDatum(rightArr))
if err != nil {
return nil, err
}
return unpackDatum(datum)
case Division:
datum, err := compute.Divide(context.TODO(), opt, compute.NewDatum(leftArr), compute.NewDatum(rightArr))
datum, err := compute.Divide(ctx, opt, compute.NewDatum(leftArr), compute.NewDatum(rightArr))
if err != nil {
return nil, err
}
return unpackDatum(datum)

// comparisions TODO:
// These return a boolean array
case Equal:
if leftArr.DataType() != rightArr.DataType() {
return nil, ErrCantCompareDifferentTypes(leftArr.DataType(), rightArr.DataType())
Expand Down Expand Up @@ -495,15 +551,13 @@ func EvalBinary(b *BinaryExpr, batch *operators.RecordBatch) (arrow.Array, error
return unpackDatum(datum)
case Like:
if leftArr.DataType() != arrow.BinaryTypes.String || rightArr.DataType() != arrow.BinaryTypes.String {
// regEx runs only on strings
return nil, errors.New("binary operator Like only works on arrays of strings")
}
var compiledRegEx = compileSqlRegEx(rightArr.ValueStr(0))
filterBuilder := array.NewBooleanBuilder(memory.NewGoAllocator())
leftStrArray := leftArr.(*array.String)
for i := 0; i < leftStrArray.Len(); i++ {
valid := validRegEx(leftStrArray.Value(i), compiledRegEx)
fmt.Printf("does %s match %s: %v\n", leftStrArray.Value(i), compiledRegEx, valid)
filterBuilder.Append(valid)
}
return filterBuilder.NewArray(), nil
Expand Down Expand Up @@ -536,6 +590,7 @@ func NewScalarFunction(function supportedFunctions, Argument Expression) *Scalar
}

func EvalScalarFunction(s *ScalarFunction, batch *operators.RecordBatch) (arrow.Array, error) {
ctx := context.Background()
switch s.Function {
case Upper:
arr, err := EvalExpression(s.Arguments, batch)
Expand All @@ -555,7 +610,7 @@ func EvalScalarFunction(s *ScalarFunction, batch *operators.RecordBatch) (arrow.
if err != nil {
return nil, err
}
datum, err := compute.AbsoluteValue(context.TODO(), compute.ArithmeticOptions{}, compute.NewDatum(arr))
datum, err := compute.AbsoluteValue(ctx, compute.ArithmeticOptions{}, compute.NewDatum(arr))
if err != nil {
return nil, err
}
Expand All @@ -565,7 +620,7 @@ func EvalScalarFunction(s *ScalarFunction, batch *operators.RecordBatch) (arrow.
if err != nil {
return nil, err
}
datum, err := compute.Round(context.TODO(), compute.DefaultRoundOptions, compute.NewDatum(arr))
datum, err := compute.Round(ctx, compute.DefaultRoundOptions, compute.NewDatum(arr))
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -600,9 +655,8 @@ func EvalCast(c *CastExpr, batch *operators.RecordBatch) (arrow.Array, error) {

// Use Arrow compute kernel to cast
castOpts := compute.SafeCastOptions(c.TargetType)
out, err := compute.CastArray(context.TODO(), arr, castOpts)
out, err := compute.CastArray(context.Background(), arr, castOpts)
if err != nil {
// This is a runtime cast error
return nil, fmt.Errorf("cast error: cannot cast %s to %s: %w",
arr.DataType(), c.TargetType, err)
}
Expand All @@ -615,6 +669,39 @@ func (c *CastExpr) String() string {
return fmt.Sprintf("Cast(%s AS %s)", c.Expr, c.TargetType)
}

type NullCheckExpr struct {
Expr Expression
}

func NewNullCheckExpr(expr Expression) *NullCheckExpr {
return &NullCheckExpr{Expr: expr}
}
func (n *NullCheckExpr) ExprNode() {}
func (n *NullCheckExpr) String() string {
return fmt.Sprintf("NullCheck(%s)", n.Expr.String())
}
func EvalNullCheckMask(expr Expression, batch *operators.RecordBatch) (arrow.Array, error) {
// Step 1: Evaluate underlying expression
arr, err := EvalExpression(expr, batch)
if err != nil {
return nil, err
}

length := arr.Len()

// Step 2: Build boolean mask
builder := array.NewBooleanBuilder(memory.DefaultAllocator)
builder.Resize(length)

for i := 0; i < length; i++ {
builder.Append(!arr.IsNull(i)) // true = not null
}
// Step 3: produce final Boolean array
mask := builder.NewArray()
builder.Release()
return mask, nil
}

func upperImpl(arr arrow.Array) (arrow.Array, error) {
strArr, ok := arr.(*array.String)
if !ok {
Expand Down
Loading
Loading