Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shard table name error when exec select sql in multi thread concurrent #1967

Closed
linyuliang opened this issue Mar 5, 2019 · 20 comments
Closed

Comments

@linyuliang
Copy link

linyuliang commented Mar 5, 2019

Which version of ShardingSphere did you use?

it happened in version3.1.0, I tested 3.00 && 3.1.0M1, it's ok

Which project did you use? Sharding-JDBC or Sharding-Proxy?

Sharding-JDBC

Expected behavior

for example my sql is:
select a from test1 where ymd =?
select b from test2 where ymd =?

expected
select a from test1_20190305 where ymd =20190305
select b from test2_20190303 where ymd =20190303

Actual behavior

select a from test2_20190305 where ymd =20190305 --table name error
select b from test1_20190303 where ymd =20190303 --table name error
select a from test1_20190305 where ymd =20190305
select b from test2_20190303 where ymd =20190303

Reason analyze (If you can)

I trace it in SQLParserEngine.java, i think it error in this line:
ParseTree parseTree = SQLParserFactory.newInstance(databaseType, sql).execute().getChild(0);

Steps to reproduce the behavior, such as: SQL to execute, sharding rule configuration, when exception occur etc.

just test it in junit test case

Example codes for reproduce this issue (such as a github link).

@Test
public void test6() {
    int i = 0;
    while (i < 100) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                List<Map<String, Object>> maps1 = appApOfflineEventMapper.getApOfflineReasonList("20190303");
                System.out.println(maps1);
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                List<Map> maps = appApOfflineEventHourStatsMapper.getApOfflineStats("20190228");
                System.out.println(maps);
            }

        }).start();
        ++i;
    }
}
@cherrylzhao
Copy link
Member

hi, linyuliang, thanks for your feedback.
please show me your sharing configuration, thanks.

@linyuliang
Copy link
Author

linyuliang commented Mar 5, 2019

I use sharding-jdbc for single database and sub-table。

my sharding config file is sharding.yml :

dataSources:
  shardingdb: !!org.apache.commons.dbcp.BasicDataSource
    driverClassName: org.mariadb.jdbc.Driver
    url: jdbc:mysql://Mysql:3306/test?useUnicode=true&characterEncoding=utf-8
    username: test
    password: test
    initialSize: 3
    minIdle: 3
    maxIdle: 20
    maxActive: 100
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1
    testWhileIdle: true
    testOnBorrow: true
    testOnReturn: false
    logAbandoned: true
    removeAbandoned: true
    removeAbandonedTimeout: 60

shardingRule:
  tables:

    #新增的mysql表分片配置
    app_ap_offline_event:
      tableStrategy:
        standard:
      #精确分片算法类名称,用于=和IN
           preciseAlgorithmClassName: com.test.sharding.DateStringStandardShardingAlgorithm
      #范围分片算法类名称,用于BETWEEN,可选
           rangeAlgorithmClassName: com.test.sharding.DateStringStandardShardingAlgorithm
           shardingColumn: ymd

    app_network_flow_minute:
      tableStrategy:
        standard:
          #精确分片算法类名称,用于=和IN
          preciseAlgorithmClassName: com.test.sharding.DateStringStandardShardingAlgorithm
          #范围分片算法类名称,用于BETWEEN,可选
          rangeAlgorithmClassName: com.test.sharding.DateStringStandardShardingAlgorithm
          shardingColumn: ymd

    app_rrm_radio_link_quality:
      tableStrategy:
        standard:
          #精确分片算法类名称,用于=和IN
          preciseAlgorithmClassName: com.test.sharding.DateStringStandardShardingAlgorithm
          #范围分片算法类名称,用于BETWEEN,可选
          rangeAlgorithmClassName: com.test.sharding.DateStringStandardShardingAlgorithm
          shardingColumn: ymd

    app_sta_working_info_day:
      tableStrategy:
        #        inline:
        #          shardingColumn: ymd
        #          algorithmExpression: app_sta_working_info_day_${ymd}
        standard:
          #精确分片算法类名称,用于=和IN
          preciseAlgorithmClassName: com.test.sharding.DateStringStandardShardingAlgorithm
          #范围分片算法类名称,用于BETWEEN,可选
          rangeAlgorithmClassName: com.test.sharding.DateStringStandardShardingAlgorithm
          shardingColumn: ymd
    app_ap_offline_event_hour_stats:
      tableStrategy:
        standard:
          #精确分片算法类名称,用于=和IN
          preciseAlgorithmClassName: com.test.sharding.DateStringStandardShardingAlgorithm
          #范围分片算法类名称,用于BETWEEN,可选
          rangeAlgorithmClassName: com.test.sharding.DateStringStandardShardingAlgorithm
          shardingColumn: ymd
    app_sta_flow_info:
      tableStrategy:
        standard:
          #精确分片算法类名称,用于=和IN
          preciseAlgorithmClassName: com.test.sharding.DateStringStandardShardingAlgorithm
          #范围分片算法类名称,用于BETWEEN,可选
          rangeAlgorithmClassName: com.test.sharding.DateStringStandardShardingAlgorithm
          shardingColumn: ymd
    app_ap_offline_summary:
      tableStrategy:
        standard:
          #精确分片算法类名称,用于=和IN
          preciseAlgorithmClassName: com.test.sharding.DateStringStandardShardingAlgorithm
          #范围分片算法类名称,用于BETWEEN,可选
          rangeAlgorithmClassName: com.test.sharding.DateStringStandardShardingAlgorithm
          shardingColumn: ymd

  bindingTables:

    #新增的mysql表需要规则绑定
    - app_ap_offline_event
    - app_network_flow_minute
    - app_rrm_radio_link_quality
    - app_sta_working_info_day
    - app_ap_offline_event_hour_stats
    - app_sta_flow_info
    - app_ap_offline_summary

My DateStringStandardShardingAlgorithm.java:

import com.google.common.collect.Range;
import io.shardingsphere.api.algorithm.sharding.PreciseShardingValue;
import io.shardingsphere.api.algorithm.sharding.RangeShardingValue;
import io.shardingsphere.api.algorithm.sharding.standard.PreciseShardingAlgorithm;
import io.shardingsphere.api.algorithm.sharding.standard.RangeShardingAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.ParseException;
import java.util.*;


