Skip to content
Snippets Groups Projects
Commit 16348f0c authored by Gerhard Gonter's avatar Gerhard Gonter :speech_balloon:
Browse files

Merge branch 'master' of github.com:gonter/redmine-sync

parents b5060ec9 66c057d2
No related branches found
No related tags found
No related merge requests found
......@@ -41,6 +41,34 @@ sub sync_project
$ctx->sync_wiki ($sp_id, $dp_id);
}
sub sync_cleanup_project
{
my $ctx= shift;
my $dp_id= shift;
my $dbh= $ctx->{'dst'}->connect();
return undef unless (defined ($dbh));
=begin comment
$ctx->{'tlt'}= undef;
not ready...
my @tables= qw(watchers);
foreach my $table (@tables)
{
my $ss_0= 'FROM `syncs` WHERE table_name=? AND
my $ss_1= "SELECT id
}
$ctx->{'tlt'}= undef;
=end comment
=cut
}
=head1 TRANSLATION
Possibly the most important aspect of a synchronisation job is the
......
......@@ -11,9 +11,9 @@ use Data::Dumper;
my $show_query= 0;
my $show_fetched= 0;
sub show_fetched { shift; $show_fetched= shift; }
sub show_query { shift; $show_query= shift; }
sub verbose { shift; $show_fetched= $show_query= shift; }
sub show_fetched { shift; my $ret= $show_fetched; $show_fetched= shift; $ret; }
sub show_query { shift; my $ret= $show_query; $show_query= shift; $ret }
sub verbose { shift; my @ret= ($show_fetched, $show_query); $show_fetched= $show_query= shift; @ret; }
sub connect
{
......@@ -22,10 +22,13 @@ sub connect
my $dbh= $self->{'_dbh'};
return $dbh if (defined ($dbh));
my $db_con= join (':', 'dbi', map { $self->{$_} } qw(adapter database host));
print "db_con=[$db_con]\n";
my $adapter= $self->{adapter};
$adapter= 'mysql' if ($adapter eq 'mysql2');
my $db_con= join (':', 'dbi', $adapter, map { $self->{$_} } qw(database host));
# print "db_con=[$db_con]\n";
$dbh= DBI->connect($db_con, map { $self->{$_} } qw(username password));
print "dbh=[$dbh]\n";
# print "dbh=[$dbh]\n";
$self->{'_dbh'}= $dbh;
}
......@@ -40,7 +43,7 @@ sub table
$t;
}
=head2 $con->get_all_x ($table_name, $query_ref)
=head2 $con->get_all_x ($table_name, $query_ref, $field_ref)
Query_ref is an array reference where the first parameter gives the WHERE clause (without the string "WHERE").
The query should not contain untrustable values, these should be indicated by placeholders (an "?" for each
......@@ -57,14 +60,12 @@ sub get_all_x
my $self= shift;
my $table= shift;
my $where= shift;
my $field_ref= shift || '*';
my $dbh= $self->connect();
return undef unless (defined ($dbh));
# my $project= new Redmine::DB::Project (%par);
# print "project: ", Dumper ($project);
my $ss= "SELECT * FROM $table";
my $ss= "SELECT $field_ref FROM $table";
my @v= ();
if (defined ($where))
......@@ -88,55 +89,130 @@ sub get_all_x
my $t= $self->table($table);
my $tt= {};
my $pri= (exists ($self->{PRI}->{$table})) ? $self->{PRI}->{$table} : 'id';
while (defined (my $x= $sth->fetchrow_hashref()))
{
print "x: ", Dumper ($x) if ($show_fetched);
my $i= $x->{'id'};
my $i= $x->{$pri};
$t->{$i}= $tt->{$i}= $x;
}
$tt;
}
sub fetch_custom
sub delete_all_x
{
my $db= shift;
my $cfid= shift;
my $cfty= shift || 'Issue';
my $self= shift;
my $table= shift;
my $where= shift;
my $field_ref= shift || '*';
my $res= $db->get_all_x ('custom_values',
[ "custom_field_id=? and customized_type=?", $cfid, $cfty ]);
$res;
my $dbh= $self->connect();
return undef unless (defined ($dbh));
my $ss= "DELETE $field_ref FROM $table";
my @v= ();
if (defined ($where))
{
# print "where: ", Dumper ($where) if ($show_query);
$ss .= ' WHERE ' . shift (@$where);
@v= @$where;
}
if ($show_query)
{
print "ss=[$ss]";
print ' vars: ', join (',', @v) if (@v);
print "\n";
}
my $sth= $dbh->prepare($ss) or print $dbh->errstr;
# print "sth=[$sth]\n";
$sth->execute(@v);
}
sub change_custom_value
sub tables
{
my $db= shift;
my $cfid= shift;
my $cfty= shift || 'Issue';
my $cfref= shift; # ticket number or whatever
my $cfrid= shift; # record id
my $cfval= shift;
my $self= shift;
my $data=
{ customized_type => $cfty, customized_id => $cfref,
custom_field_id => $cfid, value => $cfval };
my $dbh= $self->connect();
return undef unless (defined ($dbh));
print "change_custom_value: cfrid=[$cfrid] ", join (' ', %$data), "\n";
# return 0; # TODO: add flag to supress changes
my $ss= "SHOW TABLES";
my $res;
if (defined ($cfrid))
if ($show_query)
{
$db->update ('custom_values', $cfrid, $data);
$res= $cfrid;
print "ss=[$ss]\n";
}
else
my $sth= $dbh->prepare($ss) or print $dbh->errstr;
# print "sth=[$sth]\n";
$sth->execute();
my $table_filter= $self->{table_filter};
my $table_names= $self->{table_names}= {};
while (defined (my $table_name= $sth->fetchrow_array()))
{
$res= $db->insert ('custom_values', $data);
next if (defined ($table_filter) && &$table_filter($table_name) == 0);
$table_names->{$table_name}= undef;
}
$res;
$table_names;
}
sub desc_all
{
my $self= shift;
my $table_names= $self->tables();
foreach my $table_name (sort keys %$table_names)
{
$self->desc($table_name);
}
}
sub desc
{
my $self= shift;
my $table= shift;
my $dbh= $self->connect();
return undef unless (defined ($dbh));
my $ss= "DESC `$table`";
if ($show_query)
{
print "ss=[$ss]\n";
}
my $sth= $dbh->prepare($ss) or print $dbh->errstr;
# print "sth=[$sth]\n";
$sth->execute();
# get table definition
my $td= $self->{table_names}->{$table};
my $td= $self->{table_names}->{$table}= {} unless (defined ($td));
my $tt= $td->{'columns'}= [];
# my @desc_columns= qw(Field Type Null Key Default Extra);
while (defined (my @x= $sth->fetchrow_array()))
{
last unless (@x);
# print "x: ", Dumper (\@x); # if ($show_fetched);
push (@$tt, \@x);
if ($x[3] eq 'PRI')
{
$self->{PRI}->{$table}= $x[0];
}
}
$td;
}
sub insert
......@@ -208,7 +284,8 @@ sub mysql
my $self= shift;
print "self: ", Dumper ($self);
my @cmd= ('mysql', '-h', $self->{'host'}, '-u', $self->{'username'}, $self->{'database'}, '--password='.$self->{'password'});
$ENV{MYSQL_PWD}= $self->{password};
my @cmd= ('mysql', '-h', $self->{'host'}, '-u', $self->{'username'}, $self->{'database'});
print ">> cmd=[", join (' ', @cmd), "]\n";
system (@cmd);
}
......@@ -378,6 +455,47 @@ sub pcx_user_preferences
$res;
}
sub fetch_custom
{
my $db= shift;
my $cfid= shift;
my $cfty= shift || 'Issue';
my $res= $db->get_all_x ('custom_values',
[ "custom_field_id=? and customized_type=?", $cfid, $cfty ]);
$res;
}
sub change_custom_value
{
my $db= shift;
my $cfid= shift;
my $cfty= shift || 'Issue';
my $cfref= shift; # ticket number or whatever
my $cfrid= shift; # record id
my $cfval= shift;
my $data=
{ customized_type => $cfty, customized_id => $cfref,
custom_field_id => $cfid, value => $cfval };
print "change_custom_value: cfrid=[$cfrid] ", join (' ', %$data), "\n";
# return 0; # TODO: add flag to supress changes
my $res;
if (defined ($cfrid))
{
$db->update ('custom_values', $cfrid, $data);
$res= $cfrid;
}
else
{
$res= $db->insert ('custom_values', $data);
}
$res;
}
1;
__END__
......
......@@ -9,13 +9,15 @@ use warnings;
=head1 DESCRIPTION
Do stuff on a Redmine database. Used as an experimental sync-tool to
migrate individual projects to another server.
Do stuff on a Redmine MySQL database. Used as an experimental sync-tool
to migrate individual projects to another instance.
See https://github.com/gonter/redmine-sync
=head1 OPERATION MODES
--mysql <src|dst> ... connect to source or destinance instance's MySQL databae
=cut
use lib 'lib';
......@@ -56,14 +58,25 @@ my $setup=
'src' =>
{
'config' => '/home/gg/etc/src/database.yml',
'db' => 'production',
'db' => 'production',
'attachment_base' => '/home/backup/redmine-phaidra/files',
'attachment_with_directory' => 0, # Redmine version 1.x does not have that attribute
},
'dst' =>
{
'config' => '/home/gg/etc/dst/database.yml',
'db' => 'production',
'db' => 'production',
'attachment_base' => '/var/lib/redmine/default/files',
'attachment_with_directory' => 1, # Redmine version 2.x has that attribute
},
};
my $setup_OLD=
{
'dst' =>
{
'config' => '/home/gg/etc/dst/database.yml',
'db' => 'production',
'attachment_base' => '/var/lib/redmine/default/files',
'attachment_with_directory' => 1, # Redmine version 2.x has that attribute
},
......@@ -105,6 +118,7 @@ while (my $arg= shift (@ARGV))
foreach my $opt (@opts)
{
if ($opt eq 'h') { usage(); }
elsif ($opt eq 'X') { $setup= $setup_OLD; }
else { die "unknown option [$opt]"; }
}
}
......@@ -156,7 +170,7 @@ elsif ($op_mode eq 'prep')
my $dst= read_configs($setup, 'dst');
prepare_sync_table ($dst);
}
elsif ($op_mode eq 'sdp') # sdp: show destination intance's projects
elsif ($op_mode eq 'sdp') # sdp: show destination instance's projects
{
my $dst= read_configs($setup, 'dst');
......@@ -184,8 +198,10 @@ elsif ($op_mode eq 'user')
{
my $target= shift (@parameters);
usage() unless (defined ($target));
my $an= shift (@parameters);
usage() unless (defined ($an));
my $cfg= read_configs($setup, $target);
foreach my $av (@parameters)
......@@ -210,6 +226,21 @@ elsif ($op_mode eq 'syncuser')
$ctx->sync_user ($s_user_id, $res->{$s_user_id});
}
}
elsif ($op_mode eq 'cleanup')
{
my $dst= read_configs($setup, 'dst');
my $ctx= new Redmine::DB::CTX ('ctx_id' => $setup->{'sync_context_id'}, 'dst' => $dst);
foreach my $sp (@{$setup->{'sync_projects'}})
{
# print "sp: ", Dumper ($sp);
print "not yet implemented\n";
# $ctx->sync_cleanup_project ($sp->{'dst_proj'});
}
print "\n"x3, '='x72, "\n", "Statistics:", Dumper ($ctx->{'stats'});
}
elsif ($op_mode eq 'sync')
{
my $src= read_configs($setup, 'src');
......@@ -269,7 +300,7 @@ sub read_configs
# $ss->{'_cfg'}=
my $c= $x->{$db};
$c->{'adapter'}= 'mysql' if ($c->{'adapter'} eq 'mysql2');
# $c->{'adapter'}= 'mysql' if ($c->{'adapter'} eq 'mysql2');
my $m= new Redmine::DB::MySQL (%$c);
$ss->{'m'}= $m;
......@@ -293,3 +324,12 @@ sub prepare_sync_table
$ddl_syncs,
"--- >8 ---\n";
}
__END__
=head1 TODO
* currently, only MySQL source and targets were used, supporting
PostgreSQL and/or sqlite would be nice as well
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment