App-AltSQL

 view release on metacpan or  search on metacpan

README.pod  view on Meta::CPAN

=encoding utf-8

=head1 NAME

App::AltSQL - A drop in replacement to the MySQL prompt with a pluggable Perl interface

=head1 SYNOPSIS

  ./altsql -h <host> -u <username> -D <database> -p<password>

  altsql> select * from film limit 4;
  ╒═════════╤══════════════════╤════════════════════════════
  │ film_id │ title            │ description                
  ╞═════════╪══════════════════╪════════════════════════════
  │       1 │ ACADEMY DINOSAUR │ A Epic Drama of a Feminist 
  │       2 │ ACE GOLDFINGER   │ A Astounding Epistle of a D
  │       3 │ ADAPTATION HOLES │ A Astounding Reflection of 
  │       4 │ AFFAIR PREJUDICE │ A Fanciful Documentary of a
  ╘═════════╧══════════════════╧════════════════════════════

lib/App/AltSQL.pm  view on Meta::CPAN

package App::AltSQL;

=encoding utf-8

=head1 NAME

App::AltSQL - A drop in replacement to the MySQL prompt with a pluggable Perl interface

=head1 SYNOPSIS

  ./altsql -h <host> -u <username> -D <database> -p<password>

  altsql> select * from film limit 4;
  ╒═════════╤══════════════════╤════════════════════════════
  │ film_id │ title            │ description                
  ╞═════════╪══════════════════╪════════════════════════════
  │       1 │ ACADEMY DINOSAUR │ A Epic Drama of a Feminist 
  │       2 │ ACE GOLDFINGER   │ A Astounding Epistle of a D
  │       3 │ ADAPTATION HOLES │ A Astounding Reflection of 
  │       4 │ AFFAIR PREJUDICE │ A Fanciful Documentary of a
  ╘═════════╧══════════════════╧════════════════════════════

lib/App/AltSQL.pm  view on Meta::CPAN

=head2 Command Line

The following options are available.

=over 4

=item -h HOSTNAME | --host HOSTNAME

=item -u USERNAME | --user USERNAME

=item -p | --password=PASSWORD | -pPASSWORD

=item --port PORT

=item -D DATABASE | --database DATABASE

Basic connection parameters to the MySQL database.

=item --A | --no-auto-rehash

By default, upon startup and whenever the database is changed, the C<information_schema> tables will be read to perform tab completion.  Disable this behavior to get a faster startup time (but no tab complete).

lib/App/AltSQL.pm  view on Meta::CPAN

				if (my $default = $args_spec{$key}{default}) {
					$args{"_${args_class}_$key"} = $default;
				}
			}
		}
	}

	# Password is a special case
	foreach my $i (0..$#argv) {
		my $arg = $argv[$i];
		next unless $arg =~ m{^(?:-p|--password=)(.*)$};
		splice @argv, $i, 1;
		if (length $1) {
			$args{_model_password} = $1;
			# Remove the password from the program name so people can't see it in process listings
			$0 = join ' ', $0, @argv;
		}
		else {
			# Prompt the user for the password
			require Term::ReadKey;
			Term::ReadKey::ReadMode('noecho');
			print "Enter password: ";
			$args{_model_password} = Term::ReadKey::ReadLine(0);
			Term::ReadKey::ReadMode('normal');
			print "\n";
			chomp $args{_model_password};
		}
		last; # I've found what I was looking for
	}

	GetOptionsFromArray(\@argv, %opts_spec);

	# Database is a special case; if left over arguments, that's the database name
	if (@argv && int @argv == 1) {
		$args{_model_database} = $argv[0];
	}

lib/App/AltSQL/Model/MySQL.pm  view on Meta::CPAN

=head1 DESCRIPTION

This module is currently the only Model supported by L<App::AltSQL>.

Upon startup, we will read in C<$HOME/.my.cnf> and will read and respect the following configuration variables:

=over 4

=item B<user>

=item B<password>

=item B<host>

=item B<port>

=item B<prompt>

=item B<safe_update>

=item B<select_limit>

