fix issue 29

git-svn-id: https://asterixdb.googlecode.com/svn/branches/asterix_stabilization_printerfix@919 eaa15691-b419-025a-1212-ee371bd00084
diff --git a/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/IntroduceStaticTypeCastRule.java b/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/IntroduceStaticTypeCastRule.java
index c8a8f26..a3edd9a 100644
--- a/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/IntroduceStaticTypeCastRule.java
+++ b/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/IntroduceStaticTypeCastRule.java
@@ -92,43 +92,59 @@
             return false;
         context.addToDontApplySet(this, opRef.getValue());
         AbstractLogicalOperator op1 = (AbstractLogicalOperator) opRef.getValue();
-        if (op1.getOperatorTag() != LogicalOperatorTag.SINK)
-            return false;
-        AbstractLogicalOperator op2 = (AbstractLogicalOperator) op1.getInputs().get(0).getValue();
-        if (op2.getOperatorTag() != LogicalOperatorTag.INSERT_DELETE)
-            return false;
-        AbstractLogicalOperator op3 = (AbstractLogicalOperator) op2.getInputs().get(0).getValue();
-        if (op3.getOperatorTag() != LogicalOperatorTag.ASSIGN)
-            return false;
-
-        /**
-         * get required record type
-         */
-        InsertDeleteOperator insertDeleteOperator = (InsertDeleteOperator) op2;
-        AssignOperator topAssignOperator = (AssignOperator) op3;
-        AqlDataSource dataSource = (AqlDataSource) insertDeleteOperator.getDataSource();
-        IAType[] schemaTypes = (IAType[]) dataSource.getSchemaTypes();
-        ARecordType requiredRecordType = (ARecordType) schemaTypes[schemaTypes.length - 1];
-
-        /**
-         * get input record type to the insert operator
-         */
-        List<LogicalVariable> usedVariables = new ArrayList<LogicalVariable>();
-        VariableUtilities.getUsedVariables(topAssignOperator, usedVariables);
-
-        // the used variable should contain the record that will be inserted
-        // but it will not fail in many cases even if the used variable set is
-        // empty
-        if (usedVariables.size() == 0)
-            return false;
-        LogicalVariable oldRecordVariable = usedVariables.get(0);
-        LogicalVariable inputRecordVar = usedVariables.get(0);
-        IVariableTypeEnvironment env = topAssignOperator.computeOutputTypeEnvironment(context);
-        ARecordType inputRecordType = (ARecordType) env.getVarType(inputRecordVar);
-
-        AbstractLogicalOperator currentOperator = topAssignOperator;
+        AbstractLogicalOperator assignOp = null;
+        IAType requiredRecordType = null;
+        IAType inputRecordType = null;
+        boolean isTopWrite = false;
         List<LogicalVariable> producedVariables = new ArrayList<LogicalVariable>();
+        LogicalVariable oldRecordVariable;
 
