Skip to content

Commit 365a60f

Browse files
committed
Refactor select response logic
1 parent 04ccd64 commit 365a60f

4 files changed

Lines changed: 189 additions & 127 deletions

File tree

e2e/e2e_test.sh

100644100755
File mode changed.

engine/engine.go

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -99,38 +99,32 @@ func (engine *DbEngine) getSelectResponse(selectCommand *ast.SelectCommand) (*Ta
9999
return nil, err
100100
}
101101

102-
// Build transformation pipeline
103-
var transformers []TableTransformer
102+
processor := NewSelectProcessor(engine, selectCommand)
104103

104+
// Build the transformation pipeline using the builder pattern
105105
if selectCommand.HasOrderByCommand() {
106-
transformers = append(transformers, engine.withOrderBy(selectCommand))
106+
processor.WithOrderByClause()
107107
}
108108

109109
if selectCommand.HasWhereCommand() {
110-
transformers = append(transformers, engine.withWhere(selectCommand))
110+
processor.WithWhereClause()
111111
}
112112

113-
if len(transformers) == 0 {
114-
transformers = append(transformers, engine.withVanillaSelect(selectCommand))
113+
// If no WHERE or ORDER BY, the vanilla select (projection) is applied first.
114+
// Otherwise, WHERE/ORDER BY are applied, and then the projection happens within them (handled by their respective transformers).
115+
if !selectCommand.HasOrderByCommand() && !selectCommand.HasWhereCommand() {
116+
processor.WithVanillaSelectClause()
115117
}
116118

117119
if selectCommand.HasOffsetCommand() || selectCommand.HasLimitCommand() {
118-
transformers = append(transformers, engine.withOffsetLimit(selectCommand))
120+
processor.WithOffsetLimitClause()
119121
}
120122

121123
if selectCommand.HasDistinct {
122-
transformers = append(transformers, engine.withDistinct())
124+
processor.WithDistinctClause()
123125
}
124126

125-
// Apply transformations
126-
for _, transform := range transformers {
127-
table, err = transform(table)
128-
if err != nil {
129-
return nil, err
130-
}
131-
}
132-
133-
return table, nil
127+
return processor.Process(table)
134128
}
135129

136130
// createTable - initialize new table in engine with specified name