public class DateStringStandardShardingAlgorithm extends BaseShardingFilter implements PreciseShardingAlgorithm<String>, RangeShardingAlgorithm<String> {
    private static final Logger logger = LoggerFactory.getLogger(DateStringStandardShardingAlgorithm.class);

    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<String> shardingValue) {
//*********************** shardingValue.getLogicTableName()  is error **************************
        return getTableName(shardingValue.getLogicTableName() , "_" + shardingValue.getValue());
    }

    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames,
                                         RangeShardingValue<String> shardingValue) {

        List<String> result = new ArrayList<>();
        Range<String> ranges = shardingValue.getValueRange();
        final String logicTableName = shardingValue.getLogicTableName();
        Date startTime;
        try {
            startTime = YYYYMMDD.parse(ranges.lowerEndpoint());
        } catch (ParseException e) {
            throw new RuntimeException("table" + logicTableName + " timeRange start parameter not yyyyMMdd format error:" + ranges.lowerEndpoint());
        }
        Date endTime;
        try {
            endTime = YYYYMMDD.parse(ranges.upperEndpoint());
        } catch (ParseException e) {
            throw new RuntimeException("table" + logicTableName + " timeRange end parameter not yyyyMMdd format error:" + ranges.lowerEndpoint());
        }
        // 此处应该返回 tablePrefix+201808 , tablePrefix+201809,tablePrefix+201810,
        Calendar cal = Calendar.getInstance();

        result = getTableNames(result, startTime, endTime, cal, logicTableName);

        return result;
    }
}

@cherrylzhao
Copy link
Member

hi, would you please make "sql.show: true", and paste the Log information.
I can't reproduce your issue on my local simulation environment, please see my test case:
YamlConfigurationExampleTest

@linyuliang
Copy link
Author

YamlConfigurationExampleTest.java:

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 io.shardingsphere.example.jdbc;

import io.shardingsphere.example.config.ExampleConfiguration;
import io.shardingsphere.example.jdbc.nodep.YamlConfigurationExample;
import io.shardingsphere.example.jdbc.nodep.config.ShardingTablesConfigurationPrecise;
import io.shardingsphere.example.repository.api.service.CommonService;
import io.shardingsphere.example.repository.jdbc.repository.JDBCOrderItemRepositoryImpl;
import io.shardingsphere.example.repository.jdbc.repository.JDBCOrderRepositoryImpl;
import io.shardingsphere.example.repository.jdbc.service.RawPojoService;
import io.shardingsphere.shardingjdbc.api.yaml.YamlShardingDataSourceFactory;
import org.junit.Before;
import org.junit.Test;

import javax.sql.DataSource;
import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;

public class YamlConfigurationExampleTest {
    
    private File yamlFile = new File(YamlConfigurationExample.class.getResource("/META-INF/sharding-tables.yaml").getFile());
    
    @Before
    public void setUp() throws SQLException, IOException {
        DataSource dataSource = YamlShardingDataSourceFactory.createDataSource(yamlFile);
        CommonService commonService = new RawPojoService(new JDBCOrderRepositoryImpl(dataSource), new JDBCOrderItemRepositoryImpl(dataSource));
        commonService.initEnvironment();
    }
    
    @Test
    public void assertTableShardingMultiThread() throws SQLException, InterruptedException, IOException {
        final DataSource dataSource = YamlShardingDataSourceFactory.createDataSource(yamlFile);
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        final CountDownLatch latch = new CountDownLatch(100);
        final AtomicLong orderId = new AtomicLong(0);
        for (int i = 0; i < 100; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        executeQuery(dataSource, 0L);
                        latch.countDown();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            });
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        executeQueryOrderItem(dataSource, 1L);
                        latch.countDown();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        latch.await();
    }
    
    private void executeQuery(final DataSource dataSource, final Long orderId) throws SQLException {
        String sql = "select * from t_order where order_id = ?";
        try (Connection connection = dataSource.getConnection();
             PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
            preparedStatement.setLong(1, orderId);
            preparedStatement.execute();
        }
    }

    private void executeQueryOrderItem(final DataSource dataSource, final Long orderId) throws SQLException {
        String sql = "select * from t_order_item where order_id = ?";
        try (Connection connection = dataSource.getConnection();
             PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
            preparedStatement.setLong(1, orderId);
            preparedStatement.execute();
        }
    }
}

PreciseModuloShardingTableAlgorithm.java

/*
 * Copyright 2016-2018 shardingsphere.io.
 * <p>
 * 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.
 * </p>
 */

package io.shardingsphere.example.algorithm;

import io.shardingsphere.api.algorithm.sharding.PreciseShardingValue;
import io.shardingsphere.api.algorithm.sharding.standard.PreciseShardingAlgorithm;

import java.util.Collection;

public class PreciseModuloShardingTableAlgorithm implements PreciseShardingAlgorithm<Long> {
    
    @Override
    public String doSharding(final Collection<String> tableNames, final PreciseShardingValue<Long> shardingValue) {
        System.out.println(shardingValue.getLogicTableName()+":"+shardingValue.getColumnName()+":"+shardingValue.getValue());
        for (String each : tableNames) {
            if (each.endsWith(shardingValue.getValue() % 2 + "")) {
                return each;
            }
        }
        throw new UnsupportedOperationException();
    }
}

Expected print:
t_order:order_id:0
t_order_item:order_id:1

Actual print:
t_order:order_id:0
t_order:order_id:1
t_order_item:order_id:0
t_order_item:order_id:1

@cherrylzhao
Copy link
Member

thanks for reply,let me check it lately

@cherrylzhao
Copy link
Member

hi, I have checked the result, actual print result was in different thread, routing in one thread is right.
you can use Logger instead of printf to see the result.
image

@linyuliang
Copy link
Author

From your screenshot, it's still wrong.
The select sqls are:
Select * from t_order where order_id=0
or
select * from t_order_item where order_id=1

