Permalink
Browse files

fix: Change optimization to delay parameter and statement re-write to…

… immediately before execution.
  • Loading branch information...
whitingjr authored and davecramer committed Feb 22, 2016
1 parent ac8abf3 commit e59157742c77769cc19e14aeb5aebd9c042e2050
Showing with 1,016 additions and 1,193 deletions.
  1. +1 −0 .gitignore
  2. +3 −3 pgjdbc/src/main/java/org/postgresql/core/NativeQuery.java
  3. +2 −8 pgjdbc/src/main/java/org/postgresql/core/ParameterList.java
  4. +57 −37 pgjdbc/src/main/java/org/postgresql/core/Parser.java
  5. +2 −0 pgjdbc/src/main/java/org/postgresql/core/Query.java
  6. +7 −5 pgjdbc/src/main/java/org/postgresql/core/QueryExecutor.java
  7. +1 −1 pgjdbc/src/main/java/org/postgresql/core/SetupQueryRunner.java
  8. +1 −17 pgjdbc/src/main/java/org/postgresql/core/v2/FastpathParameterList.java
  9. +3 −3 pgjdbc/src/main/java/org/postgresql/core/v2/QueryExecutorImpl.java
  10. +17 −19 pgjdbc/src/main/java/org/postgresql/core/v2/SimpleParameterList.java
  11. +2 −5 pgjdbc/src/main/java/org/postgresql/core/v2/V2Query.java
  12. +80 −11 pgjdbc/src/main/java/org/postgresql/core/v3/BatchedQueryDecorator.java
  13. +1 −10 pgjdbc/src/main/java/org/postgresql/core/v3/CompositeParameterList.java
  14. +0 −3 pgjdbc/src/main/java/org/postgresql/core/v3/CompositeQuery.java
  15. +7 −7 pgjdbc/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java
  16. +30 −21 pgjdbc/src/main/java/org/postgresql/core/v3/SimpleParameterList.java
  17. +1 −4 pgjdbc/src/main/java/org/postgresql/core/v3/SimpleQuery.java
  18. +2 −0 pgjdbc/src/main/java/org/postgresql/ds/common/BaseDataSource.java
  19. +1 −1 pgjdbc/src/main/java/org/postgresql/jdbc/CachedQueryCreateAction.java
  20. +2 −2 pgjdbc/src/main/java/org/postgresql/jdbc/PgConnection.java
  21. +19 −18 pgjdbc/src/main/java/org/postgresql/jdbc/PgPreparedStatement.java
  22. +9 −6 pgjdbc/src/main/java/org/postgresql/jdbc/PgStatement.java
  23. +51 −0 pgjdbc/src/test/java/org/postgresql/core/v2/V2ParameterListTests.java
  24. +74 −0 pgjdbc/src/test/java/org/postgresql/core/v3/V3ParameterListTests.java
  25. +451 −0 pgjdbc/src/test/java/org/postgresql/jdbc/DeepBatchedInsertStatementTest.java
  26. +0 −94 pgjdbc/src/test/java/org/postgresql/test/core/v2/V2ParameterListTests.java
  27. +0 −227 pgjdbc/src/test/java/org/postgresql/test/core/v3/V3ParameterListTests.java
  28. +16 −29 pgjdbc/src/test/java/org/postgresql/test/jdbc2/BatchedInsertDoubleRowInSingleBatch.java
  29. +111 −113 pgjdbc/src/test/java/org/postgresql/test/jdbc2/BatchedInsertReWriteEnabledTest.java
  30. +61 −95 pgjdbc/src/test/java/org/postgresql/test/jdbc2/BatchedInsertStatementPreparingTest.java
  31. +0 −451 pgjdbc/src/test/java/org/postgresql/test/jdbc2/DeepBatchedInsertStatementTest.java
  32. +3 −2 pgjdbc/src/test/java/org/postgresql/test/jdbc2/Jdbc2TestSuite.java
  33. +1 −1 pgjdbc/src/test/java/org/postgresql/test/jdbc3/CompositeQueryParseTest.java
