457 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			457 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (c) 2016 MariaDB Corporation Ab
 | 
						|
 *
 | 
						|
 * Use of this software is governed by the Business Source License included
 | 
						|
 * in the LICENSE.TXT file and at www.mariadb.com/bsl11.
 | 
						|
 *
 | 
						|
 * Change Date: 2022-01-01
 | 
						|
 *
 | 
						|
 * On the date above, in accordance with the Business Source License, use
 | 
						|
 * of this software will be governed by version 2 or later of the General
 | 
						|
 * Public License.
 | 
						|
 */
 | 
						|
 | 
						|
#include "testreader.hh"
 | 
						|
#include <algorithm>
 | 
						|
#include <map>
 | 
						|
#include <iostream>
 | 
						|
 | 
						|
using std::istream;
 | 
						|
using std::string;
 | 
						|
using std::map;
 | 
						|
 | 
						|
namespace
 | 
						|
{
 | 
						|
 | 
						|
enum skip_action_t
 | 
						|
{
 | 
						|
    SKIP_NOTHING,       // Skip nothing.
 | 
						|
    SKIP_BLOCK,         // Skip until the end of next { ... }
 | 
						|
    SKIP_DELIMITER,     // Skip the new delimiter.
 | 
						|
    SKIP_LINE,          // Skip current line.
 | 
						|
    SKIP_NEXT_STATEMENT,// Skip statement starting on line following this line.
 | 
						|
    SKIP_STATEMENT,     // Skip statment starting on this line.
 | 
						|
    SKIP_TERMINATE,     // Cannot handle this, terminate.
 | 
						|
};
 | 
						|
 | 
						|
typedef std::map<std::string, skip_action_t> KeywordActionMapping;
 | 
						|
 | 
						|
static KeywordActionMapping mtl_keywords;
 | 
						|
static KeywordActionMapping plsql_keywords;
 | 
						|
 | 
						|
void init_keywords()
 | 
						|
{
 | 
						|
    struct Keyword
 | 
						|
    {
 | 
						|
        const char*   z_keyword;
 | 
						|
        skip_action_t action;
 | 
						|
    };
 | 
						|
 | 
						|
    static const Keyword MTL_KEYWORDS[] =
 | 
						|
    {
 | 
						|
        {"append_file",                SKIP_LINE          },
 | 
						|
        {"cat_file",                   SKIP_LINE          },
 | 
						|
        {"change_user",                SKIP_LINE          },
 | 
						|
        {"character_set",              SKIP_LINE          },
 | 
						|
        {"chmod",                      SKIP_LINE          },
 | 
						|
        {"connect",                    SKIP_LINE          },
 | 
						|
        {"connection",                 SKIP_LINE          },
 | 
						|
        {"copy_file",                  SKIP_LINE          },
 | 
						|
        {"dec",                        SKIP_LINE          },
 | 
						|
        {"delimiter",                  SKIP_DELIMITER     },
 | 
						|
        {"die",                        SKIP_LINE          },
 | 
						|
        {"diff_files",                 SKIP_LINE          },
 | 
						|
        {"dirty_close",                SKIP_LINE          },
 | 
						|
        {"disable_abort_on_error",     SKIP_LINE          },
 | 
						|
        {"disable_connect_log",        SKIP_LINE          },
 | 
						|
        {"disable_info",               SKIP_LINE          },
 | 
						|
        {"disable_metadata",           SKIP_LINE          },
 | 
						|
        {"disable_parsing",            SKIP_LINE          },
 | 
						|
        {"disable_ps_protocol",        SKIP_LINE          },
 | 
						|
        {"disable_query_log",          SKIP_LINE          },
 | 
						|
        {"disable_reconnect",          SKIP_LINE          },
 | 
						|
        {"disable_result_log",         SKIP_LINE          },
 | 
						|
        {"disable_rpl_parse",          SKIP_LINE          },
 | 
						|
        {"disable_session_track_info", SKIP_LINE          },
 | 
						|
        {"disable_warnings",           SKIP_LINE          },
 | 
						|
        {"disconnect",                 SKIP_LINE          },
 | 
						|
        {"echo",                       SKIP_LINE          },
 | 
						|
        {"enable_abort_on_error",      SKIP_LINE          },
 | 
						|
        {"enable_connect_log",         SKIP_LINE          },
 | 
						|
        {"enable_info",                SKIP_LINE          },
 | 
						|
        {"enable_metadata",            SKIP_LINE          },
 | 
						|
        {"enable_parsing",             SKIP_LINE          },
 | 
						|
        {"enable_ps_protocol",         SKIP_LINE          },
 | 
						|
        {"enable_query_log",           SKIP_LINE          },
 | 
						|
        {"enable_reconnect",           SKIP_LINE          },
 | 
						|
        {"enable_result_log",          SKIP_LINE          },
 | 
						|
        {"enable_rpl_parse",           SKIP_LINE          },
 | 
						|
        {"enable_session_track_info",  SKIP_LINE          },
 | 
						|
        {"enable_warnings",            SKIP_LINE          },
 | 
						|
        {"end_timer",                  SKIP_LINE          },
 | 
						|
        {"error",                      SKIP_NEXT_STATEMENT},
 | 
						|
        {"eval",                       SKIP_STATEMENT     },
 | 
						|
        {"exec",                       SKIP_LINE          },
 | 
						|
        {"file_exists",                SKIP_LINE          },
 | 
						|
        {"horizontal_results",         SKIP_LINE          },
 | 
						|
        {"inc",                        SKIP_LINE          },
 | 
						|
        {"let",                        SKIP_LINE          },
 | 
						|
        {"let",                        SKIP_LINE          },
 | 
						|
        {"list_files",                 SKIP_LINE          },
 | 
						|
        {"list_files_append_file",     SKIP_LINE          },
 | 
						|
        {"list_files_write_file",      SKIP_LINE          },
 | 
						|
        {"lowercase_result",           SKIP_LINE          },
 | 
						|
        {"mkdir",                      SKIP_LINE          },
 | 
						|
        {"move_file",                  SKIP_LINE          },
 | 
						|
        {"output",                     SKIP_LINE          },
 | 
						|
        {"perl",                       SKIP_TERMINATE     },
 | 
						|
        {"ping",                       SKIP_LINE          },
 | 
						|
        {"print",                      SKIP_LINE          },
 | 
						|
        {"query",                      SKIP_LINE          },
 | 
						|
        {"query_get_value",            SKIP_LINE          },
 | 
						|
        {"query_horizontal",           SKIP_LINE          },
 | 
						|
        {"query_vertical",             SKIP_LINE          },
 | 
						|
        {"real_sleep",                 SKIP_LINE          },
 | 
						|
        {"reap",                       SKIP_LINE          },
 | 
						|
        {"remove_file",                SKIP_LINE          },
 | 
						|
        {"remove_files_wildcard",      SKIP_LINE          },
 | 
						|
        {"replace_column",             SKIP_LINE          },
 | 
						|
        {"replace_regex",              SKIP_LINE          },
 | 
						|
        {"replace_result",             SKIP_LINE          },
 | 
						|
        {"require",                    SKIP_LINE          },
 | 
						|
        {"reset_connection",           SKIP_LINE          },
 | 
						|
        {"result",                     SKIP_LINE          },
 | 
						|
        {"result_format",              SKIP_LINE          },
 | 
						|
        {"rmdir",                      SKIP_LINE          },
 | 
						|
        {"same_master_pos",            SKIP_LINE          },
 | 
						|
        {"send",                       SKIP_LINE          },
 | 
						|
        {"send_eval",                  SKIP_LINE          },
 | 
						|
        {"send_quit",                  SKIP_LINE          },
 | 
						|
        {"send_shutdown",              SKIP_LINE          },
 | 
						|
        {"skip",                       SKIP_LINE          },
 | 
						|
        {"sleep",                      SKIP_LINE          },
 | 
						|
        {"sorted_result",              SKIP_LINE          },
 | 
						|
        {"source",                     SKIP_LINE          },
 | 
						|
        {"start_timer",                SKIP_LINE          },
 | 
						|
        {"sync_slave_with_master",     SKIP_LINE          },
 | 
						|
        {"sync_with_master",           SKIP_LINE          },
 | 
						|
        {"system",                     SKIP_LINE          },
 | 
						|
        {"vertical_results",           SKIP_LINE          },
 | 
						|
        {"write_file",                 SKIP_LINE          },
 | 
						|
    };
 | 
						|
 | 
						|
    const size_t N_MTL_KEYWORDS = sizeof(MTL_KEYWORDS) / sizeof(MTL_KEYWORDS[0]);
 | 
						|
 | 
						|
    for (size_t i = 0; i < N_MTL_KEYWORDS; ++i)
 | 
						|
    {
 | 
						|
        mtl_keywords[MTL_KEYWORDS[i].z_keyword] = MTL_KEYWORDS[i].action;
 | 
						|
    }
 | 
						|
 | 
						|
    static const Keyword PLSQL_KEYWORDS[] =
 | 
						|
    {
 | 
						|
        {"exit",  SKIP_LINE },
 | 
						|
        {"if",    SKIP_BLOCK},
 | 
						|
        {"while", SKIP_BLOCK},
 | 
						|
    };
 | 
						|
 | 
						|
    const size_t N_PLSQL_KEYWORDS = sizeof(PLSQL_KEYWORDS) / sizeof(PLSQL_KEYWORDS[0]);
 | 
						|
 | 
						|
    for (size_t i = 0; i < N_PLSQL_KEYWORDS; ++i)
 | 
						|
    {
 | 
						|
        plsql_keywords[PLSQL_KEYWORDS[i].z_keyword] = PLSQL_KEYWORDS[i].action;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
skip_action_t get_action(const string& keyword, const string& delimiter)
 | 
						|
{
 | 
						|
    skip_action_t action = SKIP_NOTHING;
 | 
						|
 | 
						|
    string key(keyword);
 | 
						|
    std::transform(key.begin(), key.end(), key.begin(), ::tolower);
 | 
						|
 | 
						|
    if (key == "delimiter")
 | 
						|
    {
 | 
						|
        // DELIMITER is directly understood by the parser so it needs to
 | 
						|
        // be handled explicitly.
 | 
						|
        action = SKIP_DELIMITER;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        KeywordActionMapping::iterator i = mtl_keywords.find(key);
 | 
						|
 | 
						|
        if (i != mtl_keywords.end())
 | 
						|
        {
 | 
						|
            action = i->second;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if ((action == SKIP_NOTHING) && (delimiter == ";"))
 | 
						|
    {
 | 
						|
        // Some mysqltest keywords, such as "while", "exit" and "if" are also
 | 
						|
        // PL/SQL keywords. We assume they can only be used in the former role,
 | 
						|
        // if the delimiter is ";".
 | 
						|
        KeywordActionMapping::iterator i = plsql_keywords.find(key);
 | 
						|
 | 
						|
        if (i != plsql_keywords.end())
 | 
						|
        {
 | 
						|
            action = i->second;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return action;
 | 
						|
}
 | 
						|
 | 
						|
inline void ltrim(std::string& s)
 | 
						|
{
 | 
						|
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
 | 
						|
}
 | 
						|
 | 
						|
inline void rtrim(std::string& s)
 | 
						|
{
 | 
						|
    s.erase(std::find_if(s.rbegin(),
 | 
						|
                         s.rend(),
 | 
						|
                         std::not1(std::ptr_fun<int, int>(std::isspace))).base(),
 | 
						|
            s.end());
 | 
						|
}
 | 
						|
 | 
						|
void trim(std::string& s)
 | 
						|
{
 | 
						|
    ltrim(s);
 | 
						|
    rtrim(s);
 | 
						|
}
 | 
						|
}
 | 
						|
 | 
						|
namespace maxscale
 | 
						|
{
 | 
						|
 | 
						|
TestReader::TestReader(istream& in,
 | 
						|
                       size_t line)
 | 
						|
    : m_in(in)
 | 
						|
    , m_line(line)
 | 
						|
    , m_delimiter(";")
 | 
						|
{
 | 
						|
    init();
 | 
						|
}
 | 
						|
 | 
						|
TestReader::result_t TestReader::get_statement(std::string& stmt)
 | 
						|
{
 | 
						|
    bool error = false;     // Whether an error has occurred.
 | 
						|
    bool found = false;     // Whether we have found a statement.
 | 
						|
    bool skip = false;      // Whether next statement should be skipped.
 | 
						|
 | 
						|
    stmt.clear();
 | 
						|
 | 
						|
    string line;
 | 
						|
 | 
						|
    while (!error && !found && std::getline(m_in, line))
 | 
						|
    {
 | 
						|
        trim(line);
 | 
						|
 | 
						|
        m_line++;
 | 
						|
 | 
						|
        if (!line.empty() && (line.at(0) != '#'))
 | 
						|
        {
 | 
						|
            // Ignore comment lines.
 | 
						|
            if ((line.substr(0, 3) == "-- ") || (line.substr(0, 1) == "#"))
 | 
						|
            {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
 | 
						|
            if (!skip)
 | 
						|
            {
 | 
						|
                if (line.substr(0, 2) == "--")
 | 
						|
                {
 | 
						|
                    line = line.substr(2);
 | 
						|
                    trim(line);
 | 
						|
                }
 | 
						|
 | 
						|
                string::iterator i = std::find_if(line.begin(),
 | 
						|
                                                  line.end(),
 | 
						|
                                                  std::ptr_fun<int, int>(std::isspace));
 | 
						|
                string keyword = line.substr(0, i - line.begin());
 | 
						|
 | 
						|
                skip_action_t action = get_action(keyword, m_delimiter);
 | 
						|
 | 
						|
                switch (action)
 | 
						|
                {
 | 
						|
                case SKIP_NOTHING:
 | 
						|
                    break;
 | 
						|
 | 
						|
                case SKIP_BLOCK:
 | 
						|
                    skip_block();
 | 
						|
                    continue;
 | 
						|
 | 
						|
                case SKIP_DELIMITER:
 | 
						|
                    line = line.substr(i - line.begin());
 | 
						|
                    trim(line);
 | 
						|
                    if (line.length() > 0)
 | 
						|
                    {
 | 
						|
                        if (line.length() >= m_delimiter.length())
 | 
						|
                        {
 | 
						|
                            if (line.substr(line.length() - m_delimiter.length()) == m_delimiter)
 | 
						|
                            {
 | 
						|
                                m_delimiter = line.substr(0, line.length() - m_delimiter.length());
 | 
						|
                            }
 | 
						|
                            else
 | 
						|
                            {
 | 
						|
                                m_delimiter = line;
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                        else
 | 
						|
                        {
 | 
						|
                            m_delimiter = line;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    continue;
 | 
						|
 | 
						|
                case SKIP_LINE:
 | 
						|
                    continue;
 | 
						|
 | 
						|
                case SKIP_NEXT_STATEMENT:
 | 
						|
                    skip = true;
 | 
						|
                    continue;
 | 
						|
 | 
						|
                case SKIP_STATEMENT:
 | 
						|
                    skip = true;
 | 
						|
                    break;
 | 
						|
 | 
						|
                case SKIP_TERMINATE:
 | 
						|
                    MXS_ERROR("Cannot handle line %u: %s", (unsigned)m_line, line.c_str());
 | 
						|
                    error = true;
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            stmt += line;
 | 
						|
 | 
						|
            // Look for a ';'. If we are dealing with a one line test statment
 | 
						|
            // the delimiter will in practice be ';' and if it is a multi-line
 | 
						|
            // test statement then the test-script delimiter will be something
 | 
						|
            // else than ';' and ';' will be the delimiter used in the multi-line
 | 
						|
            // statement.
 | 
						|
            string::size_type i = line.find(";");
 | 
						|
 | 
						|
            if (i != string::npos)
 | 
						|
            {
 | 
						|
                // Is there a "-- " or "#" after the delimiter?
 | 
						|
                if ((line.find("-- ", i) != string::npos)
 | 
						|
                    || (line.find("#", i) != string::npos))
 | 
						|
                {
 | 
						|
                    // If so, add a newline. Otherwise the rest of the
 | 
						|
                    // statement would be included in the comment.
 | 
						|
                    stmt += "\n";
 | 
						|
                }
 | 
						|
 | 
						|
                // This is somewhat fragile as a ";", "#" or "-- " inside a
 | 
						|
                // string will trigger this behaviour...
 | 
						|
            }
 | 
						|
 | 
						|
            string c;
 | 
						|
 | 
						|
            if (line.length() >= m_delimiter.length())
 | 
						|
            {
 | 
						|
                c = line.substr(line.length() - m_delimiter.length());
 | 
						|
            }
 | 
						|
 | 
						|
            if (c == m_delimiter)
 | 
						|
            {
 | 
						|
                if (c != ";")
 | 
						|
                {
 | 
						|
                    // If the delimiter was something else but ';' we need to
 | 
						|
                    // remove that before giving the line to the classifiers.
 | 
						|
                    stmt.erase(stmt.length() - m_delimiter.length());
 | 
						|
                }
 | 
						|
 | 
						|
                if (!skip)
 | 
						|
                {
 | 
						|
                    found = true;
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    skip = false;
 | 
						|
                    stmt.clear();
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else if (!skip)
 | 
						|
            {
 | 
						|
                stmt += " ";
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else if (line.substr(0, 7) == "--error")
 | 
						|
        {
 | 
						|
            // Next statement is supposed to fail, no need to check.
 | 
						|
            skip = true;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    result_t result;
 | 
						|
 | 
						|
    if (error)
 | 
						|
    {
 | 
						|
        result = RESULT_ERROR;
 | 
						|
    }
 | 
						|
    else if (found)
 | 
						|
    {
 | 
						|
        result = RESULT_STMT;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        result = RESULT_EOF;
 | 
						|
    }
 | 
						|
 | 
						|
    return result;
 | 
						|
}
 | 
						|
 | 
						|
// static
 | 
						|
void TestReader::init()
 | 
						|
{
 | 
						|
    static bool inited = false;
 | 
						|
 | 
						|
    if (!inited)
 | 
						|
    {
 | 
						|
        inited = true;
 | 
						|
 | 
						|
        init_keywords();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void TestReader::skip_block()
 | 
						|
{
 | 
						|
    int c;
 | 
						|
 | 
						|
    // Find first '{'
 | 
						|
    while (m_in && ((c = m_in.get()) != '{'))
 | 
						|
    {
 | 
						|
        if (c == '\n')
 | 
						|
        {
 | 
						|
            ++m_line;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    int n = 1;
 | 
						|
 | 
						|
    while ((n > 0) && m_in)
 | 
						|
    {
 | 
						|
        c = m_in.get();
 | 
						|
 | 
						|
        switch (c)
 | 
						|
        {
 | 
						|
        case '{':
 | 
						|
            ++n;
 | 
						|
            break;
 | 
						|
 | 
						|
        case '}':
 | 
						|
            --n;
 | 
						|
            break;
 | 
						|
 | 
						|
        case '\n':
 | 
						|
            ++m_line;
 | 
						|
            break;
 | 
						|
 | 
						|
        default:
 | 
						|
            ;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
}
 |