diff --git a/packages/filterparser/src/parseFilter.ts b/packages/filterparser/src/parseFilter.ts index e99f2989..a5004330 100644 --- a/packages/filterparser/src/parseFilter.ts +++ b/packages/filterparser/src/parseFilter.ts @@ -52,6 +52,29 @@ const compoudCondition = conditionType => conditions => { }; }; +const unaryCondition = conditionType => () => { + return { + conditionType, + expr: { + exprType: 'placeholder', + }, + }; +}; + +const binaryFixedValueCondition = value => () => { + return { + conditionType: 'binary', + operator: '=', + left: { + exprType: 'placeholder', + }, + right: { + exprType: 'value', + value, + }, + }; +}; + const parser = P.createLanguage({ string1: () => token(P.regexp(/"((?:\\.|.)*?)"/, 1)) @@ -72,16 +95,33 @@ const parser = P.createLanguage({ .desc('number'), noQuotedString: () => - P.regexp(/[^\s]+/) + P.regexp(/[^\s^,^'^"]+/) .desc('string unquoted') .map(binaryCondition('=')), comma: () => word(','), not: () => word('NOT'), - notNull: r => r.not.then(r.null).map(() => 'NOT_NULL'), - null: () => word('NULL'), + 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')), + true: () => word('TRUE').map(binaryFixedValueCondition(1)), + false: () => word('FALSE').map(binaryFixedValueCondition(0)), - element: r => P.alt(r.string1, r.string2, r.null, r.notNull, r.number, r.noQuotedString).trim(whitespace), + element: r => + P.alt( + r.string1, + r.string2, + r.null, + r.notNull, + r.number, + r.empty, + r.notEmpty, + r.true, + r.false, + // must be last + r.noQuotedString + ).trim(whitespace), factor: r => r.element.sepBy(whitespace).map(compoudCondition('and')), list: r => r.factor.sepBy(r.comma).map(compoudCondition('or')), }); diff --git a/packages/sqltree/src/dumpSqlCondition.ts b/packages/sqltree/src/dumpSqlCondition.ts index 2178496f..f3b00987 100644 --- a/packages/sqltree/src/dumpSqlCondition.ts +++ b/packages/sqltree/src/dumpSqlCondition.ts @@ -5,11 +5,34 @@ import { dumpSqlExpression } from './dumpSqlExpression'; export function dumpSqlCondition(dmp: SqlDumper, condition: Condition) { switch (condition.conditionType) { case 'binary': - dmp.put('('); dumpSqlExpression(dmp, condition.left); dmp.put(' %s ', condition.operator); dumpSqlExpression(dmp, condition.right); - dmp.put(')'); break; + case 'isNull': + dumpSqlExpression(dmp, condition.expr); + dmp.put(' ^is ^null'); + break; + case 'isNotNull': + dumpSqlExpression(dmp, condition.expr); + dmp.put(' ^is ^not ^null'); + break; + case 'isEmpty': + dmp.put('^trim('); + dumpSqlExpression(dmp, condition.expr); + dmp.put(") = ''"); + break; + case 'isNotEmpty': + dmp.put('^trim('); + dumpSqlExpression(dmp, condition.expr); + dmp.put(") <> ''"); + break; + case 'and': + case 'or': + dmp.putCollection(` ^${condition.conditionType} `, condition.conditions, cond => { + dmp.putRaw('('); + dumpSqlCondition(dmp, cond); + dmp.putRaw(')'); + }); } } diff --git a/packages/sqltree/src/types.ts b/packages/sqltree/src/types.ts index f922cab3..218df173 100644 --- a/packages/sqltree/src/types.ts +++ b/packages/sqltree/src/types.ts @@ -34,11 +34,21 @@ export interface BinaryCondition { right: Expression; } -export interface NotCondition extends UnaryCondition { +export interface NotCondition { conditionType: 'not'; + condition: Condition; } -export type Condition = BinaryCondition | NotCondition; +export interface TestCondition extends UnaryCondition { + conditionType: 'isNull' | 'isNotNull' | 'isEmpty' | 'isNotEmpty'; +} + +export interface CompoudCondition { + conditionType: 'and' | 'or'; + conditions: Condition[]; +} + +export type Condition = BinaryCondition | NotCondition | TestCondition | CompoudCondition; export interface Source { name?: NamedObjectInfo;