but the 5th line it print:
t_order:order_id:1

@cherrylzhao
Copy link
Member

ok , I have got your point ,thanks

@cherrylzhao
Copy link
Member

it's ok in 4.0.0.M1, you can verify it using dev branch. I will trace the bug reason continually.

@cherrylzhao
Copy link
Member

hi, this is the bug in FromWhereExtractor for Antlr Parsing Engine. we have fixed it.

@linyuliang
Copy link
Author

would you post the patch file link please?I want to merge it in Version 3.1.0 .

@cherrylzhao
Copy link
Member

please reference this file AbstractFromWhereExtractor.java

the point is that make predicateSegmentExtractor property only instance once.

@linyuliang
Copy link
Author

thanks,it seems work. The modified file:


/*
 * Copyright 2016-2018 shardingsphere.io.
 * <p>
 * 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.
 * </p>
 */

package io.shardingsphere.core.parsing.antlr.extractor.impl;

import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import io.shardingsphere.core.parsing.antlr.extractor.OptionalSQLSegmentExtractor;
import io.shardingsphere.core.parsing.antlr.extractor.util.ExtractorUtils;
import io.shardingsphere.core.parsing.antlr.extractor.util.RuleName;
import io.shardingsphere.core.parsing.antlr.sql.segment.FromWhereSegment;
import io.shardingsphere.core.parsing.antlr.sql.segment.condition.OrConditionSegment;
import io.shardingsphere.core.parsing.antlr.sql.segment.table.TableJoinSegment;
import io.shardingsphere.core.parsing.antlr.sql.segment.table.TableSegment;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.TerminalNode;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * From clause extractor.
 *
 * @author duhongjun
 */
public final class FromWhereExtractor implements OptionalSQLSegmentExtractor {

    private final TableNameExtractor tableNameExtractor = new TableNameExtractor();

    private PredicateExtractor predicateSegmentExtractor = new PredicateExtractor(new HashMap<>());

    @Override
    public Optional<FromWhereSegment> extract(final ParserRuleContext ancestorNode) {
        return extract(ancestorNode, ancestorNode);
    }

    /**
     * Extract SQL segment from SQL AST.
     *
     * @param ancestorNode ancestor node of AST
     * @param rootNode root node of AST
     * @return SQL segment
     */
    public Optional<FromWhereSegment> extract(final ParserRuleContext ancestorNode, final ParserRuleContext rootNode) {
        Optional<ParserRuleContext> selectClauseNode = ExtractorUtils.findFirstChildNode(ancestorNode, RuleName.SELECT_CLAUSE);
        if (!selectClauseNode.isPresent()) {
            return Optional.absent();
        }
        Optional<ParserRuleContext> fromNode = ExtractorUtils.findFirstChildNodeNoneRecursive(selectClauseNode.get().getParent(), RuleName.FROM_CLAUSE);
        if (!fromNode.isPresent()) {
            return Optional.absent();
        }
        Collection<ParserRuleContext> tableReferenceNodes = ExtractorUtils.getAllDescendantNodes(fromNode.get(), RuleName.TABLE_REFERENCE);
        if (tableReferenceNodes.isEmpty()) {
            return Optional.absent();
        }
        FromWhereSegment result = new FromWhereSegment();
//        predicateSegmentExtractor = new PredicateExtractor(result.getTableAliases());
        Collection<ParserRuleContext> questionNodes = ExtractorUtils.getAllDescendantNodes(rootNode, RuleName.QUESTION);
        result.setParameterCount(questionNodes.size());
        Map<ParserRuleContext, Integer> questionNodeIndexMap = getPlaceholderAndNodeIndexMap(questionNodes);
        extractAndFillTableSegment(result, tableReferenceNodes, questionNodeIndexMap);
        extractAndFillWhere(result, questionNodeIndexMap, fromNode.get().getParent());
        return Optional.of(result);
    }

    private Map<ParserRuleContext, Integer> getPlaceholderAndNodeIndexMap(final Collection<ParserRuleContext> questionNodes) {
        Map<ParserRuleContext, Integer> result = new HashMap<>(questionNodes.size(), 1);
        int index = 0;
        for (ParserRuleContext each : questionNodes) {
            result.put(each, index++);
        }
        return result;
    }

    private void extractAndFillTableSegment(final FromWhereSegment fromWhereSegment,
                                            final Collection<ParserRuleContext> tableReferenceNodes, final Map<ParserRuleContext, Integer> questionNodeIndexMap) {
        for (ParserRuleContext each : tableReferenceNodes) {
            for (int i = 0; i < each.getChildCount(); i++) {
                if (each.getChild(i) instanceof TerminalNode) {
                    continue;
                }
                ParserRuleContext childNode = (ParserRuleContext) each.getChild(i);
                if (RuleName.TABLE_REFERENCES.getName().equals(childNode.getClass().getSimpleName())) {
                    Collection<ParserRuleContext> subTableReferenceNodes = ExtractorUtils.getAllDescendantNodes(childNode, RuleName.TABLE_REFERENCE);
                    if (!subTableReferenceNodes.isEmpty()) {
                        extractAndFillTableSegment(fromWhereSegment, subTableReferenceNodes, questionNodeIndexMap);
                    }
                    continue;
                }
                fillTable(fromWhereSegment, childNode, questionNodeIndexMap);
            }
        }
    }

