/*
 * Copyright (c) 2003, PostgreSQL Global Development Group
 * See the LICENSE file in the project root for more information.
 */

package org.postgresql.core.v3;

import org.postgresql.core.NativeQuery;
import org.postgresql.core.ParameterList;

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 is complete. Intended to be used to wrap a Query that is present in the batchStatements collection.
Author:Jeremy Whiting jwhiting@redhat.com, Christopher Deckers (chrriis@gmail.com)
/** * 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 * is complete. Intended to be used to wrap a Query that is present in the batchStatements * collection. * * @author Jeremy Whiting jwhiting@redhat.com * @author Christopher Deckers (chrriis@gmail.com) * */
public class BatchedQuery extends SimpleQuery { private String sql; private final int valuesBraceOpenPosition; private final int valuesBraceClosePosition; private final int batchSize; private BatchedQuery[] blocks; public BatchedQuery(NativeQuery query, TypeTransferModeRegistry transferModeRegistry, int valuesBraceOpenPosition, int valuesBraceClosePosition, boolean sanitiserDisabled) { super(query, transferModeRegistry, sanitiserDisabled); this.valuesBraceOpenPosition = valuesBraceOpenPosition; this.valuesBraceClosePosition = valuesBraceClosePosition; this.batchSize = 1; } private BatchedQuery(BatchedQuery src, int batchSize) { super(src); this.valuesBraceOpenPosition = src.valuesBraceOpenPosition; this.valuesBraceClosePosition = src.valuesBraceClosePosition; this.batchSize = batchSize; } public BatchedQuery deriveForMultiBatch(int valueBlock) { if (getBatchSize() != 1) { throw new IllegalStateException("Only the original decorator can be derived."); } if (valueBlock == 1) { return this; } int index = Integer.numberOfTrailingZeros(valueBlock) - 1; if (valueBlock > 128 || valueBlock != (1 << (index + 1))) { throw new IllegalArgumentException( "Expected value block should be a power of 2 smaller or equal to 128. Actual block is " + valueBlock); } if (blocks == null) { blocks = new BatchedQuery[7]; } BatchedQuery bq = blocks[index]; if (bq == null) { bq = new BatchedQuery(this, valueBlock); blocks[index] = bq; } return bq; } @Override public int getBatchSize() { return batchSize; }
Method to return the sql based on number of batches. Skipping the initial batch.
/** * Method to return the sql based on number of batches. Skipping the initial * batch. */
@Override public String getNativeSql() { if (sql != null) { return sql; } sql = buildNativeSql(null); return sql; } private String buildNativeSql(ParameterList params) { String sql = null; // dynamically build sql with parameters for batches String nativeSql = super.getNativeSql(); int batchSize = getBatchSize(); if (batchSize < 2) { sql = nativeSql; return sql; } if (nativeSql == null) { sql = ""; return sql; } int valuesBlockCharCount = 0; // Split the values section around every dynamic parameter. int[] bindPositions = getNativeQuery().bindPositions; int[] chunkStart = new int[1 + bindPositions.length]; int[] chunkEnd = new int[1 + bindPositions.length]; chunkStart[0] = valuesBraceOpenPosition; if (bindPositions.length == 0) { valuesBlockCharCount = valuesBraceClosePosition - valuesBraceOpenPosition + 1; chunkEnd[0] = valuesBraceClosePosition + 1; } else { chunkEnd[0] = bindPositions[0]; // valuesBlockCharCount += chunks[0].length; valuesBlockCharCount += chunkEnd[0] - chunkStart[0]; for (int i = 0; i < bindPositions.length; i++) { int startIndex = bindPositions[i] + 2; int endIndex = i < bindPositions.length - 1 ? bindPositions[i + 1] : valuesBraceClosePosition + 1; for (; startIndex < endIndex; startIndex++) { if (!Character.isDigit(nativeSql.charAt(startIndex))) { break; } } chunkStart[i + 1] = startIndex; chunkEnd[i + 1] = endIndex; // valuesBlockCharCount += chunks[i + 1].length; valuesBlockCharCount += chunkEnd[i + 1] - chunkStart[i + 1]; } } int length = nativeSql.length(); //valuesBraceOpenPosition + valuesBlockCharCount; length += NativeQuery.calculateBindLength(bindPositions.length * batchSize); length -= NativeQuery.calculateBindLength(bindPositions.length); length += (valuesBlockCharCount + 1 /*comma*/) * (batchSize - 1 /* initial sql */); StringBuilder s = new StringBuilder(length); // Add query until end of values parameter block. int pos; if (bindPositions.length > 0 && params == null) { // Add the first values (...) clause, it would be values($1,..., $n), and it matches with // the values clause of a simple non-rewritten SQL s.append(nativeSql, 0, valuesBraceClosePosition + 1); pos = bindPositions.length + 1; } else { pos = 1; batchSize++; // do not use super.toString(params) as it does not work if query ends with -- // We need to carefully add (...),(...), and we do not want to get (...) --, (...) // s.append(super.toString(params)); s.append(nativeSql, 0, valuesBraceOpenPosition); } for (int i = 2; i <= batchSize; i++) { if (i > 2 || pos != 1) { // For "has binds" the first valuds s.append(','); } s.append(nativeSql, chunkStart[0], chunkEnd[0]); for (int j = 1; j < chunkStart.length; j++) { if (params == null) { NativeQuery.appendBindName(s, pos++); } else { s.append(params.toString(pos++, true)); } s.append(nativeSql, chunkStart[j], chunkEnd[j]); } } // Add trailing content: final query is like original with multi values. // This could contain "--" comments, so it is important to add them at end. s.append(nativeSql, valuesBraceClosePosition + 1, nativeSql.length()); sql = s.toString(); // Predict length only when building sql with $1, $2, ... (that is no specific params given) assert params != null || s.length() == length : "Predicted length != actual: " + length + " !=" + s.length(); return sql; } @Override public String toString(ParameterList params) { if (getBatchSize() < 2) { return super.toString(params); } return buildNativeSql(params); } }