diff --git a/caravel/assets/javascripts/SqlLab/actions.js b/caravel/assets/javascripts/SqlLab/actions.js
index 9634a6a83ab5b..6e46ae22851d6 100644
--- a/caravel/assets/javascripts/SqlLab/actions.js
+++ b/caravel/assets/javascripts/SqlLab/actions.js
@@ -17,6 +17,7 @@ export const QUERY_EDITOR_SET_SCHEMA = 'QUERY_EDITOR_SET_SCHEMA';
export const QUERY_EDITOR_SET_TITLE = 'QUERY_EDITOR_SET_TITLE';
export const QUERY_EDITOR_SET_AUTORUN = 'QUERY_EDITOR_SET_AUTORUN';
export const QUERY_EDITOR_SET_SQL = 'QUERY_EDITOR_SET_SQL';
+export const QUERY_EDITOR_SET_SELECTED_TEXT = 'QUERY_EDITOR_SET_SELECTED_TEXT';
export const SET_DATABASES = 'SET_DATABASES';
export const SET_ACTIVE_QUERY_EDITOR = 'SET_ACTIVE_QUERY_EDITOR';
export const ADD_ALERT = 'ADD_ALERT';
@@ -193,6 +194,10 @@ export function queryEditorSetSql(queryEditor, sql) {
return { type: QUERY_EDITOR_SET_SQL, queryEditor, sql };
}
+export function queryEditorSetSelectedText(queryEditor, sql) {
+ return { type: QUERY_EDITOR_SET_SELECTED_TEXT, queryEditor, sql };
+}
+
export function mergeTable(table) {
return { type: MERGE_TABLE, table };
}
diff --git a/caravel/assets/javascripts/SqlLab/components/AceEditorWrapper.jsx b/caravel/assets/javascripts/SqlLab/components/AceEditorWrapper.jsx
index ae2e5af9b726d..cd7069498c9f7 100644
--- a/caravel/assets/javascripts/SqlLab/components/AceEditorWrapper.jsx
+++ b/caravel/assets/javascripts/SqlLab/components/AceEditorWrapper.jsx
@@ -4,13 +4,16 @@ import 'brace/mode/sql';
import 'brace/theme/github';
import 'brace/ext/language_tools';
import ace from 'brace';
+import { areArraysShallowEqual } from '../../reduxUtils';
const langTools = ace.acequire('ace/ext/language_tools');
const propTypes = {
+ actions: React.PropTypes.object.isRequired,
onBlur: React.PropTypes.func,
sql: React.PropTypes.string.isRequired,
tables: React.PropTypes.array,
+ queryEditor: React.PropTypes.object.isRequired,
};
const defaultProps = {
@@ -25,6 +28,16 @@ class AceEditorWrapper extends React.PureComponent {
sql: props.sql,
};
}
+ componentDidMount() {
+ // Making sure no text is selected from previous mount
+ this.props.actions.queryEditorSetSelectedText(this.props.queryEditor, null);
+ this.setAutoCompleter();
+ }
+ componentWillReceiveProps(nextProps) {
+ if (!areArraysShallowEqual(this.props.tables, nextProps.tables)) {
+ this.setAutoCompleter();
+ }
+ }
textChange(text) {
this.setState({ sql: text });
}
@@ -32,22 +45,31 @@ class AceEditorWrapper extends React.PureComponent {
this.props.onBlur(this.state.sql);
}
getCompletions(aceEditor, session, pos, prefix, callback) {
+ callback(null, this.state.words);
+ }
+ onEditorLoad(editor) {
+ editor.$blockScrolling = Infinity; // eslint-disable-line no-param-reassign
+ editor.selection.on('changeSelection', () => {
+ this.props.actions.queryEditorSetSelectedText(
+ this.props.queryEditor, editor.getSelectedText());
+ });
+ }
+ setAutoCompleter() {
+ // Loading table and column names as auto-completable words
let words = [];
const columns = {};
const tables = this.props.tables || [];
tables.forEach(t => {
words.push({ name: t.name, value: t.name, score: 55, meta: 'table' });
- t.columns.forEach(col => {
+ const cols = t.columns || [];
+ cols.forEach(col => {
columns[col.name] = null; // using an object as a unique set
});
});
words = words.concat(Object.keys(columns).map(col => (
{ name: col, value: col, score: 50, meta: 'column' }
)));
- callback(null, words);
- }
- setAutoCompleter() {
- // Loading table and column names as auto-completable words
+ this.setState({ words });
const completer = {
getCompletions: this.getCompletions.bind(this),
};
@@ -56,11 +78,11 @@ class AceEditorWrapper extends React.PureComponent {
}
}
render() {
- this.setAutoCompleter();
return (
);
diff --git a/caravel/assets/javascripts/SqlLab/components/SqlEditor.jsx b/caravel/assets/javascripts/SqlLab/components/SqlEditor.jsx
index ab91024eb59cf..5ae681e58c572 100644
--- a/caravel/assets/javascripts/SqlLab/components/SqlEditor.jsx
+++ b/caravel/assets/javascripts/SqlLab/components/SqlEditor.jsx
@@ -58,12 +58,13 @@ class SqlEditor extends React.PureComponent {
this.startQuery(runAsync);
}
startQuery(runAsync = false, ctas = false) {
+ const qe = this.props.queryEditor;
const query = {
- dbId: this.props.queryEditor.dbId,
- sql: this.props.queryEditor.sql,
- sqlEditorId: this.props.queryEditor.id,
- tab: this.props.queryEditor.title,
- schema: this.props.queryEditor.schema,
+ dbId: qe.dbId,
+ sql: qe.selectedText ? qe.selectedText : qe.sql,
+ sqlEditorId: qe.id,
+ tab: qe.title,
+ schema: qe.schema,
tempTableName: this.state.ctas,
runAsync,
ctas,
@@ -92,17 +93,23 @@ class SqlEditor extends React.PureComponent {
render() {
let runButtons = [];
+ let runText = 'Run Query';
+ let btnStyle = 'primary';
+ if (this.props.queryEditor.selectedText) {
+ runText = 'Run Selection';
+ btnStyle = 'warning';
+ }
if (this.props.database && this.props.database.allow_run_sync) {
runButtons.push(
);
}
@@ -110,7 +117,7 @@ class SqlEditor extends React.PureComponent {
runButtons.push(