You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by ja...@apache.org on 2016/06/12 23:07:53 UTC

[2/2] phoenix git commit: PHOENIX-2886 Union ALL with Char column not present in the table in Query 1 but in Query 2 throw exception (Alicia Ying Shu)

PHOENIX-2886 Union ALL with Char column not present in the table in Query 1 but in Query 2 throw exception (Alicia Ying Shu)


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

Branch: refs/heads/master
Commit: d5389389737beacd780353d9e3a6239ecd3a4471
Parents: 951dd7a
Author: James Taylor <ja...@apache.org>
Authored: Sun Jun 12 16:07:14 2016 -0700
Committer: James Taylor <ja...@apache.org>
Committed: Sun Jun 12 16:10:17 2016 -0700

----------------------------------------------------------------------
 .../org/apache/phoenix/end2end/UnionAllIT.java  | 240 ++++++++++++++++++-
 .../apache/phoenix/compile/QueryCompiler.java   |  13 +-
 .../apache/phoenix/compile/UnionCompiler.java   | 162 ++++++++++---
 3 files changed, 369 insertions(+), 46 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/d5389389/phoenix-core/src/it/java/org/apache/phoenix/end2end/UnionAllIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/UnionAllIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/UnionAllIT.java
index b391dcc..8b60c3f 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/UnionAllIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/UnionAllIT.java
@@ -23,6 +23,7 @@ import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import java.math.BigDecimal;
 import java.sql.Connection;
 import java.sql.DriverManager;
 import java.sql.PreparedStatement;
