Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / MyDerby-10.3 / java / engine / org / apache / derby / impl / sql / compile / FromSubquery.java
diff --git a/JMCR-Stable/real-world application/MyDerby-10.3/java/engine/org/apache/derby/impl/sql/compile/FromSubquery.java b/JMCR-Stable/real-world application/MyDerby-10.3/java/engine/org/apache/derby/impl/sql/compile/FromSubquery.java
new file mode 100644 (file)
index 0000000..697d346
--- /dev/null
@@ -0,0 +1,644 @@
+/*\r
+\r
+   Derby - Class org.apache.derby.impl.sql.compile.FromSubquery\r
+\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to you under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+      http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+\r
+ */\r
+\r
+package        org.apache.derby.impl.sql.compile;\r
+\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.sql.compile.C_NodeTypes;\r
+import org.apache.derby.iapi.sql.dictionary.DataDictionary;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.util.JBitSet;\r
+\r
+\r
+/**\r
+ * A FromSubquery represents a subquery in the FROM list of a DML statement.\r
+ *\r
+ * The current implementation of this class is only\r
+ * sufficient for Insert's need to push a new\r
+ * select on top of the one the user specified,\r
+ * to make the selected structure match that\r
+ * of the insert target table.\r
+ *\r
+ */\r
+public class FromSubquery extends FromTable\r
+{\r
+       ResultSetNode   subquery;\r
+\r
+       /**\r
+        * Intializer for a table in a FROM list.\r
+        *\r
+        * @param subquery              The subquery\r
+        * @param correlationName       The correlation name\r
+        * @param derivedRCL            The derived column list\r
+        * @param tableProperties       Properties list associated with the table\r
+        */\r
+       public void init(\r
+                                       Object subquery,\r
+                                       Object correlationName,\r
+                                       Object derivedRCL,\r
+                                       Object tableProperties)\r
+       {\r
+               super.init(correlationName, tableProperties);\r
+               this.subquery = (ResultSetNode) subquery;\r
+               resultColumns = (ResultColumnList) derivedRCL;\r
+       }\r
+\r
+       /**\r
+        * Convert this object to a String.  See comments in QueryTreeNode.java\r
+        * for how this should be done for tree printing.\r
+        *\r
+        * @return      This object as a String\r
+        */\r
+\r
+       public String toString()\r
+       {\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       return  super.toString();\r
+               }\r
+               else\r
+               {\r
+                       return "";\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Prints the sub-nodes of this object.  See QueryTreeNode.java for\r
+        * how tree printing is supposed to work.\r
+        *\r
+        * @param depth         The depth of this node in the tree\r
+        */\r
+\r
+       public void printSubNodes(int depth)\r
+       {\r
+               if (SanityManager.DEBUG) {\r
+                       super.printSubNodes(depth);\r
+\r
+                       if (subquery != null)\r
+                       {\r
+                               printLabel(depth, "subquery: ");\r
+                               subquery.treePrint(depth + 1);\r
+                       }\r
+               }\r
+       }\r
+\r
+       /** \r
+        * Return the "subquery" from this node.\r
+        *\r
+        * @return ResultSetNode        The "subquery" from this node.\r
+        */\r
+       public ResultSetNode getSubquery()\r
+       {\r
+               return subquery;\r
+       }\r
+\r
+       /** \r
+        * Determine whether or not the specified name is an exposed name in\r
+        * the current query block.\r
+        *\r
+        * @param name  The specified name to search for as an exposed name.\r
+        * @param schemaName    Schema name, if non-null.\r
+        * @param exactMatch    Whether or not we need an exact match on specified schema and table\r
+        *                                              names or match on table id.\r
+        *\r
+        * @return The FromTable, if any, with the exposed name.\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       protected FromTable getFromTableByName(String name, String schemaName, boolean exactMatch)\r
+               throws StandardException\r
+       {\r
+               return super.getFromTableByName(name, schemaName, exactMatch);\r
+       }\r
+\r
+       /**\r
+        * Bind this subquery that appears in the FROM list.\r
+        *\r
+        * @param dataDictionary        The DataDictionary to use for binding\r
+        * @param fromListParam         FromList to use/append to.\r
+        *\r
+        * @return      ResultSetNode           The bound FromSubquery.\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+\r
+       public ResultSetNode bindNonVTITables(DataDictionary dataDictionary, \r
+                                                 FromList fromListParam) \r
+                                                       throws StandardException\r
+       {\r
+               /* Assign the tableNumber */\r
+               if (tableNumber == -1)  // allow re-bind, in which case use old number\r
+                       tableNumber = getCompilerContext().getNextTableNumber();\r
+\r
+               subquery = subquery.bindNonVTITables(dataDictionary, fromListParam);\r
+\r
+               return this;\r
+       }\r
+\r
+       /**\r
+        * Bind this subquery that appears in the FROM list.\r
+        *\r
+        * @param fromListParam         FromList to use/append to.\r
+        *\r
+        * @return      ResultSetNode           The bound FromSubquery.\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+\r
+       public ResultSetNode bindVTITables(FromList fromListParam) \r
+                                                       throws StandardException\r
+       {\r
+               subquery = subquery.bindVTITables(fromListParam);\r
+\r
+               return this;\r
+       }\r
+\r
+       /**\r
+        * Check for (and reject) ? parameters directly under the ResultColumns.\r
+        * This is done for SELECT statements.  For FromSubquery, we\r
+        * simply pass the check through to the subquery.\r
+        *\r
+        * @exception StandardException         Thrown if a ? parameter found\r
+        *                                                                      directly under a ResultColumn\r
+        */\r
+\r
+       public void rejectParameters() throws StandardException\r
+       {\r
+               subquery.rejectParameters();\r
+       }\r
+\r
+       /**\r
+        * Bind the expressions in this FromSubquery.  This means \r
+        * binding the sub-expressions, as well as figuring out what the return \r
+        * type is for each expression.\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+\r
+       public void bindExpressions(FromList fromListParam)\r
+                                       throws StandardException\r
+       {\r
+               FromList                        emptyFromList =\r
+                                                               (FromList) getNodeFactory().getNode(\r
+                                                                       C_NodeTypes.FROM_LIST,\r
+                                                                       getNodeFactory().doJoinOrderOptimization(),\r
+                                                                       getContextManager());\r
+               ResultColumnList        derivedRCL = resultColumns;\r
+               ResultColumnList        subqueryRCL;\r
+               FromList                        nestedFromList;\r
+\r
+               /* From subqueries cannot be correlated, so we pass an empty FromList\r
+                * to subquery.bindExpressions() and .bindResultColumns()\r
+                */\r
+               \r
+               nestedFromList = emptyFromList;\r
+               subquery.bindExpressions(nestedFromList);\r
+               subquery.bindResultColumns(nestedFromList);\r
+\r
+               /* Now that we've bound the expressions in the subquery, we \r
+                * can propagate the subquery's RCL up to the FromSubquery.\r
+                * Get the subquery's RCL, assign shallow copy back to\r
+                * it and create new VirtualColumnNodes for the original's\r
+                * ResultColumn.expressions.\r
+                * NOTE: If the size of the derived column list is less than\r
+                * the size of the subquery's RCL and the derived column list is marked\r
+                * for allowing a size mismatch, then we have a select * view\r
+                * on top of a table that has had columns added to it via alter table.\r
+                * In this case, we trim out the columns that have been added to\r
+                * the table since the view was created.\r
+                */\r
+               subqueryRCL = subquery.getResultColumns();\r
+               if (resultColumns != null && resultColumns.getCountMismatchAllowed() &&\r
+                       resultColumns.size() < subqueryRCL.size())\r
+               {\r
+                       for (int index = subqueryRCL.size() - 1; \r
+                                index >= resultColumns.size(); \r
+                                index--)\r
+                       {\r
+                               subqueryRCL.removeElementAt(index);\r
+                       }\r
+               }\r
+\r
+               subquery.setResultColumns(subqueryRCL.copyListAndObjects());\r
+               subqueryRCL.genVirtualColumnNodes(subquery, subquery.getResultColumns());\r
+               resultColumns = subqueryRCL;\r
+\r
+               /* Propagate the name info from the derived column list */\r
+               if (derivedRCL != null)\r
+               {\r
+                        resultColumns.propagateDCLInfo(derivedRCL, correlationName);\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Try to find a ResultColumn in the table represented by this FromBaseTable\r
+        * that matches the name in the given ColumnReference.\r
+        *\r
+        * @param columnReference       The columnReference whose name we're looking\r
+        *                              for in the given table.\r
+        *\r
+        * @return      A ResultColumn whose expression is the ColumnNode\r
+        *                      that matches the ColumnReference.\r
+        *              Returns null if there is no match.\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+\r
+       public ResultColumn getMatchingColumn(ColumnReference columnReference) throws StandardException\r
+       {\r
+               ResultColumn    resultColumn = null;\r
+               String                  columnsTableName;\r
+\r
+               /*\r
+               ** RESOLVE: When we add support for schemas, check to see if\r
+               ** the column name specifies a schema, and if so, if this\r
+               ** table is in that schema.\r
+               */\r
+\r
+               columnsTableName = columnReference.getTableName();\r
+\r
+               // post 681, 1 may be no longer needed. 5 is the default case\r
+               // now but what happens if the condition is false? Investigate.\r
+               if (columnReference.getGeneratedToReplaceAggregate()) // 1\r
+               {\r
+                       resultColumn = resultColumns.getResultColumn(columnReference.getColumnName());\r
+               }\r
+               else if (columnsTableName == null || columnsTableName.equals(correlationName)) // 5?\r
+               {\r
+                   resultColumn = resultColumns.getAtMostOneResultColumn(columnReference, correlationName, false);\r
+               }\r
+                   \r
+\r
+               if (resultColumn != null)\r
+               {\r
+                       columnReference.setTableNumber(tableNumber);\r
+               }\r
+\r
+               return resultColumn;\r
+       }\r
+\r
+       /**\r
+        * Preprocess a ResultSetNode - this currently means:\r
+        *      o  Generating a referenced table map for each ResultSetNode.\r
+        *  o  Putting the WHERE and HAVING clauses in conjunctive normal form (CNF).\r
+        *  o  Converting the WHERE and HAVING clauses into PredicateLists and\r
+        *         classifying them.\r
+        *  o  Ensuring that a ProjectRestrictNode is generated on top of every \r
+        *     FromBaseTable and generated in place of every FromSubquery.  \r
+        *  o  Pushing single table predicates down to the new ProjectRestrictNodes.\r
+        *\r
+        * @param numTables                     The number of tables in the DML Statement\r
+        * @param gbl                           The group by list, if any\r
+        * @param fromList                      The from list, if any\r
+        *\r
+        * @return ResultSetNode at top of preprocessed tree.\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+\r
+       public ResultSetNode preprocess(int numTables,\r
+                                                                       GroupByList gbl,\r
+                                                                       FromList fromList)\r
+                                                               throws StandardException\r
+       {\r
+               /* We want to chop out the FromSubquery from the tree and replace it \r
+                * with a ProjectRestrictNode.  One complication is that there may be \r
+                * ColumnReferences above us which point to the FromSubquery's RCL.\r
+                * What we want to return is a tree with a PRN with the\r
+                * FromSubquery's RCL on top.  (In addition, we don't want to be\r
+                * introducing any redundant ProjectRestrictNodes.)\r
+                * Another complication is that we want to be able to only only push\r
+                * projections and restrictions down to this ProjectRestrict, but\r
+                * we want to be able to push them through as well.\r
+                * So, we:\r
+                *              o call subquery.preprocess() which returns a tree with\r
+                *                a SelectNode or a RowResultSetNode on top.  \r
+                *              o If the FSqry is flattenable(), then we return (so that the\r
+                *                caller can then call flatten()), otherwise we:\r
+                *              o generate a PRN, whose RCL is the FSqry's RCL, on top of the result.\r
+                *              o create a referencedTableMap for the PRN which represents \r
+                *                the FSqry's tableNumber, since ColumnReferences in the outer\r
+                *                query block would be referring to that one.  \r
+                *                (This will allow us to push restrictions down to the PRN.)\r
+                */\r
+\r
+               subquery = subquery.preprocess(numTables, gbl, fromList);\r
+\r
+               /* Return if the FSqry is flattenable() \r
+                * NOTE: We can't flatten a FromSubquery if there is a group by list\r
+                * because the group by list must be ColumnReferences.  For:\r
+                *      select c1 from v1 group by c1,\r
+                *      where v1 is select 1 from t1\r
+                * The expression under the last redundant ResultColumn is an IntConstantNode,\r
+                * not a ColumnReference.\r
+                * We also do not flatten a subquery if tableProperties is non-null,\r
+                * as the user is specifying 1 or more properties for the derived table,\r
+                * which could potentially be lost on the flattening.\r
+                * RESOLVE - this is too restrictive.\r
+                */\r
+               if ((gbl == null || gbl.size() == 0) &&\r
+                       tableProperties == null &&\r
+                   subquery.flattenableInFromSubquery(fromList))\r
+               {\r
+                       /* Set our table map to the subquery's table map. */\r
+                       setReferencedTableMap(subquery.getReferencedTableMap());\r
+                       return this;\r
+               }\r
+\r
+               return extractSubquery(numTables);\r
+       }\r
+\r
+       /**\r
+        * Extract out and return the subquery, with a PRN on top.\r
+        * (See FromSubquery.preprocess() for more details.)\r
+        *\r
+        * @param numTables                     The number of tables in the DML Statement\r
+        *\r
+        * @return ResultSetNode at top of extracted tree.\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+\r
+       public ResultSetNode extractSubquery(int numTables)\r
+               throws StandardException\r
+       {\r
+               JBitSet           newJBS;\r
+               ResultSetNode newPRN;\r
+\r
+               newPRN = (ResultSetNode) getNodeFactory().getNode(\r
+                                                               C_NodeTypes.PROJECT_RESTRICT_NODE,\r
+                                                               subquery,               /* Child ResultSet */\r
+                                                               resultColumns,  /* Projection */\r
+                                                               null,                   /* Restriction */\r
+                                                               null,                   /* Restriction as PredicateList */\r
+                                                               null,                   /* Subquerys in Projection */\r
+                                                               null,                   /* Subquerys in Restriction */\r
+                                                               tableProperties,\r
+                                                               getContextManager()      );\r
+\r
+               /* Set up the PRN's referencedTableMap */\r
+               newJBS = new JBitSet(numTables);\r
+               newJBS.set(tableNumber);\r
+               newPRN.setReferencedTableMap(newJBS);\r
+               ((FromTable) newPRN).setTableNumber(tableNumber);\r
+\r
+               return newPRN;\r
+       }\r
+\r
+       /**\r
+        * Flatten this FSqry into the outer query block. The steps in\r
+        * flattening are:\r
+        *      o  Mark all ResultColumns as redundant, so that they are "skipped over"\r
+        *         at generate().\r
+        *      o  Append the wherePredicates to the outer list.\r
+        *      o  Return the fromList so that the caller will merge the 2 lists \r
+        *  RESOLVE - FSqrys with subqueries are currently not flattenable.  Some of\r
+        *  them can be flattened, however.  We need to merge the subquery list when\r
+        *  we relax this restriction.\r
+        *\r
+        * NOTE: This method returns NULL when flattening RowResultSetNodes\r
+        * (the node for a VALUES clause).  The reason is that no reference\r
+        * is left to the RowResultSetNode after flattening is done - the\r
+        * expressions point directly to the ValueNodes in the RowResultSetNode's\r
+        * ResultColumnList.\r
+        *\r
+        * @param rcl                           The RCL from the outer query\r
+        * @param outerPList    PredicateList to append wherePredicates to.\r
+        * @param sql                           The SubqueryList from the outer query\r
+        * @param gbl                           The group by list, if any\r
+        *\r
+        * @return FromList             The fromList from the underlying SelectNode.\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       public FromList flatten(ResultColumnList rcl,\r
+                                                       PredicateList outerPList,\r
+                                                       SubqueryList sql,\r
+                                                       GroupByList gbl)\r
+\r
+                       throws StandardException\r
+       {\r
+               FromList        fromList = null;\r
+               SelectNode      selectNode;\r
+\r
+               resultColumns.setRedundant();\r
+\r
+               subquery.getResultColumns().setRedundant();\r
+\r
+               /*\r
+               ** RESOLVE: Each type of result set should know how to remap itself.\r
+               */\r
+               if (subquery instanceof SelectNode)\r
+               {\r
+                       selectNode = (SelectNode) subquery;\r
+                       fromList = selectNode.getFromList();\r
+\r
+                       // selectNode.getResultColumns().setRedundant();\r
+\r
+                       if (selectNode.getWherePredicates().size() > 0)\r
+                       {\r
+                               outerPList.destructiveAppend(selectNode.getWherePredicates());\r
+                       }\r
+\r
+                       if (selectNode.getWhereSubquerys().size() > 0)\r
+                       {\r
+                               sql.destructiveAppend(selectNode.getWhereSubquerys());\r
+                       }\r
+               }\r
+               else if ( ! (subquery instanceof RowResultSetNode))\r
+               {\r
+                       if (SanityManager.DEBUG)\r
+                       {\r
+                               SanityManager.THROWASSERT("subquery expected to be either a SelectNode or a RowResultSetNode, but is a " + subquery.getClass().getName());\r
+                       }\r
+               }\r
+\r
+               /* Remap all ColumnReferences from the outer query to this node.\r
+                * (We replace those ColumnReferences with clones of the matching\r
+                * expression in the SELECT's RCL.\r
+                */\r
+               rcl.remapColumnReferencesToExpressions();\r
+               outerPList.remapColumnReferencesToExpressions();\r
+               if (gbl != null)\r
+               {\r
+                       gbl.remapColumnReferencesToExpressions();\r
+               }\r
+\r
+               return fromList;\r
+       }\r
+\r
+       /**\r
+        * Get the exposed name for this table, which is the name that can\r
+        * be used to refer to it in the rest of the query.\r
+        *\r
+        * @return      The exposed name for this table.\r
+        */\r
+\r
+       public String getExposedName()\r
+       {\r
+               return correlationName;\r
+       }\r
+\r
+       /**\r
+        * Expand a "*" into a ResultColumnList with all of the\r
+        * result columns from the subquery.\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       public ResultColumnList getAllResultColumns(TableName allTableName)\r
+                       throws StandardException\r
+       {\r
+               ResultColumnList rcList = null;\r
+               TableName                exposedName;\r
+        TableName        toCompare;\r
+\r
+\r
+               if(allTableName != null)\r
+             toCompare = makeTableName(allTableName.getSchemaName(),correlationName);\r
+        else\r
+            toCompare = makeTableName(null,correlationName);\r
+        \r
+        if ( allTableName != null &&\r
+             ! allTableName.equals(toCompare))\r
+        {\r
+            return null;\r
+        }\r
+\r
+               /* Cache exposed name for this table.\r
+                * The exposed name becomes the qualifier for each column\r
+                * in the expanded list.\r
+                */\r
+               exposedName = makeTableName(null, correlationName);\r
+\r
+               rcList = (ResultColumnList) getNodeFactory().getNode(\r
+                                                                               C_NodeTypes.RESULT_COLUMN_LIST,\r
+                                                                               getContextManager());\r
+\r
+               /* Build a new result column list based off of resultColumns.\r
+                * NOTE: This method will capture any column renaming due to \r
+                * a derived column list.\r
+                */\r
+               int rclSize = resultColumns.size();\r
+               for (int index = 0; index < rclSize; index++)\r
+               {\r
+                       ResultColumn resultColumn = (ResultColumn) resultColumns.elementAt(index);\r
+                       ValueNode                valueNode;\r
+                       String                   columnName;\r
+\r
+                       if (resultColumn.isGenerated())\r
+                       {\r
+                               continue;\r
+                       }\r
+\r
+                       // Build a ResultColumn/ColumnReference pair for the column //\r
+                       columnName = resultColumn.getName();\r
+                       boolean isNameGenerated = resultColumn.isNameGenerated();\r
+\r
+                       /* If this node was generated for a GROUP BY, then tablename for the CR, if any,\r
+                        * comes from the source RC.\r
+                        */\r
+                       TableName tableName;\r
+\r
+                       tableName = exposedName;\r
+\r
+                       valueNode = (ValueNode) getNodeFactory().getNode(\r
+                                                                                       C_NodeTypes.COLUMN_REFERENCE,\r
+                                                                                       columnName,\r
+                                                                                       tableName,\r
+                                                                                       getContextManager());\r
+                       resultColumn = (ResultColumn) getNodeFactory().getNode(\r
+                                                                                       C_NodeTypes.RESULT_COLUMN,\r
+                                                                                       columnName,\r
+                                                                                       valueNode,\r
+                                                                                       getContextManager());\r
+\r
+                       resultColumn.setNameGenerated(isNameGenerated);\r
+                       // Build the ResultColumnList to return //\r
+                       rcList.addResultColumn(resultColumn);\r
+               }\r
+               return rcList;\r
+       }\r
+\r
+       /** \r
+        * @see QueryTreeNode#disablePrivilegeCollection\r
+        */\r
+       public void disablePrivilegeCollection()\r
+       {\r
+               super.disablePrivilegeCollection();\r
+               subquery.disablePrivilegeCollection();\r
+       }\r
+\r
+       /**\r
+        * Search to see if a query references the specifed table name.\r
+        *\r
+        * @param name          Table name (String) to search for.\r
+        * @param baseTable     Whether or not name is for a base table\r
+        *\r
+        * @return      true if found, else false\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       public boolean referencesTarget(String name, boolean baseTable)\r
+               throws StandardException\r
+       {\r
+               return subquery.referencesTarget(name, baseTable);\r
+       }\r
+\r
+       /**\r
+        * Return true if the node references SESSION schema tables (temporary or permanent)\r
+        *\r
+        * @return      true if references SESSION schema tables, else false\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       public boolean referencesSessionSchema()\r
+               throws StandardException\r
+       {\r
+               return subquery.referencesSessionSchema();\r
+       }\r
+\r
+       /**\r
+        * Bind any untyped null nodes to the types in the given ResultColumnList.\r
+        *\r
+        * @param bindingRCL    The ResultColumnList with the types to bind to.\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       public void bindUntypedNullsToResultColumns(ResultColumnList bindingRCL)\r
+                               throws StandardException\r
+       {\r
+               subquery.bindUntypedNullsToResultColumns(bindingRCL);\r
+       }\r
+\r
+       /**\r
+        * Decrement (query block) level (0-based) for this FromTable.\r
+        * This is useful when flattening a subquery.\r
+        *\r
+        * @param decrement     The amount to decrement by.\r
+        */\r
+       void decrementLevel(int decrement)\r
+       {\r
+               super.decrementLevel(decrement);\r
+               subquery.decrementLevel(decrement);\r
+       }\r
+}\r