    private void fillTable(final FromWhereSegment fromWhereSegment, final ParserRuleContext joinOrTableFactorNode, final Map<ParserRuleContext, Integer> questionNodeIndexMap) {
        if (!RuleName.JOIN_TABLE.getName().endsWith(joinOrTableFactorNode.getClass().getSimpleName())) {
            Optional<TableSegment> tableSegment = tableNameExtractor.extract(joinOrTableFactorNode);
            Preconditions.checkState(tableSegment.isPresent());
            fillTableResult(fromWhereSegment, tableSegment.get());
        }
        Optional<ParserRuleContext> joinConditionNode = ExtractorUtils.findFirstChildNode(joinOrTableFactorNode, RuleName.JOIN_CONDITION);
        if (!joinConditionNode.isPresent()) {
            return;
        }
        Optional<ParserRuleContext> tableFactorNode = ExtractorUtils.findFirstChildNode(joinOrTableFactorNode, RuleName.TABLE_FACTOR);
        Preconditions.checkState(tableFactorNode.isPresent());
        Optional<TableSegment> tableSegment = tableNameExtractor.extract(tableFactorNode.get());
        Preconditions.checkState(tableSegment.isPresent());
        TableJoinSegment tableJoinResult = new TableJoinSegment(tableSegment.get());
        Optional<OrConditionSegment> conditionResult = buildCondition(joinConditionNode.get(), questionNodeIndexMap);
        if (conditionResult.isPresent()) {
            tableJoinResult.getJoinConditions().getAndConditions().addAll(conditionResult.get().getAndConditions());
            fromWhereSegment.getConditions().getAndConditions().addAll(conditionResult.get().getAndConditions());
        }
        fillTableResult(fromWhereSegment, tableJoinResult);
    }

    private void fillTableResult(final FromWhereSegment fromWhereSegment, final TableSegment tableSegment) {
        String alias = tableSegment.getName();
        if (tableSegment.getAlias().isPresent()) {
            alias = tableSegment.getAlias().get();
        }
        fromWhereSegment.getTableAliases().put(alias, tableSegment.getName());
    }

    private void extractAndFillWhere(final FromWhereSegment fromWhereSegment, final Map<ParserRuleContext, Integer> questionNodeIndexMap, final ParserRuleContext ancestorNode) {
        Optional<ParserRuleContext> whereNode = ExtractorUtils.findFirstChildNodeNoneRecursive(ancestorNode, RuleName.WHERE_CLAUSE);
        if (!whereNode.isPresent()) {
            return;
        }
        Optional<OrConditionSegment> conditions = buildCondition((ParserRuleContext) whereNode.get().getChild(1), questionNodeIndexMap);
        if (conditions.isPresent()) {
            fromWhereSegment.getConditions().getAndConditions().addAll(conditions.get().getAndConditions());
        }
    }

    private Optional<OrConditionSegment> buildCondition(final ParserRuleContext node, final Map<ParserRuleContext, Integer> questionNodeIndexMap) {
        Optional<ParserRuleContext> exprNode = ExtractorUtils.findFirstChildNode(node, RuleName.EXPR);
        return exprNode.isPresent() ? predicateSegmentExtractor.extractCondition(questionNodeIndexMap, exprNode.get()) : Optional.<OrConditionSegment>absent();
    }
}
 

@cherrylzhao
Copy link
Member

@codefairy08 please make a check of above modified file

codefairy08 added a commit to codefairy08/incubator-shardingsphere that referenced this issue Mar 7, 2019
@codefairy08
Copy link
Contributor

@linyuliang thanks for your code, but your modification does not fix this bug, i commit code.

@linyuliang
Copy link
Author

linyuliang commented Mar 7, 2019

@codefairy08
Is your code based on version 3.1.0?
Is version 3.1.0 the latest release version?
I don't want to change too much code based on version 3.1.0. Based on your code and version 3.1.0, I merged code like this:

ExpressionExtractor.java:

/*
 * Copyright 2016-2018 shardingsphere.io.
 * <p>
 * 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.
 * </p>
 */

package io.shardingsphere.core.parsing.antlr.extractor.impl;

import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import io.shardingsphere.core.parsing.antlr.extractor.OptionalSQLSegmentExtractor;
import io.shardingsphere.core.parsing.antlr.extractor.impl.dql.SubqueryExtractor;
import io.shardingsphere.core.parsing.antlr.extractor.util.ExtractorUtils;
import io.shardingsphere.core.parsing.antlr.extractor.util.RuleName;
import io.shardingsphere.core.parsing.antlr.sql.segment.column.ColumnSegment;
import io.shardingsphere.core.parsing.antlr.sql.segment.expr.*;
import io.shardingsphere.core.parsing.lexer.token.Symbol;
import io.shardingsphere.core.util.SQLUtil;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;

import java.util.HashMap;

/**
 * Expression extractor.
 *
 * @author duhongjun
 */
public final class ExpressionExtractor implements OptionalSQLSegmentExtractor {

    @Override
    public Optional<? extends ExpressionSegment> extract(final ParserRuleContext expressionNode) {
        String firstChildText = expressionNode.getText();
        if (firstChildText.endsWith(Symbol.STAR.getLiterals())) {
            return Optional.of(getStarExpressionSegment(expressionNode, firstChildText));
        }
        Optional<ParserRuleContext> subqueryNode = ExtractorUtils.findFirstChildNode(expressionNode, RuleName.SUBQUERY);
        return subqueryNode.isPresent() ? new SubqueryExtractor().extract(subqueryNode.get()) : Optional.of(getExpressionWithAliasSegment(expressionNode));
    }

    private ExpressionSegment getStarExpressionSegment(final ParserRuleContext expressionNode, final String text) {
        StarExpressionSegment result = new StarExpressionSegment(expressionNode.getStart().getStartIndex());
        if (text.contains(Symbol.DOT.getLiterals())) {
            result.setOwner(SQLUtil.getExactlyValue(text.substring(0, text.indexOf(Symbol.DOT.getLiterals()))));
        }
        return result;
    }

    private ExpressionWithAliasSegment getExpressionWithAliasSegment(final ParserRuleContext expressionNode) {
        ExpressionWithAliasSegment result;
        Optional<ParserRuleContext> functionNode = ExtractorUtils.findFirstChildNode(expressionNode, RuleName.FUNCTION_CALL);
        if (functionNode.isPresent()) {
            result = getFunctionExpressionSegment(expressionNode, functionNode.get());
        } else if (RuleName.COLUMN_NAME.getName().equals(expressionNode.getChild(0).getClass().getSimpleName())) {
            result = getPropertyExpressionSegment(expressionNode);
        } else {
            result = new CommonExpressionSegment(expressionNode.getStart().getStartIndex(), expressionNode.getStop().getStopIndex());
        }
        Optional<String> alias = getAlias(expressionNode);
        if (alias.isPresent()) {
            result.setAlias(alias.get());
        }
        return result;
    }