@@ -25,3 +25,4 @@ buildNumber.properties
# Ignore folders used to build binaries for old Java
/pgjdbc-jre6
/pgjdbc-jre7
/eclipsebin/
@@ -31,11 +31,11 @@ public NativeQuery(String nativeSql) {
this(nativeSql, NO_BINDS, false);
}
public NativeQuery(String nativeSql, int[] bindPositions, boolean compatible) {
public NativeQuery(String nativeSql, int[] bindPositions, boolean insertReWriteCompatible) {
this.nativeSql = nativeSql;
this.bindPositions =
bindPositions == null || bindPositions.length == 0 ? NO_BINDS : bindPositions;
this.isBatchedReWriteCompatible = compatible;
this.isBatchedReWriteCompatible = insertReWriteCompatible;
}
/**
@@ -79,7 +79,7 @@ public static String bindName(int index) {
return index < BIND_NAMES.length ? BIND_NAMES[index] : "$" + index;
}
public static int bindsCount() {
public static int bindCount() {
return BIND_NAMES.length;
}
}
@@ -171,18 +171,12 @@
*/
String toString(int index);
/**
* Overwrite current parameters with parameters in the provided list.
* Current parameters are cleared.
* @param list of parameters to overwrite with.
*/
void addAll(ParameterList list);
/**
* Use this operation to append more parameters to the current list.
* @param list of parameters to append with.
* @throws SQLException fault raised if driver or back end throw an exception
*/
void appendAll(ParameterList list);
void appendAll(ParameterList list) throws SQLException ;
/**
* Returns the bound parameter values.
@@ -37,7 +37,7 @@
* @return list of native queries
*/
public static List<NativeQuery> parseJdbcSql(String query, boolean standardConformingStrings,
boolean withParameters, boolean splitStatements) {
boolean withParameters, boolean splitStatements, boolean isAutoCommit) {
if (!withParameters && !splitStatements) {
return Collections.singletonList(new NativeQuery(query));
}
@@ -52,6 +52,7 @@
List<NativeQuery> nativeQueries = null;
boolean isCurrentReWriteCompatible = false;
int afterValuesParens = 0;
boolean insertFound = false;
boolean whitespaceOnly = true;
for (int i = 0; i < aChars.length; ++i) {
@@ -130,44 +131,63 @@
bindPositions.clear();
}
nativeSql.setLength(0);
isCurrentReWriteCompatible = false;
}
break;
case 'i':
isCurrentReWriteCompatible = isCurrentReWriteCompatible || (Parser.parseInsertKeyword(aChars, i, false));
if (Parser.parseInsertKeyword(aChars, i)) {
if ( !insertFound && (nativeQueries == null ? true : nativeQueries.size() == 0)) {
isCurrentReWriteCompatible = true;
insertFound = true;
} else {
isCurrentReWriteCompatible = false;
}
i += 5;
}
break;
case 'I':
isCurrentReWriteCompatible = isCurrentReWriteCompatible || (Parser.parseInsertKeyword(aChars, i, true));
if (Parser.parseInsertKeyword(aChars, i)) {
if ( !insertFound && (nativeQueries == null ? true : nativeQueries.size() == 0)) {
isCurrentReWriteCompatible = true;
insertFound = true;
} else {
isCurrentReWriteCompatible = false;
}
i += 5;
}
break;
case 'r':
// exclude insert statements with returning keyword
isCurrentReWriteCompatible = isCurrentReWriteCompatible && (Parser.parseReturningKeyword(aChars, i, false) == false);
// exclude re-write of insert statements with returning keyword
isCurrentReWriteCompatible = isCurrentReWriteCompatible && (Parser.parseReturningKeyword(aChars, i) == false);
break;
case 'R':
// exclude insert statements with RETURNING keyword
isCurrentReWriteCompatible = isCurrentReWriteCompatible && (Parser.parseReturningKeyword(aChars, i, true) == false);
// exclude re-write of insert statements with RETURNING keyword
isCurrentReWriteCompatible = isCurrentReWriteCompatible && (Parser.parseReturningKeyword(aChars, i) == false);
break;
case 'v':
if (Parser.parseValuesKeyword(aChars, i, false)) {
if (Parser.parseValuesKeyword(aChars, i)) {
afterValuesParens = 0 ;
i += 5;
}
break;
case 'V':
if (Parser.parseValuesKeyword(aChars, i, true)) {
if (Parser.parseValuesKeyword(aChars, i)) {
afterValuesParens = 0 ;
i += 5;
}
break;
default:
break;
}
}
isCurrentReWriteCompatible = isCurrentReWriteCompatible && !isAutoCommit
&& (nativeQueries == null ? true : nativeQueries.size() == 0);
if (fragmentStart < aChars.length && !whitespaceOnly) {
nativeSql.append(aChars, fragmentStart, aChars.length - fragmentStart);
@@ -376,63 +396,63 @@ public static int parseBlockComment(final char[] query, int offset) {
}
/**
* Parse string to check presence of INSERT keyword.
* Parse string to check presence of INSERT keyword regardless of case.
* @param query char[] of the query statement
* @param offset position of query to start checking
* @param isUpper is the text expected to be upper/lower case
* @return boolean indicates presence of word
*/
public static boolean parseInsertKeyword(final char[] query, int offset, boolean isUpper) {
public static boolean parseInsertKeyword(final char[] query, int offset) {
if (query.length < (offset + 7)) {
return false;
}
if (isUpper && query[offset] == 'I' && query[offset + 1] == 'N' && query[offset + 2] == 'S' && query[offset + 3] == 'E' && query[offset + 4] == 'R' && query[offset + 5] == 'T' ) {
return true;
} else if ( !isUpper && query[offset] == 'i' && query[offset + 1] == 'n' && query[offset + 2] == 's' && query[offset + 3] == 'e' && query[offset + 4] == 'r' && query[offset + 5] == 't') {
return true;
}
return false;
return Character.toUpperCase(query[offset]) == 'I'
&& Character.toUpperCase(query[offset + 1]) == 'N'
&& Character.toUpperCase(query[offset + 2]) == 'S'
&& Character.toUpperCase(query[offset + 3]) == 'E'
&& Character.toUpperCase(query[offset + 4]) == 'R'
&& Character.toUpperCase(query[offset + 5]) == 'T';
}
/**
* Parse string to check presence of RETURNING keyword
* Parse string to check presence of RETURNING keyword regardless of case.
* @param query char[] of the query statement
* @param offset position of query to start checking
* @param isUpper is the text expected to be upper/lower case
* @return boolean indicates presence of word
*/
public static boolean parseReturningKeyword(final char[] query, int offset, boolean isUpper) {
public static boolean parseReturningKeyword(final char[] query, int offset) {
if (query.length < (offset + 9)) {
return false;
}
if ( isUpper && query[offset] == 'R' && query[offset + 1] == 'E' && query[offset + 2] == 'T' && query[offset + 3] == 'U' && query[offset + 4] == 'R' && query[offset + 5] == 'N' && query[offset + 6] == 'I' && query[offset + 7] == 'N' && query[offset + 8] == 'G' ) {
return true;
} else if ( !isUpper && query[offset] == 'r' && query[offset + 1] == 'e' && query[offset + 2] == 't' && query[offset + 3] == 'u' && query[offset + 4] == 'r' && query[offset + 5] == 'n' && query[offset + 6] == 'i' && query[offset + 7] == 'n' && query[offset + 8] == 'g' ) {
return true;
}
return false;
return Character.toUpperCase(query[offset]) == 'R'
&& Character.toUpperCase(query[offset + 1]) == 'E'
&& Character.toUpperCase(query[offset + 2]) == 'T'
&& Character.toUpperCase(query[offset + 3]) == 'U'
&& Character.toUpperCase(query[offset + 4]) == 'R'
&& Character.toUpperCase(query[offset + 5]) == 'N'
&& Character.toUpperCase(query[offset + 6]) == 'I'
&& Character.toUpperCase(query[offset + 7]) == 'N'
&& Character.toUpperCase(query[offset + 8]) == 'G';
}
/**
* Parse string to check presence of VALUES keyword
* Parse string to check presence of VALUES keyword regardless of case.
* @param query char[] of the query statement
* @param offset position of query to start checking
* @param isUpper is the text expected to be upper/lower case
* @return boolean indicates presence of word
*/
public static boolean parseValuesKeyword(final char[] query, int offset, boolean isUpper) {
public static boolean parseValuesKeyword(final char[] query, int offset) {
if (query.length < (offset + 6)) {
return false;
}
if ( isUpper && query[offset] == 'V' && query[offset + 1] == 'A' && query[offset + 2] == 'L' && query[offset + 3] == 'U' && query[offset + 4] == 'E' && query[offset + 5] == 'S' ) {
return true;
} else if ( !isUpper && query[offset] == 'v' && query[offset + 1] == 'a' && query[offset + 2] == 'l' && query[offset + 3] == 'u' && query[offset + 4] == 'e' && query[offset + 5] == 's' ) {
return true;
}
return false;
return Character.toUpperCase(query[offset]) == 'V'
&& Character.toUpperCase(query[offset + 1]) == 'A'
&& Character.toUpperCase(query[offset + 2]) == 'L'
&& Character.toUpperCase(query[offset + 3]) == 'U'
&& Character.toUpperCase(query[offset + 4]) == 'E'
&& Character.toUpperCase(query[offset + 5]) == 'S';
}
/**
@@ -55,6 +55,8 @@
/**
* Convenience to check if the Query has an insert statement
* that can be re-written.
* @return boolean value indication if the Query is an INSERT statement and
* compatible for re-write.
*/
boolean isStatementReWritableInsert();
@@ -110,7 +110,7 @@
* Execute a Query, passing results to a provided ResultHandler.
*
* @param query the query to execute; must be a query returned from calling
* {@link #createSimpleQuery(String)} or {@link #createParameterizedQuery(String)} on this
* {@link #createSimpleQuery(String, boolean)} or {@link #createParameterizedQuery(String, boolean)} on this
* QueryExecutor object.
* @param parameters the parameters for the query. Must be non-<code>null</code> if the query
* takes parameters. Must be a parameter object returned by
@@ -129,8 +129,8 @@ void execute(Query query, ParameterList parameters, ResultHandler handler, int m
* Execute several Query, passing results to a provided ResultHandler.
*
* @param queries the queries to execute; each must be a query returned from calling
* {@link #createSimpleQuery(String)} or {@link #createParameterizedQuery(String)} on this
* QueryExecutor object.
* {@link #createSimpleQuery(String, boolean)} or {@link #createParameterizedQuery(String, boolean)}
* on this QueryExecutor object.
* @param parameterLists the parameter lists for the queries. The parameter lists correspond 1:1
* to the queries passed in the <code>queries</code> array. Each must be non-
* <code>null</code> if the corresponding query takes parameters, and must be a parameter
@@ -163,9 +163,10 @@ void execute(Query[] queries, ParameterList[] parameterLists, BatchResultHandler
* ParameterList.
*
* @param sql the SQL for the query to create
* @param autocommit indicating when connection has autocommit enabled.
* @return a new Query object
*/
Query createSimpleQuery(String sql);
Query createSimpleQuery(String sql, boolean autocommit);
/**
* Create a parameterized Query object suitable for execution by this QueryExecutor. The provided
@@ -174,9 +175,10 @@ void execute(Query[] queries, ParameterList[] parameterLists, BatchResultHandler
* ParameterList.
*
* @param sql the SQL for the query to create, with '?' placeholders for parameters.
* @param autocommit indicating when connection has autocommit enabled.
* @return a new Query object
*/
Query createParameterizedQuery(String sql); // Parsed for parameter placeholders ('?')
Query createParameterizedQuery(String sql, boolean autocommit); // Parsed for parameter placeholders ('?')
/**
* Prior to attempting to retrieve notifications, we need to pull any recently received
@@ -65,7 +65,7 @@ public void handleCompletion() throws SQLException {
public static byte[][] run(ProtocolConnection protoConnection, String queryString,
boolean wantResults) throws SQLException {
QueryExecutor executor = protoConnection.getQueryExecutor();
Query query = executor.createSimpleQuery(queryString);
Query query = executor.createSimpleQuery(queryString, false);
SimpleResultHandler handler = new SimpleResultHandler();
int flags = QueryExecutor.QUERY_ONESHOT | QueryExecutor.QUERY_SUPPRESS_BEGIN;
@@ -183,30 +183,14 @@ public void setBinaryParameter(int index, byte[] value, int oid) {
private final Object[] paramValues;
@Override
public Object[] getValues() {
return this.paramValues;
}
@Override
/**
* Replace all parameters with new values in provided list.
*/
public void addAll(ParameterList list) {
if (list instanceof SimpleParameterList ) {
// only v2.SimpleParameterList is compatible with this type
SimpleParameterList spl = (SimpleParameterList) list;
Arrays.fill(paramValues, null);
System.arraycopy(spl.getValues(), 0, paramValues, 0,
spl.getInParameterCount());
}
}
@Override
/**
* Append parameters to the list.
*/
public void appendAll(ParameterList list) {
public void appendAll(ParameterList list) throws SQLException {
if (list instanceof SimpleParameterList ) {
// only v2.SimpleParameterList is compatible with this type
SimpleParameterList spl = (SimpleParameterList) list;
@@ -48,11 +48,11 @@ public QueryExecutorImpl(ProtocolConnectionImpl protoConnection, PGStream pgStre
// Query parsing
//
public Query createSimpleQuery(String sql) {
public Query createSimpleQuery(String sql, boolean autocommit) {
return new V2Query(sql, false, protoConnection);
}
public Query createParameterizedQuery(String sql) {
public Query createParameterizedQuery(String sql, boolean autocommit) {
return new V2Query(sql, true, protoConnection);
}
@@ -120,7 +120,7 @@ public void handleCompletion() throws SQLException {
try {
// Create and issue a dummy query to use the existing prefix infrastructure
V2Query query = (V2Query) createSimpleQuery("");
V2Query query = (V2Query) createSimpleQuery("", false);
SimpleParameterList params = (SimpleParameterList) query.createParameterList();
sendQuery(query, params, "BEGIN");
processResults(query, handler, 0, 0);
Oops, something went wrong.

0 comments on commit e591577

Please sign in to comment.