merged master

Project: http://git-wip-us.apache.org/repos/asf/metamodel/repo
Commit: http://git-wip-us.apache.org/repos/asf/metamodel/commit/c4acebd5
Tree: http://git-wip-us.apache.org/repos/asf/metamodel/tree/c4acebd5
Diff: http://git-wip-us.apache.org/repos/asf/metamodel/diff/c4acebd5

Branch: refs/heads/master
Commit: c4acebd5d3911bc208c3fd919a8cf7894b371c00
Parents: a07b8fe 2fe6a2b
Author: Jörg Unbehauen <jo...@unbehauen.net>
Authored: Fri Aug 4 15:31:21 2017 +0200
Committer: Jörg Unbehauen <jo...@unbehauen.net>
Committed: Fri Aug 4 15:31:21 2017 +0200

----------------------------------------------------------------------
 CHANGES.md                                      |   2 +
 README.md                                       |  78 +++++------
 .../org/apache/metamodel/MetaModelHelper.java   | 138 +++++++++++--------
 .../factory/DataContextPropertiesImpl.java      |   3 +
 .../apache/metamodel/MetaModelHelperTest.java   |  83 +++++++++--
 jdbc/pom.xml                                    |   4 +
 .../metamodel/jdbc/JdbcMetadataLoader.java      |  92 ++++++++++---
 .../jdbc/dialects/DefaultQueryRewriter.java     |  24 ++--
 .../jdbc/dialects/HsqldbQueryRewriter.java      |  13 ++
 .../apache/metamodel/jdbc/H2databaseTest.java   | 104 +++++++++-----
 .../org/apache/metamodel/jdbc/HsqldbTest.java   | 112 +++++++++++----
 .../metamodel/jdbc/MultiJDBCDataSetTest.java    | 132 ++++++++++++++++++
 .../apache/metamodel/pojo/PojoDataContext.java  |   4 +-
 .../metamodel/pojo/PojoDataContextFactory.java  |  71 ++++++++++
 ....apache.metamodel.factory.DataContextFactory |   1 +
 pom.xml                                         |  84 ++++++++++-
 spring/pom.xml                                  |  12 --
 17 files changed, 732 insertions(+), 225 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metamodel/blob/c4acebd5/core/src/main/java/org/apache/metamodel/MetaModelHelper.java
----------------------------------------------------------------------
diff --cc core/src/main/java/org/apache/metamodel/MetaModelHelper.java
index 4f0a8dd,a2681da..f23c98e
--- a/core/src/main/java/org/apache/metamodel/MetaModelHelper.java
+++ b/core/src/main/java/org/apache/metamodel/MetaModelHelper.java
@@@ -18,19 -18,10 +18,12 @@@
   */
  package org.apache.metamodel;
  
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Collection;
- import java.util.Collections;
- import java.util.Comparator;
- import java.util.HashMap;
- import java.util.HashSet;
- import java.util.List;
- import java.util.Map;
+ import java.util.*;
  import java.util.Map.Entry;
  import java.util.stream.Collectors;
 +import java.util.stream.Stream;
 +
+ 
  import org.apache.metamodel.data.CachingDataSetHeader;
  import org.apache.metamodel.data.DataSet;
  import org.apache.metamodel.data.DataSetHeader;
@@@ -184,67 -180,86 +182,87 @@@ public final class MetaModelHelper 
          if (fromDataSets.length == 1) {
              return getFiltered(fromDataSets[0], whereItems);
          }
+         // do a nested loop join, no matter what
+         Iterator<DataSet> dsIter = Arrays.asList(fromDataSets).iterator();
  
-         List<SelectItem> selectItems = new ArrayList<SelectItem>();
-         for (DataSet dataSet : fromDataSets) {
-             for (int i = 0; i < dataSet.getSelectItems().size(); i++) {
-                 SelectItem item = dataSet.getSelectItems().get(i);
-                 selectItems.add(item);
-             }
-         }
 +
