KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > h2 > table > TableData


1 /*
2  * Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
3  * Initial Developer: H2 Group
4  */

5 package org.h2.table;
6
7 import java.sql.SQLException JavaDoc;
8 import java.util.Comparator JavaDoc;
9 import java.util.HashSet JavaDoc;
10
11 import org.h2.api.DatabaseEventListener;
12 import org.h2.constraint.Constraint;
13 import org.h2.engine.Constants;
14 import org.h2.engine.DbObject;
15 import org.h2.engine.Session;
16 import org.h2.index.BtreeIndex;
17 import org.h2.index.Cursor;
18 import org.h2.index.HashIndex;
19 import org.h2.index.Index;
20 import org.h2.index.IndexType;
21 import org.h2.index.LinearHashIndex;
22 import org.h2.index.ScanIndex;
23 import org.h2.index.TreeIndex;
24 import org.h2.message.Message;
25 import org.h2.message.Trace;
26 import org.h2.result.Row;
27 import org.h2.schema.Schema;
28 import org.h2.store.DataPage;
29 import org.h2.store.Record;
30 import org.h2.store.RecordReader;
31 import org.h2.util.ObjectArray;
32 import org.h2.util.StringUtils;
33 import org.h2.value.Value;
34
35 /**
36  * @author Thomas
37  */

