From 33ac22bfdb53eeb7655966a1aed469a8a6f62a7b Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Sun, 21 Jan 2018 22:08:18 +0100 Subject: [PATCH 001/200] start query builder ui --- .../postgres/partials/query.editor.html | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/public/app/plugins/datasource/postgres/partials/query.editor.html b/public/app/plugins/datasource/postgres/partials/query.editor.html index 163970a9ad5..635c3e6f222 100644 --- a/public/app/plugins/datasource/postgres/partials/query.editor.html +++ b/public/app/plugins/datasource/postgres/partials/query.editor.html @@ -1,10 +1,23 @@ - -
-
- - -
-
+ + +
+
+
+ + +
+
+
+ +
+
+
+ + + +
+
+
From 17be31e2167ef92af57884a2bb9975a33f7968fb Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Sun, 28 Jan 2018 22:16:51 +0100 Subject: [PATCH 002/200] call render in query --- .../app/plugins/datasource/postgres/datasource.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/public/app/plugins/datasource/postgres/datasource.ts b/public/app/plugins/datasource/postgres/datasource.ts index 8eee389d1a5..3a4bd27bb4c 100644 --- a/public/app/plugins/datasource/postgres/datasource.ts +++ b/public/app/plugins/datasource/postgres/datasource.ts @@ -1,5 +1,6 @@ import _ from 'lodash'; import ResponseParser from './response_parser'; +import PostgresQuery from 'app/plugins/datasource/postgres/postgres_query'; export class PostgresDatasource { id: any; @@ -33,16 +34,18 @@ export class PostgresDatasource { } query(options) { - var queries = _.filter(options.targets, item => { - return item.hide !== true; - }).map(item => { + var queries = _.filter(options.targets, target => { + return target.hide !== true; + }).map(target => { + var queryModel = new PostgresQuery(target, this.templateSrv, options.scopedVars); + return { - refId: item.refId, + refId: target.refId, intervalMs: options.intervalMs, maxDataPoints: options.maxDataPoints, datasourceId: this.id, - rawSql: this.templateSrv.replace(item.rawSql, options.scopedVars, this.interpolateVariable), - format: item.format, + rawSql: queryModel.render(this.interpolateVariable), + format: target.format, }; }); From a59e052a0f9ac9aab7467d49dd5586de0d08e124 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Tue, 30 Jan 2018 14:08:10 +0100 Subject: [PATCH 003/200] more query builder components --- .../postgres/partials/query.editor.html | 94 +++++++++++++------ 1 file changed, 64 insertions(+), 30 deletions(-) diff --git a/public/app/plugins/datasource/postgres/partials/query.editor.html b/public/app/plugins/datasource/postgres/partials/query.editor.html index 635c3e6f222..400855cf82f 100644 --- a/public/app/plugins/datasource/postgres/partials/query.editor.html +++ b/public/app/plugins/datasource/postgres/partials/query.editor.html @@ -1,12 +1,12 @@
-
-
- - -
-
+
+
+ + +
+
@@ -15,42 +15,76 @@ +
+ +
+
+ +
+ +
+ + +
+ +
+ +
+ +
+
+
+
+ +
+
+ + + +
+
+
- -
- -
-
-
+ +
+ +
+
+
-
-
+
+
-
-
-
-
-
+ +
+
+
+ -
-
{{ctrl.lastQueryMeta.sql}}
-
+
+
{{ctrl.lastQueryMeta.sql}}
+
-
-
Time series:
+  
+
Time series:
 - return column named time (UTC in seconds or timestamp)
 - return column(s) with numeric datatype as values
 - (Optional: return column named metric to represent the series name. If no column named metric is found the column name of the value column is used as series name)
@@ -78,13 +112,13 @@ Or build your own conditionals using these macros which just return the values:
 - $__timeTo() ->  to_timestamp(1492750877)
 - $__unixEpochFrom() ->  1492750877
 - $__unixEpochTo() ->  1492750877
-		
-
+
+
- + -
-
{{ctrl.lastQueryError}}
-
+
+
{{ctrl.lastQueryError}}
+
From 438b10bcd68abd115dbe86acad1a70a53a791c79 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Tue, 30 Jan 2018 14:10:38 +0100 Subject: [PATCH 004/200] query builder changes --- .../plugins/datasource/postgres/query_ctrl.ts | 179 ++++++++++++++++-- 1 file changed, 168 insertions(+), 11 deletions(-) diff --git a/public/app/plugins/datasource/postgres/query_ctrl.ts b/public/app/plugins/datasource/postgres/query_ctrl.ts index 7afd0cf7253..4e39105370b 100644 --- a/public/app/plugins/datasource/postgres/query_ctrl.ts +++ b/public/app/plugins/datasource/postgres/query_ctrl.ts @@ -1,12 +1,7 @@ import _ from 'lodash'; import { QueryCtrl } from 'app/plugins/sdk'; - -export interface PostgresQuery { - refId: string; - format: string; - alias: string; - rawSql: string; -} +import queryPart from './query_part'; +import PostgresQuery from './postgres_query'; export interface QueryMeta { sql: string; @@ -26,17 +21,21 @@ export class PostgresQueryCtrl extends QueryCtrl { showLastQuerySQL: boolean; formats: any[]; - target: PostgresQuery; + queryModel: PostgresQuery; lastQueryMeta: QueryMeta; lastQueryError: string; showHelp: boolean; + schemaSegment: any; + tableSegment: any; + timeColumnSegment: any; + selectMenu: any; /** @ngInject **/ - constructor($scope, $injector) { + constructor($scope, $injector, private templateSrv, private $q, private uiSegmentSrv) { super($scope, $injector); + this.target = this.target; + this.queryModel = new PostgresQuery(this.target, templateSrv, this.panel.scopedVars); - this.target.format = this.target.format || 'time_series'; - this.target.alias = ''; this.formats = [{ text: 'Time series', value: 'time_series' }, { text: 'Table', value: 'table' }]; if (!this.target.rawSql) { @@ -49,10 +48,104 @@ export class PostgresQueryCtrl extends QueryCtrl { } } + this.schemaSegment= uiSegmentSrv.newSegment(this.target.schema); + + if (!this.target.table) { + this.tableSegment = uiSegmentSrv.newSegment({value: 'select table',fake: true}); + } else { + this.tableSegment= uiSegmentSrv.newSegment(this.target.table); + } + + this.timeColumnSegment = uiSegmentSrv.newSegment(this.target.timeColumn); + + this.buildSelectMenu(); this.panelCtrl.events.on('data-received', this.onDataReceived.bind(this), $scope); this.panelCtrl.events.on('data-error', this.onDataError.bind(this), $scope); } + buildSelectMenu() { + var categories = queryPart.getCategories(); + this.selectMenu = _.reduce( + categories, + function(memo, cat, key) { + var menu = { + text: key, + submenu: cat.map(item => { + return { text: item.type, value: item.type }; + }), + }; + memo.push(menu); + return memo; + }, + [] + ); + } + + toggleEditorMode() { + try { +// this.target.query = this.queryModel.render(false); + } catch (err) { + console.log('query render error'); + } + this.target.rawQuery = !this.target.rawQuery; + } + + getSchemaSegments() { + var schemaQuery = "SELECT schema_name FROM information_schema.schemata WHERE"; + schemaQuery += " schema_name NOT LIKE 'pg_%' AND schema_name <> 'information_schema';"; + return this.datasource + .metricFindQuery(schemaQuery) + .then(this.transformToSegments(true)) + .catch(this.handleQueryError.bind(this)); + } + + getTableSegments() { + var tableQuery = "SELECT table_name FROM information_schema.tables WHERE table_schema = '" + this.target.schema + "';"; + return this.datasource + .metricFindQuery(tableQuery) + .then(this.transformToSegments(true)) + .catch(this.handleQueryError.bind(this)); + } + + getTimeColumnSegments() { + var columnQuery = "SELECT column_name FROM information_schema.columns WHERE "; + columnQuery += " table_schema = '" + this.target.schema + "'"; + columnQuery += " AND table_name = '" + this.target.table + "'"; + columnQuery += " AND data_type IN ('timestamp without time zone','timestamp with time zone','bigint','integer','double precision','real');"; + + return this.datasource + .metricFindQuery(columnQuery) + .then(this.transformToSegments(true)) + .catch(this.handleQueryError.bind(this)); + } + + getColumnSegments() { + var columnQuery = "SELECT column_name FROM information_schema.columns WHERE "; + columnQuery += " table_schema = '" + this.target.schema + "'"; + columnQuery += " AND table_name = '" + this.target.table + "'"; + columnQuery += " AND data_type IN ('bigint','integer','double precision','real');"; + + return this.datasource + .metricFindQuery(columnQuery) + .then(this.transformToSegments(true)) + .catch(this.handleQueryError.bind(this)); + } + + tableChanged() { + this.target.table = this.tableSegment.value; + this.panelCtrl.refresh(); + } + + schemaChanged() { + this.target.schema = this.schemaSegment.value; + this.panelCtrl.refresh(); + } + + timeColumnChanged() { + this.target.time = this.timeColumnSegment.value; + this.panelCtrl.refresh(); + } + onDataReceived(dataList) { this.lastQueryMeta = null; this.lastQueryError = null; @@ -72,4 +165,68 @@ export class PostgresQueryCtrl extends QueryCtrl { } } } + + transformToSegments(addTemplateVars) { + return results => { + var segments = _.map(results, segment => { + return this.uiSegmentSrv.newSegment({ + value: segment.text, + expandable: segment.expandable, + }); + }); + + if (addTemplateVars) { + for (let variable of this.templateSrv.variables) { + segments.unshift( + this.uiSegmentSrv.newSegment({ + type: 'template', + value: '/^$' + variable.name + '$/', + expandable: true, + }) + ); + } + } + + return segments; + }; + } + + addSelectPart(selectParts, cat, subitem) { + this.queryModel.addSelectPart(selectParts, subitem.value); + this.panelCtrl.refresh(); + } + + handleSelectPartEvent(selectParts, part, evt) { + switch (evt.name) { + case 'get-param-options': { + var columnQuery = "SELECT column_name FROM information_schema.columns WHERE "; + columnQuery += " table_schema = '" + this.target.schema + "'"; + columnQuery += " AND table_name = '" + this.target.table + "'"; + columnQuery += " AND data_type IN ('bigint','integer','double precision','real');"; + + return this.datasource + .metricFindQuery(columnQuery) + .then(this.transformToSegments(true)) + .catch(this.handleQueryError.bind(this)); + } + case 'part-param-changed': { + this.panelCtrl.refresh(); + break; + } + case 'action': { + this.queryModel.removeSelectPart(selectParts, part); + this.panelCtrl.refresh(); + break; + } + case 'get-part-actions': { + return this.$q.when([{ text: 'Remove', value: 'remove-part' }]); + } + } + } + + handleQueryError(err) { + this.error = err.message || 'Failed to issue metric query'; + return []; + } + } From 443504517a1bcd58c533feef202530248a6d7050 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Tue, 30 Jan 2018 22:13:55 +0100 Subject: [PATCH 005/200] add postgres_query.ts --- .../datasource/postgres/postgres_query.ts | 239 ++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 public/app/plugins/datasource/postgres/postgres_query.ts diff --git a/public/app/plugins/datasource/postgres/postgres_query.ts b/public/app/plugins/datasource/postgres/postgres_query.ts new file mode 100644 index 00000000000..3424ae24c78 --- /dev/null +++ b/public/app/plugins/datasource/postgres/postgres_query.ts @@ -0,0 +1,239 @@ +import _ from 'lodash'; +import queryPart from './query_part'; +import kbn from 'app/core/utils/kbn'; + +export default class PostgresQuery { + target: any; + selectModels: any[]; + queryBuilder: any; + groupByParts: any; + templateSrv: any; + scopedVars: any; + + /** @ngInject */ + constructor(target, templateSrv?, scopedVars?) { + this.target = target; + this.templateSrv = templateSrv; + this.scopedVars = scopedVars; + + target.schema = target.schema || 'public'; + target.format = target.format || 'time_series'; + target.timeColumn = target.timeColumn || 'time'; + target.alias = ''; + + target.orderByTime = target.orderByTime || 'ASC'; +// target.groupBy = target.groupBy || [{ type: 'time', params: ['$__interval'] }, { type: 'fill', params: ['null'] }]; + target.select = target.select || [[{ type: 'field', params: ['value'] }]]; + + this.updateProjection(); + } + + updateProjection() { + this.selectModels = _.map(this.target.select, function(parts: any) { + return _.map(parts, queryPart.create); + }); + this.groupByParts = _.map(this.target.groupBy, queryPart.create); + } + + updatePersistedParts() { + this.target.select = _.map(this.selectModels, function(selectParts) { + return _.map(selectParts, function(part: any) { + return { type: part.def.type, params: part.params }; + }); + }); + } + + hasGroupByTime() { + return _.find(this.target.groupBy, (g: any) => g.type === 'time'); + } + + hasFill() { + return _.find(this.target.groupBy, (g: any) => g.type === 'fill'); + } + + addGroupBy(value) { + var stringParts = value.match(/^(\w+)\((.*)\)$/); + var typePart = stringParts[1]; + var arg = stringParts[2]; + var partModel = queryPart.create({ type: typePart, params: [arg] }); + var partCount = this.target.groupBy.length; + + if (partCount === 0) { + this.target.groupBy.push(partModel.part); + } else if (typePart === 'time') { + this.target.groupBy.splice(0, 0, partModel.part); + } else if (typePart === 'tag') { + if (this.target.groupBy[partCount - 1].type === 'fill') { + this.target.groupBy.splice(partCount - 1, 0, partModel.part); + } else { + this.target.groupBy.push(partModel.part); + } + } else { + this.target.groupBy.push(partModel.part); + } + + this.updateProjection(); + } + + removeGroupByPart(part, index) { + var categories = queryPart.getCategories(); + + if (part.def.type === 'time') { + // remove fill + this.target.groupBy = _.filter(this.target.groupBy, (g: any) => g.type !== 'fill'); + // remove aggregations + this.target.select = _.map(this.target.select, (s: any) => { + return _.filter(s, (part: any) => { + var partModel = queryPart.create(part); + if (partModel.def.category === categories.Aggregations) { + return false; + } + if (partModel.def.category === categories.Selectors) { + return false; + } + return true; + }); + }); + } + + this.target.groupBy.splice(index, 1); + this.updateProjection(); + } + + removeSelect(index: number) { + this.target.select.splice(index, 1); + this.updateProjection(); + } + + removeSelectPart(selectParts, part) { + // if we remove the field remove the whole statement + if (part.def.type === 'field') { + if (this.selectModels.length > 1) { + var modelsIndex = _.indexOf(this.selectModels, selectParts); + this.selectModels.splice(modelsIndex, 1); + } + } else { + var partIndex = _.indexOf(selectParts, part); + selectParts.splice(partIndex, 1); + } + + this.updatePersistedParts(); + } + + addSelectPart(selectParts, type) { + var partModel = queryPart.create({ type: type }); + partModel.def.addStrategy(selectParts, partModel, this); + this.updatePersistedParts(); + } + + private renderTagCondition(tag, index, interpolate) { + var str = ''; + var operator = tag.operator; + var value = tag.value; + if (index > 0) { + str = (tag.condition || 'AND') + ' '; + } + + if (!operator) { + if (/^\/.*\/$/.test(value)) { + operator = '=~'; + } else { + operator = '='; + } + } + + // quote value unless regex + if (operator !== '=~' && operator !== '!~') { + if (interpolate) { + value = this.templateSrv.replace(value, this.scopedVars); + } + if (operator !== '>' && operator !== '<') { + value = "'" + value.replace(/\\/g, '\\\\') + "'"; + } + } else if (interpolate) { + value = this.templateSrv.replace(value, this.scopedVars, 'regex'); + } + + return str + '"' + tag.key + '" ' + operator + ' ' + value; + } + + interpolateQueryStr(value, variable, defaultFormatFn) { + // if no multi or include all do not regexEscape + if (!variable.multi && !variable.includeAll) { + return value; + } + + if (typeof value === 'string') { + return kbn.regexEscape(value); + } + + var escapedValues = _.map(value, kbn.regexEscape); + return '(' + escapedValues.join('|') + ')'; + } + + render(interpolate?) { + var target = this.target; + + if (target.rawQuery) { + if (interpolate) { + return this.templateSrv.replace(target.rawSql, this.scopedVars, this.interpolateQueryStr); + } else { + return target.rawSql; + } + } + + var query = 'SELECT '; + query += target.timeColumn + ' AS time,'; + + var i, y; + for (i = 0; i < this.selectModels.length; i++) { + let parts = this.selectModels[i]; + var selectText = ''; + for (y = 0; y < parts.length; y++) { + let part = parts[y]; + selectText = part.render(selectText); + } + + if (i > 0) { + query += ', '; + } + query += selectText; + } + + query += ' FROM ' + target.schema + '.' + target.table + ' WHERE '; + var conditions = _.map(target.tags, (tag, index) => { + return this.renderTagCondition(tag, index, interpolate); + }); + + if (conditions.length > 0) { + query += '(' + conditions.join(' ') + ') AND '; + } + + query += '$__timeFilter(time)'; + + var groupBySection = ''; + for (i = 0; i < this.groupByParts.length; i++) { + var part = this.groupByParts[i]; + if (i > 0) { + // for some reason fill has no seperator + groupBySection += part.def.type === 'fill' ? ' ' : ', '; + } + groupBySection += part.render(''); + } + + if (groupBySection.length) { + query += ' GROUP BY ' + groupBySection; + } + + query += ' ORDER BY time'; + + return query; + } + + renderAdhocFilters(filters) { + var conditions = _.map(filters, (tag, index) => { + return this.renderTagCondition(tag, index, false); + }); + return conditions.join(' '); + } +} From 571ecdc740b87aad130ce05bd827135fd2e570d4 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Tue, 30 Jan 2018 22:57:38 +0100 Subject: [PATCH 006/200] enhance render function --- .../datasource/postgres/postgres_query.ts | 8 +- .../plugins/datasource/postgres/query_part.ts | 380 ++++++++++++++++++ 2 files changed, 386 insertions(+), 2 deletions(-) create mode 100644 public/app/plugins/datasource/postgres/query_part.ts diff --git a/public/app/plugins/datasource/postgres/postgres_query.ts b/public/app/plugins/datasource/postgres/postgres_query.ts index 3424ae24c78..e6d84306a9b 100644 --- a/public/app/plugins/datasource/postgres/postgres_query.ts +++ b/public/app/plugins/datasource/postgres/postgres_query.ts @@ -28,6 +28,10 @@ export default class PostgresQuery { this.updateProjection(); } + quoteIdentifier(field) { + return '"' + field + '"'; + } + updateProjection() { this.selectModels = _.map(this.target.select, function(parts: any) { return _.map(parts, queryPart.create); @@ -183,7 +187,7 @@ export default class PostgresQuery { } var query = 'SELECT '; - query += target.timeColumn + ' AS time,'; + query += this.quoteIdentifier(target.timeColumn) + ' AS time,'; var i, y; for (i = 0; i < this.selectModels.length; i++) { @@ -209,7 +213,7 @@ export default class PostgresQuery { query += '(' + conditions.join(' ') + ') AND '; } - query += '$__timeFilter(time)'; + query += '$__timeFilter(' + this.quoteIdentifier(target.timeColumn) + ')'; var groupBySection = ''; for (i = 0; i < this.groupByParts.length; i++) { diff --git a/public/app/plugins/datasource/postgres/query_part.ts b/public/app/plugins/datasource/postgres/query_part.ts new file mode 100644 index 00000000000..dffea15dbd1 --- /dev/null +++ b/public/app/plugins/datasource/postgres/query_part.ts @@ -0,0 +1,380 @@ +import _ from 'lodash'; +import { QueryPartDef, QueryPart, functionRenderer, suffixRenderer } from 'app/core/components/query_part/query_part'; + +var index = []; +var categories = { + Aggregations: [], + Selectors: [], + Transformations: [], + Predictors: [], + Math: [], + Aliasing: [], + Fields: [], +}; + +function createPart(part): any { + var def = index[part.type]; + if (!def) { + throw { message: 'Could not find query part ' + part.type }; + } + + return new QueryPart(part, def); +} + +function register(options: any) { + index[options.type] = new QueryPartDef(options); + options.category.push(index[options.type]); +} + +var groupByTimeFunctions = []; + +function aliasRenderer(part, innerExpr) { + return innerExpr + ' AS ' + '"' + part.params[0] + '"'; +} + +function fieldRenderer(part, innerExpr) { + return '"' + part.params[0] + '"'; +} + +function replaceAggregationAddStrategy(selectParts, partModel) { + // look for existing aggregation + for (var i = 0; i < selectParts.length; i++) { + var part = selectParts[i]; + if (part.def.category === categories.Aggregations) { + selectParts[i] = partModel; + return; + } + if (part.def.category === categories.Selectors) { + selectParts[i] = partModel; + return; + } + } + + selectParts.splice(1, 0, partModel); +} + +function addTransformationStrategy(selectParts, partModel) { + var i; + // look for index to add transformation + for (i = 0; i < selectParts.length; i++) { + var part = selectParts[i]; + if (part.def.category === categories.Math || part.def.category === categories.Aliasing) { + break; + } + } + + selectParts.splice(i, 0, partModel); +} + +function addMathStrategy(selectParts, partModel) { + var partCount = selectParts.length; + if (partCount > 0) { + // if last is math, replace it + if (selectParts[partCount - 1].def.type === 'math') { + selectParts[partCount - 1] = partModel; + return; + } + // if next to last is math, replace it + if (partCount > 1 && selectParts[partCount - 2].def.type === 'math') { + selectParts[partCount - 2] = partModel; + return; + } else if (selectParts[partCount - 1].def.type === 'alias') { + // if last is alias add it before + selectParts.splice(partCount - 1, 0, partModel); + return; + } + } + selectParts.push(partModel); +} + +function addAliasStrategy(selectParts, partModel) { + var partCount = selectParts.length; + if (partCount > 0) { + // if last is alias, replace it + if (selectParts[partCount - 1].def.type === 'alias') { + selectParts[partCount - 1] = partModel; + return; + } + } + selectParts.push(partModel); +} + +function addFieldStrategy(selectParts, partModel, query) { + // copy all parts + var parts = _.map(selectParts, function(part: any) { + return createPart({ type: part.def.type, params: _.clone(part.params) }); + }); + + query.selectModels.push(parts); +} + +register({ + type: 'field', + addStrategy: addFieldStrategy, + category: categories.Fields, + params: [{ type: 'field', dynamicLookup: true }], + defaultParams: ['value'], + renderer: fieldRenderer, +}); + +// Aggregations +register({ + type: 'avg', + addStrategy: replaceAggregationAddStrategy, + category: categories.Aggregations, + params: [], + defaultParams: [], + renderer: functionRenderer, +}); + +register({ + type: 'count', + addStrategy: replaceAggregationAddStrategy, + category: categories.Aggregations, + params: [], + defaultParams: [], + renderer: functionRenderer, +}); + +register({ + type: 'sum', + addStrategy: replaceAggregationAddStrategy, + category: categories.Aggregations, + params: [], + defaultParams: [], + renderer: functionRenderer, +}); + +// transformations + +register({ + type: 'derivative', + addStrategy: addTransformationStrategy, + category: categories.Transformations, + params: [ + { + name: 'duration', + type: 'interval', + options: ['1s', '10s', '1m', '5m', '10m', '15m', '1h'], + }, + ], + defaultParams: ['10s'], + renderer: functionRenderer, +}); + +register({ + type: 'spread', + addStrategy: addTransformationStrategy, + category: categories.Transformations, + params: [], + defaultParams: [], + renderer: functionRenderer, +}); + +register({ + type: 'non_negative_derivative', + addStrategy: addTransformationStrategy, + category: categories.Transformations, + params: [ + { + name: 'duration', + type: 'interval', + options: ['1s', '10s', '1m', '5m', '10m', '15m', '1h'], + }, + ], + defaultParams: ['10s'], + renderer: functionRenderer, +}); + +register({ + type: 'difference', + addStrategy: addTransformationStrategy, + category: categories.Transformations, + params: [], + defaultParams: [], + renderer: functionRenderer, +}); + +register({ + type: 'non_negative_difference', + addStrategy: addTransformationStrategy, + category: categories.Transformations, + params: [], + defaultParams: [], + renderer: functionRenderer, +}); + +register({ + type: 'moving_average', + addStrategy: addTransformationStrategy, + category: categories.Transformations, + params: [{ name: 'window', type: 'int', options: [5, 10, 20, 30, 40] }], + defaultParams: [10], + renderer: functionRenderer, +}); + +register({ + type: 'cumulative_sum', + addStrategy: addTransformationStrategy, + category: categories.Transformations, + params: [], + defaultParams: [], + renderer: functionRenderer, +}); + +register({ + type: 'stddev', + addStrategy: addTransformationStrategy, + category: categories.Transformations, + params: [], + defaultParams: [], + renderer: functionRenderer, +}); + +register({ + type: 'time', + category: groupByTimeFunctions, + params: [ + { + name: 'interval', + type: 'time', + options: ['$__interval', '1s', '10s', '1m', '5m', '10m', '15m', '1h'], + }, + ], + defaultParams: ['$__interval'], + renderer: functionRenderer, +}); + +register({ + type: 'fill', + category: groupByTimeFunctions, + params: [ + { + name: 'fill', + type: 'string', + options: ['none', 'null', '0', 'previous', 'linear'], + }, + ], + defaultParams: ['null'], + renderer: functionRenderer, +}); + +register({ + type: 'elapsed', + addStrategy: addTransformationStrategy, + category: categories.Transformations, + params: [ + { + name: 'duration', + type: 'interval', + options: ['1s', '10s', '1m', '5m', '10m', '15m', '1h'], + }, + ], + defaultParams: ['10s'], + renderer: functionRenderer, +}); + +// predictions +register({ + type: 'holt_winters', + addStrategy: addTransformationStrategy, + category: categories.Predictors, + params: [ + { name: 'number', type: 'int', options: [5, 10, 20, 30, 40] }, + { name: 'season', type: 'int', options: [0, 1, 2, 5, 10] }, + ], + defaultParams: [10, 2], + renderer: functionRenderer, +}); + +register({ + type: 'holt_winters_with_fit', + addStrategy: addTransformationStrategy, + category: categories.Predictors, + params: [ + { name: 'number', type: 'int', options: [5, 10, 20, 30, 40] }, + { name: 'season', type: 'int', options: [0, 1, 2, 5, 10] }, + ], + defaultParams: [10, 2], + renderer: functionRenderer, +}); + +// Selectors +register({ + type: 'bottom', + addStrategy: replaceAggregationAddStrategy, + category: categories.Selectors, + params: [{ name: 'count', type: 'int' }], + defaultParams: [3], + renderer: functionRenderer, +}); + +register({ + type: 'max', + addStrategy: replaceAggregationAddStrategy, + category: categories.Selectors, + params: [], + defaultParams: [], + renderer: functionRenderer, +}); + +register({ + type: 'min', + addStrategy: replaceAggregationAddStrategy, + category: categories.Selectors, + params: [], + defaultParams: [], + renderer: functionRenderer, +}); + +register({ + type: 'percentile', + addStrategy: replaceAggregationAddStrategy, + category: categories.Selectors, + params: [{ name: 'nth', type: 'int' }], + defaultParams: [95], + renderer: functionRenderer, +}); + +register({ + type: 'top', + addStrategy: replaceAggregationAddStrategy, + category: categories.Selectors, + params: [{ name: 'count', type: 'int' }], + defaultParams: [3], + renderer: functionRenderer, +}); + +register({ + type: 'tag', + category: groupByTimeFunctions, + params: [{ name: 'tag', type: 'string', dynamicLookup: true }], + defaultParams: ['tag'], + renderer: fieldRenderer, +}); + +register({ + type: 'math', + addStrategy: addMathStrategy, + category: categories.Math, + params: [{ name: 'expr', type: 'string' }], + defaultParams: [' / 100'], + renderer: suffixRenderer, +}); + +register({ + type: 'alias', + addStrategy: addAliasStrategy, + category: categories.Aliasing, + params: [{ name: 'name', type: 'string', quote: 'double' }], + defaultParams: ['alias'], + renderMode: 'suffix', + renderer: aliasRenderer, +}); + +export default { + create: createPart, + getCategories: function() { + return categories; + }, +}; From 4dbd83fac18a3cc5e59208d396a32538f2ab1a5e Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Wed, 31 Jan 2018 15:32:32 +0100 Subject: [PATCH 007/200] add groupby to querybuilder remove unused aggregations --- .../postgres/partials/query.editor.html | 21 +++ .../datasource/postgres/postgres_query.ts | 5 +- .../plugins/datasource/postgres/query_ctrl.ts | 84 ++++++++++ .../plugins/datasource/postgres/query_part.ts | 150 +----------------- 4 files changed, 110 insertions(+), 150 deletions(-) diff --git a/public/app/plugins/datasource/postgres/partials/query.editor.html b/public/app/plugins/datasource/postgres/partials/query.editor.html index 400855cf82f..3e921e1c631 100644 --- a/public/app/plugins/datasource/postgres/partials/query.editor.html +++ b/public/app/plugins/datasource/postgres/partials/query.editor.html @@ -51,6 +51,27 @@ +
+
+ + + + +
+ +
+ +
+ +
+
+
+
+
diff --git a/public/app/plugins/datasource/postgres/postgres_query.ts b/public/app/plugins/datasource/postgres/postgres_query.ts index e6d84306a9b..389ce85aae9 100644 --- a/public/app/plugins/datasource/postgres/postgres_query.ts +++ b/public/app/plugins/datasource/postgres/postgres_query.ts @@ -22,7 +22,7 @@ export default class PostgresQuery { target.alias = ''; target.orderByTime = target.orderByTime || 'ASC'; -// target.groupBy = target.groupBy || [{ type: 'time', params: ['$__interval'] }, { type: 'fill', params: ['null'] }]; + target.groupBy = target.groupBy || [{ type: 'time', params: ['$__interval'] }, { type: 'fill', params: ['null'] }]; target.select = target.select || [[{ type: 'field', params: ['value'] }]]; this.updateProjection(); @@ -92,9 +92,6 @@ export default class PostgresQuery { if (partModel.def.category === categories.Aggregations) { return false; } - if (partModel.def.category === categories.Selectors) { - return false; - } return true; }); }); diff --git a/public/app/plugins/datasource/postgres/query_ctrl.ts b/public/app/plugins/datasource/postgres/query_ctrl.ts index 4e39105370b..020d8007789 100644 --- a/public/app/plugins/datasource/postgres/query_ctrl.ts +++ b/public/app/plugins/datasource/postgres/query_ctrl.ts @@ -29,6 +29,7 @@ export class PostgresQueryCtrl extends QueryCtrl { tableSegment: any; timeColumnSegment: any; selectMenu: any; + groupBySegment: any; /** @ngInject **/ constructor($scope, $injector, private templateSrv, private $q, private uiSegmentSrv) { @@ -59,6 +60,8 @@ export class PostgresQueryCtrl extends QueryCtrl { this.timeColumnSegment = uiSegmentSrv.newSegment(this.target.timeColumn); this.buildSelectMenu(); + this.groupBySegment = this.uiSegmentSrv.newPlusButton(); + this.panelCtrl.events.on('data-received', this.onDataReceived.bind(this), $scope); this.panelCtrl.events.on('data-error', this.onDataError.bind(this), $scope); } @@ -224,6 +227,87 @@ export class PostgresQueryCtrl extends QueryCtrl { } } + handleGroupByPartEvent(part, index, evt) { + switch (evt.name) { + case 'get-param-options': { + var columnQuery = "SELECT column_name FROM information_schema.columns WHERE "; + columnQuery += " table_schema = '" + this.target.schema + "'"; + columnQuery += " AND table_name = '" + this.target.table + "'"; + + return this.datasource + .metricFindQuery(columnQuery) + .then(this.transformToSegments(true)) + .catch(this.handleQueryError.bind(this)); + } + case 'part-param-changed': { + this.panelCtrl.refresh(); + break; + } + case 'action': { + this.queryModel.removeGroupByPart(part, index); + this.panelCtrl.refresh(); + break; + } + case 'get-part-actions': { + return this.$q.when([{ text: 'Remove', value: 'remove-part' }]); + } + } + } + + getGroupByOptions() { + var columnQuery = "SELECT column_name FROM information_schema.columns WHERE "; + columnQuery += " table_schema = '" + this.target.schema + "'"; + columnQuery += " AND table_name = '" + this.target.table + "'"; + + + return this.datasource + .metricFindQuery(columnQuery) + .then(tags => { + var options = []; + if (!this.queryModel.hasFill()) { + options.push(this.uiSegmentSrv.newSegment({ value: 'fill(null)' })); + } + if (!this.target.limit) { + options.push(this.uiSegmentSrv.newSegment({ value: 'LIMIT' })); + } + if (!this.target.slimit) { + options.push(this.uiSegmentSrv.newSegment({ value: 'SLIMIT' })); + } + if (this.target.orderByTime === 'ASC') { + options.push(this.uiSegmentSrv.newSegment({ value: 'ORDER BY time DESC' })); + } + if (!this.queryModel.hasGroupByTime()) { + options.push(this.uiSegmentSrv.newSegment({ value: 'time($interval)' })); + } + for (let tag of tags) { + options.push(this.uiSegmentSrv.newSegment({ value: 'tag(' + tag.text + ')' })); + } + return options; + }) + .catch(this.handleQueryError.bind(this)); + } + + groupByAction() { + switch (this.groupBySegment.value) { + case 'LIMIT': { + this.target.limit = 10; + break; + } + case 'ORDER BY time DESC': { + this.target.orderByTime = 'DESC'; + break; + } + default: { + this.queryModel.addGroupBy(this.groupBySegment.value); + } + } + + var plusButton = this.uiSegmentSrv.newPlusButton(); + this.groupBySegment.value = plusButton.value; + this.groupBySegment.html = plusButton.html; + this.panelCtrl.refresh(); + } + handleQueryError(err) { this.error = err.message || 'Failed to issue metric query'; return []; diff --git a/public/app/plugins/datasource/postgres/query_part.ts b/public/app/plugins/datasource/postgres/query_part.ts index dffea15dbd1..5828515ec06 100644 --- a/public/app/plugins/datasource/postgres/query_part.ts +++ b/public/app/plugins/datasource/postgres/query_part.ts @@ -4,9 +4,6 @@ import { QueryPartDef, QueryPart, functionRenderer, suffixRenderer } from 'app/c var index = []; var categories = { Aggregations: [], - Selectors: [], - Transformations: [], - Predictors: [], Math: [], Aliasing: [], Fields: [], @@ -44,10 +41,6 @@ function replaceAggregationAddStrategy(selectParts, partModel) { selectParts[i] = partModel; return; } - if (part.def.category === categories.Selectors) { - selectParts[i] = partModel; - return; - } } selectParts.splice(1, 0, partModel); @@ -147,34 +140,10 @@ register({ // transformations -register({ - type: 'derivative', - addStrategy: addTransformationStrategy, - category: categories.Transformations, - params: [ - { - name: 'duration', - type: 'interval', - options: ['1s', '10s', '1m', '5m', '10m', '15m', '1h'], - }, - ], - defaultParams: ['10s'], - renderer: functionRenderer, -}); - -register({ - type: 'spread', - addStrategy: addTransformationStrategy, - category: categories.Transformations, - params: [], - defaultParams: [], - renderer: functionRenderer, -}); - register({ type: 'non_negative_derivative', addStrategy: addTransformationStrategy, - category: categories.Transformations, + category: categories.Aggregations, params: [ { name: 'duration', @@ -186,46 +155,10 @@ register({ renderer: functionRenderer, }); -register({ - type: 'difference', - addStrategy: addTransformationStrategy, - category: categories.Transformations, - params: [], - defaultParams: [], - renderer: functionRenderer, -}); - -register({ - type: 'non_negative_difference', - addStrategy: addTransformationStrategy, - category: categories.Transformations, - params: [], - defaultParams: [], - renderer: functionRenderer, -}); - -register({ - type: 'moving_average', - addStrategy: addTransformationStrategy, - category: categories.Transformations, - params: [{ name: 'window', type: 'int', options: [5, 10, 20, 30, 40] }], - defaultParams: [10], - renderer: functionRenderer, -}); - -register({ - type: 'cumulative_sum', - addStrategy: addTransformationStrategy, - category: categories.Transformations, - params: [], - defaultParams: [], - renderer: functionRenderer, -}); - register({ type: 'stddev', addStrategy: addTransformationStrategy, - category: categories.Transformations, + category: categories.Aggregations, params: [], defaultParams: [], renderer: functionRenderer, @@ -259,60 +192,11 @@ register({ renderer: functionRenderer, }); -register({ - type: 'elapsed', - addStrategy: addTransformationStrategy, - category: categories.Transformations, - params: [ - { - name: 'duration', - type: 'interval', - options: ['1s', '10s', '1m', '5m', '10m', '15m', '1h'], - }, - ], - defaultParams: ['10s'], - renderer: functionRenderer, -}); - -// predictions -register({ - type: 'holt_winters', - addStrategy: addTransformationStrategy, - category: categories.Predictors, - params: [ - { name: 'number', type: 'int', options: [5, 10, 20, 30, 40] }, - { name: 'season', type: 'int', options: [0, 1, 2, 5, 10] }, - ], - defaultParams: [10, 2], - renderer: functionRenderer, -}); - -register({ - type: 'holt_winters_with_fit', - addStrategy: addTransformationStrategy, - category: categories.Predictors, - params: [ - { name: 'number', type: 'int', options: [5, 10, 20, 30, 40] }, - { name: 'season', type: 'int', options: [0, 1, 2, 5, 10] }, - ], - defaultParams: [10, 2], - renderer: functionRenderer, -}); - // Selectors -register({ - type: 'bottom', - addStrategy: replaceAggregationAddStrategy, - category: categories.Selectors, - params: [{ name: 'count', type: 'int' }], - defaultParams: [3], - renderer: functionRenderer, -}); - register({ type: 'max', addStrategy: replaceAggregationAddStrategy, - category: categories.Selectors, + category: categories.Aggregations, params: [], defaultParams: [], renderer: functionRenderer, @@ -321,38 +205,12 @@ register({ register({ type: 'min', addStrategy: replaceAggregationAddStrategy, - category: categories.Selectors, + category: categories.Aggregations, params: [], defaultParams: [], renderer: functionRenderer, }); -register({ - type: 'percentile', - addStrategy: replaceAggregationAddStrategy, - category: categories.Selectors, - params: [{ name: 'nth', type: 'int' }], - defaultParams: [95], - renderer: functionRenderer, -}); - -register({ - type: 'top', - addStrategy: replaceAggregationAddStrategy, - category: categories.Selectors, - params: [{ name: 'count', type: 'int' }], - defaultParams: [3], - renderer: functionRenderer, -}); - -register({ - type: 'tag', - category: groupByTimeFunctions, - params: [{ name: 'tag', type: 'string', dynamicLookup: true }], - defaultParams: ['tag'], - renderer: fieldRenderer, -}); - register({ type: 'math', addStrategy: addMathStrategy, From c65a964cdda6e4cabcc570f0199dcd5378331781 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Sat, 3 Feb 2018 18:03:00 +0100 Subject: [PATCH 008/200] add metric column selector --- .../postgres/partials/query.editor.html | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/postgres/partials/query.editor.html b/public/app/plugins/datasource/postgres/partials/query.editor.html index 3e921e1c631..e266050dff1 100644 --- a/public/app/plugins/datasource/postgres/partials/query.editor.html +++ b/public/app/plugins/datasource/postgres/partials/query.editor.html @@ -17,6 +17,15 @@
+ +
+ +
+ +
+
+
+
@@ -46,9 +55,15 @@
- + +
+ +
+
+
+
From 382a5254772d29b67658dc1ed8e38a34b7df0a11 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Sat, 3 Feb 2018 18:26:57 +0100 Subject: [PATCH 009/200] make metricColumn functional --- .../app/plugins/datasource/postgres/query_ctrl.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/public/app/plugins/datasource/postgres/query_ctrl.ts b/public/app/plugins/datasource/postgres/query_ctrl.ts index 020d8007789..0b2bce7fb05 100644 --- a/public/app/plugins/datasource/postgres/query_ctrl.ts +++ b/public/app/plugins/datasource/postgres/query_ctrl.ts @@ -27,7 +27,9 @@ export class PostgresQueryCtrl extends QueryCtrl { showHelp: boolean; schemaSegment: any; tableSegment: any; + whereSegment: any; timeColumnSegment: any; + metricColumnSegment: any; selectMenu: any; groupBySegment: any; @@ -58,6 +60,7 @@ export class PostgresQueryCtrl extends QueryCtrl { } this.timeColumnSegment = uiSegmentSrv.newSegment(this.target.timeColumn); + this.metricColumnSegment = uiSegmentSrv.newSegment(this.target.metricColumn); this.buildSelectMenu(); this.groupBySegment = this.uiSegmentSrv.newPlusButton(); @@ -122,11 +125,11 @@ export class PostgresQueryCtrl extends QueryCtrl { .catch(this.handleQueryError.bind(this)); } - getColumnSegments() { + getMetricColumnSegments() { var columnQuery = "SELECT column_name FROM information_schema.columns WHERE "; columnQuery += " table_schema = '" + this.target.schema + "'"; columnQuery += " AND table_name = '" + this.target.table + "'"; - columnQuery += " AND data_type IN ('bigint','integer','double precision','real');"; + columnQuery += " AND data_type IN ('text','char','varchar');"; return this.datasource .metricFindQuery(columnQuery) @@ -145,7 +148,12 @@ export class PostgresQueryCtrl extends QueryCtrl { } timeColumnChanged() { - this.target.time = this.timeColumnSegment.value; + this.target.timeColumn = this.timeColumnSegment.value; + this.panelCtrl.refresh(); + } + + metricColumnChanged() { + this.target.metricColumn = this.metricColumnSegment.value; this.panelCtrl.refresh(); } From 3bce45d8a66abf984644f0cad508e73e60b595bc Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Thu, 8 Feb 2018 10:19:43 +0100 Subject: [PATCH 010/200] add query_builder --- .../datasource/postgres/query_builder.ts | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 public/app/plugins/datasource/postgres/query_builder.ts diff --git a/public/app/plugins/datasource/postgres/query_builder.ts b/public/app/plugins/datasource/postgres/query_builder.ts new file mode 100644 index 00000000000..7a227f33b93 --- /dev/null +++ b/public/app/plugins/datasource/postgres/query_builder.ts @@ -0,0 +1,50 @@ + +export class PostgresQueryBuilder { + constructor(private target, private queryModel) {} + + buildSchemaQuery() { + var query = "SELECT schema_name FROM information_schema.schemata WHERE"; + query += " schema_name NOT LIKE 'pg_%' AND schema_name NOT LIKE '\\_%' AND schema_name <> 'information_schema';"; + + return query; + } + + buildTableQuery() { + var query = "SELECT table_name FROM information_schema.tables WHERE "; + query += "table_schema = " + this.queryModel.quoteLiteral(this.target.schema); + return query; + } + + buildColumnQuery(type?: string) { + var query = "SELECT column_name FROM information_schema.columns WHERE "; + query += "table_schema = " + this.queryModel.quoteLiteral(this.target.schema); + query += " AND table_name = " + this.queryModel.quoteLiteral(this.target.table); + + switch (type) { + case "time": { + query += " AND data_type IN ('timestamp without time zone','timestamp with time zone','bigint','integer','double precision','real')"; + break; + } + case "metric": { + query += " AND data_type IN ('text','char','varchar')"; + break; + } + case "value": { + query += " AND data_type IN ('bigint','integer','double precision','real')"; + break; + } + } + + return query; + } + + buildValueQuery(column: string) { + var query = "SELECT DISTINCT " + this.queryModel.quoteIdentifier(column) + "::text"; + query += " FROM " + this.queryModel.quoteIdentifier(this.target.schema); + query += "." + this.queryModel.quoteIdentifier(this.target.table); + query += " ORDER BY " + this.queryModel.quoteIdentifier(column); + query += " LIMIT 100"; + return query; + } + +} From ef18eb7fcb0449c7bc9709b8837ff8fa202ccc93 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Thu, 8 Feb 2018 10:25:32 +0100 Subject: [PATCH 011/200] add where constraint handling --- .../postgres/partials/query.editor.html | 2 +- .../datasource/postgres/postgres_query.ts | 15 +- .../plugins/datasource/postgres/query_ctrl.ts | 192 ++++++++++++++---- 3 files changed, 163 insertions(+), 46 deletions(-) diff --git a/public/app/plugins/datasource/postgres/partials/query.editor.html b/public/app/plugins/datasource/postgres/partials/query.editor.html index e266050dff1..e25a41b11c2 100644 --- a/public/app/plugins/datasource/postgres/partials/query.editor.html +++ b/public/app/plugins/datasource/postgres/partials/query.editor.html @@ -19,7 +19,7 @@
- +
diff --git a/public/app/plugins/datasource/postgres/postgres_query.ts b/public/app/plugins/datasource/postgres/postgres_query.ts index 389ce85aae9..9a5a14c2b6a 100644 --- a/public/app/plugins/datasource/postgres/postgres_query.ts +++ b/public/app/plugins/datasource/postgres/postgres_query.ts @@ -19,17 +19,22 @@ export default class PostgresQuery { target.schema = target.schema || 'public'; target.format = target.format || 'time_series'; target.timeColumn = target.timeColumn || 'time'; - target.alias = ''; + target.metricColumn = target.metricColumn || 'None'; target.orderByTime = target.orderByTime || 'ASC'; - target.groupBy = target.groupBy || [{ type: 'time', params: ['$__interval'] }, { type: 'fill', params: ['null'] }]; + target.groupBy = target.groupBy || []; + target.where = target.where || []; target.select = target.select || [[{ type: 'field', params: ['value'] }]]; this.updateProjection(); } - quoteIdentifier(field) { - return '"' + field + '"'; + quoteIdentifier(value) { + return '"' + value + '"'; + } + + quoteLiteral(value) { + return "'" + value + "'"; } updateProjection() { @@ -202,7 +207,7 @@ export default class PostgresQuery { } query += ' FROM ' + target.schema + '.' + target.table + ' WHERE '; - var conditions = _.map(target.tags, (tag, index) => { + var conditions = _.map(target.where, (tag, index) => { return this.renderTagCondition(tag, index, interpolate); }); diff --git a/public/app/plugins/datasource/postgres/query_ctrl.ts b/public/app/plugins/datasource/postgres/query_ctrl.ts index 0b2bce7fb05..f6f6641eff0 100644 --- a/public/app/plugins/datasource/postgres/query_ctrl.ts +++ b/public/app/plugins/datasource/postgres/query_ctrl.ts @@ -1,4 +1,6 @@ +import angular from 'angular'; import _ from 'lodash'; +import { PostgresQueryBuilder } from './query_builder'; import { QueryCtrl } from 'app/plugins/sdk'; import queryPart from './query_part'; import PostgresQuery from './postgres_query'; @@ -22,22 +24,25 @@ export class PostgresQueryCtrl extends QueryCtrl { showLastQuerySQL: boolean; formats: any[]; queryModel: PostgresQuery; + queryBuilder: PostgresQueryBuilder; lastQueryMeta: QueryMeta; lastQueryError: string; showHelp: boolean; schemaSegment: any; tableSegment: any; - whereSegment: any; + whereSegments: any; timeColumnSegment: any; metricColumnSegment: any; selectMenu: any; groupBySegment: any; + removeWhereFilterSegment: any; /** @ngInject **/ constructor($scope, $injector, private templateSrv, private $q, private uiSegmentSrv) { super($scope, $injector); this.target = this.target; this.queryModel = new PostgresQuery(this.target, templateSrv, this.panel.scopedVars); + this.queryBuilder = new PostgresQueryBuilder(this.target, this.queryModel); this.formats = [{ text: 'Time series', value: 'time_series' }, { text: 'Table', value: 'table' }]; @@ -63,8 +68,32 @@ export class PostgresQueryCtrl extends QueryCtrl { this.metricColumnSegment = uiSegmentSrv.newSegment(this.target.metricColumn); this.buildSelectMenu(); + this.whereSegments = []; + for (let tag of this.target.where) { + if (!tag.operator) { + if (/^\/.*\/$/.test(tag.value)) { + tag.operator = '=~'; + } else { + tag.operator = '='; + } + } + + if (tag.condition) { + this.whereSegments.push(uiSegmentSrv.newCondition(tag.condition)); + } + + this.whereSegments.push(uiSegmentSrv.newKey(tag.key)); + this.whereSegments.push(uiSegmentSrv.newOperator(tag.operator)); + this.whereSegments.push(uiSegmentSrv.newKeyValue(tag.value)); + } + + this.fixWhereSegments(); this.groupBySegment = this.uiSegmentSrv.newPlusButton(); + this.removeWhereFilterSegment = uiSegmentSrv.newSegment({ + fake: true, + value: '-- remove tag filter --', + }); this.panelCtrl.events.on('data-received', this.onDataReceived.bind(this), $scope); this.panelCtrl.events.on('data-error', this.onDataError.bind(this), $scope); } @@ -97,42 +126,29 @@ export class PostgresQueryCtrl extends QueryCtrl { } getSchemaSegments() { - var schemaQuery = "SELECT schema_name FROM information_schema.schemata WHERE"; - schemaQuery += " schema_name NOT LIKE 'pg_%' AND schema_name <> 'information_schema';"; return this.datasource - .metricFindQuery(schemaQuery) + .metricFindQuery(this.queryBuilder.buildSchemaQuery()) .then(this.transformToSegments(true)) .catch(this.handleQueryError.bind(this)); } getTableSegments() { - var tableQuery = "SELECT table_name FROM information_schema.tables WHERE table_schema = '" + this.target.schema + "';"; return this.datasource - .metricFindQuery(tableQuery) + .metricFindQuery(this.queryBuilder.buildTableQuery()) .then(this.transformToSegments(true)) .catch(this.handleQueryError.bind(this)); } getTimeColumnSegments() { - var columnQuery = "SELECT column_name FROM information_schema.columns WHERE "; - columnQuery += " table_schema = '" + this.target.schema + "'"; - columnQuery += " AND table_name = '" + this.target.table + "'"; - columnQuery += " AND data_type IN ('timestamp without time zone','timestamp with time zone','bigint','integer','double precision','real');"; - return this.datasource - .metricFindQuery(columnQuery) + .metricFindQuery(this.queryBuilder.buildColumnQuery("time")) .then(this.transformToSegments(true)) .catch(this.handleQueryError.bind(this)); } getMetricColumnSegments() { - var columnQuery = "SELECT column_name FROM information_schema.columns WHERE "; - columnQuery += " table_schema = '" + this.target.schema + "'"; - columnQuery += " AND table_name = '" + this.target.table + "'"; - columnQuery += " AND data_type IN ('text','char','varchar');"; - return this.datasource - .metricFindQuery(columnQuery) + .metricFindQuery(this.queryBuilder.buildColumnQuery("metric")) .then(this.transformToSegments(true)) .catch(this.handleQueryError.bind(this)); } @@ -210,13 +226,8 @@ export class PostgresQueryCtrl extends QueryCtrl { handleSelectPartEvent(selectParts, part, evt) { switch (evt.name) { case 'get-param-options': { - var columnQuery = "SELECT column_name FROM information_schema.columns WHERE "; - columnQuery += " table_schema = '" + this.target.schema + "'"; - columnQuery += " AND table_name = '" + this.target.table + "'"; - columnQuery += " AND data_type IN ('bigint','integer','double precision','real');"; - return this.datasource - .metricFindQuery(columnQuery) + .metricFindQuery(this.queryBuilder.buildColumnQuery("value")) .then(this.transformToSegments(true)) .catch(this.handleQueryError.bind(this)); } @@ -238,12 +249,8 @@ export class PostgresQueryCtrl extends QueryCtrl { handleGroupByPartEvent(part, index, evt) { switch (evt.name) { case 'get-param-options': { - var columnQuery = "SELECT column_name FROM information_schema.columns WHERE "; - columnQuery += " table_schema = '" + this.target.schema + "'"; - columnQuery += " AND table_name = '" + this.target.table + "'"; - return this.datasource - .metricFindQuery(columnQuery) + .metricFindQuery(this.queryBuilder.buildColumnQuery()) .then(this.transformToSegments(true)) .catch(this.handleQueryError.bind(this)); } @@ -262,14 +269,125 @@ export class PostgresQueryCtrl extends QueryCtrl { } } - getGroupByOptions() { - var columnQuery = "SELECT column_name FROM information_schema.columns WHERE "; - columnQuery += " table_schema = '" + this.target.schema + "'"; - columnQuery += " AND table_name = '" + this.target.table + "'"; + fixWhereSegments() { + var count = this.whereSegments.length; + var lastSegment = this.whereSegments[Math.max(count - 1, 0)]; + if (!lastSegment || lastSegment.type !== 'plus-button') { + this.whereSegments.push(this.uiSegmentSrv.newPlusButton()); + } + } + + getTagsOrValues(segment, index) { + if (segment.type === 'condition') { + return this.$q.when([this.uiSegmentSrv.newSegment('AND'), this.uiSegmentSrv.newSegment('OR')]); + } + if (segment.type === 'operator') { + var nextValue = this.whereSegments[index + 1].value; + if (/^\/.*\/$/.test(nextValue)) { + return this.$q.when(this.uiSegmentSrv.newOperators(['=~', '!~'])); + } else { + return this.$q.when(this.uiSegmentSrv.newOperators(['=', '!=', '<>', '<', '>'])); + } + } + + var query, addTemplateVars; + if (segment.type === 'key' || segment.type === 'plus-button') { + query = this.queryBuilder.buildColumnQuery(); + + addTemplateVars = false; + } else if (segment.type === 'value') { + query = this.queryBuilder.buildValueQuery(this.whereSegments[index -2].value); + addTemplateVars = true; + } return this.datasource - .metricFindQuery(columnQuery) + .metricFindQuery(query) + .then(this.transformToSegments(addTemplateVars)) + .then(results => { + if (segment.type === 'key') { + results.splice(0, 0, angular.copy(this.removeWhereFilterSegment)); + } + return results; + }) + .catch(this.handleQueryError.bind(this)); + } + + getTagValueOperator(tagValue, tagOperator): string { + if (tagOperator !== '=~' && tagOperator !== '!~' && /^\/.*\/$/.test(tagValue)) { + return '=~'; + } else if ((tagOperator === '=~' || tagOperator === '!~') && /^(?!\/.*\/$)/.test(tagValue)) { + return '='; + } + return null; + } + + whereSegmentUpdated(segment, index) { + this.whereSegments[index] = segment; + + // handle remove where condition + if (segment.value === this.removeWhereFilterSegment.value) { + this.whereSegments.splice(index, 3); + if (this.whereSegments.length === 0) { + this.whereSegments.push(this.uiSegmentSrv.newPlusButton()); + } else if (this.whereSegments.length > 2) { + this.whereSegments.splice(Math.max(index - 1, 0), 1); + if (this.whereSegments[this.whereSegments.length - 1].type !== 'plus-button') { + this.whereSegments.push(this.uiSegmentSrv.newPlusButton()); + } + } + } else { + if (segment.type === 'plus-button') { + if (index > 2) { + this.whereSegments.splice(index, 0, this.uiSegmentSrv.newCondition('AND')); + } + this.whereSegments.push(this.uiSegmentSrv.newOperator('=')); + this.whereSegments.push(this.uiSegmentSrv.newFake('select value', 'value', 'query-segment-value')); + segment.type = 'key'; + segment.cssClass = 'query-segment-key'; + } + + if (index + 1 === this.whereSegments.length) { + this.whereSegments.push(this.uiSegmentSrv.newPlusButton()); + } + } + + this.rebuildTargetWhereConditions(); + } + + rebuildTargetWhereConditions() { + var where = []; + var tagIndex = 0; + var tagOperator = ''; + + _.each(this.whereSegments, (segment2, index) => { + if (segment2.type === 'key') { + if (where.length === 0) { + where.push({}); + } + where[tagIndex].key = segment2.value; + } else if (segment2.type === 'value') { + tagOperator = this.getTagValueOperator(segment2.value, where[tagIndex].operator); + if (tagOperator) { + this.whereSegments[index - 1] = this.uiSegmentSrv.newOperator(tagOperator); + where[tagIndex].operator = tagOperator; + } + where[tagIndex].value = segment2.value; + } else if (segment2.type === 'condition') { + where.push({ condition: segment2.value }); + tagIndex += 1; + } else if (segment2.type === 'operator') { + where[tagIndex].operator = segment2.value; + } + }); + + this.target.where = where; + this.panelCtrl.refresh(); + } + + getGroupByOptions() { + return this.datasource + .metricFindQuery(this.queryBuilder.buildColumnQuery()) .then(tags => { var options = []; if (!this.queryModel.hasFill()) { @@ -278,12 +396,6 @@ export class PostgresQueryCtrl extends QueryCtrl { if (!this.target.limit) { options.push(this.uiSegmentSrv.newSegment({ value: 'LIMIT' })); } - if (!this.target.slimit) { - options.push(this.uiSegmentSrv.newSegment({ value: 'SLIMIT' })); - } - if (this.target.orderByTime === 'ASC') { - options.push(this.uiSegmentSrv.newSegment({ value: 'ORDER BY time DESC' })); - } if (!this.queryModel.hasGroupByTime()) { options.push(this.uiSegmentSrv.newSegment({ value: 'time($interval)' })); } From fd518846b1865385eb9775332ffd04fc5388dcc9 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Sat, 3 Mar 2018 20:57:00 +0100 Subject: [PATCH 012/200] rename field to column --- .../datasource/postgres/postgres_query.ts | 22 ++++++++----- .../plugins/datasource/postgres/query_ctrl.ts | 7 ++--- .../plugins/datasource/postgres/query_part.ts | 31 +++++++------------ 3 files changed, 27 insertions(+), 33 deletions(-) diff --git a/public/app/plugins/datasource/postgres/postgres_query.ts b/public/app/plugins/datasource/postgres/postgres_query.ts index 9a5a14c2b6a..78e9c9c1a3f 100644 --- a/public/app/plugins/datasource/postgres/postgres_query.ts +++ b/public/app/plugins/datasource/postgres/postgres_query.ts @@ -24,7 +24,7 @@ export default class PostgresQuery { target.orderByTime = target.orderByTime || 'ASC'; target.groupBy = target.groupBy || []; target.where = target.where || []; - target.select = target.select || [[{ type: 'field', params: ['value'] }]]; + target.select = target.select || [[{ type: 'column', params: ['value'] }]]; this.updateProjection(); } @@ -88,8 +88,6 @@ export default class PostgresQuery { var categories = queryPart.getCategories(); if (part.def.type === 'time') { - // remove fill - this.target.groupBy = _.filter(this.target.groupBy, (g: any) => g.type !== 'fill'); // remove aggregations this.target.select = _.map(this.target.select, (s: any) => { return _.filter(s, (part: any) => { @@ -113,7 +111,7 @@ export default class PostgresQuery { removeSelectPart(selectParts, part) { // if we remove the field remove the whole statement - if (part.def.type === 'field') { + if (part.def.type === 'column') { if (this.selectModels.length > 1) { var modelsIndex = _.indexOf(this.selectModels, selectParts); this.selectModels.splice(modelsIndex, 1); @@ -189,7 +187,12 @@ export default class PostgresQuery { } var query = 'SELECT '; - query += this.quoteIdentifier(target.timeColumn) + ' AS time,'; + + if (this.hasGroupByTime()) { + query += '$__timeGroup(' + this.quoteIdentifier(target.timeColumn) + ',1m),'; + } else { + query += this.quoteIdentifier(target.timeColumn) + ' AS time,'; + } var i, y; for (i = 0; i < this.selectModels.length; i++) { @@ -221,10 +224,13 @@ export default class PostgresQuery { for (i = 0; i < this.groupByParts.length; i++) { var part = this.groupByParts[i]; if (i > 0) { - // for some reason fill has no seperator - groupBySection += part.def.type === 'fill' ? ' ' : ', '; + groupBySection += ', '; + } + if (part.def.type === 'time') { + groupBySection += 'time'; + } else { + groupBySection += part.render(''); } - groupBySection += part.render(''); } if (groupBySection.length) { diff --git a/public/app/plugins/datasource/postgres/query_ctrl.ts b/public/app/plugins/datasource/postgres/query_ctrl.ts index f6f6641eff0..0c30e2c51ff 100644 --- a/public/app/plugins/datasource/postgres/query_ctrl.ts +++ b/public/app/plugins/datasource/postgres/query_ctrl.ts @@ -92,7 +92,7 @@ export class PostgresQueryCtrl extends QueryCtrl { this.removeWhereFilterSegment = uiSegmentSrv.newSegment({ fake: true, - value: '-- remove tag filter --', + value: '-- remove filter --', }); this.panelCtrl.events.on('data-received', this.onDataReceived.bind(this), $scope); this.panelCtrl.events.on('data-error', this.onDataError.bind(this), $scope); @@ -390,9 +390,6 @@ export class PostgresQueryCtrl extends QueryCtrl { .metricFindQuery(this.queryBuilder.buildColumnQuery()) .then(tags => { var options = []; - if (!this.queryModel.hasFill()) { - options.push(this.uiSegmentSrv.newSegment({ value: 'fill(null)' })); - } if (!this.target.limit) { options.push(this.uiSegmentSrv.newSegment({ value: 'LIMIT' })); } @@ -400,7 +397,7 @@ export class PostgresQueryCtrl extends QueryCtrl { options.push(this.uiSegmentSrv.newSegment({ value: 'time($interval)' })); } for (let tag of tags) { - options.push(this.uiSegmentSrv.newSegment({ value: 'tag(' + tag.text + ')' })); + options.push(this.uiSegmentSrv.newSegment({ value: tag.text })); } return options; }) diff --git a/public/app/plugins/datasource/postgres/query_part.ts b/public/app/plugins/datasource/postgres/query_part.ts index 5828515ec06..0086b45b848 100644 --- a/public/app/plugins/datasource/postgres/query_part.ts +++ b/public/app/plugins/datasource/postgres/query_part.ts @@ -6,7 +6,7 @@ var categories = { Aggregations: [], Math: [], Aliasing: [], - Fields: [], + Columns: [], }; function createPart(part): any { @@ -29,7 +29,7 @@ function aliasRenderer(part, innerExpr) { return innerExpr + ' AS ' + '"' + part.params[0] + '"'; } -function fieldRenderer(part, innerExpr) { +function columnRenderer(part, innerExpr) { return '"' + part.params[0] + '"'; } @@ -92,7 +92,7 @@ function addAliasStrategy(selectParts, partModel) { selectParts.push(partModel); } -function addFieldStrategy(selectParts, partModel, query) { +function addColumnStrategy(selectParts, partModel, query) { // copy all parts var parts = _.map(selectParts, function(part: any) { return createPart({ type: part.def.type, params: _.clone(part.params) }); @@ -102,12 +102,12 @@ function addFieldStrategy(selectParts, partModel, query) { } register({ - type: 'field', - addStrategy: addFieldStrategy, - category: categories.Fields, - params: [{ type: 'field', dynamicLookup: true }], + type: 'column', + addStrategy: addColumnStrategy, + category: categories.Columns, + params: [{ type: 'column', dynamicLookup: true }], defaultParams: ['value'], - renderer: fieldRenderer, + renderer: columnRenderer, }); // Aggregations @@ -170,25 +170,16 @@ register({ params: [ { name: 'interval', - type: 'time', + type: 'interval', options: ['$__interval', '1s', '10s', '1m', '5m', '10m', '15m', '1h'], }, - ], - defaultParams: ['$__interval'], - renderer: functionRenderer, -}); - -register({ - type: 'fill', - category: groupByTimeFunctions, - params: [ { name: 'fill', type: 'string', - options: ['none', 'null', '0', 'previous', 'linear'], + options: ['none', 'null', '0'], }, ], - defaultParams: ['null'], + defaultParams: ['$__interval','none'], renderer: functionRenderer, }); From 7104e6f9f8dccd5bbba53519daaa40a7cce6a329 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Sat, 3 Mar 2018 22:11:51 +0100 Subject: [PATCH 013/200] fix variable interpolation --- public/app/plugins/datasource/postgres/postgres_query.ts | 3 +++ public/app/plugins/datasource/postgres/query_ctrl.ts | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/postgres/postgres_query.ts b/public/app/plugins/datasource/postgres/postgres_query.ts index 78e9c9c1a3f..2f8b3911ebc 100644 --- a/public/app/plugins/datasource/postgres/postgres_query.ts +++ b/public/app/plugins/datasource/postgres/postgres_query.ts @@ -239,6 +239,9 @@ export default class PostgresQuery { query += ' ORDER BY time'; + if (interpolate) { + query = this.templateSrv.replace(query, this.scopedVars, this.interpolateQueryStr); + } return query; } diff --git a/public/app/plugins/datasource/postgres/query_ctrl.ts b/public/app/plugins/datasource/postgres/query_ctrl.ts index 0c30e2c51ff..bde4010e226 100644 --- a/public/app/plugins/datasource/postgres/query_ctrl.ts +++ b/public/app/plugins/datasource/postgres/query_ctrl.ts @@ -207,7 +207,7 @@ export class PostgresQueryCtrl extends QueryCtrl { segments.unshift( this.uiSegmentSrv.newSegment({ type: 'template', - value: '/^$' + variable.name + '$/', + value: '$' + variable.name, expandable: true, }) ); From e8c6341fed3811b3d6339dc5e2fc2a8815caa285 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Sun, 4 Mar 2018 10:18:11 +0100 Subject: [PATCH 014/200] clean up aggregation functions --- .../plugins/datasource/postgres/query_part.ts | 87 ++++++------------- 1 file changed, 28 insertions(+), 59 deletions(-) diff --git a/public/app/plugins/datasource/postgres/query_part.ts b/public/app/plugins/datasource/postgres/query_part.ts index 0086b45b848..600723be00d 100644 --- a/public/app/plugins/datasource/postgres/query_part.ts +++ b/public/app/plugins/datasource/postgres/query_part.ts @@ -46,19 +46,6 @@ function replaceAggregationAddStrategy(selectParts, partModel) { selectParts.splice(1, 0, partModel); } -function addTransformationStrategy(selectParts, partModel) { - var i; - // look for index to add transformation - for (i = 0; i < selectParts.length; i++) { - var part = selectParts[i]; - if (part.def.category === categories.Math || part.def.category === categories.Aliasing) { - break; - } - } - - selectParts.splice(i, 0, partModel); -} - function addMathStrategy(selectParts, partModel) { var partCount = selectParts.length; if (partCount > 0) { @@ -138,54 +125,8 @@ register({ renderer: functionRenderer, }); -// transformations - -register({ - type: 'non_negative_derivative', - addStrategy: addTransformationStrategy, - category: categories.Aggregations, - params: [ - { - name: 'duration', - type: 'interval', - options: ['1s', '10s', '1m', '5m', '10m', '15m', '1h'], - }, - ], - defaultParams: ['10s'], - renderer: functionRenderer, -}); - register({ type: 'stddev', - addStrategy: addTransformationStrategy, - category: categories.Aggregations, - params: [], - defaultParams: [], - renderer: functionRenderer, -}); - -register({ - type: 'time', - category: groupByTimeFunctions, - params: [ - { - name: 'interval', - type: 'interval', - options: ['$__interval', '1s', '10s', '1m', '5m', '10m', '15m', '1h'], - }, - { - name: 'fill', - type: 'string', - options: ['none', 'null', '0'], - }, - ], - defaultParams: ['$__interval','none'], - renderer: functionRenderer, -}); - -// Selectors -register({ - type: 'max', addStrategy: replaceAggregationAddStrategy, category: categories.Aggregations, params: [], @@ -202,6 +143,15 @@ register({ renderer: functionRenderer, }); +register({ + type: 'max', + addStrategy: replaceAggregationAddStrategy, + category: categories.Aggregations, + params: [], + defaultParams: [], + renderer: functionRenderer, +}); + register({ type: 'math', addStrategy: addMathStrategy, @@ -221,6 +171,25 @@ register({ renderer: aliasRenderer, }); +register({ + type: 'time', + category: groupByTimeFunctions, + params: [ + { + name: 'interval', + type: 'interval', + options: ['$__interval', '1s', '10s', '1m', '5m', '10m', '15m', '1h'], + }, + { + name: 'fill', + type: 'string', + options: ['none', 'NULL', '0'], + }, + ], + defaultParams: ['$__interval','none'], + renderer: functionRenderer, +}); + export default { create: createPart, getCategories: function() { From 26e09b598c809e93a120dfbc30214af8ff9ac690 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Sun, 4 Mar 2018 19:35:43 +0100 Subject: [PATCH 015/200] fix group by column --- .../datasource/postgres/postgres_query.ts | 24 ++++++++++++------- .../plugins/datasource/postgres/query_ctrl.ts | 4 ++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/public/app/plugins/datasource/postgres/postgres_query.ts b/public/app/plugins/datasource/postgres/postgres_query.ts index 2f8b3911ebc..a236fa91b18 100644 --- a/public/app/plugins/datasource/postgres/postgres_query.ts +++ b/public/app/plugins/datasource/postgres/postgres_query.ts @@ -61,17 +61,17 @@ export default class PostgresQuery { } addGroupBy(value) { - var stringParts = value.match(/^(\w+)\((.*)\)$/); + var stringParts = value.match(/^(\w+)(\((.*)\))?$/); var typePart = stringParts[1]; - var arg = stringParts[2]; - var partModel = queryPart.create({ type: typePart, params: [arg] }); + var args = stringParts[3].split(","); + var partModel = queryPart.create({ type: typePart, params: args }); var partCount = this.target.groupBy.length; if (partCount === 0) { this.target.groupBy.push(partModel.part); } else if (typePart === 'time') { this.target.groupBy.splice(0, 0, partModel.part); - } else if (typePart === 'tag') { + } else if (typePart === 'column') { if (this.target.groupBy[partCount - 1].type === 'fill') { this.target.groupBy.splice(partCount - 1, 0, partModel.part); } else { @@ -188,8 +188,16 @@ export default class PostgresQuery { var query = 'SELECT '; - if (this.hasGroupByTime()) { - query += '$__timeGroup(' + this.quoteIdentifier(target.timeColumn) + ',1m),'; + var timeGroup = this.hasGroupByTime(); + + if (timeGroup) { + var args; + if (timeGroup.params.length > 1 && timeGroup.params[1] !== "none") { + args = timeGroup.params.join(","); + } else { + args = timeGroup.params[0]; + } + query += '$__timeGroup(' + this.quoteIdentifier(target.timeColumn) + ',' + args + '),'; } else { query += this.quoteIdentifier(target.timeColumn) + ' AS time,'; } @@ -227,7 +235,7 @@ export default class PostgresQuery { groupBySection += ', '; } if (part.def.type === 'time') { - groupBySection += 'time'; + groupBySection += '1'; } else { groupBySection += part.render(''); } @@ -237,7 +245,7 @@ export default class PostgresQuery { query += ' GROUP BY ' + groupBySection; } - query += ' ORDER BY time'; + query += ' ORDER BY 1'; if (interpolate) { query = this.templateSrv.replace(query, this.scopedVars, this.interpolateQueryStr); diff --git a/public/app/plugins/datasource/postgres/query_ctrl.ts b/public/app/plugins/datasource/postgres/query_ctrl.ts index bde4010e226..b0d5d7ab9ca 100644 --- a/public/app/plugins/datasource/postgres/query_ctrl.ts +++ b/public/app/plugins/datasource/postgres/query_ctrl.ts @@ -394,10 +394,10 @@ export class PostgresQueryCtrl extends QueryCtrl { options.push(this.uiSegmentSrv.newSegment({ value: 'LIMIT' })); } if (!this.queryModel.hasGroupByTime()) { - options.push(this.uiSegmentSrv.newSegment({ value: 'time($interval)' })); + options.push(this.uiSegmentSrv.newSegment({ type: 'time', value: 'time(1m,none)' })); } for (let tag of tags) { - options.push(this.uiSegmentSrv.newSegment({ value: tag.text })); + options.push(this.uiSegmentSrv.newSegment({ type: 'column', value: 'column(' + tag.text + ')' })); } return options; }) From bf4a30d30f93aa7ac1cf01c319666242022272fe Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Sun, 4 Mar 2018 19:46:11 +0100 Subject: [PATCH 016/200] set rawSQL when rendering query builder query --- public/app/plugins/datasource/postgres/postgres_query.ts | 1 + public/app/plugins/datasource/postgres/query_ctrl.ts | 5 ----- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/public/app/plugins/datasource/postgres/postgres_query.ts b/public/app/plugins/datasource/postgres/postgres_query.ts index a236fa91b18..a708f384972 100644 --- a/public/app/plugins/datasource/postgres/postgres_query.ts +++ b/public/app/plugins/datasource/postgres/postgres_query.ts @@ -247,6 +247,7 @@ export default class PostgresQuery { query += ' ORDER BY 1'; + this.target.rawSql = query; if (interpolate) { query = this.templateSrv.replace(query, this.scopedVars, this.interpolateQueryStr); } diff --git a/public/app/plugins/datasource/postgres/query_ctrl.ts b/public/app/plugins/datasource/postgres/query_ctrl.ts index b0d5d7ab9ca..4e36e8699ae 100644 --- a/public/app/plugins/datasource/postgres/query_ctrl.ts +++ b/public/app/plugins/datasource/postgres/query_ctrl.ts @@ -117,11 +117,6 @@ export class PostgresQueryCtrl extends QueryCtrl { } toggleEditorMode() { - try { -// this.target.query = this.queryModel.render(false); - } catch (err) { - console.log('query render error'); - } this.target.rawQuery = !this.target.rawQuery; } From 83200c289a454e978ac4c0ade432ac7ccb31dbc4 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Sun, 4 Mar 2018 23:32:23 +0100 Subject: [PATCH 017/200] use metricColumn in query builder --- public/app/plugins/datasource/postgres/postgres_query.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/public/app/plugins/datasource/postgres/postgres_query.ts b/public/app/plugins/datasource/postgres/postgres_query.ts index a708f384972..4541ebae7de 100644 --- a/public/app/plugins/datasource/postgres/postgres_query.ts +++ b/public/app/plugins/datasource/postgres/postgres_query.ts @@ -217,6 +217,10 @@ export default class PostgresQuery { query += selectText; } + if (this.target.metricColumn !== 'None') { + query += "," + this.quoteIdentifier(this.target.metricColumn) + " AS metric"; + } + query += ' FROM ' + target.schema + '.' + target.table + ' WHERE '; var conditions = _.map(target.where, (tag, index) => { return this.renderTagCondition(tag, index, interpolate); From 340f679d0f7c531eaa332c036a3141eb40119bff Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Fri, 9 Mar 2018 18:09:59 +0100 Subject: [PATCH 018/200] quote schema and table --- public/app/plugins/datasource/postgres/postgres_query.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/postgres/postgres_query.ts b/public/app/plugins/datasource/postgres/postgres_query.ts index 4541ebae7de..bd66ab5eca9 100644 --- a/public/app/plugins/datasource/postgres/postgres_query.ts +++ b/public/app/plugins/datasource/postgres/postgres_query.ts @@ -221,7 +221,7 @@ export default class PostgresQuery { query += "," + this.quoteIdentifier(this.target.metricColumn) + " AS metric"; } - query += ' FROM ' + target.schema + '.' + target.table + ' WHERE '; + query += ' FROM ' + this.quoteIdentifier(target.schema) + '.' + this.quoteIdentifier(target.table) + ' WHERE '; var conditions = _.map(target.where, (tag, index) => { return this.renderTagCondition(tag, index, interpolate); }); From 1d8540ac69d37b84689eaf0016b8d155d8e899a3 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Fri, 9 Mar 2018 18:18:12 +0100 Subject: [PATCH 019/200] properly quote where constraint parts --- .../datasource/postgres/postgres_query.ts | 28 ++++--------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/public/app/plugins/datasource/postgres/postgres_query.ts b/public/app/plugins/datasource/postgres/postgres_query.ts index bd66ab5eca9..9e682d2f186 100644 --- a/public/app/plugins/datasource/postgres/postgres_query.ts +++ b/public/app/plugins/datasource/postgres/postgres_query.ts @@ -130,7 +130,7 @@ export default class PostgresQuery { this.updatePersistedParts(); } - private renderTagCondition(tag, index, interpolate) { + private renderWhereConstraint(tag, index, interpolate) { var str = ''; var operator = tag.operator; var value = tag.value; @@ -138,27 +138,11 @@ export default class PostgresQuery { str = (tag.condition || 'AND') + ' '; } - if (!operator) { - if (/^\/.*\/$/.test(value)) { - operator = '=~'; - } else { - operator = '='; - } + if (interpolate) { + value = this.templateSrv.replace(value, this.scopedVars); } - // quote value unless regex - if (operator !== '=~' && operator !== '!~') { - if (interpolate) { - value = this.templateSrv.replace(value, this.scopedVars); - } - if (operator !== '>' && operator !== '<') { - value = "'" + value.replace(/\\/g, '\\\\') + "'"; - } - } else if (interpolate) { - value = this.templateSrv.replace(value, this.scopedVars, 'regex'); - } - - return str + '"' + tag.key + '" ' + operator + ' ' + value; + return str + this.quoteIdentifier(tag.key) + ' ' + operator + ' ' + this.quoteLiteral(value); } interpolateQueryStr(value, variable, defaultFormatFn) { @@ -223,7 +207,7 @@ export default class PostgresQuery { query += ' FROM ' + this.quoteIdentifier(target.schema) + '.' + this.quoteIdentifier(target.table) + ' WHERE '; var conditions = _.map(target.where, (tag, index) => { - return this.renderTagCondition(tag, index, interpolate); + return this.renderWhereConstraint(tag, index, interpolate); }); if (conditions.length > 0) { @@ -260,7 +244,7 @@ export default class PostgresQuery { renderAdhocFilters(filters) { var conditions = _.map(filters, (tag, index) => { - return this.renderTagCondition(tag, index, false); + return this.renderWhereConstraint(tag, index, false); }); return conditions.join(' '); } From cb5278d413e85ec7fbd21490ca4edc467f691518 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Sat, 10 Mar 2018 20:18:03 +0100 Subject: [PATCH 020/200] handle variables in where constraints --- .../datasource/postgres/postgres_query.ts | 16 ++++++---------- .../plugins/datasource/postgres/query_ctrl.ts | 8 ++------ 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/public/app/plugins/datasource/postgres/postgres_query.ts b/public/app/plugins/datasource/postgres/postgres_query.ts index 9e682d2f186..9c48a94862f 100644 --- a/public/app/plugins/datasource/postgres/postgres_query.ts +++ b/public/app/plugins/datasource/postgres/postgres_query.ts @@ -56,10 +56,6 @@ export default class PostgresQuery { return _.find(this.target.groupBy, (g: any) => g.type === 'time'); } - hasFill() { - return _.find(this.target.groupBy, (g: any) => g.type === 'fill'); - } - addGroupBy(value) { var stringParts = value.match(/^(\w+)(\((.*)\))?$/); var typePart = stringParts[1]; @@ -130,19 +126,19 @@ export default class PostgresQuery { this.updatePersistedParts(); } - private renderWhereConstraint(tag, index, interpolate) { + private renderWhereConstraint(constraint, index, interpolate) { var str = ''; - var operator = tag.operator; - var value = tag.value; + var operator = constraint.operator; + var value = constraint.value; if (index > 0) { - str = (tag.condition || 'AND') + ' '; + str = (constraint.condition || 'AND') + ' '; } if (interpolate) { value = this.templateSrv.replace(value, this.scopedVars); } - return str + this.quoteIdentifier(tag.key) + ' ' + operator + ' ' + this.quoteLiteral(value); + return str + this.quoteIdentifier(constraint.key) + ' ' + operator + ' ' + this.quoteLiteral(value); } interpolateQueryStr(value, variable, defaultFormatFn) { @@ -207,7 +203,7 @@ export default class PostgresQuery { query += ' FROM ' + this.quoteIdentifier(target.schema) + '.' + this.quoteIdentifier(target.table) + ' WHERE '; var conditions = _.map(target.where, (tag, index) => { - return this.renderWhereConstraint(tag, index, interpolate); + return this.renderWhereConstraint(tag, index, false); }); if (conditions.length > 0) { diff --git a/public/app/plugins/datasource/postgres/query_ctrl.ts b/public/app/plugins/datasource/postgres/query_ctrl.ts index 4e36e8699ae..da8c28eab9c 100644 --- a/public/app/plugins/datasource/postgres/query_ctrl.ts +++ b/public/app/plugins/datasource/postgres/query_ctrl.ts @@ -353,7 +353,6 @@ export class PostgresQueryCtrl extends QueryCtrl { rebuildTargetWhereConditions() { var where = []; var tagIndex = 0; - var tagOperator = ''; _.each(this.whereSegments, (segment2, index) => { if (segment2.type === 'key') { @@ -362,11 +361,8 @@ export class PostgresQueryCtrl extends QueryCtrl { } where[tagIndex].key = segment2.value; } else if (segment2.type === 'value') { - tagOperator = this.getTagValueOperator(segment2.value, where[tagIndex].operator); - if (tagOperator) { - this.whereSegments[index - 1] = this.uiSegmentSrv.newOperator(tagOperator); - where[tagIndex].operator = tagOperator; - } + where[tagIndex].value = segment2.value; + } else if (segment2.type === 'template') { where[tagIndex].value = segment2.value; } else if (segment2.type === 'condition') { where.push({ condition: segment2.value }); From 0b358ff5f30930401a3cfa9ca35699d2903786dc Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Sat, 10 Mar 2018 22:39:42 +0100 Subject: [PATCH 021/200] remove limit --- .../postgres/partials/query.editor.html | 2 +- .../plugins/datasource/postgres/query_ctrl.ts | 31 ++----------------- 2 files changed, 4 insertions(+), 29 deletions(-) diff --git a/public/app/plugins/datasource/postgres/partials/query.editor.html b/public/app/plugins/datasource/postgres/partials/query.editor.html index e25a41b11c2..0706cf3a6cc 100644 --- a/public/app/plugins/datasource/postgres/partials/query.editor.html +++ b/public/app/plugins/datasource/postgres/partials/query.editor.html @@ -19,7 +19,7 @@
- +
diff --git a/public/app/plugins/datasource/postgres/query_ctrl.ts b/public/app/plugins/datasource/postgres/query_ctrl.ts index da8c28eab9c..101d52ee096 100644 --- a/public/app/plugins/datasource/postgres/query_ctrl.ts +++ b/public/app/plugins/datasource/postgres/query_ctrl.ts @@ -273,17 +273,12 @@ export class PostgresQueryCtrl extends QueryCtrl { } } - getTagsOrValues(segment, index) { + getWhereSegments(segment, index) { if (segment.type === 'condition') { return this.$q.when([this.uiSegmentSrv.newSegment('AND'), this.uiSegmentSrv.newSegment('OR')]); } if (segment.type === 'operator') { - var nextValue = this.whereSegments[index + 1].value; - if (/^\/.*\/$/.test(nextValue)) { - return this.$q.when(this.uiSegmentSrv.newOperators(['=~', '!~'])); - } else { - return this.$q.when(this.uiSegmentSrv.newOperators(['=', '!=', '<>', '<', '>'])); - } + return this.$q.when(this.uiSegmentSrv.newOperators(['=', '!=', '<>', '<', '>'])); } var query, addTemplateVars; @@ -308,15 +303,6 @@ export class PostgresQueryCtrl extends QueryCtrl { .catch(this.handleQueryError.bind(this)); } - getTagValueOperator(tagValue, tagOperator): string { - if (tagOperator !== '=~' && tagOperator !== '!~' && /^\/.*\/$/.test(tagValue)) { - return '=~'; - } else if ((tagOperator === '=~' || tagOperator === '!~') && /^(?!\/.*\/$)/.test(tagValue)) { - return '='; - } - return null; - } - whereSegmentUpdated(segment, index) { this.whereSegments[index] = segment; @@ -381,14 +367,11 @@ export class PostgresQueryCtrl extends QueryCtrl { .metricFindQuery(this.queryBuilder.buildColumnQuery()) .then(tags => { var options = []; - if (!this.target.limit) { - options.push(this.uiSegmentSrv.newSegment({ value: 'LIMIT' })); - } if (!this.queryModel.hasGroupByTime()) { options.push(this.uiSegmentSrv.newSegment({ type: 'time', value: 'time(1m,none)' })); } for (let tag of tags) { - options.push(this.uiSegmentSrv.newSegment({ type: 'column', value: 'column(' + tag.text + ')' })); + options.push(this.uiSegmentSrv.newSegment({ type: 'column', value: tag.text })); } return options; }) @@ -397,14 +380,6 @@ export class PostgresQueryCtrl extends QueryCtrl { groupByAction() { switch (this.groupBySegment.value) { - case 'LIMIT': { - this.target.limit = 10; - break; - } - case 'ORDER BY time DESC': { - this.target.orderByTime = 'DESC'; - break; - } default: { this.queryModel.addGroupBy(this.groupBySegment.value); } From e780b1bce5140a6c74fcc4782090e15035c5268a Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Sun, 11 Mar 2018 12:06:54 +0100 Subject: [PATCH 022/200] cleanup where segment handling --- .../plugins/datasource/postgres/query_ctrl.ts | 34 +++++++------------ 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/public/app/plugins/datasource/postgres/query_ctrl.ts b/public/app/plugins/datasource/postgres/query_ctrl.ts index 101d52ee096..afde342474b 100644 --- a/public/app/plugins/datasource/postgres/query_ctrl.ts +++ b/public/app/plugins/datasource/postgres/query_ctrl.ts @@ -68,26 +68,7 @@ export class PostgresQueryCtrl extends QueryCtrl { this.metricColumnSegment = uiSegmentSrv.newSegment(this.target.metricColumn); this.buildSelectMenu(); - this.whereSegments = []; - for (let tag of this.target.where) { - if (!tag.operator) { - if (/^\/.*\/$/.test(tag.value)) { - tag.operator = '=~'; - } else { - tag.operator = '='; - } - } - - if (tag.condition) { - this.whereSegments.push(uiSegmentSrv.newCondition(tag.condition)); - } - - this.whereSegments.push(uiSegmentSrv.newKey(tag.key)); - this.whereSegments.push(uiSegmentSrv.newOperator(tag.operator)); - this.whereSegments.push(uiSegmentSrv.newKeyValue(tag.value)); - } - - this.fixWhereSegments(); + this.buildWhereSegments(); this.groupBySegment = this.uiSegmentSrv.newPlusButton(); this.removeWhereFilterSegment = uiSegmentSrv.newSegment({ @@ -264,7 +245,18 @@ export class PostgresQueryCtrl extends QueryCtrl { } } - fixWhereSegments() { + buildWhereSegments() { + this.whereSegments = []; + for (let constraint of this.target.where) { + + if (constraint.condition) { + this.whereSegments.push(this.uiSegmentSrv.newCondition(constraint.condition)); + } + this.whereSegments.push(this.uiSegmentSrv.newKey(constraint.key)); + this.whereSegments.push(this.uiSegmentSrv.newOperator(constraint.operator)); + this.whereSegments.push(this.uiSegmentSrv.newKeyValue(constraint.value)); + } + var count = this.whereSegments.length; var lastSegment = this.whereSegments[Math.max(count - 1, 0)]; From cdb4e2ba0b44741ed9efc09fbf060a7dd5d0c6ac Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Tue, 13 Mar 2018 21:31:07 +0100 Subject: [PATCH 023/200] remove unused setting --- public/app/plugins/datasource/postgres/postgres_query.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/public/app/plugins/datasource/postgres/postgres_query.ts b/public/app/plugins/datasource/postgres/postgres_query.ts index 9c48a94862f..f9b87e89541 100644 --- a/public/app/plugins/datasource/postgres/postgres_query.ts +++ b/public/app/plugins/datasource/postgres/postgres_query.ts @@ -21,7 +21,6 @@ export default class PostgresQuery { target.timeColumn = target.timeColumn || 'time'; target.metricColumn = target.metricColumn || 'None'; - target.orderByTime = target.orderByTime || 'ASC'; target.groupBy = target.groupBy || []; target.where = target.where || []; target.select = target.select || [[{ type: 'column', params: ['value'] }]]; From b7c7030a4681e3c5c8d3e565588a02e9e8a2e9db Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Tue, 13 Mar 2018 23:06:39 +0100 Subject: [PATCH 024/200] add regex operators --- .../datasource/postgres/query_builder.ts | 8 ++++++++ .../plugins/datasource/postgres/query_ctrl.ts | 18 ++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/public/app/plugins/datasource/postgres/query_builder.ts b/public/app/plugins/datasource/postgres/query_builder.ts index 7a227f33b93..23691830b17 100644 --- a/public/app/plugins/datasource/postgres/query_builder.ts +++ b/public/app/plugins/datasource/postgres/query_builder.ts @@ -47,4 +47,12 @@ export class PostgresQueryBuilder { return query; } + buildDatatypeQuery(column: string) { + var query = "SELECT data_type FROM information_schema.columns WHERE "; + query += " table_schema = " + this.queryModel.quoteLiteral(this.target.schema); + query += " AND table_name = " + this.queryModel.quoteLiteral(this.target.table); + query += " AND column_name = " + this.queryModel.quoteLiteral(column); + return query; + } + } diff --git a/public/app/plugins/datasource/postgres/query_ctrl.ts b/public/app/plugins/datasource/postgres/query_ctrl.ts index afde342474b..b84b0ce0750 100644 --- a/public/app/plugins/datasource/postgres/query_ctrl.ts +++ b/public/app/plugins/datasource/postgres/query_ctrl.ts @@ -266,14 +266,28 @@ export class PostgresQueryCtrl extends QueryCtrl { } getWhereSegments(segment, index) { + var query, addTemplateVars; + if (segment.type === 'condition') { return this.$q.when([this.uiSegmentSrv.newSegment('AND'), this.uiSegmentSrv.newSegment('OR')]); } if (segment.type === 'operator') { - return this.$q.when(this.uiSegmentSrv.newOperators(['=', '!=', '<>', '<', '>'])); + var columnName = this.whereSegments[index - 1].value; + query = this.queryBuilder.buildDatatypeQuery(columnName); + return this.datasource.metricFindQuery(query) + .then(results => { + var datatype = results[0].text; + switch (datatype) { + case "text": + case "character varying": + return this.$q.when(this.uiSegmentSrv.newOperators(['=', '!=', '~', '~*','!~','!~*','IN'])); + default: + return this.$q.when(this.uiSegmentSrv.newOperators(['=', '!=', '<', '<=', '>', '>='])); + } + }) + .catch(this.handleQueryError.bind(this)); } - var query, addTemplateVars; if (segment.type === 'key' || segment.type === 'plus-button') { query = this.queryBuilder.buildColumnQuery(); From 958646d976ad64a3b32707c2a7b686a906cd9fc1 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Tue, 13 Mar 2018 23:15:25 +0100 Subject: [PATCH 025/200] dont quote where constraints --- public/app/plugins/datasource/postgres/postgres_query.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/postgres/postgres_query.ts b/public/app/plugins/datasource/postgres/postgres_query.ts index f9b87e89541..48f7efae760 100644 --- a/public/app/plugins/datasource/postgres/postgres_query.ts +++ b/public/app/plugins/datasource/postgres/postgres_query.ts @@ -137,7 +137,7 @@ export default class PostgresQuery { value = this.templateSrv.replace(value, this.scopedVars); } - return str + this.quoteIdentifier(constraint.key) + ' ' + operator + ' ' + this.quoteLiteral(value); + return str + constraint.key + ' ' + operator + ' ' + value; } interpolateQueryStr(value, variable, defaultFormatFn) { From 5e9a66de5f36c8e29ebeb5370f503529ac2dd7c7 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Tue, 13 Mar 2018 23:19:56 +0100 Subject: [PATCH 026/200] put values for IN in parens --- public/app/plugins/datasource/postgres/postgres_query.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/postgres/postgres_query.ts b/public/app/plugins/datasource/postgres/postgres_query.ts index 48f7efae760..e06cb3f2126 100644 --- a/public/app/plugins/datasource/postgres/postgres_query.ts +++ b/public/app/plugins/datasource/postgres/postgres_query.ts @@ -137,7 +137,11 @@ export default class PostgresQuery { value = this.templateSrv.replace(value, this.scopedVars); } - return str + constraint.key + ' ' + operator + ' ' + value; + if (operator === "IN") { + return str + constraint.key + ' ' + operator + ' (' + value + ')'; + } else { + return str + constraint.key + ' ' + operator + ' ' + value; + } } interpolateQueryStr(value, variable, defaultFormatFn) { From e6501f0f0ecec19ccafd9191bcfcdef4a1e19c6f Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Tue, 13 Mar 2018 23:24:26 +0100 Subject: [PATCH 027/200] revert special handling for IN --- public/app/plugins/datasource/postgres/postgres_query.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/public/app/plugins/datasource/postgres/postgres_query.ts b/public/app/plugins/datasource/postgres/postgres_query.ts index e06cb3f2126..48f7efae760 100644 --- a/public/app/plugins/datasource/postgres/postgres_query.ts +++ b/public/app/plugins/datasource/postgres/postgres_query.ts @@ -137,11 +137,7 @@ export default class PostgresQuery { value = this.templateSrv.replace(value, this.scopedVars); } - if (operator === "IN") { - return str + constraint.key + ' ' + operator + ' (' + value + ')'; - } else { - return str + constraint.key + ' ' + operator + ' ' + value; - } + return str + constraint.key + ' ' + operator + ' ' + value; } interpolateQueryStr(value, variable, defaultFormatFn) { From 6793fa5e549dfe31c04dd3dacfa476a9091d3f3a Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Tue, 13 Mar 2018 23:33:40 +0100 Subject: [PATCH 028/200] join multivalue variables with , --- public/app/plugins/datasource/postgres/postgres_query.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/postgres/postgres_query.ts b/public/app/plugins/datasource/postgres/postgres_query.ts index 48f7efae760..3a11c344f3e 100644 --- a/public/app/plugins/datasource/postgres/postgres_query.ts +++ b/public/app/plugins/datasource/postgres/postgres_query.ts @@ -151,7 +151,7 @@ export default class PostgresQuery { } var escapedValues = _.map(value, kbn.regexEscape); - return '(' + escapedValues.join('|') + ')'; + return '(' + escapedValues.join(',') + ')'; } render(interpolate?) { From 64fa1ce8a0a3bfe6b7bb4e3c7cbc4227d0c42af4 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Wed, 14 Mar 2018 18:09:47 +0100 Subject: [PATCH 029/200] properly handle IN queries --- .../app/plugins/datasource/postgres/postgres_query.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/public/app/plugins/datasource/postgres/postgres_query.ts b/public/app/plugins/datasource/postgres/postgres_query.ts index 3a11c344f3e..8f9f1261340 100644 --- a/public/app/plugins/datasource/postgres/postgres_query.ts +++ b/public/app/plugins/datasource/postgres/postgres_query.ts @@ -1,6 +1,5 @@ import _ from 'lodash'; import queryPart from './query_part'; -import kbn from 'app/core/utils/kbn'; export default class PostgresQuery { target: any; @@ -26,14 +25,16 @@ export default class PostgresQuery { target.select = target.select || [[{ type: 'column', params: ['value'] }]]; this.updateProjection(); + // give interpolateQueryStr access to this + this.interpolateQueryStr = this.interpolateQueryStr.bind(this); } quoteIdentifier(value) { - return '"' + value + '"'; + return '"' + value.replace('"','""') + '"'; } quoteLiteral(value) { - return "'" + value + "'"; + return "'" + value.replace("'","''") + "'"; } updateProjection() { @@ -147,10 +148,10 @@ export default class PostgresQuery { } if (typeof value === 'string') { - return kbn.regexEscape(value); + return this.quoteLiteral(value); } - var escapedValues = _.map(value, kbn.regexEscape); + var escapedValues = _.map(value, this.quoteLiteral); return '(' + escapedValues.join(',') + ')'; } From 0c3afd0e9c098c83f04f44f048f72468496aec5c Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Wed, 14 Mar 2018 22:59:48 +0100 Subject: [PATCH 030/200] add buildAggregateQuery --- public/app/plugins/datasource/postgres/query_builder.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/public/app/plugins/datasource/postgres/query_builder.ts b/public/app/plugins/datasource/postgres/query_builder.ts index 23691830b17..275d15492fe 100644 --- a/public/app/plugins/datasource/postgres/query_builder.ts +++ b/public/app/plugins/datasource/postgres/query_builder.ts @@ -55,4 +55,12 @@ export class PostgresQueryBuilder { return query; } + buildAggregateQuery() { + var query = "SELECT DISTINCT proname FROM pg_aggregate "; + query += "INNER JOIN pg_proc ON pg_aggregate.aggfnoid = pg_proc.oid "; + query += "INNER JOIN pg_type ON pg_type.oid=pg_proc.prorettype "; + query += "WHERE pronargs=1 AND typname IN ('int8','float8') AND aggkind='n' ORDER BY 1"; + return query; + } + } From 46c229188e985f74ed35d7505c21d9ec723d7b99 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Wed, 14 Mar 2018 23:03:32 +0100 Subject: [PATCH 031/200] read aggregate functions from database --- .../datasource/postgres/postgres_query.ts | 3 ++- .../plugins/datasource/postgres/query_ctrl.ts | 11 +++++++++++ .../plugins/datasource/postgres/query_part.ts | 17 +++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/postgres/postgres_query.ts b/public/app/plugins/datasource/postgres/postgres_query.ts index 8f9f1261340..4516bf4a4be 100644 --- a/public/app/plugins/datasource/postgres/postgres_query.ts +++ b/public/app/plugins/datasource/postgres/postgres_query.ts @@ -24,9 +24,10 @@ export default class PostgresQuery { target.where = target.where || []; target.select = target.select || [[{ type: 'column', params: ['value'] }]]; - this.updateProjection(); // give interpolateQueryStr access to this this.interpolateQueryStr = this.interpolateQueryStr.bind(this); + + this.updateProjection(); } quoteIdentifier(value) { diff --git a/public/app/plugins/datasource/postgres/query_ctrl.ts b/public/app/plugins/datasource/postgres/query_ctrl.ts index b84b0ce0750..4a2a0ce6c1c 100644 --- a/public/app/plugins/datasource/postgres/query_ctrl.ts +++ b/public/app/plugins/datasource/postgres/query_ctrl.ts @@ -77,9 +77,19 @@ export class PostgresQueryCtrl extends QueryCtrl { }); this.panelCtrl.events.on('data-received', this.onDataReceived.bind(this), $scope); this.panelCtrl.events.on('data-error', this.onDataError.bind(this), $scope); + } buildSelectMenu() { + + if (!queryPart.hasAggregates()) { + this.datasource.metricFindQuery(this.queryBuilder.buildAggregateQuery()) + .then(results => { + queryPart.clearAggregates(); + _.map(results, segment => { queryPart.registerAggregate(segment.text); }); + }) + .catch(this.handleQueryError.bind(this)); + } var categories = queryPart.getCategories(); this.selectMenu = _.reduce( categories, @@ -279,6 +289,7 @@ export class PostgresQueryCtrl extends QueryCtrl { var datatype = results[0].text; switch (datatype) { case "text": + case "character": case "character varying": return this.$q.when(this.uiSegmentSrv.newOperators(['=', '!=', '~', '~*','!~','!~*','IN'])); default: diff --git a/public/app/plugins/datasource/postgres/query_part.ts b/public/app/plugins/datasource/postgres/query_part.ts index 600723be00d..b63ebfae0c1 100644 --- a/public/app/plugins/datasource/postgres/query_part.ts +++ b/public/app/plugins/datasource/postgres/query_part.ts @@ -23,6 +23,17 @@ function register(options: any) { options.category.push(index[options.type]); } +function registerAggregate(name: string) { + register({ + type: name, + addStrategy: replaceAggregationAddStrategy, + category: categories.Aggregations, + params: [], + defaultParams: [], + renderer: functionRenderer, + }); +} + var groupByTimeFunctions = []; function aliasRenderer(part, innerExpr) { @@ -192,6 +203,12 @@ register({ export default { create: createPart, + registerAggregate: registerAggregate, + clearAggregates: function() { categories.Aggregations = []; }, + hasAggregates: function() { + // FIXME + return categories.Aggregations.length > 6; + }, getCategories: function() { return categories; }, From 12600a0e959866036058092d35f6b7414e98dd65 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Mon, 26 Mar 2018 13:19:14 +0200 Subject: [PATCH 032/200] support non-nested menu entries --- public/app/plugins/datasource/postgres/query_ctrl.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/postgres/query_ctrl.ts b/public/app/plugins/datasource/postgres/query_ctrl.ts index 4a2a0ce6c1c..8a5cb273ef7 100644 --- a/public/app/plugins/datasource/postgres/query_ctrl.ts +++ b/public/app/plugins/datasource/postgres/query_ctrl.ts @@ -205,7 +205,11 @@ export class PostgresQueryCtrl extends QueryCtrl { } addSelectPart(selectParts, cat, subitem) { - this.queryModel.addSelectPart(selectParts, subitem.value); + if ("submenu" in cat) { + this.queryModel.addSelectPart(selectParts, subitem.value); + } else { + this.queryModel.addSelectPart(selectParts, cat.value); + } this.panelCtrl.refresh(); } From 6c2ef7dca6b34f189ef44c416e98c386117d6010 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Mon, 26 Mar 2018 18:34:40 +0200 Subject: [PATCH 033/200] handle aggregate functions more generic --- .../plugins/datasource/postgres/query_ctrl.ts | 47 +++++------- .../plugins/datasource/postgres/query_part.ts | 75 ++----------------- 2 files changed, 27 insertions(+), 95 deletions(-) diff --git a/public/app/plugins/datasource/postgres/query_ctrl.ts b/public/app/plugins/datasource/postgres/query_ctrl.ts index 8a5cb273ef7..d0ec5dc59be 100644 --- a/public/app/plugins/datasource/postgres/query_ctrl.ts +++ b/public/app/plugins/datasource/postgres/query_ctrl.ts @@ -81,30 +81,12 @@ export class PostgresQueryCtrl extends QueryCtrl { } buildSelectMenu() { - - if (!queryPart.hasAggregates()) { - this.datasource.metricFindQuery(this.queryBuilder.buildAggregateQuery()) - .then(results => { - queryPart.clearAggregates(); - _.map(results, segment => { queryPart.registerAggregate(segment.text); }); - }) - .catch(this.handleQueryError.bind(this)); - } - var categories = queryPart.getCategories(); - this.selectMenu = _.reduce( - categories, - function(memo, cat, key) { - var menu = { - text: key, - submenu: cat.map(item => { - return { text: item.type, value: item.type }; - }), - }; - memo.push(menu); - return memo; - }, - [] - ); + this.selectMenu = [ + {text: "aggregate", value: "aggregate"}, + {text: "math", value: "math"}, + {text: "alias", value: "alias"}, + {text: "column", value: "column"}, + ]; } toggleEditorMode() { @@ -216,10 +198,19 @@ export class PostgresQueryCtrl extends QueryCtrl { handleSelectPartEvent(selectParts, part, evt) { switch (evt.name) { case 'get-param-options': { - return this.datasource - .metricFindQuery(this.queryBuilder.buildColumnQuery("value")) - .then(this.transformToSegments(true)) - .catch(this.handleQueryError.bind(this)); + switch (part.def.type) { + case "aggregate": + return this.datasource + .metricFindQuery(this.queryBuilder.buildAggregateQuery()) + .then(this.transformToSegments(false)) + .catch(this.handleQueryError.bind(this)); + case "column": + return this.datasource + .metricFindQuery(this.queryBuilder.buildColumnQuery("value")) + .then(this.transformToSegments(true)) + .catch(this.handleQueryError.bind(this)); + } + } case 'part-param-changed': { this.panelCtrl.refresh(); diff --git a/public/app/plugins/datasource/postgres/query_part.ts b/public/app/plugins/datasource/postgres/query_part.ts index b63ebfae0c1..044f51a2457 100644 --- a/public/app/plugins/datasource/postgres/query_part.ts +++ b/public/app/plugins/datasource/postgres/query_part.ts @@ -23,23 +23,16 @@ function register(options: any) { options.category.push(index[options.type]); } -function registerAggregate(name: string) { - register({ - type: name, - addStrategy: replaceAggregationAddStrategy, - category: categories.Aggregations, - params: [], - defaultParams: [], - renderer: functionRenderer, - }); -} - var groupByTimeFunctions = []; function aliasRenderer(part, innerExpr) { return innerExpr + ' AS ' + '"' + part.params[0] + '"'; } +function aggregateRenderer(part, innerExpr) { + return part.params[0] + '(' + innerExpr + ')'; +} + function columnRenderer(part, innerExpr) { return '"' + part.params[0] + '"'; } @@ -108,59 +101,13 @@ register({ renderer: columnRenderer, }); -// Aggregations register({ - type: 'avg', + type: 'aggregate', addStrategy: replaceAggregationAddStrategy, category: categories.Aggregations, - params: [], - defaultParams: [], - renderer: functionRenderer, -}); - -register({ - type: 'count', - addStrategy: replaceAggregationAddStrategy, - category: categories.Aggregations, - params: [], - defaultParams: [], - renderer: functionRenderer, -}); - -register({ - type: 'sum', - addStrategy: replaceAggregationAddStrategy, - category: categories.Aggregations, - params: [], - defaultParams: [], - renderer: functionRenderer, -}); - -register({ - type: 'stddev', - addStrategy: replaceAggregationAddStrategy, - category: categories.Aggregations, - params: [], - defaultParams: [], - renderer: functionRenderer, -}); - -register({ - type: 'min', - addStrategy: replaceAggregationAddStrategy, - category: categories.Aggregations, - params: [], - defaultParams: [], - renderer: functionRenderer, -}); - -register({ - type: 'max', - addStrategy: replaceAggregationAddStrategy, - category: categories.Aggregations, - params: [], - defaultParams: [], - renderer: functionRenderer, + params: [{name: 'name', type: 'string', dynamicLookup: true}], + defaultParams: ['avg'], + renderer: aggregateRenderer, }); register({ @@ -203,12 +150,6 @@ register({ export default { create: createPart, - registerAggregate: registerAggregate, - clearAggregates: function() { categories.Aggregations = []; }, - hasAggregates: function() { - // FIXME - return categories.Aggregations.length > 6; - }, getCategories: function() { return categories; }, From d6ac7aee899db14d2306ab9828cb39ea9854d853 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Mon, 26 Mar 2018 18:50:03 +0200 Subject: [PATCH 034/200] remove unused import --- public/app/plugins/datasource/postgres/query_ctrl.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/public/app/plugins/datasource/postgres/query_ctrl.ts b/public/app/plugins/datasource/postgres/query_ctrl.ts index d0ec5dc59be..dd1da1c75cf 100644 --- a/public/app/plugins/datasource/postgres/query_ctrl.ts +++ b/public/app/plugins/datasource/postgres/query_ctrl.ts @@ -2,7 +2,6 @@ import angular from 'angular'; import _ from 'lodash'; import { PostgresQueryBuilder } from './query_builder'; import { QueryCtrl } from 'app/plugins/sdk'; -import queryPart from './query_part'; import PostgresQuery from './postgres_query'; export interface QueryMeta { From 8b3c3081689236be24a21645ca852a928d33d9c7 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Mon, 26 Mar 2018 20:15:16 +0200 Subject: [PATCH 035/200] remove categories from queryPart --- .../datasource/postgres/postgres_query.ts | 5 +---- .../plugins/datasource/postgres/query_part.ts | 19 +------------------ 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/public/app/plugins/datasource/postgres/postgres_query.ts b/public/app/plugins/datasource/postgres/postgres_query.ts index 4516bf4a4be..a804cb70294 100644 --- a/public/app/plugins/datasource/postgres/postgres_query.ts +++ b/public/app/plugins/datasource/postgres/postgres_query.ts @@ -82,14 +82,11 @@ export default class PostgresQuery { } removeGroupByPart(part, index) { - var categories = queryPart.getCategories(); - if (part.def.type === 'time') { // remove aggregations this.target.select = _.map(this.target.select, (s: any) => { return _.filter(s, (part: any) => { - var partModel = queryPart.create(part); - if (partModel.def.category === categories.Aggregations) { + if (part.type === "aggregate") { return false; } return true; diff --git a/public/app/plugins/datasource/postgres/query_part.ts b/public/app/plugins/datasource/postgres/query_part.ts index 044f51a2457..0d2b365fe66 100644 --- a/public/app/plugins/datasource/postgres/query_part.ts +++ b/public/app/plugins/datasource/postgres/query_part.ts @@ -2,12 +2,6 @@ import _ from 'lodash'; import { QueryPartDef, QueryPart, functionRenderer, suffixRenderer } from 'app/core/components/query_part/query_part'; var index = []; -var categories = { - Aggregations: [], - Math: [], - Aliasing: [], - Columns: [], -}; function createPart(part): any { var def = index[part.type]; @@ -20,11 +14,8 @@ function createPart(part): any { function register(options: any) { index[options.type] = new QueryPartDef(options); - options.category.push(index[options.type]); } -var groupByTimeFunctions = []; - function aliasRenderer(part, innerExpr) { return innerExpr + ' AS ' + '"' + part.params[0] + '"'; } @@ -41,7 +32,7 @@ function replaceAggregationAddStrategy(selectParts, partModel) { // look for existing aggregation for (var i = 0; i < selectParts.length; i++) { var part = selectParts[i]; - if (part.def.category === categories.Aggregations) { + if (part.def.type === "aggregate") { selectParts[i] = partModel; return; } @@ -95,7 +86,6 @@ function addColumnStrategy(selectParts, partModel, query) { register({ type: 'column', addStrategy: addColumnStrategy, - category: categories.Columns, params: [{ type: 'column', dynamicLookup: true }], defaultParams: ['value'], renderer: columnRenderer, @@ -104,7 +94,6 @@ register({ register({ type: 'aggregate', addStrategy: replaceAggregationAddStrategy, - category: categories.Aggregations, params: [{name: 'name', type: 'string', dynamicLookup: true}], defaultParams: ['avg'], renderer: aggregateRenderer, @@ -113,7 +102,6 @@ register({ register({ type: 'math', addStrategy: addMathStrategy, - category: categories.Math, params: [{ name: 'expr', type: 'string' }], defaultParams: [' / 100'], renderer: suffixRenderer, @@ -122,7 +110,6 @@ register({ register({ type: 'alias', addStrategy: addAliasStrategy, - category: categories.Aliasing, params: [{ name: 'name', type: 'string', quote: 'double' }], defaultParams: ['alias'], renderMode: 'suffix', @@ -131,7 +118,6 @@ register({ register({ type: 'time', - category: groupByTimeFunctions, params: [ { name: 'interval', @@ -150,7 +136,4 @@ register({ export default { create: createPart, - getCategories: function() { - return categories; - }, }; From 731c7520b393ae7e82603032860390771b35dc7d Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Sat, 19 May 2018 15:34:48 +0200 Subject: [PATCH 036/200] return values quotes for suggestions in where expression --- public/app/plugins/datasource/postgres/query_builder.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/public/app/plugins/datasource/postgres/query_builder.ts b/public/app/plugins/datasource/postgres/query_builder.ts index 275d15492fe..3dfbf99fefd 100644 --- a/public/app/plugins/datasource/postgres/query_builder.ts +++ b/public/app/plugins/datasource/postgres/query_builder.ts @@ -39,11 +39,10 @@ export class PostgresQueryBuilder { } buildValueQuery(column: string) { - var query = "SELECT DISTINCT " + this.queryModel.quoteIdentifier(column) + "::text"; + var query = "SELECT DISTINCT quote_literal(" + column + ")"; query += " FROM " + this.queryModel.quoteIdentifier(this.target.schema); query += "." + this.queryModel.quoteIdentifier(this.target.table); - query += " ORDER BY " + this.queryModel.quoteIdentifier(column); - query += " LIMIT 100"; + query += " ORDER BY 1 LIMIT 100"; return query; } From b12d049f6f1b24ff733b0e6f71f910977751e5a2 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Sat, 19 May 2018 17:21:53 +0200 Subject: [PATCH 037/200] quote column name in buildValueQuery --- public/app/plugins/datasource/postgres/query_builder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/postgres/query_builder.ts b/public/app/plugins/datasource/postgres/query_builder.ts index 3dfbf99fefd..dd6f95b550c 100644 --- a/public/app/plugins/datasource/postgres/query_builder.ts +++ b/public/app/plugins/datasource/postgres/query_builder.ts @@ -39,7 +39,7 @@ export class PostgresQueryBuilder { } buildValueQuery(column: string) { - var query = "SELECT DISTINCT quote_literal(" + column + ")"; + var query = "SELECT DISTINCT quote_literal(" + this.queryModel.quoteIdentifier(column) + ")"; query += " FROM " + this.queryModel.quoteIdentifier(this.target.schema); query += "." + this.queryModel.quoteIdentifier(this.target.table); query += " ORDER BY 1 LIMIT 100"; From 181dfdba04d6ff31ae468eea11a5fe334290f617 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Mon, 21 May 2018 09:37:04 +0200 Subject: [PATCH 038/200] add sql_part component --- .../app/core/components/sql_part/sql_part.ts | 118 +++++++++++ .../components/sql_part/sql_part_editor.ts | 185 ++++++++++++++++++ public/app/core/core.ts | 2 + 3 files changed, 305 insertions(+) create mode 100644 public/app/core/components/sql_part/sql_part.ts create mode 100644 public/app/core/components/sql_part/sql_part_editor.ts diff --git a/public/app/core/components/sql_part/sql_part.ts b/public/app/core/components/sql_part/sql_part.ts new file mode 100644 index 00000000000..77366e6d09b --- /dev/null +++ b/public/app/core/components/sql_part/sql_part.ts @@ -0,0 +1,118 @@ +import _ from 'lodash'; + +export class SqlPartDef { + type: string; + params: any[]; + defaultParams: any[]; + renderer: any; + category: any; + addStrategy: any; + + constructor(options: any) { + this.type = options.type; + this.params = options.params; + this.defaultParams = options.defaultParams; + this.renderer = options.renderer; + this.category = options.category; + this.addStrategy = options.addStrategy; + } +} + +export class SqlPart { + part: any; + def: SqlPartDef; + params: any[]; + text: string; + + constructor(part: any, def: any) { + this.part = part; + this.def = def; + if (!this.def) { + throw { message: 'Could not find query part ' + part.type }; + } + + part.params = part.params || _.clone(this.def.defaultParams); + this.params = part.params; + this.updateText(); + } + + render(innerExpr: string) { + return this.def.renderer(this, innerExpr); + } + + hasMultipleParamsInString(strValue, index) { + if (strValue.indexOf(',') === -1) { + return false; + } + + return this.def.params[index + 1] && this.def.params[index + 1].optional; + } + + updateParam(strValue, index) { + // handle optional parameters + // if string contains ',' and next param is optional, split and update both + if (this.hasMultipleParamsInString(strValue, index)) { + _.each(strValue.split(','), (partVal, idx) => { + this.updateParam(partVal.trim(), idx); + }); + return; + } + + if (strValue === '' && this.def.params[index].optional) { + this.params.splice(index, 1); + } else { + this.params[index] = strValue; + } + + this.part.params = this.params; + this.updateText(); + } + + updateText() { + if (this.params.length === 0) { + this.text = this.def.type + '()'; + return; + } + + var text = this.def.type + '('; + text += this.params.join(', '); + text += ')'; + this.text = text; + } +} + +export function functionRenderer(part, innerExpr) { + var str = part.def.type + '('; + var parameters = _.map(part.params, (value, index) => { + var paramType = part.def.params[index]; + if (paramType.type === 'time') { + if (value === 'auto') { + value = '$__interval'; + } + } + if (paramType.quote === 'single') { + return "'" + value + "'"; + } else if (paramType.quote === 'double') { + return '"' + value + '"'; + } + + return value; + }); + + if (innerExpr) { + parameters.unshift(innerExpr); + } + return str + parameters.join(', ') + ')'; +} + +export function suffixRenderer(part, innerExpr) { + return innerExpr + ' ' + part.params[0]; +} + +export function identityRenderer(part, innerExpr) { + return part.params[0]; +} + +export function quotedIdentityRenderer(part, innerExpr) { + return '"' + part.params[0] + '"'; +} diff --git a/public/app/core/components/sql_part/sql_part_editor.ts b/public/app/core/components/sql_part/sql_part_editor.ts new file mode 100644 index 00000000000..837f280e66d --- /dev/null +++ b/public/app/core/components/sql_part/sql_part_editor.ts @@ -0,0 +1,185 @@ +import _ from 'lodash'; +import $ from 'jquery'; +import coreModule from 'app/core/core_module'; + +var template = ` +
- - + +
@@ -72,10 +72,10 @@ GROUP BY - - +
From e93276b1f822edb434b280662fb914a1e0e4a626 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Mon, 21 May 2018 11:10:00 +0200 Subject: [PATCH 040/200] use sql part component --- public/app/plugins/datasource/postgres/query_part.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/app/plugins/datasource/postgres/query_part.ts b/public/app/plugins/datasource/postgres/query_part.ts index 0d2b365fe66..d54a66783eb 100644 --- a/public/app/plugins/datasource/postgres/query_part.ts +++ b/public/app/plugins/datasource/postgres/query_part.ts @@ -1,5 +1,5 @@ import _ from 'lodash'; -import { QueryPartDef, QueryPart, functionRenderer, suffixRenderer } from 'app/core/components/query_part/query_part'; +import { SqlPartDef, SqlPart, functionRenderer, suffixRenderer } from 'app/core/components/sql_part/sql_part'; var index = []; @@ -9,11 +9,11 @@ function createPart(part): any { throw { message: 'Could not find query part ' + part.type }; } - return new QueryPart(part, def); + return new SqlPart(part, def); } function register(options: any) { - index[options.type] = new QueryPartDef(options); + index[options.type] = new SqlPartDef(options); } function aliasRenderer(part, innerExpr) { From 3af4e4e0d696ba62731bcd9b527472b5c2acef68 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Mon, 21 May 2018 11:44:37 +0200 Subject: [PATCH 041/200] separate label in template from type --- public/app/core/components/sql_part/sql_part.ts | 2 ++ public/app/core/components/sql_part/sql_part_editor.ts | 2 +- public/app/plugins/datasource/postgres/query_part.ts | 5 +++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/public/app/core/components/sql_part/sql_part.ts b/public/app/core/components/sql_part/sql_part.ts index 77366e6d09b..94dd0ff6ae8 100644 --- a/public/app/core/components/sql_part/sql_part.ts +++ b/public/app/core/components/sql_part/sql_part.ts @@ -2,6 +2,7 @@ import _ from 'lodash'; export class SqlPartDef { type: string; + label: string; params: any[]; defaultParams: any[]; renderer: any; @@ -10,6 +11,7 @@ export class SqlPartDef { constructor(options: any) { this.type = options.type; + this.label = options.label; this.params = options.params; this.defaultParams = options.defaultParams; this.renderer = options.renderer; diff --git a/public/app/core/components/sql_part/sql_part_editor.ts b/public/app/core/components/sql_part/sql_part_editor.ts index 837f280e66d..2ebeca5753a 100644 --- a/public/app/core/components/sql_part/sql_part_editor.ts +++ b/public/app/core/components/sql_part/sql_part_editor.ts @@ -4,7 +4,7 @@ import coreModule from 'app/core/core_module'; var template = ` +
+
+ + + + +
+ +
+
+
+ +
+
-
-
- - - - -
- -
-
-
- -
-