mirror of
https://github.com/apache/superset.git
synced 2026-04-20 16:44:46 +00:00
* [sql lab] disable cross schema search This is killing our metastore as people type it emits large all-table-dump as they hit the keystroke. It never returns as it times out and hammers the poor metastore. Also some improvements around the disabling the table select on the left panel and having the table name not be sticky. * typo
245 lines
8.1 KiB
JavaScript
245 lines
8.1 KiB
JavaScript
/* global notify */
|
|
import React from 'react';
|
|
import PropTypes from 'prop-types';
|
|
import { Button } from 'react-bootstrap';
|
|
import Select from 'react-virtualized-select';
|
|
import createFilterOptions from 'react-select-fast-filter-options';
|
|
|
|
import TableElement from './TableElement';
|
|
import AsyncSelect from '../../components/AsyncSelect';
|
|
import { t } from '../../locales';
|
|
|
|
const $ = window.$ = require('jquery');
|
|
|
|
const propTypes = {
|
|
queryEditor: PropTypes.object.isRequired,
|
|
height: PropTypes.number.isRequired,
|
|
tables: PropTypes.array,
|
|
actions: PropTypes.object,
|
|
database: PropTypes.object,
|
|
};
|
|
|
|
const defaultProps = {
|
|
tables: [],
|
|
actions: {},
|
|
};
|
|
|
|
class SqlEditorLeftBar extends React.PureComponent {
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = {
|
|
schemaLoading: false,
|
|
schemaOptions: [],
|
|
tableLoading: false,
|
|
tableOptions: [],
|
|
};
|
|
}
|
|
componentWillMount() {
|
|
this.fetchSchemas(this.props.queryEditor.dbId);
|
|
this.fetchTables(this.props.queryEditor.dbId, this.props.queryEditor.schema);
|
|
}
|
|
onDatabaseChange(db) {
|
|
const val = db ? db.value : null;
|
|
this.setState({ schemaOptions: [] });
|
|
this.props.actions.queryEditorSetSchema(this.props.queryEditor, null);
|
|
this.props.actions.queryEditorSetDb(this.props.queryEditor, val);
|
|
if (!(db)) {
|
|
this.setState({ tableOptions: [] });
|
|
} else {
|
|
this.fetchTables(val, this.props.queryEditor.schema);
|
|
this.fetchSchemas(val);
|
|
}
|
|
}
|
|
getTableNamesBySubStr(input) {
|
|
if (!this.props.queryEditor.dbId || !input) {
|
|
return Promise.resolve({ options: [] });
|
|
}
|
|
const url = `/superset/tables/${this.props.queryEditor.dbId}/` +
|
|
`${this.props.queryEditor.schema}/${input}`;
|
|
return $.get(url).then(data => ({ options: data.options }));
|
|
}
|
|
dbMutator(data) {
|
|
const options = data.result.map(db => ({ value: db.id, label: db.database_name }));
|
|
this.props.actions.setDatabases(data.result);
|
|
if (data.result.length === 0) {
|
|
this.props.actions.addAlert({
|
|
bsStyle: 'danger',
|
|
msg: t('It seems you don\'t have access to any database'),
|
|
});
|
|
}
|
|
return options;
|
|
}
|
|
resetState() {
|
|
this.props.actions.resetState();
|
|
}
|
|
fetchTables(dbId, schema, substr) {
|
|
// This can be large so it shouldn't be put in the Redux store
|
|
if (dbId && schema) {
|
|
this.setState({ tableLoading: true, tableOptions: [] });
|
|
const url = `/superset/tables/${dbId}/${schema}/${substr}/`;
|
|
$.get(url).done((data) => {
|
|
const filterOptions = createFilterOptions({ options: data.options });
|
|
this.setState({
|
|
filterOptions,
|
|
tableLoading: false,
|
|
tableOptions: data.options,
|
|
tableLength: data.tableLength,
|
|
});
|
|
})
|
|
.fail(() => {
|
|
this.setState({ tableLoading: false, tableOptions: [], tableLength: 0 });
|
|
notify.error(t('Error while fetching table list'));
|
|
});
|
|
} else {
|
|
this.setState({ tableLoading: false, tableOptions: [], filterOptions: null });
|
|
}
|
|
}
|
|
changeTable(tableOpt) {
|
|
if (!tableOpt) {
|
|
this.setState({ tableName: '' });
|
|
return;
|
|
}
|
|
const namePieces = tableOpt.value.split('.');
|
|
let tableName = namePieces[0];
|
|
let schemaName = this.props.queryEditor.schema;
|
|
if (namePieces.length === 1) {
|
|
this.setState({ tableName });
|
|
} else {
|
|
schemaName = namePieces[0];
|
|
tableName = namePieces[1];
|
|
this.setState({ tableName });
|
|
this.props.actions.queryEditorSetSchema(this.props.queryEditor, schemaName);
|
|
this.fetchTables(this.props.queryEditor.dbId, schemaName);
|
|
}
|
|
this.props.actions.addTable(this.props.queryEditor, tableName, schemaName);
|
|
}
|
|
changeSchema(schemaOpt) {
|
|
const schema = (schemaOpt) ? schemaOpt.value : null;
|
|
this.props.actions.queryEditorSetSchema(this.props.queryEditor, schema);
|
|
this.fetchTables(this.props.queryEditor.dbId, schema);
|
|
}
|
|
fetchSchemas(dbId) {
|
|
const actualDbId = dbId || this.props.queryEditor.dbId;
|
|
if (actualDbId) {
|
|
this.setState({ schemaLoading: true });
|
|
const url = `/superset/schemas/${actualDbId}/`;
|
|
$.get(url).done((data) => {
|
|
const schemaOptions = data.schemas.map(s => ({ value: s, label: s }));
|
|
this.setState({ schemaOptions, schemaLoading: false });
|
|
})
|
|
.fail(() => {
|
|
this.setState({ schemaLoading: false, schemaOptions: [] });
|
|
notify.error(t('Error while fetching schema list'));
|
|
});
|
|
}
|
|
}
|
|
closePopover(ref) {
|
|
this.refs[ref].hide();
|
|
}
|
|
|
|
render() {
|
|
const shouldShowReset = window.location.search === '?reset=1';
|
|
const tableMetaDataHeight = this.props.height - 130; // 130 is the height of the selects above
|
|
let tableSelectPlaceholder;
|
|
let tableSelectDisabled = false;
|
|
if (this.props.database && this.props.database.allow_multi_schema_metadata_fetch) {
|
|
tableSelectPlaceholder = t('Type to search ...');
|
|
} else {
|
|
tableSelectPlaceholder = t('Select table ');
|
|
tableSelectDisabled = true;
|
|
}
|
|
return (
|
|
<div className="clearfix sql-toolbar">
|
|
<div>
|
|
<AsyncSelect
|
|
dataEndpoint={
|
|
'/databaseasync/api/' +
|
|
'read?_flt_0_expose_in_sqllab=1&' +
|
|
'_oc_DatabaseAsync=database_name&' +
|
|
'_od_DatabaseAsync=asc'
|
|
}
|
|
onChange={this.onDatabaseChange.bind(this)}
|
|
onAsyncError={() => notify.error(t('Error while fetching database list'))}
|
|
value={this.props.queryEditor.dbId}
|
|
databaseId={this.props.queryEditor.dbId}
|
|
actions={this.props.actions}
|
|
valueRenderer={o => (
|
|
<div>
|
|
<span className="text-muted">{t('Database:')}</span> {o.label}
|
|
</div>
|
|
)}
|
|
mutator={this.dbMutator.bind(this)}
|
|
placeholder={t('Select a database')}
|
|
autoSelect
|
|
/>
|
|
</div>
|
|
<div className="m-t-5">
|
|
<Select
|
|
name="select-schema"
|
|
placeholder={t('Select a schema (%s)', this.state.schemaOptions.length)}
|
|
options={this.state.schemaOptions}
|
|
value={this.props.queryEditor.schema}
|
|
valueRenderer={o => (
|
|
<div>
|
|
<span className="text-muted">{t('Schema:')}</span> {o.label}
|
|
</div>
|
|
)}
|
|
isLoading={this.state.schemaLoading}
|
|
autosize={false}
|
|
onChange={this.changeSchema.bind(this)}
|
|
/>
|
|
</div>
|
|
<div className="m-t-5">
|
|
{this.props.queryEditor.schema &&
|
|
<Select
|
|
name="select-table"
|
|
ref="selectTable"
|
|
isLoading={this.state.tableLoading}
|
|
placeholder={t('Add a table (%s)', this.state.tableOptions.length)}
|
|
autosize={false}
|
|
onChange={this.changeTable.bind(this)}
|
|
filterOptions={this.state.filterOptions}
|
|
options={this.state.tableOptions}
|
|
/>
|
|
}
|
|
{!this.props.queryEditor.schema &&
|
|
<Select
|
|
async
|
|
name="async-select-table"
|
|
ref="selectTable"
|
|
placeholder={tableSelectPlaceholder}
|
|
disabled={tableSelectDisabled}
|
|
autosize={false}
|
|
onChange={this.changeTable.bind(this)}
|
|
loadOptions={this.getTableNamesBySubStr.bind(this)}
|
|
/>
|
|
}
|
|
</div>
|
|
<hr />
|
|
<div className="m-t-5">
|
|
<div className="scrollbar-container">
|
|
<div className="scrollbar-content" style={{ height: tableMetaDataHeight }}>
|
|
{this.props.tables.map(table => (
|
|
<TableElement
|
|
table={table}
|
|
key={table.id}
|
|
actions={this.props.actions}
|
|
/>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{shouldShowReset &&
|
|
<Button bsSize="small" bsStyle="danger" onClick={this.resetState.bind(this)}>
|
|
<i className="fa fa-bomb" /> {t('Reset State')}
|
|
</Button>
|
|
}
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
SqlEditorLeftBar.propTypes = propTypes;
|
|
SqlEditorLeftBar.defaultProps = defaultProps;
|
|
|
|
export default SqlEditorLeftBar;
|