The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package App::AltSQL::Plugin::Tail;

use Moose::Role;

=head1 TAIL

Given:

 CREATE TABLE log_entries (
   id  int primary key auto_increment,
   ts  datetime not null,
   log varchar(255) not null
 );

The SQL:

 .tail ts, log from log_entries every 30;

Will:

 * Find the column 'id', which is the only primary key and is auto_increment
 * Find the current auto_increment value of id as last_seen_max_value
 * Loop:
   - sleep 30 seconds
   - select ts, log from log_entries where id > last_seen_max_value
   - update last_seen_max_value

Other recognized forms:

 .tail * from log_entries every 30;
 .tail log_entries every 30;

 .tail log from log_entries where log like '%ERROR%' every 30;

=cut

around call_command => sub {
	my ($orig, $self, @args) = @_;
	my ($command, $input) = @args[0..1];

	if ($command ne 'tail') {
		# Call next chained call_command
		return $self->$orig(@args);
	}

	my ($from, $table, $where, $sleep_seconds) = $input =~
		m{^\.tail (.+? from|) \s+ (\S+) \s+ (where .+?|) every \s+ (\d+) \s* (?:s|seconds|)$}xi;
	if (! defined $table) {
		$self->log_error("Usage: .TAIL \$select FROM \$table WHERE \$criteria EVERY \$seconds | .TAIL \$table EVERY \$seconds");
		return 1; # handled
	}

	## Find the primary key, auto_increment column

	my $column_search = $self->model->dbh->selectall_arrayref(q|
		select
			COLUMN_NAME, IS_NULLABLE, DATA_TYPE, COLUMN_KEY, EXTRA
		from
			information_schema.COLUMNS
		where
			TABLE_SCHEMA = ? and
			TABLE_NAME = ?
	|, { Slice => {} }, $self->model->current_database, $table);

	my $key_column;
	{
		my @primary_keys = map { $_->{COLUMN_NAME} } grep { $_->{COLUMN_KEY} eq 'PRI' } @$column_search;
		my @autoinc_keys = map { $_->{COLUMN_NAME} } grep { $_->{EXTRA} eq 'auto_increment' } @$column_search;
		if (int @primary_keys == 1 && int @autoinc_keys == 1 && $autoinc_keys[0] eq $primary_keys[0]) {
			$key_column = $primary_keys[0];
		}
		else {
			$self->log_error("Unable to find an auto-incrementing, primary key on the '$table' table");
			return;
		}
	}

	## Find the current max value of this autoincrementing column

	my $last_seen_max_value;
	my $update_last_seen_max_value = sub {
		my $table_status = $self->model->dbh->selectrow_hashref(q|
			show
				table status
			where
				Name = ?
		|, undef, $table);
		$last_seen_max_value = $table_status->{Auto_increment} - 1;
	};
			
	## Construct tail SQL statement

	$from ||= '* from';
	if ($where) {
		$where .= " and $key_column > ";
	}
	else {
		$where = "where $key_column > ";
	}

	my $tail_sql_fragment = "select $from $table $where";

	## Loop

	my $break = 0;
	$SIG{INT} = sub {
		$break = 1;
	};

	my %render_opts = ( no_pager => 1 );

	while (1) {
		last if $break;
		$update_last_seen_max_value->();
		sleep $sleep_seconds;
		my $sql = $tail_sql_fragment . $last_seen_max_value;
		$self->log_info( scalar(localtime(time)) . ': ' . $sql);
		$self->model->handle_sql_input($sql, \%render_opts);
	}

	return 1;
};

no Moose::Role;

1;