diff --git a/jOOQ/src/main/java/org/jooq/conf/QuoteEscaping.java b/jOOQ/src/main/java/org/jooq/conf/QuoteEscaping.java new file mode 100644 index 0000000000..d050b63219 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/conf/QuoteEscaping.java @@ -0,0 +1,47 @@ + + + + + + + + +package org.jooq.conf; + +import javax.xml.bind.annotation.XmlEnum; +import javax.xml.bind.annotation.XmlType; + + +/** + *

Java class for QuoteEscaping. + * + *

The following schema fragment specifies the expected content contained within this class. + *

+ *

+ * <simpleType name="QuoteEscaping">
+ *   <restriction base="{http://www.w3.org/2001/XMLSchema}string">
+ *     <enumeration value="DEFAULT"/>
+ *     <enumeration value="QUOTE"/>
+ *     <enumeration value="ESCAPE"/>
+ *   </restriction>
+ * </simpleType>
+ * 
+ * + */ +@XmlType(name = "QuoteEscaping") +@XmlEnum +public enum QuoteEscaping { + + DEFAULT, + QUOTE, + BACKSLASH; + + public String value() { + return name(); + } + + public static QuoteEscaping fromValue(String v) { + return valueOf(v); + } + +} diff --git a/jOOQ/src/main/java/org/jooq/conf/Settings.java b/jOOQ/src/main/java/org/jooq/conf/Settings.java index 3c34e07de0..a895b4a8a1 100644 --- a/jOOQ/src/main/java/org/jooq/conf/Settings.java +++ b/jOOQ/src/main/java/org/jooq/conf/Settings.java @@ -54,6 +54,9 @@ public class Settings @XmlElement(defaultValue = "DEFAULT") @XmlSchemaType(name = "string") protected BackslashEscaping backslashEscaping = BackslashEscaping.DEFAULT; + @XmlElement(defaultValue = "DEFAULT") + @XmlSchemaType(name = "string") + protected QuoteEscaping quoteEscaping = QuoteEscaping.DEFAULT; @XmlElement(defaultValue = "INDEXED") @XmlSchemaType(name = "string") protected ParamType paramType = ParamType.INDEXED; @@ -347,6 +350,30 @@ public class Settings this.backslashEscaping = value; } + /** + * Whether string literals should be escaped with backslash. + * + * @return + * possible object is + * {@link QuoteEscaping } + * + */ + public QuoteEscaping getQuoteEscaping() { + return quoteEscaping; + } + + /** + * Sets the value of the quoteEscaping property. + * + * @param value + * allowed object is + * {@link QuoteEscaping } + * + */ + public void setQuoteEscaping(QuoteEscaping value) { + this.quoteEscaping = value; + } + /** * Specify how bind variables are to be rendered. *

@@ -1064,6 +1091,11 @@ public class Settings return this; } + public Settings withQuoteEscaping(QuoteEscaping value) { + setQuoteEscaping(value); + return this; + } + public Settings withParamType(ParamType value) { setParamType(value); return this; diff --git a/jOOQ/src/main/java/org/jooq/conf/SettingsTools.java b/jOOQ/src/main/java/org/jooq/conf/SettingsTools.java index 9cf3dd75bb..f4b946354b 100644 --- a/jOOQ/src/main/java/org/jooq/conf/SettingsTools.java +++ b/jOOQ/src/main/java/org/jooq/conf/SettingsTools.java @@ -135,6 +135,20 @@ public final class SettingsTools { return BackslashEscaping.DEFAULT; } + /** + * Get the value QuoteEscaping value. + */ + public static final QuoteEscaping getQuoteEscaping(Settings settings) { + if (settings != null) { + QuoteEscaping result = settings.getQuoteEscaping(); + + if (result != null) + return result; + } + + return QuoteEscaping.DEFAULT; + } + /** * Whether a {@link PreparedStatement} should be executed. */ diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java index c346e93aa6..65aa044877 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java @@ -87,6 +87,7 @@ import static org.jooq.impl.Tools.attachRecords; import static org.jooq.impl.Tools.convertBytesToHex; import static org.jooq.impl.Tools.getMappedUDTName; import static org.jooq.impl.Tools.needsBackslashEscaping; +import static org.jooq.impl.Tools.quotesNeedsBackslashEscaping; import static org.jooq.tools.jdbc.JDBCUtils.safeClose; import static org.jooq.tools.jdbc.JDBCUtils.safeFree; import static org.jooq.tools.jdbc.JDBCUtils.wasNull; @@ -715,7 +716,12 @@ public class DefaultBinding implements Binding { if (needsBackslashEscaping(context.configuration())) result = StringUtils.replace(result, "\\", "\\\\"); - return StringUtils.replace(result, "'", "''"); + if (quotesNeedsBackslashEscaping(context.configuration())) + result = StringUtils.replace(result, "'", "\\'"); + else + result = StringUtils.replace(result, "'", "''"); + + return result; } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/Tools.java b/jOOQ/src/main/java/org/jooq/impl/Tools.java index f3b2b3e8df..09265bd55a 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Tools.java +++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java @@ -58,6 +58,7 @@ import static org.jooq.conf.ParamType.INLINED; import static org.jooq.conf.ParamType.NAMED; import static org.jooq.conf.ParamType.NAMED_OR_INLINED; import static org.jooq.conf.SettingsTools.getBackslashEscaping; +import static org.jooq.conf.SettingsTools.getQuoteEscaping; import static org.jooq.conf.SettingsTools.reflectionCaching; import static org.jooq.conf.SettingsTools.updatablePrimaryKeys; import static org.jooq.conf.ThrowExceptions.THROW_FIRST; @@ -226,6 +227,7 @@ import org.jooq.UDT; import org.jooq.UDTRecord; import org.jooq.UpdatableRecord; import org.jooq.conf.BackslashEscaping; +import org.jooq.conf.QuoteEscaping; import org.jooq.conf.Settings; import org.jooq.conf.ThrowExceptions; import org.jooq.exception.DataAccessException; @@ -570,6 +572,7 @@ final class Tools { private static final char[] TOKEN_MULTI_LINE_COMMENT_CLOSE = { '*', '/' }; private static final char[] TOKEN_APOS = { '\'' }; private static final char[] TOKEN_ESCAPED_APOS = { '\'', '\'' }; + private static final char[] TOKEN_BACKSLASH_ESCAPED_APOS = { '\\', '\'' }; /** * "Suffixes" that are placed behind a "?" character to form an operator, @@ -1796,6 +1799,7 @@ final class Tools { // [#3630] Depending on this setting, we need to consider backslashes as escape characters within string literals. boolean needsBackslashEscaping = needsBackslashEscaping(ctx.configuration()); + boolean quotesNeedsBackslashEscaping = quotesNeedsBackslashEscaping(ctx.configuration()); characterLoop: for (int i = 0; i < sqlChars.length; i++) { @@ -1848,7 +1852,10 @@ final class Tools { render.sql(sqlChars[i++]); // Consume an escaped apostrophe - else if (peek(sqlChars, i, TOKEN_ESCAPED_APOS)) + else if (!quotesNeedsBackslashEscaping && peek(sqlChars, i, TOKEN_ESCAPED_APOS)) + render.sql(sqlChars[i++]); + + else if (quotesNeedsBackslashEscaping && peek(sqlChars, i, TOKEN_BACKSLASH_ESCAPED_APOS)) render.sql(sqlChars[i++]); // Break on the terminal string literal delimiter @@ -2071,6 +2078,14 @@ final class Tools { return escaping == ON || (escaping == DEFAULT && REQUIRES_BACKSLASH_ESCAPING.contains(configuration.family())); } + /** + * Whether quotes escaping with backslashes is needed. + */ + static final boolean quotesNeedsBackslashEscaping(Configuration configuration) { + QuoteEscaping escaping = getQuoteEscaping(configuration.settings()); + return escaping == QuoteEscaping.BACKSLASH; + } + /** * Peek for a string at a given index of a char[] * diff --git a/jOOQ/src/main/resources/xsd/jooq-runtime-3.11.0.xsd b/jOOQ/src/main/resources/xsd/jooq-runtime-3.11.0.xsd index 99695624a4..f9b02e3c8b 100644 --- a/jOOQ/src/main/resources/xsd/jooq-runtime-3.11.0.xsd +++ b/jOOQ/src/main/resources/xsd/jooq-runtime-3.11.0.xsd @@ -63,6 +63,10 @@ set to true, users can automatically profit from this feature in all SQL stateme + + + + @@ -423,6 +427,20 @@ Either <input/> or <inputExpression/> must be provided]]> + + + + + + + + + + + + + + @@ -470,4 +488,4 @@ Either <input/> or <inputExpression/> must be provided]]> - \ No newline at end of file +