CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-flywaydb--flyway-mysql

MySQL database type plugin for Flyway database migration engine providing specialized MySQL and MariaDB support for schema migrations.

Pending
Overview
Eval results
Files

sql-parsing.mddocs/

SQL Parsing

SQL parsing classes provide MySQL and MariaDB-specific SQL parsing capabilities, handling database-specific syntax, delimiters, comments, and statement types. They ensure proper parsing of MySQL/MariaDB SQL scripts during migrations.

Capabilities

MySQL Parser

MySQL-specific SQL parser that handles MySQL syntax, delimiters, comments, and stored procedures.

/**
 * MySQL-specific SQL parser
 * Handles MySQL SQL syntax, delimiters, comments, and statement parsing
 */
public class MySQLParser extends Parser {
    
    /**
     * Creates a new MySQL parser instance
     * @param configuration Flyway configuration
     * @param parsingContext Context for parsing operations
     */
    public MySQLParser(Configuration configuration, ParsingContext parsingContext);
    
    /**
     * Handles DELIMITER command processing
     * MySQL allows changing statement delimiters with DELIMITER command
     * @param reader Source reader for SQL content
     * @param context Parser context and state
     * @param pos Current position in source
     * @param line Current line number
     * @param col Current column number
     * @param keyword The "DELIMITER" keyword
     * @return Token representing the new delimiter
     * @throws IOException if reading fails
     */
    @Override
    protected Token handleKeyword(PeekingReader reader, ParserContext context, int pos, int line, int col, String keyword) throws IOException;
    
    /**
     * Returns the identifier quote character for MySQL
     * @return '`' (backtick) for MySQL identifiers
     */
    @Override
    protected char getIdentifierQuote();
    
    /**
     * Returns the alternative string literal quote character
     * @return '"' (double quote) for alternative string literals
     */
    @Override
    protected char getAlternativeStringLiteralQuote();
    
    /**
     * Determines if a character sequence is a single-line comment
     * Supports both -- and # comment styles
     * @param peek Character sequence to check
     * @param context Parser context
     * @param col Current column position
     * @return true if sequence is a single-line comment
     */
    @Override
    protected boolean isSingleLineComment(String peek, ParserContext context, int col);
    
    /**
     * Handles string literal parsing with MySQL-specific escaping
     * @param reader Source reader
     * @param context Parser context
     * @param pos Current position
     * @param line Current line number
     * @param col Current column number
     * @return String literal token
     * @throws IOException if reading fails
     */
    @Override
    protected Token handleStringLiteral(PeekingReader reader, ParserContext context, int pos, int line, int col) throws IOException;
    
    /**
     * Handles alternative string literal parsing (double quotes)
     * @param reader Source reader
     * @param context Parser context
     * @param pos Current position
     * @param line Current line number
     * @param col Current column number
     * @return String literal token
     * @throws IOException if reading fails
     */
    @Override
    protected Token handleAlternativeStringLiteral(PeekingReader reader, ParserContext context, int pos, int line, int col) throws IOException;
    
    /**
     * Handles MySQL comment directives (/*! ... */)
     * @param reader Source reader
     * @param context Parser context
     * @param pos Current position
     * @param line Current line number
     * @param col Current column number
     * @return Comment directive token
     * @throws IOException if reading fails
     */
    @Override
    protected Token handleCommentDirective(PeekingReader reader, ParserContext context, int pos, int line, int col) throws IOException;
    
    /**
     * Determines if text is a MySQL comment directive
     * MySQL comment directives start with /*! and contain version-specific SQL
     * @param text Text to check
     * @return true if text is a comment directive
     */
    @Override
    protected boolean isCommentDirective(String text);
    
    /**
     * Detects MySQL statement types including stored procedures
     * @param simplifiedStatement Simplified statement text
     * @param context Parser context
     * @param reader Source reader for additional parsing
     * @return Statement type classification
     */
    @Override
    protected StatementType detectStatementType(String simplifiedStatement, ParserContext context, PeekingReader reader);
    
    /**
     * Determines if block depth should be adjusted for MySQL syntax
     * @param context Parser context
     * @param tokens Current token list
     * @param token Current token being processed
     * @return true if block depth adjustment needed
     */
    @Override
    protected boolean shouldAdjustBlockDepth(ParserContext context, List<Token> tokens, Token token);
    
    /**
     * Adjusts block depth for MySQL control structures
     * @param context Parser context
     * @param tokens Current token list
     * @param keyword Current keyword token
     * @param reader Source reader for lookahead
     */
    @Override
    protected void adjustBlockDepth(ParserContext context, List<Token> tokens, Token keyword, PeekingReader reader);
    
    /**
     * Resets delimiter state (MySQL preserves delimiters across statements)
     * @param context Parser context
     */
    @Override
    protected void resetDelimiter(ParserContext context);
}