-         int selectItemOffset = 0;
-         List<Object[]> data = new ArrayList<Object[]>();
-         for (int fromDataSetIndex = 0; fromDataSetIndex < 
fromDataSets.length; fromDataSetIndex++) {
-             DataSet fromDataSet = fromDataSets[fromDataSetIndex];
-             List<SelectItem> fromSelectItems = fromDataSet.getSelectItems();
-             if (fromDataSetIndex == 0) {
-                 while (fromDataSet.next()) {
-                     Object[] values = fromDataSet.getRow().getValues();
-                     Object[] row = new Object[selectItems.size()];
-                     System.arraycopy(values, 0, row, selectItemOffset, 
values.length);
-                     data.add(row);
-                 }
-                 fromDataSet.close();
-             } else {
-                 List<Object[]> fromDataRows = new ArrayList<Object[]>();
-                 while (fromDataSet.next()) {
-                     fromDataRows.add(fromDataSet.getRow().getValues());
-                 }
-                 fromDataSet.close();
-                 for (int i = 0; i < data.size(); i = i + fromDataRows.size()) 
{
-                     Object[] originalRow = data.get(i);
-                     data.remove(i);
-                     for (int j = 0; j < fromDataRows.size(); j++) {
-                         Object[] newRow = fromDataRows.get(j);
-                         System.arraycopy(newRow, 0, originalRow, 
selectItemOffset, newRow.length);
-                         data.add(i + j, originalRow.clone());
-                     }
-                 }
-             }
-             selectItemOffset += fromSelectItems.size();
-         }
+         DataSet joined = dsIter.next();
+ 
+         while (dsIter.hasNext()) {
+             joined = nestedLoopJoin(dsIter.next(), joined, (whereItems));
  
-         if (data.isEmpty()) {
-             return new EmptyDataSet(selectItems);
          }
  
-         final DataSetHeader header = new CachingDataSetHeader(selectItems);
-         final List<Row> rows = new ArrayList<Row>(data.size());
-         for (Object[] objects : data) {
-             rows.add(new DefaultRow(header, objects, null));
+         return joined;
+ 
+     }
+ 
+     /**
+      * Executes a simple nested loop join. The innerLoopDs will be copied in 
an
+      * in-memory dataset.
+      *
+      */
+     public static InMemoryDataSet nestedLoopJoin(DataSet innerLoopDs, DataSet 
outerLoopDs,
+             Iterable<FilterItem> filtersIterable) {
+ 
+         List<FilterItem> filters = new ArrayList<>();
+         for (FilterItem fi : filtersIterable) {
+             filters.add(fi);
          }
+         List<Row> innerRows = innerLoopDs.toRows();
+ 
 -        List<SelectItem> allItems = new 
ArrayList<>(Arrays.asList(outerLoopDs.getSelectItems()));
 -        allItems.addAll(Arrays.asList(innerLoopDs.getSelectItems()));
++        List<SelectItem> allItems = new 
ArrayList<>(outerLoopDs.getSelectItems());
++        allItems.addAll(innerLoopDs.getSelectItems());
  
-         DataSet result = new InMemoryDataSet(header, rows);
-         if (whereItems != null) {
-             DataSet filteredResult = getFiltered(result, whereItems);
-             result = filteredResult;
+         Set<FilterItem> applicableFilters = applicableFilters(filters, 
allItems);
+ 
+         DataSetHeader jointHeader = new CachingDataSetHeader(allItems);
+ 
+         List<Row> resultRows = new ArrayList<>();
+         for (Row outerRow : outerLoopDs) {
+             for (Row innerRow : innerRows) {
+ 
+                 Object[] joinedRowObjects = new 
Object[outerRow.getValues().length + innerRow.getValues().length];
+ 
+                 System.arraycopy(outerRow.getValues(), 0, joinedRowObjects, 
0, outerRow.getValues().length);
+                 System.arraycopy(innerRow.getValues(), 0, joinedRowObjects, 
outerRow.getValues().length, innerRow
+                         .getValues().length);
+ 
+                 Row joinedRow = new DefaultRow(jointHeader, joinedRowObjects);
+ 
+                 if (applicableFilters.isEmpty() || 
applicableFilters.stream().allMatch(fi -> fi.accept(joinedRow))) {
+                     resultRows.add(joinedRow);
+                 }
+             }
          }
-         return result;
+ 
+         return new InMemoryDataSet(jointHeader, resultRows);
      }
  
-     public static DataSet getCarthesianProduct(DataSet[] fromDataSets, 
FilterItem... filterItems) {
-         return getCarthesianProduct(fromDataSets, Arrays.asList(filterItems));
+     /**
+      * Filters the FilterItems such that only the FilterItems are returned,
+      * which contain SelectItems that are contained in selectItemList
+      * 
+      * @param filters
+      * @param selectItemList
+      * @return
+      */
+     private static Set<FilterItem> applicableFilters(Collection<FilterItem> 
filters,
+             Collection<SelectItem> selectItemList) {
+ 
+         Set<SelectItem> items = new HashSet<SelectItem>(selectItemList);
+ 
+         return filters.stream().filter(fi -> {
+             Collection<SelectItem> fiSelectItems = new ArrayList<>();
+             fiSelectItems.add(fi.getSelectItem());
+             Object operand = fi.getOperand();
+             if (operand instanceof SelectItem) {
+                 fiSelectItems.add((SelectItem) operand);
+             }
+ 
+             return items.containsAll(fiSelectItems);
+ 
+         }).collect(Collectors.toSet());
      }
  
      public static DataSet getFiltered(DataSet dataSet, Iterable<FilterItem> 
filterItems) {

http://git-wip-us.apache.org/repos/asf/metamodel/blob/c4acebd5/core/src/test/java/org/apache/metamodel/MetaModelHelperTest.java
----------------------------------------------------------------------
diff --cc core/src/test/java/org/apache/metamodel/MetaModelHelperTest.java
index 84a09b2,a84cef1..7196bf4
--- a/core/src/test/java/org/apache/metamodel/MetaModelHelperTest.java
+++ b/core/src/test/java/org/apache/metamodel/MetaModelHelperTest.java
@@@ -116,21 -115,19 +116,21 @@@ public class MetaModelHelperTest extend
  
      public void testSimpleCarthesianProduct() throws Exception {
          DataSet dataSet = 
MetaModelHelper.getCarthesianProduct(createDataSet1(), createDataSet2());
+         List<String> results = new ArrayList<String>();
  
++
+         while (dataSet.next()) {
+             results.add(dataSet.getRow().toString());
+         }
 -        assertEquals(2, dataSet.getSelectItems().length);
 +        assertEquals(2, dataSet.getSelectItems().size());
-         assertTrue(dataSet.next());
-         assertEquals("Row[values=[f, b]]", dataSet.getRow().toString());
-         assertTrue(dataSet.next());
-         assertEquals("Row[values=[f, a]]", dataSet.getRow().toString());
-         assertTrue(dataSet.next());
-         assertTrue(dataSet.next());
-         assertTrue(dataSet.next());
-         assertTrue(dataSet.next());
-         assertTrue(dataSet.next());
-         assertTrue(dataSet.next());
-         assertTrue(dataSet.next());
-         assertEquals("Row[values=[o, r]]", dataSet.getRow().toString());
-         assertFalse(dataSet.next());
+         assertEquals(9, results.size());
+         assertTrue(results.contains("Row[values=[f, b]]"));
+         assertTrue(results.contains("Row[values=[f, a]]"));
+         assertTrue(results.contains("Row[values=[f, r]]"));
+         assertTrue(results.contains("Row[values=[o, b]]"));
+         assertTrue(results.contains("Row[values=[o, a]]"));
+         assertTrue(results.contains("Row[values=[o, r]]"));
++
      }
  
      public void testTripleCarthesianProduct() throws Exception {
@@@ -188,8 -185,8 +188,10 @@@
          data1.add(new Object[] { "f" });
          data1.add(new Object[] { "o" });
          data1.add(new Object[] { "o" });
 -        DataSet dataSet1 = createDataSet(new SelectItem[] { new 
SelectItem(new MutableColumn("foo",
 -                ColumnType.VARCHAR)) }, data1);
++
 +        DataSet dataSet1 = createDataSet(
 +                Lists.newArrayList( new SelectItem(new MutableColumn("foo", 
ColumnType.VARCHAR)) ), data1);
++
          return dataSet1;
      }
  
@@@ -206,8 -203,8 +208,10 @@@
          List<Object[]> data3 = new ArrayList<Object[]>();
          data3.add(new Object[] { "w00p", true });
          data3.add(new Object[] { "yippie", false });
 -        DataSet dataSet3 = createDataSet(new SelectItem[] { new 
SelectItem("expression", "e"), new SelectItem("webish?",
 -                "w") }, data3);
++
 +        DataSet dataSet3 = createDataSet(Lists.newArrayList(new 
SelectItem("expression", "e"),
 +                new SelectItem("webish?", "w") ), data3);
++
          return dataSet3;
      }
  
@@@ -217,6 -214,41 +221,41 @@@
          return dataSet4;
      }
  
+     private int bigDataSetSize = 3000;
+ 
+     /**
+      * 
+      * @return a big dataset, mocking an employee table
+      */
+     private DataSet createDataSet5() {
+         List<Object[]> data5 = new ArrayList<Object[]>();
+ 
+         for (int i = 0; i < bigDataSetSize; i++) {
+             data5.add(new Object[] { i, "Person_" + i, bigDataSetSize - (i + 
1) });
+         }
+ 
 -        DataSet dataSet5 = createDataSet(new SelectItem[] { new 
SelectItem(new MutableColumn("nr", ColumnType.BIGINT)),
++        DataSet dataSet5 = createDataSet(Lists.newArrayList( new 
SelectItem(new MutableColumn("nr", ColumnType.BIGINT)),
+                 new SelectItem(new MutableColumn("name", ColumnType.STRING)), 
new SelectItem(new MutableColumn("dnr",
 -                        ColumnType.BIGINT)) }, data5);
++                        ColumnType.BIGINT)) ), data5);
+         return dataSet5;
+     }
+ 
+     /**
+      * 
+      * @return a big dataset, mocking an department table
+      */
+     private DataSet createDataSet6() {
+         List<Object[]> data6 = new ArrayList<Object[]>();
+ 
+         for (int i = 0; i < bigDataSetSize; i++) {
+             data6.add(new Object[] { i, "Department_" + i });
+         }
+ 
 -        DataSet dataSet6 = createDataSet(new SelectItem[] { new 
SelectItem(new MutableColumn("nr", ColumnType.BIGINT)),
 -                new SelectItem(new MutableColumn("name", ColumnType.STRING)), 
}, data6);
++        DataSet dataSet6 = createDataSet(Lists.newArrayList(new 
SelectItem(new MutableColumn("nr", ColumnType.BIGINT)),
++                new SelectItem(new MutableColumn("name", 
ColumnType.STRING))), data6);
+         return dataSet6;
+     }
+ 
      public void testGetTables() throws Exception {
          MutableTable table1 = new MutableTable("table1");
          MutableTable table2 = new MutableTable("table2");
@@@ -325,4 -357,22 +364,22 @@@
          assertEquals("Row[values=[1, 2, null]]", 
joinedDs.getRow().toString());
          assertFalse(joinedDs.next());
      }