    private ExpressionWithAliasSegment getFunctionExpressionSegment(final ParserRuleContext expressionNode, final ParserRuleContext functionNode) {
        String functionName = functionNode.getChild(0).getText();
        int startIndex = ((TerminalNode) functionNode.getChild(1)).getSymbol().getStartIndex();
        int distinctColumnNameStartPosition = ExtractorUtils.findFirstChildNode(expressionNode, RuleName.DISTINCT).isPresent() ? calculateDistinctColumnNamePosition(functionNode) : -1;
        return new FunctionExpressionSegment(functionName, functionNode.getStart().getStartIndex(), startIndex, functionNode.getStop().getStopIndex(), distinctColumnNameStartPosition);
    }

    private int calculateDistinctColumnNamePosition(final ParserRuleContext functionNode) {
        ParseTree distinctItemNode = functionNode.getChild(3);
        if (distinctItemNode instanceof TerminalNode) {
            return ((TerminalNode) distinctItemNode).getSymbol().getStartIndex();
        }
        if (distinctItemNode instanceof ParserRuleContext) {
            return ((ParserRuleContext) distinctItemNode).getStart().getStartIndex();
        }
        return -1;
    }

    private ExpressionWithAliasSegment getPropertyExpressionSegment(final ParserRuleContext expressionNode) {
        ParserRuleContext columnNode = (ParserRuleContext) expressionNode.getChild(0);
        Optional<ColumnSegment> columnSegment = new ColumnSegmentExtractor(new HashMap<String, String>()).extract(columnNode);
        Preconditions.checkState(columnSegment.isPresent());
        return new PropertyExpressionSegment(columnSegment.get().getOwner(), columnSegment.get().getName(), columnNode.getStart().getStartIndex(), columnNode.getStop().getStopIndex());
    }

    private Optional<String> getAlias(final ParserRuleContext expressionNode) {
        Optional<ParserRuleContext> aliasNode = ExtractorUtils.findFirstChildNode(expressionNode, RuleName.ALIAS);
        return aliasNode.isPresent() ? Optional.of(SQLUtil.getExactlyValue(aliasNode.get().getText())) : Optional.absent();
    }
}


FromWhereExtractor .java

/*
 * Copyright 2016-2018 shardingsphere.io.
 * <p>
 * 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.
 * </p>
 */

package io.shardingsphere.core.parsing.antlr.extractor.impl;

import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import io.shardingsphere.core.parsing.antlr.extractor.OptionalSQLSegmentExtractor;
import io.shardingsphere.core.parsing.antlr.extractor.util.ExtractorUtils;
import io.shardingsphere.core.parsing.antlr.extractor.util.RuleName;
import io.shardingsphere.core.parsing.antlr.sql.segment.FromWhereSegment;
import io.shardingsphere.core.parsing.antlr.sql.segment.condition.OrConditionSegment;
import io.shardingsphere.core.parsing.antlr.sql.segment.table.TableJoinSegment;
import io.shardingsphere.core.parsing.antlr.sql.segment.table.TableSegment;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.TerminalNode;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * From clause extractor.
 *
 * @author duhongjun
 */
public final class FromWhereExtractor implements OptionalSQLSegmentExtractor {

    private final TableNameExtractor tableNameExtractor = new TableNameExtractor();

    private PredicateExtractor predicateSegmentExtractor = new PredicateExtractor();

    @Override
    public Optional<FromWhereSegment> extract(final ParserRuleContext ancestorNode) {
        return extract(ancestorNode, ancestorNode);
    }

    /**
     * Extract SQL segment from SQL AST.
     *
     * @param ancestorNode ancestor node of AST
     * @param rootNode root node of AST
     * @return SQL segment
     */
    public Optional<FromWhereSegment> extract(final ParserRuleContext ancestorNode, final ParserRuleContext rootNode) {
        Optional<ParserRuleContext> selectClauseNode = ExtractorUtils.findFirstChildNode(ancestorNode, RuleName.SELECT_CLAUSE);
        if (!selectClauseNode.isPresent()) {
            return Optional.absent();
        }
        Optional<ParserRuleContext> fromNode = ExtractorUtils.findFirstChildNodeNoneRecursive(selectClauseNode.get().getParent(), RuleName.FROM_CLAUSE);
        if (!fromNode.isPresent()) {
            return Optional.absent();
        }
        Collection<ParserRuleContext> tableReferenceNodes = ExtractorUtils.getAllDescendantNodes(fromNode.get(), RuleName.TABLE_REFERENCE);
        if (tableReferenceNodes.isEmpty()) {
            return Optional.absent();
        }
        FromWhereSegment result = new FromWhereSegment();
        Collection<ParserRuleContext> questionNodes = ExtractorUtils.getAllDescendantNodes(rootNode, RuleName.QUESTION);
        result.setParameterCount(questionNodes.size());
        Map<ParserRuleContext, Integer> questionNodeIndexMap = getPlaceholderAndNodeIndexMap(questionNodes);
        extractAndFillTableSegment(result, tableReferenceNodes, questionNodeIndexMap);
        extractAndFillWhere(result, questionNodeIndexMap, fromNode.get().getParent());
        return Optional.of(result);
    }

    private Map<ParserRuleContext, Integer> getPlaceholderAndNodeIndexMap(final Collection<ParserRuleContext> questionNodes) {
        Map<ParserRuleContext, Integer> result = new HashMap<>(questionNodes.size(), 1);
        int index = 0;
        for (ParserRuleContext each : questionNodes) {
            result.put(each, index++);
        }
        return result;
    }