Constants:

// Comment and parsing constants
private static final char ALTERNATIVE_SINGLE_LINE_COMMENT = '#';
private static final Pattern STORED_PROGRAM_REGEX = Pattern.compile(
    "^CREATE\\s(((DEFINER\\s(\\w+\\s)?@\\s(\\w+\\s)?)?(PROCEDURE|FUNCTION|EVENT))|TRIGGER)", 
    Pattern.CASE_INSENSITIVE);
private static final StatementType STORED_PROGRAM_STATEMENT = new StatementType();
}

Usage Examples:

// Parser creation via DatabaseType
MySQLDatabaseType dbType = new MySQLDatabaseType();
MySQLParser parser = (MySQLParser) dbType.createParser(configuration, resourceProvider, parsingContext);

// Parse MySQL script with DELIMITER changes
String sql = """
    DELIMITER $$
    CREATE PROCEDURE test_proc()
    BEGIN
        SELECT 'Hello World';
    END$$
    DELIMITER ;
    """;

List<SqlStatement> statements = parser.parse(sql);

MariaDB Parser

MariaDB-specific SQL parser extending MySQL parser with MariaDB-specific syntax support.

/**
 * MariaDB-specific SQL parser
 * Extends MySQLParser with MariaDB-specific syntax and features
 */
public class MariaDBParser extends MySQLParser {
    
    /**
     * Creates a new MariaDB parser instance
     * @param configuration Flyway configuration
     * @param parsingContext Context for parsing operations
     */
    public MariaDBParser(Configuration configuration, ParsingContext parsingContext);
    
    /**
     * Detects MariaDB-specific statement types
     * Includes support for BEGIN NOT ATOMIC and other MariaDB extensions
     * @param simplifiedStatement Simplified statement text
     * @param context Parser context
     * @param reader Source reader for additional parsing
     * @return Statement type classification
     */
    @Override
    protected StatementType detectStatementType(String simplifiedStatement, ParserContext context, PeekingReader reader);
    
    /**
     * Adjusts block depth for MariaDB-specific control structures
     * Handles BEGIN NOT ATOMIC and other MariaDB syntax
     * @param context Parser context
     * @param tokens Current token list
     * @param keyword Current keyword token
     * @param reader Source reader for lookahead
     */
    @Override
    protected void adjustBlockDepth(ParserContext context, List<Token> tokens, Token keyword, PeekingReader reader);
}

Usage Example:

// MariaDB parser creation
MariaDBDatabaseType dbType = new MariaDBDatabaseType();
MariaDBParser parser = (MariaDBParser) dbType.createParser(configuration, resourceProvider, parsingContext);

// Parse MariaDB-specific syntax
String mariadbSql = """
    BEGIN NOT ATOMIC
        DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN END;
        SELECT 'MariaDB specific syntax';
    END;
    """;

List<SqlStatement> statements = parser.parse(mariadbSql);

MySQL Syntax Features

Delimiter Handling

MySQL supports changing statement delimiters:

-- Default delimiter is semicolon
SELECT * FROM users;

-- Change delimiter for stored procedures
DELIMITER $$

CREATE PROCEDURE get_users()
BEGIN
    SELECT * FROM users;
END$$

-- Reset delimiter
DELIMITER ;

Parser Behavior:

  • Recognizes DELIMITER command
  • Updates delimiter context dynamically
  • Preserves delimiter changes across statements
  • Handles nested delimiter scenarios

