diff --git a/packages/cubejs-pinot-driver/src/PinotQuery.ts b/packages/cubejs-pinot-driver/src/PinotQuery.ts index 204bce55d46b4..565f852152a56 100644 --- a/packages/cubejs-pinot-driver/src/PinotQuery.ts +++ b/packages/cubejs-pinot-driver/src/PinotQuery.ts @@ -142,12 +142,23 @@ export class PinotQuery extends BaseQuery { const templates = super.sqlTemplates(); templates.functions.DATETRUNC = 'DATE_TRUNC({{ args_concat }})'; templates.functions.STRING_AGG = 'LISTAGG({% if distinct %}DISTINCT {% endif %}{{ args_concat }})'; - templates.statements.select = 'SELECT {{ select_concat | map(attribute=\'aliased\') | join(\', \') }} \n' + - 'FROM (\n {{ from }}\n) AS {{ from_alias }} \n' + - '{% if group_by %} GROUP BY {{ group_by }}{% endif %}' + - '{% if order_by %} ORDER BY {{ order_by | map(attribute=\'expr\') | join(\', \') }}{% endif %}' + - '{% if offset %}\nOFFSET {{ offset }}{% endif %}' + - '{% if limit %}\nLIMIT {{ limit }}{% endif %}'; + templates.statements.select = '{% if ctes %} WITH \n' + + '{{ ctes | join(\',\n\') }}\n' + + '{% endif %}' + + 'SELECT {% if distinct %}DISTINCT {% endif %}' + + '{{ select_concat | map(attribute=\'aliased\') | join(\', \') }} {% if from %}\n' + + 'FROM (\n' + + '{{ from | indent(2, true) }}\n' + + ') AS {{ from_alias }}{% elif from_prepared %}\n' + + 'FROM {{ from_prepared }}' + + '{% endif %}' + + '{% for join in joins %}\n{{ join }}{% endfor %}' + + '{% if filter %}\nWHERE {{ filter }}{% endif %}' + + '{% if group_by %}\nGROUP BY {{ group_by }}{% endif %}' + + '{% if having %}\nHAVING {{ having }}{% endif %}' + + '{% if order_by %}\nORDER BY {{ order_by | map(attribute=\'expr\') | join(\', \') }}{% endif %}' + + '{% if offset is not none %}\nOFFSET {{ offset }}{% endif %}' + + '{% if limit is not none %}\nLIMIT {{ limit }}{% endif %}'; templates.expressions.extract = 'EXTRACT({{ date_part }} FROM {{ expr }})'; templates.expressions.timestamp_literal = `fromDateTime('{{ value }}', ${DATE_TIME_FORMAT})`; // NOTE: this template contains a comma; two order expressions are being generated diff --git a/packages/cubejs-pinot-driver/test/PinotQueryTemplates.test.ts b/packages/cubejs-pinot-driver/test/PinotQueryTemplates.test.ts new file mode 100644 index 0000000000000..cac48e48af350 --- /dev/null +++ b/packages/cubejs-pinot-driver/test/PinotQueryTemplates.test.ts @@ -0,0 +1,51 @@ +import { prepareCompiler as originalPrepareCompiler } from '@cubejs-backend/schema-compiler'; +import { PinotQuery } from '../src/PinotQuery'; + +const prepareCompiler = (content: string) => originalPrepareCompiler({ + localPath: () => __dirname, + dataSchemaFiles: () => Promise.resolve([ + { fileName: 'main.js', content } + ]) +}, { adapter: 'postgres' }); + +describe('PinotQuery SQL templates', () => { + it('renders Tesseract sql_table queries with a prepared FROM source', async () => { + const { compiler, joinGraph, cubeEvaluator } = prepareCompiler(` + cube('orders', { + sql_table: 'orders', + + measures: { + count: { + type: 'count', + }, + }, + + dimensions: { + id: { + sql: 'id', + type: 'number', + primary_key: true, + }, + }, + }); + `); + + await compiler.compile(); + + const query = new PinotQuery({ joinGraph, cubeEvaluator, compiler }, { + measures: ['orders.count'], + timeDimensions: [], + filters: [], + limit: 10, + offset: 5, + useNativeSqlPlanner: true, + }); + + const [sql] = query.buildSqlAndParams(); + + expect(sql).toMatch(/FROM\s+orders\b/); + expect(sql).not.toMatch(/FROM\s*\(\s*\)\s+AS\b/); + expect(sql.indexOf('OFFSET 5')).toBeGreaterThan(-1); + expect(sql.indexOf('OFFSET 5')).toBeLessThan(sql.indexOf('LIMIT 10')); + }); +});