mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-05-02 07:53:58 +00:00
filter parser
This commit is contained in:
@@ -44,6 +44,17 @@ const binaryCondition = operator => value => ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const likeCondition = (conditionType, likeString) => value => ({
|
||||||
|
conditionType,
|
||||||
|
left: {
|
||||||
|
exprType: 'placeholder',
|
||||||
|
},
|
||||||
|
right: {
|
||||||
|
exprType: 'value',
|
||||||
|
value: likeString.replace('#VALUE#', value),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const compoudCondition = conditionType => conditions => {
|
const compoudCondition = conditionType => conditions => {
|
||||||
if (conditions.length == 1) return conditions[0];
|
if (conditions.length == 1) return conditions[0];
|
||||||
return {
|
return {
|
||||||
@@ -75,29 +86,35 @@ const binaryFixedValueCondition = value => () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const parser = P.createLanguage({
|
const negateCondition = condition => {
|
||||||
|
return {
|
||||||
|
conditionType: 'not',
|
||||||
|
condition,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const createParser = (filterType: FilterType) => {
|
||||||
|
const langDef = {
|
||||||
string1: () =>
|
string1: () =>
|
||||||
token(P.regexp(/"((?:\\.|.)*?)"/, 1))
|
token(P.regexp(/"((?:\\.|.)*?)"/, 1))
|
||||||
.map(interpretEscapes)
|
.map(interpretEscapes)
|
||||||
.map(binaryCondition('='))
|
|
||||||
.desc('string quoted'),
|
.desc('string quoted'),
|
||||||
|
|
||||||
string2: () =>
|
string2: () =>
|
||||||
token(P.regexp(/'((?:\\.|.)*?)'/, 1))
|
token(P.regexp(/'((?:\\.|.)*?)'/, 1))
|
||||||
.map(interpretEscapes)
|
.map(interpretEscapes)
|
||||||
.map(binaryCondition('='))
|
|
||||||
.desc('string quoted'),
|
.desc('string quoted'),
|
||||||
|
|
||||||
number: () =>
|
number: () =>
|
||||||
token(P.regexp(/-?(0|[1-9][0-9]*)([.][0-9]+)?([eE][+-]?[0-9]+)?/))
|
token(P.regexp(/-?(0|[1-9][0-9]*)([.][0-9]+)?([eE][+-]?[0-9]+)?/))
|
||||||
.map(Number)
|
.map(Number)
|
||||||
.map(binaryCondition('='))
|
|
||||||
.desc('number'),
|
.desc('number'),
|
||||||
|
|
||||||
noQuotedString: () =>
|
noQuotedString: () => P.regexp(/[^\s^,^'^"]+/).desc('string unquoted'),
|
||||||
P.regexp(/[^\s^,^'^"]+/)
|
|
||||||
.desc('string unquoted')
|
value: r => P.alt(...allowedValues.map(x => r[x])),
|
||||||
.map(binaryCondition('=')),
|
valueTestEq: r => r.value.map(binaryCondition('=')),
|
||||||
|
valueTestStr: r => r.value.map(likeCondition('like', '%#VALUE#%')),
|
||||||
|
|
||||||
comma: () => word(','),
|
comma: () => word(','),
|
||||||
not: () => word('NOT'),
|
not: () => word('NOT'),
|
||||||
@@ -107,26 +124,97 @@ const parser = P.createLanguage({
|
|||||||
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)),
|
true: () => word('TRUE').map(binaryFixedValueCondition(1)),
|
||||||
false: () => word('FALSE').map(binaryFixedValueCondition(0)),
|
false: () => word('FALSE').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 =>
|
element: r => P.alt(...allowedElements.map(x => r[x])).trim(whitespace),
|
||||||
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')),
|
factor: r => r.element.sepBy(whitespace).map(compoudCondition('and')),
|
||||||
list: r => r.factor.sepBy(r.comma).map(compoudCondition('or')),
|
list: r => r.factor.sepBy(r.comma).map(compoudCondition('or')),
|
||||||
});
|
};
|
||||||
|
|
||||||
|
const allowedValues = []; // 'string1', 'string2', 'number', 'noQuotedString'];
|
||||||
|
if (filterType == 'string') allowedValues.push('string1', 'string2', 'noQuotedString');
|
||||||
|
if (filterType == 'number') allowedValues.push('number');
|
||||||
|
|
||||||
|
const allowedElements = ['null', 'notNull', 'eq', 'ne'];
|
||||||
|
if (filterType == 'number' || filterType == 'datetime') allowedElements.push('lt', 'gt', 'le', 'ge');
|
||||||
|
if (filterType == 'string')
|
||||||
|
allowedElements.push(
|
||||||
|
'empty',
|
||||||
|
'notEmpty',
|
||||||
|
'startsWith',
|
||||||
|
'endsWith',
|
||||||
|
'contains',
|
||||||
|
'startsWithNot',
|
||||||
|
'endsWithNot',
|
||||||
|
'containsNot'
|
||||||
|
);
|
||||||
|
if (filterType == 'logical') allowedElements.push('true', 'false');
|
||||||
|
|
||||||
|
// must be last
|
||||||
|
if (filterType == 'string') allowedElements.push('valueTestStr');
|
||||||
|
else allowedElements.push('valueTestEq');
|
||||||
|
|
||||||
|
return P.createLanguage(langDef);
|
||||||
|
};
|
||||||
|
|
||||||
|
const parsers = {
|
||||||
|
number: createParser('number'),
|
||||||
|
string: createParser('string'),
|
||||||
|
datetime: createParser('datetime'),
|
||||||
|
logical: createParser('logical'),
|
||||||
|
};
|
||||||
|
|
||||||
export function parseFilter(value: string, filterType: FilterType) {
|
export function parseFilter(value: string, filterType: FilterType) {
|
||||||
const ast = parser.list.tryParse(value);
|
const ast = parsers[filterType].list.tryParse(value);
|
||||||
return ast;
|
return ast;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { SqlDumper } from '@dbgate/types';
|
import { SqlDumper } from '@dbgate/types';
|
||||||
import { Condition, BinaryCondition } from './types';
|
import { Condition, BinaryCondition } from './types';
|
||||||
import { dumpSqlExpression } from './dumpSqlExpression';
|
import { dumpSqlExpression } from './dumpSqlExpression';
|
||||||
|
import { link } from 'fs';
|
||||||
|
|
||||||
export function dumpSqlCondition(dmp: SqlDumper, condition: Condition) {
|
export function dumpSqlCondition(dmp: SqlDumper, condition: Condition) {
|
||||||
switch (condition.conditionType) {
|
switch (condition.conditionType) {
|
||||||
@@ -34,5 +35,21 @@ export function dumpSqlCondition(dmp: SqlDumper, condition: Condition) {
|
|||||||
dumpSqlCondition(dmp, cond);
|
dumpSqlCondition(dmp, cond);
|
||||||
dmp.putRaw(')');
|
dmp.putRaw(')');
|
||||||
});
|
});
|
||||||
|
break;
|
||||||
|
case 'like':
|
||||||
|
dumpSqlExpression(dmp, condition.left);
|
||||||
|
dmp.put(' ^like ');
|
||||||
|
dumpSqlExpression(dmp, condition.right);
|
||||||
|
break;
|
||||||
|
case 'notLike':
|
||||||
|
dumpSqlExpression(dmp, condition.left);
|
||||||
|
dmp.put(' ^not ^like ');
|
||||||
|
dumpSqlExpression(dmp, condition.right);
|
||||||
|
break;
|
||||||
|
case 'not':
|
||||||
|
dmp.put('^not (');
|
||||||
|
dumpSqlCondition(dmp, condition.condition);
|
||||||
|
dmp.put(')');
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,8 +28,14 @@ export interface UnaryCondition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface BinaryCondition {
|
export interface BinaryCondition {
|
||||||
operator: '=' | '!=' | '<' | '>' | '>=' | '<=';
|
|
||||||
conditionType: 'binary';
|
conditionType: 'binary';
|
||||||
|
operator: '=' | '!=' | '<' | '>' | '>=' | '<=';
|
||||||
|
left: Expression;
|
||||||
|
right: Expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LikeCondition {
|
||||||
|
conditionType: 'like' | 'notLike';
|
||||||
left: Expression;
|
left: Expression;
|
||||||
right: Expression;
|
right: Expression;
|
||||||
}
|
}
|
||||||
@@ -48,7 +54,7 @@ export interface CompoudCondition {
|
|||||||
conditions: Condition[];
|
conditions: Condition[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Condition = BinaryCondition | NotCondition | TestCondition | CompoudCondition;
|
export type Condition = BinaryCondition | NotCondition | TestCondition | CompoudCondition | LikeCondition;
|
||||||
|
|
||||||
export interface Source {
|
export interface Source {
|
||||||
name?: NamedObjectInfo;
|
name?: NamedObjectInfo;
|
||||||
|
|||||||
Reference in New Issue
Block a user