    private void extractAndFillTableSegment(final FromWhereSegment fromWhereSegment,
                                            final Collection<ParserRuleContext> tableReferenceNodes, final Map<ParserRuleContext, Integer> questionNodeIndexMap) {
        for (ParserRuleContext each : tableReferenceNodes) {
            for (int i = 0; i < each.getChildCount(); i++) {
                if (each.getChild(i) instanceof TerminalNode) {
                    continue;
                }
                ParserRuleContext childNode = (ParserRuleContext) each.getChild(i);
                if (RuleName.TABLE_REFERENCES.getName().equals(childNode.getClass().getSimpleName())) {
                    Collection<ParserRuleContext> subTableReferenceNodes = ExtractorUtils.getAllDescendantNodes(childNode, RuleName.TABLE_REFERENCE);
                    if (!subTableReferenceNodes.isEmpty()) {
                        extractAndFillTableSegment(fromWhereSegment, subTableReferenceNodes, questionNodeIndexMap);
                    }
                    continue;
                }
                fillTable(fromWhereSegment, childNode, questionNodeIndexMap);
            }
        }
    }

    private void fillTable(final FromWhereSegment fromWhereSegment, final ParserRuleContext joinOrTableFactorNode, final Map<ParserRuleContext, Integer> questionNodeIndexMap) {
        if (!RuleName.JOIN_TABLE.getName().endsWith(joinOrTableFactorNode.getClass().getSimpleName())) {
            Optional<TableSegment> tableSegment = tableNameExtractor.extract(joinOrTableFactorNode);
            Preconditions.checkState(tableSegment.isPresent());
            fillTableResult(fromWhereSegment, tableSegment.get());
        }
        Optional<ParserRuleContext> joinConditionNode = ExtractorUtils.findFirstChildNode(joinOrTableFactorNode, RuleName.JOIN_CONDITION);
        if (!joinConditionNode.isPresent()) {
            return;
        }
        Optional<ParserRuleContext> tableFactorNode = ExtractorUtils.findFirstChildNode(joinOrTableFactorNode, RuleName.TABLE_FACTOR);
        Preconditions.checkState(tableFactorNode.isPresent());
        Optional<TableSegment> tableSegment = tableNameExtractor.extract(tableFactorNode.get());
        Preconditions.checkState(tableSegment.isPresent());
        TableJoinSegment tableJoinResult = new TableJoinSegment(tableSegment.get());
        Optional<OrConditionSegment> conditionResult = buildCondition(fromWhereSegment.getTableAliases(), joinConditionNode.get(), questionNodeIndexMap);
        if (conditionResult.isPresent()) {
            tableJoinResult.getJoinConditions().getAndConditions().addAll(conditionResult.get().getAndConditions());
            fromWhereSegment.getConditions().getAndConditions().addAll(conditionResult.get().getAndConditions());
        }
        fillTableResult(fromWhereSegment, tableJoinResult);
    }

    private void fillTableResult(final FromWhereSegment fromWhereSegment, final TableSegment tableSegment) {
        String alias = tableSegment.getName();
        if (tableSegment.getAlias().isPresent()) {
            alias = tableSegment.getAlias().get();
        }
        fromWhereSegment.getTableAliases().put(alias, tableSegment.getName());
    }

    private void extractAndFillWhere(final FromWhereSegment fromWhereSegment, final Map<ParserRuleContext, Integer> questionNodeIndexMap, final ParserRuleContext ancestorNode) {
        Optional<ParserRuleContext> whereNode = ExtractorUtils.findFirstChildNodeNoneRecursive(ancestorNode, RuleName.WHERE_CLAUSE);
        if (!whereNode.isPresent()) {
            return;
        }
        Optional<OrConditionSegment> conditions = buildCondition(fromWhereSegment.getTableAliases(), (ParserRuleContext) whereNode.get().getChild(1), questionNodeIndexMap);
        if (conditions.isPresent()) {
            fromWhereSegment.getConditions().getAndConditions().addAll(conditions.get().getAndConditions());
        }
    }

    private Optional<OrConditionSegment> buildCondition(final Map<String, String> tableAlias, final ParserRuleContext node, final Map<ParserRuleContext, Integer> questionNodeIndexMap) {
        Optional<ParserRuleContext> exprNode = ExtractorUtils.findFirstChildNode(node, RuleName.EXPR);
        return exprNode.isPresent() ? predicateSegmentExtractor.extractCondition(tableAlias, questionNodeIndexMap, exprNode.get()) : Optional.absent();
    }
}

PredicateExtractor.java

/*
 * Copyright 2016-2018 shardingsphere.io.
 * <p>
 * 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.
 * </p>
 */

package io.shardingsphere.core.parsing.antlr.extractor.impl;

import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import io.shardingsphere.core.constant.ShardingOperator;
import io.shardingsphere.core.parsing.antlr.extractor.OptionalSQLSegmentExtractor;
import io.shardingsphere.core.parsing.antlr.extractor.util.ExtractorUtils;
import io.shardingsphere.core.parsing.antlr.extractor.util.LogicalOperator;
import io.shardingsphere.core.parsing.antlr.extractor.util.Paren;
import io.shardingsphere.core.parsing.antlr.extractor.util.RuleName;
import io.shardingsphere.core.parsing.antlr.sql.segment.column.ColumnSegment;
import io.shardingsphere.core.parsing.antlr.sql.segment.condition.AndConditionSegment;
import io.shardingsphere.core.parsing.antlr.sql.segment.condition.ConditionSegment;
import io.shardingsphere.core.parsing.antlr.sql.segment.condition.OrConditionSegment;
import io.shardingsphere.core.parsing.antlr.sql.segment.condition.PredicateSegment;
import io.shardingsphere.core.parsing.antlr.sql.segment.expr.*;
import io.shardingsphere.core.parsing.lexer.token.DefaultKeyword;
import io.shardingsphere.core.parsing.lexer.token.Symbol;
import io.shardingsphere.core.util.NumberUtil;
import lombok.RequiredArgsConstructor;
import org.antlr.v4.runtime.ParserRuleContext;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * Predicate extractor.
 *
 * @author duhongjun
 */
@RequiredArgsConstructor
public final class PredicateExtractor implements OptionalSQLSegmentExtractor {

    @Override
    public Optional<PredicateSegment> extract(final ParserRuleContext ancestorNode) {
        throw new RuntimeException();
    }

