types.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  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. // NUMERIC/BOOLEAN accepts TEXT (SQLite-compatible: dates stored as text in numeric columns)
  105. if (t == TypeNumeric || t == TypeBoolean) && (other == TypeText || other == TypeBlob) {
  106. return true
  107. }
  108. if (other == TypeNumeric || other == TypeBoolean) && (t == TypeText || t == TypeBlob) {
  109. return true
  110. }
  111. return false
  112. }
  113. // CommonType returns the common type for binary operations.
  114. func CommonType(a, b Type) Type {
  115. if a == TypeUnknown {
  116. return b
  117. }
  118. if b == TypeUnknown {
  119. return a
  120. }
  121. if a == TypeNull {
  122. return b
  123. }
  124. if b == TypeNull {
  125. return a
  126. }
  127. if a == TypeAny {
  128. return b
  129. }
  130. if b == TypeAny {
  131. return a
  132. }
  133. if a == b {
  134. return a
  135. }
  136. // Numeric promotion
  137. if a.IsNumeric() && b.IsNumeric() {
  138. if a == TypeReal || b == TypeReal {
  139. return TypeReal
  140. }
  141. if a == TypeNumeric || b == TypeNumeric {
  142. return TypeNumeric
  143. }
  144. return TypeInteger
  145. }
  146. // Text/Blob coercion
  147. if (a == TypeText || a == TypeBlob) && (b == TypeText || b == TypeBlob) {
  148. return TypeText
  149. }
  150. return TypeText // Default to TEXT for mixed types
  151. }
  152. // FunctionSignature describes a SQL function.
  153. type FunctionSignature struct {
  154. Name string
  155. MinArgs int
  156. MaxArgs int // -1 for variadic
  157. ArgTypes []Type // Expected argument types (TypeAny for flexible)
  158. ReturnType Type
  159. IsAggregate bool
  160. }
  161. // builtinFunctions contains all built-in SQL functions.
  162. var builtinFunctions = map[string]FunctionSignature{
  163. // Aggregate functions
  164. "COUNT": {Name: "COUNT", MinArgs: 0, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeInteger, IsAggregate: true},
  165. "SUM": {Name: "SUM", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeNumeric}, ReturnType: TypeNumeric, IsAggregate: true},
  166. "AVG": {Name: "AVG", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeNumeric}, ReturnType: TypeReal, IsAggregate: true},
  167. "MIN": {Name: "MIN", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeAny, IsAggregate: true},
  168. "MAX": {Name: "MAX", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeAny, IsAggregate: true},
  169. "TOTAL": {Name: "TOTAL", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeNumeric}, ReturnType: TypeReal, IsAggregate: true},
  170. "GROUP_CONCAT": {Name: "GROUP_CONCAT", MinArgs: 1, MaxArgs: 2, ArgTypes: []Type{TypeAny, TypeText}, ReturnType: TypeText, IsAggregate: true},
  171. // String functions
  172. "LENGTH": {Name: "LENGTH", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeText}, ReturnType: TypeInteger, IsAggregate: false},
  173. "UPPER": {Name: "UPPER", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeText}, ReturnType: TypeText, IsAggregate: false},
  174. "LOWER": {Name: "LOWER", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeText}, ReturnType: TypeText, IsAggregate: false},
  175. "TRIM": {Name: "TRIM", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeText}, ReturnType: TypeText, IsAggregate: false},
  176. "LTRIM": {Name: "LTRIM", MinArgs: 1, MaxArgs: 2, ArgTypes: []Type{TypeText, TypeText}, ReturnType: TypeText, IsAggregate: false},
  177. "RTRIM": {Name: "RTRIM", MinArgs: 1, MaxArgs: 2, ArgTypes: []Type{TypeText, TypeText}, ReturnType: TypeText, IsAggregate: false},
  178. "SUBSTR": {Name: "SUBSTR", MinArgs: 2, MaxArgs: 3, ArgTypes: []Type{TypeText, TypeInteger, TypeInteger}, ReturnType: TypeText, IsAggregate: false},
  179. "REPLACE": {Name: "REPLACE", MinArgs: 3, MaxArgs: 3, ArgTypes: []Type{TypeText, TypeText, TypeText}, ReturnType: TypeText, IsAggregate: false},
  180. "INSTR": {Name: "INSTR", MinArgs: 2, MaxArgs: 2, ArgTypes: []Type{TypeText, TypeText}, ReturnType: TypeInteger, IsAggregate: false},
  181. "PRINTF": {Name: "PRINTF", MinArgs: 1, MaxArgs: -1, ArgTypes: []Type{TypeText}, ReturnType: TypeText, IsAggregate: false},
  182. "CONCAT": {Name: "CONCAT", MinArgs: 1, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  183. // Numeric functions
  184. "ABS": {Name: "ABS", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeNumeric}, ReturnType: TypeNumeric, IsAggregate: false},
  185. "ROUND": {Name: "ROUND", MinArgs: 1, MaxArgs: 2, ArgTypes: []Type{TypeNumeric, TypeInteger}, ReturnType: TypeNumeric, IsAggregate: false},
  186. "CEIL": {Name: "CEIL", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeNumeric}, ReturnType: TypeInteger, IsAggregate: false},
  187. "FLOOR": {Name: "FLOOR", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeNumeric}, ReturnType: TypeInteger, IsAggregate: false},
  188. "MOD": {Name: "MOD", MinArgs: 2, MaxArgs: 2, ArgTypes: []Type{TypeInteger, TypeInteger}, ReturnType: TypeInteger, IsAggregate: false},
  189. "RANDOM": {Name: "RANDOM", MinArgs: 0, MaxArgs: 0, ArgTypes: []Type{}, ReturnType: TypeInteger, IsAggregate: false},
  190. // Null handling
  191. "COALESCE": {Name: "COALESCE", MinArgs: 1, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  192. "NULLIF": {Name: "NULLIF", MinArgs: 2, MaxArgs: 2, ArgTypes: []Type{TypeAny, TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  193. "IFNULL": {Name: "IFNULL", MinArgs: 2, MaxArgs: 2, ArgTypes: []Type{TypeAny, TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  194. "IIF": {Name: "IIF", MinArgs: 3, MaxArgs: 3, ArgTypes: []Type{TypeBoolean, TypeAny, TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  195. // Type functions
  196. "TYPEOF": {Name: "TYPEOF", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  197. "CAST": {Name: "CAST", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  198. // Date/Time functions
  199. "DATE": {Name: "DATE", MinArgs: 0, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  200. "TIME": {Name: "TIME", MinArgs: 0, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  201. "DATETIME": {Name: "DATETIME", MinArgs: 0, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  202. "JULIANDAY": {Name: "JULIANDAY", MinArgs: 0, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeReal, IsAggregate: false},
  203. "UNIXEPOCH": {Name: "UNIXEPOCH", MinArgs: 0, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeInteger, IsAggregate: false},
  204. "STRFTIME": {Name: "STRFTIME", MinArgs: 1, MaxArgs: -1, ArgTypes: []Type{TypeText, TypeAny}, ReturnType: TypeText, IsAggregate: false},
  205. "TIMEDIFF": {Name: "TIMEDIFF", MinArgs: 2, MaxArgs: 2, ArgTypes: []Type{TypeAny, TypeAny}, ReturnType: TypeText, IsAggregate: false},
  206. // SQLite specific
  207. "SQLITE_VERSION": {Name: "SQLITE_VERSION", MinArgs: 0, MaxArgs: 0, ArgTypes: []Type{}, ReturnType: TypeText, IsAggregate: false},
  208. "LAST_INSERT_ROWID": {Name: "LAST_INSERT_ROWID", MinArgs: 0, MaxArgs: 0, ArgTypes: []Type{}, ReturnType: TypeInteger, IsAggregate: false},
  209. "CHANGES": {Name: "CHANGES", MinArgs: 0, MaxArgs: 0, ArgTypes: []Type{}, ReturnType: TypeInteger, IsAggregate: false},
  210. "TOTAL_CHANGES": {Name: "TOTAL_CHANGES", MinArgs: 0, MaxArgs: 0, ArgTypes: []Type{}, ReturnType: TypeInteger, IsAggregate: false},
  211. // Other
  212. "HEX": {Name: "HEX", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeBlob}, ReturnType: TypeText, IsAggregate: false},
  213. "UNHEX": {Name: "UNHEX", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeText}, ReturnType: TypeBlob, IsAggregate: false},
  214. "ZEROBLOB": {Name: "ZEROBLOB", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeInteger}, ReturnType: TypeBlob, IsAggregate: false},
  215. "QUOTE": {Name: "QUOTE", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  216. }
  217. // LookupFunction returns the function signature for a function name.
  218. func LookupFunction(name string) (FunctionSignature, bool) {
  219. sig, ok := builtinFunctions[strings.ToUpper(name)]
  220. return sig, ok
  221. }
  222. // IsAggregateFunction returns true if the function is an aggregate.
  223. func IsAggregateFunction(name string) bool {
  224. sig, ok := LookupFunction(name)
  225. return ok && sig.IsAggregate
  226. }
  227. // ColumnInfo describes a column in a table.
  228. type ColumnInfo struct {
  229. Name string
  230. Type Type
  231. Nullable bool
  232. PrimaryKey bool
  233. Default interface{}
  234. TableName string // For qualified references
  235. }
  236. // TableInfo describes a table schema.
  237. type TableInfo struct {
  238. Name string
  239. Columns []ColumnInfo
  240. Alias string // For query-local aliases
  241. IsView bool // Views accept any column reference
  242. }
  243. // GetColumn returns a column by name.
  244. // For views (IsView=true), returns a wildcard ColumnInfo so column analysis passes.
  245. func (t *TableInfo) GetColumn(name string) (*ColumnInfo, bool) {
  246. if t.IsView {
  247. return &ColumnInfo{Name: name, TableName: t.Name, Type: TypeAny}, true
  248. }
  249. upper := strings.ToUpper(name)
  250. for i := range t.Columns {
  251. if strings.ToUpper(t.Columns[i].Name) == upper {
  252. return &t.Columns[i], true
  253. }
  254. }
  255. return nil, false
  256. }
  257. // ExprInfo contains analysis results for an expression.
  258. type ExprInfo struct {
  259. Type Type
  260. IsAggregate bool
  261. IsConstant bool
  262. Nullable bool
  263. }