You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							195 lines
						
					
					
						
							5.4 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							195 lines
						
					
					
						
							5.4 KiB
						
					
					
				| package engine | |
| 
 | |
| import ( | |
| 	"fmt" | |
| 	"strings" | |
| 	"time" | |
| 
 | |
| 	"github.com/seaweedfs/seaweedfs/weed/pb/schema_pb" | |
| ) | |
| 
 | |
| // =============================== | |
| // DATE/TIME CONSTANTS | |
| // =============================== | |
|  | |
| // CurrentDate returns the current date as a string in YYYY-MM-DD format | |
| func (e *SQLEngine) CurrentDate() (*schema_pb.Value, error) { | |
| 	now := time.Now() | |
| 	dateStr := now.Format("2006-01-02") | |
| 
 | |
| 	return &schema_pb.Value{ | |
| 		Kind: &schema_pb.Value_StringValue{StringValue: dateStr}, | |
| 	}, nil | |
| } | |
| 
 | |
| // CurrentTimestamp returns the current timestamp | |
| func (e *SQLEngine) CurrentTimestamp() (*schema_pb.Value, error) { | |
| 	now := time.Now() | |
| 
 | |
| 	// Return as TimestampValue with microseconds | |
| 	timestampMicros := now.UnixMicro() | |
| 
 | |
| 	return &schema_pb.Value{ | |
| 		Kind: &schema_pb.Value_TimestampValue{ | |
| 			TimestampValue: &schema_pb.TimestampValue{ | |
| 				TimestampMicros: timestampMicros, | |
| 			}, | |
| 		}, | |
| 	}, nil | |
| } | |
| 
 | |
| // CurrentTime returns the current time as a string in HH:MM:SS format | |
| func (e *SQLEngine) CurrentTime() (*schema_pb.Value, error) { | |
| 	now := time.Now() | |
| 	timeStr := now.Format("15:04:05") | |
| 
 | |
| 	return &schema_pb.Value{ | |
| 		Kind: &schema_pb.Value_StringValue{StringValue: timeStr}, | |
| 	}, nil | |
| } | |
| 
 | |
| // Now is an alias for CurrentTimestamp (common SQL function name) | |
| func (e *SQLEngine) Now() (*schema_pb.Value, error) { | |
| 	return e.CurrentTimestamp() | |
| } | |
| 
 | |
| // =============================== | |
| // EXTRACT FUNCTION | |
| // =============================== | |
|  | |
| // DatePart represents the part of a date/time to extract | |
| type DatePart string | |
| 
 | |
| const ( | |
| 	PartYear      DatePart = "YEAR" | |
| 	PartMonth     DatePart = "MONTH" | |
| 	PartDay       DatePart = "DAY" | |
| 	PartHour      DatePart = "HOUR" | |
| 	PartMinute    DatePart = "MINUTE" | |
| 	PartSecond    DatePart = "SECOND" | |
| 	PartWeek      DatePart = "WEEK" | |
| 	PartDayOfYear DatePart = "DOY" | |
| 	PartDayOfWeek DatePart = "DOW" | |
| 	PartQuarter   DatePart = "QUARTER" | |
| 	PartEpoch     DatePart = "EPOCH" | |
| ) | |
| 
 | |
| // Extract extracts a specific part from a date/time value | |
| func (e *SQLEngine) Extract(part DatePart, value *schema_pb.Value) (*schema_pb.Value, error) { | |
| 	if value == nil { | |
| 		return nil, fmt.Errorf("EXTRACT function requires non-null value") | |
| 	} | |
| 
 | |
| 	// Convert value to time | |
| 	t, err := e.valueToTime(value) | |
| 	if err != nil { | |
| 		return nil, fmt.Errorf("EXTRACT function time conversion error: %v", err) | |
| 	} | |
| 
 | |
| 	var result int64 | |
| 
 | |
| 	switch strings.ToUpper(string(part)) { | |
| 	case string(PartYear): | |
| 		result = int64(t.Year()) | |
| 	case string(PartMonth): | |
| 		result = int64(t.Month()) | |
| 	case string(PartDay): | |
| 		result = int64(t.Day()) | |
| 	case string(PartHour): | |
| 		result = int64(t.Hour()) | |
| 	case string(PartMinute): | |
| 		result = int64(t.Minute()) | |
| 	case string(PartSecond): | |
| 		result = int64(t.Second()) | |
| 	case string(PartWeek): | |
| 		_, week := t.ISOWeek() | |
| 		result = int64(week) | |
| 	case string(PartDayOfYear): | |
| 		result = int64(t.YearDay()) | |
| 	case string(PartDayOfWeek): | |
| 		result = int64(t.Weekday()) | |
| 	case string(PartQuarter): | |
| 		month := t.Month() | |
| 		result = int64((month-1)/3 + 1) | |
| 	case string(PartEpoch): | |
| 		result = t.Unix() | |
| 	default: | |
| 		return nil, fmt.Errorf("unsupported date part: %s", part) | |
| 	} | |
| 
 | |
| 	return &schema_pb.Value{ | |
| 		Kind: &schema_pb.Value_Int64Value{Int64Value: result}, | |
| 	}, nil | |
| } | |
| 
 | |
