UNPKG

join-monster

Version:

A GraphQL to SQL query execution layer for batch data fetching.

210 lines (209 loc) 9.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = stringifySqlAST; var _assert = _interopRequireDefault(require("assert")); var _lodash = require("lodash"); var _util = require("../util"); var _shared = require("./shared"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } async function stringifySqlAST(topNode, context, options) { (0, _util.validateSqlAST)(topNode); let dialect = options.dialectModule; if (!dialect && options.dialect) { const dialectRequireOptions = { sqlite3: require('./dialects/sqlite3'), pg: require('./dialects/pg'), oracle: require('./dialects/oracle'), mysql8: require('./dialects/mysql8'), mysql: require('./dialects/mysql'), mariadb: require('./dialects/mariadb') }; dialect = dialectRequireOptions[options.dialect]; } let { selections, tables, wheres, orders } = await _stringifySqlAST(null, topNode, [], context, [], [], [], [], options.batchScope, dialect); if (!selections.length) return ''; if (dialect.maxAliasLength) { const exceedingAliases = selections.filter(([, alias]) => alias.length > dialect.maxAliasLength); if (exceedingAliases.length) { console.warn(`Alias length exceeds the max allowed length of ${dialect.maxAliasLength} characters for ${dialect.name}: ${exceedingAliases.map(([column, alias]) => `${column} AS ${alias}`).join(', ')}`); } } selections = [...new Set(selections.map(([column, alias]) => `${column} AS ${alias}`))]; let sql = 'SELECT\n ' + selections.join(',\n ') + '\n' + tables.join('\n'); wheres = (0, _lodash.filter)(wheres); if (wheres.length) { sql += '\nWHERE ' + wheres.join(' AND '); } if (orders.length) { sql += '\nORDER BY ' + stringifyOuterOrder(orders, dialect.quote); } return sql; } async function _stringifySqlAST(parent, node, prefix, context, selections, tables, wheres, orders, batchScope, dialect) { const { quote: q } = dialect; const parentTable = node.fromOtherTable || parent && parent.as; switch (node.type) { case 'table': await handleTable(parent, node, prefix, context, selections, tables, wheres, orders, batchScope, dialect); if ((0, _shared.thisIsNotTheEndOfThisBatch)(node, parent)) { for (let child of node.children) { await _stringifySqlAST(node, child, [...prefix, node.as], context, selections, tables, wheres, orders, null, dialect); } } break; case 'union': await handleTable(parent, node, prefix, context, selections, tables, wheres, orders, batchScope, dialect); if ((0, _shared.thisIsNotTheEndOfThisBatch)(node, parent)) { for (let typeName in node.typedChildren) { for (let child of node.typedChildren[typeName]) { await _stringifySqlAST(node, child, [...prefix, node.as], context, selections, tables, wheres, orders, null, dialect); } } for (let child of node.children) { await _stringifySqlAST(node, child, [...prefix, node.as], context, selections, tables, wheres, orders, null, dialect); } } break; case 'column': selections.push([`${q(parentTable)}.${q(node.name)}`, `${q((0, _shared.joinPrefix)(prefix) + node.as)}`]); break; case 'columnDeps': for (let name in node.names) { selections.push([`${q(parentTable)}.${q(name)}`, `${q((0, _shared.joinPrefix)(prefix) + node.names[name])}`]); } break; case 'composite': selections.push([`${dialect.compositeKey(parentTable, node.name)}`, `${q((0, _shared.joinPrefix)(prefix) + node.as)}`]); break; case 'expression': const expr = await node.sqlExpr(`${q(parentTable)}`, node.args || {}, context, node); selections.push([`${expr}`, `${q((0, _shared.joinPrefix)(prefix) + node.as)}`]); break; case 'noop': return; default: throw new Error('unexpected/unknown node type reached: ' + (0, _util.inspect)(node)); } return { selections, tables, wheres, orders }; } async function handleTable(parent, node, prefix, context, selections, tables, wheres, orders, batchScope, dialect) { var _ref, _ref2; const { quote: q } = dialect; if ((0, _shared.whereConditionIsntSupposedToGoInsideSubqueryOrOnNextBatch)(node, parent)) { var _ref5; if ((_ref5 = node) != null ? (_ref5 = _ref5.junction) != null ? _ref5.where : _ref5 : _ref5) { wheres.push(await node.junction.where(`${q(node.junction.as)}`, node.args || {}, context, node)); } if (node.where) { wheres.push(await node.where(`${q(node.as)}`, node.args || {}, context, node)); } } if ((0, _shared.thisIsNotTheEndOfThisBatch)(node, parent)) { var _ref3, _ref4; if ((_ref4 = node) != null ? (_ref4 = _ref4.junction) != null ? _ref4.orderBy : _ref4 : _ref4) { orders.push({ table: node.junction.as, columns: node.junction.orderBy }); } if (node.orderBy) { orders.push({ table: node.as, columns: node.orderBy }); } if ((_ref3 = node) != null ? (_ref3 = _ref3.junction) != null ? _ref3.sortKey : _ref3 : _ref3) { orders.push({ table: node.junction.as, columns: (0, _shared.flipOrderings)(node.junction.sortKey, node.args) }); } if (node.sortKey) { orders.push({ table: node.as, columns: (0, _shared.flipOrderings)(node.sortKey, node.args) }); } } if (node.sqlJoin) { const joinCondition = await node.sqlJoin(`${q(parent.as)}`, q(node.as), node.args || {}, context, node); if (node.paginate) { await dialect.handleJoinedOneToManyPaginated(parent, node, context, tables, joinCondition); } else if (node.limit) { node.args.first = node.limit; await dialect.handleJoinedOneToManyPaginated(parent, node, context, tables, joinCondition); } else { tables.push(`LEFT JOIN ${node.name} ${q(node.as)} ON ${joinCondition}`); } } else if ((_ref2 = node) != null ? (_ref2 = _ref2.junction) != null ? _ref2.sqlBatch : _ref2 : _ref2) { if (parent) { selections.push([`${q(parent.as)}.${q(node.junction.sqlBatch.parentKey.name)}`, `${q((0, _shared.joinPrefix)(prefix) + node.junction.sqlBatch.parentKey.as)}`]); } else { const joinCondition = await node.junction.sqlBatch.sqlJoin(`${q(node.junction.as)}`, q(node.as), node.args || {}, context, node); if (node.paginate) { await dialect.handleBatchedManyToManyPaginated(parent, node, context, tables, batchScope, joinCondition); } else if (node.limit) { node.args.first = node.limit; await dialect.handleBatchedManyToManyPaginated(parent, node, context, tables, batchScope, joinCondition); } else { tables.push(`FROM ${node.junction.sqlTable} ${q(node.junction.as)}`, `LEFT JOIN ${node.name} ${q(node.as)} ON ${joinCondition}`); wheres.push(`${q(node.junction.as)}.${q(node.junction.sqlBatch.thisKey.name)} IN (${batchScope.join(',')})`); } } } else if ((_ref = node) != null ? (_ref = _ref.junction) != null ? _ref.sqlTable : _ref : _ref) { const joinCondition1 = await node.junction.sqlJoins[0](`${q(parent.as)}`, q(node.junction.as), node.args || {}, context, node); const joinCondition2 = await node.junction.sqlJoins[1](`${q(node.junction.as)}`, q(node.as), node.args || {}, context, node); if (node.paginate) { await dialect.handleJoinedManyToManyPaginated(parent, node, context, tables, joinCondition1, joinCondition2); } else if (node.limit) { node.args.first = node.limit; await dialect.handleJoinedManyToManyPaginated(parent, node, context, tables, joinCondition1, joinCondition2); } else { tables.push(`LEFT JOIN ${node.junction.sqlTable} ${q(node.junction.as)} ON ${joinCondition1}`); } tables.push(`LEFT JOIN ${node.name} ${q(node.as)} ON ${joinCondition2}`); } else if (node.sqlBatch) { if (parent) { selections.push([`${q(parent.as)}.${q(node.sqlBatch.parentKey.name)}`, `${q((0, _shared.joinPrefix)(prefix) + node.sqlBatch.parentKey.as)}`]); } else if (node.paginate) { await dialect.handleBatchedOneToManyPaginated(parent, node, context, tables, batchScope); } else if (node.limit) { node.args.first = node.limit; await dialect.handleBatchedOneToManyPaginated(parent, node, context, tables, batchScope); } else { tables.push(`FROM ${node.name} ${q(node.as)}`); wheres.push(`${q(node.as)}.${q(node.sqlBatch.thisKey.name)} IN (${batchScope.join(',')})`); } } else if (node.paginate) { await dialect.handlePaginationAtRoot(parent, node, context, tables); } else if (node.limit) { node.args.first = node.limit; await dialect.handlePaginationAtRoot(parent, node, context, tables); } else { (0, _assert.default)(!parent, `Object type for "${node.fieldName}" table must have a "sqlJoin" or "sqlBatch"`); tables.push(`FROM ${node.name} ${q(node.as)}`); } } function stringifyOuterOrder(orders, q) { const conditions = []; for (const condition of orders) { conditions.push((0, _shared.orderingsToString)(condition.columns, q, condition.table)); } return conditions.join(', '); }