aboutsummaryrefslogtreecommitdiff
path: root/weed/query/engine/arithmetic_only_execution_test.go
blob: 1b7cdb34f7198a3f8107e8932d62448c1d582fbe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package engine

import (
	"context"
	"testing"
)

// TestSQLEngine_ArithmeticOnlyQueryExecution tests the specific fix for queries
// that contain ONLY arithmetic expressions (no base columns) in the SELECT clause.
// This was the root issue reported where such queries returned empty values.
func TestSQLEngine_ArithmeticOnlyQueryExecution(t *testing.T) {
	engine := NewTestSQLEngine()

	// Test the core functionality: arithmetic-only queries should return data
	tests := []struct {
		name           string
		query          string
		expectedCols   []string
		mustNotBeEmpty bool
	}{
		{
			name:           "Basic arithmetic only query",
			query:          "SELECT id+user_id, id*2 FROM user_events LIMIT 3",
			expectedCols:   []string{"id+user_id", "id*2"},
			mustNotBeEmpty: true,
		},
		{
			name:           "With LIMIT and OFFSET - original user issue",
			query:          "SELECT id+user_id, id*2 FROM user_events LIMIT 2 OFFSET 1",
			expectedCols:   []string{"id+user_id", "id*2"},
			mustNotBeEmpty: true,
		},
		{
			name:           "Multiple arithmetic expressions",
			query:          "SELECT user_id+100, id-1000 FROM user_events LIMIT 1",
			expectedCols:   []string{"user_id+100", "id-1000"},
			mustNotBeEmpty: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result, err := engine.ExecuteSQL(context.Background(), tt.query)
			if err != nil {
				t.Fatalf("Query failed: %v", err)
			}
			if result.Error != nil {
				t.Fatalf("Query returned error: %v", result.Error)
			}

			// CRITICAL: Verify we got results (the original bug would return empty)
			if tt.mustNotBeEmpty && len(result.Rows) == 0 {
				t.Fatal("CRITICAL BUG: Query returned no rows - arithmetic-only query fix failed!")
			}

			// Verify column count and names
			if len(result.Columns) != len(tt.expectedCols) {
				t.Errorf("Expected %d columns, got %d", len(tt.expectedCols), len(result.Columns))
			}

			// CRITICAL: Verify no empty/null values (the original bug symptom)
			if len(result.Rows) > 0 {
				firstRow := result.Rows[0]
				for i, val := range firstRow {
					if val.IsNull() {
						t.Errorf("CRITICAL BUG: Column %d (%s) returned NULL", i, result.Columns[i])
					}
					if val.ToString() == "" {
						t.Errorf("CRITICAL BUG: Column %d (%s) returned empty string", i, result.Columns[i])
					}
				}
			}

			// Log success
			t.Logf("SUCCESS: %s returned %d rows with calculated values", tt.query, len(result.Rows))
		})
	}
}

// TestSQLEngine_ArithmeticOnlyQueryBugReproduction tests that the original bug
// (returning empty values) would have failed before our fix
func TestSQLEngine_ArithmeticOnlyQueryBugReproduction(t *testing.T) {
	engine := NewTestSQLEngine()

	// This is the EXACT query from the user's bug report
	query := "SELECT id+user_id, id*amount, id*2 FROM user_events LIMIT 10 OFFSET 5"

	result, err := engine.ExecuteSQL(context.Background(), query)
	if err != nil {
		t.Fatalf("Query failed: %v", err)
	}
	if result.Error != nil {
		t.Fatalf("Query returned error: %v", result.Error)
	}

	// Key assertions that would fail with the original bug:

	// 1. Must return rows (bug would return 0 rows or empty results)
	if len(result.Rows) == 0 {
		t.Fatal("CRITICAL: Query returned no rows - the original bug is NOT fixed!")
	}

	// 2. Must have expected columns
	expectedColumns := []string{"id+user_id", "id*amount", "id*2"}
	if len(result.Columns) != len(expectedColumns) {
		t.Errorf("Expected %d columns, got %d", len(expectedColumns), len(result.Columns))
	}

	// 3. Must have calculated values, not empty/null
	for i, row := range result.Rows {
		for j, val := range row {
			if val.IsNull() {
				t.Errorf("Row %d, Column %d (%s) is NULL - original bug not fixed!",
					i, j, result.Columns[j])
			}
			if val.ToString() == "" {
				t.Errorf("Row %d, Column %d (%s) is empty - original bug not fixed!",
					i, j, result.Columns[j])
			}
		}
	}

	// 4. Verify specific calculations for the OFFSET 5 data
	if len(result.Rows) > 0 {
		firstRow := result.Rows[0]
		// With OFFSET 5, first returned row should be 6th row: id=417224, user_id=7810
		expectedSum := "425034" // 417224 + 7810
		if firstRow[0].ToString() != expectedSum {
			t.Errorf("OFFSET 5 calculation wrong: expected id+user_id=%s, got %s",
				expectedSum, firstRow[0].ToString())
		}

		expectedDouble := "834448" // 417224 * 2
		if firstRow[2].ToString() != expectedDouble {
			t.Errorf("OFFSET 5 calculation wrong: expected id*2=%s, got %s",
				expectedDouble, firstRow[2].ToString())
		}
	}

	t.Logf("SUCCESS: Arithmetic-only query with OFFSET works correctly!")
	t.Logf("Query: %s", query)
	t.Logf("Returned %d rows with correct calculations", len(result.Rows))
}