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