001/**
002 * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
003 * <p>
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * <p>
008 * http://www.apache.org/licenses/LICENSE-2.0
009 * <p>
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package com.mybatisflex.core.dialect.impl;
017
018import com.mybatisflex.core.dialect.KeywordWrap;
019import com.mybatisflex.core.dialect.LimitOffsetProcessor;
020import com.mybatisflex.core.row.Row;
021import com.mybatisflex.core.table.TableInfo;
022import com.mybatisflex.core.util.CollectionUtil;
023import com.mybatisflex.core.util.StringUtil;
024
025import java.util.List;
026import java.util.Map;
027import java.util.Set;
028import java.util.StringJoiner;
029
030public class OracleDialect extends CommonsDialectImpl {
031
032    private static final Set<String> keywords = CollectionUtil.newHashSet(
033            "ACCESS", "ADD", "ALL", "ALTER",
034            "AND", "ANY", "ARRAYLEN", "AS",
035            "ASC", "AUDIT", "BETWEEN", "BY",
036            "CHAR", "CHECK", "CLUSTER", "COLUMN",
037            "COMMENT", "COMPRESS", "CONNECT", "CREATE",
038            "CURRENT", "DATE", "DECIMAL", "DEFAULT",
039            "DELETE", "DESC", "DISTINCT", "DROP",
040            "ELSE", "EXCLUSIVE", "EXISTS", "FILE",
041            "FLOAT", "FOR", "FROM", "GRANT",
042            "GROUP", "HAVING", "IDENTIFIED", "IMMEDIATE",
043            "IN", "INCREMENT", "INDEX", "INITIAL",
044            "INSERT", "INTEGER", "INTERSECT", "INTO",
045            "IS", "LEVEL", "LIKE", "LOCK",
046            "LONG", "MAXEXTENTS", "MINUS", "MODE",
047            "MODIFY", "NOAUDIT", "NOCOMPRESS", "NOT",
048            "NOTFOUND", "NOWAIT", "NULL", "NUMBER",
049            "OF", "OFFLINE", "ON", "ONLINE",
050            "OPTION", "OR", "ORDER", "PCTFREE",
051            "PRIOR", "PRIVILEGES", "PUBLIC", "RAW",
052            "RENAME", "RESOURCE", "REVOKE", "ROW",
053            "ROWID", "ROWLABEL", "ROWNUM", "ROWS",
054            "START", "SELECT", "SESSION", "SET",
055            "SHARE", "SIZE", "SMALLINT", "SQLBUF",
056            "SUCCESSFUL", "SYNONYM", "SYSDATE", "TABLE",
057            "THEN", "TO", "TRIGGER", "UID",
058            "UNION", "UNIQUE", "UPDATE", "USER",
059            "VALIDATE", "VALUES", "VARCHAR", "VARCHAR2"
060    );
061
062    public OracleDialect() {
063        this(LimitOffsetProcessor.ORACLE);
064    }
065
066    public OracleDialect(LimitOffsetProcessor limitOffsetProcessor) {
067        super(new KeywordWrap(keywords, "\"", "\""), limitOffsetProcessor);
068    }
069
070    @Override
071    public String forInsertEntityBatch(TableInfo tableInfo, List<?> entities) {
072        /**
073         * INSERT ALL
074         *    INTO t (col1, col2, col3) VALUES ('val1_1', 'val1_2', 'val1_3')
075         *    INTO t (col1, col2, col3) VALUES ('val2_1', 'val2_2', 'val2_3')
076         *    INTO t (col1, col2, col3) VALUES ('val3_1', 'val3_2', 'val3_3')
077         *    .
078         *    .
079         *    .
080         * SELECT 1 FROM DUAL;
081         */
082        StringBuilder sql = new StringBuilder();
083        sql.append("INSERT ALL");
084        String[] insertColumns = tableInfo.obtainInsertColumns(null, false);
085        String[] warpedInsertColumns = new String[insertColumns.length];
086        for (int i = 0; i < insertColumns.length; i++) {
087            warpedInsertColumns[i] = wrap(insertColumns[i]);
088        }
089
090
091        Map<String, String> onInsertColumns = tableInfo.getOnInsertColumns();
092        for (int i = 0; i < entities.size(); i++) {
093            sql.append(" INTO ").append(tableInfo.getWrapSchemaAndTableName(this));
094            sql.append(" (").append(StringUtil.join(", ", warpedInsertColumns)).append(")");
095            sql.append(" VALUES ");
096
097            StringJoiner stringJoiner = new StringJoiner(", ", "(", ")");
098            for (String insertColumn : insertColumns) {
099                if (onInsertColumns != null && onInsertColumns.containsKey(insertColumn)) {
100                    //直接读取 onInsert 配置的值,而不用 "?" 代替
101                    stringJoiner.add(onInsertColumns.get(insertColumn));
102                } else {
103                    stringJoiner.add("?");
104                }
105            }
106            sql.append(stringJoiner);
107        }
108
109        return sql.append(" SELECT 1 FROM DUAL").toString();
110    }
111
112
113    @Override
114    public String forInsertBatchWithFirstRowColumns(String schema, String tableName, List<Row> rows) {
115        /**
116         * INSERT ALL
117         *    INTO t (col1, col2, col3) VALUES ('val1_1', 'val1_2', 'val1_3')
118         *    INTO t (col1, col2, col3) VALUES ('val2_1', 'val2_2', 'val2_3')
119         *    INTO t (col1, col2, col3) VALUES ('val3_1', 'val3_2', 'val3_3')
120         *    .
121         *    .
122         *    .
123         * SELECT 1 FROM DUAL;
124         */
125        StringBuilder fields = new StringBuilder();
126        Row firstRow = rows.get(0);
127        Set<String> attrs = firstRow.obtainModifyAttrs();
128        int index = 0;
129        for (String column : attrs) {
130            fields.append(wrap(column));
131            if (index != attrs.size() - 1) {
132                fields.append(", ");
133            }
134            index++;
135        }
136
137        StringBuilder sql = new StringBuilder();
138        sql.append("INSERT ALL");
139
140        String tableNameWrap = StringUtil.isNotBlank(schema)
141                ? wrap(getRealSchema(schema)) + "." + wrap(getRealTable(tableName))
142                : wrap(getRealTable(tableName));
143        String questionStrings = buildQuestion(attrs.size(), true);
144
145        for (int i = 0; i < rows.size(); i++) {
146            sql.append(" INTO ").append(tableNameWrap);
147            sql.append(" (").append(fields).append(")");
148            sql.append(" VALUES ").append(questionStrings);
149        }
150
151        return sql.append(" SELECT 1 FROM DUAL").toString();
152    }
153}