Sisyphus repositório
Última atualização: 1 outubro 2023 | SRPMs: 18631 | Visitas: 37538712
en ru br
ALT Linux repositórios
S:0.60-alt2

Group :: Desenvolvimento/Banco de Dados
RPM: mysqldiff

 Main   Changelog   Spec   Patches   Sources   Download   Gear   Bugs e FR  Repocop 

pax_global_header00006660000000000000000000000064116562523570014526gustar00rootroot0000000000000052 comment=ce3ac9fe0d0deda1748e94c8ba52a4c6ae486c81
mysqldiff-0.43/000075500000000000000000000000001165625235700134505ustar00rootroot00000000000000mysqldiff-0.43/.gear/000075500000000000000000000000001165625235700144445ustar00rootroot00000000000000mysqldiff-0.43/.gear/mysqldiff.spec000064400000000000000000000016651165625235700173260ustar00rootroot00000000000000Name: mysqldiff
Version: 0.43
Release: alt1

Summary: Comparing the schema (table structures) of two MySQL databases

License: Public domain
Group: Development/Databases
Url: http://adamspiers.org/computing/mysqldiff/

# https://github.com/aspiers/mysqldiff
Source: %name-%version.tar

Packager: Vitaly Lipatov <lav@altlinux.ru>

BuildArch: noarch

# Automatically added by buildreq on Tue Nov 08 2011
# optimized out: MySQL-client perl-Encode perl-Pod-Escapes perl-Pod-Simple perl-YAML-Tiny perl-devel perl-podlators
BuildRequires: perl-File-Slurp perl-Module-Build

%description
MySQL-Diff is suite of Perl modules and accompanying
CLI script for comparing the schema (table structures) of two MySQL databases.

%prep
%setup

%build
%perl_vendor_build

%install
%perl_vendor_install

%files
%_bindir/%name
%perl_vendor_privlib/MySQL/


%changelog
* Tue Nov 08 2011 Vitaly Lipatov <lav@altlinux.ru> 0.43-alt1
- initial build for ALT Linux Sisyphus
mysqldiff-0.43/.gear/rules000064400000000000000000000000421165625235700155150ustar00rootroot00000000000000tar: .
spec: .gear/mysqldiff.spec
mysqldiff-0.43/.gitignore000064400000000000000000000002461165625235700154420ustar00rootroot00000000000000MySQL-Diff-*
mysqldiff-*.tar.gz

blib*
Makefile
Makefile.old
Build
_build*
pm_to_blib*
*.tar.gz
.lwpcookies
cover_db
Build.bat
*.tmp

META.yml
MYMETA.yml
MYMETA.json
mysqldiff-0.43/BUGS000064400000000000000000000012271165625235700141350ustar00rootroot00000000000000Please see: https://rt.cpan.org/Public/Dist/Display.html?Name=MySQL-Diff
which supercedes this file.

Reported by users but unconfirmed:
==================================

* If '-P1 3337' is one of the arguments it doesn't seem to get
passed to the arguments for mysqldump (according to the
debugging). [Darrell Taylor]

Others
======

* You can't specify which database to connect to for creating
temporary tables.

* Things probably break if you use --password or
-p without a parameter.

* The remote authentication code is barely tested, and hence
probably broken.

All easy to fix but I'm so short on time! Patches welcome ...
mysqldiff-0.43/Build.PL000075500000000000000000000034621165625235700147540ustar00rootroot00000000000000use strict;
use warnings;

use Module::Build;
#created by eumm-migrate.pl

my $build = Module::Build->new(
module_name => 'MySQL::Diff',
keywords => [ qw/mysql diff compare schema tables structure database/ ],
license => 'perl',

# Module::Build forces us to use v1.4 of the CPAN Meta Spec:
# https://rt.cpan.org/Ticket/Display.html?id=71502
# 'meta-spec' => {
# version => '2.0',
# url => 'http://search.cpan.org/perldoc?CPAN::Meta::Spec',
# },
meta_add => {
resources => {
license => 'http://dev.perl.org/licenses/' ,
homepage => 'http://adamspiers.org/computing/mysqldiff/',

# Module::Build forces us to use v1.4 of the CPAN Meta Spec:
# https://rt.cpan.org/Ticket/Display.html?id=71502
# bugtracker => {
# web => 'http://rt.cpan.org/Public/Dist/Display.html?Name=MySQL-Diff',
# mailto => 'mysqldiff@adamspiers.org',
# },
bugtracker => 'http://rt.cpan.org/Public/Dist/Display.html?Name=MySQL-Diff',

# Module::Build forces us to use v1.4 of the CPAN Meta Spec:
# https://rt.cpan.org/Ticket/Display.html?id=71502
# repository => {
# url => 'git:github.com/aspiers/mysqldiff.git',
# web => 'http://github.com/aspiers/mysqldiff',
# type => 'git',
# },
repository => 'http://github.com/aspiers/mysqldiff',
},
},
requires => {
'perl' => '5.006',
'Carp' => 0,
'File::Slurp' => 0,
'IO::File' => 0,
},
script_files => [ 'bin/mysqldiff' ],
all_from => 'lib/MySQL/Diff.pm',
configure_requires => {
'Module::Build' => 0,
},
);

$build->create_build_script();
mysqldiff-0.43/ChangeLog.OLD000064400000000000000000000143311165625235700156410ustar00rootroot000000000000002003-05-08 Adam Spiers <mysqldiff@adamspiers.org>

* README: document bugs

* Diff.pm, Makefile.PL, README, Utils.pm:
- 0.33
- remove Perl 5.6 requirement (thanks to Tatsuhiko Miyagawa
for the suggestion)

* t/all.t, test.pl, MANIFEST: renamed test.pl to t/all.t to use
Test::Harness

* INSTALL: Make stuff more obvious

* test.pl: don't hang when mysqld up

* test.pl: more obvious on failure

* README: Class::MakeMethods

2002-09-10 Adam Spiers <mysqldiff@adamspiers.org>

* MANIFEST: mysqldiff.pl -> mysqldiff

* Diff.pm: version 0.32

* Diff.pm, Table.pm, test.pl:
Fix bug with INDEX <-> UNIQUE, spotted by John Smith. Thanks John!
Added regression tests.

2002-09-10 Adam Spiers <mysqldiff@adamspiers.org>

* Diff.pm: version 0.32

* Diff.pm, Table.pm, test.pl:
Fix bug with INDEX <-> UNIQUE, spotted by John Smith. Thanks John!
Added regression tests.

2002-09-08 Adam Spiers <mysqldiff@adamspiers.org>

* Table.pm:
fix most tests with MySQL 4

2002-07-24 Adam Spiers <mysqldiff@adamspiers.org>

* Diff.pm: version 0.31

* Database.pm, Diff.pm, test.pl:
- add almost complete set of tests
- fix obscure bugs relating to changing of keys on auto_increment rows

2002-06-20 Adam Spiers <mysqldiff@adamspiers.org>

* Diff.pm: version 0.30

* README:
update my email

* MANIFEST:
did a 'make manifest'

* Database.pm:
allow postgres-style '-- foo' comments

2002-06-09 Adam Spiers <mysqldiff@adamspiers.org>

* README:
Requires 5.6.0 now. Thanks to scottb (at) incursio.com for pointing
this out.

2002-05-24 Adam Spiers <mysqldiff@adamspiers.org>

* mysqldiff: add -B

2002-04-02 Adam Spiers <mysqldiff@adamspiers.org>

* Makefile.PL:
require Perl 5.6

2002-03-11 Adam Spiers <mysqldiff@adamspiers.org>

* mysqldiff:
slightly nicer output

* mysqldiff, Database.pm:
check we're applying changes to a db

2002-02-27 Adam Spiers <mysqldiff@adamspiers.org>

* Utils.pm:
stop directories being treated as files containing SQL definitions

2002-02-20 Adam Spiers <mysqldiff@adamspiers.org>

* mysqldiff:
disastrous typo

* mysqldiff, Database.pm, Diff.pm, Utils.pm:
version 0.29:
- refactoring
- new -A (--apply) option
- safety check for "use", "drop database" and "create database" commands
in file being canonicalised (thanks to Tripp Lilley for the idea)

2002-02-19 Adam Spiers <mysqldiff@adamspiers.org>

* Diff.pm: 0.28

* Diff.pm, mysqldiff:
add -k option

2002-02-14 Adam Spiers <mysqldiff@adamspiers.org>

* Table.pm:
still learning MakeMethods *sigh*

* Diff.pm: debugging

2002-02-13 Adam Spiers <mysqldiff@adamspiers.org>

* Diff.pm, Makefile.PL, Table.pm:
- use Class::MakeMethods
- don't break on fulltext (doesn't diff it yet though)

2002-01-09 Adam Spiers <mysqldiff@adamspiers.org>

* Diff.pm:
didn't quite do the bugfix right, thanks AGAIN Warwick!

2002-01-08 Adam Spiers <mysqldiff@adamspiers.org>

* Table.pm:
support KEY foo(bar) format with missing space
(thanks again to Warwick Smith)

* Table.pm:
fix problem with older MySQLs which don't include TYPE=MyISAM in dump
(thanks again to Warwick Smith)

* Table.pm:
no need for brackets around primary key

* Diff.pm:
no need for brackets around primary key (thanks to Warwick Smith)

2001-11-12 Adam Spiers <mysqldiff@adamspiers.org>

* mysqldiff, Utils.pm:
patch from Geoffrey Talvola <gtalvola@mediaone.net> to
allow specifying alternate ports.

