diff --git a/packages/filterparser/src/detectSqlFilterType.ts b/packages/filterparser/src/detectSqlFilterType.ts new file mode 100644 index 000000000..f43a822b3 --- /dev/null +++ b/packages/filterparser/src/detectSqlFilterType.ts @@ -0,0 +1,12 @@ +import { isTypeNumber, isTypeString, isTypeLogical, isTypeDateTime } from 'dbgate-tools'; +import { StructuredFilterType } from 'dbgate-types'; +import { DatetimeFilterType, LogicalFilterType, NumberFilterType, StringFilterType } from './filterTypes'; + +export function detectSqlFilterType(dataType: string): StructuredFilterType { + if (!dataType) return StringFilterType; + if (isTypeNumber(dataType)) return NumberFilterType; + if (isTypeString(dataType)) return StringFilterType; + if (isTypeLogical(dataType)) return LogicalFilterType; + if (isTypeDateTime(dataType)) return DatetimeFilterType; + return StringFilterType; +} diff --git a/packages/filterparser/src/filterTypes.ts b/packages/filterparser/src/filterTypes.ts index 8fb1c0eac..822bed5c0 100644 --- a/packages/filterparser/src/filterTypes.ts +++ b/packages/filterparser/src/filterTypes.ts @@ -6,6 +6,8 @@ export const NumberFilterType: StructuredFilterType = { supportNumberLikeComparison: true, supportNullTesting: true, supportSqlCondition: true, + + allowNumberToken: true, }; export const StringFilterType: StructuredFilterType = { @@ -16,6 +18,9 @@ export const StringFilterType: StructuredFilterType = { supportNumberLikeComparison: true, supportNullTesting: true, supportSqlCondition: true, + + allowStringToken: true, + allowHexString: true, }; export const LogicalFilterType: StructuredFilterType = { @@ -50,4 +55,6 @@ export const EvalFilterType: StructuredFilterType = { supportEmpty: true, supportNumberLikeComparison: true, supportNullTesting: true, + + allowStringToken: true, }; diff --git a/packages/filterparser/src/getFilterType.ts b/packages/filterparser/src/getFilterType.ts deleted file mode 100644 index 7a1ce75fb..000000000 --- a/packages/filterparser/src/getFilterType.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { isTypeNumber, isTypeString, isTypeLogical, isTypeDateTime } from 'dbgate-tools'; -import { FilterType } from './types'; - -export function getFilterType(dataType: string): FilterType { - if (!dataType) return 'string'; - if (isTypeNumber(dataType)) return 'number'; - if (isTypeString(dataType)) return 'string'; - if (isTypeLogical(dataType)) return 'logical'; - if (isTypeDateTime(dataType)) return 'datetime'; - return 'string'; -} diff --git a/packages/filterparser/src/index.ts b/packages/filterparser/src/index.ts index 77f9df246..72bb4bc4e 100644 --- a/packages/filterparser/src/index.ts +++ b/packages/filterparser/src/index.ts @@ -1,3 +1,3 @@ export * from './parseFilter'; -export * from './getFilterType'; +export * from './detectSqlFilterType'; export * from './filterTool'; diff --git a/packages/filterparser/src/parseFilter.ts b/packages/filterparser/src/parseFilter.ts index 8a3755b4b..0038f92b8 100644 --- a/packages/filterparser/src/parseFilter.ts +++ b/packages/filterparser/src/parseFilter.ts @@ -1,11 +1,10 @@ import P from 'parsimmon'; -import moment from 'moment'; -import { FilterType } from './types'; import { Condition } from 'dbgate-sqltree'; import { interpretEscapes, token, word, whitespace } from './common'; import { mongoParser } from './mongoParser'; import { datetimeParser } from './datetimeParser'; import { hexStringToArray } from 'dbgate-tools'; +import { StructuredFilterType } from 'dbgate-types'; const binaryCondition = operator => value => ({ conditionType: 'binary', @@ -78,7 +77,7 @@ const sqlTemplate = templateSql => { }; }; -const createParser = (filterType: FilterType) => { +const createParser = (structuredFilterType: StructuredFilterType) => { const langDef = { string1: () => token(P.regexp(/"((?:\\.|.)*?)"/, 1)) @@ -156,32 +155,52 @@ const createParser = (filterType: FilterType) => { }; const allowedValues = []; // 'string1', 'string2', 'number', 'noQuotedString']; - if (filterType == 'string' || filterType == 'eval') { + if (structuredFilterType.allowStringToken) { allowedValues.push('string1', 'string2', 'noQuotedString'); } - if (filterType == 'number') { + if (structuredFilterType.allowNumberToken) { allowedValues.push('string1Num', 'string2Num', 'number'); } - const allowedElements = ['null', 'notNull', 'eq', 'ne', 'ne2', 'sql']; - if (filterType == 'number' || filterType == 'datetime' || filterType == 'eval') { + const allowedElements = []; + + if (structuredFilterType.supportNullTesting) { + allowedElements.push('null', 'notNull'); + } + + if (structuredFilterType.supportEquals) { + allowedElements.push('eq', 'ne', 'ne2'); + } + + if (structuredFilterType.supportSqlCondition) { + allowedElements.push('sql'); + } + + if (structuredFilterType.supportNumberLikeComparison || structuredFilterType.supportDatetimeComparison) { allowedElements.push('le', 'ge', 'lt', 'gt'); } - if (filterType == 'string') { - allowedElements.push('empty', 'notEmpty', 'hexTestEq'); + + if (structuredFilterType.supportEmpty) { + allowedElements.push('empty', 'notEmpty'); } - if (filterType == 'eval' || filterType == 'string') { + + if (structuredFilterType.allowHexString) { + allowedElements.push('hexTestEq'); + } + + if (structuredFilterType.supportStringInclusion) { allowedElements.push('startsWith', 'endsWith', 'contains', 'startsWithNot', 'endsWithNot', 'containsNot'); } - if (filterType == 'logical') { - allowedElements.push('true', 'false', 'trueNum', 'falseNum'); - } - if (filterType == 'eval') { - allowedElements.push('true', 'false'); + if (structuredFilterType.supportBooleanValues) { + if (structuredFilterType.allowNumberToken || structuredFilterType.allowStringToken) { + allowedElements.push('true', 'false'); + } else { + allowedElements.push('true', 'false', 'trueNum', 'falseNum'); + } } // must be last - if (filterType == 'string' || filterType == 'eval') { + if (structuredFilterType.allowStringToken) { allowedElements.push('valueTestStr'); } else { allowedElements.push('valueTestEq'); @@ -190,18 +209,25 @@ const createParser = (filterType: FilterType) => { return P.createLanguage(langDef); }; -const parsers = { - number: createParser('number'), - string: createParser('string'), - logical: createParser('logical'), - eval: createParser('eval'), - mongo: mongoParser, - datetime: datetimeParser, -}; +const cachedFilters: { [key: string]: P.Language } = {}; -export function parseFilter(value: string, filterType: FilterType): Condition { - // console.log('PARSING', value, 'WITH', filterType); - const ast = parsers[filterType].list.tryParse(value); +function getParser(structuredFilterType: StructuredFilterType) { + if (structuredFilterType.compilerType == 'mongoCondition') { + return mongoParser; + } + if (structuredFilterType.compilerType == 'datetime') { + return datetimeParser; + } + const key = JSON.stringify(structuredFilterType); + if (!cachedFilters[key]) { + cachedFilters[key] = createParser(structuredFilterType); + } + return cachedFilters[key]; +} + +export function parseFilter(value: string, structuredFilterType: StructuredFilterType): Condition { + const parser = getParser(structuredFilterType); + const ast = parser.list.tryParse(value); // console.log('AST', ast); return ast; } diff --git a/packages/filterparser/src/parserFilter.test.ts b/packages/filterparser/src/parserFilter.test.ts index 56e7a78b8..7ae819f9e 100644 --- a/packages/filterparser/src/parserFilter.test.ts +++ b/packages/filterparser/src/parserFilter.test.ts @@ -1,7 +1,8 @@ const { parseFilter } = require('./parseFilter'); +const { StringFilterType } = require('./filterTypes'); test('parse string', () => { - const ast = parseFilter('"123"', 'string'); + const ast = parseFilter('"123"', StringFilterType); console.log(JSON.stringify(ast)); expect(ast).toEqual({ conditionType: 'like', diff --git a/packages/filterparser/src/types.ts b/packages/filterparser/src/types.ts deleted file mode 100644 index 367e07d90..000000000 --- a/packages/filterparser/src/types.ts +++ /dev/null @@ -1,3 +0,0 @@ -// import types from 'dbgate-types'; - -export type FilterType = 'number' | 'string' | 'datetime' | 'logical' | 'eval' | 'mongo'; diff --git a/packages/types/engines.d.ts b/packages/types/engines.d.ts index b2f3afea6..74007e13f 100644 --- a/packages/types/engines.d.ts +++ b/packages/types/engines.d.ts @@ -3,6 +3,7 @@ import { QueryResult } from './query'; import { SqlDialect } from './dialect'; import { SqlDumper } from './dumper'; import { DatabaseInfo, NamedObjectInfo, TableInfo, ViewInfo, ProcedureInfo, FunctionInfo, TriggerInfo } from './dbinfo'; +import { StructuredFilterType } from './filter-type'; export interface StreamOptions { recordset: (columns) => void; @@ -153,6 +154,7 @@ export interface EngineDriver { getRedirectAuthUrl(connection, options): Promise<{ url: string; sid: string }>; getAuthTokenFromCode(connection, options): Promise; getAccessTokenFromAuth(connection, req): Promise; + getFilterType(dataType: string): StructuredFilterType; analyserClass?: any; dumperClass?: any; diff --git a/packages/types/filter-type.d.ts b/packages/types/filter-type.d.ts index de2e79978..a312837d7 100644 --- a/packages/types/filter-type.d.ts +++ b/packages/types/filter-type.d.ts @@ -1,4 +1,4 @@ -export type FilterParserCompilerType = 'sqlTree' | 'mongoCondition'; +export type FilterParserCompilerType = 'sqlTree' | 'mongoCondition' | 'datetime'; export interface StructuredFilterType { compilerType: FilterParserCompilerType; @@ -15,5 +15,7 @@ export interface StructuredFilterType { supportSqlCondition?: boolean; supportArrayTesting?: boolean; - // allowedOperators: Array<{ value: string; label: string }>; + allowStringToken?: boolean; + allowNumberToken?: boolean; + allowHexString?: boolean; }