[jOOQ/jOOQ#9633] Add a new Settings.interpreterNameLookupCaseSensitivity

- Default is now ParseNameCase.UPPER_IF_UNQUOTED ([jOOQ/jOOQ#9238])
- Made ParseNameCase logic reusable for ParserImpl and DDLInterpreter
- DDLInterpreter MutableNamed model now uses inner classes to share
  Configuration instance access
- Case insensitive comparisons now use Settings.renderLocale()
  TODO: [jOOQ/jOOQ#9636]
This commit is contained in:
Lukas Eder 2019-12-06 11:15:19 +01:00
parent b5b5bc4f6b
commit d5ecab1e71
5 changed files with 193 additions and 128 deletions

View File

@ -192,7 +192,7 @@ public class Settings
protected SQLDialect interpreterDialect;
@XmlElement(defaultValue = "DEFAULT")
@XmlSchemaType(name = "string")
protected InterpreterNameLookupCaseSensitivity interpreterNameCaseSensitivity = InterpreterNameLookupCaseSensitivity.DEFAULT;
protected InterpreterNameLookupCaseSensitivity interpreterNameLookupCaseSensitivity = InterpreterNameLookupCaseSensitivity.DEFAULT;
@XmlElement(type = String.class, defaultValue = "DEFAULT")
@XmlJavaTypeAdapter(SQLDialectAdapter.class)
protected SQLDialect parseDialect;
@ -1617,16 +1617,16 @@ public class Settings
* [#9633] The case sensitivity of identifiers used when interpreting SQL DDL statements.
*
*/
public InterpreterNameLookupCaseSensitivity getInterpreterNameCaseSensitivity() {
return interpreterNameCaseSensitivity;
public InterpreterNameLookupCaseSensitivity getInterpreterNameLookupCaseSensitivity() {
return interpreterNameLookupCaseSensitivity;
}
/**
* [#9633] The case sensitivity of identifiers used when interpreting SQL DDL statements.
*
*/
public void setInterpreterNameCaseSensitivity(InterpreterNameLookupCaseSensitivity value) {
this.interpreterNameCaseSensitivity = value;
public void setInterpreterNameLookupCaseSensitivity(InterpreterNameLookupCaseSensitivity value) {
this.interpreterNameLookupCaseSensitivity = value;
}
/**
@ -2296,8 +2296,8 @@ public class Settings
* [#9633] The case sensitivity of identifiers used when interpreting SQL DDL statements.
*
*/
public Settings withInterpreterNameCaseSensitivity(InterpreterNameLookupCaseSensitivity value) {
setInterpreterNameCaseSensitivity(value);
public Settings withInterpreterNameLookupCaseSensitivity(InterpreterNameLookupCaseSensitivity value) {
setInterpreterNameLookupCaseSensitivity(value);
return this;
}
@ -2456,7 +2456,7 @@ public class Settings
builder.append("executeUpdateWithoutWhere", executeUpdateWithoutWhere);
builder.append("executeDeleteWithoutWhere", executeDeleteWithoutWhere);
builder.append("interpreterDialect", interpreterDialect);
builder.append("interpreterNameCaseSensitivity", interpreterNameCaseSensitivity);
builder.append("interpreterNameLookupCaseSensitivity", interpreterNameLookupCaseSensitivity);
builder.append("parseDialect", parseDialect);
builder.append("parseNameCase", parseNameCase);
builder.append("parseWithMetaLookups", parseWithMetaLookups);
@ -3063,12 +3063,12 @@ public class Settings
return false;
}
}
if (interpreterNameCaseSensitivity == null) {
if (other.interpreterNameCaseSensitivity!= null) {
if (interpreterNameLookupCaseSensitivity == null) {
if (other.interpreterNameLookupCaseSensitivity!= null) {
return false;
}
} else {
if (!interpreterNameCaseSensitivity.equals(other.interpreterNameCaseSensitivity)) {
if (!interpreterNameLookupCaseSensitivity.equals(other.interpreterNameLookupCaseSensitivity)) {
return false;
}
}
@ -3224,7 +3224,7 @@ public class Settings
result = ((prime*result)+((executeUpdateWithoutWhere == null)? 0 :executeUpdateWithoutWhere.hashCode()));
result = ((prime*result)+((executeDeleteWithoutWhere == null)? 0 :executeDeleteWithoutWhere.hashCode()));
result = ((prime*result)+((interpreterDialect == null)? 0 :interpreterDialect.hashCode()));
result = ((prime*result)+((interpreterNameCaseSensitivity == null)? 0 :interpreterNameCaseSensitivity.hashCode()));
result = ((prime*result)+((interpreterNameLookupCaseSensitivity == null)? 0 :interpreterNameLookupCaseSensitivity.hashCode()));
result = ((prime*result)+((parseDialect == null)? 0 :parseDialect.hashCode()));
result = ((prime*result)+((parseNameCase == null)? 0 :parseNameCase.hashCode()));
result = ((prime*result)+((parseWithMetaLookups == null)? 0 :parseWithMetaLookups.hashCode()));

View File

@ -38,6 +38,7 @@
package org.jooq.impl;
import static org.jooq.Name.Quoted.QUOTED;
import static org.jooq.conf.SettingsTools.renderLocale;
import static org.jooq.impl.AbstractName.NO_NAME;
import static org.jooq.impl.Cascade.CASCADE;
import static org.jooq.impl.Cascade.RESTRICT;
@ -46,7 +47,9 @@ import static org.jooq.impl.ConstraintType.PRIMARY_KEY;
import static org.jooq.impl.SQLDataType.BIGINT;
import static org.jooq.impl.Tools.EMPTY_FIELD;
import static org.jooq.impl.Tools.intersect;
import static org.jooq.impl.Tools.normaliseNameCase;
import static org.jooq.impl.Tools.reverseIterable;
import static org.jooq.tools.StringUtils.defaultIfNull;
import java.util.AbstractList;
import java.util.ArrayList;
@ -96,16 +99,18 @@ import org.jooq.tools.JooqLogger;
@SuppressWarnings("serial")
final class DDLInterpreter {
private static final JooqLogger log = JooqLogger.getLogger(DDLInterpreter.class);
private static final JooqLogger log = JooqLogger.getLogger(DDLInterpreter.class);
private final Configuration configuration;
private final Map<Name, MutableCatalog> catalogs = new LinkedHashMap<>();
private final MutableCatalog defaultCatalog;
private final MutableSchema defaultSchema;
private MutableSchema currentSchema;
private final Configuration configuration;
private final InterpreterNameLookupCaseSensitivity caseSensitivity;
private final Map<Name, MutableCatalog> catalogs = new LinkedHashMap<>();
private final MutableCatalog defaultCatalog;
private final MutableSchema defaultSchema;
private MutableSchema currentSchema;
DDLInterpreter(Configuration configuration) {
this.configuration = configuration;
this.caseSensitivity = caseSensitivity(configuration);
this.defaultCatalog = new MutableCatalog(NO_NAME);
this.catalogs.put(defaultCatalog.name(), defaultCatalog);
this.defaultSchema = new MutableSchema(NO_NAME, defaultCatalog);
@ -1029,7 +1034,7 @@ final class DDLInterpreter {
return schema;
}
private static final MutableTable newTable(
private final MutableTable newTable(
Table<?> table,
MutableSchema schema,
List<Field<?>> columns,
@ -1172,18 +1177,62 @@ final class DDLInterpreter {
return result;
}
private static final InterpreterNameLookupCaseSensitivity caseSensitivity(Configuration configuration) {
InterpreterNameLookupCaseSensitivity result = defaultIfNull(configuration.settings().getInterpreterNameLookupCaseSensitivity(), InterpreterNameLookupCaseSensitivity.DEFAULT);
if (result == InterpreterNameLookupCaseSensitivity.DEFAULT) {
switch (defaultIfNull(configuration.settings().getInterpreterDialect(), configuration.family()).family()) {
case DERBY:
case FIREBIRD:
case H2:
case HSQLDB:
case POSTGRES:
return InterpreterNameLookupCaseSensitivity.WHEN_QUOTED;
case MYSQL:
case SQLITE:
return InterpreterNameLookupCaseSensitivity.NEVER;
case DEFAULT:
default:
return InterpreterNameLookupCaseSensitivity.WHEN_QUOTED;
}
}
return result;
}
// -------------------------------------------------------------------------
// Data model
// -------------------------------------------------------------------------
private static abstract class MutableNamed {
private abstract class MutableNamed {
private UnqualifiedName name;
private String upper;
private Comment comment;
// TODO: Access this from Settings
private InterpreterNameLookupCaseSensitivity caseSensitive = InterpreterNameLookupCaseSensitivity.WHEN_QUOTED;
MutableNamed(UnqualifiedName name) {
this(name, null);
}
@ -1202,7 +1251,7 @@ final class DDLInterpreter {
this.name = n;
// TODO: Use Settings.renderLocale()
this.upper = name.last().toUpperCase();
this.upper = name.last().toUpperCase(renderLocale(configuration.settings()));
}
Comment comment() {
@ -1214,15 +1263,21 @@ final class DDLInterpreter {
}
boolean nameEquals(UnqualifiedName other) {
switch (caseSensitivity) {
case ALWAYS:
return name.last().equals(other.last());
// TODO: Implement InterpreterNameLookupCaseSensitivity.DEFAULT
if (caseSensitive == InterpreterNameLookupCaseSensitivity.ALWAYS ||
(caseSensitive == InterpreterNameLookupCaseSensitivity.WHEN_QUOTED && (name.quoted() == QUOTED || other.quoted() == QUOTED)))
return name.last().equals(other.last());
else
case WHEN_QUOTED:
return normaliseNameCase(configuration, name.last(), name.quoted() == QUOTED).equals(
normaliseNameCase(configuration, other.last(), other.quoted() == QUOTED));
// TODO: Use Settings.renderLocale()
return upper.equalsIgnoreCase(other.last().toUpperCase());
case NEVER:
return upper.equalsIgnoreCase(other.last().toUpperCase(renderLocale(configuration.settings())));
case DEFAULT:
default:
throw new IllegalStateException();
}
}
abstract void drop();
@ -1233,7 +1288,7 @@ final class DDLInterpreter {
}
}
private static final class MutableCatalog extends MutableNamed {
private final class MutableCatalog extends MutableNamed {
List<MutableSchema> schemas = new MutableNamedList<>();
MutableCatalog(UnqualifiedName name) {
@ -1262,7 +1317,7 @@ final class DDLInterpreter {
}
}
private static final class MutableSchema extends MutableNamed {
private final class MutableSchema extends MutableNamed {
MutableCatalog catalog;
List<MutableTable> tables = new MutableNamedList<>();
List<MutableSequence> sequences = new MutableNamedList<>();
@ -1319,7 +1374,7 @@ final class DDLInterpreter {
}
}
private static final class MutableTable extends MutableNamed {
private final class MutableTable extends MutableNamed {
MutableSchema schema;
List<MutableField> fields = new MutableNamedList<>();
MutableUniqueKey primaryKey;
@ -1524,7 +1579,7 @@ final class DDLInterpreter {
}
@SuppressWarnings("unused")
private static final class MutableSequence extends MutableNamed {
private final class MutableSequence extends MutableNamed {
MutableSchema schema;
Field<? extends Number> startWith;
Field<? extends Number> incrementBy;
@ -1557,7 +1612,7 @@ final class DDLInterpreter {
}
}
private static abstract class MutableKey extends MutableNamed {
private abstract class MutableKey extends MutableNamed {
MutableTable keyTable;
List<MutableField> keyFields;
@ -1569,7 +1624,7 @@ final class DDLInterpreter {
}
}
private static final class MutableCheck extends MutableNamed {
private final class MutableCheck extends MutableNamed {
MutableTable table;
Condition condition;
@ -1584,7 +1639,7 @@ final class DDLInterpreter {
final void drop() {}
}
private static final class MutableUniqueKey extends MutableKey {
private final class MutableUniqueKey extends MutableKey {
List<MutableForeignKey> referencingKeys = new MutableNamedList<>();
MutableUniqueKey(UnqualifiedName name, MutableTable keyTable, List<MutableField> keyFields) {
@ -1598,7 +1653,7 @@ final class DDLInterpreter {
}
}
private static final class MutableForeignKey extends MutableKey {
private final class MutableForeignKey extends MutableKey {
MutableUniqueKey referencedKey;
Action onDelete;
Action onUpdate;
@ -1625,7 +1680,7 @@ final class DDLInterpreter {
}
}
private static final class MutableIndex extends MutableNamed {
private final class MutableIndex extends MutableNamed {
MutableTable table;
List<MutableSortField> fields;
boolean unique;
@ -1642,7 +1697,7 @@ final class DDLInterpreter {
final void drop() {}
}
private static final class MutableField extends MutableNamed {
private final class MutableField extends MutableNamed {
MutableTable table;
DataType<?> type;
@ -1657,7 +1712,7 @@ final class DDLInterpreter {
final void drop() {}
}
private static final class MutableSortField extends MutableNamed {
private final class MutableSortField extends MutableNamed {
MutableField field;
SortOrder sort;
@ -1672,7 +1727,7 @@ final class DDLInterpreter {
final void drop() {}
}
private static final class MutableNamedList<N extends MutableNamed> extends AbstractList<N> {
private final class MutableNamedList<N extends MutableNamed> extends AbstractList<N> {
private final List<N> delegate = new ArrayList<>();
@Override

View File

@ -50,7 +50,6 @@ import static org.jooq.JoinType.JOIN;
// ...
import static org.jooq.conf.ParseWithMetaLookups.IGNORE_ON_FAILURE;
import static org.jooq.conf.ParseWithMetaLookups.THROW_ON_FAILURE;
import static org.jooq.conf.SettingsTools.renderLocale;
import static org.jooq.impl.AbstractName.NO_NAME;
import static org.jooq.impl.DSL.abs;
import static org.jooq.impl.DSL.acos;
@ -311,7 +310,7 @@ import static org.jooq.impl.Tools.EMPTY_NAME;
import static org.jooq.impl.Tools.EMPTY_QUERYPART;
import static org.jooq.impl.Tools.EMPTY_ROWN;
import static org.jooq.impl.Tools.EMPTY_SORTFIELD;
import static org.jooq.tools.StringUtils.defaultIfNull;
import static org.jooq.impl.Tools.normaliseNameCase;
import java.io.ByteArrayOutputStream;
import java.math.BigDecimal;
@ -486,7 +485,6 @@ import org.jooq.WindowSpecificationExcludeStep;
import org.jooq.WindowSpecificationOrderByStep;
import org.jooq.WindowSpecificationRowsAndStep;
import org.jooq.WindowSpecificationRowsStep;
import org.jooq.conf.ParseNameCase;
import org.jooq.conf.ParseSearchSchema;
import org.jooq.conf.ParseUnknownFunctions;
import org.jooq.conf.ParseUnsupportedSyntax;
@ -9164,9 +9162,10 @@ final class ParserImpl implements Parser {
private static final Name parseIdentifierIf(ParserContext ctx, boolean allowAposQuotes) {
char quoteEnd = parseQuote(ctx, allowAposQuotes);
boolean quoted = quoteEnd != 0;
int start = ctx.position();
if (quoteEnd != 0)
if (quoted)
while (ctx.character() != quoteEnd && ctx.hasMore())
ctx.positionInc();
else
@ -9176,98 +9175,22 @@ final class ParserImpl implements Parser {
if (ctx.position() == start)
return null;
String result = ctx.substring(start, ctx.position());
String name = normaliseNameCase(ctx.configuration(), ctx.substring(start, ctx.position()), quoted);
switch (parseNameCase(ctx)) {
case LOWER_IF_UNQUOTED:
if (quoteEnd != 0)
break;
// no-break
case LOWER:
result = result.toLowerCase(renderLocale(ctx.settings()));
break;
case UPPER_IF_UNQUOTED:
if (quoteEnd != 0)
break;
// no-break
case UPPER:
result = result.toUpperCase(renderLocale(ctx.settings()));
break;
case AS_IS:
case DEFAULT:
default:
// Keep result
break;
}
if (quoteEnd != 0) {
if (quoted) {
if (ctx.character() != quoteEnd)
throw ctx.exception("Quoted identifier must terminate in " + quoteEnd);
ctx.positionInc();
parseWhitespaceIf(ctx);
return DSL.quotedName(result);
return DSL.quotedName(name);
}
else {
parseWhitespaceIf(ctx);
return DSL.unquotedName(result);
return DSL.unquotedName(name);
}
}
private static final ParseNameCase parseNameCase(ParserContext ctx) {
ParseNameCase result = defaultIfNull(ctx.settings().getParseNameCase(), ParseNameCase.DEFAULT);
if (result == ParseNameCase.DEFAULT) {
switch (defaultIfNull(ctx.settings().getParseDialect(), ctx.family()).family()) {
case POSTGRES:
return ParseNameCase.LOWER_IF_UNQUOTED;
case DERBY:
case FIREBIRD:
case H2:
case HSQLDB:
return ParseNameCase.UPPER_IF_UNQUOTED;
case MYSQL:
case SQLITE:
return ParseNameCase.AS_IS;
case DEFAULT:
default:
// Keep default if we don't know the case
}
}
return result;
}
private static final char parseQuote(ParserContext ctx, boolean allowAposQuotes) {
return parseIf(ctx, '"', false) ? '"'
: parseIf(ctx, '`', false) ? '`'

View File

@ -67,6 +67,7 @@ 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.reflectionCaching;
import static org.jooq.conf.SettingsTools.renderLocale;
import static org.jooq.conf.SettingsTools.updatablePrimaryKeys;
import static org.jooq.conf.ThrowExceptions.THROW_FIRST;
import static org.jooq.conf.ThrowExceptions.THROW_NONE;
@ -151,6 +152,7 @@ import static org.jooq.impl.Tools.DataCacheKey.DATA_REFLECTION_CACHE_GET_MATCHIN
import static org.jooq.impl.Tools.DataCacheKey.DATA_REFLECTION_CACHE_GET_MATCHING_SETTERS;
import static org.jooq.impl.Tools.DataCacheKey.DATA_REFLECTION_CACHE_HAS_COLUMN_ANNOTATIONS;
import static org.jooq.impl.Tools.DataKey.DATA_BLOCK_NESTING;
import static org.jooq.tools.StringUtils.defaultIfNull;
import static org.jooq.tools.reflect.Reflect.accessible;
import java.io.Serializable;
@ -252,6 +254,7 @@ import org.jooq.UDT;
import org.jooq.UDTRecord;
import org.jooq.UpdatableRecord;
import org.jooq.conf.BackslashEscaping;
import org.jooq.conf.ParseNameCase;
import org.jooq.conf.Settings;
import org.jooq.conf.SettingsTools;
import org.jooq.conf.ThrowExceptions;
@ -5317,4 +5320,88 @@ final class Tools {
return false;
}
/**
* Normalise a name case depending on the dialect and the setting for
* {@link ParseNameCase}.
*/
static final String normaliseNameCase(Configuration configuration, String name, boolean quoted) {
switch (parseNameCase(configuration)) {
case LOWER_IF_UNQUOTED:
if (quoted)
return name;
// no-break
case LOWER:
return name.toLowerCase(renderLocale(configuration.settings()));
case UPPER_IF_UNQUOTED:
if (quoted)
return name;
// no-break
case UPPER:
return name.toUpperCase(renderLocale(configuration.settings()));
case AS_IS:
case DEFAULT:
default:
return name;
}
}
/**
* Get the {@link ParseNameCase}, looking up the default value from the
* parse dialect.
*/
private static final ParseNameCase parseNameCase(Configuration configuration) {
ParseNameCase result = defaultIfNull(configuration.settings().getParseNameCase(), ParseNameCase.DEFAULT);
if (result == ParseNameCase.DEFAULT) {
switch (defaultIfNull(configuration.settings().getParseDialect(), configuration.family()).family()) {
case POSTGRES:
return ParseNameCase.LOWER_IF_UNQUOTED;
case DERBY:
case FIREBIRD:
case H2:
case HSQLDB:
return ParseNameCase.UPPER_IF_UNQUOTED;
case MYSQL:
case SQLITE:
return ParseNameCase.AS_IS;
case DEFAULT:
default:
// The SQL standard uses upper case identifiers if unquoted
return ParseNameCase.UPPER_IF_UNQUOTED;
}
}
return result;
}
}

View File

@ -391,7 +391,7 @@ jOOQ queries, for which no specific fetchSize value was specified.]]></jxb:javad
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[[#7337] The dialect that should be used to interpret SQL DDL statements. {@link SQLDialect#DEFAULT} means that jOOQ interprets the SQL itself. Any other dialect (if supported) will be interpreted on an actual JDBC connection.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="interpreterNameCaseSensitivity" type="jooq-runtime:InterpreterNameLookupCaseSensitivity" minOccurs="0" maxOccurs="1" default="DEFAULT">
<element name="interpreterNameLookupCaseSensitivity" type="jooq-runtime:InterpreterNameLookupCaseSensitivity" minOccurs="0" maxOccurs="1" default="DEFAULT">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[[#9633] The case sensitivity of identifiers used when interpreting SQL DDL statements.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>