2001-11-07 Adam Spiers <mysqldiff@adamspiers.org>

* Utils.pm:
missed a bit of the socket patch, thanks again Jody!

* README:
slight inaccuracies

* Makefile.PL:
omit postamble hack for other people

* Diff.pm, mysqldiff, Utils.pm:
- bump to 0.27
- move parse_arg and available_dbs to MySQL::Utils
- incorporate patch from Jody Biggs <jody@codegrok.com>
to support sockets

* .cvsignore:
*** empty log message ***

* Database.pm:
bug fix for empty databases from Noam Solomon <noam@socketnet.com>

* Makefile.PL:
best to stick to CPAN conventions

* Makefile.PL:
ChangeLog hack

* .cvsignore, INSTALL, Makefile.PL, MANIFEST, README, test.pl:
mysqldiff packaging

* Diff.pm:
bump up version for new release

* Database.pm, Diff.pm, Table.pm, Utils.pm:
- add missing `use strict' and fix compile errors - doh!
- support table options

2001-10-20 Adam Spiers <mysqldiff@adamspiers.org>

* Diff.pm:
fix primary key diffing

* Diff.pm: doh!

* mysqldiff, Diff.pm:
move MySQLdiff -> MySQL::Diff

2001-09-18 Adam Spiers <mysqldiff@adamspiers.org>

* Table.pm:
bugfix from Paul Mineiro (pmineiro@codegrok.com)

2001-02-19 Adam Spiers <mysqldiff@adamspiers.org>

* mysqldiff:
check that parse_arg succeeded

2001-02-14 Adam Spiers <mysqldiff@adamspiers.org>

* Database.pm, Table.pm, Utils.pm, mysqldiff:
split into pieces so we can reuse it in other utilities

2000-11-02 Adam Spiers <mysqldiff@adamspiers.org>

* mysqldiff:
- version 0.25
- rename 'hack' option to avoid clash
- fix --host0 bug

2000-10-16 Christof Damian

* mysqldiff:
- hack to be more tolerant

2000-09-04 Adam Spiers <mysqldiff@adamspiers.org>

* mysqldiff:
avoid warning

2000-08-31 Adam Spiers <mysqldiff@adamspiers.org>

* mysqldiff: 0.24

2000-08-31 Christof Damian

* mysqldiff:
- change column needs the name of old and new field

2000-08-24 Adam Spiers <mysqldiff@adamspiers.org>

* mysqldiff:
- add -t|--table-re option for only comparing tables which match a regexp
- 0.23

* mysqldiff:
- 0.22
- added -o option

* mysqldiff:
optional comments describing old defs

2000-08-23 Adam Spiers <mysqldiff@adamspiers.org>

* mysqldiff:
double-doh! there was a reason that close() was late ...

* mysqldiff:
doh! I was closing the filehandle too late, so mysqldump didn't get everything

* mysqldiff:
- add debugging
- handle UNIQUE indices

* mysqldiff:
fix bug with db:arg parsing

* mysqldiff:
keep Christof happy ;-)

* mysqldiff:
- fix broken usage text
- prepare for public release (free bug-testers *grin*)

* mysqldiff:
- added $VERSION
- $changes counter is now @changes
- outputs banner if @changes non-empty
- now compares indices

2000-08-22 Adam Spiers <mysqldiff@adamspiers.org>

* mysqldiff:
temp db names start with test_

* mysqldiff:
- first draft
- doesn't compare KEYs yet
- authentication code completely untested

mysqldiff-0.43/Changes000064400000000000000000000024311165625235700147430ustar00rootroot00000000000000# Changes log for Test::CPAN::Meta

0.43 6th October 2011
- fix missing fields in CPAN meta-data

0.43 6th October 2011
- depend on Perl 5.6
- improve docs and CPAN meta-data

0.41 5th October 2011
- tidy up POD

0.40 5th October 2011
- fix issue with hyphens in database names
- made --tolerant ignore COLLATE and AUTO_INCREMENT
- fixed 'Duplicate specification' options from Getopt::Long
- made CLI options case-sensitive
- fixed some coding style inconsistencies
- remove .cvsignore
- merged changes by Barbie
- removed use of unmaintained Class::MakeMethods
- repackaged distribution with additional package files
- restructured modules to use namespace MySQL::Diff::*
- restructured modules to use better OO style inferface
- Utils.pm now only contains debug handling
- added support for more recent MySQL dumps
- added more documentation
- added more tests
- merged changes by Alexandr Ciornii
- depend on Perl 5.5.3
- remove lib/MySQL/.cvsignore
- fix .gitignore
- upgrade Makefile.PL and Build.PL

0.33 8th May 2003
- see ChangeLog.OLD for previous changes.
mysqldiff-0.43/INSTALL000064400000000000000000000036031165625235700145030ustar00rootroot00000000000000First please consult the README to check that you have a new enough
version of Perl.

(N.B. the rest of this document looks a great deal more complicated
than it actually is, mainly because I'm trying to encourage people to
do the Right Things by using CPANPLUS instead of CPAN, and
Module::Build instead of ExtUtils::MakeMaker.)


"Automatic" installation via CPANPLUS.pm or CPAN.pm
=========================================================================

Installation from either of the recommended installers can be performed at the
command line, with either of the two following commands:

$ perl -MCPANPLUS -e 'install MySQL::Diff'

$ perl -MCPAN -e 'install MySQL::Diff'

Although CPAN.pm is the default installer for many, with the release of Perl
5.10, CPANPLUS.pm is now also available in core. However, if you use an earlier
version of Perl, you can install CPANPLUS from the CPAN with the following
command:

$ perl -MCPAN -e 'install CPANPLUS'


"Manual" installation
=========================================================================

First ensure you have File::Slurp installed.

Then there are two options:

1) Install via Module::Build (recommended)
--------------------------------------------

Ensure that Module::Build is installed, e.g.

$ perl -MCPAN -e 'install Module::Build'

or

$ perl -MCPANPLUS -e 'install Module::Build'

Then run these commands:

perl Build.PL
perl Build
perl Build test
perl Build install

2) Install via ExtUtils::MakeMaker (deprecated but simpler)
-------------------------------------------------------------

You can install MySQL::Diff in the traditional way by running these commands:

perl Makefile.PL
make
make test
make install

And finally ...
=========================================================================

Note that the test suite will not run properly unless you have
a MySQL server which it can connect to.

mysqldiff-0.43/MANIFEST000064400000000000000000000004041165625235700145770ustar00rootroot00000000000000BUGS
Build.PL
bin/mysqldiff
Changes
ChangeLog.OLD
INSTALL
lib/MySQL/Diff.pm
lib/MySQL/Diff/Database.pm
lib/MySQL/Diff/Table.pm
lib/MySQL/Diff/Utils.pm
MANIFEST
Makefile.PL
META.yml
README
t/01use.t
t/90podtest.t
t/91podcover.t
t/94metatest.t
t/all.t
META.json
mysqldiff-0.43/META.json000064400000000000000000000031031165625235700150660ustar00rootroot00000000000000{
"abstract" : "Generates a database upgrade instruction set",
"author" : [
"Adam Spiers <mysqldiff@adamspiers.org>"
],
"dynamic_config" : 1,
"generated_by" : "Module::Build version 0.38, CPAN::Meta::Converter version 2.112621",
"license" : [
"perl_5"
],
"meta-spec" : {
"url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
"version" : "2"
},
"name" : "MySQL-Diff",
"prereqs" : {
"configure" : {
"requires" : {
"Module::Build" : 0
}
},
"runtime" : {
"requires" : {
"Carp" : 0,
"File::Slurp" : 0,
"IO::File" : 0,
"perl" : "5.006"
}
}
},
"provides" : {
"MySQL::Diff" : {
"file" : "lib/MySQL/Diff.pm",
"version" : "0.43"
},
"MySQL::Diff::Database" : {
"file" : "lib/MySQL/Diff/Database.pm",
"version" : "0.43"
},
"MySQL::Diff::Table" : {
"file" : "lib/MySQL/Diff/Table.pm",
"version" : "0.43"
},
"MySQL::Diff::Utils" : {
"file" : "lib/MySQL/Diff/Utils.pm",
"version" : "0.43"
}
},
"release_status" : "stable",
"resources" : {
"bugtracker" : {
"web" : "http://rt.cpan.org/Public/Dist/Display.html?Name=MySQL-Diff"
},
"homepage" : "http://adamspiers.org/computing/mysqldiff/",
"license" : [
"http://dev.perl.org/licenses/"
],
"repository" : {
"url" : "http://github.com/aspiers/mysqldiff"
}
},
"version" : "0.43"
}
mysqldiff-0.43/META.yml000064400000000000000000000017521165625235700147260ustar00rootroot00000000000000---
abstract: 'Generates a database upgrade instruction set'
author:
- 'Adam Spiers <mysqldiff@adamspiers.org>'
build_requires: {}
configure_requires:
Module::Build: 0
dynamic_config: 1
generated_by: 'Module::Build version 0.38, CPAN::Meta::Converter version 2.112621'
license: perl
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html
version: 1.4
name: MySQL-Diff
provides:
MySQL::Diff:
file: lib/MySQL/Diff.pm
version: 0.43
MySQL::Diff::Database:
file: lib/MySQL/Diff/Database.pm
version: 0.43
MySQL::Diff::Table:
file: lib/MySQL/Diff/Table.pm
version: 0.43
MySQL::Diff::Utils:
file: lib/MySQL/Diff/Utils.pm
version: 0.43
requires:
Carp: 0
File::Slurp: 0
IO::File: 0
perl: 5.006
resources:
bugtracker: http://rt.cpan.org/Public/Dist/Display.html?Name=MySQL-Diff
homepage: http://adamspiers.org/computing/mysqldiff/
license: http://dev.perl.org/licenses/
repository: http://github.com/aspiers/mysqldiff
version: 0.43
mysqldiff-0.43/Makefile.PL000075500000000000000000000043451165625235700154330ustar00rootroot00000000000000#!/usr/bin/perl -w

use strict;

require 5.005003;

use ExtUtils::MakeMaker;
# See lib/ExtUtils/MakeMaker.pm for details of how to influence
# the contents of the Makefile that is written.
WriteMakefile1(
NAME => 'MySQL::Diff',
#KEYWORDS => [ qw/mysql diff compare schema tables structure database/ ],
LICENSE => 'perl',
META_ADD => {
resources => {
license => [ 'http://dev.perl.org/licenses/' ],
homepage => 'http://adamspiers.org/computing/mysqldiff/',
bugtracker => {
web => 'http://rt.cpan.org/Public/Dist/Display.html?Name=MySQL-Diff',
mailto => 'mysqldiff@adamspiers.org',
},
repository => {
url => 'git:github.com/aspiers/mysqldiff.git',
web => 'http://github.com/aspiers/mysqldiff',
type => 'git',
},
#repository => 'http://github.com/aspiers/mysqldiff',
},
},
MIN_PERL_VERSION => '5.006',
PREREQ_PM => {
'Carp' => 0,
'File::Slurp' => 0,
'IO::File' => 0,
},
EXE_FILES => [ 'bin/mysqldiff' ],
VERSION_FROM => 'lib/MySQL/Diff.pm', # finds $VERSION
);

sub WriteMakefile1 { #Written by Alexandr Ciornii, version 0.21. Added by eumm-upgrade.
my %params = @_;
my $eumm_version = $ExtUtils::MakeMaker::VERSION;
$eumm_version = eval $eumm_version;
die "EXTRA_META is deprecated" if exists $params{EXTRA_META};
die "License not specified" if not exists $params{LICENSE};
if ($params{BUILD_REQUIRES} and $eumm_version < 6.5503) {
#EUMM 6.5502 has problems with BUILD_REQUIRES
$params{PREREQ_PM}={ %{$params{PREREQ_PM} || {}} , %{$params{BUILD_REQUIRES}} };
delete $params{BUILD_REQUIRES};
}
delete $params{CONFIGURE_REQUIRES} if $eumm_version < 6.52;
delete $params{MIN_PERL_VERSION} if $eumm_version < 6.48;
delete $params{META_MERGE} if $eumm_version < 6.46;
delete $params{META_ADD} if $eumm_version < 6.46;
delete $params{LICENSE} if $eumm_version < 6.31;
delete $params{AUTHOR} if $] < 5.005;
delete $params{ABSTRACT_FROM} if $] < 5.005;
delete $params{BINARY_LOCATION} if $] < 5.005;

WriteMakefile(%params);
}

mysqldiff-0.43/README000064400000000000000000000024021165625235700143260ustar00rootroot00000000000000README for MySQL-Diff

MySQL-Diff
============

MySQL-Diff is suite of Perl modules and accompanying CLI script for
comparing the schema (table structures) of two MySQL databases.

Prerequisites
-------------

This suite probably requires Perl 5.005 or higher.

Availability
------------

The latest version of MySQL-Diff is available from

http://adamspiers.org/computing/mysqldiff/

and eventually (a possibly slightly older version) from the
Comprehensive Perl Archive Network (CPAN). Visit
<http://www.perl.com/CPAN> to find a CPAN site near you.

Installation
------------

See INSTALL file.

Documentation
-------------

Homepage: http://adamspiers.org/computing/mysqldiff/
Documentation at CPAN: http://search.cpan.org/dist/MySQL-Diff/

Support
-------

Questions, patches, and suggestions for MySQL-Diff should just be sent
to me at <mysqldiff@adamspiers.org>. Bug reports should be submitted
at the website below.

Known bugs
----------

See https://rt.cpan.org/Public/Dist/Display.html?Name=MySQL-Diff

Copyright
---------

(c) 2000-2011 Adam Spiers <mysqldiff@adamspiers.org>, all rights reserved.
This program is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.
mysqldiff-0.43/bin/000075500000000000000000000000001165625235700142205ustar00rootroot00000000000000mysqldiff-0.43/bin/mysqldiff000075500000000000000000000155451165625235700161560ustar00rootroot00000000000000#!/usr/bin/perl -w

=head1 NAME

mysqldiff - compare MySQL database schemas

=head1 SYNOPSIS

mysqldiff [B<options>] B<database1> B<database2>

mysqldiff --help

=head1 DESCRIPTION

F<mysqldiff> is a Perl script front-end to the
L<CPAN|http://www.perl.com/CPAN> module
L<MySQL::Diff|http://search.cpan.org/search?module=MySQL::Diff> which
compares the data structures (i.e. schema / table definitions) of two
L<MySQL|http://www.mysql.com/> databases, and returns the differences
as a sequence of MySQL commands suitable for piping into F<mysql>
which will transform the structure of the first database to be
identical to that of the second (I<c.f.> F<diff> and F<patch>).

Database structures can be compared whether they are files containing
table definitions or existing databases, local or remote.

B<N.B.> The program makes I<no> attempt to compare any of the data
which may be stored in the databases. It is purely for comparing the
table definitions. I have no plans to implement data comparison; it
is a complex problem and I have no need of such functionality anyway.
However there is another program
L<coldiff|http://rossbeyer.net/software/mysql_coldiff/>
which does this, and is based on an older program called
F<datadiff> which seems to have vanished off the 'net.

For PostgreSQL there are similar tools such as
L<pgdiff|http://pgdiff.sourceforge.net/> and
L<apgdiff|http://apgdiff.startnet.biz/>.

=head1 EXAMPLES

# compare table definitions in two files
mysqldiff db1.mysql db2.mysql

# compare table definitions in a file 'db1.mysql' with a database 'db2'
mysqldiff db1.mysql db2

# interactively upgrade schema of database 'db1' to be like the
# schema described in the file 'db2.mysql'
mysqldiff -A db1 db2.mysql

# compare table definitions in two databases on a remote machine
mysqldiff --host=remote.host.com --user=myaccount db1 db2

# compare table definitions in a local database 'foo' with a
# database 'bar' on a remote machine, when a file foo already
# exists in the current directory
mysqldiff --host2=remote.host.com --password=secret db:foo bar

=head1 OPTIONS

=for comment FIXME - add option docs here

More details to come; for now run C<mysqldiff --help>.

=head1 INTERNALS

For both of the database structures being compared, the following
happens:

=over 4

=item

If the argument is a valid filename, the file is used to create a
temporary database which C<mysqldump -d> is run on to obtain the table
definitions in canonicalised form. The temporary database is then
dropped. (The temporary database is named
C<test_mysqldiff_temp_something> because default MySQL permissions
allow anyone to create databases beginning with the prefix C<test_>.)

=item

If the argument is a database, C<mysqldump -d> is run directly on it.

=item

Where authentication is required, the hostname, username, and password
given by the corresponding options are used (type C<mysqldiff --help>
for more information).

=item

Each set of table definitions is now parsed into tables, and fields
and index keys within those tables; these are compared, and the
differences outputted in the form of MySQL statements.

=back

=cut

use strict;

use 5.006; # due to 'our' and qr//

use FindBin qw($RealBin $Script);
use lib $RealBin;
use Getopt::Long qw(:config no_ignore_case);
use IO::File;

use MySQL::Diff;

my %opts = ();
GetOptions(\%opts, "help|?", "debug|d:i", "apply|A", "batch-apply|B",
"keep-old-tables|k", "no-old-defs|n", "only-both|o", "table-re|t=s",
"host|h=s", "port|P=s", "socket|s=s", "user|u=s", "password|p:s",
"host1=s", "port1=s", "socket1=s", "user1=s", "password1:s",
"host2=s", "port2=s", "socket2=s", "user2=s", "password2:s",
"tolerant|i"
) or usage();

usage() if (@ARGV != 2 or $opts{help});

$opts{debug} ||= 0;

my $md = MySQL::Diff->new(%opts);

for my $num (1, 2) {
my $new_db = $md->register_db($ARGV[$num-1], $num);
usage($new_db) unless ref $new_db;
}

$| = 1;
my $diffs = $md->diff();
print $diffs;
apply($md, $diffs) if $opts{apply} || $opts{'batch-apply'};

exit 0;

##############################################################################

sub usage {
print STDERR @_, "\n" if @_;
die <<EOF;
Usage: $Script [ options ] <database1> <database2>

Options:
-?, --help show this help
-A, --apply interactively patch database1 to match database2
-B, --batch-apply non-interactively patch database1 to match database2
-d, --debug[=N] enable debugging [level N, default 1]
-o, --only-both only output changes for tables in both databases
-k, --keep-old-tables don't output DROP TABLE commands
-n, --no-old-defs suppress comments describing old definitions
-t, --table-re=REGEXP restrict comparisons to tables matching REGEXP
-i, --tolerant ignore DEFAULT, AUTO_INCREMENT, COLLATE, and formatting changes

-h, --host=... connect to host
-P, --port=... use this port for connection
-u, --user=... user for login if not current user
-p, --password[=...] password to use when connecting to server
-s, --socket=... socket to use when connecting to server

for <databaseN> only, where N == 1 or 2,
--hostN=... connect to host
--portN=... use this port for connection
--userN=... user for login if not current user
--passwordN[=...] password to use when connecting to server
--socketN=... socket to use when connecting to server

Databases can be either files or database names.
If there is an ambiguity, the file will be preferred;
to prevent this prefix the database argument with `db:'.
EOF
}

