content: /*
   * Copyright (C) 2016-2023 ActionTech.
   * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher.
   */
  
  package com.actiontech.dble.plan.node;
  
  import com.actiontech.dble.DbleServer;
  import com.actiontech.dble.backend.mysql.nio.handler.builder.HintNestLoopHelper;
  import com.actiontech.dble.config.ServerConfig;
  import com.actiontech.dble.config.model.sharding.SchemaConfig;
  import com.actiontech.dble.config.model.sharding.table.BaseTableConfig;
  import com.actiontech.dble.config.model.sharding.table.GlobalTableConfig;
  import com.actiontech.dble.config.model.sharding.table.ShardingTableConfig;
  import com.actiontech.dble.config.model.sharding.table.SingleTableConfig;
  import com.actiontech.dble.meta.ColumnMeta;
  import com.actiontech.dble.meta.ProxyMetaManager;
  import com.actiontech.dble.meta.TableMeta;
  import com.actiontech.dble.plan.NamedField;
  import com.actiontech.dble.plan.common.item.Item;
  import com.actiontech.dble.plan.common.item.ItemBasicConstant;
  import com.actiontech.dble.plan.common.item.ItemField;
  import com.actiontech.dble.plan.util.ToStringUtil;
  import com.actiontech.dble.route.parser.druid.RouteTableConfigInfo;
  import com.alibaba.druid.sql.ast.SQLHint;
  
  import java.sql.SQLNonTransientException;
  import java.util.*;
  
  public class TableNode extends PlanNode {
  
      private String schema;
      private String tableName;
      private TableMeta tableMeta;
      private List<String> columns;
      private List<SQLHint> hintList;
      private int charsetIndex = 63;
      private HintNestLoopHelper hintNestLoopHelper;
  
      private TableNode() {
      }
  
      public void setHintNestLoopHelper(HintNestLoopHelper hintNestLoopHelper) {
          this.hintNestLoopHelper = hintNestLoopHelper;
      }
  
      public HintNestLoopHelper getHintNestLoopHelper() {
          return hintNestLoopHelper;
      }
  
      public TableNode(String schema, String viewName, List<String> columns) {
          if (schema == null || viewName == null)
              throw new RuntimeException("Schema name or Table name is null!");
          this.schema = schema;
          this.tableName = viewName;
          ServerConfig config = DbleServer.getInstance().getConfig();
          if (DbleServer.getInstance().getSystemVariables().isLowerCaseTableNames()) {
              this.schema = this.schema.toLowerCase();
              this.tableName = this.tableName.toLowerCase();
          }
          SchemaConfig schemaConfig = config.getSchemas().get(this.schema);
          if (schemaConfig == null) {
              throw new RuntimeException("schema " + this.schema + " doesn't exist!");
          }
  
          this.columns = columns;
          this.setNoshardNode(new HashSet<>(Collections.singletonList(schemaConfig.getDefaultSingleNode())));
          this.referedTableNodes.add(this);
          this.keepFieldSchema = true;
      }
  
      public TableNode(String catalog, String tableName, ProxyMetaManager metaManager, int charsetIndex) throws SQLNonTransientException {
          if (catalog == null || tableName == null)
              throw new RuntimeException("Table db or name is null error!");
          this.charsetIndex = charsetIndex;
          this.schema = catalog;
          this.tableName = tableName;
          ServerConfig config = DbleServer.getInstance().getConfig();
          if (DbleServer.getInstance().getSystemVariables().isLowerCaseTableNames()) {
              this.schema = this.schema.toLowerCase();
              this.tableName = this.tableName.toLowerCase();
          }
          SchemaConfig schemaConfig = config.getSchemas().get(this.schema);
          if (schemaConfig == null) {
              throw new RuntimeException("schema " + this.schema + " doesn't exist!");
          }
          this.tableMeta = metaManager.getSyncTableMeta(this.schema, this.tableName);
          BaseTableConfig tableConfig = schemaConfig.getTables().get(this.tableName);
          if (this.tableMeta == null) {
              String errorMsg = "table " + this.tableName + " doesn't exist!";
              if (tableConfig != null || schemaConfig.getDefaultShardingNodes() != null) {
                  errorMsg += "You should create it OR reload metadata";
              }
              throw new RuntimeException(errorMsg);
          }
          this.referedTableNodes.add(this);
          if (tableConfig == null) {
              this.setNoshardNode(new HashSet<>(Collections.singletonList(schemaConfig.getDefaultSingleNode())));
          } else {
              if (!(tableConfig instanceof GlobalTableConfig) && !(tableConfig instanceof SingleTableConfig)) {
                  this.unGlobalTableCount = 1;
              }
              this.setNoshardNode(new HashSet<>(tableConfig.getShardingNodes()));
  
          }
          this.keepFieldSchema = true;
      }
  
      public PlanNodeType type() {
          return PlanNodeType.TABLE;
      }
  
      /**
       * @return the tableName
       */
      public String getTableName() {
          return tableName;
      }
  
      /**
       * @param tableName the tableName to set
       */
      public void setTableName(String tableName) {
          this.tableName = tableName;
      }
  
      @Override
      protected void setUpInnerFields() {
          innerFields.clear();
          String tmpTable = alias == null ? tableName : alias;
          if (tableMeta != null) {
              for (ColumnMeta cm : tableMeta.getColumns()) {
                  NamedField tmpField = new NamedField(schema, tmpTable, cm.getName(), this);
                  tmpField.setCharsetIndex(charsetIndex);
                  innerFields.put(tmpField, tmpField);
              }
          } else {
              for (String col : columns) {
                  NamedField tmpField = new NamedField(schema, tmpTable, col, this);
                  tmpField.setCharsetIndex(charsetIndex);
                  innerFields.put(tmpField, tmpField);
              }
          }
  
      }
  
      @Override
      public RouteTableConfigInfo findFieldSourceFromIndex(int index) throws Exception {
          if (columnsSelected.get(index) instanceof ItemBasicConstant) {
              return new RouteTableConfigInfo(schema, null, null, columnsSelected.get(index));
          } else {
              BaseTableConfig info = DbleServer.getInstance().getConfig().getSchemas().get(schema).getTables().get(tableName);
              if (info instanceof ShardingTableConfig) {
                  ShardingTableConfig shardingTableConfig = (ShardingTableConfig) info;
                  if (shardingTableConfig.getShardingColumn().equalsIgnoreCase(columnsSelected.get(index).getItemName())) {
                      return new RouteTableConfigInfo(schema, DbleServer.getInstance().getConfig().
                              getSchemas().get(schema).getTables().get(tableName), alias, null);
                  }
              } else {
                  return null;
              }
          }
          return null;
      }
  
      @Override
      protected void dealStarColumn() {
          List<Item> newSelects = new ArrayList<>();
          for (Item sel : columnsSelected) {
              if (!sel.isWild())
                  newSelects.add(sel);
              else {
                  for (NamedField innerField : innerFields.keySet()) {
                      ItemField col = new ItemField(null, sel.getTableName(), innerField.getName(), charsetIndex);
                      newSelects.add(col);
                  }
              }
          }
          columnsSelected = newSelects;
      }
  
      public TableNode copy() {
          TableNode newTableNode = new TableNode();
          newTableNode.schema = this.schema;
          newTableNode.tableName = this.tableName;
          newTableNode.tableMeta = this.tableMeta;
          newTableNode.columns = this.columns;
          newTableNode.referedTableNodes.add(newTableNode);
          if (this.getNoshardNode() != null) {
              Set<String> noShardNode = new HashSet<>(this.getNoshardNode());
              newTableNode.setNoshardNode(noShardNode);
          }
  
          this.copySelfTo(newTableNode);
          newTableNode.setHintList(this.hintList);
          return newTableNode;
      }
  
      @Override
      public String getPureName() {
          return this.getTableName();
      }
  
      @Override
      public String getPureSchema() {
          return this.getSchema();
      }
  
      public String getSchema() {
          return this.schema;
      }
  
      @Override
      public int getHeight() {
          return 1;
      }
  
      @Override
      public String toString(int level) {
          StringBuilder sb = new StringBuilder();
          String tabTittle = ToStringUtil.getTab(level);
          String tabContent = ToStringUtil.getTab(level + 1);
          if (this.getAlias() != null) {
              ToStringUtil.appendln(sb, tabTittle + "Query from " + this.getTableName() + " as " + this.getAlias());
          } else {
              ToStringUtil.appendln(sb, tabTittle + "Query from " + this.getTableName());
          }
          ToStringUtil.appendln(sb, tabContent + "isDistinct: " + isDistinct());
          ToStringUtil.appendln(sb, tabContent + "columns: " + ToStringUtil.itemListString(columnsSelected));
          ToStringUtil.appendln(sb, tabContent + "where: " + ToStringUtil.itemString(whereFilter));
          ToStringUtil.appendln(sb, tabContent + "having: " + ToStringUtil.itemString(havingFilter));
          ToStringUtil.appendln(sb, tabContent + "groupBy: " + ToStringUtil.orderListString(groups));
          ToStringUtil.appendln(sb, tabContent + "orderBy: " + ToStringUtil.orderListString(orderBys));
          if (this.getLimitFrom() >= 0L && this.getLimitTo() > 0L) {
              ToStringUtil.appendln(sb, tabContent + "limitFrom: " + this.getLimitFrom());
              ToStringUtil.appendln(sb, tabContent + "limitTo: " + this.getLimitTo());
          }
          ToStringUtil.appendln(sb, tabContent + "sql: " + this.getSql());
          return sb.toString();
      }
  
      public List<SQLHint> getHintList() {
          return hintList;
      }
  
      public void setHintList(List<SQLHint> hintList) {
          this.hintList = hintList;
      }
  
      public int getCharsetIndex() {
          return charsetIndex;
      }
  
      public void setCharsetIndex(int charsetIndex) {
          this.charsetIndex = charsetIndex;
      }
  }
  