package org.aksw.sparqlify.automapper; import java.sql.Connection; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.aksw.commons.util.jdbc.Column; import org.aksw.commons.util.jdbc.ColumnsReference; import org.aksw.commons.util.jdbc.ForeignKey; import org.aksw.commons.util.jdbc.JdbcUtils; import org.aksw.commons.util.jdbc.PrimaryKey; import org.aksw.commons.util.jdbc.Relation; import org.aksw.commons.util.jdbc.Schema; import org.aksw.commons.util.strings.StringUtils; import org.aksw.jena_sparql_api.exprs_ext.E_StrConcatPermissive; import org.aksw.jena_sparql_api.views.E_RdfTerm; import org.aksw.jena_sparql_api.views.VarDef; import org.aksw.sparqlify.algebra.sql.nodes.SqlOpQuery; import org.aksw.sparqlify.config.syntax.ViewDefinition; import org.aksw.sparqlify.core.DatatypeSystemDefault; import org.aksw.sparqlify.core.DatatypeSystemOld; import org.aksw.sparqlify.core.SqlDatatype; import org.apache.jena.graph.Node; import org.apache.jena.graph.NodeFactory; import org.apache.jena.graph.Triple; import org.apache.jena.sdb.core.Generator; import org.apache.jena.sdb.core.Gensym; import org.apache.jena.sparql.core.Quad; import org.apache.jena.sparql.core.Var; import org.apache.jena.sparql.expr.Expr; import org.apache.jena.sparql.expr.ExprList; import org.apache.jena.sparql.expr.ExprVar; import org.apache.jena.sparql.expr.NodeValue; import org.apache.jena.vocabulary.RDF; import org.apache.jena.vocabulary.XSD; import org.postgresql.ds.PGSimpleDataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.Multimap; interface UriSchema { String getClassUri(String relationName); } class UriSchemaDefault implements UriSchema { private String hostName; public UriSchemaDefault(){ hostName = "http://localhost"; } public String getNamespace() { return hostName; } @Override public String getClassUri(String relationName) { return getNamespace() + "/" + relationName; } } public class AutoMapper { public static final Logger logger = LoggerFactory.getLogger(AutoMapper.class); public static String getBlockKey(String key) { return key.replace("_", "").toLowerCase(); } /** * Foreign keys by convention * * @return */ /* public static Multimap<String, ForeignKey> getConventionalForeignKeys(Collection<RelationMetadata> relations) { Map<String, RelationMetadata> relationBlockKeys = new HashMap<String, RelationMetadata>(); for(RelationMetadata relation : relations) { relationBlockKeys.put(getBlockKey(relation.getName()), relation); } for(RelationMetadata relation : relations) { String relationNameBlockKey = getBlockKey(relation.getName()); for(ColumnMetadata column : relation.getColumns().values()) { // TODO person_id -> if(!column.getName().endsWith("id")) { continue; } String columnNamePrefix = column.getName().substring(0, column.getName().length() - 3); String blockName = getBlockKey(columnNamePrefix); RelationMetadata target = relationBlockKeys.get(columnNamePrefix); if(target == null) { continue; } // check if } } }*/ public static List<Column> getNonKeyColumns(Relation relation, PrimaryKey primaryKey, Collection<ForeignKey> fks) { List<Column> result = new ArrayList<Column>(); for(Column column : relation.getColumns().values()) { String name = column.getName(); if(primaryKey != null) { if(primaryKey.getSource().getColumnNames().contains(name)) { continue; } } for(ForeignKey fk : fks) { if(fk.getSource().getColumnNames().contains(name)) { continue; } } result.add(column); } return result; } public static VarDef columnReferenceToUriTermDef(String prefix, ColumnsReference columnsReference) { return new VarDef(columnReferenceToUriExpr(prefix, columnsReference)); } public static Expr columnReferenceToUriExpr(String prefix, ColumnsReference columnsReference) { return E_RdfTerm.createUri(columnReferenceToExpr(prefix, columnsReference)); } public static Expr columnReferenceToExpr(String prefix, ColumnsReference columnsReference) { ExprList exprs = new ExprList(); prefix += columnsReference.getTableName(); Expr prefixExpr = NodeValue.makeString(prefix); exprs.add(prefixExpr); List<String> columnNames = columnsReference.getColumnNames(); for(int i = 0; i < columnNames.size(); ++i) { if(i != 0) { exprs.add(NodeValue.makeString("-")); } exprs.add(new ExprVar(columnNames.get(i))); } E_StrConcatPermissive result = new E_StrConcatPermissive(exprs); return result; } public static void main(String[] args) throws SQLException { // Read commandline args (database connection + optional file with prefix definitions) /* List<Node> nodes = new ArrayList<Node>(); nodes.add(RDF.type.asNode()); nodes.add(RDF.first.asNode()); nodes.add(RDF.rest.asNode()); Query q = CannedQueryUtils.selectBySubjects(nodes); System.out.println(q); if(true) { return; } */ AutoMapper mapper = new AutoMapper(); //mapper.generate(getConnection()); DatatypeSystemOld system = new DatatypeSystemDefault(); Map<String, SqlDatatype> datatypeMap = new HashMap<String, SqlDatatype>(); datatypeMap.put("int4", DatatypeSystemDefault._INT); datatypeMap.put("varchar", DatatypeSystemDefault._STRING); datatypeMap.put("bpchar", DatatypeSystemDefault._STRING); datatypeMap.put("text", DatatypeSystemDefault._STRING); datatypeMap.put("date", DatatypeSystemDefault._DATE); datatypeMap.put("float8", DatatypeSystemDefault._FLOAT); datatypeMap.put("timestamp", DatatypeSystemDefault._DATE_TIME); Schema schema = Schema.create(getConnection()); Set<String> relationNames = JdbcUtils.fetchRelationNames(getConnection()); System.out.println(relationNames); Map<String, Relation> relations = JdbcUtils.fetchColumns(getConnection()); Multimap<String, ForeignKey> foreignKeys = schema.getForeignKeys(); System.out.println(foreignKeys); Map<String, PrimaryKey> primaryKeys = schema.getPrimaryKeys(); System.out.println(primaryKeys); // Infer Foreign keys // This method is good enough for BSBM, but could be improved for other use-cases Generator genFk = Gensym.create("fk_autogen"); for(Relation relation : relations.values()) { Collection<ForeignKey> fks = foreignKeys.get(relation.getName()); // If fks exists, we assume that all have been stated if(!fks.isEmpty()) { continue; } for(Column column : relation.getColumns().values()) { // Check if a table has the same name as the column Relation r = relations.get(column.getName()); if(r == null) { continue; } // Without primary key we have no link target PrimaryKey pk = primaryKeys.get(r.getName()); if(pk == null) { continue; } ColumnsReference source = new ColumnsReference(relation.getName()); source.getColumnNames().add(column.getName()); String fkName = genFk.next(); ForeignKey fk = new ForeignKey(fkName, source, pk.getSource()); foreignKeys.put(relation.getName(), fk); System.out.println("Auto FK: " + fk); } } //UriSchema uriSchema = new UriSchemaDefault(); String prefix = "http://localhost/am/"; /** * TODO: * - N-M Mappings */ for(Relation relation : relations.values()) { ViewDefinition viewDefinition = new ViewDefinition(); viewDefinition.setName("view_" + relation.getName()); //Generator genFkSourceVar = Gensym.create("fk_src"); Generator genFkTargetVar = Gensym.create("fk"); Generator genColumnVar = Gensym.create("o"); if(!relationNames.contains(relation.getName())) { continue; } PrimaryKey primaryKey = primaryKeys.get(relation.getName()); if(primaryKey == null) { //logger.warn(relation.getName() + ": Neither primary nor foreign keys found. Skipping."); logger.warn(relation.getName() + ": No primary key found. Skipping."); continue; } Collection<ForeignKey> fks = foreignKeys.get(relation.getName()); // FIXME Replace with safeGet if(fks == null) { fks = Collections.emptySet(); } Var mainVar = Var.alloc("s"); VarDef mainVarDef = columnReferenceToUriTermDef(prefix, primaryKey.getSource()); viewDefinition.addVarDef(mainVar, mainVarDef); Node classUri = NodeFactory.createURI(prefix + StringUtils.toUpperCamelCase(relation.getName())); viewDefinition.getConstructPattern().add(new Quad(null, new Triple(mainVar, RDF.type.asNode(), classUri))); // Create Uris that join according to the FK for(ForeignKey fk : fks) { // Product(id) Order(id, product_id) -> uri(?order_id) am:product (?product_id) /* Var sourceVar = Var.alloc(genFkSourceVar.next()); TermDef sourceVarDef = columnReferenceToUriTermDef(prefixExpr, fk.getSource()); viewDefinition.addVarDef(sourceVar, mainVarDef); */ Var targetVar = Var.alloc(genFkTargetVar.next()); //StringUtils.toUpperCamelCase(fk.getSource().getT) VarDef targetVarDef = columnReferenceToUriTermDef(prefix, fk.getSource()); viewDefinition.addVarDef(targetVar, targetVarDef); Node property = NodeFactory.createURI(prefix + StringUtils.toLowerCamelCase(fk.getTarget().getTableName())); Triple triple = new Triple(mainVar, property, targetVar); Quad quad = new Quad(Quad.defaultGraphNodeGenerated, triple); viewDefinition.getConstructPattern().add(quad); } List<Column> nonKeyColumns = getNonKeyColumns(relation, primaryKey, fks); for(Column column : nonKeyColumns) { Var var = Var.alloc(genColumnVar.next()); SqlDatatype dt = datatypeMap.get(column.getType()); //system.getByName(column.getType()); if(dt == null) { logger.warn(column.getType() + ": no datatype mapping found"); } Node datatype = NodeFactory.createURI("http://aksw.org/sparqlify/unknown-datatype"); if(dt != null) { Node xsd = dt.getXsd(); if(xsd != null) { datatype = xsd; } } if(datatype.equals(XSD.xstring.asNode())) { viewDefinition.addVarDef(var, E_RdfTerm.createPlainLiteral(new ExprVar(column.getName()))); } else { Expr dte = NodeValue.makeString(datatype.toString()); viewDefinition.addVarDef(var, E_RdfTerm.createTypedLiteral(new ExprVar(column.getName()), dte)); } // FIXME Encode name Node property = NodeFactory.createURI(prefix + column.getName()); Triple triple = new Triple(mainVar, property, var); Quad quad = new Quad(Quad.defaultGraphNodeGenerated, triple); viewDefinition.getConstructPattern().add(quad); } //String relationClass = uriSchema.getClassUri(relationName); //SqlNode relationNode = new SqlTable(relation.getName()); viewDefinition.setRelation(new SqlOpQuery(null, relation.getName())); System.out.println(viewDefinition.getDefinitionString()); } /* for(String relationName : relationNames) { }*/ } public AutoMapper() { } public static Connection getConnection() throws SQLException { PGSimpleDataSource dataSource = new PGSimpleDataSource(); dataSource.setDatabaseName("bsbm_default"); dataSource.setUser("postgres"); dataSource.setPassword("postgres"); dataSource.setServerName("localhost"); dataSource.setPortNumber(5432); Connection conn = dataSource.getConnection(); return conn; } public static List<String> getColumnNames(ResultSet rs) throws SQLException { ResultSetMetaData meta = rs.getMetaData(); List<String> result = new ArrayList<String>(meta.getColumnCount()); for(int i = 0; i < meta.getColumnCount(); ++i) { result.add(meta.getColumnName(i + 1)); } return result; } public static List<Object> getRow(ResultSet rs) throws SQLException { ResultSetMetaData meta = rs.getMetaData(); List<Object> result = new ArrayList<Object>(meta.getColumnCount()); for(int i = 0; i < meta.getColumnCount(); ++i) { result.add(rs.getObject(i + 1)); } return result; } }