types.go 12 KB

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