More goodness
diff --git a/caravel/assets/javascripts/SqlLab/TODO.md b/caravel/assets/javascripts/SqlLab/TODO.md
index 8a83c57..b8af8ba 100644
--- a/caravel/assets/javascripts/SqlLab/TODO.md
+++ b/caravel/assets/javascripts/SqlLab/TODO.md
@@ -1,22 +1,14 @@
-# Design
-* Query Log, search, filter on active tab only
-* Where to make the limit clear?
 
 # TODO
+* Figure out how to organize the left panel, integrate Search
 * collapse sql beyond 10 lines
-* add [Visualize] icon to modal
-* Security per-database
-* Overwrite workspace query
-* Async
-* Refactor timer in to its own thing
-
+* Security per-database (dropdown)
+* Get a to work
 
 ## Cosmetic
-* use icons for datatypes
 * SqlEditor buttons
 * use react-bootstrap-prompt for query title input
-* make input:text more self-evident
-* Tab cosmetic in theme
+* Make tabs look great
 
 # PROJECT
 * Write Runbook
diff --git a/caravel/assets/javascripts/SqlLab/actions.js b/caravel/assets/javascripts/SqlLab/actions.js
index a236136..0c2d3b9 100644
--- a/caravel/assets/javascripts/SqlLab/actions.js
+++ b/caravel/assets/javascripts/SqlLab/actions.js
@@ -20,6 +20,8 @@
 export const ADD_WORKSPACE_QUERY = 'ADD_WORKSPACE_QUERY';
 export const REMOVE_WORKSPACE_QUERY = 'REMOVE_WORKSPACE_QUERY';
 export const SET_ACTIVE_QUERY_EDITOR = 'SET_ACTIVE_QUERY_EDITOR';
+export const ADD_ALERT = 'ADD_ALERT';
+export const REMOVE_ALERT = 'REMOVE_ALERT';
 
 export function resetState() {
   return { type: RESET_STATE };
@@ -29,6 +31,14 @@
   return { type: ADD_QUERY_EDITOR, queryEditor };
 }
 
+export function addAlert(alert) {
+  return { type: ADD_ALERT, alert };
+}
+
+export function removeAlert(alert) {
+  return { type: REMOVE_ALERT, alert };
+}
+
 export function setActiveQueryEditor(queryEditor) {
   return { type: SET_ACTIVE_QUERY_EDITOR, queryEditor };
 }
diff --git a/caravel/assets/javascripts/SqlLab/components/Alerts.jsx b/caravel/assets/javascripts/SqlLab/components/Alerts.jsx
new file mode 100644
index 0000000..6ba16f5
--- /dev/null
+++ b/caravel/assets/javascripts/SqlLab/components/Alerts.jsx
@@ -0,0 +1,40 @@
+import React from 'react';
+import { Alert } from 'react-bootstrap';
+import { connect } from 'react-redux';
+import { bindActionCreators } from 'redux';
+import * as Actions from '../actions';
+
+class Alerts extends React.Component {
+  removeAlert(alert) {
+    this.props.actions.removeAlert(alert);
+  }
+  render() {
+    const alerts = this.props.alerts.map((alert) =>
+      <Alert
+        bsStyle={alert.bsStyle}
+        style={{ width: '500px', textAlign: 'midddle', margin: '10px auto' }}
+      >
+        {alert.msg}
+        <i
+          className="fa fa-close pull-right"
+          onClick={this.removeAlert.bind(this, alert) }
+          style={{ cursor: 'pointer' }}
+        />
+      </Alert>
+    );
+    return (
+      <div>{alerts}</div>
+    );
+  }
+}
+
+Alerts.propTypes = {
+  alerts: React.PropTypes.array,
+};
+
+function mapDispatchToProps(dispatch) {
+  return {
+    actions: bindActionCreators(Actions, dispatch),
+  };
+}
+export default connect(null, mapDispatchToProps)(Alerts);
diff --git a/caravel/assets/javascripts/SqlLab/components/LeftPane.jsx b/caravel/assets/javascripts/SqlLab/components/LeftPane.jsx
index cebeb28..0cccdb1 100644
--- a/caravel/assets/javascripts/SqlLab/components/LeftPane.jsx
+++ b/caravel/assets/javascripts/SqlLab/components/LeftPane.jsx
@@ -4,8 +4,8 @@
 import { bindActionCreators } from 'redux';
 import * as Actions from '../actions';
 import QueryLink from './QueryLink';
