package com.tesora.dve.upgrade.versions; /* * #%L * Tesora Inc. * Database Virtualization Engine * %% * Copyright (C) 2011 - 2014 Tesora Inc. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; import com.tesora.dve.common.DBHelper; import com.tesora.dve.common.InformationCallback; import com.tesora.dve.exceptions.PEException; import com.tesora.dve.sql.schema.VariableScopeKind; import com.tesora.dve.variables.KnownVariables; import com.tesora.dve.variables.VariableHandler; import com.tesora.dve.variables.VariableManager; import com.tesora.dve.variables.VariableOption; public class GlobalVariablesVersion extends ComplexCatalogVersion { public GlobalVariablesVersion(int version) { super(version, true); // TODO Auto-generated constructor stub } // our list of changes: // project.default_policy_id has been removed. any default policy should go on the variable. // project.default_persistent_group_id has been removed. any default pg should go on the variable. // the fks from project to persistent_group, dynamic_policy have been removed: // fk_project_def_sg, fk_project_def_policy // added the varconfig table. // remove the config table. // I believe what we should do is: // create the varconfig table // associate each row of config with one of the variables. build sess vars for any unknowns. // don't forget that we renamed a couple of variables (default_time_zone, etc.) // also, look up the default_dynamic_policy, default_persistent_group values, associate those. // load the varconfig table using variables + config. // drop the config table // drop the fks on project // alter project, remove the two columns. private static final String varconfigDefinition = "create table varconfig (id integer not null auto_increment, " +"description varchar(255), name varchar(255) not null, options varchar(255) not null, " +"scopes varchar(255) not null, " +"value varchar(255), value_type varchar(255) not null, primary key (id), unique(name)) engine=innodb"; @Override public void upgrade(DBHelper helper, InformationCallback stdout) throws PEException { // TODO Auto-generated method stub // can't really rely on the HostService here VariableManager vm = VariableManager.getManager(); try { helper.executeQuery(varconfigDefinition); } catch (SQLException sqle) { throw new PEException("Unable to create new variable table"); } Map<String,String> persistentValues = buildPersistentValues(helper); loadVariableDefinitions(helper,vm,persistentValues, stdout); // not all catalogs will have the new fk constraint names, go find them try { modifyProjectTable(helper); helper.executeQuery("drop table config"); } catch (SQLException sqle) { throw new PEException("Unable to upgrade catalog structure",sqle); } } private Map<String,String> buildPersistentValues(DBHelper helper) throws PEException { HashMap<String,String> persistentValues = new HashMap<String,String>(); try { ResultSet rs = null; try { if (helper.executeQuery("select name, value from config")) { rs = helper.getResultSet(); while(rs.next()) { String name = rs.getString(1); String value = rs.getString(2); if (value == null) value = VariableHandler.NULL_VALUE; persistentValues.put(VariableManager.normalize(name),value); } } } finally { if (rs != null) rs.close(); } } catch (SQLException sqle) { throw new PEException("Unable to obtain current config values",sqle); } // also, load the pers group and dyn policy values try { Long pgid = null; Long dpid = null; ResultSet rs = null; try { if (helper.executeQuery("select default_persistent_group_id, default_policy_id from project")) { rs = helper.getResultSet(); if (rs.next()) { pgid = rs.getLong(1); dpid = rs.getLong(2); } } } finally { if (rs != null) rs.close(); } if (pgid != null) { String name = (String) getSingleField(helper,String.format("select name from persistent_group where persistent_group_id = %d",pgid)); persistentValues.put(VariableManager.normalize(KnownVariables.PERSISTENT_GROUP.getName()),name); } if (dpid != null) { String name= (String) getSingleField(helper,String.format("select name from dynamic_policy where policy_id = %d",dpid)); persistentValues.put(VariableManager.normalize(KnownVariables.DYNAMIC_POLICY.getName()),name); } } catch (SQLException sqle) { throw new PEException("Unable to obtain current values for dynamic_policy, persistent_group",sqle); } return persistentValues; } @SuppressWarnings({ "rawtypes", "unchecked" }) private void loadVariableDefinitions(DBHelper helper, VariableManager vm, Map<String,String> persistentValues, InformationCallback stdout) throws PEException { try { helper.prepare("insert into varconfig (options, description, name, scopes, value, value_type) values (?,?,?,?,?,?)"); } catch (SQLException sqle) { throw new PEException("Unable to prepare varconfig insert",sqle); } for(VariableHandler vh : vm.getAllHandlers()) { String pValue = persistentValues.remove(VariableManager.normalize(vh.getName())); Object converted = null; if (pValue == null) { // not configured, check to see if this is one of the special cases if (vh == KnownVariables.TIME_ZONE) { pValue = persistentValues.remove(VariableManager.normalize("default_time_zone")); } } else { try { converted = vh.toInternal(pValue); } catch (PEException pe) { stdout.println("Variable " + vh.getName() + " has an incorrect value, will use default (" + vh.getDefaultOnMissing() + ") instead"); converted = null; } catch (Throwable t) { // any variable that relies on the catalog for validation is kind of pooched here, since the catalog is dead // fortunately these three variables are all string values, so let's just say they are ok if (vh == KnownVariables.DYNAMIC_POLICY || vh == KnownVariables.COLLATION_CONNECTION || vh == KnownVariables.COLLATION_DATABASE) { converted = pValue; } else { throw t; } } } if (converted == null) converted = vh.getDefaultOnMissing(); List<Object> params = new ArrayList<Object>(); params.add(VariableHandler.convertOptions(vh.getOptions())); params.add(vh.getDescription()); params.add(vh.getName()); params.add(VariableHandler.convertScopes(vh.getScopes())); params.add(vh.toRow(converted)); params.add(vh.getMetadata().getTypeName()); try { helper.executePrepared(params); } catch (SQLException sqle) { throw new PEException("Unable to insert varconfig for " + vh.getName(),sqle); } } // if there is anything left in persistentValues, treat it as an emulated session variable with string values. EnumSet<VariableScopeKind> sessionScope = EnumSet.of(VariableScopeKind.SESSION); EnumSet<VariableOption> options = EnumSet.of(VariableOption.EMULATED,VariableOption.PASSTHROUGH); for(Map.Entry<String, String> me : persistentValues.entrySet()) { List<Object> params = new ArrayList<Object>(); params.add(VariableHandler.convertOptions(options)); params.add(null); params.add(me.getKey()); params.add(VariableHandler.convertScopes(sessionScope)); params.add(me.getValue()); params.add("varchar"); try { helper.executePrepared(params); } catch (SQLException sqle) { throw new PEException("Unable to insert varconfig for " + me.getKey()); } } } private void modifyProjectTable(DBHelper helper) throws SQLException { ResultSet rs = null; List<String> names = new ArrayList<String>(); try { if (helper.executeQuery("select constraint_name " +"from information_schema.key_column_usage " +"where table_schema = 'dve_catalog' " +"and table_name = 'project' " +"and column_name in ('default_persistent_group_id','default_policy_id')")) { rs = helper.getResultSet(); while(rs.next()) names.add(rs.getString(1)); } } finally { if (rs != null) rs.close(); } for(String s : names) helper.executeQuery("alter table project drop foreign key " + s); String[] columns = new String[] { "default_policy_id", "default_persistent_group_id" }; for(String s : columns) helper.executeQuery("alter table project drop column " + s); } }