sub apply {
my ($md, $diffs) = @_;

if (! $diffs) {
print "No differences to apply.\n";
return;
}

my $db0 = $md->db1->name;
if ($md->db1->source_type ne 'db') {
die "$db0 is not a database; cannot apply changes.\n";
}

unless ($opts{'batch-apply'}) {
print "\nApply above changes to $db0 [y/N] ? ";
print "\n(CAUTION! Changes contain DROP TABLE commands.) "
if $diffs =~ /\bDROP TABLE\b/i;
my $reply = <STDIN>;
return unless $reply =~ /^y(es)?$/i;
}

print "Applying changes ... ";
my $args = $md->db1->auth_args;
my $pipe = "mysql$args $db0";
my $fh = IO::File->new("|$pipe") or die "Couldn't open pipe to '$pipe': $!\n";
print $fh $diffs;
$fh->close or die "Couldn't close pipe: $!\n";
print "done.\n";
}

=head1 BUGS, DEVELOPMENT, CONTRIBUTING

See L<http://software.adamspiers.org/wiki/mysqldiff>.

=head1 COPYRIGHT AND LICENSE

Copyright (c) 2000-2011 Adam Spiers. All rights reserved. This
program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

=head1 SEE ALSO

L<MySQL::Diff>, L<MySQL::Diff::Database>, L<MySQL::Diff::Table>, L<MySQL::Diff::Utils>,
L<mysql>, L<mysqldump>, L<mysqlshow>