engine/query_processor.go

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
package engine
2+
3+
import (
4+
"hash/adler32"
5+
6+
"github.com/LissaGreense/GO4SQL/ast"
7+
"github.com/LissaGreense/GO4SQL/token"
8+
)
9+
10+
// TableTransformer defines a function that takes a Table as input and then applies a transformation
11+
type TableTransformer func(*Table) (*Table, error)
12+
13+
// SelectProcessor handles the step-by-step processing of a SELECT query using a builder pattern.
14+
type SelectProcessor struct {
15+
engine *DbEngine
16+
cmd *ast.SelectCommand
17+
transformers []TableTransformer
18+
}
19+
20+
// NewSelectProcessor creates a new SelectProcessor.
21+
func NewSelectProcessor(engine *DbEngine, cmd *ast.SelectCommand) *SelectProcessor {
22+
return &SelectProcessor{
23+
engine: engine,
24+
cmd: cmd,
25+
transformers: []TableTransformer{}, // Initialize as empty slice
26+
}
27+
}
28+
29+
// WithVanillaSelectClause adds the vanilla select (projection) transformation.
30+
func (sp *SelectProcessor) WithVanillaSelectClause() *SelectProcessor {
31+
sp.transformers = append(sp.transformers, sp.getVanillaSelectTransformer())
32+
return sp
33+
}
34+
35+
// WithWhereClause adds the WHERE clause transformation.
36+
func (sp *SelectProcessor) WithWhereClause() *SelectProcessor {
37+
sp.transformers = append(sp.transformers, sp.getWhereTransformer())
38+
return sp
39+
}
40+
41+
// WithOrderByClause adds the ORDER BY clause transformation.
42+
func (sp *SelectProcessor) WithOrderByClause() *SelectProcessor {
43+
sp.transformers = append(sp.transformers, sp.getOrderByTransformer())
44+
return sp
45+
}
46+
47+
// WithOffsetLimitClause adds the OFFSET and LIMIT clause transformation.
48+
func (sp *SelectProcessor) WithOffsetLimitClause() *SelectProcessor {
49+
sp.transformers = append(sp.transformers, sp.getOffsetLimitTransformer())
50+
return sp
51+
}
52+
53+
// WithDistinctClause adds the DISTINCT clause transformation.
54+
func (sp *SelectProcessor) WithDistinctClause() *SelectProcessor {
55+
sp.transformers = append(sp.transformers, sp.getDistinctTransformer())
56+
return sp
57+
}
58+
59+
// Process applies the configured pipeline of transformations to the initialTable.
60+
func (sp *SelectProcessor) Process(initialTable *Table) (*Table, error) {
61+
table := initialTable
62+
var err error
63+
64+
for _, transform := range sp.transformers {
65+
table, err = transform(table)
66+
if err != nil {
67+
return nil, err
68+
}
69+
}
70+
return table, nil
71+
}
72+
73+
// --- Private Transformer Getters ---
74+
75+
func (sp *SelectProcessor) getVanillaSelectTransformer() TableTransformer {
76+
return func(tbl *Table) (*Table, error) {
77+
return sp.engine.selectFromProvidedTable(sp.cmd, tbl)
78+
}
79+
}
80+
81+
func (sp *SelectProcessor) getWhereTransformer() TableTransformer {
82+
return func(tbl *Table) (*Table, error) {
83+
if len(tbl.Columns) == 0 || (len(tbl.Columns) > 0 && len(tbl.Columns[0].Values) == 0) {
84+
return sp.engine.selectFromProvidedTable(sp.cmd, &Table{Columns: []*Column{}})
85+
}
86+
filtered, err := sp.engine.getFilteredTable(tbl, sp.cmd.WhereCommand, false, sp.cmd.Name.GetToken().Literal)
87+
if err != nil {
88+
return nil, err
89+
}
90+
return sp.engine.selectFromProvidedTable(sp.cmd, filtered)
91+
}
92+
}
93+
94+
func (sp *SelectProcessor) getOrderByTransformer() TableTransformer {
95+
return func(tbl *Table) (*Table, error) {
96+
emptyTable := getCopyOfTableWithoutRows(tbl)
97+
sorted, err := sp.engine.getSortedTable(sp.cmd.OrderByCommand, tbl, emptyTable, sp.cmd.Name.GetToken().Literal)
98+
if err != nil {
99+
return nil, err
100+
}
101+
return sp.engine.selectFromProvidedTable(sp.cmd, sorted)
102+
}
103+
}
104+
105+
func (sp *SelectProcessor) getOffsetLimitTransformer() TableTransformer {
106+
return func(tbl *Table) (*Table, error) {
107+
var offset = 0
108+
var limitRaw = -1
109+
110+
if sp.cmd.HasLimitCommand() {
111+
limitRaw = sp.cmd.LimitCommand.Count
112+
}
113+
if sp.cmd.HasOffsetCommand() {
114+
offset = sp.cmd.OffsetCommand.Count
115+
}
116+
117+
if len(tbl.Columns) == 0 {
118+
return tbl, nil
119+
}
120+
121+
for _, column := range tbl.Columns {
122+
var limit int
123+
124+
if limitRaw == -1 || limitRaw+offset > len(column.Values) {
125+
limit = len(column.Values)
126+
} else {
127+
limit = limitRaw + offset
128+
}
129+
130+
if offset >= len(column.Values) {
131+
column.Values = make([]ValueInterface, 0)
132+
} else if offset < len(column.Values) && limit > offset {
133+
column.Values = column.Values[offset:limit]
134+
} else {
135+
column.Values = make([]ValueInterface, 0)
136+
}
137+
}
138+
return tbl, nil
139+
}
140+
}
141+
142+
func (sp *SelectProcessor) getDistinctTransformer() TableTransformer {
143+
return func(tbl *Table) (*Table, error) {
144+
if len(tbl.Columns) == 0 || len(tbl.Columns[0].Values) == 0 {
145+
return tbl, nil
146+
}
147+
148+
distinctTable := getCopyOfTableWithoutRows(tbl)
149+
rowsCount := len(tbl.Columns[0].Values)
150+
checksumSet := make(map[uint32]struct{})
151+
152+
for iRow := range rowsCount {
153+
mergedColumnValues := ""
154+
for iColumn := range tbl.Columns {
155+
if iRow < len(tbl.Columns[iColumn].Values) {
156+
fieldValue := tbl.Columns[iColumn].Values[iRow].ToString()
157+
if tbl.Columns[iColumn].Type.Literal == token.TEXT {
158+
fieldValue = "'" + fieldValue + "'"
159+
}
160+
mergedColumnValues += fieldValue
161+
} else {
162+
mergedColumnValues += "<missing_val>"
163+
}
164+
}
165+
checksum := adler32.Checksum([]byte(mergedColumnValues))
166+
167+
if _, exist := checksumSet[checksum]; !exist {
168+
checksumSet[checksum] = struct{}{}
169+
for i, column := range distinctTable.Columns {
170+
if iRow < len(tbl.Columns[i].Values) {
171+
column.Values = append(column.Values, tbl.Columns[i].Values[iRow])
172+
}
173+
}
174+
}
175+
}
176+
return distinctTable, nil
177+
}
178+
}

engine/table_transformations.go

Lines changed: 0 additions & 110 deletions
This file was deleted.

0 commit comments

Comments
 (0)