aboutsummaryrefslogtreecommitdiff
path: root/weed/query/engine/string_literal_function_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'weed/query/engine/string_literal_function_test.go')
-rw-r--r--weed/query/engine/string_literal_function_test.go198
1 files changed, 198 insertions, 0 deletions
diff --git a/weed/query/engine/string_literal_function_test.go b/weed/query/engine/string_literal_function_test.go
new file mode 100644
index 000000000..828d8c9ed
--- /dev/null
+++ b/weed/query/engine/string_literal_function_test.go
@@ -0,0 +1,198 @@
+package engine
+
+import (
+ "context"
+ "strings"
+ "testing"
+)
+
+// TestSQLEngine_StringFunctionsAndLiterals tests the fixes for string functions and string literals
+// This covers the user's reported issues:
+// 1. String functions like UPPER(), LENGTH() being treated as aggregation functions
+// 2. String literals like 'good' returning empty values
+func TestSQLEngine_StringFunctionsAndLiterals(t *testing.T) {
+ engine := NewTestSQLEngine()
+
+ tests := []struct {
+ name string
+ query string
+ expectedCols []string
+ expectNonEmpty bool
+ validateFirstRow func(t *testing.T, row []string)
+ }{
+ {
+ name: "String functions - UPPER and LENGTH",
+ query: "SELECT status, UPPER(status), LENGTH(status) FROM user_events LIMIT 3",
+ expectedCols: []string{"status", "UPPER(status)", "LENGTH(status)"},
+ expectNonEmpty: true,
+ validateFirstRow: func(t *testing.T, row []string) {
+ if len(row) != 3 {
+ t.Errorf("Expected 3 columns, got %d", len(row))
+ return
+ }
+ // Status should exist, UPPER should be uppercase version, LENGTH should be numeric
+ status := row[0]
+ upperStatus := row[1]
+ lengthStr := row[2]
+
+ if status == "" {
+ t.Error("Status column should not be empty")
+ }
+ if upperStatus == "" {
+ t.Error("UPPER(status) should not be empty")
+ }
+ if lengthStr == "" {
+ t.Error("LENGTH(status) should not be empty")
+ }
+
+ t.Logf("Status: '%s', UPPER: '%s', LENGTH: '%s'", status, upperStatus, lengthStr)
+ },
+ },
+ {
+ name: "String literal in SELECT",
+ query: "SELECT id, user_id, 'good' FROM user_events LIMIT 2",
+ expectedCols: []string{"id", "user_id", "'good'"},
+ expectNonEmpty: true,
+ validateFirstRow: func(t *testing.T, row []string) {
+ if len(row) != 3 {
+ t.Errorf("Expected 3 columns, got %d", len(row))
+ return
+ }
+
+ literal := row[2]
+ if literal != "good" {
+ t.Errorf("Expected string literal to be 'good', got '%s'", literal)
+ }
+ },
+ },
+ {
+ name: "Mixed: columns, functions, arithmetic, and literals",
+ query: "SELECT id, UPPER(status), id*2, 'test' FROM user_events LIMIT 2",
+ expectedCols: []string{"id", "UPPER(status)", "id*2", "'test'"},
+ expectNonEmpty: true,
+ validateFirstRow: func(t *testing.T, row []string) {
+ if len(row) != 4 {
+ t.Errorf("Expected 4 columns, got %d", len(row))
+ return
+ }
+
+ // Verify the literal value
+ if row[3] != "test" {
+ t.Errorf("Expected literal 'test', got '%s'", row[3])
+ }
+
+ // Verify other values are not empty
+ for i, val := range row {
+ if val == "" {
+ t.Errorf("Column %d should not be empty", i)
+ }
+ }
+ },
+ },
+ {
+ name: "User's original failing query - fixed",
+ query: "SELECT status, action, user_type, UPPER(action), LENGTH(action) FROM user_events LIMIT 2",
+ expectedCols: []string{"status", "action", "user_type", "UPPER(action)", "LENGTH(action)"},
+ expectNonEmpty: true,
+ validateFirstRow: func(t *testing.T, row []string) {
+ if len(row) != 5 {
+ t.Errorf("Expected 5 columns, got %d", len(row))
+ return
+ }
+
+ // All values should be non-empty
+ for i, val := range row {
+ if val == "" {
+ t.Errorf("Column %d (%s) should not be empty", i, []string{"status", "action", "user_type", "UPPER(action)", "LENGTH(action)"}[i])
+ }
+ }
+
+ // UPPER should be uppercase
+ action := row[1]
+ upperAction := row[3]
+ if action != "" && upperAction != "" {
+ if upperAction != action && upperAction != strings.ToUpper(action) {
+ t.Logf("Note: UPPER(%s) = %s (may be expected)", action, upperAction)
+ }
+ }
+ },
+ },
+ }
+
+ 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)
+ }
+
+ // Verify we got results
+ if tt.expectNonEmpty && len(result.Rows) == 0 {
+ t.Fatal("Query returned no rows")
+ }
+
+ // Verify column count
+ if len(result.Columns) != len(tt.expectedCols) {
+ t.Errorf("Expected %d columns, got %d", len(tt.expectedCols), len(result.Columns))
+ }
+
+ // Check column names
+ for i, expectedCol := range tt.expectedCols {
+ if i < len(result.Columns) && result.Columns[i] != expectedCol {
+ t.Errorf("Expected column %d to be '%s', got '%s'", i, expectedCol, result.Columns[i])
+ }
+ }
+
+ // Validate first row if provided
+ if len(result.Rows) > 0 && tt.validateFirstRow != nil {
+ firstRow := result.Rows[0]
+ stringRow := make([]string, len(firstRow))
+ for i, val := range firstRow {
+ stringRow[i] = val.ToString()
+ }
+ tt.validateFirstRow(t, stringRow)
+ }
+
+ // Log results for debugging
+ t.Logf("Query: %s", tt.query)
+ t.Logf("Columns: %v", result.Columns)
+ for i, row := range result.Rows {
+ values := make([]string, len(row))
+ for j, val := range row {
+ values[j] = val.ToString()
+ }
+ t.Logf("Row %d: %v", i, values)
+ }
+ })
+ }
+}
+
+// TestSQLEngine_StringFunctionErrorHandling tests error cases for string functions
+func TestSQLEngine_StringFunctionErrorHandling(t *testing.T) {
+ engine := NewTestSQLEngine()
+
+ // This should now work (previously would error as "unsupported aggregation function")
+ result, err := engine.ExecuteSQL(context.Background(), "SELECT UPPER(status) FROM user_events LIMIT 1")
+ if err != nil {
+ t.Fatalf("UPPER function should work, got error: %v", err)
+ }
+ if result.Error != nil {
+ t.Fatalf("UPPER function should work, got query error: %v", result.Error)
+ }
+
+ t.Logf("✅ UPPER function works correctly")
+
+ // This should now work (previously would error as "unsupported aggregation function")
+ result2, err2 := engine.ExecuteSQL(context.Background(), "SELECT LENGTH(action) FROM user_events LIMIT 1")
+ if err2 != nil {
+ t.Fatalf("LENGTH function should work, got error: %v", err2)
+ }
+ if result2.Error != nil {
+ t.Fatalf("LENGTH function should work, got query error: %v", result2.Error)
+ }
+
+ t.Logf("✅ LENGTH function works correctly")
+}