001: /*
002: * DbSettings.java
003: *
004: * This file is part of SQL Workbench/J, http://www.sql-workbench.net
005: *
006: * Copyright 2002-2008, Thomas Kellerer
007: * No part of this code maybe reused without the permission of the author
008: *
009: * To contact the author please send an email to: support@sql-workbench.net
010: *
011: */
012: package workbench.db;
013:
014: import java.sql.DatabaseMetaData;
015: import java.util.HashMap;
016: import java.util.List;
017: import java.util.Map;
018: import workbench.log.LogMgr;
019: import workbench.resource.Settings;
020: import workbench.util.StringUtil;
021:
022: /**
023: * Stores and manages db specific settings.
024: *
025: * @author support@sql-workbench.net
026: */
027: public class DbSettings {
028: private String dbId;
029: private boolean caseSensitive;
030: private boolean useJdbcCommit;
031: private boolean ddlNeedsCommit;
032: private boolean trimDefaults = true;
033:
034: private boolean neverQuoteObjects;
035: private boolean reportsRealSizeAsDisplaySize = false;
036: private boolean allowExtendedCreateStatement = true;
037:
038: private boolean allowsMultipleGetUpdateCounts = true;
039: private boolean supportsBatchedStatements = false;
040:
041: private Map<Integer, String> indexTypeMapping;
042: public static final String IDX_TYPE_NORMAL = "NORMAL";
043:
044: public DbSettings(String id, String productName) {
045: this .dbId = id;
046: Settings settings = Settings.getInstance();
047:
048: this .caseSensitive = settings.getBoolProperty("workbench.db."
049: + getDbId() + ".casesensitive", false)
050: || settings.getCaseSensitivServers().contains(
051: productName);
052: this .useJdbcCommit = settings.getBoolProperty("workbench.db."
053: + getDbId() + ".usejdbccommit", false)
054: || settings.getServersWhichNeedJdbcCommit().contains(
055: productName);
056: this .ddlNeedsCommit = settings.getBoolProperty("workbench.db."
057: + getDbId() + ".ddlneedscommit", false)
058: || settings.getServersWhereDDLNeedsCommit().contains(
059: productName);
060:
061: // Migrate old list-based properties to new dbid based properties
062: // If the flags were already set with the new format, re-applying the
063: // value won't change anything
064: if (caseSensitive) {
065: settings.removeCaseSensitivServer(productName);
066: settings.setProperty("workbench.db." + getDbId()
067: + ".casesensitive", true);
068: }
069: if (ddlNeedsCommit) {
070: settings.removeDDLCommitServer(productName);
071: settings.setProperty("workbench.db." + getDbId()
072: + ".ddlneedscommit", true);
073: }
074: if (useJdbcCommit) {
075: settings.removeJdbcCommitServer(productName);
076: settings.setProperty("workbench.db." + getDbId()
077: + ".usejdbccommit", true);
078: }
079:
080: List<String> quote = StringUtil.stringToList(settings
081: .getProperty("workbench.db.neverquote", ""));
082: this .neverQuoteObjects = quote.contains(this .getDbId());
083: this .trimDefaults = settings.getBoolProperty("workbench.db."
084: + getDbId() + ".trimdefaults", true);
085: this .allowsMultipleGetUpdateCounts = settings.getBoolProperty(
086: "workbench.db." + getDbId() + ".multipleupdatecounts",
087: true);
088: this .reportsRealSizeAsDisplaySize = settings.getBoolProperty(
089: "workbench.db." + getDbId()
090: + ".charsize.usedisplaysize", false);
091: this .allowExtendedCreateStatement = settings.getBoolProperty(
092: "workbench.db." + getDbId() + ".extended.createstmt",
093: true);
094: this .supportsBatchedStatements = settings.getBoolProperty(
095: "workbench.db." + getDbId() + ".batchedstatements",
096: false);
097: }
098:
099: String getDbId() {
100: return this .dbId;
101: }
102:
103: public boolean longVarcharIsClob() {
104: return Settings.getInstance()
105: .getBoolProperty(
106: "workbench.db." + getDbId()
107: + ".clob.longvarchar", true);
108: }
109:
110: public boolean supportsBatchedStatements() {
111: return this .supportsBatchedStatements;
112: }
113:
114: public boolean allowsExtendedCreateStatement() {
115: return allowExtendedCreateStatement;
116: }
117:
118: public boolean allowsMultipleGetUpdateCounts() {
119: return this .allowsMultipleGetUpdateCounts;
120: }
121:
122: public boolean reportsRealSizeAsDisplaySize() {
123: return this .reportsRealSizeAsDisplaySize;
124: }
125:
126: public boolean ddlNeedsCommit() {
127: return ddlNeedsCommit;
128: }
129:
130: public boolean neverQuoteObjects() {
131: return neverQuoteObjects;
132: }
133:
134: public boolean trimDefaults() {
135: return trimDefaults;
136: }
137:
138: public boolean useSetNull() {
139: return Settings.getInstance().getBoolProperty(
140: "workbench.db." + getDbId() + ".import.use.setnull",
141: false);
142: }
143:
144: public boolean useJdbcCommit() {
145: return useJdbcCommit;
146: }
147:
148: public boolean isStringComparisonCaseSensitive() {
149: return this .caseSensitive;
150: }
151:
152: public boolean getDefaultBeforeNull() {
153: return Settings.getInstance().getBoolProperty(
154: "workbench.db.defaultbeforenull." + this .getDbId(),
155: false);
156: }
157:
158: /**
159: * Return the verb which does a DROP ... CASCADE for the given
160: * object type. If the current DBMS does not support cascaded dropping
161: * of objects, then null will be returned.
162: *
163: * @param aType the database object type to drop (TABLE, VIEW etc)
164: * @return a String which can be appended to a DROP type name command in order to drop dependent objects as well
165: * or null if the current DBMS does not support this.
166: */
167: public String getCascadeConstraintsVerb(String aType) {
168: if (aType == null)
169: return null;
170: String verb = Settings.getInstance().getProperty(
171: "workbench.db.drop." + aType.toLowerCase()
172: + ".cascade." + getDbId(), null);
173: return verb;
174: }
175:
176: public boolean needsCatalogIfNoCurrent() {
177: return Settings.getInstance().getBoolProperty(
178: "workbench.db." + this .getDbId()
179: + ".catalog.neededwhenempty", false);
180: }
181:
182: public String getInsertForImport() {
183: return Settings.getInstance().getProperty(
184: "workbench.db." + this .getDbId() + ".import.insert",
185: null);
186: }
187:
188: public String getRefCursorTypeName() {
189: return Settings.getInstance().getProperty(
190: "workbench.db." + getDbId() + ".refcursor.typename",
191: null);
192: }
193:
194: public int getRefCursorDataType() {
195: return Settings.getInstance().getIntProperty(
196: "workbench.db." + getDbId() + ".refcursor.typevalue",
197: Integer.MIN_VALUE);
198: }
199:
200: public boolean useWbProcedureCall() {
201: return Settings.getInstance().getBoolProperty(
202: "workbench.db." + getDbId() + ".procs.use.wbcall",
203: false);
204: }
205:
206: public boolean needsTableForDropIndex() {
207: boolean needsTable = Settings.getInstance().getBoolProperty(
208: "workbench.db." + getDbId() + ".dropindex.needstable",
209: false);
210: return needsTable;
211: }
212:
213: public boolean useSavepointForImport() {
214: return Settings.getInstance().getBoolProperty(
215: "workbench.db." + this .getDbId()
216: + ".import.usesavepoint", false);
217: }
218:
219: public boolean useSavePointForDML() {
220: return Settings.getInstance().getBoolProperty(
221: "workbench.db." + getDbId() + ".sql.usesavepoint",
222: false);
223: }
224:
225: public boolean useSavePointForDDL() {
226: return Settings.getInstance().getBoolProperty(
227: "workbench.db." + getDbId() + ".ddl.usesavepoint",
228: false);
229: }
230:
231: /**
232: * Returns the type for the formatter
233: * @return hex, octal, char
234: */
235: public String getBlobLiteralType() {
236: return Settings.getInstance().getProperty(
237: "workbench.db." + getDbId() + ".blob.literal.type",
238: "hex");
239: }
240:
241: public String getBlobLiteralPrefix() {
242: return Settings.getInstance().getProperty(
243: "workbench.db." + getDbId() + ".blob.literal.prefix",
244: null);
245: }
246:
247: public String getBlobLiteralSuffix() {
248: return Settings.getInstance().getProperty(
249: "workbench.db." + getDbId() + ".blob.literal.suffix",
250: null);
251: }
252:
253: public boolean getBlobLiteralUpperCase() {
254: return Settings.getInstance().getBoolProperty(
255: "workbench.db." + getDbId() + ".blob.literal.upcase",
256: false);
257: }
258:
259: public boolean supportSingleLineCommands() {
260: String ids = Settings.getInstance().getProperty(
261: "workbench.db.checksinglelinecmd", "");
262: if ("*".equals(ids))
263: return true;
264: List dbs = StringUtil.stringToList(ids, ",", true, true);
265: return dbs.contains(this .getDbId());
266: }
267:
268: public String getLineComment() {
269: return Settings.getInstance().getProperty(
270: "workbench.db." + getDbId() + ".linecomment", null);
271: }
272:
273: public boolean supportsQueryTimeout() {
274: boolean result = Settings.getInstance().getBoolProperty(
275: "workbench.db." + getDbId() + ".supportquerytimeout",
276: true);
277: return result;
278: }
279:
280: public boolean supportsGetPrimaryKeys() {
281: boolean result = Settings.getInstance().getBoolProperty(
282: "workbench.db." + getDbId() + ".supportgetpk", true);
283: return result;
284: }
285:
286: public boolean supportShortInclude() {
287: String ids = Settings.getInstance().getProperty(
288: "workbench.db.supportshortinclude", "");
289: if ("*".equals(ids))
290: return true;
291: List dbs = StringUtil.stringToList(ids, ",", true, true);
292: return dbs.contains(this .getDbId());
293: }
294:
295: public String getProcVersionDelimiter() {
296: return Settings.getInstance().getProperty(
297: "workbench.db.procversiondelimiter." + this .getDbId(),
298: null);
299: }
300:
301: public boolean supportsTruncate() {
302: String s = Settings.getInstance().getProperty(
303: "workbench.db.truncatesupported",
304: StringUtil.EMPTY_STRING);
305: List l = StringUtil.stringToList(s, ",");
306: return l.contains(this .getDbId());
307: }
308:
309: public boolean isViewType(String type) {
310: if (type == null)
311: return false;
312: type = type.toLowerCase();
313: if (type.toUpperCase().indexOf("VIEW") > -1)
314: return true;
315: String viewTypes = Settings.getInstance().getProperty(
316: "workbench.db." + getDbId() + ".additional.viewtypes",
317: "view").toLowerCase();
318: List types = StringUtil.stringToList(viewTypes, ",", true,
319: true, false);
320: return (types.contains(type.toLowerCase()));
321: }
322:
323: public boolean isSynonymType(String type) {
324: if (type == null)
325: return false;
326: String synTypes = Settings.getInstance().getProperty(
327: "workbench.db." + getDbId() + ".synonymtypes",
328: "synonym").toLowerCase();
329: List types = StringUtil.stringToList(synTypes, ",", true, true,
330: false);
331: return (types.contains(type.toLowerCase()));
332: }
333:
334: String mapIndexType(Object type) {
335: if (type == null)
336: return null;
337: if (type instanceof String) {
338: int t = StringUtil.getIntValue((String) type,
339: Integer.MIN_VALUE);
340: if (t == Integer.MIN_VALUE)
341: return (String) type;
342: return mapIndexType(t);
343: }
344: if (type instanceof Number) {
345: return mapIndexType(((Number) type).intValue());
346: }
347: return null;
348: }
349:
350: String mapIndexType(int type) {
351: if (indexTypeMapping == null) {
352: this .indexTypeMapping = new HashMap<Integer, String>();
353: String map = Settings.getInstance().getProperty(
354: "workbench.db." + getDbId() + ".indextypes", null);
355: if (map != null) {
356: List<String> entries = StringUtil.stringToList(map,
357: ";", true, true);
358: for (String entry : entries) {
359: String[] mapping = entry.split(",");
360: if (mapping.length != 2)
361: continue;
362: int value = StringUtil.getIntValue(mapping[0],
363: Integer.MIN_VALUE);
364: if (value != Integer.MIN_VALUE) {
365: indexTypeMapping.put(new Integer(value),
366: mapping[1]);
367: }
368: }
369: }
370: }
371: String dbmsType = this .indexTypeMapping.get(new Integer(type));
372: if (dbmsType == null) {
373: if (Settings.getInstance().getDebugMetadataSql()) {
374: LogMgr.logDebug("DbSettings.mapIndexType()",
375: "No mapping for type = " + type);
376: }
377: return IDX_TYPE_NORMAL;
378: }
379: return dbmsType;
380: }
381:
382: public boolean proceduresNeedTerminator() {
383: String value = Settings.getInstance().getProperty(
384: "workbench.db.noprocterminator", null);
385: if (value == null)
386: return true;
387: List l = StringUtil.stringToList(value, ",");
388: return !l.contains(this .dbId);
389: }
390:
391: public IdentifierCase getSchemaNameCase() {
392: // This allows overriding the default value returned by the JDBC driver
393: String nameCase = Settings.getInstance().getProperty(
394: "workbench.db." + this .getDbId() + ".schemaname.case",
395: null);
396: if (nameCase != null) {
397: if ("lower".equals(nameCase)) {
398: return IdentifierCase.lower;
399: } else if ("upper".equals(nameCase)) {
400: return IdentifierCase.upper;
401: } else if ("mixed".equals(nameCase)) {
402: return IdentifierCase.mixed;
403: }
404: }
405: return IdentifierCase.unknown;
406: }
407:
408: public IdentifierCase getObjectNameCase() {
409: // This allows overriding the default value returned by the JDBC driver
410: String nameCase = Settings.getInstance().getProperty(
411: "workbench.db." + this .getDbId() + ".objectname.case",
412: null);
413: if (nameCase != null) {
414: if ("lower".equals(nameCase)) {
415: return IdentifierCase.lower;
416: } else if ("upper".equals(nameCase)) {
417: return IdentifierCase.upper;
418: } else if ("mixed".equals(nameCase)) {
419: return IdentifierCase.mixed;
420: }
421: }
422: return IdentifierCase.unknown;
423: }
424:
425: /**
426: * Translates the numberic constants of DatabaseMetaData for trigger rules
427: * into text (e.g DatabaseMetaData.importedKeyNoAction --> NO ACTION)
428: *
429: * @param code the numeric value for a rule as defined by DatabaseMetaData.importedKeyXXXX constants
430: * @return String
431: */
432: public String getRuleDisplay(int code) {
433: StringBuilder key = new StringBuilder(40);
434: switch (code) {
435: case DatabaseMetaData.importedKeyNoAction:
436: key.append("workbench.sql.fkrule.noaction");
437: break;
438: case DatabaseMetaData.importedKeyRestrict:
439: key.append("workbench.sql.fkrule.restrict");
440: break;
441: case DatabaseMetaData.importedKeySetNull:
442: key.append("workbench.sql.fkrule.setnull");
443: break;
444: case DatabaseMetaData.importedKeyCascade:
445: key.append("workbench.sql.fkrule.cascade");
446: break;
447: case DatabaseMetaData.importedKeySetDefault:
448: key.append("workbench.sql.fkrule.setdefault");
449: break;
450: case DatabaseMetaData.importedKeyInitiallyDeferred:
451: key.append("workbench.sql.fkrule.initiallydeferred");
452: break;
453: case DatabaseMetaData.importedKeyInitiallyImmediate:
454: key.append("workbench.sql.fkrule.initiallyimmediate");
455: break;
456: case DatabaseMetaData.importedKeyNotDeferrable:
457: key.append("workbench.sql.fkrule.notdeferrable");
458: break;
459: default:
460: key = null;
461: }
462: if (key != null) {
463: key.append('.');
464: key.append(this .getDbId());
465: String display = Settings.getInstance().getProperty(
466: key.toString(), null);
467: if (display != null)
468: return display;
469: }
470: switch (code) {
471: case DatabaseMetaData.importedKeyNoAction:
472: return "NO ACTION";
473: case DatabaseMetaData.importedKeyRestrict:
474: return "RESTRICT";
475: case DatabaseMetaData.importedKeySetNull:
476: return "SET NULL";
477: case DatabaseMetaData.importedKeyCascade:
478: return "CASCADE";
479: case DatabaseMetaData.importedKeySetDefault:
480: return "SET DEFAULT";
481: case DatabaseMetaData.importedKeyInitiallyDeferred:
482: return "INITIALLY DEFERRED";
483: case DatabaseMetaData.importedKeyInitiallyImmediate:
484: return "INITIALLY IMMEDIATE";
485: case DatabaseMetaData.importedKeyNotDeferrable:
486: return "NOT DEFERRABLE";
487: default:
488: return StringUtil.EMPTY_STRING;
489: }
490: }
491:
492: public boolean useSetCatalog() {
493: return Settings.getInstance().getBoolProperty(
494: "workbench.db." + this .getDbId() + ".usesetcatalog",
495: true);
496: }
497:
498: public boolean isNotDeferrable(String deferrable) {
499: if (StringUtil.isEmptyString(deferrable))
500: return true;
501: return (deferrable
502: .equals(getRuleDisplay(DatabaseMetaData.importedKeyNotDeferrable)));
503: }
504:
505: /**
506: * Retrieve the list of datatypes that should be ignored for the current
507: * dbms. The names in that list must match the names returned
508: * by DatabaseMetaData.getTypeInfo()
509: */
510: public List<String> getDataTypesToIgnore() {
511: String types = Settings.getInstance().getProperty(
512: "workbench.ignoretypes." + getDbId(), null);
513: List<String> ignored = StringUtil.stringToList(types, ",",
514: true, true);
515: return ignored;
516: }
517:
518: public String getQueryForCurrentCatalog() {
519: String query = Settings.getInstance().getProperty(
520: "workbench.db." + this .getDbId()
521: + ".currentcatalog.query", null);
522: return query;
523: }
524:
525: public boolean getConvertDateInExport() {
526: return Settings.getInstance().getBoolProperty(
527: "workbench.db." + this .getDbId()
528: + ".export.convert.date2ts", false);
529: }
530:
531: public boolean needsExactClobLength() {
532: return Settings.getInstance().getBoolProperty(
533: "workbench.db." + this .getDbId() + ".exactcloblength",
534: false);
535: }
536:
537: public boolean getFormatViewSource() {
538: return Settings.getInstance().getBoolProperty(
539: "workbench.db." + this .getDbId()
540: + ".source.view.doformat", false);
541: }
542:
543: public String getDropSingleColumnSql() {
544: return Settings.getInstance()
545: .getProperty(
546: "workbench.db." + this .getDbId()
547: + ".drop.column", null);
548: }
549:
550: public String getDropMultipleColumnSql() {
551: return Settings.getInstance()
552: .getProperty(
553: "workbench.db." + this .getDbId()
554: + ".drop.column.multi", null);
555: }
556:
557: public boolean canDropType(String type) {
558: if (StringUtil.isEmptyString(type))
559: return false;
560: if (type.equalsIgnoreCase("column")) {
561: return getDropSingleColumnSql() != null;
562: }
563: return true;
564: }
565: }
|