1 5 package org.h2.table; 6 7 import java.sql.SQLException ; 8 import java.util.Comparator ; 9 import java.util.HashSet ; 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 38 public class TableData extends Table implements RecordReader { 39 private ScanIndex scanIndex; 40 private int rowCount; 41 private Session lockExclusive; 42 private HashSet lockShared = new HashSet (); 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 tableName, int id, ObjectArray columns, 50 boolean persistent) throws SQLException { 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 { 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 { 68 return scanIndex.getRow(key); 69 } 70 71 public void addRow(Session session, Row row) throws SQLException { 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 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 e2) { 99 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 indexName, int indexId, Column[] cols, IndexType indexType, int headPos, String indexComment) 127 throws SQLException { 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 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 e) { 175 try { 176 index.remove(session); 177 } catch(SQLException e2) { 178 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 if(index.getIndexType().isPersistent() && !database.getReadOnly() && !database.getLog().containsInDoubtTransactions()) { 195 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 { 209 final Index idx = index; 210 try { 211 list.sort(new Comparator () { 212 public int compare(Object o1, Object o2) { 213 Row r1 = (Row) o1; 214 Row r2 = (Row) o2; 215 try { 216 return idx.compareRows(r1, r2); 217 } catch(SQLException e) { 218 throw Message.convertToInternal(e); 219 } 220 } 221 }); 222 } catch(Exception 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 { 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 { 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 { 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 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 e) { 327 } 329 } 330 } 331 } 332 333 private void traceLock(Session session, boolean exclusive, String s) { 334 if(traceLock.debug()) { 335 traceLock.debug(session.getId()+" "+(exclusive?"xlock":"slock") + " " + s+" "+getName()); 336 } 337 } 338 339 public String getCreateSQL() { 340 StringBuffer buff = new StringBuffer (); 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 synchronized(database) { 385 database.notifyAll(); 386 } 387 } 388 } 389 390 public Record read(DataPage s) throws SQLException { 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 { 404 super.removeChildrenAndResources(session); 405 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 { 429 } 430 431 public void checkSupportAlter() throws SQLException { 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 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 |