1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318 |
- /* eslint-disable no-use-before-define */
- // ████████╗ ██████╗ ██╗ ██╗███████╗███╗ ██╗██╗███████╗███████╗██████╗
- // ╚══██╔══╝██╔═══██╗██║ ██╔╝██╔════╝████╗ ██║██║╚══███╔╝██╔════╝██╔══██╗
- // ██║ ██║ ██║█████╔╝ █████╗ ██╔██╗ ██║██║ ███╔╝ █████╗ ██████╔╝
- // ██║ ██║ ██║██╔═██╗ ██╔══╝ ██║╚██╗██║██║ ███╔╝ ██╔══╝ ██╔══██╗
- // ██║ ╚██████╔╝██║ ██╗███████╗██║ ╚████║██║███████╗███████╗██║ ██║
- // ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝╚═╝╚══════╝╚══════╝╚═╝ ╚═╝
- //
- // The tokenizer is responsible for taking a nested Waterline statement and
- // turning it into a flat set of keys. This allows the query to more easily be
- // parsed and prevents further recusion as the query progresses to eventually
- // end up as a native query.
- //
- // In most cases this will not be implemented by adapter authors but will be used
- // inside a database driver's `compileStatement` machine.
- var _ = require('@sailshq/lodash');
- // ╔═╗╦═╗╔═╗╔═╗╔═╗╔═╗╔═╗╔═╗╦═╗ ╔═╗╦ ╦╔╗╔╔═╗╔╦╗╦╔═╗╔╗╔╔═╗
- // ╠═╝╠╦╝║ ║║ ║╣ ╚═╗╚═╗║ ║╠╦╝ ╠╣ ║ ║║║║║ ║ ║║ ║║║║╚═╗
- // ╩ ╩╚═╚═╝╚═╝╚═╝╚═╝╚═╝╚═╝╩╚═ ╚ ╚═╝╝╚╝╚═╝ ╩ ╩╚═╝╝╚╝╚═╝
- // These are the identifiers used in RQL for various keys
- var identifiers = {
- 'select': 'SELECT',
- 'from': 'FROM',
- 'or': 'OR',
- 'and': 'AND',
- 'not': 'NOT',
- 'nin': 'NOTIN',
- 'in': 'IN',
- 'distinct': 'DISTINCT',
- 'count': 'COUNT',
- 'min': 'MIN',
- 'max': 'MAX',
- 'sum': 'SUM',
- 'avg': 'AVG',
- 'limit': 'LIMIT',
- 'skip': 'SKIP',
- 'groupBy': 'GROUPBY',
- 'orderBy': 'ORDERBY',
- 'where': 'WHERE',
- 'insert': 'INSERT',
- 'into': 'INTO',
- 'update': 'UPDATE',
- 'using': 'USING',
- 'del': 'DELETE',
- 'join': 'JOIN',
- 'innerJoin': 'JOIN',
- 'outerJoin': 'JOIN',
- 'crossJoin': 'JOIN',
- 'leftJoin': 'JOIN',
- 'leftOuterJoin': 'JOIN',
- 'rightJoin': 'JOIN',
- 'rightOuterJoin': 'JOIN',
- 'fullOuterJoin': 'JOIN',
- 'union': 'UNION',
- 'unionAll': 'UNIONALL',
- 'as': 'AS',
- '>': 'OPERATOR',
- '<': 'OPERATOR',
- '<>': 'OPERATOR',
- '<=': 'OPERATOR',
- '>=': 'OPERATOR',
- '!=': 'OPERATOR',
- 'like': 'OPERATOR',
- 'opts': 'OPTS',
- 'returning': 'RETURNING'
- };
- // If these identifiers are found within a WHERE clause, treat them as regular keys.
- var WHERE_EXEMPT = [
- 'from',
- 'distinct',
- 'count',
- 'min',
- 'max',
- 'sum',
- 'avg',
- 'insert',
- 'union',
- 'as',
- 'returning',
- 'join'
- ];
- // These are the Data Manipulation Identifiers that denote a subquery
- var DML_IDENTIFIERS = [
- 'select',
- 'insert',
- 'update',
- 'del'
- ];
- // ╔╦╗╔═╗╦╔═╔═╗╔╗╔╦╔═╗╔═╗ ┌─┐┌┐ ┬┌─┐┌─┐┌┬┐
- // ║ ║ ║╠╩╗║╣ ║║║║╔═╝║╣ │ │├┴┐ │├┤ │ │
- // ╩ ╚═╝╩ ╩╚═╝╝╚╝╩╚═╝╚═╝ └─┘└─┘└┘└─┘└─┘ ┴
- // @obj {Object} - the token obj being processed
- // @processor {Object} - a value to insert between each key in the array
- var tokenizeObject = function tokenizeObject(obj, processor, parent, isSubQuery, results) {
- // If this obj represent a sub-query, add a sub query token
- if (isSubQuery) {
- results.push({
- type: 'SUBQUERY',
- value: null
- });
- }
- // Determine whether we're currently processing a WHERE clause.
- // We use a stack of counters to do this, adding and removing from the stack
- // when subqueries are started / ended, and incrementing/decrementing the
- // first counter in the stack when WHERE clauses are started and ended.
- var inWhere = !!_.reduce(results, function(memo, item) {
- if (item.type === 'IDENTIFIER' || item.value === 'WHERE') {
- memo[0]++;
- }
- else if (item.type === 'ENDIDENTIFIER' || item.value === 'WHERE') {
- memo[0]--;
- }
- else if (item.type === 'SUBQUERY') {
- memo.unshift(0);
- }
- else if (item.type === 'ENDSUBQUERY') {
- memo.unshift();
- }
- return memo;
- }, [0])[0];
- _.each(_.keys(obj), function tokenizeKey(key, idx) {
- // Check if the key is a known identifier
- var isIdentitifier = identifiers[key];
- // If so, look ahead at it's value to determine what to do next.
- if (isIdentitifier) {
- // ╦ ╦╦ ╦╔═╗╦═╗╔═╗ ╔═╗═╗ ╦╔═╗╔╦╗╔═╗╔╦╗
- // ║║║╠═╣║╣ ╠╦╝║╣───║╣ ╔╩╦╝║╣ ║║║╠═╝ ║
- // ╚╩╝╩ ╩╚═╝╩╚═╚═╝ ╚═╝╩ ╚═╚═╝╩ ╩╩ ╩
- // If we're currently inside a WHERE clause and we encounter a key that is normally an identifier
- // but is in the WHERE_EXEMPT list, process it as a regular key.
- if (inWhere && _.contains(WHERE_EXEMPT, key)) {
- results.push({
- type: 'KEY',
- value: key
- });
- if (_.isObject(obj[key])) {
- tokenizeObject(obj[key], undefined, undefined, undefined, results);
- return;
- }
- results.push({
- type: 'VALUE',
- value: obj[key]
- });
- return;
- }
- // ╔═╗╔═╗╔═╗╦═╗╔═╗╔╦╗╔═╗╦═╗ ╔═╗╦═╗╔═╗╔╦╗╦╔═╗╔═╗╔╦╗╔═╗╔═╗
- // ║ ║╠═╝║╣ ╠╦╝╠═╣ ║ ║ ║╠╦╝ ╠═╝╠╦╝║╣ ║║║║ ╠═╣ ║ ║╣ ╚═╗
- // ╚═╝╩ ╚═╝╩╚═╩ ╩ ╩ ╚═╝╩╚═ ╩ ╩╚═╚═╝═╩╝╩╚═╝╩ ╩ ╩ ╚═╝╚═╝
- // If the identifier is an OPERATOR, add it's tokens
- if (identifiers[key] === 'OPERATOR') {
- // If there is a parent and the previous key in the results isn't
- // a KEY add it's key first. This is used when a key has multiple
- // criteria. EX: { values: { '>': 100, '<': 200 }}
- if (parent && _.last(results).type !== 'KEY') {
- results.push({
- type: 'KEY',
- value: parent
- });
- }
- processOperator(key, obj[key], results);
- return;
- }
- // If the identifier is an IN
- if (identifiers[key] === 'IN') {
- processIn(obj[key], undefined, results);
- return;
- }
- // If the identifier is an OR, start a group and add each token.
- if (identifiers[key] === 'OR') {
- processOr(obj[key], results);
- return;
- }
- // If the identifier is an AND, start a group and add each token.
- if (identifiers[key] === 'AND') {
- processAnd(obj[key], results);
- return;
- }
- // If the identifier is a NOT
- if (identifiers[key] === 'NOT') {
- processNot(obj[key], results);
- return;
- }
- // If the identifier is a NOTIN
- if (identifiers[key] === 'NOTIN') {
- processIn(obj[key], true, results);
- return;
- }
- // ╔═╗ ╦ ╦╔═╗╦═╗╦╔═╗╔═╗
- // ║═╬╗║ ║║╣ ╠╦╝║║╣ ╚═╗
- // ╚═╝╚╚═╝╚═╝╩╚═╩╚═╝╚═╝
- // If the identifier is a FROM, add it's token
- if (identifiers[key] === 'FROM') {
- processFrom(obj[key], results);
- return;
- }
- // If the identifier is a WHERE, add it's token and process it's values
- if (identifiers[key] === 'WHERE') {
- processWhere(obj[key], results);
- return;
- }
- // If the identifier is a GROUP BY aggregation
- if (identifiers[key] === 'GROUPBY') {
- processGroupBy(obj[key], results);
- return;
- }
- // If the identifier is an ORDER BY, add the sort options
- if (identifiers[key] === 'ORDERBY') {
- processOrderBy(obj[key], results);
- return;
- }
- // ╔╦╗╔╦╗╦ ╔═╗╔═╗╔╦╗╔╦╗╔═╗╔╗╔╔╦╗╔═╗
- // ║║║║║║ ║ ║ ║║║║║║║╠═╣║║║ ║║╚═╗
- // ═╩╝╩ ╩╩═╝ ╚═╝╚═╝╩ ╩╩ ╩╩ ╩╝╚╝═╩╝╚═╝
- // If the identifier is a SELECT, add it's token
- if (identifiers[key] === 'SELECT') {
- processSelect(obj[key], results);
- return;
- }
- // If the identifier is an INSERT, add it's token
- if (identifiers[key] === 'INSERT') {
- processInsert(obj[key], results);
- return;
- }
- // If the identifier is an UPDATE, add it's token
- if (identifiers[key] === 'UPDATE') {
- processUpdate(obj[key], results);
- return;
- }
- // If the identifier is a DELETE, add it's token
- if (identifiers[key] === 'DELETE') {
- processDelete(results);
- return;
- }
- // If the identifier is an INTO, add it's token
- if (identifiers[key] === 'INTO') {
- processInto(obj[key], results);
- return;
- }
- // If the identifier is an USING, add it's token
- if (identifiers[key] === 'USING') {
- processUsing(obj[key], results);
- return;
- }
- // ╔═╗╔═╗╔═╗╦═╗╔═╗╔═╗╔═╗╔╦╗╔═╗╔═╗
- // ╠═╣║ ╦║ ╦╠╦╝║╣ ║ ╦╠═╣ ║ ║╣ ╚═╗
- // ╩ ╩╚═╝╚═╝╩╚═╚═╝╚═╝╩ ╩ ╩ ╚═╝╚═╝
- // If the identifier is a AVG
- if (identifiers[key] === 'AVG') {
- processAggregations(obj[key], 'AVG', results);
- return;
- }
- // If the identifier is a SUM
- if (identifiers[key] === 'SUM') {
- processAggregations(obj[key], 'SUM', results);
- return;
- }
- // If the identifier is a MIN
- if (identifiers[key] === 'MIN') {
- processAggregations(obj[key], 'MIN', results);
- return;
- }
- // If the identifier is a MAX
- if (identifiers[key] === 'MAX') {
- processAggregations(obj[key], 'MAX', results);
- return;
- }
- // If the identifier is a COUNT
- if (identifiers[key] === 'COUNT') {
- processAggregations(obj[key], 'COUNT', results);
- return;
- }
- // ╔═╗╔╦╗╦ ╦╔═╗╦═╗
- // ║ ║ ║ ╠═╣║╣ ╠╦╝
- // ╚═╝ ╩ ╩ ╩╚═╝╩╚═
- // If the identifier is a LIMIT
- if (identifiers[key] === 'LIMIT') {
- processPagination(obj[key], 'LIMIT', results);
- return;
- }
- // If the indetifier is an SKIP
- if (identifiers[key] === 'SKIP') {
- processPagination(obj[key], 'SKIP', results);
- return;
- }
- // AS is only available on sub queries
- if (identifiers[key] === 'AS') {
- if (!isSubQuery) {
- return;
- }
- processAs(obj[key], results);
- return;
- }
- // If the indetifier is an RETURNING
- if (identifiers[key] === 'RETURNING') {
- processReturning(obj[key], results);
- return;
- }
- // ╦╔═╗╦╔╗╔╔═╗
- // ║║ ║║║║║╚═╗
- // ╚╝╚═╝╩╝╚╝╚═╝
- // If the identifier is a JOIN, add it's token and process the joins
- if (identifiers[key] === 'JOIN') {
- processJoin(obj[key], key, results);
- return;
- }
- // ╦ ╦╔╗╔╦╔═╗╔╗╔╔═╗
- // ║ ║║║║║║ ║║║║╚═╗
- // ╚═╝╝╚╝╩╚═╝╝╚╝╚═╝
- // If the identifier is a UNION
- if (identifiers[key] === 'UNION') {
- processUnion(obj[key], 'UNION', results);
- return;
- }
- // If the identifier is a UNIONALL
- if (identifiers[key] === 'UNIONALL') {
- processUnion(obj[key], 'UNIONALL', results);
- return;
- }
- // ╔═╗╔═╗╔╦╗╔═╗
- // ║ ║╠═╝ ║ ╚═╗
- // ╚═╝╩ ╩ ╚═╝
- // Handle any known values in the opts. Opts must be a dictionary.
- if (identifiers[key] === 'OPTS') {
- if (!_.isPlainObject(obj[key])) {
- return;
- }
- _.each(obj[key], function processOpt(val, key) {
- // Handle PG schema values
- if (key === 'schema') {
- return processSchema(val, results);
- }
- });
- return;
- }
- // Add the identifier
- results.push({
- type: identifiers[key],
- value: key
- });
- // If the identifier is an array, loop through each item and tokenize
- if (_.isArray(obj[key])) {
- _.each(obj[key], function tokenizeJoinPiece(expr) {
- tokenizeObject(expr, undefined, undefined, undefined, results);
- });
- return;
- }
- // If the identifier is an object, continue tokenizing it
- if (_.isPlainObject(obj[key])) {
- tokenizeObject(obj[key], undefined, key, undefined, results);
- return;
- }
- // Otherwise WTF?
- return;
- }
- // Otherwise add the token for the key
- results.push({
- type: 'KEY',
- value: key
- });
- // If the value is an object, recursively parse it unless it matches as
- // a sub query
- if (_.isPlainObject(obj[key])) {
- // Check if the value is a subquery first
- var subQuery = checkForSubquery(obj[key], results);
- if (subQuery) {
- return;
- }
- // Otherwise parse the object
- tokenizeObject(obj[key], undefined, key, undefined, results);
- return;
- }
- // If the value is a primitive add it's token
- results.push({
- type: 'VALUE',
- value: obj[key]
- });
- // If there is a processor and we are not on the last key, add it as well.
- // This is used for things like:
- // {
- // not: {
- // firstName: 'foo',
- // lastName: 'bar'
- // }
- // }
- // Where we need to insert a NOT statement between each key
- if (processor && (_.keys(obj).length > idx + 1)) {
- results.push(processor);
- }
- });
- // If this obj represent a sub-query, close the sub query token
- if (isSubQuery) {
- results.push({
- type: 'ENDSUBQUERY',
- value: null
- });
- }
- };
- // ╔═╗╦ ╦╔═╗╔═╗╦╔═ ╔═╗╔═╗╦═╗ ╔═╗╦ ╦╔╗ ╔═╗ ╦ ╦╔═╗╦═╗╦ ╦
- // ║ ╠═╣║╣ ║ ╠╩╗ ╠╣ ║ ║╠╦╝ ╚═╗║ ║╠╩╗║═╬╗║ ║║╣ ╠╦╝╚╦╝
- // ╚═╝╩ ╩╚═╝╚═╝╩ ╩ ╚ ╚═╝╩╚═ ╚═╝╚═╝╚═╝╚═╝╚╚═╝╚═╝╩╚═ ╩
- var checkForSubquery = function checkForSubquery(value, results) {
- var isSubquery = false;
- // Check if the object has any top level DML identifiers
- _.each(value, function checkForIdentifier(val, key) {
- if (_.indexOf(DML_IDENTIFIERS, key) < 0) {
- return;
- }
- isSubquery = true;
- });
- // If this is a sub query, tokenize it as such
- if (isSubquery) {
- tokenizeObject(value, undefined, undefined, isSubquery, results);
- return isSubquery;
- }
- return isSubquery;
- };
- // ╔═╗╔═╗╔═╗╦═╗╔═╗╔╦╗╔═╗╦═╗╔═╗
- // ║ ║╠═╝║╣ ╠╦╝╠═╣ ║ ║ ║╠╦╝╚═╗
- // ╚═╝╩ ╚═╝╩╚═╩ ╩ ╩ ╚═╝╩╚═╚═╝
- var processOperator = function processOperator(operator, value, results) {
- // Add the operator to the results
- results.push({
- type: 'OPERATOR',
- value: operator
- });
- results.push({
- type: 'VALUE',
- value: value
- });
- // Add the operator to the results
- results.push({
- type: 'ENDOPERATOR',
- value: operator
- });
- };
- // ╔═╗╔═╗╦ ╔═╗╔═╗╔╦╗ ╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╗╔╔╦╗
- // ╚═╗║╣ ║ ║╣ ║ ║ ╚═╗ ║ ╠═╣ ║ ║╣ ║║║║╣ ║║║ ║
- // ╚═╝╚═╝╩═╝╚═╝╚═╝ ╩ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝╩ ╩╚═╝╝╚╝ ╩
- var processSelect = function processSelect(value, results) {
- // Check if a distinct or other key is being used
- if (_.isPlainObject(value) && !_.isArray(value)) {
- if (value.distinct) {
- // Add the distinct to the results
- results.push({
- type: 'IDENTIFIER',
- value: 'DISTINCT'
- });
- // Add the value to the results
- results.push({
- type: 'VALUE',
- value: value.distinct
- });
- // Add the enddistinct to the results
- results.push({
- type: 'ENDIDENTIFIER',
- value: 'DISTINCT'
- });
- return;
- }
- }
- // If the value is not an array or object, add the value
- if (!_.isPlainObject(value) && !_.isArray(value)) {
- // Add the SELECT to the results
- results.push({
- type: 'IDENTIFIER',
- value: 'SELECT'
- });
- // Add the value to the results
- results.push({
- type: 'VALUE',
- value: value
- });
- // Add the ENDSELECT to the results
- results.push({
- type: 'ENDIDENTIFIER',
- value: 'SELECT'
- });
- return;
- }
- // If the value is not an array, make it one so that we can process each
- // element.
- if (!_.isArray(value)) {
- value = [value];
- }
- // Process each item in there SELECT statement and process subqueries as
- // needed.
- _.each(value, function processSelectKey(val) {
- // Add the SELECT to the results
- results.push({
- type: 'IDENTIFIER',
- value: 'SELECT'
- });
- // If the value isn't an object, no need to process it further
- if (!_.isPlainObject(val)) {
- results.push({
- type: 'VALUE',
- value: val
- });
- }
- // Check if the object is a sub-query
- if (_.isPlainObject(val)) {
- var isSubquery = checkForSubquery(val, results);
- // If it's not, add it's value
- if (!isSubquery) {
- results.push({
- type: 'VALUE',
- value: val
- });
- }
- }
- // Add the ENDSELECT to the results
- results.push({
- type: 'ENDIDENTIFIER',
- value: 'SELECT'
- });
- });
- };
- // ╔═╗╦═╗╔═╗╔╦╗ ╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╗╔╔╦╗
- // ╠╣ ╠╦╝║ ║║║║ ╚═╗ ║ ╠═╣ ║ ║╣ ║║║║╣ ║║║ ║
- // ╚ ╩╚═╚═╝╩ ╩ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝╩ ╩╚═╝╝╚╝ ╩
- var processFrom = function processFrom(value, results) {
- // Check if a schema is being used
- if (_.isObject(value) && !_.isFunction(value) && !_.isArray(value)) {
- // Add the FROM identifier
- results.push({
- type: 'IDENTIFIER',
- value: 'FROM'
- });
- // Check if a subquery is being used
- var isSubQuery = checkForSubquery(value, results);
- if (!isSubQuery && value.table) {
- results.push({
- type: 'VALUE',
- value: value.table
- });
- }
- results.push({
- type: 'ENDIDENTIFIER',
- value: 'FROM'
- });
- return;
- }
- // Otherwise just add the FROM identifier and value
- results.push({
- type: 'IDENTIFIER',
- value: 'FROM'
- });
- results.push({
- type: 'VALUE',
- value: value
- });
- results.push({
- type: 'ENDIDENTIFIER',
- value: 'FROM'
- });
- };
- // ╔═╗╔═╗╦ ╦╔═╗╔╦╗╔═╗ ╔═╗╔═╗╔╦╗
- // ╚═╗║ ╠═╣║╣ ║║║╠═╣ ║ ║╠═╝ ║
- // ╚═╝╚═╝╩ ╩╚═╝╩ ╩╩ ╩ ╚═╝╩ ╩
- var processSchema = function processSchema(value, results) {
- results.push({
- type: 'IDENTIFIER',
- value: 'SCHEMA'
- });
- results.push({
- type: 'VALUE',
- value: value
- });
- results.push({
- type: 'ENDIDENTIFIER',
- value: 'SCHEMA'
- });
- };
- // ╦╔╗╔╔═╗╔═╗╦═╗╔╦╗ ╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╗╔╔╦╗
- // ║║║║╚═╗║╣ ╠╦╝ ║ ╚═╗ ║ ╠═╣ ║ ║╣ ║║║║╣ ║║║ ║
- // ╩╝╚╝╚═╝╚═╝╩╚═ ╩ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝╩ ╩╚═╝╝╚╝ ╩
- var processInsert = function processInsert(value, results) {
- // Add the insert statment
- results.push({
- type: 'IDENTIFIER',
- value: 'INSERT'
- });
- // Check if an array is being used
- if (_.isArray(value)) {
- _.each(value, function appendInsertEach(record, idx) {
- // Add a group clause
- results.push({
- type: 'GROUP',
- value: idx
- });
- // If the value is a plain object, proccess it
- if (_.isObject(record) && !_.isFunction(record) && !_.isArray(record)) {
- _.each(_.keys(record), function appendInsertValue(key) {
- results.push({
- type: 'KEY',
- value: key
- });
- results.push({
- type: 'VALUE',
- value: record[key]
- });
- });
- }
- // Close the group clause
- results.push({
- type: 'ENDGROUP',
- value: idx
- });
- });
- }
- // Check if a plain object value is being used
- if (_.isObject(value) && !_.isFunction(value) && !_.isArray(value)) {
- _.each(_.keys(value), function appendInsertValue(key) {
- results.push({
- type: 'KEY',
- value: key
- });
- results.push({
- type: 'VALUE',
- value: value[key]
- });
- });
- }
- results.push({
- type: 'ENDIDENTIFIER',
- value: 'INSERT'
- });
- };
- // ╦╔╗╔╔╦╗╔═╗ ╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╗╔╔╦╗
- // ║║║║ ║ ║ ║ ╚═╗ ║ ╠═╣ ║ ║╣ ║║║║╣ ║║║ ║
- // ╩╝╚╝ ╩ ╚═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝╩ ╩╚═╝╝╚╝ ╩
- var processInto = function processInto(value, results) {
- results.push({
- type: 'IDENTIFIER',
- value: 'INTO'
- });
- results.push({
- type: 'VALUE',
- value: value
- });
- results.push({
- type: 'ENDIDENTIFIER',
- value: 'INTO'
- });
- };
- // ╦ ╦╔═╗╔╦╗╔═╗╔╦╗╔═╗ ╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╗╔╔╦╗
- // ║ ║╠═╝ ║║╠═╣ ║ ║╣ ╚═╗ ║ ╠═╣ ║ ║╣ ║║║║╣ ║║║ ║
- // ╚═╝╩ ═╩╝╩ ╩ ╩ ╚═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝╩ ╩╚═╝╝╚╝ ╩
- var processUpdate = function processUpdate(value, results) {
- // Add the update statment
- results.push({
- type: 'IDENTIFIER',
- value: 'UPDATE'
- });
- // Check if a value is being used
- if (_.isObject(value)) {
- _.each(_.keys(value), function appendUpdateValue(key) {
- results.push({
- type: 'KEY',
- value: key
- });
- results.push({
- type: 'VALUE',
- value: value[key]
- });
- });
- }
- results.push({
- type: 'ENDIDENTIFIER',
- value: 'UPDATE'
- });
- };
- // ╦ ╦╔═╗╦╔╗╔╔═╗ ╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╗╔╔╦╗
- // ║ ║╚═╗║║║║║ ╦ ╚═╗ ║ ╠═╣ ║ ║╣ ║║║║╣ ║║║ ║
- // ╚═╝╚═╝╩╝╚╝╚═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝╩ ╩╚═╝╝╚╝ ╩
- var processUsing = function processUsing(value, results) {
- results.push({
- type: 'IDENTIFIER',
- value: 'USING'
- });
- results.push({
- type: 'VALUE',
- value: value
- });
- results.push({
- type: 'ENDIDENTIFIER',
- value: 'USING'
- });
- };
- // ╔╦╗╔═╗╦ ╔═╗╔╦╗╔═╗ ╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╗╔╔╦╗
- // ║║║╣ ║ ║╣ ║ ║╣ ╚═╗ ║ ╠═╣ ║ ║╣ ║║║║╣ ║║║ ║
- // ═╩╝╚═╝╩═╝╚═╝ ╩ ╚═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝╩ ╩╚═╝╝╚╝ ╩
- var processDelete = function processDelete(results) {
- results.push({
- type: 'IDENTIFIER',
- value: 'DELETE'
- });
- results.push({
- type: 'ENDIDENTIFIER',
- value: 'DELETE'
- });
- };
- // ╔╗╔╔═╗╔╦╗ ╔═╗╔═╗╔╗╔╔╦╗╦╔╦╗╦╔═╗╔╗╔
- // ║║║║ ║ ║ ║ ║ ║║║║ ║║║ ║ ║║ ║║║║
- // ╝╚╝╚═╝ ╩ ╚═╝╚═╝╝╚╝═╩╝╩ ╩ ╩╚═╝╝╚╝
- var processNot = function processNot(value, results) {
- // Add a condition
- var condition = {
- type: 'CONDITION',
- value: 'NOT'
- };
- results.push(condition);
- // Tokenize the values within the condition
- if (_.isObject(value) && !_.isFunction(value) && !_.isArray(value)) {
- tokenizeObject(value, condition, undefined, undefined, results);
- return;
- }
- results.push({
- type: 'VALUE',
- value: value
- });
- results.push({
- type: 'ENDCONDITION',
- value: 'NOT'
- });
- };
- // ╦╔╗╔ ╔═╗╔═╗╔╗╔╔╦╗╦╔╦╗╦╔═╗╔╗╔
- // ║║║║ ║ ║ ║║║║ ║║║ ║ ║║ ║║║║
- // ╩╝╚╝ ╚═╝╚═╝╝╚╝═╩╝╩ ╩ ╩╚═╝╝╚╝
- var processIn = function processIn(value, negate, results) {
- // Add a condition
- var startCondition;
- var endCondition;
- if (negate) {
- startCondition = {
- type: 'CONDITION',
- value: 'NOTIN'
- };
- endCondition = {
- type: 'ENDCONDITION',
- value: 'NOTIN'
- };
- } else {
- startCondition = {
- type: 'CONDITION',
- value: 'IN'
- };
- endCondition = {
- type: 'ENDCONDITION',
- value: 'IN'
- };
- }
- results.push(startCondition);
- // If the value isn't an object, no need to process it further
- if (!_.isPlainObject(value)) {
- results.push({
- type: 'VALUE',
- value: value
- });
- }
- // Check if the object is a sub-query
- if (_.isObject(value) && !_.isFunction(value) && !_.isArray(value)) {
- var isSubquery = checkForSubquery(value, results);
- // If it's not, add it's value
- if (!isSubquery) {
- results.push({
- type: 'VALUE',
- value: value
- });
- }
- }
- results.push(endCondition);
- };
- // ╦ ╦╦ ╦╔═╗╦═╗╔═╗ ╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╗╔╔╦╗
- // ║║║╠═╣║╣ ╠╦╝║╣ ╚═╗ ║ ╠═╣ ║ ║╣ ║║║║╣ ║║║ ║
- // ╚╩╝╩ ╩╚═╝╩╚═╚═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝╩ ╩╚═╝╝╚╝ ╩
- var processWhere = function processWhere(value, results) {
- // Tokenize the where and then call the tokenizer on the where values
- results.push({
- type: 'IDENTIFIER',
- value: 'WHERE'
- });
- tokenizeObject(value, undefined, undefined, undefined, results);
- results.push({
- type: 'ENDIDENTIFIER',
- value: 'WHERE'
- });
- };
- // ╔═╗╦═╗ ╔═╗╦═╗╔═╗╦ ╦╔═╗╦╔╗╔╔═╗
- // ║ ║╠╦╝ ║ ╦╠╦╝║ ║║ ║╠═╝║║║║║ ╦
- // ╚═╝╩╚═ ╚═╝╩╚═╚═╝╚═╝╩ ╩╝╚╝╚═╝
- var processOr = function processOr(value, results) {
- // Add the Or token
- results.push({
- type: 'CONDITION',
- value: 'OR'
- });
- // For each condition in the OR, add a group token and process the criteria.
- _.forEach(value, function appendOrCrieria(criteria, idx) {
- // Start a group
- results.push({
- type: 'GROUP',
- value: idx
- });
- tokenizeObject(criteria, undefined, undefined, undefined, results);
- // End a group
- results.push({
- type: 'ENDGROUP',
- value: idx
- });
- });
- // Close the condition
- results.push({
- type: 'ENDCONDITION',
- value: 'OR'
- });
- };
- // ╔═╗╔╗╔╔╦╗ ╔═╗╦═╗╔═╗╦ ╦╔═╗╦╔╗╔╔═╗
- // ╠═╣║║║ ║║ ║ ╦╠╦╝║ ║║ ║╠═╝║║║║║ ╦
- // ╩ ╩╝╚╝═╩╝ ╚═╝╩╚═╚═╝╚═╝╩ ╩╝╚╝╚═╝
- var processAnd = function processAnd(value, results) {
- // Only process grouped AND's if the value is an array
- if (!_.isArray(value)) {
- return;
- }
- // Add the AND token
- results.push({
- type: 'CONDITION',
- value: 'AND'
- });
- // For each condition in the OR, add a group token and process the criteria.
- _.each(value, function appendAndCrieria(criteria, idx) {
- // Start a group
- results.push({
- type: 'GROUP',
- value: idx
- });
- tokenizeObject(criteria, undefined, undefined, undefined, results);
- // End a group
- results.push({
- type: 'ENDGROUP',
- value: idx
- });
- });
- // Close the condition
- results.push({
- type: 'ENDCONDITION',
- value: 'AND'
- });
- };
- // ╦╔═╗╦╔╗╔ ╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╗╔╔╦╗╔═╗
- // ║║ ║║║║║ ╚═╗ ║ ╠═╣ ║ ║╣ ║║║║╣ ║║║ ║ ╚═╗
- // ╚╝╚═╝╩╝╚╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝╩ ╩╚═╝╝╚╝ ╩ ╚═╝
- var processJoin = function processJoin(value, joinType, results) {
- // Ensure we have an array value
- if (!_.isArray(value)) {
- value = [value];
- }
- _.each(value, function processJoinInstructions(joinInstructions) {
- // Add a JOIN token
- results.push({
- type: 'IDENTIFIER',
- value: joinType.toUpperCase()
- });
- // Ensure the instructions include a FROM and an ON and that the ON
- // is made up of two table keys.
- if (!_.has(joinInstructions, 'from') || !_.has(joinInstructions, 'on')) {
- throw new Error('Invalid join instructions');
- }
- // Check if this is an AND or an OR join statement. An AND statement will
- // just be an array of conditions and an OR statement will have a single
- // OR key as the value.
- // Process AND
- if (_.isArray(joinInstructions.on)) {
- (function andInstructions() {
- var JOIN_TABLE = joinInstructions.from;
- results.push({ type: 'KEY', value: 'TABLE' });
- results.push({ type: 'VALUE', value: JOIN_TABLE });
- _.each(joinInstructions.on, function onSet(set) {
- var PARENT_TABLE = _.first(_.keys(set));
- var CHILD_TABLE = _.keys(set)[1];
- var PARENT_COLUMN = set[_.first(_.keys(set))];
- var CHILD_COLUMN = set[_.keys(set)[1]];
- var setKeys = [
- { type: 'COMBINATOR', value: 'AND' },
- { type: 'KEY', value: 'TABLE_KEY' },
- { type: 'VALUE', value: PARENT_TABLE },
- { type: 'KEY', value: 'COLUMN_KEY' },
- { type: 'VALUE', value: PARENT_COLUMN },
- { type: 'KEY', value: 'TABLE_KEY' },
- { type: 'VALUE', value: CHILD_TABLE },
- { type: 'KEY', value: 'COLUMN_KEY' },
- { type: 'VALUE', value: CHILD_COLUMN }
- ];
- _.each(setKeys, function appendSet(set) {
- results.push(set);
- });
- });
- })();
- // Process OR
- } else if (_.isArray(joinInstructions.on.or)) {
- (function orInstructions() {
- var JOIN_TABLE = joinInstructions.from;
- results.push({ type: 'KEY', value: 'TABLE' });
- results.push({ type: 'VALUE', value: JOIN_TABLE });
- _.each(joinInstructions.on.or, function orSet(set) {
- var PARENT_TABLE = _.first(_.keys(set));
- var CHILD_TABLE = _.keys(set)[1];
- var PARENT_COLUMN = set[_.first(_.keys(set))];
- var CHILD_COLUMN = set[_.keys(set)[1]];
- var setKeys = [
- { type: 'COMBINATOR', value: 'OR' },
- { type: 'KEY', value: 'TABLE_KEY' },
- { type: 'VALUE', value: PARENT_TABLE },
- { type: 'KEY', value: 'COLUMN_KEY' },
- { type: 'VALUE', value: PARENT_COLUMN },
- { type: 'KEY', value: 'TABLE_KEY' },
- { type: 'VALUE', value: CHILD_TABLE },
- { type: 'KEY', value: 'COLUMN_KEY' },
- { type: 'VALUE', value: CHILD_COLUMN }
- ];
- _.each(setKeys, function appendSet(set) {
- results.push(set);
- });
- });
- })();
- // Otherwise ensure that the ON key has two keys
- } else if (!_.isPlainObject(joinInstructions.on) || _.keys(joinInstructions.on).length !== 2) {
- throw new Error('Invalid join instructions');
- // Handle normal, single level joins
- } else {
- (function buildJoinResults() {
- var JOIN_TABLE = joinInstructions.from;
- var PARENT_TABLE = _.first(_.keys(joinInstructions.on));
- var CHILD_TABLE = _.keys(joinInstructions.on)[1];
- var PARENT_COLUMN = joinInstructions.on[_.first(_.keys(joinInstructions.on))];
- var CHILD_COLUMN = joinInstructions.on[_.keys(joinInstructions.on)[1]];
- var joinResults = [
- { type: 'KEY', value: 'TABLE' },
- { type: 'VALUE', value: JOIN_TABLE },
- { type: 'KEY', value: 'TABLE_KEY' },
- { type: 'VALUE', value: PARENT_TABLE },
- { type: 'KEY', value: 'COLUMN_KEY' },
- { type: 'VALUE', value: PARENT_COLUMN },
- { type: 'KEY', value: 'TABLE_KEY' },
- { type: 'VALUE', value: CHILD_TABLE },
- { type: 'KEY', value: 'COLUMN_KEY' },
- { type: 'VALUE', value: CHILD_COLUMN }
- ];
- _.each(joinResults, function appendSet(set) {
- results.push(set);
- });
- })();
- }
- results.push({
- type: 'ENDIDENTIFIER',
- value: joinType.toUpperCase()
- });
- });
- };
- // ╔═╗╦═╗╔═╗╦ ╦╔═╗ ╔╗ ╦ ╦
- // ║ ╦╠╦╝║ ║║ ║╠═╝ ╠╩╗╚╦╝
- // ╚═╝╩╚═╚═╝╚═╝╩ ╚═╝ ╩
- var processGroupBy = function processGroupBy(value, results) {
- results.push({
- type: 'IDENTIFIER',
- value: 'GROUPBY'
- });
- results.push({
- type: 'VALUE',
- value: value
- });
- results.push({
- type: 'ENDIDENTIFIER',
- value: 'GROUPBY'
- });
- };
- // ╔═╗╦═╗╔╦╗╔═╗╦═╗ ╔╗ ╦ ╦
- // ║ ║╠╦╝ ║║║╣ ╠╦╝ ╠╩╗╚╦╝
- // ╚═╝╩╚══╩╝╚═╝╩╚═ ╚═╝ ╩
- var processOrderBy = function processOrderBy(values, results) {
- // Tokenize the order by and then call the tokenizer on the values
- results.push({
- type: 'IDENTIFIER',
- value: 'ORDERBY'
- });
- if (!_.isArray(values)) {
- values = [values];
- }
- _.each(values, function tokenizeSet(tokenSet) {
- tokenizeObject(tokenSet, undefined, undefined, undefined, results);
- });
- results.push({
- type: 'ENDIDENTIFIER',
- value: 'ORDERBY'
- });
- };
- // ╔═╗╔═╗╔═╗╦═╗╔═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗
- // ╠═╣║ ╦║ ╦╠╦╝║╣ ║ ╦╠═╣ ║ ║║ ║║║║╚═╗
- // ╩ ╩╚═╝╚═╝╩╚═╚═╝╚═╝╩ ╩ ╩ ╩╚═╝╝╚╝╚═╝
- var processAggregations = function processAggregations(value, aggregation, results) {
- results.push({
- type: 'IDENTIFIER',
- value: aggregation
- });
- results.push({
- type: 'VALUE',
- value: value
- });
- results.push({
- type: 'ENDIDENTIFIER',
- value: aggregation
- });
- };
- // ╔═╗╔═╗╔═╗╦╔╗╔╔═╗╔╦╗╦╔═╗╔╗╔
- // ╠═╝╠═╣║ ╦║║║║╠═╣ ║ ║║ ║║║║
- // ╩ ╩ ╩╚═╝╩╝╚╝╩ ╩ ╩ ╩╚═╝╝╚╝
- var processPagination = function processPagination(value, operator, results) {
- results.push({
- type: 'IDENTIFIER',
- value: operator
- });
- results.push({
- type: 'VALUE',
- value: value
- });
- results.push({
- type: 'ENDIDENTIFIER',
- value: operator
- });
- };
- // ╔═╗╦═╗╔═╗╔═╗╔═╗╔═╗╔═╗ ╦ ╦╔╗╔╦╔═╗╔╗╔
- // ╠═╝╠╦╝║ ║║ ║╣ ╚═╗╚═╗ ║ ║║║║║║ ║║║║
- // ╩ ╩╚═╚═╝╚═╝╚═╝╚═╝╚═╝ ╚═╝╝╚╝╩╚═╝╝╚╝
- var processUnion = function processUnion(values, type, results) {
- results.push({
- type: 'UNION',
- value: type
- });
- _.each(values, function processUnionValue(value, idx) {
- // Start each union subquery with an ENDGROUP
- results.push({
- type: 'GROUP',
- value: idx
- });
- // Build the subquery
- checkForSubquery(value, results);
- // Close each subquery with an ENDGROUP token
- results.push({
- type: 'ENDGROUP',
- value: idx
- });
- });
- results.push({
- type: 'ENDUNION',
- value: type
- });
- };
- // ╔═╗╦═╗╔═╗╔═╗╔═╗╔═╗╔═╗ ╔═╗╔═╗
- // ╠═╝╠╦╝║ ║║ ║╣ ╚═╗╚═╗ ╠═╣╚═╗
- // ╩ ╩╚═╚═╝╚═╝╚═╝╚═╝╚═╝ ╩ ╩╚═╝
- var processAs = function processAs(value, results) {
- results.push({
- type: 'IDENTIFIER',
- value: 'AS'
- });
- results.push({
- type: 'VALUE',
- value: value
- });
- results.push({
- type: 'ENDIDENTIFIER',
- value: 'AS'
- });
- };
- // ╦═╗╔═╗╔╦╗╦ ╦╦═╗╔╗╔╦╔╗╔╔═╗
- // ╠╦╝║╣ ║ ║ ║╠╦╝║║║║║║║║ ╦
- // ╩╚═╚═╝ ╩ ╚═╝╩╚═╝╚╝╩╝╚╝╚═╝
- var processReturning = function processReturning(value, results) {
- // Add the RETURNING to the results
- results.push({
- type: 'IDENTIFIER',
- value: 'RETURNING'
- });
- results.push({
- type: 'VALUE',
- value: value
- });
- results.push({
- type: 'ENDIDENTIFIER',
- value: 'RETURNING'
- });
- };
- module.exports = function tokenizer(expression) {
- if (!expression) {
- throw new Error('Missing expression');
- }
- // Hold the built up results
- var results = [];
- // Kick off recursive parsing of the RQL object
- tokenizeObject(expression, undefined, undefined, undefined, results);
- // Return the tokenenized result set
- return results;
- };
- /* eslint-enable no-use-before-define */
|