package DBIx::OnlineDDL::Helper::SQLite;

our $AUTHORITY = 'cpan:GSG';
# ABSTRACT: Private OnlineDDL helper for SQLite-specific code
use version;
our $VERSION = 'v1.0.0'; # VERSION

use v5.10;
use Moo;

extends 'DBIx::OnlineDDL::Helper::Base';

use Types::Standard qw( InstanceOf );

use Sub::Util qw( set_subname );

use namespace::clean;  # don't export the above

#pod =encoding utf8
#pod
#pod =head1 DESCRIPTION
#pod
#pod This is a private helper module for any SQLite-specific code.  B<As a private module, any
#pod methods or attributes here are subject to change.>
#pod
#pod You should really be reading documentation for L<DBIx::OnlineDDL>.  Or, if you want to
#pod create a helper module for a different RDBMS, read the docs for
#pod L<DBIx::OnlineDDL::Helper::Base>.
#pod
#pod =cut

sub dbms_uses_global_fk_namespace { 0 }
sub child_fks_need_adjusting      { 0 }
sub null_safe_equals_op           { 'IS' }

sub current_catalog_schema {
    my $self = shift;

    my $databases = $self->dbh->selectall_hashref('PRAGMA database_list', 'seq');
    my $schema = $databases->{0}{name};  # probably 'main'
    return (undef, $schema);
}

sub insert_select_stmt {
    my ($self, $column_list_str) = @_;

    my $dbh = $self->dbh;

    my $id_name_quote = $dbh->quote_identifier( $self->copy_opts->{id_name} );

    my $orig_table_name_quote = $dbh->quote_identifier($self->table_name);
    my $new_table_name_quote  = $dbh->quote_identifier($self->new_table_name);

    # Use INSERT OR IGNORE to ignore dupe key errors
    return join("\n",
        "INSERT OR IGNORE INTO $new_table_name_quote",
        "    ($column_list_str)",
        "SELECT",
        "    $column_list_str",
        "FROM $orig_table_name_quote",
        "WHERE $id_name_quote BETWEEN ? AND ?",
    );
}

sub post_connection_stmts {
    my $self = shift;

    my $db_timeouts = $self->db_timeouts;
    my @stmts = (
        # See FK comment in MySQL module.  FKs in SQLite are a per-connection enabled
        # feature, so this is always a "session" command.
        'PRAGMA foreign_keys = OFF',

        # DB timeouts
        'PRAGMA busy_timeout = '.int($db_timeouts->{lock_file} * 1_000),  # busy_timeout uses ms
    );

    # SQLite version 3.25.0 fixes table renames to also rename references to the table,
    # ie: child FKs.  Since SQLite doesn't yet have an DDL statement for renaming the FKs
    # back to the old name, setting this PRAGMA variable is the only option.
    #
    # Also, while this change was introduced in 3.25.0, it seems to only manifest itself
    # when the driver reports version 3.26.0, possibly due to how their production
    # releases work.
    push @stmts, 'PRAGMA legacy_alter_table = ON' if $self->mmver >= 3.026;

    return @stmts;
}

sub is_error_retryable {
    my ($self, $error) = @_;

    # Disable /x flag to allow for whitespace within string, but turn it on for newlines
    # and comments.
    return $error =~ m<
        # Locks
        (?-x:database( table)? is locked)|

        # Connections
        (?-x:attempt to [\w\s]+ on inactive database handle)|

        # Queries
        (?-x:query aborted)|
        (?-x:interrupted)
    >xi;
}

sub create_table_sql {
    my ($self, $table_name) = @_;

    my $create_sql;
    $self->dbh_runner(run => set_subname '_create_table_sql', sub {
        ($create_sql) = $_->selectrow_array('SELECT sql FROM sqlite_master WHERE name = ?', undef, $table_name);
    });

    return $create_sql;
}

# Keep Base->rename_fks_in_table_sql (not used)

sub has_conflicting_triggers_on_table {
    my ($self, $table_name) = @_;

    return $self->dbh_runner(run => set_subname '_has_triggers_on_table', sub {
        $_->selectrow_array(
            'SELECT name FROM sqlite_master WHERE type = ? AND tbl_name = ?',
            undef, 'trigger', $table_name
        );
    });
}

sub find_new_trigger_identifier {
    my ($self, $trigger_name) = @_;

    return $self->find_new_identifier(
        $trigger_name => sub {
            $_[0]->selectrow_array(
                'SELECT name FROM sqlite_master WHERE type = ? AND name = ?',
                undef, 'trigger', $_[1]
            );
        },
    );
}

# Keep Base->modify_trigger_dml_stmts (nothing changed)

sub analyze_table {
    my ($self, $table_name) = @_;
    my $table_name_quote = $self->dbh->quote_identifier($table_name);
    $self->dbh_runner_do("ANALYZE $table_name_quote");
}

# Keep Base->swap_tables (has transactional DDL)

# Keep the other FK methods (not used)

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

DBIx::OnlineDDL::Helper::SQLite - Private OnlineDDL helper for SQLite-specific code

=head1 VERSION

version v1.0.0

=head1 DESCRIPTION

This is a private helper module for any SQLite-specific code.  B<As a private module, any
methods or attributes here are subject to change.>

You should really be reading documentation for L<DBIx::OnlineDDL>.  Or, if you want to
create a helper module for a different RDBMS, read the docs for
L<DBIx::OnlineDDL::Helper::Base>.

=head1 AUTHOR

Grant Street Group <developers@grantstreet.com>

=head1 COPYRIGHT AND LICENSE

This software is Copyright (c) 2018 - 2022 by Grant Street Group.

This is free software, licensed under:

  The Artistic License 2.0 (GPL Compatible)

=cut