EMMA Coverage Report (generated Sun Mar 01 22:06:14 CET 2015)
[all classes][org.h2.table]

COVERAGE SUMMARY FOR SOURCE FILE [RegularTable.java]

nameclass, %method, %block, %line, %
RegularTable.java100% (2/2)66%  (29/44)41%  (737/1804)41%  (164.7/404)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class RegularTable100% (1/1)64%  (27/42)40%  (725/1792)41%  (163.7/403)
canDrop (): boolean 0%   (0/1)0%   (0/2)0%   (0/1)
canGetRowCount (): boolean 0%   (0/1)0%   (0/2)0%   (0/1)
canTruncate (): boolean 0%   (0/1)0%   (0/44)0%   (0/11)
checkDeadlock (Session, Session, Set): ArrayList 0%   (0/1)0%   (0/99)0%   (0/28)
checkRename (): void 0%   (0/1)0%   (0/1)0%   (0/1)
getDeadlockDetails (ArrayList, boolean): String 0%   (0/1)0%   (0/101)0%   (0/17)
getDiskSpaceUsed (): long 0%   (0/1)0%   (0/4)0%   (0/1)
getRow (Session, long): Row 0%   (0/1)0%   (0/6)0%   (0/1)
getRowIdColumn (): Column 0%   (0/1)0%   (0/18)0%   (0/4)
getUniqueIndex (): Index 0%   (0/1)0%   (0/20)0%   (0/5)
isLockedExclusively (): boolean 0%   (0/1)0%   (0/7)0%   (0/1)
removeChildrenAndResources (Session): void 0%   (0/1)0%   (0/101)0%   (0/24)
removeRow (Session, Row): void 0%   (0/1)0%   (0/108)0%   (0/30)
toString (): String 0%   (0/1)0%   (0/3)0%   (0/1)
truncate (Session): void 0%   (0/1)0%   (0/31)0%   (0/7)
checkRowCount (Session, Index, int): void 100% (1/1)14%  (7/50)33%  (2/6)
traceLock (Session, boolean, String): void 100% (1/1)15%  (5/34)67%  (2/3)
doLock1 (Session, int, boolean): void 100% (1/1)17%  (21/123)18%  (6/33)
doLock2 (Session, int, boolean): boolean 100% (1/1)28%  (22/80)33%  (7/21)
analyzeIfRequired (Session): void 100% (1/1)35%  (14/40)22%  (2/9)
addRow (Session, Row): void 100% (1/1)41%  (50/123)39%  (12/31)
lock (Session, boolean, boolean): boolean 100% (1/1)62%  (64/104)74%  (17/23)
addIndex (Session, String, int, IndexColumn [], IndexType, boolean, String): ... 100% (1/1)71%  (261/368)73%  (52.6/72)
unlock (Session): void 100% (1/1)73%  (37/51)75%  (8.3/11)
getRowCount (Session): long 100% (1/1)77%  (10/13)67%  (2/3)
RegularTable (CreateTableData): void 100% (1/1)81%  (88/108)82%  (14/17)
isLockedExclusivelyBy (Session): boolean 100% (1/1)88%  (7/8)87%  (0.9/1)
getMainIndexColumn (IndexType, IndexColumn []): int 100% (1/1)89%  (32/36)82%  (9/11)
addRowsToIndex (Session, ArrayList, Index): void 100% (1/1)100% (26/26)100% (7/7)
checkSupportAlter (): void 100% (1/1)100% (1/1)100% (1/1)
close (Session): void 100% (1/1)100% (16/16)100% (4/4)
commit (short, Row): void 100% (1/1)100% (27/27)100% (5/5)
createRow (Value []): Row 100% (1/1)100% (6/6)100% (1/1)
getContainsLargeObject (): boolean 100% (1/1)100% (3/3)100% (1/1)
getIndexes (): ArrayList 100% (1/1)100% (3/3)100% (1/1)
getMaxDataModificationId (): long 100% (1/1)100% (3/3)100% (1/1)
getRowCountApproximation (): long 100% (1/1)100% (4/4)100% (1/1)
getScanIndex (Session): Index 100% (1/1)100% (6/6)100% (1/1)
getTableType (): String 100% (1/1)100% (2/2)100% (1/1)
isDeterministic (): boolean 100% (1/1)100% (2/2)100% (1/1)
setCompareMode (CompareMode): void 100% (1/1)100% (4/4)100% (2/2)
setRowCount (long): void 100% (1/1)100% (4/4)100% (2/2)
     