+ 
+     public void testCarthesianProductScalability() {
+ 
+         DataSet employees = createDataSet5();
+         DataSet departmens = createDataSet6();
+ 
 -        FilterItem fi = new FilterItem(employees.getSelectItems()[2], 
OperatorType.EQUALS_TO, departmens
 -                .getSelectItems()[0]);
++        FilterItem fi = new FilterItem(employees.getSelectItems().get(2), 
OperatorType.EQUALS_TO, departmens
++                .getSelectItems().get(0));
+ 
+         DataSet joined = MetaModelHelper.getCarthesianProduct(new DataSet[] { 
employees, departmens }, fi);
+         int count = 0;
+         while (joined.next()) {
+             count++;
+         }
+ 
+         assertTrue(count == bigDataSetSize);
+ 
+     }
  }

http://git-wip-us.apache.org/repos/asf/metamodel/blob/c4acebd5/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcMetadataLoader.java
----------------------------------------------------------------------
diff --cc jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcMetadataLoader.java
index 49e242b,2c29405..ce64b87
--- a/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcMetadataLoader.java
+++ b/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcMetadataLoader.java
@@@ -452,9 -468,42 +467,42 @@@ final class JdbcMetadataLoader implemen
                  logger.error("pkColumn={}", pkColumn);
                  logger.error("fkColumn={}", fkColumn);
              } else {
-                 MutableRelationship.createRelationship(pkColumn, fkColumn);
+ 
+                 if (!relations.containsKey(pkTable)) {
+                     relations.put(pkTable, new HashMap<>());
+                 }
+ 
+                 // get or init the columns tuple
+                 ColumnsTuple ct = relations.get(pkTable).get(fkTable);
+                 if (Objects.isNull(ct)) {
+                     ct = new ColumnsTuple();
+                     relations.get(pkTable).put(fkTable, ct);
+                 }
+                 // we can now safely add the columns
+                 ct.getPkCols().add(pkColumn);
+                 ct.getFkCols().add(fkColumn);
              }
          }