38 public class TableData extends Table implements RecordReader {
39     private ScanIndex scanIndex;
40     private int rowCount;
41     private Session lockExclusive;
42     private HashSet JavaDoc lockShared = new HashSet JavaDoc();
43     private Trace traceLock;
44     private boolean globalTemporary;
45     private boolean onCommitDrop, onCommitTruncate;
46     private ObjectArray indexes = new ObjectArray();
47     private long lastModificationId;
48
49     public TableData(Schema schema, String JavaDoc tableName, int id, ObjectArray columns,
50             boolean persistent) throws SQLException JavaDoc {
51         super(schema, id, tableName, persistent);
52         Column[] cols = new Column[columns.size()];
53         columns.toArray(cols);
54         setColumns(cols);
55         scanIndex = new ScanIndex(this, id, cols, IndexType.createScan(persistent));
56         indexes.add(scanIndex);
57         traceLock = database.getTrace(Trace.LOCK);
58     }
59
60     public void close(Session session) throws SQLException JavaDoc {
61         for (int i = 0; i < indexes.size(); i++) {
62             Index index = (Index) indexes.get(i);
63             index.close(session);
64         }
65     }
66
67     public Row getRow(int key) throws SQLException JavaDoc {
68         return scanIndex.getRow(key);
69     }
70
71     public void addRow(Session session, Row row) throws SQLException JavaDoc {
72         int i = 0;
73         lastModificationId = database.getNextModificationDataId();
74         try {
75             for (; i < indexes.size(); i++) {
76                 Index index = (Index) indexes.get(i);
77                 index.add(session, row);
78                 if(Constants.CHECK) {
79                     int rc = index.getRowCount();
80                     if(rc != rowCount+1) {
81                         throw Message.getInternalError("rowCount expected "+(rowCount+1)+" got "+rc);
82                     }
83                 }
84             }
85             rowCount++;
86         } catch (SQLException JavaDoc e) {
87             try {
88                 while(--i >= 0) {
89                     Index index = (Index) indexes.get(i);
90                     index.remove(session, row);
91                     if(Constants.CHECK) {
92                         int rc = index.getRowCount();
93                         if(rc != rowCount) {
94                             throw Message.getInternalError("rowCount expected "+(rowCount)+" got "+rc);
95                         }
96                     }
97                 }
98             } catch(SQLException JavaDoc e2) {
99                 // this could happend, for example on failure in the storage
100
// but if that is not the case it means there is something wrong with the database
101
// TODO log this problem
102
throw e2;
103             }
104             throw e;
105         }
106     }
107
108     public Index getScanIndex(Session session) {
109         return (Index) indexes.get(0);
110     }
111
112     public Index getUniqueIndex() {
113         for(int i=0; i<indexes.size(); i++) {
114             Index idx = (Index) indexes.get(i);
115             if(idx.getIndexType().isUnique()) {
116                 return idx;
117             }
118         }
119         return null;
120     }
121
122     public ObjectArray getIndexes() {
123         return indexes;
124     }
125
126     public Index addIndex(Session session, String JavaDoc indexName, int indexId, Column[] cols, IndexType indexType, int headPos, String JavaDoc indexComment)
127             throws SQLException JavaDoc {
128         if(indexType.isPrimaryKey()) {
129             indexName = getSchema().getUniqueIndexName(Constants.PRIMARY_KEY_PREFIX);
130             for(int i=0; i<cols.length; i++) {
131                 Column column = cols[i];
132                 if(column.getNullable()) {
133                     throw Message.getSQLException(Message.COLUMN_MUST_NOT_BE_NULLABLE_1, column.getName());
134                 }
135             }
136         }
137         Index index;
138         if(isPersistent() && indexType.isPersistent()) {
139             if(indexType.isHash()) {
140                 index = new LinearHashIndex(session, this, indexId, indexName, cols, indexType);
141             } else {
142                 index = new BtreeIndex(session, this, indexId, indexName, cols, indexType, headPos);
143             }
144         } else {
145             if(indexType.isHash()) {
146                 index = new HashIndex(this, indexId, indexName, cols, indexType);
147             } else {
148                 index = new TreeIndex(this, indexId, indexName, cols, indexType);
149             }
150         }
151         if(index.needRebuild()) {
152             try {
153                 Index scan = getScanIndex(session);
154                 int remaining = scan.getRowCount();
155                 int total = remaining;
156                 Cursor cursor = scan.find(session, null, null);
157                 int i = 0;
158                 int bufferSize = Constants.DEFAULT_MAX_MEMORY_ROWS;
159                 ObjectArray buffer = new ObjectArray(bufferSize);
160                 while (cursor.next()) {
161                     database.setProgress(DatabaseEventListener.STATE_CREATE_INDEX, getName(), i++, total);
162                     Row row = cursor.get();
163                     // index.add(session, row);
164
buffer.add(row);
165                     if(buffer.size() >= bufferSize) {
166                         addRowsToIndex(session, buffer, index);
167                     }
168                     remaining--;
169                 }
170                 addRowsToIndex(session, buffer, index);
171                 if(Constants.CHECK && remaining != 0) {
172                     throw Message.getInternalError("rowcount remaining=" + remaining);
173                 }
174             } catch(SQLException JavaDoc e) {
175                 try {
176                     index.remove(session);
177                 } catch(SQLException JavaDoc e2) {
178                     // this could happend, for example on failure in the storage
179
// but if that is not the case it means there is something wrong with the database
180
// TODO log this problem
181
throw e2;
182                 }
183                 throw e;
184             }
185         }
186         boolean temporary = getTemporary();
187         index.setTemporary(temporary);
188         if(index.getCreateSQL() != null) {
189             index.setComment(indexComment);
190             database.addSchemaObject(session, index);
191             // Need to update, because maybe the index is rebuilt at startup,
192
// and so the head pos may have changed, which needs to be stored now.
193
// addSchemaObject doesn't update the sys table at startup
194
if(index.getIndexType().isPersistent() && !database.getReadOnly() && !database.getLog().containsInDoubtTransactions()) {
195                 // can not save anything in the log file if it contains in-doubt transactions
196
database.update(session, index);
197             }
198         }
199         indexes.add(index);
200         setModified();
201         return index;
202     }
203
204     public boolean canGetRowCount() {
205         return true;
206     }
207     
208     private void addRowsToIndex(Session session, ObjectArray list, Index index) throws SQLException JavaDoc {
209         final Index idx = index;
210         try {
211             list.sort(new Comparator JavaDoc() {
212                 public int compare(Object JavaDoc o1, Object JavaDoc o2) {
213                     Row r1 = (Row) o1;
214                     Row r2 = (Row) o2;
215                     try {
216                         return idx.compareRows(r1, r2);
217                     } catch(SQLException JavaDoc e) {
218                         throw Message.convertToInternal(e);
219                     }
220                 }
221             });
222         } catch(Exception JavaDoc e) {
223             throw Message.convert(e);
224         }
225         for(int i=0; i<list.size(); i++) {
226             Row r = (Row) list.get(i);
227             index.add(session, r);
228         }
229         list.clear();
230     }
231
232     public boolean canDrop() {
233         return true;
234     }
235
236     public int getRowCount() {
237         return rowCount;
238     }
239
240     public void removeRow(Session session, Row row) throws SQLException JavaDoc {
241         lastModificationId = database.getNextModificationDataId();
242         for (int i = indexes.size() - 1; i >= 0; i--) {
243             Index index = (Index) indexes.get(i);
244             index.remove(session, row);
245             if(Constants.CHECK) {
246                 int rc = index.getRowCount();
247                 if(rc != rowCount-1) {
248                     throw Message.getInternalError("rowCount expected "+(rowCount-1)+" got "+rc);
249                 }
250             }
251         }
252         rowCount--;
253     }
254
255     public void truncate(Session session) throws SQLException JavaDoc {
256         lastModificationId = database.getNextModificationDataId();
257         for (int i = indexes.size() - 1; i >= 0; i--) {
258             Index index = (Index) indexes.get(i);
259             index.truncate(session);
260             if(Constants.CHECK) {
261                 int rc = index.getRowCount();
262                 if(rc != 0) {
263                     throw Message.getInternalError("rowCount expected 0 got "+rc);
264                 }
265             }
266         }
267         rowCount = 0;
268     }
269
270     public void lock(Session session, boolean exclusive) throws SQLException JavaDoc {
271         int lockMode = database.getLockMode();
272         if(lockMode == Constants.LOCK_MODE_OFF) {
273             return;
274         }
275         long max = System.currentTimeMillis() + session.getLockTimeout();
276         synchronized(database) {
277             while (true) {
278                 if (lockExclusive == session) {
279                     return;
280                 }
281                 if (exclusive) {
282                     if (lockExclusive == null) {
283                         if (lockShared.isEmpty()) {
284                             traceLock(session, exclusive, "ok");
285                             session.addLock(this);
286                             lockExclusive = session;
287                             return;
288                         } else if (lockShared.size() == 1
289                                 && lockShared.contains(session)) {
290                             traceLock(session, exclusive, "ok (upgrade)");
291                             lockExclusive = session;
292                             return;
293                         }
294                     }
295                 } else {
296                     if (lockExclusive == null) {
297                         if(lockMode == Constants.LOCK_MODE_READ_COMMITTED) {
298                             // READ_COMMITTED means 'wait until no write locks', but no read lock is added
299
return;
300                         } else if(!lockShared.contains(session)) {
301                             traceLock(session, exclusive, "ok");
302                             session.addLock(this);
303                             lockShared.add(session);
304                         }
305                         return;
306                     }
307                 }
308                 long now = System.currentTimeMillis();
309                 if (now >= max) {
310                     traceLock(session, exclusive, "timeout " + session.getLockTimeout());
311                     throw Message.getSQLException(Message.LOCK_TIMEOUT_1, getName());
312                 }
313                 try {
314                     traceLock(session, exclusive, "waiting");
315                     if(database.getLockMode() == Constants.LOCK_MODE_TABLE_GC) {
316                         for(int i=0; i<20; i++) {
317                             long free = Runtime.getRuntime().freeMemory();
318                             System.gc();
319                             long free2 = Runtime.getRuntime().freeMemory();
320                             if(free == free2) {
321                                 break;
322                             }
323                         }
324                     }
325                     database.wait(max - now);
326                 } catch (InterruptedException JavaDoc e) {
327                     // ignore
328
}
329             }
330         }
331     }
332
333     private void traceLock(Session session, boolean exclusive, String JavaDoc s) {
334         if(traceLock.debug()) {
335             traceLock.debug(session.getId()+" "+(exclusive?"xlock":"slock") + " " + s+" "+getName());
336         }
337     }
338
339     public String JavaDoc getCreateSQL() {
340         StringBuffer JavaDoc buff = new StringBuffer JavaDoc();
341         buff.append("CREATE ");
342         if(getTemporary()) {
343             if(globalTemporary) {
344                 buff.append("GLOBAL ");
345             } else {
346                 buff.append("LOCAL ");
347             }
348             buff.append("TEMPORARY ");
349         } else if(isPersistent()) {
350             buff.append("CACHED ");
351         } else {
352             buff.append("MEMORY ");
353         }
354         buff.append("TABLE ");
355         buff.append(getSQL());
356         if(comment != null) {
357             buff.append(" COMMENT ");
358             buff.append(StringUtils.quoteStringSQL(comment));
359         }
360         buff.append("(\n ");
361         for (int i = 0; i < columns.length; i++) {
362             Column column = columns[i];
363             if (i > 0) {
364                 buff.append(",\n ");
365             }
366             buff.append(column.getCreateSQL());
367         }
368         buff.append("\n)");
369         return buff.toString();
370     }
371
372     public boolean isLockedExclusively() {
373         return lockExclusive != null;
374     }
375
376     public void unlock(Session s) {
377         if(database != null) {
378             traceLock(s, lockExclusive==s, "unlock");
379             if(lockExclusive == s) {
380                 lockExclusive = null;
381             }
382             lockShared.remove(s);
383             // TODO lock: maybe we need we fifo-queue to make sure nobody starves. check what other databases do
384
synchronized(database) {
385                 database.notifyAll();
386             }
387         }
388     }
389
390     public Record read(DataPage s) throws SQLException JavaDoc {
391         int len = s.readInt();
392         Value[] data = new Value[len];
393         for(int i=0; i<len; i++) {
394             data[i] = s.readValue();
395         }
396         return new Row(data);
397     }
398
399     public void setRowCount(int count) {
400         this.rowCount = count;
401     }
402
403     public void removeChildrenAndResources(Session session) throws SQLException JavaDoc {
404         super.removeChildrenAndResources(session);
405         // go backwards because database.removeIndex will call table.removeIndex
406
while(indexes.size() > 1) {
407             Index index = (Index) indexes.get(1);
408             if(index.getName() != null) {
409                 database.removeSchemaObject(session, index);
410             }
411         }
412         if(Constants.CHECK) {
413             ObjectArray list = database.getAllSchemaObjects(DbObject.INDEX);
414             for(int i=0; i<list.size(); i++) {
415                 Index index = (Index) list.get(i);
416                 if(index.getTable() == this) {
417                     throw Message.getInternalError("index not dropped: "+ index.getName());
418                 }
419             }
420         }
421         scanIndex.remove(session);
422         scanIndex = null;
423         lockExclusive = null;
424         lockShared = null;
425         invalidate();
426     }
427
428     public void checkRename() throws SQLException JavaDoc {
429     }
430
431     public void checkSupportAlter() throws SQLException JavaDoc {
432     }
433
434     public boolean canTruncate() {
435         ObjectArray constraints = getConstraints();
436         for(int i=0; constraints!=null && i<constraints.size(); i++) {
437             Constraint c = (Constraint) constraints.get(i);
438             if(!(c.getConstraintType().equals(Constraint.REFERENTIAL))) {
439                 continue;
440             }
441             return false;
442         }
443         return true;
444     }
445
446     public String JavaDoc getTableType() {
447         return Table.TABLE;
448     }
449
450     public void setGlobalTemporary(boolean globalTemporary) {
451         this.globalTemporary = globalTemporary;
452     }
453
454     public boolean getGlobalTemporary() {
455         return globalTemporary;
456     }
457
458     public boolean isOnCommitDrop() {
459         return onCommitDrop;
460     }
461
462     public void setOnCommitDrop(boolean onCommitDrop) {
463         this.onCommitDrop = onCommitDrop;
464     }
465
466     public boolean isOnCommitTruncate() {
467         return onCommitTruncate;
468     }
469
470     public void setOnCommitTruncate(boolean onCommitTruncate) {
471         this.onCommitTruncate = onCommitTruncate;
472     }
473
474     public long getMaxDataModificationId() {
475         return lastModificationId;
476     }
477
478 }
479
Popular Tags