/// <summary>
    /// Updates the connection string with the data parsed from the parameter and opens a connection
    /// to the database.
    /// </summary>
    /// <param name="connectDbStatement"></param>
    private async Task ConnectToDatabaseAsync(string connectDbStatement, CancellationToken cancellationToken = default)
    {
        // CONNECT 'filespec'
        // [USER 'username']
        // [PASSWORD 'password']
        // [CACHE int]
        // [ROLE 'rolename']
        var parser = new SqlStringParser(connectDbStatement);

        parser.Tokens = StandardParseTokens;
        using (var enumerator = parser.Parse().GetEnumerator())
        {
            enumerator.MoveNext();
            if (enumerator.Current.Text.ToUpperInvariant() != "CONNECT")
            {
                throw new ArgumentException("Malformed isql CONNECT statement. Expected keyword CONNECT but something else was found.");
            }
            enumerator.MoveNext();
            _connectionString.Database = enumerator.Current.Text.Replace("'", string.Empty);
            while (enumerator.MoveNext())
            {
                switch (enumerator.Current.Text.ToUpperInvariant())
                {
                case "USER":
                    enumerator.MoveNext();
                    _connectionString.UserID = enumerator.Current.Text.Replace("'", string.Empty);
                    break;

                case "PASSWORD":
                    enumerator.MoveNext();
                    _connectionString.Password = enumerator.Current.Text.Replace("'", string.Empty);
                    break;

                case "CACHE":
                    enumerator.MoveNext();
                    break;

                case "ROLE":
                    enumerator.MoveNext();
                    _connectionString.Role = enumerator.Current.Text.Replace("'", string.Empty);
                    break;

                default:
                    throw new ArgumentException("Unexpected token '" + enumerator.Current.Text + "' on isql CONNECT statement.");
                }
            }
        }
        _requiresNewConnection = true;
        await ProvideConnectionAsync(cancellationToken).ConfigureAwait(false);
    }
    /// <summary>
    /// Parses the SQL code and loads the SQL statements into the StringCollection <see cref="Results"/>.
    /// </summary>
    /// <returns>The number of statements found.</returns>
    public int Parse()
    {
        _results.Clear();
        foreach (var statement in _parser.Parse())
        {
            if (IsSetTermStatement(statement.CleanText, out var newParserToken))
            {
                _parser.Tokens = new[] { newParserToken };
                continue;
            }

            if (statement.CleanText != string.Empty)
            {
                var type = GetStatementType(statement.CleanText);
                if (type != null)
                {
                    statement.SetStatementType((SqlStatementType)type);
                    _results.Add(statement);
                    continue;
                }
            }

            if (statement.Text.Trim() != string.Empty)
            {
                var unknownStatementEventArgs = new UnknownStatementEventArgs(statement);
                UnknownStatement?.Invoke(this, unknownStatementEventArgs);
                if (unknownStatementEventArgs.Handled && !unknownStatementEventArgs.Ignore)
                {
                    statement.SetStatementType(unknownStatementEventArgs.NewStatementType);
                    _results.Add(statement);
                    continue;
                }
                else if (!unknownStatementEventArgs.Handled && unknownStatementEventArgs.Ignore)
                {
                    continue;
                }
                else if (unknownStatementEventArgs.Handled && unknownStatementEventArgs.Ignore)
                {
                    throw new InvalidOperationException($"Both {nameof(UnknownStatementEventArgs.Handled)} and {nameof(UnknownStatementEventArgs.Ignore)} should not be set.");
                }
                else
                {
                    throw new ArgumentException($"The type of the SQL statement could not be determined. See also {nameof(UnknownStatement)} event.{Environment.NewLine}Statement: {statement.Text}.");
                }
            }
        }
        return(_results.Count);
    }
    /// <summary>
    /// Parses the isql statement SET NAMES and sets the character set to current connection string.
    /// </summary>
    /// <param name="setNamesStatement">The set names statement.</param>
    private void SetNames(string setNamesStatement)
    {
        // SET NAMES charset
        var parser = new SqlStringParser(setNamesStatement);

        parser.Tokens = StandardParseTokens;
        using (var enumerator = parser.Parse().GetEnumerator())
        {
            enumerator.MoveNext();
            if (enumerator.Current.Text.ToUpperInvariant() != "SET")
            {
                throw new ArgumentException("Malformed isql SET statement. Expected keyword SET but something else was found.");
            }
            enumerator.MoveNext();             // NAMES
            enumerator.MoveNext();
            _connectionString.Charset = enumerator.Current.Text;
        }
    }
    /// <summary>
    /// Parses the isql statement SET SQL DIALECT and sets the dialect set to current connection string.
    /// </summary>
    /// <param name="setSqlDialectStatement">The set sql dialect statement.</param>
    private void SetSqlDialect(string setSqlDialectStatement)
    {
        // SET SQL DIALECT dialect
        var parser = new SqlStringParser(setSqlDialectStatement);

        parser.Tokens = StandardParseTokens;
        using (var enumerator = parser.Parse().GetEnumerator())
        {
            enumerator.MoveNext();
            if (enumerator.Current.Text.ToUpperInvariant() != "SET")
            {
                throw new ArgumentException("Malformed isql SET statement. Expected keyword SET but something else was found.");
            }
            enumerator.MoveNext();             // SQL
            enumerator.MoveNext();             // DIALECT
            enumerator.MoveNext();
            int.TryParse(enumerator.Current.Text, out var dialect);
            _connectionString.Dialect = dialect;
        }
    }
    /// <summary>
    /// Parses the isql statement SET AUTODDL and sets the character set to current connection string.
    /// </summary>
    /// <param name="setAutoDdlStatement">The set names statement.</param>
    private void SetAutoDdl(string setAutoDdlStatement, ref bool autoCommit)
    {
        // SET AUTODDL [ON | OFF]
        var parser = new SqlStringParser(setAutoDdlStatement);

        parser.Tokens = StandardParseTokens;
        using (var enumerator = parser.Parse().GetEnumerator())
        {
            enumerator.MoveNext();
            if (enumerator.Current.Text.ToUpperInvariant() != "SET")
            {
                throw new ArgumentException("Malformed isql SET statement. Expected keyword SET but something else was found.");
            }
            enumerator.MoveNext();             // AUTO
            if (enumerator.MoveNext())
            {
                var onOff = enumerator.Current.Text.ToUpperInvariant();
                if (onOff == "ON")
                {
                    autoCommit = true;
                }
                else if (onOff == "OFF")
                {
                    autoCommit = false;
                }
                else
                {
                    throw new ArgumentException("Expected the ON or OFF but something else was found.");
                }
            }
            else
            {
                autoCommit = !autoCommit;
            }
        }
    }
    /// <summary>
    /// Parses the isql statement CREATE DATABASE and creates the database and opens a connection to the recently created database.
    /// </summary>
    /// <param name="createDatabaseStatement">The create database statement.</param>
    private async Task CreateDatabaseAsync(string createDatabaseStatement, CancellationToken cancellationToken = default)
    {
        // CREATE {DATABASE | SCHEMA} 'filespec'
        // [USER 'username' [PASSWORD 'password']]
        // [PAGE_SIZE [=] int]
        // [LENGTH [=] int [PAGE[S]]]
        // [DEFAULT CHARACTER SET charset]
        // [<secondary_file>];
        var pageSize = 0;
        var parser   = new SqlStringParser(createDatabaseStatement);

        parser.Tokens = StandardParseTokens;
        using (var enumerator = parser.Parse().GetEnumerator())
        {
            enumerator.MoveNext();
            if (enumerator.Current.Text.ToUpperInvariant() != "CREATE")
            {
                throw new ArgumentException("Malformed isql CREATE statement. Expected keyword CREATE but something else was found.");
            }
            enumerator.MoveNext();             // {DATABASE | SCHEMA}
            enumerator.MoveNext();
            _connectionString.Database = enumerator.Current.Text.Replace("'", string.Empty);
            while (enumerator.MoveNext())
            {
                switch (enumerator.Current.Text.ToUpperInvariant())
                {
                case "USER":
                    enumerator.MoveNext();
                    _connectionString.UserID = enumerator.Current.Text.Replace("'", string.Empty);
                    break;

                case "PASSWORD":
                    enumerator.MoveNext();
                    _connectionString.Password = enumerator.Current.Text.Replace("'", string.Empty);
                    break;

                case "PAGE_SIZE":
                    enumerator.MoveNext();
                    if (enumerator.Current.Text == "=")
                    {
                        enumerator.MoveNext();
                    }
                    int.TryParse(enumerator.Current.Text, out pageSize);
                    break;

                case "DEFAULT":
                    enumerator.MoveNext();
                    if (enumerator.Current.Text.ToUpperInvariant() != "CHARACTER")
                    {
                        throw new ArgumentException("Expected the keyword CHARACTER but something else was found.");
                    }

                    enumerator.MoveNext();
                    if (enumerator.Current.Text.ToUpperInvariant() != "SET")
                    {
                        throw new ArgumentException("Expected the keyword SET but something else was found.");
                    }

                    enumerator.MoveNext();
                    _connectionString.Charset = enumerator.Current.Text;
                    break;
                }
            }
        }
        await FbConnection.CreateDatabaseAsync(_connectionString.ToString(), pageSize : pageSize, cancellationToken : cancellationToken).ConfigureAwait(false);

        _requiresNewConnection = true;
        await ProvideConnectionAsync(cancellationToken).ConfigureAwait(false);
    }