cte.go 5.1 KB

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