grule-rule-engine icon indicating copy to clipboard operation
grule-rule-engine copied to clipboard

Temporary variables with the same name across rules may override each other's values.

Open dkkb opened this issue 7 months ago • 0 comments

Describe the bug

Temporary variables with the same name across rules may override each other's values.

To Reproduce

I wrote two tests to reproduce this issue, the code below is the first test, and it runs as expected.

func TestGruleSameNameVariableOverrideIssue1(t *testing.T) {
	rules := `
rule UpdateFirstName "Update first name" {
    when
        Fact.IsAdult == true
    then
        Fact.FirstName = "John";
		Retract("UpdateFirstName");
}

rule UpdateLastName "Update last name" {
	when
		Fact.IsAdult == true
	then
		Fact.LastName = "Doe";
		Retract("UpdateLastName");
}
`
	knowledgeLibrary := ast.NewKnowledgeLibrary()
	ruleBuilder := builder.NewRuleBuilder(knowledgeLibrary)
	err := ruleBuilder.BuildRuleFromResource("TestRules", "0.0.1", pkg.NewBytesResource([]byte(rules)))
	assert.NoError(t, err)
	knowledgeBase, err := knowledgeLibrary.NewKnowledgeBaseInstance("TestRules", "0.0.1")
	assert.NoError(t, err)
	dataContext := ast.NewDataContext()
	fact := &testFact{
		IsAdult: true,
	}
	err = dataContext.Add("Fact", fact)
	assert.NoError(t, err)
	eng := engine.NewGruleEngine()
	err = eng.Execute(dataContext, knowledgeBase)
	assert.NoError(t, err)

	assert.Equal(t, "John", fact.FirstName)
	assert.Equal(t, "Doe", fact.LastName)
}

After that, I wrote another test. As you can see, the only difference is that I use a temporary variable to store a value before assigning it to a field of the fact.

However, it appears that this temporary variable is shared across rules—if a value is assigned in ruleA, it can still be accessed in ruleB.

I'm not sure whether this is a bug, as I couldn't find any relevant explanation or example in the official documentation. But the behavior does seem unexpected.

func TestGruleSameNameVariableOverrideIssue2(t *testing.T) {
	rules := `
rule UpdateFirstName "Update first name" {
    when
        Fact.IsAdult == true
    then
		tmp = "John";
        Fact.FirstName = tmp;
		Retract("UpdateFirstName");
}

rule UpdateLastName "Update last name" {
	when
		Fact.IsAdult == true
	then
		tmp = "Doe";
		Fact.LastName = tmp;
		Retract("UpdateLastName");
}
`
	// ...
}

Output:

&{true John John} or &{true Doe Doe}

If I rename the variables to tmp1 and tmp2, everything works as expected again. It seems that Grule does support temporary variables within rules, but the behavior is somewhat unreliable.

dkkb avatar Jun 14 '25 10:06 dkkb