+ 
+         relations.values().stream().flatMap(map -> 
map.values().stream()).forEach(ct -> MutableRelationship
 -                .createRelationship(ct.getPkCols().toArray(new Column[0]), 
ct.getFkCols().toArray(new Column[0])));
++                .createRelationship(ct.getPkCols(), ct.getFkCols()));
+     }
+ 
+     /**
+      * Represents the columns of a relationship while it is being built from a
+      * {@link ResultSet}.
+      */
+     private static class ColumnsTuple {
+         private final List<Column> pkCols = new ArrayList<>();
+         private final List<Column> fkCols = new ArrayList<>();
+ 
+         public List<Column> getFkCols() {
+             return fkCols;
+         }
+ 
+         public List<Column> getPkCols() {
+             return pkCols;
+         }
      }
  
  }

http://git-wip-us.apache.org/repos/asf/metamodel/blob/c4acebd5/jdbc/src/test/java/org/apache/metamodel/jdbc/H2databaseTest.java
----------------------------------------------------------------------
diff --cc jdbc/src/test/java/org/apache/metamodel/jdbc/H2databaseTest.java
index e7b5386,d46bc31..18b02de
--- a/jdbc/src/test/java/org/apache/metamodel/jdbc/H2databaseTest.java
+++ b/jdbc/src/test/java/org/apache/metamodel/jdbc/H2databaseTest.java
@@@ -276,14 -281,17 +281,17 @@@ public class H2databaseTest extends Tes
  
              @Override
              public void run(UpdateCallback cb) {
-                 JdbcCreateTableBuilder createTableBuilder = 
(JdbcCreateTableBuilder) cb.createTable(schema, "test_table");
+                 JdbcCreateTableBuilder createTableBuilder = 
(JdbcCreateTableBuilder) cb.createTable(schema,
+                         "test_table");
                  Table writtenTable = 
createTableBuilder.withColumn("id").asPrimaryKey().ofType(ColumnType.INTEGER)
-                         
.withColumn("name").ofSize(255).ofType(ColumnType.VARCHAR).withColumn("age").ofType(ColumnType.INTEGER)
-                         .execute();
+                         
.withColumn("name").ofSize(255).ofType(ColumnType.VARCHAR).withColumn("age").ofType(
+                                 ColumnType.INTEGER).execute();
                  String sql = createTableBuilder.createSqlStatement();
-                 assertEquals("CREATE TABLE PUBLIC.test_table (id INTEGER, 
name VARCHAR(255), age INTEGER, PRIMARY KEY(id))", sql);
+                 assertEquals(
+                         "CREATE TABLE PUBLIC.test_table (id INTEGER, name 
VARCHAR(255), age INTEGER, PRIMARY KEY(id))",
+                         sql);
                  assertNotNull(writtenTable);
 -                assertEquals("[ID, NAME, AGE]", 
Arrays.toString(writtenTable.getColumnNames()));
 +                assertEquals("[ID, NAME, AGE]", 
Arrays.toString(writtenTable.getColumnNames().toArray()));
  
                  writtenTableRef.set(writtenTable);
              }
