types.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  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(strings.TrimSpace(name))
  47. // Array types report their element type's affinity. The analyzer has no
  48. // distinct array type; element storage stays a canonical text value.
  49. upper = strings.TrimSuffix(upper, "[]")
  50. // Rule 1: If the type contains "INT" -> INTEGER
  51. if strings.Contains(upper, "INT") {
  52. return TypeInteger
  53. }
  54. // Rule 2: If the type contains "CHAR", "CLOB", or "TEXT" -> TEXT
  55. if strings.Contains(upper, "CHAR") ||
  56. strings.Contains(upper, "CLOB") ||
  57. strings.Contains(upper, "TEXT") {
  58. return TypeText
  59. }
  60. // UUID is stored as text (SQLite-style), not as a native PostgreSQL UUID.
  61. if upper == "UUID" {
  62. return TypeText
  63. }
  64. // Rule 3: If the type contains "BLOB" or is empty -> BLOB
  65. if strings.Contains(upper, "BLOB") || upper == "" {
  66. return TypeBlob
  67. }
  68. // Rule 4: If the type contains "REAL", "FLOA", or "DOUB" -> REAL
  69. if strings.Contains(upper, "REAL") ||
  70. strings.Contains(upper, "FLOA") ||
  71. strings.Contains(upper, "DOUB") {
  72. return TypeReal
  73. }
  74. // Rule 5: Otherwise -> NUMERIC
  75. // This includes NUMERIC, DECIMAL, BOOLEAN, DATE, DATETIME
  76. switch upper {
  77. case "BOOLEAN", "BOOL":
  78. return TypeBoolean
  79. default:
  80. return TypeNumeric
  81. }
  82. }
  83. // IsNumeric returns true if the type can hold numeric values.
  84. func (t Type) IsNumeric() bool {
  85. switch t {
  86. case TypeInteger, TypeReal, TypeNumeric, TypeBoolean:
  87. return true
  88. default:
  89. return false
  90. }
  91. }
  92. // IsComparable returns true if two types can be compared.
  93. func (t Type) IsComparable(other Type) bool {
  94. // NULL is comparable to anything
  95. if t == TypeNull || other == TypeNull {
  96. return true
  97. }
  98. // ANY matches anything
  99. if t == TypeAny || other == TypeAny {
  100. return true
  101. }
  102. // Same type
  103. if t == other {
  104. return true
  105. }
  106. // Numeric types are inter-comparable
  107. if t.IsNumeric() && other.IsNumeric() {
  108. return true
  109. }
  110. // TEXT and BLOB can be compared
  111. if (t == TypeText || t == TypeBlob) && (other == TypeText || other == TypeBlob) {
  112. return true
  113. }
  114. // NUMERIC/BOOLEAN accepts TEXT (SQLite-compatible: dates stored as text in numeric columns)
  115. if (t == TypeNumeric || t == TypeBoolean) && (other == TypeText || other == TypeBlob) {
  116. return true
  117. }
  118. if (other == TypeNumeric || other == TypeBoolean) && (t == TypeText || t == TypeBlob) {
  119. return true
  120. }
  121. return false
  122. }
  123. // CommonType returns the common type for binary operations.
  124. func CommonType(a, b Type) Type {
  125. if a == TypeUnknown {
  126. return b
  127. }
  128. if b == TypeUnknown {
  129. return a
  130. }
  131. if a == TypeNull {
  132. return b
  133. }
  134. if b == TypeNull {
  135. return a
  136. }
  137. if a == TypeAny {
  138. return b
  139. }
  140. if b == TypeAny {
  141. return a
  142. }
  143. if a == b {
  144. return a
  145. }
  146. // Numeric promotion
  147. if a.IsNumeric() && b.IsNumeric() {
  148. if a == TypeReal || b == TypeReal {
  149. return TypeReal
  150. }
  151. if a == TypeNumeric || b == TypeNumeric {
  152. return TypeNumeric
  153. }
  154. return TypeInteger
  155. }
  156. // Text/Blob coercion
  157. if (a == TypeText || a == TypeBlob) && (b == TypeText || b == TypeBlob) {
  158. return TypeText
  159. }
  160. return TypeText // Default to TEXT for mixed types
  161. }
  162. // FunctionSignature describes a SQL function.
  163. type FunctionSignature struct {
  164. Name string
  165. MinArgs int
  166. MaxArgs int // -1 for variadic
  167. ArgTypes []Type // Expected argument types (TypeAny for flexible)
  168. ReturnType Type
  169. IsAggregate bool
  170. }
  171. // builtinFunctions contains all built-in SQL functions.
  172. var builtinFunctions = map[string]FunctionSignature{
  173. // Aggregate functions
  174. "COUNT": {Name: "COUNT", MinArgs: 0, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeInteger, IsAggregate: true},
  175. "SUM": {Name: "SUM", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeNumeric}, ReturnType: TypeNumeric, IsAggregate: true},
  176. "AVG": {Name: "AVG", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeNumeric}, ReturnType: TypeReal, IsAggregate: true},
  177. "MIN": {Name: "MIN", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeAny, IsAggregate: true},
  178. "MAX": {Name: "MAX", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeAny, IsAggregate: true},
  179. "TOTAL": {Name: "TOTAL", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeNumeric}, ReturnType: TypeReal, IsAggregate: true},
  180. "GROUP_CONCAT": {Name: "GROUP_CONCAT", MinArgs: 1, MaxArgs: 2, ArgTypes: []Type{TypeAny, TypeText}, ReturnType: TypeText, IsAggregate: true},
  181. "STRING_AGG": {Name: "STRING_AGG", MinArgs: 1, MaxArgs: 2, ArgTypes: []Type{TypeAny, TypeText}, ReturnType: TypeText, IsAggregate: true},
  182. // String functions
  183. "LENGTH": {Name: "LENGTH", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeText}, ReturnType: TypeInteger, IsAggregate: false},
  184. "UPPER": {Name: "UPPER", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeText}, ReturnType: TypeText, IsAggregate: false},
  185. "LOWER": {Name: "LOWER", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeText}, ReturnType: TypeText, IsAggregate: false},
  186. "TRIM": {Name: "TRIM", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeText}, ReturnType: TypeText, IsAggregate: false},
  187. "LTRIM": {Name: "LTRIM", MinArgs: 1, MaxArgs: 2, ArgTypes: []Type{TypeText, TypeText}, ReturnType: TypeText, IsAggregate: false},
  188. "RTRIM": {Name: "RTRIM", MinArgs: 1, MaxArgs: 2, ArgTypes: []Type{TypeText, TypeText}, ReturnType: TypeText, IsAggregate: false},
  189. "SUBSTR": {Name: "SUBSTR", MinArgs: 2, MaxArgs: 3, ArgTypes: []Type{TypeText, TypeInteger, TypeInteger}, ReturnType: TypeText, IsAggregate: false},
  190. "REPLACE": {Name: "REPLACE", MinArgs: 3, MaxArgs: 3, ArgTypes: []Type{TypeText, TypeText, TypeText}, ReturnType: TypeText, IsAggregate: false},
  191. "INSTR": {Name: "INSTR", MinArgs: 2, MaxArgs: 2, ArgTypes: []Type{TypeText, TypeText}, ReturnType: TypeInteger, IsAggregate: false},
  192. "PRINTF": {Name: "PRINTF", MinArgs: 1, MaxArgs: -1, ArgTypes: []Type{TypeText}, ReturnType: TypeText, IsAggregate: false},
  193. "CONCAT": {Name: "CONCAT", MinArgs: 1, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  194. "CONCAT_WS": {Name: "CONCAT_WS", MinArgs: 2, MaxArgs: -1, ArgTypes: []Type{TypeText, TypeAny}, ReturnType: TypeText, IsAggregate: false},
  195. // Numeric functions
  196. "ABS": {Name: "ABS", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeNumeric}, ReturnType: TypeNumeric, IsAggregate: false},
  197. "ROUND": {Name: "ROUND", MinArgs: 1, MaxArgs: 2, ArgTypes: []Type{TypeNumeric, TypeInteger}, ReturnType: TypeNumeric, IsAggregate: false},
  198. "CEIL": {Name: "CEIL", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeNumeric}, ReturnType: TypeInteger, IsAggregate: false},
  199. "FLOOR": {Name: "FLOOR", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeNumeric}, ReturnType: TypeInteger, IsAggregate: false},
  200. "MOD": {Name: "MOD", MinArgs: 2, MaxArgs: 2, ArgTypes: []Type{TypeInteger, TypeInteger}, ReturnType: TypeInteger, IsAggregate: false},
  201. "RANDOM": {Name: "RANDOM", MinArgs: 0, MaxArgs: 0, ArgTypes: []Type{}, ReturnType: TypeInteger, IsAggregate: false},
  202. // Null handling
  203. "COALESCE": {Name: "COALESCE", MinArgs: 1, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  204. "NULLIF": {Name: "NULLIF", MinArgs: 2, MaxArgs: 2, ArgTypes: []Type{TypeAny, TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  205. "IFNULL": {Name: "IFNULL", MinArgs: 2, MaxArgs: 2, ArgTypes: []Type{TypeAny, TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  206. "IIF": {Name: "IIF", MinArgs: 3, MaxArgs: 3, ArgTypes: []Type{TypeBoolean, TypeAny, TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  207. // Array functions
  208. "ARRAY_LENGTH": {Name: "ARRAY_LENGTH", MinArgs: 2, MaxArgs: 2, ArgTypes: []Type{TypeAny, TypeInteger}, ReturnType: TypeInteger, IsAggregate: false},
  209. "UNNEST": {Name: "UNNEST", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  210. // Type functions
  211. "TYPEOF": {Name: "TYPEOF", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  212. "CAST": {Name: "CAST", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  213. // Date/Time functions
  214. "DATE": {Name: "DATE", MinArgs: 0, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  215. "TIME": {Name: "TIME", MinArgs: 0, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  216. "DATETIME": {Name: "DATETIME", MinArgs: 0, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  217. "JULIANDAY": {Name: "JULIANDAY", MinArgs: 0, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeReal, IsAggregate: false},
  218. "UNIXEPOCH": {Name: "UNIXEPOCH", MinArgs: 0, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeInteger, IsAggregate: false},
  219. "STRFTIME": {Name: "STRFTIME", MinArgs: 1, MaxArgs: -1, ArgTypes: []Type{TypeText, TypeAny}, ReturnType: TypeText, IsAggregate: false},
  220. "TIMEDIFF": {Name: "TIMEDIFF", MinArgs: 2, MaxArgs: 2, ArgTypes: []Type{TypeAny, TypeAny}, ReturnType: TypeText, IsAggregate: false},
  221. // SQLite specific
  222. "SQLITE_VERSION": {Name: "SQLITE_VERSION", MinArgs: 0, MaxArgs: 0, ArgTypes: []Type{}, ReturnType: TypeText, IsAggregate: false},
  223. "PIZZASQL_VERSION": {Name: "PIZZASQL_VERSION", MinArgs: 0, MaxArgs: 0, ArgTypes: []Type{}, ReturnType: TypeText, IsAggregate: false},
  224. "PG_BACKEND_PID": {Name: "PG_BACKEND_PID", MinArgs: 0, MaxArgs: 0, ArgTypes: []Type{}, ReturnType: TypeInteger, IsAggregate: false},
  225. "LAST_INSERT_ROWID": {Name: "LAST_INSERT_ROWID", MinArgs: 0, MaxArgs: 0, ArgTypes: []Type{}, ReturnType: TypeInteger, IsAggregate: false},
  226. "CHANGES": {Name: "CHANGES", MinArgs: 0, MaxArgs: 0, ArgTypes: []Type{}, ReturnType: TypeInteger, IsAggregate: false},
  227. "TOTAL_CHANGES": {Name: "TOTAL_CHANGES", MinArgs: 0, MaxArgs: 0, ArgTypes: []Type{}, ReturnType: TypeInteger, IsAggregate: false},
  228. // Other
  229. "HEX": {Name: "HEX", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeBlob}, ReturnType: TypeText, IsAggregate: false},
  230. "UNHEX": {Name: "UNHEX", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeText}, ReturnType: TypeBlob, IsAggregate: false},
  231. "ZEROBLOB": {Name: "ZEROBLOB", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeInteger}, ReturnType: TypeBlob, IsAggregate: false},
  232. "QUOTE": {Name: "QUOTE", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  233. // GoatCounter compatibility: scalar percentage difference.
  234. "PERCENT_DIFF": {Name: "PERCENT_DIFF", MinArgs: 2, MaxArgs: 2, ArgTypes: []Type{TypeNumeric, TypeNumeric}, ReturnType: TypeReal, IsAggregate: false},
  235. // JSON1 scalar functions.
  236. "JSON": {Name: "JSON", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  237. "JSONB": {Name: "JSONB", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  238. "JSON_VALID": {Name: "JSON_VALID", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeInteger, IsAggregate: false},
  239. "JSONB_VALID": {Name: "JSONB_VALID", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeInteger, IsAggregate: false},
  240. "JSON_TYPE": {Name: "JSON_TYPE", MinArgs: 1, MaxArgs: 2, ArgTypes: []Type{TypeAny, TypeText}, ReturnType: TypeText, IsAggregate: false},
  241. "JSON_EXTRACT": {Name: "JSON_EXTRACT", MinArgs: 2, MaxArgs: -1, ArgTypes: []Type{TypeAny, TypeText}, ReturnType: TypeAny, IsAggregate: false},
  242. "JSONB_EXTRACT": {Name: "JSONB_EXTRACT", MinArgs: 2, MaxArgs: -1, ArgTypes: []Type{TypeAny, TypeText}, ReturnType: TypeAny, IsAggregate: false},
  243. "JSON_SET": {Name: "JSON_SET", MinArgs: 3, MaxArgs: -1, ArgTypes: []Type{TypeAny, TypeText, TypeAny}, ReturnType: TypeText, IsAggregate: false},
  244. "JSONB_SET": {Name: "JSONB_SET", MinArgs: 3, MaxArgs: -1, ArgTypes: []Type{TypeAny, TypeText, TypeAny}, ReturnType: TypeText, IsAggregate: false},
  245. "JSON_INSERT": {Name: "JSON_INSERT", MinArgs: 3, MaxArgs: -1, ArgTypes: []Type{TypeAny, TypeText, TypeAny}, ReturnType: TypeText, IsAggregate: false},
  246. "JSONB_INSERT": {Name: "JSONB_INSERT", MinArgs: 3, MaxArgs: -1, ArgTypes: []Type{TypeAny, TypeText, TypeAny}, ReturnType: TypeText, IsAggregate: false},
  247. "JSON_REPLACE": {Name: "JSON_REPLACE", MinArgs: 3, MaxArgs: -1, ArgTypes: []Type{TypeAny, TypeText, TypeAny}, ReturnType: TypeText, IsAggregate: false},
  248. "JSONB_REPLACE": {Name: "JSONB_REPLACE", MinArgs: 3, MaxArgs: -1, ArgTypes: []Type{TypeAny, TypeText, TypeAny}, ReturnType: TypeText, IsAggregate: false},
  249. "JSON_REMOVE": {Name: "JSON_REMOVE", MinArgs: 2, MaxArgs: -1, ArgTypes: []Type{TypeAny, TypeText}, ReturnType: TypeText, IsAggregate: false},
  250. "JSONB_REMOVE": {Name: "JSONB_REMOVE", MinArgs: 2, MaxArgs: -1, ArgTypes: []Type{TypeAny, TypeText}, ReturnType: TypeText, IsAggregate: false},
  251. "JSON_ARRAY": {Name: "JSON_ARRAY", MinArgs: 0, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  252. "JSON_ARRAY_LENGTH": {Name: "JSON_ARRAY_LENGTH", MinArgs: 1, MaxArgs: 2, ArgTypes: []Type{TypeAny, TypeText}, ReturnType: TypeInteger, IsAggregate: false},
  253. "JSONB_ARRAY": {Name: "JSONB_ARRAY", MinArgs: 0, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  254. "JSON_OBJECT": {Name: "JSON_OBJECT", MinArgs: 0, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  255. "JSONB_OBJECT": {Name: "JSONB_OBJECT", MinArgs: 0, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  256. "JSON_QUOTE": {Name: "JSON_QUOTE", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  257. "JSONB_QUOTE": {Name: "JSONB_QUOTE", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  258. // JSON1 aggregate.
  259. "JSON_GROUP_ARRAY": {Name: "JSON_GROUP_ARRAY", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: true},
  260. "JSONB_GROUP_ARRAY": {Name: "JSONB_GROUP_ARRAY", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: true},
  261. }
  262. // LookupFunction returns the function signature for a function name.
  263. func LookupFunction(name string) (FunctionSignature, bool) {
  264. sig, ok := builtinFunctions[strings.ToUpper(name)]
  265. return sig, ok
  266. }
  267. // IsAggregateFunction returns true if the function is an aggregate.
  268. func IsAggregateFunction(name string) bool {
  269. sig, ok := LookupFunction(name)
  270. return ok && sig.IsAggregate
  271. }
  272. // BuiltinFunctions returns all built-in functions sorted by name.
  273. func BuiltinFunctions() []FunctionSignature {
  274. functions := make([]FunctionSignature, 0, len(builtinFunctions))
  275. for _, sig := range builtinFunctions {
  276. functions = append(functions, sig)
  277. }
  278. sort.Slice(functions, func(i, j int) bool {
  279. return functions[i].Name < functions[j].Name
  280. })
  281. return functions
  282. }
  283. // ColumnInfo describes a column in a table.
  284. type ColumnInfo struct {
  285. Name string
  286. Type Type
  287. Nullable bool
  288. PrimaryKey bool
  289. Default interface{}
  290. TableName string // For qualified references
  291. Generated bool // generated columns cannot be written by the user
  292. // Array marks a one-dimensional array column. Type holds the element's
  293. // affinity, so comparisons need this flag to accept a text array literal.
  294. Array bool
  295. }
  296. // TableInfo describes a table schema.
  297. type TableInfo struct {
  298. Name string
  299. Columns []ColumnInfo
  300. Alias string // For query-local aliases
  301. IsView bool // Views accept any column reference
  302. }
  303. // GetColumn returns a column by name.
  304. // For views (IsView=true), returns a wildcard ColumnInfo so column analysis passes.
  305. func (t *TableInfo) GetColumn(name string) (*ColumnInfo, bool) {
  306. if t.IsView {
  307. return &ColumnInfo{Name: name, TableName: t.Name, Type: TypeAny}, true
  308. }
  309. upper := strings.ToUpper(name)
  310. for i := range t.Columns {
  311. if strings.ToUpper(t.Columns[i].Name) == upper {
  312. return &t.Columns[i], true
  313. }
  314. }
  315. return nil, false
  316. }
  317. // ExprInfo contains analysis results for an expression.
  318. type ExprInfo struct {
  319. Type Type
  320. IsAggregate bool
  321. IsConstant bool
  322. Nullable bool
  323. // Array marks an expression whose value is an array.
  324. Array bool
  325. }