Skip to content

Commit

Permalink
[#6012] Implement specialised Name for unqualified Names
Browse files Browse the repository at this point in the history
  • Loading branch information
lukaseder committed Mar 24, 2017
1 parent ec403fa commit c938846
Show file tree
Hide file tree
Showing 5 changed files with 336 additions and 138 deletions.
Expand Up @@ -49,147 +49,24 @@
import org.jooq.Select; import org.jooq.Select;
import org.jooq.WindowDefinition; import org.jooq.WindowDefinition;
import org.jooq.WindowSpecification; 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 * @author Lukas Eder
*/ */
final class NameImpl extends AbstractQueryPart implements Name { abstract class AbstractName extends AbstractQueryPart implements Name {


/** /**
* Generated UID * Generated UID
*/ */
private static final long serialVersionUID = 8562325639223483938L; 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 @Override
public final Clause[] clauses(Context<?> ctx) { public final Clause[] clauses(Context<?> ctx) {
return null; 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 @Override
public final WindowDefinition as(WindowSpecification window) { public final WindowDefinition as(WindowSpecification window) {
return new WindowDefinitionImpl(this, window); return new WindowDefinitionImpl(this, window);
Expand All @@ -203,10 +80,10 @@ public final <R extends Record> CommonTableExpression<R> as(Select<R> select) {


@Override @Override
public final DerivedColumnListImpl fields(String... fieldNames) { public final DerivedColumnListImpl fields(String... fieldNames) {
if (qualifiedName.length != 1) if (getName().length != 1)
throw new IllegalStateException("Cannot create a DerivedColumnList from a qualified name : " + Arrays.asList(qualifiedName)); throw new IllegalStateException("Cannot create a DerivedColumnList from a qualified name : " + Arrays.asList(getName()));


return new DerivedColumnListImpl(first(), fieldNames); return new DerivedColumnListImpl(last(), fieldNames);
} }




Expand Down Expand Up @@ -375,8 +252,8 @@ public boolean equals(Object that) {


// [#1626] NameImpl equality can be decided without executing the // [#1626] NameImpl equality can be decided without executing the
// rather expensive implementation of AbstractQueryPart.equals() // rather expensive implementation of AbstractQueryPart.equals()
if (that instanceof NameImpl) if (that instanceof AbstractName)
return Arrays.equals(getName(), (((NameImpl) that).getName())); return Arrays.equals(getName(), (((AbstractName) that).getName()));


return super.equals(that); return super.equals(that);
} }
Expand Down
75 changes: 68 additions & 7 deletions jOOQ/src/main/java/org/jooq/impl/DSL.java
Expand Up @@ -7227,6 +7227,33 @@ public static Keyword keyword(String keyword) {
// XXX Names // 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. * Create a new SQL identifier using a qualified name.
* <p> * <p>
Expand All @@ -7251,7 +7278,10 @@ public static Keyword keyword(String keyword) {
* @return A {@link QueryPart} that will render the SQL identifier * @return A {@link QueryPart} that will render the SQL identifier
*/ */
public static Name name(String... qualifiedName) { 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]);
} }


/** /**
Expand Down Expand Up @@ -7282,7 +7312,7 @@ public static Name name(String... qualifiedName) {
* @return A {@link QueryPart} that will render the SQL identifier * @return A {@link QueryPart} that will render the SQL identifier
*/ */
public static Name name(Name... nameParts) { public static Name name(Name... nameParts) {
return new NameImpl(nameParts); return new QualifiedName(nameParts);
} }


/** /**
Expand All @@ -7309,7 +7339,21 @@ public static Name name(Name... nameParts) {
* @return A {@link QueryPart} that will render the SQL identifier * @return A {@link QueryPart} that will render the SQL identifier
*/ */
public static Name name(Collection<String> qualifiedName) { 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);
} }


/** /**
Expand All @@ -7323,7 +7367,7 @@ public static Name name(Collection<String> qualifiedName) {
* @return A {@link QueryPart} that will render the SQL identifier * @return A {@link QueryPart} that will render the SQL identifier
*/ */
public static Name quotedName(String... qualifiedName) { public static Name quotedName(String... qualifiedName) {
return new NameImpl(qualifiedName, true); return new QualifiedName(qualifiedName, true);
} }


/** /**
Expand All @@ -7337,7 +7381,21 @@ public static Name quotedName(String... qualifiedName) {
* @return A {@link QueryPart} that will render the SQL identifier * @return A {@link QueryPart} that will render the SQL identifier
*/ */
public static Name quotedName(Collection<String> qualifiedName) { 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);
} }


/** /**
Expand All @@ -7351,7 +7409,10 @@ public static Name quotedName(Collection<String> qualifiedName) {
* @return A {@link QueryPart} that will render the SQL identifier * @return A {@link QueryPart} that will render the SQL identifier
*/ */
public static Name unquotedName(String... qualifiedName) { 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);
} }


/** /**
Expand All @@ -7365,7 +7426,7 @@ public static Name unquotedName(String... qualifiedName) {
* @return A {@link QueryPart} that will render the SQL identifier * @return A {@link QueryPart} that will render the SQL identifier
*/ */
public static Name unquotedName(Collection<String> qualifiedName) { public static Name unquotedName(Collection<String> qualifiedName) {
return new NameImpl(qualifiedName.toArray(Tools.EMPTY_STRING), false); return unquotedName(qualifiedName.toArray(Tools.EMPTY_STRING));
} }


// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion jOOQ/src/main/java/org/jooq/impl/QualifiedField.java
Expand Up @@ -67,7 +67,7 @@ final class QualifiedField<T> extends AbstractField<T> implements TableField<Rec
super(DSL.name(defaultIfNull(name.last(), "")), type); super(DSL.name(defaultIfNull(name.last(), "")), type);


this.name = name; 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))) ? DSL.table(DSL.name(Arrays.copyOf(name.getName(), name.getName().length - 1)))
: null; : null;
} }
Expand Down

0 comments on commit c938846

Please sign in to comment.