| /* |
| |
| Derby - Class org.apache.derbyTesting.perf.clients.SingleRecordFiller |
| |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| |
| */ |
| |
| package org.apache.derbyTesting.perf.clients; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.StringReader; |
| import java.sql.Connection; |
| import java.sql.PreparedStatement; |
| import java.sql.SQLException; |
| import java.sql.Statement; |
| import java.sql.Types; |
| import java.util.Random; |
| import org.apache.derbyTesting.functionTests.util.UniqueRandomSequence; |
| |
| /** |
| * Class which generates and populates tables that can be used by |
| * {@code SingleRecordSelectClient} and {@code SingleRecordUpdateClient}. |
| * This tables contain rows with an int column (id) and a varchar(100) column |
| * (text). The id column is declared as primary key. |
| */ |
| public class SingleRecordFiller implements DBFiller { |
| |
| /** The number of tables to distribute the load over. */ |
| private final int numberOfTables; |
| /** The number of rows in each table. */ |
| private final int tableSize; |
| /** |
| * The data type of the text column (a constant from |
| * {@code java.sql.Types}). |
| */ |
| private final int dataType; |
| /** SQL name of the data type specified by {@code dataType}. */ |
| private final String dataTypeString; |
| /** |
| * Whether or not the table includes an integer column with unique values |
| * in random order. A UNIQUE index will be created for the column. |
| */ |
| private final boolean withSecIndexColumn; |
| /** |
| * Whether or not the table includes an integer column with unique values |
| * in random order not backed by an index. |
| */ |
| private final boolean withNonIndexedColumn; |
| |
| static final int TEXT_SIZE = 100; |
| |
| /** |
| * Generate a filler that creates the specified number of tables, each of |
| * which contains the specified number of records. When this constructor |
| * is used, the table only contains two columns: a primary key column (INT) |
| * and a text column (VARCHAR(100)). |
| * |
| * @param records the number of records in each table |
| * @param tables the number of tables to create |
| */ |
| public SingleRecordFiller(int records, int tables) { |
| this(records, tables, Types.VARCHAR, false, false); |
| } |
| |
| /** |
| * Generate a filler that creates the specified number of tables, each of |
| * which contains the specified number of records. |
| * |
| * @param records the number of records in each table |
| * @param tables the number of tables to create |
| * @param type which SQL type to store the text as (one of |
| * {@code java.sql.Types.VARCHAR}, {@code java.sql.Types.BLOB} and |
| * {@code java.sql.Types.CLOB}. |
| */ |
| public SingleRecordFiller(int records, int tables, int type, |
| boolean withSecIndex, boolean withNonIndexed) { |
| tableSize = records; |
| numberOfTables = tables; |
| dataType = type; |
| switch (type) { |
| case Types.VARCHAR: |
| dataTypeString = "VARCHAR"; |
| break; |
| case Types.BLOB: |
| dataTypeString = "BLOB"; |
| break; |
| case Types.CLOB: |
| dataTypeString = "CLOB"; |
| break; |
| default: |
| throw new IllegalArgumentException("type = " + type); |
| } |
| withSecIndexColumn = withSecIndex; |
| withNonIndexedColumn = withNonIndexed; |
| } |
| |
| public void fill(Connection c) throws SQLException { |
| c.setAutoCommit(false); |
| Statement s = c.createStatement(); |
| for (int table = 0; table < numberOfTables; table++) { |
| String tableName = getTableName(tableSize, table, dataType, |
| withSecIndexColumn, withNonIndexedColumn); |
| WisconsinFiller.dropTable(c, tableName); |
| s.executeUpdate( |
| "CREATE TABLE " + tableName + "(ID INT NOT NULL, " + |
| (withSecIndexColumn ? "SEC INT, " : "") + |
| (withNonIndexedColumn ? "NI INT, " : "") + |
| "TEXT " + dataTypeString + "(" + TEXT_SIZE + "))"); |
| |
| String extraCols = ""; |
| String extraParams = ""; |
| if (withSecIndexColumn) { |
| extraCols += ", SEC"; |
| extraParams += ", ?"; |
| } |
| if (withNonIndexedColumn) { |
| extraCols += ", NI"; |
| extraParams += ", ?"; |
| } |
| |
| PreparedStatement ps = |
| c.prepareStatement("INSERT INTO " + tableName + |
| "(ID, TEXT" + extraCols + |
| ") VALUES (?, ?" + extraParams + ")"); |
| |
| UniqueRandomSequence secIdSequence = null; |
| if (withSecIndexColumn) { |
| secIdSequence = new UniqueRandomSequence(tableSize); |
| } |
| |
| UniqueRandomSequence nonIndexedSequence = null; |
| if (withNonIndexedColumn) { |
| nonIndexedSequence = new UniqueRandomSequence(tableSize); |
| } |
| |
| for (int i = 0; i < tableSize; i++) { |
| int col = 1; |
| ps.setInt(col++, i); |
| if (dataType == Types.VARCHAR) { |
| ps.setString(col++, randomString(i)); |
| } else if (dataType == Types.CLOB) { |
| StringReader reader = new StringReader(randomString(i)); |
| ps.setCharacterStream(col++, reader, TEXT_SIZE); |
| } else if (dataType == Types.BLOB) { |
| ByteArrayInputStream stream = |
| new ByteArrayInputStream(randomBytes(i)); |
| ps.setBinaryStream(col++, stream, TEXT_SIZE); |
| } |
| if (withSecIndexColumn) { |
| ps.setInt(col++, secIdSequence.nextValue()); |
| } |
| if (withNonIndexedColumn) { |
| ps.setInt(col++, nonIndexedSequence.nextValue()); |
| } |
| ps.executeUpdate(); |
| if ((i % 1000) == 0) { |
| c.commit(); |
| } |
| } |
| |
| s.executeUpdate("ALTER TABLE " + tableName + " ADD CONSTRAINT " + |
| tableName + "_PK PRIMARY KEY (ID)"); |
| |
| if (withSecIndexColumn) { |
| s.executeUpdate( |
| "CREATE INDEX " + tableName + "_SECONDARY_INDEX ON " + |
| tableName + "(SEC)"); |
| } |
| |
| ps.close(); |
| c.commit(); |
| } |
| |
| s.close(); |
| } |
| |
| private static final String[] RANDOM_STRINGS = new String[16]; |
| private static final byte[][] RANDOM_BYTES = new byte[16][TEXT_SIZE]; |
| static { |
| final String alphabet = "abcdefghijklmnopqrstuvwxyz" + |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + |
| "01234567890_-+*/(){}[]&%$#!"; |
| final Random r = new Random(); |
| for (int i = 0; i < RANDOM_STRINGS.length; i++) { |
| StringBuffer buf = new StringBuffer(TEXT_SIZE); |
| for (int x = 0; x < TEXT_SIZE; x++) { |
| buf.append(alphabet.charAt(r.nextInt(alphabet.length()))); |
| } |
| RANDOM_STRINGS[i] = buf.toString(); |
| for (int j = 0; j < TEXT_SIZE; j++) { |
| RANDOM_BYTES[i][j] = (byte) RANDOM_STRINGS[i].charAt(j); |
| } |
| } |
| } |
| |
| /** |
| * Pick a random string. |
| * |
| * @param seed a seed used to decide which random string to pick |
| * @return a (somewhat) random string |
| */ |
| static String randomString(int seed) { |
| return RANDOM_STRINGS[(seed & 0x7fffffff) % RANDOM_STRINGS.length]; |
| } |
| |
| /** |
| * Pick a random byte string. |
| * |
| * @param seed a seed used to decide which random string to pick |
| * @return a (somewhat) random sequence of bytes |
| */ |
| static byte[] randomBytes(int seed) { |
| return RANDOM_BYTES[(seed & 0x7fffffff) % RANDOM_BYTES.length]; |
| } |
| |
| /** |
| * Get the name of a table generated by this class. |
| * |
| * @param records the number of records in the table |
| * @param table the number of the table, between 0 (inclusive) and the |
| * total number of tables (exclusive) |
| * @param dataType the {@code java.sql.Types} constant specifying the |
| * data type of the text column |
| * @return the name of the table specified by the arguments |
| */ |
| static String getTableName(int records, int table, int dataType, |
| boolean withSecIndex, boolean withNonIndexed) { |
| String name = "SINGLE_RECORD_" + records + "_" + table; |
| if (withSecIndex) { |
| name += "_SECIDX"; |
| } |
| if (withNonIndexed) { |
| name += "_NONIDX"; |
| } |
| if (dataType == Types.VARCHAR) { |
| name += "_VARCHAR"; |
| } else if (dataType == Types.BLOB) { |
| name += "_BLOB"; |
| } else if (dataType == Types.CLOB) { |
| name += "_CLOB"; |
| } else { |
| throw new IllegalArgumentException("dataType = " + dataType); |
| } |
| return name; |
| } |
| } |