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