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

joaoreis pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra-gocql-driver.git


The following commit(s) were added to refs/heads/trunk by this push:
     new d7df11b8 Prevent panic in compileMetadata() when final func is not 
defined for an aggregate
d7df11b8 is described below

commit d7df11b840781b69950549e22c8b095da4c265e0
Author: Bohdan Siryk <[email protected]>
AuthorDate: Thu Jan 29 11:12:16 2026 +0200

    Prevent panic in compileMetadata() when final func is not defined for an 
aggregate
    
    Previously, when there was a user defined aggregate without FINAL_FUNC 
defined the compileMetadata() function would panic due to nil pointer 
dereference. Gocql should properly handle this case since FINAL_FUNC is 
optional for user defined aggregates.
    
    This patch fixes the described problem by adding nil-checking before 
dereferencing.
    
    Patch by Bohdan Siryk; Reviewed by João Reis for CASSGO-105
---
 CHANGELOG.md     |   1 +
 metadata.go      |  10 +++-
 metadata_test.go | 160 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 169 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 872ebdd1..cfd0714a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,6 +18,7 @@ and this project adheres to [Semantic 
Versioning](https://semver.org/spec/v2.0.0
 - Return correct values from RowData (CASSGO-95)
 - Prevent setting a compression flag in a frame header when native proto v5 is 
being used (CASSGO-98)
 - Use protocol downgrading approach during protocol negotiation (CASSGO-97)
+- Prevent panic iin compileMetadata() when final func is not defined for an 
aggregate (CASSGO-105)
 
 ## [2.0.0]
 
diff --git a/metadata.go b/metadata.go
index a1e98eda..7909f22d 100644
--- a/metadata.go
+++ b/metadata.go
@@ -359,8 +359,14 @@ func compileMetadata(
        }
        keyspace.Aggregates = make(map[string]*AggregateMetadata, 
len(aggregates))
        for i, _ := range aggregates {
-               aggregates[i].FinalFunc = 
*keyspace.Functions[aggregates[i].finalFunc]
-               aggregates[i].StateFunc = 
*keyspace.Functions[aggregates[i].stateFunc]
+               finalFunc := keyspace.Functions[aggregates[i].finalFunc]
+               if finalFunc != nil {
+                       aggregates[i].FinalFunc = *finalFunc
+               }
+               stateFunc := keyspace.Functions[aggregates[i].stateFunc]
+               if stateFunc != nil {
+                       aggregates[i].StateFunc = *stateFunc
+               }
                keyspace.Aggregates[aggregates[i].Name] = &aggregates[i]
        }
        keyspace.UserTypes = make(map[string]*UserTypeMetadata, len(uTypes))
diff --git a/metadata_test.go b/metadata_test.go
index e2af014e..884d3cc2 100644
--- a/metadata_test.go
+++ b/metadata_test.go
@@ -34,6 +34,8 @@ package gocql
 import (
        "strconv"
        "testing"
+
+       "github.com/stretchr/testify/require"
 )
 
 // Tests V1 and V2 metadata "compilation" from example data which might be 
returned
@@ -596,3 +598,161 @@ func assertParseNonCompositeTypes(
                }
        }
 }