    /**
     * Extract SQL segment from SQL AST.
     *
     * @param questionNodeIndexMap question node map
     * @param exprNode             expression node of AST
     * @return or condition
     */
    public Optional<OrConditionSegment> extractCondition(final Map<String, String> tableAlias, final Map<ParserRuleContext, Integer> questionNodeIndexMap, final ParserRuleContext exprNode) {
        int index = -1;
        for (int i = 0; i < exprNode.getChildCount(); i++) {
            if (LogicalOperator.isLogicalOperator(exprNode.getChild(i).getText())) {
                index = i;
                break;
            }
        }
        if (index > 0) {
            Optional<OrConditionSegment> leftOrCondition = extractCondition(tableAlias, questionNodeIndexMap, (ParserRuleContext) exprNode.getChild(index - 1));
            Optional<OrConditionSegment> rightOrCondition = extractCondition(tableAlias, questionNodeIndexMap, (ParserRuleContext) exprNode.getChild(index + 1));
            if (leftOrCondition.isPresent() && rightOrCondition.isPresent()) {
                return Optional.of(mergeCondition(leftOrCondition.get(), rightOrCondition.get(), exprNode.getChild(index).getText()));
            }
            return leftOrCondition.isPresent() ? leftOrCondition : rightOrCondition;
        }
        return extractConditionForParen(tableAlias, questionNodeIndexMap, exprNode);
    }

    private OrConditionSegment mergeCondition(final OrConditionSegment leftOrCondition, final OrConditionSegment rightOrCondition, final String operator) {
        if (LogicalOperator.isOrOperator(operator)) {
            leftOrCondition.getAndConditions().addAll(rightOrCondition.getAndConditions());
            return leftOrCondition;
        }
        OrConditionSegment result = new OrConditionSegment();
        for (AndConditionSegment each : leftOrCondition.getAndConditions()) {
            for (AndConditionSegment eachRightOr : rightOrCondition.getAndConditions()) {
                AndConditionSegment tempList = new AndConditionSegment();
                tempList.getConditions().addAll(each.getConditions());
                tempList.getConditions().addAll(eachRightOr.getConditions());
                result.getAndConditions().add(tempList);
            }
        }
        return result;
    }

    private Optional<OrConditionSegment> extractConditionForParen(final Map<String, String> tableAlias, final Map<ParserRuleContext, Integer> questionNodeIndexMap, final ParserRuleContext exprNode) {
        int index = -1;
        for (int i = 0; i < exprNode.getChildCount(); i++) {
            if (Paren.isLeftParen(exprNode.getChild(i).getText())) {
                index = i;
                break;
            }
        }
        if (-1 != index) {
            Preconditions.checkState(Paren.match(exprNode.getChild(index).getText(), exprNode.getChild(index + 2).getText()), "Missing right paren.");
            if (RuleName.EXPR.getName().equals(exprNode.getChild(index + 1).getClass().getSimpleName())) {
                return extractCondition(tableAlias, questionNodeIndexMap, (ParserRuleContext) exprNode.getChild(index + 1));
            }
            return Optional.absent();
        }
        Optional<ConditionSegment> condition = buildCondition(tableAlias, questionNodeIndexMap, exprNode);
        if (!condition.isPresent()) {
            return Optional.absent();
        }
        OrConditionSegment result = new OrConditionSegment();
        AndConditionSegment newAndCondition = new AndConditionSegment();
        newAndCondition.getConditions().add(condition.get());
        result.getAndConditions().add(newAndCondition);
        return Optional.of(result);
    }

    private Optional<ConditionSegment> buildCondition(final Map<String, String> tableAlias, final Map<ParserRuleContext, Integer> questionNodeIndexMap, final ParserRuleContext exprNode) {
        Optional<ConditionSegment> result = buildEqualCondition(tableAlias, questionNodeIndexMap, exprNode);
        if (result.isPresent()) {
            return result;
        }
        return buildPredicateCondition(tableAlias, questionNodeIndexMap, exprNode);
    }

    private Optional<ConditionSegment> buildEqualCondition(final Map<String, String> tableAlias, final Map<ParserRuleContext, Integer> questionNodeIndexMap, final ParserRuleContext exprNode) {
        Optional<ParserRuleContext> comparisionNode = ExtractorUtils.findFirstChildNode(exprNode, RuleName.COMPARISON_OPERATOR);
        if (!comparisionNode.isPresent() || !isValidEqualCondition(comparisionNode.get())) {
            return Optional.absent();
        }
        Optional<ParserRuleContext> leftNode = ExtractorUtils.findFirstChildNode((ParserRuleContext) comparisionNode.get().parent.getChild(0), RuleName.COLUMN_NAME);
        Optional<ParserRuleContext> rightNode = ExtractorUtils.findFirstChildNode((ParserRuleContext) comparisionNode.get().parent.getChild(2), RuleName.COLUMN_NAME);
        if (!leftNode.isPresent() && !rightNode.isPresent()) {
            return Optional.absent();
        }
        if (leftNode.isPresent() && rightNode.isPresent()) {
            Optional<ColumnSegment> leftColumn = buildColumn(tableAlias, leftNode.get());
            Optional<ColumnSegment> rightColumn = buildColumn(tableAlias, rightNode.get());
            Preconditions.checkState(leftColumn.isPresent() && rightColumn.isPresent());
            return Optional.of(new ConditionSegment(leftColumn.get(), ShardingOperator.EQUAL, rightColumn.get()));
        }
        Optional<ColumnSegment> column = buildColumn(tableAlias, exprNode);
        Preconditions.checkState(column.isPresent());
        ParserRuleContext valueNode = leftNode.isPresent() ? (ParserRuleContext) comparisionNode.get().parent.getChild(2) : (ParserRuleContext) comparisionNode.get().parent.getChild(0);
        Optional<? extends ExpressionSegment> sqlExpression = buildExpression(questionNodeIndexMap, valueNode);
        return sqlExpression.isPresent()
                ? Optional.of(new ConditionSegment(column.get(), ShardingOperator.EQUAL, new EqualsValueExpressionSegment(sqlExpression.get()))) : Optional.absent();
    }