@@@ -521,4 -531,32 +531,32 @@@
      public void testInterpretationOfNull() throws Exception {
          JdbcTestTemplates.interpretationOfNulls(conn);
      }
+ 
+     public void testCompositeFkRelation() throws Exception {
+ 
+         try (Statement stmt = conn.createStatement()) {
+             stmt.execute(
+                     "CREATE TABLE PARENT (P1 INTEGER, P2 INTEGER, P3 INTEGER, 
P4 INTEGER, PRIMARY  KEY (P1,P2, P3, P4))");
+             stmt.execute(
+                     "CREATE TABLE CHILD (C1 INTEGER PRIMARY KEY, CP1 INTEGER 
, CP2 INTEGER, CP3 INTEGER, CP4 INTEGER, FOREIGN  KEY (CP1,CP2,CP3,CP4) 
REFERENCES  PARENT(P1,P2,P3,P4))");
+         }
+ 
+         final JdbcDataContext dc = new JdbcDataContext(conn);
+ 
+         final Schema schema = dc.getDefaultSchema();
+ 
 -        assertEquals(1, schema.getRelationships().length);
++        assertEquals(1, schema.getRelationships().size());
+ 
 -        Relationship rel = schema.getRelationships()[0];
++        Relationship rel = schema.getRelationships().iterator().next();
+ 
 -        assertEquals("CP1", rel.getForeignColumns()[0].getName());
 -        assertEquals("CP2", rel.getForeignColumns()[1].getName());
 -        assertEquals("CP3", rel.getForeignColumns()[2].getName());
 -        assertEquals("CP4", rel.getForeignColumns()[3].getName());
++        assertEquals("CP1", rel.getForeignColumns().get(0).getName());
++        assertEquals("CP2", rel.getForeignColumns().get(1).getName());
++        assertEquals("CP3", rel.getForeignColumns().get(2).getName());
++        assertEquals("CP4", rel.getForeignColumns().get(3).getName());
+ 
 -        assertEquals("P1", rel.getPrimaryColumns()[0].getName());
 -        assertEquals("P2", rel.getPrimaryColumns()[1].getName());
 -        assertEquals("P3", rel.getPrimaryColumns()[2].getName());
 -        assertEquals("P4", rel.getPrimaryColumns()[3].getName());
++        assertEquals("P1", rel.getPrimaryColumns().get(0).getName());
++        assertEquals("P2", rel.getPrimaryColumns().get(1).getName());
++        assertEquals("P3", rel.getPrimaryColumns().get(2).getName());
++        assertEquals("P4", rel.getPrimaryColumns().get(3).getName());
+     }
  }

