mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 01:12:56 +03:00
suggest column default values (#2352)
This commit is contained in:
parent
00e911e3cd
commit
93a7c2c734
@ -62,6 +62,7 @@ export const passMTRenameTable = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const passMTRenameColumn = () => {
|
export const passMTRenameColumn = () => {
|
||||||
|
cy.wait(10000);
|
||||||
cy.get(getElementFromAlias('modify-table-edit-column-0')).click();
|
cy.get(getElementFromAlias('modify-table-edit-column-0')).click();
|
||||||
cy.get(getElementFromAlias('edit-col-name'))
|
cy.get(getElementFromAlias('edit-col-name'))
|
||||||
.clear()
|
.clear()
|
||||||
|
45
console/package-lock.json
generated
45
console/package-lock.json
generated
@ -12669,6 +12669,26 @@
|
|||||||
"integrity": "sha1-wStu/cIkfBDae4dw0YUICnsEcVY=",
|
"integrity": "sha1-wStu/cIkfBDae4dw0YUICnsEcVY=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"react-autosuggest": {
|
||||||
|
"version": "9.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-autosuggest/-/react-autosuggest-9.4.3.tgz",
|
||||||
|
"integrity": "sha512-wFbp5QpgFQRfw9cwKvcgLR8theikOUkv8PFsuLYqI2PUgVlx186Cz8MYt5bLxculi+jxGGUUVt+h0esaBZZouw==",
|
||||||
|
"requires": {
|
||||||
|
"prop-types": "^15.5.10",
|
||||||
|
"react-autowhatever": "^10.1.2",
|
||||||
|
"shallow-equal": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"react-autowhatever": {
|
||||||
|
"version": "10.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-autowhatever/-/react-autowhatever-10.2.0.tgz",
|
||||||
|
"integrity": "sha512-dqHH4uqiJldPMbL8hl/i2HV4E8FMTDEdVlOIbRqYnJi0kTpWseF9fJslk/KS9pGDnm80JkYzVI+nzFjnOG/u+g==",
|
||||||
|
"requires": {
|
||||||
|
"prop-types": "^15.5.8",
|
||||||
|
"react-themeable": "^1.1.0",
|
||||||
|
"section-iterator": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-base16-styling": {
|
"react-base16-styling": {
|
||||||
"version": "0.5.3",
|
"version": "0.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.5.3.tgz",
|
||||||
@ -12953,6 +12973,21 @@
|
|||||||
"prop-types": "^15.5.0"
|
"prop-types": "^15.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-themeable": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-themeable/-/react-themeable-1.1.0.tgz",
|
||||||
|
"integrity": "sha1-fURm3ZsrX6dQWHJ4JenxUro3mg4=",
|
||||||
|
"requires": {
|
||||||
|
"object-assign": "^3.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"object-assign": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz",
|
||||||
|
"integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-toggle": {
|
"react-toggle": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-toggle/-/react-toggle-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-toggle/-/react-toggle-4.0.2.tgz",
|
||||||
@ -14185,6 +14220,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"section-iterator": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/section-iterator/-/section-iterator-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-v0RNev7rlK1Dw5rS+yYVFifMuio="
|
||||||
|
},
|
||||||
"semver": {
|
"semver": {
|
||||||
"version": "5.5.1",
|
"version": "5.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz",
|
||||||
@ -14304,6 +14344,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"shallow-equal": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-0SW1nWo1hnabO62SEeHsl8nmTVVEzguVWZCj5gaQrgWAxz/BaCja4OWdJBWLVPDxdtE/WU7c98uUCCXyPHSCvw=="
|
||||||
|
},
|
||||||
"shallowequal": {
|
"shallowequal": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
|
||||||
|
@ -79,6 +79,7 @@
|
|||||||
"query-string": "^6.1.0",
|
"query-string": "^6.1.0",
|
||||||
"react": "16.8.2",
|
"react": "16.8.2",
|
||||||
"react-ace": "^6.1.1",
|
"react-ace": "^6.1.1",
|
||||||
|
"react-autosuggest": "^9.4.3",
|
||||||
"react-bootstrap": "^0.32.1",
|
"react-bootstrap": "^0.32.1",
|
||||||
"react-copy-to-clipboard": "^5.0.0",
|
"react-copy-to-clipboard": "^5.0.0",
|
||||||
"react-dom": "16.8.2",
|
"react-dom": "16.8.2",
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import Autosuggest from 'react-autosuggest';
|
||||||
|
|
||||||
|
const CustomInputAutoSuggest = props => {
|
||||||
|
const [suggestions, setSuggestions] = useState([]);
|
||||||
|
|
||||||
|
const { options, theme = require('./Theme.scss') } = props;
|
||||||
|
|
||||||
|
const getSuggestions = value => {
|
||||||
|
const inputValue = value.trim().toLowerCase();
|
||||||
|
const inputLength = inputValue.length;
|
||||||
|
const filterResults = () => {
|
||||||
|
return options.map(option => {
|
||||||
|
return {
|
||||||
|
title: option.title,
|
||||||
|
suggestions: option.suggestions.filter(
|
||||||
|
op => op.value.toLowerCase().slice(0, inputLength) === inputValue
|
||||||
|
),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return inputLength === 0 ? [...options] : filterResults();
|
||||||
|
};
|
||||||
|
const onSuggestionsFetchRequested = ob => {
|
||||||
|
const { value } = ob;
|
||||||
|
setSuggestions(getSuggestions(value));
|
||||||
|
};
|
||||||
|
const getSuggestionValue = suggestion => suggestion.value;
|
||||||
|
const onSuggestionsClearRequested = () => {
|
||||||
|
setSuggestions([]);
|
||||||
|
};
|
||||||
|
const renderSuggestion = suggestion => <div>{suggestion.value}</div>;
|
||||||
|
|
||||||
|
/* Don't render the section when there are no suggestions in it */
|
||||||
|
const renderSectionTitle = section => {
|
||||||
|
return section.suggestions.length > 0 ? section.title : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSectionSuggestions = section => {
|
||||||
|
return section.suggestions;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Autosuggest
|
||||||
|
suggestions={suggestions}
|
||||||
|
onSuggestionsFetchRequested={onSuggestionsFetchRequested}
|
||||||
|
onSuggestionsClearRequested={onSuggestionsClearRequested}
|
||||||
|
getSuggestionValue={getSuggestionValue}
|
||||||
|
renderSuggestion={renderSuggestion}
|
||||||
|
inputProps={{ ...props }}
|
||||||
|
theme={theme}
|
||||||
|
multiSection
|
||||||
|
renderSectionTitle={renderSectionTitle}
|
||||||
|
shouldRenderSuggestions={() => true}
|
||||||
|
getSectionSuggestions={getSectionSuggestions}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
CustomInputAutoSuggest.propTypes = {
|
||||||
|
options: PropTypes.array.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CustomInputAutoSuggest;
|
@ -0,0 +1,11 @@
|
|||||||
|
@import '../Theme.scss';
|
||||||
|
|
||||||
|
.suggestionsContainerOpen {
|
||||||
|
top: 30px;
|
||||||
|
width: 280px;
|
||||||
|
left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestion {
|
||||||
|
padding: 6px 12px;
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
@import '../Theme.scss';
|
||||||
|
|
||||||
|
.suggestionsContainerOpen {
|
||||||
|
top: 30px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestion {
|
||||||
|
padding: 6px 12px;
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
$suggestion-width: 280px;
|
||||||
|
$set-top: 34px;
|
||||||
|
$suggestion-padding: 6px 12px;
|
||||||
|
|
||||||
|
.container {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.input {
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputFocussed {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputOpen {
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestionsContainer {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestionsContainerOpen {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: $set-top;
|
||||||
|
min-width: 100%;
|
||||||
|
width: $suggestion-width;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
z-index: 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestionsList {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestion {
|
||||||
|
cursor: pointer;
|
||||||
|
word-break: break-word;
|
||||||
|
// padding: $suggestion-padding;
|
||||||
|
|
||||||
|
background-color: transparent;
|
||||||
|
color: inherit;
|
||||||
|
cursor: default;
|
||||||
|
display: block;
|
||||||
|
font-size: inherit;
|
||||||
|
padding: 8px 12px;
|
||||||
|
width: 100%;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-tap-highlight-color: rgba(0,0,0,0);
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #DEEBFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestionHighlighted {
|
||||||
|
background-color: #DEEBFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionTitle {
|
||||||
|
color: #999;
|
||||||
|
cursor: default;
|
||||||
|
display: block;
|
||||||
|
font-size: 75%;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 0.25em;
|
||||||
|
padding-left: 12px;
|
||||||
|
padding-right: 12px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionContainer {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionContainerFirst {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
@ -38,6 +38,7 @@ const SearchableSelectBox = ({
|
|||||||
value,
|
value,
|
||||||
bsClass,
|
bsClass,
|
||||||
styleOverrides,
|
styleOverrides,
|
||||||
|
placeholder,
|
||||||
filterOption,
|
filterOption,
|
||||||
}) => {
|
}) => {
|
||||||
/* Select element style customization */
|
/* Select element style customization */
|
||||||
@ -68,7 +69,7 @@ const SearchableSelectBox = ({
|
|||||||
isSearchable
|
isSearchable
|
||||||
components={{ Option: CustomOption }}
|
components={{ Option: CustomOption }}
|
||||||
classNamePrefix={`${bsClass}`}
|
classNamePrefix={`${bsClass}`}
|
||||||
placeholder="column_type"
|
placeholder={placeholder}
|
||||||
options={options}
|
options={options}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
value={value}
|
value={value}
|
||||||
|
@ -9,6 +9,8 @@ import {
|
|||||||
import { UPDATE_MIGRATION_STATUS_ERROR } from '../../../Main/Actions';
|
import { UPDATE_MIGRATION_STATUS_ERROR } from '../../../Main/Actions';
|
||||||
import { setTable } from '../DataActions.js';
|
import { setTable } from '../DataActions.js';
|
||||||
|
|
||||||
|
import { isPostgresFunction } from '../utils';
|
||||||
|
|
||||||
const SET_DEFAULTS = 'AddTable/SET_DEFAULTS';
|
const SET_DEFAULTS = 'AddTable/SET_DEFAULTS';
|
||||||
const SET_TABLENAME = 'AddTable/SET_TABLENAME';
|
const SET_TABLENAME = 'AddTable/SET_TABLENAME';
|
||||||
const SET_TABLECOMMENT = 'AddTable/SET_TABLECOMMENT';
|
const SET_TABLECOMMENT = 'AddTable/SET_TABLECOMMENT';
|
||||||
@ -121,7 +123,14 @@ const createTableSql = () => {
|
|||||||
) {
|
) {
|
||||||
if (currentCols[i].type === 'text') {
|
if (currentCols[i].type === 'text') {
|
||||||
// if a column type is text and if it has a default value, add a single quote by default
|
// if a column type is text and if it has a default value, add a single quote by default
|
||||||
|
const checkIfFunctionFormat = isPostgresFunction(
|
||||||
|
currentCols[i].default.value
|
||||||
|
);
|
||||||
|
if (!checkIfFunctionFormat) {
|
||||||
tableColumns += " DEFAULT '" + currentCols[i].default.value + "'";
|
tableColumns += " DEFAULT '" + currentCols[i].default.value + "'";
|
||||||
|
} else {
|
||||||
|
tableColumns += ' DEFAULT ' + currentCols[i].default.value;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (currentCols[i].type === 'uuid') {
|
if (currentCols[i].type === 'uuid') {
|
||||||
isUUIDDefault = true;
|
isUUIDDefault = true;
|
||||||
|
@ -29,7 +29,7 @@ import {
|
|||||||
setUniqueKeys,
|
setUniqueKeys,
|
||||||
} from './AddActions';
|
} from './AddActions';
|
||||||
|
|
||||||
import { fetchColumnTypes, RESET_COLUMN_TYPE_LIST } from '../DataActions';
|
import { fetchColumnTypeInfo, RESET_COLUMN_TYPE_INFO } from '../DataActions';
|
||||||
import { setDefaults, setPk, createTableSql } from './AddActions';
|
import { setDefaults, setPk, createTableSql } from './AddActions';
|
||||||
import { validationError, resetValidation } from './AddActions';
|
import { validationError, resetValidation } from './AddActions';
|
||||||
|
|
||||||
@ -71,12 +71,12 @@ class AddTable extends Component {
|
|||||||
this.setColDefaultValue = this.setColDefaultValue.bind(this);
|
this.setColDefaultValue = this.setColDefaultValue.bind(this);
|
||||||
}
|
}
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.props.dispatch(fetchColumnTypes());
|
this.props.dispatch(fetchColumnTypeInfo());
|
||||||
}
|
}
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.props.dispatch(setDefaults());
|
this.props.dispatch(setDefaults());
|
||||||
this.props.dispatch({
|
this.props.dispatch({
|
||||||
type: RESET_COLUMN_TYPE_LIST,
|
type: RESET_COLUMN_TYPE_INFO,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
onTableNameChange = e => {
|
onTableNameChange = e => {
|
||||||
@ -123,9 +123,9 @@ class AddTable extends Component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
setColDefaultValue = (i, isNullableChecked, e) => {
|
setColDefaultValue = (i, isNullableChecked, value) => {
|
||||||
const { dispatch } = this.props;
|
const { dispatch } = this.props;
|
||||||
dispatch(setColDefault(e.target.value, i, isNullableChecked));
|
dispatch(setColDefault(value, i, isNullableChecked));
|
||||||
};
|
};
|
||||||
|
|
||||||
columnValidation() {
|
columnValidation() {
|
||||||
@ -285,6 +285,8 @@ class AddTable extends Component {
|
|||||||
internalError,
|
internalError,
|
||||||
dataTypes,
|
dataTypes,
|
||||||
schemaList,
|
schemaList,
|
||||||
|
columnDefaultFunctions,
|
||||||
|
columnTypeCasts,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const styles = require('../../../Common/TableCommon/Table.scss');
|
const styles = require('../../../Common/TableCommon/Table.scss');
|
||||||
const getCreateBtnText = () => {
|
const getCreateBtnText = () => {
|
||||||
@ -322,6 +324,8 @@ class AddTable extends Component {
|
|||||||
<TableColumns
|
<TableColumns
|
||||||
uniqueKeys={uniqueKeys}
|
uniqueKeys={uniqueKeys}
|
||||||
dataTypes={dataTypes}
|
dataTypes={dataTypes}
|
||||||
|
columnDefaultFunctions={columnDefaultFunctions}
|
||||||
|
columnTypeCasts={columnTypeCasts}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
onRemoveColumn={this.onRemoveColumn}
|
onRemoveColumn={this.onRemoveColumn}
|
||||||
onColumnChange={this.onColumnNameChange}
|
onColumnChange={this.onColumnNameChange}
|
||||||
@ -435,6 +439,8 @@ const mapStateToProps = state => ({
|
|||||||
allSchemas: state.tables.allSchemas,
|
allSchemas: state.tables.allSchemas,
|
||||||
currentSchema: state.tables.currentSchema,
|
currentSchema: state.tables.currentSchema,
|
||||||
dataTypes: state.tables.columnDataTypes,
|
dataTypes: state.tables.columnDataTypes,
|
||||||
|
columnDefaultFunctions: state.tables.columnDefaultFunctions,
|
||||||
|
columnTypeCasts: state.tables.columnTypeCasts,
|
||||||
columnDataTypeFetchErr: state.tables.columnDataTypeFetchErr,
|
columnDataTypeFetchErr: state.tables.columnDataTypeFetchErr,
|
||||||
schemaList: state.tables.schemaList,
|
schemaList: state.tables.schemaList,
|
||||||
});
|
});
|
||||||
|
@ -3,11 +3,9 @@ import PropTypes from 'prop-types';
|
|||||||
|
|
||||||
import SearchableSelectBox from '../../../Common/SearchableSelect/SearchableSelect';
|
import SearchableSelectBox from '../../../Common/SearchableSelect/SearchableSelect';
|
||||||
import { commonDataTypes } from '../utils';
|
import { commonDataTypes } from '../utils';
|
||||||
import {
|
import { getDataOptions, inferDefaultValues } from '../Common/utils';
|
||||||
getDataOptions,
|
|
||||||
getPlaceholder,
|
import TableColumnDefault from './TableColumnDefault';
|
||||||
getDefaultValue,
|
|
||||||
} from '../Common/utils';
|
|
||||||
|
|
||||||
/* Custom style object for searchable select box */
|
/* Custom style object for searchable select box */
|
||||||
const customSelectBoxStyles = {
|
const customSelectBoxStyles = {
|
||||||
@ -17,6 +15,9 @@ const customSelectBoxStyles = {
|
|||||||
singleValue: {
|
singleValue: {
|
||||||
color: '#555555',
|
color: '#555555',
|
||||||
},
|
},
|
||||||
|
valueContainer: {
|
||||||
|
padding: '0px 12px',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const TableColumn = props => {
|
const TableColumn = props => {
|
||||||
@ -32,6 +33,8 @@ const TableColumn = props => {
|
|||||||
onColNullableChange,
|
onColNullableChange,
|
||||||
onColUniqueChange,
|
onColUniqueChange,
|
||||||
dataTypes: restTypes,
|
dataTypes: restTypes,
|
||||||
|
columnDefaultFunctions,
|
||||||
|
columnTypeCasts,
|
||||||
uniqueKeys,
|
uniqueKeys,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@ -56,6 +59,7 @@ const TableColumn = props => {
|
|||||||
restTypes,
|
restTypes,
|
||||||
i
|
i
|
||||||
);
|
);
|
||||||
|
|
||||||
const getRemoveIcon = colLen => {
|
const getRemoveIcon = colLen => {
|
||||||
let removeIcon;
|
let removeIcon;
|
||||||
if (i + 1 === colLen) {
|
if (i + 1 === colLen) {
|
||||||
@ -71,6 +75,16 @@ const TableColumn = props => {
|
|||||||
return removeIcon;
|
return removeIcon;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Collect list of relevant default values if the type doesn't have any default values
|
||||||
|
* */
|
||||||
|
const getInferredDefaultValues = () =>
|
||||||
|
inferDefaultValues(columnDefaultFunctions, columnTypeCasts)(column.type);
|
||||||
|
|
||||||
|
const defaultFunctions =
|
||||||
|
column.type in columnDefaultFunctions
|
||||||
|
? columnDefaultFunctions[column.type]
|
||||||
|
: getInferredDefaultValues();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={i} className={`${styles.display_flex} form-group`}>
|
<div key={i} className={`${styles.display_flex} form-group`}>
|
||||||
<input
|
<input
|
||||||
@ -92,8 +106,19 @@ const TableColumn = props => {
|
|||||||
bsClass={`col-type-${i} add_table_column_selector`}
|
bsClass={`col-type-${i} add_table_column_selector`}
|
||||||
styleOverrides={customSelectBoxStyles}
|
styleOverrides={customSelectBoxStyles}
|
||||||
filterOption={'prefix'}
|
filterOption={'prefix'}
|
||||||
|
placeholder="column_type"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
<span className={`${styles.inputDefault} ${styles.defaultWidth}`}>
|
||||||
|
<TableColumnDefault
|
||||||
|
onChange={setColDefaultValue}
|
||||||
|
colIndex={i}
|
||||||
|
testId={`col-default-${i}`}
|
||||||
|
column={column}
|
||||||
|
colDefaultFunctions={defaultFunctions}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
{/*
|
||||||
<input
|
<input
|
||||||
placeholder={getPlaceholder(column)}
|
placeholder={getPlaceholder(column)}
|
||||||
type="text"
|
type="text"
|
||||||
@ -107,7 +132,8 @@ const TableColumn = props => {
|
|||||||
column.nullable || false
|
column.nullable || false
|
||||||
)}
|
)}
|
||||||
data-test={`col-default-${i}`}
|
data-test={`col-default-${i}`}
|
||||||
/>{' '}
|
/>
|
||||||
|
*/}{' '}
|
||||||
<input
|
<input
|
||||||
className={`${styles.inputCheckbox} form-control `}
|
className={`${styles.inputCheckbox} form-control `}
|
||||||
checked={column.nullable}
|
checked={column.nullable}
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import CustomInputAutoSuggest from '../../../Common/CustomInputAutoSuggest/CustomInputAutoSuggest';
|
||||||
|
import {
|
||||||
|
getPlaceholder,
|
||||||
|
getDefaultValue,
|
||||||
|
getDefaultFunctionsOptions,
|
||||||
|
} from '../Common/utils';
|
||||||
|
|
||||||
|
const TableColumnDefault = ({
|
||||||
|
column,
|
||||||
|
colDefaultFunctions,
|
||||||
|
onChange,
|
||||||
|
testId,
|
||||||
|
colIndex: i,
|
||||||
|
}) => {
|
||||||
|
// const styles = require('../../../Common/TableCommon/Table.scss');
|
||||||
|
const handleColDefaultValueChange = (e, data) => {
|
||||||
|
const { newValue } = data;
|
||||||
|
onChange(i, column.nullable || false, newValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderTableColumnDefaultHtml = () => {
|
||||||
|
const dfVal = getDefaultValue(column);
|
||||||
|
|
||||||
|
/* Collect direct default functions and the indirect default functions */
|
||||||
|
const defaultValues = getDefaultFunctionsOptions(colDefaultFunctions, i);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CustomInputAutoSuggest
|
||||||
|
options={defaultValues}
|
||||||
|
onChange={handleColDefaultValueChange}
|
||||||
|
value={dfVal}
|
||||||
|
className={`col-default-value-${i} add_table_default_value_selector form-control`}
|
||||||
|
placeholder={getPlaceholder(column)}
|
||||||
|
id={`col-default-value-${i}`}
|
||||||
|
data-test={testId}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return renderTableColumnDefaultHtml();
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TableColumnDefault;
|
@ -1,8 +1,14 @@
|
|||||||
import { aggCategory, pgCategoryCode } from './PgInfo';
|
import { aggCategory, pgCategoryCode } from './PgInfo';
|
||||||
|
|
||||||
|
const commonlyUsedFunctions = ['now', 'gen_random_uuid', 'random'];
|
||||||
|
|
||||||
|
const getParanthesized = name => {
|
||||||
|
return `${name}()`;
|
||||||
|
};
|
||||||
|
|
||||||
const splitDbRow = row => {
|
const splitDbRow = row => {
|
||||||
/* Splits comma seperated type names
|
/* Splits comma seperated type names
|
||||||
* Splits comma seperated type display names
|
* Splits comma seperated type user friendly type names
|
||||||
* Splits comma seperated type descriptions
|
* Splits comma seperated type descriptions
|
||||||
* */
|
* */
|
||||||
return {
|
return {
|
||||||
@ -59,6 +65,39 @@ const getDataTypeInfo = (row, categoryInfo, colId, cached = {}) => {
|
|||||||
return { typInfo: currTypeObj, typValueMap: columnTypeValueMap };
|
return { typInfo: currTypeObj, typValueMap: columnTypeValueMap };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getDefaultFunctionsOptions = (funcs, identifier) => {
|
||||||
|
const defaultValues = [
|
||||||
|
{
|
||||||
|
title: 'All Functions',
|
||||||
|
suggestions: [],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
funcs.forEach((f, i) => {
|
||||||
|
const funcVal = getParanthesized(f);
|
||||||
|
const suggestionObj = {
|
||||||
|
value: funcVal,
|
||||||
|
label: funcVal,
|
||||||
|
description: funcVal,
|
||||||
|
key: i,
|
||||||
|
colIdentifier: identifier,
|
||||||
|
title: 'All Functions',
|
||||||
|
};
|
||||||
|
if (commonlyUsedFunctions.indexOf(f) !== -1) {
|
||||||
|
if (defaultValues.length === 1) {
|
||||||
|
defaultValues.push({
|
||||||
|
title: 'Frequently Used Functions',
|
||||||
|
suggestions: [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
defaultValues[1].suggestions.push(suggestionObj);
|
||||||
|
} else {
|
||||||
|
defaultValues[0].suggestions.push(suggestionObj);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
/* Reversing the array just so that if frequently used types were present, they come first */
|
||||||
|
return defaultValues.reverse();
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Input arguments:
|
* Input arguments:
|
||||||
* dataTypes -> Frequently used types
|
* dataTypes -> Frequently used types
|
||||||
@ -136,10 +175,41 @@ const getDefaultValue = column => {
|
|||||||
return ('default' in column && column.default.value) || '';
|
return ('default' in column && column.default.value) || '';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getRecommendedTypeCasts = (dataType, typeCasts) => {
|
||||||
|
return (dataType in typeCasts && typeCasts[dataType][3].split(',')) || [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const inferDefaultValues = (defaultFuncs, typeCasts) => {
|
||||||
|
let defaultValues = [];
|
||||||
|
const visitedType = {};
|
||||||
|
/* Current type is the type for which default values needs to be computed
|
||||||
|
* Algorithm:
|
||||||
|
* Look for the types which the current type can be casted to
|
||||||
|
* Try to find the default values for the right type and accumulate it to an array
|
||||||
|
* */
|
||||||
|
const computeDefaultValues = currentType => {
|
||||||
|
visitedType[currentType] = true;
|
||||||
|
/* Retrieve the recommended type casts for the current type */
|
||||||
|
const validRightCasts = getRecommendedTypeCasts(currentType, typeCasts);
|
||||||
|
validRightCasts.forEach(v => {
|
||||||
|
if (!visitedType[v]) {
|
||||||
|
if (v in defaultFuncs) {
|
||||||
|
visitedType[v] = true;
|
||||||
|
defaultValues = [...defaultValues, ...defaultFuncs[v]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return defaultValues;
|
||||||
|
};
|
||||||
|
return computeDefaultValues;
|
||||||
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
getDataOptions,
|
getDataOptions,
|
||||||
getPlaceholder,
|
getPlaceholder,
|
||||||
getDefaultValue,
|
getDefaultValue,
|
||||||
getDataTypeInfo,
|
getDataTypeInfo,
|
||||||
getAllDataTypeMap,
|
getAllDataTypeMap,
|
||||||
|
getDefaultFunctionsOptions,
|
||||||
|
inferDefaultValues,
|
||||||
};
|
};
|
||||||
|
@ -29,7 +29,10 @@ import {
|
|||||||
fetchTrackedTableListQuery,
|
fetchTrackedTableListQuery,
|
||||||
mergeLoadSchemaData,
|
mergeLoadSchemaData,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
import { fetchColumnTypesQuery } from './utils';
|
|
||||||
|
import { fetchColumnTypesQuery, fetchColumnDefaultFunctions } from './utils';
|
||||||
|
|
||||||
|
import { fetchColumnCastsQuery, convertArrayToJson } from './TableModify/utils';
|
||||||
|
|
||||||
import { SERVER_CONSOLE_MODE } from '../../../constants';
|
import { SERVER_CONSOLE_MODE } from '../../../constants';
|
||||||
|
|
||||||
@ -47,9 +50,9 @@ const UPDATE_REMOTE_SCHEMA_MANUAL_REL = 'Data/UPDATE_SCHEMA_MANUAL_REL';
|
|||||||
const SET_CONSISTENT_SCHEMA = 'Data/SET_CONSISTENT_SCHEMA';
|
const SET_CONSISTENT_SCHEMA = 'Data/SET_CONSISTENT_SCHEMA';
|
||||||
const SET_CONSISTENT_FUNCTIONS = 'Data/SET_CONSISTENT_FUNCTIONS';
|
const SET_CONSISTENT_FUNCTIONS = 'Data/SET_CONSISTENT_FUNCTIONS';
|
||||||
|
|
||||||
const FETCH_COLUMN_TYPE_LIST = 'Data/FETCH_COLUMN_TYPE_LIST';
|
const FETCH_COLUMN_TYPE_INFO = 'Data/FETCH_COLUMN_TYPE_INFO';
|
||||||
const FETCH_COLUMN_TYPE_LIST_FAIL = 'Data/FETCH_COLUMN_TYPE_LIST_FAIL';
|
const FETCH_COLUMN_TYPE_INFO_FAIL = 'Data/FETCH_COLUMN_TYPE_INFO_FAIL';
|
||||||
const RESET_COLUMN_TYPE_LIST = 'Data/RESET_COLUMN_TYPE_LIST';
|
const RESET_COLUMN_TYPE_INFO = 'Data/RESET_COLUMN_TYPE_INFO';
|
||||||
|
|
||||||
const MAKE_REQUEST = 'ModifyTable/MAKE_REQUEST';
|
const MAKE_REQUEST = 'ModifyTable/MAKE_REQUEST';
|
||||||
const REQUEST_SUCCESS = 'ModifyTable/REQUEST_SUCCESS';
|
const REQUEST_SUCCESS = 'ModifyTable/REQUEST_SUCCESS';
|
||||||
@ -535,15 +538,39 @@ const makeMigrationCall = (
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchColumnTypes = () => {
|
const getBulkColumnInfoFetchQuery = schema => {
|
||||||
return (dispatch, getState) => {
|
const fetchColumnTypes = {
|
||||||
const url = Endpoints.getSchema;
|
|
||||||
const reqQuery = {
|
|
||||||
type: 'run_sql',
|
type: 'run_sql',
|
||||||
args: {
|
args: {
|
||||||
sql: fetchColumnTypesQuery,
|
sql: fetchColumnTypesQuery,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
const fetchTypeDefaultValues = {
|
||||||
|
type: 'run_sql',
|
||||||
|
args: {
|
||||||
|
sql: fetchColumnDefaultFunctions(schema),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchValidTypeCasts = {
|
||||||
|
type: 'run_sql',
|
||||||
|
args: {
|
||||||
|
sql: fetchColumnCastsQuery,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'bulk',
|
||||||
|
args: [fetchColumnTypes, fetchTypeDefaultValues, fetchValidTypeCasts],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchColumnTypeInfo = () => {
|
||||||
|
return (dispatch, getState) => {
|
||||||
|
const url = Endpoints.getSchema;
|
||||||
|
const currState = getState();
|
||||||
|
const { currentSchema } = currState.tables;
|
||||||
|
const reqQuery = getBulkColumnInfoFetchQuery(currentSchema);
|
||||||
const options = {
|
const options = {
|
||||||
credentials: globalCookiePolicy,
|
credentials: globalCookiePolicy,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -552,9 +579,20 @@ const fetchColumnTypes = () => {
|
|||||||
};
|
};
|
||||||
return dispatch(requestAction(url, options)).then(
|
return dispatch(requestAction(url, options)).then(
|
||||||
data => {
|
data => {
|
||||||
|
const resultData = data[1].result.slice(1);
|
||||||
|
const typeFuncsMap = {};
|
||||||
|
|
||||||
|
resultData.forEach(r => {
|
||||||
|
typeFuncsMap[r[1]] = r[0].split(',');
|
||||||
|
});
|
||||||
|
const columnDataTypeInfo = {
|
||||||
|
columnDataTypes: data[0].result.slice(1),
|
||||||
|
columnTypeDefaultValues: typeFuncsMap,
|
||||||
|
columnTypeCasts: convertArrayToJson(data[2].result.slice(1)),
|
||||||
|
};
|
||||||
return dispatch({
|
return dispatch({
|
||||||
type: FETCH_COLUMN_TYPE_LIST,
|
type: FETCH_COLUMN_TYPE_INFO,
|
||||||
data: data.result.slice(1),
|
data: columnDataTypeInfo,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
@ -567,7 +605,7 @@ const fetchColumnTypes = () => {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
return dispatch({
|
return dispatch({
|
||||||
type: FETCH_COLUMN_TYPE_LIST_FAIL,
|
type: FETCH_COLUMN_TYPE_INFO_FAIL,
|
||||||
data: error,
|
data: error,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -680,24 +718,30 @@ const dataReducer = (state = defaultState, action) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
case FETCH_COLUMN_TYPE_LIST:
|
case FETCH_COLUMN_TYPE_INFO:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
columnDataTypes: action.data,
|
columnDataTypes: action.data.columnDataTypes,
|
||||||
columnDataTypeFetchErr: defaultState.columnDataTypeFetchErr,
|
columnDefaultFunctions: action.data.columnTypeDefaultValues,
|
||||||
|
columnDataTypeInfoErr: null,
|
||||||
|
columnTypeCasts: action.data.columnTypeCasts,
|
||||||
};
|
};
|
||||||
|
|
||||||
case FETCH_COLUMN_TYPE_LIST_FAIL:
|
case FETCH_COLUMN_TYPE_INFO_FAIL:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
columnDataTypes: [],
|
columnDataTypes: [],
|
||||||
columnDataTypeFetchErr: action.data,
|
columnDefaultFunctions: {},
|
||||||
|
columnTypeCasts: {},
|
||||||
|
columnDataTypeInfoErr: action.data,
|
||||||
};
|
};
|
||||||
case RESET_COLUMN_TYPE_LIST:
|
case RESET_COLUMN_TYPE_INFO:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
columnDataTypes: [...defaultState.columnDataTypes],
|
columnDataTypes: [...defaultState.columnDataTypes],
|
||||||
columnDataTypeFetchErr: defaultState.columnDataTypeFetchErr,
|
columnDefaultFunctions: { ...defaultState.columnDefaultFunctions },
|
||||||
|
columnTypeCasts: { ...defaultState.columnTypeCasts },
|
||||||
|
columnDataTypeInfoErr: defaultState.columnDataTypeInfoErr,
|
||||||
};
|
};
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
@ -727,7 +771,7 @@ export {
|
|||||||
LOAD_SCHEMA,
|
LOAD_SCHEMA,
|
||||||
setConsistentSchema,
|
setConsistentSchema,
|
||||||
setConsistentFunctions,
|
setConsistentFunctions,
|
||||||
fetchColumnTypes,
|
fetchColumnTypeInfo,
|
||||||
RESET_COLUMN_TYPE_LIST,
|
RESET_COLUMN_TYPE_INFO,
|
||||||
setUntrackedRelations,
|
setUntrackedRelations,
|
||||||
};
|
};
|
||||||
|
@ -136,7 +136,9 @@ const defaultModifyState = {
|
|||||||
|
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
columnDataTypes: [], // To store list of column types supported by postgres
|
columnDataTypes: [], // To store list of column types supported by postgres
|
||||||
columnDataTypeFetchErr: null,
|
columnDataTypeInfoErr: null,
|
||||||
|
columnDefaultFunctions: {},
|
||||||
|
columnTypeCasts: {},
|
||||||
currentTable: null,
|
currentTable: null,
|
||||||
view: { ...defaultViewState },
|
view: { ...defaultViewState },
|
||||||
modify: { ...defaultModifyState },
|
modify: { ...defaultModifyState },
|
||||||
|
@ -129,7 +129,8 @@ class InsertItem extends Component {
|
|||||||
type: 'text',
|
type: 'text',
|
||||||
};
|
};
|
||||||
|
|
||||||
const colType = col.data_type;
|
// const colType = col.data_type;
|
||||||
|
const colType = col.udt_name;
|
||||||
const placeHolder = hasDefault
|
const placeHolder = hasDefault
|
||||||
? col.column_default
|
? col.column_default
|
||||||
: getPlaceholder(colType);
|
: getPlaceholder(colType);
|
||||||
|
@ -4,8 +4,13 @@ import gqlPattern, { gqlColumnErrorNotif } from '../Common/GraphQLValidation';
|
|||||||
import { commonDataTypes } from '../utils';
|
import { commonDataTypes } from '../utils';
|
||||||
|
|
||||||
import SearchableSelectBox from '../../../Common/SearchableSelect/SearchableSelect';
|
import SearchableSelectBox from '../../../Common/SearchableSelect/SearchableSelect';
|
||||||
|
import CustomInputAutoSuggest from '../../../Common/CustomInputAutoSuggest/CustomInputAutoSuggest';
|
||||||
|
|
||||||
import { getDataOptions } from '../Common/utils';
|
import {
|
||||||
|
getDataOptions,
|
||||||
|
getDefaultFunctionsOptions,
|
||||||
|
inferDefaultValues,
|
||||||
|
} from '../Common/utils';
|
||||||
|
|
||||||
import Button from '../../../Common/Button/Button';
|
import Button from '../../../Common/Button/Button';
|
||||||
import { addColSql } from '../TableModify/ModifyActions';
|
import { addColSql } from '../TableModify/ModifyActions';
|
||||||
@ -90,15 +95,22 @@ const useColumnEditor = (dispatch, tableName) => {
|
|||||||
},
|
},
|
||||||
colDefault: {
|
colDefault: {
|
||||||
value: colDefault,
|
value: colDefault,
|
||||||
onChange: e => {
|
onChange: (e, data) => {
|
||||||
setColumnState({ ...columnState, colDefault: e.target.value });
|
const { newValue } = data;
|
||||||
|
setColumnState({ ...columnState, colDefault: newValue });
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
onSubmit,
|
onSubmit,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const ColumnCreator = ({ dispatch, tableName, dataTypes: restTypes = [] }) => {
|
const ColumnCreator = ({
|
||||||
|
dispatch,
|
||||||
|
tableName,
|
||||||
|
dataTypes: restTypes = [],
|
||||||
|
validTypeCasts,
|
||||||
|
columnDefaultFunctions,
|
||||||
|
}) => {
|
||||||
const {
|
const {
|
||||||
colName,
|
colName,
|
||||||
colType,
|
colType,
|
||||||
@ -108,6 +120,37 @@ const ColumnCreator = ({ dispatch, tableName, dataTypes: restTypes = [] }) => {
|
|||||||
onSubmit,
|
onSubmit,
|
||||||
} = useColumnEditor(dispatch, tableName);
|
} = useColumnEditor(dispatch, tableName);
|
||||||
|
|
||||||
|
let defaultOptions = [];
|
||||||
|
|
||||||
|
const getInferredDefaultValues = () =>
|
||||||
|
inferDefaultValues(columnDefaultFunctions, validTypeCasts)(colType.value);
|
||||||
|
|
||||||
|
const colDefaultFunctions =
|
||||||
|
colType.value in columnDefaultFunctions
|
||||||
|
? columnDefaultFunctions[colType.value]
|
||||||
|
: getInferredDefaultValues();
|
||||||
|
|
||||||
|
if (colDefaultFunctions && colDefaultFunctions.length > 0) {
|
||||||
|
defaultOptions = getDefaultFunctionsOptions(colDefaultFunctions, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getDefaultInput = () => {
|
||||||
|
const theme = require('../../../Common/CustomInputAutoSuggest/CustomThemes/AddColumnDefault.scss');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CustomInputAutoSuggest
|
||||||
|
placeholder="default value"
|
||||||
|
options={defaultOptions}
|
||||||
|
className={`${styles.input}
|
||||||
|
${styles.defaultInput}
|
||||||
|
input-sm form-control`}
|
||||||
|
{...colDefault}
|
||||||
|
data-test="default-value"
|
||||||
|
theme={theme}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const { columnDataTypes, columnTypeValueMap } = getDataOptions(
|
const { columnDataTypes, columnTypeValueMap } = getDataOptions(
|
||||||
commonDataTypes,
|
commonDataTypes,
|
||||||
restTypes,
|
restTypes,
|
||||||
@ -153,6 +196,7 @@ const ColumnCreator = ({ dispatch, tableName, dataTypes: restTypes = [] }) => {
|
|||||||
bsClass={`col-type-${0} modify_select`}
|
bsClass={`col-type-${0} modify_select`}
|
||||||
styleOverrides={customSelectBoxStyles}
|
styleOverrides={customSelectBoxStyles}
|
||||||
filterOption={'prefix'}
|
filterOption={'prefix'}
|
||||||
|
placeholder="column_type"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<input
|
<input
|
||||||
@ -170,7 +214,8 @@ const ColumnCreator = ({ dispatch, tableName, dataTypes: restTypes = [] }) => {
|
|||||||
data-test="unique-checkbox"
|
data-test="unique-checkbox"
|
||||||
/>
|
/>
|
||||||
<label className={styles.nullLabel}>Unique</label>
|
<label className={styles.nullLabel}>Unique</label>
|
||||||
|
{getDefaultInput()}
|
||||||
|
{/*
|
||||||
<input
|
<input
|
||||||
placeholder="default value"
|
placeholder="default value"
|
||||||
type="text"
|
type="text"
|
||||||
@ -180,6 +225,7 @@ const ColumnCreator = ({ dispatch, tableName, dataTypes: restTypes = [] }) => {
|
|||||||
{...colDefault}
|
{...colDefault}
|
||||||
data-test="default-value"
|
data-test="default-value"
|
||||||
/>
|
/>
|
||||||
|
*/}
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
|
|
||||||
import SearchableSelectBox from '../../../Common/SearchableSelect/SearchableSelect';
|
import SearchableSelectBox from '../../../Common/SearchableSelect/SearchableSelect';
|
||||||
|
import CustomInputAutoSuggest from '../../../Common/CustomInputAutoSuggest/CustomInputAutoSuggest';
|
||||||
|
|
||||||
import { getValidAlterOptions } from './utils';
|
import { getValidAlterOptions } from './utils';
|
||||||
|
|
||||||
@ -12,6 +13,7 @@ const ColumnEditor = ({
|
|||||||
selectedProperties,
|
selectedProperties,
|
||||||
editColumn,
|
editColumn,
|
||||||
alterTypeOptions,
|
alterTypeOptions,
|
||||||
|
defaultOptions,
|
||||||
}) => {
|
}) => {
|
||||||
const colName = columnProperties.name;
|
const colName = columnProperties.name;
|
||||||
|
|
||||||
@ -62,8 +64,9 @@ const ColumnEditor = ({
|
|||||||
const updateColumnType = selected => {
|
const updateColumnType = selected => {
|
||||||
dispatch(editColumn(colName, 'type', selected.value));
|
dispatch(editColumn(colName, 'type', selected.value));
|
||||||
};
|
};
|
||||||
const updateColumnDef = e => {
|
const updateColumnDef = (e, data) => {
|
||||||
dispatch(editColumn(colName, 'default', e.target.value));
|
const { newValue } = data;
|
||||||
|
dispatch(editColumn(colName, 'default', newValue));
|
||||||
};
|
};
|
||||||
const updateColumnComment = e => {
|
const updateColumnComment = e => {
|
||||||
dispatch(editColumn(colName, 'comment', e.target.value));
|
dispatch(editColumn(colName, 'comment', e.target.value));
|
||||||
@ -75,6 +78,23 @@ const ColumnEditor = ({
|
|||||||
dispatch(editColumn(colName, 'isUnique', e.target.value === 'true'));
|
dispatch(editColumn(colName, 'isUnique', e.target.value === 'true'));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getDefaultInput = () => {
|
||||||
|
const theme = require('../../../Common/CustomInputAutoSuggest/CustomThemes/EditColumnDefault.scss');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CustomInputAutoSuggest
|
||||||
|
options={defaultOptions}
|
||||||
|
className="input-sm form-control"
|
||||||
|
value={selectedProperties[colName].default || ''}
|
||||||
|
onChange={updateColumnDef}
|
||||||
|
type="text"
|
||||||
|
disabled={columnProperties.pkConstraint}
|
||||||
|
data-test="edit-col-default"
|
||||||
|
theme={theme}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`${styles.colEditor} container-fluid`}>
|
<div className={`${styles.colEditor} container-fluid`}>
|
||||||
<form className="form-horizontal" onSubmit={onSubmit}>
|
<form className="form-horizontal" onSubmit={onSubmit}>
|
||||||
@ -100,6 +120,7 @@ const ColumnEditor = ({
|
|||||||
bsClass={`col-type-${0} modify_select`}
|
bsClass={`col-type-${0} modify_select`}
|
||||||
styleOverrides={customSelectBoxStyles}
|
styleOverrides={customSelectBoxStyles}
|
||||||
filterOption={'prefix'}
|
filterOption={'prefix'}
|
||||||
|
placeholder="column_type"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -136,6 +157,8 @@ const ColumnEditor = ({
|
|||||||
<div className={`${styles.display_flex} form-group`}>
|
<div className={`${styles.display_flex} form-group`}>
|
||||||
<label className="col-xs-2">Default</label>
|
<label className="col-xs-2">Default</label>
|
||||||
<div className="col-xs-6">
|
<div className="col-xs-6">
|
||||||
|
{getDefaultInput()}
|
||||||
|
{/*
|
||||||
<input
|
<input
|
||||||
className="input-sm form-control"
|
className="input-sm form-control"
|
||||||
value={selectedProperties[colName].default || ''}
|
value={selectedProperties[colName].default || ''}
|
||||||
@ -144,6 +167,7 @@ const ColumnEditor = ({
|
|||||||
disabled={columnProperties.pkConstraint}
|
disabled={columnProperties.pkConstraint}
|
||||||
data-test="edit-col-default"
|
data-test="edit-col-default"
|
||||||
/>
|
/>
|
||||||
|
*/}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={`${styles.display_flex} form-group`}>
|
<div className={`${styles.display_flex} form-group`}>
|
||||||
|
@ -12,6 +12,11 @@ import {
|
|||||||
import { ordinalColSort } from '../utils';
|
import { ordinalColSort } from '../utils';
|
||||||
import { defaultDataTypeToCast } from '../constants';
|
import { defaultDataTypeToCast } from '../constants';
|
||||||
|
|
||||||
|
import {
|
||||||
|
getDefaultFunctionsOptions,
|
||||||
|
inferDefaultValues,
|
||||||
|
} from '../Common/utils';
|
||||||
|
|
||||||
import styles from './ModifyTable.scss';
|
import styles from './ModifyTable.scss';
|
||||||
|
|
||||||
const ColumnEditorList = ({
|
const ColumnEditorList = ({
|
||||||
@ -21,6 +26,7 @@ const ColumnEditorList = ({
|
|||||||
dispatch,
|
dispatch,
|
||||||
validTypeCasts,
|
validTypeCasts,
|
||||||
dataTypeIndexMap,
|
dataTypeIndexMap,
|
||||||
|
columnDefaultFunctions,
|
||||||
}) => {
|
}) => {
|
||||||
const tableName = tableSchema.table_name;
|
const tableName = tableSchema.table_name;
|
||||||
|
|
||||||
@ -131,21 +137,55 @@ const ColumnEditorList = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* If the dataTypeIndexMap is not loaded, then just load the current type information
|
||||||
|
* */
|
||||||
|
|
||||||
const getValidTypeCasts = udtName => {
|
const getValidTypeCasts = udtName => {
|
||||||
const lowerUdtName = udtName.toLowerCase();
|
const lowerUdtName = udtName.toLowerCase();
|
||||||
if (lowerUdtName in validTypeCasts) {
|
if (lowerUdtName in validTypeCasts) {
|
||||||
return validTypeCasts[lowerUdtName];
|
return validTypeCasts[lowerUdtName];
|
||||||
}
|
}
|
||||||
|
if (dataTypeIndexMap && Object.keys(dataTypeIndexMap).length > 0) {
|
||||||
return [
|
return [
|
||||||
...dataTypeIndexMap[lowerUdtName],
|
...dataTypeIndexMap[lowerUdtName],
|
||||||
...dataTypeIndexMap[defaultDataTypeToCast],
|
...dataTypeIndexMap[defaultDataTypeToCast],
|
||||||
];
|
];
|
||||||
|
}
|
||||||
|
return [lowerUdtName, lowerUdtName, ''];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getValidDefaultTypes = udtName => {
|
||||||
|
const lowerUdtName = udtName.toLowerCase();
|
||||||
|
let defaultOptions = [];
|
||||||
|
if (lowerUdtName in columnDefaultFunctions) {
|
||||||
|
defaultOptions = columnDefaultFunctions[lowerUdtName];
|
||||||
|
} else {
|
||||||
|
defaultOptions = inferDefaultValues(
|
||||||
|
columnDefaultFunctions,
|
||||||
|
validTypeCasts
|
||||||
|
)(lowerUdtName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return getDefaultFunctionsOptions(defaultOptions);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Alter type options contains a list of items and its valid castable types
|
||||||
|
* [
|
||||||
|
* "Data type",
|
||||||
|
* "User friendly name of the data type",
|
||||||
|
* "Description of the data type",
|
||||||
|
* "Comma seperated castable data types",
|
||||||
|
* "Comma seperated user friendly names of the castable data types",
|
||||||
|
* "Colon seperated user friendly description of the castable data types"
|
||||||
|
* ]
|
||||||
|
* */
|
||||||
|
|
||||||
const colEditorExpanded = () => {
|
const colEditorExpanded = () => {
|
||||||
return (
|
return (
|
||||||
<ColumnEditor
|
<ColumnEditor
|
||||||
alterTypeOptions={getValidTypeCasts(col.udt_name)}
|
alterTypeOptions={getValidTypeCasts(col.udt_name)}
|
||||||
|
defaultOptions={getValidDefaultTypes(col.udt_name)}
|
||||||
column={col}
|
column={col}
|
||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
onDelete={safeOnDelete}
|
onDelete={safeOnDelete}
|
||||||
|
@ -25,6 +25,8 @@ import {
|
|||||||
getUniqueConstraintName,
|
getUniqueConstraintName,
|
||||||
} from '../Common/ReusableComponents/utils';
|
} from '../Common/ReusableComponents/utils';
|
||||||
|
|
||||||
|
import { isPostgresFunction } from '../utils';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
fetchColumnCastsQuery,
|
fetchColumnCastsQuery,
|
||||||
convertArrayToJson,
|
convertArrayToJson,
|
||||||
@ -881,7 +883,9 @@ const addColSql = (
|
|||||||
callback
|
callback
|
||||||
) => {
|
) => {
|
||||||
let defWithQuotes = "''";
|
let defWithQuotes = "''";
|
||||||
if (colType === 'text' && colDefault !== '') {
|
|
||||||
|
const checkIfFunctionFormat = isPostgresFunction(colDefault);
|
||||||
|
if (colType === 'text' && colDefault !== '' && !checkIfFunctionFormat) {
|
||||||
defWithQuotes = "'" + colDefault + "'";
|
defWithQuotes = "'" + colDefault + "'";
|
||||||
} else {
|
} else {
|
||||||
defWithQuotes = colDefault;
|
defWithQuotes = colDefault;
|
||||||
@ -1167,9 +1171,10 @@ const saveColumnChangesSql = (colName, column) => {
|
|||||||
const comment = columnEdit.comment || '';
|
const comment = columnEdit.comment || '';
|
||||||
const newName = columnEdit.name;
|
const newName = columnEdit.name;
|
||||||
const currentSchema = columnEdit.schemaName;
|
const currentSchema = columnEdit.schemaName;
|
||||||
|
const checkIfFunctionFormat = isPostgresFunction(def);
|
||||||
// ALTER TABLE <table> ALTER COLUMN <column> TYPE <column_type>;
|
// ALTER TABLE <table> ALTER COLUMN <column> TYPE <column_type>;
|
||||||
let defWithQuotes;
|
let defWithQuotes;
|
||||||
if (colType === 'text') {
|
if (colType === 'text' && !checkIfFunctionFormat) {
|
||||||
defWithQuotes = `'${def}'`;
|
defWithQuotes = `'${def}'`;
|
||||||
} else {
|
} else {
|
||||||
defWithQuotes = def;
|
defWithQuotes = def;
|
||||||
|
@ -8,13 +8,12 @@ import {
|
|||||||
deleteTableSql,
|
deleteTableSql,
|
||||||
untrackTableSql,
|
untrackTableSql,
|
||||||
RESET,
|
RESET,
|
||||||
fetchColumnCasts,
|
|
||||||
setUniqueKeys,
|
setUniqueKeys,
|
||||||
} from '../TableModify/ModifyActions';
|
} from '../TableModify/ModifyActions';
|
||||||
import {
|
import {
|
||||||
setTable,
|
setTable,
|
||||||
fetchColumnTypes,
|
fetchColumnTypeInfo,
|
||||||
RESET_COLUMN_TYPE_LIST,
|
RESET_COLUMN_TYPE_INFO,
|
||||||
} from '../DataActions';
|
} from '../DataActions';
|
||||||
import Button from '../../../Common/Button/Button';
|
import Button from '../../../Common/Button/Button';
|
||||||
import ColumnEditorList from './ColumnEditorList';
|
import ColumnEditorList from './ColumnEditorList';
|
||||||
@ -31,12 +30,11 @@ class ModifyTable extends React.Component {
|
|||||||
const { dispatch } = this.props;
|
const { dispatch } = this.props;
|
||||||
dispatch({ type: RESET });
|
dispatch({ type: RESET });
|
||||||
dispatch(setTable(this.props.tableName));
|
dispatch(setTable(this.props.tableName));
|
||||||
dispatch(fetchColumnTypes());
|
dispatch(fetchColumnTypeInfo());
|
||||||
dispatch(fetchColumnCasts());
|
|
||||||
}
|
}
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.props.dispatch({
|
this.props.dispatch({
|
||||||
type: RESET_COLUMN_TYPE_LIST,
|
type: RESET_COLUMN_TYPE_INFO,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
render() {
|
render() {
|
||||||
@ -53,6 +51,7 @@ class ModifyTable extends React.Component {
|
|||||||
dataTypes,
|
dataTypes,
|
||||||
validTypeCasts,
|
validTypeCasts,
|
||||||
uniqueKeyModify,
|
uniqueKeyModify,
|
||||||
|
columnDefaultFunctions,
|
||||||
schemaList,
|
schemaList,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
@ -135,6 +134,7 @@ class ModifyTable extends React.Component {
|
|||||||
columnEdit={columnEdit}
|
columnEdit={columnEdit}
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
currentSchema={currentSchema}
|
currentSchema={currentSchema}
|
||||||
|
columnDefaultFunctions={columnDefaultFunctions}
|
||||||
/>
|
/>
|
||||||
<hr />
|
<hr />
|
||||||
<h4 className={styles.subheading_text}>Add a new column</h4>
|
<h4 className={styles.subheading_text}>Add a new column</h4>
|
||||||
@ -142,6 +142,8 @@ class ModifyTable extends React.Component {
|
|||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
tableName={tableName}
|
tableName={tableName}
|
||||||
dataTypes={dataTypes}
|
dataTypes={dataTypes}
|
||||||
|
validTypeCasts={validTypeCasts}
|
||||||
|
columnDefaultFunctions={columnDefaultFunctions}
|
||||||
/>
|
/>
|
||||||
<hr />
|
<hr />
|
||||||
<h4 className={styles.subheading_text}>Primary Key</h4>
|
<h4 className={styles.subheading_text}>Primary Key</h4>
|
||||||
@ -212,7 +214,8 @@ const mapStateToProps = (state, ownProps) => ({
|
|||||||
pkModify: state.tables.modify.pkModify,
|
pkModify: state.tables.modify.pkModify,
|
||||||
fkModify: state.tables.modify.fkModify,
|
fkModify: state.tables.modify.fkModify,
|
||||||
dataTypes: state.tables.columnDataTypes,
|
dataTypes: state.tables.columnDataTypes,
|
||||||
validTypeCasts: state.tables.modify.alterColumnOptions,
|
columnDefaultFunctions: state.tables.columnDefaultFunctions,
|
||||||
|
validTypeCasts: state.tables.columnTypeCasts,
|
||||||
columnDataTypeFetchErr: state.tables.columnDataTypeFetchErr,
|
columnDataTypeFetchErr: state.tables.columnDataTypeFetchErr,
|
||||||
schemaList: state.tables.schemaList,
|
schemaList: state.tables.schemaList,
|
||||||
...state.tables.modify,
|
...state.tables.modify,
|
||||||
|
@ -14,22 +14,31 @@ const getValidAlterOptions = (alterTypeOptions, colName) => {
|
|||||||
colName,
|
colName,
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
|
/*
|
||||||
|
* alterTypeOptions can also only contain only three elements
|
||||||
|
*/
|
||||||
|
let allInfo = [...currentInfo];
|
||||||
|
let allOptionsMap = {
|
||||||
|
...currentMap,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (alterTypeOptions.length > 3) {
|
||||||
const {
|
const {
|
||||||
typInfo: validOptions,
|
typInfo: validOptions,
|
||||||
typValueMap: validOptionsMap,
|
typValueMap: validOptionsMap,
|
||||||
} = getDataTypeInfo(alterTypeOptions.slice(3, 6), colName, 0);
|
} = getDataTypeInfo(alterTypeOptions.slice(3, 6), colName, 0);
|
||||||
|
|
||||||
const _allInfo = [...currentInfo, ...validOptions];
|
allInfo = allInfo.concat(validOptions);
|
||||||
|
// const allInfo = [...currentInfo, ...validOptions];
|
||||||
const _allOptionsMap = {
|
allOptionsMap = {
|
||||||
...validOptionsMap,
|
...validOptionsMap,
|
||||||
...currentMap,
|
...currentMap,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
alterOptions: _allInfo,
|
alterOptions: allInfo,
|
||||||
alterOptionsValueMap: _allOptionsMap,
|
alterOptionsValueMap: allOptionsMap,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -587,3 +587,29 @@ WHERE (t.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHER
|
|||||||
AND t.typname != 'unknown'
|
AND t.typname != 'unknown'
|
||||||
AND t.typcategory != 'P'
|
AND t.typcategory != 'P'
|
||||||
GROUP BY t.typcategory;`;
|
GROUP BY t.typcategory;`;
|
||||||
|
|
||||||
|
export const fetchColumnDefaultFunctions = (schema = 'public') => `
|
||||||
|
SELECT string_agg(pgp.proname, ','),
|
||||||
|
t.typname as "Type"
|
||||||
|
from pg_proc pgp
|
||||||
|
JOIN pg_type t
|
||||||
|
ON pgp.prorettype = t.oid
|
||||||
|
JOIN pg_namespace pgn
|
||||||
|
ON pgn.oid = pgp.pronamespace
|
||||||
|
WHERE (t.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid))
|
||||||
|
AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type el WHERE el.oid = t.typelem AND el.typarray = t.oid)
|
||||||
|
AND pg_catalog.pg_type_is_visible(t.oid)
|
||||||
|
AND t.typname != 'unknown'
|
||||||
|
AND t.typcategory != 'P'
|
||||||
|
AND (array_length(pgp.proargtypes, 1) = 0)
|
||||||
|
AND ( pgn.nspname = '${schema}' OR pgn.nspname = 'pg_catalog' )
|
||||||
|
AND pgp.proretset=false
|
||||||
|
AND pgp.prokind='f'
|
||||||
|
GROUP BY t.typname
|
||||||
|
ORDER BY t.typname ASC;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const postgresFunctionTester = /.*\(\)$/gm;
|
||||||
|
|
||||||
|
export const isPostgresFunction = str =>
|
||||||
|
new RegExp(postgresFunctionTester).test(str);
|
||||||
|
@ -473,7 +473,7 @@ export const metadataReducer = (state = defaultState, action) => {
|
|||||||
...state,
|
...state,
|
||||||
allowedQueries: [
|
allowedQueries: [
|
||||||
...state.allowedQueries.map(q =>
|
...state.allowedQueries.map(q =>
|
||||||
q.name === action.data.queryName ? action.data.newQuery : q
|
(q.name === action.data.queryName ? action.data.newQuery : q)
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
2
console/src/theme/bootstrap.overrides.scss
vendored
2
console/src/theme/bootstrap.overrides.scss
vendored
@ -13,7 +13,7 @@
|
|||||||
max-height: 30px !important;
|
max-height: 30px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.add_table_column_selector__control {
|
.add_table_column_selector__control, .add_table_default_value_selector__control {
|
||||||
min-height: 34px !important;
|
min-height: 34px !important;
|
||||||
max-height: 34px !important;
|
max-height: 34px !important;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user