package clone;

# This file is part of sqlsus
#
# Copyright (c) 2008-2011 Jérémy Ruffet (sativouf)
# http://sqlsus.sourceforge.net/
#
# Licensed under GPLv3+: GNU GPL version 3 or later
# See LICENSE file or http://www.gnu.org/licenses/gpl.html

use strict;
use warnings;

##################################
sub table {
	my $table = shift;
	my $columns = shift;

	my $interrupt;

	my @cols;
	if ($columns eq "*") {
		@cols = &db::query("SELECT column_name FROM databases WHERE database_name = ? AND table_name = ?", $main::database, $table);
		if (not @cols) {
			&msg::error("Impossible to determine the columns for \"$table\"... Please \"get columns $table\" first, or specify columns.");
			return;
		} else {
			$columns = join ',', @cols;
		}
	}
	my $clonedb = "$main::dumpdbbase.$main::database.db";
	my $dbhout = DBI->connect("dbi:SQLite:dbname=$clonedb");
	$dbhout->do("CREATE TABLE IF NOT EXISTS $table ($columns)");
	my ($rows_already_fetched) = $dbhout->selectrow_array("SELECT count(*) FROM $table");
	$dbhout->disconnect;

	if ($rows_already_fetched) {
		&msg::info("Cloning $main::database.$table (resuming at row $rows_already_fetched)");
	} else {
		&msg::info("Cloning $main::database.$table");
	}

	my ($table_lines_count) = &db::query("SELECT count FROM databases WHERE database_name = ? AND table_name = ?", $main::database, $table);
	$table_lines_count = $conf::top_row unless $table_lines_count;

	return 0 if $rows_already_fetched == $table_lines_count;

	my $log_table = &select::mass_query("SELECT $columns FROM $table LIMIT $rows_already_fetched,$table_lines_count");
	return 0 unless $log_table;

	$dbhout = DBI->connect("dbi:SQLite:dbname=$clonedb","","", {AutoCommit => 0});
	my $dbhin = DBI->connect("dbi:SQLite:dbname=$main::logdb","","");

	my ($limit) = $dbhin->selectrow_array("SELECT count(*) FROM $log_table");

	my @header = &db::get_header($log_table);

	if (&main::is_interrupted() and $conf::blind) {
		&msg::info("Interrupted while cloning in blind mode, only storing the first completed rows");
		my @real_columns = map { $_ = &db::get_real_column($log_table,$_) } @header;
		my $i;
		for $i (0..$limit) {
			my ($row_has_null) = &db::query("SELECT count(*) FROM $log_table WHERE row = $i AND (" . join (' IS NULL OR ', @real_columns) . " IS NULL)");
			if ($row_has_null) {
				$limit = $i;
				last;
			}
		}
	}

	# remove some unwanted chars
	for (@header) {
		s/[\\'"\x00\(\)]//g;
	}
	my $sthin = $dbhin->prepare("SELECT * FROM $log_table ORDER BY ABS(row) LIMIT $limit");
	$sthin->execute();

	while (my @row = $sthin->fetchrow_array) {
		my @placeholders;
		my @values;

		shift @row;
		for my $value (@row) {
			if (defined $value and $value eq "\x06") {
				push @placeholders, "NULL";
			} else {
				push @placeholders, "?";
				push @values, $value;
			}
		}
		$dbhout->do("INSERT INTO $table VALUES(" . join(',', @placeholders) . ")", undef, @values);
	}

	$dbhout->commit();
	&db::drop_table($log_table);
}


##################################
sub command {
	my $args = shift;

	my $table;
	my $columns = "*";

	if (not $main::database) {
		&msg::error("Current database not defined, use \"start\" first");
		return 0;
	}

	if ($args) {
		if ($args =~ /^(\S+)(\s+.*)?$/) {
			$table = $1;
			$columns = $2 if $2;
			$columns =~ s/\s+//;
		}
	}

	my @tables;
	if ($table) {
		push @tables, $table;
	} else {
		@tables = &db::query("SELECT DISTINCT table_name FROM databases WHERE database_name = ? AND table_name != ''", $main::database);
	}

	if (@tables) {
		for my $table (@tables) {
			last if &main::is_interrupted();
			&clone::table($table, $columns);
		}
	} else {
		&msg::error("No tables stored for this database. Try \"get db\" first, or specify the table and the columns to clone.");
	}
}

1