http://git-wip-us.apache.org/repos/asf/metamodel/blob/c4acebd5/jdbc/src/test/java/org/apache/metamodel/jdbc/HsqldbTest.java
----------------------------------------------------------------------
diff --cc jdbc/src/test/java/org/apache/metamodel/jdbc/HsqldbTest.java
index bf64fdd,d9f0587..3d9d268
--- a/jdbc/src/test/java/org/apache/metamodel/jdbc/HsqldbTest.java
+++ b/jdbc/src/test/java/org/apache/metamodel/jdbc/HsqldbTest.java
@@@ -146,8 -145,8 +145,9 @@@ public class HsqldbTest extends TestCas
          Table productsTable = schema.getTableByName("PRODUCTS");
          Table factTable = schema.getTableByName("ORDERFACT");
  
 -        Query q = new Query().from(new FromItem(JoinType.INNER, 
productsTable.getRelationships(factTable)[0]))
 -                .select(productsTable.getColumns()[0], 
factTable.getColumns()[0]);
 +        Query q = new Query().from(new FromItem(JoinType.INNER, 
productsTable.getRelationships(factTable).iterator().next())).select(
 +                productsTable.getColumns().get(0), 
factTable.getColumns().get(0));
++
          assertEquals(
                  "SELECT \"PRODUCTS\".\"PRODUCTCODE\", 
\"ORDERFACT\".\"ORDERNUMBER\" FROM PUBLIC.\"PRODUCTS\" INNER JOIN 
PUBLIC.\"ORDERFACT\" ON \"PRODUCTS\".\"PRODUCTCODE\" = 
\"ORDERFACT\".\"PRODUCTCODE\"",
                  q.toString());

http://git-wip-us.apache.org/repos/asf/metamodel/blob/c4acebd5/pojo/src/main/java/org/apache/metamodel/pojo/PojoDataContext.java
----------------------------------------------------------------------

Reply via email to