2
0

scope.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. package analyzer
  2. import (
  3. "strings"
  4. "sync"
  5. )
  6. // Scope represents a symbol table scope for name resolution.
  7. type Scope struct {
  8. mu sync.RWMutex
  9. parent *Scope
  10. tables map[string]*TableInfo // Tables in this scope
  11. columns map[string]*ColumnInfo // Direct column references (for single table queries)
  12. }
  13. // NewScope creates a new empty scope.
  14. func NewScope(parent *Scope) *Scope {
  15. return &Scope{
  16. parent: parent,
  17. tables: make(map[string]*TableInfo),
  18. columns: make(map[string]*ColumnInfo),
  19. }
  20. }
  21. // DefineTable adds a table to this scope.
  22. func (s *Scope) DefineTable(info *TableInfo) {
  23. s.mu.Lock()
  24. defer s.mu.Unlock()
  25. name := strings.ToUpper(info.Name)
  26. s.tables[name] = info
  27. // If there's an alias, also register by alias
  28. if info.Alias != "" {
  29. s.tables[strings.ToUpper(info.Alias)] = info
  30. }
  31. // Add columns to direct reference if this is the only table
  32. // This allows unqualified column references
  33. for i := range info.Columns {
  34. col := &info.Columns[i]
  35. s.columns[strings.ToUpper(col.Name)] = col
  36. }
  37. }
  38. // DefineSelectAlias registers a SELECT column alias for ORDER BY/HAVING reference.
  39. func (s *Scope) DefineSelectAlias(alias string, colType Type) {
  40. s.mu.Lock()
  41. defer s.mu.Unlock()
  42. upper := strings.ToUpper(alias)
  43. // Create a virtual column for the alias
  44. s.columns[upper] = &ColumnInfo{
  45. Name: alias,
  46. Type: colType,
  47. }
  48. }
  49. // LookupTable finds a table by name or alias.
  50. func (s *Scope) LookupTable(name string) (*TableInfo, bool) {
  51. s.mu.RLock()
  52. upper := strings.ToUpper(name)
  53. t, ok := s.tables[upper]
  54. s.mu.RUnlock()
  55. if ok {
  56. return t, true
  57. }
  58. if s.parent != nil {
  59. return s.parent.LookupTable(name)
  60. }
  61. return nil, false
  62. }
  63. // LookupColumn finds a column, optionally qualified by table name.
  64. func (s *Scope) LookupColumn(tableName, columnName string) (*ColumnInfo, *TableInfo, bool) {
  65. upperCol := strings.ToUpper(columnName)
  66. if tableName != "" {
  67. // Qualified reference: table.column
  68. table, ok := s.LookupTable(tableName)
  69. if !ok {
  70. return nil, nil, false
  71. }
  72. col, ok := table.GetColumn(columnName)
  73. if !ok {
  74. return nil, table, false
  75. }
  76. return col, table, true
  77. }
  78. s.mu.RLock()
  79. // Unqualified reference: try direct column lookup first
  80. if col, ok := s.columns[upperCol]; ok {
  81. // Find which table this column belongs to
  82. for _, t := range s.tables {
  83. if _, found := t.GetColumn(columnName); found {
  84. s.mu.RUnlock()
  85. return col, t, true
  86. }
  87. }
  88. s.mu.RUnlock()
  89. return col, nil, true
  90. }
  91. // Search all tables in scope
  92. var foundCol *ColumnInfo
  93. var foundTable *TableInfo
  94. ambiguous := false
  95. for _, table := range s.tables {
  96. if col, ok := table.GetColumn(columnName); ok {
  97. if foundCol != nil {
  98. ambiguous = true
  99. }
  100. foundCol = col
  101. foundTable = table
  102. }
  103. }
  104. s.mu.RUnlock()
  105. if ambiguous {
  106. // Return nil to indicate ambiguous reference
  107. return nil, nil, false
  108. }
  109. if foundCol != nil {
  110. return foundCol, foundTable, true
  111. }
  112. // Try parent scope
  113. if s.parent != nil {
  114. return s.parent.LookupColumn("", columnName)
  115. }
  116. return nil, nil, false
  117. }
  118. // GetAllColumns returns all columns available in this scope.
  119. func (s *Scope) GetAllColumns() []*ColumnInfo {
  120. s.mu.RLock()
  121. defer s.mu.RUnlock()
  122. var cols []*ColumnInfo
  123. seen := make(map[string]bool)
  124. for _, table := range s.tables {
  125. for i := range table.Columns {
  126. col := &table.Columns[i]
  127. key := strings.ToUpper(table.Name + "." + col.Name)
  128. if !seen[key] {
  129. seen[key] = true
  130. cols = append(cols, col)
  131. }
  132. }
  133. }
  134. return cols
  135. }
  136. // GetTables returns all tables in this scope.
  137. func (s *Scope) GetTables() []*TableInfo {
  138. s.mu.RLock()
  139. defer s.mu.RUnlock()
  140. var tables []*TableInfo
  141. seen := make(map[string]bool)
  142. for name, table := range s.tables {
  143. // Use actual table name to avoid duplicates from aliases
  144. key := strings.ToUpper(table.Name)
  145. if !seen[key] {
  146. seen[key] = true
  147. _ = name // Silence unused variable
  148. tables = append(tables, table)
  149. }
  150. }
  151. return tables
  152. }
  153. // Catalog represents the database schema catalog.
  154. type Catalog struct {
  155. mu sync.RWMutex
  156. tables map[string]*TableInfo
  157. }
  158. // NewCatalog creates a new empty catalog.
  159. func NewCatalog() *Catalog {
  160. return &Catalog{
  161. tables: make(map[string]*TableInfo),
  162. }
  163. }
  164. // CreateTable adds a table to the catalog.
  165. func (c *Catalog) CreateTable(info *TableInfo) error {
  166. c.mu.Lock()
  167. defer c.mu.Unlock()
  168. name := strings.ToUpper(info.Name)
  169. if _, exists := c.tables[name]; exists {
  170. return &AnalysisError{
  171. Type: ErrTableExists,
  172. Message: "table already exists: " + info.Name,
  173. }
  174. }
  175. c.tables[name] = info
  176. return nil
  177. }
  178. // DropTable removes a table from the catalog.
  179. func (c *Catalog) DropTable(name string) error {
  180. c.mu.Lock()
  181. defer c.mu.Unlock()
  182. upper := strings.ToUpper(name)
  183. if _, exists := c.tables[upper]; !exists {
  184. return &AnalysisError{
  185. Type: ErrTableNotFound,
  186. Message: "table not found: " + name,
  187. }
  188. }
  189. delete(c.tables, upper)
  190. return nil
  191. }
  192. // GetTable returns a table by name.
  193. func (c *Catalog) GetTable(name string) (*TableInfo, bool) {
  194. c.mu.RLock()
  195. defer c.mu.RUnlock()
  196. t, ok := c.tables[strings.ToUpper(name)]
  197. return t, ok
  198. }
  199. // GetTables returns all tables in the catalog.
  200. func (c *Catalog) GetTables() []*TableInfo {
  201. c.mu.RLock()
  202. defer c.mu.RUnlock()
  203. var tables []*TableInfo
  204. for _, t := range c.tables {
  205. tables = append(tables, t)
  206. }
  207. return tables
  208. }
  209. // TableExists returns true if a table exists.
  210. func (c *Catalog) TableExists(name string) bool {
  211. c.mu.RLock()
  212. defer c.mu.RUnlock()
  213. _, ok := c.tables[strings.ToUpper(name)]
  214. return ok
  215. }