+
+func TestCompileMetadataWithFunctions(t *testing.T) {
+       session := &Session{
+               cfg: ClusterConfig{
+                       ProtoVersion: protoVersion5,
+                       Logger:       NewLogger(LogLevelInfo),
+               },
+               types: GlobalTypes.Copy(),
+       }
+
+       keyspace := &KeyspaceMetadata{
+               Name: "test_keyspace",
+       }
+
+       functions := []FunctionMetadata{
+               {
+                       Keyspace:          "test_keyspace",
+                       Name:              "test_func",
+                       ArgumentTypes:     []TypeInfo{intTypeInfo{}},
+                       ArgumentNames:     []string{"arg1"},
+                       Body:              "return arg1 + 1;",
+                       CalledOnNullInput: false,
+               },
+               {
+                       Keyspace:          "test_keyspace",
+                       Name:              "test_func_no_args",
+                       ArgumentTypes:     []TypeInfo{},
+                       ArgumentNames:     []string{},
+                       Body:              "return 1;",
+                       CalledOnNullInput: false,
+               },
+               {
+                       Keyspace:          "test_keyspace",
+                       Name:              "test_func_null_input",
+                       ArgumentTypes:     []TypeInfo{intTypeInfo{}},
+                       ArgumentNames:     []string{"arg1"},
+                       Body:              "if (arg1 == null) return 0; else 
return arg1;",
+                       CalledOnNullInput: true,
+               },
+       }
+
+       compileMetadata(session, keyspace, nil, nil, functions, nil, nil, nil)
+
+       require.Len(t, keyspace.Functions, 3, "Expected to have 3 functions")
+       require.Contains(t, keyspace.Functions, "test_func")
+       require.Contains(t, keyspace.Functions, "test_func_no_args")
+       require.Contains(t, keyspace.Functions, "test_func_null_input")
+
+       testFunc := keyspace.Functions["test_func"]
+       require.Equal(t, "test_func", testFunc.Name)
+       require.Len(t, testFunc.ArgumentTypes, 1)
+       require.Equal(t, TypeInt, testFunc.ArgumentTypes[0].Type())
+       require.Len(t, testFunc.ArgumentNames, 1)
+       require.Equal(t, "arg1", testFunc.ArgumentNames[0])
+       require.Equal(t, "return arg1 + 1;", testFunc.Body)
+       require.False(t, testFunc.CalledOnNullInput)
+
+       testFuncNoArgs := keyspace.Functions["test_func_no_args"]
+       require.Equal(t, "test_func_no_args", testFuncNoArgs.Name)
+       require.Empty(t, testFuncNoArgs.ArgumentTypes)
+       require.Empty(t, testFuncNoArgs.ArgumentNames)
+       require.Equal(t, "return 1;", testFuncNoArgs.Body)
+       require.False(t, testFuncNoArgs.CalledOnNullInput)
+
+       testFuncNullInput := keyspace.Functions["test_func_null_input"]
+       require.Equal(t, "test_func_null_input", testFuncNullInput.Name)
+       require.Len(t, testFuncNullInput.ArgumentTypes, 1)
+       require.Equal(t, TypeInt, testFuncNullInput.ArgumentTypes[0].Type())
+       require.Len(t, testFuncNullInput.ArgumentNames, 1)
+       require.Equal(t, "arg1", testFuncNullInput.ArgumentNames[0])
+       require.Equal(t, "if (arg1 == null) return 0; else return arg1;", 
testFuncNullInput.Body)
+       require.True(t, testFuncNullInput.CalledOnNullInput)
+}
+
+func TestCompileMetadataWithAggregates(t *testing.T) {
+       session := &Session{
+               cfg: ClusterConfig{
+                       ProtoVersion: protoVersion5,
+                       Logger:       NewLogger(LogLevelInfo),
+               },
+               types: GlobalTypes.Copy(),
+       }
+
+       keyspace := &KeyspaceMetadata{
+               Name: "test_keyspace",
+       }
+
+       functions := []FunctionMetadata{
+               {
+                       Keyspace:          "test_keyspace",
+                       Name:              "test_state_func",
+                       ArgumentTypes:     []TypeInfo{intTypeInfo{}},
+                       ArgumentNames:     []string{"arg1"},
+                       Body:              "return arg1 + 1;",
+                       CalledOnNullInput: false,
+               },
+               {
+                       Keyspace:          "test_keyspace",
+                       Name:              "test_final_func",
+                       ArgumentTypes:     []TypeInfo{floatTypeInfo{}},
+                       ArgumentNames:     []string{"arg1"},
+                       Body:              "return arg1 + 1;",
+                       CalledOnNullInput: false,
+               },
+       }
+
+       aggregates := []AggregateMetadata{
+               {
+                       Keyspace: "test_keyspace",
+                       Name:     "test_agg",
+                       ArgumentTypes: []TypeInfo{
+                               intTypeInfo{},
+                       },
+                       InitCond:   "0",
+                       StateFunc:  functions[0],
+                       FinalFunc:  functions[1],
+                       ReturnType: intTypeInfo{},
+                       StateType:  intTypeInfo{},
+                       stateFunc:  "test_state_func",
+                       finalFunc:  "test_final_func",
+               },
+               {
+                       Keyspace: "test_keyspace",
+                       Name:     "test_agg_no_final_func",
+                       ArgumentTypes: []TypeInfo{
+                               doubleTypeInfo{},
+                       },
+                       InitCond:   "0",
+                       StateFunc:  functions[0],
+                       ReturnType: doubleTypeInfo{},
+                       StateType:  doubleTypeInfo{},
+                       stateFunc:  "test_state_func",
+                       finalFunc:  "",
+               },
+       }
+
+       compileMetadata(session, keyspace, nil, nil, functions, aggregates, 
nil, nil)
+
+       require.Len(t, keyspace.Aggregates, 2, "Expected to have 2 aggregates")
+       require.Contains(t, keyspace.Aggregates, "test_agg")
+       require.Contains(t, keyspace.Aggregates, "test_agg_no_final_func")
+
+       testAgg := keyspace.Aggregates["test_agg"]
+       require.Equal(t, "test_agg", testAgg.Name)
+       require.Len(t, testAgg.ArgumentTypes, 1)
+       require.Equal(t, TypeInt, testAgg.ArgumentTypes[0].Type())
+       require.Equal(t, "0", testAgg.InitCond)
+       require.Equal(t, TypeInt, testAgg.ReturnType.Type())
+       require.Equal(t, TypeInt, testAgg.StateType.Type())
+
+       testAggNoFinalFunc := keyspace.Aggregates["test_agg_no_final_func"]
+       require.Equal(t, "test_agg_no_final_func", testAggNoFinalFunc.Name)
+       require.Len(t, testAggNoFinalFunc.ArgumentTypes, 1)
+       require.Equal(t, TypeDouble, testAggNoFinalFunc.ArgumentTypes[0].Type())
+       require.Equal(t, "0", testAggNoFinalFunc.InitCond)
+       require.Equal(t, TypeDouble, testAggNoFinalFunc.ReturnType.Type())
+       require.Equal(t, TypeDouble, testAggNoFinalFunc.StateType.Type())
+}


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

Reply via email to