=head1 AUTHOR

Adam Spiers <mysqldiff@adamspiers.org>

=cut
mysqldiff-0.43/lib/000075500000000000000000000000001165625235700142165ustar00rootroot00000000000000mysqldiff-0.43/lib/MySQL/000075500000000000000000000000001165625235700151635ustar00rootroot00000000000000mysqldiff-0.43/lib/MySQL/Diff.pm000064400000000000000000000323121165625235700163720ustar00rootroot00000000000000package MySQL::Diff;

=head1 NAME

MySQL::Diff - Generates a database upgrade instruction set

=head1 SYNOPSIS

use MySQL::Diff;

my $md = MySQL::Diff->new( %options );
my $db1 = $md->register_db($ARGV[0], 1);
my $db2 = $md->register_db($ARGV[1], 2);
my $diffs = $md->diff();

=head1 DESCRIPTION

Generates the SQL instructions required to upgrade the first database to match
the second.

=cut

use warnings;
use strict;

our $VERSION = '0.43';

# ------------------------------------------------------------------------------
# Libraries

use MySQL::Diff::Database;
use MySQL::Diff::Utils qw(debug debug_level debug_file);

use Data::Dumper;

# ------------------------------------------------------------------------------

=head1 METHODS

=head2 Constructor

=over 4

=item new( %options )

Instantiate the objects, providing the command line options for database
access and process requirements.

=back

=cut

sub new {
my $class = shift;
my %hash = @_;
my $self = {};
bless $self, ref $class || $class;

$self->{opts} = \%hash;

if($hash{debug}) { debug_level($hash{debug}) ; delete $hash{debug}; }
if($hash{debug_file}) { debug_file($hash{debug_file}) ; delete $hash{debug_file}; }

debug(3,"\nconstructing new MySQL::Diff");

return $self;
}

=head2 Public Methods

Fuller documentation will appear here in time :)

=over 4

=item * register_db($name,$inx)

Reference the database, and setup a connection. The name can be an already
existing 'MySQL::Diff::Database' database object. The index can be '1' or '2',
and refers both to the order of the diff, and to the host, port, username and
password arguments that have been supplied.

=cut

sub register_db {
my ($self, $name, $inx) = @_;
return unless $inx == 1 || $inx == 2;

my $db = ref $name eq 'MySQL::Diff::Database' ? $name : $self->_load_database($name,$inx);
$self->{databases}[$inx-1] = $db;
return $db;
}

=item * db1()

=item * db2()

Return the first and second databases registered via C<register_db()>.

=cut

sub db1 { shift->{databases}->[0] }
sub db2 { shift->{databases}->[1] }

=item * diff()

Performs the diff, returning a string containing the commands needed to change
the schema of the first database into that of the second.

=back

=cut

sub diff {
my $self = shift;
my $table_re = $self->{opts}{'table-re'};
my @changes;

debug(1, "\ncomparing databases");

for my $table1 ($self->db1->tables()) {
my $name = $table1->name();
debug(4, "table 1 $name = ".Dumper($table1));
if ($table_re && $name !~ $table_re) {
debug(2,"table '$name' didn't match /$table_re/; ignoring");
next;
}
debug(2,"looking at tables called '$name'");
if (my $table2 = $self->db2->table_by_name($name)) {
debug(3,"comparing tables called '$name'");
push @changes, $self->_diff_tables($table1, $table2);
} else {
debug(3,"table '$name' dropped");
push @changes, "DROP TABLE $name;\n\n"
unless $self->{opts}{'only-both'} || $self->{opts}{'keep-old-tables'};
}
}

for my $table2 ($self->db2->tables()) {
my $name = $table2->name();
debug(4, "table 2 $name = ".Dumper($table2));
if ($table_re && $name !~ $table_re) {
debug(2,"table '$name' matched $self->{opts}{'table-re'}; ignoring");
next;
}
if (! $self->db1->table_by_name($name)) {
debug(3,"table '$name' added");
debug(4,"table '$name' added '".$table2->def()."'");
push @changes, $table2->def() . "\n"
unless $self->{opts}{'only-both'};
}
}

debug(4,join '', @changes);

my $out = '';
if (@changes) {
$out .= $self->_diff_banner();
$out .= join '', @changes;
}
return $out;
}

# ------------------------------------------------------------------------------
# Private Methods

sub _diff_banner {
my ($self) = @_;

my $summary1 = $self->db1->summary();
my $summary2 = $self->db2->summary();

my $opt_text =
join ', ',
map { $self->{opts}{$_} eq '1' ? $_ : "$_=$self->{opts}{$_}" }
keys %{$self->{opts}};
$opt_text = "## Options: $opt_text\n" if $opt_text;

my $now = scalar localtime();
return <<EOF;
## mysqldiff $VERSION
##
## Run on $now
$opt_text##
## --- $summary1
## +++ $summary2

EOF
}

sub _diff_tables {
my $self = shift;
my @changes = (
$self->_diff_fields(@_),
$self->_diff_indices(@_),
$self->_diff_primary_key(@_),
$self->_diff_options(@_)
);

$changes[-1] =~ s/\n*$/\n/ if (@changes);
return @changes;
}

sub _diff_fields {
my ($self, $table1, $table2) = @_;

my $name1 = $table1->name();

my $fields1 = $table1->fields;
my $fields2 = $table2->fields;

return () unless $fields1 || $fields2;

my @changes;

if($fields1) {
for my $field (keys %$fields1) {
debug(3,"table1 had field '$field'");
my $f1 = $fields1->{$field};
my $f2 = $fields2->{$field};
if ($fields2 && $f2) {
if ($self->{opts}{tolerant}) {
for ($f1, $f2) {
s/ COLLATE [\w_]+//gi;
}
}
if ($f1 ne $f2) {
if (not $self->{opts}{tolerant} or
(($f1 !~ m/$f2\(\d+,\d+\)/) and
($f1 ne "$f2 DEFAULT '' NOT NULL") and
($f1 ne "$f2 NOT NULL") ))
{
debug(3,"field '$field' changed");

my $change = "ALTER TABLE $name1 CHANGE COLUMN $field $field $f2;";
$change .= " # was $f1" unless $self->{opts}{'no-old-defs'};
$change .= "\n";
push @changes, $change;
}
}
} else {
debug(3,"field '$field' removed");
my $change = "ALTER TABLE $name1 DROP COLUMN $field;";
$change .= " # was $fields1->{$field}" unless $self->{opts}{'no-old-defs'};
$change .= "\n";
push @changes, $change;
}
}
}

if($fields2) {
for my $field (keys %$fields2) {
unless($fields1 && $fields1->{$field}) {
debug(3,"field '$field' added");
push @changes, "ALTER TABLE $name1 ADD COLUMN $field $fields2->{$field};\n";
}
}
}

return @changes;
}

sub _diff_indices {
my ($self, $table1, $table2) = @_;

my $name1 = $table1->name();

my $indices1 = $table1->indices();
my $indices2 = $table2->indices();

return () unless $indices1 || $indices2;

my @changes;

if($indices1) {
for my $index (keys %$indices1) {
debug(3,"table1 had index '$index'");
my $old_type = $table1->is_unique($index) ? 'UNIQUE' :
$table1->is_fulltext($index) ? 'FULLTEXT INDEX' : 'INDEX';

if ($indices2 && $indices2->{$index}) {
if( ($indices1->{$index} ne $indices2->{$index}) or
($table1->is_unique($index) xor $table2->is_unique($index)) or
($table1->is_fulltext($index) xor $table2->is_fulltext($index)) )
{
debug(3,"index '$index' changed");
my $new_type = $table2->is_unique($index) ? 'UNIQUE' :
$table2->is_fulltext($index) ? 'FULLTEXT INDEX' : 'INDEX';

my $changes = "ALTER TABLE $name1 DROP INDEX $index;";
$changes .= " # was $old_type ($indices1->{$index})"
unless $self->{opts}{'no-old-defs'};
$changes .= "\nALTER TABLE $name1 ADD $new_type $index ($indices2->{$index});\n";
push @changes, $changes;
}
} else {
debug(3,"index '$index' removed");
my $auto = _check_for_auto_col($table2, $indices1->{$index}, 1) || '';
my $changes = $auto ? _index_auto_col($table1, $indices1->{$index}) : '';
$changes .= "ALTER TABLE $name1 DROP INDEX $index;";
$changes .= " # was $old_type ($indices1->{$index})"
unless $self->{opts}{'no-old-defs'};
$changes .= "\n";
push @changes, $changes;
}
}
}

if($indices2) {
for my $index (keys %$indices2) {
next if($indices1 && $indices1->{$index});
debug(3,"index '$index' added");
my $new_type = $table2->is_unique($index) ? 'UNIQUE' : 'INDEX';
push @changes, "ALTER TABLE $name1 ADD $new_type $index ($indices2->{$index});\n";
}
}

return @changes;
}

