001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.dao.db;
016    
017    import com.liferay.counter.service.CounterLocalServiceUtil;
018    import com.liferay.portal.dao.orm.common.SQLTransformer;
019    import com.liferay.portal.kernel.dao.db.DB;
020    import com.liferay.portal.kernel.dao.db.Index;
021    import com.liferay.portal.kernel.dao.jdbc.DataAccess;
022    import com.liferay.portal.kernel.exception.SystemException;
023    import com.liferay.portal.kernel.io.unsync.UnsyncBufferedReader;
024    import com.liferay.portal.kernel.io.unsync.UnsyncStringReader;
025    import com.liferay.portal.kernel.log.Log;
026    import com.liferay.portal.kernel.log.LogFactoryUtil;
027    import com.liferay.portal.kernel.util.FileUtil;
028    import com.liferay.portal.kernel.util.GetterUtil;
029    import com.liferay.portal.kernel.util.PropertiesUtil;
030    import com.liferay.portal.kernel.util.StringBundler;
031    import com.liferay.portal.kernel.util.StringPool;
032    import com.liferay.portal.kernel.util.StringUtil;
033    import com.liferay.portal.kernel.util.Validator;
034    import com.liferay.portal.util.ClassLoaderUtil;
035    import com.liferay.portal.velocity.VelocityUtil;
036    import com.liferay.util.SimpleCounter;
037    
038    import java.io.File;
039    import java.io.FileReader;
040    import java.io.IOException;
041    import java.io.InputStream;
042    
043    import java.sql.Connection;
044    import java.sql.SQLException;
045    import java.sql.Statement;
046    
047    import java.util.Collections;
048    import java.util.Enumeration;
049    import java.util.HashMap;
050    import java.util.HashSet;
051    import java.util.List;
052    import java.util.Map;
053    import java.util.Properties;
054    import java.util.Set;
055    import java.util.regex.Matcher;
056    import java.util.regex.Pattern;
057    
058    import javax.naming.NamingException;
059    
060    /**
061     * @author Alexander Chow
062     * @author Ganesh Ram
063     * @author Brian Wing Shun Chan
064     * @author Daniel Kocsis
065     */
066    public abstract class BaseDB implements DB {
067    
068            @Override
069            public void addIndexes(
070                            Connection con, String indexesSQL, Set<String> validIndexNames)
071                    throws IOException {
072    
073                    if (_log.isInfoEnabled()) {
074                            _log.info("Adding indexes");
075                    }
076    
077                    UnsyncBufferedReader bufferedReader = new UnsyncBufferedReader(
078                            new UnsyncStringReader(indexesSQL));
079    
080                    String sql = null;
081    
082                    while ((sql = bufferedReader.readLine()) != null) {
083                            if (Validator.isNull(sql)) {
084                                    continue;
085                            }
086    
087                            int y = sql.indexOf(" on ");
088                            int x = sql.lastIndexOf(" ", y - 1);
089    
090                            String indexName = sql.substring(x + 1, y);
091    
092                            if (validIndexNames.contains(indexName)) {
093                                    continue;
094                            }
095    
096                            if (_log.isInfoEnabled()) {
097                                    _log.info(sql);
098                            }
099    
100                            try {
101                                    runSQL(con, sql);
102                            }
103                            catch (Exception e) {
104                                    if (_log.isWarnEnabled()) {
105                                            _log.warn(e.getMessage() + ": " + sql);
106                                    }
107                            }
108                    }
109            }
110    
111            @Override
112            public void buildCreateFile(String sqlDir, String databaseName)
113                    throws IOException {
114    
115                    buildCreateFile(sqlDir, databaseName, POPULATED);
116                    buildCreateFile(sqlDir, databaseName, MINIMAL);
117                    buildCreateFile(sqlDir, databaseName, SHARDED);
118            }
119    
120            @Override
121            public void buildCreateFile(
122                            String sqlDir, String databaseName, int population)
123                    throws IOException {
124    
125                    String suffix = getSuffix(population);
126    
127                    File file = new File(
128                            sqlDir + "/create" + suffix + "/create" + suffix + "-" +
129                                    getServerName() + ".sql");
130    
131                    if (population != SHARDED) {
132                            String content = buildCreateFileContent(
133                                    sqlDir, databaseName, population);
134    
135                            if (content != null) {
136                                    FileUtil.write(file, content);
137                            }
138                    }
139                    else {
140                            String content = buildCreateFileContent(
141                                    sqlDir, databaseName, MINIMAL);
142    
143                            if (content != null) {
144                                    FileUtil.write(file, content);
145                            }
146    
147                            content = buildCreateFileContent(
148                                    sqlDir, databaseName + "1", MINIMAL);
149    
150                            if (content != null) {
151                                    FileUtil.write(file, content, false, true);
152                            }
153    
154                            content = buildCreateFileContent(
155                                    sqlDir, databaseName + "2", MINIMAL);
156    
157                            if (content != null) {
158                                    FileUtil.write(file, content, false, true);
159                            }
160                    }
161            }
162    
163            @Override
164            public abstract String buildSQL(String template) throws IOException;
165    
166            @Override
167            public void buildSQLFile(String sqlDir, String fileName)
168                    throws IOException {
169    
170                    String template = buildTemplate(sqlDir, fileName);
171    
172                    if (Validator.isNull(template)) {
173                            return;
174                    }
175    
176                    template = buildSQL(template);
177    
178                    FileUtil.write(
179                            sqlDir + "/" + fileName + "/" + fileName + "-" + getServerName() +
180                                    ".sql",
181                            template);
182            }
183    
184            @Override
185            @SuppressWarnings("unused")
186            public List<Index> getIndexes(Connection con) throws SQLException {
187                    return Collections.emptyList();
188            }
189    
190            @Override
191            public String getTemplateFalse() {
192                    return getTemplate()[2];
193            }
194    
195            @Override
196            public String getTemplateTrue() {
197                    return getTemplate()[1];
198            }
199    
200            @Override
201            public String getType() {
202                    return _type;
203            }
204    
205            @Override
206            public long increment() throws SystemException {
207                    return CounterLocalServiceUtil.increment();
208            }
209    
210            @Override
211            public long increment(String name) throws SystemException {
212                    return CounterLocalServiceUtil.increment(name);
213            }
214    
215            @Override
216            public boolean isSupportsAlterColumnName() {
217                    return _SUPPORTS_ALTER_COLUMN_NAME;
218            }
219    
220            @Override
221            public boolean isSupportsAlterColumnType() {
222                    return _SUPPORTS_ALTER_COLUMN_TYPE;
223            }
224    
225            @Override
226            public boolean isSupportsDateMilliseconds() {
227                    return _SUPPORTS_DATE_MILLISECONDS;
228            }
229    
230            @Override
231            public boolean isSupportsInlineDistinct() {
232                    return _SUPPORTS_INLINE_DISTINCT;
233            }
234    
235            @Override
236            public boolean isSupportsScrollableResults() {
237                    return _SUPPORTS_SCROLLABLE_RESULTS;
238            }
239    
240            @Override
241            public boolean isSupportsStringCaseSensitiveQuery() {
242                    return _supportsStringCaseSensitiveQuery;
243            }
244    
245            @Override
246            public boolean isSupportsUpdateWithInnerJoin() {
247                    return _SUPPORTS_UPDATE_WITH_INNER_JOIN;
248            }
249    
250            @Override
251            public void runSQL(Connection con, String sql)
252                    throws IOException, SQLException {
253    
254                    runSQL(con, new String[] {sql});
255            }
256    
257            @Override
258            public void runSQL(Connection con, String[] sqls)
259                    throws IOException, SQLException {
260    
261                    Statement s = null;
262    
263                    try {
264                            s = con.createStatement();
265    
266                            for (int i = 0; i < sqls.length; i++) {
267                                    String sql = buildSQL(sqls[i]);
268    
269                                    sql = SQLTransformer.transform(sql.trim());
270    
271                                    if (sql.endsWith(";")) {
272                                            sql = sql.substring(0, sql.length() - 1);
273                                    }
274    
275                                    if (sql.endsWith("go")) {
276                                            sql = sql.substring(0, sql.length() - 2);
277                                    }
278    
279                                    if (_log.isDebugEnabled()) {
280                                            _log.debug(sql);
281                                    }
282    
283                                    try {
284                                            s.executeUpdate(sql);
285                                    }
286                                    catch (SQLException sqle) {
287                                            handleSQLException(sql, sqle);
288                                    }
289                            }
290                    }
291                    finally {
292                            DataAccess.cleanUp(s);
293                    }
294            }
295    
296            @Override
297            public void runSQL(String sql) throws IOException, SQLException {
298                    runSQL(new String[] {sql});
299            }
300    
301            @Override
302            public void runSQL(String[] sqls) throws IOException, SQLException {
303                    Connection con = DataAccess.getConnection();
304    
305                    try {
306                            runSQL(con, sqls);
307                    }
308                    finally {
309                            DataAccess.cleanUp(con);
310                    }
311            }
312    
313            @Override
314            public void runSQLTemplate(String path)
315                    throws IOException, NamingException, SQLException {
316    
317                    runSQLTemplate(path, true);
318            }
319    
320            @Override
321            public void runSQLTemplate(String path, boolean failOnError)
322                    throws IOException, NamingException, SQLException {
323    
324                    ClassLoader classLoader = ClassLoaderUtil.getContextClassLoader();
325    
326                    InputStream is = classLoader.getResourceAsStream(
327                            "com/liferay/portal/tools/sql/dependencies/" + path);
328    
329                    if (is == null) {
330                            is = classLoader.getResourceAsStream(path);
331                    }
332    
333                    if (is == null) {
334                            _log.error("Invalid path " + path);
335    
336                            if (failOnError) {
337                                    throw new IOException("Invalid path " + path);
338                            }
339                            else {
340                                    return;
341                            }
342                    }
343    
344                    String template = StringUtil.read(is);
345    
346                    is.close();
347    
348                    boolean evaluate = path.endsWith(".vm");
349    
350                    runSQLTemplateString(template, evaluate, failOnError);
351            }
352    
353            @Override
354            public void runSQLTemplateString(
355                            String template, boolean evaluate, boolean failOnError)
356                    throws IOException, NamingException, SQLException {
357    
358                    if (evaluate) {
359                            try {
360                                    template = evaluateVM(template);
361                            }
362                            catch (Exception e) {
363                                    _log.error(e, e);
364                            }
365                    }
366    
367                    StringBundler sb = new StringBundler();
368    
369                    UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader(
370                            new UnsyncStringReader(template));
371    
372                    String line = null;
373    
374                    while ((line = unsyncBufferedReader.readLine()) != null) {
375                            if (!line.startsWith("##")) {
376                                    if (line.startsWith("@include ")) {
377                                            int pos = line.indexOf(" ");
378    
379                                            String includeFileName = line.substring(pos + 1);
380    
381                                            ClassLoader classLoader =
382                                                    ClassLoaderUtil.getContextClassLoader();
383    
384                                            InputStream is = classLoader.getResourceAsStream(
385                                                    "com/liferay/portal/tools/sql/dependencies/" +
386                                                            includeFileName);
387    
388                                            if (is == null) {
389                                                    is = classLoader.getResourceAsStream(includeFileName);
390                                            }
391    
392                                            String include = StringUtil.read(is);
393    
394                                            is.close();
395    
396                                            if (includeFileName.endsWith(".vm")) {
397                                                    try {
398                                                            include = evaluateVM(include);
399                                                    }
400                                                    catch (Exception e) {
401                                                            _log.error(e, e);
402                                                    }
403                                            }
404    
405                                            include = convertTimestamp(include);
406                                            include = replaceTemplate(include, getTemplate());
407    
408                                            runSQLTemplateString(include, false, true);
409                                    }
410                                    else {
411                                            sb.append(line);
412    
413                                            if (line.endsWith(";")) {
414                                                    String sql = sb.toString();
415    
416                                                    sb.setIndex(0);
417    
418                                                    try {
419                                                            if (!sql.equals("COMMIT_TRANSACTION;")) {
420                                                                    runSQL(sql);
421                                                            }
422                                                            else {
423                                                                    if (_log.isDebugEnabled()) {
424                                                                            _log.debug("Skip commit sql");
425                                                                    }
426                                                            }
427                                                    }
428                                                    catch (IOException ioe) {
429                                                            if (failOnError) {
430                                                                    throw ioe;
431                                                            }
432                                                            else if (_log.isWarnEnabled()) {
433                                                                    _log.warn(ioe.getMessage());
434                                                            }
435                                                    }
436                                                    catch (SecurityException se) {
437                                                            if (failOnError) {
438                                                                    throw se;
439                                                            }
440                                                            else if (_log.isWarnEnabled()) {
441                                                                    _log.warn(se.getMessage());
442                                                            }
443                                                    }
444                                                    catch (SQLException sqle) {
445                                                            if (failOnError) {
446                                                                    throw sqle;
447                                                            }
448                                                            else if (_log.isWarnEnabled()) {
449                                                                    String message = GetterUtil.getString(
450                                                                            sqle.getMessage());
451    
452                                                                    if (!message.startsWith("Duplicate key name")) {
453                                                                            _log.warn(message + ": " + buildSQL(sql));
454                                                                    }
455    
456                                                                    if (message.startsWith("Duplicate entry") ||
457                                                                            message.startsWith(
458                                                                                    "Specified key was too long")) {
459    
460                                                                            _log.error(line);
461                                                                    }
462                                                            }
463                                                    }
464                                            }
465                                    }
466                            }
467                    }
468    
469                    unsyncBufferedReader.close();
470            }
471    
472            @Override
473            public void setSupportsStringCaseSensitiveQuery(
474                    boolean supportsStringCaseSensitiveQuery) {
475    
476                    if (_log.isInfoEnabled()) {
477                            if (supportsStringCaseSensitiveQuery) {
478                                    _log.info("Database supports case sensitive queries");
479                            }
480                            else {
481                                    _log.info("Database does not support case sensitive queries");
482                            }
483                    }
484    
485                    _supportsStringCaseSensitiveQuery = supportsStringCaseSensitiveQuery;
486            }
487    
488            @Override
489            public void updateIndexes(
490                            Connection con, String tablesSQL, String indexesSQL,
491                            String indexesProperties, boolean dropIndexes)
492                    throws IOException, SQLException {
493    
494                    List<Index> indexes = getIndexes(con);
495    
496                    Set<String> validIndexNames = null;
497    
498                    if (dropIndexes) {
499                            validIndexNames = dropIndexes(
500                                    con, tablesSQL, indexesSQL, indexesProperties, indexes);
501                    }
502                    else {
503                            validIndexNames = new HashSet<String>();
504    
505                            for (Index index : indexes) {
506                                    String indexName = index.getIndexName().toUpperCase();
507    
508                                    validIndexNames.add(indexName);
509                            }
510                    }
511    
512                    addIndexes(con, indexesSQL, validIndexNames);
513            }
514    
515            protected BaseDB(String type) {
516                    _type = type;
517    
518                    String[] actual = getTemplate();
519    
520                    for (int i = 0; i < TEMPLATE.length; i++) {
521                            _templateMap.put(TEMPLATE[i], actual[i]);
522                    }
523            }
524    
525            protected String[] buildColumnNameTokens(String line) {
526                    String[] words = StringUtil.split(line, ' ');
527    
528                    String nullable = "";
529    
530                    if (words.length == 7) {
531                            nullable = "not null;";
532                    }
533    
534                    String[] template = {
535                            words[1], words[2], words[3], words[4], nullable
536                    };
537    
538                    return template;
539            }
540    
541            protected String[] buildColumnTypeTokens(String line) {
542                    String[] words = StringUtil.split(line, ' ');
543    
544                    String nullable = "";
545    
546                    if (words.length == 6) {
547                            nullable = "not null;";
548                    }
549                    else if (words.length == 5) {
550                            nullable = words[4];
551                    }
552                    else if (words.length == 4) {
553                            nullable = "not null;";
554    
555                            if (words[3].endsWith(";")) {
556                                    words[3] = words[3].substring(0, words[3].length() - 1);
557                            }
558                    }
559    
560                    String[] template = {
561                            words[1], words[2], "", words[3], nullable
562                    };
563    
564                    return template;
565            }
566    
567            protected abstract String buildCreateFileContent(
568                            String sqlDir, String databaseName, int population)
569                    throws IOException;
570    
571            protected String[] buildTableNameTokens(String line) {
572                    String[] words = StringUtil.split(line, StringPool.SPACE);
573    
574                    return new String[] {words[1], words[2]};
575            }
576    
577            protected String buildTemplate(String sqlDir, String fileName)
578                    throws IOException {
579    
580                    String template = readFile(sqlDir + "/" + fileName + ".sql");
581    
582                    if (fileName.equals("portal") || fileName.equals("portal-minimal") ||
583                            fileName.equals("update-5.0.1-5.1.0")) {
584    
585                            UnsyncBufferedReader unsyncBufferedReader =
586                                    new UnsyncBufferedReader(new UnsyncStringReader(template));
587    
588                            StringBundler sb = new StringBundler();
589    
590                            String line = null;
591    
592                            while ((line = unsyncBufferedReader.readLine()) != null) {
593                                    if (line.startsWith("@include ")) {
594                                            int pos = line.indexOf(" ");
595    
596                                            String includeFileName = line.substring(pos + 1);
597    
598                                            File includeFile = new File(sqlDir + "/" + includeFileName);
599    
600                                            if (!includeFile.exists()) {
601                                                    continue;
602                                            }
603    
604                                            String include = FileUtil.read(includeFile);
605    
606                                            if (includeFileName.endsWith(".vm")) {
607                                                    try {
608                                                            include = evaluateVM(include);
609                                                    }
610                                                    catch (Exception e) {
611                                                            _log.error(e, e);
612                                                    }
613                                            }
614    
615                                            include = convertTimestamp(include);
616                                            include = replaceTemplate(include, getTemplate());
617    
618                                            sb.append(include);
619                                            sb.append("\n\n");
620                                    }
621                                    else {
622                                            sb.append(line);
623                                            sb.append("\n");
624                                    }
625                            }
626    
627                            unsyncBufferedReader.close();
628    
629                            template = sb.toString();
630                    }
631    
632                    if (fileName.equals("indexes") && (this instanceof SybaseDB)) {
633                            template = removeBooleanIndexes(sqlDir, template);
634                    }
635    
636                    return template;
637            }
638    
639            protected String convertTimestamp(String data) {
640                    String s = null;
641    
642                    if (this instanceof MySQLDB) {
643                            s = StringUtil.replace(data, "SPECIFIC_TIMESTAMP_", "");
644                    }
645                    else {
646                            Matcher matcher = _timestampPattern.matcher(data);
647    
648                            s = matcher.replaceAll("CURRENT_TIMESTAMP");
649                    }
650    
651                    return s;
652            }
653    
654            protected Set<String> dropIndexes(
655                            Connection con, String tablesSQL, String indexesSQL,
656                            String indexesProperties, List<Index> indexes)
657                    throws IOException, SQLException {
658    
659                    if (_log.isInfoEnabled()) {
660                            _log.info("Dropping stale indexes");
661                    }
662    
663                    Set<String> validIndexNames = new HashSet<String>();
664    
665                    if (indexes.isEmpty()) {
666                            return validIndexNames;
667                    }
668    
669                    String tablesSQLLowerCase = tablesSQL.toLowerCase();
670                    String indexesSQLLowerCase = indexesSQL.toLowerCase();
671    
672                    Properties indexesPropertiesObj = PropertiesUtil.load(
673                            indexesProperties);
674    
675                    Enumeration<String> enu =
676                            (Enumeration<String>)indexesPropertiesObj.propertyNames();
677    
678                    while (enu.hasMoreElements()) {
679                            String key = enu.nextElement();
680    
681                            String value = indexesPropertiesObj.getProperty(key);
682    
683                            indexesPropertiesObj.setProperty(key.toLowerCase(), value);
684                    }
685    
686                    for (Index index : indexes) {
687                            String indexNameUpperCase = index.getIndexName().toUpperCase();
688                            String indexNameLowerCase = indexNameUpperCase.toLowerCase();
689                            String tableName = index.getTableName();
690                            String tableNameLowerCase = tableName.toLowerCase();
691                            boolean unique = index.isUnique();
692    
693                            validIndexNames.add(indexNameUpperCase);
694    
695                            if (indexesPropertiesObj.containsKey(indexNameLowerCase)) {
696                                    if (unique &&
697                                            indexesSQLLowerCase.contains(
698                                                    "create unique index " + indexNameLowerCase + " ")) {
699    
700                                            continue;
701                                    }
702    
703                                    if (!unique &&
704                                            indexesSQLLowerCase.contains(
705                                                    "create index " + indexNameLowerCase + " ")) {
706    
707                                            continue;
708                                    }
709                            }
710                            else if (!tablesSQLLowerCase.contains(
711                                                    "create table " + tableNameLowerCase + " (")) {
712    
713                                    continue;
714                            }
715    
716                            validIndexNames.remove(indexNameUpperCase);
717    
718                            String sql =
719                                    "drop index " + indexNameUpperCase + " on " + tableName;
720    
721                            if (_log.isInfoEnabled()) {
722                                    _log.info(sql);
723                            }
724    
725                            runSQL(con, sql);
726                    }
727    
728                    return validIndexNames;
729            }
730    
731            protected String evaluateVM(String template) throws Exception {
732                    Map<String, Object> variables = new HashMap<String, Object>();
733    
734                    variables.put("counter", new SimpleCounter());
735    
736                    ClassLoader classLoader = ClassLoaderUtil.getContextClassLoader();
737    
738                    try {
739                            ClassLoaderUtil.setContextClassLoader(
740                                    ClassLoaderUtil.getPortalClassLoader());
741    
742                            template = VelocityUtil.evaluate(template, variables);
743                    }
744                    finally {
745                            ClassLoaderUtil.setContextClassLoader(classLoader);
746                    }
747    
748                    // Trim insert statements because it breaks MySQL Query Browser
749    
750                    UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader(
751                            new UnsyncStringReader(template));
752    
753                    StringBundler sb = new StringBundler();
754    
755                    String line = null;
756    
757                    while ((line = unsyncBufferedReader.readLine()) != null) {
758                            line = line.trim();
759    
760                            sb.append(line);
761                            sb.append("\n");
762                    }
763    
764                    unsyncBufferedReader.close();
765    
766                    template = sb.toString();
767                    template = StringUtil.replace(template, "\n\n\n", "\n\n");
768    
769                    return template;
770            }
771    
772            protected String getCreateTablesContent(String sqlDir, String suffix)
773                    throws IOException {
774    
775                    StringBundler sb = new StringBundler(8);
776    
777                    sb.append(sqlDir);
778    
779                    if (!sqlDir.endsWith("/WEB-INF/sql")) {
780                            sb.append("/portal");
781                            sb.append(suffix);
782                            sb.append("/portal");
783                    }
784                    else {
785                            sb.append("/tables");
786                            sb.append(suffix);
787                            sb.append("/tables");
788                    }
789    
790                    sb.append(suffix);
791                    sb.append(StringPool.DASH);
792                    sb.append(getServerName());
793                    sb.append(".sql");
794    
795                    return readFile(sb.toString());
796            }
797    
798            protected abstract String getServerName();
799    
800            protected String getSuffix(int type) {
801                    if (type == MINIMAL) {
802                            return "-minimal";
803                    }
804                    else if (type == SHARDED) {
805                            return "-sharded";
806                    }
807                    else {
808                            return StringPool.BLANK;
809                    }
810            }
811    
812            protected abstract String[] getTemplate();
813    
814            protected void handleSQLException(String sql, SQLException sqle)
815                    throws SQLException {
816    
817                    if (_log.isDebugEnabled()) {
818                            StringBundler sb = new StringBundler(18);
819    
820                            sb.append("SQL: ");
821                            sb.append(sql);
822                            sb.append("\nSQL state: ");
823                            sb.append(sqle.getSQLState());
824                            sb.append("\nVendor: ");
825                            sb.append(getType());
826                            sb.append("\nVendor error code: ");
827                            sb.append(sqle.getErrorCode());
828                            sb.append("\nVendor error message: ");
829                            sb.append(sqle.getMessage());
830    
831                            _log.debug(sb.toString());
832                    }
833    
834                    throw sqle;
835            }
836    
837            protected String readFile(String fileName) throws IOException {
838                    if (FileUtil.exists(fileName)) {
839                            return FileUtil.read(fileName);
840                    }
841                    else {
842                            return StringPool.BLANK;
843                    }
844            }
845    
846            protected String readSQL(String fileName, String comments, String eol)
847                    throws IOException {
848    
849                    if (!FileUtil.exists(fileName)) {
850                            return StringPool.BLANK;
851                    }
852    
853                    UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader(
854                            new FileReader(new File(fileName)));
855    
856                    StringBundler sb = new StringBundler();
857    
858                    String line = null;
859    
860                    while ((line = unsyncBufferedReader.readLine()) != null) {
861                            if (!line.startsWith(comments)) {
862                                    line = StringUtil.replace(
863                                            line, new String[] {"\n", "\t"}, new String[] {"", ""});
864    
865                                    if (line.endsWith(";")) {
866                                            sb.append(line.substring(0, line.length() - 1));
867                                            sb.append(eol);
868                                    }
869                                    else {
870                                            sb.append(line);
871                                    }
872                            }
873                    }
874    
875                    unsyncBufferedReader.close();
876    
877                    return sb.toString();
878            }
879    
880            protected String removeBooleanIndexes(String sqlDir, String data)
881                    throws IOException {
882    
883                    String portalData = readFile(sqlDir + "/portal-tables.sql");
884    
885                    if (Validator.isNull(portalData)) {
886                            return StringPool.BLANK;
887                    }
888    
889                    UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader(
890                            new UnsyncStringReader(data));
891    
892                    StringBundler sb = new StringBundler();
893    
894                    String line = null;
895    
896                    while ((line = unsyncBufferedReader.readLine()) != null) {
897                            boolean append = true;
898    
899                            int x = line.indexOf(" on ");
900    
901                            if (x != -1) {
902                                    int y = line.indexOf(" (", x);
903    
904                                    String table = line.substring(x + 4, y);
905    
906                                    x = y + 2;
907                                    y = line.indexOf(")", x);
908    
909                                    String[] columns = StringUtil.split(line.substring(x, y));
910    
911                                    x = portalData.indexOf("create table " + table + " (");
912                                    y = portalData.indexOf(");", x);
913    
914                                    String portalTableData = portalData.substring(x, y);
915    
916                                    for (int i = 0; i < columns.length; i++) {
917                                            if (portalTableData.contains(
918                                                            columns[i].trim() + " BOOLEAN")) {
919    
920                                                    append = false;
921    
922                                                    break;
923                                            }
924                                    }
925                            }
926    
927                            if (append) {
928                                    sb.append(line);
929                                    sb.append("\n");
930                            }
931                    }
932    
933                    unsyncBufferedReader.close();
934    
935                    return sb.toString();
936            }
937    
938            protected String removeInserts(String data) throws IOException {
939                    UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader(
940                            new UnsyncStringReader(data));
941    
942                    StringBundler sb = new StringBundler();
943    
944                    String line = null;
945    
946                    while ((line = unsyncBufferedReader.readLine()) != null) {
947                            if (!line.startsWith("insert into ") &&
948                                    !line.startsWith("update ")) {
949    
950                                    sb.append(line);
951                                    sb.append("\n");
952                            }
953                    }
954    
955                    unsyncBufferedReader.close();
956    
957                    return sb.toString();
958            }
959    
960            protected String removeLongInserts(String data) throws IOException {
961                    UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader(
962                            new UnsyncStringReader(data));
963    
964                    StringBundler sb = new StringBundler();
965    
966                    String line = null;
967    
968                    while ((line = unsyncBufferedReader.readLine()) != null) {
969                            if (!line.startsWith("insert into Image (") &&
970                                    !line.startsWith("insert into JournalArticle (") &&
971                                    !line.startsWith("insert into JournalStructure (") &&
972                                    !line.startsWith("insert into JournalTemplate (")) {
973    
974                                    sb.append(line);
975                                    sb.append("\n");
976                            }
977                    }
978    
979                    unsyncBufferedReader.close();
980    
981                    return sb.toString();
982            }
983    
984            protected String removeNull(String content) {
985                    content = StringUtil.replace(content, " = null", " = NULL");
986                    content = StringUtil.replace(content, " is null", " IS NULL");
987                    content = StringUtil.replace(content, " not null", " not_null");
988                    content = StringUtil.replace(content, " null", "");
989                    content = StringUtil.replace(content, " not_null", " not null");
990    
991                    return content;
992            }
993    
994            protected String replaceTemplate(String template, String[] actual) {
995                    if ((template == null) || (TEMPLATE == null) || (actual == null)) {
996                            return null;
997                    }
998    
999                    if (TEMPLATE.length != actual.length) {
1000                            return template;
1001                    }
1002    
1003                    StringBundler sb = null;
1004    
1005                    int endIndex = 0;
1006    
1007                    Matcher matcher = _templatePattern.matcher(template);
1008    
1009                    while (matcher.find()) {
1010                            int startIndex = matcher.start();
1011    
1012                            if (sb == null) {
1013                                    sb = new StringBundler();
1014                            }
1015    
1016                            sb.append(template.substring(endIndex, startIndex));
1017    
1018                            endIndex = matcher.end();
1019    
1020                            String matched = template.substring(startIndex, endIndex);
1021    
1022                            sb.append(_templateMap.get(matched));
1023                    }
1024    
1025                    if (sb == null) {
1026                            return template;
1027                    }
1028    
1029                    if (template.length() > endIndex) {
1030                            sb.append(template.substring(endIndex));
1031                    }
1032    
1033                    return sb.toString();
1034            }
1035    
1036            protected abstract String reword(String data) throws IOException;
1037    
1038            protected static final String ALTER_COLUMN_NAME = "alter_column_name ";
1039    
1040            protected static final String ALTER_COLUMN_TYPE = "alter_column_type ";
1041    
1042            protected static final String ALTER_TABLE_NAME = "alter_table_name ";
1043    
1044            protected static final String DROP_INDEX = "drop index";
1045    
1046            protected static final String DROP_PRIMARY_KEY = "drop primary key";
1047    
1048            protected static final String[] RENAME_TABLE_TEMPLATE = {
1049                    "@old-table@", "@new-table@"
1050            };
1051    
1052            protected static final String[] REWORD_TEMPLATE = {
1053                    "@table@", "@old-column@", "@new-column@", "@type@", "@nullable@"
1054            };
1055    
1056            protected static final String[] TEMPLATE = {
1057                    "##", "TRUE", "FALSE", "'01/01/1970'", "CURRENT_TIMESTAMP", " BLOB",
1058                    " SBLOB", " BOOLEAN", " DATE", " DOUBLE", " INTEGER", " LONG",
1059                    " STRING", " TEXT", " VARCHAR", " IDENTITY", "COMMIT_TRANSACTION"
1060            };
1061    
1062            private static final boolean _SUPPORTS_ALTER_COLUMN_NAME = true;
1063    
1064            private static final boolean _SUPPORTS_ALTER_COLUMN_TYPE = true;
1065    
1066            private static final boolean _SUPPORTS_DATE_MILLISECONDS = true;
1067    
1068            private static final boolean _SUPPORTS_INLINE_DISTINCT = true;
1069    
1070            private static final boolean _SUPPORTS_SCROLLABLE_RESULTS = true;
1071    
1072            private static final boolean _SUPPORTS_UPDATE_WITH_INNER_JOIN = true;
1073    
1074            private static Log _log = LogFactoryUtil.getLog(BaseDB.class);
1075    
1076            private static Pattern _templatePattern;
1077            private static Pattern _timestampPattern = Pattern.compile(
1078                    "SPECIFIC_TIMESTAMP_\\d+");
1079    
1080            private boolean _supportsStringCaseSensitiveQuery;
1081            private Map<String, String> _templateMap = new HashMap<String, String>();
1082            private String _type;
1083    
1084            static {
1085                    StringBundler sb = new StringBundler(TEMPLATE.length * 3 - 3);
1086    
1087                    for (int i = 0; i < TEMPLATE.length; i++) {
1088                            String variable = TEMPLATE[i];
1089    
1090                            if (variable.equals("##") || variable.equals("'01/01/1970'")) {
1091                                    sb.append(variable);
1092                            }
1093                            else {
1094                                    sb.append(variable);
1095                                    sb.append("\\b");
1096                            }
1097    
1098                            if (i < (TEMPLATE.length - 1)) {
1099                                    sb.append(StringPool.PIPE);
1100                            }
1101                    }
1102    
1103                    _templatePattern = Pattern.compile(sb.toString());
1104            }
1105    
1106    }