/* Copyright (c) 2001-2019, 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.RowInputInterface;
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.4.1
Since:1.8.0
/** * Metadata for a result set. * * @author Fred Toussi (fredt@users dot sourceforge.net) * @version 2.4.1 * @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 newSingleColumnMetaData(String colName) { ResultMetaData md = ResultMetaData.newResultMetaData(1); md.columns[0] = new ColumnBase(null, null, null, colName); md.columns[0].setType(Type.SQL_VARCHAR_DEFAULT); md.prepareData(); return md; } public static ResultMetaData newDoubleColumnMetaData(String colNameA, String colNameB) { ResultMetaData md = ResultMetaData.newResultMetaData(2); md.columns[0] = new ColumnBase(null, null, null, colNameA); md.columns[0].setType(Type.SQL_VARCHAR_DEFAULT); md.columns[1] = new ColumnBase(null, null, null, colNameB); md.columns[1].setType(Type.SQL_VARCHAR_DEFAULT); md.prepareData(); return md; } 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); column.setSearchable((in & 0x00000010) != 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; } if (column.isSearchable()) { out |= 0x00000010; } 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(RowInputInterface 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(RowInputInterface 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(RowInputInterface 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, Type.SQL_VARCHAR.getCharacterSet(), Type.SQL_VARCHAR.getCollation(), size, scale); if (isArray) { type = new ArrayType(type, ArrayType.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; } public boolean areTypesCompatible(ResultMetaData newMeta) { if (columnCount != newMeta.columnCount) { return false; } for (int i = 0; i < columnCount; i++) { if (!columnTypes[i].canConvertFrom(newMeta.columnTypes[i])) { return false; } } return true; } }