/*
* Copyright 2004-2019 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.engine;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.result.LocalResult;
import org.h2.result.Row;
import org.h2.table.Column;
import org.h2.table.Table;
import org.h2.util.StringUtils;
import org.h2.util.Utils;
import org.h2.value.Value;
import org.h2.value.ValueNull;
Class for gathering and processing of generated keys.
/**
* Class for gathering and processing of generated keys.
*/
public final class GeneratedKeys {
Data for result set with generated keys.
/**
* Data for result set with generated keys.
*/
private final ArrayList<Map<Column, Value>> data = Utils.newSmallArrayList();
Columns with generated keys in the current row.
/**
* Columns with generated keys in the current row.
*/
private final ArrayList<Column> row = Utils.newSmallArrayList();
All columns with generated keys.
/**
* All columns with generated keys.
*/
private final ArrayList<Column> allColumns = Utils.newSmallArrayList();
Request for keys gathering. false
if generated keys are not needed, true
if generated keys should be configured automatically, int[]
to specify column indices to return generated keys from, or String[]
to specify column names to return generated keys from. /**
* Request for keys gathering. {@code false} if generated keys are not needed,
* {@code true} if generated keys should be configured automatically,
* {@code int[]} to specify column indices to return generated keys from, or
* {@code String[]} to specify column names to return generated keys from.
*/
private Object generatedKeysRequest;
Processed table.
/**
* Processed table.
*/
private Table table;
Remembers columns with generated keys.
Params: - column –
table column
/**
* Remembers columns with generated keys.
*
* @param column
* table column
*/
public void add(Column column) {
if (Boolean.FALSE.equals(generatedKeysRequest)) {
return;
}
row.add(column);
}
Clears all information from previous runs and sets a new request for
gathering of generated keys.
Params: - generatedKeysRequest –
false
if generated keys are not needed, true
if generated keys should be configured automatically, int[]
to specify column indices to return generated keys from, or String[]
to specify column names to return generated keys from
/**
* Clears all information from previous runs and sets a new request for
* gathering of generated keys.
*
* @param generatedKeysRequest
* {@code false} if generated keys are not needed, {@code true} if
* generated keys should be configured automatically, {@code int[]}
* to specify column indices to return generated keys from, or
* {@code String[]} to specify column names to return generated keys
* from
*/
public void clear(Object generatedKeysRequest) {
this.generatedKeysRequest = generatedKeysRequest;
data.clear();
row.clear();
allColumns.clear();
table = null;
}
Saves row with generated keys if any.
Params: - tableRow –
table row that was inserted
/**
* Saves row with generated keys if any.
*
* @param tableRow
* table row that was inserted
*/
public void confirmRow(Row tableRow) {
if (Boolean.FALSE.equals(generatedKeysRequest)) {
return;
}
int size = row.size();
if (size > 0) {
if (size == 1) {
Column column = row.get(0);
data.add(Collections.singletonMap(column, tableRow.getValue(column.getColumnId())));
if (!allColumns.contains(column)) {
allColumns.add(column);
}
} else {
HashMap<Column, Value> map = new HashMap<>();
for (Column column : row) {
map.put(column, tableRow.getValue(column.getColumnId()));
if (!allColumns.contains(column)) {
allColumns.add(column);
}
}
data.add(map);
}
row.clear();
}
}
Returns generated keys.
Params: - session –
session
Returns: local result with generated keys
/**
* Returns generated keys.
*
* @param session
* session
* @return local result with generated keys
*/
public LocalResult getKeys(Session session) {
Database db = session.getDatabase();
if (Boolean.FALSE.equals(generatedKeysRequest)) {
clear(null);
return db.getResultFactory().create();
}
ArrayList<ExpressionColumn> expressionColumns;
if (Boolean.TRUE.equals(generatedKeysRequest)) {
expressionColumns = new ArrayList<>(allColumns.size());
for (Column column : allColumns) {
expressionColumns.add(new ExpressionColumn(db, column));
}
} else if (generatedKeysRequest instanceof int[]) {
if (table != null) {
int[] indices = (int[]) generatedKeysRequest;
Column[] columns = table.getColumns();
int cnt = columns.length;
allColumns.clear();
expressionColumns = new ArrayList<>(indices.length);
for (int idx : indices) {
if (idx >= 1 && idx <= cnt) {
Column column = columns[idx - 1];
expressionColumns.add(new ExpressionColumn(db, column));
allColumns.add(column);
}
}
} else {
clear(null);
return db.getResultFactory().create();
}
} else if (generatedKeysRequest instanceof String[]) {
if (table != null) {
String[] names = (String[]) generatedKeysRequest;
allColumns.clear();
expressionColumns = new ArrayList<>(names.length);
for (String name : names) {
Column column;
search: if (table.doesColumnExist(name)) {
column = table.getColumn(name);
} else {
name = StringUtils.toUpperEnglish(name);
if (table.doesColumnExist(name)) {
column = table.getColumn(name);
} else {
for (Column c : table.getColumns()) {
if (c.getName().equalsIgnoreCase(name)) {
column = c;
break search;
}
}
continue;
}
}
expressionColumns.add(new ExpressionColumn(db, column));
allColumns.add(column);
}
} else {
clear(null);
return db.getResultFactory().create();
}
} else {
clear(null);
return db.getResultFactory().create();
}
int columnCount = expressionColumns.size();
if (columnCount == 0) {
clear(null);
return db.getResultFactory().create();
}
LocalResult result = db.getResultFactory().create(session,
expressionColumns.toArray(new Expression[0]), columnCount);
for (Map<Column, Value> map : data) {
Value[] row = new Value[columnCount];
for (Map.Entry<Column, Value> entry : map.entrySet()) {
int idx = allColumns.indexOf(entry.getKey());
if (idx >= 0) {
row[idx] = entry.getValue();
}
}
for (int i = 0; i < columnCount; i++) {
if (row[i] == null) {
row[i] = ValueNull.INSTANCE;
}
}
result.addRow(row);
}
clear(null);
return result;
}
Initializes processing of the specified table. Should be called after clear()
, but before other methods. Params: - table –
table
/**
* Initializes processing of the specified table. Should be called after
* {@code clear()}, but before other methods.
*
* @param table
* table
*/
public void initialize(Table table) {
this.table = table;
}
Clears unsaved information about previous row, if any. Should be called
before processing of a new row if previous row was not confirmed or simply
always before each row.
/**
* Clears unsaved information about previous row, if any. Should be called
* before processing of a new row if previous row was not confirmed or simply
* always before each row.
*/
public void nextRow() {
row.clear();
}
@Override
public String toString() {
return allColumns + ": " + data.size();
}
}