001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.db.table;
031:
032: import com.caucho.db.Database;
033: import com.caucho.db.index.BTree;
034: import com.caucho.db.index.KeyCompare;
035: import com.caucho.db.sql.CreateQuery;
036: import com.caucho.db.sql.Expr;
037: import com.caucho.db.sql.Parser;
038: import com.caucho.db.sql.QueryContext;
039: import com.caucho.db.store.Block;
040: import com.caucho.db.store.Lock;
041: import com.caucho.db.store.Store;
042: import com.caucho.db.store.Transaction;
043: import com.caucho.log.Log;
044: import com.caucho.sql.SQLExceptionWrapper;
045: import com.caucho.util.L10N;
046: import com.caucho.vfs.Path;
047: import com.caucho.vfs.ReadStream;
048: import com.caucho.vfs.TempBuffer;
049: import com.caucho.vfs.TempStream;
050: import com.caucho.vfs.WriteStream;
051:
052: import java.io.IOException;
053: import java.sql.SQLException;
054: import java.util.ArrayList;
055: import java.util.logging.Level;
056: import java.util.logging.Logger;
057:
058: /**
059: * Table format:
060: *
061: * <pre>
062: * Block 0: allocation table
063: * Block 1: fragment table
064: * Block 2: table definition
065: * 0 - store data
066: * 1024 - table data
067: * 1024 - index pointers
068: * 2048 - CREATE text
069: * Block 3: first data
070: * </pre>
071: */
072: public class Table extends Store {
073: private final static Logger log = Log.open(Table.class);
074: private final static L10N L = new L10N(Table.class);
075:
076: private final static int ROOT_DATA_OFFSET = STORE_CREATE_END;
077: private final static int INDEX_ROOT_OFFSET = ROOT_DATA_OFFSET + 32;
078:
079: private final static int ROOT_DATA_END = ROOT_DATA_OFFSET + 1024;
080:
081: public final static int INLINE_BLOB_SIZE = 120;
082:
083: public final static long ROW_CLOCK_MIN = 1024;
084:
085: public final static byte ROW_VALID = 0x1;
086: public final static byte ROW_ALLOC = 0x2;
087: public final static byte ROW_MASK = 0x3;
088:
089: private final static String DB_VERSION = "Resin-DB 3.1.1";
090: private final static String MIN_VERSION = "Resin-DB 3.1.1";
091:
092: private final Row _row;
093:
094: private final int _rowLength;
095: private final int _rowsPerBlock;
096: private final int _rowEnd;
097:
098: private final Constraint[] _constraints;
099:
100: private final Column _autoIncrementColumn;
101:
102: private long _entries;
103:
104: private final Object _rowClockLock = new Object();
105: private long _rowClockAddr;
106: private long _rowClockTotal;
107: private long _rowClockUsed;
108: private int _rowClockCount;
109: private int _rowAllocCount;
110:
111: private long _autoIncrementValue = -1;
112:
113: private Lock _allocLock;
114: private Lock _insertLock;
115:
116: Table(Database database, String name, Row row,
117: Constraint constraints[]) {
118: super (database, name, null);
119:
120: _row = row;
121: _constraints = constraints;
122:
123: _rowLength = _row.getLength();
124: _rowsPerBlock = BLOCK_SIZE / _rowLength;
125: _rowEnd = _rowLength * _rowsPerBlock;
126:
127: _rowClockAddr = 0;
128:
129: Column[] columns = _row.getColumns();
130: Column autoIncrementColumn = null;
131: for (int i = 0; i < columns.length; i++) {
132: columns[i].setTable(this );
133:
134: if (columns[i].getAutoIncrement() >= 0)
135: autoIncrementColumn = columns[i];
136: }
137: _autoIncrementColumn = autoIncrementColumn;
138:
139: _insertLock = new Lock("table-insert:" + name);
140: _allocLock = new Lock("table-alloc:" + name);
141: }
142:
143: Row getRow() {
144: return _row;
145: }
146:
147: /**
148: * Returns the length of a row.
149: */
150: int getRowLength() {
151: return _rowLength;
152: }
153:
154: /**
155: * Returns the end of the row
156: */
157: int getRowEnd() {
158: return _rowEnd;
159: }
160:
161: public final Column[] getColumns() {
162: return _row.getColumns();
163: }
164:
165: /**
166: * Returns the table's constraints.
167: */
168: public final Constraint[] getConstraints() {
169: return _constraints;
170: }
171:
172: /**
173: * Returns the auto-increment column.
174: */
175: public Column getAutoIncrementColumn() {
176: return _autoIncrementColumn;
177: }
178:
179: /**
180: * Returns the column for the given column name.
181: *
182: * @param name the column name
183: *
184: * @return the column
185: */
186: public Column getColumn(String name) {
187: Column[] columns = getColumns();
188:
189: for (int i = 0; i < columns.length; i++) {
190: if (columns[i].getName().equals(name))
191: return columns[i];
192: }
193:
194: return null;
195: }
196:
197: /**
198: * Returns the column index for the given column name.
199: *
200: * @param name the column name
201: *
202: * @return the column index.
203: */
204: public int getColumnIndex(String name) throws SQLException {
205: Column[] columns = getColumns();
206:
207: for (int i = 0; i < columns.length; i++) {
208: if (columns[i].getName().equals(name))
209: return i;
210: }
211:
212: return -1;
213: }
214:
215: /**
216: * Loads the table from the file.
217: */
218: public static Table loadFromFile(Database db, String name)
219: throws IOException, SQLException {
220: Path path = db.getPath().lookup(name + ".db");
221:
222: if (!path.exists())
223: throw new IOException(L.l("table {0} does not exist", name));
224:
225: String version = null;
226:
227: ReadStream is = path.openRead();
228: try {
229: // skip allocation table and fragment table
230: is.skip(DATA_START + ROOT_DATA_OFFSET);
231:
232: StringBuilder sb = new StringBuilder();
233: int ch;
234:
235: while ((ch = is.read()) > 0) {
236: sb.append((char) ch);
237: }
238:
239: version = sb.toString();
240:
241: if (!version.startsWith("Resin-DB")) {
242: throw new SQLException(L.l(
243: "table {0} is not a Resin DB. Version '{1}'",
244: name, version));
245: } else if (version.compareTo(MIN_VERSION) < 0
246: || DB_VERSION.compareTo(version) < 0) {
247: throw new SQLException(L.l(
248: "table {0} is out of date. Old version {1}.",
249: name, version));
250: }
251: } finally {
252: is.close();
253: }
254:
255: is = path.openRead();
256: try {
257: // skip allocation table and fragment table
258: is.skip(DATA_START + ROOT_DATA_END);
259:
260: StringBuilder cb = new StringBuilder();
261:
262: int ch;
263: while ((ch = is.read()) > 0) {
264: cb.append((char) ch);
265: }
266:
267: String sql = cb.toString();
268:
269: if (log.isLoggable(Level.FINER))
270: log.finer("Table[" + name + "] " + version
271: + " loading\n" + sql);
272:
273: try {
274: CreateQuery query = (CreateQuery) Parser.parse(db, sql);
275:
276: TableFactory factory = query.getFactory();
277:
278: if (!factory.getName().equalsIgnoreCase(name))
279: throw new IOException(L.l(
280: "factory {0} does not match", name));
281:
282: Table table = new Table(db, factory.getName(), factory
283: .getRow(), factory.getConstraints());
284:
285: table.init();
286:
287: table.clearIndexes();
288: table.initIndexes();
289: table.rebuildIndexes();
290:
291: return table;
292: } catch (Exception e) {
293: log.log(Level.FINE, e.toString(), e);
294:
295: log.warning(e.toString());
296:
297: throw new SQLException(L.l(
298: "can't load table {0} in {1}.\n{2}", name, path
299: .getNativePath(), e.toString()));
300: }
301: } finally {
302: is.close();
303: }
304: }
305:
306: /**
307: * Creates the table.
308: */
309: public void create() throws IOException, SQLException {
310: super .create();
311:
312: initIndexes();
313:
314: byte[] tempBuffer = new byte[BLOCK_SIZE];
315:
316: readBlock(BLOCK_SIZE, tempBuffer, 0, BLOCK_SIZE);
317:
318: TempStream ts = new TempStream();
319:
320: WriteStream os = new WriteStream(ts);
321:
322: try {
323: for (int i = 0; i < ROOT_DATA_OFFSET; i++)
324: os.write(tempBuffer[i]);
325:
326: writeTableHeader(os);
327: } finally {
328: os.close();
329: }
330:
331: TempBuffer head = ts.getHead();
332: int offset = 0;
333: for (; head != null; head = head.getNext()) {
334: byte[] buffer = head.getBuffer();
335:
336: int length = head.getLength();
337:
338: System.arraycopy(buffer, 0, tempBuffer, offset, length);
339:
340: for (; length < buffer.length; length++) {
341: tempBuffer[offset + length] = 0;
342: }
343:
344: offset += buffer.length;
345: }
346:
347: for (; offset < BLOCK_SIZE; offset++)
348: tempBuffer[offset] = 0;
349:
350: writeBlock(BLOCK_SIZE, tempBuffer, 0, BLOCK_SIZE);
351:
352: _database.addTable(this );
353: }
354:
355: /**
356: * Initialize the indexes
357: */
358: private void initIndexes() throws IOException, SQLException {
359: Column[] columns = _row.getColumns();
360: for (int i = 0; i < columns.length; i++) {
361: Column column = columns[i];
362:
363: if (!column.isUnique())
364: continue;
365:
366: KeyCompare keyCompare = column.getIndexKeyCompare();
367:
368: if (keyCompare == null)
369: continue;
370:
371: Block rootBlock = allocateIndexBlock();
372: long rootBlockId = rootBlock.getBlockId();
373: rootBlock.free();
374:
375: BTree btree = new BTree(this , rootBlockId, column
376: .getLength(), keyCompare);
377:
378: column.setIndex(btree);
379: }
380: }
381:
382: /**
383: * Clears the indexes
384: */
385: private void clearIndexes() throws IOException {
386: Column[] columns = _row.getColumns();
387:
388: for (int i = 0; i < columns.length; i++) {
389: BTree index = columns[i].getIndex();
390:
391: if (index == null)
392: continue;
393:
394: long rootAddr = index.getIndexRoot();
395:
396: Block block = readBlock(addressToBlockId(rootAddr));
397:
398: try {
399: byte[] blockBuffer = block.getBuffer();
400:
401: synchronized (blockBuffer) {
402: for (int j = 0; j < blockBuffer.length; j++) {
403: blockBuffer[j] = 0;
404: }
405:
406: block.setDirty(0, BLOCK_SIZE);
407: }
408: } finally {
409: block.free();
410: }
411: }
412:
413: long blockAddr = 0;
414:
415: while ((blockAddr = firstBlock(blockAddr + BLOCK_SIZE,
416: ALLOC_INDEX)) > 0) {
417: freeBlock(blockAddr);
418: }
419: }
420:
421: /**
422: * Rebuilds the indexes
423: */
424: private void rebuildIndexes() throws IOException, SQLException {
425: Transaction xa = Transaction.create();
426: xa.setAutoCommit(true);
427:
428: try {
429: TableIterator iter = createTableIterator();
430:
431: iter.init(xa);
432:
433: Column[] columns = _row.getColumns();
434:
435: while (iter.nextBlock()) {
436: iter.initRow();
437:
438: byte[] blockBuffer = iter.getBuffer();
439:
440: while (iter.nextRow()) {
441: long rowAddress = iter.getRowAddress();
442: int rowOffset = iter.getRowOffset();
443:
444: for (int i = 0; i < columns.length; i++) {
445: Column column = columns[i];
446:
447: column.setIndex(xa, blockBuffer, rowOffset,
448: rowAddress, null);
449: }
450: }
451: }
452: } finally {
453: xa.commit();
454: }
455: }
456:
457: private void writeTableHeader(WriteStream os) throws IOException {
458: os.print(DB_VERSION);
459: os.write(0);
460:
461: while (os.getBufferOffset() < INDEX_ROOT_OFFSET)
462: os.write(0);
463:
464: Column[] columns = _row.getColumns();
465: for (int i = 0; i < columns.length; i++) {
466: if (!columns[i].isUnique())
467: continue;
468:
469: BTree index = columns[i].getIndex();
470:
471: if (index != null) {
472: writeLong(os, index.getIndexRoot());
473: } else {
474: writeLong(os, 0);
475: }
476: }
477:
478: while (os.getBufferOffset() < ROOT_DATA_END)
479: os.write(0);
480:
481: os.print("CREATE TABLE " + getName() + "(");
482: for (int i = 0; i < _row.getColumns().length; i++) {
483: Column column = _row.getColumns()[i];
484:
485: if (i != 0)
486: os.print(",");
487:
488: os.print(column.getName());
489: os.print(" ");
490:
491: switch (column.getTypeCode()) {
492: case Column.VARCHAR:
493: os
494: .print("VARCHAR(" + column.getDeclarationSize()
495: + ")");
496: break;
497: case Column.VARBINARY:
498: os.print("VARBINARY(" + column.getDeclarationSize()
499: + ")");
500: break;
501: case Column.INT:
502: os.print("INTEGER");
503: break;
504: case Column.LONG:
505: os.print("BIGINT");
506: break;
507: case Column.DOUBLE:
508: os.print("DOUBLE");
509: break;
510: case Column.DATE:
511: os.print("TIMESTAMP");
512: break;
513: case Column.BLOB:
514: os.print("BLOB");
515: break;
516: case Column.NUMERIC: {
517: NumericColumn numeric = (NumericColumn) column;
518:
519: os.print("NUMERIC(" + numeric.getPrecision() + ","
520: + numeric.getScale() + ")");
521: break;
522: }
523: default:
524: throw new UnsupportedOperationException();
525: }
526:
527: if (column.isPrimaryKey())
528: os.print(" PRIMARY KEY");
529: else if (column.isUnique())
530: os.print(" UNIQUE");
531:
532: if (column.isNotNull())
533: os.print(" NOT NULL");
534:
535: Expr defaultExpr = column.getDefault();
536:
537: if (defaultExpr != null) {
538: os.print(" DEFAULT (");
539: os.print(defaultExpr);
540: os.print(")");
541: }
542:
543: if (column.getAutoIncrement() >= 0)
544: os.print(" auto_increment");
545: }
546: os.print(")");
547:
548: /*
549: writeLong(os, _blockMax);
550: writeLong(os, _entries);
551: writeLong(os, _clockAddr);
552: */
553: }
554:
555: public TableIterator createTableIterator() {
556: assertStoreActive();
557:
558: return new TableIterator(this );
559: }
560:
561: /**
562: * Returns the next auto-increment value.
563: */
564: public long nextAutoIncrement(QueryContext context)
565: throws SQLException {
566: synchronized (this ) {
567: if (_autoIncrementValue >= 0)
568: return ++_autoIncrementValue;
569: }
570:
571: long max = 0;
572:
573: try {
574: TableIterator iter = createTableIterator();
575: iter.init(context);
576: while (iter.next()) {
577: byte[] buffer = iter.getBuffer();
578:
579: long value = _autoIncrementColumn.getLong(buffer, iter
580: .getRowOffset());
581:
582: if (max < value)
583: max = value;
584: }
585: } catch (IOException e) {
586: throw new SQLExceptionWrapper(e);
587: }
588:
589: synchronized (this ) {
590: if (_autoIncrementValue < max)
591: _autoIncrementValue = max;
592:
593: return ++_autoIncrementValue;
594: }
595: }
596:
597: /**
598: * Inserts a new row, returning the row address.
599: */
600: public long insert(QueryContext queryContext, Transaction xa,
601: ArrayList<Column> columns, ArrayList<Expr> values)
602: throws IOException, SQLException {
603: if (log.isLoggable(Level.FINEST))
604: log
605: .finest("db table " + getName() + " insert row xa:"
606: + xa);
607:
608: Block block = null;
609:
610: try {
611: long addr;
612: int rowOffset = 0;
613:
614: boolean isLoop = false;
615: boolean hasRow = false;
616:
617: int rowClockCount = 0;
618: long rowClockAddr = 0;
619: long rowClockUsed = 0;
620: long rowClockTotal = 0;
621:
622: do {
623: long blockId = 0;
624:
625: if (block != null) {
626: block.free();
627: block = null;
628: }
629:
630: synchronized (_rowClockLock) {
631: blockId = firstRow(_rowClockAddr);
632:
633: if (blockId >= 0) {
634: } else if (!isLoop
635: && (ROW_CLOCK_MIN < _rowClockTotal
636: && 4 * _rowClockUsed < 3 * _rowClockTotal || _rowAllocCount > 8)) {
637: // System.out.println("LOOP: used:" + _rowClockUsed + " total:" + _rowClockTotal + " frac:" + (double) _rowClockUsed / (double) (_rowClockTotal + 0.01));
638: // go around loop if there are sufficient entries, i.e. over
639: // ROW_CLOCK_MIN and at least 1/4 free entries.
640: isLoop = true;
641: _rowClockCount = 0;
642: _rowClockAddr = 0;
643: _rowClockUsed = 0;
644: _rowClockTotal = 0;
645: _rowAllocCount = 0;
646: continue;
647: } else {
648: //System.out.println("ROW: used:" + _rowClockUsed + " total:" + _rowClockTotal + " frac:" + (double) _rowClockUsed / (double) (_rowClockTotal + 0.01));
649:
650: _rowAllocCount++;
651:
652: // if no free row is available, allocate a new one
653: block = xa.allocateRow(this );
654: //System.out.println("ALLOC: " + block);
655:
656: blockId = block.getBlockId();
657: }
658:
659: rowClockCount = _rowClockCount;
660: rowClockAddr = blockIdToAddress(blockId);
661: rowClockUsed = _rowClockUsed;
662: rowClockTotal = _rowClockTotal;
663:
664: // the next insert will try the following block
665: _rowClockCount++;
666: _rowClockAddr = rowClockAddr + BLOCK_SIZE;
667: _rowClockUsed = rowClockUsed + _rowsPerBlock;
668: _rowClockTotal = rowClockTotal + _rowsPerBlock;
669: }
670:
671: if (block == null)
672: block = xa.readBlock(this , blockId);
673:
674: Lock blockLock = block.getLock();
675:
676: if (xa.lockReadAndWriteNoWait(blockLock)) {
677: try {
678: rowOffset = 0;
679:
680: byte[] buffer = block.getBuffer();
681:
682: for (; rowOffset < _rowEnd; rowOffset += _rowLength) {
683: if (buffer[rowOffset] == 0) {
684: block
685: .setDirty(rowOffset,
686: rowOffset + 1);
687:
688: hasRow = true;
689: buffer[rowOffset] = ROW_ALLOC;
690: break;
691: }
692: }
693: } finally {
694: xa.unlockReadAndWrite(blockLock);
695: }
696: }
697: } while (!hasRow);
698:
699: insertRow(queryContext, xa, columns, values, block,
700: rowOffset);
701:
702: synchronized (_rowClockLock) {
703: if (rowClockCount < _rowClockCount) {
704: // the next insert will retry this block
705: int blocks = _rowClockCount - rowClockCount;
706:
707: _rowClockCount = rowClockCount;
708: _rowClockAddr = rowClockAddr;
709: _rowClockUsed -= blocks * _rowsPerBlock;
710: _rowClockTotal -= blocks * _rowsPerBlock;
711: }
712: }
713:
714: return blockIdToAddress(block.getBlockId(), rowOffset);
715: } finally {
716: if (block != null)
717: block.free();
718: }
719: }
720:
721: public void insertRow(QueryContext queryContext, Transaction xa,
722: ArrayList<Column> columns, ArrayList<Expr> values,
723: Block block, int rowOffset) throws SQLException {
724: byte[] buffer = block.getBuffer();
725:
726: long rowAddr = blockIdToAddress(block.getBlockId(), rowOffset);
727: //System.out.println("ADDR:" + rowAddr + " " + rowOffset + " " + block);
728:
729: TableIterator iter = createTableIterator();
730: TableIterator[] iterSet = new TableIterator[] { iter };
731: // QueryContext context = QueryContext.allocate();
732: queryContext.init(xa, iterSet, true);
733: iter.init(queryContext);
734:
735: boolean isOkay = false;
736: queryContext.lock();
737: try {
738: iter.setRow(block, rowOffset);
739:
740: block.setDirty(rowOffset, rowOffset + _rowLength);
741:
742: for (int i = rowOffset + _rowLength - 1; rowOffset < i; i--)
743: buffer[i] = 0;
744:
745: for (int i = 0; i < columns.size(); i++) {
746: Column column = columns.get(i);
747: Expr value = values.get(i);
748:
749: column.setExpr(xa, buffer, rowOffset, value,
750: queryContext);
751: }
752:
753: // lock for insert, i.e. entries, indices, and validation
754: // XXX: the set index needs to handle the validation
755: //xa.lockWrite(_insertLock);
756: try {
757: validate(block, rowOffset, queryContext, xa);
758:
759: buffer[rowOffset] = (byte) ((buffer[rowOffset] & ~ROW_MASK) | ROW_VALID);
760:
761: for (int i = 0; i < columns.size(); i++) {
762: Column column = columns.get(i);
763: Expr value = values.get(i);
764:
765: column.setIndex(xa, buffer, rowOffset, rowAddr,
766: queryContext);
767: }
768:
769: xa.addUpdateBlock(block);
770:
771: if (_autoIncrementColumn != null) {
772: long value = _autoIncrementColumn.getLong(buffer,
773: rowOffset);
774:
775: synchronized (this ) {
776: if (_autoIncrementValue < value)
777: _autoIncrementValue = value;
778: }
779: }
780:
781: _entries++;
782:
783: isOkay = true;
784: } finally {
785: // xa.unlockWrite(_insertLock);
786:
787: if (!isOkay)
788: delete(xa, block, buffer, rowOffset);
789: }
790: } finally {
791: queryContext.unlock();
792: }
793: }
794:
795: /**
796: * Validates the given row.
797: */
798: private void validate(Block block, int rowOffset,
799: QueryContext queryContext, Transaction xa)
800: throws SQLException {
801: TableIterator row = createTableIterator();
802: TableIterator[] rows = new TableIterator[] { row };
803:
804: row.setRow(block, rowOffset);
805:
806: for (int i = 0; i < _constraints.length; i++) {
807: _constraints[i].validate(rows, queryContext, xa);
808: }
809: }
810:
811: void delete(Transaction xa, Block block, byte[] buffer,
812: int rowOffset) throws SQLException {
813: byte rowState = buffer[rowOffset];
814:
815: if ((rowState & ROW_MASK) != ROW_VALID)
816: return;
817:
818: buffer[rowOffset] = (byte) ((rowState & ~ROW_MASK) | ROW_ALLOC);
819:
820: Column[] columns = _row.getColumns();
821:
822: for (int i = 0; i < columns.length; i++) {
823: columns[i].delete(xa, buffer, rowOffset);
824: }
825:
826: buffer[rowOffset] = 0;
827:
828: synchronized (_rowClockLock) {
829: long addr = blockIdToAddress(block.getBlockId());
830:
831: if (addr <= _rowClockAddr) {
832: _rowClockUsed--;
833: }
834: }
835: }
836:
837: private void writeLong(WriteStream os, long value)
838: throws IOException {
839: os.write((int) (value >> 56));
840: os.write((int) (value >> 48));
841: os.write((int) (value >> 40));
842: os.write((int) (value >> 32));
843: os.write((int) (value >> 24));
844: os.write((int) (value >> 16));
845: os.write((int) (value >> 8));
846: os.write((int) value);
847: }
848:
849: private void setLong(byte[] buffer, int offset, long value)
850: throws IOException {
851: buffer[offset + 0] = (byte) (value >> 56);
852: buffer[offset + 1] = (byte) (value >> 48);
853: buffer[offset + 2] = (byte) (value >> 40);
854: buffer[offset + 3] = (byte) (value >> 32);
855: buffer[offset + 4] = (byte) (value >> 24);
856: buffer[offset + 5] = (byte) (value >> 16);
857: buffer[offset + 6] = (byte) (value >> 8);
858: buffer[offset + 7] = (byte) (value);
859: }
860:
861: private long getLong(byte[] buffer, int offset) throws IOException {
862: long value = (((buffer[offset + 0] & 0xffL) << 56)
863: + ((buffer[offset + 1] & 0xffL) << 48)
864: + ((buffer[offset + 2] & 0xffL) << 40)
865: + ((buffer[offset + 3] & 0xffL) << 32) +
866:
867: ((buffer[offset + 4] & 0xffL) << 24)
868: + ((buffer[offset + 5] & 0xffL) << 16)
869: + ((buffer[offset + 6] & 0xffL) << 8) + ((buffer[offset + 7] & 0xffL)));
870:
871: return value;
872: }
873:
874: public String toString() {
875: return "Table[" + getName() + ":" + getId() + "]";
876: }
877: }
|