/*
 * 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.table;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.h2.command.ddl.CreateTableData;
import org.h2.constraint.Constraint;
import org.h2.constraint.ConstraintReferential;
import org.h2.engine.Session;
import org.h2.index.Index;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.value.DataType;
import org.h2.value.Value;

Most tables are an instance of this class. For this table, the data is stored in the database. The actual data is not kept here, instead it is kept in the indexes. There is at least one index, the scan index.
/** * Most tables are an instance of this class. For this table, the data is stored * in the database. The actual data is not kept here, instead it is kept in the * indexes. There is at least one index, the scan index. */
public abstract class RegularTable extends TableBase {
Appends the specified rows to the specified index.
Params:
  • session – the session
  • list – the rows, list is cleared on completion
  • index – the index to append to
/** * Appends the specified rows to the specified index. * * @param session * the session * @param list * the rows, list is cleared on completion * @param index * the index to append to */
protected static void addRowsToIndex(Session session, ArrayList<Row> list, Index index) { sortRows(list, index); for (Row row : list) { index.add(session, row); } list.clear(); }
Formats details of a deadlock.
Params:
  • sessions – the list of sessions
  • exclusive – true if waiting for exclusive lock, false otherwise
Returns:formatted details of a deadlock
/** * Formats details of a deadlock. * * @param sessions * the list of sessions * @param exclusive * true if waiting for exclusive lock, false otherwise * @return formatted details of a deadlock */
protected static String getDeadlockDetails(ArrayList<Session> sessions, boolean exclusive) { // We add the thread details here to make it easier for customers to // match up these error messages with their own logs. StringBuilder builder = new StringBuilder(); for (Session s : sessions) { Table lock = s.getWaitForLock(); Thread thread = s.getWaitForLockThread(); builder.append("\nSession ").append(s.toString()).append(" on thread ").append(thread.getName()) .append(" is waiting to lock ").append(lock.toString()) .append(exclusive ? " (exclusive)" : " (shared)").append(" while locking "); Table[] locks = s.getLocks(); for (int i = 0, length = locks.length; i < length; i++) { Table t = locks[i]; if (i > 0) { builder.append(", "); } builder.append(t.toString()); if (t instanceof RegularTable) { if (((RegularTable) t).lockExclusiveSession == s) { builder.append(" (exclusive)"); } else { builder.append(" (shared)"); } } } builder.append('.'); } return builder.toString(); }
Sorts the specified list of rows for a specified index.
Params:
  • list – the list of rows
  • index – the index to sort for
/** * Sorts the specified list of rows for a specified index. * * @param list * the list of rows * @param index * the index to sort for */
protected static void sortRows(ArrayList<? extends SearchRow> list, final Index index) { Collections.sort(list, new Comparator<SearchRow>() { @Override public int compare(SearchRow r1, SearchRow r2) { return index.compareRows(r1, r2); } }); }
Whether the table contains a CLOB or BLOB.
/** * Whether the table contains a CLOB or BLOB. */
protected final boolean containsLargeObject;
The session (if any) that has exclusively locked this table.
/** * The session (if any) that has exclusively locked this table. */
protected volatile Session lockExclusiveSession;
The set of sessions (if any) that have a shared lock on the table. Here we are using using a ConcurrentHashMap as a set, as there is no ConcurrentHashSet.
/** * The set of sessions (if any) that have a shared lock on the table. Here * we are using using a ConcurrentHashMap as a set, as there is no * ConcurrentHashSet. */
protected final ConcurrentHashMap<Session, Session> lockSharedSessions = new ConcurrentHashMap<>(); private Column rowIdColumn; protected RegularTable(CreateTableData data) { super(data); this.isHidden = data.isHidden; boolean b = false; for (Column col : getColumns()) { if (DataType.isLargeObject(col.getType().getValueType())) { b = true; break; } } containsLargeObject = b; } @Override public boolean canDrop() { return true; } @Override public boolean canGetRowCount() { return true; } @Override public boolean canTruncate() { if (getCheckForeignKeyConstraints() && database.getReferentialIntegrity()) { ArrayList<Constraint> constraints = getConstraints(); if (constraints != null) { for (Constraint c : constraints) { if (c.getConstraintType() != Constraint.Type.REFERENTIAL) { continue; } ConstraintReferential ref = (ConstraintReferential) c; if (ref.getRefTable() == this) { return false; } } } } return true; } @Override public ArrayList<Session> checkDeadlock(Session session, Session clash, Set<Session> visited) { // only one deadlock check at any given time synchronized (getClass()) { if (clash == null) { // verification is started clash = session; visited = new HashSet<>(); } else if (clash == session) { // we found a cycle where this session is involved return new ArrayList<>(0); } else if (visited.contains(session)) { // we have already checked this session. // there is a cycle, but the sessions in the cycle need to // find it out themselves return null; } visited.add(session); ArrayList<Session> error = null; for (Session s : lockSharedSessions.keySet()) { if (s == session) { // it doesn't matter if we have locked the object already continue; } Table t = s.getWaitForLock(); if (t != null) { error = t.checkDeadlock(s, clash, visited); if (error != null) { error.add(session); break; } } } // take a local copy so we don't see inconsistent data, since we are // not locked while checking the lockExclusiveSession value Session copyOfLockExclusiveSession = lockExclusiveSession; if (error == null && copyOfLockExclusiveSession != null) { Table t = copyOfLockExclusiveSession.getWaitForLock(); if (t != null) { error = t.checkDeadlock(copyOfLockExclusiveSession, clash, visited); if (error != null) { error.add(session); } } } return error; } } @Override public void checkRename() { // ok } @Override public void checkSupportAlter() { // ok } public boolean getContainsLargeObject() { return containsLargeObject; } @Override public Column getRowIdColumn() { if (rowIdColumn == null) { rowIdColumn = new Column(Column.ROWID, Value.LONG); rowIdColumn.setTable(this, SearchRow.ROWID_INDEX); rowIdColumn.setRowId(true); } return rowIdColumn; } @Override public TableType getTableType() { return TableType.TABLE; } @Override public boolean isDeterministic() { return true; } @Override public boolean isLockedExclusively() { return lockExclusiveSession != null; } @Override public boolean isLockedExclusivelyBy(Session session) { return lockExclusiveSession == session; } @Override public String toString() { return getSQL(false); } }