tokenizer.js 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318
  1. /* eslint-disable no-use-before-define */
  2. // ████████╗ ██████╗ ██╗ ██╗███████╗███╗ ██╗██╗███████╗███████╗██████╗
  3. // ╚══██╔══╝██╔═══██╗██║ ██╔╝██╔════╝████╗ ██║██║╚══███╔╝██╔════╝██╔══██╗
  4. // ██║ ██║ ██║█████╔╝ █████╗ ██╔██╗ ██║██║ ███╔╝ █████╗ ██████╔╝
  5. // ██║ ██║ ██║██╔═██╗ ██╔══╝ ██║╚██╗██║██║ ███╔╝ ██╔══╝ ██╔══██╗
  6. // ██║ ╚██████╔╝██║ ██╗███████╗██║ ╚████║██║███████╗███████╗██║ ██║
  7. // ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝╚═╝╚══════╝╚══════╝╚═╝ ╚═╝
  8. //
  9. // The tokenizer is responsible for taking a nested Waterline statement and
  10. // turning it into a flat set of keys. This allows the query to more easily be
  11. // parsed and prevents further recusion as the query progresses to eventually
  12. // end up as a native query.
  13. //
  14. // In most cases this will not be implemented by adapter authors but will be used
  15. // inside a database driver's `compileStatement` machine.
  16. var _ = require('@sailshq/lodash');
  17. // ╔═╗╦═╗╔═╗╔═╗╔═╗╔═╗╔═╗╔═╗╦═╗ ╔═╗╦ ╦╔╗╔╔═╗╔╦╗╦╔═╗╔╗╔╔═╗
  18. // ╠═╝╠╦╝║ ║║ ║╣ ╚═╗╚═╗║ ║╠╦╝ ╠╣ ║ ║║║║║ ║ ║║ ║║║║╚═╗
  19. // ╩ ╩╚═╚═╝╚═╝╚═╝╚═╝╚═╝╚═╝╩╚═ ╚ ╚═╝╝╚╝╚═╝ ╩ ╩╚═╝╝╚╝╚═╝
  20. // These are the identifiers used in RQL for various keys
  21. var identifiers = {
  22. 'select': 'SELECT',
  23. 'from': 'FROM',
  24. 'or': 'OR',
  25. 'and': 'AND',
  26. 'not': 'NOT',
  27. 'nin': 'NOTIN',
  28. 'in': 'IN',
  29. 'distinct': 'DISTINCT',
  30. 'count': 'COUNT',
  31. 'min': 'MIN',
  32. 'max': 'MAX',
  33. 'sum': 'SUM',
  34. 'avg': 'AVG',
  35. 'limit': 'LIMIT',
  36. 'skip': 'SKIP',
  37. 'groupBy': 'GROUPBY',
  38. 'orderBy': 'ORDERBY',
  39. 'where': 'WHERE',
  40. 'insert': 'INSERT',
  41. 'into': 'INTO',
  42. 'update': 'UPDATE',
  43. 'using': 'USING',
  44. 'del': 'DELETE',
  45. 'join': 'JOIN',
  46. 'innerJoin': 'JOIN',
  47. 'outerJoin': 'JOIN',
  48. 'crossJoin': 'JOIN',
  49. 'leftJoin': 'JOIN',
  50. 'leftOuterJoin': 'JOIN',
  51. 'rightJoin': 'JOIN',
  52. 'rightOuterJoin': 'JOIN',
  53. 'fullOuterJoin': 'JOIN',
  54. 'union': 'UNION',
  55. 'unionAll': 'UNIONALL',
  56. 'as': 'AS',
  57. '>': 'OPERATOR',
  58. '<': 'OPERATOR',
  59. '<>': 'OPERATOR',
  60. '<=': 'OPERATOR',
  61. '>=': 'OPERATOR',
  62. '!=': 'OPERATOR',
  63. 'like': 'OPERATOR',
  64. 'opts': 'OPTS',
  65. 'returning': 'RETURNING'
  66. };
  67. // If these identifiers are found within a WHERE clause, treat them as regular keys.
  68. var WHERE_EXEMPT = [
  69. 'from',
  70. 'distinct',
  71. 'count',
  72. 'min',
  73. 'max',
  74. 'sum',
  75. 'avg',
  76. 'insert',
  77. 'union',
  78. 'as',
  79. 'returning',
  80. 'join'
  81. ];
  82. // These are the Data Manipulation Identifiers that denote a subquery
  83. var DML_IDENTIFIERS = [
  84. 'select',
  85. 'insert',
  86. 'update',
  87. 'del'
  88. ];
  89. // ╔╦╗╔═╗╦╔═╔═╗╔╗╔╦╔═╗╔═╗ ┌─┐┌┐ ┬┌─┐┌─┐┌┬┐
  90. // ║ ║ ║╠╩╗║╣ ║║║║╔═╝║╣ │ │├┴┐ │├┤ │ │
  91. // ╩ ╚═╝╩ ╩╚═╝╝╚╝╩╚═╝╚═╝ └─┘└─┘└┘└─┘└─┘ ┴
  92. // @obj {Object} - the token obj being processed
  93. // @processor {Object} - a value to insert between each key in the array
  94. var tokenizeObject = function tokenizeObject(obj, processor, parent, isSubQuery, results) {
  95. // If this obj represent a sub-query, add a sub query token
  96. if (isSubQuery) {
  97. results.push({
  98. type: 'SUBQUERY',
  99. value: null
  100. });
  101. }
  102. // Determine whether we're currently processing a WHERE clause.
  103. // We use a stack of counters to do this, adding and removing from the stack
  104. // when subqueries are started / ended, and incrementing/decrementing the
  105. // first counter in the stack when WHERE clauses are started and ended.
  106. var inWhere = !!_.reduce(results, function(memo, item) {
  107. if (item.type === 'IDENTIFIER' || item.value === 'WHERE') {
  108. memo[0]++;
  109. }
  110. else if (item.type === 'ENDIDENTIFIER' || item.value === 'WHERE') {
  111. memo[0]--;
  112. }
  113. else if (item.type === 'SUBQUERY') {
  114. memo.unshift(0);
  115. }
  116. else if (item.type === 'ENDSUBQUERY') {
  117. memo.unshift();
  118. }
  119. return memo;
  120. }, [0])[0];
  121. _.each(_.keys(obj), function tokenizeKey(key, idx) {
  122. // Check if the key is a known identifier
  123. var isIdentitifier = identifiers[key];
  124. // If so, look ahead at it's value to determine what to do next.
  125. if (isIdentitifier) {
  126. // ╦ ╦╦ ╦╔═╗╦═╗╔═╗ ╔═╗═╗ ╦╔═╗╔╦╗╔═╗╔╦╗
  127. // ║║║╠═╣║╣ ╠╦╝║╣───║╣ ╔╩╦╝║╣ ║║║╠═╝ ║
  128. // ╚╩╝╩ ╩╚═╝╩╚═╚═╝ ╚═╝╩ ╚═╚═╝╩ ╩╩ ╩
  129. // If we're currently inside a WHERE clause and we encounter a key that is normally an identifier
  130. // but is in the WHERE_EXEMPT list, process it as a regular key.
  131. if (inWhere && _.contains(WHERE_EXEMPT, key)) {
  132. results.push({
  133. type: 'KEY',
  134. value: key
  135. });
  136. if (_.isObject(obj[key])) {
  137. tokenizeObject(obj[key], undefined, undefined, undefined, results);
  138. return;
  139. }
  140. results.push({
  141. type: 'VALUE',
  142. value: obj[key]
  143. });
  144. return;
  145. }
  146. // ╔═╗╔═╗╔═╗╦═╗╔═╗╔╦╗╔═╗╦═╗ ╔═╗╦═╗╔═╗╔╦╗╦╔═╗╔═╗╔╦╗╔═╗╔═╗
  147. // ║ ║╠═╝║╣ ╠╦╝╠═╣ ║ ║ ║╠╦╝ ╠═╝╠╦╝║╣ ║║║║ ╠═╣ ║ ║╣ ╚═╗
  148. // ╚═╝╩ ╚═╝╩╚═╩ ╩ ╩ ╚═╝╩╚═ ╩ ╩╚═╚═╝═╩╝╩╚═╝╩ ╩ ╩ ╚═╝╚═╝
  149. // If the identifier is an OPERATOR, add it's tokens
  150. if (identifiers[key] === 'OPERATOR') {
  151. // If there is a parent and the previous key in the results isn't
  152. // a KEY add it's key first. This is used when a key has multiple
  153. // criteria. EX: { values: { '>': 100, '<': 200 }}
  154. if (parent && _.last(results).type !== 'KEY') {
  155. results.push({
  156. type: 'KEY',
  157. value: parent
  158. });
  159. }
  160. processOperator(key, obj[key], results);
  161. return;
  162. }
  163. // If the identifier is an IN
  164. if (identifiers[key] === 'IN') {
  165. processIn(obj[key], undefined, results);
  166. return;
  167. }
  168. // If the identifier is an OR, start a group and add each token.
  169. if (identifiers[key] === 'OR') {
  170. processOr(obj[key], results);
  171. return;
  172. }
  173. // If the identifier is an AND, start a group and add each token.
  174. if (identifiers[key] === 'AND') {
  175. processAnd(obj[key], results);
  176. return;
  177. }
  178. // If the identifier is a NOT
  179. if (identifiers[key] === 'NOT') {
  180. processNot(obj[key], results);
  181. return;
  182. }
  183. // If the identifier is a NOTIN
  184. if (identifiers[key] === 'NOTIN') {
  185. processIn(obj[key], true, results);
  186. return;
  187. }
  188. // ╔═╗ ╦ ╦╔═╗╦═╗╦╔═╗╔═╗
  189. // ║═╬╗║ ║║╣ ╠╦╝║║╣ ╚═╗
  190. // ╚═╝╚╚═╝╚═╝╩╚═╩╚═╝╚═╝
  191. // If the identifier is a FROM, add it's token
  192. if (identifiers[key] === 'FROM') {
  193. processFrom(obj[key], results);
  194. return;
  195. }
  196. // If the identifier is a WHERE, add it's token and process it's values
  197. if (identifiers[key] === 'WHERE') {
  198. processWhere(obj[key], results);
  199. return;
  200. }
  201. // If the identifier is a GROUP BY aggregation
  202. if (identifiers[key] === 'GROUPBY') {
  203. processGroupBy(obj[key], results);
  204. return;
  205. }
  206. // If the identifier is an ORDER BY, add the sort options
  207. if (identifiers[key] === 'ORDERBY') {
  208. processOrderBy(obj[key], results);
  209. return;
  210. }
  211. // ╔╦╗╔╦╗╦ ╔═╗╔═╗╔╦╗╔╦╗╔═╗╔╗╔╔╦╗╔═╗
  212. // ║║║║║║ ║ ║ ║║║║║║║╠═╣║║║ ║║╚═╗
  213. // ═╩╝╩ ╩╩═╝ ╚═╝╚═╝╩ ╩╩ ╩╩ ╩╝╚╝═╩╝╚═╝
  214. // If the identifier is a SELECT, add it's token
  215. if (identifiers[key] === 'SELECT') {
  216. processSelect(obj[key], results);
  217. return;
  218. }
  219. // If the identifier is an INSERT, add it's token
  220. if (identifiers[key] === 'INSERT') {
  221. processInsert(obj[key], results);
  222. return;
  223. }
  224. // If the identifier is an UPDATE, add it's token
  225. if (identifiers[key] === 'UPDATE') {
  226. processUpdate(obj[key], results);
  227. return;
  228. }
  229. // If the identifier is a DELETE, add it's token
  230. if (identifiers[key] === 'DELETE') {
  231. processDelete(results);
  232. return;
  233. }
  234. // If the identifier is an INTO, add it's token
  235. if (identifiers[key] === 'INTO') {
  236. processInto(obj[key], results);
  237. return;
  238. }
  239. // If the identifier is an USING, add it's token
  240. if (identifiers[key] === 'USING') {
  241. processUsing(obj[key], results);
  242. return;
  243. }
  244. // ╔═╗╔═╗╔═╗╦═╗╔═╗╔═╗╔═╗╔╦╗╔═╗╔═╗
  245. // ╠═╣║ ╦║ ╦╠╦╝║╣ ║ ╦╠═╣ ║ ║╣ ╚═╗
  246. // ╩ ╩╚═╝╚═╝╩╚═╚═╝╚═╝╩ ╩ ╩ ╚═╝╚═╝
  247. // If the identifier is a AVG
  248. if (identifiers[key] === 'AVG') {
  249. processAggregations(obj[key], 'AVG', results);
  250. return;
  251. }
  252. // If the identifier is a SUM
  253. if (identifiers[key] === 'SUM') {
  254. processAggregations(obj[key], 'SUM', results);
  255. return;
  256. }
  257. // If the identifier is a MIN
  258. if (identifiers[key] === 'MIN') {
  259. processAggregations(obj[key], 'MIN', results);
  260. return;
  261. }
  262. // If the identifier is a MAX
  263. if (identifiers[key] === 'MAX') {
  264. processAggregations(obj[key], 'MAX', results);
  265. return;
  266. }
  267. // If the identifier is a COUNT
  268. if (identifiers[key] === 'COUNT') {
  269. processAggregations(obj[key], 'COUNT', results);
  270. return;
  271. }
  272. // ╔═╗╔╦╗╦ ╦╔═╗╦═╗
  273. // ║ ║ ║ ╠═╣║╣ ╠╦╝
  274. // ╚═╝ ╩ ╩ ╩╚═╝╩╚═
  275. // If the identifier is a LIMIT
  276. if (identifiers[key] === 'LIMIT') {
  277. processPagination(obj[key], 'LIMIT', results);
  278. return;
  279. }
  280. // If the indetifier is an SKIP
  281. if (identifiers[key] === 'SKIP') {
  282. processPagination(obj[key], 'SKIP', results);
  283. return;
  284. }
  285. // AS is only available on sub queries
  286. if (identifiers[key] === 'AS') {
  287. if (!isSubQuery) {
  288. return;
  289. }
  290. processAs(obj[key], results);
  291. return;
  292. }
  293. // If the indetifier is an RETURNING
  294. if (identifiers[key] === 'RETURNING') {
  295. processReturning(obj[key], results);
  296. return;
  297. }
  298. // ╦╔═╗╦╔╗╔╔═╗
  299. // ║║ ║║║║║╚═╗
  300. // ╚╝╚═╝╩╝╚╝╚═╝
  301. // If the identifier is a JOIN, add it's token and process the joins
  302. if (identifiers[key] === 'JOIN') {
  303. processJoin(obj[key], key, results);
  304. return;
  305. }
  306. // ╦ ╦╔╗╔╦╔═╗╔╗╔╔═╗
  307. // ║ ║║║║║║ ║║║║╚═╗
  308. // ╚═╝╝╚╝╩╚═╝╝╚╝╚═╝
  309. // If the identifier is a UNION
  310. if (identifiers[key] === 'UNION') {
  311. processUnion(obj[key], 'UNION', results);
  312. return;
  313. }
  314. // If the identifier is a UNIONALL
  315. if (identifiers[key] === 'UNIONALL') {
  316. processUnion(obj[key], 'UNIONALL', results);
  317. return;
  318. }
  319. // ╔═╗╔═╗╔╦╗╔═╗
  320. // ║ ║╠═╝ ║ ╚═╗
  321. // ╚═╝╩ ╩ ╚═╝
  322. // Handle any known values in the opts. Opts must be a dictionary.
  323. if (identifiers[key] === 'OPTS') {
  324. if (!_.isPlainObject(obj[key])) {
  325. return;
  326. }
  327. _.each(obj[key], function processOpt(val, key) {
  328. // Handle PG schema values
  329. if (key === 'schema') {
  330. return processSchema(val, results);
  331. }
  332. });
  333. return;
  334. }
  335. // Add the identifier
  336. results.push({
  337. type: identifiers[key],
  338. value: key
  339. });
  340. // If the identifier is an array, loop through each item and tokenize
  341. if (_.isArray(obj[key])) {
  342. _.each(obj[key], function tokenizeJoinPiece(expr) {
  343. tokenizeObject(expr, undefined, undefined, undefined, results);
  344. });
  345. return;
  346. }
  347. // If the identifier is an object, continue tokenizing it
  348. if (_.isPlainObject(obj[key])) {
  349. tokenizeObject(obj[key], undefined, key, undefined, results);
  350. return;
  351. }
  352. // Otherwise WTF?
  353. return;
  354. }
  355. // Otherwise add the token for the key
  356. results.push({
  357. type: 'KEY',
  358. value: key
  359. });
  360. // If the value is an object, recursively parse it unless it matches as
  361. // a sub query
  362. if (_.isPlainObject(obj[key])) {
  363. // Check if the value is a subquery first
  364. var subQuery = checkForSubquery(obj[key], results);
  365. if (subQuery) {
  366. return;
  367. }
  368. // Otherwise parse the object
  369. tokenizeObject(obj[key], undefined, key, undefined, results);
  370. return;
  371. }
  372. // If the value is a primitive add it's token
  373. results.push({
  374. type: 'VALUE',
  375. value: obj[key]
  376. });
  377. // If there is a processor and we are not on the last key, add it as well.
  378. // This is used for things like:
  379. // {
  380. // not: {
  381. // firstName: 'foo',
  382. // lastName: 'bar'
  383. // }
  384. // }
  385. // Where we need to insert a NOT statement between each key
  386. if (processor && (_.keys(obj).length > idx + 1)) {
  387. results.push(processor);
  388. }
  389. });
  390. // If this obj represent a sub-query, close the sub query token
  391. if (isSubQuery) {
  392. results.push({
  393. type: 'ENDSUBQUERY',
  394. value: null
  395. });
  396. }
  397. };
  398. // ╔═╗╦ ╦╔═╗╔═╗╦╔═ ╔═╗╔═╗╦═╗ ╔═╗╦ ╦╔╗ ╔═╗ ╦ ╦╔═╗╦═╗╦ ╦
  399. // ║ ╠═╣║╣ ║ ╠╩╗ ╠╣ ║ ║╠╦╝ ╚═╗║ ║╠╩╗║═╬╗║ ║║╣ ╠╦╝╚╦╝
  400. // ╚═╝╩ ╩╚═╝╚═╝╩ ╩ ╚ ╚═╝╩╚═ ╚═╝╚═╝╚═╝╚═╝╚╚═╝╚═╝╩╚═ ╩
  401. var checkForSubquery = function checkForSubquery(value, results) {
  402. var isSubquery = false;
  403. // Check if the object has any top level DML identifiers
  404. _.each(value, function checkForIdentifier(val, key) {
  405. if (_.indexOf(DML_IDENTIFIERS, key) < 0) {
  406. return;
  407. }
  408. isSubquery = true;
  409. });
  410. // If this is a sub query, tokenize it as such
  411. if (isSubquery) {
  412. tokenizeObject(value, undefined, undefined, isSubquery, results);
  413. return isSubquery;
  414. }
  415. return isSubquery;
  416. };
  417. // ╔═╗╔═╗╔═╗╦═╗╔═╗╔╦╗╔═╗╦═╗╔═╗
  418. // ║ ║╠═╝║╣ ╠╦╝╠═╣ ║ ║ ║╠╦╝╚═╗
  419. // ╚═╝╩ ╚═╝╩╚═╩ ╩ ╩ ╚═╝╩╚═╚═╝
  420. var processOperator = function processOperator(operator, value, results) {
  421. // Add the operator to the results
  422. results.push({
  423. type: 'OPERATOR',
  424. value: operator
  425. });
  426. results.push({
  427. type: 'VALUE',
  428. value: value
  429. });
  430. // Add the operator to the results
  431. results.push({
  432. type: 'ENDOPERATOR',
  433. value: operator
  434. });
  435. };
  436. // ╔═╗╔═╗╦ ╔═╗╔═╗╔╦╗ ╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╗╔╔╦╗
  437. // ╚═╗║╣ ║ ║╣ ║ ║ ╚═╗ ║ ╠═╣ ║ ║╣ ║║║║╣ ║║║ ║
  438. // ╚═╝╚═╝╩═╝╚═╝╚═╝ ╩ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝╩ ╩╚═╝╝╚╝ ╩
  439. var processSelect = function processSelect(value, results) {
  440. // Check if a distinct or other key is being used
  441. if (_.isPlainObject(value) && !_.isArray(value)) {
  442. if (value.distinct) {
  443. // Add the distinct to the results
  444. results.push({
  445. type: 'IDENTIFIER',
  446. value: 'DISTINCT'
  447. });
  448. // Add the value to the results
  449. results.push({
  450. type: 'VALUE',
  451. value: value.distinct
  452. });
  453. // Add the enddistinct to the results
  454. results.push({
  455. type: 'ENDIDENTIFIER',
  456. value: 'DISTINCT'
  457. });
  458. return;
  459. }
  460. }
  461. // If the value is not an array or object, add the value
  462. if (!_.isPlainObject(value) && !_.isArray(value)) {
  463. // Add the SELECT to the results
  464. results.push({
  465. type: 'IDENTIFIER',
  466. value: 'SELECT'
  467. });
  468. // Add the value to the results
  469. results.push({
  470. type: 'VALUE',
  471. value: value
  472. });
  473. // Add the ENDSELECT to the results
  474. results.push({
  475. type: 'ENDIDENTIFIER',
  476. value: 'SELECT'
  477. });
  478. return;
  479. }
  480. // If the value is not an array, make it one so that we can process each
  481. // element.
  482. if (!_.isArray(value)) {
  483. value = [value];
  484. }
  485. // Process each item in there SELECT statement and process subqueries as
  486. // needed.
  487. _.each(value, function processSelectKey(val) {
  488. // Add the SELECT to the results
  489. results.push({
  490. type: 'IDENTIFIER',
  491. value: 'SELECT'
  492. });
  493. // If the value isn't an object, no need to process it further
  494. if (!_.isPlainObject(val)) {
  495. results.push({
  496. type: 'VALUE',
  497. value: val
  498. });
  499. }
  500. // Check if the object is a sub-query
  501. if (_.isPlainObject(val)) {
  502. var isSubquery = checkForSubquery(val, results);
  503. // If it's not, add it's value
  504. if (!isSubquery) {
  505. results.push({
  506. type: 'VALUE',
  507. value: val
  508. });
  509. }
  510. }
  511. // Add the ENDSELECT to the results
  512. results.push({
  513. type: 'ENDIDENTIFIER',
  514. value: 'SELECT'
  515. });
  516. });
  517. };
  518. // ╔═╗╦═╗╔═╗╔╦╗ ╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╗╔╔╦╗
  519. // ╠╣ ╠╦╝║ ║║║║ ╚═╗ ║ ╠═╣ ║ ║╣ ║║║║╣ ║║║ ║
  520. // ╚ ╩╚═╚═╝╩ ╩ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝╩ ╩╚═╝╝╚╝ ╩
  521. var processFrom = function processFrom(value, results) {
  522. // Check if a schema is being used
  523. if (_.isObject(value) && !_.isFunction(value) && !_.isArray(value)) {
  524. // Add the FROM identifier
  525. results.push({
  526. type: 'IDENTIFIER',
  527. value: 'FROM'
  528. });
  529. // Check if a subquery is being used
  530. var isSubQuery = checkForSubquery(value, results);
  531. if (!isSubQuery && value.table) {
  532. results.push({
  533. type: 'VALUE',
  534. value: value.table
  535. });
  536. }
  537. results.push({
  538. type: 'ENDIDENTIFIER',
  539. value: 'FROM'
  540. });
  541. return;
  542. }
  543. // Otherwise just add the FROM identifier and value
  544. results.push({
  545. type: 'IDENTIFIER',
  546. value: 'FROM'
  547. });
  548. results.push({
  549. type: 'VALUE',
  550. value: value
  551. });
  552. results.push({
  553. type: 'ENDIDENTIFIER',
  554. value: 'FROM'
  555. });
  556. };
  557. // ╔═╗╔═╗╦ ╦╔═╗╔╦╗╔═╗ ╔═╗╔═╗╔╦╗
  558. // ╚═╗║ ╠═╣║╣ ║║║╠═╣ ║ ║╠═╝ ║
  559. // ╚═╝╚═╝╩ ╩╚═╝╩ ╩╩ ╩ ╚═╝╩ ╩
  560. var processSchema = function processSchema(value, results) {
  561. results.push({
  562. type: 'IDENTIFIER',
  563. value: 'SCHEMA'
  564. });
  565. results.push({
  566. type: 'VALUE',
  567. value: value
  568. });
  569. results.push({
  570. type: 'ENDIDENTIFIER',
  571. value: 'SCHEMA'
  572. });
  573. };
  574. // ╦╔╗╔╔═╗╔═╗╦═╗╔╦╗ ╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╗╔╔╦╗
  575. // ║║║║╚═╗║╣ ╠╦╝ ║ ╚═╗ ║ ╠═╣ ║ ║╣ ║║║║╣ ║║║ ║
  576. // ╩╝╚╝╚═╝╚═╝╩╚═ ╩ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝╩ ╩╚═╝╝╚╝ ╩
  577. var processInsert = function processInsert(value, results) {
  578. // Add the insert statment
  579. results.push({
  580. type: 'IDENTIFIER',
  581. value: 'INSERT'
  582. });
  583. // Check if an array is being used
  584. if (_.isArray(value)) {
  585. _.each(value, function appendInsertEach(record, idx) {
  586. // Add a group clause
  587. results.push({
  588. type: 'GROUP',
  589. value: idx
  590. });
  591. // If the value is a plain object, proccess it
  592. if (_.isObject(record) && !_.isFunction(record) && !_.isArray(record)) {
  593. _.each(_.keys(record), function appendInsertValue(key) {
  594. results.push({
  595. type: 'KEY',
  596. value: key
  597. });
  598. results.push({
  599. type: 'VALUE',
  600. value: record[key]
  601. });
  602. });
  603. }
  604. // Close the group clause
  605. results.push({
  606. type: 'ENDGROUP',
  607. value: idx
  608. });
  609. });
  610. }
  611. // Check if a plain object value is being used
  612. if (_.isObject(value) && !_.isFunction(value) && !_.isArray(value)) {
  613. _.each(_.keys(value), function appendInsertValue(key) {
  614. results.push({
  615. type: 'KEY',
  616. value: key
  617. });
  618. results.push({
  619. type: 'VALUE',
  620. value: value[key]
  621. });
  622. });
  623. }
  624. results.push({
  625. type: 'ENDIDENTIFIER',
  626. value: 'INSERT'
  627. });
  628. };
  629. // ╦╔╗╔╔╦╗╔═╗ ╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╗╔╔╦╗
  630. // ║║║║ ║ ║ ║ ╚═╗ ║ ╠═╣ ║ ║╣ ║║║║╣ ║║║ ║
  631. // ╩╝╚╝ ╩ ╚═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝╩ ╩╚═╝╝╚╝ ╩
  632. var processInto = function processInto(value, results) {
  633. results.push({
  634. type: 'IDENTIFIER',
  635. value: 'INTO'
  636. });
  637. results.push({
  638. type: 'VALUE',
  639. value: value
  640. });
  641. results.push({
  642. type: 'ENDIDENTIFIER',
  643. value: 'INTO'
  644. });
  645. };
  646. // ╦ ╦╔═╗╔╦╗╔═╗╔╦╗╔═╗ ╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╗╔╔╦╗
  647. // ║ ║╠═╝ ║║╠═╣ ║ ║╣ ╚═╗ ║ ╠═╣ ║ ║╣ ║║║║╣ ║║║ ║
  648. // ╚═╝╩ ═╩╝╩ ╩ ╩ ╚═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝╩ ╩╚═╝╝╚╝ ╩
  649. var processUpdate = function processUpdate(value, results) {
  650. // Add the update statment
  651. results.push({
  652. type: 'IDENTIFIER',
  653. value: 'UPDATE'
  654. });
  655. // Check if a value is being used
  656. if (_.isObject(value)) {
  657. _.each(_.keys(value), function appendUpdateValue(key) {
  658. results.push({
  659. type: 'KEY',
  660. value: key
  661. });
  662. results.push({
  663. type: 'VALUE',
  664. value: value[key]
  665. });
  666. });
  667. }
  668. results.push({
  669. type: 'ENDIDENTIFIER',
  670. value: 'UPDATE'
  671. });
  672. };
  673. // ╦ ╦╔═╗╦╔╗╔╔═╗ ╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╗╔╔╦╗
  674. // ║ ║╚═╗║║║║║ ╦ ╚═╗ ║ ╠═╣ ║ ║╣ ║║║║╣ ║║║ ║
  675. // ╚═╝╚═╝╩╝╚╝╚═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝╩ ╩╚═╝╝╚╝ ╩
  676. var processUsing = function processUsing(value, results) {
  677. results.push({
  678. type: 'IDENTIFIER',
  679. value: 'USING'
  680. });
  681. results.push({
  682. type: 'VALUE',
  683. value: value
  684. });
  685. results.push({
  686. type: 'ENDIDENTIFIER',
  687. value: 'USING'
  688. });
  689. };
  690. // ╔╦╗╔═╗╦ ╔═╗╔╦╗╔═╗ ╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╗╔╔╦╗
  691. // ║║║╣ ║ ║╣ ║ ║╣ ╚═╗ ║ ╠═╣ ║ ║╣ ║║║║╣ ║║║ ║
  692. // ═╩╝╚═╝╩═╝╚═╝ ╩ ╚═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝╩ ╩╚═╝╝╚╝ ╩
  693. var processDelete = function processDelete(results) {
  694. results.push({
  695. type: 'IDENTIFIER',
  696. value: 'DELETE'
  697. });
  698. results.push({
  699. type: 'ENDIDENTIFIER',
  700. value: 'DELETE'
  701. });
  702. };
  703. // ╔╗╔╔═╗╔╦╗ ╔═╗╔═╗╔╗╔╔╦╗╦╔╦╗╦╔═╗╔╗╔
  704. // ║║║║ ║ ║ ║ ║ ║║║║ ║║║ ║ ║║ ║║║║
  705. // ╝╚╝╚═╝ ╩ ╚═╝╚═╝╝╚╝═╩╝╩ ╩ ╩╚═╝╝╚╝
  706. var processNot = function processNot(value, results) {
  707. // Add a condition
  708. var condition = {
  709. type: 'CONDITION',
  710. value: 'NOT'
  711. };
  712. results.push(condition);
  713. // Tokenize the values within the condition
  714. if (_.isObject(value) && !_.isFunction(value) && !_.isArray(value)) {
  715. tokenizeObject(value, condition, undefined, undefined, results);
  716. return;
  717. }
  718. results.push({
  719. type: 'VALUE',
  720. value: value
  721. });
  722. results.push({
  723. type: 'ENDCONDITION',
  724. value: 'NOT'
  725. });
  726. };
  727. // ╦╔╗╔ ╔═╗╔═╗╔╗╔╔╦╗╦╔╦╗╦╔═╗╔╗╔
  728. // ║║║║ ║ ║ ║║║║ ║║║ ║ ║║ ║║║║
  729. // ╩╝╚╝ ╚═╝╚═╝╝╚╝═╩╝╩ ╩ ╩╚═╝╝╚╝
  730. var processIn = function processIn(value, negate, results) {
  731. // Add a condition
  732. var startCondition;
  733. var endCondition;
  734. if (negate) {
  735. startCondition = {
  736. type: 'CONDITION',
  737. value: 'NOTIN'
  738. };
  739. endCondition = {
  740. type: 'ENDCONDITION',
  741. value: 'NOTIN'
  742. };
  743. } else {
  744. startCondition = {
  745. type: 'CONDITION',
  746. value: 'IN'
  747. };
  748. endCondition = {
  749. type: 'ENDCONDITION',
  750. value: 'IN'
  751. };
  752. }
  753. results.push(startCondition);
  754. // If the value isn't an object, no need to process it further
  755. if (!_.isPlainObject(value)) {
  756. results.push({
  757. type: 'VALUE',
  758. value: value
  759. });
  760. }
  761. // Check if the object is a sub-query
  762. if (_.isObject(value) && !_.isFunction(value) && !_.isArray(value)) {
  763. var isSubquery = checkForSubquery(value, results);
  764. // If it's not, add it's value
  765. if (!isSubquery) {
  766. results.push({
  767. type: 'VALUE',
  768. value: value
  769. });
  770. }
  771. }
  772. results.push(endCondition);
  773. };
  774. // ╦ ╦╦ ╦╔═╗╦═╗╔═╗ ╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╗╔╔╦╗
  775. // ║║║╠═╣║╣ ╠╦╝║╣ ╚═╗ ║ ╠═╣ ║ ║╣ ║║║║╣ ║║║ ║
  776. // ╚╩╝╩ ╩╚═╝╩╚═╚═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝╩ ╩╚═╝╝╚╝ ╩
  777. var processWhere = function processWhere(value, results) {
  778. // Tokenize the where and then call the tokenizer on the where values
  779. results.push({
  780. type: 'IDENTIFIER',
  781. value: 'WHERE'
  782. });
  783. tokenizeObject(value, undefined, undefined, undefined, results);
  784. results.push({
  785. type: 'ENDIDENTIFIER',
  786. value: 'WHERE'
  787. });
  788. };
  789. // ╔═╗╦═╗ ╔═╗╦═╗╔═╗╦ ╦╔═╗╦╔╗╔╔═╗
  790. // ║ ║╠╦╝ ║ ╦╠╦╝║ ║║ ║╠═╝║║║║║ ╦
  791. // ╚═╝╩╚═ ╚═╝╩╚═╚═╝╚═╝╩ ╩╝╚╝╚═╝
  792. var processOr = function processOr(value, results) {
  793. // Add the Or token
  794. results.push({
  795. type: 'CONDITION',
  796. value: 'OR'
  797. });
  798. // For each condition in the OR, add a group token and process the criteria.
  799. _.forEach(value, function appendOrCrieria(criteria, idx) {
  800. // Start a group
  801. results.push({
  802. type: 'GROUP',
  803. value: idx
  804. });
  805. tokenizeObject(criteria, undefined, undefined, undefined, results);
  806. // End a group
  807. results.push({
  808. type: 'ENDGROUP',
  809. value: idx
  810. });
  811. });
  812. // Close the condition
  813. results.push({
  814. type: 'ENDCONDITION',
  815. value: 'OR'
  816. });
  817. };
  818. // ╔═╗╔╗╔╔╦╗ ╔═╗╦═╗╔═╗╦ ╦╔═╗╦╔╗╔╔═╗
  819. // ╠═╣║║║ ║║ ║ ╦╠╦╝║ ║║ ║╠═╝║║║║║ ╦
  820. // ╩ ╩╝╚╝═╩╝ ╚═╝╩╚═╚═╝╚═╝╩ ╩╝╚╝╚═╝
  821. var processAnd = function processAnd(value, results) {
  822. // Only process grouped AND's if the value is an array
  823. if (!_.isArray(value)) {
  824. return;
  825. }
  826. // Add the AND token
  827. results.push({
  828. type: 'CONDITION',
  829. value: 'AND'
  830. });
  831. // For each condition in the OR, add a group token and process the criteria.
  832. _.each(value, function appendAndCrieria(criteria, idx) {
  833. // Start a group
  834. results.push({
  835. type: 'GROUP',
  836. value: idx
  837. });
  838. tokenizeObject(criteria, undefined, undefined, undefined, results);
  839. // End a group
  840. results.push({
  841. type: 'ENDGROUP',
  842. value: idx
  843. });
  844. });
  845. // Close the condition
  846. results.push({
  847. type: 'ENDCONDITION',
  848. value: 'AND'
  849. });
  850. };
  851. // ╦╔═╗╦╔╗╔ ╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╗╔╔╦╗╔═╗
  852. // ║║ ║║║║║ ╚═╗ ║ ╠═╣ ║ ║╣ ║║║║╣ ║║║ ║ ╚═╗
  853. // ╚╝╚═╝╩╝╚╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝╩ ╩╚═╝╝╚╝ ╩ ╚═╝
  854. var processJoin = function processJoin(value, joinType, results) {
  855. // Ensure we have an array value
  856. if (!_.isArray(value)) {
  857. value = [value];
  858. }
  859. _.each(value, function processJoinInstructions(joinInstructions) {
  860. // Add a JOIN token
  861. results.push({
  862. type: 'IDENTIFIER',
  863. value: joinType.toUpperCase()
  864. });
  865. // Ensure the instructions include a FROM and an ON and that the ON
  866. // is made up of two table keys.
  867. if (!_.has(joinInstructions, 'from') || !_.has(joinInstructions, 'on')) {
  868. throw new Error('Invalid join instructions');
  869. }
  870. // Check if this is an AND or an OR join statement. An AND statement will
  871. // just be an array of conditions and an OR statement will have a single
  872. // OR key as the value.
  873. // Process AND
  874. if (_.isArray(joinInstructions.on)) {
  875. (function andInstructions() {
  876. var JOIN_TABLE = joinInstructions.from;
  877. results.push({ type: 'KEY', value: 'TABLE' });
  878. results.push({ type: 'VALUE', value: JOIN_TABLE });
  879. _.each(joinInstructions.on, function onSet(set) {
  880. var PARENT_TABLE = _.first(_.keys(set));
  881. var CHILD_TABLE = _.keys(set)[1];
  882. var PARENT_COLUMN = set[_.first(_.keys(set))];
  883. var CHILD_COLUMN = set[_.keys(set)[1]];
  884. var setKeys = [
  885. { type: 'COMBINATOR', value: 'AND' },
  886. { type: 'KEY', value: 'TABLE_KEY' },
  887. { type: 'VALUE', value: PARENT_TABLE },
  888. { type: 'KEY', value: 'COLUMN_KEY' },
  889. { type: 'VALUE', value: PARENT_COLUMN },
  890. { type: 'KEY', value: 'TABLE_KEY' },
  891. { type: 'VALUE', value: CHILD_TABLE },
  892. { type: 'KEY', value: 'COLUMN_KEY' },
  893. { type: 'VALUE', value: CHILD_COLUMN }
  894. ];
  895. _.each(setKeys, function appendSet(set) {
  896. results.push(set);
  897. });
  898. });
  899. })();
  900. // Process OR
  901. } else if (_.isArray(joinInstructions.on.or)) {
  902. (function orInstructions() {
  903. var JOIN_TABLE = joinInstructions.from;
  904. results.push({ type: 'KEY', value: 'TABLE' });
  905. results.push({ type: 'VALUE', value: JOIN_TABLE });
  906. _.each(joinInstructions.on.or, function orSet(set) {
  907. var PARENT_TABLE = _.first(_.keys(set));
  908. var CHILD_TABLE = _.keys(set)[1];
  909. var PARENT_COLUMN = set[_.first(_.keys(set))];
  910. var CHILD_COLUMN = set[_.keys(set)[1]];
  911. var setKeys = [
  912. { type: 'COMBINATOR', value: 'OR' },
  913. { type: 'KEY', value: 'TABLE_KEY' },
  914. { type: 'VALUE', value: PARENT_TABLE },
  915. { type: 'KEY', value: 'COLUMN_KEY' },
  916. { type: 'VALUE', value: PARENT_COLUMN },
  917. { type: 'KEY', value: 'TABLE_KEY' },
  918. { type: 'VALUE', value: CHILD_TABLE },
  919. { type: 'KEY', value: 'COLUMN_KEY' },
  920. { type: 'VALUE', value: CHILD_COLUMN }
  921. ];
  922. _.each(setKeys, function appendSet(set) {
  923. results.push(set);
  924. });
  925. });
  926. })();
  927. // Otherwise ensure that the ON key has two keys
  928. } else if (!_.isPlainObject(joinInstructions.on) || _.keys(joinInstructions.on).length !== 2) {
  929. throw new Error('Invalid join instructions');
  930. // Handle normal, single level joins
  931. } else {
  932. (function buildJoinResults() {
  933. var JOIN_TABLE = joinInstructions.from;
  934. var PARENT_TABLE = _.first(_.keys(joinInstructions.on));
  935. var CHILD_TABLE = _.keys(joinInstructions.on)[1];
  936. var PARENT_COLUMN = joinInstructions.on[_.first(_.keys(joinInstructions.on))];
  937. var CHILD_COLUMN = joinInstructions.on[_.keys(joinInstructions.on)[1]];
  938. var joinResults = [
  939. { type: 'KEY', value: 'TABLE' },
  940. { type: 'VALUE', value: JOIN_TABLE },
  941. { type: 'KEY', value: 'TABLE_KEY' },
  942. { type: 'VALUE', value: PARENT_TABLE },
  943. { type: 'KEY', value: 'COLUMN_KEY' },
  944. { type: 'VALUE', value: PARENT_COLUMN },
  945. { type: 'KEY', value: 'TABLE_KEY' },
  946. { type: 'VALUE', value: CHILD_TABLE },
  947. { type: 'KEY', value: 'COLUMN_KEY' },
  948. { type: 'VALUE', value: CHILD_COLUMN }
  949. ];
  950. _.each(joinResults, function appendSet(set) {
  951. results.push(set);
  952. });
  953. })();
  954. }
  955. results.push({
  956. type: 'ENDIDENTIFIER',
  957. value: joinType.toUpperCase()
  958. });
  959. });
  960. };
  961. // ╔═╗╦═╗╔═╗╦ ╦╔═╗ ╔╗ ╦ ╦
  962. // ║ ╦╠╦╝║ ║║ ║╠═╝ ╠╩╗╚╦╝
  963. // ╚═╝╩╚═╚═╝╚═╝╩ ╚═╝ ╩
  964. var processGroupBy = function processGroupBy(value, results) {
  965. results.push({
  966. type: 'IDENTIFIER',
  967. value: 'GROUPBY'
  968. });
  969. results.push({
  970. type: 'VALUE',
  971. value: value
  972. });
  973. results.push({
  974. type: 'ENDIDENTIFIER',
  975. value: 'GROUPBY'
  976. });
  977. };
  978. // ╔═╗╦═╗╔╦╗╔═╗╦═╗ ╔╗ ╦ ╦
  979. // ║ ║╠╦╝ ║║║╣ ╠╦╝ ╠╩╗╚╦╝
  980. // ╚═╝╩╚══╩╝╚═╝╩╚═ ╚═╝ ╩
  981. var processOrderBy = function processOrderBy(values, results) {
  982. // Tokenize the order by and then call the tokenizer on the values
  983. results.push({
  984. type: 'IDENTIFIER',
  985. value: 'ORDERBY'
  986. });
  987. if (!_.isArray(values)) {
  988. values = [values];
  989. }
  990. _.each(values, function tokenizeSet(tokenSet) {
  991. tokenizeObject(tokenSet, undefined, undefined, undefined, results);
  992. });
  993. results.push({
  994. type: 'ENDIDENTIFIER',
  995. value: 'ORDERBY'
  996. });
  997. };
  998. // ╔═╗╔═╗╔═╗╦═╗╔═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗
  999. // ╠═╣║ ╦║ ╦╠╦╝║╣ ║ ╦╠═╣ ║ ║║ ║║║║╚═╗
  1000. // ╩ ╩╚═╝╚═╝╩╚═╚═╝╚═╝╩ ╩ ╩ ╩╚═╝╝╚╝╚═╝
  1001. var processAggregations = function processAggregations(value, aggregation, results) {
  1002. results.push({
  1003. type: 'IDENTIFIER',
  1004. value: aggregation
  1005. });
  1006. results.push({
  1007. type: 'VALUE',
  1008. value: value
  1009. });
  1010. results.push({
  1011. type: 'ENDIDENTIFIER',
  1012. value: aggregation
  1013. });
  1014. };
  1015. // ╔═╗╔═╗╔═╗╦╔╗╔╔═╗╔╦╗╦╔═╗╔╗╔
  1016. // ╠═╝╠═╣║ ╦║║║║╠═╣ ║ ║║ ║║║║
  1017. // ╩ ╩ ╩╚═╝╩╝╚╝╩ ╩ ╩ ╩╚═╝╝╚╝
  1018. var processPagination = function processPagination(value, operator, results) {
  1019. results.push({
  1020. type: 'IDENTIFIER',
  1021. value: operator
  1022. });
  1023. results.push({
  1024. type: 'VALUE',
  1025. value: value
  1026. });
  1027. results.push({
  1028. type: 'ENDIDENTIFIER',
  1029. value: operator
  1030. });
  1031. };
  1032. // ╔═╗╦═╗╔═╗╔═╗╔═╗╔═╗╔═╗ ╦ ╦╔╗╔╦╔═╗╔╗╔
  1033. // ╠═╝╠╦╝║ ║║ ║╣ ╚═╗╚═╗ ║ ║║║║║║ ║║║║
  1034. // ╩ ╩╚═╚═╝╚═╝╚═╝╚═╝╚═╝ ╚═╝╝╚╝╩╚═╝╝╚╝
  1035. var processUnion = function processUnion(values, type, results) {
  1036. results.push({
  1037. type: 'UNION',
  1038. value: type
  1039. });
  1040. _.each(values, function processUnionValue(value, idx) {
  1041. // Start each union subquery with an ENDGROUP
  1042. results.push({
  1043. type: 'GROUP',
  1044. value: idx
  1045. });
  1046. // Build the subquery
  1047. checkForSubquery(value, results);
  1048. // Close each subquery with an ENDGROUP token
  1049. results.push({
  1050. type: 'ENDGROUP',
  1051. value: idx
  1052. });
  1053. });
  1054. results.push({
  1055. type: 'ENDUNION',
  1056. value: type
  1057. });
  1058. };
  1059. // ╔═╗╦═╗╔═╗╔═╗╔═╗╔═╗╔═╗ ╔═╗╔═╗
  1060. // ╠═╝╠╦╝║ ║║ ║╣ ╚═╗╚═╗ ╠═╣╚═╗
  1061. // ╩ ╩╚═╚═╝╚═╝╚═╝╚═╝╚═╝ ╩ ╩╚═╝
  1062. var processAs = function processAs(value, results) {
  1063. results.push({
  1064. type: 'IDENTIFIER',
  1065. value: 'AS'
  1066. });
  1067. results.push({
  1068. type: 'VALUE',
  1069. value: value
  1070. });
  1071. results.push({
  1072. type: 'ENDIDENTIFIER',
  1073. value: 'AS'
  1074. });
  1075. };
  1076. // ╦═╗╔═╗╔╦╗╦ ╦╦═╗╔╗╔╦╔╗╔╔═╗
  1077. // ╠╦╝║╣ ║ ║ ║╠╦╝║║║║║║║║ ╦
  1078. // ╩╚═╚═╝ ╩ ╚═╝╩╚═╝╚╝╩╝╚╝╚═╝
  1079. var processReturning = function processReturning(value, results) {
  1080. // Add the RETURNING to the results
  1081. results.push({
  1082. type: 'IDENTIFIER',
  1083. value: 'RETURNING'
  1084. });
  1085. results.push({
  1086. type: 'VALUE',
  1087. value: value
  1088. });
  1089. results.push({
  1090. type: 'ENDIDENTIFIER',
  1091. value: 'RETURNING'
  1092. });
  1093. };
  1094. module.exports = function tokenizer(expression) {
  1095. if (!expression) {
  1096. throw new Error('Missing expression');
  1097. }
  1098. // Hold the built up results
  1099. var results = [];
  1100. // Kick off recursive parsing of the RQL object
  1101. tokenizeObject(expression, undefined, undefined, undefined, results);
  1102. // Return the tokenenized result set
  1103. return results;
  1104. };
  1105. /* eslint-enable no-use-before-define */