/* * _____________________________________________________________________________ * * INAF - OATS National Institute for Astrophysics - Astronomical Observatory of * Trieste INAF - IA2 Italian Center for Astronomical Archives * _____________________________________________________________________________ * * Copyright (C) 2017 Istituto Nazionale di Astrofisica * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU 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 General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package it.inaf.ia2.tsm; import static it.inaf.ia2.tsm.TapSchema.COLUMNS_TABLE; import static it.inaf.ia2.tsm.TapSchema.KEYS_TABLE; import static it.inaf.ia2.tsm.TapSchema.KEY_COLUMNS_TABLE; import static it.inaf.ia2.tsm.TapSchema.SCHEMAS_TABLE; import static it.inaf.ia2.tsm.TapSchema.TABLES_TABLE; import it.inaf.ia2.tsm.datalayer.DBBroker; import it.inaf.ia2.tsm.model.ColumnModel; import it.inaf.ia2.tsm.model.SchemaModel; import it.inaf.ia2.tsm.model.TableModel; import it.inaf.ia2.tsm.model.TypeMapping; import it.inaf.ia2.tsm.model.TypesMapping; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.regex.Pattern; /** * Loads the TAP_SCHEMA and performs consistency checking. * * @author Sonia Zorba {@literal } */ public class TapSchemaLoader { private final TapSchema tapSchema; private final DBBroker tapSchemaDBBroker; private ConsistencyChecks consistencyChecks; public static ConsistencyChecks loadExistingTapSchema(TapSchema tapSchema) throws SQLException { TapSchemaLoader loader = new TapSchemaLoader(tapSchema); return loader.loadExistingTapSchema(); } private TapSchemaLoader(TapSchema tapSchema) { this.tapSchema = tapSchema; tapSchemaDBBroker = tapSchema.getTapSchemaDBBroker(); } private ConsistencyChecks loadExistingTapSchema() throws SQLException { consistencyChecks = new ConsistencyChecks(); loadAndCheckSchemata(); loadAndCheckTables(); loadAndCheckColumns(); loadAndCheckKeys(); checkModel(tapSchema.getTapSchemaModel()); checkObscore(); return consistencyChecks; } private void checkObscore() { if (tapSchema.isHasObscore()) { String ivoaSchemaName = tapSchema.getIvoaSchemaName(); if (tapSchema.getChild(ivoaSchemaName) == null) { if (tapSchema.isAddable(tapSchema.getRealSchemaName(ivoaSchemaName))) { consistencyChecks.setObscoreToAdd(true); } else { consistencyChecks.setMissingObscore(true); } } else { checkModel(tapSchema.getIvoaSchemaModel()); } } } private void checkModel(SchemaModel schemaModel) { String schemaName = schemaModel.getName(); if (TapSchema.STANDARD_TAP_SCHEMA_NAME.equals(schemaName)) { schemaName = tapSchema.getName(); } Schema schema = tapSchema.getChild(schemaName); for (TableModel tableModel : schemaModel.getTables()) { String tableName = tableModel.getName(); Table table = schema.getChild(tableName); if (table == null) { if (schema.isAddable(tableModel.getName())) { consistencyChecks.addTableToAdd(schema.getName(), tableName); } else { consistencyChecks.addMissingTable(schema.getName(), tableName); } } else { for (ColumnModel columnModel : tableModel.getColumns()) { Column column = table.getChild(columnModel.getName()); ColumnHolder ch = new ColumnHolder(schemaName, table.getName(), columnModel.getName()); if (column == null) { if (columnModel.isMandatory()) { if (table.isAddable(columnModel.getName())) { consistencyChecks.addColumnToAdd(ch); } else { consistencyChecks.addMissingColumn(ch, columnModel); } } else if (table.isAddable(columnModel.getName())) { consistencyChecks.addUnaddedOptionalColumn(ch); } } else { // Data type checking String originalDataType = (String) column.getMetadata(Column.ORIGINAL_DATATYPE_KEY); String modelDataType = (String) column.getMetadata(Column.DATATYPE_KEY); TypeMapping originalType = TypesMapping.getTypeMapping(originalDataType, tapSchema.getDataTypeMode()); TypeMapping modelType = TypesMapping.getTypeMapping(modelDataType, tapSchema.getDataTypeMode()); if (originalType.getJavaType() != modelType.getJavaType()) { consistencyChecks.addWrongDataType(ch, originalDataType, modelDataType, columnModel.getType(), columnModel.getSize()); } } } } } } private boolean equalsOneOf(String key, String... names) { for (String name : names) { if (name.equals(key)) { return true; } } return false; } private boolean hasFixedValue(Column column, String key) { EntityProperty ep = column.getProperty(key); if (!ep.isUpdatable()) { return true; } // Here we check fixed values for columns having a XML model. // We need to be aware that the user could alter these structures // adding custom tables and columns, so we have to check for null // parts of the models. // Schema parentSchema = column.getParent().getParent(); SchemaModel schemaModel; if (parentSchema.getName().equals(tapSchema.getName())) { if (parentSchema.getName().equals(tapSchema.getName())) { schemaModel = tapSchema.getTapSchemaModel(); } else { schemaModel = tapSchema.getIvoaSchemaModel(); } if (schemaModel != null) { TableModel tableModel = schemaModel.getTable(column.getParent().getName()); if (tableModel != null) { ColumnModel columnModel = tableModel.get(column.getName()); if (columnModel != null) { if (parentSchema.getName().equals(tapSchema.getName())) { // is a TAP_SCHEMA column return equalsOneOf(key, Column.STD_KEY, Column.PRINCIPAL_KEY); } else { // is an obscore column return equalsOneOf(key, Column.STD_KEY, Column.PRINCIPAL_KEY, Column.UCD_KEY, Column.UNIT_KEY, Column.UTYPE_KEY, Column.DATATYPE_KEY); } } } } } return false; } private Object getCompatibleIntOrBoolValue(String key, boolean value) { if (tapSchema.getTapSchemaModel().getTable(COLUMNS_TABLE).get(key).getJavaType() == Integer.class) { return value ? 1 : 0; } return value; } private Object getCorrectValue(Column column, String key) { EntityProperty ep = column.getProperty(key); if (!ep.isUpdatable()) { return column.getMetadata(key); } Schema parentSchema = column.getParent().getParent(); SchemaModel schemaModel; if (parentSchema.getName().equals(tapSchema.getName())) { schemaModel = tapSchema.getTapSchemaModel(); } else { schemaModel = tapSchema.getIvoaSchemaModel(); } ColumnModel columnModel = schemaModel.getTable(column.getParent().getName()).get(column.getName()); switch (key) { case Column.STD_KEY: return getCompatibleIntOrBoolValue(Column.STD_KEY, columnModel.isStandard()); case Column.PRINCIPAL_KEY: return getCompatibleIntOrBoolValue(Column.PRINCIPAL_KEY, columnModel.isPrincipal()); case Column.UCD_KEY: return columnModel.getUcd(); case Column.UNIT_KEY: return columnModel.getUnit(); case Column.UTYPE_KEY: return columnModel.getUtype(); case Column.DATATYPE_KEY: return TypesMapping.getDataType(columnModel.getType(), tapSchema.getDataTypeMode()); } throw new RuntimeException("Unable to retrieve correct value for " + key); } private void loadSavedProperties(TapSchemaEntity tapSchemaEntity, Map savedProperties) { for (Map.Entry entry : savedProperties.entrySet()) { String key = entry.getKey(); Object savedValue = entry.getValue(); if (tapSchemaEntity instanceof Column) { Column column = (Column) tapSchemaEntity; if (hasFixedValue(column, key)) { Object correctValue = getCorrectValue(column, key); if (!Objects.equals(savedValue, correctValue)) { InconsistentColumnProperty inconsistentValue = new InconsistentColumnProperty( new ColumnHolder(column), key, savedValue, correctValue ); consistencyChecks.addInconsistency(inconsistentValue); } } } tapSchemaEntity.initProperty(key, savedValue); } } private void loadAndCheckSchemata() throws SQLException { for (Map schemaProps : tapSchemaDBBroker.getSavedItems(tapSchema.getRealName(), tapSchema.getTableModel(SCHEMAS_TABLE))) { String schemaName = (String) schemaProps.get(Schema.SCHEMA_NAME_KEY); Schema schema = tapSchema.addChild(schemaName); if (schema == null) { consistencyChecks.addUnexistingSchema(schemaName); } else { loadSavedProperties(schema, schemaProps); schema.setStatus(Status.ADDED_PERSISTED); } } } private void loadAndCheckTables() throws SQLException { for (Map tableProps : tapSchemaDBBroker.getSavedItems(tapSchema.getRealName(), tapSchema.getTableModel(TABLES_TABLE))) { String tableCompleteName = (String) tableProps.get(Table.TABLE_NAME_KEY); String[] tableNameSplit = tableCompleteName.split(Pattern.quote(".")); String schemaName = tableNameSplit[0]; String tableName = tableNameSplit[1]; Schema schema = tapSchema.getChild(schemaName, Status.ADDED_PERSISTED); if (schema == null) { consistencyChecks.addUnexistingSchema(schemaName); } else { Table table = schema.addChild(tableName); if (table == null) { consistencyChecks.addUnexistingTable(schemaName, tableName); } else { loadSavedProperties(table, tableProps); table.setStatus(Status.ADDED_PERSISTED); } } } } private void loadAndCheckColumns() throws SQLException { for (Map columnProps : tapSchemaDBBroker.getSavedItems(tapSchema.getRealName(), tapSchema.getTableModel(COLUMNS_TABLE))) { String tableCompleteName = (String) columnProps.get(Column.TABLE_NAME_KEY); String[] tableNameSplit = tableCompleteName.split(Pattern.quote(".")); String schemaName = tableNameSplit[0]; String tableName = tableNameSplit[1]; String columnName = (String) columnProps.get(Column.COLUMN_NAME_KEY); Schema schema = tapSchema.getChild(schemaName, Status.ADDED_PERSISTED); if (schema == null) { consistencyChecks.addUnexistingSchema(schemaName); } else { Table table = schema.getChild(tableName, Status.ADDED_PERSISTED); if (table == null) { consistencyChecks.addUnexistingTable(schemaName, tableName); } else { Column column = table.addChild(columnName); if (column == null) { consistencyChecks.addUnexistingColumn(schemaName, tableName, columnName); } else { loadSavedProperties(column, columnProps); column.setStatus(Status.ADDED_PERSISTED); } } } } } private void loadAndCheckKeys() throws SQLException { List> keysProps = tapSchemaDBBroker.getSavedItems(tapSchema.getRealName(), tapSchema.getTableModel(KEYS_TABLE)); List> keysColumnsProps = tapSchemaDBBroker.getSavedItems(tapSchema.getRealName(), tapSchema.getTableModel(KEY_COLUMNS_TABLE)); for (Map keyProp : keysProps) { String fromTable = (String) keyProp.get(Key.FROM_TABLE_KEY); String targetTable = (String) keyProp.get(Key.TARGET_TABLE_KEY); String keyId = (String) keyProp.get(Key.ID_KEY); assert keyId != null; List> kcPropsById = new ArrayList<>(); for (Map kcp : keysColumnsProps) { String keyColumnId = (String) kcp.get(KeyColumn.KEY_ID_KEY); assert keyColumnId != null; if (keyColumnId.equals(keyId)) { kcPropsById.add(kcp); } } // Searching the key boolean keyFound = false; for (Key key : tapSchema.getAllKeys()) { if (key.getFromTableCompleteName().equals(fromTable) && key.getTargetTableCompleteName().equals(targetTable)) { // Search the key columns having proper key id // Verifying the matching List matchedKeyColumns = new ArrayList<>(); if (kcPropsById.size() == key.getKeyColumns().size()) { for (Map kcp : kcPropsById) { String fromColumn = (String) kcp.get(KeyColumn.FROM_COLUMN_KEY); String targetColumn = (String) kcp.get(KeyColumn.TARGET_COLUMN_KEY); for (KeyColumn keyColumn : key.getKeyColumns()) { if (keyColumn.getFromColumn().equals(fromColumn) && keyColumn.getTargetColumn().equals(targetColumn)) { matchedKeyColumns.add(keyColumn); } } } } if (kcPropsById.size() == matchedKeyColumns.size()) { keyFound = true; int index = 0; loadSavedProperties(key, keyProp); for (Map kcp : kcPropsById) { KeyColumn kc = matchedKeyColumns.get(index); loadSavedProperties(kc, kcp); index++; } } } } if (!keyFound) { boolean setKeyToRemove = true; if (setKeyToRemove) { String[] fromColumns = new String[kcPropsById.size()]; String[] targetColumns = new String[kcPropsById.size()]; int i = 0; for (Map kcp : kcPropsById) { fromColumns[i] = (String) kcp.get(KeyColumn.FROM_COLUMN_KEY); targetColumns[i] = (String) kcp.get(KeyColumn.TARGET_COLUMN_KEY); i++; } consistencyChecks.addUnexistingKey(keyId, fromTable, fromColumns, targetTable, targetColumns); } } } } }