lib/App/AltSQL/Model/MySQL.pm  view on Meta::CPAN

		require DBIx::MyParsePP;
	};
	if ($@) {
		return 0; # when we use this we check for definedness as well as boolean
	}
	return DBIx::MyParsePP->new();
});
has 'dbh'        => (is => 'rw');
has 'current_database' => (is => 'rw');

has [qw(host user password database port)] => ( is => 'ro' );
has [qw(no_auto_rehash select_limit safe_update prompt)] => ( is => 'ro' );

sub args_spec {
	return (
		host => {
			cli  => 'host|h=s',
			help => '-h HOSTNAME | --host HOSTNAME',
		},
		user => {
			cli  => 'user|u=s',
			help => '-u USERNAME | --user USERNAME',
		},
		password => {
			help => '-p | --password=PASSWORD | -pPASSWORD',
		},
		database => {
			cli  => 'database|d=s',
			help => '-d DATABASE | --database DATABASE',
		},
		port => {
			cli  => 'port=i',
			help => '--port PORT',
		},
		no_auto_rehash => {

lib/App/AltSQL/Model/MySQL.pm  view on Meta::CPAN

	foreach my $path (@config_paths) {
		(-e $path) or next;
		$self->read_my_dot_cnf($path);
	}
}

sub read_my_dot_cnf {
	my $self = shift;
	my $path = shift;

	my @valid_keys = qw( user password host port database prompt safe_update select_limit no_auto_rehash ); # keys we'll read
	my @valid_sections = qw( client mysql ); # valid [section] names
	my @boolean_keys = qw( safe_update no_auto_rehash );

	open MYCNF, "<$path";

	# ignore lines in file until we hit a valid [section]
	# then read key=value pairs
	my $in_valid_section = 0;
	while(<MYCNF>) {

lib/App/AltSQL/Model/MySQL.pm  view on Meta::CPAN

	close MYCNF;
}

sub db_connect {
	my $self = shift;
	my $dsn = 'DBI:mysql:' . join (';',
		map { "$_=" . $self->$_ }
		grep { defined $self->$_ }
		qw(database host port)
	);
	my $dbh = DBI->connect($dsn, $self->user, $self->password, {
		PrintError => 0,
		mysql_auto_reconnect => 1,
		mysql_enable_utf8 => 1,
	}) or die $DBI::errstr . "\nDSN used: '$dsn'\n";
	$self->dbh($dbh);

	## Update autocomplete entries

	if ($self->database) {
		$self->current_database($self->database);

lib/App/AltSQL/Term/Plugin/SyntaxHighlight.pm  view on Meta::CPAN

			fields file first flush for foreign from full function
			global grant grants group
			having heap high_priority hosts hour hour_minute hour_second
			identified ignore index infile inner insert insert_id into isam
			join
			key keys kill last_insert_id leading left limit lines load local lock logs long 
			low_priority
			match max_rows middleint min_rows minute minute_second modify month myisam
			natural no
			on optimize option optionally order outer outfile
			pack_keys partial password primary privileges procedure process processlist
			read references reload rename replace restrict returns revoke right row rows
			second select show shutdown soname sql_big_result sql_big_selects sql_big_tables sql_log_off
			sql_log_update sql_low_priority_updates sql_select_limit sql_small_result sql_warnings starting
			status straight_join string
			table tables temporary terminated to trailing type
			unique unlock unsigned update usage use using
			values varbinary variables varying
			where with write
			year_month
			zerofill

t/001_arguments.t  view on Meta::CPAN

use Test::More;
use Test::Deep;

BEGIN {
	use_ok 'App::AltSQL';
}

ok(App::AltSQL->parse_cli_args(), "Can call without arguments");

cmp_deeply(
	App::AltSQL->parse_cli_args([ qw(-u ewaters -ptestpassword -h localhost sakila) ]),
	superhashof({
		_model_user     => 'ewaters',
		_model_password => 'testpassword',
		_model_host     => 'localhost',
		_model_database => 'sakila',
	}),
	'Basic parse_cli_args',
);

cmp_deeply(
	App::AltSQL->parse_cli_args([qw(--port 12345 -A --help)]),
	superhashof({
		_model_port => 12345,

t/002_my_cnf.t  view on Meta::CPAN

}

my $app = bless {}, 'App::AltSQL';

{
	my $instance = App::AltSQL::Model::MySQL->new( app => $app );

	my $filename = write_config(<<ENDFILE);
[client]
user = ewaters
password = 12345
host=localhost
database = sakila

ENDFILE

	$instance->read_my_dot_cnf($filename);
	unbless($instance);
	cmp_deeply(
		$instance,
		superhashof({
			user        => 'ewaters',
			password    => '12345',
			host        => 'localhost',
			database    => 'sakila',
		}),
		'Perfectly Simple my.cnf',
	);
	unlink $filename;
}
{
	my $instance = App::AltSQL::Model::MySQL->new( app => $app );

	my $filename = write_config(<<ENDFILE);
[client]
user = ewaters
password = 12345
host = localhost

[mysql]
database = sakila
default-character-set = utf8
prompt = \\u@\\h[\\R:\\m:\\s]>
safe-update = false
ENDFILE

	$instance->read_my_dot_cnf($filename);
	unbless( $instance );
	cmp_deeply(
		$instance,
		superhashof({
			user        => 'ewaters',
			password    => '12345',
			host        => 'localhost',
			database    => 'sakila',
			safe_update => 0,
			prompt      => '\\u@\\h[\\R:\\m:\\s]>',
		}),
		'Multi-section my.cnf',
	);
	unlink $filename;
}
{
	my $instance = App::AltSQL::Model::MySQL->new( app => $app );

	my $filename = write_config(<<ENDFILE);
[client]
user = ewaters
password = 12345
host = localhost

[mysql]
database = sakila
default-character-set = utf8
prompt = \\u@\\h[\\R:\\m:\\s]>
safe-update = false
ENDFILE

	$instance->read_my_dot_cnf($filename);
	unbless($instance);
	cmp_deeply(
		$instance,
		superhashof({
			user        => 'ewaters',
			password    => '12345',
			host        => 'localhost',
			database    => 'sakila',
			safe_update => 0,
			prompt      => '\\u@\\h[\\R:\\m:\\s]>',
		}),
		'Multi-section my.cnf',
	);
	unlink $filename;
}

{
	my $instance = App::AltSQL::Model::MySQL->new( app => $app );

	my $filename = write_config(<<ENDFILE);
[client]

user=firesun
password=password123

[mysql]

#use this to get faster startup and avoid the following message:
#Reading table information for completion of table and column names
#You can turn off this feature to get a quicker startup with -A
skip-auto-rehash

select_limit = 50

#use this to set your initial database
database = sakila
ENDFILE

	$instance->read_my_dot_cnf($filename);
	unbless($instance);
	cmp_deeply(
		$instance,
		superhashof({
			user           => 'firesun',
			password       => 'password123',
			database       => 'sakila',
			no_auto_rehash => 1,
			select_limit   => 50,
		}),
		'Comments and whitespace',
	);
	unlink $filename;
}

done_testing;

t/sql/sakila-schema.sql  view on Meta::CPAN

CREATE TABLE staff (
  staff_id TINYINT UNSIGNED NOT NULL AUTO_INCREMENT,
  first_name VARCHAR(45) NOT NULL,
  last_name VARCHAR(45) NOT NULL,
  address_id SMALLINT UNSIGNED NOT NULL,
  picture BLOB DEFAULT NULL,
  email VARCHAR(50) DEFAULT NULL,
  store_id TINYINT UNSIGNED NOT NULL,
  active BOOLEAN NOT NULL DEFAULT TRUE,
  username VARCHAR(16) NOT NULL,
  password VARCHAR(40) BINARY DEFAULT NULL,
  last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY  (staff_id),
  KEY idx_fk_store_id (store_id),
  KEY idx_fk_address_id (address_id),
  CONSTRAINT fk_staff_store FOREIGN KEY (store_id) REFERENCES store (store_id) ON DELETE RESTRICT ON UPDATE CASCADE,
  CONSTRAINT fk_staff_address FOREIGN KEY (address_id) REFERENCES address (address_id) ON DELETE RESTRICT ON UPDATE CASCADE
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

--
-- Table structure for table `store`



( run in 0.719 second using v1.01-cache-2.11-cpan-49f99fa48dc )