[jOOQ/jOOQ#16270] Refactor QualifiedName to improve performance

This commit is contained in:
Lukas Eder 2024-02-12 17:53:26 +01:00
parent 1e5fb6f0bd
commit a1cf391006
4 changed files with 120 additions and 150 deletions

View File

@ -75,15 +75,12 @@ implements
public final Name append(Name name) {
if (empty())
return name;
else if (name.empty())
else if (name == null || name.empty())
return this;
Name[] p1 = parts();
Name[] p2 = name.parts();
Name[] array = new Name[p1.length + p2.length];
System.arraycopy(p1, 0, array, 0, p1.length);
System.arraycopy(p2, 0, array, p1.length, p2.length);
return new QualifiedName(array);
else if (name instanceof UnqualifiedName last)
return new QualifiedName(this, last);
else
return append(name.qualifier()).append(name.unqualifiedName());
}
@Override

View File

@ -12975,10 +12975,15 @@ public class DSL {
@NotNull
@Support
public static Name name(String... qualifiedName) {
if (qualifiedName == null || qualifiedName.length != 1)
return new QualifiedName(qualifiedName);
else
return new UnqualifiedName(qualifiedName[0]);
if (Tools.isEmpty(qualifiedName))
return new UnqualifiedName(null);
Name result = new UnqualifiedName(qualifiedName[0]);
for (int i = 1; i < qualifiedName.length; i++)
result = result.append(new UnqualifiedName(qualifiedName[i]));
return result;
}
/**
@ -13011,7 +13016,15 @@ public class DSL {
@NotNull
@Support
public static Name name(Name... nameParts) {
return new QualifiedName(nameParts);
if (Tools.isEmpty(nameParts))
return new UnqualifiedName(null);
Name result = nameParts[0] != null ? nameParts[0] : new UnqualifiedName(null);
for (int i = 1; i < nameParts.length; i++)
result = result.append(nameParts[i]);
return result;
}
/**
@ -13076,10 +13089,15 @@ public class DSL {
@NotNull
@Support
public static Name quotedName(String... qualifiedName) {
if (qualifiedName == null || qualifiedName.length != 1)
return new QualifiedName(qualifiedName, Quoted.QUOTED);
else
return new UnqualifiedName(qualifiedName[0], Quoted.QUOTED);
if (Tools.isEmpty(qualifiedName))
return new UnqualifiedName(null);
Name result = new UnqualifiedName(qualifiedName[0], Quoted.QUOTED);
for (int i = 1; i < qualifiedName.length; i++)
result = result.append(new UnqualifiedName(qualifiedName[i], Quoted.QUOTED));
return result;
}
/**
@ -13131,10 +13149,15 @@ public class DSL {
@NotNull
@Support
public static Name unquotedName(String... qualifiedName) {
if (qualifiedName == null || qualifiedName.length != 1)
return new QualifiedName(qualifiedName, Quoted.UNQUOTED);
else
return new UnqualifiedName(qualifiedName[0], Quoted.UNQUOTED);
if (Tools.isEmpty(qualifiedName))
return new UnqualifiedName(null);
Name result = new UnqualifiedName(qualifiedName[0], Quoted.UNQUOTED);
for (int i = 1; i < qualifiedName.length; i++)
result = result.append(new UnqualifiedName(qualifiedName[i], Quoted.UNQUOTED));
return result;
}
/**
@ -13185,10 +13208,15 @@ public class DSL {
@NotNull
@Support
public static Name systemName(String... qualifiedName) {
if (qualifiedName == null || qualifiedName.length != 1)
return new QualifiedName(qualifiedName, Quoted.SYSTEM);
else
return new UnqualifiedName(qualifiedName[0], Quoted.SYSTEM);
if (Tools.isEmpty(qualifiedName))
return new UnqualifiedName(null);
Name result = new UnqualifiedName(qualifiedName[0], Quoted.SYSTEM);
for (int i = 1; i < qualifiedName.length; i++)
result = result.append(new UnqualifiedName(qualifiedName[i], Quoted.SYSTEM));
return result;
}
/**

View File

@ -37,16 +37,15 @@
*/
package org.jooq.impl;
import static org.jooq.Name.Quoted.DEFAULT;
import static org.jooq.Name.Quoted.MIXED;
// ...
import static org.jooq.impl.Tools.allMatch;
import static org.jooq.impl.Tools.map;
import static org.jooq.impl.Tools.EMPTY_NAME;
import static org.jooq.impl.Tools.EMPTY_STRING;
import static org.jooq.impl.Tools.stringLiteral;
import org.jooq.Context;
import org.jooq.Name;
import org.jooq.tools.StringUtils;
// ...
/**
* The default implementation for a qualified SQL identifier.
@ -55,93 +54,27 @@ import org.jooq.tools.StringUtils;
*/
final class QualifiedName extends AbstractName {
private final UnqualifiedName[] qualifiedName;
final Name qualifier;
final UnqualifiedName last;
QualifiedName(String[] qualifiedName) {
this(qualifiedName, DEFAULT);
QualifiedName(Name qualifier, UnqualifiedName last) {
this.qualifier = qualifier;
this.last = last;
}
QualifiedName(String[] qualifiedName, Quoted quoted) {
this(names(qualifiedName, quoted));
}
QualifiedName(Name[] qualifiedName) {
this(last(nonEmpty(qualifiedName)));
}
private QualifiedName(UnqualifiedName[] qualifiedName) {
this.qualifiedName = qualifiedName;
}
private static final UnqualifiedName[] names(String[] qualifiedName, Quoted quoted) {
return map(nonEmpty(qualifiedName), s -> new UnqualifiedName(s, quoted), UnqualifiedName[]::new);
}
private static final UnqualifiedName[] last(Name[] qualifiedName) {
if (qualifiedName instanceof UnqualifiedName[] u)
return u;
UnqualifiedName[] result = new UnqualifiedName[qualifiedName.length];
for (int i = 0; i < qualifiedName.length; i++)
if (qualifiedName[i] instanceof QualifiedName q) {
result[i] = q.qualifiedName[q.qualifiedName.length - 1];
}
else if (qualifiedName[i] instanceof UnqualifiedName u)
result[i] = u;
else
result[i] = new UnqualifiedName(qualifiedName[i].last());
return result;
}
private static final String[] nonEmpty(String[] qualifiedName) {
String[] result;
int nulls = 0;
for (String name : qualifiedName)
if (StringUtils.isEmpty(name))
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;
}
return result;
}
private static final Name[] nonEmpty(Name[] names) {
Name[] result;
int nulls = 0;
for (Name name : names)
if (name == null || name.equals(NO_NAME))
nulls++;
if (nulls > 0) {
result = new Name[names.length - nulls];
for (int i = names.length - 1; i >= 0; i--)
if (names[i] == null || names[i].equals(NO_NAME))
nulls--;
else
result[i - nulls] = names[i];
}
else {
result = names;
}
return result;
}
@Override
public final void accept(Context<?> ctx) {
@ -159,97 +92,108 @@ final class QualifiedName extends AbstractName {
// [#3437] Fully qualify this field only if allowed in the current context
if (ctx.qualify()) {
String separator = "";
for (UnqualifiedName name : qualifiedName) {
ctx.sql(separator).visit(name);
separator = ".";
}
}
else {
ctx.visit(qualifiedName[qualifiedName.length - 1]);
}
if (ctx.qualify() && qualifier != null)
ctx.visit(qualifier).sql('.').visit(last);
else
ctx.visit(last);
}
@Override
public final String first() {
return qualifiedName.length > 0 ? qualifiedName[0].last() : null;
return qualifier != null ? qualifier.first() : last.name;
}
@Override
public final String last() {
return qualifiedName.length > 0 ? qualifiedName[qualifiedName.length - 1].last() : null;
return last.name;
}
@Override
public final boolean empty() {
return allMatch(qualifiedName, n -> n.empty());
return last.empty();
}
@Override
public final boolean qualified() {
return qualifiedName.length > 1;
return qualifier != null;
}
@Override
public final boolean qualifierQualified() {
return qualifiedName.length > 2;
return qualifier != null && qualifier.qualified();
}
@Override
public final Name qualifier() {
if (qualifiedName.length <= 1)
return null;
if (qualifiedName.length == 2)
return qualifiedName[0];
UnqualifiedName[] qualifier = new UnqualifiedName[qualifiedName.length - 1];
System.arraycopy(qualifiedName, 0, qualifier, 0, qualifier.length);
return new QualifiedName(qualifier);
return qualifier;
}
@Override
public final Name unqualifiedName() {
if (qualifiedName.length == 0)
return this;
else
return qualifiedName[qualifiedName.length - 1];
return last;
}
@Override
public final Quoted quoted() {
Quoted result = null;
for (UnqualifiedName name : qualifiedName)
if (result == null)
result = name.quoted();
else if (result != name.quoted())
return MIXED;
return result == null ? DEFAULT : result;
Quoted result = qualifier != null ? qualifier.quoted() : null;
return result == null || result == last.quoted() ? last.quoted() : MIXED;
}
@Override
public final Name quotedName() {
return new QualifiedName(map(qualifiedName, n -> n.quotedName(), Name[]::new));
return qualifier.quotedName().append(last.quotedName());
}
@Override
public final Name unquotedName() {
return new QualifiedName(map(qualifiedName, n -> n.unquotedName(), Name[]::new));
return qualifier.unquotedName().append(last.unquotedName());
}
@Override
public final String[] getName() {
return map(qualifiedName, n -> n.last(), String[]::new);
if (empty())
return EMPTY_STRING;
int i = 1;
Name q = qualifier;
while (q != null) {
q = q.qualifier();
i++;
}
String[] name = new String[i];
name[--i] = last.name;
q = qualifier;
while (q != null) {
name[--i] = q.last();
q = q.qualifier();
}
return name;
}
@Override
public final Name[] parts() {
return qualifiedName.clone();
if (empty())
return EMPTY_NAME;
int i = 1;
Name q = qualifier;
while (q != null) {
q = q.qualifier();
i++;
}
Name[] parts = new Name[i];
parts[--i] = last;
q = qualifier;
while (q != null) {
parts[--i] = q.unqualifiedName();
q = q.qualifier();
}
return parts;
}
// ------------------------------------------------------------------------
@ -266,13 +210,12 @@ final class QualifiedName extends AbstractName {
if (hash == null) {
int h = 1;
for (int i = 0; i < qualifiedName.length; i++) {
UnqualifiedName n = qualifiedName[i];
h = 31 * h + hashCode0(last);
if (n.name == null)
h = 31 * h + 0;
else
h = 31 * h + hashCode0(n);
Name q = qualifier;
while (q != null) {
h = 31 * h + hashCode0(q.unqualifiedName());
q = q.qualifier();
}
hash = h;

View File

@ -42,6 +42,8 @@ import static org.jooq.Name.Quoted.QUOTED;
import static org.jooq.Name.Quoted.SYSTEM;
import static org.jooq.Name.Quoted.UNQUOTED;
// ...
import static org.jooq.impl.Tools.EMPTY_NAME;
import static org.jooq.impl.Tools.EMPTY_STRING;
import static org.jooq.impl.Tools.stringLiteral;
import java.util.Arrays;
@ -155,12 +157,12 @@ final class UnqualifiedName extends AbstractName {
@Override
public final String[] getName() {
return new String[] { name };
return empty() ? EMPTY_STRING : new String[] { name };
}
@Override
public final Name[] parts() {
return new Name[] { this };
return empty() ? EMPTY_NAME : new Name[] { this };
}
// ------------------------------------------------------------------------