literal.test.ts 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. import { Op, cast, fn, json, sql, where } from '@sequelize/core';
  2. import { expectsql, sequelize } from '../../support';
  3. const dialect = sequelize.dialect;
  4. const queryGenerator = sequelize.queryGenerator;
  5. describe('json', () => {
  6. if (!dialect.supports.jsonOperations || !dialect.supports.jsonExtraction.quoted) {
  7. return;
  8. }
  9. it('supports WhereOptions', () => {
  10. const conditions = {
  11. metadata: {
  12. language: 'icelandic',
  13. pg_rating: { dk: 'G' },
  14. },
  15. another_json_field: { x: 1 },
  16. };
  17. expectsql(() => queryGenerator.escape(json(conditions)), {
  18. postgres: `("metadata"->'language' = '"icelandic"' AND "metadata"#>ARRAY['pg_rating','dk']::VARCHAR(255)[] = '"G"') AND "another_json_field"->'x' = '1'`,
  19. sqlite3: `(json_extract(\`metadata\`,'$.language') = '"icelandic"' AND json_extract(\`metadata\`,'$.pg_rating.dk') = '"G"') AND json_extract(\`another_json_field\`,'$.x') = '1'`,
  20. mariadb: `(json_compact(json_extract(\`metadata\`,'$.language')) = '"icelandic"' AND json_compact(json_extract(\`metadata\`,'$.pg_rating.dk')) = '"G"') AND json_compact(json_extract(\`another_json_field\`,'$.x')) = '1'`,
  21. mysql: `(json_extract(\`metadata\`,'$.language') = CAST('"icelandic"' AS JSON) AND json_extract(\`metadata\`,'$.pg_rating.dk') = CAST('"G"' AS JSON)) AND json_extract(\`another_json_field\`,'$.x') = CAST('1' AS JSON)`,
  22. });
  23. });
  24. it('supports the json path notation', () => {
  25. const path = 'metadata.pg_rating.dk';
  26. expectsql(() => queryGenerator.escape(json(path)), {
  27. postgres: `"metadata"#>ARRAY['pg_rating','dk']::VARCHAR(255)[]`,
  28. mariadb: `json_compact(json_extract(\`metadata\`,'$.pg_rating.dk'))`,
  29. 'sqlite3 mysql': `json_extract(\`metadata\`,'$.pg_rating.dk')`,
  30. });
  31. });
  32. it('supports numbers in the dot notation', () => {
  33. expectsql(() => queryGenerator.escape(json('profile.id.0.1')), {
  34. postgres: `"profile"#>ARRAY['id','0','1']::VARCHAR(255)[]`,
  35. mariadb: `json_compact(json_extract(\`profile\`,'$.id."0"."1"'))`,
  36. 'sqlite3 mysql': `json_extract(\`profile\`,'$.id."0"."1"')`,
  37. });
  38. });
  39. it('can take a value to compare against', () => {
  40. const path = 'metadata.pg_rating.is';
  41. const value = 'U';
  42. expectsql(() => queryGenerator.escape(json(path, value)), {
  43. postgres: `"metadata"#>ARRAY['pg_rating','is']::VARCHAR(255)[] = '"U"'`,
  44. sqlite3: `json_extract(\`metadata\`,'$.pg_rating.is') = '"U"'`,
  45. mariadb: `json_compact(json_extract(\`metadata\`,'$.pg_rating.is')) = '"U"'`,
  46. mysql: `json_extract(\`metadata\`,'$.pg_rating.is') = CAST('"U"' AS JSON)`,
  47. });
  48. });
  49. // TODO: add a way to let `where` know what the type of the value is in raw queries
  50. // it('accepts a condition object', () => {
  51. // expectsql(queryGenerator.escape(json({ id: 1 })), {
  52. // postgres: `"id" = '1'`,
  53. // });
  54. // });
  55. //
  56. // it('column named "json"', () => {
  57. // expectsql(queryGenerator.escape(where(json('json'), Op.eq, {})), {
  58. // postgres: `("json"#>>'{}') = '{}'`,
  59. // });
  60. // });
  61. it('accepts a nested condition object', () => {
  62. expectsql(() => queryGenerator.escape(json({ profile: { id: 1 } })), {
  63. postgres: `"profile"->'id' = '1'`,
  64. sqlite3: `json_extract(\`profile\`,'$.id') = '1'`,
  65. mariadb: `json_compact(json_extract(\`profile\`,'$.id')) = '1'`,
  66. mysql: `json_extract(\`profile\`,'$.id') = CAST('1' AS JSON)`,
  67. });
  68. });
  69. it('accepts multiple condition object', () => {
  70. expectsql(
  71. () => queryGenerator.escape(json({ property: { value: 1 }, another: { value: 'string' } })),
  72. {
  73. postgres: `"property"->'value' = '1' AND "another"->'value' = '"string"'`,
  74. sqlite3: `json_extract(\`property\`,'$.value') = '1' AND json_extract(\`another\`,'$.value') = '"string"'`,
  75. mariadb: `json_compact(json_extract(\`property\`,'$.value')) = '1' AND json_compact(json_extract(\`another\`,'$.value')) = '"string"'`,
  76. mysql: `json_extract(\`property\`,'$.value') = CAST('1' AS JSON) AND json_extract(\`another\`,'$.value') = CAST('"string"' AS JSON)`,
  77. },
  78. );
  79. });
  80. it('can be used inside of where', () => {
  81. expectsql(() => queryGenerator.escape(where(json('profile.id'), '1')), {
  82. postgres: `"profile"->'id' = '"1"'`,
  83. sqlite3: `json_extract(\`profile\`,'$.id') = '"1"'`,
  84. mariadb: `json_compact(json_extract(\`profile\`,'$.id')) = '"1"'`,
  85. mysql: `json_extract(\`profile\`,'$.id') = CAST('"1"' AS JSON)`,
  86. });
  87. });
  88. });
  89. describe('cast', () => {
  90. it('accepts condition object (auto casting)', () => {
  91. expectsql(
  92. () =>
  93. queryGenerator.escape(
  94. fn(
  95. 'SUM',
  96. cast(
  97. {
  98. [Op.or]: {
  99. foo: 'foo',
  100. bar: 'bar',
  101. },
  102. },
  103. 'int',
  104. ),
  105. ),
  106. ),
  107. {
  108. default: `SUM(CAST(([foo] = 'foo' OR [bar] = 'bar') AS INT))`,
  109. mssql: `SUM(CAST(([foo] = N'foo' OR [bar] = N'bar') AS INT))`,
  110. },
  111. );
  112. });
  113. });
  114. describe('fn', () => {
  115. // this was a band-aid over a deeper problem ('$bind' being considered to be a bind parameter when it's a string), which has been fixed
  116. it('should not escape $ in fn() arguments', () => {
  117. const out = queryGenerator.escape(fn('upper', '$user'));
  118. expectsql(out, {
  119. default: `upper('$user')`,
  120. mssql: `upper(N'$user')`,
  121. });
  122. });
  123. it('accepts all sorts of values as arguments', () => {
  124. const out = queryGenerator.escape(
  125. fn(
  126. 'concat',
  127. 'user',
  128. 1,
  129. true,
  130. new Date(Date.UTC(2011, 2, 27, 10, 1, 55)),
  131. fn('lower', 'user'),
  132. ),
  133. );
  134. expectsql(out, {
  135. postgres: `concat('user', 1, true, '2011-03-27 10:01:55.000 +00:00', lower('user'))`,
  136. mssql: `concat(N'user', 1, 1, N'2011-03-27 10:01:55.000 +00:00', lower(N'user'))`,
  137. sqlite3: `concat('user', 1, 1, '2011-03-27 10:01:55.000 +00:00', lower('user'))`,
  138. ibmi: `concat('user', 1, 1, '2011-03-27 10:01:55.000', lower('user'))`,
  139. default: `concat('user', 1, true, '2011-03-27 10:01:55.000', lower('user'))`,
  140. });
  141. });
  142. it('accepts arrays', () => {
  143. if (!dialect.supports.dataTypes.ARRAY) {
  144. return;
  145. }
  146. const out = queryGenerator.escape(fn('concat', ['abc']));
  147. expectsql(out, {
  148. default: `concat(ARRAY['abc'])`,
  149. postgres: `concat(ARRAY['abc']::VARCHAR(255)[])`,
  150. });
  151. });
  152. });
  153. describe('sql.join', () => {
  154. it('joins parts with a separator', () => {
  155. const columns = ['a', 'b', 'c'];
  156. // SQL expression parts, string separator
  157. expectsql(
  158. queryGenerator.escape(
  159. sql`SELECT ${sql.join(
  160. columns.map(col => sql.identifier(col)),
  161. ', ',
  162. )} FROM users`,
  163. ),
  164. {
  165. default: `SELECT [a], [b], [c] FROM users`,
  166. },
  167. );
  168. // string parts, SQL expression separator
  169. expectsql(
  170. queryGenerator.escape(
  171. sql`SELECT a FROM users WHERE id IN (${sql.join(['id1', 'id2', 'id3'], sql`, `)}) FROM users`,
  172. ),
  173. {
  174. default: `SELECT a FROM users WHERE id IN ('id1', 'id2', 'id3') FROM users`,
  175. mssql: `SELECT a FROM users WHERE id IN (N'id1', N'id2', N'id3') FROM users`,
  176. },
  177. );
  178. });
  179. });
  180. describe('sql.identifier', () => {
  181. it('accepts strings', () => {
  182. const out = queryGenerator.escape(sql.identifier('foo'));
  183. expectsql(out, {
  184. default: `[foo]`,
  185. });
  186. });
  187. it('accepts table structures', () => {
  188. const out = queryGenerator.escape(sql.identifier({ schema: 'foo', tableName: 'bar' }));
  189. expectsql(out, {
  190. default: `[foo].[bar]`,
  191. sqlite3: '`foo.bar`',
  192. });
  193. });
  194. it('accepts model classes', () => {
  195. const User = sequelize.define(
  196. 'User',
  197. {},
  198. {
  199. schema: 'schema',
  200. tableName: 'users',
  201. },
  202. );
  203. const out = queryGenerator.escape(sql.identifier(User));
  204. expectsql(out, {
  205. default: `[schema].[users]`,
  206. sqlite3: '`schema.users`',
  207. });
  208. });
  209. it('accepts model definitions', () => {
  210. const User = sequelize.define(
  211. 'User',
  212. {},
  213. {
  214. schema: 'schema',
  215. tableName: 'users',
  216. },
  217. );
  218. const out = queryGenerator.escape(sql.identifier(User.modelDefinition));
  219. expectsql(out, {
  220. default: `[schema].[users]`,
  221. sqlite3: '`schema.users`',
  222. });
  223. });
  224. it('accepts multiple parameters', () => {
  225. const User = sequelize.define(
  226. 'User',
  227. {},
  228. {
  229. schema: 'schema',
  230. tableName: 'table',
  231. },
  232. );
  233. const out = queryGenerator.escape(sql.identifier('database', User, 'column'));
  234. expectsql(out, {
  235. default: `[database].[schema].[table].[column]`,
  236. sqlite3: '`database`.`schema.table`.`column`',
  237. });
  238. });
  239. });