@@ -56,7 +57,7 @@ public class UnionAllIT extends BaseOwnClusterHBaseManagedTimeIT {
 
         try {
             String ddl = "CREATE TABLE test_table " +
-                    "  (a_string varchar not null, col1 integer" +
+                    "  (a_string varchar(10) not null, col1 integer" +
                     "  CONSTRAINT pk PRIMARY KEY (a_string))\n";
             createTestTable(getUrl(), ddl);
 
@@ -68,7 +69,7 @@ public class UnionAllIT extends BaseOwnClusterHBaseManagedTimeIT {
             conn.commit();
 
             ddl = "CREATE TABLE b_table " +
-                    "  (a_string varchar not null, col1 integer" +
+                    "  (a_string char(20) not null, col1 bigint" +
                     "  CONSTRAINT pk PRIMARY KEY (a_string))\n";
             createTestTable(getUrl(), ddl);
             dml = "UPSERT INTO b_table VALUES(?, ?)";
@@ -87,10 +88,10 @@ public class UnionAllIT extends BaseOwnClusterHBaseManagedTimeIT {
             assertEquals("a",rs.getString(1));
             assertEquals(10,rs.getInt(2));
             assertTrue(rs.next());
-            assertEquals("b",rs.getString(1));
+            assertEquals("b",rs.getString(1).trim());
             assertEquals(20,rs.getInt(2));
             assertTrue(rs.next());
-            assertEquals("c",rs.getString(1));
+            assertEquals("c",rs.getString(1).trim());
             assertEquals(20,rs.getInt(2));
             assertTrue(rs.next());
             assertEquals("a",rs.getString(1));
@@ -109,7 +110,7 @@ public class UnionAllIT extends BaseOwnClusterHBaseManagedTimeIT {
 
         try {
             String ddl = "CREATE TABLE test_table " +
-                    "  (a_string varchar not null, col1 integer" +
+                    "  (a_string char(5) not null, col1 tinyint" +
                     "  CONSTRAINT pk PRIMARY KEY (a_string))\n";
             createTestTable(getUrl(), ddl);
 
@@ -719,11 +720,236 @@ public class UnionAllIT extends BaseOwnClusterHBaseManagedTimeIT {
             assertEquals("b",rs.getString(1));
             assertEquals(20,rs.getInt(2));
             assertFalse(rs.next()); 
-        } catch (Exception ex) {
-            ex.printStackTrace();
         } finally {
             conn.close();
         }
     } 
 
+    @Test
+    public void testDiffDataTypes() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+
+        String ddl = "create table person ( id bigint not null primary key, " +
+                "firstname varchar(10), lastname varchar(10) )";
+        createTestTable(getUrl(), ddl);
+        String dml = "upsert into person values (?, ?, ?)";
+        PreparedStatement stmt = conn.prepareStatement(dml);
+        stmt.setInt(1, 1);
+        stmt.setString(2, "john");
+        stmt.setString(3, "doe");
+        stmt.execute();
+        stmt.setInt(1, 2);
+        stmt.setString(2, "jane");
+        stmt.setString(3, "doe");
+        stmt.execute();
+        conn.commit();
+
+        ddl = "create table user ( id integer not null primary key, firstname char(12)," +
+                " lastname varchar(12) )";
+        createTestTable(getUrl(), ddl);
+        dml = "upsert into user values (?, ?, ?)";
+        stmt = conn.prepareStatement(dml);
+        stmt.setInt(1, 1);
+        stmt.setString(2, "sam");
+        stmt.setString(3, "johnson");
+        stmt.execute();
+        stmt.setInt(1, 2);
+        stmt.setString(2, "ann");
+        stmt.setString(3, "wiely");
+        stmt.execute();
+        conn.commit();
+
+        ddl = "create table t1 ( id varchar(20) not null primary key)";
+        createTestTable(getUrl(), ddl);
+        dml = "upsert into t1 values ('abcd')";
+        stmt = conn.prepareStatement(dml);
+        stmt.execute();
+        conn.commit();
+        ddl = "create table t2 ( id char(50) not null primary key)";
+        createTestTable(getUrl(), ddl);
+        dml = "upsert into t2 values ('xyz')";
+        stmt = conn.prepareStatement(dml);
+        stmt.execute();
+        conn.commit();
+        String query = "select id, 'foo' firstname, lastname from person union all" +
+                " select * from user";
+        try {
+            PreparedStatement pstmt = conn.prepareStatement(query);
+            ResultSet rs = pstmt.executeQuery();
+            assertTrue(rs.next());
+            assertEquals(1, rs.getInt(1));
+            assertEquals("foo", rs.getString(2));
+            assertEquals("doe", rs.getString(3));
+            assertTrue(rs.next());
+            assertEquals(2, rs.getInt(1));
+            assertEquals("foo", rs.getString(2));
+            assertEquals("doe", rs.getString(3));
+            assertTrue(rs.next());
+            assertEquals(1, rs.getInt(1));
+            assertEquals("sam", rs.getString(2).trim());
+            assertEquals("johnson", rs.getString(3));
+            assertTrue(rs.next());
+            assertEquals(2, rs.getInt(1));
+            assertEquals("ann", rs.getString(2).trim());
+            assertEquals("wiely", rs.getString(3));
+            assertFalse(rs.next());
+
+            pstmt = conn.prepareStatement("select * from t1 union all select * from t2");
+            rs = pstmt.executeQuery();
+            assertTrue(rs.next());
+            assertEquals("abcd", rs.getString(1));
+            assertTrue(rs.next());
+            assertEquals("xyz", rs.getString(1).trim());
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testDiffScaleSortOrder() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+
+        String ddl = "create table person ( id bigint not null primary key desc, " +
+                "firstname char(10), lastname varchar(10) )";
+        createTestTable(getUrl(), ddl);
+        String dml = "upsert into person values (?, ?, ?)";
+        PreparedStatement stmt = conn.prepareStatement(dml);
+        stmt.setInt(1, 1);
+        stmt.setString(2, "john");
+        stmt.setString(3, "doe");
+        stmt.execute();
+        stmt.setInt(1, 2);
+        stmt.setString(2, "jane");
+        stmt.setString(3, "doe");
+        stmt.execute();
+        conn.commit();
+
+        ddl = "create table user ( id integer not null primary key asc, " +
+                "firstname varchar(12), lastname varchar(10) )";
+        createTestTable(getUrl(), ddl);
+        dml = "upsert into user values (?, ?, ?)";
+        stmt = conn.prepareStatement(dml);
+        stmt.setInt(1, 1);
+        stmt.setString(2, "sam");
+        stmt.setString(3, "johnson");
+        stmt.execute();
+        stmt.setInt(1, 2);
+        stmt.setString(2, "ann");
+        stmt.setString(3, "wiely");
+        stmt.execute();
+        conn.commit();
+
+        ddl = "create table t1 ( id varchar(20) not null primary key, col1 decimal)";
+        createTestTable(getUrl(), ddl);
+        dml = "upsert into t1 values ('abcd', 234.23)";
+        stmt = conn.prepareStatement(dml);
+        stmt.execute();
+        conn.commit();
+        ddl = "create table t2 ( id char(50) not null primary key, col1 decimal(12,4))";
+        createTestTable(getUrl(), ddl);
+        dml = "upsert into t2 values ('xyz', 1342.1234)";
+        stmt = conn.prepareStatement(dml);
+        stmt.execute();
+        conn.commit();
+
+        String query = "select * from user union all select * from person";
+        try {
+            PreparedStatement pstmt = conn.prepareStatement(query);
+            ResultSet rs = pstmt.executeQuery();
+            assertTrue(rs.next());
+            assertEquals(1, rs.getInt(1));
+            assertEquals("sam", rs.getString(2));
+            assertEquals("johnson", rs.getString(3));
+            assertTrue(rs.next());
+            assertEquals(2, rs.getInt(1));
+            assertEquals("ann", rs.getString(2));
+            assertEquals("wiely", rs.getString(3));
+            assertTrue(rs.next());
+            assertEquals(2, rs.getInt(1));
+            assertEquals("jane", rs.getString(2).trim());
+            assertEquals("doe", rs.getString(3));
+            assertTrue(rs.next());
+            assertEquals(1, rs.getInt(1));
+            assertEquals("john", rs.getString(2).trim());
+            assertEquals("doe", rs.getString(3));
+            assertFalse(rs.next());
+
+            pstmt = conn.prepareStatement("select * from t1 union all select * from t2");
+            rs = pstmt.executeQuery();
+            assertTrue(rs.next());
+            assertEquals("abcd", rs.getString(1));
+            assertEquals(BigDecimal.valueOf(234.2300), rs.getBigDecimal(2));
+            assertTrue(rs.next());
+            assertEquals("xyz", rs.getString(1).trim());
+            assertEquals(BigDecimal.valueOf(1342.1234), rs.getBigDecimal(2));
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testVarcharChar() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+
+        String ddl = "create table user ( id integer not null primary key asc, " +
+                "firstname char(8), lastname varchar )";
+        createTestTable(getUrl(), ddl);
+        String dml = "upsert into user values (?, ?, ?)";
+        PreparedStatement  stmt = conn.prepareStatement(dml);
+        stmt.setInt(1, 1);
+        stmt.setString(2, "sam");
+        stmt.setString(3, "johnson");
+        stmt.execute();
+        stmt.setInt(1, 2);
+        stmt.setString(2, "ann");
+        stmt.setString(3, "wiely");
+        stmt.execute();
+        conn.commit();
+
+        ddl = "create table person ( id bigint not null primary key desc, " +
+                "firstname varchar(10), lastname char(10) )";
+        createTestTable(getUrl(), ddl);
+        dml = "upsert into person values (?, ?, ?)";
+        stmt = conn.prepareStatement(dml);
+        stmt.setInt(1, 1);
+        stmt.setString(2, "john");
+        stmt.setString(3, "doe");
+        stmt.execute();
+        stmt.setInt(1, 2);
+        stmt.setString(2, "jane");
+        stmt.setString(3, "doe");
+        stmt.execute();
+        conn.commit();
+
+        String query = "select id, 'baa' firstname, lastname from user " +
+                "union all select * from person";
+        try {
+            PreparedStatement pstmt = conn.prepareStatement(query);
+            ResultSet rs = pstmt.executeQuery();
+            assertTrue(rs.next());
+            assertEquals(1, rs.getInt(1));
+            assertEquals("baa", rs.getString(2));
+            assertEquals("johnson", rs.getString(3));
+            assertTrue(rs.next());
+            assertEquals(2, rs.getInt(1));
+            assertEquals("baa", rs.getString(2));
+            assertEquals("wiely", rs.getString(3));
+            assertTrue(rs.next());
+            assertEquals(2, rs.getInt(1));
+            assertEquals("jane", rs.getString(2).trim());
+            assertEquals("doe", rs.getString(3).trim());
+            assertTrue(rs.next());
+            assertEquals(1, rs.getInt(1));
+            assertEquals("john", rs.getString(2).trim());
+            assertEquals("doe", rs.getString(3).trim());
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/d5389389/phoenix-core/src/main/java/org/apache/phoenix/compile/QueryCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/QueryCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/QueryCompiler.java
index 4488aff..af2254b 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/QueryCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/QueryCompiler.java
@@ -174,19 +174,16 @@ public class QueryCompiler {
                 }
             }
             QueryPlan subPlan = compileSubquery(subSelect, true);
-            TupleProjector projector = new TupleProjector(subPlan.getProjector());
-            subPlan = new TupleProjectionPlan(subPlan, projector, null);
             plans.add(subPlan);
         }
-        UnionCompiler.checkProjectionNumAndTypes(plans);
-
-        TableRef tableRef = UnionCompiler.contructSchemaTable(statement, plans.get(0), select.hasWildcard() ? null : select.getSelect());
+        TableRef tableRef = UnionCompiler.contructSchemaTable(statement, plans,
+            select.hasWildcard() ? null : select.getSelect());
         ColumnResolver resolver = FromCompiler.getResolver(tableRef);
         StatementContext context = new StatementContext(statement, resolver, scan, sequenceManager);
-
         QueryPlan plan = compileSingleFlatQuery(context, select, statement.getParameters(), false, false, null, null, false);
-        plan = new UnionPlan(context, select, tableRef, plan.getProjector(), plan.getLimit(), plan.getOffset(),
-                plan.getOrderBy(), GroupBy.EMPTY_GROUP_BY, plans, context.getBindManager().getParameterMetaData());
+        plan = new UnionPlan(context, select, tableRef, plan.getProjector(), plan.getLimit(),
+            plan.getOffset(), plan.getOrderBy(), GroupBy.EMPTY_GROUP_BY, plans,
+            context.getBindManager().getParameterMetaData());
         return plan;
     }
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/d5389389/phoenix-core/src/main/java/org/apache/phoenix/compile/UnionCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/UnionCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/UnionCompiler.java
index df04a85..bc3466c 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/UnionCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/UnionCompiler.java
@@ -24,6 +24,9 @@ import java.util.List;
 import org.apache.hadoop.hbase.HConstants;
 import org.apache.phoenix.exception.SQLExceptionCode;
 import org.apache.phoenix.exception.SQLExceptionInfo;
+import org.apache.phoenix.execute.TupleProjectionPlan;
+import org.apache.phoenix.execute.TupleProjector;
+import org.apache.phoenix.expression.CoerceExpression;
 import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.jdbc.PhoenixStatement;
 import org.apache.phoenix.parse.AliasedNode;
@@ -34,6 +37,7 @@ import org.apache.phoenix.schema.PNameFactory;
 import org.apache.phoenix.schema.PTable;
 import org.apache.phoenix.schema.PTableImpl;
 import org.apache.phoenix.schema.PTableType;
+import org.apache.phoenix.schema.SortOrder;
 import org.apache.phoenix.schema.TableRef;
 import org.apache.phoenix.schema.types.PDataType;
 import org.apache.phoenix.util.SchemaUtil;
@@ -43,50 +47,146 @@ public class UnionCompiler {
     private static final PName UNION_SCHEMA_NAME = PNameFactory.newName("unionSchemaName");
     private static final PName UNION_TABLE_NAME = PNameFactory.newName("unionTableName");
 
-    public static List<QueryPlan> checkProjectionNumAndTypes(List<QueryPlan> selectPlans) throws SQLException {
-        QueryPlan plan = selectPlans.get(0);
-        int columnCount = plan.getProjector().getColumnCount();
-        List<? extends ColumnProjector> projectors = plan.getProjector().getColumnProjectors();
-        List<PDataType> selectTypes = new ArrayList<PDataType>();
-        for (ColumnProjector pro : projectors) {
-            selectTypes.add(pro.getExpression().getDataType());
-        }
+    private static List<TargetDataExpression> checkProjectionNumAndExpressions(
+        List<QueryPlan> selectPlans) throws SQLException {
+        int columnCount = selectPlans.get(0).getProjector().getColumnCount();
+        List<TargetDataExpression> targetTypes = new ArrayList<TargetDataExpression>(columnCount);
 
-        for (int i = 1;  i < selectPlans.size(); i++) {     
-            plan = selectPlans.get(i);
-            if (columnCount !=plan.getProjector().getColumnCount()) {
-                throw new SQLExceptionInfo.Builder(SQLExceptionCode.SELECT_COLUMN_NUM_IN_UNIONALL_DIFFS).setMessage(".").build().buildException();
-            }
-            List<? extends ColumnProjector> pros =  plan.getProjector().getColumnProjectors();
-            for (int j = 0; j < columnCount; j++) {
-                PDataType type = pros.get(j).getExpression().getDataType();
-                if (type != null && !type.isCoercibleTo(selectTypes.get(j))) {
-                    throw new SQLExceptionInfo.Builder(SQLExceptionCode.SELECT_COLUMN_TYPE_IN_UNIONALL_DIFFS).setMessage(".").build().buildException();
+        for (int i = 0; i < columnCount; i++) {
+            for (QueryPlan plan : selectPlans) {
+                if (columnCount !=plan.getProjector().getColumnCount()) {
+                    throw new SQLExceptionInfo.Builder(SQLExceptionCode
+                        .SELECT_COLUMN_NUM_IN_UNIONALL_DIFFS).setMessage(".")
+                        .build().buildException();
+                }
+                ColumnProjector colproj = plan.getProjector().getColumnProjector(i);
+                if(targetTypes.size() < i+1 ) {
+                    targetTypes.add(new TargetDataExpression(colproj.getExpression()));
+                } else {
+                    compareExperssions(i, colproj.getExpression(), targetTypes);
                 }
             }
         }
-        return selectPlans;
+        return targetTypes;
     }
 
-    public static TableRef contructSchemaTable(PhoenixStatement statement, QueryPlan plan, List<AliasedNode> selectNodes) throws SQLException {
+    public static TableRef contructSchemaTable(PhoenixStatement statement, List<QueryPlan> plans,
+            List<AliasedNode> selectNodes) throws SQLException {
+        List<TargetDataExpression> targetTypes = checkProjectionNumAndExpressions(plans);
+        for (int i = 0; i < plans.size(); i++) {
+            QueryPlan subPlan = plans.get(i);
+            TupleProjector projector = getTupleProjector(subPlan.getProjector(), targetTypes);
+            subPlan = new TupleProjectionPlan(subPlan, projector, null);
+            plans.set(i, subPlan);
+        }
+        QueryPlan plan = plans.get(0);
         List<PColumn> projectedColumns = new ArrayList<PColumn>();
-        for (int i=0; i< plan.getProjector().getColumnCount(); i++) {
+        for (int i = 0; i < plan.getProjector().getColumnCount(); i++) {
             ColumnProjector colProj = plan.getProjector().getColumnProjector(i);
-            Expression sourceExpression = colProj.getExpression();
             String name = selectNodes == null ? colProj.getName() : selectNodes.get(i).getAlias();
-            PColumnImpl projectedColumn = new PColumnImpl(PNameFactory.newName(name), UNION_FAMILY_NAME,
-                    sourceExpression.getDataType(), sourceExpression.getMaxLength(), sourceExpression.getScale(), sourceExpression.isNullable(),
-                    i, sourceExpression.getSortOrder(), 500, null, false, sourceExpression.toString(), false, false);
+            PColumnImpl projectedColumn = new PColumnImpl(PNameFactory.newName(name),
+                UNION_FAMILY_NAME, targetTypes.get(i).getType(), targetTypes.get(i).getMaxLength(),
+                targetTypes.get(i).getScale(), colProj.getExpression().isNullable(), i,
+                targetTypes.get(i).getSortOrder(), 500, null, false,
+                colProj.getExpression().toString(), false, false);
             projectedColumns.add(projectedColumn);
         }
         Long scn = statement.getConnection().getSCN();
-        PTable tempTable = PTableImpl.makePTable(statement.getConnection().getTenantId(), UNION_SCHEMA_NAME,
-                UNION_TABLE_NAME, PTableType.SUBQUERY, null, HConstants.LATEST_TIMESTAMP,
-                scn == null ? HConstants.LATEST_TIMESTAMP : scn, null, null, projectedColumns, null, null, null, true,
-                null, null, null, true, true, true, null, null, null, false, false, 0, 0L,
-                SchemaUtil.isNamespaceMappingEnabled(PTableType.SUBQUERY,
-                        statement.getConnection().getQueryServices().getProps()), null, false);
+        PTable tempTable = PTableImpl.makePTable(statement.getConnection().getTenantId(),
+            UNION_SCHEMA_NAME, UNION_TABLE_NAME, PTableType.SUBQUERY, null,
+            HConstants.LATEST_TIMESTAMP, scn == null ? HConstants.LATEST_TIMESTAMP : scn,
+            null, null, projectedColumns, null, null, null, true, null, null, null, true,
+            true, true, null, null, null, false, false, 0, 0L,
+            SchemaUtil.isNamespaceMappingEnabled(PTableType.SUBQUERY,
+                statement.getConnection().getQueryServices().getProps()), null, false);
         TableRef tableRef = new TableRef(null, tempTable, 0, false);
         return tableRef;
     }
+
+    private static void compareExperssions(int i, Expression expression,
+            List<TargetDataExpression> targetTypes) throws SQLException {
+        PDataType type = expression.getDataType();
+        if (type != null && type.isCoercibleTo(targetTypes.get(i).getType())) {
+            ;
+        }
+        else if (targetTypes.get(i).getType() == null || targetTypes.get(i).getType().isCoercibleTo(type)) {
+            targetTypes.get(i).setType(type);
+        } else {
+            throw new SQLExceptionInfo.Builder(SQLExceptionCode
+                .SELECT_COLUMN_TYPE_IN_UNIONALL_DIFFS).setMessage(".")
+                .build().buildException();
+        }
+        Integer len = expression.getMaxLength();
+        if (len != null && (targetTypes.get(i).getMaxLength() == null ||
+                len > targetTypes.get(i).getMaxLength())) {
+            targetTypes.get(i).setMaxLength(len);
+        }
+        Integer scale = expression.getScale();
+        if (scale != null && (targetTypes.get(i).getScale() == null ||
+                scale > targetTypes.get(i).getScale())){
+            targetTypes.get(i).setScale(scale);
+        }
+       SortOrder sortOrder = expression.getSortOrder();
+        if (sortOrder != null && (!sortOrder.equals(targetTypes.get(i).getSortOrder())))
+            targetTypes.get(i).setSortOrder(SortOrder.getDefault()); 
+    }
+
+    private static TupleProjector getTupleProjector(RowProjector rowProj,
+            List<TargetDataExpression> targetTypes) throws SQLException {
+        Expression[] exprs = new Expression[targetTypes.size()];
+        int i = 0;
+        for (ColumnProjector colProj : rowProj.getColumnProjectors()) {
+            exprs[i] = CoerceExpression.create(colProj.getExpression(),
+                targetTypes.get(i).getType(), targetTypes.get(i).getSortOrder(),
+                targetTypes.get(i).getMaxLength());
+            i++;
+        }
+        return new TupleProjector(exprs);
+    }
+
+    private static class TargetDataExpression {
+        private PDataType type;
+        private Integer maxLength;
+        private Integer scale;
+        private SortOrder sortOrder;
+
+        public TargetDataExpression(Expression expr) {
+            this.type = expr.getDataType();
+            this.maxLength = expr.getMaxLength();
+            this.scale = expr.getScale();
+            this.sortOrder = expr.getSortOrder();
+        }
+
+        public PDataType getType() {
+            return type;
+        }
+
+        public void setType(PDataType type) {
+            this.type = type;
+        }
+
+        public Integer getMaxLength() {
+            return maxLength;
+        }
+
+        public void setMaxLength(Integer maxLength) {
+            this.maxLength = maxLength;
+        }
+
+        public Integer getScale() {
+            return scale;
+        }
+
+        public void setScale(Integer scale) {
+            this.scale = scale;
+        }
+
+        public SortOrder getSortOrder() {
+            return sortOrder;
+        }
+
+        public void setSortOrder(SortOrder sortOrder) {
+            this.sortOrder = sortOrder;
+        }
+    }
 }