[jOOQ/jOOQ#7527] Added nullCondition diagnostic

This includes:

- [jOOQ/jOOQ#14138] DiagnosticsConnection should store duplicate SQL strings in Configuration, not static variable
This commit is contained in:
Lukas Eder 2022-10-26 15:59:06 +02:00
parent a167560143
commit 11481b2964
6 changed files with 134 additions and 17 deletions

View File

@ -209,6 +209,30 @@ public interface DiagnosticsListener {

View File

@ -130,6 +130,8 @@ public class Settings
protected Boolean diagnosticsUnnecessaryWasNullCall = true;
@XmlElement(defaultValue = "true")
protected Boolean diagnosticsTrivialCondition = true;
@XmlElement(defaultValue = "true")
protected Boolean diagnosticsNullCondition = true;
@XmlElement(defaultValue = "false")
protected Boolean transformPatterns = false;
@XmlElement(defaultValue = "true")
@ -1435,6 +1437,35 @@ public class Settings
this.diagnosticsTrivialCondition = value;
}
/**
* Whether to run the {@link org.jooq.DiagnosticsListener#nullConditoin(org.jooq.DiagnosticsContext) diagnostic.
* <p>
* Diagnostics are turned off if no {@link org.jooq.Configuration#diagnosticsListenerProviders()} are configured.
* Once configured, this diagnostic is turned on by default.
* <p>
* This feature is available in the commercial distribution only.
*
* @return
* possible object is
* {@link Boolean }
*
*/
public Boolean isDiagnosticsNullCondition() {
return diagnosticsNullCondition;
}
/**
* Sets the value of the diagnosticsNullCondition property.
*
* @param value
* allowed object is
* {@link Boolean }
*
*/
public void setDiagnosticsNullCondition(Boolean value) {
this.diagnosticsNullCondition = value;
}
/**
* Transform various syntax patterns to better versions, if possible.
* <p>
@ -4818,6 +4849,11 @@ public class Settings
return this;
}
public Settings withDiagnosticsNullCondition(Boolean value) {
setDiagnosticsNullCondition(value);
return this;
}
public Settings withTransformPatterns(Boolean value) {
setTransformPatterns(value);
return this;
@ -5854,6 +5890,7 @@ public class Settings
builder.append("diagnosticsTooManyRowsFetched", diagnosticsTooManyRowsFetched);
builder.append("diagnosticsUnnecessaryWasNullCall", diagnosticsUnnecessaryWasNullCall);
builder.append("diagnosticsTrivialCondition", diagnosticsTrivialCondition);
builder.append("diagnosticsNullCondition", diagnosticsNullCondition);
builder.append("transformPatterns", transformPatterns);
builder.append("transformPatternsLogging", transformPatternsLogging);
builder.append("transformPatternsTrim", transformPatternsTrim);
@ -6367,6 +6404,15 @@ public class Settings
return false;
}
}
if (diagnosticsNullCondition == null) {
if (other.diagnosticsNullCondition!= null) {
return false;
}
} else {
if (!diagnosticsNullCondition.equals(other.diagnosticsNullCondition)) {
return false;
}
}
if (transformPatterns == null) {
if (other.transformPatterns!= null) {
return false;
@ -7602,6 +7648,7 @@ public class Settings
result = ((prime*result)+((diagnosticsTooManyRowsFetched == null)? 0 :diagnosticsTooManyRowsFetched.hashCode()));
result = ((prime*result)+((diagnosticsUnnecessaryWasNullCall == null)? 0 :diagnosticsUnnecessaryWasNullCall.hashCode()));
result = ((prime*result)+((diagnosticsTrivialCondition == null)? 0 :diagnosticsTrivialCondition.hashCode()));
result = ((prime*result)+((diagnosticsNullCondition == null)? 0 :diagnosticsNullCondition.hashCode()));
result = ((prime*result)+((transformPatterns == null)? 0 :transformPatterns.hashCode()));
result = ((prime*result)+((transformPatternsLogging == null)? 0 :transformPatternsLogging.hashCode()));
result = ((prime*result)+((transformPatternsTrim == null)? 0 :transformPatternsTrim.hashCode()));

View File

@ -38,6 +38,7 @@
package org.jooq.impl;
import static java.lang.Boolean.FALSE;
import static java.util.Collections.synchronizedMap;
// ...
import static org.jooq.conf.ParamType.FORCE_INDEXED;
@ -46,20 +47,22 @@ import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Predicate;
import java.util.Set;
import java.util.function.Predicate;
import org.jooq.Condition;
import org.jooq.Configuration;
import org.jooq.Parser;
// ...
import org.jooq.Queries;
import org.jooq.QueryPart;
import org.jooq.RenderContext;
// ...
import org.jooq.conf.Settings;
import org.jooq.impl.QOM.Eq;
import org.jooq.tools.jdbc.DefaultConnection;
@ -73,7 +76,6 @@ final class DiagnosticsConnection extends DefaultConnection {
static final int LRU_SIZE_GLOBAL = 50000;
static final int LRU_SIZE_LOCAL = 500;
static final int DUP_SIZE = 500;
static final Map<String, Set<String>> DUPLICATE_SQL = Collections.synchronizedMap(new LRU<>(LRU_SIZE_GLOBAL));
final Map<String, List<String>> repeatedSQL = new LRU<>(LRU_SIZE_LOCAL);
final Configuration configuration;
@ -168,13 +170,27 @@ final class DiagnosticsConnection extends DefaultConnection {
return !FALSE.equals(test.test(configuration.settings()));
}
@SuppressWarnings("unchecked")
final Map<String, Set<String>> duplicateSql() {
return (Map<String, Set<String>>) configuration.data().computeIfAbsent(
"org.jooq.diagnostics.duplicate-sql",
k -> synchronizedMap(new LRU<>(LRU_SIZE_GLOBAL))
);
}
final String parse(String sql) {
Queries queries = null;
Queries transformed = null;
String normalised;
try {
queries = parser.parse(sql);
normalised = normalisingRenderer.render(queries);
// [#14137] TODO: Avoid unnecessary work, depending on the Settings
transformed = queries = parser.parse(sql);
normalised = normalisingRenderer.render(transformed);
}
catch (ParserException exception) {
normalised = sql;
@ -186,10 +202,7 @@ final class DiagnosticsConnection extends DefaultConnection {
try {
if (check(Settings::isDiagnosticsDuplicateStatements)) {
Set<String> duplicates = null;
synchronized (DUPLICATE_SQL) {
duplicates = duplicates(DUPLICATE_SQL, sql, normalised);
}
Set<String> duplicates = duplicates(duplicateSql(), sql, normalised);
if (duplicates != null)
listeners.duplicateStatements(new DefaultDiagnosticsContext(
@ -223,6 +236,11 @@ final class DiagnosticsConnection extends DefaultConnection {
@ -244,16 +262,25 @@ final class DiagnosticsConnection extends DefaultConnection {
return sql;
}
private Set<String> duplicates(Map<String, Set<String>> map, String sql, String normalised) {
Set<String> v = map.computeIfAbsent(normalised, k -> new HashSet<>());
if (v.size() >= DUP_SIZE || (v.add(sql) && v.size() > 1))
return v;
else
return null;
private final Set<String> duplicates(Map<String, Set<String>> map, String sql, String normalised) {
synchronized (map) {
Set<String> v = map.computeIfAbsent(normalised, k -> new HashSet<>());
if (v.size() >= DUP_SIZE || (v.add(sql) && v.size() > 1))
return v;
else
return null;
}
}
private List<String> repetitions(Map<String, List<String>> map, String sql, String normalised) {
private final List<String> repetitions(Map<String, List<String>> map, String sql, String normalised) {
List<String> v = map.computeIfAbsent(normalised, k -> new ArrayList<>());
if (v.size() >= DUP_SIZE || (v.add(sql) && v.size() > 1))
@ -263,7 +290,7 @@ final class DiagnosticsConnection extends DefaultConnection {
}
// See https://stackoverflow.com/a/1953516/521799
static class LRU<V> extends LinkedHashMap<String, V> {
static final class LRU<V> extends LinkedHashMap<String, V> {
private final int size;
LRU(int size) {

View File

@ -120,6 +120,13 @@ final class DiagnosticsListeners implements DiagnosticsListener {
@Override
public final void exception(DiagnosticsContext ctx) {
for (DiagnosticsListener listener : listeners)

View File

@ -1683,6 +1683,9 @@ package org.jooq.impl;

View File

@ -330,6 +330,15 @@ Once configured, this diagnostic is turned on by default.
This feature is available in the commercial distribution only.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="diagnosticsNullCondition" type="boolean" minOccurs="0" maxOccurs="1" default="true">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Whether to run the {@link org.jooq.DiagnosticsListener#nullConditoin(org.jooq.DiagnosticsContext) diagnostic.
<p>
Diagnostics are turned off if no {@link org.jooq.Configuration#diagnosticsListenerProviders()} are configured.
Once configured, this diagnostic is turned on by default.
<p>
This feature is available in the commercial distribution only.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="transformPatterns" type="boolean" minOccurs="0" maxOccurs="1" default="false">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Transform various syntax patterns to better versions, if possible.
<p>