bug修改

This commit is contained in:
wubeidou
2023-11-30 11:29:43 +08:00
parent 20658c79ca
commit 214a52a2f8
18 changed files with 237 additions and 50 deletions

View File

@ -142,6 +142,10 @@ Connection conn = DriverManager.getConnection(url);
The driver supports the V3 frontend/backend protocols. The V3 protocol was introduced in 7.4 and The driver supports the V3 frontend/backend protocols. The V3 protocol was introduced in 7.4 and
the driver will by default try to connect using the V3 protocol. the driver will by default try to connect using the V3 protocol.
* **quoteReturningIdentifiers** = boolean
By default we double quote returning identifiers. Some ORM's alraedy allows quote them. Switch allows them to turn this off.
* **loggerLevel** = String * **loggerLevel** = String

View File

@ -61,6 +61,17 @@ public enum PGProperty {
"Force use of a particular protocol version when connecting, currently only version 3 is supported.", "Force use of a particular protocol version when connecting, currently only version 3 is supported.",
false, "3"), false, "3"),
/**
* Quote returning columns.
* There are some ORM's that quote everything, including returning columns
* If we quote them, then we end up sending ""colname"" to the backend
* which will not be found
*/
QUOTE_RETURNING_IDENTIFIERS("quoteReturningIdentifiers",
"true",
"Quote identifiers provided in returning array",
false),
/** /**
* <p>Logger level of the driver. Allowed values: {@code OFF}, {@code DEBUG} or {@code TRACE}.</p> * <p>Logger level of the driver. Allowed values: {@code OFF}, {@code DEBUG} or {@code TRACE}.</p>
* *

View File

@ -7,6 +7,8 @@ package org.postgresql.core;
import org.postgresql.util.CanEstimateSize; import org.postgresql.util.CanEstimateSize;
import java.util.ArrayList;
/** /**
* Stores information on the parsed JDBC query. It is used to cut parsing overhead when executing * Stores information on the parsed JDBC query. It is used to cut parsing overhead when executing
* the same query through {@link java.sql.Connection#prepareStatement(String)}. * the same query through {@link java.sql.Connection#prepareStatement(String)}.
@ -20,7 +22,8 @@ public class CachedQuery implements CanEstimateSize {
public final boolean isFunction; public final boolean isFunction;
public final boolean isACompatibilityFunction; public final boolean isACompatibilityFunction;
private int executeCount; private int executeCount;
// record queries after rewrite
private final ArrayList<Query> rewriteQueries = new ArrayList<>();
public CachedQuery(Object key, Query query, boolean isFunction, boolean isACompatibilityFunction) { public CachedQuery(Object key, Query query, boolean isFunction, boolean isACompatibilityFunction) {
assert key instanceof String || key instanceof CanEstimateSize assert key instanceof String || key instanceof CanEstimateSize
: "CachedQuery.key should either be String or implement CanEstimateSize." : "CachedQuery.key should either be String or implement CanEstimateSize."
@ -66,6 +69,42 @@ public class CachedQuery implements CanEstimateSize {
+ 100L /* entry in hash map, CachedQuery wrapper, etc */; + 100L /* entry in hash map, CachedQuery wrapper, etc */;
} }
/**
* add rewrite query to list
*
* @param query the query to add
*/
public void addRewriteQueries(Query query) {
rewriteQueries.add(query);
}
/**
* get rewrite queries list
*
* @return rewrite queries list
*/
public ArrayList<Query> getRewriteQueries() {
return rewriteQueries;
}
/**
* check if rewrite queries list empty
*
* @return check result
*/
public boolean isRewriteQueriesEmpty() {
return rewriteQueries.isEmpty();
}
/**
* clear rewrite queries
*/
public void clearRewriteQueries() {
if (rewriteQueries.isEmpty()) {
return;
}
rewriteQueries.clear();
}
@Override @Override
public String toString() { public String toString() {
return "CachedQuery{" return "CachedQuery{"

View File

@ -65,7 +65,8 @@ class CachedQueryCreateAction implements LruCache.CreateAction<Object, CachedQue
List<NativeQuery> queries = Parser.parseJdbcSql(parsedSql, List<NativeQuery> queries = Parser.parseJdbcSql(parsedSql,
queryExecutor.getStandardConformingStrings(), isParameterized, splitStatements, queryExecutor.getStandardConformingStrings(), isParameterized, splitStatements,
queryExecutor.isReWriteBatchedInsertsEnabled(), returningColumns); queryExecutor.isReWriteBatchedInsertsEnabled(), queryExecutor.getQuoteReturningIdentifiers(),
returningColumns);
Query query = queryExecutor.wrap(queries); Query query = queryExecutor.wrap(queries);
return new CachedQuery(key, query, isFunction, isACompatibilityFunction); return new CachedQuery(key, query, isFunction, isACompatibilityFunction);

View File

@ -51,6 +51,7 @@ public class Parser {
* @param withParameters whether to replace ?, ? with $1, $2, etc * @param withParameters whether to replace ?, ? with $1, $2, etc
* @param splitStatements whether to split statements by semicolon * @param splitStatements whether to split statements by semicolon
* @param isBatchedReWriteConfigured whether re-write optimization is enabled * @param isBatchedReWriteConfigured whether re-write optimization is enabled
* @param isQuotedReturningIdentifiers whether to quote identifiers returned using returning clause
* @param returningColumnNames for simple insert, update, delete add returning with given column names * @param returningColumnNames for simple insert, update, delete add returning with given column names
* @return list of native queries * @return list of native queries
* @throws SQLException if unable to add returning clause (invalid column names) * @throws SQLException if unable to add returning clause (invalid column names)
@ -58,6 +59,7 @@ public class Parser {
public static List<NativeQuery> parseJdbcSql(String query, boolean standardConformingStrings, public static List<NativeQuery> parseJdbcSql(String query, boolean standardConformingStrings,
boolean withParameters, boolean splitStatements, boolean withParameters, boolean splitStatements,
boolean isBatchedReWriteConfigured, boolean isBatchedReWriteConfigured,
boolean isQuotedReturningIdentifiers,
String... returningColumnNames) throws SQLException { String... returningColumnNames) throws SQLException {
int numOfOverSymble = 0; int numOfOverSymble = 0;
if(startWithComment(query)) { if(startWithComment(query)) {
@ -151,8 +153,8 @@ public class Parser {
} }
fragmentStart = i + 1; fragmentStart = i + 1;
if (nativeSql.length() > 0) { if (nativeSql.length() > 0) {
if (addReturning( if (addReturning(nativeSql, currentCommandType, returningColumnNames, isReturningPresent,
nativeSql, currentCommandType, returningColumnNames, isReturningPresent)) { isQuotedReturningIdentifiers)) {
isReturningPresent = true; isReturningPresent = true;
} }
@ -256,7 +258,8 @@ public class Parser {
} }
fragmentStart = i + 1; fragmentStart = i + 1;
if (nativeSql.length() > 0) { if (nativeSql.length() > 0) {
if (addReturning(nativeSql, currentCommandType, returningColumnNames, isReturningPresent)) { if (addReturning(nativeSql, currentCommandType, returningColumnNames, isReturningPresent,
isQuotedReturningIdentifiers)) {
isReturningPresent = true; isReturningPresent = true;
} }
@ -378,7 +381,7 @@ public class Parser {
if (inParen != 0 || aChar == ')') { if (inParen != 0 || aChar == ')') {
// RETURNING and VALUES cannot be present in braces // RETURNING and VALUES cannot be present in braces
} else if (wordLength == 9 && parseReturningKeyword(aChars, keywordStart)) { } else if (wordLength == 9 && parseReturningKeyword(aChars, keywordStart)) {
isReturningPresent = true; isReturningPresent = true;
} else if (wordLength == 6 && parseValuesKeyword(aChars, keywordStart)) { } else if (wordLength == 6 && parseValuesKeyword(aChars, keywordStart)) {
isValuesFound = true; isValuesFound = true;
} }
@ -416,7 +419,8 @@ public class Parser {
return nativeQueries != null ? nativeQueries : Collections.<NativeQuery>emptyList(); return nativeQueries != null ? nativeQueries : Collections.<NativeQuery>emptyList();
} }
if (addReturning(nativeSql, currentCommandType, returningColumnNames, isReturningPresent)) { if (addReturning(nativeSql, currentCommandType, returningColumnNames, isReturningPresent,
isQuotedReturningIdentifiers)) {
isReturningPresent = true; isReturningPresent = true;
} }
@ -525,8 +529,9 @@ public class Parser {
return null; return null;
} }
private static boolean addReturning(StringBuilder nativeSql, SqlCommandType currentCommandType, private static boolean addReturning(StringBuilder nativeSql, SqlCommandType currentCommandType,
String[] returningColumnNames, boolean isReturningPresent) throws SQLException { String[] returningColumnNames, boolean isReturningPresent, boolean isQuotedReturningIdentifiers)
throws SQLException {
if (isReturningPresent || returningColumnNames.length == 0) { if (isReturningPresent || returningColumnNames.length == 0) {
return false; return false;
} }
@ -547,7 +552,11 @@ public class Parser {
if (col > 0) { if (col > 0) {
nativeSql.append(", "); nativeSql.append(", ");
} }
Utils.escapeIdentifier(nativeSql, columnName); if (isQuotedReturningIdentifiers) {
Utils.escapeIdentifier(nativeSql, columnName);
} else {
nativeSql.append(columnName);
}
} }
return true; return true;
} }
@ -670,6 +679,7 @@ public class Parser {
case "PROCEDURE": case "PROCEDURE":
case "FUNCTION": case "FUNCTION":
case "DECLARE": case "DECLARE":
case "TRIGGER":
return true; return true;
case "CREATE": case "CREATE":
if (i == 0) { if (i == 0) {
@ -897,6 +907,7 @@ public class Parser {
&& (query[offset + 8] | 32) == 'g'; && (query[offset + 8] | 32) == 'g';
} }
/** /**
* Parse string to check presence of SELECT keyword regardless of case. * Parse string to check presence of SELECT keyword regardless of case.
* *

View File

@ -445,6 +445,12 @@ public interface QueryExecutor extends TypeTransferModeRegistry {
boolean getStandardConformingStrings(); boolean getStandardConformingStrings();
/** /**
* get quote identifier
*
* @return true if we are going to quote identifier provided in the returning array default is true
*/
boolean getQuoteReturningIdentifiers();
/**
* Returns backend timezone in java format. * Returns backend timezone in java format.
* @return backend timezone in java format. * @return backend timezone in java format.
*/ */

View File

@ -41,6 +41,7 @@ public abstract class QueryExecutorBase implements QueryExecutor {
private TransactionState transactionState; private TransactionState transactionState;
private final boolean reWriteBatchedInserts; private final boolean reWriteBatchedInserts;
private final boolean columnSanitiserDisabled; private final boolean columnSanitiserDisabled;
private final boolean isQuotedReturningIdentifiers;
private final PreferQueryMode preferQueryMode; private final PreferQueryMode preferQueryMode;
private AutoSave autoSave; private AutoSave autoSave;
private boolean flushCacheOnDeallocate = true; private boolean flushCacheOnDeallocate = true;
@ -64,22 +65,29 @@ public abstract class QueryExecutorBase implements QueryExecutor {
this.cancelSignalTimeout = cancelSignalTimeout; this.cancelSignalTimeout = cancelSignalTimeout;
this.reWriteBatchedInserts = PGProperty.REWRITE_BATCHED_INSERTS.getBoolean(info); this.reWriteBatchedInserts = PGProperty.REWRITE_BATCHED_INSERTS.getBoolean(info);
this.columnSanitiserDisabled = PGProperty.DISABLE_COLUMN_SANITISER.getBoolean(info); this.columnSanitiserDisabled = PGProperty.DISABLE_COLUMN_SANITISER.getBoolean(info);
this.isQuotedReturningIdentifiers = PGProperty.QUOTE_RETURNING_IDENTIFIERS.getBoolean(info);
String preferMode = PGProperty.PREFER_QUERY_MODE.get(info); String preferMode = PGProperty.PREFER_QUERY_MODE.get(info);
this.preferQueryMode = PreferQueryMode.of(preferMode); this.preferQueryMode = PreferQueryMode.of(preferMode);
this.autoSave = AutoSave.of(PGProperty.AUTOSAVE.get(info)); this.autoSave = AutoSave.of(PGProperty.AUTOSAVE.get(info));
this.cachedQueryCreateAction = new CachedQueryCreateAction(this); this.cachedQueryCreateAction = new CachedQueryCreateAction(this);
this.props = info; this.props = info;
statementCache = new LruCache<Object, CachedQuery>( statementCache = new LruCache<Object, CachedQuery>(
Math.max(0, PGProperty.PREPARED_STATEMENT_CACHE_QUERIES.getInt(info)), Math.max(0, PGProperty.PREPARED_STATEMENT_CACHE_QUERIES.getInt(info)),
Math.max(0, PGProperty.PREPARED_STATEMENT_CACHE_SIZE_MIB.getInt(info) * 1024 * 1024), Math.max(0, PGProperty.PREPARED_STATEMENT_CACHE_SIZE_MIB.getInt(info) * 1024 * 1024),
false, false,
cachedQueryCreateAction, cachedQueryCreateAction,
new LruCache.EvictAction<CachedQuery>() { new LruCache.EvictAction<CachedQuery>() {
@Override @Override
public void evict(CachedQuery cachedQuery) throws SQLException { public void evict(CachedQuery cachedQuery) throws SQLException {
cachedQuery.query.close(); cachedQuery.query.close();
} if (!cachedQuery.isRewriteQueriesEmpty()) {
}); for (Query query : cachedQuery.getRewriteQueries()) {
query.close();
}
cachedQuery.clearRewriteQueries();
}
}
});
} }
protected abstract void sendCloseMessage() throws IOException; protected abstract void sendCloseMessage() throws IOException;
@ -261,6 +269,11 @@ public abstract class QueryExecutorBase implements QueryExecutor {
return standardConformingStrings; return standardConformingStrings;
} }
@Override
public boolean getQuoteReturningIdentifiers() {
return isQuotedReturningIdentifiers;
}
@Override @Override
public synchronized TransactionState getTransactionState() { public synchronized TransactionState getTransactionState() {
return transactionState; return transactionState;

View File

@ -5,10 +5,10 @@
package org.postgresql.core.v3; package org.postgresql.core.v3;
import org.postgresql.core.CachedQuery;
import org.postgresql.core.NativeQuery; import org.postgresql.core.NativeQuery;
import org.postgresql.core.ParameterList; import org.postgresql.core.ParameterList;
/** /**
* Purpose of this object is to support batched query re write behaviour. Responsibility for * Purpose of this object is to support batched query re write behaviour. Responsibility for
* tracking the batch size and implement the clean up of the query fragments after the batch execute * tracking the batch size and implement the clean up of the query fragments after the batch execute
@ -27,6 +27,8 @@ public class BatchedQuery extends SimpleQuery {
private final int batchSize; private final int batchSize;
private BatchedQuery[] blocks; private BatchedQuery[] blocks;
// record the origin query of the rewrite query
private CachedQuery originalPrepareQuery;
public BatchedQuery(NativeQuery query, TypeTransferModeRegistry transferModeRegistry, public BatchedQuery(NativeQuery query, TypeTransferModeRegistry transferModeRegistry,
int valuesBraceOpenPosition, int valuesBraceOpenPosition,
int valuesBraceClosePosition, boolean sanitiserDisabled) { int valuesBraceClosePosition, boolean sanitiserDisabled) {
@ -85,6 +87,24 @@ public class BatchedQuery extends SimpleQuery {
return sql; return sql;
} }
/**
* get original prepareQuery
*
* @return the originalPrepareQuery
*/
public CachedQuery getOriginalPrepareQuery() {
return originalPrepareQuery;
}
/**
* set original prepareQuery
*
* @param originalPrepareQuery the originalPrepareQuery to be set
*/
public void setOriginalPrepareQuery(CachedQuery originalPrepareQuery) {
this.originalPrepareQuery = originalPrepareQuery;
}
private String buildNativeSql(ParameterList params) { private String buildNativeSql(ParameterList params) {
String buildSql = null; String buildSql = null;
// dynamically build sql with parameters for batches // dynamically build sql with parameters for batches

View File

@ -10,6 +10,7 @@ import org.postgresql.PGProperty;
import org.postgresql.copy.CopyIn; import org.postgresql.copy.CopyIn;
import org.postgresql.copy.CopyOperation; import org.postgresql.copy.CopyOperation;
import org.postgresql.copy.CopyOut; import org.postgresql.copy.CopyOut;
import org.postgresql.core.CachedQuery;
import org.postgresql.core.CommandCompleteParser; import org.postgresql.core.CommandCompleteParser;
import org.postgresql.core.Encoding; import org.postgresql.core.Encoding;
import org.postgresql.core.EncodingPredictor; import org.postgresql.core.EncodingPredictor;
@ -283,7 +284,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
public Query createSimpleQuery(String sql) throws SQLException { public Query createSimpleQuery(String sql) throws SQLException {
List<NativeQuery> queries = Parser.parseJdbcSql(sql, List<NativeQuery> queries = Parser.parseJdbcSql(sql,
getStandardConformingStrings(), false, true, getStandardConformingStrings(), false, true,
isReWriteBatchedInsertsEnabled()); isReWriteBatchedInsertsEnabled(), getQuoteReturningIdentifiers());
return wrap(queries); return wrap(queries);
} }
@ -1553,6 +1554,17 @@ public class QueryExecutorImpl extends QueryExecutorBase {
pendingDescribePortalQueue.add(sync); pendingDescribePortalQueue.add(sync);
} }
private void checkAndUpdateRewriteQueries(SimpleQuery query) {
if (!(query instanceof BatchedQuery)) {
return;
}
BatchedQuery batchedQuery = (BatchedQuery) query;
CachedQuery originalPrepareQuery = batchedQuery.getOriginalPrepareQuery();
if (originalPrepareQuery == null) {
return;
}
originalPrepareQuery.addRewriteQueries(batchedQuery);
}
private void sendParse(SimpleQuery query, SimpleParameterList params, boolean oneShot) private void sendParse(SimpleQuery query, SimpleParameterList params, boolean oneShot)
throws IOException { throws IOException {
// Already parsed, or we have a Parse pending and the types are right? // Already parsed, or we have a Parse pending and the types are right?
@ -1582,6 +1594,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
query.setStatementName(statementName, deallocateEpoch, getClientEncoding()); query.setStatementName(statementName, deallocateEpoch, getClientEncoding());
query.setPrepareTypes(typeOIDs); query.setPrepareTypes(typeOIDs);
registerParsedQuery(query, statementName); registerParsedQuery(query, statementName);
checkAndUpdateRewriteQueries(query);
} }
byte[] encodedStatementName = query.getEncodedStatementName(); byte[] encodedStatementName = query.getEncodedStatementName();

View File

@ -322,6 +322,22 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
} }
/** /**
* @return quoteReturningIdentifiers
* @see PGProperty#QUOTE_RETURNING_IDENTIFIERS
*/
public boolean getQuoteReturningIdentifiers() {
return PGProperty.QUOTE_RETURNING_IDENTIFIERS.getBoolean(properties);
}
/**
* @param isQuotedIdentifiers indicate whether to quote identifiers
* @see PGProperty#QUOTE_RETURNING_IDENTIFIERS
*/
public void setQuoteReturningIdentifiers(boolean isQuotedIdentifiers) {
PGProperty.QUOTE_RETURNING_IDENTIFIERS.set(properties, isQuotedIdentifiers);
}
/**
* @return receive buffer size * @return receive buffer size
* @see PGProperty#RECEIVE_BUFFER_SIZE * @see PGProperty#RECEIVE_BUFFER_SIZE
*/ */

View File

@ -243,7 +243,7 @@ public class ClientLogic {
//Replace the query syntax from jdbc syntax with ? for bind parameters to $1 $2 ... $n //Replace the query syntax from jdbc syntax with ? for bind parameters to $1 $2 ... $n
List<NativeQuery> queries; List<NativeQuery> queries;
try { try {
queries = Parser.parseJdbcSql(query, true, true, true, true); queries = Parser.parseJdbcSql(query, true, true, true, true, true);
} catch (SQLException e) { } catch (SQLException e) {
throw new ClientLogicException(ERROR_PARSER_FAILURE, ERROR_TEXT_PARSER_FAILURE, true); throw new ClientLogicException(ERROR_PARSER_FAILURE, ERROR_TEXT_PARSER_FAILURE, true);
} }

View File

@ -1725,6 +1725,7 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
} }
// Find appropriate batch for block count. // Find appropriate batch for block count.
BatchedQuery bq = originalQuery.deriveForMultiBatch(valueBlock); BatchedQuery bq = originalQuery.deriveForMultiBatch(valueBlock);
bq.setOriginalPrepareQuery(preparedQuery);
ParameterList newPl = bq.createParameterList(); ParameterList newPl = bq.createParameterList();
for (int j = 0; j < valueBlock; j++) { for (int j = 0; j < valueBlock; j++) {
ParameterList pl = batchParameters.get(offset++); ParameterList pl = batchParameters.get(offset++);

View File

@ -155,7 +155,7 @@ public class ParserTest {
"insert test(id, name) select 1, 'value' as RETURNING from test2"; "insert test(id, name) select 1, 'value' as RETURNING from test2";
List<NativeQuery> qry = List<NativeQuery> qry =
Parser.parseJdbcSql( Parser.parseJdbcSql(
query, true, true, true, true); query, true, true, true, true, true);
boolean returningKeywordPresent = qry.get(0).command.isReturningKeywordPresent(); boolean returningKeywordPresent = qry.get(0).command.isReturningKeywordPresent();
Assert.assertFalse("Query does not have returning clause " + query, returningKeywordPresent); Assert.assertFalse("Query does not have returning clause " + query, returningKeywordPresent);
} }
@ -166,7 +166,7 @@ public class ParserTest {
"insert test(id, name) select 1, 'value' from test2 RETURNING id"; "insert test(id, name) select 1, 'value' from test2 RETURNING id";
List<NativeQuery> qry = List<NativeQuery> qry =
Parser.parseJdbcSql( Parser.parseJdbcSql(
query, true, true, true, true); query, true, true, true, true, true);
boolean returningKeywordPresent = qry.get(0).command.isReturningKeywordPresent(); boolean returningKeywordPresent = qry.get(0).command.isReturningKeywordPresent();
Assert.assertTrue("Query has a returning clause " + query, returningKeywordPresent); Assert.assertTrue("Query has a returning clause " + query, returningKeywordPresent);
} }
@ -177,7 +177,7 @@ public class ParserTest {
"with x as (insert into mytab(x) values(1) returning x) insert test(id, name) select 1, 'value' from test2"; "with x as (insert into mytab(x) values(1) returning x) insert test(id, name) select 1, 'value' from test2";
List<NativeQuery> qry = List<NativeQuery> qry =
Parser.parseJdbcSql( Parser.parseJdbcSql(
query, true, true, true, true); query, true, true, true, true, true);
boolean returningKeywordPresent = qry.get(0).command.isReturningKeywordPresent(); boolean returningKeywordPresent = qry.get(0).command.isReturningKeywordPresent();
Assert.assertFalse("There's no top-level <<returning>> clause " + query, returningKeywordPresent); Assert.assertFalse("There's no top-level <<returning>> clause " + query, returningKeywordPresent);
} }
@ -185,7 +185,7 @@ public class ParserTest {
@Test @Test
public void insertBatchedReWriteOnConflict() throws SQLException { public void insertBatchedReWriteOnConflict() throws SQLException {
String query = "insert into test(id, name) values (:id,:name) ON CONFLICT (id) DO NOTHING"; String query = "insert into test(id, name) values (:id,:name) ON CONFLICT (id) DO NOTHING";
List<NativeQuery> qry = Parser.parseJdbcSql(query, true, true, true, true); List<NativeQuery> qry = Parser.parseJdbcSql(query, true, true, true, true, true);
SqlCommand command = qry.get(0).getCommand(); SqlCommand command = qry.get(0).getCommand();
Assert.assertEquals(34, command.getBatchRewriteValuesBraceOpenPosition()); Assert.assertEquals(34, command.getBatchRewriteValuesBraceOpenPosition());
Assert.assertEquals(44, command.getBatchRewriteValuesBraceClosePosition()); Assert.assertEquals(44, command.getBatchRewriteValuesBraceClosePosition());
@ -194,7 +194,7 @@ public class ParserTest {
@Test @Test
public void insertBatchedReWriteOnConflictUpdateBind() throws SQLException { public void insertBatchedReWriteOnConflictUpdateBind() throws SQLException {
String query = "insert into test(id, name) values (?,?) ON CONFLICT (id) UPDATE SET name=?"; String query = "insert into test(id, name) values (?,?) ON CONFLICT (id) UPDATE SET name=?";
List<NativeQuery> qry = Parser.parseJdbcSql(query, true, true, true, true); List<NativeQuery> qry = Parser.parseJdbcSql(query, true, true, true, true, true);
SqlCommand command = qry.get(0).getCommand(); SqlCommand command = qry.get(0).getCommand();
Assert.assertFalse("update set name=? is NOT compatible with insert rewrite", command.isBatchedReWriteCompatible()); Assert.assertFalse("update set name=? is NOT compatible with insert rewrite", command.isBatchedReWriteCompatible());
} }
@ -202,7 +202,7 @@ public class ParserTest {
@Test @Test
public void insertBatchedReWriteOnConflictUpdateConstant() throws SQLException { public void insertBatchedReWriteOnConflictUpdateConstant() throws SQLException {
String query = "insert into test(id, name) values (?,?) ON CONFLICT (id) UPDATE SET name='default'"; String query = "insert into test(id, name) values (?,?) ON CONFLICT (id) UPDATE SET name='default'";
List<NativeQuery> qry = Parser.parseJdbcSql(query, true, true, true, true); List<NativeQuery> qry = Parser.parseJdbcSql(query, true, true, true, true, true);
SqlCommand command = qry.get(0).getCommand(); SqlCommand command = qry.get(0).getCommand();
Assert.assertTrue("update set name='default' is compatible with insert rewrite", command.isBatchedReWriteCompatible()); Assert.assertTrue("update set name='default' is compatible with insert rewrite", command.isBatchedReWriteCompatible());
} }
@ -211,7 +211,7 @@ public class ParserTest {
public void insertMultiInsert() throws SQLException { public void insertMultiInsert() throws SQLException {
String query = String query =
"insert into test(id, name) values (:id,:name),(:id,:name) ON CONFLICT (id) DO NOTHING"; "insert into test(id, name) values (:id,:name),(:id,:name) ON CONFLICT (id) DO NOTHING";
List<NativeQuery> qry = Parser.parseJdbcSql(query, true, true, true, true); List<NativeQuery> qry = Parser.parseJdbcSql(query, true, true, true, true, true);
SqlCommand command = qry.get(0).getCommand(); SqlCommand command = qry.get(0).getCommand();
Assert.assertEquals(34, command.getBatchRewriteValuesBraceOpenPosition()); Assert.assertEquals(34, command.getBatchRewriteValuesBraceOpenPosition());
Assert.assertEquals(56, command.getBatchRewriteValuesBraceClosePosition()); Assert.assertEquals(56, command.getBatchRewriteValuesBraceClosePosition());
@ -241,7 +241,7 @@ public class ParserTest {
for (String mySql: sqlTests) { for (String mySql: sqlTests) {
List<NativeQuery> queries = Parser.parseJdbcSql(mySql, List<NativeQuery> queries = Parser.parseJdbcSql(mySql,
false,false, false,false,
true, false, new String[0]); true, false, true, new String[0]);
assertEquals(1, queries.size()); assertEquals(1, queries.size());
} }
} }

View File

@ -53,7 +53,7 @@ public class ReturningParserTest {
String query = String query =
"insert into\"prep\"(a, " + prefix + columnName + suffix + ")values(1,2)" + prefix "insert into\"prep\"(a, " + prefix + columnName + suffix + ")values(1,2)" + prefix
+ returning + suffix; + returning + suffix;
List<NativeQuery> qry = Parser.parseJdbcSql(query, true, true, true, true); List<NativeQuery> qry = Parser.parseJdbcSql(query, true, true, true, true, true);
boolean returningKeywordPresent = qry.get(0).command.isReturningKeywordPresent(); boolean returningKeywordPresent = qry.get(0).command.isReturningKeywordPresent();
boolean expectedReturning = this.returning.equalsIgnoreCase("returning") boolean expectedReturning = this.returning.equalsIgnoreCase("returning")

View File

@ -387,6 +387,7 @@ public class TestUtil {
props.put(PGProperty.PREFER_QUERY_MODE.getName(), value); props.put(PGProperty.PREFER_QUERY_MODE.getName(), value);
} }
} }
PGProperty.QUOTE_RETURNING_IDENTIFIERS.set(props, false);
// Enable Base4 tests to override host,port,database // Enable Base4 tests to override host,port,database
String hostport = props.getProperty(SERVER_HOST_PORT_PROP, getServer() + ":" + getPort()); String hostport = props.getProperty(SERVER_HOST_PORT_PROP, getServer() + ":" + getPort());
String database = props.getProperty(DATABASE_PROP, getDatabase()); String database = props.getProperty(DATABASE_PROP, getDatabase());

View File

@ -95,16 +95,16 @@ public class CompositeQueryParseTest {
@Test @Test
public void testHasReturning() throws SQLException { public void testHasReturning() throws SQLException {
List<NativeQuery> queries = Parser.parseJdbcSql("insert into foo (a,b,c) values (?,?,?) RetuRning a", true, true, false, List<NativeQuery> queries = Parser.parseJdbcSql("insert into foo (a,b,c) values (?,?,?) RetuRning a", true, true, false,
true); true, true);
NativeQuery query = queries.get(0); NativeQuery query = queries.get(0);
assertTrue("The parser should find the word returning", query.command.isReturningKeywordPresent()); assertTrue("The parser should find the word returning", query.command.isReturningKeywordPresent());
queries = Parser.parseJdbcSql("insert into foo (a,b,c) values (?,?,?)", true, true, false, true); queries = Parser.parseJdbcSql("insert into foo (a,b,c) values (?,?,?)", true, true, false, true, true);
query = queries.get(0); query = queries.get(0);
assertFalse("The parser should not find the word returning", query.command.isReturningKeywordPresent()); assertFalse("The parser should not find the word returning", query.command.isReturningKeywordPresent());
queries = Parser.parseJdbcSql("insert into foo (a,b,c) values ('returning',?,?)", true, true, false, queries = Parser.parseJdbcSql("insert into foo (a,b,c) values ('returning',?,?)", true, true, false,
true); true, true);
query = queries.get(0); query = queries.get(0);
assertFalse("The parser should not find the word returning as it is in quotes ", query.command.isReturningKeywordPresent()); assertFalse("The parser should not find the word returning as it is in quotes ", query.command.isReturningKeywordPresent());
} }
@ -112,7 +112,7 @@ public class CompositeQueryParseTest {
@Test @Test
public void testSelect() throws SQLException { public void testSelect() throws SQLException {
List<NativeQuery> queries; List<NativeQuery> queries;
queries = Parser.parseJdbcSql("select 1 as returning from (update table)", true, true, false, true); queries = Parser.parseJdbcSql("select 1 as returning from (update table)", true, true, false, true, true);
NativeQuery query = queries.get(0); NativeQuery query = queries.get(0);
assertEquals("This is a select ", SqlCommandType.SELECT, query.command.getType()); assertEquals("This is a select ", SqlCommandType.SELECT, query.command.getType());
assertTrue("Returning is OK here as it is not an insert command ", query.command.isReturningKeywordPresent()); assertTrue("Returning is OK here as it is not an insert command ", query.command.isReturningKeywordPresent());
@ -121,7 +121,7 @@ public class CompositeQueryParseTest {
@Test @Test
public void testDelete() throws SQLException { public void testDelete() throws SQLException {
List<NativeQuery> queries = Parser.parseJdbcSql("DeLeTe from foo where a=1", true, true, false, List<NativeQuery> queries = Parser.parseJdbcSql("DeLeTe from foo where a=1", true, true, false,
true); true, true);
NativeQuery query = queries.get(0); NativeQuery query = queries.get(0);
assertEquals("This is a delete command", SqlCommandType.DELETE, query.command.getType()); assertEquals("This is a delete command", SqlCommandType.DELETE, query.command.getType());
} }
@ -130,7 +130,7 @@ public class CompositeQueryParseTest {
public void testMultiQueryWithBind() throws SQLException { public void testMultiQueryWithBind() throws SQLException {
// braces around (42) are required to puzzle the parser // braces around (42) are required to puzzle the parser
String sql = "INSERT INTO inttable(a) VALUES (?);SELECT (42)"; String sql = "INSERT INTO inttable(a) VALUES (?);SELECT (42)";
List<NativeQuery> queries = Parser.parseJdbcSql(sql, true, true, true,true); List<NativeQuery> queries = Parser.parseJdbcSql(sql, true, true, true,true, true);
NativeQuery query = queries.get(0); NativeQuery query = queries.get(0);
assertEquals("query(0) of " + sql, assertEquals("query(0) of " + sql,
"INSERT: INSERT INTO inttable(a) VALUES ($1)", "INSERT: INSERT INTO inttable(a) VALUES ($1)",
@ -143,7 +143,7 @@ public class CompositeQueryParseTest {
@Test @Test
public void testMove() throws SQLException { public void testMove() throws SQLException {
List<NativeQuery> queries = Parser.parseJdbcSql("MoVe NEXT FROM FOO", true, true, false, true); List<NativeQuery> queries = Parser.parseJdbcSql("MoVe NEXT FROM FOO", true, true, false, true, true);
NativeQuery query = queries.get(0); NativeQuery query = queries.get(0);
assertEquals("This is a move command", SqlCommandType.MOVE, query.command.getType()); assertEquals("This is a move command", SqlCommandType.MOVE, query.command.getType());
} }
@ -152,7 +152,7 @@ public class CompositeQueryParseTest {
public void testUpdate() throws SQLException { public void testUpdate() throws SQLException {
List<NativeQuery> queries; List<NativeQuery> queries;
NativeQuery query; NativeQuery query;
queries = Parser.parseJdbcSql("update foo set (a=?,b=?,c=?)", true, true, false, true); queries = Parser.parseJdbcSql("update foo set (a=?,b=?,c=?)", true, true, false, true, true);
query = queries.get(0); query = queries.get(0);
assertEquals("This is an UPDATE command", SqlCommandType.UPDATE, query.command.getType()); assertEquals("This is an UPDATE command", SqlCommandType.UPDATE, query.command.getType());
} }
@ -160,11 +160,11 @@ public class CompositeQueryParseTest {
@Test @Test
public void testInsert() throws SQLException { public void testInsert() throws SQLException {
List<NativeQuery> queries = Parser.parseJdbcSql("InSeRt into foo (a,b,c) values (?,?,?) returning a", true, true, false, List<NativeQuery> queries = Parser.parseJdbcSql("InSeRt into foo (a,b,c) values (?,?,?) returning a", true, true, false,
true); true, true);
NativeQuery query = queries.get(0); NativeQuery query = queries.get(0);
assertEquals("This is an INSERT command", SqlCommandType.INSERT, query.command.getType()); assertEquals("This is an INSERT command", SqlCommandType.INSERT, query.command.getType());
queries = Parser.parseJdbcSql("select 1 as insert", true, true, false, true); queries = Parser.parseJdbcSql("select 1 as insert", true, true, false, true, true);
query = queries.get(0); query = queries.get(0);
assertEquals("This is a SELECT command", SqlCommandType.SELECT, query.command.getType()); assertEquals("This is a SELECT command", SqlCommandType.SELECT, query.command.getType());
} }
@ -172,7 +172,7 @@ public class CompositeQueryParseTest {
@Test @Test
public void testWithSelect() throws SQLException { public void testWithSelect() throws SQLException {
List<NativeQuery> queries; List<NativeQuery> queries;
queries = Parser.parseJdbcSql("with update as (update foo set (a=?,b=?,c=?)) select * from update", true, true, false, true); queries = Parser.parseJdbcSql("with update as (update foo set (a=?,b=?,c=?)) select * from update", true, true, false, true, true);
NativeQuery query = queries.get(0); NativeQuery query = queries.get(0);
assertEquals("with ... () select", SqlCommandType.SELECT, query.command.getType()); assertEquals("with ... () select", SqlCommandType.SELECT, query.command.getType());
} }
@ -180,7 +180,7 @@ public class CompositeQueryParseTest {
@Test @Test
public void testWithInsert() throws SQLException { public void testWithInsert() throws SQLException {
List<NativeQuery> queries; List<NativeQuery> queries;
queries = Parser.parseJdbcSql("with update as (update foo set (a=?,b=?,c=?)) insert into table(select) values(1)", true, true, false, true); queries = Parser.parseJdbcSql("with update as (update foo set (a=?,b=?,c=?)) insert into table(select) values(1)", true, true, false, true, true);
NativeQuery query = queries.get(0); NativeQuery query = queries.get(0);
assertEquals("with ... () insert", SqlCommandType.INSERT, query.command.getType()); assertEquals("with ... () insert", SqlCommandType.INSERT, query.command.getType());
} }
@ -201,7 +201,7 @@ public class CompositeQueryParseTest {
boolean splitStatements) { boolean splitStatements) {
try { try {
return toString( return toString(
Parser.parseJdbcSql(query, standardConformingStrings, withParameters, splitStatements, false)); Parser.parseJdbcSql(query, standardConformingStrings, withParameters, splitStatements, false, true));
} catch (SQLException e) { } catch (SQLException e) {
throw new IllegalStateException("Parser.parseJdbcSql: " + e.getMessage(), e); throw new IllegalStateException("Parser.parseJdbcSql: " + e.getMessage(), e);
} }

View File

@ -47,7 +47,7 @@ public class SqlCommandParseTest {
@Test @Test
public void run() throws SQLException { public void run() throws SQLException {
List<NativeQuery> queries; List<NativeQuery> queries;
queries = Parser.parseJdbcSql(sql, true, true, false, true); queries = Parser.parseJdbcSql(sql, true, true, false, true, true);
NativeQuery query = queries.get(0); NativeQuery query = queries.get(0);
assertEquals(sql, type, query.command.getType()); assertEquals(sql, type, query.command.getType());
} }

View File

@ -11,10 +11,7 @@ import org.postgresql.util.ExecuteUtil;
import org.postgresql.util.RsParser; import org.postgresql.util.RsParser;
import java.sql.Connection; import java.sql.Connection;
import java.sql.ResultSet; import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Properties; import java.util.Properties;
@ -63,6 +60,60 @@ public class SelectFunctionTest {
} }
} }
@Test
public void testTriggerQuery() throws Exception {
String sqlTable = "create table t_tinyint0006 (" + "id int primary key auto_increment,"
+ "my_data tinyint" + ");";
String sqlTrigger = "create trigger trigger_tinyint0006 before insert on t_tinyint0006" + " for each row "
+ "begin" + " update t_tinyint0006 set my_data=1;" + "end;";
try (Connection conn = createConnection()) {
ExecuteUtil.execute(conn, sqlTable);
ExecuteUtil.execute(conn, sqlTrigger);
}
}
@Test
public void testReturningQuery() throws Exception {
String returnString = "INSERT INTO CIMMIT (DATA_ENABLE) VALUES (1)";
try (Connection conn = createConnection()) {
PreparedStatement st = conn.prepareStatement(returnString, new String[] {"ID"});
st.execute();
}
}
@Test
public void testBatchInsert() throws Exception {
Properties props = new Properties();
props.put("preparedStatementCacheQueries", "2");
props.put("prepareThreshold", "2");
props.put("fetchSize", "5");
props.put("batchMode", "OFF");
props.put("reWriteBatchedInserts", "true");
try (Connection conn = TestUtil.openDB(props)) {
for (int j = 1; j <= 1000; j++) {
ExecuteUtil.execute(conn, "set session_timeout = 0;");
ExecuteUtil.execute(conn, "drop table if exists t" + j);
ExecuteUtil.execute(conn, "create table t" + j
+ "(id int, id1 int, id2 int, id3 int, id4 int, id5 int, data varchar(2048));");
String batchInsert = "insert into t" + j + " values (?,?,?,?,?,?,?)";
PreparedStatement preparedStatement = conn.prepareStatement(batchInsert);
for (int i = 1; i <= 1000; i++) {
preparedStatement.setInt(1, 1);
preparedStatement.setInt(2, i);
preparedStatement.setInt(3, i);
preparedStatement.setInt(4, i);
preparedStatement.setInt(5, i);
preparedStatement.setInt(6, i);
preparedStatement.setString(7, "Huawei");
preparedStatement.addBatch();
}
preparedStatement.executeBatch();
preparedStatement.close();
}
// block
}
}
private static Connection createConnection() throws Exception { private static Connection createConnection() throws Exception {
return TestUtil.openDB(); return TestUtil.openDB();
} }