types.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. package analyzer
  2. import "strings"
  3. // Type represents a SQL type with SQLite affinity rules.
  4. type Type int
  5. const (
  6. TypeUnknown Type = iota // Unresolved type
  7. TypeNull // NULL value
  8. TypeInteger // INTEGER affinity
  9. TypeReal // REAL affinity
  10. TypeText // TEXT affinity
  11. TypeBlob // BLOB affinity
  12. TypeNumeric // NUMERIC affinity (flexible)
  13. TypeBoolean // Boolean (stored as INTEGER in SQLite)
  14. TypeAny // Any type (for polymorphic functions)
  15. )
  16. func (t Type) String() string {
  17. switch t {
  18. case TypeUnknown:
  19. return "UNKNOWN"
  20. case TypeNull:
  21. return "NULL"
  22. case TypeInteger:
  23. return "INTEGER"
  24. case TypeReal:
  25. return "REAL"
  26. case TypeText:
  27. return "TEXT"
  28. case TypeBlob:
  29. return "BLOB"
  30. case TypeNumeric:
  31. return "NUMERIC"
  32. case TypeBoolean:
  33. return "BOOLEAN"
  34. case TypeAny:
  35. return "ANY"
  36. default:
  37. return "UNKNOWN"
  38. }
  39. }
  40. // TypeFromName returns the Type for a SQL type name using SQLite affinity rules.
  41. // See: https://www.sqlite.org/datatype3.html
  42. func TypeFromName(name string) Type {
  43. upper := strings.ToUpper(name)
  44. // Rule 1: If the type contains "INT" -> INTEGER
  45. if strings.Contains(upper, "INT") {
  46. return TypeInteger
  47. }
  48. // Rule 2: If the type contains "CHAR", "CLOB", or "TEXT" -> TEXT
  49. if strings.Contains(upper, "CHAR") ||
  50. strings.Contains(upper, "CLOB") ||
  51. strings.Contains(upper, "TEXT") {
  52. return TypeText
  53. }
  54. // Rule 3: If the type contains "BLOB" or is empty -> BLOB
  55. if strings.Contains(upper, "BLOB") || upper == "" {
  56. return TypeBlob
  57. }
  58. // Rule 4: If the type contains "REAL", "FLOA", or "DOUB" -> REAL
  59. if strings.Contains(upper, "REAL") ||
  60. strings.Contains(upper, "FLOA") ||
  61. strings.Contains(upper, "DOUB") {
  62. return TypeReal
  63. }
  64. // Rule 5: Otherwise -> NUMERIC
  65. // This includes NUMERIC, DECIMAL, BOOLEAN, DATE, DATETIME
  66. switch upper {
  67. case "BOOLEAN", "BOOL":
  68. return TypeBoolean
  69. default:
  70. return TypeNumeric
  71. }
  72. }
  73. // IsNumeric returns true if the type can hold numeric values.
  74. func (t Type) IsNumeric() bool {
  75. switch t {
  76. case TypeInteger, TypeReal, TypeNumeric, TypeBoolean:
  77. return true
  78. default:
  79. return false
  80. }
  81. }
  82. // IsComparable returns true if two types can be compared.
  83. func (t Type) IsComparable(other Type) bool {
  84. // NULL is comparable to anything
  85. if t == TypeNull || other == TypeNull {
  86. return true
  87. }
  88. // ANY matches anything
  89. if t == TypeAny || other == TypeAny {
  90. return true
  91. }
  92. // Same type
  93. if t == other {
  94. return true
  95. }
  96. // Numeric types are inter-comparable
  97. if t.IsNumeric() && other.IsNumeric() {
  98. return true
  99. }
  100. // TEXT and BLOB can be compared
  101. if (t == TypeText || t == TypeBlob) && (other == TypeText || other == TypeBlob) {
  102. return true
  103. }
  104. return false
  105. }
  106. // CommonType returns the common type for binary operations.
  107. func CommonType(a, b Type) Type {
  108. if a == TypeUnknown {
  109. return b
  110. }
  111. if b == TypeUnknown {
  112. return a
  113. }
  114. if a == TypeNull {
  115. return b
  116. }
  117. if b == TypeNull {
  118. return a
  119. }
  120. if a == TypeAny {
  121. return b
  122. }
  123. if b == TypeAny {
  124. return a
  125. }
  126. if a == b {
  127. return a
  128. }
  129. // Numeric promotion
  130. if a.IsNumeric() && b.IsNumeric() {
  131. if a == TypeReal || b == TypeReal {
  132. return TypeReal
  133. }
  134. if a == TypeNumeric || b == TypeNumeric {
  135. return TypeNumeric
  136. }
  137. return TypeInteger
  138. }
  139. // Text/Blob coercion
  140. if (a == TypeText || a == TypeBlob) && (b == TypeText || b == TypeBlob) {
  141. return TypeText
  142. }
  143. return TypeText // Default to TEXT for mixed types
  144. }
  145. // FunctionSignature describes a SQL function.
  146. type FunctionSignature struct {
  147. Name string
  148. MinArgs int
  149. MaxArgs int // -1 for variadic
  150. ArgTypes []Type // Expected argument types (TypeAny for flexible)
  151. ReturnType Type
  152. IsAggregate bool
  153. }
  154. // builtinFunctions contains all built-in SQL functions.
  155. var builtinFunctions = map[string]FunctionSignature{
  156. // Aggregate functions
  157. "COUNT": {Name: "COUNT", MinArgs: 0, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeInteger, IsAggregate: true},
  158. "SUM": {Name: "SUM", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeNumeric}, ReturnType: TypeNumeric, IsAggregate: true},
  159. "AVG": {Name: "AVG", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeNumeric}, ReturnType: TypeReal, IsAggregate: true},
  160. "MIN": {Name: "MIN", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeAny, IsAggregate: true},
  161. "MAX": {Name: "MAX", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeAny, IsAggregate: true},
  162. "TOTAL": {Name: "TOTAL", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeNumeric}, ReturnType: TypeReal, IsAggregate: true},
  163. "GROUP_CONCAT": {Name: "GROUP_CONCAT", MinArgs: 1, MaxArgs: 2, ArgTypes: []Type{TypeAny, TypeText}, ReturnType: TypeText, IsAggregate: true},
  164. // String functions
  165. "LENGTH": {Name: "LENGTH", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeText}, ReturnType: TypeInteger, IsAggregate: false},
  166. "UPPER": {Name: "UPPER", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeText}, ReturnType: TypeText, IsAggregate: false},
  167. "LOWER": {Name: "LOWER", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeText}, ReturnType: TypeText, IsAggregate: false},
  168. "TRIM": {Name: "TRIM", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeText}, ReturnType: TypeText, IsAggregate: false},
  169. "LTRIM": {Name: "LTRIM", MinArgs: 1, MaxArgs: 2, ArgTypes: []Type{TypeText, TypeText}, ReturnType: TypeText, IsAggregate: false},
  170. "RTRIM": {Name: "RTRIM", MinArgs: 1, MaxArgs: 2, ArgTypes: []Type{TypeText, TypeText}, ReturnType: TypeText, IsAggregate: false},
  171. "SUBSTR": {Name: "SUBSTR", MinArgs: 2, MaxArgs: 3, ArgTypes: []Type{TypeText, TypeInteger, TypeInteger}, ReturnType: TypeText, IsAggregate: false},
  172. "REPLACE": {Name: "REPLACE", MinArgs: 3, MaxArgs: 3, ArgTypes: []Type{TypeText, TypeText, TypeText}, ReturnType: TypeText, IsAggregate: false},
  173. "INSTR": {Name: "INSTR", MinArgs: 2, MaxArgs: 2, ArgTypes: []Type{TypeText, TypeText}, ReturnType: TypeInteger, IsAggregate: false},
  174. "PRINTF": {Name: "PRINTF", MinArgs: 1, MaxArgs: -1, ArgTypes: []Type{TypeText}, ReturnType: TypeText, IsAggregate: false},
  175. "CONCAT": {Name: "CONCAT", MinArgs: 1, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  176. // Numeric functions
  177. "ABS": {Name: "ABS", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeNumeric}, ReturnType: TypeNumeric, IsAggregate: false},
  178. "ROUND": {Name: "ROUND", MinArgs: 1, MaxArgs: 2, ArgTypes: []Type{TypeNumeric, TypeInteger}, ReturnType: TypeNumeric, IsAggregate: false},
  179. "CEIL": {Name: "CEIL", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeNumeric}, ReturnType: TypeInteger, IsAggregate: false},
  180. "FLOOR": {Name: "FLOOR", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeNumeric}, ReturnType: TypeInteger, IsAggregate: false},
  181. "MOD": {Name: "MOD", MinArgs: 2, MaxArgs: 2, ArgTypes: []Type{TypeInteger, TypeInteger}, ReturnType: TypeInteger, IsAggregate: false},
  182. "RANDOM": {Name: "RANDOM", MinArgs: 0, MaxArgs: 0, ArgTypes: []Type{}, ReturnType: TypeInteger, IsAggregate: false},
  183. // Null handling
  184. "COALESCE": {Name: "COALESCE", MinArgs: 1, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  185. "NULLIF": {Name: "NULLIF", MinArgs: 2, MaxArgs: 2, ArgTypes: []Type{TypeAny, TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  186. "IFNULL": {Name: "IFNULL", MinArgs: 2, MaxArgs: 2, ArgTypes: []Type{TypeAny, TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  187. "IIF": {Name: "IIF", MinArgs: 3, MaxArgs: 3, ArgTypes: []Type{TypeBoolean, TypeAny, TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  188. // Type functions
  189. "TYPEOF": {Name: "TYPEOF", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  190. "CAST": {Name: "CAST", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  191. // Date/Time functions
  192. "DATE": {Name: "DATE", MinArgs: 1, MaxArgs: -1, ArgTypes: []Type{TypeText}, ReturnType: TypeText, IsAggregate: false},
  193. "TIME": {Name: "TIME", MinArgs: 1, MaxArgs: -1, ArgTypes: []Type{TypeText}, ReturnType: TypeText, IsAggregate: false},
  194. "DATETIME": {Name: "DATETIME", MinArgs: 1, MaxArgs: -1, ArgTypes: []Type{TypeText}, ReturnType: TypeText, IsAggregate: false},
  195. "JULIANDAY": {Name: "JULIANDAY", MinArgs: 1, MaxArgs: -1, ArgTypes: []Type{TypeText}, ReturnType: TypeReal, IsAggregate: false},
  196. "STRFTIME": {Name: "STRFTIME", MinArgs: 2, MaxArgs: -1, ArgTypes: []Type{TypeText, TypeText}, ReturnType: TypeText, IsAggregate: false},
  197. // SQLite specific
  198. "SQLITE_VERSION": {Name: "SQLITE_VERSION", MinArgs: 0, MaxArgs: 0, ArgTypes: []Type{}, ReturnType: TypeText, IsAggregate: false},
  199. "LAST_INSERT_ROWID": {Name: "LAST_INSERT_ROWID", MinArgs: 0, MaxArgs: 0, ArgTypes: []Type{}, ReturnType: TypeInteger, IsAggregate: false},
  200. "CHANGES": {Name: "CHANGES", MinArgs: 0, MaxArgs: 0, ArgTypes: []Type{}, ReturnType: TypeInteger, IsAggregate: false},
  201. "TOTAL_CHANGES": {Name: "TOTAL_CHANGES", MinArgs: 0, MaxArgs: 0, ArgTypes: []Type{}, ReturnType: TypeInteger, IsAggregate: false},
  202. // Other
  203. "HEX": {Name: "HEX", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeBlob}, ReturnType: TypeText, IsAggregate: false},
  204. "UNHEX": {Name: "UNHEX", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeText}, ReturnType: TypeBlob, IsAggregate: false},
  205. "ZEROBLOB": {Name: "ZEROBLOB", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeInteger}, ReturnType: TypeBlob, IsAggregate: false},
  206. "QUOTE": {Name: "QUOTE", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  207. }
  208. // LookupFunction returns the function signature for a function name.
  209. func LookupFunction(name string) (FunctionSignature, bool) {
  210. sig, ok := builtinFunctions[strings.ToUpper(name)]
  211. return sig, ok
  212. }
  213. // IsAggregateFunction returns true if the function is an aggregate.
  214. func IsAggregateFunction(name string) bool {
  215. sig, ok := LookupFunction(name)
  216. return ok && sig.IsAggregate
  217. }
  218. // ColumnInfo describes a column in a table.
  219. type ColumnInfo struct {
  220. Name string
  221. Type Type
  222. Nullable bool
  223. PrimaryKey bool
  224. Default interface{}
  225. TableName string // For qualified references
  226. }
  227. // TableInfo describes a table schema.
  228. type TableInfo struct {
  229. Name string
  230. Columns []ColumnInfo
  231. Alias string // For query-local aliases
  232. IsView bool // Views accept any column reference
  233. }
  234. // GetColumn returns a column by name.
  235. // For views (IsView=true), returns a wildcard ColumnInfo so column analysis passes.
  236. func (t *TableInfo) GetColumn(name string) (*ColumnInfo, bool) {
  237. if t.IsView {
  238. return &ColumnInfo{Name: name, TableName: t.Name, Type: TypeAny}, true
  239. }
  240. upper := strings.ToUpper(name)
  241. for i := range t.Columns {
  242. if strings.ToUpper(t.Columns[i].Name) == upper {
  243. return &t.Columns[i], true
  244. }
  245. }
  246. return nil, false
  247. }
  248. // ExprInfo contains analysis results for an expression.
  249. type ExprInfo struct {
  250. Type Type
  251. IsAggregate bool
  252. IsConstant bool
  253. Nullable bool
  254. }