/*
* Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.sql.
ResultSet;
import java.sql.
ResultSetMetaData;
import java.sql.
SQLException;
import java.util.
ArrayList;
import java.util.
Collections;
import java.util.
HashMap;
import java.util.
Map;
import org.h2.tools.
SimpleResultSet;
/**
* Merged result set. Used to combine several result sets into one. Merged
* result set will contain rows from all appended result sets. Result sets are
* not required to have the same lists of columns, but required to have
* compatible column definitions, for example, if one result set has a
* {@link java.sql.Types#VARCHAR} column {@code NAME} then another results sets
* that have {@code NAME} column should also define it with the same type.
*/
public final class
MergedResultSet {
/**
* Metadata of a column.
*/
private static final class
ColumnInfo {
final
String name;
final int
type;
final int
precision;
final int
scale;
/**
* Creates metadata.
*
* @param name
* name of the column
* @param type
* type of the column, see {@link java.sql.Types}
* @param precision
* precision of the column
* @param scale
* scale of the column
*/
ColumnInfo(
String name, int
type, int
precision, int
scale) {
this.
name =
name;
this.
type =
type;
this.
precision =
precision;
this.
scale =
scale;
}
@
Override
public boolean
equals(
Object obj) {
if (this ==
obj) {
return true;
}
if (
obj == null ||
getClass() !=
obj.
getClass()) {
return false;
}
ColumnInfo other = (
ColumnInfo)
obj;
return
name.
equals(
other.
name);
}
@
Override
public int
hashCode() {
return
name.
hashCode();
}
}
private final
ArrayList<
Map<
ColumnInfo,
Object>>
data =
New.
arrayList();
private final
ArrayList<
ColumnInfo>
columns =
New.
arrayList();
/**
* Appends a result set.
*
* @param rs
* result set to append
* @throws SQLException
* on SQL exception
*/
public void
add(
ResultSet rs) throws
SQLException {
ResultSetMetaData meta =
rs.
getMetaData();
int
cols =
meta.
getColumnCount();
if (
cols == 0) {
return;
}
ColumnInfo[]
info = new
ColumnInfo[
cols];
for (int
i = 1;
i <=
cols;
i++) {
ColumnInfo ci = new
ColumnInfo(
meta.
getColumnName(
i),
meta.
getColumnType(
i),
meta.
getPrecision(
i),
meta.
getScale(
i));
info[
i - 1] =
ci;
if (!
columns.
contains(
ci)) {
columns.
add(
ci);
}
}
while (
rs.
next()) {
if (
cols == 1) {
data.
add(
Collections.
singletonMap(
info[0],
rs.
getObject(1)));
} else {
HashMap<
ColumnInfo,
Object>
map = new
HashMap<>();
for (int
i = 1;
i <=
cols;
i++) {
ColumnInfo ci =
info[
i - 1];
map.
put(
ci,
rs.
getObject(
i));
}
data.
add(
map);
}
}
}
/**
* Returns merged results set.
*
* @return result set with rows from all appended result sets
*/
public
SimpleResultSet getResult() {
SimpleResultSet rs = new
SimpleResultSet();
for (
ColumnInfo ci :
columns) {
rs.
addColumn(
ci.
name,
ci.
type,
ci.
precision,
ci.
scale);
}
for (
Map<
ColumnInfo,
Object>
map :
data) {
Object[]
row = new
Object[
columns.
size()];
for (
Map.
Entry<
ColumnInfo,
Object>
entry :
map.
entrySet()) {
row[
columns.
indexOf(
entry.
getKey())] =
entry.
getValue();
}
rs.
addRow(
row);
}
return
rs;
}
@
Override
public
String toString() {
return
columns + ": " +
data.
size();
}
}