package org.h2.result;
import java.util.ArrayList;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.store.Data;
import org.h2.store.FileStore;
import org.h2.util.Utils;
import org.h2.value.DataType;
import org.h2.value.Value;
public class RowList implements AutoCloseable {
private final Session session;
private final ArrayList<Row> list = Utils.newSmallArrayList();
private int size;
private int index, listIndex;
private FileStore file;
private Data rowBuff;
private ArrayList<Value> lobs;
private final int maxMemory;
private int memory;
private boolean written;
public RowList(Session session) {
this.session = session;
if (session.getDatabase().isPersistent()) {
maxMemory = session.getDatabase().getMaxOperationMemory();
} else {
maxMemory = 0;
}
}
private void writeRow(Data buff, Row r) {
buff.checkCapacity(2 + Data.LENGTH_INT * 3 + Data.LENGTH_LONG);
buff.writeByte((byte) 1);
buff.writeInt(r.getMemory());
int columnCount = r.getColumnCount();
buff.writeInt(columnCount);
buff.writeLong(r.getKey());
buff.writeByte(r.isDeleted() ? (byte) 1 : (byte) 0);
for (int i = 0; i < columnCount; i++) {
Value v = r.getValue(i);
buff.checkCapacity(1);
if (v == null) {
buff.writeByte((byte) 0);
} else {
buff.writeByte((byte) 1);
if (DataType.isLargeObject(v.getValueType())) {
if (v.getSmall() == null && v.getTableId() == 0) {
if (lobs == null) {
lobs = Utils.newSmallArrayList();
}
v = v.copyToTemp();
lobs.add(v);
}
}
buff.checkCapacity(buff.getValueLen(v));
buff.writeValue(v);
}
}
}
private void writeAllRows() {
if (file == null) {
Database db = session.getDatabase();
String fileName = db.createTempFile();
file = db.openFile(fileName, "rw", false);
file.setCheckedWriting(false);
file.seek(FileStore.HEADER_LENGTH);
rowBuff = Data.create(db, Constants.DEFAULT_PAGE_SIZE, true);
file.seek(FileStore.HEADER_LENGTH);
}
Data buff = rowBuff;
initBuffer(buff);
for (int i = 0, size = list.size(); i < size; i++) {
if (i > 0 && buff.length() > Constants.IO_BUFFER_SIZE) {
flushBuffer(buff);
initBuffer(buff);
}
Row r = list.get(i);
writeRow(buff, r);
}
flushBuffer(buff);
list.clear();
memory = 0;
}
private static void initBuffer(Data buff) {
buff.reset();
buff.writeInt(0);
}
private void flushBuffer(Data buff) {
buff.checkCapacity(1);
buff.writeByte((byte) 0);
buff.fillAligned();
buff.setInt(0, buff.length() / Constants.FILE_BLOCK_SIZE);
file.write(buff.getBytes(), 0, buff.length());
}
public void add(Row r) {
list.add(r);
memory += r.getMemory() + Constants.MEMORY_POINTER;
if (maxMemory > 0 && memory > maxMemory) {
writeAllRows();
}
size++;
}
public void reset() {
index = 0;
if (file != null) {
listIndex = 0;
if (!written) {
writeAllRows();
written = true;
}
list.clear();
file.seek(FileStore.HEADER_LENGTH);
}
}
public boolean hasNext() {
return index < size;
}
private Row readRow(Data buff) {
if (buff.readByte() == 0) {
return null;
}
int mem = buff.readInt();
int columnCount = buff.readInt();
long key = buff.readLong();
boolean deleted = buff.readByte() != 0;
Value[] values = new Value[columnCount];
for (int i = 0; i < columnCount; i++) {
Value v;
if (buff.readByte() == 0) {
v = null;
} else {
v = buff.readValue();
if (v.isLinkedToTable()) {
if (v.getTableId() == 0) {
session.removeAtCommit(v);
}
}
}
values[i] = v;
}
Row row = session.createRow(values, mem);
row.setKey(key);
row.setDeleted(deleted);
return row;
}
public Row next() {
Row r;
if (file == null) {
r = list.get(index++);
} else {
if (listIndex >= list.size()) {
list.clear();
listIndex = 0;
Data buff = rowBuff;
buff.reset();
int min = Constants.FILE_BLOCK_SIZE;
file.readFully(buff.getBytes(), 0, min);
int len = buff.readInt() * Constants.FILE_BLOCK_SIZE;
buff.checkCapacity(len);
if (len - min > 0) {
file.readFully(buff.getBytes(), min, len - min);
}
while (true) {
r = readRow(buff);
if (r == null) {
break;
}
list.add(r);
}
}
index++;
r = list.get(listIndex++);
}
return r;
}
public int size() {
return size;
}
@Override
public void close() {
if (file != null) {
file.closeAndDeleteSilently();
file = null;
rowBuff = null;
}
}
}