[jOOQ/jOOQ#1571] Add support for REGEXP_REPLACE()

This commit is contained in:
Lukas Eder 2020-05-01 16:37:47 +02:00
parent 7ce9a64fc3
commit 233d52f888
5 changed files with 227 additions and 2 deletions

View File

@ -14001,6 +14001,38 @@ public class DSL {
return new Replace(nullSafe(field), nullSafe(search), nullSafe(replace));
}
/**
* Get the <code>REGEXP_REPLACE_ALL</code> function.
*/
@Support({ H2, HSQLDB, MARIADB, MYSQL, POSTGRES })
public static Field<String> regexpReplaceAll(Field<String> field, String pattern, String replacement) {
return regexpReplaceAll(field, Tools.field(pattern), Tools.field(replacement));
}
/**
* Get the <code>REGEXP_REPLACE_ALL</code> function.
*/
@Support({ H2, HSQLDB, MARIADB, MYSQL, POSTGRES })
public static Field<String> regexpReplaceAll(Field<String> field, Field<String> pattern, Field<String> replacement) {
return new RegexpReplace(field, nullSafe(pattern), nullSafe(replacement), true);
}
/**
* Get the <code>REGEXP_REPLACE_ALL</code> function.
*/
@Support({ MYSQL, POSTGRES })
public static Field<String> regexpReplaceFirst(Field<String> field, String pattern, String replacement) {
return regexpReplaceFirst(field, Tools.field(pattern), Tools.field(replacement));
}
/**
* Get the <code>REGEXP_REPLACE_ALL</code> function.
*/
@Support({ MYSQL, POSTGRES })
public static Field<String> regexpReplaceFirst(Field<String> field, Field<String> pattern, Field<String> replacement) {
return new RegexpReplace(field, nullSafe(pattern), nullSafe(replacement), false);
}
/**
* Get the position(in, search) function.
*

View File

@ -170,6 +170,8 @@ final class Names {
static final Name N_RANDOM = unquotedName("random");
static final Name N_RATIO_TO_REPORT = unquotedName("ratio_to_report");
static final Name N_RAWTOHEX = unquotedName("rawtohex");
static final Name N_REGEX_REPLACE = unquotedName("regex_replace");
static final Name N_REGEXP_REPLACE = unquotedName("regexp_replace");
static final Name N_REPEAT = unquotedName("repeat");
static final Name N_REPLACE = unquotedName("replace");
static final Name N_REPLICATE = unquotedName("replicate");

View File

@ -224,6 +224,8 @@ import static org.jooq.impl.DSL.rangeUnboundedFollowing;
import static org.jooq.impl.DSL.rangeUnboundedPreceding;
import static org.jooq.impl.DSL.rank;
import static org.jooq.impl.DSL.ratioToReport;
import static org.jooq.impl.DSL.regexpReplaceAll;
import static org.jooq.impl.DSL.regexpReplaceFirst;
import static org.jooq.impl.DSL.regrAvgX;
import static org.jooq.impl.DSL.regrAvgY;
import static org.jooq.impl.DSL.regrCount;
@ -6478,6 +6480,8 @@ final class ParserImpl implements Parser {
if (S.is(type))
if ((field = parseFieldReplaceIf(ctx)) != null)
return field;
else if ((field = parseFieldRegexpReplaceIf(ctx)) != null)
return field;
else if ((field = parseFieldRepeatIf(ctx)) != null)
return field;
else if ((field = parseFieldReverseIf(ctx)) != null)
@ -8307,6 +8311,71 @@ final class ParserImpl implements Parser {
return null;
}
private static final Field<?> parseFieldRegexpReplaceIf(ParserContext ctx) {
boolean all = parseFunctionNameIf(ctx, "REGEXP_REPLACE_ALL");
boolean first = !all && parseFunctionNameIf(ctx, "REGEXP_REPLACE_FIRST");
boolean ifx = !all && !first && parseFunctionNameIf(ctx, "REGEX_REPLACE");
if (all || first || ifx || parseFunctionNameIf(ctx, "REGEXP_REPLACE")) {
parse(ctx, '(');
Field field = parseField(ctx, S);
parse(ctx, ',');
Field pattern = parseField(ctx, S);
Field replacement = parseIf(ctx, ',') ? parseField(ctx, S) : null;
Long i1;
Long i2;
if (replacement == null) {
replacement = inline("");
}
else if (ifx) {
if (parseIf(ctx, ','))
if (1L == parseUnsignedInteger(ctx))
first = true;
else
throw ctx.expected("Only a limit of 1 is currently supported");
}
else if (!all && !first) {
if (parseIf(ctx, ',')) {
String s = parseStringLiteralIf(ctx);
if (s != null) {
if (s.contains("g"))
all = true;
}
else {
i1 = parseUnsignedInteger(ctx);
parse(ctx, ',');
i2 = parseUnsignedInteger(ctx);
if (Long.valueOf(1L).equals(i1) && Long.valueOf(1L).equals(i2))
all = true;
else
throw ctx.expected("Only start and occurence values of 1 are currently supported");
}
}
if (!all) switch (ctx.family()) {
case POSTGRES:
first = true;
break;
}
}
parse(ctx, ')');
return first
? regexpReplaceFirst(field, pattern, replacement)
: regexpReplaceAll(field, pattern, replacement);
}
return null;
}
private static final Field<?> parseFieldReverseIf(ParserContext ctx) {
if (parseFunctionNameIf(ctx, "REVERSE")) {
parse(ctx, '(');

View File

@ -0,0 +1,122 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Other licenses:
* -----------------------------------------------------------------------------
* Commercial licenses for this work are available. These replace the above
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
* database integrations.
*
* For more information, please visit: http://www.jooq.org/licenses
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq.impl;
import static org.jooq.impl.Names.N_REGEXP_REPLACE;
import static org.jooq.impl.Names.N_REGEX_REPLACE;
import org.jooq.Context;
import org.jooq.Field;
/**
* @author Lukas Eder
*/
final class RegexpReplace extends AbstractField<String> {
/**
* Generated UID
*/
private static final long serialVersionUID = -1659917024582732223L;
private final Field<String> field;
private final Field<String> pattern;
private final Field<String> replacement;
private final boolean all;
RegexpReplace(Field<String> field, Field<String> pattern, Field<String> replacement, boolean all) {
super(N_REGEXP_REPLACE, field.getDataType());
this.field = field;
this.pattern = pattern;
this.replacement = replacement;
this.all = all;
}
@Override
public final void accept(Context<?> ctx) {
switch (ctx.family()) {
case POSTGRES:
ctx.visit(N_REGEXP_REPLACE).sql('(').visit(field).sql(", ").visit(pattern).sql(", ").visit(replacement);
if (all)
ctx.sql(", 'g')");
else
ctx.sql(')');
break;
case MYSQL:
// [#10151] TODO: Emulate REGEXP_REPLACE_FIRST for these three
case H2:
case HSQLDB:
case MARIADB:
default:
ctx.visit(N_REGEXP_REPLACE).sql('(').visit(field).sql(", ").visit(pattern).sql(", ").visit(replacement);
if (all)
ctx.sql(')');
else
ctx.sql(", 1, 1)");
break;
}
}
}

View File

@ -38,7 +38,7 @@
<!-- DefaultRecordMapper and jOOQ-meta-extensions can read JPA annotations -->
<javax.persistence-api.version>2.2</javax.persistence-api.version>
<!-- Examples and/or extension modules use these dependencies -->
<hibernate.version>5.4.10.Final</hibernate.version>
<liquibase.version>3.8.8</liquibase.version>
@ -456,7 +456,7 @@
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
</plugin>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>