sub _diff_primary_key {
my ($self, $table1, $table2) = @_;

my $name1 = $table1->name();

my $primary1 = $table1->primary_key();
my $primary2 = $table2->primary_key();

return () unless $primary1 || $primary2;

my @changes;

if ($primary1 && ! $primary2) {
debug(3,"primary key '$primary1' dropped");
my $changes = _index_auto_col($table2, $primary1);
$changes .= "ALTER TABLE $name1 DROP PRIMARY KEY;";
$changes .= " # was $primary1" unless $self->{opts}{'no-old-defs'};
return ( "$changes\n" );
}

if (! $primary1 && $primary2) {
debug(3,"primary key '$primary2' added");
return ("ALTER TABLE $name1 ADD PRIMARY KEY $primary2;\n");
}

if ($primary1 ne $primary2) {
debug(3,"primary key changed");
my $auto = _check_for_auto_col($table2, $primary1) || '';
my $changes = $auto ? _index_auto_col($table2, $auto) : '';
$changes .= "ALTER TABLE $name1 DROP PRIMARY KEY;";
$changes .= " # was $primary1" unless $self->{opts}{'no-old-defs'};
$changes .= "\nALTER TABLE $name1 ADD PRIMARY KEY $primary2;\n";
$changes .= "ALTER TABLE $name1 DROP INDEX $auto;\n" if($auto);
push @changes, $changes;
}

return @changes;
}

# If we're about to drop a composite (multi-column) index, we need to
# check whether any of the columns in the composite index are
# auto_increment; if so, we have to add an index for that
# auto_increment column *before* dropping the composite index, since
# auto_increment columns must always be indexed.
sub _check_for_auto_col {
my ($table, $fields, $primary) = @_;

$fields =~ s/^\s*\((.*)\)\s*$/$1/g; # strip brackets if any
my @fields = split /\s*,\s*/, $fields;

for my $field (@fields) {
next if($table->field($field) !~ /auto_increment/i);
next if($table->isa_index($field));
next if($primary && $table->isa_primary($field));

return $field;
}

return;
}

sub _index_auto_col {
my ($table, $field) = @_;
my $name = $table->name;
return "ALTER TABLE $name ADD INDEX ($field); # auto columns must always be indexed\n";
}

sub _diff_options {
my ($self, $table1, $table2) = @_;

my $name = $table1->name();
my $options1 = $table1->options();
my $options2 = $table2->options();

return () unless $options1 || $options2;

my @changes;

if ($self->{opts}{tolerant}) {
for ($options1, $options2) {
s/ AUTO_INCREMENT=\d+//gi;
s/ COLLATE=[\w_]+//gi;
}
}

if ($options1 ne $options2) {
my $change = "ALTER TABLE $name $options2;";
$change .= " # was " . ($options1 || 'blank') unless $self->{opts}{'no-old-defs'};
$change .= "\n";
push @changes, $change;
}

return @changes;
}

sub _load_database {
my ($self, $arg, $authnum) = @_;

debug(2, "parsing arg $authnum: '$arg'\n");

my %auth;
for my $auth (qw/dbh host port user password socket/) {
$auth{$auth} = $self->{opts}{"$auth$authnum"} || $self->{opts}{$auth};
delete $auth{$auth} unless $auth{$auth};
}

if ($arg =~ /^db:(.*)/) {
return MySQL::Diff::Database->new(db => $1, auth => \%auth);
}

if ($self->{opts}{"dbh"} ||
$self->{opts}{"host$authnum"} ||
$self->{opts}{"port$authnum"} ||
$self->{opts}{"user$authnum"} ||
$self->{opts}{"password$authnum"} ||
$self->{opts}{"socket$authnum"}) {
return MySQL::Diff::Database->new(db => $arg, auth => \%auth);
}

if (-f $arg) {
return MySQL::Diff::Database->new(file => $arg, auth => \%auth);
}

my %dbs = MySQL::Diff::Database::available_dbs(%auth);
debug(2, " available databases: ", (join ', ', keys %dbs), "\n");

if ($dbs{$arg}) {
return MySQL::Diff::Database->new(db => $arg, auth => \%auth);
}

warn "'$arg' is not a valid file or database.\n";
return;
}

sub _debug_level {
my ($self,$level) = @_;
debug_level($level);
}

1;

__END__

=head1 COPYRIGHT AND LICENSE

Copyright (c) 2000-2011 Adam Spiers. All rights reserved. This
program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

=head1 SEE ALSO

L<mysqldiff>, L<MySQL::Diff::Database>, L<MySQL::Diff::Table>, L<MySQL::Diff::Utils>

=head1 AUTHOR

Adam Spiers <mysqldiff@adamspiers.org>

=cut
mysqldiff-0.43/lib/MySQL/Diff/000075500000000000000000000000001165625235700160335ustar00rootroot00000000000000mysqldiff-0.43/lib/MySQL/Diff/Database.pm000064400000000000000000000154221165625235700201010ustar00rootroot00000000000000package MySQL::Diff::Database;

=head1 NAME

MySQL::Diff::Database - Database Definition Class

=head1 SYNOPSIS

use MySQL::Diff::Database;

my $db = MySQL::Diff::Database->new(%options);
my $source = $db->source_type();
my $summary = $db->summary();
my $name = $db->name();
my @tables = $db->tables();
my $table_def = $db->table_by_name($table);

my @dbs = MySQL::Diff::Database::available_dbs();

=head1 DESCRIPTION

Parses a database definition into component parts.

=cut

use warnings;
use strict;

our $VERSION = '0.43';

# ------------------------------------------------------------------------------
# Libraries

use Carp qw(:DEFAULT);
use File::Slurp;
use IO::File;

use MySQL::Diff::Utils qw(debug);
use MySQL::Diff::Table;

# ------------------------------------------------------------------------------

=head1 METHODS

=head2 Constructor

=over 4

=item new( %options )

Instantiate the objects, providing the command line options for database
access and process requirements.

=back

=cut

sub new {
my $class = shift;
my %p = @_;
my $self = {};
bless $self, ref $class || $class;

debug(3,"\nconstructing new MySQL::Diff::Database");

my $string = _auth_args_string(%{$p{auth}});
debug(3,"auth args: $string");
$self->{_source}{auth} = $string;
$self->{_source}{dbh} = $p{dbh} if($p{dbh});

if ($p{file}) {
$self->_canonicalise_file($p{file});
} elsif ($p{db}) {
$self->_read_db($p{db});
} else {
confess "MySQL::Diff::Database::new called without db or file params";
}

$self->_parse_defs();
return $self;
}

=head2 Public Methods

=over 4

=item * source_type()

Returns 'file' if the data source is a text file, and 'db' if connected
directly to a database.

=cut

sub source_type {
my $self = shift;
return 'file' if $self->{_source}{file};
return 'db' if $self->{_source}{db};
}

=item * summary()

Provides a summary of the database.

=cut

sub summary {
my $self = shift;

if ($self->{_source}{file}) {
return "file: " . $self->{_source}{file};
} elsif ($self->{_source}{db}) {
my $args = $self->{_source}{auth};
$args =~ tr/-//d;
$args =~ s/\bpassword=\S+//;
$args =~ s/^\s*(.*?)\s*$/$1/;
my $summary = " db: " . $self->{_source}{db};
$summary .= " ($args)" if $args;
return $summary;
} else {
return 'unknown';
}
}

=item * name()

Returns the name of the database.

=cut

sub name {
my $self = shift;
return $self->{_source}{file} || $self->{_source}{db};
}

=item * tables()

Returns a list of tables for the current database.

=cut

sub tables {
my $self = shift;
return @{$self->{_tables}};
}

=item * table_by_name( $name )

Returns the table definition (see L<MySQL::Diff::Table>) for the given table.

=cut

sub table_by_name {
my ($self,$name) = @_;
return $self->{_by_name}{$name};
}

=back

=head1 FUNCTIONS

=head2 Public Functions

=over 4

=item * available_dbs()

Returns a list of the available databases.

Note that is used as a function call, not a method call.

=cut

sub available_dbs {
my %auth = @_;
my $args = _auth_args_string(%auth);

# evil but we don't use DBI because I don't want to implement -p properly
# not that this works with -p anyway ...
my $fh = IO::File->new("mysqlshow$args |") or die "Couldn't execute 'mysqlshow$args': $!\n";
my @dbs;
while (<$fh>) {
next unless /^\| ([\w-]+)/;
push @dbs, $1;
}
$fh->close() or die "mysqlshow$args failed: $!";

return map { $_ => 1 } @dbs;
}

