All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.oceanbase.tools.sqlparser.adapter.mysql.MySQLTableElementFactory Maven / Gradle / Ivy

There is a newer version: 1.3.1
Show newest version
/*
 * Copyright (c) 2023 OceanBase.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.oceanbase.tools.sqlparser.adapter.mysql;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.antlr.v4.runtime.ParserRuleContext;

import com.oceanbase.tools.sqlparser.adapter.StatementFactory;
import com.oceanbase.tools.sqlparser.obmysql.OBParser.Column_attributeContext;
import com.oceanbase.tools.sqlparser.obmysql.OBParser.Column_definitionContext;
import com.oceanbase.tools.sqlparser.obmysql.OBParser.Cur_timestamp_funcContext;
import com.oceanbase.tools.sqlparser.obmysql.OBParser.Generated_column_attributeContext;
import com.oceanbase.tools.sqlparser.obmysql.OBParser.Index_using_algorithmContext;
import com.oceanbase.tools.sqlparser.obmysql.OBParser.Now_or_signed_literalContext;
import com.oceanbase.tools.sqlparser.obmysql.OBParser.Opt_column_attribute_listContext;
import com.oceanbase.tools.sqlparser.obmysql.OBParser.Opt_generated_column_attribute_listContext;
import com.oceanbase.tools.sqlparser.obmysql.OBParser.Opt_index_optionsContext;
import com.oceanbase.tools.sqlparser.obmysql.OBParser.Opt_reference_option_listContext;
import com.oceanbase.tools.sqlparser.obmysql.OBParser.Out_of_line_constraintContext;
import com.oceanbase.tools.sqlparser.obmysql.OBParser.Out_of_line_indexContext;
import com.oceanbase.tools.sqlparser.obmysql.OBParser.Out_of_line_primary_indexContext;
import com.oceanbase.tools.sqlparser.obmysql.OBParser.Out_of_line_unique_indexContext;
import com.oceanbase.tools.sqlparser.obmysql.OBParser.Reference_actionContext;
import com.oceanbase.tools.sqlparser.obmysql.OBParser.Reference_optionContext;
import com.oceanbase.tools.sqlparser.obmysql.OBParser.References_clauseContext;
import com.oceanbase.tools.sqlparser.obmysql.OBParser.Signed_literalContext;
import com.oceanbase.tools.sqlparser.obmysql.OBParser.Sort_column_listContext;
import com.oceanbase.tools.sqlparser.obmysql.OBParser.Table_elementContext;
import com.oceanbase.tools.sqlparser.obmysql.OBParserBaseVisitor;
import com.oceanbase.tools.sqlparser.statement.Expression;
import com.oceanbase.tools.sqlparser.statement.Operator;
import com.oceanbase.tools.sqlparser.statement.common.DataType;
import com.oceanbase.tools.sqlparser.statement.common.RelationFactor;
import com.oceanbase.tools.sqlparser.statement.createtable.ColumnAttributes;
import com.oceanbase.tools.sqlparser.statement.createtable.ColumnDefinition;
import com.oceanbase.tools.sqlparser.statement.createtable.ColumnDefinition.Location;
import com.oceanbase.tools.sqlparser.statement.createtable.ConstraintState;
import com.oceanbase.tools.sqlparser.statement.createtable.ForeignReference;
import com.oceanbase.tools.sqlparser.statement.createtable.ForeignReference.MatchOption;
import com.oceanbase.tools.sqlparser.statement.createtable.ForeignReference.OnOption;
import com.oceanbase.tools.sqlparser.statement.createtable.GenerateOption;
import com.oceanbase.tools.sqlparser.statement.createtable.GenerateOption.Type;
import com.oceanbase.tools.sqlparser.statement.createtable.InLineCheckConstraint;
import com.oceanbase.tools.sqlparser.statement.createtable.InLineConstraint;
import com.oceanbase.tools.sqlparser.statement.createtable.IndexOptions;
import com.oceanbase.tools.sqlparser.statement.createtable.OutOfLineCheckConstraint;
import com.oceanbase.tools.sqlparser.statement.createtable.OutOfLineConstraint;
import com.oceanbase.tools.sqlparser.statement.createtable.OutOfLineForeignConstraint;
import com.oceanbase.tools.sqlparser.statement.createtable.OutOfLineIndex;
import com.oceanbase.tools.sqlparser.statement.createtable.SortColumn;
import com.oceanbase.tools.sqlparser.statement.createtable.TableElement;
import com.oceanbase.tools.sqlparser.statement.expression.ColumnReference;
import com.oceanbase.tools.sqlparser.statement.expression.CompoundExpression;
import com.oceanbase.tools.sqlparser.statement.expression.ConstExpression;
import com.oceanbase.tools.sqlparser.statement.expression.ExpressionParam;
import com.oceanbase.tools.sqlparser.statement.expression.FunctionCall;
import com.oceanbase.tools.sqlparser.statement.expression.FunctionParam;

import lombok.NonNull;

/**
 * {@link MySQLTableElementFactory}
 *
 * @author yh263208
 * @date 2023-05-23
 * @since ODC_release_4.2.0
 */
