commit be73d93515f41de7263d2803f28aa0fcae6fe3a4 from: Sergey Bronnikov date: Fri Aug 02 18:52:10 2024 UTC tests: add box.execute test with grammar mutations commit - 95733c361de3a318d33765e17d336fbbcdce0d58 commit + be73d93515f41de7263d2803f28aa0fcae6fe3a4 blob - /dev/null blob + cc92932dc7dfda8779ef4019f49a44ec5ff11180 (mode 644) --- /dev/null +++ tests/tarantool_box_execute-mutator.lua @@ -0,0 +1,243 @@ +--[[ +-- TODO: +-- https://in2test.lsi.uniovi.es/sqlmutation/ +-- https://cosette.cs.washington.edu/ +-- https://www.cs.cmu.edu/~15811/papers/db.pdf +-- https://github.com/uwdb/Cosette +-- https://github.com/sqlancer/sqlancer +-- https://github.com/Practical-Formal-Methods/queryFuzz +-- https://github.com/PSU-Security-Universe/sqlright +-- +-- https://github.com/PingCAP-QE/go-sqlancer/tree/master/pkg +-- https://github.com/tarantool/sqlparser +-- +-- Corpus: +-- - https://github.com/PSU-Security-Universe/sqlright/tree/main/SQLite/docker/fuzz_root/inputs +-- +-- Automatic Test Generation for Mutation Testing on Database Applications +-- +-- Sqlsmith +-- +-- An Experimental Case Study to Applying Mutation Analysis for SQL Queries +-- +-- SQLMutation - Generation of mutants for testing SQL database queries +-- +-- Generating test data for killing SQL mutants: A constraint-based approach +-- +]] + +local lpeg = require("lpeg") +local locale = lpeg.locale +local P = lpeg.P +--local R = lpeg.R +--local S = lpeg.S +local V = lpeg.V +local C = lpeg.C +--local Cb = lpeg.Cb +--local Cc = lpeg.Cc +--local Cf = lpeg.Cf +--local Cg = lpeg.Cg +--local Cp = lpeg.Cp +--local Cs = lpeg.Cs +--local Ct = lpeg.Ct +--local Cmt = lpeg.Cmt + +--- +-- Returns a pattern which matches the literal string caselessly. +-- +-- @param literal A literal string to match case-insensitively. +-- @return An LPeg pattern. +-- +local function caseless(literal) + local caseless = lpeg.Cf((lpeg.P(1) / function (a) + return lpeg.S(a:lower() .. a:upper()) + end)^1, function (a, b) + return a * b + end) + return assert(caseless:match(literal)) +end + +local K = caseless + +--- Simple printf-style function. +local function printf(...) + print(string.format(...)) +end + +--- +-- Adds hooks to a grammar to print debugging information. +-- +-- Debugging LPeg grammars can be difficult. Calling this function on your +-- grammmar will cause it to print ENTER and LEAVE statements for each rule, as +-- well as position and subject after each successful rule match. +-- +-- For convenience, the modified grammar is returned; a copy is not made +-- though, and the original grammar is modified as well. +-- +-- Credits: http://lua-users.org/lists/lua-l/2009-10/msg00774.html +-- +-- @param grammar The LPeg grammar to modify +-- @param printer A printf-style formatting printer function to use. +-- Default: stdnse.debug1 +-- @return The modified grammar. +-- +local function debug(grammar, printer) + printer = printer or printf + for k, p in pairs(grammar) do + local enter = lpeg.Cmt(lpeg.P(true), function(s, p, ...) + printer("ENTER %s", k) + return p + end) + local leave = lpeg.Cmt(lpeg.P(true), function(s, p, ...) + printer("LEAVE %s", k) + return p + end) * (lpeg.P("k") - lpeg.P "k") + grammar[k] = lpeg.Cmt(enter * p + leave, function(s, p, ...) + printer("---%s---", k) + printer("pos: %d, [%s]", p, s:sub(1, p-1)) + return p + end) + end + + return grammar +end + +local mysql = locale { + + V "sql_stmt", + + sql_stmt = V "space"^0 * ( + V "select_stmt" + + V "update_stmt") * + V "space"^0 * ( + P ";" + -1), + + select_stmt = K "SELECT" * + V "space"^0 * + V "select_expr_list" * + V "space"^0 * + V "from_clause"^-1, + + from_clause = K "FROM" * + V "space"^0 * + V "table_references" * + V "space"^0 * + V "where_clause"^-1, + + where_clause = K "WHERE" * + V "space"^0 * + V "where_condition", + + update_stmt = K "UPDATE" * + V "space"^0 * + P "t set id = 1", -- TODO + + select_expr_list = V "select_expr" * + V "space"^0 * + (P "," * + V "space"^0 * + V "select_expr")^0, + + select_expr = P "*" + ( + V "table_name" * + P "." * + P "*") + + V "column_item", -- TODO sql function + + -- TODO + table_references = P "table1", + + where_condition = V "expr", + + column_item = (V "expr" * + V "space"^0 * + K "AS" * + V "space"^0 * + V "column_alias" + + V "expr" * + V "space"^0 * + V "column_alias" + + V "expr"), + + -- See expr in http://www.sqlite.org/lang_select.html + -- See http://dev.mysql.com/doc/refman/5.5/en/expressions.html + expr = (V "atomic_expr" * (V "space"^0 * + V "binary_operator" * + V "space"^0 * + V "expr" + )^-1), + + atomic_expr = (V "literal_value" + + V "variable" + + V "column_expr" + + V "unary_operator" * + V "space"^0 * + V "expr"), + + column_expr = (V "schema_name" * + P "." * + V "table_name" * + P "." * + V "column_name" + + V "table_name" * + P "." * + V "column_name" + + V "column_name"), + + schema_name = V "name", + column_alias = V "name", + column_name = V "name", + table_name = V "name", + + binary_operator = (K "OR" + + P "||" + + K "XOR" + + K "AND" + + P "&&" + ) + V "comparison_operator", + + comparison_operator = (P "=" + + P ">=" + + P ">" + + P "<=" + + P "<" + + P "<>" + + P "!="), + + unary_operator = (K "NOT" + + P "!"), + + variable = V "name", + + literal_value = V "numeric_literal" + + V "string_literal" + + P "NULL" + + P "CURRENT_TIME" + + P "CURRENT_DATE" + + P "CURRENT_TIMESTAMP", -- see http://dev.mysql.com/doc/refman/5.5/en/literals.html + + -- not enough, see http://dev.mysql.com/doc/refman/5.5/en/number-literals.html + numeric_literal = V "digit"^1, + + string_literal = (P "_" * + V "charset_name" + + caseless "n")^-1 * V "real_string_literal", + + -- not enough, see http://dev.mysql.com/doc/refman/5.5/en/string-literals.html + real_string_literal = P '"' * (1 - P '"')^0 * P '"' + P "'" * (1 - P "'")^0 * P "'", + + charset_name = V "name", + + name = P "`"^-1 * ( V "alnum" + P "_" )^1 * P "`"^-1, +} + +local mysql_grammar = P(C(mysql)) + +local res = mysql_grammar:match("SELECT * FROM table1 WHERE a = 1") +if res then + print(res) +else + local mysql_grammar_debug = P(debug(mysql)) + res = mysql_grammar_debug:match("SELECT * FROM table1 WHERE a = 1") + print(res) +end