|
|
@ -3,6 +3,7 @@ package engine |
|
|
|
import ( |
|
|
|
"context" |
|
|
|
"fmt" |
|
|
|
"math" |
|
|
|
"regexp" |
|
|
|
"strconv" |
|
|
|
"strings" |
|
|
@ -189,6 +190,9 @@ func (e *SQLEngine) executeSelectStatement(ctx context.Context, stmt *sqlparser. |
|
|
|
if parseErr != nil { |
|
|
|
return &QueryResult{Error: parseErr}, parseErr |
|
|
|
} |
|
|
|
if limit64 > math.MaxInt32 || limit64 < 0 { |
|
|
|
return &QueryResult{Error: fmt.Errorf("LIMIT value %d is out of valid range", limit64)}, fmt.Errorf("LIMIT value %d is out of valid range", limit64) |
|
|
|
} |
|
|
|
limit = int(limit64) |
|
|
|
} |
|
|
|
} |
|
|
@ -239,9 +243,9 @@ func (e *SQLEngine) executeSelectWithSampleData(ctx context.Context, stmt *sqlpa |
|
|
|
// Live log data (recent)
|
|
|
|
{ |
|
|
|
Values: map[string]*schema_pb.Value{ |
|
|
|
"user_id": {Kind: &schema_pb.Value_Int32Value{Int32Value: 1003}}, |
|
|
|
"user_id": {Kind: &schema_pb.Value_Int32Value{Int32Value: 1003}}, |
|
|
|
"event_type": {Kind: &schema_pb.Value_StringValue{StringValue: "live_login"}}, |
|
|
|
"data": {Kind: &schema_pb.Value_StringValue{StringValue: `{"ip": "10.0.0.1", "live": true}`}}, |
|
|
|
"data": {Kind: &schema_pb.Value_StringValue{StringValue: `{"ip": "10.0.0.1", "live": true}`}}, |
|
|
|
}, |
|
|
|
Timestamp: now - 300000000000, // 5 minutes ago
|
|
|
|
Key: []byte("live-1003"), |
|
|
@ -249,9 +253,9 @@ func (e *SQLEngine) executeSelectWithSampleData(ctx context.Context, stmt *sqlpa |
|
|
|
}, |
|
|
|
{ |
|
|
|
Values: map[string]*schema_pb.Value{ |
|
|
|
"user_id": {Kind: &schema_pb.Value_Int32Value{Int32Value: 1004}}, |
|
|
|
"user_id": {Kind: &schema_pb.Value_Int32Value{Int32Value: 1004}}, |
|
|
|
"event_type": {Kind: &schema_pb.Value_StringValue{StringValue: "live_click"}}, |
|
|
|
"data": {Kind: &schema_pb.Value_StringValue{StringValue: `{"button": "submit", "live": true}`}}, |
|
|
|
"data": {Kind: &schema_pb.Value_StringValue{StringValue: `{"button": "submit", "live": true}`}}, |
|
|
|
}, |
|
|
|
Timestamp: now - 120000000000, // 2 minutes ago
|
|
|
|
Key: []byte("live-1004"), |
|
|
@ -260,9 +264,9 @@ func (e *SQLEngine) executeSelectWithSampleData(ctx context.Context, stmt *sqlpa |
|
|
|
// Archived Parquet data (older)
|
|
|
|
{ |
|
|
|
Values: map[string]*schema_pb.Value{ |
|
|
|
"user_id": {Kind: &schema_pb.Value_Int32Value{Int32Value: 1001}}, |
|
|
|
"user_id": {Kind: &schema_pb.Value_Int32Value{Int32Value: 1001}}, |
|
|
|
"event_type": {Kind: &schema_pb.Value_StringValue{StringValue: "archived_login"}}, |
|
|
|
"data": {Kind: &schema_pb.Value_StringValue{StringValue: `{"ip": "192.168.1.1", "archived": true}`}}, |
|
|
|
"data": {Kind: &schema_pb.Value_StringValue{StringValue: `{"ip": "192.168.1.1", "archived": true}`}}, |
|
|
|
}, |
|
|
|
Timestamp: now - 3600000000000, // 1 hour ago
|
|
|
|
Key: []byte("archived-1001"), |
|
|
@ -270,9 +274,9 @@ func (e *SQLEngine) executeSelectWithSampleData(ctx context.Context, stmt *sqlpa |
|
|
|
}, |
|
|
|
{ |
|
|
|
Values: map[string]*schema_pb.Value{ |
|
|
|
"user_id": {Kind: &schema_pb.Value_Int32Value{Int32Value: 1002}}, |
|
|
|
"user_id": {Kind: &schema_pb.Value_Int32Value{Int32Value: 1002}}, |
|
|
|
"event_type": {Kind: &schema_pb.Value_StringValue{StringValue: "archived_logout"}}, |
|
|
|
"data": {Kind: &schema_pb.Value_StringValue{StringValue: `{"duration": 1800, "archived": true}`}}, |
|
|
|
"data": {Kind: &schema_pb.Value_StringValue{StringValue: `{"duration": 1800, "archived": true}`}}, |
|
|
|
}, |
|
|
|
Timestamp: now - 1800000000000, // 30 minutes ago
|
|
|
|
Key: []byte("archived-1002"), |
|
|
@ -284,7 +288,7 @@ func (e *SQLEngine) executeSelectWithSampleData(ctx context.Context, stmt *sqlpa |
|
|
|
// Live system logs
|
|
|
|
{ |
|
|
|
Values: map[string]*schema_pb.Value{ |
|
|
|
"level": {Kind: &schema_pb.Value_StringValue{StringValue: "INFO"}}, |
|
|
|
"level": {Kind: &schema_pb.Value_StringValue{StringValue: "INFO"}}, |
|
|
|
"message": {Kind: &schema_pb.Value_StringValue{StringValue: "Live service heartbeat"}}, |
|
|
|
"service": {Kind: &schema_pb.Value_StringValue{StringValue: "api-gateway"}}, |
|
|
|
}, |
|
|
@ -295,7 +299,7 @@ func (e *SQLEngine) executeSelectWithSampleData(ctx context.Context, stmt *sqlpa |
|
|
|
// Archived system logs
|
|
|
|
{ |
|
|
|
Values: map[string]*schema_pb.Value{ |
|
|
|
"level": {Kind: &schema_pb.Value_StringValue{StringValue: "ERROR"}}, |
|
|
|
"level": {Kind: &schema_pb.Value_StringValue{StringValue: "ERROR"}}, |
|
|
|
"message": {Kind: &schema_pb.Value_StringValue{StringValue: "Database connection timeout"}}, |
|
|
|
"service": {Kind: &schema_pb.Value_StringValue{StringValue: "user-service"}}, |
|
|
|
}, |
|
|
@ -314,6 +318,11 @@ func (e *SQLEngine) executeSelectWithSampleData(ctx context.Context, stmt *sqlpa |
|
|
|
if stmt.Limit != nil && stmt.Limit.Rowcount != nil { |
|
|
|
if limitExpr, ok := stmt.Limit.Rowcount.(*sqlparser.SQLVal); ok && limitExpr.Type == sqlparser.IntVal { |
|
|
|
if limit64, err := strconv.ParseInt(string(limitExpr.Val), 10, 64); err == nil { |
|
|
|
if limit64 > math.MaxInt32 || limit64 < 0 { |
|
|
|
return &QueryResult{ |
|
|
|
Error: fmt.Errorf("LIMIT value %d is out of valid range", limit64), |
|
|
|
}, fmt.Errorf("LIMIT value %d is out of valid range", limit64) |
|
|
|
} |
|
|
|
limit := int(limit64) |
|
|
|
if limit > 0 && limit < len(sampleResults) { |
|
|
|
sampleResults = sampleResults[:limit] |
|
|
@ -474,14 +483,14 @@ func (e *SQLEngine) isTimeColumn(columnName string) bool { |
|
|
|
|
|
|
|
// System timestamp columns
|
|
|
|
timeColumns := []string{ |
|
|
|
"_timestamp_ns", // SeaweedFS MQ system timestamp (nanoseconds)
|
|
|
|
"timestamp_ns", // Alternative naming
|
|
|
|
"timestamp", // Common timestamp field
|
|
|
|
"created_at", // Common creation time field
|
|
|
|
"updated_at", // Common update time field
|
|
|
|
"event_time", // Event timestamp
|
|
|
|
"log_time", // Log timestamp
|
|
|
|
"ts", // Short form
|
|
|
|
"_timestamp_ns", // SeaweedFS MQ system timestamp (nanoseconds)
|
|
|
|
"timestamp_ns", // Alternative naming
|
|
|
|
"timestamp", // Common timestamp field
|
|
|
|
"created_at", // Common creation time field
|
|
|
|
"updated_at", // Common update time field
|
|
|
|
"event_time", // Event timestamp
|
|
|
|
"log_time", // Log timestamp
|
|
|
|
"ts", // Short form
|
|
|
|
} |
|
|
|
|
|
|
|
for _, timeCol := range timeColumns { |
|
|
@ -690,6 +699,9 @@ func (e *SQLEngine) valuesEqual(fieldValue *schema_pb.Value, compareValue interf |
|
|
|
switch v := fieldValue.Kind.(type) { |
|
|
|
case *schema_pb.Value_Int32Value: |
|
|
|
if intVal, ok := compareValue.(int64); ok { |
|
|
|
if intVal > math.MaxInt32 || intVal < math.MinInt32 { |
|
|
|
return false // Value out of range for int32, cannot be equal
|
|
|
|
} |
|
|
|
return v.Int32Value == int32(intVal) |
|
|
|
} |
|
|
|
case *schema_pb.Value_Int64Value: |
|
|
@ -708,6 +720,12 @@ func (e *SQLEngine) valueLessThan(fieldValue *schema_pb.Value, compareValue inte |
|
|
|
switch v := fieldValue.Kind.(type) { |
|
|
|
case *schema_pb.Value_Int32Value: |
|
|
|
if intVal, ok := compareValue.(int64); ok { |
|
|
|
if intVal > math.MaxInt32 { |
|
|
|
return true // int32 value is always less than values > MaxInt32
|
|
|
|
} |
|
|
|
if intVal < math.MinInt32 { |
|
|
|
return false // int32 value is always greater than values < MinInt32
|
|
|
|
} |
|
|
|
return v.Int32Value < int32(intVal) |
|
|
|
} |
|
|
|
case *schema_pb.Value_Int64Value: |
|
|
@ -722,6 +740,12 @@ func (e *SQLEngine) valueGreaterThan(fieldValue *schema_pb.Value, compareValue i |
|
|
|
switch v := fieldValue.Kind.(type) { |
|
|
|
case *schema_pb.Value_Int32Value: |
|
|
|
if intVal, ok := compareValue.(int64); ok { |
|
|
|
if intVal > math.MaxInt32 { |
|
|
|
return false // int32 value is never greater than values > MaxInt32
|
|
|
|
} |
|
|
|
if intVal < math.MinInt32 { |
|
|
|
return true // int32 value is always greater than values < MinInt32
|
|
|
|
} |
|
|
|
return v.Int32Value > int32(intVal) |
|
|
|
} |
|
|
|
case *schema_pb.Value_Int64Value: |
|
|
|