001package gudusoft.gsqlparser.stmt.oceanbase;
002
003import gudusoft.gsqlparser.EDbVendor;
004import gudusoft.gsqlparser.ESqlStatementType;
005import gudusoft.gsqlparser.TCustomSqlStatement;
006import gudusoft.gsqlparser.TSourceToken;
007import gudusoft.gsqlparser.TSourceTokenList;
008import gudusoft.gsqlparser.nodes.TConstant;
009import gudusoft.gsqlparser.nodes.TObjectName;
010import gudusoft.gsqlparser.nodes.TParseTreeVisitor;
011import gudusoft.gsqlparser.nodes.oceanbase.TOceanbaseShowTenantSqlNode;
012import gudusoft.gsqlparser.nodes.oceanbase.TOceanbaseShowTenantSqlNode.EShowType;
013
014/**
015 * OceanBase system-tenant {@code SHOW} statement covering the OB-only
016 * variants that are not understood by the inherited MySQL grammar:
017 * {@code SHOW TENANT [LIKE 'pattern']}, {@code SHOW CREATE TENANT name},
018 * and {@code SHOW RESOURCE POOL}.
019 *
020 * <p>Tagged {@link ESqlStatementType#sstoceanbase_show_tenant}. Use
021 * {@link #getShowType()} to distinguish between the variants.
022 *
023 * @since 4.0.1.4
024 */
025public class TShowTenantSqlStatement extends TCustomSqlStatement {
026
027    private EShowType showType;
028    private TObjectName tenantName;
029    private TConstant likePattern;
030
031    public TShowTenantSqlStatement(EDbVendor dbvendor) {
032        super(dbvendor);
033        this.sqlstatementtype = ESqlStatementType.sstoceanbase_show_tenant;
034    }
035
036    public EShowType getShowType() {
037        return showType;
038    }
039
040    public TObjectName getTenantName() {
041        return tenantName;
042    }
043
044    public TConstant getLikePattern() {
045        return likePattern;
046    }
047
048    @Override
049    public int doParseStatement(TCustomSqlStatement psql) {
050        if (rootNode == null) return -1;
051        super.doParseStatement(psql);
052        // Two production paths feed this statement class:
053        //
054        //   1. The OceanBase grammar rule `showOceanBaseTenantStmt`
055        //      (SHOW TENANT [LIKE ...] and SHOW RESOURCE POOL) creates a
056        //      first-class TOceanbaseShowTenantSqlNode with structured
057        //      fields.
058        //
059        //   2. SHOW CREATE TENANT cannot be expressed as a new grammar
060        //      alternative without breaking the existing SHOW CREATE
061        //      TABLE / VIEW / SCHEMA stub (they share the
062        //      `RW_SHOW RW_CREATE` 2-token prefix whose make_stmt magic
063        //      eats the remaining tokens). The splitter recognizes it
064        //      via addCmd("show", "create", "tenant") and routes the
065        //      statement through the inherited mysqlStubStmt rule,
066        //      producing a generic TStubStmtSqlNode root. When that
067        //      happens we reconstruct the show variant / tenant name
068        //      from the raw token stream.
069        if (rootNode instanceof TOceanbaseShowTenantSqlNode) {
070            TOceanbaseShowTenantSqlNode node = (TOceanbaseShowTenantSqlNode) rootNode;
071            this.showType = node.getShowType();
072            this.tenantName = node.getTenantName();
073            this.likePattern = node.getLikePattern();
074        } else {
075            // Stub-path fallback — only SHOW CREATE TENANT lands here.
076            populateFromSourceTokens();
077        }
078        return 0;
079    }
080
081    /**
082     * Walk the statement's {@code sourcetokenlist} to identify the SHOW
083     * variant and capture the tenant name. Used only when the grammar
084     * path produced a generic stub node (SHOW CREATE TENANT route).
085     */
086    private void populateFromSourceTokens() {
087        this.showType = EShowType.createTenant;
088        TSourceTokenList tokens = this.sourcetokenlist;
089        if (tokens == null) return;
090        // Find the TENANT keyword (by text — the lexer token code
091        // varies by dialect) and grab the first identifier following
092        // it. The splitter has already validated the prefix, so a
093        // missing tenant name is silently ignored.
094        for (int i = 0; i < tokens.size(); i++) {
095            TSourceToken t = tokens.get(i);
096            if (t == null || t.isnonsolidtoken()) continue;
097            if ("TENANT".equalsIgnoreCase(t.toString())) {
098                // Next solid token is the tenant name identifier.
099                for (int j = i + 1; j < tokens.size(); j++) {
100                    TSourceToken n = tokens.get(j);
101                    if (n == null || n.isnonsolidtoken()) continue;
102                    String text = n.toString();
103                    if (text != null && !";".equals(text)) {
104                        this.tenantName = new TObjectName();
105                        this.tenantName.setObjectToken(n);
106                        this.tenantName.setStartToken(n);
107                        this.tenantName.setEndToken(n);
108                    }
109                    break;
110                }
111                return;
112            }
113        }
114    }
115
116    @Override
117    public void accept(TParseTreeVisitor v) {
118        v.preVisit(this);
119        v.postVisit(this);
120    }
121
122    @Override
123    public void acceptChildren(TParseTreeVisitor v) {
124        v.preVisit(this);
125        v.postVisit(this);
126    }
127}