class RegularTable$1100% (1/1)100% (2/2)100% (12/12)100% (2/2)
RegularTable$1 (Index): void 100% (1/1)100% (6/6)100% (1/1)
compare (Row, Row): int 100% (1/1)100% (6/6)100% (1/1)

1/*
2 * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
3 * and the EPL 1.0 (http://h2database.com/html/license.html).
4 * Initial Developer: H2 Group
5 */
6package org.h2.table;
7 
8import java.util.ArrayDeque;
9import java.util.ArrayList;
10import java.util.Collections;
11import java.util.Comparator;
12import java.util.HashSet;
13import java.util.Set;
14import org.h2.api.DatabaseEventListener;
15import org.h2.api.ErrorCode;
16import org.h2.command.ddl.Analyze;
17import org.h2.command.ddl.CreateTableData;
18import org.h2.constraint.Constraint;
19import org.h2.constraint.ConstraintReferential;
20import org.h2.engine.Constants;
21import org.h2.engine.DbObject;
22import org.h2.engine.Session;
23import org.h2.engine.SysProperties;
24import org.h2.index.Cursor;
25import org.h2.index.HashIndex;
26import org.h2.index.Index;
27import org.h2.index.IndexType;
28import org.h2.index.MultiVersionIndex;
29import org.h2.index.NonUniqueHashIndex;
30import org.h2.index.PageBtreeIndex;
31import org.h2.index.PageDataIndex;
32import org.h2.index.PageDelegateIndex;
33import org.h2.index.ScanIndex;
34import org.h2.index.SpatialTreeIndex;
35import org.h2.index.TreeIndex;
36import org.h2.message.DbException;
37import org.h2.message.Trace;
38import org.h2.result.Row;
39import org.h2.result.SortOrder;
40import org.h2.schema.SchemaObject;
41import org.h2.util.MathUtils;
42import org.h2.util.New;
43import org.h2.value.CompareMode;
44import org.h2.value.DataType;
45import org.h2.value.Value;
46 
47/**
48 * Most tables are an instance of this class. For this table, the data is stored
49 * in the database. The actual data is not kept here, instead it is kept in the
50 * indexes. There is at least one index, the scan index.
51 */
52public class RegularTable extends TableBase {
53 
54    private Index scanIndex;
55    private long rowCount;
56    private volatile Session lockExclusiveSession;
57    private HashSet<Session> lockSharedSessions = New.hashSet();
58 
59    /**
60     * The queue of sessions waiting to lock the table. It is a FIFO queue to
61     * prevent starvation, since Java's synchronized locking is biased.
62     */
63    private final ArrayDeque<Session> waitingSessions = new ArrayDeque<Session>();
64    private final Trace traceLock;
65    private final ArrayList<Index> indexes = New.arrayList();
66    private long lastModificationId;
67    private boolean containsLargeObject;
68    private final PageDataIndex mainIndex;
69    private int changesSinceAnalyze;
70    private int nextAnalyze;
71    private Column rowIdColumn;
72 
73    public RegularTable(CreateTableData data) {
74        super(data);
75        nextAnalyze = database.getSettings().analyzeAuto;
76        this.isHidden = data.isHidden;
77        for (Column col : getColumns()) {
78            if (DataType.isLargeObject(col.getType())) {
79                containsLargeObject = true;
80            }
81        }
82        if (data.persistData && database.isPersistent()) {
83            mainIndex = new PageDataIndex(this, data.id,
84                    IndexColumn.wrap(getColumns()),
85                    IndexType.createScan(data.persistData),
86                    data.create, data.session);
87            scanIndex = mainIndex;
88        } else {
89            mainIndex = null;
90            scanIndex = new ScanIndex(this, data.id,
91                    IndexColumn.wrap(getColumns()), IndexType.createScan(data.persistData));
92        }
93        indexes.add(scanIndex);
94        traceLock = database.getTrace(Trace.LOCK);
95    }
96 
97    @Override
98    public void close(Session session) {
99        for (Index index : indexes) {
100            index.close(session);
101        }
102    }
103 
104    @Override
105    public Row getRow(Session session, long key) {
106        return scanIndex.getRow(session, key);
107    }
108 
109    @Override
110    public void addRow(Session session, Row row) {
111        lastModificationId = database.getNextModificationDataId();
112        if (database.isMultiVersion()) {
113            row.setSessionId(session.getId());
114        }
115        int i = 0;
116        try {
117            for (int size = indexes.size(); i < size; i++) {
118                Index index = indexes.get(i);
119                index.add(session, row);
120                checkRowCount(session, index, 1);
121            }
122            rowCount++;
123        } catch (Throwable e) {
124            try {
125                while (--i >= 0) {
126                    Index index = indexes.get(i);
127                    index.remove(session, row);
128                    checkRowCount(session, index, 0);
129                }
130            } catch (DbException e2) {
131                // this could happen, for example on failure in the storage
132                // but if that is not the case it means there is something wrong
133                // with the database
134                trace.error(e2, "could not undo operation");
135                throw e2;
136            }
137            DbException de = DbException.convert(e);
138            if (de.getErrorCode() == ErrorCode.DUPLICATE_KEY_1) {
139                for (int j = 0; j < indexes.size(); j++) {
140                    Index index = indexes.get(j);
141                    if (index.getIndexType().isUnique() && index instanceof MultiVersionIndex) {
142                        MultiVersionIndex mv = (MultiVersionIndex) index;
143                        if (mv.isUncommittedFromOtherSession(session, row)) {
144                            throw DbException.get(
145                                    ErrorCode.CONCURRENT_UPDATE_1, index.getName());
146                        }
147                    }
148                }
149            }
150            throw de;
151        }
152        analyzeIfRequired(session);
153    }
154 
155    @Override
156    public void commit(short operation, Row row) {
157        lastModificationId = database.getNextModificationDataId();
158        for (int i = 0, size = indexes.size(); i < size; i++) {
159            Index index = indexes.get(i);
160            index.commit(operation, row);
161        }
162    }
163 
164    private void checkRowCount(Session session, Index index, int offset) {
165        if (SysProperties.CHECK && !database.isMultiVersion()) {
166            if (!(index instanceof PageDelegateIndex)) {
167                long rc = index.getRowCount(session);
168                if (rc != rowCount + offset) {
169                    DbException.throwInternalError(
170                            "rowCount expected " + (rowCount + offset) +
171                            " got " + rc + " " + getName() + "." + index.getName());
172                }
173            }
174        }
175    }
176 
177    @Override
178    public Index getScanIndex(Session session) {
179        return indexes.get(0);
180    }
181 
182    @Override
183    public Index getUniqueIndex() {
184        for (Index idx : indexes) {
185            if (idx.getIndexType().isUnique()) {
186                return idx;
187            }
188        }
189        return null;
190    }
191 
192    @Override
193    public ArrayList<Index> getIndexes() {
194        return indexes;
195    }
196 
197    @Override
198    public Index addIndex(Session session, String indexName, int indexId,
199            IndexColumn[] cols, IndexType indexType, boolean create,
200            String indexComment) {
201        if (indexType.isPrimaryKey()) {
202            for (IndexColumn c : cols) {
203                Column column = c.column;
204                if (column.isNullable()) {
205                    throw DbException.get(
206                            ErrorCode.COLUMN_MUST_NOT_BE_NULLABLE_1, column.getName());
207                }
208                column.setPrimaryKey(true);
209            }
210        }
211        boolean isSessionTemporary = isTemporary() && !isGlobalTemporary();
212        if (!isSessionTemporary) {
213            database.lockMeta(session);
214        }
215        Index index;
216        if (isPersistIndexes() && indexType.isPersistent()) {
217            int mainIndexColumn;
218            if (database.isStarting() &&
219                    database.getPageStore().getRootPageId(indexId) != 0) {
220                mainIndexColumn = -1;
221            } else if (!database.isStarting() && mainIndex.getRowCount(session) != 0) {
222                mainIndexColumn = -1;
223            } else {
224                mainIndexColumn = getMainIndexColumn(indexType, cols);
225            }
226            if (mainIndexColumn != -1) {
227                mainIndex.setMainIndexColumn(mainIndexColumn);
228                index = new PageDelegateIndex(this, indexId, indexName,
229                        indexType, mainIndex, create, session);
230            } else if (indexType.isSpatial()) {
231                index = new SpatialTreeIndex(this, indexId, indexName, cols,
232                        indexType, true, create, session);
233            } else {
234                index = new PageBtreeIndex(this, indexId, indexName, cols,
235                        indexType, create, session);
236            }
237        } else {
238            if (indexType.isHash()) {
239                if (cols.length != 1) {
240                    throw DbException.getUnsupportedException(
241                            "hash indexes may index only one column");
242                }
243                if (indexType.isUnique()) {
244                    index = new HashIndex(this, indexId, indexName, cols,
245                            indexType);
246                } else {
247                    index = new NonUniqueHashIndex(this, indexId, indexName,
248                            cols, indexType);
249                }
250            } else if (indexType.isSpatial()) {
251                index = new SpatialTreeIndex(this, indexId, indexName, cols,
252                        indexType, false, true, session);
253            } else {
254                index = new TreeIndex(this, indexId, indexName, cols, indexType);
255            }
256        }
257        if (database.isMultiVersion()) {
258            index = new MultiVersionIndex(index, this);
259        }
260        if (index.needRebuild() && rowCount > 0) {
261            try {
262                Index scan = getScanIndex(session);
263                long remaining = scan.getRowCount(session);
264                long total = remaining;
265                Cursor cursor = scan.find(session, null, null);
266                long i = 0;
267                int bufferSize = (int) Math.min(rowCount, database.getMaxMemoryRows());
268                ArrayList<Row> buffer = New.arrayList(bufferSize);
269                String n = getName() + ":" + index.getName();
270                int t = MathUtils.convertLongToInt(total);
271                while (cursor.next()) {
272                    database.setProgress(DatabaseEventListener.STATE_CREATE_INDEX, n,
273                            MathUtils.convertLongToInt(i++), t);
274                    Row row = cursor.get();
275                    buffer.add(row);
276                    if (buffer.size() >= bufferSize) {
277                        addRowsToIndex(session, buffer, index);
278                    }
279                    remaining--;
280                }
281                addRowsToIndex(session, buffer, index);
282                if (SysProperties.CHECK && remaining != 0) {
283                    DbException.throwInternalError("rowcount remaining=" +
284                            remaining + " " + getName());
285                }
286            } catch (DbException e) {
287                getSchema().freeUniqueName(indexName);
288                try {
289                    index.remove(session);
290                } catch (DbException e2) {
291                    // this could happen, for example on failure in the storage
292                    // but if that is not the case it means
293                    // there is something wrong with the database
294                    trace.error(e2, "could not remove index");
295                    throw e2;
296                }
297                throw e;
298            }
299        }
300        index.setTemporary(isTemporary());
301        if (index.getCreateSQL() != null) {
302            index.setComment(indexComment);
303            if (isSessionTemporary) {
304                session.addLocalTempTableIndex(index);
305            } else {
306                database.addSchemaObject(session, index);
307            }
308        }
309        indexes.add(index);
310        setModified();
311        return index;
312    }
313 
314    private int getMainIndexColumn(IndexType indexType, IndexColumn[] cols) {
315        if (mainIndex.getMainIndexColumn() != -1) {
316            return -1;
317        }
318        if (!indexType.isPrimaryKey() || cols.length != 1) {
319            return -1;
320        }
321        IndexColumn first = cols[0];
322        if (first.sortType != SortOrder.ASCENDING) {
323            return -1;
324        }
325        switch(first.column.getType()) {
326        case Value.BYTE:
327        case Value.SHORT:
328        case Value.INT:
329        case Value.LONG:
330            break;
331        default:
332            return -1;
333        }
334        return first.column.getColumnId();
335    }
336 
337    @Override
338    public boolean canGetRowCount() {
339        return true;
340    }
341 
342    private static void addRowsToIndex(Session session, ArrayList<Row> list,
343            Index index) {
344        final Index idx = index;
345        Collections.sort(list, new Comparator<Row>() {
346            @Override
347            public int compare(Row r1, Row r2) {
348                return idx.compareRows(r1, r2);
349            }
350        });
351        for (Row row : list) {
352            index.add(session, row);
353        }
354        list.clear();
355    }
356 
357    @Override
358    public boolean canDrop() {
359        return true;
360    }
361 
362    @Override
363    public long getRowCount(Session session) {
364        if (database.isMultiVersion()) {
365            return getScanIndex(session).getRowCount(session);
366        }
367        return rowCount;
368    }
369 
370    @Override
371    public void removeRow(Session session, Row row) {
372        if (database.isMultiVersion()) {
373            if (row.isDeleted()) {
374                throw DbException.get(ErrorCode.CONCURRENT_UPDATE_1, getName());
375            }
376            int old = row.getSessionId();
377            int newId = session.getId();
378            if (old == 0) {
379                row.setSessionId(newId);
380            } else if (old != newId) {
381                throw DbException.get(ErrorCode.CONCURRENT_UPDATE_1, getName());
382            }
383        }
384        lastModificationId = database.getNextModificationDataId();
385        int i = indexes.size() - 1;
386        try {
387            for (; i >= 0; i--) {
388                Index index = indexes.get(i);
389                index.remove(session, row);
390                checkRowCount(session, index, -1);
391            }
392            rowCount--;
393        } catch (Throwable e) {
394            try {
395                while (++i < indexes.size()) {
396                    Index index = indexes.get(i);
397                    index.add(session, row);
398                    checkRowCount(session, index, 0);
399                }
400            } catch (DbException e2) {
401                // this could happen, for example on failure in the storage
402                // but if that is not the case it means there is something wrong
403                // with the database
404                trace.error(e2, "could not undo operation");
405                throw e2;
406            }
407            throw DbException.convert(e);
408        }
409        analyzeIfRequired(session);
410    }
411 
412    @Override
413    public void truncate(Session session) {
414        lastModificationId = database.getNextModificationDataId();
415        for (int i = indexes.size() - 1; i >= 0; i--) {
416            Index index = indexes.get(i);
417            index.truncate(session);
418        }
419        rowCount = 0;
420        changesSinceAnalyze = 0;
421    }
422 
423    private void analyzeIfRequired(Session session) {
424        if (nextAnalyze == 0 || nextAnalyze > changesSinceAnalyze++) {
425            return;
426        }
427        changesSinceAnalyze = 0;
428        int n = 2 * nextAnalyze;
429        if (n > 0) {
430            nextAnalyze = n;
431        }
432        int rows = session.getDatabase().getSettings().analyzeSample / 10;
433        Analyze.analyzeTable(session, this, rows, false);
434    }
435 
436    @Override
437    public boolean isLockedExclusivelyBy(Session session) {
438        return lockExclusiveSession == session;
439    }
440 
441    @Override
442    public boolean lock(Session session, boolean exclusive,
443            boolean forceLockEvenInMvcc) {
444        int lockMode = database.getLockMode();
445        if (lockMode == Constants.LOCK_MODE_OFF) {
446            return lockExclusiveSession != null;
447        }
448        if (!forceLockEvenInMvcc && database.isMultiVersion()) {
449            // MVCC: update, delete, and insert use a shared lock.
450            // Select doesn't lock except when using FOR UPDATE
451            if (exclusive) {
452                exclusive = false;
453            } else {
454                if (lockExclusiveSession == null) {
455                    return false;
456                }
457            }
458        }
459        if (lockExclusiveSession == session) {
460            return true;
461        }
462        synchronized (database) {
463            if (lockExclusiveSession == session) {
464                return true;
465            }
466            if (!exclusive && lockSharedSessions.contains(session)) {
467                return true;
468            }
469            session.setWaitForLock(this, Thread.currentThread());
470            waitingSessions.addLast(session);
471            try {
472                doLock1(session, lockMode, exclusive);
473            } finally {
474                session.setWaitForLock(null, null);
475                waitingSessions.remove(session);
476            }
477        }
478        return false;
479    }
480 
481    private void doLock1(Session session, int lockMode, boolean exclusive) {
482        traceLock(session, exclusive, "requesting for");
483        // don't get the current time unless necessary
484        long max = 0;
485        boolean checkDeadlock = false;
486        while (true) {
487            // if I'm the next one in the queue
488            if (waitingSessions.getFirst() == session) {
489                if (doLock2(session, lockMode, exclusive)) {
490                    return;
491                }
492            }
493            if (checkDeadlock) {
494                ArrayList<Session> sessions = checkDeadlock(session, null, null);
495                if (sessions != null) {
496                    throw DbException.get(ErrorCode.DEADLOCK_1,
497                            getDeadlockDetails(sessions, exclusive));
498                }
499            } else {
500                // check for deadlocks from now on
501                checkDeadlock = true;
502            }
503            long now = System.currentTimeMillis();
504            if (max == 0) {
505                // try at least one more time
506                max = now + session.getLockTimeout();
507            } else if (now >= max) {
508                traceLock(session, exclusive, "timeout after " + session.getLockTimeout());
509                throw DbException.get(ErrorCode.LOCK_TIMEOUT_1, getName());
510            }
511            try {
512                traceLock(session, exclusive, "waiting for");
513                if (database.getLockMode() == Constants.LOCK_MODE_TABLE_GC) {
514                    for (int i = 0; i < 20; i++) {
515                        long free = Runtime.getRuntime().freeMemory();
516                        System.gc();
517                        long free2 = Runtime.getRuntime().freeMemory();
518                        if (free == free2) {
519                            break;
520                        }
521                    }
522                }
523                // don't wait too long so that deadlocks are detected early
524                long sleep = Math.min(Constants.DEADLOCK_CHECK, max - now);
525                if (sleep == 0) {
526                    sleep = 1;
527                }
528                database.wait(sleep);
529            } catch (InterruptedException e) {
530                // ignore
531            }
532        }
533    }
534 
535    private boolean doLock2(Session session, int lockMode, boolean exclusive) {
536        if (exclusive) {
537            if (lockExclusiveSession == null) {
538                if (lockSharedSessions.isEmpty()) {
539                    traceLock(session, exclusive, "added for");
540                    session.addLock(this);
541                    lockExclusiveSession = session;
542                    return true;
543                } else if (lockSharedSessions.size() == 1 &&
544                        lockSharedSessions.contains(session)) {
545                    traceLock(session, exclusive, "add (upgraded) for ");
546                    lockExclusiveSession = session;
547                    return true;
548                }
549            }
550        } else {
551            if (lockExclusiveSession == null) {
552                if (lockMode == Constants.LOCK_MODE_READ_COMMITTED) {
553                    if (!database.isMultiThreaded() && !database.isMultiVersion()) {
554                        // READ_COMMITTED: a read lock is acquired,
555                        // but released immediately after the operation
556                        // is complete.
557                        // When allowing only one thread, no lock is
558                        // required.
559                        // Row level locks work like read committed.
560                        return true;
561                    }
562                }
563                if (!lockSharedSessions.contains(session)) {
564                    traceLock(session, exclusive, "ok");
565                    session.addLock(this);
566                    lockSharedSessions.add(session);
567                }
568                return true;
569            }
570        }
571        return false;
572    }
573    private static String getDeadlockDetails(ArrayList<Session> sessions, boolean exclusive) {
574        // We add the thread details here to make it easier for customers to
575        // match up these error messages with their own logs.
576        StringBuilder buff = new StringBuilder();
577        for (Session s : sessions) {
578            Table lock = s.getWaitForLock();
579            Thread thread = s.getWaitForLockThread();
580            buff.append("\nSession ").
581                append(s.toString()).
582                append(" on thread ").
583                append(thread.getName()).
584                append(" is waiting to lock ").
585                append(lock.toString()).
586                append(exclusive ? " (exclusive)" : " (shared)").
587                append(" while locking ");
588            int i = 0;
589            for (Table t : s.getLocks()) {
590                if (i++ > 0) {
591                    buff.append(", ");
592                }
593                buff.append(t.toString());
594                if (t instanceof RegularTable) {
595                    if (((RegularTable) t).lockExclusiveSession == s) {
596                        buff.append(" (exclusive)");
597                    } else {
598                        buff.append(" (shared)");
599                    }
600                }
601            }
602            buff.append('.');
603        }
604        return buff.toString();
605    }
606 
607    @Override
608    public ArrayList<Session> checkDeadlock(Session session, Session clash,
609            Set<Session> visited) {
610        // only one deadlock check at any given time
611        synchronized (RegularTable.class) {
612            if (clash == null) {
613                // verification is started
614                clash = session;
615                visited = New.hashSet();
616            } else if (clash == session) {
617                // we found a circle where this session is involved
618                return New.arrayList();
619            } else if (visited.contains(session)) {
620                // we have already checked this session.
621                // there is a circle, but the sessions in the circle need to
622                // find it out themselves
623                return null;
624            }
625            visited.add(session);
626            ArrayList<Session> error = null;
627            for (Session s : lockSharedSessions) {
628                if (s == session) {
629                    // it doesn't matter if we have locked the object already
630                    continue;
631                }
632                Table t = s.getWaitForLock();
633                if (t != null) {
634                    error = t.checkDeadlock(s, clash, visited);
635                    if (error != null) {
636                        error.add(session);
637                        break;
638                    }
639                }
640            }
641            if (error == null && lockExclusiveSession != null) {
642                Table t = lockExclusiveSession.getWaitForLock();
643                if (t != null) {
644                    error = t.checkDeadlock(lockExclusiveSession, clash, visited);
645                    if (error != null) {
646                        error.add(session);
647                    }
648                }
649            }
650            return error;
651        }
652    }
653 
654    private void traceLock(Session session, boolean exclusive, String s) {
655        if (traceLock.isDebugEnabled()) {
656            traceLock.debug("{0} {1} {2} {3}", session.getId(),
657                    exclusive ? "exclusive write lock" : "shared read lock", s, getName());
658        }
659    }
660 
661    @Override
662    public boolean isLockedExclusively() {
663        return lockExclusiveSession != null;
664    }
665 
666    @Override
667    public void unlock(Session s) {
668        if (database != null) {
669            traceLock(s, lockExclusiveSession == s, "unlock");
670            if (lockExclusiveSession == s) {
671                lockExclusiveSession = null;
672            }
673            if (lockSharedSessions.size() > 0) {
674                lockSharedSessions.remove(s);
675            }
676            synchronized (database) {
677                if (!waitingSessions.isEmpty()) {
678                    database.notifyAll();
679                }
680            }
681        }
682    }
683 
684    /**
685     * Create a row from the values.
686     *
687     * @param data the value list
688     * @return the row
689     */
690    public static Row createRow(Value[] data) {
691        return new Row(data, Row.MEMORY_CALCULATE);
692    }
693 
694    /**
695     * Set the row count of this table.
696     *
697     * @param count the row count
698     */
699    public void setRowCount(long count) {
700        this.rowCount = count;
701    }
702 
703    @Override
704    public void removeChildrenAndResources(Session session) {
705        if (containsLargeObject) {
706            // unfortunately, the data is gone on rollback
707            truncate(session);
708            database.getLobStorage().removeAllForTable(getId());
709            database.lockMeta(session);
710        }
711        super.removeChildrenAndResources(session);
712        // go backwards because database.removeIndex will call table.removeIndex
713        while (indexes.size() > 1) {
714            Index index = indexes.get(1);
715            if (index.getName() != null) {
716                database.removeSchemaObject(session, index);
717            }
718            // needed for session temporary indexes
719            indexes.remove(index);
720        }
721        if (SysProperties.CHECK) {
722            for (SchemaObject obj : database.getAllSchemaObjects(DbObject.INDEX)) {
723                Index index = (Index) obj;
724                if (index.getTable() == this) {
725                    DbException.throwInternalError("index not dropped: " + index.getName());
726                }
727            }
728        }
729        scanIndex.remove(session);
730        database.removeMeta(session, getId());
731        scanIndex = null;
732        lockExclusiveSession = null;
733        lockSharedSessions = null;
734        invalidate();
735    }
736 
737    @Override
738    public String toString() {
739        return getSQL();
740    }
741 
742    @Override
743    public void checkRename() {
744        // ok
745    }
746 
747    @Override
748    public void checkSupportAlter() {
749        // ok
750    }
751 
752    @Override
753    public boolean canTruncate() {
754        if (getCheckForeignKeyConstraints() && database.getReferentialIntegrity()) {
755            ArrayList<Constraint> constraints = getConstraints();
756            if (constraints != null) {
757                for (int i = 0, size = constraints.size(); i < size; i++) {
758                    Constraint c = constraints.get(i);
759                    if (!(c.getConstraintType().equals(Constraint.REFERENTIAL))) {
760                        continue;
761                    }
762                    ConstraintReferential ref = (ConstraintReferential) c;
763                    if (ref.getRefTable() == this) {
764                        return false;
765                    }
766                }
767            }
768        }
769        return true;
770    }
771 
772    @Override
773    public String getTableType() {
774        return Table.TABLE;
775    }
776 
777    @Override
778    public long getMaxDataModificationId() {
779        return lastModificationId;
780    }
781 
782    public boolean getContainsLargeObject() {
783        return containsLargeObject;
784    }
785 
786    @Override
787    public long getRowCountApproximation() {
788        return scanIndex.getRowCountApproximation();
789    }
790 
791    @Override
792    public long getDiskSpaceUsed() {
793        return scanIndex.getDiskSpaceUsed();
794    }
795 
796    public void setCompareMode(CompareMode compareMode) {
797        this.compareMode = compareMode;
798    }
799 
800    @Override
801    public boolean isDeterministic() {
802        return true;
803    }
804 
805    @Override
806    public Column getRowIdColumn() {
807        if (rowIdColumn == null) {
808            rowIdColumn = new Column(Column.ROWID, Value.LONG);
809            rowIdColumn.setTable(this, -1);
810        }
811        return rowIdColumn;
812    }
813 
814}

[all classes][org.h2.table]
EMMA 2.0.5312 (C) Vladimir Roubtsov