+import shortid from 'shortid';
 
-// CSS
 import 'react-select/dist/react-select.css';
 
 const LeftPane = (props) => {
@@ -43,6 +43,12 @@
           <Button onClick={props.actions.resetState.bind(this)}>
             Reset State
           </Button>
+          <Button onClick={props.actions.addAlert.bind(this, {
+            msg: 'This info alert is a demo alert',
+            bsStyle: 'info',
+          })}>
+            Add Alert
+          </Button>
         </div>
       </div>
     </div>
diff --git a/caravel/assets/javascripts/SqlLab/components/SqlEditor.jsx b/caravel/assets/javascripts/SqlLab/components/SqlEditor.jsx
index 3746e11..5e35a17 100644
--- a/caravel/assets/javascripts/SqlLab/components/SqlEditor.jsx
+++ b/caravel/assets/javascripts/SqlLab/components/SqlEditor.jsx
@@ -46,25 +46,12 @@
       this.startQuery();
     }
   }
-  getTableOptions(input, callback) {
-    const url = '/tableasync/api/read?_oc_DatabaseAsync=database_name&_od_DatabaseAsync=asc';
-    $.get(url, function (data) {
-      const options = [];
-      for (let i = 0; i < data.pks.length; i++) {
-        options.push({ value: data.pks[i], label: data.result[i].table_name });
-      }
-      callback(null, {
-        options,
-        cache: false,
-      });
-    });
-  }
   startQuery() {
     const that = this;
     const query = {
       id: shortid.generate(),
       sqlEditorId: this.props.queryEditor.id,
-      sql: this.state.sql,
+      sql: this.props.queryEditor.sql,
       state: 'running',
       tab: this.props.queryEditor.title,
       dbId: this.props.queryEditor.dbId,
@@ -72,7 +59,7 @@
     };
     const url = '/caravel/sql_json/';
     const data = {
-      sql: this.state.sql,
+      sql: this.props.queryEditor.sql,
       database_id: this.props.queryEditor.dbId,
       schema: this.props.queryEditor.schema,
       json: true,
diff --git a/caravel/assets/javascripts/SqlLab/components/SqlEditorTopToolbar.jsx b/caravel/assets/javascripts/SqlLab/components/SqlEditorTopToolbar.jsx
index 87948d4..3e42365 100644
--- a/caravel/assets/javascripts/SqlLab/components/SqlEditorTopToolbar.jsx
+++ b/caravel/assets/javascripts/SqlLab/components/SqlEditorTopToolbar.jsx
@@ -29,19 +29,6 @@
     this.fetchSchemas();
     this.fetchTables();
   }
-  getTableOptions(input, callback) {
-    const url = '/tableasync/api/read?_oc_DatabaseAsync=database_name&_od_DatabaseAsync=asc';
-    $.get(url, function (data) {
-      const options = [];
-      for (let i = 0; i < data.pks.length; i++) {
-        options.push({ value: data.pks[i], label: data.result[i].table_name });
-      }
-      callback(null, {
-        options,
-        cache: false,
-      });
-    });
-  }
   getSql(table) {
     let cols = '';
     table.columns.forEach(function (col, i) {
@@ -70,16 +57,15 @@
     const actualDbId = dbId || this.props.queryEditor.dbId;
     if (actualDbId) {
       const actualSchema = schema || this.props.queryEditor.schema;
-      const that = this;
       this.setState({ tableLoading: true });
       this.setState({ tableOptions: [] });
       const url = `/caravel/tables/${actualDbId}/${actualSchema}`;
-      $.get(url, function (data) {
+      $.get(url, (data) => {
         let tableOptions = data.tables.map((s) => ({ value: s, label: s }));
         const views = data.views.map((s) => ({ value: s, label: '[view] ' + s }));
         tableOptions = [...tableOptions, ...views];
-        that.setState({ tableOptions });
-        that.setState({ tableLoading: false });
+        this.setState({ tableOptions });
+        this.setState({ tableLoading: false });
       });
     }
   }
@@ -89,16 +75,15 @@
     this.fetchTables(this.props.queryEditor.dbId, schema);
   }
   fetchSchemas(dbId) {
-    const that = this;
     const actualDbId = dbId || this.props.queryEditor.dbId;
     if (actualDbId) {
       this.setState({ schemaLoading: true });
       const url = `/databasetablesasync/api/read?_flt_0_id=${actualDbId}`;
-      $.get(url, function (data) {
+      $.get(url, (data) => {
         const schemas = data.result[0].all_schema_names;
         const schemaOptions = schemas.map((s) => ({ value: s, label: s }));
-        that.setState({ schemaOptions });
-        that.setState({ schemaLoading: false });
+        this.setState({ schemaOptions });
+        this.setState({ schemaLoading: false });
       });
     }
   }
@@ -115,12 +100,11 @@
   }
   fetchDatabaseOptions() {
     this.setState({ databaseLoading: true });
-    const that = this;
     const url = '/databaseasync/api/read';
-    $.get(url, function (data) {
+    $.get(url, (data) => {
       const options = data.result.map((db) => ({ value: db.id, label: db.database_name }));
-      that.setState({ databaseOptions: options });
-      that.setState({ databaseLoading: false });
+      this.setState({ databaseOptions: options });
+      this.setState({ databaseLoading: false });
     });
   }
   closePopover(ref) {
@@ -128,20 +112,25 @@
   }
   changeTable(tableOpt) {
     const tableName = tableOpt.value;
-    const that = this;
     const qe = this.props.queryEditor;
     const url = `/caravel/table/${qe.dbId}/${tableName}/${qe.schema}/`;
-    $.get(url, function (data) {
-      that.props.actions.addTable({
+    $.get(url, (data) => {
+      this.props.actions.addTable({
         id: shortid.generate(),
-        dbId: that.props.queryEditor.dbId,
-        queryEditorId: that.props.queryEditor.id,
+        dbId: this.props.queryEditor.dbId,
+        queryEditorId: this.props.queryEditor.id,
         name: data.name,
         schema: qe.schema,
         columns: data.columns,
         expanded: true,
         showPopup: false,
       });
+    })
+    .fail((err) => {
+      this.props.actions.addAlert({
+        msg: 'Error occurred while fetching metadata',
+        bsStyle: 'danger',
+      });
     });
   }
   render() {
diff --git a/caravel/assets/javascripts/SqlLab/components/TabbedSqlEditors.jsx b/caravel/assets/javascripts/SqlLab/components/TabbedSqlEditors.jsx
index c7d6b24..6613ed8 100644
--- a/caravel/assets/javascripts/SqlLab/components/TabbedSqlEditors.jsx
+++ b/caravel/assets/javascripts/SqlLab/components/TabbedSqlEditors.jsx
@@ -15,13 +15,24 @@
       this.props.actions.queryEditorSetTitle(qe, newTitle);
     }
   }
+  activeQueryEditor() {
+    const qeid = this.props.tabHistory[this.props.tabHistory.length - 1];
+    for (let i = 0; i < this.props.queryEditors.length; i++) {
+      const qe = this.props.queryEditors[i]
+      if (qe.id === qeid) {
+        return qe;
+      }
+    }
+  }
   newQueryEditor() {
     queryCount++;
-    const dbId = (this.props.workspaceDatabase) ? this.props.workspaceDatabase.id : null;
+    const activeQueryEditor = this.activeQueryEditor();
+    console.log(activeQueryEditor);
     const qe = {
       id: shortid.generate(),
       title: `Query ${queryCount}`,
-      dbId,
+      dbId: (activeQueryEditor) ? activeQueryEditor.dbId : null,
+      schema: (activeQueryEditor) ? activeQueryEditor.schema : null,
       autorun: false,
       sql: 'SELECT ...',
     };
@@ -49,7 +60,7 @@
           <DropdownButton
             bsSize="small"
             id={'ddbtn-tab-' + i}
-            className="no-shadow"
+            className="no-shadow tab-caret"
             id="bg-vertical-dropdown-1"
           >
             <MenuItem eventKey="1" onClick={this.props.actions.removeQueryEditor.bind(this, qe)}>
@@ -92,7 +103,6 @@
   queries: React.PropTypes.array,
   queryEditors: React.PropTypes.array,
   tabHistory: React.PropTypes.array,
-  workspaceDatabase: React.PropTypes.object,
 };
 QueryEditors.defaultProps = {
   tabHistory: [],
@@ -103,7 +113,6 @@
   return {
     queryEditors: state.queryEditors,
     queries: state.queries,
-    workspaceDatabase: state.workspaceDatabase,
     tabHistory: state.tabHistory,
   };
 }
diff --git a/caravel/assets/javascripts/SqlLab/components/VisualizeModal.jsx b/caravel/assets/javascripts/SqlLab/components/VisualizeModal.jsx
index c0ecc90..05f5361 100644
--- a/caravel/assets/javascripts/SqlLab/components/VisualizeModal.jsx
+++ b/caravel/assets/javascripts/SqlLab/components/VisualizeModal.jsx
@@ -1,5 +1,5 @@
 import React from 'react';
-import { Alert, Modal } from 'react-bootstrap';
+import { Alert, Button, Grid, Row, Col, Modal } from 'react-bootstrap';
 
 import { connect } from 'react-redux';
 import { bindActionCreators } from 'redux';
@@ -7,65 +7,145 @@
 
 import Select from 'react-select';
 import { Table } from 'reactable';
+import shortid from 'shortid';
+
+const $ = require('jquery');
 
 class VisualizeModal extends React.Component {
   constructor(props) {
     super(props);
     this.state = {
-      chartType: null,
+      chartType: 'line',
+      datasourceName: shortid.generate(),
+      columns: {},
     };
   }
-  changeChartType(event) {
-    this.setState({ chartType: event.target.value });
+  changeChartType(option) {
+    this.setState({ chartType: (option) ? option.value : null });
+  }
+  mergedColumns() {
+    const columns = Object.assign({}, this.state.columns);
+    if (this.props.query && this.props.query.results.columns) {
+      this.props.query.results.columns.forEach((col) => {
+        if (columns[col] === undefined) {
+          columns[col] = {};
+        }
+      });
+    }
+    return columns;
+  }
+  visualize() {
+    const vizOptions = {
+      chartType: this.state.chartType,
+      datasourceName: this.state.datasourceName,
+      columns: this.state.columns,
+      sql: this.props.query.sql,
+    };
+    window.open('/caravel/sqllab_viz/?' + $.param(vizOptions));
+  }
+  changeDatasourceName(event) {
+    this.setState({ datasourceName: event.target.value });
+  }
+  changeCheckbox(attr, col, event) {
+    console.log([attr, col, event]);
+    let columns = this.mergedColumns();
+    const column = Object.assign({}, columns[col], { [attr]: event.target.checked });
+    columns = Object.assign({}, columns, { [col]: column });
+    this.setState({ columns });
+  }
+  changeAggFunction(col, option) {
+    let columns = this.mergedColumns();
+    const val = (option) ? option.value : null;
+    const column = Object.assign({}, columns[col], { agg: option.value });
+    columns = Object.assign({}, columns, { [col]: column });
+    this.setState({ columns });
   }
   render() {
+    console.log(this.state);
     if (!(this.props.query)) {
       return <div />;
     }
-    const cols = this.props.query.results.columns;
+    const tableData = this.props.query.results.columns.map((col) => ({
+      column: col,
+      is_dimension: (
+        <input
+          type="checkbox"
+          onChange={this.changeCheckbox.bind(this, 'is_dim', col)}
+          checked={(this.state.columns[col]) ? this.state.columns[col].is_dim : false}
+          className="form-control"
+        />
+      ),
+      is_date: (
+        <input
+          type="checkbox"
+          className="form-control"
+          onChange={this.changeCheckbox.bind(this, 'is_date', col)}
+          checked={(this.state.columns[col]) ? this.state.columns[col].is_date : false}
+        />
+      ),
+      agg_func: (
+        <Select
+          options={[
+            { value: 'sum', label: 'SUM(x)' },
+            { value: 'min', label: 'MIN(x)' },
+            { value: 'max', label: 'MAX(x)' },
+            { value: 'avg', label: 'AVG(x)' },
+            { value: 'count_distinct', label: 'COUNT(DISTINCT x)' },
+          ]}
+          onChange={this.changeAggFunction.bind(this, col)}
+          value={(this.state.columns[col]) ? this.state.columns[col].agg : null}
+        />
+      ),
+    }))
     const modal = (
       <div className="VisualizeModal">
         <Modal show={this.props.show} onHide={this.props.onHide}>
           <Modal.Header closeButton>
-            <Modal.Title>Visualize (mock)</Modal.Title>
+            <Modal.Title>
+              Visualize <span className="alert alert-danger">under construction</span>
+            </Modal.Title>
           </Modal.Header>
           <Modal.Body>
-            <Alert bsStyle="danger">Not functional - Work in progress!</Alert>
-            <div>
-              <Select
-                name="select-chart-type"
-                placeholder="[Chart Type]"
-                options={[
-                  { value: 'line', label: 'Time Series - Line Chart' },
-                  { value: 'bar', label: 'Time Series - Bar Chart' },
-                  { value: 'bar_dist', label: 'Distribution - Bar Chart' },
-                  { value: 'pie', label: 'Pie Chart' },
-                ]}
-                value={this.state.chartType}
-                autosize={false}
-                onChange={this.changeChartType.bind(this)}
-              />
-              <Table
-                className="table table-condensed"
-                columns={['column', 'is_dimension', 'is_date', 'agg_func']}
-                data={cols.map((col) => ({
-                  column: col,
-                  is_dimension: <input type="checkbox" className="form-control" />,
-                  is_date: <input type="checkbox" className="form-control" />,
-                  agg_func: (
-                    <Select
-                      options={[
-                        { value: 'sum', label: 'SUM(x)' },
-                        { value: 'min', label: 'MIN(x)' },
-                        { value: 'max', label: 'MAX(x)' },
-                        { value: 'avg', label: 'AVG(x)' },
-                        { value: 'count_distinct', label: 'COUNT(DISTINCT x)' },
-                      ]}
-                    />
-                  ),
-                }))}
-              />
+            <div className="row">
+              <Col md={6}>
+                Chart Type
+                <Select
+                  name="select-chart-type"
+                  placeholder="[Chart Type]"
+                  options={[
+                    { value: 'line', label: 'Time Series - Line Chart' },
+                    { value: 'bar', label: 'Time Series - Bar Chart' },
+                    { value: 'bar_dist', label: 'Distribution - Bar Chart' },
+                    { value: 'pie', label: 'Pie Chart' },
+                  ]}
+                  value={this.state.chartType}
+                  autosize={false}
+                  onChange={this.changeChartType.bind(this)}
+                />
+              </Col>
+              <Col md={6}>
+                Datasource Name
+                <input
+                  type="text"
+                  className="form-control"
+                  placeholder="datasource name"
+                  onChange={this.changeDatasourceName.bind(this)}
+                  value={this.state.datasourceName}
+                />
+              </Col>
             </div>
+            <hr/>
+            <Table
+              className="table table-condensed"
+              columns={['column', 'is_dimension', 'is_date', 'agg_func']}
+              data={tableData}
+            />
+            <Button
+              onClick={this.visualize.bind(this)}
+              bsStyle="primary"
+            >
+              Visualize
+            </Button>
           </Modal.Body>
         </Modal>
       </div>
diff --git a/caravel/assets/javascripts/SqlLab/index.jsx b/caravel/assets/javascripts/SqlLab/index.jsx
index af948e9..c7de02a 100644
--- a/caravel/assets/javascripts/SqlLab/index.jsx
+++ b/caravel/assets/javascripts/SqlLab/index.jsx
@@ -4,13 +4,16 @@
 
 import React from 'react';
 import { render } from 'react-dom';
+import { connect } from 'react-redux';
+import { bindActionCreators } from 'redux';
+import * as Actions from './actions';
 
 import SplitPane from 'react-split-pane';
-
 import { Label, Tab, Tabs } from 'react-bootstrap';
 
 import LeftPane from './components/LeftPane';
 import TabbedSqlEditors from './components/TabbedSqlEditors';
+import Alerts from './components/Alerts';
 
 import { compose, createStore } from 'redux';
 import { Provider } from 'react-redux';
@@ -25,11 +28,12 @@
 // jquery hack to highlight the navbar menu
 $('a[href="/caravel/sqllab"]').parent().addClass('active');
 
-const App = React.createClass({
+class App extends React.Component {
   render() {
     return (
       <div className="App SqlLab">
         <div className="container-fluid">
+          <Alerts alerts={this.props.alerts} />
           <SplitPane split="vertical" minSize={200} defaultSize={300}>
             <div className="pane-cell pane-west m-t-5">
               <LeftPane />
@@ -41,8 +45,21 @@
         </div>
       </div>
     );
-  },
-});
+  }
+}
+
+function mapStateToProps(state) {
+  return {
+    alerts: state.alerts,
+  };
+}
+function mapDispatchToProps(dispatch) {
+  return {
+    actions: bindActionCreators(Actions, dispatch),
+  };
+}
+
+App = connect(mapStateToProps, mapDispatchToProps)(App);
 
 render(
   <Provider store={store}>
@@ -50,3 +67,4 @@
   </Provider>,
   document.getElementById('app')
 );
+
diff --git a/caravel/assets/javascripts/SqlLab/main.css b/caravel/assets/javascripts/SqlLab/main.css
index cfab266..e8bbb2f 100644
--- a/caravel/assets/javascripts/SqlLab/main.css
+++ b/caravel/assets/javascripts/SqlLab/main.css
@@ -250,3 +250,7 @@
     padding-bottom: 3px;
     padding-top: 3px;
 }
+button.tab-caret {
+    padding: 5px !important;
+    border-color: transparent;
+}
diff --git a/caravel/assets/javascripts/SqlLab/reducers.js b/caravel/assets/javascripts/SqlLab/reducers.js
index 1c18fca..4636a84 100644
--- a/caravel/assets/javascripts/SqlLab/reducers.js
+++ b/caravel/assets/javascripts/SqlLab/reducers.js
@@ -12,11 +12,12 @@
 };
 
 export const initialState = {
-  queryEditors: [defaultQueryEditor],
+  alerts: [],
   queries: [],
+  queryEditors: [defaultQueryEditor],
+  tabHistory: [defaultQueryEditor.id],
   tables: [],
   workspaceQueries: [],
-  tabHistory: [defaultQueryEditor.id],
 };
 
 
@@ -46,6 +47,9 @@
 }
 
 function addToArr(state, arrKey, obj) {
+  if (!(obj.id)) {
+    obj.id = shortid.generate();
+  }
   const newState = {};
   newState[arrKey] = [...state[arrKey], Object.assign({}, obj)];
   return Object.assign({}, state, newState);
@@ -137,6 +141,12 @@
     [actions.REMOVE_WORKSPACE_QUERY]() {
       return removeFromArr(state, 'workspaceQueries', action.query);
     },
+    [actions.ADD_ALERT]() {
+      return addToArr(state, 'alerts', action.alert);
+    },
+    [actions.REMOVE_ALERT]() {
+      return removeFromArr(state, 'alerts', action.alert);
+    },
   };
   if (action.type in actionHandlers) {
     return actionHandlers[action.type]();
diff --git a/caravel/assets/package.json b/caravel/assets/package.json
index 81078b7..cddfc85 100644
--- a/caravel/assets/package.json
+++ b/caravel/assets/package.json
@@ -8,7 +8,7 @@
   },
   "scripts": {
     "test": "npm run lint && mocha --compilers js:babel-core/register --required spec/helpers/browser.js spec/**/*_spec.*",
-    "dev": "NODE_ENV=dev webpack -d --watch --colors",
+    "dev": "NODE_ENV=dev webpack -d --watch --colors --progress",
     "prod": "NODE_ENV=production webpack -p --colors --progress",
     "lint": "npm run --silent lint:js",
     "lint:js": "eslint --ignore-path=.eslintignore --ext .js ."
diff --git a/caravel/config.py b/caravel/config.py
index 869dd4c..fd2062e 100644
--- a/caravel/config.py
+++ b/caravel/config.py
@@ -201,6 +201,9 @@
 """
 CELERY_CONFIG = None
 
+# The db id here results in selecting this one as a default in SQL Lab
+DEFAULT_DB_ID = None
+
 try:
     from caravel_config import *  # noqa
 except ImportError:
diff --git a/caravel/views.py b/caravel/views.py
index 2fdc418..22f6b8f 100755
--- a/caravel/views.py
+++ b/caravel/views.py
@@ -1259,6 +1259,12 @@
             dash_edit_perm=dash_edit_perm)
 
     @has_access
+    @expose("/sqllab_viz/")
+    @log_this
+    def sqllab_viz(self):
+        return json.dumps(request.args.to_dict(), indent=4)
+
+    @has_access
     @expose("/sql/<database_id>/")
     @log_this
     def sql(self, database_id):