+        if (op1.getOperatorTag() == LogicalOperatorTag.SINK) {
+            AbstractLogicalOperator op2 = (AbstractLogicalOperator) op1.getInputs().get(0).getValue();
+            if (op2.getOperatorTag() != LogicalOperatorTag.INSERT_DELETE)
+                return false;
+            assignOp = (AbstractLogicalOperator) op2.getInputs().get(0).getValue();
+            if (assignOp.getOperatorTag() != LogicalOperatorTag.ASSIGN)
+                return false;
+            /**
+             * get required record type
+             */
+            InsertDeleteOperator insertDeleteOperator = (InsertDeleteOperator) op2;
+            AqlDataSource dataSource = (AqlDataSource) insertDeleteOperator.getDataSource();
+            IAType[] schemaTypes = (IAType[]) dataSource.getSchemaTypes();
+            requiredRecordType = schemaTypes[schemaTypes.length - 1];
+
+            AssignOperator topAssignOperator = (AssignOperator) assignOp;
+            List<LogicalVariable> usedVariables = new ArrayList<LogicalVariable>();
+            VariableUtilities.getUsedVariables(topAssignOperator, usedVariables);
+
+            // the used variable should contain the record that will be inserted
+            // but it will not fail in many cases even if the used variable set is
+            // empty
+            if (usedVariables.size() == 0)
+                return false;
+            oldRecordVariable = usedVariables.get(0);
+            LogicalVariable inputRecordVar = usedVariables.get(0);
+            IVariableTypeEnvironment env = topAssignOperator.computeOutputTypeEnvironment(context);
+            inputRecordType = (IAType) env.getVarType(inputRecordVar);
+        } else if (op1.getOperatorTag() == LogicalOperatorTag.WRITE) {
+            AbstractLogicalOperator op2 = (AbstractLogicalOperator) op1.getInputs().get(0).getValue();
+            if (op2.getOperatorTag() != LogicalOperatorTag.PROJECT)
+                return false;
+            assignOp = (AbstractLogicalOperator) op2.getInputs().get(0).getValue();
+            if (assignOp.getOperatorTag() != LogicalOperatorTag.ASSIGN)
+                return false;
+            isTopWrite = true;
+            VariableUtilities.getProducedVariables(assignOp, producedVariables);
+            oldRecordVariable = producedVariables.get(0);
+            LogicalVariable inputVar = producedVariables.get(0);
+            IVariableTypeEnvironment env = assignOp.computeOutputTypeEnvironment(context);
+            inputRecordType = (IAType) env.getVarType(inputVar);
+        } else {
+            return false;
+        }
+
+        AbstractLogicalOperator currentOperator = assignOp;
         /**
          * find the assign operator for the "input record" to the insert_delete
          * operator
@@ -151,9 +167,17 @@
                         ScalarFunctionCallExpression funcExpr = (ScalarFunctionCallExpression) expr;
                         // that expression has been rewritten, and it will not
                         // fail but just return false
-                        if (TypeComputerUtilities.getRequiredType(funcExpr) != null)
+                        if (TypeComputerUtilities.getRequiredType(funcExpr) != null) {
                             return false;
-                        IVariableTypeEnvironment assignEnv = topAssignOperator.computeOutputTypeEnvironment(context);
+                        }
+                        if (isTopWrite) {
+                            if (funcExpr.getFunctionIdentifier() != AsterixBuiltinFunctions.UNORDERED_LIST_CONSTRUCTOR
+                                    && funcExpr.getFunctionIdentifier() != AsterixBuiltinFunctions.ORDERED_LIST_CONSTRUCTOR) {
+                                return false;
+                            }
+                            requiredRecordType = inputRecordType;
+                        }
+                        IVariableTypeEnvironment assignEnv = assignOp.computeOutputTypeEnvironment(context);
                         rewriteFuncExpr(funcExpr, requiredRecordType, inputRecordType, assignEnv);
                     }
                     context.computeAndSetTypeEnvironmentForOperator(originalAssign);
diff --git a/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/SetClosedRecordConstructorsRule.java b/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/SetClosedRecordConstructorsRule.java
index c121ff6..ad70d6f 100644
--- a/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/SetClosedRecordConstructorsRule.java
+++ b/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/SetClosedRecordConstructorsRule.java
@@ -92,47 +92,55 @@
             boolean changed = false;
             if (expr.getFunctionIdentifier().equals(AsterixBuiltinFunctions.OPEN_RECORD_CONSTRUCTOR)) {
                 ARecordType reqType = (ARecordType) TypeComputerUtilities.getRequiredType(expr);
-                if (reqType != null) {
-                    if (reqType.isOpen())
-                        allClosed = false;
-                }
-                int n = expr.getArguments().size();
-                if (n % 2 > 0) {
-                    throw new AlgebricksException("Record constructor expected to have an even number of arguments: "
-                            + expr);
-                }
-                for (int i = 0; i < n / 2; i++) {
-                    ILogicalExpression a0 = expr.getArguments().get(2 * i).getValue();
-                    if (a0.getExpressionTag() != LogicalExpressionTag.CONSTANT) {
-                        allClosed = false;
+                if (reqType == null || !reqType.isOpen()) {
+                    int n = expr.getArguments().size();
+                    if (n % 2 > 0) {
+                        throw new AlgebricksException(
+                                "Record constructor expected to have an even number of arguments: " + expr);
                     }
-                    Mutable<ILogicalExpression> aRef1 = expr.getArguments().get(2 * i + 1);
-                    ILogicalExpression a1 = aRef1.getValue();
-                    ClosedDataInfo cdi = a1.accept(this, arg);
-                    if (!cdi.dataIsClosed) {
-                        allClosed = false;
+                    for (int i = 0; i < n / 2; i++) {
+                        ILogicalExpression a0 = expr.getArguments().get(2 * i).getValue();
+                        if (a0.getExpressionTag() != LogicalExpressionTag.CONSTANT) {
+                            allClosed = false;
+                        }
+                        Mutable<ILogicalExpression> aRef1 = expr.getArguments().get(2 * i + 1);
+                        ILogicalExpression a1 = aRef1.getValue();
+                        ClosedDataInfo cdi = a1.accept(this, arg);
+                        if (!cdi.dataIsClosed) {
+                            allClosed = false;
+                        }
+                        if (cdi.expressionChanged) {
+                            aRef1.setValue(cdi.expression);
+                            changed = true;
+                        }
                     }
-                    if (cdi.expressionChanged) {
-                        aRef1.setValue(cdi.expression);
+                    if (allClosed) {
+                        expr.setFunctionInfo(FunctionUtils
+                                .getFunctionInfo(AsterixBuiltinFunctions.CLOSED_RECORD_CONSTRUCTOR));
+                        GlobalConfig.ASTERIX_LOGGER.finest("Switching to CLOSED record constructor in " + expr + ".\n");
                         changed = true;
                     }
                 }
-                if (allClosed) {
-                    expr.setFunctionInfo(FunctionUtils
-                            .getFunctionInfo(AsterixBuiltinFunctions.CLOSED_RECORD_CONSTRUCTOR));
-                    GlobalConfig.ASTERIX_LOGGER.finest("Switching to CLOSED record constructor in " + expr + ".\n");
-                    changed = true;
-                }
             } else {
-                for (Mutable<ILogicalExpression> e : expr.getArguments()) {
-                    ILogicalExpression ale = e.getValue();
-                    ClosedDataInfo cdi = ale.accept(this, arg);
-                    if (!cdi.dataIsClosed) {
-                        allClosed = false;
+                boolean rewrite = true;
+                if (expr.getFunctionIdentifier().equals(AsterixBuiltinFunctions.ORDERED_LIST_CONSTRUCTOR)
+                        || (expr.getFunctionIdentifier().equals(AsterixBuiltinFunctions.UNORDERED_LIST_CONSTRUCTOR))) {
+                    IAType reqType = TypeComputerUtilities.getRequiredType(expr);
+                    if (reqType == null) {
+                        rewrite = false;
                     }
-                    if (cdi.expressionChanged) {
-                        e.setValue(cdi.expression);
-                        changed = true;
+                }
+                if (rewrite) {
+                    for (Mutable<ILogicalExpression> e : expr.getArguments()) {
+                        ILogicalExpression ale = e.getValue();
+                        ClosedDataInfo cdi = ale.accept(this, arg);
+                        if (!cdi.dataIsClosed) {
+                            allClosed = false;
+                        }
+                        if (cdi.expressionChanged) {
+                            e.setValue(cdi.expression);
+                            changed = true;
+                        }
                     }
                 }
             }
@@ -144,7 +152,8 @@
                 throws AlgebricksException {
             Object varType = env.getVarType(expr.getVariableReference());
             if (varType == null) {
-                throw new AlgebricksException("Could not infer type for variable '" + expr.getVariableReference() + "'.");
+                throw new AlgebricksException("Could not infer type for variable '" + expr.getVariableReference()
+                        + "'.");
             }
             boolean dataIsClosed = isClosedRec((IAType) varType);
             return new ClosedDataInfo(false, dataIsClosed, expr);
diff --git a/asterix-app/src/test/resources/runtimets/queries/open-closed/query-issue29.aql b/asterix-app/src/test/resources/runtimets/queries/open-closed/query-issue29.aql
new file mode 100644
index 0000000..240330f
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/queries/open-closed/query-issue29.aql
@@ -0,0 +1,63 @@
+write output to nc1:"rttest/open-closed_query-issue29.adm";
+
+let $tweets := 
+{{
+   {
+      "tweetid": "1023",
+      "user": {
+         "screen-name": "dflynn24",
+         "lang": "en",
+         "friends_count": 46,
+         "statuses_count": 987,
+         "name": "danielle flynn",
+         "followers_count": 47
+      },
+      "sender-location": "40.904177,-72.958996",
+      "send-time": "2010-02-21T11:56:02-05:00",
+      "referred-topics": {{ "verizon" }},
+      "message-text": "i need a #verizon phone like nowwwww! :("
+   },
+   {
+      "tweetid": "1024",
+      "user": {
+         "screen-name": "miriamorous",
+         "lang": "en",
+         "friends_count": 69,
+         "statuses_count": 1068,
+         "name": "Miriam Songco",
+         "followers_count": 78
+      },
+      "send-time": "2010-02-21T11:11:43-08:00",
+      "referred-topics": {{ "commercials", "verizon", "att" }},
+      "message-text": "#verizon & #att #commercials, so competitive"
+   },
+   {
+      "tweetid": "1025",
+      "user": {
+         "screen-name": "dj33",
+         "lang": "en",
+         "friends_count": 96,
+         "statuses_count": 1696,
+         "name": "Don Jango",
+         "followers_count": 22
+      },
+      "send-time": "2010-02-21T12:38:44-05:00",
+      "referred-topics": {{ "charlotte" }},
+      "message-text": "Chillin at dca waiting for 900am flight to #charlotte and from there to providenciales"
+   },
+   {
+      "tweetid": "1026",
+      "user": {
+      "screen-name": "reallyleila",
+         "lang": "en",
+         "friends_count": 106,
+         "statuses_count": 107,
+         "name": "Leila Samii",
+         "followers_count": 52
+      },
+      "send-time": "2010-02-21T21:31:57-06:00",
+      "referred-topics": {{ "verizon", "at&t", "iphone" }},
+      "message-text": "I think a switch from #verizon to #at&t may be in my near future... my smartphone is like a land line compared to the #iphone!"
+   }
+}}
+return $tweets
\ No newline at end of file
diff --git a/asterix-app/src/test/resources/runtimets/results/open-closed/query-issue29.adm b/asterix-app/src/test/resources/runtimets/results/open-closed/query-issue29.adm
new file mode 100644
index 0000000..3d2819e
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/results/open-closed/query-issue29.adm
@@ -0,0 +1 @@
+{{ { "tweetid": "1023", "user": { "screen-name": "dflynn24", "lang": "en", "friends_count": 46, "statuses_count": 987, "name": "danielle flynn", "followers_count": 47 }, "sender-location": "40.904177,-72.958996", "send-time": "2010-02-21T11:56:02-05:00", "referred-topics": {{ "verizon" }}, "message-text": "i need a #verizon phone like nowwwww! :(" }, { "tweetid": "1024", "user": { "screen-name": "miriamorous", "lang": "en", "friends_count": 69, "statuses_count": 1068, "name": "Miriam Songco", "followers_count": 78 }, "send-time": "2010-02-21T11:11:43-08:00", "referred-topics": {{ "commercials", "verizon", "att" }}, "message-text": "#verizon & #att #commercials, so competitive" }, { "tweetid": "1025", "user": { "screen-name": "dj33", "lang": "en", "friends_count": 96, "statuses_count": 1696, "name": "Don Jango", "followers_count": 22 }, "send-time": "2010-02-21T12:38:44-05:00", "referred-topics": {{ "charlotte" }}, "message-text": "Chillin at dca waiting for 900am flight to #charlotte and from there to providenciales" }, { "tweetid": "1026", "user": { "screen-name": "reallyleila", "lang": "en", "friends_count": 106, "statuses_count": 107, "name": "Leila Samii", "followers_count": 52 }, "send-time": "2010-02-21T21:31:57-06:00", "referred-topics": {{ "verizon", "at&t", "iphone" }}, "message-text": "I think a switch from #verizon to #at&t may be in my near future... my smartphone is like a land line compared to the #iphone!" } }}
diff --git a/asterix-om/src/main/java/edu/uci/ics/asterix/runtime/pointables/printer/AListPrinter.java b/asterix-om/src/main/java/edu/uci/ics/asterix/runtime/pointables/printer/AListPrinter.java
index e20b1059a..5e38f93 100644
--- a/asterix-om/src/main/java/edu/uci/ics/asterix/runtime/pointables/printer/AListPrinter.java
+++ b/asterix-om/src/main/java/edu/uci/ics/asterix/runtime/pointables/printer/AListPrinter.java
@@ -57,7 +57,7 @@
         //print the beginning part
         ps.print(leftParen);
 
-        // print record 0 to n-2
+        // print item 0 to n-2
         for (int i = 0; i < items.size() - 1; i++) {
             IVisitablePointable itemTypeTag = itemTags.get(i);
             IVisitablePointable item = items.get(i);
@@ -69,7 +69,7 @@
             ps.print(COMMA);
         }
 
-        // print record n-1
+        // print item n-1
         if (items.size() > 0) {
             IVisitablePointable itemTypeTag = itemTags.get(itemTags.size() - 1);
             IVisitablePointable item = items.get(items.size() - 1);
diff --git a/asterix-om/src/main/java/edu/uci/ics/asterix/runtime/pointables/printer/ARecordPrinter.java b/asterix-om/src/main/java/edu/uci/ics/asterix/runtime/pointables/printer/ARecordPrinter.java
index b0b3dfa..86d502d 100644
--- a/asterix-om/src/main/java/edu/uci/ics/asterix/runtime/pointables/printer/ARecordPrinter.java
+++ b/asterix-om/src/main/java/edu/uci/ics/asterix/runtime/pointables/printer/ARecordPrinter.java
@@ -55,7 +55,7 @@
         // print the beginning part
         ps.print(LEFT_PAREN);
 
-        // print record 0 to n-2
+        // print field 0 to n-2
         for (int i = 0; i < fieldNames.size() - 1; i++) {
             IVisitablePointable itemTypeTag = fieldTags.get(i);
             IVisitablePointable item = fieldValues.get(i);
@@ -73,7 +73,7 @@
             ps.print(COMMA);
         }
 
-        // print record n-1
+        // print field n-1
         if (fieldValues.size() > 0) {
             IVisitablePointable itemTypeTag = fieldTags.get(fieldTags.size() - 1);
             IVisitablePointable item = fieldValues.get(fieldValues.size() - 1);