From 4405d618459ebef1e8036b07202ebdf04ca9d23e Mon Sep 17 00:00:00 2001 From: Javier Goizueta Date: Tue, 12 Dec 2017 16:17:42 +0100 Subject: [PATCH] Remove support for arbitrary aggregation SQL expressions. Only the supported aggregate functions can be used now, currently count, sum, avg, min & max. --- .../aggregation/aggregation-templates.js | 43 +++++++++++++------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/lib/cartodb/models/aggregation/aggregation-templates.js b/lib/cartodb/models/aggregation/aggregation-templates.js index d7e92f04..109d0275 100644 --- a/lib/cartodb/models/aggregation/aggregation-templates.js +++ b/lib/cartodb/models/aggregation/aggregation-templates.js @@ -9,7 +9,25 @@ module.exports = (options) => { return templateFn; }; -const aggregate_columns = ctx => { +const SUPPORTED_AGGREGATE_FUNCTIONS = { + 'count': { + sql: (column_name, params) => `count(${params.aggregated_column || '*'})` + }, + 'avg': { + sql: (column_name, params) => `avg(${params.aggregated_column || column_name})` + }, + 'sum': { + sql: (column_name, params) => `sum(${params.aggregated_column || column_name})` + }, + 'min': { + sql: (column_name, params) => `min(${params.aggregated_column || column_name})` + }, + 'max': { + sql: (column_name, params) => `max(${params.aggregated_column || column_name})` + } +}; + +const aggregateColumns = ctx => { let columns = ctx.columns || {}; if (Object.keys(columns).length === 0) { // default aggregation @@ -20,21 +38,22 @@ const aggregate_columns = ctx => { }; } return Object.keys(columns).map(column_name => { - let aggregate_expression = columns[column_name].aggregate_expression; - if (!aggregate_expression) { - const aggregate_function = columns[column_name].aggregate_function || 'count'; - const aggregated_column = columns[column_name].aggregated_column || '*'; - aggregate_expression = `${aggregate_function}(${aggregated_column})`; + const aggregate_function = columns[column_name].aggregate_function || 'count'; + const aggregate_definition = SUPPORTED_AGGREGATE_FUNCTIONS[aggregate_function]; + if (!aggregate_definition) { + throw new Error("Invalid Aggregate function: '" + aggregate_function + "'"); } + const aggregate_expression = aggregate_definition.sql(column_name, columns[column_name]); return `${aggregate_expression} AS ${column_name}`; }).join(', '); }; // Notes: // * ${ctx.res*0.00028/256}*!scale_denominator! is equivalent to -// * ${ctx.res/256}*CDB_XYZ_Resolution(CDB_ZoomFromScale(!scale_denominator!)) -// * We need to filter spatially using !bbox! to make the queries efficient because the filter added by Mapnik -// * (wrapping the query) is only applied after the aggregation. +// ${ctx.res/256}*CDB_XYZ_Resolution(CDB_ZoomFromScale(!scale_denominator!)) +// * We need to filter spatially using !bbox! to make the queries efficient because +// the filter added by Mapnik (wrapping the query) +// is only applied after the aggregation. // * This queries are used for rendering and the_geom is omitted in the results for better performance const aggregationQueryTemplates = { @@ -52,7 +71,7 @@ const aggregationQueryTemplates = { AVG(ST_Y(_cdb_query.the_geom_webmercator)) ), 3857 ) AS the_geom_webmercator, - ${aggregate_columns(ctx)} + ${aggregateColumns(ctx)} FROM (${ctx.sourceQuery}) _cdb_query, _cdb_params WHERE _cdb_query.the_geom_webmercator && _cdb_params.bbox GROUP BY @@ -77,7 +96,7 @@ const aggregationQueryTemplates = { ) AS the_geom_webmercator, Floor(ST_X(_cdb_query.the_geom_webmercator)/_cdb_params.res)::int AS _cdb_gx, Floor(ST_Y(_cdb_query.the_geom_webmercator)/_cdb_params.res)::int AS _cdb_gy, - ${aggregate_columns(ctx)} + ${aggregateColumns(ctx)} FROM (${ctx.sourceQuery}) _cdb_query, _cdb_params WHERE the_geom_webmercator && _cdb_params.bbox GROUP BY _cdb_gx, _cdb_gy @@ -97,7 +116,7 @@ const aggregationQueryTemplates = { ), _cdb_clusters AS ( SELECT MIN(cartodb_id) AS cartodb_id, - ${aggregate_columns(ctx)} + ${aggregateColumns(ctx)} FROM (${ctx.sourceQuery}) _cdb_query, _cdb_params WHERE _cdb_query.the_geom_webmercator && _cdb_params.bbox GROUP BY