=back

=cut

# ------------------------------------------------------------------------------
# Private Methods

sub _canonicalise_file {
my ($self, $file) = @_;

$self->{_source}{file} = $file;
debug(2,"fetching table defs from file $file");

# FIXME: option to avoid create-and-dump bit
# create a temporary database using defs from file ...
# hopefully the temp db is unique!
my $temp_db = sprintf "test_mysqldiff-temp-%d_%d_%d", time(), $$, rand();
debug(3,"creating temporary database $temp_db");

my $defs = read_file($file);
die "$file contains dangerous command '$1'; aborting.\n"
if $defs =~ /;\s*(use|((drop|create)\s+database))\b/i;

my $args = $self->{_source}{auth};
my $fh = IO::File->new("| mysql $args") or die "Couldn't execute 'mysql$args': $!\n";
print $fh "\nCREATE DATABASE \`$temp_db\`;\nUSE \`$temp_db\`;\n";
print $fh $defs;
$fh->close;

# ... and then retrieve defs from mysqldump. Hence we've used
# MySQL to massage the defs file into canonical form.
$self->_get_defs($temp_db);

debug(3,"dropping temporary database $temp_db");
$fh = IO::File->new("| mysql $args") or die "Couldn't execute 'mysql$args': $!\n";
print $fh "DROP DATABASE \`$temp_db\`;\n";
$fh->close;
}

sub _read_db {
my ($self, $db) = @_;
$self->{_source}{db} = $db;
debug(3, "fetching table defs from db $db");
$self->_get_defs($db);
}

sub _get_defs {
my ($self, $db) = @_;

my $args = $self->{_source}{auth};
my $fh = IO::File->new("mysqldump -d $args $db 2>&1 |")
or die "Couldn't read ${db}'s table defs via mysqldump: $!\n";
debug(3, "running mysqldump -d $args $db");
my $defs = $self->{_defs} = [ <$fh> ];
$fh->close;

if (grep /mysqldump: Got error: .*: Unknown database/, @$defs) {
die <<EOF;
Failed to create temporary database $db
during canonicalization. Make sure that your mysql.db table has a row
authorizing full access to all databases matching 'test\\_%', and that
the database doesn't already exist.
EOF
}
}

