types.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  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. TypeJSONB // Native JSONB (canonical JSON text, compared as jsonb)
  19. )
  20. func (t Type) String() string {
  21. switch t {
  22. case TypeUnknown:
  23. return "UNKNOWN"
  24. case TypeNull:
  25. return "NULL"
  26. case TypeInteger:
  27. return "INTEGER"
  28. case TypeReal:
  29. return "REAL"
  30. case TypeText:
  31. return "TEXT"
  32. case TypeBlob:
  33. return "BLOB"
  34. case TypeNumeric:
  35. return "NUMERIC"
  36. case TypeBoolean:
  37. return "BOOLEAN"
  38. case TypeAny:
  39. return "ANY"
  40. case TypeJSONB:
  41. return "JSONB"
  42. default:
  43. return "UNKNOWN"
  44. }
  45. }
  46. // TypeFromName returns the Type for a SQL type name using SQLite affinity rules.
  47. // See: https://www.sqlite.org/datatype3.html
  48. func TypeFromName(name string) Type {
  49. upper := strings.ToUpper(strings.TrimSpace(name))
  50. // Array types report their element type's affinity. The analyzer has no
  51. // distinct array type; element storage stays a canonical text value.
  52. upper = strings.TrimSuffix(upper, "[]")
  53. // JSONB is a native type: values are canonical JSON text but compare, order,
  54. // and report as jsonb rather than as text or numeric affinity.
  55. if upper == "JSONB" {
  56. return TypeJSONB
  57. }
  58. // Rule 1: If the type contains "INT" -> INTEGER
  59. if strings.Contains(upper, "INT") {
  60. return TypeInteger
  61. }
  62. // Rule 2: If the type contains "CHAR", "CLOB", or "TEXT" -> TEXT
  63. if strings.Contains(upper, "CHAR") ||
  64. strings.Contains(upper, "CLOB") ||
  65. strings.Contains(upper, "TEXT") {
  66. return TypeText
  67. }
  68. // UUID is stored as text (SQLite-style), not as a native PostgreSQL UUID.
  69. if upper == "UUID" {
  70. return TypeText
  71. }
  72. // Rule 3: If the type contains "BLOB" or is empty -> BLOB
  73. if strings.Contains(upper, "BLOB") || upper == "" {
  74. return TypeBlob
  75. }
  76. // Rule 4: If the type contains "REAL", "FLOA", or "DOUB" -> REAL
  77. if strings.Contains(upper, "REAL") ||
  78. strings.Contains(upper, "FLOA") ||
  79. strings.Contains(upper, "DOUB") {
  80. return TypeReal
  81. }
  82. // Rule 5: Otherwise -> NUMERIC
  83. // This includes NUMERIC, DECIMAL, BOOLEAN, DATE, DATETIME
  84. switch upper {
  85. case "BOOLEAN", "BOOL":
  86. return TypeBoolean
  87. default:
  88. return TypeNumeric
  89. }
  90. }
  91. // IsNumeric returns true if the type can hold numeric values.
  92. func (t Type) IsNumeric() bool {
  93. switch t {
  94. case TypeInteger, TypeReal, TypeNumeric, TypeBoolean:
  95. return true
  96. default:
  97. return false
  98. }
  99. }
  100. // IsComparable returns true if two types can be compared.
  101. func (t Type) IsComparable(other Type) bool {
  102. // NULL is comparable to anything
  103. if t == TypeNull || other == TypeNull {
  104. return true
  105. }
  106. // ANY matches anything
  107. if t == TypeAny || other == TypeAny {
  108. return true
  109. }
  110. // Same type
  111. if t == other {
  112. return true
  113. }
  114. // Numeric types are inter-comparable
  115. if t.IsNumeric() && other.IsNumeric() {
  116. return true
  117. }
  118. // TEXT and BLOB can be compared
  119. if (t == TypeText || t == TypeBlob) && (other == TypeText || other == TypeBlob) {
  120. return true
  121. }
  122. // JSONB compares with jsonb or with text that is read as a jsonb document.
  123. if t == TypeJSONB || other == TypeJSONB {
  124. textLike := func(x Type) bool {
  125. return x == TypeJSONB || x == TypeText || x == TypeBlob || x == TypeNull || x == TypeAny
  126. }
  127. return textLike(t) && textLike(other)
  128. }
  129. // NUMERIC/BOOLEAN accepts TEXT (SQLite-compatible: dates stored as text in numeric columns)
  130. if (t == TypeNumeric || t == TypeBoolean) && (other == TypeText || other == TypeBlob) {
  131. return true
  132. }
  133. if (other == TypeNumeric || other == TypeBoolean) && (t == TypeText || t == TypeBlob) {
  134. return true
  135. }
  136. return false
  137. }
  138. // CommonType returns the common type for binary operations.
  139. func CommonType(a, b Type) Type {
  140. if a == TypeUnknown {
  141. return b
  142. }
  143. if b == TypeUnknown {
  144. return a
  145. }
  146. if a == TypeNull {
  147. return b
  148. }
  149. if b == TypeNull {
  150. return a
  151. }
  152. if a == TypeAny {
  153. return b
  154. }
  155. if b == TypeAny {
  156. return a
  157. }
  158. if a == b {
  159. return a
  160. }
  161. // Numeric promotion
  162. if a.IsNumeric() && b.IsNumeric() {
  163. if a == TypeReal || b == TypeReal {
  164. return TypeReal
  165. }
  166. if a == TypeNumeric || b == TypeNumeric {
  167. return TypeNumeric
  168. }
  169. return TypeInteger
  170. }
  171. // Text/Blob coercion
  172. if (a == TypeText || a == TypeBlob) && (b == TypeText || b == TypeBlob) {
  173. return TypeText
  174. }
  175. // JSONB wins over text: a CASE or COALESCE branch that produces jsonb keeps
  176. // its native semantics.
  177. if a == TypeJSONB && (b == TypeText || b == TypeBlob) {
  178. return TypeJSONB
  179. }
  180. if b == TypeJSONB && (a == TypeText || a == TypeBlob) {
  181. return TypeJSONB
  182. }
  183. return TypeText // Default to TEXT for mixed types
  184. }
  185. // FunctionSignature describes a SQL function.
  186. type FunctionSignature struct {
  187. Name string
  188. MinArgs int
  189. MaxArgs int // -1 for variadic
  190. ArgTypes []Type // Expected argument types (TypeAny for flexible)
  191. ReturnType Type
  192. IsAggregate bool
  193. }
  194. // builtinFunctions contains all built-in SQL functions.
  195. var builtinFunctions = map[string]FunctionSignature{
  196. // Aggregate functions
  197. "COUNT": {Name: "COUNT", MinArgs: 0, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeInteger, IsAggregate: true},
  198. "SUM": {Name: "SUM", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeNumeric}, ReturnType: TypeNumeric, IsAggregate: true},
  199. "AVG": {Name: "AVG", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeNumeric}, ReturnType: TypeReal, IsAggregate: true},
  200. "MIN": {Name: "MIN", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeAny, IsAggregate: true},
  201. "MAX": {Name: "MAX", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeAny, IsAggregate: true},
  202. "TOTAL": {Name: "TOTAL", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeNumeric}, ReturnType: TypeReal, IsAggregate: true},
  203. "GROUP_CONCAT": {Name: "GROUP_CONCAT", MinArgs: 1, MaxArgs: 2, ArgTypes: []Type{TypeAny, TypeText}, ReturnType: TypeText, IsAggregate: true},
  204. "STRING_AGG": {Name: "STRING_AGG", MinArgs: 1, MaxArgs: 2, ArgTypes: []Type{TypeAny, TypeText}, ReturnType: TypeText, IsAggregate: true},
  205. // String functions
  206. "LENGTH": {Name: "LENGTH", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeText}, ReturnType: TypeInteger, IsAggregate: false},
  207. "UPPER": {Name: "UPPER", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeText}, ReturnType: TypeText, IsAggregate: false},
  208. "LOWER": {Name: "LOWER", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeText}, ReturnType: TypeText, IsAggregate: false},
  209. "TRIM": {Name: "TRIM", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeText}, ReturnType: TypeText, IsAggregate: false},
  210. "LTRIM": {Name: "LTRIM", MinArgs: 1, MaxArgs: 2, ArgTypes: []Type{TypeText, TypeText}, ReturnType: TypeText, IsAggregate: false},
  211. "RTRIM": {Name: "RTRIM", MinArgs: 1, MaxArgs: 2, ArgTypes: []Type{TypeText, TypeText}, ReturnType: TypeText, IsAggregate: false},
  212. "SUBSTR": {Name: "SUBSTR", MinArgs: 2, MaxArgs: 3, ArgTypes: []Type{TypeText, TypeInteger, TypeInteger}, ReturnType: TypeText, IsAggregate: false},
  213. "REPLACE": {Name: "REPLACE", MinArgs: 3, MaxArgs: 3, ArgTypes: []Type{TypeText, TypeText, TypeText}, ReturnType: TypeText, IsAggregate: false},
  214. "INSTR": {Name: "INSTR", MinArgs: 2, MaxArgs: 2, ArgTypes: []Type{TypeText, TypeText}, ReturnType: TypeInteger, IsAggregate: false},
  215. "PRINTF": {Name: "PRINTF", MinArgs: 1, MaxArgs: -1, ArgTypes: []Type{TypeText}, ReturnType: TypeText, IsAggregate: false},
  216. "CONCAT": {Name: "CONCAT", MinArgs: 1, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  217. "CONCAT_WS": {Name: "CONCAT_WS", MinArgs: 2, MaxArgs: -1, ArgTypes: []Type{TypeText, TypeAny}, ReturnType: TypeText, IsAggregate: false},
  218. // Numeric functions
  219. "ABS": {Name: "ABS", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeNumeric}, ReturnType: TypeNumeric, IsAggregate: false},
  220. "ROUND": {Name: "ROUND", MinArgs: 1, MaxArgs: 2, ArgTypes: []Type{TypeNumeric, TypeInteger}, ReturnType: TypeNumeric, IsAggregate: false},
  221. "CEIL": {Name: "CEIL", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeNumeric}, ReturnType: TypeInteger, IsAggregate: false},
  222. "FLOOR": {Name: "FLOOR", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeNumeric}, ReturnType: TypeInteger, IsAggregate: false},
  223. "MOD": {Name: "MOD", MinArgs: 2, MaxArgs: 2, ArgTypes: []Type{TypeInteger, TypeInteger}, ReturnType: TypeInteger, IsAggregate: false},
  224. "RANDOM": {Name: "RANDOM", MinArgs: 0, MaxArgs: 0, ArgTypes: []Type{}, ReturnType: TypeInteger, IsAggregate: false},
  225. // Null handling
  226. "COALESCE": {Name: "COALESCE", MinArgs: 1, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  227. "NULLIF": {Name: "NULLIF", MinArgs: 2, MaxArgs: 2, ArgTypes: []Type{TypeAny, TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  228. "IFNULL": {Name: "IFNULL", MinArgs: 2, MaxArgs: 2, ArgTypes: []Type{TypeAny, TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  229. "IIF": {Name: "IIF", MinArgs: 3, MaxArgs: 3, ArgTypes: []Type{TypeBoolean, TypeAny, TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  230. // Array functions
  231. "ARRAY_LENGTH": {Name: "ARRAY_LENGTH", MinArgs: 2, MaxArgs: 2, ArgTypes: []Type{TypeAny, TypeInteger}, ReturnType: TypeInteger, IsAggregate: false},
  232. "UNNEST": {Name: "UNNEST", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  233. // Type functions
  234. "TYPEOF": {Name: "TYPEOF", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  235. "CAST": {Name: "CAST", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  236. // Date/Time functions
  237. "DATE": {Name: "DATE", MinArgs: 0, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  238. "TIME": {Name: "TIME", MinArgs: 0, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  239. "DATETIME": {Name: "DATETIME", MinArgs: 0, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  240. "JULIANDAY": {Name: "JULIANDAY", MinArgs: 0, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeReal, IsAggregate: false},
  241. "UNIXEPOCH": {Name: "UNIXEPOCH", MinArgs: 0, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeInteger, IsAggregate: false},
  242. "STRFTIME": {Name: "STRFTIME", MinArgs: 1, MaxArgs: -1, ArgTypes: []Type{TypeText, TypeAny}, ReturnType: TypeText, IsAggregate: false},
  243. "TIMEDIFF": {Name: "TIMEDIFF", MinArgs: 2, MaxArgs: 2, ArgTypes: []Type{TypeAny, TypeAny}, ReturnType: TypeText, IsAggregate: false},
  244. // SQLite specific
  245. "SQLITE_VERSION": {Name: "SQLITE_VERSION", MinArgs: 0, MaxArgs: 0, ArgTypes: []Type{}, ReturnType: TypeText, IsAggregate: false},
  246. "PIZZASQL_VERSION": {Name: "PIZZASQL_VERSION", MinArgs: 0, MaxArgs: 0, ArgTypes: []Type{}, ReturnType: TypeText, IsAggregate: false},
  247. "PG_BACKEND_PID": {Name: "PG_BACKEND_PID", MinArgs: 0, MaxArgs: 0, ArgTypes: []Type{}, ReturnType: TypeInteger, IsAggregate: false},
  248. "VERSION": {Name: "VERSION", MinArgs: 0, MaxArgs: 0, ArgTypes: []Type{}, ReturnType: TypeText, IsAggregate: false},
  249. // PostgreSQL catalog-introspection functions. The engine answers these from
  250. // its virtual pg_catalog snapshot; obj_description, col_description, and
  251. // pg_get_serial_sequence return NULL because there are no comments or
  252. // sequences to describe.
  253. "CURRENT_SCHEMA": {Name: "CURRENT_SCHEMA", MinArgs: 0, MaxArgs: 0, ArgTypes: []Type{}, ReturnType: TypeText, IsAggregate: false},
  254. "CURRENT_DATABASE": {Name: "CURRENT_DATABASE", MinArgs: 0, MaxArgs: 0, ArgTypes: []Type{}, ReturnType: TypeText, IsAggregate: false},
  255. "CURRENT_SETTING": {Name: "CURRENT_SETTING", MinArgs: 1, MaxArgs: 2, ArgTypes: []Type{TypeText, TypeBoolean}, ReturnType: TypeText, IsAggregate: false},
  256. "FORMAT_TYPE": {Name: "FORMAT_TYPE", MinArgs: 1, MaxArgs: 2, ArgTypes: []Type{TypeAny, TypeInteger}, ReturnType: TypeText, IsAggregate: false},
  257. "PG_TABLE_IS_VISIBLE": {Name: "PG_TABLE_IS_VISIBLE", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeBoolean, IsAggregate: false},
  258. "PG_GET_INDEXDEF": {Name: "PG_GET_INDEXDEF", MinArgs: 1, MaxArgs: 3, ArgTypes: []Type{TypeAny, TypeAny, TypeBoolean}, ReturnType: TypeText, IsAggregate: false},
  259. "PG_GET_CONSTRAINTDEF": {Name: "PG_GET_CONSTRAINTDEF", MinArgs: 1, MaxArgs: 2, ArgTypes: []Type{TypeAny, TypeBoolean}, ReturnType: TypeText, IsAggregate: false},
  260. "PG_GET_EXPR": {Name: "PG_GET_EXPR", MinArgs: 1, MaxArgs: 3, ArgTypes: []Type{TypeAny, TypeAny, TypeBoolean}, ReturnType: TypeText, IsAggregate: false},
  261. "PG_GET_SERIAL_SEQUENCE": {Name: "PG_GET_SERIAL_SEQUENCE", MinArgs: 2, MaxArgs: 2, ArgTypes: []Type{TypeText, TypeText}, ReturnType: TypeText, IsAggregate: false},
  262. "OBJ_DESCRIPTION": {Name: "OBJ_DESCRIPTION", MinArgs: 1, MaxArgs: 2, ArgTypes: []Type{TypeAny, TypeText}, ReturnType: TypeText, IsAggregate: false},
  263. "COL_DESCRIPTION": {Name: "COL_DESCRIPTION", MinArgs: 2, MaxArgs: 2, ArgTypes: []Type{TypeAny, TypeInteger}, ReturnType: TypeText, IsAggregate: false},
  264. "HAS_TABLE_PRIVILEGE": {Name: "HAS_TABLE_PRIVILEGE", MinArgs: 2, MaxArgs: 3, ArgTypes: []Type{TypeAny, TypeText, TypeText}, ReturnType: TypeBoolean, IsAggregate: false},
  265. "PG_GET_USERBYID": {Name: "PG_GET_USERBYID", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  266. "TO_REGCLASS": {Name: "TO_REGCLASS", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeText}, ReturnType: TypeAny, IsAggregate: false},
  267. "TO_REGTYPE": {Name: "TO_REGTYPE", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeText}, ReturnType: TypeAny, IsAggregate: false},
  268. "LAST_INSERT_ROWID": {Name: "LAST_INSERT_ROWID", MinArgs: 0, MaxArgs: 0, ArgTypes: []Type{}, ReturnType: TypeInteger, IsAggregate: false},
  269. "CHANGES": {Name: "CHANGES", MinArgs: 0, MaxArgs: 0, ArgTypes: []Type{}, ReturnType: TypeInteger, IsAggregate: false},
  270. "TOTAL_CHANGES": {Name: "TOTAL_CHANGES", MinArgs: 0, MaxArgs: 0, ArgTypes: []Type{}, ReturnType: TypeInteger, IsAggregate: false},
  271. // Other
  272. "HEX": {Name: "HEX", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeBlob}, ReturnType: TypeText, IsAggregate: false},
  273. "UNHEX": {Name: "UNHEX", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeText}, ReturnType: TypeBlob, IsAggregate: false},
  274. "ZEROBLOB": {Name: "ZEROBLOB", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeInteger}, ReturnType: TypeBlob, IsAggregate: false},
  275. "QUOTE": {Name: "QUOTE", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  276. // GoatCounter compatibility: scalar percentage difference.
  277. "PERCENT_DIFF": {Name: "PERCENT_DIFF", MinArgs: 2, MaxArgs: 2, ArgTypes: []Type{TypeNumeric, TypeNumeric}, ReturnType: TypeReal, IsAggregate: false},
  278. // JSON1 scalar functions.
  279. "JSON": {Name: "JSON", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  280. "JSONB": {Name: "JSONB", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  281. "JSON_VALID": {Name: "JSON_VALID", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeInteger, IsAggregate: false},
  282. "JSONB_VALID": {Name: "JSONB_VALID", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeInteger, IsAggregate: false},
  283. "JSON_TYPE": {Name: "JSON_TYPE", MinArgs: 1, MaxArgs: 2, ArgTypes: []Type{TypeAny, TypeText}, ReturnType: TypeText, IsAggregate: false},
  284. "JSON_EXTRACT": {Name: "JSON_EXTRACT", MinArgs: 2, MaxArgs: -1, ArgTypes: []Type{TypeAny, TypeText}, ReturnType: TypeAny, IsAggregate: false},
  285. "JSONB_EXTRACT": {Name: "JSONB_EXTRACT", MinArgs: 2, MaxArgs: -1, ArgTypes: []Type{TypeAny, TypeText}, ReturnType: TypeAny, IsAggregate: false},
  286. "JSON_SET": {Name: "JSON_SET", MinArgs: 3, MaxArgs: -1, ArgTypes: []Type{TypeAny, TypeText, TypeAny}, ReturnType: TypeText, IsAggregate: false},
  287. "JSONB_SET": {Name: "JSONB_SET", MinArgs: 3, MaxArgs: -1, ArgTypes: []Type{TypeAny, TypeText, TypeAny}, ReturnType: TypeJSONB, IsAggregate: false},
  288. "JSON_INSERT": {Name: "JSON_INSERT", MinArgs: 3, MaxArgs: -1, ArgTypes: []Type{TypeAny, TypeText, TypeAny}, ReturnType: TypeText, IsAggregate: false},
  289. "JSONB_INSERT": {Name: "JSONB_INSERT", MinArgs: 3, MaxArgs: -1, ArgTypes: []Type{TypeAny, TypeText, TypeAny}, ReturnType: TypeJSONB, IsAggregate: false},
  290. "JSON_REPLACE": {Name: "JSON_REPLACE", MinArgs: 3, MaxArgs: -1, ArgTypes: []Type{TypeAny, TypeText, TypeAny}, ReturnType: TypeText, IsAggregate: false},
  291. "JSONB_REPLACE": {Name: "JSONB_REPLACE", MinArgs: 3, MaxArgs: -1, ArgTypes: []Type{TypeAny, TypeText, TypeAny}, ReturnType: TypeJSONB, IsAggregate: false},
  292. "JSON_REMOVE": {Name: "JSON_REMOVE", MinArgs: 2, MaxArgs: -1, ArgTypes: []Type{TypeAny, TypeText}, ReturnType: TypeText, IsAggregate: false},
  293. "JSONB_REMOVE": {Name: "JSONB_REMOVE", MinArgs: 2, MaxArgs: -1, ArgTypes: []Type{TypeAny, TypeText}, ReturnType: TypeJSONB, IsAggregate: false},
  294. "JSON_ARRAY": {Name: "JSON_ARRAY", MinArgs: 0, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  295. "JSON_ARRAY_LENGTH": {Name: "JSON_ARRAY_LENGTH", MinArgs: 1, MaxArgs: 2, ArgTypes: []Type{TypeAny, TypeText}, ReturnType: TypeInteger, IsAggregate: false},
  296. "JSONB_ARRAY": {Name: "JSONB_ARRAY", MinArgs: 0, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  297. "JSON_OBJECT": {Name: "JSON_OBJECT", MinArgs: 0, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  298. "JSONB_OBJECT": {Name: "JSONB_OBJECT", MinArgs: 0, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  299. "JSON_QUOTE": {Name: "JSON_QUOTE", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  300. "JSONB_QUOTE": {Name: "JSONB_QUOTE", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  301. // JSON1 aggregate.
  302. "JSON_GROUP_ARRAY": {Name: "JSON_GROUP_ARRAY", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: true},
  303. "JSONB_GROUP_ARRAY": {Name: "JSONB_GROUP_ARRAY", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: true},
  304. // Native JSONB scalar functions.
  305. "TO_JSONB": {Name: "TO_JSONB", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeJSONB, IsAggregate: false},
  306. "JSONB_TYPEOF": {Name: "JSONB_TYPEOF", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  307. "JSONB_ARRAY_LENGTH": {Name: "JSONB_ARRAY_LENGTH", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeInteger, IsAggregate: false},
  308. "JSONB_STRIP_NULLS": {Name: "JSONB_STRIP_NULLS", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeJSONB, IsAggregate: false},
  309. "JSONB_PRETTY": {Name: "JSONB_PRETTY", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  310. "JSONB_BUILD_OBJECT": {Name: "JSONB_BUILD_OBJECT", MinArgs: 0, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeJSONB, IsAggregate: false},
  311. "JSONB_BUILD_ARRAY": {Name: "JSONB_BUILD_ARRAY", MinArgs: 0, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeJSONB, IsAggregate: false},
  312. // Native JSONB set-returning functions. They are only executable as the
  313. // single FROM table function; a projection call is rejected with 0A000.
  314. "JSONB_EACH": {Name: "JSONB_EACH", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  315. "JSONB_EACH_TEXT": {Name: "JSONB_EACH_TEXT", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  316. "JSONB_ARRAY_ELEMENTS": {Name: "JSONB_ARRAY_ELEMENTS", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  317. "JSONB_ARRAY_ELEMENTS_TEXT": {Name: "JSONB_ARRAY_ELEMENTS_TEXT", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  318. "JSONB_OBJECT_KEYS": {Name: "JSONB_OBJECT_KEYS", MinArgs: 1, MaxArgs: 1, ArgTypes: []Type{TypeAny}, ReturnType: TypeText, IsAggregate: false},
  319. // jsonpath is not implemented. The names are registered so the executor can
  320. // reject them with SQLSTATE 0A000 instead of the analyzer reporting an
  321. // unknown function.
  322. "JSONB_PATH_EXISTS": {Name: "JSONB_PATH_EXISTS", MinArgs: 2, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeBoolean, IsAggregate: false},
  323. "JSONB_PATH_EXISTS_TZ": {Name: "JSONB_PATH_EXISTS_TZ", MinArgs: 2, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeBoolean, IsAggregate: false},
  324. "JSONB_PATH_MATCH": {Name: "JSONB_PATH_MATCH", MinArgs: 2, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeBoolean, IsAggregate: false},
  325. "JSONB_PATH_MATCH_TZ": {Name: "JSONB_PATH_MATCH_TZ", MinArgs: 2, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeBoolean, IsAggregate: false},
  326. "JSONB_PATH_QUERY": {Name: "JSONB_PATH_QUERY", MinArgs: 2, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  327. "JSONB_PATH_QUERY_TZ": {Name: "JSONB_PATH_QUERY_TZ", MinArgs: 2, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  328. "JSONB_PATH_QUERY_ARRAY": {Name: "JSONB_PATH_QUERY_ARRAY", MinArgs: 2, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  329. "JSONB_PATH_QUERY_ARRAY_TZ": {Name: "JSONB_PATH_QUERY_ARRAY_TZ", MinArgs: 2, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  330. "JSONB_PATH_QUERY_FIRST": {Name: "JSONB_PATH_QUERY_FIRST", MinArgs: 2, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  331. "JSONB_PATH_QUERY_FIRST_TZ": {Name: "JSONB_PATH_QUERY_FIRST_TZ", MinArgs: 2, MaxArgs: -1, ArgTypes: []Type{TypeAny}, ReturnType: TypeAny, IsAggregate: false},
  332. }
  333. // LookupFunction returns the function signature for a function name.
  334. func LookupFunction(name string) (FunctionSignature, bool) {
  335. sig, ok := builtinFunctions[strings.ToUpper(name)]
  336. return sig, ok
  337. }
  338. // IsAggregateFunction returns true if the function is an aggregate.
  339. func IsAggregateFunction(name string) bool {
  340. sig, ok := LookupFunction(name)
  341. return ok && sig.IsAggregate
  342. }
  343. // BuiltinFunctions returns all built-in functions sorted by name.
  344. func BuiltinFunctions() []FunctionSignature {
  345. functions := make([]FunctionSignature, 0, len(builtinFunctions))
  346. for _, sig := range builtinFunctions {
  347. functions = append(functions, sig)
  348. }
  349. sort.Slice(functions, func(i, j int) bool {
  350. return functions[i].Name < functions[j].Name
  351. })
  352. return functions
  353. }
  354. // ColumnInfo describes a column in a table.
  355. type ColumnInfo struct {
  356. Name string
  357. Type Type
  358. Nullable bool
  359. PrimaryKey bool
  360. Default interface{}
  361. TableName string // For qualified references
  362. Generated bool // generated columns cannot be written by the user
  363. // Array marks a one-dimensional array column. Type holds the element's
  364. // affinity, so comparisons need this flag to accept a text array literal.
  365. Array bool
  366. }
  367. // TableInfo describes a table schema.
  368. type TableInfo struct {
  369. Name string
  370. Columns []ColumnInfo
  371. Alias string // For query-local aliases
  372. IsView bool // Views accept any column reference
  373. }
  374. // GetColumn returns a column by name.
  375. // For views (IsView=true), returns a wildcard ColumnInfo so column analysis passes.
  376. func (t *TableInfo) GetColumn(name string) (*ColumnInfo, bool) {
  377. if t.IsView {
  378. return &ColumnInfo{Name: name, TableName: t.Name, Type: TypeAny}, true
  379. }
  380. upper := strings.ToUpper(name)
  381. for i := range t.Columns {
  382. if strings.ToUpper(t.Columns[i].Name) == upper {
  383. return &t.Columns[i], true
  384. }
  385. }
  386. return nil, false
  387. }
  388. // ExprInfo contains analysis results for an expression.
  389. type ExprInfo struct {
  390. Type Type
  391. IsAggregate bool
  392. IsConstant bool
  393. Nullable bool
  394. // Array marks an expression whose value is an array.
  395. Array bool
  396. }