public static List<List<String>> dump(SetType type, SessionVariable sessionVar, PatternMatcher matcher) {
        List<List<String>> rows = Lists.newArrayList();
        // Hold the read lock when session dump, because this option need to access global variable.
        RLOCK.lock();
        try {
            for (Map.Entry<String, VarContext> entry : CTX_BY_VAR_NAME.entrySet()) {
                // Filter variable not match to the regex.
                String name = StringUtils.isBlank(entry.getValue().getVarAttr().show()) ? entry.getKey()
                        : entry.getValue().getVarAttr().show();

                if (matcher != null && !matcher.match(name)) {
                    continue;
                }
                VarContext ctx = entry.getValue();

                // For session variables, the flag is VariableMgr.SESSION | VariableMgr.INVISIBLE
                // For global variables, the flag is VariableMgr.GLOBAL | VariableMgr.INVISIBLE
                if ((ctx.getFlag() > VariableMgr.INVISIBLE) && sessionVar != null &&
                        !sessionVar.isEnableShowAllVariables()) {
                    continue;
                }

                List<String> row = Lists.newArrayList();
                if (type != SetType.GLOBAL && ctx.getObj() == DEFAULT_SESSION_VARIABLE) {
                    // In this condition, we may retrieve session variables for caller.
                    if (sessionVar != null) {
                        row.add(name);
                        String currentValue = getValue(sessionVar, ctx.getField());
                        row.add(currentValue);
                        if (type == SetType.VERBOSE) {
                            row.add(ctx.defaultValue);
                            row.add(ctx.defaultValue.equals(currentValue) ? "0" : "1");
                        }
                    } else {
                        LOG.error("sessionVar is null during dumping session variables.");
                        continue;
                    }
                } else {
                    row.add(name);
                    String currentValue = getValue(ctx.getObj(), ctx.getField());
                    row.add(currentValue);
                    if (type == SetType.VERBOSE) {
                        row.add(ctx.defaultValue);
                        row.add(ctx.defaultValue.equals(currentValue) ? "0" : "1");
                    }
                }

                if (row.get(0).equalsIgnoreCase(SessionVariable.SQL_MODE)) {
                    try {
                        row.set(1, SqlModeHelper.decode(Long.valueOf(row.get(1))));
                    } catch (DdlException e) {
                        row.set(1, "");
                        LOG.warn("Decode sql mode failed");
                    }
                }

                rows.add(row);
            }
        } finally {
            RLOCK.unlock();
        }

        // Sort all variables by variable name.
        rows.sort(Comparator.comparing(o -> o.get(0)));

        return rows;
    }
@Test
    public void testDumpInvisible() {
        SessionVariable sv = new SessionVariable();
        List<List<String>> vars = VariableMgr.dump(SetType.SESSION, sv, null);
        Assert.assertFalse(vars.toString().contains("enable_show_all_variables"));
        Assert.assertFalse(vars.toString().contains("cbo_use_correlated_join_estimate"));

        sv.setEnableShowAllVariables(true);
        vars = VariableMgr.dump(SetType.SESSION, sv, null);
        Assert.assertTrue(vars.toString().contains("cbo_use_correlated_join_estimate"));

        vars = VariableMgr.dump(SetType.SESSION, null, null);
        List<List<String>> vars1 = VariableMgr.dump(SetType.GLOBAL, null, null);
        Assert.assertTrue(vars.size() < vars1.size());

        List<List<String>> vars2 = VariableMgr.dump(SetType.SESSION, null, null);
        Assert.assertTrue(vars.size() == vars2.size());
    }