4 changed files with 95 additions and 335 deletions
-
4SQL_FEATURE_PLAN.md
-
242test/postgres/coverage_test.go
-
71weed/query/engine/time_filter_test.go
-
113weed/server/postgres/protocol.go
@ -1,242 +0,0 @@ |
|||||
package main |
|
||||
|
|
||||
import ( |
|
||||
"database/sql" |
|
||||
"fmt" |
|
||||
"log" |
|
||||
"testing" |
|
||||
"time" |
|
||||
|
|
||||
_ "github.com/lib/pq" |
|
||||
) |
|
||||
|
|
||||
// TestSHOWTablesRecovery tests that SHOW TABLES doesn't crash with nil pointer dereference
|
|
||||
func TestSHOWTablesRecovery(t *testing.T) { |
|
||||
db := setupTestDB(t) |
|
||||
defer db.Close() |
|
||||
|
|
||||
// Test SHOW TABLES (this was causing panics before the fix)
|
|
||||
rows, err := db.Query("SHOW TABLES") |
|
||||
if err != nil { |
|
||||
t.Fatalf("SHOW TABLES failed: %v", err) |
|
||||
} |
|
||||
defer rows.Close() |
|
||||
|
|
||||
tableCount := 0 |
|
||||
for rows.Next() { |
|
||||
var tableName string |
|
||||
if err := rows.Scan(&tableName); err != nil { |
|
||||
t.Errorf("Error scanning table name: %v", err) |
|
||||
continue |
|
||||
} |
|
||||
tableCount++ |
|
||||
log.Printf("Found table: %s", tableName) |
|
||||
} |
|
||||
|
|
||||
if tableCount == 0 { |
|
||||
t.Error("Expected at least one table, got 0") |
|
||||
} |
|
||||
log.Printf("✓ SHOW TABLES recovery test passed - found %d tables", tableCount) |
|
||||
} |
|
||||
|
|
||||
// TestSHOWTablesFromDatabase tests SHOW TABLES FROM database syntax
|
|
||||
func TestSHOWTablesFromDatabase(t *testing.T) { |
|
||||
db := setupTestDB(t) |
|
||||
defer db.Close() |
|
||||
|
|
||||
// Test SHOW TABLES FROM specific database
|
|
||||
rows, err := db.Query(`SHOW TABLES FROM "logs"`) |
|
||||
if err != nil { |
|
||||
t.Fatalf("SHOW TABLES FROM logs failed: %v", err) |
|
||||
} |
|
||||
defer rows.Close() |
|
||||
|
|
||||
found := false |
|
||||
for rows.Next() { |
|
||||
var tableName string |
|
||||
if err := rows.Scan(&tableName); err != nil { |
|
||||
t.Errorf("Error scanning table name: %v", err) |
|
||||
continue |
|
||||
} |
|
||||
found = true |
|
||||
log.Printf("Found table in logs database: %s", tableName) |
|
||||
} |
|
||||
|
|
||||
if !found { |
|
||||
t.Error("Expected tables in logs database, found none") |
|
||||
} |
|
||||
log.Printf("✓ SHOW TABLES FROM database test passed") |
|
||||
} |
|
||||
|
|
||||
// TestSystemQueriesIndividualConnections tests system queries with fresh connections
|
|
||||
func TestSystemQueriesIndividualConnections(t *testing.T) { |
|
||||
queries := []struct { |
|
||||
name string |
|
||||
query string |
|
||||
}{ |
|
||||
{"Version", "SELECT version()"}, |
|
||||
{"Current User", "SELECT current_user"}, |
|
||||
{"Current Database", "SELECT current_database()"}, |
|
||||
{"Server Encoding", "SELECT current_setting('server_encoding')"}, |
|
||||
} |
|
||||
|
|
||||
connStr := fmt.Sprintf("host=%s port=%s user=%s dbname=%s sslmode=disable", |
|
||||
getEnv("POSTGRES_HOST", "postgres-server"), |
|
||||
getEnv("POSTGRES_PORT", "5432"), |
|
||||
getEnv("POSTGRES_USER", "seaweedfs"), |
|
||||
getEnv("POSTGRES_DB", "logs")) |
|
||||
|
|
||||
for _, q := range queries { |
|
||||
t.Run(q.name, func(t *testing.T) { |
|
||||
// Create fresh connection for each system query (this was the fix)
|
|
||||
db, err := sql.Open("postgres", connStr) |
|
||||
if err != nil { |
|
||||
t.Fatalf("Failed to create connection for %s: %v", q.name, err) |
|
||||
} |
|
||||
defer db.Close() |
|
||||
|
|
||||
var result string |
|
||||
err = db.QueryRow(q.query).Scan(&result) |
|
||||
if err != nil { |
|
||||
t.Errorf("System query %s failed: %v", q.name, err) |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
if result == "" { |
|
||||
t.Errorf("System query %s returned empty result", q.name) |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
log.Printf("✓ %s: %s", q.name, result) |
|
||||
}) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// TestDatabaseConnectionSwitching tests connecting to different databases
|
|
||||
func TestDatabaseConnectionSwitching(t *testing.T) { |
|
||||
databases := []string{"analytics", "ecommerce", "logs"} |
|
||||
|
|
||||
host := getEnv("POSTGRES_HOST", "postgres-server") |
|
||||
port := getEnv("POSTGRES_PORT", "5432") |
|
||||
user := getEnv("POSTGRES_USER", "seaweedfs") |
|
||||
|
|
||||
for _, dbName := range databases { |
|
||||
t.Run(fmt.Sprintf("Connect to %s", dbName), func(t *testing.T) { |
|
||||
// Create fresh connection to specific database (this was the fix instead of USE commands)
|
|
||||
connStr := fmt.Sprintf("host=%s port=%s user=%s dbname=%s sslmode=disable", |
|
||||
host, port, user, dbName) |
|
||||
|
|
||||
db, err := sql.Open("postgres", connStr) |
|
||||
if err != nil { |
|
||||
t.Fatalf("Failed to connect to database %s: %v", dbName, err) |
|
||||
} |
|
||||
defer db.Close() |
|
||||
|
|
||||
var currentDB string |
|
||||
err = db.QueryRow("SELECT current_database()").Scan(¤tDB) |
|
||||
if err != nil { |
|
||||
t.Errorf("Failed to verify database connection to %s: %v", dbName, err) |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
log.Printf("✓ Successfully connected to database: %s", currentDB) |
|
||||
}) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// TestCOUNTFunctionParsing tests COUNT(*) parsing that was fixed
|
|
||||
func TestCOUNTFunctionParsing(t *testing.T) { |
|
||||
db := setupTestDB(t) |
|
||||
defer db.Close() |
|
||||
|
|
||||
// Test COUNT(*) on known table (this was fixed in the parser)
|
|
||||
var count int |
|
||||
err := db.QueryRow("SELECT COUNT(*) FROM application_logs").Scan(&count) |
|
||||
if err != nil { |
|
||||
t.Fatalf("COUNT(*) query failed: %v", err) |
|
||||
} |
|
||||
|
|
||||
if count <= 0 { |
|
||||
t.Error("Expected COUNT(*) to return positive number") |
|
||||
} |
|
||||
|
|
||||
log.Printf("✓ COUNT(*) parsing test passed - found %d records", count) |
|
||||
} |
|
||||
|
|
||||
// TestParquetLogicalTypesDisplay tests that Parquet logical types are displayed correctly
|
|
||||
func TestParquetLogicalTypesDisplay(t *testing.T) { |
|
||||
db := setupTestDB(t) |
|
||||
defer db.Close() |
|
||||
|
|
||||
// Test that timestamp logical types are visible in query results
|
|
||||
rows, err := db.Query("SELECT timestamp, id FROM application_logs LIMIT 2") |
|
||||
if err != nil { |
|
||||
t.Fatalf("Failed to query logical types: %v", err) |
|
||||
} |
|
||||
defer rows.Close() |
|
||||
|
|
||||
count := 0 |
|
||||
for rows.Next() { |
|
||||
var timestamp, id string |
|
||||
if err := rows.Scan(×tamp, &id); err != nil { |
|
||||
t.Errorf("Error scanning logical type data: %v", err) |
|
||||
continue |
|
||||
} |
|
||||
|
|
||||
// Check that timestamp contains the logical type structure
|
|
||||
if !containsAny(timestamp, []string{"timestamp_value", "timestamp_mic"}) { |
|
||||
t.Errorf("Expected timestamp to contain logical type structure, got: %s", timestamp) |
|
||||
} |
|
||||
|
|
||||
count++ |
|
||||
log.Printf("Row %d - Timestamp: %s, ID: %s", count, timestamp, id) |
|
||||
} |
|
||||
|
|
||||
if count == 0 { |
|
||||
t.Error("Expected to retrieve logical type data, got none") |
|
||||
} |
|
||||
|
|
||||
log.Printf("✓ Parquet logical types display test passed - %d rows", count) |
|
||||
} |
|
||||
|
|
||||
// Helper functions
|
|
||||
|
|
||||
func setupTestDB(t *testing.T) *sql.DB { |
|
||||
connStr := fmt.Sprintf("host=%s port=%s user=%s dbname=%s sslmode=disable", |
|
||||
getEnv("POSTGRES_HOST", "postgres-server"), |
|
||||
getEnv("POSTGRES_PORT", "5432"), |
|
||||
getEnv("POSTGRES_USER", "seaweedfs"), |
|
||||
getEnv("POSTGRES_DB", "logs")) |
|
||||
|
|
||||
db, err := sql.Open("postgres", connStr) |
|
||||
if err != nil { |
|
||||
t.Fatalf("Failed to connect to database: %v", err) |
|
||||
} |
|
||||
|
|
||||
// Wait for server to be ready
|
|
||||
for i := 0; i < 30; i++ { |
|
||||
if err = db.Ping(); err == nil { |
|
||||
break |
|
||||
} |
|
||||
time.Sleep(500 * time.Millisecond) |
|
||||
} |
|
||||
|
|
||||
if err != nil { |
|
||||
t.Fatalf("Database not ready after 15 seconds: %v", err) |
|
||||
} |
|
||||
|
|
||||
return db |
|
||||
} |
|
||||
|
|
||||
func containsAny(str string, substrings []string) bool { |
|
||||
for _, sub := range substrings { |
|
||||
if len(str) >= len(sub) { |
|
||||
for i := 0; i <= len(str)-len(sub); i++ { |
|
||||
if str[i:i+len(sub)] == sub { |
|
||||
return true |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
return false |
|
||||
} |
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue