From f785138d3f4e18ff38a1179a16c1f7d933f67054 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 21 Dec 2023 09:17:03 +0000 Subject: [PATCH] feat: add character class support in regex if string literal pattern provided --- packages/nocodb/src/db/functionMappings/pg.ts | 46 ++++++++++++++++++- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/packages/nocodb/src/db/functionMappings/pg.ts b/packages/nocodb/src/db/functionMappings/pg.ts index e52f90e2ca..84e79fb607 100644 --- a/packages/nocodb/src/db/functionMappings/pg.ts +++ b/packages/nocodb/src/db/functionMappings/pg.ts @@ -3,7 +3,25 @@ import commonFns from './commonFns'; import type { MapFnArgs } from '../mapFunctionName'; import { convertUnits } from '~/helpers/convertUnits'; import { getWeekdayByText } from '~/helpers/formulaFnHelper'; - +const replaceCharClassShorthandlers = (str: string) => { + return str.replace(/(.|^)\\([dDsSwW])/g, (fullMatch, prevChar, alias) => { + if (prevChar === '\\') return fullMatch; + switch (alias) { + case 'd': + return `${prevChar}[0-9]`; + case 'D': + return `${prevChar}[^0-9]`; + case 's': + return `${prevChar}[\f\n\r\t\v\u0020\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]`; + case 'S': + return `${prevChar}[^\f\n\r\t\v\u0020\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]`; + case 'w': + return `${prevChar}[A-Za-z0-9_]`; + case 'W': + return `${prevChar}[^A-Za-z0-9_]`; + } + }); +}; const pg = { ...commonFns, LEN: 'length', @@ -227,15 +245,31 @@ const pg = { }, REGEX_MATCH: async ({ fn, knex, pt, colAlias }: MapFnArgs) => { const source = (await fn(pt.arguments[0])).builder; + + // replace shorthand character classes with their equivalent character sets + if (pt.arguments[1].type === 'Literal') { + pt.arguments[1].value = replaceCharClassShorthandlers( + pt.arguments[1].value, + ); + } + const pattern = (await fn(pt.arguments[1])).builder; return { builder: knex.raw( - `CASE WHEN (${source}::TEXT ~ ${pattern}::TEXT) THEN 1 ELSE 0 END ${colAlias}`, + `CASE WHEN REGEXP_MATCH(${source}::TEXT, ${pattern}::TEXT) IS NULL THEN 0 ELSE 1 END ${colAlias}`, ), }; }, REGEX_EXTRACT: async ({ fn, knex, pt, colAlias }: MapFnArgs) => { const source = (await fn(pt.arguments[0])).builder; + + // replace shorthand character classes with their equivalent character sets + if (pt.arguments[1].type === 'Literal') { + pt.arguments[1].value = replaceCharClassShorthandlers( + pt.arguments[1].value, + ); + } + const pattern = (await fn(pt.arguments[1])).builder; return { builder: knex.raw( @@ -248,6 +282,14 @@ const pg = { REGEX_REPLACE: async ({ fn, knex, pt, colAlias }: MapFnArgs) => { const source = (await fn(pt.arguments[0])).builder; const pattern = (await fn(pt.arguments[1])).builder; + + // replace shorthand character classes with their equivalent character sets + if (pt.arguments[1].type === 'Literal') { + pt.arguments[1].value = replaceCharClassShorthandlers( + pt.arguments[1].value, + ); + } + const replacement = (await fn(pt.arguments[2])).builder; return { builder: knex.raw(