Skip to content

Commit

Permalink
[#5998] Add DSL.name(Name...) to construct a Name from Name.last() el…
Browse files Browse the repository at this point in the history
…ements
  • Loading branch information
lukaseder committed Mar 24, 2017
1 parent 8c4edac commit ec403fa
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 68 deletions.
31 changes: 31 additions & 0 deletions jOOQ/src/main/java/org/jooq/impl/DSL.java
Expand Up @@ -7254,6 +7254,37 @@ public static Name name(String... qualifiedName) {
return new NameImpl(qualifiedName);
}

/**
* Create a new SQL identifier using a qualified name.
* <p>
* Unlike other {@link #name(String...)} constructors, this one constructs a
* name from its argument {@link Name#last()} parts, retaining the quoted
* flag, to construct a new 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 qualified name here
* name(quotedName("book"), unquotedName("title"));
*
* // ... will render this SQL on SQL Server with RenderNameStyle.QUOTED set
* [book].title
* </pre></code>
*
* @param nameParts The SQL identifier's qualified name parts
* @return A {@link QueryPart} that will render the SQL identifier
*/
public static Name name(Name... nameParts) {
return new NameImpl(nameParts);
}

/**
* Create a new SQL identifier using a qualified name.
* <p>
Expand Down
62 changes: 52 additions & 10 deletions jOOQ/src/main/java/org/jooq/impl/NameImpl.java
Expand Up @@ -64,17 +64,49 @@ final class NameImpl extends AbstractQueryPart implements Name {
private static final long serialVersionUID = 8562325639223483938L;

private final String[] qualifiedName;
private final Boolean quoted;
private final Boolean[] quoted;

NameImpl(String[] qualifiedName) {
this(qualifiedName, null);
this(qualifiedName, new Boolean[qualifiedName.length]);
}

NameImpl(String[] qualifiedName, Boolean quoted) {
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;
Expand Down Expand Up @@ -103,24 +135,34 @@ private static final String[] nonEmpty(String[] qualifiedName) {
public final void accept(Context<?> ctx) {
boolean previous = ctx.quote();

if (quoted != null)
ctx.quote(quoted);

// [#3437] Fully qualify this field only if allowed in the current context
if (ctx.qualify()) {
String separator = "";

for (String name : qualifiedName) {
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 != null)
ctx.quote(previous);
if (quoted[last] != null)
ctx.quote(previous);
}
}

@Override
Expand Down
75 changes: 17 additions & 58 deletions jOOQ/src/main/java/org/jooq/impl/ParserImpl.java
Expand Up @@ -1369,7 +1369,7 @@ private static final DDLQuery parseRename(ParserContext ctx) {
parseKeyword(ctx, "TO");
Field<?> newName = parseFieldName(ctx);

return ctx.dsl.alterTable(oldName.getTable().getName()).renameColumn(oldName).to(newName);
return ctx.dsl.alterTable(oldName.getTable()).renameColumn(oldName).to(newName);
}

break;
Expand Down Expand Up @@ -3828,60 +3828,13 @@ private static final Name parseIndexName(ParserContext ctx) {
}

static final Name parseName(ParserContext ctx) {
parseWhitespaceIf(ctx);
if (ctx.done())
throw ctx.exception();

List<String> name = new ArrayList<String>();
StringBuilder sb = new StringBuilder();
int i = ctx.position;
boolean identifierStart = true;
boolean identifierEnd = false;
for (;;) {
char c = ctx.character(i);

// TODO Quoted identifiers
if (c == '.') {
if (identifierStart)
throw new ParserException(ctx);

name.add(sb.toString());
sb = new StringBuilder();
identifierStart = true;
identifierEnd = false;
}

// Better way to distinguish identifier parts
else if (!identifierEnd && Character.isJavaIdentifierPart(c)) {
sb.append(c);
identifierStart = false;
}

else if (Character.isWhitespace(c)) {
identifierEnd = !identifierStart;
}

else {
name.add(sb.toString());
identifierEnd = !identifierStart;
break;
}

if (++i == ctx.sql.length) {
if (identifierStart)
throw ctx.exception();

name.add(sb.toString());
identifierEnd = !identifierStart;
break;
}
}
List<Name> result = new ArrayList<Name>();
result.add(parseIdentifier(ctx));

if (ctx.position == i)
throw ctx.exception();
while (parseIf(ctx, '.'))
result.add(parseIdentifier(ctx));

ctx.position = i;
return DSL.name(name.toArray(EMPTY_STRING));
return result.size() == 1 ? result.get(0) : DSL.name(result.toArray(EMPTY_NAME));
}

private static final List<Name> parseIdentifiers(ParserContext ctx) {
Expand All @@ -3896,12 +3849,12 @@ private static final List<Name> parseIdentifiers(ParserContext ctx) {
}

private static final Name parseIdentifier(ParserContext ctx) {
Name alias = parseIdentifierIf(ctx);
Name result = parseIdentifierIf(ctx);

if (alias == null)
if (result == null)
throw ctx.exception();

return alias;
return result;
}

private static final Name parseIdentifierIf(ParserContext ctx) {
Expand All @@ -3915,19 +3868,25 @@ private static final Name parseIdentifierIf(ParserContext ctx) {

int start = ctx.position;
if (quoteEnd != 0)
while (ctx.character() != quoteEnd)
while (ctx.character() != quoteEnd && ctx.position < ctx.sql.length)
ctx.position = ctx.position + 1;
else
while (ctx.isIdentifierPart())
while (ctx.isIdentifierPart() && ctx.position < ctx.sql.length)
ctx.position = ctx.position + 1;


if (ctx.position == start)
return null;

if (ctx.position - start < 0)
throw ctx.exception();

String result = new String(ctx.sql, start, ctx.position - start);

if (quoteEnd != 0) {
if (ctx.character() != quoteEnd)
throw ctx.exception();

ctx.position = ctx.position + 1;
return DSL.quotedName(result);
}
Expand Down

0 comments on commit ec403fa

Please sign in to comment.