├── .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 | 
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 | 
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 | 
49 |
50 | (Use a template vartiable in query editor)
51 | 
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 | 
60 |
61 | (Annotations in a graph)
62 | 
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 | 
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 |
15 |
16 | Query parts
17 |
59 |
--------------------------------------------------------------------------------
/dist/partials/config.html:
--------------------------------------------------------------------------------
1 | SqlDB Details
2 |
3 |
50 |
51 |
59 |
--------------------------------------------------------------------------------
/dist/partials/query.editor.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
29 |
30 |
49 |
50 |
78 |
79 |
106 |
107 |
108 |
109 |
124 |
125 |
126 |
--------------------------------------------------------------------------------
/dist/partials/query.options.html:
--------------------------------------------------------------------------------
1 |
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 |
15 |
16 | Query parts
17 |
59 |
--------------------------------------------------------------------------------
/src/public/partials/config.html:
--------------------------------------------------------------------------------
1 | SqlDB Details
2 |
3 |
50 |
51 |
59 |
--------------------------------------------------------------------------------
/src/public/partials/query.editor.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
29 |
30 |
49 |
50 |
78 |
79 |
106 |
107 |
108 |
109 |
124 |
125 |
126 |
--------------------------------------------------------------------------------
/src/public/partials/query.options.html:
--------------------------------------------------------------------------------
1 |
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 |
--------------------------------------------------------------------------------