001
014
015 package com.liferay.portal.dao.db;
016
017 import com.liferay.counter.service.CounterLocalServiceUtil;
018 import com.liferay.portal.kernel.dao.db.DB;
019 import com.liferay.portal.kernel.dao.db.DBFactoryUtil;
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.velocity.VelocityUtil;
035 import com.liferay.util.SimpleCounter;
036
037 import java.io.File;
038 import java.io.FileReader;
039 import java.io.IOException;
040 import java.io.InputStream;
041
042 import java.sql.Connection;
043 import java.sql.SQLException;
044 import java.sql.Statement;
045
046 import java.util.Collections;
047 import java.util.Enumeration;
048 import java.util.HashMap;
049 import java.util.HashSet;
050 import java.util.List;
051 import java.util.Map;
052 import java.util.Properties;
053 import java.util.Set;
054
055 import javax.naming.NamingException;
056
057
062 public abstract class BaseDB implements DB {
063
064 public void buildCreateFile(String sqlDir, String databaseName)
065 throws IOException {
066
067 buildCreateFile(sqlDir, databaseName, POPULATED);
068 buildCreateFile(sqlDir, databaseName, MINIMAL);
069 buildCreateFile(sqlDir, databaseName, SHARDED);
070 }
071
072 public void buildCreateFile(
073 String sqlDir, String databaseName, int population)
074 throws IOException {
075
076 String suffix = getSuffix(population);
077
078 File file = new File(
079 sqlDir + "/create" + suffix + "/create" + suffix + "-" +
080 getServerName() + ".sql");
081
082 if (population != SHARDED) {
083 String content = buildCreateFileContent(
084 sqlDir, databaseName, population);
085
086 if (content != null) {
087 FileUtil.write(file, content);
088 }
089 }
090 else {
091 String content = buildCreateFileContent(
092 sqlDir, databaseName, MINIMAL);
093
094 if (content != null) {
095 FileUtil.write(file, content);
096 }
097
098 content = buildCreateFileContent(
099 sqlDir, databaseName + "1", MINIMAL);
100
101 if (content != null) {
102 FileUtil.write(file, content, false, true);
103 }
104
105 content = buildCreateFileContent(
106 sqlDir, databaseName + "2", MINIMAL);
107
108 if (content != null) {
109 FileUtil.write(file, content, false, true);
110 }
111 }
112 }
113
114 public abstract String buildSQL(String template) throws IOException;
115
116 public void buildSQLFile(String sqlDir, String fileName)
117 throws IOException {
118
119 String template = buildTemplate(sqlDir, fileName);
120
121 if (Validator.isNull(template)) {
122 return;
123 }
124
125 template = buildSQL(template);
126
127 FileUtil.write(
128 sqlDir + "/" + fileName + "/" + fileName + "-" + getServerName() +
129 ".sql",
130 template);
131 }
132
133 @SuppressWarnings("unused")
134 public List<Index> getIndexes() throws SQLException {
135 return Collections.EMPTY_LIST;
136 }
137
138 public String getTemplateFalse() {
139 return getTemplate()[2];
140 }
141
142 public String getTemplateTrue() {
143 return getTemplate()[1];
144 }
145
146 public String getType() {
147 return _type;
148 }
149
150 public long increment() throws SystemException {
151 return CounterLocalServiceUtil.increment();
152 }
153
154 public boolean isSupportsAlterColumnName() {
155 return _SUPPORTS_ALTER_COLUMN_NAME;
156 }
157
158 public boolean isSupportsAlterColumnType() {
159 return _SUPPORTS_ALTER_COLUMN_TYPE;
160 }
161
162 public boolean isSupportsDateMilliseconds() {
163 return _SUPPORTS_DATE_MILLISECONDS;
164 }
165
166 public boolean isSupportsScrollableResults() {
167 return _SUPPORTS_SCROLLABLE_RESULTS;
168 }
169
170 public boolean isSupportsStringCaseSensitiveQuery() {
171 return _supportsStringCaseSensitiveQuery;
172 }
173
174 public boolean isSupportsUpdateWithInnerJoin() {
175 return _SUPPORTS_UPDATE_WITH_INNER_JOIN;
176 }
177
178 public void runSQL(String sql) throws IOException, SQLException {
179 runSQL(new String[] {sql});
180 }
181
182 public void runSQL(Connection con, String sql)
183 throws IOException, SQLException {
184
185 runSQL(con, new String[] {sql});
186 }
187
188 public void runSQL(String[] sqls) throws IOException, SQLException {
189 Connection con = DataAccess.getConnection();
190
191 try {
192 runSQL(con, sqls);
193 }
194 finally {
195 DataAccess.cleanUp(con);
196 }
197 }
198
199 public void runSQL(Connection con, String[] sqls)
200 throws IOException, SQLException {
201
202 Statement s = null;
203
204 try {
205 s = con.createStatement();
206
207 for (int i = 0; i < sqls.length; i++) {
208 String sql = buildSQL(sqls[i]);
209
210 sql = sql.trim();
211
212 if (sql.endsWith(";")) {
213 sql = sql.substring(0, sql.length() - 1);
214 }
215
216 if (sql.endsWith("go")) {
217 sql = sql.substring(0, sql.length() - 2);
218 }
219
220 if (_log.isDebugEnabled()) {
221 _log.debug(sql);
222 }
223
224 try {
225 s.executeUpdate(sql);
226 }
227 catch (SQLException sqle) {
228 throw sqle;
229 }
230 }
231 }
232 finally {
233 DataAccess.cleanUp(s);
234 }
235 }
236
237 public void runSQLTemplate(String path)
238 throws IOException, NamingException, SQLException {
239
240 runSQLTemplate(path, true);
241 }
242
243 public void runSQLTemplate(String path, boolean failOnError)
244 throws IOException, NamingException, SQLException {
245
246 Thread currentThread = Thread.currentThread();
247
248 ClassLoader classLoader = currentThread.getContextClassLoader();
249
250 InputStream is = classLoader.getResourceAsStream(
251 "com/liferay/portal/tools/sql/dependencies/" + path);
252
253 if (is == null) {
254 is = classLoader.getResourceAsStream(path);
255 }
256
257 if (is == null) {
258 _log.error("Invalid path " + path);
259
260 if (failOnError) {
261 throw new IOException("Invalid path " + path);
262 }
263 else {
264 return;
265 }
266 }
267
268 String template = StringUtil.read(is);
269
270 is.close();
271
272 boolean evaluate = path.endsWith(".vm");
273
274 runSQLTemplateString(template, evaluate, failOnError);
275 }
276
277 public void runSQLTemplateString(
278 String template, boolean evaluate, boolean failOnError)
279 throws IOException, NamingException, SQLException {
280
281 if (evaluate) {
282 try {
283 template = evaluateVM(template);
284 }
285 catch (Exception e) {
286 _log.error(e, e);
287 }
288 }
289
290 StringBundler sb = new StringBundler();
291
292 UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader(
293 new UnsyncStringReader(template));
294
295 String line = null;
296
297 while ((line = unsyncBufferedReader.readLine()) != null) {
298 if (!line.startsWith("##")) {
299 if (line.startsWith("@include ")) {
300 int pos = line.indexOf(" ");
301
302 String includeFileName = line.substring(pos + 1);
303
304 Thread currentThread = Thread.currentThread();
305
306 ClassLoader classLoader =
307 currentThread.getContextClassLoader();
308
309 InputStream is = classLoader.getResourceAsStream(
310 "com/liferay/portal/tools/sql/dependencies/" +
311 includeFileName);
312
313 if (is == null) {
314 is = classLoader.getResourceAsStream(includeFileName);
315 }
316
317 String include = StringUtil.read(is);
318
319 is.close();
320
321 if (includeFileName.endsWith(".vm")) {
322 try {
323 include = evaluateVM(include);
324 }
325 catch (Exception e) {
326 _log.error(e, e);
327 }
328 }
329
330 include = convertTimestamp(include);
331 include = replaceTemplate(include, getTemplate());
332
333 runSQLTemplateString(include, false, true);
334 }
335 else{
336 sb.append(line);
337
338 if (line.endsWith(";")) {
339 String sql = sb.toString();
340
341 sb.setIndex(0);
342
343 try {
344 if (!sql.equals("COMMIT_TRANSACTION;")) {
345 runSQL(sql);
346 }
347 else {
348 if (_log.isDebugEnabled()) {
349 _log.debug("Skip commit sql");
350 }
351 }
352 }
353 catch (IOException ioe) {
354 if (failOnError) {
355 throw ioe;
356 }
357 else if (_log.isWarnEnabled()) {
358 _log.warn(ioe.getMessage());
359 }
360 }
361 catch (SQLException sqle) {
362 if (failOnError) {
363 throw sqle;
364 }
365 else if (_log.isWarnEnabled()) {
366 String message = GetterUtil.getString(
367 sqle.getMessage());
368
369 if (!message.startsWith("Duplicate key name")) {
370 _log.warn(message + ": " + sql);
371 }
372
373 if (message.startsWith("Duplicate entry") ||
374 message.startsWith(
375 "Specified key was too long")) {
376
377 _log.error(line);
378 }
379 }
380 }
381 }
382 }
383 }
384 }
385
386 unsyncBufferedReader.close();
387 }
388
389 public void setSupportsStringCaseSensitiveQuery(
390 boolean supportsStringCaseSensitiveQuery) {
391
392 if (_log.isInfoEnabled()) {
393 if (supportsStringCaseSensitiveQuery) {
394 _log.info("Database supports case sensitive queries");
395 }
396 else {
397 _log.info("Database does not support case sensitive queries");
398 }
399 }
400
401 _supportsStringCaseSensitiveQuery = supportsStringCaseSensitiveQuery;
402 }
403
404 public void updateIndexes(
405 String tablesSQL, String indexesSQL, String indexesProperties,
406 boolean dropIndexes)
407 throws IOException, SQLException {
408
409 List<Index> indexes = getIndexes();
410
411 Set<String> validIndexNames = null;
412
413 if (dropIndexes) {
414 validIndexNames = dropIndexes(
415 tablesSQL, indexesSQL, indexesProperties, indexes);
416 }
417 else {
418 validIndexNames = new HashSet<String>();
419
420 for (Index index : indexes) {
421 String indexName = index.getIndexName().toUpperCase();
422
423 validIndexNames.add(indexName);
424 }
425 }
426
427 addIndexes(indexesSQL, validIndexNames);
428 }
429
430 protected BaseDB(String type) {
431 _type = type;
432 }
433
434 protected void addIndexes(String indexesSQL, Set<String> validIndexNames)
435 throws IOException {
436
437 if (_log.isInfoEnabled()) {
438 _log.info("Adding indexes");
439 }
440
441 DB db = DBFactoryUtil.getDB();
442
443 UnsyncBufferedReader bufferedReader = new UnsyncBufferedReader(
444 new UnsyncStringReader(indexesSQL));
445
446 String sql = null;
447
448 while ((sql = bufferedReader.readLine()) != null) {
449 if (Validator.isNull(sql)) {
450 continue;
451 }
452
453 int y = sql.indexOf(" on ");
454 int x = sql.lastIndexOf(" ", y - 1);
455
456 String indexName = sql.substring(x + 1, y);
457
458 if (validIndexNames.contains(indexName)) {
459 continue;
460 }
461
462 if (_log.isInfoEnabled()) {
463 _log.info(sql);
464 }
465
466 try {
467 db.runSQL(sql);
468 }
469 catch (Exception e) {
470 if (_log.isWarnEnabled()) {
471 _log.warn(e.getMessage());
472 }
473 }
474 }
475 }
476
477 protected abstract String buildCreateFileContent(
478 String sqlDir, String databaseName, int population)
479 throws IOException;
480
481 protected String[] buildColumnNameTokens(String line) {
482 String[] words = StringUtil.split(line, " ");
483
484 if (words.length == 7) {
485 words[5] = "not null;";
486 }
487
488 String[] template = {
489 words[1], words[2], words[3], words[4], words[5]
490 };
491
492 return template;
493 }
494
495 protected String[] buildColumnTypeTokens(String line) {
496 String[] words = StringUtil.split(line, " ");
497
498 String nullable = "";
499
500 if (words.length == 6) {
501 nullable = "not null;";
502 }
503 else if (words.length == 5) {
504 nullable = words[4];
505 }
506 else if (words.length == 4) {
507 nullable = "not null;";
508
509 if (words[3].endsWith(";")) {
510 words[3] = words[3].substring(0, words[3].length() - 1);
511 }
512 }
513
514 String[] template = {
515 words[1], words[2], "", words[3], nullable
516 };
517
518 return template;
519 }
520
521 protected String buildTemplate(String sqlDir, String fileName)
522 throws IOException {
523
524 String template = readFile(sqlDir + "/" + fileName + ".sql");
525
526 if (fileName.equals("portal") || fileName.equals("portal-minimal") ||
527 fileName.equals("update-5.0.1-5.1.0")) {
528
529 UnsyncBufferedReader unsyncBufferedReader =
530 new UnsyncBufferedReader(new UnsyncStringReader(template));
531
532 StringBundler sb = new StringBundler();
533
534 String line = null;
535
536 while ((line = unsyncBufferedReader.readLine()) != null) {
537 if (line.startsWith("@include ")) {
538 int pos = line.indexOf(" ");
539
540 String includeFileName = line.substring(pos + 1);
541
542 File includeFile = new File(
543 sqlDir + "/" + includeFileName);
544
545 if (!includeFile.exists()) {
546 continue;
547 }
548
549 String include = FileUtil.read(includeFile);
550
551 if (includeFileName.endsWith(".vm")) {
552 try {
553 include = evaluateVM(include);
554 }
555 catch (Exception e) {
556 _log.error(e, e);
557 }
558 }
559
560 include = convertTimestamp(include);
561 include = replaceTemplate(include, getTemplate());
562
563 sb.append(include);
564 sb.append("\n\n");
565 }
566 else {
567 sb.append(line);
568 sb.append("\n");
569 }
570 }
571
572 unsyncBufferedReader.close();
573
574 template = sb.toString();
575 }
576
577 if (fileName.equals("indexes") && (this instanceof SybaseDB)) {
578 template = removeBooleanIndexes(sqlDir, template);
579 }
580
581 return template;
582 }
583
584 protected String convertTimestamp(String data) {
585 String s = null;
586
587 if (this instanceof MySQLDB) {
588 s = StringUtil.replace(data, "SPECIFIC_TIMESTAMP_", "");
589 }
590 else {
591 s = data.replaceAll(
592 "SPECIFIC_TIMESTAMP_" + "\\d+", "CURRENT_TIMESTAMP");
593 }
594
595 return s;
596 }
597
598 protected Set<String> dropIndexes(
599 String tablesSQL, String indexesSQL, String indexesProperties,
600 List<Index> indexes)
601 throws IOException, SQLException {
602
603 if (_log.isInfoEnabled()) {
604 _log.info("Dropping stale indexes");
605 }
606
607 Set<String> validIndexNames = new HashSet<String>();
608
609 if (indexes.isEmpty()) {
610 return validIndexNames;
611 }
612
613 DB db = DBFactoryUtil.getDB();
614
615 String tablesSQLLowerCase = tablesSQL.toLowerCase();
616 String indexesSQLLowerCase = indexesSQL.toLowerCase();
617
618 Properties indexesPropertiesObj = PropertiesUtil.load(
619 indexesProperties);
620
621 Enumeration<String> enu =
622 (Enumeration<String>)indexesPropertiesObj.propertyNames();
623
624 while (enu.hasMoreElements()) {
625 String key = enu.nextElement();
626
627 String value = indexesPropertiesObj.getProperty(key);
628
629 indexesPropertiesObj.setProperty(key.toLowerCase(), value);
630 }
631
632 for (Index index : indexes) {
633 String indexNameUpperCase = index.getIndexName().toUpperCase();
634 String indexNameLowerCase = indexNameUpperCase.toLowerCase();
635 String tableName = index.getTableName();
636 String tableNameLowerCase = tableName.toLowerCase();
637 boolean unique = index.isUnique();
638
639 validIndexNames.add(indexNameUpperCase);
640
641 if (indexesPropertiesObj.containsKey(indexNameLowerCase)) {
642 if (unique &&
643 indexesSQLLowerCase.contains(
644 "create unique index " + indexNameLowerCase + " ")) {
645
646 continue;
647 }
648
649 if (!unique &&
650 indexesSQLLowerCase.contains(
651 "create index " + indexNameLowerCase + " ")) {
652
653 continue;
654 }
655 }
656 else {
657 if (!tablesSQLLowerCase.contains(
658 "create table " + tableNameLowerCase + " (")) {
659
660 continue;
661 }
662 }
663
664 validIndexNames.remove(indexNameUpperCase);
665
666 db.runSQL("drop index " + indexNameUpperCase + " on " + tableName);
667 }
668
669 return validIndexNames;
670 }
671
672 protected String evaluateVM(String template) throws Exception {
673 Map<String, Object> variables = new HashMap<String, Object>();
674
675 variables.put("counter", new SimpleCounter());
676
677 template = VelocityUtil.evaluate(template, variables);
678
679
680
681 UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader(
682 new UnsyncStringReader(template));
683
684 StringBundler sb = new StringBundler();
685
686 String line = null;
687
688 while ((line = unsyncBufferedReader.readLine()) != null) {
689 line = line.trim();
690
691 sb.append(line);
692 sb.append("\n");
693 }
694
695 unsyncBufferedReader.close();
696
697 template = sb.toString();
698 template = StringUtil.replace(template, "\n\n\n", "\n\n");
699
700 return template;
701 }
702
703 protected abstract String getServerName();
704
705 protected String getSuffix(int type) {
706 if (type == MINIMAL) {
707 return "-minimal";
708 }
709 else if (type == SHARDED) {
710 return "-sharded";
711 }
712 else {
713 return StringPool.BLANK;
714 }
715 }
716
717 protected abstract String[] getTemplate();
718
719 protected String readFile(String fileName) throws IOException {
720 if (FileUtil.exists(fileName)) {
721 return FileUtil.read(fileName);
722 }
723 else {
724 return StringPool.BLANK;
725 }
726 }
727
728 protected String readSQL(String fileName, String comments, String eol)
729 throws IOException {
730
731 if (!FileUtil.exists(fileName)) {
732 return StringPool.BLANK;
733 }
734
735 UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader(
736 new FileReader(new File(fileName)));
737
738 StringBundler sb = new StringBundler();
739
740 String line = null;
741
742 while ((line = unsyncBufferedReader.readLine()) != null) {
743 if (!line.startsWith(comments)) {
744 line = StringUtil.replace(
745 line,
746 new String[] {"\n", "\t"},
747 new String[] {"", ""});
748
749 if (line.endsWith(";")) {
750 sb.append(line.substring(0, line.length() - 1));
751 sb.append(eol);
752 }
753 else {
754 sb.append(line);
755 }
756 }
757 }
758
759 unsyncBufferedReader.close();
760
761 return sb.toString();
762 }
763
764 protected String removeBooleanIndexes(String sqlDir, String data)
765 throws IOException {
766
767 String portalData = readFile(sqlDir + "/portal-tables.sql");
768
769 if (Validator.isNull(portalData)) {
770 return StringPool.BLANK;
771 }
772
773 UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader(
774 new UnsyncStringReader(data));
775
776 StringBundler sb = new StringBundler();
777
778 String line = null;
779
780 while ((line = unsyncBufferedReader.readLine()) != null) {
781 boolean append = true;
782
783 int x = line.indexOf(" on ");
784
785 if (x != -1) {
786 int y = line.indexOf(" (", x);
787
788 String table = line.substring(x + 4, y);
789
790 x = y + 2;
791 y = line.indexOf(")", x);
792
793 String[] columns = StringUtil.split(line.substring(x, y));
794
795 x = portalData.indexOf("create table " + table + " (");
796 y = portalData.indexOf(");", x);
797
798 String portalTableData = portalData.substring(x, y);
799
800 for (int i = 0; i < columns.length; i++) {
801 if (portalTableData.indexOf(
802 columns[i].trim() + " BOOLEAN") != -1) {
803
804 append = false;
805
806 break;
807 }
808 }
809 }
810
811 if (append) {
812 sb.append(line);
813 sb.append("\n");
814 }
815 }
816
817 unsyncBufferedReader.close();
818
819 return sb.toString();
820 }
821
822 protected String removeInserts(String data) throws IOException {
823 UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader(
824 new UnsyncStringReader(data));
825
826 StringBundler sb = new StringBundler();
827
828 String line = null;
829
830 while ((line = unsyncBufferedReader.readLine()) != null) {
831 if (!line.startsWith("insert into ") &&
832 !line.startsWith("update ")) {
833
834 sb.append(line);
835 sb.append("\n");
836 }
837 }
838
839 unsyncBufferedReader.close();
840
841 return sb.toString();
842 }
843
844 protected String removeLongInserts(String data) throws IOException {
845 UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader(
846 new UnsyncStringReader(data));
847
848 StringBundler sb = new StringBundler();
849
850 String line = null;
851
852 while ((line = unsyncBufferedReader.readLine()) != null) {
853 if (!line.startsWith("insert into Image (") &&
854 !line.startsWith("insert into JournalArticle (") &&
855 !line.startsWith("insert into JournalStructure (") &&
856 !line.startsWith("insert into JournalTemplate (")) {
857
858 sb.append(line);
859 sb.append("\n");
860 }
861 }
862
863 unsyncBufferedReader.close();
864
865 return sb.toString();
866 }
867
868 protected String removeNull(String content) {
869 content = StringUtil.replace(content, " is null", " IS NULL");
870 content = StringUtil.replace(content, " not null", " not_null");
871 content = StringUtil.replace(content, " null", "");
872 content = StringUtil.replace(content, " not_null", " not null");
873
874 return content;
875 }
876
877 protected String replaceTemplate(String template, String[] actual) {
878 if ((template == null) || (TEMPLATE == null) || (actual == null)) {
879 return null;
880 }
881
882 if (TEMPLATE.length != actual.length) {
883 return template;
884 }
885
886 for (int i = 0; i < TEMPLATE.length; i++) {
887 if (TEMPLATE[i].equals("##") ||
888 TEMPLATE[i].equals("'01/01/1970'")) {
889
890 template = template.replaceAll(TEMPLATE[i], actual[i]);
891 }
892 else {
893 template = template.replaceAll(
894 "\\b" + TEMPLATE[i] + "\\b", actual[i]);
895 }
896 }
897
898 return template;
899 }
900
901 protected abstract String reword(String data) throws IOException;
902
903 protected static String ALTER_COLUMN_TYPE = "alter_column_type ";
904
905 protected static String ALTER_COLUMN_NAME = "alter_column_name ";
906
907 protected static String DROP_INDEX = "drop index";
908
909 protected static String DROP_PRIMARY_KEY = "drop primary key";
910
911 protected static String[] REWORD_TEMPLATE = {
912 "@table@", "@old-column@", "@new-column@", "@type@", "@nullable@"
913 };
914
915 protected static String[] TEMPLATE = {
916 "##", "TRUE", "FALSE",
917 "'01/01/1970'", "CURRENT_TIMESTAMP",
918 " BLOB", " BOOLEAN", " DATE",
919 " DOUBLE", " INTEGER", " LONG",
920 " STRING", " TEXT", " VARCHAR",
921 " IDENTITY", "COMMIT_TRANSACTION"
922 };
923
924 private static boolean _SUPPORTS_ALTER_COLUMN_NAME = true;
925
926 private static boolean _SUPPORTS_ALTER_COLUMN_TYPE = true;
927
928 private static boolean _SUPPORTS_DATE_MILLISECONDS = true;
929
930 private static boolean _SUPPORTS_SCROLLABLE_RESULTS = true;
931
932 private static boolean _SUPPORTS_UPDATE_WITH_INNER_JOIN;
933
934 private static Log _log = LogFactoryUtil.getLog(BaseDB.class);
935
936 private String _type;
937 private boolean _supportsStringCaseSensitiveQuery;
938
939 }