| /* Copyright (c) 2001-2010, The HSQL Development Group |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * Redistributions of source code must retain the above copyright notice, this |
| * list of conditions and the following disclaimer. |
| * |
| * Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * Neither the name of the HSQL Development Group nor the names of its |
| * contributors may be used to endorse or promote products derived from this |
| * software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG, |
| * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| |
| package org.hsqldb.result; |
| |
| import java.io.IOException; |
| |
| import org.hsqldb.ColumnBase; |
| import org.hsqldb.error.Error; |
| import org.hsqldb.error.ErrorCode; |
| import org.hsqldb.lib.ArrayUtil; |
| import org.hsqldb.rowio.RowInputBinary; |
| import org.hsqldb.rowio.RowOutputInterface; |
| import org.hsqldb.types.ArrayType; |
| import org.hsqldb.types.Type; |
| import org.hsqldb.types.Types; |
| |
| /** |
| * Metadata for a result set. |
| * |
| * @author Fred Toussi (fredt@users dot sourceforge.net) |
| * @version 2.0.0 |
| * @since 1.8.0 |
| */ |
| public final class ResultMetaData { |
| |
| public static final int RESULT_METADATA = 1; |
| public static final int SIMPLE_RESULT_METADATA = 2; |
| public static final int UPDATE_RESULT_METADATA = 3; |
| public static final int PARAM_METADATA = 4; |
| public static final int GENERATED_INDEX_METADATA = 5; |
| public static final int GENERATED_NAME_METADATA = 6; |
| |
| // |
| private int type; |
| |
| // values overriding table column |
| public String[] columnLabels; |
| public Type[] columnTypes; |
| private int columnCount; |
| private int extendedColumnCount; |
| public static final ResultMetaData emptyResultMetaData = |
| newResultMetaData(0); |
| public static final ResultMetaData emptyParamMetaData = |
| newParameterMetaData(0); |
| |
| // column indexes for mapping or for generated columns |
| public int[] colIndexes; |
| |
| // columns for data columns |
| public ColumnBase[] columns; |
| |
| // param mode and nullability for parameter metadata |
| public byte[] paramModes; |
| public byte[] paramNullable; |
| |
| // |
| private ResultMetaData(int type) { |
| this.type = type; |
| } |
| |
| public static ResultMetaData newUpdateResultMetaData(Type[] types) { |
| |
| ResultMetaData md = new ResultMetaData(UPDATE_RESULT_METADATA); |
| |
| md.columnTypes = new Type[types.length]; |
| md.columnCount = types.length; |
| md.extendedColumnCount = types.length; |
| |
| ArrayUtil.copyArray(types, md.columnTypes, types.length); |
| |
| return md; |
| } |
| |
| public static ResultMetaData newSimpleResultMetaData(Type[] types) { |
| |
| ResultMetaData md = new ResultMetaData(SIMPLE_RESULT_METADATA); |
| |
| md.columnTypes = types; |
| md.columnCount = types.length; |
| md.extendedColumnCount = types.length; |
| |
| return md; |
| } |
| |
| public static ResultMetaData newResultMetaData(int colCount) { |
| |
| Type[] types = new Type[colCount]; |
| |
| return newResultMetaData(types, null, colCount, colCount); |
| } |
| |
| public static ResultMetaData newResultMetaData(Type[] types, |
| int[] baseColumnIndexes, int colCount, int extColCount) { |
| |
| ResultMetaData md = new ResultMetaData(RESULT_METADATA); |
| |
| md.columnLabels = new String[colCount]; |
| md.columns = new ColumnBase[colCount]; |
| md.columnTypes = types; |
| md.colIndexes = baseColumnIndexes; |
| md.columnCount = colCount; |
| md.extendedColumnCount = extColCount; |
| |
| return md; |
| } |
| |
| public static ResultMetaData newParameterMetaData(int colCount) { |
| |
| ResultMetaData md = new ResultMetaData(PARAM_METADATA); |
| |
| md.columnTypes = new Type[colCount]; |
| md.columnLabels = new String[colCount]; |
| md.paramModes = new byte[colCount]; |
| md.paramNullable = new byte[colCount]; |
| md.columnCount = colCount; |
| md.extendedColumnCount = colCount; |
| |
| return md; |
| } |
| |
| public static ResultMetaData newGeneratedColumnsMetaData( |
| int[] columnIndexes, String[] columnNames) { |
| |
| if (columnIndexes != null) { |
| ResultMetaData md = new ResultMetaData(GENERATED_INDEX_METADATA); |
| |
| md.columnCount = columnIndexes.length; |
| md.extendedColumnCount = columnIndexes.length; |
| md.colIndexes = new int[columnIndexes.length]; |
| |
| for (int i = 0; i < columnIndexes.length; i++) { |
| md.colIndexes[i] = columnIndexes[i] - 1; |
| } |
| |
| return md; |
| } else if (columnNames != null) { |
| ResultMetaData md = new ResultMetaData(GENERATED_NAME_METADATA); |
| |
| md.columnLabels = new String[columnNames.length]; |
| md.columnCount = columnNames.length; |
| md.extendedColumnCount = columnNames.length; |
| md.columnLabels = columnNames; |
| |
| return md; |
| } else { |
| return null; |
| } |
| } |
| |
| public void prepareData() { |
| |
| if (columns != null) { |
| for (int i = 0; i < columnCount; i++) { |
| if (columnTypes[i] == null) { |
| columnTypes[i] = columns[i].getDataType(); |
| } |
| } |
| } |
| } |
| |
| public int getColumnCount() { |
| return columnCount; |
| } |
| |
| public int getExtendedColumnCount() { |
| return extendedColumnCount; |
| } |
| |
| public void resetExtendedColumnCount() { |
| extendedColumnCount = columnCount; |
| } |
| |
| public Type[] getParameterTypes() { |
| return columnTypes; |
| } |
| |
| public String[] getGeneratedColumnNames() { |
| return columnLabels; |
| } |
| |
| public int[] getGeneratedColumnIndexes() { |
| return colIndexes; |
| } |
| |
| public boolean isTableColumn(int i) { |
| |
| String colName = columns[i].getNameString(); |
| String tableName = columns[i].getTableNameString(); |
| |
| return tableName != null && tableName.length() > 0 && colName != null |
| && colName.length() > 0; |
| } |
| |
| private static void decodeTableColumnAttrs(int in, ColumnBase column) { |
| |
| column.setNullability((byte) (in & 0x00000003)); |
| column.setIdentity((in & 0x00000004) != 0); |
| column.setWriteable((in & 0x00000008) != 0); |
| } |
| |
| private static int encodeTableColumnAttrs(ColumnBase column) { |
| |
| int out = column.getNullability(); // always between 0x00 and 0x02 |
| |
| if (column.isIdentity()) { |
| out |= 0x00000004; |
| } |
| |
| if (column.isWriteable()) { |
| out |= 0x00000008; |
| } |
| |
| return out; |
| } |
| |
| private void decodeParamColumnAttrs(int in, int columnIndex) { |
| paramNullable[columnIndex] = (byte) (in & 0x00000003); |
| paramModes[columnIndex] = (byte) ((in >> 4) & 0x0000000f); |
| } |
| |
| private int encodeParamColumnAttrs(int columnIndex) { |
| |
| int out = paramModes[columnIndex] << 4; |
| |
| out |= paramNullable[columnIndex]; |
| |
| return out; |
| } |
| |
| ResultMetaData(RowInputBinary in) throws IOException { |
| |
| type = in.readInt(); |
| columnCount = in.readInt(); |
| |
| switch (type) { |
| |
| case UPDATE_RESULT_METADATA : |
| case SIMPLE_RESULT_METADATA : { |
| columnTypes = new Type[columnCount]; |
| |
| for (int i = 0; i < columnCount; i++) { |
| columnTypes[i] = readDataTypeSimple(in); |
| } |
| |
| return; |
| } |
| case GENERATED_INDEX_METADATA : { |
| colIndexes = new int[columnCount]; |
| |
| for (int i = 0; i < columnCount; i++) { |
| colIndexes[i] = in.readInt(); |
| } |
| |
| return; |
| } |
| case GENERATED_NAME_METADATA : { |
| columnLabels = new String[columnCount]; |
| |
| for (int i = 0; i < columnCount; i++) { |
| columnLabels[i] = in.readString(); |
| } |
| |
| return; |
| } |
| case PARAM_METADATA : { |
| columnTypes = new Type[columnCount]; |
| columnLabels = new String[columnCount]; |
| paramModes = new byte[columnCount]; |
| paramNullable = new byte[columnCount]; |
| |
| for (int i = 0; i < columnCount; i++) { |
| columnTypes[i] = readDataType(in); |
| columnLabels[i] = in.readString(); |
| |
| decodeParamColumnAttrs(in.readByte(), i); |
| } |
| |
| return; |
| } |
| case RESULT_METADATA : { |
| extendedColumnCount = in.readInt(); |
| columnTypes = new Type[extendedColumnCount]; |
| columnLabels = new String[columnCount]; |
| columns = new ColumnBase[columnCount]; |
| |
| if (columnCount != extendedColumnCount) { |
| colIndexes = new int[columnCount]; |
| } |
| |
| for (int i = 0; i < extendedColumnCount; i++) { |
| Type type = readDataType(in); |
| |
| columnTypes[i] = type; |
| } |
| |
| for (int i = 0; i < columnCount; i++) { |
| columnLabels[i] = in.readString(); |
| |
| String catalog = in.readString(); |
| String schema = in.readString(); |
| String table = in.readString(); |
| String name = in.readString(); |
| ColumnBase column = new ColumnBase(catalog, schema, table, |
| name); |
| |
| column.setType(columnTypes[i]); |
| decodeTableColumnAttrs(in.readByte(), column); |
| |
| columns[i] = column; |
| } |
| |
| if (columnCount != extendedColumnCount) { |
| for (int i = 0; i < columnCount; i++) { |
| colIndexes[i] = in.readInt(); |
| } |
| } |
| |
| return; |
| } |
| default : { |
| throw Error.runtimeError(ErrorCode.U_S0500, "ResultMetaData"); |
| } |
| } |
| } |
| |
| Type readDataTypeSimple(RowInputBinary in) throws IOException { |
| |
| int typeCode = in.readType(); |
| boolean isArray = typeCode == Types.SQL_ARRAY; |
| |
| if (isArray) { |
| typeCode = in.readType(); |
| return Type.getDefaultArrayType(typeCode); |
| } |
| |
| return Type.getDefaultType(typeCode); |
| } |
| |
| |
| Type readDataType(RowInputBinary in) throws IOException { |
| |
| int typeCode = in.readType(); |
| boolean isArray = typeCode == Types.SQL_ARRAY; |
| |
| if (isArray) { |
| typeCode = in.readType(); |
| } |
| |
| long size = in.readLong(); |
| int scale = in.readInt(); |
| Type type = Type.getType(typeCode, 0, size, scale); |
| |
| if (isArray) { |
| type = new ArrayType(type, Type.defaultArrayCardinality); |
| } |
| |
| return type; |
| } |
| |
| void writeDataType(RowOutputInterface out, Type type) { |
| |
| out.writeType(type.typeCode); |
| |
| if (type.isArrayType()) { |
| out.writeType(type.collectionBaseType().typeCode); |
| } |
| |
| out.writeLong(type.precision); |
| out.writeInt(type.scale); |
| } |
| |
| void writeDataTypeCodes(RowOutputInterface out, Type type) { |
| |
| out.writeType(type.typeCode); |
| |
| if (type.isArrayType()) { |
| out.writeType(type.collectionBaseType().typeCode); |
| } |
| } |
| |
| void write(RowOutputInterface out) throws IOException { |
| |
| out.writeInt(type); |
| out.writeInt(columnCount); |
| |
| switch (type) { |
| |
| case UPDATE_RESULT_METADATA : |
| case SIMPLE_RESULT_METADATA : { |
| for (int i = 0; i < columnCount; i++) { |
| writeDataTypeCodes(out, columnTypes[i]); |
| } |
| |
| return; |
| } |
| case GENERATED_INDEX_METADATA : { |
| for (int i = 0; i < columnCount; i++) { |
| out.writeInt(colIndexes[i]); |
| } |
| |
| return; |
| } |
| case GENERATED_NAME_METADATA : { |
| for (int i = 0; i < columnCount; i++) { |
| out.writeString(columnLabels[i]); |
| } |
| |
| return; |
| } |
| case PARAM_METADATA : |
| for (int i = 0; i < columnCount; i++) { |
| writeDataType(out, columnTypes[i]); |
| out.writeString(columnLabels[i]); |
| out.writeByte(encodeParamColumnAttrs(i)); |
| } |
| |
| return; |
| |
| case RESULT_METADATA : { |
| out.writeInt(extendedColumnCount); |
| |
| for (int i = 0; i < extendedColumnCount; i++) { |
| if (columnTypes[i] == null) { |
| ColumnBase column = columns[i]; |
| |
| columnTypes[i] = column.getDataType(); |
| } |
| |
| writeDataType(out, columnTypes[i]); |
| } |
| |
| for (int i = 0; i < columnCount; i++) { |
| ColumnBase column = columns[i]; |
| |
| out.writeString(columnLabels[i]); |
| out.writeString(column.getCatalogNameString()); |
| out.writeString(column.getSchemaNameString()); |
| out.writeString(column.getTableNameString()); |
| out.writeString(column.getNameString()); |
| out.writeByte(encodeTableColumnAttrs(column)); |
| } |
| |
| if (columnCount != extendedColumnCount) { |
| for (int i = 0; i < colIndexes.length; i++) { |
| out.writeInt(colIndexes[i]); |
| } |
| } |
| |
| return; |
| } |
| default : { |
| throw Error.runtimeError(ErrorCode.U_S0500, "ResultMetaData"); |
| } |
| } |
| } |
| |
| public ResultMetaData getNewMetaData(int[] columnMap) { |
| |
| ResultMetaData newMeta = newResultMetaData(columnMap.length); |
| |
| ArrayUtil.projectRow(columnLabels, columnMap, newMeta.columnLabels); |
| ArrayUtil.projectRow(columnTypes, columnMap, newMeta.columnTypes); |
| ArrayUtil.projectRow(columns, columnMap, newMeta.columns); |
| |
| return newMeta; |
| } |
| } |