| // =============================== | |
| // DATE_TRUNC FUNCTION | |
| // =============================== | |
|  | |
| // DateTrunc truncates a date/time to the specified precision | |
| func (e *SQLEngine) DateTrunc(precision string, value *schema_pb.Value) (*schema_pb.Value, error) { | |
| 	if value == nil { | |
| 		return nil, fmt.Errorf("DATE_TRUNC function requires non-null value") | |
| 	} | |
| 
 | |
| 	// Convert value to time | |
| 	t, err := e.valueToTime(value) | |
| 	if err != nil { | |
| 		return nil, fmt.Errorf("DATE_TRUNC function time conversion error: %v", err) | |
| 	} | |
| 
 | |
| 	var truncated time.Time | |
| 
 | |
| 	switch strings.ToLower(precision) { | |
| 	case "microsecond", "microseconds": | |
| 		// No truncation needed for microsecond precision | |
| 		truncated = t | |
| 	case "millisecond", "milliseconds": | |
| 		truncated = t.Truncate(time.Millisecond) | |
| 	case "second", "seconds": | |
| 		truncated = t.Truncate(time.Second) | |
| 	case "minute", "minutes": | |
| 		truncated = t.Truncate(time.Minute) | |
| 	case "hour", "hours": | |
| 		truncated = t.Truncate(time.Hour) | |
| 	case "day", "days": | |
| 		truncated = time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()) | |
| 	case "week", "weeks": | |
| 		// Truncate to beginning of week (Monday) | |
| 		days := int(t.Weekday()) | |
| 		if days == 0 { // Sunday = 0, adjust to make Monday = 0 | |
| 			days = 6 | |
| 		} else { | |
| 			days = days - 1 | |
| 		} | |
| 		truncated = time.Date(t.Year(), t.Month(), t.Day()-days, 0, 0, 0, 0, t.Location()) | |
| 	case "month", "months": | |
| 		truncated = time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, t.Location()) | |
| 	case "quarter", "quarters": | |
| 		month := t.Month() | |
| 		quarterMonth := ((int(month)-1)/3)*3 + 1 | |
| 		truncated = time.Date(t.Year(), time.Month(quarterMonth), 1, 0, 0, 0, 0, t.Location()) | |
| 	case "year", "years": | |
| 		truncated = time.Date(t.Year(), 1, 1, 0, 0, 0, 0, t.Location()) | |
| 	case "decade", "decades": | |
| 		year := (t.Year() / 10) * 10 | |
| 		truncated = time.Date(year, 1, 1, 0, 0, 0, 0, t.Location()) | |
| 	case "century", "centuries": | |
| 		year := ((t.Year()-1)/100)*100 + 1 | |
| 		truncated = time.Date(year, 1, 1, 0, 0, 0, 0, t.Location()) | |
| 	case "millennium", "millennia": | |
| 		year := ((t.Year()-1)/1000)*1000 + 1 | |
| 		truncated = time.Date(year, 1, 1, 0, 0, 0, 0, t.Location()) | |
| 	default: | |
| 		return nil, fmt.Errorf("unsupported date truncation precision: %s", precision) | |
| 	} | |
| 
 | |
| 	// Return as TimestampValue | |
| 	return &schema_pb.Value{ | |
| 		Kind: &schema_pb.Value_TimestampValue{ | |
| 			TimestampValue: &schema_pb.TimestampValue{ | |
| 				TimestampMicros: truncated.UnixMicro(), | |
| 			}, | |
| 		}, | |
| 	}, nil | |
| }
 |