[#6012] Implement specialised Name for unqualified Names
This commit is contained in:
parent
ec403fa8c0
commit
c938846377
@ -49,147 +49,24 @@ import org.jooq.Record;
|
||||
import org.jooq.Select;
|
||||
import org.jooq.WindowDefinition;
|
||||
import org.jooq.WindowSpecification;
|
||||
import org.jooq.tools.StringUtils;
|
||||
|
||||
/**
|
||||
* The default implementation for a SQL identifier
|
||||
* The default implementation for a qualified SQL identifier.
|
||||
*
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
final class NameImpl extends AbstractQueryPart implements Name {
|
||||
abstract class AbstractName extends AbstractQueryPart implements Name {
|
||||
|
||||
/**
|
||||
* Generated UID
|
||||
*/
|
||||
private static final long serialVersionUID = 8562325639223483938L;
|
||||
|
||||
private final String[] qualifiedName;
|
||||
private final Boolean[] quoted;
|
||||
|
||||
NameImpl(String[] qualifiedName) {
|
||||
this(qualifiedName, new Boolean[qualifiedName.length]);
|
||||
}
|
||||
|
||||
NameImpl(Name[] qualifiedName) {
|
||||
this(last(qualifiedName), quoted(qualifiedName));
|
||||
}
|
||||
|
||||
NameImpl(String[] qualifiedName, boolean quoted) {
|
||||
this(qualifiedName, booleans(quoted, qualifiedName.length));
|
||||
}
|
||||
|
||||
NameImpl(String[] qualifiedName, Boolean[] quoted) {
|
||||
this.qualifiedName = nonEmpty(qualifiedName);
|
||||
this.quoted = quoted;
|
||||
}
|
||||
|
||||
private static final Boolean[] booleans(boolean val, int length) {
|
||||
Boolean[] result = new Boolean[length];
|
||||
Arrays.fill(result, val);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static final String[] last(Name[] qualifiedName) {
|
||||
String[] result = new String[qualifiedName.length];
|
||||
|
||||
for (int i = 0; i < qualifiedName.length; i++)
|
||||
result[i] = qualifiedName[i].last();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static final Boolean[] quoted(Name[] qualifiedName) {
|
||||
Boolean[] result = new Boolean[qualifiedName.length];
|
||||
|
||||
for (int i = 0; i < qualifiedName.length; i++)
|
||||
result[i] = ((NameImpl) qualifiedName[i]).quoted[((NameImpl) qualifiedName[i]).quoted.length - 1];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static final String[] nonEmpty(String[] qualifiedName) {
|
||||
String[] result;
|
||||
int nulls = 0;
|
||||
|
||||
for (int i = 0; i < qualifiedName.length; i++)
|
||||
if (StringUtils.isEmpty(qualifiedName[i]))
|
||||
nulls++;
|
||||
|
||||
if (nulls > 0) {
|
||||
result = new String[qualifiedName.length - nulls];
|
||||
|
||||
for (int i = qualifiedName.length - 1; i >= 0; i--)
|
||||
if (StringUtils.isEmpty(qualifiedName[i]))
|
||||
nulls--;
|
||||
else
|
||||
result[i - nulls] = qualifiedName[i];
|
||||
}
|
||||
else {
|
||||
result = qualifiedName.clone();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void accept(Context<?> ctx) {
|
||||
boolean previous = ctx.quote();
|
||||
|
||||
// [#3437] Fully qualify this field only if allowed in the current context
|
||||
if (ctx.qualify()) {
|
||||
String separator = "";
|
||||
|
||||
for (int i = 0; i < qualifiedName.length; i++) {
|
||||
String name = qualifiedName[i];
|
||||
|
||||
if (quoted[i] != null)
|
||||
ctx.quote(quoted[i]);
|
||||
|
||||
ctx.sql(separator).literal(name);
|
||||
|
||||
if (quoted[i] != null)
|
||||
ctx.quote(previous);
|
||||
|
||||
separator = ".";
|
||||
}
|
||||
}
|
||||
else {
|
||||
int last = quoted.length - 1;
|
||||
if (quoted[last] != null)
|
||||
ctx.quote(quoted[last]);
|
||||
|
||||
ctx.literal(last());
|
||||
|
||||
if (quoted[last] != null)
|
||||
ctx.quote(previous);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Clause[] clauses(Context<?> ctx) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String first() {
|
||||
return qualifiedName.length > 0 ? qualifiedName[0] : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String last() {
|
||||
return qualifiedName.length > 0 ? qualifiedName[qualifiedName.length - 1] : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean qualified() {
|
||||
return qualifiedName.length > 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String[] getName() {
|
||||
return qualifiedName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final WindowDefinition as(WindowSpecification window) {
|
||||
return new WindowDefinitionImpl(this, window);
|
||||
@ -203,10 +80,10 @@ final class NameImpl extends AbstractQueryPart implements Name {
|
||||
|
||||
@Override
|
||||
public final DerivedColumnListImpl fields(String... fieldNames) {
|
||||
if (qualifiedName.length != 1)
|
||||
throw new IllegalStateException("Cannot create a DerivedColumnList from a qualified name : " + Arrays.asList(qualifiedName));
|
||||
if (getName().length != 1)
|
||||
throw new IllegalStateException("Cannot create a DerivedColumnList from a qualified name : " + Arrays.asList(getName()));
|
||||
|
||||
return new DerivedColumnListImpl(first(), fieldNames);
|
||||
return new DerivedColumnListImpl(last(), fieldNames);
|
||||
}
|
||||
|
||||
|
||||
@ -375,8 +252,8 @@ final class NameImpl extends AbstractQueryPart implements Name {
|
||||
|
||||
// [#1626] NameImpl equality can be decided without executing the
|
||||
// rather expensive implementation of AbstractQueryPart.equals()
|
||||
if (that instanceof NameImpl)
|
||||
return Arrays.equals(getName(), (((NameImpl) that).getName()));
|
||||
if (that instanceof AbstractName)
|
||||
return Arrays.equals(getName(), (((AbstractName) that).getName()));
|
||||
|
||||
return super.equals(that);
|
||||
}
|
||||
@ -7227,6 +7227,33 @@ public class DSL {
|
||||
// XXX Names
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Create a new SQL identifier using an unqualified name.
|
||||
* <p>
|
||||
* Use this method to construct syntax-safe, SQL-injection-safe SQL
|
||||
* identifiers for use in plain SQL where {@link QueryPart} objects are
|
||||
* accepted. For instance, this can be used with any of these methods:
|
||||
* <ul>
|
||||
* <li> {@link #field(Name)}</li>
|
||||
* <li> {@link #field(Name, Class)}</li>
|
||||
* <li> {@link #field(Name, DataType)}</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* An example: <code><pre>
|
||||
* // This unqualified name here
|
||||
* name("book");
|
||||
*
|
||||
* // ... will render this SQL on SQL Server with RenderNameStyle.QUOTED set
|
||||
* [book].[title]
|
||||
* </pre></code>
|
||||
*
|
||||
* @param unqualifiedName The SQL identifier's unqualified name
|
||||
* @return A {@link QueryPart} that will render the SQL identifier
|
||||
*/
|
||||
public static Name name(String unqualifiedName) {
|
||||
return new UnqualifiedName(unqualifiedName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new SQL identifier using a qualified name.
|
||||
* <p>
|
||||
@ -7251,7 +7278,10 @@ public class DSL {
|
||||
* @return A {@link QueryPart} that will render the SQL identifier
|
||||
*/
|
||||
public static Name name(String... qualifiedName) {
|
||||
return new NameImpl(qualifiedName);
|
||||
if (qualifiedName == null || qualifiedName.length != 1)
|
||||
return new QualifiedName(qualifiedName);
|
||||
else
|
||||
return new UnqualifiedName(qualifiedName[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -7282,7 +7312,7 @@ public class DSL {
|
||||
* @return A {@link QueryPart} that will render the SQL identifier
|
||||
*/
|
||||
public static Name name(Name... nameParts) {
|
||||
return new NameImpl(nameParts);
|
||||
return new QualifiedName(nameParts);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -7309,7 +7339,21 @@ public class DSL {
|
||||
* @return A {@link QueryPart} that will render the SQL identifier
|
||||
*/
|
||||
public static Name name(Collection<String> qualifiedName) {
|
||||
return new NameImpl(qualifiedName.toArray(Tools.EMPTY_STRING));
|
||||
return name(qualifiedName.toArray(Tools.EMPTY_STRING));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new SQL identifier using an unqualified, quoted name.
|
||||
* <p>
|
||||
* This works like {@link #name(String...)}, except that generated
|
||||
* identifiers will be guaranteed to be quoted in databases that support
|
||||
* quoted identifiers.
|
||||
*
|
||||
* @param unqualifiedName The SQL identifier's unqualified name
|
||||
* @return A {@link QueryPart} that will render the SQL identifier
|
||||
*/
|
||||
public static Name quotedName(String unqualifiedName) {
|
||||
return new UnqualifiedName(unqualifiedName, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -7323,7 +7367,7 @@ public class DSL {
|
||||
* @return A {@link QueryPart} that will render the SQL identifier
|
||||
*/
|
||||
public static Name quotedName(String... qualifiedName) {
|
||||
return new NameImpl(qualifiedName, true);
|
||||
return new QualifiedName(qualifiedName, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -7337,7 +7381,21 @@ public class DSL {
|
||||
* @return A {@link QueryPart} that will render the SQL identifier
|
||||
*/
|
||||
public static Name quotedName(Collection<String> qualifiedName) {
|
||||
return new NameImpl(qualifiedName.toArray(Tools.EMPTY_STRING), true);
|
||||
return quotedName(qualifiedName.toArray(Tools.EMPTY_STRING));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new SQL identifier using an unqualified, quoted name.
|
||||
* <p>
|
||||
* This works like {@link #name(String...)}, except that generated
|
||||
* identifiers will be guaranteed to be quoted in databases that support
|
||||
* quoted identifiers.
|
||||
*
|
||||
* @param unqualifiedName The SQL identifier's unqualified name
|
||||
* @return A {@link QueryPart} that will render the SQL identifier
|
||||
*/
|
||||
public static Name unquotedName(String unqualifiedName) {
|
||||
return new UnqualifiedName(unqualifiedName, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -7351,7 +7409,10 @@ public class DSL {
|
||||
* @return A {@link QueryPart} that will render the SQL identifier
|
||||
*/
|
||||
public static Name unquotedName(String... qualifiedName) {
|
||||
return new NameImpl(qualifiedName, false);
|
||||
if (qualifiedName == null || qualifiedName.length != 1)
|
||||
return new QualifiedName(qualifiedName, false);
|
||||
else
|
||||
return new UnqualifiedName(qualifiedName[0], false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -7365,7 +7426,7 @@ public class DSL {
|
||||
* @return A {@link QueryPart} that will render the SQL identifier
|
||||
*/
|
||||
public static Name unquotedName(Collection<String> qualifiedName) {
|
||||
return new NameImpl(qualifiedName.toArray(Tools.EMPTY_STRING), false);
|
||||
return unquotedName(qualifiedName.toArray(Tools.EMPTY_STRING));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@ -67,7 +67,7 @@ final class QualifiedField<T> extends AbstractField<T> implements TableField<Rec
|
||||
super(DSL.name(defaultIfNull(name.last(), "")), type);
|
||||
|
||||
this.name = name;
|
||||
this.table = name.getName().length > 1
|
||||
this.table = name.qualified()
|
||||
? DSL.table(DSL.name(Arrays.copyOf(name.getName(), name.getName().length - 1)))
|
||||
: null;
|
||||
}
|
||||
|
||||
165
jOOQ/src/main/java/org/jooq/impl/QualifiedName.java
Normal file
165
jOOQ/src/main/java/org/jooq/impl/QualifiedName.java
Normal file
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* 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 org.jooq.Context;
|
||||
import org.jooq.Name;
|
||||
import org.jooq.tools.StringUtils;
|
||||
|
||||
/**
|
||||
* The default implementation for a qualified SQL identifier.
|
||||
*
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
final class QualifiedName extends AbstractName {
|
||||
|
||||
/**
|
||||
* Generated UID
|
||||
*/
|
||||
private static final long serialVersionUID = 8562325639223483938L;
|
||||
|
||||
private final UnqualifiedName[] qualifiedName;
|
||||
|
||||
QualifiedName(String[] qualifiedName) {
|
||||
this(qualifiedName, null);
|
||||
}
|
||||
|
||||
QualifiedName(String[] qualifiedName, Boolean quoted) {
|
||||
this(names(qualifiedName, quoted));
|
||||
}
|
||||
|
||||
QualifiedName(Name[] qualifiedName) {
|
||||
this.qualifiedName = last(qualifiedName);
|
||||
}
|
||||
|
||||
private QualifiedName(UnqualifiedName[] qualifiedName) {
|
||||
this.qualifiedName = qualifiedName;
|
||||
}
|
||||
|
||||
private static final UnqualifiedName[] names(String[] qualifiedName, Boolean quoted) {
|
||||
String[] nonEmpty = nonEmpty(qualifiedName);
|
||||
UnqualifiedName[] result = new UnqualifiedName[nonEmpty.length];
|
||||
|
||||
for (int i = 0; i < nonEmpty.length; i++)
|
||||
result[i] = new UnqualifiedName(nonEmpty[i], quoted);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static final UnqualifiedName[] last(Name[] qualifiedName) {
|
||||
if (qualifiedName instanceof UnqualifiedName[])
|
||||
return (UnqualifiedName[]) qualifiedName;
|
||||
|
||||
UnqualifiedName[] result = new UnqualifiedName[qualifiedName.length];
|
||||
|
||||
for (int i = 0; i < qualifiedName.length; i++)
|
||||
if (qualifiedName[i] instanceof QualifiedName) {
|
||||
QualifiedName q = (QualifiedName) qualifiedName[i];
|
||||
result[i] = q.qualifiedName[q.qualifiedName.length - 1];
|
||||
}
|
||||
else if (qualifiedName[i] instanceof UnqualifiedName)
|
||||
result[i] = (UnqualifiedName) qualifiedName[i];
|
||||
else
|
||||
result[i] = new UnqualifiedName(qualifiedName[i].last());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static final String[] nonEmpty(String[] qualifiedName) {
|
||||
String[] result;
|
||||
int nulls = 0;
|
||||
|
||||
for (int i = 0; i < qualifiedName.length; i++)
|
||||
if (StringUtils.isEmpty(qualifiedName[i]))
|
||||
nulls++;
|
||||
|
||||
if (nulls > 0) {
|
||||
result = new String[qualifiedName.length - nulls];
|
||||
|
||||
for (int i = qualifiedName.length - 1; i >= 0; i--)
|
||||
if (StringUtils.isEmpty(qualifiedName[i]))
|
||||
nulls--;
|
||||
else
|
||||
result[i - nulls] = qualifiedName[i];
|
||||
}
|
||||
else {
|
||||
result = qualifiedName.clone();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void accept(Context<?> ctx) {
|
||||
|
||||
// [#3437] Fully qualify this field only if allowed in the current context
|
||||
if (ctx.qualify()) {
|
||||
String separator = "";
|
||||
|
||||
for (int i = 0; i < qualifiedName.length; i++) {
|
||||
ctx.sql(separator).visit(qualifiedName[i]);
|
||||
separator = ".";
|
||||
}
|
||||
}
|
||||
else {
|
||||
ctx.visit(qualifiedName[qualifiedName.length - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String first() {
|
||||
return qualifiedName.length > 0 ? qualifiedName[0].last() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String last() {
|
||||
return qualifiedName.length > 0 ? qualifiedName[qualifiedName.length - 1].last() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean qualified() {
|
||||
return qualifiedName.length > 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String[] getName() {
|
||||
String[] result = new String[qualifiedName.length];
|
||||
|
||||
for (int i = 0; i < qualifiedName.length; i++)
|
||||
result[i] = qualifiedName[i].last();
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
95
jOOQ/src/main/java/org/jooq/impl/UnqualifiedName.java
Normal file
95
jOOQ/src/main/java/org/jooq/impl/UnqualifiedName.java
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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 org.jooq.Context;
|
||||
|
||||
/**
|
||||
* The default implementation for an unqualified SQL identifier.
|
||||
*
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
final class UnqualifiedName extends AbstractName {
|
||||
|
||||
/**
|
||||
* Generated UID
|
||||
*/
|
||||
private static final long serialVersionUID = 8562325639223483938L;
|
||||
|
||||
private final String name;
|
||||
private final Boolean quoted;
|
||||
|
||||
UnqualifiedName(String name) {
|
||||
this(name, null);
|
||||
}
|
||||
|
||||
UnqualifiedName(String name, Boolean quoted) {
|
||||
this.name = name;
|
||||
this.quoted = quoted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void accept(Context<?> ctx) {
|
||||
boolean previous = ctx.quote();
|
||||
|
||||
if (quoted != null)
|
||||
ctx.quote(quoted);
|
||||
|
||||
ctx.literal(name);
|
||||
|
||||
if (quoted != null)
|
||||
ctx.quote(previous);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String first() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String last() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean qualified() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String[] getName() {
|
||||
return new String[] { name };
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user