Bladeren bron

!723 feat: 达梦flowable 6.8适配
Merge pull request !723 from dhb52/master

芋道源码 1 jaar geleden
bovenliggende
commit
d07869f461

+ 28 - 0
sql/dm/flowable-6.8.md

@@ -0,0 +1,28 @@
+# 达梦 flowable 适配
+
+参考文档: [《Flowable6.8(6.x 版本通用)整合集成达梦 8 数据库(DM8)详解,解决自动生成表时 dmn 相关表语法报错问题》](https://blog.csdn.net/TangBoBoa/article/details/130392495)
+
+## 1. 覆盖 flowable,liquibase 相关代码
+
+把`flowable-patch/src`下的文件按文件结果添加到`yudao-server`或者`yudao-module-bpm-biz`项目对应目录中,甚至你可以做个独立模块。
+
+## 2. 修改相关 DAO
+
+例如`cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOALeaveDO`
+
+```diff
+- @TableField("`type`")
+private String type;
+```
+
+## 3. 关于`flowable.database-schema-update`配置
+
+首次运行,修改`flowable.database-schema-update=true`,系统会自动建表,第二次运行需要修改为`false`。
+
+## 4. TODO
+
+配置`flowable.database-schema-update=true`第二次运行失败,报错
+
+```text
+Object [FLW_EV_DATABASECHANGELOG] already exists
+```

+ 598 - 0
sql/dm/flowable-patch/src/main/java/liquibase/database/core/DmDatabase.java

@@ -0,0 +1,598 @@
+package liquibase.database.core;
+
+import java.lang.reflect.Method;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import liquibase.CatalogAndSchema;
+import liquibase.Scope;
+import liquibase.database.AbstractJdbcDatabase;
+import liquibase.database.DatabaseConnection;
+import liquibase.database.OfflineConnection;
+import liquibase.database.jvm.JdbcConnection;
+import liquibase.exception.DatabaseException;
+import liquibase.exception.UnexpectedLiquibaseException;
+import liquibase.exception.ValidationErrors;
+import liquibase.executor.ExecutorService;
+import liquibase.statement.DatabaseFunction;
+import liquibase.statement.SequenceCurrentValueFunction;
+import liquibase.statement.SequenceNextValueFunction;
+import liquibase.statement.core.RawCallStatement;
+import liquibase.statement.core.RawSqlStatement;
+import liquibase.structure.DatabaseObject;
+import liquibase.structure.core.Catalog;
+import liquibase.structure.core.Index;
+import liquibase.structure.core.PrimaryKey;
+import liquibase.structure.core.Schema;
+import liquibase.util.JdbcUtils;
+import liquibase.util.StringUtil;
+
+public class DmDatabase extends AbstractJdbcDatabase {
+    private static final String PRODUCT_NAME = "DM DBMS";
+
+    @Override
+    protected String getDefaultDatabaseProductName() {
+        return PRODUCT_NAME;
+    }
+
+    /**
+     * Is this AbstractDatabase subclass the correct one to use for the given connection.
+     *
+     * @param conn
+     */
+    @Override
+    public boolean isCorrectDatabaseImplementation(DatabaseConnection conn) throws DatabaseException {
+        return PRODUCT_NAME.equalsIgnoreCase(conn.getDatabaseProductName());
+    }
+
+    /**
+     * If this database understands the given url, return the default driver class name.  Otherwise return null.
+     *
+     * @param url
+     */
+    @Override
+    public String getDefaultDriver(String url) {
+        if(url.startsWith("jdbc:dm")) {
+            return "dm.jdbc.driver.DmDriver";
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns an all-lower-case short name of the product.  Used for end-user selecting of database type
+     * such as the DBMS precondition.
+     */
+    @Override
+    public String getShortName() {
+        return "dm";
+    }
+
+    @Override
+    public Integer getDefaultPort() {
+        return 5236;
+    }
+
+    /**
+     * Returns whether this database support initially deferrable columns.
+     */
+    @Override
+    public boolean supportsInitiallyDeferrableColumns() {
+        return true;
+    }
+
+    @Override
+    public boolean supportsTablespaces() {
+        return true;
+    }
+
+    @Override
+    public int getPriority() {
+        return PRIORITY_DEFAULT;
+    }
+
+    private static final Pattern PROXY_USER = Pattern.compile(".*(?:thin|oci)\\:(.+)/@.*");
+
+    protected final int SHORT_IDENTIFIERS_LENGTH = 30;
+    protected final int LONG_IDENTIFIERS_LEGNTH = 128;
+    public static final int ORACLE_12C_MAJOR_VERSION = 12;
+
+    private Set<String> reservedWords = new HashSet<>();
+    private Set<String> userDefinedTypes;
+    private Map<String, String> savedSessionNlsSettings;
+
+    private Boolean canAccessDbaRecycleBin;
+    private Integer databaseMajorVersion;
+    private Integer databaseMinorVersion;
+
+    /**
+     * Default constructor for an object that represents the Oracle Database DBMS.
+     */
+    public DmDatabase() {
+        super.unquotedObjectsAreUppercased = true;
+        //noinspection HardCodedStringLiteral
+        super.setCurrentDateTimeFunction("SYSTIMESTAMP");
+        // Setting list of Oracle's native functions
+        //noinspection HardCodedStringLiteral
+        dateFunctions.add(new DatabaseFunction("SYSDATE"));
+        //noinspection HardCodedStringLiteral
+        dateFunctions.add(new DatabaseFunction("SYSTIMESTAMP"));
+        //noinspection HardCodedStringLiteral
+        dateFunctions.add(new DatabaseFunction("CURRENT_TIMESTAMP"));
+        //noinspection HardCodedStringLiteral
+        super.sequenceNextValueFunction = "%s.nextval";
+        //noinspection HardCodedStringLiteral
+        super.sequenceCurrentValueFunction = "%s.currval";
+    }
+
+    private void tryProxySession(final String url, final Connection con) {
+        Matcher m = PROXY_USER.matcher(url);
+        if (m.matches()) {
+            Properties props = new Properties();
+            props.put("PROXY_USER_NAME", m.group(1));
+            try {
+                Method method = con.getClass().getMethod("openProxySession", int.class, Properties.class);
+                method.setAccessible(true);
+                method.invoke(con, 1, props);
+            } catch (Exception e) {
+                Scope.getCurrentScope().getLog(getClass()).info("Could not open proxy session on OracleDatabase: " + e.getCause().getMessage());
+            }
+        }
+    }
+
+    @Override
+    public int getDatabaseMajorVersion() throws DatabaseException {
+        if (databaseMajorVersion == null) {
+            return super.getDatabaseMajorVersion();
+        } else {
+            return databaseMajorVersion;
+        }
+    }
+
+    @Override
+    public int getDatabaseMinorVersion() throws DatabaseException {
+        if (databaseMinorVersion == null) {
+            return super.getDatabaseMinorVersion();
+        } else {
+            return databaseMinorVersion;
+        }
+    }
+
+    @Override
+    public String getJdbcCatalogName(CatalogAndSchema schema) {
+        return null;
+    }
+
+    @Override
+    public String getJdbcSchemaName(CatalogAndSchema schema) {
+        return correctObjectName((schema.getCatalogName() == null) ? schema.getSchemaName() : schema.getCatalogName(), Schema.class);
+    }
+
+    @Override
+    protected String getAutoIncrementClause(final String generationType, final Boolean defaultOnNull) {
+        if (StringUtil.isEmpty(generationType)) {
+            return super.getAutoIncrementClause();
+        }
+
+        String autoIncrementClause = "GENERATED %s AS IDENTITY"; // %s -- [ ALWAYS | BY DEFAULT [ ON NULL ] ]
+        String generationStrategy = generationType;
+        if (Boolean.TRUE.equals(defaultOnNull) && generationType.toUpperCase().equals("BY DEFAULT")) {
+            generationStrategy += " ON NULL";
+        }
+        return String.format(autoIncrementClause, generationStrategy);
+    }
+
+    @Override
+    public String generatePrimaryKeyName(String tableName) {
+        if (tableName.length() > 27) {
+            //noinspection HardCodedStringLiteral
+            return "PK_" + tableName.toUpperCase(Locale.US).substring(0, 27);
+        } else {
+            //noinspection HardCodedStringLiteral
+            return "PK_" + tableName.toUpperCase(Locale.US);
+        }
+    }
+
+    @Override
+    public boolean isReservedWord(String objectName) {
+        return reservedWords.contains(objectName.toUpperCase());
+    }
+
+    @Override
+    public boolean supportsSequences() {
+        return true;
+    }
+
+    /**
+     * Oracle supports catalogs in liquibase terms
+     *
+     * @return false
+     */
+    @Override
+    public boolean supportsSchemas() {
+        return false;
+    }
+
+    @Override
+    protected String getConnectionCatalogName() throws DatabaseException {
+        if (getConnection() instanceof OfflineConnection) {
+            return getConnection().getCatalog();
+        }
+        try {
+            //noinspection HardCodedStringLiteral
+            return Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("jdbc", this).queryForObject(new RawCallStatement("select sys_context( 'userenv', 'current_schema' ) from dual"), String.class);
+        } catch (Exception e) {
+            //noinspection HardCodedStringLiteral
+            Scope.getCurrentScope().getLog(getClass()).info("Error getting default schema", e);
+        }
+        return null;
+    }
+
+    @Override
+    public String getDefaultCatalogName() {//NOPMD
+        return (super.getDefaultCatalogName() == null) ? null : super.getDefaultCatalogName().toUpperCase(Locale.US);
+    }
+
+    /**
+     * <p>Returns an Oracle date literal with the same value as a string formatted using ISO 8601.</p>
+     *
+     * <p>Convert an ISO8601 date string to one of the following results:
+     * to_date('1995-05-23', 'YYYY-MM-DD')
+     * to_date('1995-05-23 09:23:59', 'YYYY-MM-DD HH24:MI:SS')</p>
+     * <p>
+     * Implementation restriction:<br>
+     * Currently, only the following subsets of ISO8601 are supported:<br>
+     * <ul>
+     * <li>YYYY-MM-DD</li>
+     * <li>YYYY-MM-DDThh:mm:ss</li>
+     * </ul>
+     */
+    @Override
+    public String getDateLiteral(String isoDate) {
+        String normalLiteral = super.getDateLiteral(isoDate);
+
+        if (isDateOnly(isoDate)) {
+            return "TO_DATE(" + normalLiteral + ", 'YYYY-MM-DD')";
+        } else if (isTimeOnly(isoDate)) {
+            return "TO_DATE(" + normalLiteral + ", 'HH24:MI:SS')";
+        } else if (isTimestamp(isoDate)) {
+            return "TO_TIMESTAMP(" + normalLiteral + ", 'YYYY-MM-DD HH24:MI:SS.FF')";
+        } else if (isDateTime(isoDate)) {
+            int seppos = normalLiteral.lastIndexOf('.');
+            if (seppos != -1) {
+                normalLiteral = normalLiteral.substring(0, seppos) + "'";
+            }
+            return "TO_DATE(" + normalLiteral + ", 'YYYY-MM-DD HH24:MI:SS')";
+        }
+        return "UNSUPPORTED:" + isoDate;
+    }
+
+    @Override
+    public boolean isSystemObject(DatabaseObject example) {
+        if (example == null) {
+            return false;
+        }
+
+        if (this.isLiquibaseObject(example)) {
+            return false;
+        }
+
+        if (example instanceof Schema) {
+            //noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral
+            if ("SYSTEM".equals(example.getName()) || "SYS".equals(example.getName()) || "CTXSYS".equals(example.getName()) || "XDB".equals(example.getName())) {
+                return true;
+            }
+            //noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral
+            if ("SYSTEM".equals(example.getSchema().getCatalogName()) || "SYS".equals(example.getSchema().getCatalogName()) || "CTXSYS".equals(example.getSchema().getCatalogName()) || "XDB".equals(example.getSchema().getCatalogName())) {
+                return true;
+            }
+        } else if (isSystemObject(example.getSchema())) {
+            return true;
+        }
+        if (example instanceof Catalog) {
+            //noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral
+            if (("SYSTEM".equals(example.getName()) || "SYS".equals(example.getName()) || "CTXSYS".equals(example.getName()) || "XDB".equals(example.getName()))) {
+                return true;
+            }
+        } else if (example.getName() != null) {
+            //noinspection HardCodedStringLiteral
+            if (example.getName().startsWith("BIN$")) { //oracle deleted table
+                boolean filteredInOriginalQuery = this.canAccessDbaRecycleBin();
+                if (!filteredInOriginalQuery) {
+                    filteredInOriginalQuery = StringUtil.trimToEmpty(example.getSchema().getName()).equalsIgnoreCase(this.getConnection().getConnectionUserName());
+                }
+
+                if (filteredInOriginalQuery) {
+                    return !((example instanceof PrimaryKey) || (example instanceof Index) || (example instanceof
+                            liquibase.statement.UniqueConstraint));
+                } else {
+                    return true;
+                }
+            } else //noinspection HardCodedStringLiteral
+                if (example.getName().startsWith("AQ$")) { //oracle AQ tables
+                    return true;
+                } else //noinspection HardCodedStringLiteral
+                    if (example.getName().startsWith("DR$")) { //oracle index tables
+                        return true;
+                    } else //noinspection HardCodedStringLiteral
+                        if (example.getName().startsWith("SYS_IOT_OVER")) { //oracle system table
+                            return true;
+                        } else //noinspection HardCodedStringLiteral,HardCodedStringLiteral
+                            if ((example.getName().startsWith("MDRT_") || example.getName().startsWith("MDRS_")) && example.getName().endsWith("$")) {
+                                // CORE-1768 - Oracle creates these for spatial indices and will remove them when the index is removed.
+                                return true;
+                            } else //noinspection HardCodedStringLiteral
+                                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.
+                                    return true;
+                                } else //noinspection HardCodedStringLiteral
+                                    if (example.getName().startsWith("RUPD$_")) { //Created by materialized view log tables using primary keys. Not available for DDL operations.
+                                        return true;
+                                    } else //noinspection HardCodedStringLiteral
+                                        if (example.getName().startsWith("WM$_")) { //Workspace Manager backup tables.
+                                            return true;
+                                        } else //noinspection HardCodedStringLiteral
+                                            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.
+                                                return true;
+                                            } else //noinspection HardCodedStringLiteral
+                                                if ("JAVA$CLASS$MD5$TABLE".equals(example.getName())) { //This is a hash table that tracks the loading of Java objects into a schema.
+                                                    return true;
+                                                } else //noinspection HardCodedStringLiteral
+                                                    if (example.getName().startsWith("ISEQ$$_")) { //System-generated sequence
+                                                        return true;
+                                                    } else //noinspection HardCodedStringLiteral
+                                                        if (example.getName().startsWith("USLOG$")) { //for update materialized view
+                                                            return true;
+                                                        } else if (example.getName().startsWith("SYS_FBA")) { //for Flashback tables
+                                                            return true;
+                                                        }
+        }
+
+        return super.isSystemObject(example);
+    }
+
+    @Override
+    public boolean supportsAutoIncrement() {
+        // Oracle supports Identity beginning with version 12c
+        boolean isAutoIncrementSupported = false;
+
+        try {
+            if (getDatabaseMajorVersion() >= 12) {
+                isAutoIncrementSupported = true;
+            }
+
+            // Returning true will generate create table command with 'IDENTITY' clause, example:
+            // 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));
+
+            // While returning false will continue to generate create table command without 'IDENTITY' clause, example:
+            // CREATE TABLE AutoIncTest (IDPrimaryKey NUMBER(19) NOT NULL, TypeID NUMBER(3) NOT NULL, Description NVARCHAR2(50), CONSTRAINT PK_AutoIncTest PRIMARY KEY (IDPrimaryKey));
+
+        } catch (DatabaseException ex) {
+            isAutoIncrementSupported = false;
+        }
+
+        return isAutoIncrementSupported;
+    }
+
+
+//    public Set<UniqueConstraint> findUniqueConstraints(String schema) throws DatabaseException {
+//        Set<UniqueConstraint> returnSet = new HashSet<UniqueConstraint>();
+//
+//        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"));
+//
+//        UniqueConstraint constraint = null;
+//        for (Map map : maps) {
+//            if (constraint == null || !constraint.getName().equals(constraint.getName())) {
+//                returnSet.add(constraint);
+//                Table table = new Table((String) map.get("TABLE_NAME"));
+//                constraint = new UniqueConstraint(map.get("CONSTRAINT_NAME").toString(), table);
+//            }
+//        }
+//        if (constraint != null) {
+//            returnSet.add(constraint);
+//        }
+//
+//        return returnSet;
+//    }
+
+    @Override
+    public boolean supportsRestrictForeignKeys() {
+        return false;
+    }
+
+    @Override
+    public int getDataTypeMaxParameters(String dataTypeName) {
+        //noinspection HardCodedStringLiteral
+        if ("BINARY_FLOAT".equals(dataTypeName.toUpperCase())) {
+            return 0;
+        }
+        //noinspection HardCodedStringLiteral
+        if ("BINARY_DOUBLE".equals(dataTypeName.toUpperCase())) {
+            return 0;
+        }
+        return super.getDataTypeMaxParameters(dataTypeName);
+    }
+
+    public String getSystemTableWhereClause(String tableNameColumn) {
+        List<String> clauses = new ArrayList<String>(Arrays.asList("BIN$",
+                "AQ$",
+                "DR$",
+                "SYS_IOT_OVER",
+                "MLOG$_",
+                "RUPD$_",
+                "WM$_",
+                "ISEQ$$_",
+                "USLOG$",
+                "SYS_FBA"));
+
+        for (int i = 0;i<clauses.size(); i++) {
+            clauses.set(i, tableNameColumn+" NOT LIKE '"+clauses.get(i)+"%'");
+        }
+        return "("+ StringUtil.join(clauses, " AND ") + ")";
+    }
+
+    @Override
+    public boolean jdbcCallsCatalogsSchemas() {
+        return true;
+    }
+
+    public Set<String> getUserDefinedTypes() {
+        if (userDefinedTypes == null) {
+            userDefinedTypes = new HashSet<>();
+            if ((getConnection() != null) && !(getConnection() instanceof OfflineConnection)) {
+                try {
+                    try {
+                        //noinspection HardCodedStringLiteral
+                        userDefinedTypes.addAll(Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("jdbc", this).queryForList(new RawSqlStatement("SELECT DISTINCT TYPE_NAME FROM ALL_TYPES"), String.class));
+                    } catch (DatabaseException e) { //fall back to USER_TYPES if the user cannot see ALL_TYPES
+                        //noinspection HardCodedStringLiteral
+                        userDefinedTypes.addAll(Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("jdbc", this).queryForList(new RawSqlStatement("SELECT TYPE_NAME FROM USER_TYPES"), String.class));
+                    }
+                } catch (DatabaseException e) {
+                    //ignore error
+                }
+            }
+        }
+
+        return userDefinedTypes;
+    }
+
+    @Override
+    public String generateDatabaseFunctionValue(DatabaseFunction databaseFunction) {
+        //noinspection HardCodedStringLiteral
+        if ((databaseFunction != null) && "current_timestamp".equalsIgnoreCase(databaseFunction.toString())) {
+            return databaseFunction.toString();
+        }
+        if ((databaseFunction instanceof SequenceNextValueFunction) || (databaseFunction instanceof
+                SequenceCurrentValueFunction)) {
+            String quotedSeq = super.generateDatabaseFunctionValue(databaseFunction);
+            // replace "myschema.my_seq".nextval with "myschema"."my_seq".nextval
+            return quotedSeq.replaceFirst("\"([^\\.\"]+)\\.([^\\.\"]+)\"", "\"$1\".\"$2\"");
+
+        }
+
+        return super.generateDatabaseFunctionValue(databaseFunction);
+    }
+
+    @Override
+    public ValidationErrors validate() {
+        ValidationErrors errors = super.validate();
+        DatabaseConnection connection = getConnection();
+        if ((connection == null) || (connection instanceof OfflineConnection)) {
+            //noinspection HardCodedStringLiteral
+            Scope.getCurrentScope().getLog(getClass()).info("Cannot validate offline database");
+            return errors;
+        }
+
+        if (!canAccessDbaRecycleBin()) {
+            errors.addWarning(getDbaRecycleBinWarning());
+        }
+
+        return errors;
+
+    }
+
+    public String getDbaRecycleBinWarning() {
+        //noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,
+        // HardCodedStringLiteral
+        //noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral
+        return "Liquibase needs to access the DBA_RECYCLEBIN table so we can automatically handle the case where " +
+                "constraints are deleted and restored. Since Oracle doesn't properly restore the original table names " +
+                "referenced in the constraint, we use the information from the DBA_RECYCLEBIN to automatically correct this" +
+                " issue.\n" +
+                "\n" +
+                "The user you used to connect to the database (" + getConnection().getConnectionUserName() +
+                ") needs to have \"SELECT ON SYS.DBA_RECYCLEBIN\" permissions set before we can perform this operation. " +
+                "Please run the following SQL to set the appropriate permissions, and try running the command again.\n" +
+                "\n" +
+                "     GRANT SELECT ON SYS.DBA_RECYCLEBIN TO " + getConnection().getConnectionUserName() + ";";
+    }
+
+    public boolean canAccessDbaRecycleBin() {
+        if (canAccessDbaRecycleBin == null) {
+            DatabaseConnection connection = getConnection();
+            if ((connection == null) || (connection instanceof OfflineConnection)) {
+                return false;
+            }
+
+            Statement statement = null;
+            try {
+                statement = ((JdbcConnection) connection).createStatement();
+                @SuppressWarnings("HardCodedStringLiteral") ResultSet resultSet = statement.executeQuery("select 1 from dba_recyclebin where 0=1");
+                resultSet.close(); //don't need to do anything with the result set, just make sure statement ran.
+                this.canAccessDbaRecycleBin = true;
+            } catch (Exception e) {
+                //noinspection HardCodedStringLiteral
+                if ((e instanceof SQLException) && e.getMessage().startsWith("ORA-00942")) { //ORA-00942: table or view does not exist
+                    this.canAccessDbaRecycleBin = false;
+                } else {
+                    //noinspection HardCodedStringLiteral
+                    Scope.getCurrentScope().getLog(getClass()).warning("Cannot check dba_recyclebin access", e);
+                    this.canAccessDbaRecycleBin = false;
+                }
+            } finally {
+                JdbcUtils.close(null, statement);
+            }
+        }
+
+        return canAccessDbaRecycleBin;
+    }
+
+    @Override
+    public boolean supportsNotNullConstraintNames() {
+        return true;
+    }
+
+    /**
+     * Tests if the given String would be a valid identifier in Oracle DBMS. In Oracle, a valid identifier has
+     * the following form (case-insensitive comparison):
+     * 1st character: A-Z
+     * 2..n characters: A-Z0-9$_#
+     * The maximum length of an identifier differs by Oracle version and object type.
+     */
+    public boolean isValidOracleIdentifier(String identifier, Class<? extends DatabaseObject> type) {
+        if ((identifier == null) || (identifier.length() < 1))
+            return false;
+
+        if (!identifier.matches("^(i?)[A-Z][A-Z0-9\\$\\_\\#]*$"))
+            return false;
+
+        /*
+         * @todo It seems we currently do not have a class for tablespace identifiers, and all other classes
+         * we do know seem to be supported as 12cR2 long identifiers, so:
+         */
+        return (identifier.length() <= LONG_IDENTIFIERS_LEGNTH);
+    }
+
+    /**
+     * Returns the maximum number of bytes (NOT: characters) for an identifier. For Oracle <=12c Release 20, this
+     * is 30 bytes, and starting from 12cR2, up to 128 (except for tablespaces, PDB names and some other rather rare
+     * object types).
+     *
+     * @return the maximum length of an object identifier, in bytes
+     */
+    public int getIdentifierMaximumLength() {
+        try {
+            if (getDatabaseMajorVersion() < ORACLE_12C_MAJOR_VERSION) {
+                return SHORT_IDENTIFIERS_LENGTH;
+            } else if ((getDatabaseMajorVersion() == ORACLE_12C_MAJOR_VERSION) && (getDatabaseMinorVersion() <= 1)) {
+                return SHORT_IDENTIFIERS_LENGTH;
+            } else {
+                return LONG_IDENTIFIERS_LEGNTH;
+            }
+        } catch (DatabaseException ex) {
+            throw new UnexpectedLiquibaseException("Cannot determine the Oracle database version number", ex);
+        }
+
+    }
+}

+ 165 - 0
sql/dm/flowable-patch/src/main/java/liquibase/datatype/core/BooleanType.java

@@ -0,0 +1,165 @@
+package liquibase.datatype.core;
+
+import liquibase.change.core.LoadDataChange;
+import liquibase.database.Database;
+import liquibase.database.core.*;
+import liquibase.datatype.DataTypeInfo;
+import liquibase.datatype.DatabaseDataType;
+import liquibase.datatype.LiquibaseDataType;
+import liquibase.exception.UnexpectedLiquibaseException;
+import liquibase.statement.DatabaseFunction;
+import liquibase.util.StringUtil;
+
+import java.util.Locale;
+import java.util.regex.Pattern;
+
+@DataTypeInfo(name = "boolean", aliases = {"java.sql.Types.BOOLEAN", "java.lang.Boolean", "bit", "bool"}, minParameters = 0, maxParameters = 0, priority = LiquibaseDataType.PRIORITY_DEFAULT)
+public class BooleanType extends LiquibaseDataType {
+
+    @Override
+    public DatabaseDataType toDatabaseDataType(Database database) {
+        String originalDefinition = StringUtil.trimToEmpty(getRawDefinition());
+        if ((database instanceof Firebird3Database)) {
+            return new DatabaseDataType("BOOLEAN");
+        }
+
+        if ((database instanceof Db2zDatabase) || (database instanceof FirebirdDatabase)) {
+            return new DatabaseDataType("SMALLINT");
+        } else if (database instanceof MSSQLDatabase) {
+            return new DatabaseDataType(database.escapeDataTypeName("bit"));
+        } else if (database instanceof MySQLDatabase) {
+            if (originalDefinition.toLowerCase(Locale.US).startsWith("bit")) {
+                return new DatabaseDataType("BIT", getParameters());
+            }
+            return new DatabaseDataType("BIT", 1);
+        } else if (database instanceof OracleDatabase) {
+            return new DatabaseDataType("NUMBER", 1);
+        } else if ((database instanceof SybaseASADatabase) || (database instanceof SybaseDatabase)) {
+            return new DatabaseDataType("BIT");
+        } else if (database instanceof DerbyDatabase) {
+            if (((DerbyDatabase) database).supportsBooleanDataType()) {
+                return new DatabaseDataType("BOOLEAN");
+            } else {
+                return new DatabaseDataType("SMALLINT");
+            }
+        } else if (database instanceof DB2Database) {
+            if (((DB2Database) database).supportsBooleanDataType())
+                return new DatabaseDataType("BOOLEAN");
+            else
+                return new DatabaseDataType("SMALLINT");
+        } else if (database instanceof HsqlDatabase) {
+            return new DatabaseDataType("BOOLEAN");
+        } else if (database instanceof PostgresDatabase) {
+            if (originalDefinition.toLowerCase(Locale.US).startsWith("bit")) {
+                return new DatabaseDataType("BIT", getParameters());
+            }
+        } else if (database instanceof DmDatabase) { // dhb52: DM Support
+            return new DatabaseDataType("bit");
+        }
+
+        return super.toDatabaseDataType(database);
+    }
+
+    @Override
+    public String objectToSql(Object value, Database database) {
+        if ((value == null) || "null".equals(value.toString().toLowerCase(Locale.US))) {
+            return null;
+        }
+
+        String returnValue;
+        if (value instanceof String) {
+            value = ((String) value).replaceAll("'", "");
+            if ("true".equals(((String) value).toLowerCase(Locale.US)) || "1".equals(value) || "b'1'".equals(((String) value).toLowerCase(Locale.US)) || "t".equals(((String) value).toLowerCase(Locale.US)) || ((String) value).toLowerCase(Locale.US).equals(this.getTrueBooleanValue(database).toLowerCase(Locale.US))) {
+                returnValue = this.getTrueBooleanValue(database);
+            } else if ("false".equals(((String) value).toLowerCase(Locale.US)) || "0".equals(value) || "b'0'".equals(
+                ((String) value).toLowerCase(Locale.US)) || "f".equals(((String) value).toLowerCase(Locale.US)) || ((String) value).toLowerCase(Locale.US).equals(this.getFalseBooleanValue(database).toLowerCase(Locale.US))) {
+                returnValue = this.getFalseBooleanValue(database);
+            } else if (database instanceof PostgresDatabase && Pattern.matches("b?([01])\\1*(::bit|::\"bit\")?", (String) value)) {
+                returnValue = "b'"
+                    + value.toString()
+                    .replace("b", "")
+                    .replace("\"", "")
+                    .replace("::it", "")
+                    + "'::\"bit\"";
+            } else {
+                throw new UnexpectedLiquibaseException("Unknown boolean value: " + value);
+            }
+        } else if (value instanceof Long) {
+            if (Long.valueOf(1).equals(value)) {
+                returnValue = this.getTrueBooleanValue(database);
+            } else {
+                returnValue = this.getFalseBooleanValue(database);
+            }
+        } else if (value instanceof Number) {
+            if (value.equals(1) || "1".equals(value.toString()) || "1.0".equals(value.toString())) {
+                returnValue = this.getTrueBooleanValue(database);
+            } else {
+                returnValue = this.getFalseBooleanValue(database);
+            }
+        } else if (value instanceof DatabaseFunction) {
+            return value.toString();
+        } else if (value instanceof Boolean) {
+            if (((Boolean) value)) {
+                returnValue = this.getTrueBooleanValue(database);
+            } else {
+                returnValue = this.getFalseBooleanValue(database);
+            }
+        } else {
+            throw new UnexpectedLiquibaseException("Cannot convert type " + value.getClass() + " to a boolean value");
+        }
+
+        return returnValue;
+    }
+
+    protected boolean isNumericBoolean(Database database) {
+        if (database instanceof Firebird3Database) {
+            return false;
+        }
+        if (database instanceof DerbyDatabase) {
+            return !((DerbyDatabase) database).supportsBooleanDataType();
+        } else if (database instanceof DB2Database) {
+            return !((DB2Database) database).supportsBooleanDataType();
+        }
+        return (database instanceof Db2zDatabase)
+            || (database instanceof FirebirdDatabase)
+            || (database instanceof MSSQLDatabase)
+            || (database instanceof MySQLDatabase)
+            || (database instanceof OracleDatabase)
+            || (database instanceof SQLiteDatabase)
+            || (database instanceof SybaseASADatabase)
+            || (database instanceof SybaseDatabase)
+            || (database instanceof DmDatabase); // dhb52: DM Support
+    }
+
+    /**
+     * The database-specific value to use for "false" "boolean" columns.
+     */
+    public String getFalseBooleanValue(Database database) {
+        if (isNumericBoolean(database)) {
+            return "0";
+        }
+        if (database instanceof InformixDatabase) {
+            return "'f'";
+        }
+        return "FALSE";
+    }
+
+    /**
+     * The database-specific value to use for "true" "boolean" columns.
+     */
+    public String getTrueBooleanValue(Database database) {
+        if (isNumericBoolean(database)) {
+            return "1";
+        }
+        if (database instanceof InformixDatabase) {
+            return "'t'";
+        }
+        return "TRUE";
+    }
+
+    @Override
+    public LoadDataChange.LOAD_DATA_TYPE getLoadTypeName() {
+        return LoadDataChange.LOAD_DATA_TYPE.BOOLEAN;
+    }
+
+}

File diff suppressed because it is too large
+ 2068 - 0
sql/dm/flowable-patch/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java


+ 1 - 0
sql/dm/flowable-patch/src/main/resources/META-INF/package-info.md

@@ -0,0 +1 @@
+防止IDEA将`.`和`/`混为一谈

+ 21 - 0
sql/dm/flowable-patch/src/main/resources/META-INF/services/liquibase.database.Database

@@ -0,0 +1,21 @@
+liquibase.database.core.CockroachDatabase
+liquibase.database.core.DB2Database
+liquibase.database.core.Db2zDatabase
+liquibase.database.core.DerbyDatabase
+liquibase.database.core.Firebird3Database
+liquibase.database.core.FirebirdDatabase
+liquibase.database.core.H2Database
+liquibase.database.core.HsqlDatabase
+liquibase.database.core.InformixDatabase
+liquibase.database.core.Ingres9Database
+liquibase.database.core.MSSQLDatabase
+liquibase.database.core.MariaDBDatabase
+liquibase.database.core.MockDatabase
+liquibase.database.core.MySQLDatabase
+liquibase.database.core.OracleDatabase
+liquibase.database.core.PostgresDatabase
+liquibase.database.core.SQLiteDatabase
+liquibase.database.core.SybaseASADatabase
+liquibase.database.core.SybaseDatabase
+liquibase.database.core.DmDatabase
+liquibase.database.core.UnsupportedDatabase