diff --git a/docs/content/querying/sql.md b/docs/content/querying/sql.md
index 883904ced99e..a03a712e1345 100644
--- a/docs/content/querying/sql.md
+++ b/docs/content/querying/sql.md
@@ -22,6 +22,13 @@ title: "SQL"
~ under the License.
-->
+
+
# SQL
diff --git a/web-console/.gitignore b/web-console/.gitignore
index 1e0bebd4e2ed..7228ec055320 100644
--- a/web-console/.gitignore
+++ b/web-console/.gitignore
@@ -9,6 +9,8 @@ coordinator-console/
pages/
index.html
+lib/sql-function-doc.ts
+
.tscache
tscommand-*.tmp.txt
diff --git a/web-console/script/build b/web-console/script/build
index 75371226c92d..cbc9092b32e4 100755
--- a/web-console/script/build
+++ b/web-console/script/build
@@ -27,6 +27,9 @@ echo "Copying blueprint assets in..."
sed 's|url("assets|url("/assets|g' "./node_modules/@blueprintjs/core/dist/blueprint.css" > lib/blueprint.css
cp -r "./node_modules/@blueprintjs/core/dist/assets" .
+echo "Adding SQL function doc..."
+PATH="./target/node:$PATH" ./script/create-sql-function-doc
+
echo "Transpiling ReactTable CSS..."
PATH="./target/node:$PATH" ./node_modules/.bin/stylus lib/react-table.styl -o lib/react-table.css
diff --git a/web-console/script/create-sql-function-doc b/web-console/script/create-sql-function-doc
new file mode 100755
index 000000000000..146a39e147a3
--- /dev/null
+++ b/web-console/script/create-sql-function-doc
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+readfile='../docs/content/querying/sql.md'
+writefile='lib/sql-function-doc.ts'
+
+> "$writefile"
+
+echo -e "// This file is auto generated and should not be modified\n" > "$writefile"
+echo -e 'export const SQLFunctionDoc: any[] = [' >> "$writefile"
+
+isFunction=false
+
+while read -r line; do
+ if [[ $line =~ ^###.*functions$ ]]; then
+ isFunction=true
+ elif [[ $line =~ ^## ]] ; then
+ isFunction=false
+ elif [[ $isFunction == true ]]; then
+ if [[ $line =~ \|\`.*\`\|.*\| ]]; then
+ syntax=$(echo $line | grep -o '|`.*`|')
+ syntax=${syntax:2:${#syntax}-4}
+ syntax=${syntax//\\/}
+ description=$(echo $line | grep -o '`|.*.|')
+ description=${description//\"/\'}
+ description=${description:2:${#description}-4}
+ echo -e " {" >> "$writefile"
+ echo -e " syntax: \"$syntax\"," >> "$writefile"
+ echo -e " description: \"$description\"" >> "$writefile"
+ echo -e " }," >> "$writefile"
+ fi
+ fi
+done < "$readfile"
+
+echo -e ']' >> "$writefile"
\ No newline at end of file
diff --git a/web-console/src/components/sql-control.scss b/web-console/src/components/sql-control.scss
new file mode 100644
index 000000000000..9e55c5a76157
--- /dev/null
+++ b/web-console/src/components/sql-control.scss
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+.sql-control {
+
+ .ace_scroller {
+ background-color: #232C35;
+ }
+
+ .ace_gutter-layer {
+ background-color: #27313c;
+ }
+
+ .buttons {
+
+ button{
+ margin-right: 15px;
+ }
+
+ }
+
+}
+
+.auto-complete-checkbox {
+ margin:10px;
+ min-width: 120px;
+}
+
+.ace_tooltip {
+ padding: 10px;
+ background-color: #333D47;
+ color: #C1CCD5;
+ width: 500px;
+ display: block;
+ height: auto;
+ white-space: initial;
+
+ hr {
+ margin: 10px 0;
+ }
+
+ .function-doc-name {
+ font-size: 18px;
+ }
+}
\ No newline at end of file
diff --git a/web-console/src/components/sql-control.tsx b/web-console/src/components/sql-control.tsx
index 96dd780a449b..a2d21b577670 100644
--- a/web-console/src/components/sql-control.tsx
+++ b/web-console/src/components/sql-control.tsx
@@ -17,6 +17,8 @@
*/
import * as React from 'react';
+import * as ReactDOMServer from 'react-dom/server';
+import axios from "axios";
import * as classNames from 'classnames';
import * as ace from 'brace'
import AceEditor from "react-ace";
@@ -24,8 +26,13 @@ import 'brace/mode/sql';
import 'brace/mode/hjson';
import 'brace/theme/solarized_dark';
import 'brace/ext/language_tools';
-import { Intent, Button } from "@blueprintjs/core";
+import {Intent, Button, Popover, Checkbox, Classes, Position} from "@blueprintjs/core";
+import { SQLFunctionDoc } from "../../lib/sql-function-doc";
import { IconNames } from './filler';
+import './sql-control.scss'
+import {AppToaster} from "../singletons/toaster";
+
+const langTools = ace.acequire('ace/ext/language_tools');
export interface SqlControlProps extends React.Props
{
initSql: string | null;
@@ -35,6 +42,7 @@ export interface SqlControlProps extends React.Props {
export interface SqlControlState {
query: string;
autoCompleteOn: boolean;
+ autoCompleteLoading: boolean;
}
export class SqlControl extends React.Component {
@@ -42,8 +50,111 @@ export class SqlControl extends React.Component =>{
+ const datasourceResp = await axios.post("/druid/v2/sql", { query: `SELECT datasource FROM sys.segments GROUP BY 1`})
+ const datasourceList: any[] = datasourceResp.data.map((d: any) => {
+ const datasourceName: string = d.datasource;
+ return {
+ value: datasourceName,
+ score: 50,
+ meta: "datasource"
+ };
+ });
+
+ const completer = {
+ getCompletions: (editor:any , session: any, pos: any, prefix: any, callback: any) => {
+ callback(null, datasourceList);
+ }
+ };
+
+ langTools.addCompleter(completer);
+ }
+
+ private addColumnNameAutoCompleter = async (): Promise => {
+ const columnNameResp = await axios.post("/druid/v2/sql", {query: `SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'druid'`})
+ const columnNameList: any[] = columnNameResp.data.map((d: any) => {
+ const columnName: string = d.COLUMN_NAME;
+ return {
+ value: columnName,
+ score: 50,
+ meta: "column"
+ };
+ });
+
+ const completer = {
+ getCompletions: (editor:any , session: any, pos: any, prefix: any, callback: any) => {
+ callback(null, columnNameList);
+ }
};
+
+ langTools.addCompleter(completer);
+ }
+
+ private addFunctionAutoCompleter = (): void => {
+ const functionList: any[]= SQLFunctionDoc.map((entry: any) => {
+ let funcName: string = entry.syntax.replace(/\(.*\)/,"()");
+ if (!funcName.includes("(")) funcName = funcName.substr(0,10);
+ return {
+ value: funcName,
+ score: 80,
+ meta: "function",
+ syntax: entry.syntax,
+ description: entry.description,
+ completer: {
+ insertMatch: (editor:any, data:any) => {
+ editor.completer.insertMatch({value: data.caption});
+ const pos = editor.getCursorPosition();
+ editor.gotoLine(pos.row+1, pos.column-1);
+ }
+ }
+ };
+ });
+
+ const completer = {
+ getCompletions: (editor:any , session: any, pos: any, prefix: any, callback: any) => {
+ callback(null, functionList);
+ },
+ getDocTooltip: (item: any) => {
+ if (item.meta === "function") {
+ const functionName = item.caption.slice(0,-2);
+ item.docHTML = ReactDOMServer.renderToStaticMarkup((
+
+
{functionName}
+
+
Syntax:
+
{item.syntax}
+
+
Description:
+
{item.description}
+
+ ))
+ }
+ }
+ };
+ langTools.addCompleter(completer);
+ }
+
+ private addCompleters = async () => {
+ try {
+ this.addFunctionAutoCompleter();
+ await this.addDatasourceAutoCompleter();
+ await this.addColumnNameAutoCompleter();
+ } catch (e) {
+ AppToaster.show({
+ message: "Failed to load SQL auto completer",
+ intent: Intent.DANGER
+ });
+ }
+ }
+
+ componentDidMount(): void {
+ this.addCompleters();
}
private handleChange = (newValue: string): void => {
@@ -58,6 +169,15 @@ export class SqlControl extends React.Component
+ this.setState({autoCompleteOn: !autoCompleteOn})}
+ />
+ ;
+
// Set the key in the AceEditor to force a rebind and prevent an error that happens otherwise
return