diff --git a/packages/filterparser/src/parseFilter.ts b/packages/filterparser/src/parseFilter.ts index 1154311a5..53151c6e2 100644 --- a/packages/filterparser/src/parseFilter.ts +++ b/packages/filterparser/src/parseFilter.ts @@ -1,4 +1,5 @@ import P from 'parsimmon'; +import moment from 'moment'; import { FilterType } from './types'; import { Condition } from 'dbgate-sqltree'; import { TransformType } from 'dbgate-types'; @@ -34,7 +35,7 @@ function interpretEscapes(str) { }); } -const binaryCondition = (operator) => (value) => ({ +const binaryCondition = operator => value => ({ conditionType: 'binary', operator, left: { @@ -46,7 +47,7 @@ const binaryCondition = (operator) => (value) => ({ }, }); -const likeCondition = (conditionType, likeString) => (value) => ({ +const likeCondition = (conditionType, likeString) => value => ({ conditionType, left: { exprType: 'placeholder', @@ -57,7 +58,7 @@ const likeCondition = (conditionType, likeString) => (value) => ({ }, }); -const compoudCondition = (conditionType) => (conditions) => { +const compoudCondition = conditionType => conditions => { if (conditions.length == 1) return conditions[0]; return { conditionType, @@ -65,7 +66,7 @@ const compoudCondition = (conditionType) => (conditions) => { }; }; -const unaryCondition = (conditionType) => () => { +const unaryCondition = conditionType => () => { return { conditionType, expr: { @@ -74,7 +75,7 @@ const unaryCondition = (conditionType) => () => { }; }; -const binaryFixedValueCondition = (value) => () => { +const binaryFixedValueCondition = value => () => { return { conditionType: 'binary', operator: '=', @@ -88,7 +89,7 @@ const binaryFixedValueCondition = (value) => () => { }; }; -const negateCondition = (condition) => { +const negateCondition = condition => { return { conditionType: 'not', condition, @@ -113,11 +114,11 @@ function getTransformCondition(transform: TransformType, value) { }; } -const yearCondition = () => (value) => { +const yearCondition = () => value => { return getTransformCondition('YEAR', value); }; -const yearMonthCondition = () => (value) => { +const yearMonthCondition = () => value => { const m = value.match(/(\d\d\d\d)-(\d\d?)/); return { @@ -126,7 +127,7 @@ const yearMonthCondition = () => (value) => { }; }; -const yearMonthDayCondition = () => (value) => { +const yearMonthDayCondition = () => value => { const m = value.match(/(\d\d\d\d)-(\d\d?)-(\d\d?)/); return { @@ -139,6 +140,43 @@ const yearMonthDayCondition = () => (value) => { }; }; +const fixedIntervalCondition = (start, end) => () => { + return { + conditionType: 'and', + conditions: [ + { + conditionType: 'binary', + operator: '>=', + left: { + exprType: 'placeholder', + }, + right: { + exprType: 'value', + value: start, + }, + }, + { + conditionType: 'binary', + operator: '<', + left: { + exprType: 'placeholder', + }, + right: { + exprType: 'value', + value: end, + }, + }, + ], + }; +}; + +const fixedMomentIntervalCondition = (intervalType, diff) => { + return fixedIntervalCondition( + moment().add(intervalType, diff).startOf(intervalType).toISOString(), + moment().add(intervalType, diff).endOf(intervalType).toISOString() + ); +}; + const createParser = (filterType: FilterType) => { const langDef = { string1: () => @@ -172,36 +210,60 @@ const createParser = (filterType: FilterType) => { yearMonthNum: () => P.regexp(/\d\d\d\d-\d\d?/).map(yearMonthCondition()), yearMonthDayNum: () => P.regexp(/\d\d\d\d-\d\d?-\d\d?/).map(yearMonthDayCondition()), - value: (r) => P.alt(...allowedValues.map((x) => r[x])), - valueTestEq: (r) => r.value.map(binaryCondition('=')), - valueTestStr: (r) => r.value.map(likeCondition('like', '%#VALUE#%')), + value: r => P.alt(...allowedValues.map(x => r[x])), + valueTestEq: r => r.value.map(binaryCondition('=')), + valueTestStr: r => r.value.map(likeCondition('like', '%#VALUE#%')), comma: () => word(','), not: () => word('NOT'), - notNull: (r) => r.not.then(r.null).map(unaryCondition('isNotNull')), + notNull: r => r.not.then(r.null).map(unaryCondition('isNotNull')), null: () => word('NULL').map(unaryCondition('isNull')), empty: () => word('EMPTY').map(unaryCondition('isEmpty')), - notEmpty: (r) => r.not.then(r.empty).map(unaryCondition('isNotEmpty')), + notEmpty: r => r.not.then(r.empty).map(unaryCondition('isNotEmpty')), true: () => word('TRUE').map(binaryFixedValueCondition(1)), false: () => word('FALSE').map(binaryFixedValueCondition(0)), trueNum: () => word('1').map(binaryFixedValueCondition(1)), falseNum: () => word('0').map(binaryFixedValueCondition(0)), - eq: (r) => word('=').then(r.value).map(binaryCondition('=')), - ne: (r) => word('!=').then(r.value).map(binaryCondition('<>')), - lt: (r) => word('<').then(r.value).map(binaryCondition('<')), - gt: (r) => word('>').then(r.value).map(binaryCondition('>')), - le: (r) => word('<=').then(r.value).map(binaryCondition('<=')), - ge: (r) => word('>=').then(r.value).map(binaryCondition('>=')), - startsWith: (r) => word('^').then(r.value).map(likeCondition('like', '#VALUE#%')), - endsWith: (r) => word('$').then(r.value).map(likeCondition('like', '%#VALUE#')), - contains: (r) => word('+').then(r.value).map(likeCondition('like', '%#VALUE#%')), - startsWithNot: (r) => word('!^').then(r.value).map(likeCondition('like', '#VALUE#%')).map(negateCondition), - endsWithNot: (r) => word('!$').then(r.value).map(likeCondition('like', '%#VALUE#')).map(negateCondition), - containsNot: (r) => word('~').then(r.value).map(likeCondition('like', '%#VALUE#%')).map(negateCondition), - element: (r) => P.alt(...allowedElements.map((x) => r[x])).trim(whitespace), - factor: (r) => r.element.sepBy(whitespace).map(compoudCondition('and')), - list: (r) => r.factor.sepBy(r.comma).map(compoudCondition('or')), + this: () => word('THIS'), + last: () => word('LAST'), + next: () => word('NEXT'), + week: () => word('WEEK'), + month: () => word('MONTH'), + year: () => word('YEAR'), + + yesterday: () => word('YESTERDAY').map(fixedMomentIntervalCondition('day', -1)), + today: () => word('TODAY').map(fixedMomentIntervalCondition('day', 0)), + tomorrow: () => word('TOMORROW').map(fixedMomentIntervalCondition('day', 1)), + + lastWeek: r => r.last.then(r.week).map(fixedMomentIntervalCondition('week', -1)), + thisWeek: r => r.this.then(r.week).map(fixedMomentIntervalCondition('week', 0)), + nextWeek: r => r.next.then(r.week).map(fixedMomentIntervalCondition('week', 1)), + + lastMonth: r => r.last.then(r.month).map(fixedMomentIntervalCondition('month', -1)), + thisMonth: r => r.this.then(r.month).map(fixedMomentIntervalCondition('month', 0)), + nextMonth: r => r.next.then(r.month).map(fixedMomentIntervalCondition('month', 1)), + + lastYear: r => r.last.then(r.year).map(fixedMomentIntervalCondition('year', -1)), + thisYear: r => r.this.then(r.year).map(fixedMomentIntervalCondition('year', 0)), + nextYear: r => r.next.then(r.year).map(fixedMomentIntervalCondition('year', 1)), + + eq: r => word('=').then(r.value).map(binaryCondition('=')), + ne: r => word('!=').then(r.value).map(binaryCondition('<>')), + lt: r => word('<').then(r.value).map(binaryCondition('<')), + gt: r => word('>').then(r.value).map(binaryCondition('>')), + le: r => word('<=').then(r.value).map(binaryCondition('<=')), + ge: r => word('>=').then(r.value).map(binaryCondition('>=')), + startsWith: r => word('^').then(r.value).map(likeCondition('like', '#VALUE#%')), + endsWith: r => word('$').then(r.value).map(likeCondition('like', '%#VALUE#')), + contains: r => word('+').then(r.value).map(likeCondition('like', '%#VALUE#%')), + startsWithNot: r => word('!^').then(r.value).map(likeCondition('like', '#VALUE#%')).map(negateCondition), + endsWithNot: r => word('!$').then(r.value).map(likeCondition('like', '%#VALUE#')).map(negateCondition), + containsNot: r => word('~').then(r.value).map(likeCondition('like', '%#VALUE#%')).map(negateCondition), + + element: r => P.alt(...allowedElements.map(x => r[x])).trim(whitespace), + factor: r => r.element.sepBy(whitespace).map(compoudCondition('and')), + list: r => r.factor.sepBy(r.comma).map(compoudCondition('or')), }; const allowedValues = []; // 'string1', 'string2', 'number', 'noQuotedString']; @@ -222,7 +284,24 @@ const createParser = (filterType: FilterType) => { 'containsNot' ); if (filterType == 'logical') allowedElements.push('true', 'false', 'trueNum', 'falseNum'); - if (filterType == 'datetime') allowedElements.push('yearMonthDayNum', 'yearMonthNum', 'yearNum'); + if (filterType == 'datetime') + allowedElements.push( + 'yearMonthDayNum', + 'yearMonthNum', + 'yearNum', + 'yesterday', + 'today', + 'tomorrow', + 'lastWeek', + 'thisWeek', + 'nextWeek', + 'lastMonth', + 'thisMonth', + 'nextMonth', + 'lastYear', + 'thisYear', + 'nextYear' + ); // must be last if (filterType == 'string') allowedElements.push('valueTestStr'); @@ -240,5 +319,6 @@ const parsers = { export function parseFilter(value: string, filterType: FilterType): Condition { const ast = parsers[filterType].list.tryParse(value); + // console.log('AST', ast); return ast; } diff --git a/packages/web/src/datagrid/DataFilterControl.js b/packages/web/src/datagrid/DataFilterControl.js index abcdc4b23..8a09d4191 100644 --- a/packages/web/src/datagrid/DataFilterControl.js +++ b/packages/web/src/datagrid/DataFilterControl.js @@ -42,7 +42,7 @@ const FilterDiv = styled.div` const FilterInput = styled.input` flex: 1; min-width: 10px; - background-color: ${(props) => + background-color: ${props => props.state == 'ok' ? props.theme.input_background_green[1] : props.state == 'error' @@ -58,68 +58,68 @@ function DropDownContent({ filterType, setFilter, filterMultipleValues, openFilt case 'number': return ( <> - setFilter('')}>Clear Filter - filterMultipleValues()}>Filter multiple values - openFilterWindow('=')}>Equals... - openFilterWindow('<>')}>Does Not Equal... - setFilter('NULL')}>Is Null - setFilter('NOT NULL')}>Is Not Null - openFilterWindow('>')}>Greater Than... - openFilterWindow('>=')}>Greater Than Or Equal To... - openFilterWindow('<')}>Less Than... - openFilterWindow('<=')}>Less Than Or Equal To... + setFilter('')}>Clear Filter + filterMultipleValues()}>Filter multiple values + openFilterWindow('=')}>Equals... + openFilterWindow('<>')}>Does Not Equal... + setFilter('NULL')}>Is Null + setFilter('NOT NULL')}>Is Not Null + openFilterWindow('>')}>Greater Than... + openFilterWindow('>=')}>Greater Than Or Equal To... + openFilterWindow('<')}>Less Than... + openFilterWindow('<=')}>Less Than Or Equal To... ); case 'logical': return ( <> - setFilter('')}>Clear Filter - filterMultipleValues()}>Filter multiple values - setFilter('NULL')}>Is Null - setFilter('NOT NULL')}>Is Not Null - setFilter('TRUE')}>Is True - setFilter('FALSE')}>Is False - setFilter('TRUE, NULL')}>Is True or NULL - setFilter('FALSE, NULL')}>Is False or NULL + setFilter('')}>Clear Filter + filterMultipleValues()}>Filter multiple values + setFilter('NULL')}>Is Null + setFilter('NOT NULL')}>Is Not Null + setFilter('TRUE')}>Is True + setFilter('FALSE')}>Is False + setFilter('TRUE, NULL')}>Is True or NULL + setFilter('FALSE, NULL')}>Is False or NULL ); case 'datetime': return ( <> - setFilter('')}>Clear Filter - filterMultipleValues()}>Filter multiple values - setFilter('NULL')}>Is Null - setFilter('NOT NULL')}>Is Not Null + setFilter('')}>Clear Filter + filterMultipleValues()}>Filter multiple values + setFilter('NULL')}>Is Null + setFilter('NOT NULL')}>Is Not Null - openFilterWindow('<=')}>Before... - openFilterWindow('>=')}>After... - openFilterWindow('>=;<=')}>Between... + openFilterWindow('<=')}>Before... + openFilterWindow('>=')}>After... + openFilterWindow('>=;<=')}>Between... - setFilter('TOMORROW')}>Tomorrow - setFilter('TODAY')}>Today - setFilter('YESTERDAY')}>Yesterday + setFilter('TOMORROW')}>Tomorrow + setFilter('TODAY')}>Today + setFilter('YESTERDAY')}>Yesterday - setFilter('NEXT WEEK')}>Next Week - setFilter('THIS WEEK')}>This Week - setFilter('LAST WEEK')}>Last Week + setFilter('NEXT WEEK')}>Next Week + setFilter('THIS WEEK')}>This Week + setFilter('LAST WEEK')}>Last Week - setFilter('NEXT MONTH')}>Next Month - setFilter('THIS MONTH')}>This Month - setFilter('LAST MONTH')}>Last Month + setFilter('NEXT MONTH')}>Next Month + setFilter('THIS MONTH')}>This Month + setFilter('LAST MONTH')}>Last Month - setFilter('NEXT YEAR')}>Next Year - setFilter('THIS YEAR')}>This Year - setFilter('LAST YEAR')}>Last Year + setFilter('NEXT YEAR')}>Next Year + setFilter('THIS YEAR')}>This Year + setFilter('LAST YEAR')}>Last Year @@ -151,24 +151,24 @@ function DropDownContent({ filterType, setFilter, filterMultipleValues, openFilt case 'string': return ( <> - setFilter('')}>Clear Filter - filterMultipleValues()}>Filter multiple values + setFilter('')}>Clear Filter + filterMultipleValues()}>Filter multiple values - openFilterWindow('=')}>Equals... - openFilterWindow('<>')}>Does Not Equal... - setFilter('NULL')}>Is Null - setFilter('NOT NULL')}>Is Not Null - setFilter('EMPTY, NULL')}>Is Empty Or Null - setFilter('NOT EMPTY NOT NULL')}>Has Not Empty Value + openFilterWindow('=')}>Equals... + openFilterWindow('<>')}>Does Not Equal... + setFilter('NULL')}>Is Null + setFilter('NOT NULL')}>Is Not Null + setFilter('EMPTY, NULL')}>Is Empty Or Null + setFilter('NOT EMPTY NOT NULL')}>Has Not Empty Value - openFilterWindow('+')}>Contains... - openFilterWindow('~')}>Does Not Contain... - openFilterWindow('^')}>Begins With... - openFilterWindow('!^')}>Does Not Begin With... - openFilterWindow('$')}>Ends With... - openFilterWindow('!$')}>Does Not End With... + openFilterWindow('+')}>Contains... + openFilterWindow('~')}>Does Not Contain... + openFilterWindow('^')}>Begins With... + openFilterWindow('!^')}>Does Not Begin With... + openFilterWindow('$')}>Ends With... + openFilterWindow('!$')}>Does Not End With... ); } @@ -186,7 +186,7 @@ export default function DataFilterControl({ const showMenu = useShowMenu(); const theme = useTheme(); const [filterState, setFilterState] = React.useState('empty'); - const setFilterText = (filter) => { + const setFilterText = filter => { setFilter(filter); editorRef.current.value = filter || ''; updateFilterState(); @@ -196,19 +196,19 @@ export default function DataFilterControl({ setFilter(editorRef.current.value); }; const filterMultipleValues = () => { - showModal((modalState) => ( + showModal(modalState => ( setFilterText(createMultiLineFilter(mode, text))} /> )); }; - const openFilterWindow = (operator) => { - showModal((modalState) => ( + const openFilterWindow = operator => { + showModal(modalState => ( setFilterText(text)} + onFilter={text => setFilterText(text)} condition1={operator} /> )); @@ -220,7 +220,7 @@ export default function DataFilterControl({ if (focusIndex) editorRef.current.focus(); }, [focusIndex]); - const handleKeyDown = (ev) => { + const handleKeyDown = ev => { if (isReadOnly) return; if (ev.keyCode == keycodes.enter) { applyFilter(); @@ -248,6 +248,7 @@ export default function DataFilterControl({ setFilterState('empty'); } } catch (err) { + // console.log('PARSE ERROR', err); setFilterState('error'); } }; diff --git a/packages/web/src/datagrid/SqlDataGridCore.js b/packages/web/src/datagrid/SqlDataGridCore.js index ed8a0ec82..62c02ec85 100644 --- a/packages/web/src/datagrid/SqlDataGridCore.js +++ b/packages/web/src/datagrid/SqlDataGridCore.js @@ -78,7 +78,7 @@ export default function SqlDataGridCore(props) { initialValues.sourceDatabaseName = database; initialValues.sourceSql = display.getExportQuery(); initialValues.sourceList = display.baseTable ? [display.baseTable.pureName] : []; - showModal((modalState) => ); + showModal(modalState => ); } function openActiveChart() { openNewTab( @@ -94,7 +94,7 @@ export default function SqlDataGridCore(props) { { editor: { config: { chartType: 'bar' }, - sql: display.getExportQuery((select) => { + sql: display.getExportQuery(select => { select.orderBy = null; }), }, @@ -102,18 +102,22 @@ export default function SqlDataGridCore(props) { ); } function openQuery() { - openNewTab({ - title: 'Query', - icon: 'img sql-file', - tabComponent: 'QueryTab', - props: { - initialScript: display.getExportQuery(), - schemaName: display.baseTable.schemaName, - pureName: display.baseTable.pureName, - conid, - database, + openNewTab( + { + title: 'Query', + icon: 'img sql-file', + tabComponent: 'QueryTab', + props: { + schemaName: display.baseTable.schemaName, + pureName: display.baseTable.pureName, + conid, + database, + }, }, - }); + { + editor: display.getExportQuery(), + } + ); } function handleSave() { @@ -135,7 +139,7 @@ export default function SqlDataGridCore(props) { }); const { errorMessage } = resp.data || {}; if (errorMessage) { - showModal((modalState) => ( + showModal(modalState => ( )); } else {