    private boolean isValidEqualCondition(final ParserRuleContext comparisionNode) {
        return Symbol.EQ.getLiterals().equalsIgnoreCase(comparisionNode.getText()) && 3 == comparisionNode.getParent().getChildCount();
    }

    private Optional<? extends ExpressionSegment> buildExpression(final Map<ParserRuleContext, Integer> questionNodeIndexMap, final ParserRuleContext valueNode) {
        Optional<? extends ExpressionSegment> sqlExpression = new ExpressionExtractor().extract(valueNode);
        if (!sqlExpression.isPresent() || !(sqlExpression.get() instanceof CommonExpressionSegment)) {
            return sqlExpression;
        }
        CommonExpressionSegment commonExpressionSegment = (CommonExpressionSegment) sqlExpression.get();
        Optional<ParserRuleContext> expressionNode = ExtractorUtils.findFirstChildNode(valueNode, RuleName.QUESTION);
        if (expressionNode.isPresent()) {
            Integer index = questionNodeIndexMap.get(expressionNode.get());
            commonExpressionSegment.setIndex(index);
        } else {
            Optional<ParserRuleContext> bitExprNode = ExtractorUtils.findFirstChildNode(valueNode, RuleName.BIT_EXPR);
            expressionNode = ExtractorUtils.findFirstChildNode(valueNode, RuleName.NUMBER);
            if (expressionNode.isPresent() && (!bitExprNode.isPresent() || 1 == bitExprNode.get().getChildCount())) {
                commonExpressionSegment.setValue(NumberUtil.getExactlyNumber(expressionNode.get().getText(), 10));
            }
        }
        return sqlExpression;
    }

    private Optional<ConditionSegment> buildPredicateCondition(final Map<String, String> tableAlias, final Map<ParserRuleContext, Integer> questionNodeIndexMap, final ParserRuleContext exprNode) {
        Optional<ParserRuleContext> predicateNode = ExtractorUtils.findFirstChildNode(exprNode, RuleName.PREDICATE);
        if (!predicateNode.isPresent()) {
            return Optional.absent();
        }
        if (1 != predicateNode.get().getParent().getChildCount()) {
            return Optional.absent();
        }
        if (5 == predicateNode.get().getChildCount() && DefaultKeyword.BETWEEN.name().equalsIgnoreCase(predicateNode.get().getChild(1).getText())) {
            Optional<ConditionSegment> result = buildBetweenCondition(tableAlias, questionNodeIndexMap, predicateNode.get());
            if (result.isPresent()) {
                return result;
            }
        }
        if (5 <= predicateNode.get().getChildCount() && DefaultKeyword.IN.name().equalsIgnoreCase(predicateNode.get().getChild(1).getText())) {
            Optional<ConditionSegment> result = buildInCondition(tableAlias, questionNodeIndexMap, predicateNode.get());
            if (result.isPresent()) {
                return result;
            }
        }
        return Optional.absent();
    }

    private Optional<ConditionSegment> buildBetweenCondition(final Map<String, String> tableAlias, final Map<ParserRuleContext, Integer> questionNodeIndexMap, final ParserRuleContext predicateNode) {
        Optional<ColumnSegment> column = buildColumn(tableAlias, (ParserRuleContext) predicateNode.getChild(0));
        if (!column.isPresent()) {
            return Optional.absent();
        }
        Optional<? extends ExpressionSegment> beginSQLExpression = buildExpression(questionNodeIndexMap, (ParserRuleContext) predicateNode.getChild(2));
        Optional<? extends ExpressionSegment> endSQLExpression = buildExpression(questionNodeIndexMap, (ParserRuleContext) predicateNode.getChild(4));
        if (beginSQLExpression.isPresent() && endSQLExpression.isPresent()) {
            return Optional.of(new ConditionSegment(column.get(), ShardingOperator.BETWEEN, new BetweenValueExpressionSegment(beginSQLExpression.get(), endSQLExpression.get())));
        }
        return Optional.absent();
    }

    private Optional<ConditionSegment> buildInCondition(final Map<String, String> tableAlias, final Map<ParserRuleContext, Integer> questionNodeIndexMap, final ParserRuleContext predicateNode) {
        Optional<ColumnSegment> column = buildColumn(tableAlias, (ParserRuleContext) predicateNode.getChild(0));
        if (!column.isPresent()) {
            return Optional.absent();
        }
        List<ExpressionSegment> sqlExpressions = new LinkedList<>();
        for (int i = 3; i < predicateNode.getChildCount(); i++) {
            if (RuleName.SIMPLE_EXPR.getName().equals(predicateNode.getChild(i).getClass().getSimpleName())) {
                Optional<? extends ExpressionSegment> expression = buildExpression(questionNodeIndexMap, (ParserRuleContext) predicateNode.getChild(i));
                if (!expression.isPresent()) {
                    sqlExpressions.clear();
                    break;
                }
                sqlExpressions.add(expression.get());
            }
        }
        if (!sqlExpressions.isEmpty()) {
            InValueExpressionSegment inExpressionSegment = new InValueExpressionSegment();
            inExpressionSegment.getSqlExpressions().addAll(sqlExpressions);
            return Optional.of(new ConditionSegment(column.get(), ShardingOperator.IN, inExpressionSegment));
        }
        return Optional.absent();
    }

    private Optional<ColumnSegment> buildColumn(final Map<String, String> tableAlias, final ParserRuleContext parentNode) {
        return new ColumnSegmentExtractor(tableAlias).extract(parentNode);
    }
}

@cherrylzhao
Copy link
Member

we'd like to close this issue, any question?

@linyuliang
Copy link
Author

After modification, in my environment 3.1.0 Version, it works, can be closed

@jakara
Copy link

jakara commented May 16, 2019

After modification, in my environment 3.1.0 Version, it works, can be closed

hi @linyuliang , I would like to clone your modified 3.1.0 version, would you please make it public in your personal fork, or is it exactly the same as the code pasted above? Any help would be appreciated

@linyuliang
Copy link
Author

@jakara It exactly the same as the code pasted above, You can copy it directly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants