Source Code Cross Referenced for DumpCommand.java in  » Database-Client » Henplus » henplus » commands » 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 Client » Henplus » henplus.commands 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * This is free software, licensed under the Gnu Public License (GPL)
0003:         * get a copy from <http://www.gnu.org/licenses/gpl.html>
0004:         * $Id: DumpCommand.java,v 1.37 2005/11/27 16:20:28 hzeller Exp $ 
0005:         * author: Henner Zeller <H.Zeller@acm.org>
0006:         */
0007:        package henplus.commands;
0008:
0009:        import henplus.AbstractCommand;
0010:        import henplus.CommandDispatcher;
0011:        import henplus.HenPlus;
0012:        import henplus.Interruptable;
0013:        import henplus.SQLMetaData;
0014:        import henplus.SQLMetaDataBuilder;
0015:        import henplus.SQLSession;
0016:        import henplus.SigIntHandler;
0017:        import henplus.Version;
0018:        import henplus.sqlmodel.Table;
0019:        import henplus.util.DependencyResolver;
0020:        import henplus.util.DependencyResolver.ResolverResult;
0021:        import henplus.view.Column;
0022:        import henplus.view.ColumnMetaData;
0023:        import henplus.view.TableRenderer;
0024:        import henplus.view.util.NameCompleter;
0025:        import henplus.view.util.CancelWriter;
0026:
0027:        import henplus.view.util.ProgressWriter;
0028:
0029:        import java.math.BigDecimal;
0030:        import java.io.File;
0031:        import java.io.FileInputStream;
0032:        import java.io.FileOutputStream;
0033:        import java.io.IOException;
0034:        import java.io.InputStream;
0035:        import java.io.InputStreamReader;
0036:        import java.io.LineNumberReader;
0037:        import java.io.OutputStream;
0038:        import java.io.PrintStream;
0039:        import java.io.Reader;
0040:        import java.sql.Connection;
0041:        import java.sql.DatabaseMetaData;
0042:        import java.sql.ResultSetMetaData;
0043:        import java.sql.PreparedStatement;
0044:        import java.sql.ResultSet;
0045:        import java.sql.SQLException;
0046:        import java.sql.Statement;
0047:        import java.sql.Time;
0048:        import java.sql.Timestamp;
0049:        import java.sql.Types;
0050:        import java.util.ArrayList;
0051:        import java.util.Collection;
0052:        import java.util.HashMap;
0053:        import java.util.HashSet;
0054:        import java.util.Iterator;
0055:        import java.util.List;
0056:        import java.util.Map;
0057:        import java.util.Set;
0058:        import java.util.LinkedHashSet;
0059:        import java.util.SortedSet;
0060:        import java.util.StringTokenizer;
0061:        import java.util.zip.GZIPInputStream;
0062:        import java.util.zip.GZIPOutputStream;
0063:
0064:        /**
0065:         * Dump out and read that dump of a table; database-independently.
0066:         * This reads directly from the stream, so only needs not much
0067:         * memory, no matter the size of the file.
0068:         ---------------------------
0069:         (tabledump 'student'
0070:         (dump-version 1 1)
0071:         (henplus-version 0.3.3)
0072:         (database-info 'MySQL - 3.23.47')
0073:         (meta ('name',   'sex',    'student_id')
0074:         ('STRING', 'STRING', 'INTEGER'   ))
0075:         (data ('Megan','F',1)
0076:         ('Joseph','M',2)
0077:         ('Kyle','M',3)
0078:         ('Mac Donald\'s','M',44))
0079:         (rows 4))
0080:         ---------------------------
0081:         *
0082:         * QUICK AND DIRTY HACK .. NOT YET NICE. Too long. grown. Refactor..!
0083:         * (create an henplus.dump package so that this can be used
0084:         * @author Henner Zeller
0085:         */
0086:        public class DumpCommand extends AbstractCommand implements 
0087:                Interruptable {
0088:            private final static ColumnMetaData META_HEADERS[];
0089:            static {
0090:                META_HEADERS = new ColumnMetaData[3];
0091:                META_HEADERS[0] = new ColumnMetaData("Field");
0092:                META_HEADERS[1] = new ColumnMetaData("Type");
0093:                META_HEADERS[2] = new ColumnMetaData("Max. length found",
0094:                        ColumnMetaData.ALIGN_RIGHT);
0095:            }
0096:
0097:            private final String FILE_ENCODING = "UTF-8";
0098:            private final static int DUMP_VERSION = 1;
0099:            private final static String NULL_STR = "NULL";
0100:            private final static Map JDBCTYPE2TYPENAME = new HashMap();
0101:
0102:            // differentiated types by dump
0103:            private final static String TYPES[] = new String[9];
0104:            private final static int HP_STRING = 0;
0105:            private final static int HP_INTEGER = 1;
0106:            private final static int HP_NUMERIC = 2;
0107:            private final static int HP_DOUBLE = 3;
0108:            private final static int HP_DATE = 4;
0109:            private final static int HP_TIME = 5;
0110:            private final static int HP_TIMESTAMP = 6;
0111:            private final static int HP_BLOB = 7;
0112:            private final static int HP_CLOB = 8;
0113:
0114:            static {
0115:                TYPES[HP_STRING] = "STRING";
0116:                TYPES[HP_INTEGER] = "INTEGER";
0117:                TYPES[HP_NUMERIC] = "NUMERIC";
0118:                TYPES[HP_DOUBLE] = "DOUBLE";
0119:                TYPES[HP_DATE] = "DATE";
0120:                TYPES[HP_TIME] = "TIME";
0121:                TYPES[HP_TIMESTAMP] = "TIMESTAMP";
0122:                TYPES[HP_BLOB] = "BLOB";
0123:                TYPES[HP_CLOB] = "CLOB";
0124:
0125:                JDBCTYPE2TYPENAME
0126:                        .put(new Integer(Types.CHAR), TYPES[HP_STRING]);
0127:                JDBCTYPE2TYPENAME.put(new Integer(Types.VARCHAR),
0128:                        TYPES[HP_STRING]);
0129:
0130:                // hope that, 'OTHER' can be read/written as String..
0131:                JDBCTYPE2TYPENAME.put(new Integer(Types.OTHER),
0132:                        TYPES[HP_STRING]);
0133:
0134:                JDBCTYPE2TYPENAME.put(new Integer(Types.LONGVARBINARY),
0135:                        TYPES[HP_BLOB]);
0136:                // CLOB not supported .. try string.
0137:                JDBCTYPE2TYPENAME.put(new Integer(Types.LONGVARCHAR),
0138:                        TYPES[HP_STRING]);
0139:
0140:                // not supported yet.
0141:                JDBCTYPE2TYPENAME.put(new Integer(Types.BLOB), TYPES[HP_BLOB]);
0142:                // CLOB not supported .. try string.
0143:                JDBCTYPE2TYPENAME
0144:                        .put(new Integer(Types.CLOB), TYPES[HP_STRING]);
0145:
0146:                // generic float.
0147:                JDBCTYPE2TYPENAME.put(new Integer(Types.DOUBLE),
0148:                        TYPES[HP_DOUBLE]);
0149:                JDBCTYPE2TYPENAME.put(new Integer(Types.FLOAT),
0150:                        TYPES[HP_DOUBLE]);
0151:
0152:                // generic numeric. could be integer or double
0153:                JDBCTYPE2TYPENAME.put(new Integer(Types.BIGINT),
0154:                        TYPES[HP_NUMERIC]);
0155:                JDBCTYPE2TYPENAME.put(new Integer(Types.NUMERIC),
0156:                        TYPES[HP_NUMERIC]);
0157:                JDBCTYPE2TYPENAME.put(new Integer(Types.DECIMAL),
0158:                        TYPES[HP_NUMERIC]);
0159:                JDBCTYPE2TYPENAME.put(new Integer(Types.BOOLEAN),
0160:                        TYPES[HP_NUMERIC]);
0161:                // generic integer.
0162:                JDBCTYPE2TYPENAME.put(new Integer(Types.INTEGER),
0163:                        TYPES[HP_INTEGER]);
0164:                JDBCTYPE2TYPENAME.put(new Integer(Types.SMALLINT),
0165:                        TYPES[HP_INTEGER]);
0166:                JDBCTYPE2TYPENAME.put(new Integer(Types.TINYINT),
0167:                        TYPES[HP_INTEGER]);
0168:
0169:                JDBCTYPE2TYPENAME.put(new Integer(Types.DATE), TYPES[HP_DATE]);
0170:                JDBCTYPE2TYPENAME.put(new Integer(Types.TIME), TYPES[HP_TIME]);
0171:                JDBCTYPE2TYPENAME.put(new Integer(Types.TIMESTAMP),
0172:                        TYPES[HP_TIMESTAMP]);
0173:            }
0174:
0175:            private final ListUserObjectsCommand _tableCompleter;
0176:            private final LoadCommand _fileOpener;
0177:            private volatile boolean _running;
0178:
0179:            public DumpCommand(ListUserObjectsCommand tc, LoadCommand lc) {
0180:                _tableCompleter = tc;
0181:                _fileOpener = lc;
0182:                _running = false;
0183:            }
0184:
0185:            /**
0186:             * returns the command-strings this command can handle.
0187:             */
0188:            public String[] getCommandList() {
0189:                return new String[] { "dump-out", "dump-in", "verify-dump",
0190:                        "dump-conditional", "dump-select" };
0191:            }
0192:
0193:            /**
0194:             * verify works without session.
0195:             */
0196:            public boolean requiresValidSession(String cmd) {
0197:                return false;
0198:            }
0199:
0200:            /**
0201:             * dump-in and verify-dump is complete as single-liner.
0202:             * dump-out and dump-conditional needs a semicolon.
0203:             */
0204:            public boolean isComplete(String command) {
0205:                if (command.startsWith("dump-in")
0206:                        || command.startsWith("verify-dump"))
0207:                    return true;
0208:                return command.endsWith(";");
0209:            }
0210:
0211:            /**
0212:             * execute the command given.
0213:             */
0214:            public int execute(SQLSession session, String cmd, String param) {
0215:                //final String FILE_ENCODING = System.getProperty("file.encoding");
0216:                StringTokenizer st = new StringTokenizer(param);
0217:                int argc = st.countTokens();
0218:
0219:                if ("dump-select".equals(cmd)) {
0220:                    if (session == null) {
0221:                        HenPlus.msg().println("not connected.");
0222:                        return EXEC_FAILED;
0223:                    }
0224:                    if ((argc < 4))
0225:                        return SYNTAX_ERROR;
0226:                    final String fileName = st.nextToken();
0227:                    final String tabName = st.nextToken();
0228:                    final String select = st.nextToken();
0229:                    if (!select.toUpperCase().equals("SELECT")) {
0230:                        HenPlus.msg().println("'select' expected..");
0231:                        return SYNTAX_ERROR;
0232:                    }
0233:                    final StringBuffer statement = new StringBuffer("select");
0234:                    while (st.hasMoreElements()) {
0235:                        statement.append(" ").append(st.nextToken());
0236:                    }
0237:                    PrintStream out = null;
0238:                    beginInterruptableSection();
0239:                    try {
0240:                        out = openOutputStream(fileName, FILE_ENCODING);
0241:                        int result = dumpSelect(session, tabName, statement
0242:                                .toString(), out, FILE_ENCODING);
0243:                        return result;
0244:                    } catch (Exception e) {
0245:                        HenPlus.msg().println("failed: " + e.getMessage());
0246:                        e.printStackTrace();
0247:                        return EXEC_FAILED;
0248:                    } finally {
0249:                        if (out != null)
0250:                            out.close();
0251:                        endInterruptableSection();
0252:                    }
0253:                }
0254:
0255:                else if ("dump-conditional".equals(cmd)) {
0256:                    if (session == null) {
0257:                        HenPlus.msg().println("not connected.");
0258:                        return EXEC_FAILED;
0259:                    }
0260:                    if ((argc < 2))
0261:                        return SYNTAX_ERROR;
0262:                    String fileName = (String) st.nextElement();
0263:                    String tabName = (String) st.nextElement();
0264:                    String whereClause = null;
0265:                    if (argc >= 3) {
0266:                        whereClause = st.nextToken("\n"); // till EOL
0267:                        whereClause = whereClause.trim();
0268:                        if (whereClause.toUpperCase().startsWith("WHERE")) {
0269:                            whereClause = whereClause.substring(5);
0270:                            whereClause = whereClause.trim();
0271:                        }
0272:                    }
0273:                    PrintStream out = null;
0274:                    beginInterruptableSection();
0275:                    try {
0276:                        out = openOutputStream(fileName, FILE_ENCODING);
0277:                        int result = dumpTable(session, tabName, whereClause,
0278:                                out, FILE_ENCODING);
0279:                        return result;
0280:                    } catch (Exception e) {
0281:                        HenPlus.msg().println("failed: " + e.getMessage());
0282:                        e.printStackTrace();
0283:                        return EXEC_FAILED;
0284:                    } finally {
0285:                        if (out != null)
0286:                            out.close();
0287:                        endInterruptableSection();
0288:                    }
0289:                }
0290:
0291:                else if ("dump-out".equals(cmd)) {
0292:                    if (session == null) {
0293:                        HenPlus.msg().println("not connected.");
0294:                        return EXEC_FAILED;
0295:                    }
0296:                    if ((argc < 2))
0297:                        return SYNTAX_ERROR;
0298:                    String fileName = (String) st.nextElement();
0299:                    PrintStream out = null;
0300:                    String tabName = null;
0301:                    beginInterruptableSection();
0302:                    try {
0303:                        final long startTime = System.currentTimeMillis();
0304:                        Set alreadyDumped = new HashSet(); // which tables got already dumped?
0305:
0306:                        out = openOutputStream(fileName, FILE_ENCODING);
0307:                        Set/*<String>*/tableSet = new LinkedHashSet();
0308:
0309:                        /* right now, we do only a sort, if there is any '*' found in tables. Probably
0310:                         * we might want to make this an option to dump-in */
0311:                        boolean needsSort = false;
0312:
0313:                        int dumpResult = SUCCESS;
0314:
0315:                        /* 1) collect tables */
0316:                        while (st.hasMoreElements()) {
0317:                            String nextToken = st.nextToken();
0318:
0319:                            if ("*".equals(nextToken)
0320:                                    || nextToken.indexOf('*') > -1) {
0321:                                needsSort = true;
0322:
0323:                                Iterator iter = null;
0324:
0325:                                if ("*".equals(nextToken)) {
0326:                                    iter = _tableCompleter
0327:                                            .getTableNamesIteratorForSession(session);
0328:                                } else if (nextToken.indexOf('*') > -1) {
0329:                                    String tablePrefix = nextToken.substring(0,
0330:                                            nextToken.length() - 1);
0331:                                    SortedSet tableNames = _tableCompleter
0332:                                            .getTableNamesForSession(session);
0333:                                    NameCompleter compl = new NameCompleter(
0334:                                            tableNames);
0335:                                    iter = compl.getAlternatives(tablePrefix);
0336:                                }
0337:                                while (iter.hasNext()) {
0338:                                    tableSet.add(iter.next());
0339:                                }
0340:                            } else {
0341:                                tableSet.add(nextToken);
0342:                            }
0343:                        }
0344:
0345:                        /* 2) resolve dependencies */
0346:                        ResolverResult resolverResult = null;
0347:                        List/*<String>*/tableSequence;
0348:                        if (needsSort) {
0349:                            tableSequence = new ArrayList();
0350:                            HenPlus
0351:                                    .msg()
0352:                                    .println(
0353:                                            "Retrieving and sorting tables. This may take a while, please be patient.");
0354:
0355:                            // get sorted tables
0356:                            SQLMetaData meta = new SQLMetaDataBuilder()
0357:                                    .getMetaData(session, tableSet.iterator());
0358:                            DependencyResolver dr = new DependencyResolver(meta
0359:                                    .getTables());
0360:                            resolverResult = dr.sortTables();
0361:                            List/*<Table>*/tabs = resolverResult.getTables();
0362:                            Iterator it = tabs.iterator();
0363:                            while (it.hasNext()) {
0364:                                tableSequence
0365:                                        .add(((Table) it.next()).getName());
0366:                            }
0367:                        } else {
0368:                            tableSequence = new ArrayList(tableSet);
0369:                        }
0370:
0371:                        /* 3) dump out */
0372:                        if (tableSequence.size() > 1) {
0373:                            HenPlus.msg().println(
0374:                                    tableSequence.size() + " tables to dump.");
0375:                        }
0376:                        Iterator it = tableSequence.iterator();
0377:                        while (_running && it.hasNext()) {
0378:                            final String table = (String) it.next();
0379:                            if (!alreadyDumped.contains(table)) {
0380:                                int result = dumpTable(session, table, null,
0381:                                        out, FILE_ENCODING, alreadyDumped);
0382:                                if (result != SUCCESS) {
0383:                                    dumpResult = result;
0384:                                }
0385:                            }
0386:                        }
0387:
0388:                        if (tableSequence.size() > 1) {
0389:                            final long duration = System.currentTimeMillis()
0390:                                    - startTime;
0391:                            HenPlus.msg().print(
0392:                                    "Dumping " + tableSequence.size()
0393:                                            + " tables took ");
0394:                            TimeRenderer.printTime(duration, HenPlus.msg());
0395:                            HenPlus.msg().println();
0396:                        }
0397:
0398:                        /* 4) warn about cycles */
0399:                        if (resolverResult != null
0400:                                && resolverResult.getCyclicDependencies() != null
0401:                                && resolverResult.getCyclicDependencies()
0402:                                        .size() > 0) {
0403:                            HenPlus
0404:                                    .msg()
0405:                                    .println(
0406:                                            "-----------\n"
0407:                                                    + "NOTE: There have been cyclic dependencies between several tables detected.\n"
0408:                                                    + "These may cause trouble when dumping in the currently dumped data.");
0409:                            Iterator iter = resolverResult
0410:                                    .getCyclicDependencies().iterator();
0411:                            int count = 0;
0412:                            StringBuffer sb = new StringBuffer();
0413:                            while (iter.hasNext()) {
0414:                                Iterator iter2 = ((List) iter.next())
0415:                                        .iterator();
0416:                                sb.append("Cycle ").append(count).append(": ");
0417:                                ;
0418:                                while (iter2.hasNext()) {
0419:                                    sb.append(((Table) iter2.next()).getName())
0420:                                            .append(" -> ");
0421:                                }
0422:                                sb.delete(sb.length() - 4, sb.length()).append(
0423:                                        '\n');
0424:                            }
0425:                            HenPlus.msg().print(sb.toString());
0426:                            /* todo: print out, what constraint to disable */
0427:                        }
0428:
0429:                        return dumpResult;
0430:                    } catch (Exception e) {
0431:                        HenPlus.msg().println(
0432:                                "dump table '" + tabName + "' failed: "
0433:                                        + e.getMessage());
0434:                        e.printStackTrace();
0435:                        return EXEC_FAILED;
0436:                    } finally {
0437:                        if (out != null)
0438:                            out.close();
0439:                        endInterruptableSection();
0440:                    }
0441:                }
0442:
0443:                else if ("dump-in".equals(cmd)) {
0444:                    if (session == null) {
0445:                        HenPlus.msg().println(
0446:                                "not connected. Only verify-dump possible.");
0447:                        return EXEC_FAILED;
0448:                    }
0449:                    if (argc < 1 || argc > 2)
0450:                        return SYNTAX_ERROR;
0451:                    String fileName = (String) st.nextElement();
0452:                    int commitPoint = -1;
0453:                    if (argc == 2) {
0454:                        try {
0455:                            String val = (String) st.nextElement();
0456:                            commitPoint = Integer.valueOf(val).intValue();
0457:                        } catch (NumberFormatException e) {
0458:                            HenPlus.msg().println(
0459:                                    "commit point number expected: " + e);
0460:                            return SYNTAX_ERROR;
0461:                        }
0462:                    }
0463:                    return retryReadDump(fileName, session, commitPoint);
0464:                }
0465:
0466:                else if ("verify-dump".equals(cmd)) {
0467:                    if (argc != 1)
0468:                        return SYNTAX_ERROR;
0469:                    String fileName = (String) st.nextElement();
0470:                    return retryReadDump(fileName, null, -1);
0471:                }
0472:                return SYNTAX_ERROR;
0473:            }
0474:
0475:            /**
0476:             * reads a dump and does a retry if the file encoding does
0477:             * not match.
0478:             */
0479:            private int retryReadDump(String fileName, SQLSession session,
0480:                    int commitPoint) {
0481:                LineNumberReader in = null;
0482:                boolean hot = (session != null);
0483:                beginInterruptableSection();
0484:                try {
0485:                    String fileEncoding = FILE_ENCODING;
0486:                    boolean retryPossible = true;
0487:                    do {
0488:                        try {
0489:                            in = openInputReader(fileName, fileEncoding);
0490:                            while (skipWhite(in)) {
0491:                                int result = readTableDump(in, fileEncoding,
0492:                                        session, hot, commitPoint);
0493:                                retryPossible = false;
0494:                                if (!_running) {
0495:                                    HenPlus.msg().println("interrupted.");
0496:                                    return result;
0497:                                }
0498:                                if (result != SUCCESS) {
0499:                                    return result;
0500:                                }
0501:                            }
0502:                        } catch (EncodingMismatchException e) {
0503:                            // did we already retry with another encoding?
0504:                            if (!fileEncoding.equals(FILE_ENCODING)) {
0505:                                throw new Exception(
0506:                                        "got file encoding problem twice");
0507:                            }
0508:                            fileEncoding = e.getEncoding();
0509:                            HenPlus.msg().println(
0510:                                    "got a different encoding; retry with "
0511:                                            + fileEncoding);
0512:                        }
0513:                    } while (retryPossible);
0514:                    return SUCCESS;
0515:                } catch (Exception e) {
0516:                    HenPlus.msg().println("failed: " + e.getMessage());
0517:                    e.printStackTrace();
0518:                    return EXEC_FAILED;
0519:                } finally {
0520:                    try {
0521:                        if (in != null)
0522:                            in.close();
0523:                    } catch (IOException e) {
0524:                        HenPlus.msg().println("closing file failed.");
0525:                    }
0526:                    endInterruptableSection();
0527:                }
0528:            }
0529:
0530:            private PrintStream openOutputStream(String fileName,
0531:                    String encoding) throws IOException {
0532:                File f = _fileOpener.openFile(fileName);
0533:                OutputStream outStream = new FileOutputStream(f);
0534:                if (fileName.endsWith(".gz")) {
0535:                    outStream = new GZIPOutputStream(outStream, 4096);
0536:                }
0537:                return new PrintStream(outStream, false, encoding);
0538:            }
0539:
0540:            private LineNumberReader openInputReader(String fileName,
0541:                    String fileEncoding) throws IOException {
0542:                File f = _fileOpener.openFile(fileName);
0543:                InputStream inStream = new FileInputStream(f);
0544:                if (fileName.endsWith(".gz")) {
0545:                    inStream = new GZIPInputStream(inStream);
0546:                }
0547:                Reader fileIn = new InputStreamReader(inStream, fileEncoding);
0548:                return new LineNumberReader(fileIn);
0549:            }
0550:
0551:            // to make the field-name and field-type nicely aligned
0552:            private void printWidth(PrintStream out, String s, int width,
0553:                    boolean comma) {
0554:                if (comma)
0555:                    out.print(", ");
0556:                out.print("'");
0557:                out.print(s);
0558:                out.print("'");
0559:                for (int i = s.length(); i < width; ++i) {
0560:                    out.print(' ');
0561:                }
0562:            }
0563:
0564:            private int dumpTable(SQLSession session, String tabName,
0565:                    String whereClause, PrintStream dumpOut,
0566:                    String fileEncoding, Set/*<String>*/alreadyDumped)
0567:                    throws Exception {
0568:                int result = dumpTable(session, tabName, whereClause, dumpOut,
0569:                        fileEncoding);
0570:                alreadyDumped.add(tabName);
0571:                return result;
0572:            }
0573:
0574:            private int dumpSelect(SQLSession session, String exportTable,
0575:                    String statement, PrintStream dumpOut, String fileEncoding)
0576:                    throws Exception {
0577:                return dumpTable(session, new SelectDumpSource(session,
0578:                        exportTable, statement), dumpOut, fileEncoding);
0579:            }
0580:
0581:            private int dumpTable(SQLSession session, String tabName,
0582:                    String whereClause, PrintStream dumpOut, String fileEncoding)
0583:                    throws Exception {
0584:
0585:                // asking for meta data is only possible with the correct
0586:                // table name.
0587:                boolean correctName = true;
0588:                if (tabName.startsWith("\"")) {
0589:                    //tabName = stripQuotes(tabName);
0590:                    correctName = false;
0591:                }
0592:
0593:                // separate schama and table.
0594:                String schema = null;
0595:                int schemaDelim = tabName.indexOf('.');
0596:                if (schemaDelim > 0) {
0597:                    schema = tabName.substring(0, schemaDelim);
0598:                    tabName = tabName.substring(schemaDelim + 1);
0599:                }
0600:
0601:                if (correctName) {
0602:                    String alternative = _tableCompleter
0603:                            .correctTableName(tabName);
0604:                    if (alternative != null && !alternative.equals(tabName)) {
0605:                        tabName = alternative;
0606:                        HenPlus.out().println(
0607:                                "dumping table: '" + tabName
0608:                                        + "' (corrected name)");
0609:                    }
0610:                }
0611:                final TableDumpSource tableSource = new TableDumpSource(schema,
0612:                        tabName, !correctName, session);
0613:                tableSource.setWhereClause(whereClause);
0614:                return dumpTable(session, tableSource, dumpOut, fileEncoding);
0615:            }
0616:
0617:            private int dumpTable(SQLSession session, DumpSource dumpSource,
0618:                    PrintStream dumpOut, String fileEncoding) throws Exception {
0619:                final long startTime = System.currentTimeMillis();
0620:                MetaProperty[] metaProps = dumpSource.getMetaProperties();
0621:                if (metaProps.length == 0) {
0622:                    HenPlus.msg().println(
0623:                            "No fields in " + dumpSource.getDescription()
0624:                                    + " found.");
0625:                    return EXEC_FAILED;
0626:                }
0627:
0628:                HenPlus.msg()
0629:                        .println("dump " + dumpSource.getTableName() + ":");
0630:
0631:                dumpOut.println("(tabledump '" + dumpSource.getTableName()
0632:                        + "'");
0633:                dumpOut.println("  (file-encoding '" + fileEncoding + "')");
0634:                dumpOut.println("  (dump-version " + DUMP_VERSION + " "
0635:                        + DUMP_VERSION + ")");
0636:                /*
0637:                if (whereClause != null) {
0638:                    dumpOut.print("  (where-clause ");
0639:                    quoteString(dumpOut, whereClause);
0640:                    dumpOut.println(")");
0641:                }
0642:                 */
0643:                dumpOut.println("  (henplus-version '" + Version.getVersion()
0644:                        + "')");
0645:                dumpOut.println("  (time '"
0646:                        + new Timestamp(System.currentTimeMillis()) + "')");
0647:                dumpOut.print("  (database-info ");
0648:                quoteString(dumpOut, session.getDatabaseInfo());
0649:                dumpOut.println(")");
0650:
0651:                final long expectedRows = dumpSource.getExpectedRows();
0652:                dumpOut.println("  (estimated-rows '" + expectedRows + "')");
0653:
0654:                dumpOut.print("  (meta (");
0655:                for (int i = 0; i < metaProps.length; ++i) {
0656:                    final MetaProperty p = metaProps[i];
0657:                    printWidth(dumpOut, p.fieldName, p.renderWidth(), i != 0);
0658:                }
0659:                dumpOut.println(")");
0660:                dumpOut.print("\t(");
0661:                for (int i = 0; i < metaProps.length; ++i) {
0662:                    final MetaProperty p = metaProps[i];
0663:                    printWidth(dumpOut, p.typeName, p.renderWidth(), i != 0);
0664:                }
0665:                dumpOut.println("))");
0666:
0667:                dumpOut.print("  (data ");
0668:                ResultSet rset = null;
0669:                Statement stmt = null;
0670:                try {
0671:                    long rows = 0;
0672:                    ProgressWriter progressWriter = new ProgressWriter(
0673:                            expectedRows, HenPlus.msg());
0674:                    rset = dumpSource.getResultSet();
0675:                    stmt = dumpSource.getStatement();
0676:                    boolean isFirst = true;
0677:                    while (_running && rset.next()) {
0678:                        ++rows;
0679:                        progressWriter.update(rows);
0680:                        if (!isFirst)
0681:                            dumpOut.print("\n\t");
0682:                        isFirst = false;
0683:                        dumpOut.print("(");
0684:
0685:                        for (int i = 0; i < metaProps.length; ++i) {
0686:                            final int col = i + 1;
0687:                            final int this Type = metaProps[i].getType();
0688:                            switch (this Type) {
0689:                            case HP_INTEGER:
0690:                            case HP_NUMERIC:
0691:                            case HP_DOUBLE: {
0692:                                String val = rset.getString(col);
0693:                                if (rset.wasNull())
0694:                                    dumpOut.print(NULL_STR);
0695:                                else
0696:                                    dumpOut.print(val);
0697:                                break;
0698:                            }
0699:
0700:                            case HP_TIMESTAMP: {
0701:                                Timestamp val = rset.getTimestamp(col);
0702:                                if (rset.wasNull())
0703:                                    dumpOut.print(NULL_STR);
0704:                                else {
0705:                                    quoteString(dumpOut, val.toString());
0706:                                }
0707:                                break;
0708:                            }
0709:
0710:                            case HP_TIME: {
0711:                                Time val = rset.getTime(col);
0712:                                if (rset.wasNull())
0713:                                    dumpOut.print(NULL_STR);
0714:                                else {
0715:                                    quoteString(dumpOut, val.toString());
0716:                                }
0717:                                break;
0718:                            }
0719:
0720:                            case HP_DATE: {
0721:                                java.sql.Date val = rset.getDate(col);
0722:                                if (rset.wasNull())
0723:                                    dumpOut.print(NULL_STR);
0724:                                else {
0725:                                    quoteString(dumpOut, val.toString());
0726:                                }
0727:                                break;
0728:                            }
0729:
0730:                            case HP_STRING: {
0731:                                String val = rset.getString(col);
0732:                                if (rset.wasNull())
0733:                                    dumpOut.print(NULL_STR);
0734:                                else {
0735:                                    quoteString(dumpOut, val);
0736:                                }
0737:                                break;
0738:                            }
0739:
0740:                            default:
0741:                                throw new IllegalArgumentException("type "
0742:                                        + TYPES[this Type]
0743:                                        + " not supported yet");
0744:                            }
0745:                            if (metaProps.length > col)
0746:                                dumpOut.print(",");
0747:                            else
0748:                                dumpOut.print(")");
0749:                        }
0750:                    }
0751:                    progressWriter.finish();
0752:                    dumpOut.println(")");
0753:                    dumpOut.println("  (rows " + rows + "))\n");
0754:
0755:                    HenPlus.msg().print("(" + rows + " rows)\n");
0756:                    long execTime = System.currentTimeMillis() - startTime;
0757:
0758:                    HenPlus.msg()
0759:                            .print(
0760:                                    "dumping '" + dumpSource.getTableName()
0761:                                            + "' took ");
0762:                    TimeRenderer.printTime(execTime, HenPlus.msg());
0763:                    HenPlus.msg().print(" total; ");
0764:                    TimeRenderer.printFraction(execTime, rows, HenPlus.msg());
0765:                    HenPlus.msg().println(" / row");
0766:                    if (expectedRows >= 0 && rows != expectedRows) {
0767:                        HenPlus.msg().println(
0768:                                " == Warning: 'select count(*)' in the"
0769:                                        + " beginning resulted in "
0770:                                        + expectedRows
0771:                                        + " but the dump exported " + rows
0772:                                        + " rows == ");
0773:                    }
0774:
0775:                    if (!_running) {
0776:                        HenPlus
0777:                                .msg()
0778:                                .println(
0779:                                        " == INTERRUPTED. Wait for statement to cancel.. ==");
0780:                        if (stmt != null)
0781:                            stmt.cancel();
0782:                    }
0783:                } catch (Exception e) {
0784:                    //HenPlus.msg().println(selectStmt.toString());
0785:                    throw e; // handle later.
0786:                } finally {
0787:                    if (rset != null) {
0788:                        try {
0789:                            rset.close();
0790:                        } catch (Exception e) {
0791:                        }
0792:                    }
0793:                    if (stmt != null) {
0794:                        try {
0795:                            stmt.close();
0796:                        } catch (Exception e) {
0797:                        }
0798:                    }
0799:                }
0800:                return SUCCESS;
0801:            }
0802:
0803:            private Number readNumber(LineNumberReader in) throws IOException {
0804:                String token = readToken(in);
0805:                // separated sign.
0806:                if (token.length() == 1
0807:                        && (token.equals("+") || token.equals("-"))) {
0808:                    token += readToken(in);
0809:                }
0810:                if (token.equals(NULL_STR))
0811:                    return null;
0812:                try {
0813:                    if (token.indexOf('.') > 0) {
0814:                        return Double.valueOf(token);
0815:                    }
0816:                    if (token.length() < 10) {
0817:                        return Integer.valueOf(token);
0818:                    } else if (token.length() < 19) {
0819:                        return Long.valueOf(token);
0820:                    } else {
0821:                        return new BigDecimal(token);
0822:                    }
0823:                } catch (NumberFormatException e) {
0824:                    raiseException(in, "Number format " + token + ": "
0825:                            + e.getMessage());
0826:                }
0827:                return null;
0828:            }
0829:
0830:            private int readTableDump(LineNumberReader reader,
0831:                    String fileEncoding, SQLSession session, boolean hot,
0832:                    int commitPoint) throws IOException, SQLException,
0833:                    InterruptedException {
0834:                MetaProperty[] metaProperty = null;
0835:                String tableName = null;
0836:                int dumpVersion = -1;
0837:                int compatibleVersion = -1;
0838:                String henplusVersion = null;
0839:                String databaseInfo = null;
0840:                String dumpTime = null;
0841:                String whereClause = null;
0842:                String token;
0843:                long importedRows = -1;
0844:                long expectedRows = -1;
0845:                long estimatedRows = -1;
0846:                long problemRows = -1;
0847:                Connection conn = null;
0848:                PreparedStatement stmt = null;
0849:
0850:                expect(reader, '(');
0851:                token = readToken(reader);
0852:                if (!"tabledump".equals(token))
0853:                    raiseException(reader, "'tabledump' expected");
0854:                tableName = readString(reader);
0855:                long startTime = System.currentTimeMillis();
0856:                while (_running) {
0857:                    skipWhite(reader);
0858:                    int rawChar = reader.read();
0859:                    if (rawChar == -1)
0860:                        return SUCCESS; // EOF reached.
0861:                    char inCh = (char) rawChar;
0862:                    if (inCh == ')')
0863:                        break;
0864:                    if (inCh != '(') {
0865:                        raiseException(reader, "'(' or ')' expected");
0866:                    }
0867:                    token = readToken(reader);
0868:
0869:                    if ("dump-version".equals(token)) {
0870:                        token = readToken(reader);
0871:                        try {
0872:                            dumpVersion = Integer.valueOf(token).intValue();
0873:                        } catch (Exception e) {
0874:                            raiseException(reader,
0875:                                    "expected dump version number");
0876:                        }
0877:                        token = readToken(reader);
0878:                        try {
0879:                            compatibleVersion = Integer.valueOf(token)
0880:                                    .intValue();
0881:                        } catch (Exception e) {
0882:                            raiseException(reader,
0883:                                    "expected compatible version number");
0884:                        }
0885:                        checkSupported(compatibleVersion);
0886:                        expect(reader, ')');
0887:                    }
0888:
0889:                    else if ("file-encoding".equals(token)) {
0890:                        token = readString(reader);
0891:                        if (!token.equals(fileEncoding)) {
0892:                            throw new EncodingMismatchException(token);
0893:                        }
0894:                        expect(reader, ')');
0895:                    }
0896:
0897:                    else if ("henplus-version".equals(token)) {
0898:                        token = readString(reader);
0899:                        henplusVersion = token;
0900:                        expect(reader, ')');
0901:                    }
0902:
0903:                    else if ("rows".equals(token)) {
0904:                        token = readToken(reader);
0905:                        expectedRows = Integer.valueOf(token).intValue();
0906:                        expect(reader, ')');
0907:                    }
0908:
0909:                    else if ("estimated-rows".equals(token)) {
0910:                        token = readString(reader);
0911:                        estimatedRows = Integer.valueOf(token).intValue();
0912:                        expect(reader, ')');
0913:                    }
0914:
0915:                    else if ("database-info".equals(token)) {
0916:                        databaseInfo = readString(reader);
0917:                        expect(reader, ')');
0918:                    }
0919:
0920:                    else if ("where-clause".equals(token)) {
0921:                        whereClause = readString(reader);
0922:                        expect(reader, ')');
0923:                    }
0924:
0925:                    else if ("time".equals(token)) {
0926:                        dumpTime = readString(reader);
0927:                        expect(reader, ')');
0928:                    }
0929:
0930:                    else if ("meta".equals(token)) {
0931:                        if (dumpVersion < 0 || compatibleVersion < 0) {
0932:                            raiseException(reader,
0933:                                    "cannot read meta data without dump-version information");
0934:                        }
0935:                        metaProperty = parseMetaData(reader);
0936:                    }
0937:
0938:                    else if ("data".equals(token)) {
0939:                        if (metaProperty == null) {
0940:                            raiseException(reader, "no meta-data available");
0941:                        }
0942:                        if (tableName == null) {
0943:                            raiseException(reader, "no table name known");
0944:                        }
0945:                        if (hot) {
0946:                            StringBuffer prep = new StringBuffer("INSERT INTO ");
0947:                            prep.append(tableName);
0948:                            prep.append(" (");
0949:                            for (int i = 0; i < metaProperty.length; ++i) {
0950:                                prep.append(metaProperty[i].fieldName);
0951:                                if (i + 1 < metaProperty.length)
0952:                                    prep.append(",");
0953:                            }
0954:                            prep.append(") VALUES (");
0955:                            for (int i = 0; i < metaProperty.length; ++i) {
0956:                                prep.append("?");
0957:                                if (i + 1 < metaProperty.length)
0958:                                    prep.append(",");
0959:                            }
0960:                            prep.append(")");
0961:                            //HenPlus.msg().println(prep.toString());
0962:                            conn = session.getConnection();
0963:                            stmt = conn.prepareStatement(prep.toString());
0964:                        }
0965:
0966:                        HenPlus.msg().println(
0967:                                (hot ? "importing" : "verifying")
0968:                                        + " table dump created with HenPlus "
0969:                                        + henplusVersion
0970:                                        + "\nfor table           : "
0971:                                        + tableName
0972:                                        + "\nfrom database       : "
0973:                                        + databaseInfo
0974:                                        + "\nat                  : " + dumpTime
0975:                                        + "\ndump format version : "
0976:                                        + dumpVersion);
0977:                        if (whereClause != null) {
0978:                            HenPlus.msg().println(
0979:                                    "projection          : " + whereClause);
0980:                        }
0981:
0982:                        ProgressWriter progressWriter = new ProgressWriter(
0983:                                estimatedRows, HenPlus.msg());
0984:                        importedRows = 0;
0985:                        problemRows = 0;
0986:                        _running = true;
0987:                        while (_running) {
0988:                            skipWhite(reader);
0989:                            inCh = (char) reader.read();
0990:                            if (inCh == ')')
0991:                                break;
0992:                            if (inCh != '(') {
0993:                                raiseException(reader, "'(' or ')' expected");
0994:                            }
0995:                            // we are now at the beginning of the row.
0996:                            ++importedRows;
0997:                            progressWriter.update(importedRows);
0998:                            for (int i = 0; i < metaProperty.length; ++i) {
0999:                                final int col = i + 1;
1000:                                final int type = metaProperty[i].type;
1001:                                switch (type) {
1002:                                case HP_NUMERIC:
1003:                                case HP_DOUBLE:
1004:                                case HP_INTEGER: {
1005:                                    Number number = readNumber(reader);
1006:                                    if (stmt != null) {
1007:                                        if (number == null) {
1008:                                            if (type == HP_NUMERIC) {
1009:                                                stmt
1010:                                                        .setNull(col,
1011:                                                                Types.NUMERIC);
1012:                                            } else if (type == HP_INTEGER) {
1013:                                                stmt
1014:                                                        .setNull(col,
1015:                                                                Types.INTEGER);
1016:                                            } else if (type == HP_DOUBLE) {
1017:                                                stmt.setNull(col, Types.DOUBLE);
1018:                                            }
1019:                                        } else {
1020:                                            if (number instanceof  Integer) {
1021:                                                stmt.setInt(col, number
1022:                                                        .intValue());
1023:                                            } else if (number instanceof  Long) {
1024:                                                stmt.setLong(col, number
1025:                                                        .longValue());
1026:                                            } else if (number instanceof  Double) {
1027:                                                stmt.setDouble(col, number
1028:                                                        .doubleValue());
1029:                                            } else if (number instanceof  BigDecimal) {
1030:                                                stmt.setBigDecimal(col,
1031:                                                        (BigDecimal) number);
1032:                                            }
1033:                                        }
1034:                                    }
1035:                                    break;
1036:                                }
1037:
1038:                                case HP_TIMESTAMP: {
1039:                                    String val = readString(reader);
1040:                                    metaProperty[i].updateMaxLength(val);
1041:                                    if (stmt != null) {
1042:                                        if (val == null) {
1043:                                            stmt.setTimestamp(col, null);
1044:                                        } else {
1045:                                            stmt.setTimestamp(col, Timestamp
1046:                                                    .valueOf(val));
1047:                                        }
1048:                                    }
1049:                                    break;
1050:                                }
1051:
1052:                                case HP_TIME: {
1053:                                    String val = readString(reader);
1054:                                    metaProperty[i].updateMaxLength(val);
1055:                                    if (stmt != null) {
1056:                                        if (val == null) {
1057:                                            stmt.setTime(col, null);
1058:                                        } else {
1059:                                            stmt
1060:                                                    .setTime(col, Time
1061:                                                            .valueOf(val));
1062:                                        }
1063:                                    }
1064:                                    break;
1065:                                }
1066:
1067:                                case HP_DATE: {
1068:                                    String val = readString(reader);
1069:                                    metaProperty[i].updateMaxLength(val);
1070:                                    if (stmt != null) {
1071:                                        if (val == null) {
1072:                                            stmt.setDate(col, null);
1073:                                        } else {
1074:                                            stmt.setDate(col, java.sql.Date
1075:                                                    .valueOf(val));
1076:                                        }
1077:                                    }
1078:                                    break;
1079:                                }
1080:
1081:                                case HP_STRING: {
1082:                                    String val = readString(reader);
1083:                                    metaProperty[i].updateMaxLength(val);
1084:                                    if (stmt != null) {
1085:                                        stmt.setString(col, val);
1086:                                    }
1087:                                    break;
1088:                                }
1089:
1090:                                default:
1091:                                    throw new IllegalArgumentException("type "
1092:                                            + TYPES[metaProperty[i].type]
1093:                                            + " not supported yet");
1094:                                }
1095:                                expect(reader,
1096:                                        (i + 1 < metaProperty.length) ? ','
1097:                                                : ')');
1098:                            }
1099:                            try {
1100:                                if (stmt != null)
1101:                                    stmt.execute();
1102:                            } catch (SQLException e) {
1103:                                String msg = e.getMessage();
1104:                                // oracle adds CR for some reason.
1105:                                if (msg != null)
1106:                                    msg = msg.trim();
1107:                                reportProblem(msg);
1108:                                ++problemRows;
1109:                            }
1110:
1111:                            // commit every once in a while.
1112:                            if (hot && (commitPoint >= 0)
1113:                                    && importedRows % commitPoint == 0) {
1114:                                conn.commit();
1115:                            }
1116:                        }
1117:                        progressWriter.finish();
1118:                    }
1119:
1120:                    else {
1121:                        HenPlus.msg()
1122:                                .println("ignoring unknown token " + token);
1123:                        dumpTime = readString(reader);
1124:                        expect(reader, ')');
1125:                    }
1126:                }
1127:
1128:                // return final count.
1129:                finishProblemReports();
1130:
1131:                if (!hot) {
1132:                    printMetaDataInfo(metaProperty);
1133:                }
1134:
1135:                // final commit, if commitPoints are enabled.
1136:                if (hot && commitPoint >= 0) {
1137:                    conn.commit();
1138:                }
1139:
1140:                // we're done.
1141:                if (stmt != null) {
1142:                    try {
1143:                        stmt.close();
1144:                    } catch (Exception e) {
1145:                    }
1146:                }
1147:
1148:                if (expectedRows >= 0 && expectedRows != importedRows) {
1149:                    HenPlus.msg().println(
1150:                            "WARNING: expected " + expectedRows + " but got "
1151:                                    + importedRows + " rows");
1152:                } else {
1153:                    HenPlus.msg().println("ok. ");
1154:                }
1155:                HenPlus.msg().print("(" + importedRows + " rows total");
1156:                if (hot)
1157:                    HenPlus.msg().print(" / " + problemRows + " with errors");
1158:                HenPlus.msg().print("; ");
1159:                long execTime = System.currentTimeMillis() - startTime;
1160:                TimeRenderer.printTime(execTime, HenPlus.msg());
1161:                HenPlus.msg().print(" total; ");
1162:                TimeRenderer.printFraction(execTime, importedRows, HenPlus
1163:                        .msg());
1164:                HenPlus.msg().println(" / row)");
1165:                return SUCCESS;
1166:            }
1167:
1168:            public MetaProperty[] parseMetaData(LineNumberReader in)
1169:                    throws IOException {
1170:                List metaList = new ArrayList();
1171:                expect(in, '(');
1172:                for (;;) {
1173:                    String colName = readString(in);
1174:                    metaList.add(new MetaProperty(colName));
1175:                    skipWhite(in);
1176:                    char inCh = (char) in.read();
1177:                    if (inCh == ')')
1178:                        break;
1179:                    if (inCh != ',') {
1180:                        raiseException(in, "',' or ')' expected");
1181:                    }
1182:                }
1183:                expect(in, '(');
1184:                MetaProperty[] result = (MetaProperty[]) metaList
1185:                        .toArray(new MetaProperty[metaList.size()]);
1186:                for (int i = 0; i < result.length; ++i) {
1187:                    String typeName = readString(in);
1188:                    result[i].setTypeName(typeName);
1189:                    expect(in, (i + 1 < result.length) ? ',' : ')');
1190:                }
1191:                expect(in, ')');
1192:                return result;
1193:            }
1194:
1195:            String lastProblem = null;
1196:            long problemCount = 0;
1197:
1198:            private void reportProblem(String msg) {
1199:                if (msg == null)
1200:                    return;
1201:                if (msg.equals(lastProblem)) {
1202:                    ++problemCount;
1203:                } else {
1204:                    finishProblemReports();
1205:                    problemCount = 1;
1206:                    HenPlus.msg().print("Problem: " + msg);
1207:                    lastProblem = msg;
1208:                }
1209:            }
1210:
1211:            private void finishProblemReports() {
1212:                if (problemCount > 1) {
1213:                    HenPlus.msg().print("   (" + problemCount + " times)");
1214:                }
1215:                if (problemCount > 0) {
1216:                    HenPlus.msg().println();
1217:                }
1218:                lastProblem = null;
1219:                problemCount = 0;
1220:            }
1221:
1222:            public void checkSupported(int version)
1223:                    throws IllegalArgumentException {
1224:                if (version <= 0 || version > DUMP_VERSION) {
1225:                    throw new IllegalArgumentException(
1226:                            "incompatible dump-version");
1227:                }
1228:            }
1229:
1230:            public void expect(LineNumberReader in, char ch) throws IOException {
1231:                skipWhite(in);
1232:                char inCh = (char) in.read();
1233:                if (ch != inCh)
1234:                    raiseException(in, "'" + ch + "' expected");
1235:            }
1236:
1237:            private void quoteString(PrintStream out, String in) {
1238:                StringBuffer buf = new StringBuffer();
1239:                buf.append("'");
1240:                int len = in.length();
1241:                for (int i = 0; i < len; ++i) {
1242:                    char c = in.charAt(i);
1243:                    if (c == '\'' || c == '\\') {
1244:                        buf.append("\\");
1245:                    }
1246:                    buf.append(c);
1247:                }
1248:                buf.append("'");
1249:                out.print(buf.toString());
1250:            }
1251:
1252:            /**
1253:             * skip whitespace. return false, if EOF reached.
1254:             */
1255:            private boolean skipWhite(Reader in) throws IOException {
1256:                in.mark(1);
1257:                int c;
1258:                while ((c = in.read()) > 0) {
1259:                    if (!Character.isWhitespace((char) c)) {
1260:                        in.reset();
1261:                        return true;
1262:                    }
1263:                    in.mark(1);
1264:                }
1265:                return false;
1266:            }
1267:
1268:            private String readToken(LineNumberReader in) throws IOException {
1269:                skipWhite(in);
1270:                StringBuffer token = new StringBuffer();
1271:                in.mark(1);
1272:                int c;
1273:                while ((c = in.read()) > 0) {
1274:                    char ch = (char) c;
1275:                    if (Character.isWhitespace(ch) || ch == ';' || ch == ','
1276:                            || ch == '(' || ch == ')') {
1277:                        in.reset();
1278:                        break;
1279:                    }
1280:                    token.append(ch);
1281:                    in.mark(1);
1282:                }
1283:                return token.toString();
1284:            }
1285:
1286:            /**
1287:             * read a string. This is either NULL without quotes or a quoted
1288:             * string.
1289:             */
1290:            private String readString(LineNumberReader in) throws IOException {
1291:                int nullParseState = 0;
1292:                int c;
1293:                while ((c = in.read()) > 0) {
1294:                    char ch = (char) c;
1295:                    // unless we already parse the NULL string, skip whitespaces.
1296:                    if (nullParseState == 0 && Character.isWhitespace(ch))
1297:                        continue;
1298:                    if (ch == '\'')
1299:                        break; // -> opening string.
1300:                    if (Character.toUpperCase(ch) == NULL_STR
1301:                            .charAt(nullParseState)) {
1302:                        ++nullParseState;
1303:                        if (nullParseState == NULL_STR.length())
1304:                            return null;
1305:                        continue;
1306:                    }
1307:                    raiseException(in, "unecpected character '" + ch + "'");
1308:                }
1309:
1310:                // ok, we found an opening quote.
1311:                StringBuffer result = new StringBuffer();
1312:                while ((c = in.read()) > 0) {
1313:                    if (c == '\\') {
1314:                        c = in.read();
1315:                        if (c < 0) {
1316:                            raiseException(in,
1317:                                    "excpected character after backslash escape");
1318:                        }
1319:                        result.append((char) c);
1320:                        continue;
1321:                    }
1322:                    char ch = (char) c;
1323:                    if (ch == '\'')
1324:                        break; // End Of String.
1325:                    result.append((char) c);
1326:                }
1327:                return result.toString();
1328:            }
1329:
1330:            /**
1331:             * convenience method to throw Exceptions containing the line
1332:             * number
1333:             */
1334:            private void raiseException(LineNumberReader in, String msg)
1335:                    throws IOException {
1336:                throw new IOException("line " + (in.getLineNumber() + 1) + ": "
1337:                        + msg);
1338:            }
1339:
1340:            private void printMetaDataInfo(MetaProperty[] prop) {
1341:                HenPlus.out().println();
1342:                META_HEADERS[0].resetWidth();
1343:                META_HEADERS[1].resetWidth();
1344:                TableRenderer table = new TableRenderer(META_HEADERS, HenPlus
1345:                        .out());
1346:                for (int i = 0; i < prop.length; ++i) {
1347:                    Column[] row = new Column[3];
1348:                    row[0] = new Column(prop[i].getFieldName());
1349:                    row[1] = new Column(prop[i].getTypeName());
1350:                    row[2] = new Column(prop[i].getMaxLength());
1351:                    table.addRow(row);
1352:                }
1353:                table.closeTable();
1354:            }
1355:
1356:            //-- Interruptable interface
1357:            public synchronized void interrupt() {
1358:                _running = false;
1359:            }
1360:
1361:            private void beginInterruptableSection() {
1362:                _running = true;
1363:                SigIntHandler.getInstance().pushInterruptable(this );
1364:            }
1365:
1366:            private void endInterruptableSection() {
1367:                SigIntHandler.getInstance().popInterruptable();
1368:            }
1369:
1370:            /**
1371:             * complete the table name.
1372:             */
1373:            public Iterator complete(CommandDispatcher disp,
1374:                    String partialCommand, String lastWord) {
1375:                StringTokenizer st = new StringTokenizer(partialCommand);
1376:                String cmd = (String) st.nextElement();
1377:                int argc = st.countTokens();
1378:                if (lastWord.length() > 0) {
1379:                    argc--;
1380:                }
1381:
1382:                if ("dump-conditional".equals(cmd)) {
1383:                    if (argc == 0) {
1384:                        return new FileCompletionIterator(partialCommand,
1385:                                lastWord);
1386:                    } else if (argc == 1) {
1387:                        if (lastWord.startsWith("\"")) {
1388:                            lastWord = lastWord.substring(1);
1389:                        }
1390:                        return _tableCompleter.completeTableName(HenPlus
1391:                                .getInstance().getCurrentSession(), lastWord);
1392:                    } else if (argc > 1) {
1393:                        st.nextElement(); // discard filename.
1394:                        String table = (String) st.nextElement();
1395:                        Collection columns = _tableCompleter.columnsFor(table);
1396:                        NameCompleter compl = new NameCompleter(columns);
1397:                        return compl.getAlternatives(lastWord);
1398:                    }
1399:                } else if ("dump-out".equals(cmd)) {
1400:                    // this is true for dump-out und verify-dump
1401:                    if (argc == 0) {
1402:                        return new FileCompletionIterator(partialCommand,
1403:                                lastWord);
1404:                    }
1405:                    if (argc > 0) {
1406:                        if (lastWord.startsWith("\"")) {
1407:                            lastWord = lastWord.substring(1);
1408:                        }
1409:                        final HashSet alreadyGiven = new HashSet();
1410:                        /*
1411:                         * do not complete the tables we already gave on the
1412:                         * commandline.
1413:                         */
1414:                        while (st.hasMoreElements()) {
1415:                            alreadyGiven.add(st.nextElement());
1416:                        }
1417:
1418:                        final Iterator it = _tableCompleter.completeTableName(
1419:                                HenPlus.getInstance().getCurrentSession(),
1420:                                lastWord);
1421:                        return new Iterator() {
1422:                            String table = null;
1423:
1424:                            public boolean hasNext() {
1425:                                while (it.hasNext()) {
1426:                                    table = (String) it.next();
1427:                                    if (alreadyGiven.contains(table)) {
1428:                                        continue;
1429:                                    }
1430:                                    return true;
1431:                                }
1432:                                return false;
1433:                            }
1434:
1435:                            public Object next() {
1436:                                return table;
1437:                            }
1438:
1439:                            public void remove() {
1440:                                throw new UnsupportedOperationException("no!");
1441:                            }
1442:                        };
1443:                    }
1444:                } else {
1445:                    if (argc == 0) {
1446:                        return new FileCompletionIterator(partialCommand,
1447:                                lastWord);
1448:                    }
1449:                }
1450:                return null;
1451:            }
1452:
1453:            /**
1454:             * return a descriptive string.
1455:             */
1456:            public String getShortDescription() {
1457:                return "handle table dumps";
1458:            }
1459:
1460:            public String getSynopsis(String cmd) {
1461:                if ("dump-out".equals(cmd)) {
1462:                    return cmd + " <filename> (<tablename> | <prefix>* | *)+;";
1463:                } else if ("dump-conditional".equals(cmd)) {
1464:                    return cmd + " <filename> <tablename> [<where-clause>]";
1465:                } else if ("dump-select".equals(cmd)) {
1466:                    return cmd + " <filename> <exported-tablename> select ...";
1467:                } else if ("dump-in".equals(cmd)) {
1468:                    return cmd + " <filename> [<commit-intervall>]";
1469:                } else if ("verify-dump".equals(cmd)) {
1470:                    return cmd + " <filename>";
1471:                }
1472:                return cmd;
1473:            }
1474:
1475:            public String getLongDescription(String cmd) {
1476:                String dsc = null;
1477:                if ("dump-out".equals(cmd)) {
1478:                    dsc = "\tDump out the contents of the table(s) given to the file\n"
1479:                            + "\twith the given name. If the filename ends with '.gz', the\n"
1480:                            + "\tcontent is gzip'ed automatically .. that saves space.\n"
1481:                            + "\n"
1482:                            + "\tFor the selection of the tables you want to dump-out,\n"
1483:                            + "\tyou are able to use wildcards (*) to match all tables or\n"
1484:                            + "\ta specific set of tables.\n"
1485:                            + "\tE.g. you might specify \"*\" to match all tables, or\"tb_*\"\n"
1486:                            + "\tto match all tables starting with \"tb_\".\n"
1487:                            + "\n"
1488:                            + "\tThe dump-format allows to read in the data back into\n"
1489:                            + "\tthe database ('dump-in' command). And unlike pure SQL-insert\n"
1490:                            + "\tstatements, this works even across databases.\n"
1491:                            + "\tSo you can make a dump of your MySQL database and read it\n"
1492:                            + "\tback into Oracle, for instance. To achive this database\n"
1493:                            + "\tindependence, the data is stored in a canonical form\n"
1494:                            + "\t(e.g. the Time/Date). The file itself is human readable\n"
1495:                            + "\tand uses less space than simple SQL-inserts:\n"
1496:                            + "\t----------------\n"
1497:                            + "\t  (tabledump 'student'\n"
1498:                            + "\t    (dump-version 1 1)\n"
1499:                            + "\t    (henplus-version 0.3.3)\n"
1500:                            + "\t    (database-info 'MySQL - 3.23.47')\n"
1501:                            + "\t    (meta ('name',   'sex',    'student_id')\n"
1502:                            + "\t          ('STRING', 'STRING', 'INTEGER'   ))\n"
1503:                            + "\t    (data ('Megan','F',1)\n"
1504:                            + "\t          ('Joseph','M',2)\n"
1505:                            + "\t          ('Kyle','M',3)\n"
1506:                            + "\t          ('Mac Donald\\'s','M',4))\n"
1507:                            + "\t    (rows 4))\n"
1508:                            + "\t----------------\n\n"
1509:                            + "\tTODOs\n"
1510:                            + "\tThis format contains only the data, no\n"
1511:                            + "\tcanonical 'create table' statement - so the table must\n"
1512:                            + "\talready exist at import time. Both these features will\n"
1513:                            + "\tbe in later versions of HenPlus.";
1514:                }
1515:
1516:                else if ("dump-conditional".equals(cmd)) {
1517:                    dsc = "\tLike dump-out, but dump only the rows of a single table\n"
1518:                            + "\tthat match the where clause.";
1519:                }
1520:
1521:                else if ("dump-in".equals(cmd)) {
1522:                    dsc = "\tRead back in the data that has been dumped out with the\n"
1523:                            + "\t'dump-out' command. If the filename ends with '.gz',\n"
1524:                            + "\tthen the content is assumed to be gzipped and is\n"
1525:                            + "\tunpacked on the fly. The 'dump-in' command fills\n"
1526:                            + "\texisting tables, it does not create missing ones!\n\n"
1527:                            + "\tExisting content ist not deleted before, dump-in just\n"
1528:                            + "\tinserts all data found in the dump.\n\n"
1529:                            + "\tInternally, the import uses a prepared statement that is\n"
1530:                            + "\tfed with the typed data according to the meta data (see\n"
1531:                            + "\tdump-out for the file format). This evens out differences\n"
1532:                            + "\tbetween databases and of course enhances speed compared\n"
1533:                            + "\tto non-prepared statements.\n\n"
1534:                            + "\tThe import is done in the current transaction, unless\n"
1535:                            + "\tyou specify the commit-interval. The commit-interval specify\n"
1536:                            + "\tthe number of inserts, that are executed before an commit\n"
1537:                            + "\tis done. For a large amount of data this option is\n"
1538:                            + "\tnecessary, since otherwise your rollback-segments\n"
1539:                            + "\tmight get a problem ;-)";
1540:                }
1541:
1542:                else if ("verify-dump".equals(cmd)) {
1543:                    dsc = "\tLike dump-in, but a 'dry run'. Won't change anything\n"
1544:                            + "\tbut parses the whole file to determine whether it has\n"
1545:                            + "\tsyntax errors or is damaged. Any syntax errors are\n"
1546:                            + "\treported as it were a 'dump-in'. Problems that might\n"
1547:                            + "\toccur in a 'real' import in the database (that might\n"
1548:                            + "\tdetect, that the import would create duplicate keys for\n"
1549:                            + "\tinstance) can not be determined, of course.";
1550:                }
1551:                return dsc;
1552:            }
1553:
1554:            /**
1555:             * A source for dumps.
1556:             */
1557:            private interface DumpSource {
1558:                MetaProperty[] getMetaProperties() throws SQLException;
1559:
1560:                String getDescription();
1561:
1562:                String getTableName();
1563:
1564:                Statement getStatement() throws SQLException;
1565:
1566:                ResultSet getResultSet() throws SQLException;
1567:
1568:                long getExpectedRows();
1569:            }
1570:
1571:            private static class SelectDumpSource implements  DumpSource {
1572:                private final SQLSession _session;
1573:                private final String _sqlStat;
1574:                private final String _exportTable;
1575:                private MetaProperty[] _meta;
1576:                private Statement _workingStatement;
1577:                private ResultSet _resultSet;
1578:
1579:                SelectDumpSource(SQLSession session, String exportTable,
1580:                        String sqlStat) {
1581:                    _session = session;
1582:                    _sqlStat = sqlStat;
1583:                    _exportTable = exportTable;
1584:                }
1585:
1586:                public MetaProperty[] getMetaProperties() throws SQLException {
1587:                    if (_meta != null)
1588:                        return _meta;
1589:                    ResultSet rset = getResultSet();
1590:
1591:                    ResultSetMetaData rsMeta = rset.getMetaData();
1592:                    final int cols = rsMeta.getColumnCount();
1593:                    _meta = new MetaProperty[cols];
1594:                    for (int i = 0; i < cols; ++i) {
1595:                        _meta[i] = new MetaProperty(
1596:                                rsMeta.getColumnName(i + 1), rsMeta
1597:                                        .getColumnType(i + 1));
1598:                    }
1599:                    return _meta;
1600:                }
1601:
1602:                public String getDescription() {
1603:                    return _sqlStat;
1604:                }
1605:
1606:                public String getTableName() {
1607:                    return _exportTable;
1608:                }
1609:
1610:                public Statement getStatement() throws SQLException {
1611:                    return _workingStatement;
1612:                }
1613:
1614:                public ResultSet getResultSet() throws SQLException {
1615:                    if (_resultSet != null) {
1616:                        return _resultSet;
1617:                    }
1618:                    _workingStatement = _session.createStatement();
1619:                    try {
1620:                        _workingStatement.setFetchSize(1000);
1621:                    } catch (Exception e) {
1622:                        // ignore
1623:                    }
1624:                    _resultSet = _workingStatement.executeQuery(_sqlStat);
1625:                    return _resultSet;
1626:                }
1627:
1628:                public long getExpectedRows() {
1629:                    return -1;
1630:                }
1631:            }
1632:
1633:            private static class TableDumpSource implements  DumpSource {
1634:                private final SQLSession _session;
1635:                private final String _table;
1636:                private final String _schema;
1637:                private final boolean _caseSensitive;
1638:                private MetaProperty[] _meta;
1639:                private Statement _workingStatement;
1640:                private String _whereClause;
1641:
1642:                TableDumpSource(String schema, String table,
1643:                        boolean caseSensitive, SQLSession session) {
1644:                    _session = session;
1645:                    _schema = schema;
1646:                    _table = table;
1647:                    _caseSensitive = caseSensitive;
1648:                }
1649:
1650:                public String getDescription() {
1651:                    return "table '" + _table + "'";
1652:                }
1653:
1654:                public String getTableName() {
1655:                    return _table;
1656:                }
1657:
1658:                public void setWhereClause(String whereClause) {
1659:                    _whereClause = whereClause;
1660:                }
1661:
1662:                public Statement getStatement() {
1663:                    return _workingStatement;
1664:                }
1665:
1666:                public MetaProperty[] getMetaProperties() throws SQLException {
1667:                    if (_meta != null)
1668:                        return _meta;
1669:
1670:                    List metaList = new ArrayList();
1671:                    Connection conn = _session.getConnection();
1672:                    ResultSet rset = null;
1673:                    try {
1674:                        /*
1675:                         * if the same column is in more than one schema defined, then
1676:                         * oracle seems to write them out twice..
1677:                         */
1678:                        Set doubleCheck = new HashSet();
1679:                        DatabaseMetaData meta = conn.getMetaData();
1680:                        rset = meta.getColumns(conn.getCatalog(), _schema,
1681:                                _table, null);
1682:                        while (rset.next()) {
1683:                            String columnName = rset.getString(4);
1684:                            if (doubleCheck.contains(columnName))
1685:                                continue;
1686:                            doubleCheck.add(columnName);
1687:                            metaList.add(new MetaProperty(columnName, rset
1688:                                    .getInt(5)));
1689:                        }
1690:                    } finally {
1691:                        if (rset != null) {
1692:                            try {
1693:                                rset.close();
1694:                            } catch (Exception e) {
1695:                            }
1696:                        }
1697:                    }
1698:                    _meta = (MetaProperty[]) metaList
1699:                            .toArray(new MetaProperty[metaList.size()]);
1700:                    return _meta;
1701:                }
1702:
1703:                public ResultSet getResultSet() throws SQLException {
1704:                    final StringBuffer selectStmt = new StringBuffer("SELECT ");
1705:                    for (int i = 0; i < _meta.length; ++i) {
1706:                        final MetaProperty p = _meta[i];
1707:                        if (i != 0)
1708:                            selectStmt.append(", ");
1709:                        selectStmt.append(p.fieldName);
1710:                    }
1711:
1712:                    selectStmt.append(" FROM ").append(_table);
1713:                    if (_whereClause != null) {
1714:                        selectStmt.append(" WHERE ").append(_whereClause);
1715:                    }
1716:                    _workingStatement = _session.createStatement();
1717:                    try {
1718:                        _workingStatement.setFetchSize(1000);
1719:                    } catch (Exception e) {
1720:                        // ignore
1721:                    }
1722:                    return _workingStatement
1723:                            .executeQuery(selectStmt.toString());
1724:                }
1725:
1726:                public long getExpectedRows() {
1727:                    CancelWriter selectInfo = new CancelWriter(HenPlus.msg());
1728:                    Statement stmt = null;
1729:                    ResultSet rset = null;
1730:                    try {
1731:                        selectInfo.print("determining number of rows...");
1732:                        stmt = _session.createStatement();
1733:                        StringBuffer countStmt = new StringBuffer(
1734:                                "SELECT count(*) from ");
1735:                        countStmt.append(_table);
1736:                        if (_whereClause != null) {
1737:                            countStmt.append(" WHERE ");
1738:                            countStmt.append(_whereClause);
1739:                        }
1740:                        rset = stmt.executeQuery(countStmt.toString());
1741:                        rset.next();
1742:                        return rset.getLong(1);
1743:                    } catch (Exception e) {
1744:                        return -1;
1745:                    } finally {
1746:                        if (rset != null) {
1747:                            try {
1748:                                rset.close();
1749:                            } catch (Exception e) {
1750:                            }
1751:                        }
1752:                        if (stmt != null) {
1753:                            try {
1754:                                stmt.close();
1755:                            } catch (Exception e) {
1756:                            }
1757:                        }
1758:                        selectInfo.cancel();
1759:                    }
1760:                }
1761:            }
1762:
1763:            private static class MetaProperty {
1764:                private int maxLen;
1765:                public final String fieldName;
1766:                public int type;
1767:                public String typeName;
1768:
1769:                public MetaProperty(String fieldName) {
1770:                    this .fieldName = fieldName;
1771:                    maxLen = -1;
1772:                }
1773:
1774:                public MetaProperty(String fieldName, int jdbcType) {
1775:                    this .fieldName = fieldName;
1776:                    this .typeName = (String) JDBCTYPE2TYPENAME.get(new Integer(
1777:                            jdbcType));
1778:                    if (this .typeName == null) {
1779:                        HenPlus.msg()
1780:                                .println(
1781:                                        "cannot handle type '" + type
1782:                                                + "' for field '"
1783:                                                + this .fieldName
1784:                                                + "'; trying String..");
1785:                        this .type = HP_STRING;
1786:                        this .typeName = TYPES[this .type];
1787:                    } else {
1788:                        this .type = findType(typeName);
1789:                    }
1790:                    maxLen = -1;
1791:                }
1792:
1793:                public String getFieldName() {
1794:                    return fieldName;
1795:                }
1796:
1797:                public String getTypeName() {
1798:                    return typeName;
1799:                }
1800:
1801:                public void setTypeName(String typeName) {
1802:                    this .type = findType(typeName);
1803:                    this .typeName = typeName;
1804:                }
1805:
1806:                public void updateMaxLength(String val) {
1807:                    if (val != null) {
1808:                        updateMaxLength(val.length());
1809:                    }
1810:                }
1811:
1812:                public void updateMaxLength(int maxLen) {
1813:                    if (maxLen > this .maxLen) {
1814:                        this .maxLen = maxLen;
1815:                    }
1816:                }
1817:
1818:                public int getMaxLength() {
1819:                    return this .maxLen;
1820:                }
1821:
1822:                /**
1823:                 * find the type in the array. uses linear search, but this is
1824:                 * only a small list.
1825:                 */
1826:                private int findType(String typeName) {
1827:                    if (typeName == null) {
1828:                        throw new IllegalArgumentException("empty type ?");
1829:                    }
1830:                    typeName = typeName.toUpperCase();
1831:                    for (int i = 0; i < TYPES.length; ++i) {
1832:                        if (TYPES[i].equals(typeName))
1833:                            return i;
1834:                    }
1835:                    throw new IllegalArgumentException("invalid type "
1836:                            + typeName);
1837:                }
1838:
1839:                public int getType() {
1840:                    return type;
1841:                }
1842:
1843:                public int renderWidth() {
1844:                    return Math.max(typeName.length(), fieldName.length());
1845:                }
1846:            }
1847:
1848:            private static class EncodingMismatchException extends IOException {
1849:                private static final long serialVersionUID = 1;
1850:                private final String _encoding;
1851:
1852:                public EncodingMismatchException(String encoding) {
1853:                    super ("file encoding Mismatch Exception; got " + encoding);
1854:                    _encoding = encoding;
1855:                }
1856:
1857:                public String getEncoding() {
1858:                    return _encoding;
1859:                }
1860:            }
1861:
1862:            // reading BLOBs.
1863:            //private static class Base64InputStream extends InputStream { }
1864:
1865:            // reading CLOBs
1866:            //private static class Base64Reader extends Reader { }
1867:        }
1868:
1869:        /*
1870:         * Local variables:
1871:         * c-basic-offset: 4
1872:         * compile-command: "ant -emacs -find build.xml"
1873:         * End:
1874:         */
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.