[#5873] Add Setting.quoteEscaping to allow for alternative escaping of single quotes

This commit is contained in:
zhurs 2018-03-15 17:07:10 +03:00
parent 8b501a9236
commit eb3ee64587
6 changed files with 135 additions and 3 deletions

View File

@ -0,0 +1,47 @@
package org.jooq.conf;
import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for QuoteEscaping.
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
* <pre>
* &lt;simpleType name="QuoteEscaping">
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}string">
* &lt;enumeration value="DEFAULT"/>
* &lt;enumeration value="QUOTE"/>
* &lt;enumeration value="ESCAPE"/>
* &lt;/restriction>
* &lt;/simpleType>
* </pre>
*
*/
@XmlType(name = "QuoteEscaping")
@XmlEnum
public enum QuoteEscaping {
DEFAULT,
QUOTE,
BACKSLASH;
public String value() {
return name();
}
public static QuoteEscaping fromValue(String v) {
return valueOf(v);
}
}

View File

@ -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.
* <p>
@ -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;

View File

@ -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.
*/

View File

@ -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;
@ -711,7 +712,12 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
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

View File

@ -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 <code>index</code> of a <code>char[]</code>
*

View File

@ -63,6 +63,10 @@ set to true, users can automatically profit from this feature in all SQL stateme
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Whether string literals should be escaped with backslash.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="quoteEscaping" type="jooq-runtime:QuoteEscaping" minOccurs="0" maxOccurs="1" default="DEFAULT">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Whether quotes in strings literals should be escaped with quote repeating or backslash.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="paramType" type="jooq-runtime:ParamType" minOccurs="0" maxOccurs="1" default="INDEXED">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Specify how bind variables are to be rendered.
<p>
@ -423,6 +427,20 @@ Either &lt;input/> or &lt;inputExpression/> must be provided]]></jxb:javadoc></j
</restriction>
</simpleType>
<simpleType name="QuoteEscaping">
<restriction base="string">
<!-- Use the database's most sensible default value -->
<enumeration value="DEFAULT"/>
<!-- Escape with quote repeat. -->
<enumeration value="QUOTE"/>
<!-- Escape with backslash. -->
<enumeration value="BACKSLASH"/>
</restriction>
</simpleType>
<simpleType name="ThrowExceptions">
<restriction base="string">
@ -470,4 +488,4 @@ Either &lt;input/> or &lt;inputExpression/> must be provided]]></jxb:javadoc></j
<enumeration value="THROW_ON_FAILURE"/>
</restriction>
</simpleType>
</schema>
</schema>