limit with offset

This commit is contained in:
chrislu
2025-09-04 09:13:54 -07:00
parent 164158b2a1
commit 02d36637de
3 changed files with 53 additions and 45 deletions

View File

@@ -350,7 +350,8 @@ func (e *SQLEngine) executeAggregationQuery(ctx context.Context, hybridScanner *
// executeAggregationQueryWithPlan handles SELECT queries with aggregation functions and populates execution plan // executeAggregationQueryWithPlan handles SELECT queries with aggregation functions and populates execution plan
func (e *SQLEngine) executeAggregationQueryWithPlan(ctx context.Context, hybridScanner *HybridMessageScanner, aggregations []AggregationSpec, stmt *SelectStatement, plan *QueryExecutionPlan) (*QueryResult, error) { func (e *SQLEngine) executeAggregationQueryWithPlan(ctx context.Context, hybridScanner *HybridMessageScanner, aggregations []AggregationSpec, stmt *SelectStatement, plan *QueryExecutionPlan) (*QueryResult, error) {
// Parse LIMIT and OFFSET for aggregation results (do this first) // Parse LIMIT and OFFSET for aggregation results (do this first)
limit := 0 // Use -1 to distinguish "no LIMIT" from "LIMIT 0"
limit := -1
offset := 0 offset := 0
if stmt.Limit != nil && stmt.Limit.Rowcount != nil { if stmt.Limit != nil && stmt.Limit.Rowcount != nil {
if limitExpr, ok := stmt.Limit.Rowcount.(*SQLVal); ok && limitExpr.Type == IntVal { if limitExpr, ok := stmt.Limit.Rowcount.(*SQLVal); ok && limitExpr.Type == IntVal {
@@ -391,29 +392,31 @@ func (e *SQLEngine) executeAggregationQueryWithPlan(ctx context.Context, hybridS
if isDebugMode(ctx) { if isDebugMode(ctx) {
fmt.Printf("Using fast hybrid statistics for aggregation (parquet stats + live log counts)\n") fmt.Printf("Using fast hybrid statistics for aggregation (parquet stats + live log counts)\n")
} }
// Apply OFFSET and LIMIT to fast path results too // Apply OFFSET and LIMIT to fast path results too
if offset > 0 || limit > 0 { // Limit semantics: -1 = no limit, 0 = LIMIT 0 (empty), >0 = limit to N rows
if offset > 0 || limit >= 0 {
rows := fastResult.Rows rows := fastResult.Rows
// Apply OFFSET first // Handle LIMIT 0 first
if offset > 0 { if limit == 0 {
if offset >= len(rows) { rows = [][]sqltypes.Value{}
rows = [][]sqltypes.Value{} } else {
} else { // Apply OFFSET first
rows = rows[offset:] if offset > 0 {
if offset >= len(rows) {
rows = [][]sqltypes.Value{}
} else {
rows = rows[offset:]
}
} }
} // Apply LIMIT after OFFSET (only if limit > 0)
// Apply LIMIT after OFFSET if limit > 0 && len(rows) > limit {
if limit >= 0 { // Handle LIMIT 0 case
if limit == 0 {
rows = [][]sqltypes.Value{}
} else if len(rows) > limit {
rows = rows[:limit] rows = rows[:limit]
} }
} }
fastResult.Rows = rows fastResult.Rows = rows
} }
return fastResult, nil return fastResult, nil
} }
} }
@@ -484,22 +487,24 @@ func (e *SQLEngine) executeAggregationQueryWithPlan(ctx context.Context, hybridS
} }
// Apply OFFSET and LIMIT to aggregation results // Apply OFFSET and LIMIT to aggregation results
// Limit semantics: -1 = no limit, 0 = LIMIT 0 (empty), >0 = limit to N rows
rows := [][]sqltypes.Value{row} rows := [][]sqltypes.Value{row}
if offset > 0 || limit > 0 { if offset > 0 || limit >= 0 {
// Apply OFFSET first // Handle LIMIT 0 first
if offset > 0 { if limit == 0 {
if offset >= len(rows) { rows = [][]sqltypes.Value{}
rows = [][]sqltypes.Value{} } else {
} else { // Apply OFFSET first
rows = rows[offset:] if offset > 0 {
if offset >= len(rows) {
rows = [][]sqltypes.Value{}
} else {
rows = rows[offset:]
}
} }
}
// Apply LIMIT after OFFSET // Apply LIMIT after OFFSET (only if limit > 0)
if limit >= 0 { // Handle LIMIT 0 case if limit > 0 && len(rows) > limit {
if limit == 0 {
rows = [][]sqltypes.Value{}
} else if len(rows) > limit {
rows = rows[:limit] rows = rows[:limit]
} }
} }

View File

@@ -1416,7 +1416,8 @@ func (e *SQLEngine) executeSelectStatement(ctx context.Context, stmt *SelectStat
} }
// Parse LIMIT and OFFSET clauses // Parse LIMIT and OFFSET clauses
limit := 0 // Use -1 to distinguish "no LIMIT" from "LIMIT 0"
limit := -1
offset := 0 offset := 0
if stmt.Limit != nil && stmt.Limit.Rowcount != nil { if stmt.Limit != nil && stmt.Limit.Rowcount != nil {
switch limitExpr := stmt.Limit.Rowcount.(type) { switch limitExpr := stmt.Limit.Rowcount.(type) {
@@ -1605,7 +1606,8 @@ func (e *SQLEngine) executeSelectStatementWithBrokerStats(ctx context.Context, s
} }
// Parse LIMIT and OFFSET clauses // Parse LIMIT and OFFSET clauses
limit := 0 // Use -1 to distinguish "no LIMIT" from "LIMIT 0"
limit := -1
offset := 0 offset := 0
if stmt.Limit != nil && stmt.Limit.Rowcount != nil { if stmt.Limit != nil && stmt.Limit.Rowcount != nil {
switch limitExpr := stmt.Limit.Rowcount.(type) { switch limitExpr := stmt.Limit.Rowcount.(type) {

View File

@@ -233,25 +233,26 @@ func (hms *HybridMessageScanner) ScanWithStats(ctx context.Context, options Hybr
} }
// Apply final OFFSET and LIMIT processing (done once at the end) // Apply final OFFSET and LIMIT processing (done once at the end)
// Limit semantics: -1 = no limit, 0 = LIMIT 0 (empty), >0 = limit to N rows
if options.Offset > 0 || options.Limit >= 0 { if options.Offset > 0 || options.Limit >= 0 {
// Handle LIMIT 0 special case - return empty result immediately // Handle LIMIT 0 special case first
if options.Limit == 0 { if options.Limit == 0 {
results = []HybridScanResult{} return []HybridScanResult{}, stats, nil
} else { }
// Apply OFFSET first
if options.Offset > 0 {
if options.Offset >= len(results) {
results = []HybridScanResult{}
} else {
results = results[options.Offset:]
}
}
// Apply LIMIT after OFFSET // Apply OFFSET first
if options.Limit > 0 && len(results) > options.Limit { if options.Offset > 0 {
results = results[:options.Limit] if options.Offset >= len(results) {
results = []HybridScanResult{}
} else {
results = results[options.Offset:]
} }
} }
// Apply LIMIT after OFFSET (only if limit > 0)
if options.Limit > 0 && len(results) > options.Limit {
results = results[:options.Limit]
}
} }
return results, stats, nil return results, stats, nil