dengzedong
2024-12-19 85b2001c0ec2f1adc598db3bf47ad457dcca7074
提交 | 用户 | 时间
e7c126 1 package liquibase.database.core;
H 2
3 import java.lang.reflect.Method;
4 import java.sql.Connection;
5 import java.sql.ResultSet;
6 import java.sql.SQLException;
7 import java.sql.Statement;
8 import java.util.ArrayList;
9 import java.util.Arrays;
10 import java.util.HashSet;
11 import java.util.List;
12 import java.util.Locale;
13 import java.util.Map;
14 import java.util.Properties;
15 import java.util.Set;
16 import java.util.regex.Matcher;
17 import java.util.regex.Pattern;
18 import liquibase.CatalogAndSchema;
19 import liquibase.Scope;
20 import liquibase.database.AbstractJdbcDatabase;
21 import liquibase.database.DatabaseConnection;
22 import liquibase.database.OfflineConnection;
23 import liquibase.database.jvm.JdbcConnection;
24 import liquibase.exception.DatabaseException;
25 import liquibase.exception.UnexpectedLiquibaseException;
26 import liquibase.exception.ValidationErrors;
27 import liquibase.executor.ExecutorService;
28 import liquibase.statement.DatabaseFunction;
29 import liquibase.statement.SequenceCurrentValueFunction;
30 import liquibase.statement.SequenceNextValueFunction;
31 import liquibase.statement.core.RawCallStatement;
32 import liquibase.statement.core.RawSqlStatement;
33 import liquibase.structure.DatabaseObject;
34 import liquibase.structure.core.Catalog;
35 import liquibase.structure.core.Index;
36 import liquibase.structure.core.PrimaryKey;
37 import liquibase.structure.core.Schema;
38 import liquibase.util.JdbcUtils;
39 import liquibase.util.StringUtil;
40
41 public class DmDatabase extends AbstractJdbcDatabase {
42     private static final String PRODUCT_NAME = "DM DBMS";
43
44     @Override
45     protected String getDefaultDatabaseProductName() {
46         return PRODUCT_NAME;
47     }
48
49     /**
50      * Is this AbstractDatabase subclass the correct one to use for the given connection.
51      *
52      * @param conn
53      */
54     @Override
55     public boolean isCorrectDatabaseImplementation(DatabaseConnection conn) throws DatabaseException {
56         return PRODUCT_NAME.equalsIgnoreCase(conn.getDatabaseProductName());
57     }
58
59     /**
60      * If this database understands the given url, return the default driver class name.  Otherwise return null.
61      *
62      * @param url
63      */
64     @Override
65     public String getDefaultDriver(String url) {
66         if(url.startsWith("jdbc:dm")) {
67             return "dm.jdbc.driver.DmDriver";
68         }
69
70         return null;
71     }
72
73     /**
74      * Returns an all-lower-case short name of the product.  Used for end-user selecting of database type
75      * such as the DBMS precondition.
76      */
77     @Override
78     public String getShortName() {
79         return "dm";
80     }
81
82     @Override
83     public Integer getDefaultPort() {
84         return 5236;
85     }
86
87     /**
88      * Returns whether this database support initially deferrable columns.
89      */
90     @Override
91     public boolean supportsInitiallyDeferrableColumns() {
92         return true;
93     }
94
95     @Override
96     public boolean supportsTablespaces() {
97         return true;
98     }
99
100     @Override
101     public int getPriority() {
102         return PRIORITY_DEFAULT;
103     }
104
105     private static final Pattern PROXY_USER = Pattern.compile(".*(?:thin|oci)\\:(.+)/@.*");
106
107     protected final int SHORT_IDENTIFIERS_LENGTH = 30;
108     protected final int LONG_IDENTIFIERS_LEGNTH = 128;
109     public static final int ORACLE_12C_MAJOR_VERSION = 12;
110
111     private Set<String> reservedWords = new HashSet<>();
112     private Set<String> userDefinedTypes;
113     private Map<String, String> savedSessionNlsSettings;
114
115     private Boolean canAccessDbaRecycleBin;
116     private Integer databaseMajorVersion;
117     private Integer databaseMinorVersion;
118
119     /**
120      * Default constructor for an object that represents the Oracle Database DBMS.
121      */
122     public DmDatabase() {
123         super.unquotedObjectsAreUppercased = true;
124         //noinspection HardCodedStringLiteral
125         super.setCurrentDateTimeFunction("SYSTIMESTAMP");
126         // Setting list of Oracle's native functions
127         //noinspection HardCodedStringLiteral
128         dateFunctions.add(new DatabaseFunction("SYSDATE"));
129         //noinspection HardCodedStringLiteral
130         dateFunctions.add(new DatabaseFunction("SYSTIMESTAMP"));
131         //noinspection HardCodedStringLiteral
132         dateFunctions.add(new DatabaseFunction("CURRENT_TIMESTAMP"));
133         //noinspection HardCodedStringLiteral
134         super.sequenceNextValueFunction = "%s.nextval";
135         //noinspection HardCodedStringLiteral
136         super.sequenceCurrentValueFunction = "%s.currval";
137     }
138
139     private void tryProxySession(final String url, final Connection con) {
140         Matcher m = PROXY_USER.matcher(url);
141         if (m.matches()) {
142             Properties props = new Properties();
143             props.put("PROXY_USER_NAME", m.group(1));
144             try {
145                 Method method = con.getClass().getMethod("openProxySession", int.class, Properties.class);
146                 method.setAccessible(true);
147                 method.invoke(con, 1, props);
148             } catch (Exception e) {
149                 Scope.getCurrentScope().getLog(getClass()).info("Could not open proxy session on OracleDatabase: " + e.getCause().getMessage());
150             }
151         }
152     }
153
154     @Override
155     public int getDatabaseMajorVersion() throws DatabaseException {
156         if (databaseMajorVersion == null) {
157             return super.getDatabaseMajorVersion();
158         } else {
159             return databaseMajorVersion;
160         }
161     }
162
163     @Override
164     public int getDatabaseMinorVersion() throws DatabaseException {
165         if (databaseMinorVersion == null) {
166             return super.getDatabaseMinorVersion();
167         } else {
168             return databaseMinorVersion;
169         }
170     }
171
172     @Override
173     public String getJdbcCatalogName(CatalogAndSchema schema) {
174         return null;
175     }
176
177     @Override
178     public String getJdbcSchemaName(CatalogAndSchema schema) {
179         return correctObjectName((schema.getCatalogName() == null) ? schema.getSchemaName() : schema.getCatalogName(), Schema.class);
180     }
181
182     @Override
183     protected String getAutoIncrementClause(final String generationType, final Boolean defaultOnNull) {
184         if (StringUtil.isEmpty(generationType)) {
185             return super.getAutoIncrementClause();
186         }
187
188         String autoIncrementClause = "GENERATED %s AS IDENTITY"; // %s -- [ ALWAYS | BY DEFAULT [ ON NULL ] ]
189         String generationStrategy = generationType;
190         if (Boolean.TRUE.equals(defaultOnNull) && generationType.toUpperCase().equals("BY DEFAULT")) {
191             generationStrategy += " ON NULL";
192         }
193         return String.format(autoIncrementClause, generationStrategy);
194     }
195
196     @Override
197     public String generatePrimaryKeyName(String tableName) {
198         if (tableName.length() > 27) {
199             //noinspection HardCodedStringLiteral
200             return "PK_" + tableName.toUpperCase(Locale.US).substring(0, 27);
201         } else {
202             //noinspection HardCodedStringLiteral
203             return "PK_" + tableName.toUpperCase(Locale.US);
204         }
205     }
206
207     @Override
208     public boolean isReservedWord(String objectName) {
209         return reservedWords.contains(objectName.toUpperCase());
210     }
211
212     @Override
213     public boolean supportsSequences() {
214         return true;
215     }
216
217     /**
218      * Oracle supports catalogs in liquibase terms
219      *
220      * @return false
221      */
222     @Override
223     public boolean supportsSchemas() {
224         return false;
225     }
226
227     @Override
228     protected String getConnectionCatalogName() throws DatabaseException {
229         if (getConnection() instanceof OfflineConnection) {
230             return getConnection().getCatalog();
231         }
232         try {
233             //noinspection HardCodedStringLiteral
234             return Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("jdbc", this).queryForObject(new RawCallStatement("select sys_context( 'userenv', 'current_schema' ) from dual"), String.class);
235         } catch (Exception e) {
236             //noinspection HardCodedStringLiteral
237             Scope.getCurrentScope().getLog(getClass()).info("Error getting default schema", e);
238         }
239         return null;
240     }
241
242     @Override
243     public String getDefaultCatalogName() {//NOPMD
244         return (super.getDefaultCatalogName() == null) ? null : super.getDefaultCatalogName().toUpperCase(Locale.US);
245     }
246
247     /**
248      * <p>Returns an Oracle date literal with the same value as a string formatted using ISO 8601.</p>
249      *
250      * <p>Convert an ISO8601 date string to one of the following results:
251      * to_date('1995-05-23', 'YYYY-MM-DD')
252      * to_date('1995-05-23 09:23:59', 'YYYY-MM-DD HH24:MI:SS')</p>
253      * <p>
254      * Implementation restriction:<br>
255      * Currently, only the following subsets of ISO8601 are supported:<br>
256      * <ul>
257      * <li>YYYY-MM-DD</li>
258      * <li>YYYY-MM-DDThh:mm:ss</li>
259      * </ul>
260      */
261     @Override
262     public String getDateLiteral(String isoDate) {
263         String normalLiteral = super.getDateLiteral(isoDate);
264
265         if (isDateOnly(isoDate)) {
266             return "TO_DATE(" + normalLiteral + ", 'YYYY-MM-DD')";
267         } else if (isTimeOnly(isoDate)) {
268             return "TO_DATE(" + normalLiteral + ", 'HH24:MI:SS')";
269         } else if (isTimestamp(isoDate)) {
270             return "TO_TIMESTAMP(" + normalLiteral + ", 'YYYY-MM-DD HH24:MI:SS.FF')";
271         } else if (isDateTime(isoDate)) {
272             int seppos = normalLiteral.lastIndexOf('.');
273             if (seppos != -1) {
274                 normalLiteral = normalLiteral.substring(0, seppos) + "'";
275             }
276             return "TO_DATE(" + normalLiteral + ", 'YYYY-MM-DD HH24:MI:SS')";
277         }
278         return "UNSUPPORTED:" + isoDate;
279     }
280
281     @Override
282     public boolean isSystemObject(DatabaseObject example) {
283         if (example == null) {
284             return false;
285         }
286
287         if (this.isLiquibaseObject(example)) {
288             return false;
289         }
290
291         if (example instanceof Schema) {
292             //noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral
293             if ("SYSTEM".equals(example.getName()) || "SYS".equals(example.getName()) || "CTXSYS".equals(example.getName()) || "XDB".equals(example.getName())) {
294                 return true;
295             }
296             //noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral
297             if ("SYSTEM".equals(example.getSchema().getCatalogName()) || "SYS".equals(example.getSchema().getCatalogName()) || "CTXSYS".equals(example.getSchema().getCatalogName()) || "XDB".equals(example.getSchema().getCatalogName())) {
298                 return true;
299             }
300         } else if (isSystemObject(example.getSchema())) {
301             return true;
302         }
303         if (example instanceof Catalog) {
304             //noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral
305             if (("SYSTEM".equals(example.getName()) || "SYS".equals(example.getName()) || "CTXSYS".equals(example.getName()) || "XDB".equals(example.getName()))) {
306                 return true;
307             }
308         } else if (example.getName() != null) {
309             //noinspection HardCodedStringLiteral
310             if (example.getName().startsWith("BIN$")) { //oracle deleted table
311                 boolean filteredInOriginalQuery = this.canAccessDbaRecycleBin();
312                 if (!filteredInOriginalQuery) {
313                     filteredInOriginalQuery = StringUtil.trimToEmpty(example.getSchema().getName()).equalsIgnoreCase(this.getConnection().getConnectionUserName());
314                 }
315
316                 if (filteredInOriginalQuery) {
317                     return !((example instanceof PrimaryKey) || (example instanceof Index) || (example instanceof
318                             liquibase.statement.UniqueConstraint));
319                 } else {
320                     return true;
321                 }
322             } else //noinspection HardCodedStringLiteral
323                 if (example.getName().startsWith("AQ$")) { //oracle AQ tables
324                     return true;
325                 } else //noinspection HardCodedStringLiteral
326                     if (example.getName().startsWith("DR$")) { //oracle index tables
327                         return true;
328                     } else //noinspection HardCodedStringLiteral
329                         if (example.getName().startsWith("SYS_IOT_OVER")) { //oracle system table
330                             return true;
331                         } else //noinspection HardCodedStringLiteral,HardCodedStringLiteral
332                             if ((example.getName().startsWith("MDRT_") || example.getName().startsWith("MDRS_")) && example.getName().endsWith("$")) {
333                                 // CORE-1768 - Oracle creates these for spatial indices and will remove them when the index is removed.
334                                 return true;
335                             } else //noinspection HardCodedStringLiteral
336                                 if (example.getName().startsWith("MLOG$_")) { //Created by materliaized view logs for every table that is part of a materialized view. Not available for DDL operations.
337                                     return true;
338                                 } else //noinspection HardCodedStringLiteral
339                                     if (example.getName().startsWith("RUPD$_")) { //Created by materialized view log tables using primary keys. Not available for DDL operations.
340                                         return true;
341                                     } else //noinspection HardCodedStringLiteral
342                                         if (example.getName().startsWith("WM$_")) { //Workspace Manager backup tables.
343                                             return true;
344                                         } else //noinspection HardCodedStringLiteral
345                                             if ("CREATE$JAVA$LOB$TABLE".equals(example.getName())) { //This table contains the name of the Java object, the date it was loaded, and has a BLOB column to store the Java object.
346                                                 return true;
347                                             } else //noinspection HardCodedStringLiteral
348                                                 if ("JAVA$CLASS$MD5$TABLE".equals(example.getName())) { //This is a hash table that tracks the loading of Java objects into a schema.
349                                                     return true;
350                                                 } else //noinspection HardCodedStringLiteral
351                                                     if (example.getName().startsWith("ISEQ$$_")) { //System-generated sequence
352                                                         return true;
353                                                     } else //noinspection HardCodedStringLiteral
354                                                         if (example.getName().startsWith("USLOG$")) { //for update materialized view
355                                                             return true;
356                                                         } else if (example.getName().startsWith("SYS_FBA")) { //for Flashback tables
357                                                             return true;
358                                                         }
359         }
360
361         return super.isSystemObject(example);
362     }
363
364     @Override
365     public boolean supportsAutoIncrement() {
366         // Oracle supports Identity beginning with version 12c
367         boolean isAutoIncrementSupported = false;
368
369         try {
370             if (getDatabaseMajorVersion() >= 12) {
371                 isAutoIncrementSupported = true;
372             }
373
374             // Returning true will generate create table command with 'IDENTITY' clause, example:
375             // CREATE TABLE AutoIncTest (IDPrimaryKey NUMBER(19) GENERATED BY DEFAULT AS IDENTITY NOT NULL, TypeID NUMBER(3) NOT NULL, Description NVARCHAR2(50), CONSTRAINT PK_AutoIncTest PRIMARY KEY (IDPrimaryKey));
376
377             // While returning false will continue to generate create table command without 'IDENTITY' clause, example:
378             // CREATE TABLE AutoIncTest (IDPrimaryKey NUMBER(19) NOT NULL, TypeID NUMBER(3) NOT NULL, Description NVARCHAR2(50), CONSTRAINT PK_AutoIncTest PRIMARY KEY (IDPrimaryKey));
379
380         } catch (DatabaseException ex) {
381             isAutoIncrementSupported = false;
382         }
383
384         return isAutoIncrementSupported;
385     }
386
387
388 //    public Set<UniqueConstraint> findUniqueConstraints(String schema) throws DatabaseException {
389 //        Set<UniqueConstraint> returnSet = new HashSet<UniqueConstraint>();
390 //
391 //        List<Map> maps = new Executor(this).queryForList(new RawSqlStatement("SELECT UC.CONSTRAINT_NAME, UCC.TABLE_NAME, UCC.COLUMN_NAME FROM USER_CONSTRAINTS UC, USER_CONS_COLUMNS UCC WHERE UC.CONSTRAINT_NAME=UCC.CONSTRAINT_NAME AND CONSTRAINT_TYPE='U' ORDER BY UC.CONSTRAINT_NAME"));
392 //
393 //        UniqueConstraint constraint = null;
394 //        for (Map map : maps) {
395 //            if (constraint == null || !constraint.getName().equals(constraint.getName())) {
396 //                returnSet.add(constraint);
397 //                Table table = new Table((String) map.get("TABLE_NAME"));
398 //                constraint = new UniqueConstraint(map.get("CONSTRAINT_NAME").toString(), table);
399 //            }
400 //        }
401 //        if (constraint != null) {
402 //            returnSet.add(constraint);
403 //        }
404 //
405 //        return returnSet;
406 //    }
407
408     @Override
409     public boolean supportsRestrictForeignKeys() {
410         return false;
411     }
412
413     @Override
414     public int getDataTypeMaxParameters(String dataTypeName) {
415         //noinspection HardCodedStringLiteral
416         if ("BINARY_FLOAT".equals(dataTypeName.toUpperCase())) {
417             return 0;
418         }
419         //noinspection HardCodedStringLiteral
420         if ("BINARY_DOUBLE".equals(dataTypeName.toUpperCase())) {
421             return 0;
422         }
423         return super.getDataTypeMaxParameters(dataTypeName);
424     }
425
426     public String getSystemTableWhereClause(String tableNameColumn) {
427         List<String> clauses = new ArrayList<String>(Arrays.asList("BIN$",
428                 "AQ$",
429                 "DR$",
430                 "SYS_IOT_OVER",
431                 "MLOG$_",
432                 "RUPD$_",
433                 "WM$_",
434                 "ISEQ$$_",
435                 "USLOG$",
436                 "SYS_FBA"));
437
438         for (int i = 0;i<clauses.size(); i++) {
439             clauses.set(i, tableNameColumn+" NOT LIKE '"+clauses.get(i)+"%'");
440         }
441         return "("+ StringUtil.join(clauses, " AND ") + ")";
442     }
443
444     @Override
445     public boolean jdbcCallsCatalogsSchemas() {
446         return true;
447     }
448
449     public Set<String> getUserDefinedTypes() {
450         if (userDefinedTypes == null) {
451             userDefinedTypes = new HashSet<>();
452             if ((getConnection() != null) && !(getConnection() instanceof OfflineConnection)) {
453                 try {
454                     try {
455                         //noinspection HardCodedStringLiteral
456                         userDefinedTypes.addAll(Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("jdbc", this).queryForList(new RawSqlStatement("SELECT DISTINCT TYPE_NAME FROM ALL_TYPES"), String.class));
457                     } catch (DatabaseException e) { //fall back to USER_TYPES if the user cannot see ALL_TYPES
458                         //noinspection HardCodedStringLiteral
459                         userDefinedTypes.addAll(Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("jdbc", this).queryForList(new RawSqlStatement("SELECT TYPE_NAME FROM USER_TYPES"), String.class));
460                     }
461                 } catch (DatabaseException e) {
462                     //ignore error
463                 }
464             }
465         }
466
467         return userDefinedTypes;
468     }
469
470     @Override
471     public String generateDatabaseFunctionValue(DatabaseFunction databaseFunction) {
472         //noinspection HardCodedStringLiteral
473         if ((databaseFunction != null) && "current_timestamp".equalsIgnoreCase(databaseFunction.toString())) {
474             return databaseFunction.toString();
475         }
476         if ((databaseFunction instanceof SequenceNextValueFunction) || (databaseFunction instanceof
477                 SequenceCurrentValueFunction)) {
478             String quotedSeq = super.generateDatabaseFunctionValue(databaseFunction);
479             // replace "myschema.my_seq".nextval with "myschema"."my_seq".nextval
480             return quotedSeq.replaceFirst("\"([^\\.\"]+)\\.([^\\.\"]+)\"", "\"$1\".\"$2\"");
481
482         }
483
484         return super.generateDatabaseFunctionValue(databaseFunction);
485     }
486
487     @Override
488     public ValidationErrors validate() {
489         ValidationErrors errors = super.validate();
490         DatabaseConnection connection = getConnection();
491         if ((connection == null) || (connection instanceof OfflineConnection)) {
492             //noinspection HardCodedStringLiteral
493             Scope.getCurrentScope().getLog(getClass()).info("Cannot validate offline database");
494             return errors;
495         }
496
497         if (!canAccessDbaRecycleBin()) {
498             errors.addWarning(getDbaRecycleBinWarning());
499         }
500
501         return errors;
502
503     }
504
505     public String getDbaRecycleBinWarning() {
506         //noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,
507         // HardCodedStringLiteral
508         //noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral
509         return "Liquibase needs to access the DBA_RECYCLEBIN table so we can automatically handle the case where " +
510                 "constraints are deleted and restored. Since Oracle doesn't properly restore the original table names " +
511                 "referenced in the constraint, we use the information from the DBA_RECYCLEBIN to automatically correct this" +
512                 " issue.\n" +
513                 "\n" +
514                 "The user you used to connect to the database (" + getConnection().getConnectionUserName() +
515                 ") needs to have \"SELECT ON SYS.DBA_RECYCLEBIN\" permissions set before we can perform this operation. " +
516                 "Please run the following SQL to set the appropriate permissions, and try running the command again.\n" +
517                 "\n" +
518                 "     GRANT SELECT ON SYS.DBA_RECYCLEBIN TO " + getConnection().getConnectionUserName() + ";";
519     }
520
521     public boolean canAccessDbaRecycleBin() {
522         if (canAccessDbaRecycleBin == null) {
523             DatabaseConnection connection = getConnection();
524             if ((connection == null) || (connection instanceof OfflineConnection)) {
525                 return false;
526             }
527
528             Statement statement = null;
529             try {
530                 statement = ((JdbcConnection) connection).createStatement();
531                 @SuppressWarnings("HardCodedStringLiteral") ResultSet resultSet = statement.executeQuery("select 1 from dba_recyclebin where 0=1");
532                 resultSet.close(); //don't need to do anything with the result set, just make sure statement ran.
533                 this.canAccessDbaRecycleBin = true;
534             } catch (Exception e) {
535                 //noinspection HardCodedStringLiteral
536                 if ((e instanceof SQLException) && e.getMessage().startsWith("ORA-00942")) { //ORA-00942: table or view does not exist
537                     this.canAccessDbaRecycleBin = false;
538                 } else {
539                     //noinspection HardCodedStringLiteral
540                     Scope.getCurrentScope().getLog(getClass()).warning("Cannot check dba_recyclebin access", e);
541                     this.canAccessDbaRecycleBin = false;
542                 }
543             } finally {
544                 JdbcUtils.close(null, statement);
545             }
546         }
547
548         return canAccessDbaRecycleBin;
549     }
550
551     @Override
552     public boolean supportsNotNullConstraintNames() {
553         return true;
554     }
555
556     /**
557      * Tests if the given String would be a valid identifier in Oracle DBMS. In Oracle, a valid identifier has
558      * the following form (case-insensitive comparison):
559      * 1st character: A-Z
560      * 2..n characters: A-Z0-9$_#
561      * The maximum length of an identifier differs by Oracle version and object type.
562      */
563     public boolean isValidOracleIdentifier(String identifier, Class<? extends DatabaseObject> type) {
564         if ((identifier == null) || (identifier.length() < 1))
565             return false;
566
567         if (!identifier.matches("^(i?)[A-Z][A-Z0-9\\$\\_\\#]*$"))
568             return false;
569
570         /*
571          * @todo It seems we currently do not have a class for tablespace identifiers, and all other classes
572          * we do know seem to be supported as 12cR2 long identifiers, so:
573          */
574         return (identifier.length() <= LONG_IDENTIFIERS_LEGNTH);
575     }
576
577     /**
578      * Returns the maximum number of bytes (NOT: characters) for an identifier. For Oracle <=12c Release 20, this
579      * is 30 bytes, and starting from 12cR2, up to 128 (except for tablespaces, PDB names and some other rather rare
580      * object types).
581      *
582      * @return the maximum length of an object identifier, in bytes
583      */
584     public int getIdentifierMaximumLength() {
585         try {
586             if (getDatabaseMajorVersion() < ORACLE_12C_MAJOR_VERSION) {
587                 return SHORT_IDENTIFIERS_LENGTH;
588             } else if ((getDatabaseMajorVersion() == ORACLE_12C_MAJOR_VERSION) && (getDatabaseMinorVersion() <= 1)) {
589                 return SHORT_IDENTIFIERS_LENGTH;
590             } else {
591                 return LONG_IDENTIFIERS_LEGNTH;
592             }
593         } catch (DatabaseException ex) {
594             throw new UnexpectedLiquibaseException("Cannot determine the Oracle database version number", ex);
595         }
596
597     }
598 }