added support1: newly added columns can by any data type now. support2: able to change the primary keys for a table when xcatd starts if the schema chenages

git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/trunk@4014 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd
This commit is contained in:
linggao 2009-08-20 03:26:32 +00:00
parent c5aa017fa7
commit 15dfd3f270

View File

@ -308,33 +308,16 @@ sub buildcreatestmt
my $retv = "CREATE TABLE $tabn (\n ";
my $col;
my $types=$descr->{types};
my $pkset=0;
foreach $col (@{$descr->{cols}})
{
if (($types) && ($types->{$col})) {
if ($types->{$col} =~ /INTEGER AUTO_INCREMENT/) {
if ($xcatcfg =~ /^SQLite:/) {
$retv .= "\"$col\" INTEGER PRIMARY KEY AUTOINCREMENT ";
$pkset=1;
} elsif ($xcatcfg =~ /^Pg:/) {
$retv .= "\"$col\" SERIAL ";
} elsif ($xcatcfg =~ /^mysql:/){
$retv .= "\"$col\" INTEGER AUTO_INCREMENT ";
} elsif ($xcatcfg =~ /^db2:/){
$retv .= "\"$col\" INTEGER GENERATED ALWAYS AS IDENTITY "; #have not tested on DB2
} else {
}
} else {
$retv .= "\"$col\" " . $types->{$col};
my $datatype=get_datatype_string($col,$xcatcfg, $types);
if ($datatype eq "TEXT") {
if (isAKey(\@{$descr->{keys}}, $col)) { # keys need defined length
$datatype = "VARCHAR(128)";
}
} else {
if (isAKey(\@{$descr->{keys}},$col)) { # keys need defined length
$retv .= "\"$col\" VARCHAR(128)";
} else {
$retv .= "\"$col\" TEXT";
}
}
$retv .= "\"$col\" $datatype ";
if (grep /^$col$/, @{$descr->{required}})
{
@ -342,7 +325,7 @@ sub buildcreatestmt
}
$retv .= ",\n ";
}
if ($pkset) {
if ($retv =~ /PRIMARY KEY/) {
$retv =~ s/,\n $/\n)/;
} else {
$retv .= "PRIMARY KEY (";
@ -353,7 +336,70 @@ sub buildcreatestmt
$retv =~ s/,$/)\n)/;
}
#print "retv=$retv\n";
return $retv;
return $retv;
}
sub get_datatype_string {
my $col=shift; #column name
my $xcatcfg=shift; #db config string
my $types=shift; #hash pointer
my $ret;
if (($types) && ($types->{$col})) {
if ($types->{$col} =~ /INTEGER AUTO_INCREMENT/) {
if ($xcatcfg =~ /^SQLite:/) {
$ret = "INTEGER PRIMARY KEY AUTOINCREMENT";
} elsif ($xcatcfg =~ /^Pg:/) {
$ret = "SERIAL";
} elsif ($xcatcfg =~ /^mysql:/){
$ret = "INTEGER AUTO_INCREMENT";
} elsif ($xcatcfg =~ /^db2:/){
$ret = "INTEGER GENERATED ALWAYS AS IDENTITY"; #have not tested on DB2
} else {
}
} else {
$ret = $types->{$col};
}
} else {
$ret = "TEXT";
}
return $ret;
}
sub get_xcatcfg
{
my $xcatcfg = (defined $ENV{'XCATCFG'} ? $ENV{'XCATCFG'} : '');
unless ($xcatcfg) {
if (-r "/etc/xcat/cfgloc") {
my $cfgl;
open($cfgl,"<","/etc/xcat/cfgloc");
$xcatcfg = <$cfgl>;
close($cfgl);
chomp($xcatcfg);
$ENV{'XCATCFG'}=$xcatcfg; #Store it in env to avoid many file reads
}
}
if ($xcatcfg =~ /^$/)
{
if (-d "/opt/xcat/cfg")
{
$xcatcfg = "SQLite:/opt/xcat/cfg";
}
else
{
if (-d "/etc/xcat")
{
$xcatcfg = "SQLite:/etc/xcat";
}
}
}
($xcatcfg =~ /^$/) && die "Can't locate xCAT configuration";
unless ($xcatcfg =~ /:/)
{
$xcatcfg = "SQLite:" . $xcatcfg;
}
return $xcatcfg;
}
#--------------------------------------------------------------------------
@ -416,36 +462,8 @@ sub new
$self->{dbuser}="";
$self->{dbpass}="";
my $xcatcfg = (defined $ENV{'XCATCFG'} ? $ENV{'XCATCFG'} : '');
unless ($xcatcfg) {
if (-r "/etc/xcat/cfgloc") {
my $cfgl;
open($cfgl,"<","/etc/xcat/cfgloc");
$xcatcfg = <$cfgl>;
close($cfgl);
chomp($xcatcfg);
$ENV{'XCATCFG'}=$xcatcfg; #Store it in env to avoid many file reads
}
}
if ($xcatcfg =~ /^$/)
{
if (-d "/opt/xcat/cfg")
{
$xcatcfg = "SQLite:/opt/xcat/cfg";
}
else
{
if (-d "/etc/xcat")
{
$xcatcfg = "SQLite:/etc/xcat";
}
}
}
($xcatcfg =~ /^$/) && die "Can't locate xCAT configuration";
unless ($xcatcfg =~ /:/)
{
$xcatcfg = "SQLite:" . $xcatcfg;
}
my $xcatcfg =get_xcatcfg();
if ($xcatcfg =~ /^SQLite:/)
{
$self->{backend_type} = 'sqlite';
@ -542,7 +560,7 @@ sub new
}
updateschema($self);
updateschema($self, $xcatcfg);
} #END DB ACCESS SPECIFIC SECTION
if ($self->{tabname} eq 'nodelist')
{
@ -586,7 +604,11 @@ sub updateschema
#This determines alter table statements required..
my $self = shift;
my $xcatcfg = shift;
my $descr=$xCAT::Schema::tabspec{$self->{tabname}};
my @columns;
my %dbkeys;
if ($self->{backend_type} eq 'sqlite')
{
my $dbexistq =
@ -599,44 +621,178 @@ sub updateschema
#my $cstmt = $result->{sql};
$cstmt =~ s/.*\(//;
$cstmt =~ s/\)$//;
my @entries = split /,/, $cstmt;
#print "cstmt=$cstmt\n";
my @entries = split /\n/, $cstmt;
foreach (@entries)
{
s/VARCHAR\(\d+\)/TEXT/;
unless (/\(/)
if (/\(/)
{
my $keynames=$_;
if ($keynames =~ /PRIMARY KEY/) {
$keynames =~ s/\"//g;
$keynames =~ /\((.*)\)/;
$keynames=$1;
# print "keynames=$keynames\n";
my @keyname_arrays=split(',', $keynames);
foreach my $key_col (@keyname_arrays) {
$dbkeys{$key_col}=1;
#print "key_col=$key_col\n";
}
}
}
else
{ #Filter out the PRIMARY KEY statement, but not if on a col
my $colname = $_;
my $iskey=0;
if ($colname =~ /PRIMARY KEY/) {
$iskey=1;
}
$colname =~ s/^\s*(\S+)\s+.*\s*$/$1/
; #I don't understand why it won't work otherwise for " colname TEXT "
$colname =~ s/^"//;
$colname =~ s/"$//;
push @columns, $colname;
}
if ($iskey) { $dbkeys{$colname}=1;}
}
}
} else { #Attempt generic dbi..
#my $sth = $self->{dbh}->column_info('','',$self->{tabname},'');
my $sth = $self->{dbh}->column_info(undef,undef,$self->{tabname},'%');
while (my $cd = $sth->fetchrow_hashref) {
#print Dumper($cd);
push @columns,$cd->{'COLUMN_NAME'};
}
foreach (@columns) { #Column names may end up quoted by database engin
s/"//g;
}
#get primary keys
$sth = $self->{dbh}->primary_key_info(undef,undef,$self->{tabname});
my $data = $sth->fetchall_arrayref;
#print "data=". Dumper($data);
foreach my $cd (@$data) {
$dbkeys{$cd->[3]}=1;
}
}
#Now @columns reflects the *actual* columns in the database
my $dcol;
foreach $dcol (@{$self->{colnames}})
{
unless (grep /^$dcol$/, @columns)
{
#Now @columns reflects the *actual* columns in the database
my $dcol;
my $types=$descr->{types};
#TODO: log/notify of schema upgrade?
my $stmt =
"ALTER TABLE " . $self->{tabname} . " ADD $dcol TEXT";
$self->{dbh}->do($stmt);
}
foreach $dcol (@{$self->{colnames}})
{
unless (grep /^$dcol$/, @columns)
{
#TODO: log/notify of schema upgrade?
my $datatype=get_datatype_string($dcol, $xcatcfg, $types);
if ($datatype eq "TEXT") {
if (isAKey(\@{$descr->{keys}}, $dcol)) { # keys need defined length
$datatype = "VARCHAR(128)";
}
}
if (grep /^$dcol$/, @{$descr->{required}})
{
$datatype .= " NOT NULL";
}
my $stmt =
"ALTER TABLE " . $self->{tabname} . " ADD $dcol $datatype";
$self->{dbh}->do($stmt);
}
}
#for existing columns that are new keys now,
my @new_dbkeys=@{$descr->{keys}};
#my @old_dbkeys=keys %dbkeys;
#print "new_dbkeys=@new_dbkeys; old_dbkeys=@old_dbkeys\n";
my $change_keys=0;
foreach my $dbkey (@new_dbkeys) {
if (! exists($dbkeys{$dbkey})) {
$change_keys=1;
#for my sql, we do not have to recreate table, but we have to make sure the type is correct,
#TEXT is not a valid type for a primary key
if ($xcatcfg =~ /^mysql:/) {
my $datatype=get_datatype_string($dbkey, $xcatcfg, $types);
if ($datatype eq "TEXT") {
if (isAKey(\@{$descr->{keys}}, $dbkey)) { # keys need defined length
$datatype = "VARCHAR(128)";
}
}
if (grep /^$dbkey$/, @{$descr->{required}})
{
$datatype .= " NOT NULL";
}
my $stmt =
"ALTER TABLE " . $self->{tabname} . " MODIFY COLUMN $dbkey $datatype";
print "stmt=$stmt\n";
$self->{dbh}->do($stmt);
if ($self->{dbh}->errstr) {
xCAT::MsgUtils->message("S", "Error changing the keys for table " . $self->{tabname} .":" . $self->{dbh}->errstr);
}
}
}
}
#check for cloumns that used to be keys but now are not
if (!$change_keys) {
foreach(keys %dbkeys) {
if (! isAKey(\@new_dbkeys, $_)) {
$change_keys=1;
last;
}
}
}
#finaly drop the old keys and add the new keys
if ($change_keys) {
if ($xcatcfg =~ /^mysql:/) { #for mysql, just alter the table
my $tmp=join(',',@new_dbkeys);
my $stmt =
"ALTER TABLE " . $self->{tabname} . " DROP PRIMARY KEY, ADD PRIMARY KEY ($tmp)";
print "stmt=$stmt\n";
$self->{dbh}->do($stmt);
if ($self->{dbh}->errstr) {
xCAT::MsgUtils->message("S", "Error changing the keys for table " . $self->{tabname} .":" . $self->{dbh}->errstr);
}
} else { #for the rest, recreate the table
print "need to change keys\n";
my $tn=$self->{tabname};
my $btn=$tn . "_xcatbackup";
#remove the backup table just in case;
my $str="DROP TABLE $btn";
$self->{dbh}->do($str);
#rename the table name to name_xcatbackup
$str = "ALTER TABLE $tn RENAME TO $btn";
$self->{dbh}->do($str);
if ($self->{dbh}->errstr) {
xCAT::MsgUtils->message("S", "Error renaming the table from $tn to $btn:" . $self->{dbh}->errstr);
}
#create the table again
$str =
buildcreatestmt($tn,
$descr,
$xcatcfg);
$self->{dbh}->do($str);
if ($self->{dbh}->errstr) {
xCAT::MsgUtils->message("S", "Error recreating table $tn:" . $self->{dbh}->errstr);
}
#copy the data from backup to the table
$str = "INSERT INTO $tn SELECT * FROM $btn";
$self->{dbh}->do($str);
if ($self->{dbh}->errstr) {
xCAT::MsgUtils->message("S", "Error copying data from table $btn to $tn:" . $self->{dbh}->errstr);
} else {
#drop the backup table
$str = "DROP TABLE $btn";
$self->{dbh}->do($str);
}
}
}
}
#--------------------------------------------------------------------------