sub _parse_defs {
my $self = shift;

return if $self->{_tables};

debug(2, "parsing table defs");
my $defs = join '', grep ! /^\s*(\#|--|SET|\/\*)/, @{$self->{_defs}};
$defs =~ s/`//sg;
my @tables = split /(?=^\s*(?:create|alter|drop)\s+table\s+)/im, $defs;
$self->{_tables} = [];
for my $table (@tables) {
debug(4, " table def [$table]");
if($table =~ /create\s+table/i) {
my $obj = MySQL::Diff::Table->new(source => $self->{_source}, def => $table);
push @{$self->{_tables}}, $obj;
$self->{_by_name}{$obj->name()} = $obj;
}
}
}

sub _auth_args_string {
my %auth = @_;
my $args = '';
for my $arg (qw/host port user password socket/) {
$args .= " --$arg=$auth{$arg}" if $auth{$arg};
}
return $args;
}

1;

__END__

=head1 COPYRIGHT AND LICENSE

Copyright (c) 2000-2011 Adam Spiers. All rights reserved. This
program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

=head1 SEE ALSO

L<mysqldiff>, L<MySQL::Diff>, L<MySQL::Diff::Table>, L<MySQL::Diff::Utils>

=head1 AUTHOR

Adam Spiers <mysqldiff@adamspiers.org>

=cut
mysqldiff-0.43/lib/MySQL/Diff/Table.pm000064400000000000000000000156251165625235700174310ustar00rootroot00000000000000package MySQL::Diff::Table;

=head1 NAME

MySQL::Diff::Table - Table Definition Class

=head1 SYNOPSIS

use MySQL::Diff::Table

my $db = MySQL::Diff::Database->new(%options);
my $def = $db->def();
my $name = $db->name();
my $field = $db->field();
my $fields = $db->fields(); # %$fields
my $primary_key = $db->primary_key();
my $indices = $db->indices(); # %$indices
my $options = $db->options();

my $isfield = $db->isa_field($field);
my $isprimary = $db->isa_primary($field);
my $isindex = $db->isa_index($field);
my $isunique = $db->is_unique($field);
my $isfulltext = $db->is_fulltext($field);

=head1 DESCRIPTION

Parses a table definition into component parts.

=cut

use warnings;
use strict;

our $VERSION = '0.43';

# ------------------------------------------------------------------------------
# Libraries

use Carp qw(:DEFAULT);
use MySQL::Diff::Utils qw(debug);

# ------------------------------------------------------------------------------

=head1 METHODS

=head2 Constructor

=over 4

=item new( %options )

Instantiate the objects, providing the command line options for database
access and process requirements.

=cut

sub new {
my $class = shift;
my %hash = @_;
my $self = {};
bless $self, ref $class || $class;

$self->{$_} = $hash{$_} for(keys %hash);

debug(3,"\nconstructing new MySQL::Diff::Table");
croak "MySQL::Diff::Table::new called without def params" unless $self->{def};
$self->_parse;
return $self;
}

=back

=head2 Public Methods

Fuller documentation will appear here in time :)

=over 4

=item * def

Returns the table definition as a string.

=item * name

Returns the name of the current table.

=item * field

Returns the current field definition of the given field.

=item * fields

Returns an array reference to a list of fields.

=item * primary_key

Returns a hash reference to fields used as primary key fields.

=item * indices

Returns a hash reference to fields used as index fields.

=item * options

Returns the additional options added to the table definition.

=item * isa_field

Returns 1 if given field is used in the current table definition, otherwise
returns 0.

=item * isa_primary

Returns 1 if given field is defined as a primary key, otherwise returns 0.

=item * isa_index

Returns 1 if given field is used as an index field, otherwise returns 0.

=item * is_unique

Returns 1 if given field is used as unique index field, otherwise returns 0.

=item * is_fulltext

Returns 1 if given field is used as fulltext index field, otherwise returns 0.

=back

=cut

sub def { my $self = shift; return $self->{def}; }
sub name { my $self = shift; return $self->{name}; }
sub field { my $self = shift; return $self->{fields}{$_[0]}; }
sub fields { my $self = shift; return $self->{fields}; }
sub primary_key { my $self = shift; return $self->{primary_key}; }
sub indices { my $self = shift; return $self->{indices}; }
sub options { my $self = shift; return $self->{options}; }

sub isa_field { my $self = shift; return $_[0] && $self->{fields}{$_[0]} ? 1 : 0; }
sub isa_primary { my $self = shift; return $_[0] && $self->{primary}{$_[0]} ? 1 : 0; }
sub isa_index { my $self = shift; return $_[0] && $self->{indices}{$_[0]} ? 1 : 0; }
sub is_unique { my $self = shift; return $_[0] && $self->{unique}{$_[0]} ? 1 : 0; }
sub is_fulltext { my $self = shift; return $_[0] && $self->{fulltext}{$_[0]} ? 1 : 0; }

# ------------------------------------------------------------------------------
# Private Methods

sub _parse {
my $self = shift;

$self->{def} =~ s/`([^`]+)`/$1/gs; # later versions quote names
$self->{def} =~ s/\n+/\n/;
$self->{lines} = [ grep ! /^\s*$/, split /(?=^)/m, $self->{def} ];
my @lines = @{$self->{lines}};
debug(4,"parsing table def '$self->{def}'");

my $name;
if ($lines[0] =~ /^\s*create\s+table\s+(\S+)\s+\(\s*$/i) {
$self->{name} = $1;
debug(3,"got table name '$self->{name}'");
shift @lines;
} else {
croak "couldn't figure out table name";
}

while (@lines) {
$_ = shift @lines;
s/^\s*(.*?),?\s*$/$1/; # trim whitespace and trailing commas
debug(4,"line: [$_]");
if (/^PRIMARY\s+KEY\s+(.+)$/) {
my $primary = $1;
croak "two primary keys in table '$self->{name}': '$primary', '$self->{primary_key}'\n"
if $self->{primary_key};
debug(4,"got primary key $primary");
$self->{primary_key} = $primary;
$primary =~ s/\((.*?)\)/$1/;
$self->{primary}{$_} = 1 for(split(/,/, $primary));

next;
}

if (/^(KEY|UNIQUE(?: KEY)?)\s+(\S+?)(?:\s+USING\s+(?:BTREE|HASH|RTREE))?\s*\((.*)\)$/) {
my ($type, $key, $val) = ($1, $2, $3);
croak "index '$key' duplicated in table '$name'\n"
if $self->{indices}{$key};
$self->{indices}{$key} = $val;
$self->{unique}{$key} = 1 if($type =~ /unique/i);
debug(4, "got ", defined $self->{unique}{$key} ? 'unique ' : '', "index key '$key': ($val)");
next;
}

if (/^(FULLTEXT(?:\s+KEY|INDEX)?)\s+(\S+?)\s*\((.*)\)$/) {
my ($type, $key, $val) = ($1, $2, $3);
croak "FULLTEXT index '$key' duplicated in table '$name'\n"
if $self->{fulltext}{$key};
$self->{indices}{$key} = $val;
$self->{fulltext}{$key} = 1;
debug(4,"got FULLTEXT index '$key': ($val)");
next;
}

if (/^\)\s*(.*?);$/) { # end of table definition
$self->{options} = $1;
debug(4,"got table options '$self->{options}'");
last;
}

if (/^(\S+)\s*(.*)/) {
my ($field, $fdef) = ($1, $2);
croak "definition for field '$field' duplicated in table '$name'\n"
if $self->{fields}{$field};
$self->{fields}{$field} = $fdef;
debug(4,"got field def '$field': $fdef");
next;
}

croak "unparsable line in definition for table '$self->{name}':\n$_";
}

warn "table '$self->{name}' didn't have terminator\n"
unless defined $self->{options};

@lines = grep ! m{^/\*!40\d{3} .*? \*/;}, @lines;
@lines = grep ! m{^(SET |DROP TABLE)}, @lines;

warn "table '$self->{name}' had trailing garbage:\n", join '', @lines
if @lines;
}

1;

__END__

=head1 COPYRIGHT AND LICENSE

Copyright (c) 2000-2011 Adam Spiers. All rights reserved. This
program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

=head1 SEE ALSO

L<mysqldiff>, L<MySQL::Diff>, L<MySQL::Diff::Database>, L<MySQL::Diff::Utils>

=head1 AUTHOR

Adam Spiers <mysqldiff@adamspiers.org>

=cut
mysqldiff-0.43/lib/MySQL/Diff/Utils.pm000064400000000000000000000045341165625235700174770ustar00rootroot00000000000000package MySQL::Diff::Utils;

=head1 NAME

MySQL::Diff::Utils - Supporting functions for MySQL:Diff

=head1 SYNOPSIS

use MySQL::Diff::Utils qw(debug_level debug);

=head1 DESCRIPTION

Currently contains the debug message handling routines.

=cut

use warnings;
use strict;

our $VERSION = '0.43';

# ------------------------------------------------------------------------------
# Libraries

use IO::File;

# ------------------------------------------------------------------------------
# Export Components

use base qw(Exporter);
our @EXPORT_OK = qw(debug_file debug_level debug);

# ------------------------------------------------------------------------------

=head1 FUNCTIONS

=head2 Public Functions

Fuller documentation will appear here in time :)

=over 4

=item * debug_file( $file )

Accessor to set/get the current debug log file.

=item * debug_level( $level )

Accessor to set/get the current debug level for messages.

Current levels range from 1 to 4, with 1 being very brief processing messages,
2 providing high level process flow messages, 3 providing low level process
flow messages and 4 providing data dumps, etc where appropriate.

=item * debug

Writes to debug log file (if specified) and STDERR the given message, provided
is equal to or lower than the current debug level.

=back

=cut

{
my $debug_file;
my $debug_level = 0;

sub debug_file {
my ($new_debug_file) = @_;
$debug_file = $new_debug_file if defined $new_debug_file;
return $debug_file;
}

sub debug_level {
my ($new_debug_level) = @_;
$debug_level = $new_debug_level if defined $new_debug_level;
return $debug_level;
}

sub debug {
my $level = shift;
return unless($debug_level >= $level && @_);

if($debug_file) {
if(my $fh = IO::File->new($debug_file, 'a+')) {
print $fh @_,"\n";
$fh->close;
return;
}
}

print STDERR @_,"\n";
}
}

1;

__END__

=head1 COPYRIGHT AND LICENSE

Copyright (c) 2000-2011 Adam Spiers. All rights reserved. This
program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

=head1 SEE ALSO

L<mysqldiff>, L<MySQL::Diff>, L<MySQL::Diff::Database>, L<MySQL::Diff::Table>

=head1 AUTHOR

Adam Spiers <mysqldiff@adamspiers.org>

=cut
mysqldiff-0.43/t/000075500000000000000000000000001165625235700137135ustar00rootroot00000000000000mysqldiff-0.43/t/01use.t000064400000000000000000000003071165625235700150350ustar00rootroot00000000000000#!/usr/bin/perl -w
use strict;

use Test::More tests => 4;

BEGIN {
use_ok( 'MySQL::Diff' );
use_ok( 'MySQL::Diff::Database' );
use_ok( 'MySQL::Diff::Table' );
use_ok( 'MySQL::Diff::Utils' );
}

mysqldiff-0.43/t/90podtest.t000064400000000000000000000004141165625235700157320ustar00rootroot00000000000000use Test::More;

# Skip if doing a regular install
plan skip_all => "Author tests not required for installation"
unless ( $ENV{AUTOMATED_TESTING} );

eval "use Test::Pod 1.00";
plan skip_all => "Test::Pod 1.00 required for testing POD" if $@;
all_pod_files_ok();

mysqldiff-0.43/t/91podcover.t000064400000000000000000000004531165625235700160750ustar00rootroot00000000000000use Test::More;

# Skip if doing a regular install
plan skip_all => "Author tests not required for installation"
unless ( $ENV{AUTOMATED_TESTING} );

eval "use Test::Pod::Coverage 0.08";
plan skip_all => "Test::Pod::Coverage 0.08 required for testing POD coverage" if $@;
all_pod_coverage_ok();
mysqldiff-0.43/t/94metatest.t000064400000000000000000000011661165625235700161070ustar00rootroot00000000000000use Test::More;
use MySQL::Diff;

# Skip if doing a regular install
plan skip_all => "Author tests not required for installation"
unless ( $ENV{AUTOMATED_TESTING} );

eval "use Test::CPAN::Meta 0.16";
plan skip_all => "Test::CPAN::Meta 0.16 required for testing META.yml" if $@;

plan no_plan;

my $yaml = meta_spec_ok(undef,undef,@_);

is($yaml->{version},$MySQL::Diff::VERSION,
'META.yml distribution version matches');

if($yaml->{provides}) {
for my $mod (keys %{$yaml->{provides}}) {
is($yaml->{provides}{$mod}{version},$MySQL::Diff::VERSION,
"META.yml entry [$mod] version matches");
}
}
mysqldiff-0.43/t/all.t000075500000000000000000000241551165625235700146620ustar00rootroot00000000000000#!/usr/bin/perl -w

use strict;

use Test::More;
use MySQL::Diff;
use MySQL::Diff::Database;

my $TEST_USER = 'test';
my @VALID_ENGINES = qw(MyISAM InnoDB);
my $VALID_ENGINES = join '|', @VALID_ENGINES;

my %tables = (
foo1 => '
CREATE TABLE foo (
id INT(11) NOT NULL auto_increment,
foreign_id INT(11) NOT NULL,
PRIMARY KEY (id)
);
',

foo2 => '
# here be a comment

CREATE TABLE foo (
id INT(11) NOT NULL auto_increment,
foreign_id INT(11) NOT NULL, # another random comment
field BLOB,
PRIMARY KEY (id)
);
',

foo3 => '
CREATE TABLE foo (
id INT(11) NOT NULL auto_increment,
foreign_id INT(11) NOT NULL,
field TINYBLOB,
PRIMARY KEY (id)
);
',

foo4 => '
CREATE TABLE foo (
id INT(11) NOT NULL auto_increment,
foreign_id INT(11) NOT NULL,
field TINYBLOB,
PRIMARY KEY (id, foreign_id)
);
',

bar1 => '
CREATE TABLE bar (
id INT AUTO_INCREMENT NOT NULL PRIMARY KEY,
ctime DATETIME,
utime DATETIME,
name CHAR(16),
age INT
);
',

bar2 => '
CREATE TABLE bar (
id INT AUTO_INCREMENT NOT NULL PRIMARY KEY,
ctime DATETIME,
utime DATETIME, # FOO!
name CHAR(16),
age INT,
UNIQUE (name, age)
);
',

bar3 => '
CREATE TABLE bar (
id INT AUTO_INCREMENT NOT NULL PRIMARY KEY,
ctime DATETIME,
utime DATETIME,
name CHAR(16),
age INT,
UNIQUE (id, name, age)
);
',

baz1 => '
CREATE TABLE baz (
firstname CHAR(16),
surname CHAR(16)
);
',

baz2 => '
CREATE TABLE baz (
firstname CHAR(16),
surname CHAR(16),
UNIQUE (firstname, surname)
);
',

baz3 => '
CREATE TABLE baz (
firstname CHAR(16),
surname CHAR(16),
KEY (firstname, surname)
);
',
);

my %tests = (
'add column' =>
[
{},
@tables{qw/foo1 foo2/},
'## mysqldiff <VERSION>
##
## Run on <DATE>
##
## --- file: tmp.db1
## +++ file: tmp.db2

ALTER TABLE foo ADD COLUMN field blob;
',
],

'drop column' =>
[
{},
@tables{qw/foo2 foo1/},
'## mysqldiff <VERSION>
##
## Run on <DATE>
##
## --- file: tmp.db1
## +++ file: tmp.db2

ALTER TABLE foo DROP COLUMN field; # was blob
',
],

'change column' =>
[
{},
@tables{qw/foo2 foo3/},
'## mysqldiff <VERSION>
##
## Run on <DATE>
##
## --- file: tmp.db1
## +++ file: tmp.db2

ALTER TABLE foo CHANGE COLUMN field field tinyblob; # was blob
'
],

'no-old-defs' =>
[
{ 'no-old-defs' => 1 },
@tables{qw/foo2 foo1/},
'## mysqldiff <VERSION>
##
## Run on <DATE>
## Options: no-old-defs
##
## --- file: tmp.db1
## +++ file: tmp.db2

ALTER TABLE foo DROP COLUMN field;
',
],

'add table' =>
[
{ },
$tables{foo1}, $tables{foo2} . $tables{bar1},
'## mysqldiff <VERSION>
##
## Run on <DATE>
##
## --- file: tmp.db1
## +++ file: tmp.db2

ALTER TABLE foo ADD COLUMN field blob;
CREATE TABLE bar (
id int(11) NOT NULL auto_increment,
ctime datetime default NULL,
utime datetime default NULL,
name char(16) default NULL,
age int(11) default NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

',
],

'drop table' =>
[
{ },
$tables{foo1} . $tables{bar1}, $tables{foo2},
'## mysqldiff <VERSION>
##
## Run on <DATE>
##
## --- file: tmp.db1
## +++ file: tmp.db2

DROP TABLE bar;

ALTER TABLE foo ADD COLUMN field blob;
',
],

'only-both' =>
[
{ 'only-both' => 1 },
$tables{foo1} . $tables{bar1}, $tables{foo2},
'## mysqldiff <VERSION>
##
## Run on <DATE>
## Options: only-both
##
## --- file: tmp.db1
## +++ file: tmp.db2

ALTER TABLE foo ADD COLUMN field blob;
',
],

'keep-old-tables' =>
[
{ 'keep-old-tables' => 1 },
$tables{foo1} . $tables{bar1}, $tables{foo2},
'## mysqldiff <VERSION>
##
## Run on <DATE>
## Options: keep-old-tables
##
## --- file: tmp.db1
## +++ file: tmp.db2

ALTER TABLE foo ADD COLUMN field blob;
',
],

'table-re' =>
[
{ 'table-re' => 'ba' },
$tables{foo1} . $tables{bar1} . $tables{baz1},
$tables{foo2} . $tables{bar2} . $tables{baz2},
'## mysqldiff <VERSION>
##
## Run on <DATE>
## Options: table-re=ba
##
## --- file: tmp.db1
## +++ file: tmp.db2

ALTER TABLE bar ADD UNIQUE name (name,age);
ALTER TABLE baz ADD UNIQUE firstname (firstname,surname);
',
],

'drop primary key with auto weirdness' =>
[
{},
$tables{foo3},
$tables{foo4},
'## mysqldiff <VERSION>
##
## Run on <DATE>
##
## --- file: tmp.db1
## +++ file: tmp.db2

ALTER TABLE foo ADD INDEX (id); # auto columns must always be indexed
ALTER TABLE foo DROP PRIMARY KEY; # was (id)
ALTER TABLE foo ADD PRIMARY KEY (id,foreign_id);
ALTER TABLE foo DROP INDEX id;
',
],

'drop additional primary key' =>
[
{},
$tables{foo4},
$tables{foo3},
'## mysqldiff <VERSION>
##
## Run on <DATE>
##
## --- file: tmp.db1
## +++ file: tmp.db2

ALTER TABLE foo ADD INDEX (id); # auto columns must always be indexed
ALTER TABLE foo DROP PRIMARY KEY; # was (id,foreign_id)
ALTER TABLE foo ADD PRIMARY KEY (id);
ALTER TABLE foo DROP INDEX id;
',
],

'unique changes' =>
[
{},
$tables{bar1},
$tables{bar2},
'## mysqldiff <VERSION>
##
## Run on <DATE>
##
## --- file: tmp.db1
## +++ file: tmp.db2

ALTER TABLE bar ADD UNIQUE name (name,age);
',
],

'drop index' =>
[
{},
$tables{bar2},
$tables{bar1},
'## mysqldiff <VERSION>
##
## Run on <DATE>
##
## --- file: tmp.db1
## +++ file: tmp.db2

ALTER TABLE bar DROP INDEX name; # was UNIQUE (name,age)
',
],

'alter indices' =>
[
{},
$tables{bar2},
$tables{bar3},
'## mysqldiff <VERSION>
##
## Run on <DATE>
##
## --- file: tmp.db1
## +++ file: tmp.db2

ALTER TABLE bar DROP INDEX name; # was UNIQUE (name,age)
ALTER TABLE bar ADD UNIQUE id (id,name,age);
',
],

'alter indices 2' =>
[
{},
$tables{bar3},
$tables{bar2},
'## mysqldiff <VERSION>
##
## Run on <DATE>
##
## --- file: tmp.db1
## +++ file: tmp.db2

ALTER TABLE bar DROP INDEX id; # was UNIQUE (id,name,age)
ALTER TABLE bar ADD UNIQUE name (name,age);
',
],

'add unique index' =>
[
{},
$tables{bar1},
$tables{bar3},
'## mysqldiff <VERSION>
##
## Run on <DATE>
##
## --- file: tmp.db1
## +++ file: tmp.db2

ALTER TABLE bar ADD UNIQUE id (id,name,age);
',
],

'drop unique index' =>
[
{},
$tables{bar3},
$tables{bar1},
'## mysqldiff <VERSION>
##
## Run on <DATE>
##
## --- file: tmp.db1
## +++ file: tmp.db2

ALTER TABLE bar DROP INDEX id; # was UNIQUE (id,name,age)
',
],

'alter unique index' =>
[
{},
$tables{baz2},
$tables{baz3},
'## mysqldiff <VERSION>
##
## Run on <DATE>
##
## --- file: tmp.db1
## +++ file: tmp.db2

ALTER TABLE baz DROP INDEX firstname; # was UNIQUE (firstname,surname)
ALTER TABLE baz ADD INDEX firstname (firstname,surname);
',
],

'alter unique index 2' =>
[
{},
$tables{baz3},
$tables{baz2},
'## mysqldiff <VERSION>
##
## Run on <DATE>
##
## --- file: tmp.db1
## +++ file: tmp.db2

ALTER TABLE baz DROP INDEX firstname; # was INDEX (firstname,surname)
ALTER TABLE baz ADD UNIQUE firstname (firstname,surname);
',
],
);

my $BAIL = check_setup();
plan skip_all => $BAIL if($BAIL);

my $total = scalar(keys %tests) * 5;
plan tests => $total;

use Data::Dumper;

my @tests = (keys %tests); #keys %tests

{
my %debug = ( debug_file => 'debug.log', debug => 9 );
unlink $debug{debug_file};

for my $test (@tests) {
note( "Testing $test\n" );

my ($opts, $db1_defs, $db2_defs, $expected) = @{$tests{$test}};

note("test=".Dumper($tests{$test}));

my $diff = MySQL::Diff->new(%$opts, %debug);
isa_ok($diff,'MySQL::Diff');

my $db1 = get_db($db1_defs, 1);
my $db2 = get_db($db2_defs, 2);

my $d1 = $diff->register_db($db1, 1);
my $d2 = $diff->register_db($db2, 2);
note("d1=" . Dumper($d1));
note("d2=" . Dumper($d2));

isa_ok($d1, 'MySQL::Diff::Database');
isa_ok($d2, 'MySQL::Diff::Database');

my $diffs = $diff->diff();
$diffs =~ s/^## mysqldiff [\d.]+/## mysqldiff <VERSION>/m;
$diffs =~ s/^## Run on .*/## Run on <DATE>/m;
$diffs =~ s{/\*!40\d{3} .*? \*/;\n*}{}m;
$diffs =~ s/ *$//gm;
for ($diffs, $expected) {
s/ default\b/ DEFAULT/gi;
s/PRIMARY KEY +\(/PRIMARY KEY (/g;
s/auto_increment/AUTO_INCREMENT/gi;
}

my $engine = 'InnoDB';
my $ENGINE_RE = qr/ENGINE=($VALID_ENGINES)/;
if ($diffs =~ $ENGINE_RE) {
$engine = $1;
$expected =~ s/$ENGINE_RE/ENGINE=$engine/g;
}

note("diffs = " . Dumper($diffs));
note("expected = " . Dumper($expected));

is_deeply($diffs, $expected, ".. expected differences for $test");

# Now test that $diffs correctly patches $db1_defs to $db2_defs.
my $patched = get_db($db1_defs . "\n" . $diffs, 1);
$diff->register_db($patched, 1);
is_deeply($diff->diff(), '', ".. patched differences for $test");
}
}


sub get_db {
my ($defs, $num) = @_;

note("defs=$defs");

my $file = "tmp.db$num";
open(TMP, ">$file") or die "open: $!";
print TMP $defs;
close(TMP);
my $db = MySQL::Diff::Database->new(file => $file, auth => { user => $TEST_USER });
unlink $file;
return $db;
}

sub check_setup {
my $failure_string = "Cannot proceed with tests without ";
_output_matches("mysql --help", qr/--password/) or
return $failure_string . 'a MySQL client';
_output_matches("mysqldump --help", qr/--password/) or
return $failure_string . 'mysqldump';
_output_matches("echo status | mysql -u $TEST_USER 2>&1", qr/Connection id:/) or
return $failure_string . 'a valid connection';
return '';
}

sub _output_matches {
my ($cmd, $re) = @_;
my ($exit, $out) = _run($cmd);

my $issue;
if (defined $exit) {
if ($exit == 0) {
$issue = "Output from '$cmd' didn't match /$re/:\n$out" if $out !~ $re;
}
else {
$issue = "'$cmd' exited with status code $exit";
}
}
else {
$issue = "Failed to execute '$cmd'";
}

if ($issue) {
warn $issue, "\n";
return 0;
}
return 1;
}

sub _run {
my ($cmd) = @_;
unless (open(CMD, "$cmd|")) {
return (undef, "Failed to execute '$cmd': $!\n");
}
my $out = join '', <CMD>;
close(CMD);
return ($?, $out);
}
 
projeto & código: Vladimir Lettiev aka crux © 2004-2005, Andrew Avramenko aka liks © 2007-2008
mantenedor atual: Michael Shigorin
mantenedor da tradução: Fernando Martini aka fmartini © 2009