cte.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. package executor
  2. import (
  3. "fmt"
  4. "strings"
  5. "github.com/danfragoso/pizzasql-next/pkg/parser"
  6. "github.com/danfragoso/pizzasql-next/pkg/storage"
  7. )
  8. // maxRecursiveCTEIterations bounds fixpoint iteration so a recursive CTE with a
  9. // cycle cannot spin forever.
  10. const maxRecursiveCTEIterations = 1000
  11. // cteTable is a materialized common table expression. types is positionally
  12. // aligned with columns and carries the result types the CTE's SELECT reported,
  13. // so array subscripts and comparisons in the outer query keep their typing.
  14. type cteTable struct {
  15. columns []string
  16. types []string
  17. rows []storage.Row
  18. }
  19. // cteTableFor returns the materialized CTE named name, if any.
  20. func (e *Executor) cteTableFor(name string) (*cteTable, bool) {
  21. if e.cteTables == nil {
  22. return nil, false
  23. }
  24. t, ok := e.cteTables[strings.ToLower(name)]
  25. return t, ok
  26. }
  27. // executeWith materializes every CTE in order, then runs the main query with the
  28. // CTE tables available to table resolution. Nested WITH clauses keep the outer
  29. // tables visible.
  30. func (e *Executor) executeWith(stmt *parser.SelectStmt) (*Result, error) {
  31. prev := e.cteTables
  32. next := make(map[string]*cteTable, len(stmt.With)+len(prev))
  33. for k, v := range prev {
  34. next[k] = v
  35. }
  36. e.cteTables = next
  37. defer func() { e.cteTables = prev }()
  38. for _, cte := range stmt.With {
  39. if err := e.materializeCTE(cte); err != nil {
  40. return nil, err
  41. }
  42. }
  43. main := *stmt
  44. main.With = nil
  45. return e.executeSelect(&main)
  46. }
  47. func (e *Executor) materializeCTE(cte *parser.CTE) error {
  48. // WITH RECURSIVE marks the whole clause; only a compound CTE with a
  49. // self-referencing leg is actually recursive. A plain SELECT is just a CTE.
  50. compound := cte.Query.Compound
  51. if !cte.Recursive || compound == nil {
  52. res, err := e.executeSelect(cte.Query)
  53. if err != nil {
  54. return err
  55. }
  56. e.registerCTEResult(cte.Name, cte.Columns, res)
  57. return nil
  58. }
  59. anchor, err := e.executeSelect(compound.Left)
  60. if err != nil {
  61. return err
  62. }
  63. cols := cte.Columns
  64. if len(cols) == 0 {
  65. cols = anchor.Columns
  66. }
  67. accumulated := resultToCTERows(anchor, cols)
  68. working := accumulated
  69. distinct := compound.Op != parser.SetOpUnionAll
  70. seen := make(map[string]bool)
  71. if distinct {
  72. for _, row := range accumulated {
  73. seen[cteRowKey(row, cols)] = true
  74. }
  75. }
  76. for i := 0; i < maxRecursiveCTEIterations; i++ {
  77. e.cteTables[strings.ToLower(cte.Name)] = &cteTable{columns: cols, types: anchor.ColumnTypes, rows: working}
  78. recursive, err := e.executeSelect(compound.Right)
  79. if err != nil {
  80. return err
  81. }
  82. fresh := make([]storage.Row, 0)
  83. for _, row := range resultToCTERows(recursive, cols) {
  84. if distinct {
  85. key := cteRowKey(row, cols)
  86. if seen[key] {
  87. continue
  88. }
  89. seen[key] = true
  90. }
  91. fresh = append(fresh, row)
  92. }
  93. if len(fresh) == 0 {
  94. break
  95. }
  96. accumulated = append(accumulated, fresh...)
  97. working = fresh
  98. }
  99. e.cteTables[strings.ToLower(cte.Name)] = &cteTable{columns: cols, types: anchor.ColumnTypes, rows: accumulated}
  100. return nil
  101. }
  102. // registerCTEResult stores a query result as a CTE. Declared column names win;
  103. // otherwise the result's own column names are used.
  104. func (e *Executor) registerCTEResult(name string, declared []string, res *Result) {
  105. cols := declared
  106. if len(cols) == 0 {
  107. cols = res.Columns
  108. }
  109. e.cteTables[strings.ToLower(name)] = &cteTable{columns: cols, types: res.ColumnTypes, rows: resultToCTERows(res, cols)}
  110. }
  111. // resultToCTERows maps each result row to a storage.Row keyed by cols
  112. // positionally, which renames a recursive term's columns to the anchor's names.
  113. func resultToCTERows(res *Result, cols []string) []storage.Row {
  114. rows := make([]storage.Row, 0, len(res.Rows))
  115. for _, values := range res.Rows {
  116. row := make(storage.Row, len(cols))
  117. for i, name := range cols {
  118. if i < len(values) {
  119. row[name] = values[i]
  120. }
  121. }
  122. rows = append(rows, row)
  123. }
  124. return rows
  125. }
  126. // cteRowsToValues converts a materialized CTE to positional row values.
  127. func cteRowsToValues(cte *cteTable) [][]interface{} {
  128. values := make([][]interface{}, len(cte.rows))
  129. for i, row := range cte.rows {
  130. vals := make([]interface{}, len(cte.columns))
  131. for j, col := range cte.columns {
  132. vals[j] = row[col]
  133. }
  134. values[i] = vals
  135. }
  136. return values
  137. }
  138. // cteTableExists reports whether name is a materialized CTE.
  139. func (e *Executor) cteTableExists(name string) bool {
  140. _, ok := e.cteTableFor(name)
  141. return ok
  142. }
  143. // materializeJoinSubquery runs a derived table used on the right side of a JOIN
  144. // and returns its rows, schema, and the result column types.
  145. func (e *Executor) materializeJoinSubquery(ref *parser.TableRef) ([]storage.Row, *storage.Schema, []string, []string, error) {
  146. res, err := e.executeSelect(ref.Subquery)
  147. if err != nil {
  148. return nil, nil, nil, nil, err
  149. }
  150. rows := make([]storage.Row, 0, len(res.Rows))
  151. for _, values := range res.Rows {
  152. row := make(storage.Row, len(res.Columns))
  153. for i, col := range res.Columns {
  154. if i < len(values) {
  155. row[col] = values[i]
  156. }
  157. }
  158. rows = append(rows, row)
  159. }
  160. schema := &storage.Schema{Name: "derived", Columns: make([]storage.Column, len(res.Columns))}
  161. for i, col := range res.Columns {
  162. schema.Columns[i] = storage.Column{Name: col, Type: typeAt(res.ColumnTypes, i)}
  163. }
  164. return rows, schema, res.Columns, res.ColumnTypes, nil
  165. }
  166. // cloneRows returns a shallow copy of each row so callers cannot mutate a
  167. // materialized CTE's stored rows.
  168. func cloneRows(rows []storage.Row) []storage.Row {
  169. out := make([]storage.Row, len(rows))
  170. for i, r := range rows {
  171. c := make(storage.Row, len(r))
  172. for k, v := range r {
  173. c[k] = v
  174. }
  175. out[i] = c
  176. }
  177. return out
  178. }
  179. func cteRowKey(row storage.Row, cols []string) string {
  180. var b strings.Builder
  181. for _, c := range cols {
  182. fmt.Fprintf(&b, "%v\x00", row[c])
  183. }
  184. return b.String()
  185. }