Comment Support

Single-Line Comments

Two formats supported:

-- Standard SQL comment
SELECT * FROM users; -- End of line comment

# MySQL hash comment
SELECT * FROM users; # Alternative comment style

Note: Hash comments (#) are disabled when # is set as delimiter.

Multi-Line Comments

/* Standard multi-line comment */
SELECT * FROM users;

/*! MySQL-specific comment directive */
/*!50717 SELECT * FROM users */; -- Only execute on MySQL 5.7.17+

String Literals

Single Quotes (Standard)

SELECT 'Hello World';
SELECT 'Don\'t worry'; -- Escaped quote

Double Quotes (Alternative)

SELECT "Hello World";
SELECT "Don\"t worry"; -- Escaped quote

Parser Features:

  • Handles backslash escaping (\', \", \\)
  • Supports both quote styles
  • Proper escape sequence processing

Identifier Quoting

MySQL uses backticks for identifiers:

SELECT `column name with spaces` FROM `table name`;
CREATE TABLE `order` (`index` INT); -- Reserved word quoting

Statement Type Detection

Stored Programs

Detects MySQL stored procedures, functions, events, and triggers:

// Pattern for stored program detection
"^CREATE\\s(((DEFINER\\s(\\w+\\s)?@\\s(\\w+\\s)?)?(PROCEDURE|FUNCTION|EVENT))|TRIGGER)"

Supported Patterns:

CREATE PROCEDURE proc_name() ...
CREATE FUNCTION func_name() RETURNS INT ...
CREATE EVENT event_name ...
CREATE TRIGGER trigger_name ...
CREATE DEFINER=`user`@`host` PROCEDURE ...

Control Structures

Block depth tracking for MySQL control structures:

  • BEGIN...END blocks
  • IF...END IF statements
  • WHILE...END WHILE loops
  • REPEAT...UNTIL loops
  • CASE...END CASE statements

MariaDB-Specific Features

BEGIN NOT ATOMIC

MariaDB supports BEGIN NOT ATOMIC for compound statements:

BEGIN NOT ATOMIC
    DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN END;
    INSERT INTO logs VALUES ('Starting operation');
    -- More statements
END;

Parser Behavior:

  • Recognizes BEGIN NOT ATOMIC pattern
  • Handles block depth correctly
  • Supports MariaDB-specific error handling

Statement Detection

MariaDB parser extends MySQL detection with:

  • BEGIN NOT ATOMIC compound statements
  • MariaDB-specific syntax variations
  • Enhanced error handling constructs

Comment Directives

MySQL comment directives provide version-specific SQL:

Version-Specific Comments

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!50717 SELECT JSON_EXTRACT(data, '$.key') FROM table1 */;

Parser Processing

  • Extracts version number from directive
  • Preserves directive content
  • Enables conditional SQL execution based on version

Parser Configuration

Block Depth Tracking

Parsers maintain block depth for:

  • Stored procedure parsing
  • Control structure nesting
  • Transaction block handling
  • Error handling blocks

Context Management

Parser context tracks:

  • Current delimiter setting
  • Block nesting level
  • Statement boundaries
  • Token stream position

Error Handling

Parsing Errors

Common parsing scenarios:

  • Malformed DELIMITER commands
  • Unmatched quotes in string literals
  • Invalid comment directive syntax
  • Unterminated block structures

Recovery Strategies

Parsers implement recovery for:

  • Missing delimiter reset
  • Incomplete string literals
  • Nested comment issues
  • Block structure mismatches

Performance Considerations

Lookahead Optimization

Parsers use efficient lookahead for:

  • Keyword detection
  • Statement type classification
  • Block structure analysis
  • Comment boundary detection

Token Stream Processing

Optimized processing for:

  • Large SQL script files
  • Complex stored procedures
  • Deeply nested structures
  • Multiple delimiter changes

Install with Tessl CLI

npx tessl i tessl/maven-org-flywaydb--flyway-mysql

docs

connection-management.md

database-operations.md

database-types.md

index.md

schema-management.md

sql-parsing.md

utility-components.md

tile.json