/* Copyright (c) 2001-2018, 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
*/
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;
}
}