/*****************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
****************************************************************/
package org.apache.cayenne.access.dbsync;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* @since 3.0
*/
class SchemaAnalyzer {
private Map<String, String> mapTableInDB;
private List<String> tableNoInDB;
private Map<String, Collection<String>> nameSchemaMap;
private Map<String, Collection<String>> schemaNameMap;
private Map<Map<String, String>, Collection<DbAttribute>> entityTables;
private String errorMessage;
SchemaAnalyzer() {
errorMessage = null;
mapTableInDB = new HashMap<>();
tableNoInDB = new ArrayList<>();
nameSchemaMap = new HashMap<>();
schemaNameMap = new HashMap<>();
entityTables = new HashMap<>();
}
public List<String> getTableNoInDB() {
return tableNoInDB;
}
public void compareColumns(DatabaseMetaData md) throws SQLException {
for (Map.Entry<String, String> map : mapTableInDB.entrySet()) {
String schema = map.getValue();
String name = map.getKey();
try (ResultSet rs = md.getColumns(null, schema, name, null);) {
Map<String, String> schemaName = new HashMap<>();
schemaName.put(name, schema);
Collection<DbAttribute> atribute = entityTables.get(schemaName);
if (atribute == null) {
schemaName.remove(name);
schemaName.put(name, null);
atribute = entityTables.get(schemaName);
}
if (atribute != null && rs.getFetchSize() != 0) {
int countColumn = 0;
int isInEntity = 0;
while (rs.next()) {
countColumn++;
String columnName = rs.getString("COLUMN_NAME");
for (DbAttribute attr : atribute) {
if (attr.getName().equalsIgnoreCase(columnName)) {
isInEntity++;
continue;
}
}
}
if (countColumn != atribute.size()) {
errorMessage = "different number of columns in table " + name;
continue;
}
if (countColumn != isInEntity && errorMessage == null) {
errorMessage = "no columns in table " + name + " or does not match the type of column";
continue;
}
}
}
}
}
public boolean compareTables(DatabaseMetaData md, Collection<DbEntity> entities) {
boolean isIncluded = true;
for (DbEntity ent : entities) {
String name = ent.getName();
String schema = ent.getSchema();
Collection<DbAttribute> atributes = ent.getAttributes();
if (schema != null) {
if (schemaNameMap.get(schema) != null) {
Collection<String> names = schemaNameMap.get(schema);
if (names.contains(name)) {
mapTableInDB.put(name, schema);
} else {
tableNoInDB.add(name);
}
} else {
isIncluded = false;
errorMessage = "no schema " + schema + " in db";
break;
}
} else {
if (nameSchemaMap.get(name) != null
|| !ent.getDataMap().isQuotingSQLIdentifiers()
&& (nameSchemaMap.get(name.toLowerCase()) != null || nameSchemaMap.get(name.toUpperCase()) != null)) {
Collection<String> sc = nameSchemaMap.get(name);
if (sc == null) {
if (nameSchemaMap.get(name.toLowerCase()) != null) {
sc = nameSchemaMap.get(name.toLowerCase());
} else {
sc = nameSchemaMap.get(name.toUpperCase());
}
}
if (sc.size() == 1) {
mapTableInDB.put(name, sc.iterator().next());
} else {
errorMessage = " enter the schema. Table found in the schemas: ";
Iterator<String> it = sc.iterator();
String names = "";
while (it.hasNext()) {
names += it.next() + ", ";
}
errorMessage = errorMessage + names;
}
} else {
tableNoInDB.add(name);
}
}
Map<String, String> schemaName = new HashMap<>();
schemaName.put(name, schema);
entityTables.put(schemaName, atributes);
}
return isIncluded;
}
public void analyzeSchemas(List<String> schemas, DatabaseMetaData md) throws SQLException {
if (schemas.size() == 0) {
schemas.add("%");
}
for (String schema : schemas) {
Collection<String> tableInSchema = new ArrayList<>();
try (ResultSet tables = md.getTables(null, schema, null, null);) {
while (tables.next()) {
String name = tables.getString("TABLE_NAME");
if (name == null || name.startsWith("BIN$")) {
continue;
}
tableInSchema.add(name);
if (nameSchemaMap.get(name) != null) {
Collection<String> sc = nameSchemaMap.get(name);
Iterator<String> iSc = sc.iterator();
boolean inSchema = false;
while (iSc.hasNext()) {
if (iSc.next().equals(schema)) {
inSchema = true;
}
}
if (!inSchema) {
sc.add(schema);
nameSchemaMap.remove(name);
nameSchemaMap.put(name, sc);
}
} else {
Collection<String> sc = new ArrayList<>();
sc.add(schema);
nameSchemaMap.put(name, sc);
}
}
schemaNameMap.put(schema, tableInSchema);
}
}
}
public String getErrorMessage() {
return errorMessage;
}
}