Source Code Cross Referenced for DiskFile.java in  » Database-DBMS » h2database » org » h2 » store » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Database DBMS » h2database » org.h2.store 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
0003:         * (http://h2database.com/html/license.html).
0004:         * Initial Developer: H2 Group
0005:         */
0006:        package org.h2.store;
0007:
0008:        import java.io.ByteArrayInputStream;
0009:        import java.io.ByteArrayOutputStream;
0010:        import java.io.DataInputStream;
0011:        import java.io.DataOutputStream;
0012:        import java.io.IOException;
0013:        import java.io.OutputStream;
0014:        import java.sql.SQLException;
0015:        import java.util.Comparator;
0016:        import java.util.HashSet;
0017:        import java.util.Iterator;
0018:
0019:        import org.h2.api.DatabaseEventListener;
0020:        import org.h2.constant.ErrorCode;
0021:        import org.h2.constant.SysProperties;
0022:        import org.h2.engine.Constants;
0023:        import org.h2.engine.Database;
0024:        import org.h2.engine.Session;
0025:        import org.h2.log.LogSystem;
0026:        import org.h2.log.RedoLogRecord;
0027:        import org.h2.message.Message;
0028:        import org.h2.message.Trace;
0029:        import org.h2.util.BitField;
0030:        import org.h2.util.Cache;
0031:        import org.h2.util.Cache2Q;
0032:        import org.h2.util.CacheLRU;
0033:        import org.h2.util.CacheObject;
0034:        import org.h2.util.CacheWriter;
0035:        import org.h2.util.FileUtils;
0036:        import org.h2.util.IntArray;
0037:        import org.h2.util.MathUtils;
0038:        import org.h2.util.ObjectArray;
0039:        import org.h2.util.ObjectUtils;
0040:
0041:        /**
0042:         * This class represents a file that is usually written to disk. The two main
0043:         * files are .data.db and .index.db. For each such file, a number of
0044:         * {@link Storage} objects exists. The disk file is responsible for caching;
0045:         * each object contains a {@link Cache} object. Changes in the file are logged
0046:         * in a {@link LogSystem} object. Reading and writing to the file is delegated
0047:         * to the {@link FileStore} class.
0048:         * <p>
0049:         * There are 'blocks' of 128 bytes (DiskFile.BLOCK_SIZE). Each objects own one
0050:         * or more pages; each page size is 64 blocks (DiskFile.BLOCKS_PER_PAGE). That
0051:         * is 8 KB page size. However pages are not read or written as one unit; only
0052:         * individual objects (multiple blocks at a time) are read or written.
0053:         * <p>
0054:         * Currently there are no in-place updates. Each row occupies one or multiple
0055:         * blocks. Row can occupy multiple pages. Rows are always contiguous (except
0056:         * LOBs, they are stored in their own files).
0057:         */
0058:        public class DiskFile implements  CacheWriter {
0059:
0060:            /**
0061:             * The size of a block in bytes.
0062:             * A block is the minimum row size.
0063:             */
0064:            public static final int BLOCK_SIZE = 128;
0065:
0066:            /**
0067:             * The size of a page in blocks.
0068:             * Each page contains blocks from the same storage.
0069:             */
0070:            static final int BLOCK_PAGE_PAGE_SHIFT = 6;
0071:            public static final int BLOCKS_PER_PAGE = 1 << BLOCK_PAGE_PAGE_SHIFT;
0072:            public static final int OFFSET = FileStore.HEADER_LENGTH;
0073:            static final int FREE_PAGE = -1;
0074:            // TODO storage: header should probably be 4 KB or so 
0075:            // (to match block size of operating system)
0076:            private Database database;
0077:            private String fileName;
0078:            private FileStore file;
0079:            private BitField used;
0080:            private BitField deleted;
0081:            private HashSet potentiallyFreePages;
0082:            private int fileBlockCount;
0083:            private IntArray pageOwners;
0084:            private Cache cache;
0085:            private LogSystem log;
0086:            private DataPage rowBuff;
0087:            private DataPage freeBlock;
0088:            private boolean dataFile;
0089:            private boolean logChanges;
0090:            private int recordOverhead;
0091:            private boolean init, initAlreadyTried;
0092:            private ObjectArray redoBuffer;
0093:            private int redoBufferSize;
0094:            private int readCount, writeCount;
0095:            private String mode;
0096:            private int nextDeleteId = 1;
0097:
0098:            public DiskFile(Database database, String fileName, String mode,
0099:                    boolean dataFile, boolean logChanges, int cacheSize)
0100:                    throws SQLException {
0101:                reset();
0102:                this .database = database;
0103:                this .log = database.getLog();
0104:                this .fileName = fileName;
0105:                this .mode = mode;
0106:                this .dataFile = dataFile;
0107:                this .logChanges = logChanges;
0108:                String cacheType = database.getCacheType();
0109:                if (Cache2Q.TYPE_NAME.equals(cacheType)) {
0110:                    this .cache = new Cache2Q(this , cacheSize);
0111:                } else {
0112:                    this .cache = new CacheLRU(this , cacheSize);
0113:                }
0114:                rowBuff = DataPage.create(database, BLOCK_SIZE);
0115:                // TODO: the overhead is larger in the log file, so this value is too high :-(
0116:                recordOverhead = 4 * rowBuff.getIntLen() + 1
0117:                        + rowBuff.getFillerLength();
0118:                freeBlock = DataPage.create(database, BLOCK_SIZE);
0119:                freeBlock.fill(BLOCK_SIZE);
0120:                freeBlock.updateChecksum();
0121:                try {
0122:                    if (FileUtils.exists(fileName)) {
0123:                        file = database.openFile(fileName, mode, true);
0124:                        long length = file.length();
0125:                        database.notifyFileSize(length);
0126:                        int blocks = (int) ((length - OFFSET) / BLOCK_SIZE);
0127:                        setBlockCount(blocks);
0128:                    } else {
0129:                        create();
0130:                    }
0131:                } catch (SQLException e) {
0132:                    close();
0133:                    throw e;
0134:                }
0135:            }
0136:
0137:            private void reset() {
0138:                used = new BitField();
0139:                deleted = new BitField();
0140:                pageOwners = new IntArray();
0141:                // init pageOwners
0142:                setBlockCount(fileBlockCount);
0143:                redoBuffer = new ObjectArray();
0144:                potentiallyFreePages = new HashSet();
0145:            }
0146:
0147:            private void setBlockCount(int count) {
0148:                fileBlockCount = count;
0149:                int pages = getPage(count);
0150:                while (pages >= pageOwners.size()) {
0151:                    pageOwners.add(FREE_PAGE);
0152:                }
0153:            }
0154:
0155:            int getBlockCount() {
0156:                return fileBlockCount;
0157:            }
0158:
0159:            private void create() throws SQLException {
0160:                file = database.openFile(fileName, mode, false);
0161:                DataPage header = DataPage.create(database, OFFSET);
0162:                file.seek(FileStore.HEADER_LENGTH);
0163:                header.fill(OFFSET);
0164:                header.updateChecksum();
0165:                file.write(header.getBytes(), 0, OFFSET);
0166:            }
0167:
0168:            private void freeUnusedPages() throws SQLException {
0169:                for (int i = 0; i < pageOwners.size(); i++) {
0170:                    if (pageOwners.get(i) != FREE_PAGE && isPageFree(i)) {
0171:                        setPageOwner(i, FREE_PAGE);
0172:                    }
0173:                }
0174:            }
0175:
0176:            public byte[] getSummary() throws SQLException {
0177:                synchronized (database) {
0178:                    try {
0179:                        ByteArrayOutputStream buff = new ByteArrayOutputStream();
0180:                        DataOutputStream out = new DataOutputStream(buff);
0181:                        int blocks = (int) ((file.length() - OFFSET) / BLOCK_SIZE);
0182:                        out.writeInt(blocks);
0183:                        for (int i = 0, x = 0; i < blocks / 8; i++) {
0184:                            int mask = 0;
0185:                            for (int j = 0; j < 8; j++) {
0186:                                if (used.get(x)) {
0187:                                    mask |= 1 << j;
0188:                                }
0189:                                x++;
0190:                            }
0191:                            out.write(mask);
0192:                        }
0193:                        out.writeInt(pageOwners.size());
0194:                        ObjectArray storages = new ObjectArray();
0195:                        for (int i = 0; i < pageOwners.size(); i++) {
0196:                            int s = pageOwners.get(i);
0197:                            out.writeInt(s);
0198:                            if (s >= 0
0199:                                    && (s >= storages.size() || storages.get(s) == null)) {
0200:                                Storage storage = database.getStorage(s, this );
0201:                                while (storages.size() <= s) {
0202:                                    storages.add(null);
0203:                                }
0204:                                storages.set(s, storage);
0205:                            }
0206:                        }
0207:                        for (int i = 0; i < storages.size(); i++) {
0208:                            Storage storage = (Storage) storages.get(i);
0209:                            if (storage != null) {
0210:                                out.writeInt(i);
0211:                                out.writeInt(storage.getRecordCount());
0212:                            }
0213:                        }
0214:                        out.writeInt(-1);
0215:                        out.close();
0216:                        byte[] b2 = buff.toByteArray();
0217:                        return b2;
0218:                    } catch (IOException e) {
0219:                        // will probably never happen, because only in-memory structures are
0220:                        // used
0221:                        return null;
0222:                    }
0223:                }
0224:            }
0225:
0226:            boolean isPageFree(int page) {
0227:                for (int i = page * BLOCKS_PER_PAGE; i < (page + 1)
0228:                        * BLOCKS_PER_PAGE; i++) {
0229:                    if (used.get(i)) {
0230:                        return false;
0231:                    }
0232:                }
0233:                return true;
0234:            }
0235:
0236:            public void initFromSummary(byte[] summary) {
0237:                synchronized (database) {
0238:                    if (summary == null || summary.length == 0) {
0239:                        ObjectArray list = database.getAllStorages();
0240:                        for (int i = 0; i < list.size(); i++) {
0241:                            Storage s = (Storage) list.get(i);
0242:                            if (s != null && s.getDiskFile() == this ) {
0243:                                database.removeStorage(s.getId(), this );
0244:                            }
0245:                        }
0246:                        reset();
0247:                        initAlreadyTried = false;
0248:                        init = false;
0249:                        return;
0250:                    }
0251:                    if (database.getRecovery()
0252:                            || (initAlreadyTried && (!dataFile || !SysProperties.CHECK))) {
0253:                        return;
0254:                    }
0255:                    initAlreadyTried = true;
0256:                    int stage = 0;
0257:                    try {
0258:                        DataInputStream in = new DataInputStream(
0259:                                new ByteArrayInputStream(summary));
0260:                        int b2 = in.readInt();
0261:                        if (b2 > fileBlockCount) {
0262:                            database.getTrace(Trace.DATABASE).info(
0263:                                    "unexpected size " + b2
0264:                                            + " when initializing summary for "
0265:                                            + fileName + " expected:"
0266:                                            + fileBlockCount);
0267:                            return;
0268:                        }
0269:                        stage++;
0270:                        for (int i = 0, x = 0; i < b2 / 8; i++) {
0271:                            int mask = in.read();
0272:                            if (init) {
0273:                                for (int j = 0; j < 8; j++, x++) {
0274:                                    if (used.get(x) != ((mask & (1 << j)) != 0)) {
0275:                                        throw Message
0276:                                                .getInternalError("Redo failure, block: "
0277:                                                        + x
0278:                                                        + " expected in-use bit: "
0279:                                                        + used.get(x));
0280:                                    }
0281:                                }
0282:                            } else {
0283:                                for (int j = 0; j < 8; j++, x++) {
0284:                                    if ((mask & (1 << j)) != 0) {
0285:                                        used.set(x);
0286:                                    }
0287:                                }
0288:                            }
0289:                        }
0290:                        stage++;
0291:                        int len = in.readInt();
0292:                        ObjectArray storages = new ObjectArray();
0293:                        for (int i = 0; i < len; i++) {
0294:                            int s = in.readInt();
0295:                            while (storages.size() <= s) {
0296:                                storages.add(null);
0297:                            }
0298:                            if (init) {
0299:                                int old = getPageOwner(i);
0300:                                if (old != -1 && old != s) {
0301:                                    throw Message
0302:                                            .getInternalError("Redo failure, expected page owner: "
0303:                                                    + old + " got: " + s);
0304:                                }
0305:                            } else {
0306:                                if (s >= 0) {
0307:                                    Storage storage = database.getStorage(s,
0308:                                            this );
0309:                                    storages.set(s, storage);
0310:                                    storage.addPage(i);
0311:                                }
0312:                                setPageOwner(i, s);
0313:                            }
0314:                        }
0315:                        stage++;
0316:                        while (true) {
0317:                            int s = in.readInt();
0318:                            if (s < 0) {
0319:                                break;
0320:                            }
0321:                            int recordCount = in.readInt();
0322:                            Storage storage = (Storage) storages.get(s);
0323:                            if (init) {
0324:                                if (storage != null) {
0325:                                    int current = storage.getRecordCount();
0326:                                    if (current != recordCount) {
0327:                                        throw Message
0328:                                                .getInternalError("Redo failure, expected row count: "
0329:                                                        + current
0330:                                                        + " got: "
0331:                                                        + recordCount);
0332:                                    }
0333:                                }
0334:                            } else {
0335:                                storage.setRecordCount(recordCount);
0336:                            }
0337:                        }
0338:                        stage++;
0339:                        freeUnusedPages();
0340:                        init = true;
0341:                    } catch (Exception e) {
0342:                        database.getTrace(Trace.DATABASE).error(
0343:                                "error initializing summary for " + fileName
0344:                                        + " size:" + summary.length + " stage:"
0345:                                        + stage, e);
0346:                        // ignore - init is still false in this case
0347:                    }
0348:                }
0349:            }
0350:
0351:            public void init() throws SQLException {
0352:                synchronized (database) {
0353:                    if (init) {
0354:                        return;
0355:                    }
0356:                    ObjectArray storages = database.getAllStorages();
0357:                    for (int i = 0; i < storages.size(); i++) {
0358:                        Storage s = (Storage) storages.get(i);
0359:                        if (s != null && s.getDiskFile() == this ) {
0360:                            s.setRecordCount(0);
0361:                        }
0362:                    }
0363:                    int blockHeaderLen = Math.max(Constants.FILE_BLOCK_SIZE,
0364:                            2 * rowBuff.getIntLen());
0365:                    byte[] buff = new byte[blockHeaderLen];
0366:                    DataPage s = DataPage.create(database, buff);
0367:                    long time = 0;
0368:                    for (int i = 0; i < fileBlockCount;) {
0369:                        long t2 = System.currentTimeMillis();
0370:                        if (t2 > time + 10) {
0371:                            time = t2;
0372:                            database.setProgress(
0373:                                    DatabaseEventListener.STATE_SCAN_FILE,
0374:                                    this .fileName, i, fileBlockCount);
0375:                        }
0376:                        go(i);
0377:                        file.readFully(buff, 0, blockHeaderLen);
0378:                        s.reset();
0379:                        int blockCount = s.readInt();
0380:                        if (SysProperties.CHECK && blockCount < 0) {
0381:                            throw Message.getInternalError();
0382:                        }
0383:                        if (blockCount == 0) {
0384:                            setUnused(null, i, 1);
0385:                            i++;
0386:                        } else {
0387:                            int id = s.readInt();
0388:                            if (SysProperties.CHECK && id < 0) {
0389:                                throw Message.getInternalError();
0390:                            }
0391:                            Storage storage = database.getStorage(id, this );
0392:                            setUnused(null, i, blockCount);
0393:                            setBlockOwner(null, storage, i, blockCount, true);
0394:                            storage.incrementRecordCount();
0395:                            i += blockCount;
0396:                        }
0397:                    }
0398:                    database.setProgress(DatabaseEventListener.STATE_SCAN_FILE,
0399:                            this .fileName, fileBlockCount, fileBlockCount);
0400:                    init = true;
0401:                }
0402:            }
0403:
0404:            public void flush() throws SQLException {
0405:                synchronized (database) {
0406:                    database.checkPowerOff();
0407:                    ObjectArray list = cache.getAllChanged();
0408:                    CacheObject.sort(list);
0409:                    for (int i = 0; i < list.size(); i++) {
0410:                        Record rec = (Record) list.get(i);
0411:                        writeBack(rec);
0412:                    }
0413:                    for (int i = 0; i < fileBlockCount; i++) {
0414:                        i = deleted.nextSetBit(i);
0415:                        if (i < 0) {
0416:                            break;
0417:                        }
0418:                        if (deleted.get(i)) {
0419:                            writeDirectDeleted(i, 1);
0420:                            deleted.clear(i);
0421:                        }
0422:                    }
0423:                }
0424:            }
0425:
0426:            // this implementation accesses the file in a linear way
0427:            //    public void flushNew() throws SQLException {
0428:            //        int todoTest;
0429:            //        synchronized (database) {
0430:            //            database.checkPowerOff();
0431:            //            ObjectArray list = cache.getAllChanged();
0432:            //            CacheObject.sort(list);
0433:            //            int deletePos = deleted.nextSetBit(0);
0434:            //            int writeIndex = 0;
0435:            //            Record writeRecord = null;
0436:            //            while (true) {
0437:            //                if (writeRecord == null && writeIndex < list.size()) {
0438:            //                    writeRecord = (Record) list.get(writeIndex++);
0439:            //                }
0440:            //                if (writeRecord != null && 
0441:            //                       (deletePos < 0 || writeRecord.getPos() < deletePos)) {
0442:            //                    writeBack(writeRecord);
0443:            //                    writeRecord = null;
0444:            //                } else if (deletePos < fileBlockCount && deletePos >= 0) {
0445:            //                    writeDirectDeleted(deletePos, 1);
0446:            //                    deleted.clear(deletePos);
0447:            //                    deletePos = deleted.nextSetBit(deletePos);
0448:            //                } else {
0449:            //                    break;
0450:            //                }
0451:            //            }
0452:            //        }
0453:            //    }
0454:
0455:            public void close() throws SQLException {
0456:                synchronized (database) {
0457:                    SQLException closeException = null;
0458:                    if (!database.getReadOnly()) {
0459:                        try {
0460:                            flush();
0461:                        } catch (SQLException e) {
0462:                            closeException = e;
0463:                        }
0464:                    }
0465:                    cache.clear();
0466:                    // continue with close even if flush was not possible (file storage
0467:                    // problem)
0468:                    if (file != null) {
0469:                        file.closeSilently();
0470:                        file = null;
0471:                    }
0472:                    if (closeException != null) {
0473:                        throw closeException;
0474:                    }
0475:                    readCount = writeCount = 0;
0476:                }
0477:            }
0478:
0479:            private void go(int block) throws SQLException {
0480:                database.checkPowerOff();
0481:                file.seek(getFilePos(block));
0482:            }
0483:
0484:            private long getFilePos(int block) {
0485:                return ((long) block * BLOCK_SIZE) + OFFSET;
0486:            }
0487:
0488:            Record getRecordIfStored(Session session, int pos,
0489:                    RecordReader reader, int storageId) throws SQLException {
0490:                synchronized (database) {
0491:                    try {
0492:                        int owner = getPageOwner(getPage(pos));
0493:                        if (owner != storageId) {
0494:                            return null;
0495:                        }
0496:                        go(pos);
0497:                        rowBuff.reset();
0498:                        byte[] buff = rowBuff.getBytes();
0499:                        file.readFully(buff, 0, BLOCK_SIZE);
0500:                        DataPage s = DataPage.create(database, buff);
0501:                        s.readInt(); // blockCount
0502:                        int id = s.readInt();
0503:                        if (id != storageId) {
0504:                            return null;
0505:                        }
0506:                    } catch (Exception e) {
0507:                        return null;
0508:                    }
0509:                    return getRecord(session, pos, reader, storageId);
0510:                }
0511:            }
0512:
0513:            Record getRecord(Session session, int pos, RecordReader reader,
0514:                    int storageId) throws SQLException {
0515:                synchronized (database) {
0516:                    if (file == null) {
0517:                        throw Message
0518:                                .getSQLException(ErrorCode.SIMULATED_POWER_OFF);
0519:                    }
0520:                    Record record = (Record) cache.get(pos);
0521:                    if (record != null) {
0522:                        return record;
0523:                    }
0524:                    readCount++;
0525:                    go(pos);
0526:                    rowBuff.reset();
0527:                    byte[] buff = rowBuff.getBytes();
0528:                    file.readFully(buff, 0, BLOCK_SIZE);
0529:                    DataPage s = DataPage.create(database, buff);
0530:                    int blockCount = s.readInt();
0531:                    int id = s.readInt();
0532:                    if (SysProperties.CHECK && storageId != id) {
0533:                        throw Message.getInternalError("File ID mismatch got="
0534:                                + id + " expected=" + storageId + " pos=" + pos
0535:                                + " " + logChanges + " " + this 
0536:                                + " blockCount:" + blockCount);
0537:                    }
0538:                    if (SysProperties.CHECK && blockCount == 0) {
0539:                        throw Message.getInternalError("0 blocks to read pos="
0540:                                + pos);
0541:                    }
0542:                    if (blockCount > 1) {
0543:                        byte[] b2 = new byte[blockCount * BLOCK_SIZE];
0544:                        System.arraycopy(buff, 0, b2, 0, BLOCK_SIZE);
0545:                        buff = b2;
0546:                        file.readFully(buff, BLOCK_SIZE, blockCount
0547:                                * BLOCK_SIZE - BLOCK_SIZE);
0548:                        s = DataPage.create(database, buff);
0549:                        s.readInt();
0550:                        s.readInt();
0551:                    }
0552:                    s.check(blockCount * BLOCK_SIZE);
0553:                    Record r = reader.read(session, s);
0554:                    r.setStorageId(storageId);
0555:                    r.setPos(pos);
0556:                    r.setBlockCount(blockCount);
0557:                    cache.put(r);
0558:                    return r;
0559:                }
0560:            }
0561:
0562:            int allocate(Storage storage, int blockCount) throws SQLException {
0563:                reuseSpace();
0564:                synchronized (database) {
0565:                    if (file == null) {
0566:                        throw Message
0567:                                .getSQLException(ErrorCode.SIMULATED_POWER_OFF);
0568:                    }
0569:                    blockCount = getPage(blockCount + BLOCKS_PER_PAGE - 1)
0570:                            * BLOCKS_PER_PAGE;
0571:                    int lastPage = getPage(getBlockCount());
0572:                    int pageCount = getPage(blockCount);
0573:                    int pos = -1;
0574:                    boolean found = false;
0575:                    for (int i = 0; i < lastPage; i++) {
0576:                        found = true;
0577:                        for (int j = i; j < i + pageCount; j++) {
0578:                            if (j >= lastPage || getPageOwner(j) != FREE_PAGE) {
0579:                                found = false;
0580:                                break;
0581:                            }
0582:                        }
0583:                        if (found) {
0584:                            pos = i * BLOCKS_PER_PAGE;
0585:                            break;
0586:                        }
0587:                    }
0588:                    if (!found) {
0589:                        int max = getBlockCount();
0590:                        pos = MathUtils.roundUp(max, BLOCKS_PER_PAGE);
0591:                        if (rowBuff instanceof  DataPageText) {
0592:                            if (pos > max) {
0593:                                writeDirectDeleted(max, pos - max);
0594:                            }
0595:                            writeDirectDeleted(pos, blockCount);
0596:                        } else {
0597:                            long min = ((long) pos + blockCount) * BLOCK_SIZE;
0598:                            min = MathUtils.scaleUp50Percent(
0599:                                    Constants.FILE_MIN_SIZE, min,
0600:                                    Constants.FILE_PAGE_SIZE,
0601:                                    Constants.FILE_MAX_INCREMENT)
0602:                                    + OFFSET;
0603:                            if (min > file.length()) {
0604:                                file.setLength(min);
0605:                                database.notifyFileSize(min);
0606:                            }
0607:                        }
0608:                    }
0609:                    setBlockOwner(null, storage, pos, blockCount, false);
0610:                    for (int i = 0; i < blockCount; i++) {
0611:                        storage.free(i + pos, 1);
0612:                    }
0613:                    return pos;
0614:                }
0615:            }
0616:
0617:            private void setBlockOwner(Session session, Storage storage,
0618:                    int pos, int blockCount, boolean inUse) throws SQLException {
0619:                if (pos + blockCount > fileBlockCount) {
0620:                    setBlockCount(pos + blockCount);
0621:                }
0622:                if (!inUse) {
0623:                    setUnused(session, pos, blockCount);
0624:                }
0625:                for (int i = getPage(pos); i <= getPage(pos + blockCount - 1); i++) {
0626:                    setPageOwner(i, storage.getId());
0627:                }
0628:                if (inUse) {
0629:                    used.setRange(pos, blockCount, true);
0630:                    deleted.setRange(pos, blockCount, false);
0631:                }
0632:            }
0633:
0634:            private void setUnused(Session session, int pos, int blockCount)
0635:                    throws SQLException {
0636:                if (pos + blockCount > fileBlockCount) {
0637:                    setBlockCount(pos + blockCount);
0638:                }
0639:                uncommittedDelete(session);
0640:                for (int i = pos; i < pos + blockCount; i++) {
0641:                    used.clear(i);
0642:                    if ((i % BLOCKS_PER_PAGE == 0)
0643:                            && (pos + blockCount >= i + BLOCKS_PER_PAGE)) {
0644:                        // if this is the first page of a block and if the whole page is free
0645:                        freePage(getPage(i));
0646:                    }
0647:                }
0648:            }
0649:
0650:            void reuseSpace() throws SQLException {
0651:                if (SysProperties.REUSE_SPACE_QUICKLY) {
0652:                    if (potentiallyFreePages.size() >= SysProperties.REUSE_SPACE_AFTER) {
0653:                        Session[] sessions = database.getSessions();
0654:                        int oldest = 0;
0655:                        for (int i = 0; i < sessions.length; i++) {
0656:                            int deleteId = sessions[i]
0657:                                    .getLastUncommittedDelete();
0658:                            if (oldest == 0
0659:                                    || (deleteId != 0 && deleteId < oldest)) {
0660:                                oldest = deleteId;
0661:                            }
0662:                        }
0663:                        for (Iterator it = potentiallyFreePages.iterator(); it
0664:                                .hasNext();) {
0665:                            int p = ((Integer) it.next()).intValue();
0666:                            if (oldest == 0) {
0667:                                setPageOwner(p, FREE_PAGE);
0668:                                it.remove();
0669:                            }
0670:                        }
0671:                    }
0672:                }
0673:            }
0674:
0675:            void uncommittedDelete(Session session) throws SQLException {
0676:                if (session != null && logChanges
0677:                        && SysProperties.REUSE_SPACE_QUICKLY) {
0678:                    int deleteId = session.getLastUncommittedDelete();
0679:                    if (deleteId == 0 || deleteId < nextDeleteId) {
0680:                        deleteId = ++nextDeleteId;
0681:                        session.setLastUncommittedDelete(deleteId);
0682:                    }
0683:                }
0684:            }
0685:
0686:            void freePage(int page) throws SQLException {
0687:                if (!logChanges) {
0688:                    setPageOwner(page, FREE_PAGE);
0689:                } else {
0690:                    if (SysProperties.REUSE_SPACE_QUICKLY) {
0691:                        potentiallyFreePages.add(ObjectUtils.getInteger(page));
0692:                        reuseSpace();
0693:                    }
0694:                }
0695:            }
0696:
0697:            int getPage(int pos) {
0698:                return pos >>> BLOCK_PAGE_PAGE_SHIFT;
0699:            }
0700:
0701:            int getPageOwner(int page) {
0702:                if (page * BLOCKS_PER_PAGE > fileBlockCount
0703:                        || page >= pageOwners.size()) {
0704:                    return FREE_PAGE;
0705:                }
0706:                return pageOwners.get(page);
0707:            }
0708:
0709:            public void setPageOwner(int page, int storageId)
0710:                    throws SQLException {
0711:                int old = pageOwners.get(page);
0712:                if (old == storageId) {
0713:                    return;
0714:                }
0715:                if (SysProperties.CHECK && old >= 0 && storageId >= 0
0716:                        && old != storageId) {
0717:                    for (int i = 0; i < BLOCKS_PER_PAGE; i++) {
0718:                        if (used.get(i + page * BLOCKS_PER_PAGE)) {
0719:                            throw Message
0720:                                    .getInternalError("double allocation in file "
0721:                                            + fileName
0722:                                            + " page "
0723:                                            + page
0724:                                            + " blocks "
0725:                                            + (BLOCKS_PER_PAGE * page)
0726:                                            + "-"
0727:                                            + (BLOCKS_PER_PAGE * (page + 1) - 1));
0728:                        }
0729:                    }
0730:                }
0731:                if (old >= 0) {
0732:                    database.getStorage(old, this ).removePage(page);
0733:                    if (!logChanges) {
0734:                        // need to clean the page, otherwise it may never get cleaned
0735:                        // and can become corrupted
0736:                        writeDirectDeleted(page * BLOCKS_PER_PAGE,
0737:                                BLOCKS_PER_PAGE);
0738:                    }
0739:                }
0740:                if (storageId >= 0) {
0741:                    database.getStorage(storageId, this ).addPage(page);
0742:                    if (SysProperties.REUSE_SPACE_QUICKLY) {
0743:                        potentiallyFreePages.remove(ObjectUtils
0744:                                .getInteger(page));
0745:                    }
0746:                }
0747:                pageOwners.set(page, storageId);
0748:            }
0749:
0750:            void setUsed(int pos, int blockCount) {
0751:                synchronized (database) {
0752:                    if (pos + blockCount > fileBlockCount) {
0753:                        setBlockCount(pos + blockCount);
0754:                    }
0755:                    used.setRange(pos, blockCount, true);
0756:                    deleted.setRange(pos, blockCount, false);
0757:                }
0758:            }
0759:
0760:            public void delete() throws SQLException {
0761:                synchronized (database) {
0762:                    try {
0763:                        cache.clear();
0764:                        file.close();
0765:                        FileUtils.delete(fileName);
0766:                    } catch (IOException e) {
0767:                        throw Message.convertIOException(e, fileName);
0768:                    } finally {
0769:                        file = null;
0770:                        fileName = null;
0771:                    }
0772:                }
0773:            }
0774:
0775:            //    private int allocateBest(int start, int blocks) {
0776:            //        while (true) {
0777:            //            int p = getLastUsedPlusOne(start, blocks);
0778:            //            if (p == start) {
0779:            //                start = p;
0780:            //                break;
0781:            //            }
0782:            //            start = p;
0783:            //        }
0784:            //        allocate(start, blocks);
0785:            //        return start;
0786:            //    }
0787:
0788:            public void writeBack(CacheObject obj) throws SQLException {
0789:                synchronized (database) {
0790:                    writeCount++;
0791:                    Record record = (Record) obj;
0792:                    int blockCount = record.getBlockCount();
0793:                    record.prepareWrite();
0794:                    go(record.getPos());
0795:                    DataPage buff = rowBuff;
0796:                    buff.reset();
0797:                    buff.checkCapacity(blockCount * BLOCK_SIZE);
0798:                    buff.writeInt(blockCount);
0799:                    buff.writeInt(record.getStorageId());
0800:                    record.write(buff);
0801:                    buff.fill(blockCount * BLOCK_SIZE);
0802:                    buff.updateChecksum();
0803:                    file.write(buff.getBytes(), 0, buff.length());
0804:                    record.setChanged(false);
0805:                }
0806:            }
0807:
0808:            /*
0809:             * Must be synchronized externally
0810:             */
0811:            BitField getUsed() {
0812:                return used;
0813:            }
0814:
0815:            void updateRecord(Session session, Record record)
0816:                    throws SQLException {
0817:                synchronized (database) {
0818:                    record.setChanged(true);
0819:                    int pos = record.getPos();
0820:                    Record old = (Record) cache.update(pos, record);
0821:                    if (SysProperties.CHECK) {
0822:                        if (old != null) {
0823:                            if (old != record) {
0824:                                database.checkPowerOff();
0825:                                throw Message
0826:                                        .getInternalError("old != record old="
0827:                                                + old + " new=" + record);
0828:                            }
0829:                            int blockCount = record.getBlockCount();
0830:                            for (int i = 0; i < blockCount; i++) {
0831:                                if (deleted.get(i + pos)) {
0832:                                    throw Message
0833:                                            .getInternalError("update marked as deleted: "
0834:                                                    + (i + pos));
0835:                                }
0836:                            }
0837:                        }
0838:                    }
0839:                    if (logChanges) {
0840:                        log.add(session, this , record);
0841:                    }
0842:                }
0843:            }
0844:
0845:            void writeDirectDeleted(int recordId, int blockCount)
0846:                    throws SQLException {
0847:                synchronized (database) {
0848:                    go(recordId);
0849:                    for (int i = 0; i < blockCount; i++) {
0850:                        file.write(freeBlock.getBytes(), 0, freeBlock.length());
0851:                    }
0852:                    free(recordId, blockCount);
0853:                }
0854:            }
0855:
0856:            void writeDirect(Storage storage, int pos, byte[] data, int offset)
0857:                    throws SQLException {
0858:                synchronized (database) {
0859:                    go(pos);
0860:                    file.write(data, offset, BLOCK_SIZE);
0861:                    setBlockOwner(null, storage, pos, 1, true);
0862:                }
0863:            }
0864:
0865:            public int copyDirect(int pos, OutputStream out)
0866:                    throws SQLException {
0867:                synchronized (database) {
0868:                    try {
0869:                        if (pos < 0) {
0870:                            // read the header
0871:                            byte[] buffer = new byte[OFFSET];
0872:                            file.seek(0);
0873:                            file.readFullyDirect(buffer, 0, OFFSET);
0874:                            out.write(buffer);
0875:                            return 0;
0876:                        }
0877:                        if (pos >= fileBlockCount) {
0878:                            return -1;
0879:                        }
0880:                        int blockSize = DiskFile.BLOCK_SIZE;
0881:                        byte[] buff = new byte[blockSize];
0882:                        DataPage s = DataPage.create(database, buff);
0883:                        database.setProgress(
0884:                                DatabaseEventListener.STATE_BACKUP_FILE,
0885:                                this .fileName, pos, fileBlockCount);
0886:                        go(pos);
0887:                        file.readFully(buff, 0, blockSize);
0888:                        s.reset();
0889:                        int blockCount = s.readInt();
0890:                        if (SysProperties.CHECK && blockCount < 0) {
0891:                            throw Message.getInternalError();
0892:                        }
0893:                        if (blockCount == 0) {
0894:                            blockCount = 1;
0895:                        }
0896:                        int id = s.readInt();
0897:                        if (SysProperties.CHECK && id < 0) {
0898:                            throw Message.getInternalError();
0899:                        }
0900:                        s.checkCapacity(blockCount * blockSize);
0901:                        if (blockCount > 1) {
0902:                            file.readFully(s.getBytes(), blockSize, blockCount
0903:                                    * blockSize - blockSize);
0904:                        }
0905:                        if (file.isEncrypted()) {
0906:                            s.reset();
0907:                            go(pos);
0908:                            file.readFullyDirect(s.getBytes(), 0, blockCount
0909:                                    * blockSize);
0910:                        }
0911:                        out.write(s.getBytes(), 0, blockCount * blockSize);
0912:                        return pos + blockCount;
0913:                    } catch (IOException e) {
0914:                        throw Message.convertIOException(e, fileName);
0915:                    }
0916:                }
0917:            }
0918:
0919:            void removeRecord(Session session, int pos, Record record,
0920:                    int blockCount) throws SQLException {
0921:                synchronized (database) {
0922:                    if (logChanges) {
0923:                        log.add(session, this , record);
0924:                    }
0925:                    cache.remove(pos);
0926:                    deleted.setRange(pos, blockCount, true);
0927:                    setUnused(session, pos, blockCount);
0928:                }
0929:            }
0930:
0931:            void addRecord(Session session, Record record) throws SQLException {
0932:                synchronized (database) {
0933:                    if (logChanges) {
0934:                        log.add(session, this , record);
0935:                    }
0936:                    cache.put(record);
0937:                }
0938:            }
0939:
0940:            /*
0941:             * Must be synchronized externally
0942:             */
0943:            public Cache getCache() {
0944:                return cache;
0945:            }
0946:
0947:            void free(int pos, int blockCount) {
0948:                synchronized (database) {
0949:                    used.setRange(pos, blockCount, false);
0950:                }
0951:            }
0952:
0953:            public int getRecordOverhead() {
0954:                return recordOverhead;
0955:            }
0956:
0957:            public void truncateStorage(Session session, Storage storage,
0958:                    IntArray pages) throws SQLException {
0959:                synchronized (database) {
0960:                    int storageId = storage.getId();
0961:                    // make sure the cache records of this storage are not flushed to disk
0962:                    // afterwards
0963:                    ObjectArray list = cache.getAllChanged();
0964:                    for (int i = 0; i < list.size(); i++) {
0965:                        Record r = (Record) list.get(i);
0966:                        if (r.getStorageId() == storageId) {
0967:                            r.setChanged(false);
0968:                        }
0969:                    }
0970:                    int[] pagesCopy = new int[pages.size()];
0971:                    // can not use pages directly, because setUnused removes rows from there
0972:                    pages.toArray(pagesCopy);
0973:                    for (int i = 0; i < pagesCopy.length; i++) {
0974:                        int page = pagesCopy[i];
0975:                        if (logChanges) {
0976:                            log.addTruncate(session, this , storageId, page
0977:                                    * BLOCKS_PER_PAGE, BLOCKS_PER_PAGE);
0978:                        }
0979:                        for (int j = 0; j < BLOCKS_PER_PAGE; j++) {
0980:                            Record r = (Record) cache.find(page
0981:                                    * BLOCKS_PER_PAGE + j);
0982:                            if (r != null) {
0983:                                cache.remove(r.getPos());
0984:                            }
0985:                        }
0986:                        deleted.setRange(page * BLOCKS_PER_PAGE,
0987:                                BLOCKS_PER_PAGE, true);
0988:                        setUnused(session, page * BLOCKS_PER_PAGE,
0989:                                BLOCKS_PER_PAGE);
0990:                    }
0991:                }
0992:            }
0993:
0994:            public void sync() {
0995:                synchronized (database) {
0996:                    if (file != null) {
0997:                        file.sync();
0998:                    }
0999:                }
1000:            }
1001:
1002:            public boolean isDataFile() {
1003:                return dataFile;
1004:            }
1005:
1006:            public void setLogChanges(boolean b) {
1007:                synchronized (database) {
1008:                    this .logChanges = b;
1009:                }
1010:            }
1011:
1012:            /**
1013:             * Add a redo-log entry to the redo buffer.
1014:             * 
1015:             * @param storage the storage
1016:             * @param recordId the record id of the entry
1017:             * @param blockCount the number of blocks
1018:             * @param rec the record
1019:             */
1020:            public void addRedoLog(Storage storage, int recordId,
1021:                    int blockCount, DataPage rec) throws SQLException {
1022:                synchronized (database) {
1023:                    byte[] data = null;
1024:                    if (rec != null) {
1025:                        DataPage all = rowBuff;
1026:                        all.reset();
1027:                        all.writeInt(blockCount);
1028:                        all.writeInt(storage.getId());
1029:                        all.writeDataPageNoSize(rec);
1030:                        // the buffer may have some additional fillers - just ignore them
1031:                        all.fill(blockCount * BLOCK_SIZE);
1032:                        all.updateChecksum();
1033:                        if (SysProperties.CHECK
1034:                                && all.length() != BLOCK_SIZE * blockCount) {
1035:                            throw Message.getInternalError("blockCount:"
1036:                                    + blockCount + " length: " + all.length()
1037:                                    * BLOCK_SIZE);
1038:                        }
1039:                        data = new byte[all.length()];
1040:                        System.arraycopy(all.getBytes(), 0, data, 0, all
1041:                                .length());
1042:                    }
1043:                    for (int i = 0; i < blockCount; i++) {
1044:                        RedoLogRecord log = new RedoLogRecord();
1045:                        log.recordId = recordId + i;
1046:                        log.offset = i * BLOCK_SIZE;
1047:                        log.storage = storage;
1048:                        log.data = data;
1049:                        log.sequenceId = redoBuffer.size();
1050:                        redoBuffer.add(log);
1051:                        redoBufferSize += log.getSize();
1052:                    }
1053:                    if (redoBufferSize > SysProperties.REDO_BUFFER_SIZE) {
1054:                        flushRedoLog();
1055:                    }
1056:                }
1057:            }
1058:
1059:            public void flushRedoLog() throws SQLException {
1060:                synchronized (database) {
1061:                    if (redoBuffer.size() == 0) {
1062:                        return;
1063:                    }
1064:                    redoBuffer.sort(new Comparator() {
1065:                        public int compare(Object o1, Object o2) {
1066:                            RedoLogRecord e1 = (RedoLogRecord) o1;
1067:                            RedoLogRecord e2 = (RedoLogRecord) o2;
1068:                            int comp = e1.recordId - e2.recordId;
1069:                            if (comp == 0) {
1070:                                comp = e1.sequenceId - e2.sequenceId;
1071:                            }
1072:                            return comp;
1073:                        }
1074:                    });
1075:                    // first write all deleted entries
1076:                    RedoLogRecord last = null;
1077:                    for (int i = 0; i < redoBuffer.size(); i++) {
1078:                        RedoLogRecord entry = (RedoLogRecord) redoBuffer.get(i);
1079:                        if (entry.data != null) {
1080:                            continue;
1081:                        }
1082:                        if (last != null && entry.recordId != last.recordId) {
1083:                            writeRedoLog(last);
1084:                        }
1085:                        last = entry;
1086:                    }
1087:                    if (last != null) {
1088:                        writeRedoLog(last);
1089:                    }
1090:                    // now write the last entry, skipping deleted entries
1091:                    last = null;
1092:                    for (int i = 0; i < redoBuffer.size(); i++) {
1093:                        RedoLogRecord entry = (RedoLogRecord) redoBuffer.get(i);
1094:                        if (last != null && entry.recordId != last.recordId) {
1095:                            if (last.data != null) {
1096:                                writeRedoLog(last);
1097:                            }
1098:                        }
1099:                        last = entry;
1100:                    }
1101:                    if (last != null && last.data != null) {
1102:                        writeRedoLog(last);
1103:                    }
1104:                    redoBuffer.clear();
1105:                    redoBufferSize = 0;
1106:                }
1107:            }
1108:
1109:            private void writeRedoLog(RedoLogRecord entry) throws SQLException {
1110:                if (entry.data == null) {
1111:                    writeDirectDeleted(entry.recordId, 1);
1112:                } else {
1113:                    writeDirect(entry.storage, entry.recordId, entry.data,
1114:                            entry.offset);
1115:                }
1116:            }
1117:
1118:            public int getWriteCount() {
1119:                return writeCount;
1120:            }
1121:
1122:            public int getReadCount() {
1123:                return readCount;
1124:            }
1125:
1126:            public void flushLog() throws SQLException {
1127:                if (log != null) {
1128:                    log.flush();
1129:                }
1130:            }
1131:
1132:            public String toString() {
1133:                return getClass().getName() + ":" + fileName;
1134:            }
1135:
1136:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.