public class MySQLTableElementFactory extends OBParserBaseVisitor
        implements StatementFactory {

    private final ParserRuleContext parserRuleContext;

    public MySQLTableElementFactory(@NonNull Table_elementContext tableElementContext) {
        this.parserRuleContext = tableElementContext;
    }

    public MySQLTableElementFactory(@NonNull Column_definitionContext columnDefinitionContext) {
        this.parserRuleContext = columnDefinitionContext;
    }

    public MySQLTableElementFactory(@NonNull Out_of_line_indexContext context) {
        this.parserRuleContext = context;
    }

    public MySQLTableElementFactory(@NonNull Out_of_line_constraintContext context) {
        this.parserRuleContext = context;
    }

    @Override
    public TableElement generate() {
        return visit(this.parserRuleContext);
    }

    @Override
    public TableElement visitOut_of_line_constraint(Out_of_line_constraintContext ctx) {
        OutOfLineConstraint constraint;
        if (ctx.out_of_line_unique_index() != null) {
            constraint = new OutOfLineConstraint(ctx, (OutOfLineConstraint) visit(ctx.out_of_line_unique_index()));
        } else if (ctx.out_of_line_primary_index() != null) {
            constraint = new OutOfLineConstraint(ctx, (OutOfLineConstraint) visit(ctx.out_of_line_primary_index()));
        } else if (ctx.FOREIGN() != null) {
            List columns = ctx.column_name_list().column_name().stream()
                    .map(c -> new SortColumn(c, new ColumnReference(c, null, null, c.getText())))
                    .collect(Collectors.toList());
            constraint =
                    new OutOfLineForeignConstraint(ctx, null, columns, visitForeignReference(ctx.references_clause()));
            constraint.setIndexName(ctx.index_name() == null ? null : ctx.index_name().getText());
        } else {
            ConstraintState state = null;
            if (ctx.check_state() != null) {
                state = new ConstraintState(ctx.check_state());
                state.setEnforced(ctx.check_state().NOT() == null);
            }
            constraint = new OutOfLineCheckConstraint(ctx, state, new MySQLExpressionFactory(ctx.expr()).generate());
        }
        if (ctx.opt_constraint_name() != null && ctx.opt_constraint_name().constraint_name() != null) {
            constraint.setConstraintName(ctx.opt_constraint_name().constraint_name().getText());
        }
        return constraint;
    }

    @Override
    public TableElement visitOut_of_line_index(Out_of_line_indexContext ctx) {
        String name = ctx.index_name() == null ? null : ctx.index_name().getText();
        OutOfLineIndex index = new OutOfLineIndex(ctx, name, getSortColumns(ctx.sort_column_list()));
        index.setIndexOptions(getIndexOptions(ctx.index_using_algorithm(), ctx.opt_index_options()));
        index.setSpatial(ctx.SPATIAL() != null);
        index.setFullText(ctx.FULLTEXT() != null);
        if (ctx.partition_option() != null) {
            index.setPartition(new MySQLPartitionFactory(ctx.partition_option()).generate());
        } else if (ctx.auto_partition_option() != null) {
            index.setPartition(new MySQLPartitionFactory(ctx.auto_partition_option()).generate());
        }
        return index;
    }

    @Override
    public TableElement visitOut_of_line_primary_index(Out_of_line_primary_indexContext ctx) {
        List columns = ctx.column_name_list().column_name().stream()
                .map(c -> new SortColumn(c, new ColumnReference(c, null, null, c.getText())))
                .collect(Collectors.toList());
        IndexOptions indexOptions = getIndexOptions(ctx.index_using_algorithm(), ctx.opt_index_options());
        ConstraintState state = null;
        if (indexOptions != null) {
            state = ctx.opt_index_options() != null
                    ? new ConstraintState(ctx.opt_index_options())
                    : new ConstraintState(ctx.index_using_algorithm());
            state.setIndexOptions(indexOptions);
        }
        OutOfLineConstraint constraint = new OutOfLineConstraint(ctx, state, columns);
        constraint.setPrimaryKey(true);
        constraint.setIndexName(ctx.index_name() == null ? null : ctx.index_name().getText());
        return constraint;
    }

    @Override
    public TableElement visitOut_of_line_unique_index(Out_of_line_unique_indexContext ctx) {
        IndexOptions indexOptions = getIndexOptions(ctx.index_using_algorithm(), ctx.opt_index_options());
        ConstraintState state = null;
        if (indexOptions != null) {
            state = ctx.opt_index_options() != null
                    ? new ConstraintState(ctx.opt_index_options())
                    : new ConstraintState(ctx.index_using_algorithm());
            state.setIndexOptions(indexOptions);
        }
        if (ctx.partition_option() != null) {
            if (state == null) {
                state = new ConstraintState(ctx.partition_option());
            }
            state.setPartition(new MySQLPartitionFactory(ctx.partition_option()).generate());
        } else if (ctx.auto_partition_option() != null) {
            if (state == null) {
                state = new ConstraintState(ctx.auto_partition_option());
            }
            state.setPartition(new MySQLPartitionFactory(ctx.auto_partition_option()).generate());
        }
        OutOfLineConstraint constraint = new OutOfLineConstraint(ctx, state, getSortColumns(ctx.sort_column_list()));
        constraint.setUniqueKey(true);
        constraint.setIndexName(ctx.index_name() == null ? null : ctx.index_name().getText());
        return constraint;
    }

    @Override
    public TableElement visitColumn_definition(Column_definitionContext ctx) {
        DataType dataType = null;
        if (ctx.data_type() != null) {
            dataType = new MySQLDataTypeFactory(ctx.data_type()).generate();
        }
        StatementFactory factory = new MySQLColumnRefFactory(ctx.column_definition_ref());
        ColumnDefinition definition = new ColumnDefinition(ctx, factory.generate(), dataType);
        if (ctx.AS() != null) {
            // generated column
            StatementFactory exprFactory = new MySQLExpressionFactory(ctx.expr());
            GenerateOption generateOption = new GenerateOption(ctx, exprFactory.generate());
            if (ctx.opt_generated_option_list() != null) {
                generateOption.setGenerateOption(ctx.opt_generated_option_list().getText());
            }
            if (ctx.VIRTUAL() != null) {
                generateOption.setType(Type.VIRTUAL);
            } else if (ctx.STORED() != null) {
                generateOption.setType(Type.STORED);
            }
            definition.setGenerateOption(generateOption);
        }
        if (ctx.opt_column_attribute_list() != null) {
            ColumnAttributes attributes = visitColumnAttributeList(ctx.opt_column_attribute_list());
            definition.setColumnAttributes(attributes);
        } else if (ctx.opt_generated_column_attribute_list() != null) {
            ColumnAttributes attributes = visitGeneratedColumnAttributeList(ctx.opt_generated_column_attribute_list());
            definition.setColumnAttributes(attributes);
        }
        if (ctx.FIRST() != null) {
            definition.setLocation(new Location(ctx.FIRST().getText(), null));
        } else if (ctx.BEFORE() != null) {
            ColumnReference r = new ColumnReference(ctx.column_name(), null, null, ctx.column_name().getText());
            definition.setLocation(new Location(ctx.BEFORE().getText(), r));
        } else if (ctx.AFTER() != null) {
            ColumnReference r = new ColumnReference(ctx.column_name(), null, null, ctx.column_name().getText());
            definition.setLocation(new Location(ctx.AFTER().getText(), r));
        }
        return definition;
    }

    private ForeignReference visitForeignReference(References_clauseContext context) {
        RelationFactor factor = MySQLFromReferenceFactory.getRelationFactor(context.relation_factor());
        List columns = new ArrayList<>();
        if (context.column_name_list() != null) {
            columns = context.column_name_list().column_name().stream()
                    .map(c1 -> new ColumnReference(c1, null, null, c1.getText()))
                    .collect(Collectors.toList());
        }
        ForeignReference foreignReference = new ForeignReference(context,
                factor.getSchema(), factor.getRelation(), columns);
        foreignReference.setUserVariable(factor.getUserVariable());
        if (context.match_action() != null) {
            foreignReference.setMatchOption(MatchOption.valueOf(context.match_action().getText().toUpperCase()));
        }
        if (context.opt_reference_option_list() != null) {
            Map attributes = visitReferenceOptions(context.opt_reference_option_list());
            attributes.putAll(visitReferenceOption(context.reference_option()));
            foreignReference.setDeleteOption(attributes.get("DELETE"));
            foreignReference.setUpdateOption(attributes.get("UPDATE"));
        }
        return foreignReference;
    }

    private Map visitReferenceOptions(Opt_reference_option_listContext context) {
        Map attributes = new HashMap<>();
        if (context.opt_reference_option_list() != null) {
            attributes.putAll(visitReferenceOptions(context.opt_reference_option_list()));
        }
        if (context.reference_option() == null) {
            return attributes;
        }
        attributes.putAll(visitReferenceOption(context.reference_option()));
        return attributes;
    }

    private Map visitReferenceOption(Reference_optionContext context) {
        String key;
        Map attributes = new HashMap<>();
        if (context.DELETE() != null) {
            key = "DELETE";
        } else {
            key = "UPDATE";
        }
        Reference_actionContext ctx = context.reference_action();
        if (ctx.RESTRICT() != null) {
            attributes.put(key, OnOption.RESTRICT);
        } else if (ctx.CASCADE() != null) {
            attributes.put(key, OnOption.CASCADE);
        } else if (ctx.NULLX() != null) {
            attributes.put(key, OnOption.SET_NULL);
        } else if (ctx.ACTION() != null) {
            attributes.put(key, OnOption.NO_ACTION);
        } else {
            attributes.put(key, OnOption.SET_DEFAULT);
        }
        return attributes;
    }

    private IndexOptions getIndexOptions(Index_using_algorithmContext c1,
            Opt_index_optionsContext c2) {
        if (c1 == null && c2 == null) {
            return null;
        } else if (c2 != null && c1 == null) {
            return new MySQLIndexOptionsFactory(c2).generate();
        } else if (c2 == null) {
            IndexOptions indexOptions = new IndexOptions(c1);
            indexOptions.setUsingBtree(isUsingBTree(c1));
            indexOptions.setUsingHash(isUsingHash(c1));
            return indexOptions;
        }
        IndexOptions first = getIndexOptions(c1, null);
        IndexOptions second = getIndexOptions(null, c2);
        if (second.getUsingHash() == null) {
            second.setUsingHash(first.getUsingHash());
        }
        if (second.getUsingBtree() == null) {
            second.setUsingBtree(first.getUsingBtree());
        }
        return second;
    }

    private ColumnAttributes visitGeneratedColumnAttributeList(Opt_generated_column_attribute_listContext cxt) {
        ColumnAttributes attributes = new ColumnAttributes(cxt);
        if (cxt.opt_generated_column_attribute_list() != null) {
            attributes.merge(visitGeneratedColumnAttributeList(cxt.opt_generated_column_attribute_list()));
        }
        attributes.merge(visitGeneratedColumnAttribute(cxt.generated_column_attribute()));
        return attributes;
    }

    private ColumnAttributes visitGeneratedColumnAttribute(Generated_column_attributeContext ctx) {
        ColumnAttributes attributes = new ColumnAttributes(ctx);
        if (ctx.ID() != null) {
            attributes.setId(Integer.valueOf(ctx.INTNUM().getText()));
        } else if (ctx.COMMENT() != null) {
            attributes.setComment(ctx.STRING_VALUE().getText());
        } else if (ctx.SRID() != null) {
            attributes.setSrid(Integer.valueOf(ctx.INTNUM().getText()));
        } else {
            InLineConstraint attribute = new InLineConstraint(ctx, null, null);
            if (ctx.NULLX() != null) {
                attribute.setNullable(ctx.NOT() == null);
                attributes.setConstraints(Collections.singletonList(attribute));
            } else if (ctx.PRIMARY() != null) {
                attribute.setPrimaryKey(true);
                attributes.setConstraints(Collections.singletonList(attribute));
            } else if (ctx.UNIQUE() != null) {
                attribute.setUniqueKey(true);
                attributes.setConstraints(Collections.singletonList(attribute));
            } else if (ctx.KEY() != null) {
                attribute.setPrimaryKey(true);
                attributes.setConstraints(Collections.singletonList(attribute));
            } else if (ctx.CHECK() != null) {
                Expression expr = new MySQLExpressionFactory(ctx.expr()).generate();
                ConstraintState state = null;
                if (ctx.check_state() != null) {
                    state = new ConstraintState(ctx.check_state());
                    state.setEnforced(ctx.check_state().NOT() == null);
                }
                String constraintName = null;
                if (ctx.opt_constraint_name() != null && ctx.opt_constraint_name().constraint_name() != null) {
                    constraintName = ctx.opt_constraint_name().constraint_name().getText();
                }
                attributes.setConstraints(
                        Collections.singletonList(new InLineCheckConstraint(ctx, constraintName, state, expr)));
            }
        }
        return attributes;
    }

    private ColumnAttributes visitColumnAttributeList(Opt_column_attribute_listContext context) {
        ColumnAttributes attributes = new ColumnAttributes(context);
        if (context.opt_column_attribute_list() != null) {
            attributes.merge(visitColumnAttributeList(context.opt_column_attribute_list()));
        }
        attributes.merge(visitColumnAttribute(context.column_attribute()));
        return attributes;
    }

    private ColumnAttributes visitColumnAttribute(Column_attributeContext ctx) {
        ColumnAttributes attributes = new ColumnAttributes(ctx);
        InLineConstraint attribute = new InLineConstraint(ctx, null, null);
        if (ctx.NULLX() != null) {
            attribute.setNullable(ctx.not() == null);
            attributes.setConstraints(Collections.singletonList(attribute));
        } else if (ctx.PRIMARY() != null) {
            attribute.setPrimaryKey(true);
            attributes.setConstraints(Collections.singletonList(attribute));
        } else if (ctx.UNIQUE() != null) {
            attribute.setUniqueKey(true);
            attributes.setConstraints(Collections.singletonList(attribute));
        } else if (ctx.KEY() != null) {
            attribute.setPrimaryKey(true);
            attributes.setConstraints(Collections.singletonList(attribute));
        } else if (ctx.CHECK() != null) {
            ConstraintState state = null;
            if (ctx.check_state() != null) {
                state = new ConstraintState(ctx.check_state());
                state.setEnforced(ctx.check_state().NOT() == null);
            }
            String constraintName = null;
            if (ctx.opt_constraint_name() != null && ctx.opt_constraint_name().constraint_name() != null) {
                constraintName = ctx.opt_constraint_name().constraint_name().getText();
            }
            attribute = new InLineCheckConstraint(ctx, constraintName, state,
                    new MySQLExpressionFactory(ctx.expr()).generate());
            attributes.setConstraints(Collections.singletonList(attribute));
        } else if (ctx.DEFAULT() != null || ctx.ORIG_DEFAULT() != null) {
            Expression expr = visitNowOrSignedLiteral(ctx.now_or_signed_literal());
            if (ctx.DEFAULT() != null) {
                attributes.setDefaultValue(expr);
            } else {
                attributes.setOrigDefault(expr);
            }
        } else if (ctx.AUTO_INCREMENT() != null) {
            attributes.setAutoIncrement(true);
        } else if (ctx.COMMENT() != null) {
            attributes.setComment(ctx.STRING_VALUE().getText());
        } else if (ctx.ON() != null && ctx.UPDATE() != null) {
            attributes.setOnUpdate(visitCurTimestampFunc(ctx.cur_timestamp_func()));
        } else if (ctx.ID() != null) {
            attributes.setId(Integer.valueOf(ctx.INTNUM().getText()));
        } else if (ctx.SRID() != null) {
            attributes.setSrid(Integer.valueOf(ctx.INTNUM().getText()));
        } else if (ctx.collation_name() != null) {
            attributes.setCollation(ctx.collation_name().getText());
        }
        return attributes;
    }

    private Expression visitNowOrSignedLiteral(Now_or_signed_literalContext context) {
        if (context.cur_timestamp_func() != null) {
            return visitCurTimestampFunc(context.cur_timestamp_func());
        }
        return getSignedLiteral(context.signed_literal());
    }

    public static Expression getSignedLiteral(Signed_literalContext context) {
        if (context == null) {
            return null;
        }
        Expression constExpr;
        if (context.literal() != null) {
            constExpr = new MySQLExpressionFactory(context.literal()).generate();
        } else {
            constExpr = new ConstExpression(context.number_literal());
        }
        Operator operator = null;
        if (context.Minus() != null) {
            operator = Operator.SUB;
        } else if (context.Plus() != null) {
            operator = Operator.ADD;
        }
        return operator == null ? constExpr : new CompoundExpression(context, constExpr, null, operator);
    }

    private Expression visitCurTimestampFunc(Cur_timestamp_funcContext context) {
        String funcName = context.getChild(0).getText();
        List params = new ArrayList<>();
        if (context.INTNUM() != null) {
            params.add(new ExpressionParam(new ConstExpression(context.INTNUM())));
        }
        return new FunctionCall(context, funcName, params);
    }

    public static Boolean isUsingBTree(Index_using_algorithmContext ctx) {
        if (ctx == null) {
            return null;
        } else if (ctx.BTREE() == null) {
            return null;
        }
        return true;
    }

    public static Boolean isUsingHash(Index_using_algorithmContext ctx) {
        if (ctx == null) {
            return null;
        } else if (ctx.HASH() == null) {
            return null;
        }
        return true;
    }

    private List getSortColumns(@NonNull Sort_column_listContext ctx) {
        return ctx.sort_column_key().stream().map(c -> new MySQLSortColumnFactory(c).generate())
                .collect(Collectors.toList());
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy