├── .gitignore ├── Gruntfile.js ├── LICENSE ├── NOTICE ├── README.md ├── dist ├── LICENSE ├── NOTICE ├── datasource.d.ts ├── datasource.js ├── datasource.js.map ├── datasource.ts ├── module.d.ts ├── module.js ├── module.js.map ├── module.ts ├── partials │ ├── annotations.editor.html │ ├── config.html │ ├── query.editor.html │ ├── query.options.html │ └── query_part.html ├── plugin.json ├── query_builder.d.ts ├── query_builder.js ├── query_ctrl.d.ts ├── query_ctrl.js ├── query_ctrl.js.map ├── query_ctrl.ts ├── query_part.d.ts ├── query_part.js ├── query_part.js.map ├── query_part.ts ├── query_part_editor.js ├── query_part_funcs.d.ts ├── query_part_funcs.js ├── query_part_funcs.js.map ├── query_part_funcs.ts ├── response_parser.d.ts ├── response_parser.js ├── response_parser.js.map ├── response_parser.ts ├── sql_query.d.ts ├── sql_query.js ├── sql_query.js.map ├── sql_query.ts ├── sql_series.d.ts └── sql_series.js ├── package.json ├── plugin.json └── src └── public ├── app ├── core │ └── utils │ │ └── datemath.d.ts ├── features │ └── panel │ │ └── query_ctrl.d.ts ├── headers │ ├── common.d.ts │ └── es6-shim │ │ └── es6-shim.d.ts └── plugins │ └── sdk.d.ts ├── datasource.ts ├── module.ts ├── partials ├── annotations.editor.html ├── config.html ├── query.editor.html ├── query.options.html └── query_part.html ├── query_builder.d.ts ├── query_builder.js ├── query_ctrl.ts ├── query_part.ts ├── query_part_editor.js ├── query_part_funcs.ts ├── response_parser.ts ├── sql_query.ts ├── sql_series.d.ts ├── sql_series.js └── vendor └── npm └── rxjs ├── Observable.d.ts ├── Observer.d.ts ├── Operator.d.ts ├── Scheduler.d.ts ├── Subject.d.ts ├── Subscriber.d.ts ├── Subscription.d.ts ├── observable ├── ErrorObservable.d.ts └── IfObservable.d.ts └── scheduler └── Action.d.ts /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | src_gen 3 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | require('load-grunt-tasks')(grunt); 3 | 4 | grunt.loadNpmTasks('grunt-contrib-clean'); 5 | grunt.loadNpmTasks('grunt-typescript'); 6 | 7 | grunt.initConfig({ 8 | clean: ['src_gen', 'dist'], 9 | 10 | watch: { 11 | rebuild_all: { 12 | files: ['src/**/*'], 13 | tasks: ['default'], 14 | options: {spawn: false} 15 | } 16 | }, 17 | 18 | copy: { 19 | src_js: { 20 | expand: true, 21 | cwd: 'src', 22 | src: ['**/*.js', '**/*.ts', '**/*.d.ts'], 23 | dest: 'src_gen/' 24 | }, 25 | 26 | dist_js: { 27 | expand: true, 28 | flatten: true, 29 | cwd: 'src_gen/public/', 30 | src: ['*.js', '*.ts', '*.d.ts', '*.js.map'], 31 | dest: 'dist/' 32 | }, 33 | dist_html: { 34 | expand: true, 35 | flatten: true, 36 | cwd: 'src/public/partials', 37 | src: ['*.html'], 38 | dest: 'dist/partials/' 39 | }, 40 | dist_statics: { 41 | expand: true, 42 | src: ['plugin.json', 'LICENSE', 'NOTICE'], 43 | dest: 'dist/' 44 | } 45 | }, 46 | 47 | typescript: { 48 | build: { 49 | src: ['src_gen/**/*.ts', '!**/*.d.ts'], 50 | out: 'dist', 51 | options: { 52 | module: 'system', 53 | target: 'es5', 54 | declaration: true, 55 | emitDecoratorMetadata: true, 56 | experimentalDecorators: true, 57 | sourceMap: true, 58 | noImplicitAny: false, 59 | } 60 | } 61 | } 62 | }); 63 | 64 | grunt.registerTask('default', [ 65 | 'clean', 66 | 'copy:src_js', 67 | 'typescript:build', 68 | 'copy:dist_js', 69 | 'copy:dist_html', 70 | 'copy:dist_statics' 71 | ]); 72 | }; 73 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016 SRA OSS, Inc. Japan 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you 4 | may not use this file except in compliance with the License. You may 5 | obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | implied. See the License for the specific language governing 13 | permissions and limitations under the License. 14 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | This plugin is based on InfluxDB datasource in Grafana 2 | 3 | Copyright 2014-2016 Torkel Ödegaard, Raintank Inc. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); You 6 | may not use this file except in compliance with the License. You 7 | may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # grafana-sqldb-datasource 2 | 3 | SQL DB database is the datasource of Grafana 3.0, which provides the support of MySQL, PostgreSQL and ClickHouse as a backend database. 4 | 5 | ## Features 6 | 7 | ### 1. Query Editor 8 | 9 | Forked from InfluxDB plugin, this datasource frontend has been implemented. Therefore, you can issue SQL queries in the same manner as in influxDB. 10 | 11 | (Defining with query editor)
12 | ![Query Editor](https://github.com/sraoss/grafana-sqldb-datasource/wiki/images/query-editor.png) 13 | 14 | Each parts (schema, table, column name and data type) refer to information_schema in RDB. 15 | 16 | ### 2. Text Editor Mode (support raw SQL) 17 | 18 | You can switch to raw query mode by clicking icon. 19 | 20 | (Toggling edit mode)
21 | ![Query Editor](https://github.com/sraoss/grafana-sqldb-datasource/wiki/images/rawquery.png) 22 | 23 | Raw queries are generated referring to inputs of query editor. You can modify them (add JOIN another tables, sub queries in WHERE clause, and so on). 24 | 25 | #### Macros 26 | 27 | If you tries to modify a raw query or define it by yourself without choosing parts in query editor, you can use these macros. 28 | 29 | | macro | detail | 30 | |:------|:-------| 31 | | $timeColumn | This is replaced to "TIME" from [TIME : TYPE] in query editor. | 32 | | $dateColumn | This is replaced to "DATE" from [DATE] in query editor (optional). | 33 | | $timeFilter | This is replaced to "$timeColumn < $from AND $timeColumn > $to". | 34 | | $from | This is replaced to "from" word of the time range for panels, and this is casted as "TYPE" from [TIME : TYPE] in query editor. | 35 | | $to | This is replaced to "to" of the time range for panels, and this is casted as "TYPE" from [TIME : TYPE] in query editor.| 36 | | $unixFrom | This is replaced to "from" of the time range for panels, and this is casted as number of unix timestamp. | 37 | | $unixTo | This is replaced to "to" of the time range for panels, and this is casted as number of unix timestamp. | 38 | | $timeFrom | This is replaced to "from" of the time range for panels, and this is casted as timestamp. | 39 | | $timeTo | This is replaced to "to" of the time range for panels, and this is casted as timestamp. | 40 | | $dateFrom | This is replaced to "from" of the time range for panels, and this is casted as date. | 41 | | $dateTo | This is replaced to "to" of the time range for panels, and this is casted as date. | 42 | 43 | ### 3. Templating 44 | 45 | You can create a template variable in Grafana and have that variable filled with values from any SQL Database metric exploration query. Then, You can use this variable in your SQL Database metric queries. 46 | 47 | (Defining a template)
48 | ![Template Editor](https://github.com/sraoss/grafana-sqldb-datasource/wiki/images/template_var.png) 49 | 50 | (Use a template vartiable in query editor)
51 | ![Query Editor](https://github.com/sraoss/grafana-sqldb-datasource/wiki/images/template_tag.png) 52 | 53 | 54 | ### 4. Annotations 55 | 56 | Annotaions is also supported. You can issue SQL queries and add event information above graphes.  57 | 58 | (Defining an annotation)
59 | ![Annotations Editor](https://github.com/sraoss/grafana-sqldb-datasource/wiki/images/annotation.png) 60 | 61 | (Annotations in a graph)
62 | ![Annotations Graph](https://github.com/sraoss/grafana-sqldb-datasource/wiki/images/annotation_graph.png) 63 | 64 | ### 5. Using timestamp and unixtimestamp as a time-serise column 65 | 66 | You can choose a time-series column from the table definition. 67 | 68 | (Choosing the column for time series)
69 | ![time series](https://github.com/sraoss/grafana-sqldb-datasource/wiki/images/time-series.png) 70 | 71 | #### Data types 72 | 73 | The supported data types for time-series are: 74 | 75 | | category | PostgreSQL | MySQL | ClickHouse | 76 | |:---------|:-----------|:-------|:-----------| 77 | | timestamp type | timestamp without time zone
timestamp with time zone | timestamp
datetime | 78 | | number type
(if you use unixtimesamp as a time-serise column) | bigint
integer (int)
float
real
double precision
decimal
numeric | bigint
integer (int)
float
real
double precision
decimal
numeric | DateTime, any numeric type | 79 | | date type | | | Date, any numeric type | 80 | 81 | ## Example 82 | ### Query with variables 83 | ``` 84 | SELECT $unixtimeColumn * 1000 AS time_msec, 85 | avg(cpu_usr) 86 | FROM myschema.dstat 87 | WHERE tag ~* '/^$host_t$/' AND 88 | $timeFilter 89 | GROUP BY $unixtimeColumn 90 | ORDER BY $unixtimeColumn 91 | ``` 92 | 93 | ### Actual query in PostgreSQL 94 | #### timestamp type 95 | ``` 96 | SELECT round(extract(epoch from coltime::timestamptz) / 1200) * 1200 * 1000 AS time_msec, 97 | avg(cpu_usr) 98 | FROM myschema.dstat 99 | WHERE tag ~* '/^webserver123$/' AND 100 | coltime > (now() - '7d'::interval) 101 | GROUP BY round(extract(epoch from coltime::timestamptz) / 1200) * 1200 102 | ORDER BY round(extract(epoch from coltime::timestamptz) / 1200) * 1200 103 | ``` 104 | 105 | #### number type 106 | ``` 107 | SELECT round(coltime / 1200) * 1200 * 1000 AS time_msec, 108 | avg(cpu_usr) 109 | FROM myschema.dstat 110 | WHERE tag ~* '/^webserver123$/' AND 111 | coltime > extract(epoch from (now() - '7d'::interval)::timestamptz) 112 | GROUP BY round(coltime / 1200) * 1200 113 | ORDER BY round(coltime / 1200) * 1200 114 | ``` 115 | 116 | ### Actual query in MySQL 117 | #### timestamp type 118 | ``` 119 | SELECT (UNIX_TIMESTAMP(coltime) DIV 1200) * 1200 * 1000 AS time_msec, 120 | avg(cpu_usr) 121 | FROM myschema.dstat 122 | WHERE tag REGEXP '^webserver123$' AND 123 | coltime > DATE_SUB(CURDATE(), INTERVAL 7 DAY) 124 | GROUP BY (UNIX_TIMESTAMP(coltime) DIV 1200) * 1200 125 | ORDER BY (UNIX_TIMESTAMP(coltime) DIV 1200) * 1200; 126 | ``` 127 | 128 | #### number type 129 | ``` 130 | SELECT (coltime DIV 1200) * 1200 * 1000 AS time_msec, 131 | avg(cpu_usr) 132 | FROM myschema.dstat 133 | WHERE tag REGEXP '^webserver123$' AND 134 | coltime > UNIX_TIMESTAMP(DATE_SUB(CURDATE(), INTERVAL 7 DAY)) 135 | GROUP BY (coltime DIV 1200) * 1200 136 | ORDER BY (coltime DIV 1200) * 1200 137 | ``` 138 | 139 | ## Tested versions of RDBMS 140 | 141 | * PostgreSQL 142 | 143 | | 9.5 | 9.4 | 9.3 | 9.2 | 9.1 | 144 | |:----|:----|:----|:----|:----| 145 | | OK | OK | (not yet) | OK | (not yet) | 146 | 147 | * MySQL 148 | 149 | | 5.7 | 5.6 | 5.5 | 150 | |:----|:----|:----| 151 | | (not yet) | (not yet) | OK | 152 | 153 | * ClickHouse 154 | 155 | | 1.1.54164 | 156 | |:----------| 157 | | OK | 158 | 159 | ## References 160 | 161 | * [Generic SQL Datasource [WIP] #3964](https://github.com/grafana/grafana/pull/3964) 162 | -------------------------------------------------------------------------------- /dist/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016 SRA OSS, Inc. Japan 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you 4 | may not use this file except in compliance with the License. You may 5 | obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | implied. See the License for the specific language governing 13 | permissions and limitations under the License. 14 | -------------------------------------------------------------------------------- /dist/NOTICE: -------------------------------------------------------------------------------- 1 | This plugin is based on InfluxDB datasource in Grafana 2 | 3 | Copyright 2014-2016 Torkel Ödegaard, Raintank Inc. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); You 6 | may not use this file except in compliance with the License. You 7 | may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | -------------------------------------------------------------------------------- /dist/datasource.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | export default class SqlDatasource { 3 | private $q; 4 | private backendSrv; 5 | private templateSrv; 6 | type: string; 7 | username: string; 8 | password: string; 9 | name: string; 10 | database: any; 11 | interval: any; 12 | supportAnnotations: boolean; 13 | supportMetrics: boolean; 14 | responseParser: any; 15 | url: string; 16 | dbms: string; 17 | queryBuilder: any; 18 | /** @ngInject */ 19 | constructor(instanceSettings: any, $q: any, backendSrv: any, templateSrv: any); 20 | getTagKeys(): any; 21 | getTagValues(options: any): any; 22 | query(options: any): any; 23 | annotationQuery(options: any): any; 24 | metricFindQuery(query: any): any; 25 | _seriesQuery(query: any): any; 26 | serializeParams(params: any): any; 27 | testDatasource(): any; 28 | _sqlRequest(method: any, url: any, data: any): any; 29 | _replaceQueryVars(query: any, options: any, target: any): any; 30 | _getTimeFilter(isToNow: any): string; 31 | _getDateFilter(isToNow: any): string; 32 | _getSubTimestamp(date: any, toDataType: any, roundUp: any): any; 33 | _getRoundUnixTime(target: any): string; 34 | _num2Ts(str: any, toDataType: any): any; 35 | _ts2Num(str: any, toDataType: any): any; 36 | _getIntervalNum(str: any): any; 37 | _abstractDataType(datatype: any): any; 38 | _pgShortTs(str: any): any; 39 | } 40 | -------------------------------------------------------------------------------- /dist/module.d.ts: -------------------------------------------------------------------------------- 1 | import SqlDatasource from './datasource'; 2 | import { SqlQueryCtrl } from './query_ctrl'; 3 | declare class SqlConfigCtrl { 4 | static templateUrl: string; 5 | } 6 | declare class SqlQueryOptionsCtrl { 7 | static templateUrl: string; 8 | } 9 | declare class SqlAnnotationsQueryCtrl { 10 | static templateUrl: string; 11 | } 12 | export { SqlDatasource as Datasource, SqlQueryCtrl as QueryCtrl, SqlConfigCtrl as ConfigCtrl, SqlQueryOptionsCtrl as QueryOptionsCtrl, SqlAnnotationsQueryCtrl as AnnotationsQueryCtrl }; 13 | -------------------------------------------------------------------------------- /dist/module.js: -------------------------------------------------------------------------------- 1 | System.register(['./datasource', './query_ctrl'], function(exports_1) { 2 | var datasource_1, query_ctrl_1; 3 | var SqlConfigCtrl, SqlQueryOptionsCtrl, SqlAnnotationsQueryCtrl; 4 | return { 5 | setters:[ 6 | function (datasource_1_1) { 7 | datasource_1 = datasource_1_1; 8 | }, 9 | function (query_ctrl_1_1) { 10 | query_ctrl_1 = query_ctrl_1_1; 11 | }], 12 | execute: function() { 13 | SqlConfigCtrl = (function () { 14 | function SqlConfigCtrl() { 15 | } 16 | SqlConfigCtrl.templateUrl = 'partials/config.html'; 17 | return SqlConfigCtrl; 18 | })(); 19 | SqlQueryOptionsCtrl = (function () { 20 | function SqlQueryOptionsCtrl() { 21 | } 22 | SqlQueryOptionsCtrl.templateUrl = 'partials/query.options.html'; 23 | return SqlQueryOptionsCtrl; 24 | })(); 25 | SqlAnnotationsQueryCtrl = (function () { 26 | function SqlAnnotationsQueryCtrl() { 27 | } 28 | SqlAnnotationsQueryCtrl.templateUrl = 'partials/annotations.editor.html'; 29 | return SqlAnnotationsQueryCtrl; 30 | })(); 31 | exports_1("Datasource", datasource_1.default); 32 | exports_1("QueryCtrl", query_ctrl_1.SqlQueryCtrl); 33 | exports_1("ConfigCtrl", SqlConfigCtrl); 34 | exports_1("QueryOptionsCtrl", SqlQueryOptionsCtrl); 35 | exports_1("AnnotationsQueryCtrl", SqlAnnotationsQueryCtrl); 36 | } 37 | } 38 | }); 39 | //# sourceMappingURL=module.js.map -------------------------------------------------------------------------------- /dist/module.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"module.js","sourceRoot":"","sources":["module.ts"],"names":["SqlConfigCtrl","SqlConfigCtrl.constructor","SqlQueryOptionsCtrl","SqlQueryOptionsCtrl.constructor","SqlAnnotationsQueryCtrl","SqlAnnotationsQueryCtrl.constructor"],"mappings":";;;;;;;;;;;;YAGA;gBAAAA;gBAEAC,CAACA;gBADQD,yBAAWA,GAAGA,sBAAsBA,CAACA;gBAC9CA,oBAACA;YAADA,CAACA,AAFD,IAEC;YAED;gBAAAE;gBAEAC,CAACA;gBADQD,+BAAWA,GAAGA,6BAA6BA,CAACA;gBACrDA,0BAACA;YAADA,CAACA,AAFD,IAEC;YAED;gBAAAE;gBAEAC,CAACA;gBADQD,mCAAWA,GAAGA,kCAAkCA,CAACA;gBAC1DA,8BAACA;YAADA,CAACA,AAFD,IAEC;YAGkB,6CAAU;YACX,iDAAS;YACR,sCAAU;YACJ,kDAAgB;YACZ,0DAAoB"} -------------------------------------------------------------------------------- /dist/module.ts: -------------------------------------------------------------------------------- 1 | import SqlDatasource from './datasource'; 2 | import {SqlQueryCtrl} from './query_ctrl'; 3 | 4 | class SqlConfigCtrl { 5 | static templateUrl = 'partials/config.html'; 6 | } 7 | 8 | class SqlQueryOptionsCtrl { 9 | static templateUrl = 'partials/query.options.html'; 10 | } 11 | 12 | class SqlAnnotationsQueryCtrl { 13 | static templateUrl = 'partials/annotations.editor.html'; 14 | } 15 | 16 | export { 17 | SqlDatasource as Datasource, 18 | SqlQueryCtrl as QueryCtrl, 19 | SqlConfigCtrl as ConfigCtrl, 20 | SqlQueryOptionsCtrl as QueryOptionsCtrl, 21 | SqlAnnotationsQueryCtrl as AnnotationsQueryCtrl, 22 | }; 23 | 24 | 25 | -------------------------------------------------------------------------------- /dist/partials/annotations.editor.html: -------------------------------------------------------------------------------- 1 | 2 |
Query 3 | You can use some variables to specify the condition about span: 4 | (ex1) $timeColumn > $timeFrom AND $timeColumn < $timeTo 5 | (ex2) $timeColumn > $unixFrom AND $timeColumn < $unixTo 6 | (ex3) $dateColumn > $dateFrom AND $dateColumn < $dateTo 7 | (ex4) $timeFilter 8 | 9 |
10 |
11 |
12 | 13 |
14 |
15 | 16 |
Query parts
17 |
18 |
19 |
20 | Schema 21 | 22 |
23 | 24 |
25 | Table 26 | 27 |
28 |
29 | 30 |
31 |
32 | Time 33 | 34 |
35 | 36 |
37 | Type of Time 38 | 39 |
40 |
41 | 42 |
43 |
44 | Tags 45 | 46 |
47 | 48 |
49 | Title 50 | 51 |
52 | 53 |
54 | Text 55 | 56 |
57 |
58 |
59 | -------------------------------------------------------------------------------- /dist/partials/config.html: -------------------------------------------------------------------------------- 1 |

SqlDB Details

2 | 3 |
4 |
5 |
6 | DBMS 7 | 8 |
9 |
10 | SSL 11 | 12 | 13 |
14 |
15 | 16 | 17 | 18 | 19 |
20 |
21 |
22 | Host 23 | 24 |
25 |
26 | Port 27 | 28 |
29 |
30 | 31 |
32 |
33 | Database 34 | 35 |
36 |
37 | 38 |
39 |
40 | User 41 | 42 |
43 |
44 | Password 45 | 46 |
47 |
48 |
49 |
50 | 51 |
52 |
53 | Default group by time 54 | 56 | 57 |
58 |
59 | -------------------------------------------------------------------------------- /dist/partials/query.editor.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |
6 | 7 |
8 | 9 |
10 |
11 | 12 | 13 | 14 | 15 |
16 | 17 |
18 | 19 |
20 | 21 |
22 | 23 |
24 | 25 |
26 |
27 |
28 |
29 | 30 |
31 |
32 | 33 | 34 | 35 |
36 | 37 |
38 | 39 |
40 | 41 |
42 | 43 |
44 | 45 |
46 |
47 |
48 |
49 | 50 |
51 |
52 | 55 |
56 | 57 |
58 | 64 | 65 |
66 | 67 |
68 | 72 |
73 | 74 |
75 |
76 |
77 |
78 | 79 |
80 |
81 | 84 | 85 | 92 | 93 |
94 | 95 |
96 | 100 |
101 | 102 |
103 |
104 |
105 |
106 | 107 |
108 | 109 |
110 |
111 | 112 | 113 |
114 |
115 | 116 |
117 | 118 |
119 |
120 |
121 |
122 |
123 |
124 | 125 |
126 | -------------------------------------------------------------------------------- /dist/partials/query.options.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | 6 | Group by 7 | 9 | 10 |
11 |
12 |
13 | 36 |
37 |
38 |
39 | 40 |
41 |
42 | 43 |
44 |
Alias patterns
45 |
    46 |
  • $t = replaced with table name
  • 47 |
  • $table = replaced with table name
  • 48 |
  • $col = replaced with column name
  • 49 |
  • $alias = replaced with an alias (e.g. $country)
  • 50 |
51 |
52 | 53 |
54 |
Stacking
55 |
    56 |
  • When stacking is enabled it important that points align
  • 57 |
  • If there are missing points for one series it can cause gaps or missing bars
  • 58 |
  • Use the group by time option below your queries and specify for example >10s if your metrics are written every 10 seconds
  • 59 |
  • This will insert zeros for series that are missing tables and will make stacking work properly
  • 60 |
61 |
62 | 63 |
64 |
Group by time
65 |
    66 |
  • Group by time is important, otherwise the query could return many thousands of datapoints that will slow down Grafana
  • 67 |
  • Leave the group by time field empty for each query and it will be calculated based on time range and pixel width of the graph
  • 68 |
  • The low limit can only be set in the group by time option below your queries
  • 69 |
  • You set a low limit by adding a greater sign before the interval
  • 70 |
  • Example: >60s if you write metrics to RDB every 60 seconds
  • 71 |
72 |
Variables
73 |
    74 |
  • $timeColumn > $timeFrom AND $timeColumn < $timeTo
  • 75 |
  • $timeColumn > $unixFrom AND $timeColumn < $unixTo
  • 76 |
  • $dateColumn > $dateFrom AND $dateColumn < $dateTo
  • 77 |
  • $timeFilter - expands to time and date filter.
  • 78 |
  • $filter - expands to Ad-Hoc filters or '1'.
  • 79 | 80 |
81 |
82 |
83 | 84 | 85 | -------------------------------------------------------------------------------- /dist/partials/query_part.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | {{part.def.type}}() 6 | -------------------------------------------------------------------------------- /dist/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "datasource", 3 | "name": "SqlDB", 4 | "id": "sqldb", 5 | 6 | "defaultMatchFormat": "regex values", 7 | "metrics": true, 8 | "annotations": true, 9 | 10 | "info": { 11 | "description": "SqlDB data source which provides support for MySQL, PostgreSQL and ClickHouse.", 12 | "author": { 13 | "name": "SRA OSS, Inc. Japan", 14 | "url": "http://www.sraoss.co.jp/" 15 | }, 16 | "keywords": ["sqldb", "datasource"], 17 | "links": [ 18 | {"name": "Project site", "url": "https://github.com/sraoss/grafana-sqldb-datasource"}, 19 | {"name": "Apache 2.0", "url": ""} 20 | ], 21 | "screenshots": [ 22 | ], 23 | "version": "1.2", 24 | "updated": "2017-03-12" 25 | }, 26 | 27 | "dependencies": { 28 | "grafanaVersion": "3.x.x", 29 | "plugins": [ ] 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /dist/query_builder.d.ts: -------------------------------------------------------------------------------- 1 | declare var test: any; 2 | export default test; 3 | -------------------------------------------------------------------------------- /dist/query_builder.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'lodash' 3 | ], 4 | function (_) { 5 | 'use strict'; 6 | 7 | function SqlQueryBuilder(target, dbms) { 8 | this.target = target; 9 | this.dbms = dbms; 10 | } 11 | 12 | function renderTagCondition (tag, index) { 13 | var str = ""; 14 | var operator = tag.operator; 15 | var value = tag.value; 16 | if (index > 0) { 17 | str = (tag.condition || 'AND') + ' '; 18 | } 19 | 20 | // quote value unless regex or number 21 | if (isNaN(+value)) { 22 | value = "'" + value + "'"; 23 | } 24 | 25 | return str + '"' + tag.key + '" ' + operator + ' ' + value; 26 | } 27 | 28 | var p = SqlQueryBuilder.prototype; 29 | 30 | p.build = function() { 31 | return this.target.rawQuery ? this._modifyRawQuery() : this._buildQuery(); 32 | }; 33 | 34 | p.buildExploreQuery = function(type, withKey) { 35 | var query; 36 | var table; 37 | 38 | var templates = { 39 | base: { 40 | defaults: { 41 | colType: 'concat(column_name, \' : \', data_type)', 42 | numericTypes: "'numeric', 'decimal', 'bigint', 'integer', " + 43 | "'double', 'double precision', 'float'", 44 | exceptSchemaArr: "'information_schema', 'pg_catalog'" 45 | }, 46 | SCHEMA: 'SELECT schema_name FROM information_schema.schemata ORDER BY schema_name', 47 | TABLES: 'SELECT column_name FROM information_schema.tables ' + 48 | 'WHERE table_schema = \'${schema}\'' + 49 | 'ORDER BY table_name', 50 | FIELDS: 'SELECT ${colType} FROM information_schema.columns ' + 51 | 'WHERE table_schema = \'${schema}\' AND table_name = \'${table}\' ' + 52 | 'ORDER BY ordinal_position', 53 | TAG_KEYS: 'SELECT column_name FROM information_schema.columns ' + 54 | 'WHERE table_schema = \'${schema}\' AND table_name = \'${table}\' ' + 55 | 'ORDER BY ordinal_position', 56 | TAG_VALUES: 'SELECT distinct(${key}) FROM "${schema}"."${table}" ORDER BY ${key}', 57 | SET_DEFAULT: 'SELECT table_schema, table_name, ${colType} FROM information_schema.columns ' + 58 | 'WHERE table_schema NOT IN (${exceptSchemaArr}) ' + 59 | 'ORDER BY (data_type LIKE \'timestamp%\') desc, ' + 60 | '(data_type = \'datetime\') desc, ' + 61 | 'table_schema, table_name, ' + 62 | '(data_type IN (${numericTypes})) desc, ordinal_position LIMIT 1' 63 | }, 64 | 65 | postgres: { 66 | defaults: {colType: 'column_name || \' : \' || data_type'} 67 | }, 68 | 69 | clickhouse: { 70 | defaults: {schema: 'default', colType: 'name || \' : \' || type', table: '%', schema: '%'}, 71 | SCHEMA: 'SELECT name FROM system.databases ORDER BY name', 72 | TABLES: 'SELECT distinct(name) FROM system.tables WHERE database LIKE \'${schema}\' ORDER BY name', 73 | FIELDS: 'SELECT ${colType} as col FROM system.columns ' + 74 | 'WHERE database LIKE \'${schema}\' AND table LIKE \'${table}\' ORDER BY col', 75 | TAG_KEYS: 'SELECT distinct(name) FROM system.columns ' + 76 | 'WHERE database LIKE \'${schema}\' AND table LIKE \'${table}\' ORDER BY name', 77 | TAG_VALUES: 'SELECT 1', 78 | SET_DEFAULT: 'SELECT 1' 79 | } 80 | } 81 | 82 | /* Specialisations of base template */ 83 | templates.postgres = _.merge({}, templates.base, templates.postgres); 84 | 85 | var template = templates[this.dbms]; 86 | if (!template) { 87 | template = templates.base; 88 | } 89 | 90 | var vars = _.defaults(this.target, template.defaults); 91 | vars.key = withKey; 92 | switch (type) { 93 | case 'TAG_KEYS': 94 | case 'TAG_VALUES': 95 | case 'TABLES': 96 | case 'FIELDS': 97 | case 'SCHEMA': 98 | case 'SET_DEFAULT': 99 | return _.template(template[type])(vars); 100 | default: 101 | break; 102 | } 103 | 104 | if (table) { 105 | if (!table.match('^/.*/') && !table.match(/^merge\(.*\)/)) { 106 | table = '"' + table+ '"'; 107 | } 108 | query += ' FROM ' + table; 109 | } 110 | 111 | if (this.target.filters && this.target.filters.length > 0) { 112 | var whereConditions = _.reduce(this.target.filters, function(memo, tag) { 113 | // do not add a condition for the key we want to explore for 114 | if (tag.key === withKey) { 115 | return memo; 116 | } 117 | memo.push(renderTagCondition(tag, memo.length)); 118 | return memo; 119 | }, []); 120 | 121 | if (whereConditions.length > 0) { 122 | query += ' WHERE ' + whereConditions.join(' '); 123 | } 124 | } 125 | 126 | return query; 127 | }; 128 | 129 | return SqlQueryBuilder; 130 | }); 131 | -------------------------------------------------------------------------------- /dist/query_ctrl.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import SqlQuery from './sql_query'; 3 | import { QueryCtrl } from 'app/plugins/sdk'; 4 | export declare class SqlQueryCtrl extends QueryCtrl { 5 | private templateSrv; 6 | private $q; 7 | private uiSegmentSrv; 8 | static templateUrl: string; 9 | queryModel: SqlQuery; 10 | queryBuilder: any; 11 | resultFormats: any[]; 12 | schemaSegment: any; 13 | timeColDataTypeSegment: any; 14 | dateColDataTypeSegment: any; 15 | tagSegments: any[]; 16 | selectMenu: any; 17 | groupByMenu: any; 18 | tableSegment: any; 19 | removeTagFilterSegment: any; 20 | matchOperators: any; 21 | panel: any; 22 | datasource: any; 23 | target: any; 24 | /** @ngInject **/ 25 | constructor($scope: any, $injector: any, templateSrv: any, $q: any, uiSegmentSrv: any); 26 | buildSelectMenu(): any; 27 | getGroupByOptions(part: any): any; 28 | addGroupByPart(cat: any, subitem: any): void; 29 | removeGroupByPart(part: any, index: any): void; 30 | addSelectPart(selectParts: any, cat: any, subitem: any): void; 31 | removeSelectPart(selectParts: any, part: any): void; 32 | selectPartUpdated(): void; 33 | fixTagSegments(): void; 34 | tableChanged(): void; 35 | getSchemaSegments(): any; 36 | schemaChanged(): void; 37 | getTimeColDataTypeSegments(): any; 38 | timeColDataTypeChanged(): void; 39 | dateColDataTypeChanged(): void; 40 | toggleEditorMode(): void; 41 | getTableSegments(): any; 42 | getPartOptions(part: any): any; 43 | handleQueryError(err: any): any[]; 44 | transformToSegments(addTemplateVars: any): (results: any) => any; 45 | getTagsOrValues(segment: any, index: any): any; 46 | getFieldSegments(): any; 47 | tagSegmentUpdated(segment: any, index: any): void; 48 | rebuildTargetTagConditions(): void; 49 | getTagValueOperator(tagValue: any, tagOperator: any): any; 50 | getCollapsedText(): any; 51 | } 52 | -------------------------------------------------------------------------------- /dist/query_ctrl.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import './query_part_editor'; 4 | 5 | import angular from 'angular'; 6 | import _ from 'lodash'; 7 | import SqlQueryBuilder from './query_builder'; 8 | import SqlQuery from './sql_query'; 9 | import queryPart from './query_part'; 10 | import {QueryCtrl} from 'app/plugins/sdk'; 11 | 12 | export class SqlQueryCtrl extends QueryCtrl { 13 | static templateUrl = 'partials/query.editor.html'; 14 | 15 | queryModel: SqlQuery; 16 | queryBuilder: any; 17 | resultFormats: any[]; 18 | schemaSegment: any; 19 | timeColDataTypeSegment: any; 20 | dateColDataTypeSegment: any; 21 | tagSegments: any[]; 22 | selectMenu: any; 23 | groupByMenu: any; 24 | tableSegment: any; 25 | removeTagFilterSegment: any; 26 | matchOperators: any; 27 | panel: any; 28 | datasource: any; 29 | target: any; 30 | 31 | /** @ngInject **/ 32 | constructor($scope, $injector, private templateSrv, private $q, private uiSegmentSrv) { 33 | super($scope, $injector); 34 | 35 | this.target = this.target; 36 | 37 | this.matchOperators = queryPart.getMatchOperators(this.datasource.dbms); 38 | 39 | this.queryModel = new SqlQuery(this.target, templateSrv, this.panel.scopedVars); 40 | this.queryModel.dbms = this.datasource.dbms; 41 | this.queryBuilder = new SqlQueryBuilder( 42 | this.target, this.datasource.dbms, { matchOperators: this.matchOperators } 43 | ); 44 | 45 | this.resultFormats = [ 46 | {text: 'Time series', value: 'time_series'}, 47 | {text: 'Table', value: 'table'}, 48 | {text: 'Docs', value: 'docs'}, 49 | ]; 50 | 51 | this.schemaSegment = uiSegmentSrv.newSegment( 52 | this.target.schema || {fake: true, value: '-- schema --'} 53 | ); 54 | 55 | this.tableSegment = uiSegmentSrv.newSegment( 56 | this.target.table || {fake: true, value: '-- table --'} 57 | ); 58 | 59 | this.timeColDataTypeSegment = uiSegmentSrv.newSegment( 60 | this.target.timeColDataType || {fake: true, value: '-- time : type --'} 61 | ); 62 | 63 | this.dateColDataTypeSegment = uiSegmentSrv.newSegment( 64 | this.target.dateColDataType || {value: 'date : Date'} 65 | ); 66 | 67 | this.tagSegments = []; 68 | for (let tag of this.target.tags) { 69 | if (!tag.operator) { 70 | if (/^\/.*\/$/.test(tag.value)) { 71 | tag.operator = this.matchOperators.match; 72 | } else { 73 | tag.operator = '='; 74 | } 75 | } 76 | 77 | if (tag.condition) { 78 | this.tagSegments.push(uiSegmentSrv.newCondition(tag.condition)); 79 | } 80 | 81 | this.tagSegments.push(uiSegmentSrv.newKey(tag.key)); 82 | this.tagSegments.push(uiSegmentSrv.newOperator(tag.operator)); 83 | this.tagSegments.push(uiSegmentSrv.newKeyValue(tag.value)); 84 | } 85 | 86 | this.fixTagSegments(); 87 | this.selectMenu = this.buildSelectMenu(); 88 | this.groupByMenu = _.filter(this.selectMenu, e => { 89 | return e.text != 'Aggregations' && e.text != 'Selectors'; 90 | }); 91 | this.removeTagFilterSegment = uiSegmentSrv.newSegment({ 92 | fake: true, value: '-- remove tag filter --' 93 | }); 94 | } 95 | 96 | /* 97 | setDefault() { 98 | var query = this.queryBuilder.buildExploreQuery('SET_DEFAULT'); 99 | this.datasource._seriesQuery(query).then(data => { 100 | if (!data.results[0].series[0].values) { return; } 101 | var result = data.results[0].series[0].values[0]; 102 | this.target.schema = result[0]; 103 | this.target.table = result[1]; 104 | this.target.timeColDataType = result[2]; 105 | 106 | this.schemaSegment = this.uiSegmentSrv.newSegment(this.target.schema); 107 | this.tableSegment = this.uiSegmentSrv.newSegment(this.target.table); 108 | this.timeColDataTypeSegment = this.uiSegmentSrv.newSegment(this.target.timeColDataType); 109 | }); 110 | } 111 | */ 112 | 113 | buildSelectMenu() { 114 | var dbms = this.queryModel.dbms; 115 | var categories = queryPart.getCategories(); 116 | return _.reduce(categories, function(memo, cat, key) { 117 | var menu = { 118 | text: key, 119 | submenu: cat 120 | .filter(item => { return !item.dbms || item.dbms == dbms; }) 121 | .map(item => { return {text: item.type, value: item.type}; }) 122 | }; 123 | if (menu.submenu.length > 0) { 124 | memo.push(menu); 125 | } 126 | return memo; 127 | }, []); 128 | } 129 | 130 | getGroupByOptions(part) { 131 | var query = this.queryBuilder.buildExploreQuery('TAG_KEYS'); 132 | 133 | return this.datasource.metricFindQuery(query).then(tags => { 134 | var options = []; 135 | if (!this.queryModel.hasGroupByTime()) { 136 | options.push(this.uiSegmentSrv.newSegment({value: 'time($interval)'})); 137 | } 138 | for (let tag of tags) { 139 | options.push(this.uiSegmentSrv.newSegment({value: 'tag(' + tag.text + ')'})); 140 | } 141 | return options; 142 | }).catch(this.handleQueryError.bind(this)); 143 | 144 | } 145 | 146 | addGroupByPart(cat, subitem) { 147 | this.queryModel.addGroupBy(subitem.value); 148 | this.panelCtrl.refresh(); 149 | } 150 | 151 | removeGroupByPart(part, index) { 152 | this.queryModel.removeGroupByPart(part, index); 153 | this.panelCtrl.refresh(); 154 | } 155 | 156 | addSelectPart(selectParts, cat, subitem) { 157 | this.queryModel.addSelectPart(selectParts, subitem.value); 158 | this.panelCtrl.refresh(); 159 | } 160 | 161 | removeSelectPart(selectParts, part) { 162 | this.queryModel.removeSelectPart(selectParts, part); 163 | this.panelCtrl.refresh(); 164 | } 165 | 166 | selectPartUpdated() { 167 | this.panelCtrl.refresh(); 168 | } 169 | 170 | fixTagSegments() { 171 | var count = this.tagSegments.length; 172 | var lastSegment = this.tagSegments[Math.max(count-1, 0)]; 173 | 174 | if (!lastSegment || lastSegment.type !== 'plus-button') { 175 | this.tagSegments.push(this.uiSegmentSrv.newPlusButton()); 176 | } 177 | } 178 | 179 | tableChanged() { 180 | this.target.table = this.tableSegment.value; 181 | this.panelCtrl.refresh(); 182 | } 183 | 184 | getSchemaSegments() { 185 | var schemasQuery = this.queryBuilder.buildExploreQuery('SCHEMA'); 186 | return this.datasource.metricFindQuery(schemasQuery) 187 | .then(this.transformToSegments(false)) 188 | .catch(this.handleQueryError.bind(this)); 189 | } 190 | 191 | schemaChanged() { 192 | this.target.schema = this.schemaSegment.value; 193 | this.panelCtrl.refresh(); 194 | } 195 | 196 | getTimeColDataTypeSegments() { 197 | var timeColQuery = this.queryBuilder.buildExploreQuery('FIELDS'); 198 | return this.datasource.metricFindQuery(timeColQuery) 199 | .then(this.transformToSegments(false)) 200 | .catch(this.handleQueryError.bind(this)); 201 | } 202 | 203 | timeColDataTypeChanged() { 204 | this.target.timeColDataType = this.timeColDataTypeSegment.value; 205 | this.panelCtrl.refresh(); 206 | } 207 | 208 | dateColDataTypeChanged() { 209 | this.target.dateColDataType = this.dateColDataTypeSegment.value; 210 | this.panelCtrl.refresh(); 211 | } 212 | 213 | toggleEditorMode() { 214 | try { 215 | this.target.query = this.queryModel.render(false); 216 | } catch (err) { 217 | console.log('query render error'); 218 | } 219 | this.target.rawQuery = !this.target.rawQuery; 220 | } 221 | 222 | getTableSegments() { 223 | var query = this.queryBuilder.buildExploreQuery('TABLES'); 224 | return this.datasource.metricFindQuery(query) 225 | .then(this.transformToSegments(true)) 226 | .catch(this.handleQueryError.bind(this)); 227 | } 228 | 229 | getPartOptions(part) { 230 | var fieldsQuery = this.queryBuilder.buildExploreQuery('TAG_KEYS'); 231 | return this.datasource.metricFindQuery(fieldsQuery) 232 | .then(this.transformToSegments(true)) 233 | .catch(this.handleQueryError.bind(this)); 234 | } 235 | 236 | handleQueryError(err) { 237 | this.error = err.message || 'Failed to issue metric query'; 238 | return []; 239 | } 240 | 241 | transformToSegments(addTemplateVars) { 242 | return (results) => { 243 | var segments = _.map(results, segment => { 244 | return this.uiSegmentSrv.newSegment({ value: segment.text, expandable: segment.expandable }); 245 | }); 246 | 247 | if (addTemplateVars) { 248 | for (let variable of this.templateSrv.variables) { 249 | segments.unshift(this.uiSegmentSrv.newSegment({ 250 | type: 'template', value: '/^$' + variable.name + '$/', expandable: true 251 | })); 252 | segments.unshift(this.uiSegmentSrv.newSegment({ 253 | type: 'template', value: '$' + variable.name, expandable: true 254 | })); 255 | } 256 | } 257 | 258 | return segments; 259 | }; 260 | } 261 | 262 | getTagsOrValues(segment, index) { 263 | if (segment.type === 'condition') { 264 | return this.$q.when([ 265 | this.uiSegmentSrv.newSegment('AND'), this.uiSegmentSrv.newSegment('OR') 266 | ]); 267 | } 268 | if (segment.type === 'operator') { 269 | var nextValue = this.tagSegments[index+1].value; 270 | if (/^\/.*\/$/.test(nextValue)) { 271 | return this.$q.when(this.uiSegmentSrv.newOperators([ 272 | this.matchOperators.match, this.matchOperators.not 273 | ])); 274 | } else { 275 | return this.$q.when(this.uiSegmentSrv.newOperators([ 276 | '=', '<>', '<', '<=', '>', '>=', 'IN', 'NOT IN' 277 | ])); 278 | } 279 | } 280 | 281 | var query, addTemplateVars; 282 | if (segment.type === 'key' || segment.type === 'plus-button') { 283 | query = this.queryBuilder.buildExploreQuery('TAG_KEYS'); 284 | addTemplateVars = false; 285 | } else if (segment.type === 'value') { 286 | query = this.queryBuilder.buildExploreQuery('TAG_VALUES', this.tagSegments[index-2].value); 287 | addTemplateVars = true; 288 | } 289 | 290 | return this.datasource.metricFindQuery(query) 291 | .then(this.transformToSegments(addTemplateVars)) 292 | .then(results => { 293 | if (segment.type === 'key') { 294 | results.splice(0, 0, angular.copy(this.removeTagFilterSegment)); 295 | } 296 | return results; 297 | }) 298 | .catch(this.handleQueryError.bind(this)); 299 | } 300 | 301 | getFieldSegments() { 302 | var fieldsQuery = this.queryBuilder.buildExploreQuery('TAG_KEYS'); 303 | return this.datasource.metricFindQuery(fieldsQuery) 304 | .then(this.transformToSegments(false)) 305 | .catch(this.handleQueryError); 306 | } 307 | 308 | tagSegmentUpdated(segment, index) { 309 | this.tagSegments[index] = segment; 310 | 311 | // handle remove tag condition 312 | if (segment.value === this.removeTagFilterSegment.value) { 313 | this.tagSegments.splice(index, 3); 314 | if (this.tagSegments.length === 0) { 315 | this.tagSegments.push(this.uiSegmentSrv.newPlusButton()); 316 | } else if (this.tagSegments.length > 2) { 317 | this.tagSegments.splice(Math.max(index-1, 0), 1); 318 | if (this.tagSegments[this.tagSegments.length-1].type !== 'plus-button') { 319 | this.tagSegments.push(this.uiSegmentSrv.newPlusButton()); 320 | } 321 | } 322 | } else { 323 | if (segment.type === 'plus-button') { 324 | if (index > 2) { 325 | this.tagSegments.splice(index, 0, this.uiSegmentSrv.newCondition('AND')); 326 | } 327 | this.tagSegments.push(this.uiSegmentSrv.newOperator('=')); 328 | this.tagSegments.push(this.uiSegmentSrv.newFake( 329 | 'select tag value', 'value', 'query-segment-value' 330 | )); 331 | segment.type = 'key'; 332 | segment.cssClass = 'query-segment-key'; 333 | } 334 | 335 | if ((index+1) === this.tagSegments.length) { 336 | this.tagSegments.push(this.uiSegmentSrv.newPlusButton()); 337 | } 338 | } 339 | 340 | this.rebuildTargetTagConditions(); 341 | } 342 | 343 | rebuildTargetTagConditions() { 344 | var tags = []; 345 | var tagIndex = 0; 346 | var tagOperator = ""; 347 | 348 | _.each(this.tagSegments, (segment2, index) => { 349 | if (segment2.type === 'key') { 350 | if (tags.length === 0) { 351 | tags.push({}); 352 | } 353 | tags[tagIndex].key = segment2.value; 354 | } else if (segment2.type === 'value') { 355 | tagOperator = this.getTagValueOperator(segment2.value, tags[tagIndex].operator); 356 | if (tagOperator) { 357 | this.tagSegments[index-1] = this.uiSegmentSrv.newOperator(tagOperator); 358 | tags[tagIndex].operator = tagOperator; 359 | } 360 | tags[tagIndex].value = segment2.value; 361 | } else if (segment2.type === 'condition') { 362 | tags.push({ condition: segment2.value }); 363 | tagIndex += 1; 364 | } else if (segment2.type === 'operator') { 365 | tags[tagIndex].operator = segment2.value; 366 | } 367 | }); 368 | 369 | this.target.tags = tags; 370 | this.panelCtrl.refresh(); 371 | } 372 | 373 | getTagValueOperator(tagValue, tagOperator) { 374 | if (tagOperator !== this.matchOperators.match && 375 | tagOperator !== this.matchOperators.not && 376 | /^\/.*\/$/.test(tagValue)) { 377 | return this.matchOperators.match; 378 | 379 | } else if ((tagOperator === this.matchOperators.match || 380 | tagOperator === this.matchOperators.not) && 381 | /^(?!\/.*\/$)/.test(tagValue)) { 382 | return '='; 383 | } 384 | } 385 | 386 | getCollapsedText() { 387 | return this.queryModel.render(false); 388 | } 389 | } 390 | 391 | -------------------------------------------------------------------------------- /dist/query_part.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | declare var _default: { 3 | create: (part: any) => any; 4 | getCategories: () => { 5 | Aggregations: any[]; 6 | Selectors: any[]; 7 | Math: any[]; 8 | Transform: any[]; 9 | Aliasing: any[]; 10 | Fields: any[]; 11 | }; 12 | getMatchOperators: (dbms: any) => any; 13 | }; 14 | export default _default; 15 | -------------------------------------------------------------------------------- /dist/query_part.js: -------------------------------------------------------------------------------- 1 | /// 2 | System.register(['lodash', './query_part_funcs'], function(exports_1) { 3 | var lodash_1, query_part_funcs_1; 4 | var index, categories, QueryPartDef, partRenderer, partDefault, QueryPart; 5 | function functionRenderer(part, innerExpr) { 6 | var str = part.def.type + '('; 7 | var parameters = lodash_1.default.map(part.params, function (value, index) { 8 | var paramType = part.def.params[index]; 9 | if (paramType.type === 'time') { 10 | if (value === 'auto') { 11 | value = '$interval'; 12 | } 13 | } 14 | if (paramType.quote === 'single') { 15 | return "'" + value + "'"; 16 | } 17 | else if (paramType.quote === 'double') { 18 | return '"' + value + '"'; 19 | } 20 | return value; 21 | }); 22 | if (innerExpr) { 23 | parameters.unshift(innerExpr); 24 | } 25 | return str + parameters.join(', ') + ')'; 26 | } 27 | function parametricFunctionRenderer(part, innerExpr) { 28 | var str = part.def.type; 29 | var parameters = lodash_1.default.map(part.params, function (value, index) { 30 | var paramType = part.def.params[index]; 31 | if (paramType.type === 'time') { 32 | if (value === 'auto') { 33 | value = '$interval'; 34 | } 35 | } 36 | if (paramType.quote === 'single') { 37 | return "'" + value + "'"; 38 | } 39 | else if (paramType.quote === 'double') { 40 | return '"' + value + '"'; 41 | } 42 | return value; 43 | }); 44 | if (innerExpr) { 45 | parameters.unshift(innerExpr); 46 | } 47 | return str + '(' + parameters.pop() + ')(' + parameters.join(', ') + ')'; 48 | } 49 | function aliasRenderer(part, innerExpr) { 50 | return innerExpr + ' AS ' + part.params[0]; 51 | } 52 | function suffixRenderer(part, innerExpr) { 53 | return innerExpr + ' ' + part.params[0]; 54 | } 55 | function identityRenderer(part, innerExpr) { 56 | return part.params[0]; 57 | } 58 | function quotedIdentityRenderer(part, innerExpr) { 59 | return '"' + part.params[0] + '"'; 60 | } 61 | function fieldRenderer(part, innerExpr) { 62 | return part.params[0]; 63 | } 64 | function replaceAggregationAddStrategy(selectParts, partModel) { 65 | // look for existing aggregation 66 | for (var i = 0; i < selectParts.length; i++) { 67 | var part = selectParts[i]; 68 | if (part.def.category === categories.Aggregations) { 69 | selectParts[i] = partModel; 70 | return; 71 | } 72 | if (part.def.category === categories.Selectors) { 73 | selectParts[i] = partModel; 74 | return; 75 | } 76 | } 77 | selectParts.splice(1, 0, partModel); 78 | } 79 | function addMathStrategy(selectParts, partModel) { 80 | var partCount = selectParts.length; 81 | if (partCount > 0) { 82 | // if last is math, replace it 83 | if (selectParts[partCount - 1].def.type === 'math') { 84 | selectParts[partCount - 1] = partModel; 85 | return; 86 | } 87 | // if next to last is math, replace it 88 | if (selectParts[partCount - 2].def.type === 'math') { 89 | selectParts[partCount - 2] = partModel; 90 | return; 91 | } 92 | else if (selectParts[partCount - 1].def.type === 'alias') { 93 | selectParts.splice(partCount - 1, 0, partModel); 94 | return; 95 | } 96 | } 97 | selectParts.push(partModel); 98 | } 99 | function addAliasStrategy(selectParts, partModel) { 100 | var partCount = selectParts.length; 101 | if (partCount > 0) { 102 | // if last is alias, replace it 103 | if (selectParts[partCount - 1].def.type === 'alias') { 104 | selectParts[partCount - 1] = partModel; 105 | return; 106 | } 107 | } 108 | selectParts.push(partModel); 109 | } 110 | function addFieldStrategy(selectParts, partModel, query) { 111 | // copy all parts 112 | var parts = lodash_1.default.map(selectParts, function (part) { 113 | return new QueryPart({ type: part.def.type, params: lodash_1.default.clone(part.params) }); 114 | }); 115 | query.selectModels.push(parts); 116 | } 117 | function addTransformStrategy(selectParts, partModel) { 118 | var partCount = selectParts.length; 119 | if (partCount > 0) { 120 | // if last is alias add it before 121 | if (selectParts[partCount - 1].def.type === 'alias') { 122 | selectParts.splice(partCount - 1, 0, partModel); 123 | return; 124 | } 125 | } 126 | selectParts.push(partModel); 127 | } 128 | return { 129 | setters:[ 130 | function (lodash_1_1) { 131 | lodash_1 = lodash_1_1; 132 | }, 133 | function (query_part_funcs_1_1) { 134 | query_part_funcs_1 = query_part_funcs_1_1; 135 | }], 136 | execute: function() { 137 | index = {}; 138 | categories = { 139 | Aggregations: [], 140 | Selectors: [], 141 | Math: [], 142 | Transform: [], 143 | Aliasing: [], 144 | Fields: [], 145 | }; 146 | QueryPartDef = (function () { 147 | function QueryPartDef(options) { 148 | this.type = options.type; 149 | this.params = options.params; 150 | this.defaultParams = options.defaultParams; 151 | this.renderer = options.renderer; 152 | this.category = options.category; 153 | this.addStrategy = options.addStrategy; 154 | this.dbms = options.dbms; 155 | } 156 | QueryPartDef.register = function (options) { 157 | index[options.type] = new QueryPartDef(options); 158 | options.category.push(index[options.type]); 159 | }; 160 | return QueryPartDef; 161 | })(); 162 | // Defaults 163 | partRenderer = { 164 | functionRenderer: functionRenderer, 165 | parametricFunctionRenderer: parametricFunctionRenderer, 166 | aliasRenderer: aliasRenderer, 167 | suffixRenderer: suffixRenderer, 168 | identityRenderer: identityRenderer, 169 | quotedIdentityRenderer: quotedIdentityRenderer, 170 | fieldRenderer: fieldRenderer 171 | }; 172 | partDefault = { 173 | Aggregations: { 174 | addStrategy: replaceAggregationAddStrategy, 175 | category: categories.Aggregations, 176 | params: [], 177 | defaultParams: [], 178 | renderer: functionRenderer, 179 | }, 180 | Selectors: { 181 | addStrategy: replaceAggregationAddStrategy, 182 | category: categories.Selectors, 183 | params: [], 184 | defaultParams: [], 185 | renderer: functionRenderer, 186 | }, 187 | Transform: { 188 | addStrategy: addTransformStrategy, 189 | renderer: functionRenderer, 190 | params: [], 191 | defaultParams: [], 192 | category: categories.Transform, 193 | }, 194 | }; 195 | QueryPartDef.register({ 196 | type: 'field', 197 | addStrategy: addFieldStrategy, 198 | category: categories.Fields, 199 | params: [{ type: 'field', dynamicLookup: true }], 200 | defaultParams: ['value'], 201 | renderer: fieldRenderer, 202 | }); 203 | // Aggregations 204 | QueryPartDef.register(lodash_1.default.assign({}, partDefault.Aggregations, { type: 'count' })); 205 | QueryPartDef.register(lodash_1.default.assign({}, partDefault.Aggregations, { type: 'avg' })); 206 | QueryPartDef.register(lodash_1.default.assign({}, partDefault.Aggregations, { type: 'sum' })); 207 | QueryPartDef.register(lodash_1.default.assign({}, partDefault.Aggregations, { type: 'median' })); 208 | // transformations 209 | QueryPartDef.register(lodash_1.default.assign({}, partDefault.Transform, { 210 | type: 'time', 211 | params: [{ name: "interval", type: "time", options: ['auto', '1s', '10s', '1m', '5m', '10m', '15m', '1h'] }], 212 | defaultParams: ['auto'], 213 | })); 214 | // Selectors 215 | QueryPartDef.register(lodash_1.default.assign({}, partDefault.Selectors, { type: 'max' })); 216 | QueryPartDef.register(lodash_1.default.assign({}, partDefault.Selectors, { type: 'min' })); 217 | QueryPartDef.register({ 218 | type: 'tag', 219 | category: categories.Fields, 220 | params: [{ name: 'tag', type: 'string', dynamicLookup: true }], 221 | defaultParams: ['tag'], 222 | renderer: fieldRenderer, 223 | }); 224 | QueryPartDef.register({ 225 | type: 'math', 226 | addStrategy: addMathStrategy, 227 | category: categories.Math, 228 | params: [{ name: "expr", type: "string" }], 229 | defaultParams: [' / 100'], 230 | renderer: suffixRenderer, 231 | }); 232 | QueryPartDef.register({ 233 | type: 'alias', 234 | addStrategy: addAliasStrategy, 235 | category: categories.Aliasing, 236 | params: [{ name: "name", type: "string", quote: 'double' }], 237 | defaultParams: ['alias'], 238 | renderMode: 'suffix', 239 | renderer: aliasRenderer, 240 | }); 241 | // Include dbms specific functions 242 | lodash_1.default.each(query_part_funcs_1.default, function (functionCategories, dbms) { 243 | lodash_1.default.each(functionCategories, function (functions, category) { 244 | /* Break to category and subcategory */ 245 | var catItem = category.split('_'); 246 | var categorySub = null; 247 | if (catItem[1]) { 248 | if (!categories[catItem[1]]) { 249 | categories[catItem[1]] = []; 250 | } 251 | categorySub = categories[catItem[1]]; 252 | } 253 | var defaults = partDefault[catItem[0]]; 254 | /* Register all function specs with updated defaults */ 255 | lodash_1.default.each(functions, function (spec) { 256 | if (spec.renderer) { 257 | spec.renderer = partRenderer[spec.renderer]; 258 | } 259 | if (categorySub) { 260 | spec.category = categorySub; 261 | } 262 | spec.dbms = dbms; 263 | /* Update defaults, the object allocations are kind 264 | of sluggish here, so let's make it less pretty */ 265 | for (var k in defaults) { 266 | if (spec[k] === undefined) { 267 | spec[k] = defaults[k]; 268 | } 269 | } 270 | QueryPartDef.register(spec); 271 | }); 272 | }); 273 | }); 274 | QueryPart = (function () { 275 | function QueryPart(part) { 276 | this.part = part; 277 | this.def = index[part.type]; 278 | if (!this.def) { 279 | throw { message: 'Could not find query part ' + part.type }; 280 | } 281 | part.params = part.params || lodash_1.default.clone(this.def.defaultParams); 282 | this.params = part.params; 283 | this.updateText(); 284 | } 285 | QueryPart.prototype.render = function (innerExpr) { 286 | return this.def.renderer(this, innerExpr); 287 | }; 288 | QueryPart.prototype.hasMultipleParamsInString = function (strValue, index) { 289 | if (strValue.indexOf(',') === -1) { 290 | return false; 291 | } 292 | return this.def.params[index + 1] && this.def.params[index + 1].optional; 293 | }; 294 | QueryPart.prototype.updateParam = function (strValue, index) { 295 | // handle optional parameters 296 | // if string contains ',' and next param is optional, split and update both 297 | if (this.hasMultipleParamsInString(strValue, index)) { 298 | lodash_1.default.each(strValue.split(','), function (partVal, idx) { 299 | this.updateParam(partVal.trim(), idx); 300 | }, this); 301 | return; 302 | } 303 | if (strValue === '' && this.def.params[index].optional) { 304 | this.params.splice(index, 1); 305 | } 306 | else { 307 | this.params[index] = strValue; 308 | } 309 | this.part.params = this.params; 310 | this.updateText(); 311 | }; 312 | QueryPart.prototype.updateText = function () { 313 | if (this.params.length === 0) { 314 | this.text = this.def.type + '()'; 315 | return; 316 | } 317 | var text = this.def.type + '('; 318 | text += this.params.join(', '); 319 | text += ')'; 320 | this.text = text; 321 | }; 322 | return QueryPart; 323 | })(); 324 | exports_1("default",{ 325 | create: function (part) { 326 | return new QueryPart(part); 327 | }, 328 | getCategories: function () { 329 | return categories; 330 | }, 331 | getMatchOperators: function (dbms) { 332 | var rtn = null; 333 | switch (dbms) { 334 | case 'postgres': 335 | rtn = { 'match': '~*', 'not': '!~*' }; 336 | break; 337 | case 'mysql': 338 | rtn = { 'match': 'REGEXP', 'not': 'NOT REGEXP' }; 339 | break; 340 | case 'clickhouse': 341 | rtn = { 'match': 'LIKE', 'not': 'NOT LIKE' }; 342 | break; 343 | default: 344 | break; 345 | } 346 | ; 347 | return rtn; 348 | }, 349 | }); 350 | } 351 | } 352 | }); 353 | //# sourceMappingURL=query_part.js.map -------------------------------------------------------------------------------- /dist/query_part.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"query_part.js","sourceRoot":"","sources":["query_part.ts"],"names":["functionRenderer","parametricFunctionRenderer","aliasRenderer","suffixRenderer","identityRenderer","quotedIdentityRenderer","fieldRenderer","replaceAggregationAddStrategy","addMathStrategy","addAliasStrategy","addFieldStrategy","addTransformStrategy","QueryPartDef","QueryPartDef.constructor","QueryPartDef.register","QueryPart","QueryPart.constructor","QueryPart.render","QueryPart.hasMultipleParamsInString","QueryPart.updateParam","QueryPart.updateText"],"mappings":"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ACA,CAACA,CAACA,GAAGA,CAACA,IAAIA,KAAKA,MAAMA,CAACA,CAACA,CAACA;gBACjDA,WAAWA,CAACA,SAASA,GAACA,CAACA,CAACA,GAAGA,SAASA,CAACA;gBACrCA,MAAMA,CAACA;YACTA,CAACA;YACDA,sCAAsCA;YACtCA,EAAEA,CAACA,CAACA,WAAWA,CAACA,SAASA,GAACA,CAACA,CAACA,CAACA,GAAGA,CAACA,IAAIA,KAAKA,MAAMA,CAACA,CAACA,CAACA;gBACjDA,WAAWA,CAACA,SAASA,GAACA,CAACA,CAACA,GAAGA,SAASA,CAACA;gBACrCA,MAAMA,CAACA;YACTA,CAACA;YAACA,IAAIA,CAACA,EAAEA,CAACA,CAACA,WAAWA,CAACA,SAASA,GAACA,CAACA,CAACA,CAACA,GAAGA,CAACA,IAAIA,KAAKA,OAAOA,CAACA,CAACA,CAACA;gBACzDA,WAAWA,CAACA,MAAMA,CAACA,SAASA,GAACA,CAACA,EAAEA,CAACA,EAAEA,SAASA,CAACA,CAACA;gBAC9CA,MAAMA,CAACA;YACTA,CAACA;QACHA,CAACA;QACDA,WAAWA,CAACA,IAAIA,CAACA,SAASA,CAACA,CAACA;IAC9BA,CAACA;IAED,0BAA0B,WAAW,EAAE,SAAS;QAC9CC,IAAIA,SAASA,GAAGA,WAAWA,CAACA,MAAMA,CAACA;QACnCA,EAAEA,CAACA,CAACA,SAASA,GAAGA,CAACA,CAACA,CAACA,CAACA;YAClBA,+BAA+BA;YAC/BA,EAAEA,CAACA,CAACA,WAAWA,CAACA,SAASA,GAACA,CAACA,CAACA,CAACA,GAAGA,CAACA,IAAIA,KAAKA,OAAOA,CAACA,CAACA,CAACA;gBAClDA,WAAWA,CAACA,SAASA,GAACA,CAACA,CAACA,GAAGA,SAASA,CAACA;gBACrCA,MAAMA,CAACA;YACTA,CAACA;QACHA,CAACA;QACDA,WAAWA,CAACA,IAAIA,CAACA,SAASA,CAACA,CAACA;IAC9BA,CAACA;IAED,0BAA0B,WAAW,EAAE,SAAS,EAAE,KAAK;QACrDC,iBAAiBA;QACjBA,IAAIA,KAA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"} -------------------------------------------------------------------------------- /dist/query_part.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import _ from 'lodash'; 4 | import extraFunctions from './query_part_funcs'; 5 | 6 | var index = {}; 7 | var categories = { 8 | Aggregations: [], 9 | Selectors: [], 10 | Math: [], 11 | Transform: [], 12 | Aliasing: [], 13 | Fields: [], 14 | }; 15 | 16 | class QueryPartDef { 17 | type: string; 18 | params: any[]; 19 | defaultParams: any[]; 20 | renderer: any; 21 | category: any; 22 | addStrategy: any; 23 | dbms: any[]; 24 | 25 | constructor(options: any) { 26 | this.type = options.type; 27 | this.params = options.params; 28 | this.defaultParams = options.defaultParams; 29 | this.renderer = options.renderer; 30 | this.category = options.category; 31 | this.addStrategy = options.addStrategy; 32 | this.dbms = options.dbms; 33 | } 34 | 35 | static register(options: any) { 36 | index[options.type] = new QueryPartDef(options); 37 | options.category.push(index[options.type]); 38 | } 39 | } 40 | 41 | function functionRenderer(part, innerExpr) { 42 | var str = part.def.type + '('; 43 | var parameters = _.map(part.params, (value, index) => { 44 | var paramType = part.def.params[index]; 45 | if (paramType.type === 'time') { 46 | if (value === 'auto') { 47 | value = '$interval'; 48 | } 49 | } 50 | if (paramType.quote === 'single') { 51 | return "'" + value + "'"; 52 | } else if (paramType.quote === 'double') { 53 | return '"' + value + '"'; 54 | } 55 | return value; 56 | }); 57 | 58 | if (innerExpr) { 59 | parameters.unshift(innerExpr); 60 | } 61 | return str + parameters.join(', ') + ')'; 62 | } 63 | 64 | function parametricFunctionRenderer(part, innerExpr) { 65 | var str = part.def.type; 66 | var parameters = _.map(part.params, (value, index) => { 67 | var paramType = part.def.params[index]; 68 | if (paramType.type === 'time') { 69 | if (value === 'auto') { 70 | value = '$interval'; 71 | } 72 | } 73 | if (paramType.quote === 'single') { 74 | return "'" + value + "'"; 75 | } else if (paramType.quote === 'double') { 76 | return '"' + value + '"'; 77 | } 78 | return value; 79 | }); 80 | 81 | if (innerExpr) { 82 | parameters.unshift(innerExpr); 83 | } 84 | return str + '(' + parameters.pop() + ')(' + parameters.join(', ') + ')'; 85 | } 86 | 87 | function aliasRenderer(part, innerExpr) { 88 | return innerExpr + ' AS ' + part.params[0]; 89 | } 90 | 91 | function suffixRenderer(part, innerExpr) { 92 | return innerExpr + ' ' + part.params[0]; 93 | } 94 | 95 | function identityRenderer(part, innerExpr) { 96 | return part.params[0]; 97 | } 98 | 99 | function quotedIdentityRenderer(part, innerExpr) { 100 | return '"' + part.params[0] + '"'; 101 | } 102 | 103 | function fieldRenderer(part, innerExpr) { 104 | return part.params[0]; 105 | } 106 | 107 | function replaceAggregationAddStrategy(selectParts, partModel) { 108 | // look for existing aggregation 109 | for (var i = 0; i < selectParts.length; i++) { 110 | var part = selectParts[i]; 111 | if (part.def.category === categories.Aggregations) { 112 | selectParts[i] = partModel; 113 | return; 114 | } 115 | if (part.def.category === categories.Selectors) { 116 | selectParts[i] = partModel; 117 | return; 118 | } 119 | } 120 | 121 | selectParts.splice(1, 0, partModel); 122 | } 123 | 124 | function addMathStrategy(selectParts, partModel) { 125 | var partCount = selectParts.length; 126 | if (partCount > 0) { 127 | // if last is math, replace it 128 | if (selectParts[partCount-1].def.type === 'math') { 129 | selectParts[partCount-1] = partModel; 130 | return; 131 | } 132 | // if next to last is math, replace it 133 | if (selectParts[partCount-2].def.type === 'math') { 134 | selectParts[partCount-2] = partModel; 135 | return; 136 | } else if (selectParts[partCount-1].def.type === 'alias') { // if last is alias add it before 137 | selectParts.splice(partCount-1, 0, partModel); 138 | return; 139 | } 140 | } 141 | selectParts.push(partModel); 142 | } 143 | 144 | function addAliasStrategy(selectParts, partModel) { 145 | var partCount = selectParts.length; 146 | if (partCount > 0) { 147 | // if last is alias, replace it 148 | if (selectParts[partCount-1].def.type === 'alias') { 149 | selectParts[partCount-1] = partModel; 150 | return; 151 | } 152 | } 153 | selectParts.push(partModel); 154 | } 155 | 156 | function addFieldStrategy(selectParts, partModel, query) { 157 | // copy all parts 158 | var parts = _.map(selectParts, function(part: any) { 159 | return new QueryPart({type: part.def.type, params: _.clone(part.params)}); 160 | }); 161 | 162 | query.selectModels.push(parts); 163 | } 164 | 165 | function addTransformStrategy(selectParts, partModel) { 166 | var partCount = selectParts.length; 167 | if (partCount > 0) { 168 | // if last is alias add it before 169 | if (selectParts[partCount-1].def.type === 'alias') { 170 | selectParts.splice(partCount-1, 0, partModel); 171 | return; 172 | } 173 | } 174 | selectParts.push(partModel); 175 | } 176 | 177 | // Defaults 178 | var partRenderer = { 179 | functionRenderer: functionRenderer, 180 | parametricFunctionRenderer: parametricFunctionRenderer, 181 | aliasRenderer: aliasRenderer, 182 | suffixRenderer: suffixRenderer, 183 | identityRenderer: identityRenderer, 184 | quotedIdentityRenderer: quotedIdentityRenderer, 185 | fieldRenderer: fieldRenderer 186 | } 187 | 188 | var partDefault = { 189 | Aggregations: { 190 | addStrategy: replaceAggregationAddStrategy, 191 | category: categories.Aggregations, 192 | params: [], 193 | defaultParams: [], 194 | renderer: functionRenderer, 195 | }, 196 | Selectors: { 197 | addStrategy: replaceAggregationAddStrategy, 198 | category: categories.Selectors, 199 | params: [], 200 | defaultParams: [], 201 | renderer: functionRenderer, 202 | }, 203 | Transform: { 204 | addStrategy: addTransformStrategy, 205 | renderer: functionRenderer, 206 | params: [], 207 | defaultParams: [], 208 | category: categories.Transform, 209 | }, 210 | }; 211 | 212 | QueryPartDef.register({ 213 | type: 'field', 214 | addStrategy: addFieldStrategy, 215 | category: categories.Fields, 216 | params: [{type: 'field', dynamicLookup: true}], 217 | defaultParams: ['value'], 218 | renderer: fieldRenderer, 219 | }); 220 | 221 | // Aggregations 222 | QueryPartDef.register(_.assign({}, partDefault.Aggregations, { type: 'count' })); 223 | QueryPartDef.register(_.assign({}, partDefault.Aggregations, { type: 'avg' })); 224 | QueryPartDef.register(_.assign({}, partDefault.Aggregations, { type: 'sum' })); 225 | QueryPartDef.register(_.assign({}, partDefault.Aggregations, { type: 'median' })); 226 | 227 | // transformations 228 | 229 | QueryPartDef.register(_.assign({}, partDefault.Transform, { 230 | type: 'time', 231 | params: [{ name: "interval", type: "time", options: ['auto', '1s', '10s', '1m', '5m', '10m', '15m', '1h'] }], 232 | defaultParams: ['auto'], 233 | })); 234 | 235 | // Selectors 236 | 237 | QueryPartDef.register(_.assign({}, partDefault.Selectors, { type: 'max' })); 238 | QueryPartDef.register(_.assign({}, partDefault.Selectors, { type: 'min' })); 239 | 240 | QueryPartDef.register({ 241 | type: 'tag', 242 | category: categories.Fields, 243 | params: [{name: 'tag', type: 'string', dynamicLookup: true}], 244 | defaultParams: ['tag'], 245 | renderer: fieldRenderer, 246 | }); 247 | 248 | QueryPartDef.register({ 249 | type: 'math', 250 | addStrategy: addMathStrategy, 251 | category: categories.Math, 252 | params: [{ name: "expr", type: "string"}], 253 | defaultParams: [' / 100'], 254 | renderer: suffixRenderer, 255 | }); 256 | 257 | QueryPartDef.register({ 258 | type: 'alias', 259 | addStrategy: addAliasStrategy, 260 | category: categories.Aliasing, 261 | params: [{ name: "name", type: "string", quote: 'double'}], 262 | defaultParams: ['alias'], 263 | renderMode: 'suffix', 264 | renderer: aliasRenderer, 265 | }); 266 | 267 | // Include dbms specific functions 268 | _.each(extraFunctions, (functionCategories, dbms) => { 269 | _.each(functionCategories, (functions, category) => { 270 | /* Break to category and subcategory */ 271 | var catItem = category.split('_'); 272 | var categorySub = null; 273 | if (catItem[1]) { 274 | if (!categories[catItem[1]]) { 275 | categories[catItem[1]] = []; 276 | } 277 | categorySub = categories[catItem[1]]; 278 | } 279 | var defaults = partDefault[catItem[0]]; 280 | /* Register all function specs with updated defaults */ 281 | _.each(functions, spec => { 282 | if (spec.renderer) { 283 | spec.renderer = partRenderer[spec.renderer]; 284 | } 285 | if (categorySub) { 286 | spec.category = categorySub; 287 | } 288 | spec.dbms = dbms; 289 | /* Update defaults, the object allocations are kind 290 | of sluggish here, so let's make it less pretty */ 291 | for (var k in defaults) { 292 | if (spec[k] === undefined) { 293 | spec[k] = defaults[k]; 294 | } 295 | } 296 | QueryPartDef.register(spec); 297 | }); 298 | }); 299 | }); 300 | 301 | class QueryPart { 302 | part: any; 303 | def: QueryPartDef; 304 | params: any[]; 305 | text: string; 306 | 307 | constructor(part: any) { 308 | this.part = part; 309 | this.def = index[part.type]; 310 | if (!this.def) { 311 | throw {message: 'Could not find query part ' + part.type}; 312 | } 313 | 314 | part.params = part.params || _.clone(this.def.defaultParams); 315 | this.params = part.params; 316 | this.updateText(); 317 | } 318 | 319 | render(innerExpr: string) { 320 | return this.def.renderer(this, innerExpr); 321 | } 322 | 323 | hasMultipleParamsInString (strValue, index) { 324 | if (strValue.indexOf(',') === -1) { 325 | return false; 326 | } 327 | 328 | return this.def.params[index + 1] && this.def.params[index + 1].optional; 329 | } 330 | 331 | updateParam (strValue, index) { 332 | // handle optional parameters 333 | // if string contains ',' and next param is optional, split and update both 334 | if (this.hasMultipleParamsInString(strValue, index)) { 335 | _.each(strValue.split(','), function(partVal: string, idx) { 336 | this.updateParam(partVal.trim(), idx); 337 | }, this); 338 | return; 339 | } 340 | 341 | if (strValue === '' && this.def.params[index].optional) { 342 | this.params.splice(index, 1); 343 | } else { 344 | this.params[index] = strValue; 345 | } 346 | 347 | this.part.params = this.params; 348 | this.updateText(); 349 | } 350 | 351 | updateText() { 352 | if (this.params.length === 0) { 353 | this.text = this.def.type + '()'; 354 | return; 355 | } 356 | 357 | var text = this.def.type + '('; 358 | text += this.params.join(', '); 359 | text += ')'; 360 | this.text = text; 361 | } 362 | } 363 | 364 | export default { 365 | create: function(part): any { 366 | return new QueryPart(part); 367 | }, 368 | 369 | getCategories: function() { 370 | return categories; 371 | }, 372 | 373 | getMatchOperators: function(dbms) { 374 | var rtn = null; 375 | switch (dbms) { 376 | case 'postgres': 377 | rtn = { 'match': '~*', 'not': '!~*' }; 378 | break; 379 | case 'mysql': 380 | rtn = { 'match': 'REGEXP', 'not': 'NOT REGEXP' }; 381 | break; 382 | case 'clickhouse': 383 | rtn = { 'match': 'LIKE', 'not': 'NOT LIKE' }; 384 | break; 385 | default: 386 | break; 387 | }; 388 | 389 | return rtn; 390 | }, 391 | }; 392 | -------------------------------------------------------------------------------- /dist/query_part_editor.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'angular', 3 | 'lodash', 4 | 'jquery', 5 | ], 6 | function (angular, _, $) { 7 | 'use strict'; 8 | 9 | angular 10 | .module('grafana.directives') 11 | .directive('sqlQueryPartEditor', function($compile, templateSrv) { 12 | 13 | var paramTemplate = ''; 15 | return { 16 | restrict: 'E', 17 | templateUrl: 'public/plugins/sqldb/partials/query_part.html', 18 | scope: { 19 | part: "=", 20 | removeAction: "&", 21 | partUpdated: "&", 22 | getOptions: "&", 23 | }, 24 | link: function postLink($scope, elem) { 25 | var part = $scope.part; 26 | var partDef = part.def; 27 | var $paramsContainer = elem.find('.query-part-parameters'); 28 | var $controlsContainer = elem.find('.tight-form-func-controls'); 29 | 30 | function clickFuncParam(paramIndex) { 31 | /*jshint validthis:true */ 32 | var $link = $(this); 33 | var $input = $link.next(); 34 | 35 | $input.val(part.params[paramIndex]); 36 | $input.css('width', ($link.width() + 16) + 'px'); 37 | 38 | $link.hide(); 39 | $input.show(); 40 | $input.focus(); 41 | $input.select(); 42 | 43 | var typeahead = $input.data('typeahead'); 44 | if (typeahead) { 45 | $input.val(''); 46 | typeahead.lookup(); 47 | } 48 | } 49 | 50 | function inputBlur(paramIndex) { 51 | /*jshint validthis:true */ 52 | var $input = $(this); 53 | var $link = $input.prev(); 54 | var newValue = $input.val(); 55 | 56 | if (newValue !== '' || part.def.params[paramIndex].optional) { 57 | $link.html(templateSrv.highlightVariablesAsHtml(newValue)); 58 | 59 | part.updateParam($input.val(), paramIndex); 60 | $scope.$apply($scope.partUpdated); 61 | } 62 | 63 | $input.hide(); 64 | $link.show(); 65 | } 66 | 67 | function inputKeyPress(paramIndex, e) { 68 | /*jshint validthis:true */ 69 | if(e.which === 13) { 70 | inputBlur.call(this, paramIndex); 71 | } 72 | } 73 | 74 | function inputKeyDown() { 75 | /*jshint validthis:true */ 76 | this.style.width = (3 + this.value.length) * 8 + 'px'; 77 | } 78 | 79 | function addTypeahead($input, param, paramIndex) { 80 | if (!param.options && !param.dynamicLookup) { 81 | return; 82 | } 83 | 84 | var typeaheadSource = function (query, callback) { 85 | if (param.options) { return param.options; } 86 | 87 | $scope.$apply(function() { 88 | $scope.getOptions().then(function(result) { 89 | var dynamicOptions = _.map(result, function(op) { return op.value; }); 90 | callback(dynamicOptions); 91 | }); 92 | }); 93 | }; 94 | 95 | $input.attr('data-provide', 'typeahead'); 96 | var options = param.options; 97 | if (param.type === 'int') { 98 | options = _.map(options, function(val) { return val.toString(); }); 99 | } 100 | 101 | $input.typeahead({ 102 | source: typeaheadSource, 103 | minLength: 0, 104 | items: 1000, 105 | updater: function (value) { 106 | setTimeout(function() { 107 | inputBlur.call($input[0], paramIndex); 108 | }, 0); 109 | return value; 110 | } 111 | }); 112 | 113 | var typeahead = $input.data('typeahead'); 114 | typeahead.lookup = function () { 115 | this.query = this.$element.val() || ''; 116 | var items = this.source(this.query, $.proxy(this.process, this)); 117 | return items ? this.process(items) : items; 118 | }; 119 | } 120 | 121 | $scope.toggleControls = function() { 122 | var targetDiv = elem.closest('.tight-form'); 123 | 124 | if (elem.hasClass('show-function-controls')) { 125 | elem.removeClass('show-function-controls'); 126 | targetDiv.removeClass('has-open-function'); 127 | $controlsContainer.hide(); 128 | return; 129 | } 130 | 131 | elem.addClass('show-function-controls'); 132 | targetDiv.addClass('has-open-function'); 133 | $controlsContainer.show(); 134 | }; 135 | 136 | $scope.removeActionInternal = function() { 137 | $scope.toggleControls(); 138 | $scope.removeAction(); 139 | }; 140 | 141 | function addElementsAndCompile() { 142 | _.each(partDef.params, function(param, index) { 143 | if (param.optional && part.params.length <= index) { 144 | return; 145 | } 146 | 147 | if (index > 0) { 148 | $(', ').appendTo($paramsContainer); 149 | } 150 | 151 | var paramValue = templateSrv.highlightVariablesAsHtml(part.params[index] || param.name); 152 | var $paramLink = $('' + paramValue + ''); 153 | var $input = $(paramTemplate); 154 | 155 | $paramLink.appendTo($paramsContainer); 156 | $input.appendTo($paramsContainer); 157 | 158 | $input.blur(_.partial(inputBlur, index)); 159 | $input.keyup(inputKeyDown); 160 | $input.keypress(_.partial(inputKeyPress, index)); 161 | $paramLink.click(_.partial(clickFuncParam, index)); 162 | 163 | addTypeahead($input, param, index); 164 | }); 165 | } 166 | 167 | function relink() { 168 | $paramsContainer.empty(); 169 | addElementsAndCompile(); 170 | } 171 | 172 | relink(); 173 | } 174 | }; 175 | 176 | }); 177 | 178 | }); 179 | -------------------------------------------------------------------------------- /dist/query_part_funcs.d.ts: -------------------------------------------------------------------------------- 1 | declare var _default: { 2 | clickhouse: { 3 | Aggregations: ({ 4 | type: string; 5 | } | { 6 | type: string; 7 | params: { 8 | name: string; 9 | type: string; 10 | dynamicLookup: boolean; 11 | }[]; 12 | defaultParams: string[]; 13 | } | { 14 | type: string; 15 | params: ({ 16 | name: string; 17 | type: string; 18 | dynamicLookup: boolean; 19 | } | { 20 | name: string; 21 | type: string; 22 | options: string[]; 23 | })[]; 24 | defaultParams: string[]; 25 | renderer: string; 26 | })[]; 27 | Transform: { 28 | type: string; 29 | params: { 30 | name: string; 31 | dynamicLookup: boolean; 32 | }[]; 33 | defaultParams: string[]; 34 | }[]; 35 | Transform_Bit: { 36 | type: string; 37 | params: { 38 | name: string; 39 | dynamicLookup: boolean; 40 | }[]; 41 | }[]; 42 | Transform_Casting: { 43 | type: string; 44 | params: ({ 45 | name: string; 46 | dynamicLookup: boolean; 47 | } | { 48 | name: string; 49 | type: string; 50 | })[]; 51 | }[]; 52 | Transform_DateTime: ({ 53 | type: string; 54 | params: { 55 | name: string; 56 | dynamicLookup: boolean; 57 | }[]; 58 | } | { 59 | type: string; 60 | })[]; 61 | Transform_String: { 62 | type: string; 63 | params: ({ 64 | name: string; 65 | dynamicLookup: boolean; 66 | } | { 67 | name: string; 68 | })[]; 69 | }[]; 70 | Transform_Array: { 71 | type: string; 72 | params: ({ 73 | name: string; 74 | dynamicLookup: boolean; 75 | } | { 76 | name: string; 77 | })[]; 78 | }[]; 79 | Transform_URL: { 80 | type: string; 81 | params: ({ 82 | name: string; 83 | dynamicLookup: boolean; 84 | } | { 85 | name: string; 86 | })[]; 87 | }[]; 88 | Transform_IPAddress: { 89 | type: string; 90 | params: { 91 | name: string; 92 | dynamicLookup: boolean; 93 | }[]; 94 | }[]; 95 | Transform_Rand: { 96 | type: string; 97 | params: any[]; 98 | }[]; 99 | Transform_Hash: { 100 | type: string; 101 | params: { 102 | name: string; 103 | dynamicLookup: boolean; 104 | }[]; 105 | }[]; 106 | Transform_Encoding: { 107 | type: string; 108 | params: { 109 | name: string; 110 | dynamicLookup: boolean; 111 | }[]; 112 | }[]; 113 | Transform_Math: { 114 | type: string; 115 | params: ({ 116 | name: string; 117 | dynamicLookup: boolean; 118 | } | { 119 | name: string; 120 | })[]; 121 | }[]; 122 | Transform_Dictionary: { 123 | type: string; 124 | params: ({ 125 | name: string; 126 | } | { 127 | name: string; 128 | dynamicLookup: boolean; 129 | })[]; 130 | }[]; 131 | }; 132 | }; 133 | export default _default; 134 | -------------------------------------------------------------------------------- /dist/response_parser.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | export default class ResponseParser { 3 | parse(query: any, results: any): any; 4 | } 5 | -------------------------------------------------------------------------------- /dist/response_parser.js: -------------------------------------------------------------------------------- 1 | /// 2 | System.register(['lodash'], function(exports_1) { 3 | var lodash_1; 4 | var ResponseParser; 5 | function addUnique(arr, value) { 6 | arr[value] = value; 7 | } 8 | return { 9 | setters:[ 10 | function (lodash_1_1) { 11 | lodash_1 = lodash_1_1; 12 | }], 13 | execute: function() { 14 | ResponseParser = (function () { 15 | function ResponseParser() { 16 | } 17 | ResponseParser.prototype.parse = function (query, results) { 18 | if (!results || results.results.length === 0) { 19 | return []; 20 | } 21 | var sqlResults = results.results[0]; 22 | if (!sqlResults.series) { 23 | return []; 24 | } 25 | var res = {}; 26 | lodash_1.default.each(sqlResults.series, function (serie) { 27 | lodash_1.default.each(serie.values, function (value) { 28 | if (lodash_1.default.isArray(value)) { 29 | addUnique(res, value[0]); 30 | } 31 | else { 32 | addUnique(res, value); 33 | } 34 | }); 35 | }); 36 | return lodash_1.default.map(res, function (value) { 37 | return { text: value }; 38 | }); 39 | }; 40 | return ResponseParser; 41 | })(); 42 | exports_1("default", ResponseParser); 43 | } 44 | } 45 | }); 46 | //# sourceMappingURL=response_parser.js.map -------------------------------------------------------------------------------- /dist/response_parser.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"response_parser.js","sourceRoot":"","sources":["response_parser.ts"],"names":["addUnique","ResponseParser","ResponseParser.constructor","ResponseParser.parse"],"mappings":"AAAA,+CAA+C;;;;IA+B/C,mBAAmB,GAAG,EAAE,KAAK;QAC3BA,GAAGA,CAACA,KAAKA,CAACA,GAAGA,KAAKA,CAACA;IACrBA,CAACA;;;;;;;YA7BD;gBAAAC;gBAyBAC,CAACA;gBAvBCD,8BAAKA,GAALA,UAAMA,KAAKA,EAAEA,OAAOA;oBAClBE,EAAEA,CAACA,CAACA,CAACA,OAAOA,IAAIA,OAAOA,CAACA,OAAOA,CAACA,MAAMA,KAAKA,CAACA,CAACA,CAACA,CAACA;wBAACA,MAAMA,CAACA,EAAEA,CAACA;oBAACA,CAACA;oBAE5DA,IAAIA,UAAUA,GAAGA,OAAOA,CAACA,OAAOA,CAACA,CAACA,CAACA,CAACA;oBACpCA,EAAEA,CAACA,CAACA,CAACA,UAAUA,CAACA,MAAMA,CAACA,CAACA,CAACA;wBACvBA,MAAMA,CAACA,EAAEA,CAACA;oBACZA,CAACA;oBAEDA,IAAIA,GAAGA,GAAGA,EAAEA,CAACA;oBACbA,gBAACA,CAACA,IAAIA,CAACA,UAAUA,CAACA,MAAMA,EAAEA,UAAAA,KAAKA;wBAC7BA,gBAACA,CAACA,IAAIA,CAACA,KAAKA,CAACA,MAAMA,EAAEA,UAAAA,KAAKA;4BACxBA,EAAEA,CAACA,CAACA,gBAACA,CAACA,OAAOA,CAACA,KAAKA,CAACA,CAACA,CAACA,CAACA;gCACrBA,SAASA,CAACA,GAAGA,EAAEA,KAAKA,CAACA,CAACA,CAACA,CAACA,CAACA;4BAC3BA,CAACA;4BAACA,IAAIA,CAACA,CAACA;gCACNA,SAASA,CAACA,GAAGA,EAAEA,KAAKA,CAACA,CAACA;4BACxBA,CAACA;wBACHA,CAACA,CAACA,CAACA;oBACLA,CAACA,CAACA,CAACA;oBAEHA,MAAMA,CAACA,gBAACA,CAACA,GAAGA,CAACA,GAAGA,EAAEA,UAAAA,KAAKA;wBACrBA,MAAMA,CAACA,EAAEA,IAAIA,EAAEA,KAAKA,EAACA,CAACA;oBACxBA,CAACA,CAACA,CAACA;gBACLA,CAACA;gBACHF,qBAACA;YAADA,CAACA,AAzBD,IAyBC;YAzBD,oCAyBC,CAAA"} -------------------------------------------------------------------------------- /dist/response_parser.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import _ from 'lodash'; 4 | 5 | export default class ResponseParser { 6 | 7 | parse(query, results) { 8 | if (!results || results.results.length === 0) { return []; } 9 | 10 | var sqlResults = results.results[0]; 11 | if (!sqlResults.series) { 12 | return []; 13 | } 14 | 15 | var res = {}; 16 | _.each(sqlResults.series, serie => { 17 | _.each(serie.values, value => { 18 | if (_.isArray(value)) { 19 | addUnique(res, value[0]); 20 | } else { 21 | addUnique(res, value); 22 | } 23 | }); 24 | }); 25 | 26 | return _.map(res, value => { 27 | return { text: value}; 28 | }); 29 | } 30 | } 31 | 32 | function addUnique(arr, value) { 33 | arr[value] = value; 34 | } 35 | -------------------------------------------------------------------------------- /dist/sql_query.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | export default class SqlQuery { 3 | dbms: string; 4 | target: any; 5 | selectModels: any[]; 6 | queryBuilder: any; 7 | groupByParts: any; 8 | templateSrv: any; 9 | scopedVars: any; 10 | /** @ngInject */ 11 | constructor(target: any, templateSrv?: any, scopedVars?: any); 12 | updateProjection(): void; 13 | updatePersistedParts(): void; 14 | hasGroupByTime(): any; 15 | addGroupBy(type: any): void; 16 | removeGroupByPart(part: any, index: any): void; 17 | removeSelect(index: number): void; 18 | removeSelectPart(selectParts: any, part: any): void; 19 | addSelectPart(selectParts: any, type: any): void; 20 | private renderTagCondition(tag, index, interpolate); 21 | gettableAndSchema(interpolate: any): any; 22 | render(interpolate?: any): any; 23 | } 24 | -------------------------------------------------------------------------------- /dist/sql_query.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"sql_query.js","sourceRoot":"","sources":["sql_query.ts"],"names":["SqlQuery","SqlQuery.constructor","SqlQuery.updateProjection","SqlQuery.updatePersistedParts","SqlQuery.hasGroupByTime","SqlQuery.addGroupBy","SqlQuery.removeGroupByPart","SqlQuery.removeSelect","SqlQuery.removeSelectPart","SqlQuery.addSelectPart","SqlQuery.renderTagCondition","SqlQuery.gettableAndSchema","SqlQuery.render"],"mappings":"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"} -------------------------------------------------------------------------------- /dist/sql_query.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import _ from 'lodash'; 4 | import queryPart from './query_part'; 5 | 6 | export default class SqlQuery { 7 | dbms: string; 8 | target: any; 9 | selectModels: any[]; 10 | queryBuilder: any; 11 | groupByParts: any; 12 | templateSrv: any; 13 | scopedVars: any; 14 | 15 | /** @ngInject */ 16 | constructor(target, templateSrv?, scopedVars?) { 17 | this.dbms = null; 18 | this.target = target; 19 | this.templateSrv = templateSrv; 20 | this.scopedVars = scopedVars; 21 | 22 | target.schema = target.schema; 23 | target.dsType = 'sqldb'; 24 | target.timeColDataType = target.timeColDataType; 25 | target.resultFormat = target.resultFormat || 'time_series'; 26 | target.tags = target.tags || []; 27 | target.filters = target.filters || []; 28 | target.groupBy = target.groupBy || [ 29 | {type: 'time', params: ['$interval']}, 30 | ]; 31 | target.targetLists = target.targetLists || [[ 32 | {type: 'field', params: ['*']}, 33 | {type: 'count', params: []}, 34 | ]]; 35 | target.alias = target.alias || '$t.$col'; 36 | 37 | this.updateProjection(); 38 | } 39 | 40 | updateProjection() { 41 | this.selectModels = _.map(this.target.targetLists, function(parts: any) { 42 | return _.map(parts, queryPart.create); 43 | }); 44 | this.groupByParts = _.map(this.target.groupBy, queryPart.create); 45 | } 46 | 47 | updatePersistedParts() { 48 | this.target.targetLists = _.map(this.selectModels, function(selectParts) { 49 | return _.map(selectParts, function(part: any) { 50 | return {type: part.def.type, params: part.params}; 51 | }); 52 | }); 53 | } 54 | 55 | hasGroupByTime() { 56 | return _.find(this.target.groupBy, (g: any) => g.type === 'time'); 57 | } 58 | 59 | addGroupBy(type) { 60 | var partModel = queryPart.create({type: type}); 61 | var partCount = this.target.groupBy.length; 62 | 63 | if (partCount === 0) { 64 | this.target.groupBy.push(partModel.part); 65 | } else if (type === 'time') { 66 | this.target.groupBy.splice(0, 0, partModel.part); 67 | } else { 68 | this.target.groupBy.push(partModel.part); 69 | } 70 | 71 | this.updateProjection(); 72 | } 73 | 74 | removeGroupByPart(part, index) { 75 | var categories = queryPart.getCategories(); 76 | 77 | if (part.def.type === 'time') { 78 | // remove aggregations 79 | this.target.targetLists = _.map(this.target.targetLists, (s: any) => { 80 | return _.filter(s, (part: any) => { 81 | var partModel = queryPart.create(part); 82 | if (partModel.def.category === categories.Aggregations) { 83 | return false; 84 | } 85 | if (partModel.def.category === categories.Selectors) { 86 | return false; 87 | } 88 | return true; 89 | }); 90 | }); 91 | } 92 | 93 | this.target.groupBy.splice(index, 1); 94 | this.updateProjection(); 95 | } 96 | 97 | removeSelect(index: number) { 98 | this.target.targetLists.splice(index, 1); 99 | this.updateProjection(); 100 | } 101 | 102 | removeSelectPart(selectParts, part) { 103 | // if we remove the field remove the whole statement 104 | if (part.def.type === 'field') { 105 | if (this.selectModels.length > 1) { 106 | var modelsIndex = _.indexOf(this.selectModels, selectParts); 107 | this.selectModels.splice(modelsIndex, 1); 108 | } 109 | } else { 110 | var partIndex = _.indexOf(selectParts, part); 111 | selectParts.splice(partIndex, 1); 112 | } 113 | 114 | this.updatePersistedParts(); 115 | } 116 | 117 | addSelectPart(selectParts, type) { 118 | var partModel = queryPart.create({type: type}); 119 | partModel.def.addStrategy(selectParts, partModel, this); 120 | this.updatePersistedParts(); 121 | } 122 | 123 | private renderTagCondition(tag, index, interpolate) { 124 | var str = ""; 125 | var operator = tag.operator; 126 | var value = tag.value; 127 | 128 | if (!operator) { 129 | if (/^\/.*\/$/.test(value)) { 130 | operator = '=~'; 131 | } else { 132 | operator = '='; 133 | } 134 | } 135 | 136 | // Support wildcard values to behave like regular filters 137 | if (interpolate) { 138 | for (var i in this.templateSrv.variables) { 139 | var v = this.templateSrv.variables[i]; 140 | if (v.name == value.slice(1) && this.templateSrv.isAllValue(v.current.value)) { 141 | return str; 142 | } 143 | } 144 | } 145 | 146 | // quote value unless regex or number(s) 147 | var matchOperators = queryPart.getMatchOperators(this.dbms); 148 | if (operator.indexOf('IN') > -1) { 149 | // IN/NOT IN operators may have a tupe on the right side 150 | value = value.replace(/[()]/g, ''); 151 | if (interpolate) { 152 | value = this.templateSrv.replace(value, this.scopedVars); 153 | } 154 | // Check if the array is all numbers 155 | var values = _.map(value.split(','), x => x.trim()); 156 | var intArray = _.reduce(values, (memo, x) => { 157 | return memo && !isNaN(+x); 158 | }, true); 159 | // Force quotes and braces 160 | for (var i in values) { 161 | values[i] = values[i].replace(/\'\"/,''); 162 | if (!intArray) { 163 | values[i] = "'" + value.replace(/\\/g, '\\\\') + "'"; 164 | } 165 | } 166 | value = '(' + values.join(', ') + ')'; 167 | } else if (!matchOperators || (operator !== matchOperators.match && operator !== matchOperators.not)) { 168 | if (interpolate) { 169 | value = this.templateSrv.replace(value, this.scopedVars); 170 | } 171 | 172 | if (!operator.startsWith('>') && !operator.startsWith('<') && isNaN(+value)) { 173 | value = "'" + value.replace(/\\/g, '\\\\') + "'"; 174 | } 175 | } else if (interpolate) { 176 | value = this.templateSrv.replace(value, this.scopedVars, 'regex'); 177 | value = "'" + value.replace(/^\//, '').replace(/\/$/, '') + "'"; 178 | } else if (isNaN(+value)) { 179 | value = "'" + value.replace(/^\//, '').replace(/\/$/, '') + "'"; 180 | } 181 | 182 | if (index > 0) { 183 | str = (tag.condition || 'AND') + ' '; 184 | } 185 | return str + tag.key + ' ' + operator + ' ' + value; 186 | } 187 | 188 | gettableAndSchema(interpolate) { 189 | var schema = this.target.schema; 190 | var table = this.target.table || 'table'; 191 | 192 | if (!table.match('^/.*/')) { 193 | table = table; 194 | } else if (interpolate) { 195 | table = this.templateSrv.replace(table, this.scopedVars, 'regex'); 196 | } 197 | 198 | if (schema !== 'default') { 199 | schema = this.target.schema + '.'; 200 | } else { 201 | schema = ""; 202 | } 203 | 204 | var rtn = schema + table; 205 | 206 | return rtn; 207 | } 208 | 209 | render(interpolate?) { 210 | var target = this.target; 211 | 212 | if (target.rawQuery) { 213 | /* There is no structural information about raw query, so best 214 | effort parse the GROUP BY column to be able to detect series. 215 | Only grouped by expressions or their aliases are inferred. */ 216 | var parts = /GROUP BY (.+)\s*(?:ORDER|LIMIT|HAVING|$)/.exec(target.query); 217 | if (parts) { 218 | var last = parts[parts.length - 1]; 219 | target.groupBy = _.map(last.split(','), (e, i) => { 220 | return {type: i > 0 ? 'field' : 'time', params: [e.trim()]}; 221 | }); 222 | } 223 | if (interpolate) { 224 | var q = this.templateSrv.replace(target.query, this.scopedVars, 'regex'); 225 | /* Support filter expansion in raw queries as well. */ 226 | var conditions = _.map(target.filters, (tag, index) => { 227 | return this.renderTagCondition(tag, index, interpolate); 228 | }); 229 | return q.replace(/\$filter/, (conditions.length > 0 ? conditions.join(' ') : '1')); 230 | } else { 231 | return target.query; 232 | } 233 | } 234 | 235 | var hasTimeGroupBy = false; 236 | var selectClause = []; 237 | var groupByClause = []; 238 | var orderByClause = ''; 239 | var usePositions = (this.dbms !== 'clickhouse'); 240 | 241 | if (target.groupBy.length !== 0) { 242 | _.each(this.target.groupBy, function(groupBy, i) { 243 | 244 | var alias = null; 245 | switch (groupBy.type) { 246 | case 'time': 247 | selectClause.push('$unixtimeColumn * 1000 AS time_msec'); 248 | if (!usePositions) { 249 | alias = 'time_msec'; 250 | } 251 | break; 252 | 253 | case 'alias': 254 | var part = selectClause.pop(); 255 | selectClause.push(queryPart.create(groupBy).render(part)); 256 | groupByClause.pop(); 257 | alias = groupBy.params[0]; 258 | break; 259 | 260 | default: 261 | var part = queryPart.create(groupBy).render(); 262 | selectClause.push(part); 263 | if (!usePositions) { 264 | alias = part; 265 | } 266 | break; 267 | } 268 | 269 | if (alias !== null) { 270 | groupByClause.push(alias); 271 | } else { 272 | groupByClause.push((i + 1).toFixed(0)); 273 | } 274 | }); 275 | } 276 | 277 | var query = 'SELECT '; 278 | if (selectClause.length > 0) { 279 | query += selectClause.join(', ') + ', '; 280 | } 281 | 282 | var i, j; 283 | var targetList = ''; 284 | for (i = 0; i < this.selectModels.length; i++) { 285 | let parts = this.selectModels[i]; 286 | var selectText = ""; 287 | for (j = 0; j < parts.length; j++) { 288 | let part = parts[j]; 289 | selectText = part.render(selectText); 290 | } 291 | 292 | if (i > 0) { 293 | targetList += ', '; 294 | } 295 | targetList += selectText; 296 | } 297 | query += targetList; 298 | 299 | query += ' FROM ' + this.gettableAndSchema(interpolate) + ' WHERE '; 300 | var conditions = _.map(target.filters, (tag, index) => { 301 | return this.renderTagCondition(tag, index, interpolate); 302 | }); 303 | 304 | query += conditions.join(' '); 305 | query += (conditions.length > 0 ? ' AND ' : '') + '$timeFilter'; 306 | 307 | if (groupByClause.length > 0) { 308 | query += ' GROUP BY ' + groupByClause.join(', '); 309 | } 310 | 311 | orderByClause = groupByClause.join(', ') || targetList; 312 | query += ' ORDER BY ' + orderByClause; 313 | 314 | return query; 315 | } 316 | } 317 | -------------------------------------------------------------------------------- /dist/sql_series.d.ts: -------------------------------------------------------------------------------- 1 | declare var test: any; 2 | export default test; 3 | -------------------------------------------------------------------------------- /dist/sql_series.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'lodash', 3 | 'app/core/table_model', 4 | ], 5 | function (_, TableModel) { 6 | 'use strict'; 7 | 8 | function SqlSeries(options) { 9 | this.series = options.series; 10 | this.table = options.table; 11 | this.alias = options.alias; 12 | this.annotation = options.annotation; 13 | /* Flatten aliases */ 14 | this.groupBy = _.reduce(options.groupBy, function(memo, v) { 15 | if (v.type == 'alias') { 16 | memo.pop(); 17 | } 18 | memo.push(v); 19 | return memo; 20 | }, []); 21 | } 22 | 23 | var p = SqlSeries.prototype; 24 | 25 | 26 | p.getTimeSeries = function() { 27 | var output = []; 28 | var self = this; 29 | 30 | if (self.series.length === 0) { 31 | return output; 32 | } 33 | 34 | var seriesDatapoints = {}; 35 | _.each(self.series.values, function(row) { 36 | var tags = {}; 37 | var tagList = []; 38 | var addTags = true; 39 | 40 | _.each(self.groupBy, function(groupBy, k) { 41 | if (k !== 0) { 42 | tagList.push(groupBy.params[0] + ': ' + row[k]); 43 | } 44 | tags[groupBy.params[0]] = row[k]; 45 | /* Note down if format string contains tags. */ 46 | if (self.alias && self.alias.indexOf('$' + groupBy.params[0]) > -1) { 47 | addTags = false; 48 | } 49 | }); 50 | 51 | var tagsStr = ''; 52 | if (tagList.length !== 0) { 53 | tagsStr = ' {' + tagList.join(', ') + '}'; 54 | } 55 | 56 | _.each(row, function(value, i) { 57 | if (i < self.groupBy.length) { 58 | return; 59 | } 60 | 61 | var seriesName = self.table; 62 | var columnName = self.series.columns[i]; 63 | if (columnName !== 'value') { 64 | if (seriesName) { 65 | seriesName = seriesName + '.' + columnName; 66 | } else { 67 | seriesName = columnName; 68 | } 69 | } 70 | 71 | /* Do not print tags if it is a part of formatting string */ 72 | if (self.alias) { 73 | seriesName = self._getSeriesName(self.series, i, tags); 74 | } 75 | if (addTags) { 76 | seriesName = seriesName + tagsStr; 77 | } 78 | 79 | if (! seriesDatapoints[seriesName]) { 80 | seriesDatapoints[seriesName] = []; 81 | } 82 | 83 | seriesDatapoints[seriesName].push([ 84 | self._formatValue(value), // numeric value 85 | self._formatValue(row[0]) // timestamp 86 | ]); 87 | }); 88 | }); 89 | 90 | _.each(seriesDatapoints, function(datapoints, seriesName) { 91 | output.push({ target: seriesName, datapoints: datapoints }); 92 | }); 93 | 94 | return output; 95 | }; 96 | 97 | p._getSeriesName = function(series, index, tags) { 98 | var self = this; 99 | var regex = /\$(\w+)|\[\[([\s\S]+?)\]\]/g; 100 | 101 | return this.alias.replace(regex, function(match, g1, g2) { 102 | var group = g1 || g2; 103 | 104 | if (group === 't' || group === 'table') { return self.table || series.name; } 105 | if (group === 'col') { return series.columns[index]; } 106 | if (group in tags) { return tags[group]; } 107 | return match; 108 | }); 109 | }; 110 | 111 | p.getAnnotations = function () { 112 | var list = []; 113 | var self = this; 114 | 115 | _.each(this.series, function (series) { 116 | var titleCol = null; 117 | var timeCol = null; 118 | var tagsCol = null; 119 | var textCol = null; 120 | 121 | _.each(series.columns, function(column, index) { 122 | if (column === 'time') { timeCol = index; return; } 123 | if (column === 'tags') { tagsCol = index; return; } 124 | if (column === 'title') { titleCol = index; return; } 125 | if (column === 'text') { textCol = index; return; } 126 | if (!titleCol) { titleCol = index; } 127 | }); 128 | 129 | _.each(series.values, function (value) { 130 | var data = { 131 | annotation: self.annotation, 132 | time: + new Date(self._formatValue(value[timeCol])), 133 | title: value[titleCol], 134 | tags: value[tagsCol], 135 | text: value[textCol] 136 | }; 137 | 138 | list.push(data); 139 | }); 140 | }); 141 | 142 | return list; 143 | }; 144 | 145 | p.getTable = function() { 146 | var table = new TableModel.default(); 147 | var self = this; 148 | 149 | _.each(self.series.columns, function(column) { 150 | table.columns.push({ text: column }); 151 | }); 152 | 153 | _.each(self.series.values, function(row) { 154 | var formated = _.map(row, function(value) { 155 | return self._formatValue(value); 156 | }); 157 | 158 | table.rows.push(formated); 159 | }); 160 | 161 | return table; 162 | }; 163 | 164 | p.getDocs = function() { 165 | var self = this; 166 | var rows = { datapoints: [], target: self.series.name, type: 'docs' }; 167 | 168 | _.each(self.series.values, function(values) { 169 | var formated = {}; 170 | 171 | _.each(values, function(value, i) { 172 | var column = self.series.columns[i]; 173 | formated[column] = self._formatValue(value); 174 | }); 175 | 176 | rows.datapoints.push(formated); 177 | }); 178 | 179 | return rows; 180 | }; 181 | 182 | p._formatValue = function(value) { 183 | var v_numeric = Number(value); 184 | 185 | if (isNaN(value)) { 186 | return value; 187 | } else { 188 | return parseFloat(v_numeric); 189 | } 190 | }; 191 | 192 | return SqlSeries; 193 | }); 194 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grafana-sqldb-datasource", 3 | "private": true, 4 | "version": "1.2.0", 5 | "description": "", 6 | "main": "index.js", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/sraoss/grafana-sqldb-datasource" 10 | }, 11 | "author": "SRA OSS, Inc. Japan", 12 | "license": "Apache 2.0", 13 | "bugs": { 14 | "url": "https://github.com/sraoss/grafana-sqldb-datasource/issues" 15 | }, 16 | "devDependencies": { 17 | "babel": "~6.5.1", 18 | "grunt": "~0.4.5", 19 | "grunt-babel": "~6.0.0", 20 | "grunt-contrib-clean": "~0.6.0", 21 | "grunt-contrib-copy": "~0.8.2", 22 | "grunt-contrib-uglify": "~0.11.0", 23 | "grunt-contrib-watch": "^0.6.1", 24 | "grunt-systemjs-builder": "^0.2.5", 25 | "grunt-typescript": "", 26 | "load-grunt-tasks": "~3.2.0" 27 | }, 28 | "dependencies": { 29 | "lodash": "~4.0.0", 30 | "babel-plugin-transform-es2015-modules-systemjs": "^6.5.0", 31 | "babel-plugin-transform-es2015-for-of": "^6.5.0", 32 | "babel-preset-es2015": "^6.5.0" 33 | }, 34 | "homepage": "https://github.com/sraoss/grafana-sqldb-datasource" 35 | } 36 | -------------------------------------------------------------------------------- /plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "datasource", 3 | "name": "SqlDB", 4 | "id": "sqldb", 5 | 6 | "defaultMatchFormat": "regex values", 7 | "metrics": true, 8 | "annotations": true, 9 | 10 | "info": { 11 | "description": "SqlDB data source which provides support for MySQL, PostgreSQL and ClickHouse.", 12 | "author": { 13 | "name": "SRA OSS, Inc. Japan", 14 | "url": "http://www.sraoss.co.jp/" 15 | }, 16 | "keywords": ["sqldb", "datasource"], 17 | "links": [ 18 | {"name": "Project site", "url": "https://github.com/sraoss/grafana-sqldb-datasource"}, 19 | {"name": "Apache 2.0", "url": ""} 20 | ], 21 | "screenshots": [ 22 | ], 23 | "version": "1.2", 24 | "updated": "2017-03-12" 25 | }, 26 | 27 | "dependencies": { 28 | "grafanaVersion": "3.x.x", 29 | "plugins": [ ] 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/public/app/core/utils/datemath.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | export declare function parse(text: any, roundUp?: any): any; 3 | export declare function isValid(text: any): any; 4 | export declare function parseDateMath(mathString: any, time: any, roundUp?: any): any; 5 | -------------------------------------------------------------------------------- /src/public/app/features/panel/query_ctrl.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | export declare class QueryCtrl { 3 | $scope: any; 4 | private $injector; 5 | target: any; 6 | datasource: any; 7 | panelCtrl: any; 8 | panel: any; 9 | hasRawMode: boolean; 10 | error: string; 11 | constructor($scope: any, $injector: any); 12 | refresh(): void; 13 | } 14 | -------------------------------------------------------------------------------- /src/public/app/headers/common.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare var System: any; 4 | 5 | // dummy modules 6 | declare module 'app/core/config' { 7 | var config: any; 8 | export default config; 9 | } 10 | 11 | declare module 'lodash' { 12 | var lodash: any; 13 | export default lodash; 14 | } 15 | 16 | declare module 'moment' { 17 | var moment: any; 18 | export default moment; 19 | } 20 | 21 | declare module 'angular' { 22 | var angular: any; 23 | export default angular; 24 | } 25 | 26 | declare module 'jquery' { 27 | var jquery: any; 28 | export default jquery; 29 | } 30 | 31 | declare module 'app/core/utils/kbn' { 32 | var kbn: any; 33 | export default kbn; 34 | } 35 | 36 | declare module 'app/core/store' { 37 | var store: any; 38 | export default store; 39 | } 40 | 41 | declare module 'tether' { 42 | var config: any; 43 | export default config; 44 | } 45 | 46 | declare module 'tether-drop' { 47 | var config: any; 48 | export default config; 49 | } 50 | 51 | declare module 'eventemitter3' { 52 | var config: any; 53 | export default config; 54 | } 55 | -------------------------------------------------------------------------------- /src/public/app/plugins/sdk.d.ts: -------------------------------------------------------------------------------- 1 | import { QueryCtrl } from 'app/features/panel/query_ctrl'; 2 | export declare function loadPluginCss(options: any): void; 3 | export { QueryCtrl }; 4 | -------------------------------------------------------------------------------- /src/public/module.ts: -------------------------------------------------------------------------------- 1 | import SqlDatasource from './datasource'; 2 | import {SqlQueryCtrl} from './query_ctrl'; 3 | 4 | class SqlConfigCtrl { 5 | static templateUrl = 'partials/config.html'; 6 | } 7 | 8 | class SqlQueryOptionsCtrl { 9 | static templateUrl = 'partials/query.options.html'; 10 | } 11 | 12 | class SqlAnnotationsQueryCtrl { 13 | static templateUrl = 'partials/annotations.editor.html'; 14 | } 15 | 16 | export { 17 | SqlDatasource as Datasource, 18 | SqlQueryCtrl as QueryCtrl, 19 | SqlConfigCtrl as ConfigCtrl, 20 | SqlQueryOptionsCtrl as QueryOptionsCtrl, 21 | SqlAnnotationsQueryCtrl as AnnotationsQueryCtrl, 22 | }; 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/public/partials/annotations.editor.html: -------------------------------------------------------------------------------- 1 | 2 |
Query 3 | You can use some variables to specify the condition about span: 4 | (ex1) $timeColumn > $timeFrom AND $timeColumn < $timeTo 5 | (ex2) $timeColumn > $unixFrom AND $timeColumn < $unixTo 6 | (ex3) $dateColumn > $dateFrom AND $dateColumn < $dateTo 7 | (ex4) $timeFilter 8 | 9 |
10 |
11 |
12 | 13 |
14 |
15 | 16 |
Query parts
17 |
18 |
19 |
20 | Schema 21 | 22 |
23 | 24 |
25 | Table 26 | 27 |
28 |
29 | 30 |
31 |
32 | Time 33 | 34 |
35 | 36 |
37 | Type of Time 38 | 39 |
40 |
41 | 42 |
43 |
44 | Tags 45 | 46 |
47 | 48 |
49 | Title 50 | 51 |
52 | 53 |
54 | Text 55 | 56 |
57 |
58 |
59 | -------------------------------------------------------------------------------- /src/public/partials/config.html: -------------------------------------------------------------------------------- 1 |

SqlDB Details

2 | 3 |
4 |
5 |
6 | DBMS 7 | 8 |
9 |
10 | SSL 11 | 12 | 13 |
14 |
15 | 16 | 17 | 18 | 19 |
20 |
21 |
22 | Host 23 | 24 |
25 |
26 | Port 27 | 28 |
29 |
30 | 31 |
32 |
33 | Database 34 | 35 |
36 |
37 | 38 |
39 |
40 | User 41 | 42 |
43 |
44 | Password 45 | 46 |
47 |
48 |
49 |
50 | 51 |
52 |
53 | Default group by time 54 | 56 | 57 |
58 |
59 | -------------------------------------------------------------------------------- /src/public/partials/query.editor.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |
6 | 7 |
8 | 9 |
10 |
11 | 12 | 13 | 14 | 15 |
16 | 17 |
18 | 19 |
20 | 21 |
22 | 23 |
24 | 25 |
26 |
27 |
28 |
29 | 30 |
31 |
32 | 33 | 34 | 35 |
36 | 37 |
38 | 39 |
40 | 41 |
42 | 43 |
44 | 45 |
46 |
47 |
48 |
49 | 50 |
51 |
52 | 55 |
56 | 57 |
58 | 64 | 65 |
66 | 67 |
68 | 72 |
73 | 74 |
75 |
76 |
77 |
78 | 79 |
80 |
81 | 84 | 85 | 92 | 93 |
94 | 95 |
96 | 100 |
101 | 102 |
103 |
104 |
105 |
106 | 107 |
108 | 109 |
110 |
111 | 112 | 113 |
114 |
115 | 116 |
117 | 118 |
119 |
120 |
121 |
122 |
123 |
124 | 125 |
126 | -------------------------------------------------------------------------------- /src/public/partials/query.options.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | 6 | Group by 7 | 9 | 10 |
11 |
12 |
13 | 36 |
37 |
38 |
39 | 40 |
41 |
42 | 43 |
44 |
Alias patterns
45 |
    46 |
  • $t = replaced with table name
  • 47 |
  • $table = replaced with table name
  • 48 |
  • $col = replaced with column name
  • 49 |
  • $alias = replaced with an alias (e.g. $country)
  • 50 |
51 |
52 | 53 |
54 |
Stacking
55 |
    56 |
  • When stacking is enabled it important that points align
  • 57 |
  • If there are missing points for one series it can cause gaps or missing bars
  • 58 |
  • Use the group by time option below your queries and specify for example >10s if your metrics are written every 10 seconds
  • 59 |
  • This will insert zeros for series that are missing tables and will make stacking work properly
  • 60 |
61 |
62 | 63 |
64 |
Group by time
65 |
    66 |
  • Group by time is important, otherwise the query could return many thousands of datapoints that will slow down Grafana
  • 67 |
  • Leave the group by time field empty for each query and it will be calculated based on time range and pixel width of the graph
  • 68 |
  • The low limit can only be set in the group by time option below your queries
  • 69 |
  • You set a low limit by adding a greater sign before the interval
  • 70 |
  • Example: >60s if you write metrics to RDB every 60 seconds
  • 71 |
72 |
Variables
73 |
    74 |
  • $timeColumn > $timeFrom AND $timeColumn < $timeTo
  • 75 |
  • $timeColumn > $unixFrom AND $timeColumn < $unixTo
  • 76 |
  • $dateColumn > $dateFrom AND $dateColumn < $dateTo
  • 77 |
  • $timeFilter - expands to time and date filter.
  • 78 |
  • $filter - expands to Ad-Hoc filters or '1'.
  • 79 | 80 |
81 |
82 |
83 | 84 | 85 | -------------------------------------------------------------------------------- /src/public/partials/query_part.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | {{part.def.type}}() 6 | -------------------------------------------------------------------------------- /src/public/query_builder.d.ts: -------------------------------------------------------------------------------- 1 | declare var test: any; 2 | export default test; 3 | -------------------------------------------------------------------------------- /src/public/query_builder.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'lodash' 3 | ], 4 | function (_) { 5 | 'use strict'; 6 | 7 | function SqlQueryBuilder(target, dbms) { 8 | this.target = target; 9 | this.dbms = dbms; 10 | } 11 | 12 | function renderTagCondition (tag, index) { 13 | var str = ""; 14 | var operator = tag.operator; 15 | var value = tag.value; 16 | if (index > 0) { 17 | str = (tag.condition || 'AND') + ' '; 18 | } 19 | 20 | // quote value unless regex or number 21 | if (isNaN(+value)) { 22 | value = "'" + value + "'"; 23 | } 24 | 25 | return str + '"' + tag.key + '" ' + operator + ' ' + value; 26 | } 27 | 28 | var p = SqlQueryBuilder.prototype; 29 | 30 | p.build = function() { 31 | return this.target.rawQuery ? this._modifyRawQuery() : this._buildQuery(); 32 | }; 33 | 34 | p.buildExploreQuery = function(type, withKey) { 35 | var query; 36 | var table; 37 | 38 | var templates = { 39 | base: { 40 | defaults: { 41 | colType: 'concat(column_name, \' : \', data_type)', 42 | numericTypes: "'numeric', 'decimal', 'bigint', 'integer', " + 43 | "'double', 'double precision', 'float'", 44 | exceptSchemaArr: "'information_schema', 'pg_catalog'" 45 | }, 46 | SCHEMA: 'SELECT schema_name FROM information_schema.schemata ORDER BY schema_name', 47 | TABLES: 'SELECT column_name FROM information_schema.tables ' + 48 | 'WHERE table_schema = \'${schema}\'' + 49 | 'ORDER BY table_name', 50 | FIELDS: 'SELECT ${colType} FROM information_schema.columns ' + 51 | 'WHERE table_schema = \'${schema}\' AND table_name = \'${table}\' ' + 52 | 'ORDER BY ordinal_position', 53 | TAG_KEYS: 'SELECT column_name FROM information_schema.columns ' + 54 | 'WHERE table_schema = \'${schema}\' AND table_name = \'${table}\' ' + 55 | 'ORDER BY ordinal_position', 56 | TAG_VALUES: 'SELECT distinct(${key}) FROM "${schema}"."${table}" ORDER BY ${key}', 57 | SET_DEFAULT: 'SELECT table_schema, table_name, ${colType} FROM information_schema.columns ' + 58 | 'WHERE table_schema NOT IN (${exceptSchemaArr}) ' + 59 | 'ORDER BY (data_type LIKE \'timestamp%\') desc, ' + 60 | '(data_type = \'datetime\') desc, ' + 61 | 'table_schema, table_name, ' + 62 | '(data_type IN (${numericTypes})) desc, ordinal_position LIMIT 1' 63 | }, 64 | 65 | postgres: { 66 | defaults: {colType: 'column_name || \' : \' || data_type'} 67 | }, 68 | 69 | clickhouse: { 70 | defaults: {schema: 'default', colType: 'name || \' : \' || type', table: '%', schema: '%'}, 71 | SCHEMA: 'SELECT name FROM system.databases ORDER BY name', 72 | TABLES: 'SELECT distinct(name) FROM system.tables WHERE database LIKE \'${schema}\' ORDER BY name', 73 | FIELDS: 'SELECT ${colType} as col FROM system.columns ' + 74 | 'WHERE database LIKE \'${schema}\' AND table LIKE \'${table}\' ORDER BY col', 75 | TAG_KEYS: 'SELECT distinct(name) FROM system.columns ' + 76 | 'WHERE database LIKE \'${schema}\' AND table LIKE \'${table}\' ORDER BY name', 77 | TAG_VALUES: 'SELECT 1', 78 | SET_DEFAULT: 'SELECT 1' 79 | } 80 | } 81 | 82 | /* Specialisations of base template */ 83 | templates.postgres = _.merge({}, templates.base, templates.postgres); 84 | 85 | var template = templates[this.dbms]; 86 | if (!template) { 87 | template = templates.base; 88 | } 89 | 90 | var vars = _.defaults(this.target, template.defaults); 91 | vars.key = withKey; 92 | switch (type) { 93 | case 'TAG_KEYS': 94 | case 'TAG_VALUES': 95 | case 'TABLES': 96 | case 'FIELDS': 97 | case 'SCHEMA': 98 | case 'SET_DEFAULT': 99 | return _.template(template[type])(vars); 100 | default: 101 | break; 102 | } 103 | 104 | if (table) { 105 | if (!table.match('^/.*/') && !table.match(/^merge\(.*\)/)) { 106 | table = '"' + table+ '"'; 107 | } 108 | query += ' FROM ' + table; 109 | } 110 | 111 | if (this.target.filters && this.target.filters.length > 0) { 112 | var whereConditions = _.reduce(this.target.filters, function(memo, tag) { 113 | // do not add a condition for the key we want to explore for 114 | if (tag.key === withKey) { 115 | return memo; 116 | } 117 | memo.push(renderTagCondition(tag, memo.length)); 118 | return memo; 119 | }, []); 120 | 121 | if (whereConditions.length > 0) { 122 | query += ' WHERE ' + whereConditions.join(' '); 123 | } 124 | } 125 | 126 | return query; 127 | }; 128 | 129 | return SqlQueryBuilder; 130 | }); 131 | -------------------------------------------------------------------------------- /src/public/query_ctrl.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import './query_part_editor'; 4 | 5 | import angular from 'angular'; 6 | import _ from 'lodash'; 7 | import SqlQueryBuilder from './query_builder'; 8 | import SqlQuery from './sql_query'; 9 | import queryPart from './query_part'; 10 | import {QueryCtrl} from 'app/plugins/sdk'; 11 | 12 | export class SqlQueryCtrl extends QueryCtrl { 13 | static templateUrl = 'partials/query.editor.html'; 14 | 15 | queryModel: SqlQuery; 16 | queryBuilder: any; 17 | resultFormats: any[]; 18 | schemaSegment: any; 19 | timeColDataTypeSegment: any; 20 | dateColDataTypeSegment: any; 21 | tagSegments: any[]; 22 | selectMenu: any; 23 | groupByMenu: any; 24 | tableSegment: any; 25 | removeTagFilterSegment: any; 26 | matchOperators: any; 27 | panel: any; 28 | datasource: any; 29 | target: any; 30 | 31 | /** @ngInject **/ 32 | constructor($scope, $injector, private templateSrv, private $q, private uiSegmentSrv) { 33 | super($scope, $injector); 34 | 35 | this.target = this.target; 36 | 37 | this.matchOperators = queryPart.getMatchOperators(this.datasource.dbms); 38 | 39 | this.queryModel = new SqlQuery(this.target, templateSrv, this.panel.scopedVars); 40 | this.queryModel.dbms = this.datasource.dbms; 41 | this.queryBuilder = new SqlQueryBuilder( 42 | this.target, this.datasource.dbms, { matchOperators: this.matchOperators } 43 | ); 44 | 45 | this.resultFormats = [ 46 | {text: 'Time series', value: 'time_series'}, 47 | {text: 'Table', value: 'table'}, 48 | {text: 'Docs', value: 'docs'}, 49 | ]; 50 | 51 | this.schemaSegment = uiSegmentSrv.newSegment( 52 | this.target.schema || {fake: true, value: '-- schema --'} 53 | ); 54 | 55 | this.tableSegment = uiSegmentSrv.newSegment( 56 | this.target.table || {fake: true, value: '-- table --'} 57 | ); 58 | 59 | this.timeColDataTypeSegment = uiSegmentSrv.newSegment( 60 | this.target.timeColDataType || {fake: true, value: '-- time : type --'} 61 | ); 62 | 63 | this.dateColDataTypeSegment = uiSegmentSrv.newSegment( 64 | this.target.dateColDataType || {value: 'date : Date'} 65 | ); 66 | 67 | this.tagSegments = []; 68 | for (let tag of this.target.tags) { 69 | if (!tag.operator) { 70 | if (/^\/.*\/$/.test(tag.value)) { 71 | tag.operator = this.matchOperators.match; 72 | } else { 73 | tag.operator = '='; 74 | } 75 | } 76 | 77 | if (tag.condition) { 78 | this.tagSegments.push(uiSegmentSrv.newCondition(tag.condition)); 79 | } 80 | 81 | this.tagSegments.push(uiSegmentSrv.newKey(tag.key)); 82 | this.tagSegments.push(uiSegmentSrv.newOperator(tag.operator)); 83 | this.tagSegments.push(uiSegmentSrv.newKeyValue(tag.value)); 84 | } 85 | 86 | this.fixTagSegments(); 87 | this.selectMenu = this.buildSelectMenu(); 88 | this.groupByMenu = _.filter(this.selectMenu, e => { 89 | return e.text != 'Aggregations' && e.text != 'Selectors'; 90 | }); 91 | this.removeTagFilterSegment = uiSegmentSrv.newSegment({ 92 | fake: true, value: '-- remove tag filter --' 93 | }); 94 | } 95 | 96 | /* 97 | setDefault() { 98 | var query = this.queryBuilder.buildExploreQuery('SET_DEFAULT'); 99 | this.datasource._seriesQuery(query).then(data => { 100 | if (!data.results[0].series[0].values) { return; } 101 | var result = data.results[0].series[0].values[0]; 102 | this.target.schema = result[0]; 103 | this.target.table = result[1]; 104 | this.target.timeColDataType = result[2]; 105 | 106 | this.schemaSegment = this.uiSegmentSrv.newSegment(this.target.schema); 107 | this.tableSegment = this.uiSegmentSrv.newSegment(this.target.table); 108 | this.timeColDataTypeSegment = this.uiSegmentSrv.newSegment(this.target.timeColDataType); 109 | }); 110 | } 111 | */ 112 | 113 | buildSelectMenu() { 114 | var dbms = this.queryModel.dbms; 115 | var categories = queryPart.getCategories(); 116 | return _.reduce(categories, function(memo, cat, key) { 117 | var menu = { 118 | text: key, 119 | submenu: cat 120 | .filter(item => { return !item.dbms || item.dbms == dbms; }) 121 | .map(item => { return {text: item.type, value: item.type}; }) 122 | }; 123 | if (menu.submenu.length > 0) { 124 | memo.push(menu); 125 | } 126 | return memo; 127 | }, []); 128 | } 129 | 130 | getGroupByOptions(part) { 131 | var query = this.queryBuilder.buildExploreQuery('TAG_KEYS'); 132 | 133 | return this.datasource.metricFindQuery(query).then(tags => { 134 | var options = []; 135 | if (!this.queryModel.hasGroupByTime()) { 136 | options.push(this.uiSegmentSrv.newSegment({value: 'time($interval)'})); 137 | } 138 | for (let tag of tags) { 139 | options.push(this.uiSegmentSrv.newSegment({value: 'tag(' + tag.text + ')'})); 140 | } 141 | return options; 142 | }).catch(this.handleQueryError.bind(this)); 143 | 144 | } 145 | 146 | addGroupByPart(cat, subitem) { 147 | this.queryModel.addGroupBy(subitem.value); 148 | this.panelCtrl.refresh(); 149 | } 150 | 151 | removeGroupByPart(part, index) { 152 | this.queryModel.removeGroupByPart(part, index); 153 | this.panelCtrl.refresh(); 154 | } 155 | 156 | addSelectPart(selectParts, cat, subitem) { 157 | this.queryModel.addSelectPart(selectParts, subitem.value); 158 | this.panelCtrl.refresh(); 159 | } 160 | 161 | removeSelectPart(selectParts, part) { 162 | this.queryModel.removeSelectPart(selectParts, part); 163 | this.panelCtrl.refresh(); 164 | } 165 | 166 | selectPartUpdated() { 167 | this.panelCtrl.refresh(); 168 | } 169 | 170 | fixTagSegments() { 171 | var count = this.tagSegments.length; 172 | var lastSegment = this.tagSegments[Math.max(count-1, 0)]; 173 | 174 | if (!lastSegment || lastSegment.type !== 'plus-button') { 175 | this.tagSegments.push(this.uiSegmentSrv.newPlusButton()); 176 | } 177 | } 178 | 179 | tableChanged() { 180 | this.target.table = this.tableSegment.value; 181 | this.panelCtrl.refresh(); 182 | } 183 | 184 | getSchemaSegments() { 185 | var schemasQuery = this.queryBuilder.buildExploreQuery('SCHEMA'); 186 | return this.datasource.metricFindQuery(schemasQuery) 187 | .then(this.transformToSegments(false)) 188 | .catch(this.handleQueryError.bind(this)); 189 | } 190 | 191 | schemaChanged() { 192 | this.target.schema = this.schemaSegment.value; 193 | this.panelCtrl.refresh(); 194 | } 195 | 196 | getTimeColDataTypeSegments() { 197 | var timeColQuery = this.queryBuilder.buildExploreQuery('FIELDS'); 198 | return this.datasource.metricFindQuery(timeColQuery) 199 | .then(this.transformToSegments(false)) 200 | .catch(this.handleQueryError.bind(this)); 201 | } 202 | 203 | timeColDataTypeChanged() { 204 | this.target.timeColDataType = this.timeColDataTypeSegment.value; 205 | this.panelCtrl.refresh(); 206 | } 207 | 208 | dateColDataTypeChanged() { 209 | this.target.dateColDataType = this.dateColDataTypeSegment.value; 210 | this.panelCtrl.refresh(); 211 | } 212 | 213 | toggleEditorMode() { 214 | try { 215 | this.target.query = this.queryModel.render(false); 216 | } catch (err) { 217 | console.log('query render error'); 218 | } 219 | this.target.rawQuery = !this.target.rawQuery; 220 | } 221 | 222 | getTableSegments() { 223 | var query = this.queryBuilder.buildExploreQuery('TABLES'); 224 | return this.datasource.metricFindQuery(query) 225 | .then(this.transformToSegments(true)) 226 | .catch(this.handleQueryError.bind(this)); 227 | } 228 | 229 | getPartOptions(part) { 230 | var fieldsQuery = this.queryBuilder.buildExploreQuery('TAG_KEYS'); 231 | return this.datasource.metricFindQuery(fieldsQuery) 232 | .then(this.transformToSegments(true)) 233 | .catch(this.handleQueryError.bind(this)); 234 | } 235 | 236 | handleQueryError(err) { 237 | this.error = err.message || 'Failed to issue metric query'; 238 | return []; 239 | } 240 | 241 | transformToSegments(addTemplateVars) { 242 | return (results) => { 243 | var segments = _.map(results, segment => { 244 | return this.uiSegmentSrv.newSegment({ value: segment.text, expandable: segment.expandable }); 245 | }); 246 | 247 | if (addTemplateVars) { 248 | for (let variable of this.templateSrv.variables) { 249 | segments.unshift(this.uiSegmentSrv.newSegment({ 250 | type: 'template', value: '/^$' + variable.name + '$/', expandable: true 251 | })); 252 | segments.unshift(this.uiSegmentSrv.newSegment({ 253 | type: 'template', value: '$' + variable.name, expandable: true 254 | })); 255 | } 256 | } 257 | 258 | return segments; 259 | }; 260 | } 261 | 262 | getTagsOrValues(segment, index) { 263 | if (segment.type === 'condition') { 264 | return this.$q.when([ 265 | this.uiSegmentSrv.newSegment('AND'), this.uiSegmentSrv.newSegment('OR') 266 | ]); 267 | } 268 | if (segment.type === 'operator') { 269 | var nextValue = this.tagSegments[index+1].value; 270 | if (/^\/.*\/$/.test(nextValue)) { 271 | return this.$q.when(this.uiSegmentSrv.newOperators([ 272 | this.matchOperators.match, this.matchOperators.not 273 | ])); 274 | } else { 275 | return this.$q.when(this.uiSegmentSrv.newOperators([ 276 | '=', '<>', '<', '<=', '>', '>=', 'IN', 'NOT IN' 277 | ])); 278 | } 279 | } 280 | 281 | var query, addTemplateVars; 282 | if (segment.type === 'key' || segment.type === 'plus-button') { 283 | query = this.queryBuilder.buildExploreQuery('TAG_KEYS'); 284 | addTemplateVars = false; 285 | } else if (segment.type === 'value') { 286 | query = this.queryBuilder.buildExploreQuery('TAG_VALUES', this.tagSegments[index-2].value); 287 | addTemplateVars = true; 288 | } 289 | 290 | return this.datasource.metricFindQuery(query) 291 | .then(this.transformToSegments(addTemplateVars)) 292 | .then(results => { 293 | if (segment.type === 'key') { 294 | results.splice(0, 0, angular.copy(this.removeTagFilterSegment)); 295 | } 296 | return results; 297 | }) 298 | .catch(this.handleQueryError.bind(this)); 299 | } 300 | 301 | getFieldSegments() { 302 | var fieldsQuery = this.queryBuilder.buildExploreQuery('TAG_KEYS'); 303 | return this.datasource.metricFindQuery(fieldsQuery) 304 | .then(this.transformToSegments(false)) 305 | .catch(this.handleQueryError); 306 | } 307 | 308 | tagSegmentUpdated(segment, index) { 309 | this.tagSegments[index] = segment; 310 | 311 | // handle remove tag condition 312 | if (segment.value === this.removeTagFilterSegment.value) { 313 | this.tagSegments.splice(index, 3); 314 | if (this.tagSegments.length === 0) { 315 | this.tagSegments.push(this.uiSegmentSrv.newPlusButton()); 316 | } else if (this.tagSegments.length > 2) { 317 | this.tagSegments.splice(Math.max(index-1, 0), 1); 318 | if (this.tagSegments[this.tagSegments.length-1].type !== 'plus-button') { 319 | this.tagSegments.push(this.uiSegmentSrv.newPlusButton()); 320 | } 321 | } 322 | } else { 323 | if (segment.type === 'plus-button') { 324 | if (index > 2) { 325 | this.tagSegments.splice(index, 0, this.uiSegmentSrv.newCondition('AND')); 326 | } 327 | this.tagSegments.push(this.uiSegmentSrv.newOperator('=')); 328 | this.tagSegments.push(this.uiSegmentSrv.newFake( 329 | 'select tag value', 'value', 'query-segment-value' 330 | )); 331 | segment.type = 'key'; 332 | segment.cssClass = 'query-segment-key'; 333 | } 334 | 335 | if ((index+1) === this.tagSegments.length) { 336 | this.tagSegments.push(this.uiSegmentSrv.newPlusButton()); 337 | } 338 | } 339 | 340 | this.rebuildTargetTagConditions(); 341 | } 342 | 343 | rebuildTargetTagConditions() { 344 | var tags = []; 345 | var tagIndex = 0; 346 | var tagOperator = ""; 347 | 348 | _.each(this.tagSegments, (segment2, index) => { 349 | if (segment2.type === 'key') { 350 | if (tags.length === 0) { 351 | tags.push({}); 352 | } 353 | tags[tagIndex].key = segment2.value; 354 | } else if (segment2.type === 'value') { 355 | tagOperator = this.getTagValueOperator(segment2.value, tags[tagIndex].operator); 356 | if (tagOperator) { 357 | this.tagSegments[index-1] = this.uiSegmentSrv.newOperator(tagOperator); 358 | tags[tagIndex].operator = tagOperator; 359 | } 360 | tags[tagIndex].value = segment2.value; 361 | } else if (segment2.type === 'condition') { 362 | tags.push({ condition: segment2.value }); 363 | tagIndex += 1; 364 | } else if (segment2.type === 'operator') { 365 | tags[tagIndex].operator = segment2.value; 366 | } 367 | }); 368 | 369 | this.target.tags = tags; 370 | this.panelCtrl.refresh(); 371 | } 372 | 373 | getTagValueOperator(tagValue, tagOperator) { 374 | if (tagOperator !== this.matchOperators.match && 375 | tagOperator !== this.matchOperators.not && 376 | /^\/.*\/$/.test(tagValue)) { 377 | return this.matchOperators.match; 378 | 379 | } else if ((tagOperator === this.matchOperators.match || 380 | tagOperator === this.matchOperators.not) && 381 | /^(?!\/.*\/$)/.test(tagValue)) { 382 | return '='; 383 | } 384 | } 385 | 386 | getCollapsedText() { 387 | return this.queryModel.render(false); 388 | } 389 | } 390 | 391 | -------------------------------------------------------------------------------- /src/public/query_part.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import _ from 'lodash'; 4 | import extraFunctions from './query_part_funcs'; 5 | 6 | var index = {}; 7 | var categories = { 8 | Aggregations: [], 9 | Selectors: [], 10 | Math: [], 11 | Transform: [], 12 | Aliasing: [], 13 | Fields: [], 14 | }; 15 | 16 | class QueryPartDef { 17 | type: string; 18 | params: any[]; 19 | defaultParams: any[]; 20 | renderer: any; 21 | category: any; 22 | addStrategy: any; 23 | dbms: any[]; 24 | 25 | constructor(options: any) { 26 | this.type = options.type; 27 | this.params = options.params; 28 | this.defaultParams = options.defaultParams; 29 | this.renderer = options.renderer; 30 | this.category = options.category; 31 | this.addStrategy = options.addStrategy; 32 | this.dbms = options.dbms; 33 | } 34 | 35 | static register(options: any) { 36 | index[options.type] = new QueryPartDef(options); 37 | options.category.push(index[options.type]); 38 | } 39 | } 40 | 41 | function functionRenderer(part, innerExpr) { 42 | var str = part.def.type + '('; 43 | var parameters = _.map(part.params, (value, index) => { 44 | var paramType = part.def.params[index]; 45 | if (paramType.type === 'time') { 46 | if (value === 'auto') { 47 | value = '$interval'; 48 | } 49 | } 50 | if (paramType.quote === 'single') { 51 | return "'" + value + "'"; 52 | } else if (paramType.quote === 'double') { 53 | return '"' + value + '"'; 54 | } 55 | return value; 56 | }); 57 | 58 | if (innerExpr) { 59 | parameters.unshift(innerExpr); 60 | } 61 | return str + parameters.join(', ') + ')'; 62 | } 63 | 64 | function parametricFunctionRenderer(part, innerExpr) { 65 | var str = part.def.type; 66 | var parameters = _.map(part.params, (value, index) => { 67 | var paramType = part.def.params[index]; 68 | if (paramType.type === 'time') { 69 | if (value === 'auto') { 70 | value = '$interval'; 71 | } 72 | } 73 | if (paramType.quote === 'single') { 74 | return "'" + value + "'"; 75 | } else if (paramType.quote === 'double') { 76 | return '"' + value + '"'; 77 | } 78 | return value; 79 | }); 80 | 81 | if (innerExpr) { 82 | parameters.unshift(innerExpr); 83 | } 84 | return str + '(' + parameters.pop() + ')(' + parameters.join(', ') + ')'; 85 | } 86 | 87 | function aliasRenderer(part, innerExpr) { 88 | return innerExpr + ' AS ' + part.params[0]; 89 | } 90 | 91 | function suffixRenderer(part, innerExpr) { 92 | return innerExpr + ' ' + part.params[0]; 93 | } 94 | 95 | function identityRenderer(part, innerExpr) { 96 | return part.params[0]; 97 | } 98 | 99 | function quotedIdentityRenderer(part, innerExpr) { 100 | return '"' + part.params[0] + '"'; 101 | } 102 | 103 | function fieldRenderer(part, innerExpr) { 104 | return part.params[0]; 105 | } 106 | 107 | function replaceAggregationAddStrategy(selectParts, partModel) { 108 | // look for existing aggregation 109 | for (var i = 0; i < selectParts.length; i++) { 110 | var part = selectParts[i]; 111 | if (part.def.category === categories.Aggregations) { 112 | selectParts[i] = partModel; 113 | return; 114 | } 115 | if (part.def.category === categories.Selectors) { 116 | selectParts[i] = partModel; 117 | return; 118 | } 119 | } 120 | 121 | selectParts.splice(1, 0, partModel); 122 | } 123 | 124 | function addMathStrategy(selectParts, partModel) { 125 | var partCount = selectParts.length; 126 | if (partCount > 0) { 127 | // if last is math, replace it 128 | if (selectParts[partCount-1].def.type === 'math') { 129 | selectParts[partCount-1] = partModel; 130 | return; 131 | } 132 | // if next to last is math, replace it 133 | if (selectParts[partCount-2].def.type === 'math') { 134 | selectParts[partCount-2] = partModel; 135 | return; 136 | } else if (selectParts[partCount-1].def.type === 'alias') { // if last is alias add it before 137 | selectParts.splice(partCount-1, 0, partModel); 138 | return; 139 | } 140 | } 141 | selectParts.push(partModel); 142 | } 143 | 144 | function addAliasStrategy(selectParts, partModel) { 145 | var partCount = selectParts.length; 146 | if (partCount > 0) { 147 | // if last is alias, replace it 148 | if (selectParts[partCount-1].def.type === 'alias') { 149 | selectParts[partCount-1] = partModel; 150 | return; 151 | } 152 | } 153 | selectParts.push(partModel); 154 | } 155 | 156 | function addFieldStrategy(selectParts, partModel, query) { 157 | // copy all parts 158 | var parts = _.map(selectParts, function(part: any) { 159 | return new QueryPart({type: part.def.type, params: _.clone(part.params)}); 160 | }); 161 | 162 | query.selectModels.push(parts); 163 | } 164 | 165 | function addTransformStrategy(selectParts, partModel) { 166 | var partCount = selectParts.length; 167 | if (partCount > 0) { 168 | // if last is alias add it before 169 | if (selectParts[partCount-1].def.type === 'alias') { 170 | selectParts.splice(partCount-1, 0, partModel); 171 | return; 172 | } 173 | } 174 | selectParts.push(partModel); 175 | } 176 | 177 | // Defaults 178 | var partRenderer = { 179 | functionRenderer: functionRenderer, 180 | parametricFunctionRenderer: parametricFunctionRenderer, 181 | aliasRenderer: aliasRenderer, 182 | suffixRenderer: suffixRenderer, 183 | identityRenderer: identityRenderer, 184 | quotedIdentityRenderer: quotedIdentityRenderer, 185 | fieldRenderer: fieldRenderer 186 | } 187 | 188 | var partDefault = { 189 | Aggregations: { 190 | addStrategy: replaceAggregationAddStrategy, 191 | category: categories.Aggregations, 192 | params: [], 193 | defaultParams: [], 194 | renderer: functionRenderer, 195 | }, 196 | Selectors: { 197 | addStrategy: replaceAggregationAddStrategy, 198 | category: categories.Selectors, 199 | params: [], 200 | defaultParams: [], 201 | renderer: functionRenderer, 202 | }, 203 | Transform: { 204 | addStrategy: addTransformStrategy, 205 | renderer: functionRenderer, 206 | params: [], 207 | defaultParams: [], 208 | category: categories.Transform, 209 | }, 210 | }; 211 | 212 | QueryPartDef.register({ 213 | type: 'field', 214 | addStrategy: addFieldStrategy, 215 | category: categories.Fields, 216 | params: [{type: 'field', dynamicLookup: true}], 217 | defaultParams: ['value'], 218 | renderer: fieldRenderer, 219 | }); 220 | 221 | // Aggregations 222 | QueryPartDef.register(_.assign({}, partDefault.Aggregations, { type: 'count' })); 223 | QueryPartDef.register(_.assign({}, partDefault.Aggregations, { type: 'avg' })); 224 | QueryPartDef.register(_.assign({}, partDefault.Aggregations, { type: 'sum' })); 225 | QueryPartDef.register(_.assign({}, partDefault.Aggregations, { type: 'median' })); 226 | 227 | // transformations 228 | 229 | QueryPartDef.register(_.assign({}, partDefault.Transform, { 230 | type: 'time', 231 | params: [{ name: "interval", type: "time", options: ['auto', '1s', '10s', '1m', '5m', '10m', '15m', '1h'] }], 232 | defaultParams: ['auto'], 233 | })); 234 | 235 | // Selectors 236 | 237 | QueryPartDef.register(_.assign({}, partDefault.Selectors, { type: 'max' })); 238 | QueryPartDef.register(_.assign({}, partDefault.Selectors, { type: 'min' })); 239 | 240 | QueryPartDef.register({ 241 | type: 'tag', 242 | category: categories.Fields, 243 | params: [{name: 'tag', type: 'string', dynamicLookup: true}], 244 | defaultParams: ['tag'], 245 | renderer: fieldRenderer, 246 | }); 247 | 248 | QueryPartDef.register({ 249 | type: 'math', 250 | addStrategy: addMathStrategy, 251 | category: categories.Math, 252 | params: [{ name: "expr", type: "string"}], 253 | defaultParams: [' / 100'], 254 | renderer: suffixRenderer, 255 | }); 256 | 257 | QueryPartDef.register({ 258 | type: 'alias', 259 | addStrategy: addAliasStrategy, 260 | category: categories.Aliasing, 261 | params: [{ name: "name", type: "string", quote: 'double'}], 262 | defaultParams: ['alias'], 263 | renderMode: 'suffix', 264 | renderer: aliasRenderer, 265 | }); 266 | 267 | // Include dbms specific functions 268 | _.each(extraFunctions, (functionCategories, dbms) => { 269 | _.each(functionCategories, (functions, category) => { 270 | /* Break to category and subcategory */ 271 | var catItem = category.split('_'); 272 | var categorySub = null; 273 | if (catItem[1]) { 274 | if (!categories[catItem[1]]) { 275 | categories[catItem[1]] = []; 276 | } 277 | categorySub = categories[catItem[1]]; 278 | } 279 | var defaults = partDefault[catItem[0]]; 280 | /* Register all function specs with updated defaults */ 281 | _.each(functions, spec => { 282 | if (spec.renderer) { 283 | spec.renderer = partRenderer[spec.renderer]; 284 | } 285 | if (categorySub) { 286 | spec.category = categorySub; 287 | } 288 | spec.dbms = dbms; 289 | /* Update defaults, the object allocations are kind 290 | of sluggish here, so let's make it less pretty */ 291 | for (var k in defaults) { 292 | if (spec[k] === undefined) { 293 | spec[k] = defaults[k]; 294 | } 295 | } 296 | QueryPartDef.register(spec); 297 | }); 298 | }); 299 | }); 300 | 301 | class QueryPart { 302 | part: any; 303 | def: QueryPartDef; 304 | params: any[]; 305 | text: string; 306 | 307 | constructor(part: any) { 308 | this.part = part; 309 | this.def = index[part.type]; 310 | if (!this.def) { 311 | throw {message: 'Could not find query part ' + part.type}; 312 | } 313 | 314 | part.params = part.params || _.clone(this.def.defaultParams); 315 | this.params = part.params; 316 | this.updateText(); 317 | } 318 | 319 | render(innerExpr: string) { 320 | return this.def.renderer(this, innerExpr); 321 | } 322 | 323 | hasMultipleParamsInString (strValue, index) { 324 | if (strValue.indexOf(',') === -1) { 325 | return false; 326 | } 327 | 328 | return this.def.params[index + 1] && this.def.params[index + 1].optional; 329 | } 330 | 331 | updateParam (strValue, index) { 332 | // handle optional parameters 333 | // if string contains ',' and next param is optional, split and update both 334 | if (this.hasMultipleParamsInString(strValue, index)) { 335 | _.each(strValue.split(','), function(partVal: string, idx) { 336 | this.updateParam(partVal.trim(), idx); 337 | }, this); 338 | return; 339 | } 340 | 341 | if (strValue === '' && this.def.params[index].optional) { 342 | this.params.splice(index, 1); 343 | } else { 344 | this.params[index] = strValue; 345 | } 346 | 347 | this.part.params = this.params; 348 | this.updateText(); 349 | } 350 | 351 | updateText() { 352 | if (this.params.length === 0) { 353 | this.text = this.def.type + '()'; 354 | return; 355 | } 356 | 357 | var text = this.def.type + '('; 358 | text += this.params.join(', '); 359 | text += ')'; 360 | this.text = text; 361 | } 362 | } 363 | 364 | export default { 365 | create: function(part): any { 366 | return new QueryPart(part); 367 | }, 368 | 369 | getCategories: function() { 370 | return categories; 371 | }, 372 | 373 | getMatchOperators: function(dbms) { 374 | var rtn = null; 375 | switch (dbms) { 376 | case 'postgres': 377 | rtn = { 'match': '~*', 'not': '!~*' }; 378 | break; 379 | case 'mysql': 380 | rtn = { 'match': 'REGEXP', 'not': 'NOT REGEXP' }; 381 | break; 382 | case 'clickhouse': 383 | rtn = { 'match': 'LIKE', 'not': 'NOT LIKE' }; 384 | break; 385 | default: 386 | break; 387 | }; 388 | 389 | return rtn; 390 | }, 391 | }; 392 | -------------------------------------------------------------------------------- /src/public/query_part_editor.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'angular', 3 | 'lodash', 4 | 'jquery', 5 | ], 6 | function (angular, _, $) { 7 | 'use strict'; 8 | 9 | angular 10 | .module('grafana.directives') 11 | .directive('sqlQueryPartEditor', function($compile, templateSrv) { 12 | 13 | var paramTemplate = ''; 15 | return { 16 | restrict: 'E', 17 | templateUrl: 'public/plugins/sqldb/partials/query_part.html', 18 | scope: { 19 | part: "=", 20 | removeAction: "&", 21 | partUpdated: "&", 22 | getOptions: "&", 23 | }, 24 | link: function postLink($scope, elem) { 25 | var part = $scope.part; 26 | var partDef = part.def; 27 | var $paramsContainer = elem.find('.query-part-parameters'); 28 | var $controlsContainer = elem.find('.tight-form-func-controls'); 29 | 30 | function clickFuncParam(paramIndex) { 31 | /*jshint validthis:true */ 32 | var $link = $(this); 33 | var $input = $link.next(); 34 | 35 | $input.val(part.params[paramIndex]); 36 | $input.css('width', ($link.width() + 16) + 'px'); 37 | 38 | $link.hide(); 39 | $input.show(); 40 | $input.focus(); 41 | $input.select(); 42 | 43 | var typeahead = $input.data('typeahead'); 44 | if (typeahead) { 45 | $input.val(''); 46 | typeahead.lookup(); 47 | } 48 | } 49 | 50 | function inputBlur(paramIndex) { 51 | /*jshint validthis:true */ 52 | var $input = $(this); 53 | var $link = $input.prev(); 54 | var newValue = $input.val(); 55 | 56 | if (newValue !== '' || part.def.params[paramIndex].optional) { 57 | $link.html(templateSrv.highlightVariablesAsHtml(newValue)); 58 | 59 | part.updateParam($input.val(), paramIndex); 60 | $scope.$apply($scope.partUpdated); 61 | } 62 | 63 | $input.hide(); 64 | $link.show(); 65 | } 66 | 67 | function inputKeyPress(paramIndex, e) { 68 | /*jshint validthis:true */ 69 | if(e.which === 13) { 70 | inputBlur.call(this, paramIndex); 71 | } 72 | } 73 | 74 | function inputKeyDown() { 75 | /*jshint validthis:true */ 76 | this.style.width = (3 + this.value.length) * 8 + 'px'; 77 | } 78 | 79 | function addTypeahead($input, param, paramIndex) { 80 | if (!param.options && !param.dynamicLookup) { 81 | return; 82 | } 83 | 84 | var typeaheadSource = function (query, callback) { 85 | if (param.options) { return param.options; } 86 | 87 | $scope.$apply(function() { 88 | $scope.getOptions().then(function(result) { 89 | var dynamicOptions = _.map(result, function(op) { return op.value; }); 90 | callback(dynamicOptions); 91 | }); 92 | }); 93 | }; 94 | 95 | $input.attr('data-provide', 'typeahead'); 96 | var options = param.options; 97 | if (param.type === 'int') { 98 | options = _.map(options, function(val) { return val.toString(); }); 99 | } 100 | 101 | $input.typeahead({ 102 | source: typeaheadSource, 103 | minLength: 0, 104 | items: 1000, 105 | updater: function (value) { 106 | setTimeout(function() { 107 | inputBlur.call($input[0], paramIndex); 108 | }, 0); 109 | return value; 110 | } 111 | }); 112 | 113 | var typeahead = $input.data('typeahead'); 114 | typeahead.lookup = function () { 115 | this.query = this.$element.val() || ''; 116 | var items = this.source(this.query, $.proxy(this.process, this)); 117 | return items ? this.process(items) : items; 118 | }; 119 | } 120 | 121 | $scope.toggleControls = function() { 122 | var targetDiv = elem.closest('.tight-form'); 123 | 124 | if (elem.hasClass('show-function-controls')) { 125 | elem.removeClass('show-function-controls'); 126 | targetDiv.removeClass('has-open-function'); 127 | $controlsContainer.hide(); 128 | return; 129 | } 130 | 131 | elem.addClass('show-function-controls'); 132 | targetDiv.addClass('has-open-function'); 133 | $controlsContainer.show(); 134 | }; 135 | 136 | $scope.removeActionInternal = function() { 137 | $scope.toggleControls(); 138 | $scope.removeAction(); 139 | }; 140 | 141 | function addElementsAndCompile() { 142 | _.each(partDef.params, function(param, index) { 143 | if (param.optional && part.params.length <= index) { 144 | return; 145 | } 146 | 147 | if (index > 0) { 148 | $(', ').appendTo($paramsContainer); 149 | } 150 | 151 | var paramValue = templateSrv.highlightVariablesAsHtml(part.params[index] || param.name); 152 | var $paramLink = $('' + paramValue + ''); 153 | var $input = $(paramTemplate); 154 | 155 | $paramLink.appendTo($paramsContainer); 156 | $input.appendTo($paramsContainer); 157 | 158 | $input.blur(_.partial(inputBlur, index)); 159 | $input.keyup(inputKeyDown); 160 | $input.keypress(_.partial(inputKeyPress, index)); 161 | $paramLink.click(_.partial(clickFuncParam, index)); 162 | 163 | addTypeahead($input, param, index); 164 | }); 165 | } 166 | 167 | function relink() { 168 | $paramsContainer.empty(); 169 | addElementsAndCompile(); 170 | } 171 | 172 | relink(); 173 | } 174 | }; 175 | 176 | }); 177 | 178 | }); 179 | -------------------------------------------------------------------------------- /src/public/response_parser.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import _ from 'lodash'; 4 | 5 | export default class ResponseParser { 6 | 7 | parse(query, results) { 8 | if (!results || results.results.length === 0) { return []; } 9 | 10 | var sqlResults = results.results[0]; 11 | if (!sqlResults.series) { 12 | return []; 13 | } 14 | 15 | var res = {}; 16 | _.each(sqlResults.series, serie => { 17 | _.each(serie.values, value => { 18 | if (_.isArray(value)) { 19 | addUnique(res, value[0]); 20 | } else { 21 | addUnique(res, value); 22 | } 23 | }); 24 | }); 25 | 26 | return _.map(res, value => { 27 | return { text: value}; 28 | }); 29 | } 30 | } 31 | 32 | function addUnique(arr, value) { 33 | arr[value] = value; 34 | } 35 | -------------------------------------------------------------------------------- /src/public/sql_query.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import _ from 'lodash'; 4 | import queryPart from './query_part'; 5 | 6 | export default class SqlQuery { 7 | dbms: string; 8 | target: any; 9 | selectModels: any[]; 10 | queryBuilder: any; 11 | groupByParts: any; 12 | templateSrv: any; 13 | scopedVars: any; 14 | 15 | /** @ngInject */ 16 | constructor(target, templateSrv?, scopedVars?) { 17 | this.dbms = null; 18 | this.target = target; 19 | this.templateSrv = templateSrv; 20 | this.scopedVars = scopedVars; 21 | 22 | target.schema = target.schema; 23 | target.dsType = 'sqldb'; 24 | target.timeColDataType = target.timeColDataType; 25 | target.resultFormat = target.resultFormat || 'time_series'; 26 | target.tags = target.tags || []; 27 | target.filters = target.filters || []; 28 | target.groupBy = target.groupBy || [ 29 | {type: 'time', params: ['$interval']}, 30 | ]; 31 | target.targetLists = target.targetLists || [[ 32 | {type: 'field', params: ['*']}, 33 | {type: 'count', params: []}, 34 | ]]; 35 | target.alias = target.alias || '$t.$col'; 36 | 37 | this.updateProjection(); 38 | } 39 | 40 | updateProjection() { 41 | this.selectModels = _.map(this.target.targetLists, function(parts: any) { 42 | return _.map(parts, queryPart.create); 43 | }); 44 | this.groupByParts = _.map(this.target.groupBy, queryPart.create); 45 | } 46 | 47 | updatePersistedParts() { 48 | this.target.targetLists = _.map(this.selectModels, function(selectParts) { 49 | return _.map(selectParts, function(part: any) { 50 | return {type: part.def.type, params: part.params}; 51 | }); 52 | }); 53 | } 54 | 55 | hasGroupByTime() { 56 | return _.find(this.target.groupBy, (g: any) => g.type === 'time'); 57 | } 58 | 59 | addGroupBy(type) { 60 | var partModel = queryPart.create({type: type}); 61 | var partCount = this.target.groupBy.length; 62 | 63 | if (partCount === 0) { 64 | this.target.groupBy.push(partModel.part); 65 | } else if (type === 'time') { 66 | this.target.groupBy.splice(0, 0, partModel.part); 67 | } else { 68 | this.target.groupBy.push(partModel.part); 69 | } 70 | 71 | this.updateProjection(); 72 | } 73 | 74 | removeGroupByPart(part, index) { 75 | var categories = queryPart.getCategories(); 76 | 77 | if (part.def.type === 'time') { 78 | // remove aggregations 79 | this.target.targetLists = _.map(this.target.targetLists, (s: any) => { 80 | return _.filter(s, (part: any) => { 81 | var partModel = queryPart.create(part); 82 | if (partModel.def.category === categories.Aggregations) { 83 | return false; 84 | } 85 | if (partModel.def.category === categories.Selectors) { 86 | return false; 87 | } 88 | return true; 89 | }); 90 | }); 91 | } 92 | 93 | this.target.groupBy.splice(index, 1); 94 | this.updateProjection(); 95 | } 96 | 97 | removeSelect(index: number) { 98 | this.target.targetLists.splice(index, 1); 99 | this.updateProjection(); 100 | } 101 | 102 | removeSelectPart(selectParts, part) { 103 | // if we remove the field remove the whole statement 104 | if (part.def.type === 'field') { 105 | if (this.selectModels.length > 1) { 106 | var modelsIndex = _.indexOf(this.selectModels, selectParts); 107 | this.selectModels.splice(modelsIndex, 1); 108 | } 109 | } else { 110 | var partIndex = _.indexOf(selectParts, part); 111 | selectParts.splice(partIndex, 1); 112 | } 113 | 114 | this.updatePersistedParts(); 115 | } 116 | 117 | addSelectPart(selectParts, type) { 118 | var partModel = queryPart.create({type: type}); 119 | partModel.def.addStrategy(selectParts, partModel, this); 120 | this.updatePersistedParts(); 121 | } 122 | 123 | private renderTagCondition(tag, index, interpolate) { 124 | var str = ""; 125 | var operator = tag.operator; 126 | var value = tag.value; 127 | 128 | if (!operator) { 129 | if (/^\/.*\/$/.test(value)) { 130 | operator = '=~'; 131 | } else { 132 | operator = '='; 133 | } 134 | } 135 | 136 | // Support wildcard values to behave like regular filters 137 | if (interpolate) { 138 | for (var i in this.templateSrv.variables) { 139 | var v = this.templateSrv.variables[i]; 140 | if (v.name == value.slice(1) && this.templateSrv.isAllValue(v.current.value)) { 141 | return str; 142 | } 143 | } 144 | } 145 | 146 | // quote value unless regex or number(s) 147 | var matchOperators = queryPart.getMatchOperators(this.dbms); 148 | if (operator.indexOf('IN') > -1) { 149 | // IN/NOT IN operators may have a tupe on the right side 150 | value = value.replace(/[()]/g, ''); 151 | if (interpolate) { 152 | value = this.templateSrv.replace(value, this.scopedVars); 153 | } 154 | // Check if the array is all numbers 155 | var values = _.map(value.split(','), x => x.trim()); 156 | var intArray = _.reduce(values, (memo, x) => { 157 | return memo && !isNaN(+x); 158 | }, true); 159 | // Force quotes and braces 160 | for (var i in values) { 161 | values[i] = values[i].replace(/\'\"/,''); 162 | if (!intArray) { 163 | values[i] = "'" + value.replace(/\\/g, '\\\\') + "'"; 164 | } 165 | } 166 | value = '(' + values.join(', ') + ')'; 167 | } else if (!matchOperators || (operator !== matchOperators.match && operator !== matchOperators.not)) { 168 | if (interpolate) { 169 | value = this.templateSrv.replace(value, this.scopedVars); 170 | } 171 | 172 | if (!operator.startsWith('>') && !operator.startsWith('<') && isNaN(+value)) { 173 | value = "'" + value.replace(/\\/g, '\\\\') + "'"; 174 | } 175 | } else if (interpolate) { 176 | value = this.templateSrv.replace(value, this.scopedVars, 'regex'); 177 | value = "'" + value.replace(/^\//, '').replace(/\/$/, '') + "'"; 178 | } else if (isNaN(+value)) { 179 | value = "'" + value.replace(/^\//, '').replace(/\/$/, '') + "'"; 180 | } 181 | 182 | if (index > 0) { 183 | str = (tag.condition || 'AND') + ' '; 184 | } 185 | return str + tag.key + ' ' + operator + ' ' + value; 186 | } 187 | 188 | gettableAndSchema(interpolate) { 189 | var schema = this.target.schema; 190 | var table = this.target.table || 'table'; 191 | 192 | if (!table.match('^/.*/')) { 193 | table = table; 194 | } else if (interpolate) { 195 | table = this.templateSrv.replace(table, this.scopedVars, 'regex'); 196 | } 197 | 198 | if (schema !== 'default') { 199 | schema = this.target.schema + '.'; 200 | } else { 201 | schema = ""; 202 | } 203 | 204 | var rtn = schema + table; 205 | 206 | return rtn; 207 | } 208 | 209 | render(interpolate?) { 210 | var target = this.target; 211 | 212 | if (target.rawQuery) { 213 | /* There is no structural information about raw query, so best 214 | effort parse the GROUP BY column to be able to detect series. 215 | Only grouped by expressions or their aliases are inferred. */ 216 | var parts = /GROUP BY (.+)\s*(?:ORDER|LIMIT|HAVING|$)/.exec(target.query); 217 | if (parts) { 218 | var last = parts[parts.length - 1]; 219 | target.groupBy = _.map(last.split(','), (e, i) => { 220 | return {type: i > 0 ? 'field' : 'time', params: [e.trim()]}; 221 | }); 222 | } 223 | if (interpolate) { 224 | var q = this.templateSrv.replace(target.query, this.scopedVars, 'regex'); 225 | /* Support filter expansion in raw queries as well. */ 226 | var conditions = _.map(target.filters, (tag, index) => { 227 | return this.renderTagCondition(tag, index, interpolate); 228 | }); 229 | return q.replace(/\$filter/, (conditions.length > 0 ? conditions.join(' ') : '1')); 230 | } else { 231 | return target.query; 232 | } 233 | } 234 | 235 | var hasTimeGroupBy = false; 236 | var selectClause = []; 237 | var groupByClause = []; 238 | var orderByClause = ''; 239 | var usePositions = (this.dbms !== 'clickhouse'); 240 | 241 | if (target.groupBy.length !== 0) { 242 | _.each(this.target.groupBy, function(groupBy, i) { 243 | 244 | var alias = null; 245 | switch (groupBy.type) { 246 | case 'time': 247 | selectClause.push('$unixtimeColumn * 1000 AS time_msec'); 248 | if (!usePositions) { 249 | alias = 'time_msec'; 250 | } 251 | break; 252 | 253 | case 'alias': 254 | var part = selectClause.pop(); 255 | selectClause.push(queryPart.create(groupBy).render(part)); 256 | groupByClause.pop(); 257 | alias = groupBy.params[0]; 258 | break; 259 | 260 | default: 261 | var part = queryPart.create(groupBy).render(); 262 | selectClause.push(part); 263 | if (!usePositions) { 264 | alias = part; 265 | } 266 | break; 267 | } 268 | 269 | if (alias !== null) { 270 | groupByClause.push(alias); 271 | } else { 272 | groupByClause.push((i + 1).toFixed(0)); 273 | } 274 | }); 275 | } 276 | 277 | var query = 'SELECT '; 278 | if (selectClause.length > 0) { 279 | query += selectClause.join(', ') + ', '; 280 | } 281 | 282 | var i, j; 283 | var targetList = ''; 284 | for (i = 0; i < this.selectModels.length; i++) { 285 | let parts = this.selectModels[i]; 286 | var selectText = ""; 287 | for (j = 0; j < parts.length; j++) { 288 | let part = parts[j]; 289 | selectText = part.render(selectText); 290 | } 291 | 292 | if (i > 0) { 293 | targetList += ', '; 294 | } 295 | targetList += selectText; 296 | } 297 | query += targetList; 298 | 299 | query += ' FROM ' + this.gettableAndSchema(interpolate) + ' WHERE '; 300 | var conditions = _.map(target.filters, (tag, index) => { 301 | return this.renderTagCondition(tag, index, interpolate); 302 | }); 303 | 304 | query += conditions.join(' '); 305 | query += (conditions.length > 0 ? ' AND ' : '') + '$timeFilter'; 306 | 307 | if (groupByClause.length > 0) { 308 | query += ' GROUP BY ' + groupByClause.join(', '); 309 | } 310 | 311 | orderByClause = groupByClause.join(', ') || targetList; 312 | query += ' ORDER BY ' + orderByClause; 313 | 314 | return query; 315 | } 316 | } 317 | -------------------------------------------------------------------------------- /src/public/sql_series.d.ts: -------------------------------------------------------------------------------- 1 | declare var test: any; 2 | export default test; 3 | -------------------------------------------------------------------------------- /src/public/sql_series.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'lodash', 3 | 'app/core/table_model', 4 | ], 5 | function (_, TableModel) { 6 | 'use strict'; 7 | 8 | function SqlSeries(options) { 9 | this.series = options.series; 10 | this.table = options.table; 11 | this.alias = options.alias; 12 | this.annotation = options.annotation; 13 | /* Flatten aliases */ 14 | this.groupBy = _.reduce(options.groupBy, function(memo, v) { 15 | if (v.type == 'alias') { 16 | memo.pop(); 17 | } 18 | memo.push(v); 19 | return memo; 20 | }, []); 21 | } 22 | 23 | var p = SqlSeries.prototype; 24 | 25 | 26 | p.getTimeSeries = function() { 27 | var output = []; 28 | var self = this; 29 | 30 | if (self.series.length === 0) { 31 | return output; 32 | } 33 | 34 | var seriesDatapoints = {}; 35 | _.each(self.series.values, function(row) { 36 | var tags = {}; 37 | var tagList = []; 38 | var addTags = true; 39 | 40 | _.each(self.groupBy, function(groupBy, k) { 41 | if (k !== 0) { 42 | tagList.push(groupBy.params[0] + ': ' + row[k]); 43 | } 44 | tags[groupBy.params[0]] = row[k]; 45 | /* Note down if format string contains tags. */ 46 | if (self.alias && self.alias.indexOf('$' + groupBy.params[0]) > -1) { 47 | addTags = false; 48 | } 49 | }); 50 | 51 | var tagsStr = ''; 52 | if (tagList.length !== 0) { 53 | tagsStr = ' {' + tagList.join(', ') + '}'; 54 | } 55 | 56 | _.each(row, function(value, i) { 57 | if (i < self.groupBy.length) { 58 | return; 59 | } 60 | 61 | var seriesName = self.table; 62 | var columnName = self.series.columns[i]; 63 | if (columnName !== 'value') { 64 | if (seriesName) { 65 | seriesName = seriesName + '.' + columnName; 66 | } else { 67 | seriesName = columnName; 68 | } 69 | } 70 | 71 | /* Do not print tags if it is a part of formatting string */ 72 | if (self.alias) { 73 | seriesName = self._getSeriesName(self.series, i, tags); 74 | } 75 | if (addTags) { 76 | seriesName = seriesName + tagsStr; 77 | } 78 | 79 | if (! seriesDatapoints[seriesName]) { 80 | seriesDatapoints[seriesName] = []; 81 | } 82 | 83 | seriesDatapoints[seriesName].push([ 84 | self._formatValue(value), // numeric value 85 | self._formatValue(row[0]) // timestamp 86 | ]); 87 | }); 88 | }); 89 | 90 | _.each(seriesDatapoints, function(datapoints, seriesName) { 91 | output.push({ target: seriesName, datapoints: datapoints }); 92 | }); 93 | 94 | return output; 95 | }; 96 | 97 | p._getSeriesName = function(series, index, tags) { 98 | var self = this; 99 | var regex = /\$(\w+)|\[\[([\s\S]+?)\]\]/g; 100 | 101 | return this.alias.replace(regex, function(match, g1, g2) { 102 | var group = g1 || g2; 103 | 104 | if (group === 't' || group === 'table') { return self.table || series.name; } 105 | if (group === 'col') { return series.columns[index]; } 106 | if (group in tags) { return tags[group]; } 107 | return match; 108 | }); 109 | }; 110 | 111 | p.getAnnotations = function () { 112 | var list = []; 113 | var self = this; 114 | 115 | _.each(this.series, function (series) { 116 | var titleCol = null; 117 | var timeCol = null; 118 | var tagsCol = null; 119 | var textCol = null; 120 | 121 | _.each(series.columns, function(column, index) { 122 | if (column === 'time') { timeCol = index; return; } 123 | if (column === 'tags') { tagsCol = index; return; } 124 | if (column === 'title') { titleCol = index; return; } 125 | if (column === 'text') { textCol = index; return; } 126 | if (!titleCol) { titleCol = index; } 127 | }); 128 | 129 | _.each(series.values, function (value) { 130 | var data = { 131 | annotation: self.annotation, 132 | time: + new Date(self._formatValue(value[timeCol])), 133 | title: value[titleCol], 134 | tags: value[tagsCol], 135 | text: value[textCol] 136 | }; 137 | 138 | list.push(data); 139 | }); 140 | }); 141 | 142 | return list; 143 | }; 144 | 145 | p.getTable = function() { 146 | var table = new TableModel.default(); 147 | var self = this; 148 | 149 | _.each(self.series.columns, function(column) { 150 | table.columns.push({ text: column }); 151 | }); 152 | 153 | _.each(self.series.values, function(row) { 154 | var formated = _.map(row, function(value) { 155 | return self._formatValue(value); 156 | }); 157 | 158 | table.rows.push(formated); 159 | }); 160 | 161 | return table; 162 | }; 163 | 164 | p.getDocs = function() { 165 | var self = this; 166 | var rows = { datapoints: [], target: self.series.name, type: 'docs' }; 167 | 168 | _.each(self.series.values, function(values) { 169 | var formated = {}; 170 | 171 | _.each(values, function(value, i) { 172 | var column = self.series.columns[i]; 173 | formated[column] = self._formatValue(value); 174 | }); 175 | 176 | rows.datapoints.push(formated); 177 | }); 178 | 179 | return rows; 180 | }; 181 | 182 | p._formatValue = function(value) { 183 | var v_numeric = Number(value); 184 | 185 | if (isNaN(value)) { 186 | return value; 187 | } else { 188 | return parseFloat(v_numeric); 189 | } 190 | }; 191 | 192 | return SqlSeries; 193 | }); 194 | -------------------------------------------------------------------------------- /src/public/vendor/npm/rxjs/Observable.d.ts: -------------------------------------------------------------------------------- 1 | import { PartialObserver, Observer } from './Observer'; 2 | import { Operator } from './Operator'; 3 | import { Subscriber } from './Subscriber'; 4 | import { Subscription, AnonymousSubscription, TeardownLogic } from './Subscription'; 5 | import { IfObservable } from './observable/IfObservable'; 6 | import { ErrorObservable } from './observable/ErrorObservable'; 7 | export interface Subscribable { 8 | subscribe(observer: Observer): AnonymousSubscription; 9 | } 10 | export declare type SubscribableOrPromise = Subscribable | Promise; 11 | export declare type ArrayOrIterator = Iterator | ArrayLike; 12 | export declare type ObservableInput = SubscribableOrPromise | ArrayOrIterator; 13 | /** 14 | * A representation of any set of values over any amount of time. This the most basic building block 15 | * of RxJS. 16 | * 17 | * @class Observable 18 | */ 19 | export declare class Observable implements Subscribable { 20 | _isScalar: boolean; 21 | protected source: Observable; 22 | protected operator: Operator; 23 | /** 24 | * @constructor 25 | * @param {Function} subscribe the function that is called when the Observable is 26 | * initially subscribed to. This function is given a Subscriber, to which new values 27 | * can be `next`ed, or an `error` method can be called to raise an error, or 28 | * `complete` can be called to notify of a successful completion. 29 | */ 30 | constructor(subscribe?: (subscriber: Subscriber) => TeardownLogic); 31 | /** 32 | * Creates a new cold Observable by calling the Observable constructor 33 | * @static true 34 | * @owner Observable 35 | * @method create 36 | * @param {Function} subscribe? the subscriber function to be passed to the Observable constructor 37 | * @return {Observable} a new cold observable 38 | */ 39 | static create: Function; 40 | /** 41 | * Creates a new Observable, with this Observable as the source, and the passed 42 | * operator defined as the new observable's operator. 43 | * @method lift 44 | * @param {Operator} operator the operator defining the operation to take on the observable 45 | * @return {Observable} a new observable with the Operator applied 46 | */ 47 | lift(operator: Operator): Observable; 48 | /** 49 | * Registers handlers for handling emitted values, error and completions from the observable, and 50 | * executes the observable's subscriber function, which will take action to set up the underlying data stream 51 | * @method subscribe 52 | * @param {PartialObserver|Function} observerOrNext (optional) either an observer defining all functions to be called, 53 | * or the first of three possible handlers, which is the handler for each value emitted from the observable. 54 | * @param {Function} error (optional) a handler for a terminal event resulting from an error. If no error handler is provided, 55 | * the error will be thrown as unhandled 56 | * @param {Function} complete (optional) a handler for a terminal event resulting from successful completion. 57 | * @return {ISubscription} a subscription reference to the registered handlers 58 | */ 59 | subscribe(observerOrNext?: PartialObserver | ((value: T) => void), error?: (error: any) => void, complete?: () => void): Subscription; 60 | /** 61 | * @method forEach 62 | * @param {Function} next a handler for each value emitted by the observable 63 | * @param {PromiseConstructor} [PromiseCtor] a constructor function used to instantiate the Promise 64 | * @return {Promise} a promise that either resolves on observable completion or 65 | * rejects with the handled error 66 | */ 67 | forEach(next: (value: T) => void, PromiseCtor?: typeof Promise): Promise; 68 | protected _subscribe(subscriber: Subscriber): TeardownLogic; 69 | static if: typeof IfObservable.create; 70 | static throw: typeof ErrorObservable.create; 71 | } 72 | -------------------------------------------------------------------------------- /src/public/vendor/npm/rxjs/Observer.d.ts: -------------------------------------------------------------------------------- 1 | export interface NextObserver { 2 | isUnsubscribed?: boolean; 3 | next: (value: T) => void; 4 | error?: (err: any) => void; 5 | complete?: () => void; 6 | } 7 | export interface ErrorObserver { 8 | isUnsubscribed?: boolean; 9 | next?: (value: T) => void; 10 | error: (err: any) => void; 11 | complete?: () => void; 12 | } 13 | export interface CompletionObserver { 14 | isUnsubscribed?: boolean; 15 | next?: (value: T) => void; 16 | error?: (err: any) => void; 17 | complete: () => void; 18 | } 19 | export declare type PartialObserver = NextObserver | ErrorObserver | CompletionObserver; 20 | export interface Observer { 21 | isUnsubscribed?: boolean; 22 | next: (value: T) => void; 23 | error: (err: any) => void; 24 | complete: () => void; 25 | } 26 | export declare const empty: Observer; 27 | -------------------------------------------------------------------------------- /src/public/vendor/npm/rxjs/Operator.d.ts: -------------------------------------------------------------------------------- 1 | import { Subscriber } from './Subscriber'; 2 | export declare class Operator { 3 | call(subscriber: Subscriber): Subscriber; 4 | } 5 | -------------------------------------------------------------------------------- /src/public/vendor/npm/rxjs/Scheduler.d.ts: -------------------------------------------------------------------------------- 1 | import { Subscription } from './Subscription'; 2 | import { Action } from './scheduler/Action'; 3 | export interface Scheduler { 4 | now(): number; 5 | schedule(work: (state?: T) => Subscription | void, delay?: number, state?: T): Subscription; 6 | flush(): void; 7 | active: boolean; 8 | actions: Action[]; 9 | scheduledId: number; 10 | } 11 | -------------------------------------------------------------------------------- /src/public/vendor/npm/rxjs/Subject.d.ts: -------------------------------------------------------------------------------- 1 | import { Operator } from './Operator'; 2 | import { Observer } from './Observer'; 3 | import { Observable } from './Observable'; 4 | import { Subscriber } from './Subscriber'; 5 | import { Subscription, ISubscription, TeardownLogic } from './Subscription'; 6 | /** 7 | * @class Subject 8 | */ 9 | export declare class Subject extends Observable implements Observer, ISubscription { 10 | protected destination: Observer; 11 | protected source: Observable; 12 | static create: Function; 13 | constructor(destination?: Observer, source?: Observable); 14 | observers: Observer[]; 15 | isUnsubscribed: boolean; 16 | protected isStopped: boolean; 17 | protected hasErrored: boolean; 18 | protected errorValue: any; 19 | protected dispatching: boolean; 20 | protected hasCompleted: boolean; 21 | lift(operator: Operator): Observable; 22 | add(subscription: TeardownLogic): Subscription; 23 | remove(subscription: Subscription): void; 24 | unsubscribe(): void; 25 | protected _subscribe(subscriber: Subscriber): TeardownLogic; 26 | protected _unsubscribe(): void; 27 | next(value: T): void; 28 | error(err?: any): void; 29 | complete(): void; 30 | asObservable(): Observable; 31 | protected _next(value: T): void; 32 | protected _finalNext(value: T): void; 33 | protected _error(err: any): void; 34 | protected _finalError(err: any): void; 35 | protected _complete(): void; 36 | protected _finalComplete(): void; 37 | private throwIfUnsubscribed(); 38 | } 39 | -------------------------------------------------------------------------------- /src/public/vendor/npm/rxjs/Subscriber.d.ts: -------------------------------------------------------------------------------- 1 | import { Observer, PartialObserver } from './Observer'; 2 | import { Subscription } from './Subscription'; 3 | export declare class Subscriber extends Subscription implements Observer { 4 | static create(next?: (x?: T) => void, error?: (e?: any) => void, complete?: () => void): Subscriber; 5 | syncErrorValue: any; 6 | syncErrorThrown: boolean; 7 | syncErrorThrowable: boolean; 8 | protected isStopped: boolean; 9 | protected destination: PartialObserver; 10 | constructor(destinationOrNext?: PartialObserver | ((value: T) => void), error?: (e?: any) => void, complete?: () => void); 11 | next(value?: T): void; 12 | error(err?: any): void; 13 | complete(): void; 14 | unsubscribe(): void; 15 | protected _next(value: T): void; 16 | protected _error(err: any): void; 17 | protected _complete(): void; 18 | } 19 | -------------------------------------------------------------------------------- /src/public/vendor/npm/rxjs/Subscription.d.ts: -------------------------------------------------------------------------------- 1 | export interface AnonymousSubscription { 2 | unsubscribe(): void; 3 | } 4 | export declare type TeardownLogic = AnonymousSubscription | Function | void; 5 | export interface ISubscription extends AnonymousSubscription { 6 | unsubscribe(): void; 7 | isUnsubscribed: boolean; 8 | add(teardown: TeardownLogic): ISubscription; 9 | remove(sub: ISubscription): void; 10 | } 11 | export declare class Subscription implements ISubscription { 12 | static EMPTY: Subscription; 13 | isUnsubscribed: boolean; 14 | constructor(_unsubscribe?: () => void); 15 | unsubscribe(): void; 16 | /** 17 | * Adds a tear down to be called during the unsubscribe() of this subscription. 18 | * 19 | * If the tear down being added is a subscription that is already unsubscribed, 20 | * is the same reference `add` is being called on, or is `Subscription.EMPTY`, 21 | * it will not be added. 22 | * 23 | * If this subscription is already in an `isUnsubscribed` state, the passed tear down logic 24 | * will be executed immediately 25 | * 26 | * @param {TeardownLogic} teardown the additional logic to execute on teardown. 27 | * @returns {Subscription} returns the subscription used or created to be added to the inner 28 | * subscriptions list. This subscription can be used with `remove()` to remove the passed teardown 29 | * logic from the inner subscriptions list. 30 | */ 31 | add(teardown: TeardownLogic): Subscription; 32 | /** 33 | * removes a subscription from the internal list of subscriptions that will unsubscribe 34 | * during unsubscribe process of this subscription. 35 | * @param {Subscription} subscription the subscription to remove 36 | */ 37 | remove(subscription: Subscription): void; 38 | } 39 | export declare class UnsubscriptionError extends Error { 40 | errors: any[]; 41 | constructor(errors: any[]); 42 | } 43 | -------------------------------------------------------------------------------- /src/public/vendor/npm/rxjs/observable/ErrorObservable.d.ts: -------------------------------------------------------------------------------- 1 | import { Scheduler } from '../Scheduler'; 2 | import { Observable } from '../Observable'; 3 | import { TeardownLogic } from '../Subscription'; 4 | /** 5 | * We need this JSDoc comment for affecting ESDoc. 6 | * @extends {Ignored} 7 | * @hide true 8 | */ 9 | export declare class ErrorObservable extends Observable { 10 | error: any; 11 | private scheduler; 12 | /** 13 | * @param error 14 | * @param scheduler 15 | * @return {ErrorObservable} 16 | * @static true 17 | * @name throw 18 | * @owner Observable 19 | */ 20 | static create(error: any, scheduler?: Scheduler): ErrorObservable; 21 | static dispatch({error, subscriber}: { 22 | error: any; 23 | subscriber: any; 24 | }): void; 25 | constructor(error: any, scheduler?: Scheduler); 26 | protected _subscribe(subscriber: any): TeardownLogic; 27 | } 28 | -------------------------------------------------------------------------------- /src/public/vendor/npm/rxjs/observable/IfObservable.d.ts: -------------------------------------------------------------------------------- 1 | import { Observable, SubscribableOrPromise } from '../Observable'; 2 | import { Subscriber } from '../Subscriber'; 3 | import { TeardownLogic } from '../Subscription'; 4 | /** 5 | * We need this JSDoc comment for affecting ESDoc. 6 | * @extends {Ignored} 7 | * @hide true 8 | */ 9 | export declare class IfObservable extends Observable { 10 | private condition; 11 | private thenSource; 12 | private elseSource; 13 | static create(condition: () => boolean | void, thenSource?: SubscribableOrPromise | void, elseSource?: SubscribableOrPromise | void): Observable; 14 | constructor(condition: () => boolean | void, thenSource?: SubscribableOrPromise | void, elseSource?: SubscribableOrPromise | void); 15 | protected _subscribe(subscriber: Subscriber): TeardownLogic; 16 | } 17 | -------------------------------------------------------------------------------- /src/public/vendor/npm/rxjs/scheduler/Action.d.ts: -------------------------------------------------------------------------------- 1 | import { Subscription } from '../Subscription'; 2 | import { Scheduler } from '../Scheduler'; 3 | export interface Action extends Subscription { 4 | work: (state?: any) => void | Subscription; 5 | state?: any; 6 | delay?: number; 7 | schedule(state?: any, delay?: number): void; 8 | execute(): void; 9 | scheduler: Scheduler; 10 | error: any; 11 | } 12 | --------------------------------------------------------------------------------