/***************************************************************************** * Copyright (C) 2008 EnterpriseDB Corporation. * Copyright (C) 2011 Stado Global Development Group. * * This file is part of Stado. * * Stado is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Stado is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Stado. If not, see <http://www.gnu.org/licenses/>. * * You can find Stado at http://www.stado.us * ****************************************************************************/ package org.postgresql.stado.parser; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.Types; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.log4j.Level; import org.postgresql.stado.common.util.Props; import org.postgresql.stado.common.util.XLogger; import org.postgresql.stado.constraintchecker.IConstraintChecker; import org.postgresql.stado.constraintchecker.UpdateForeignKeyChecker; import org.postgresql.stado.constraintchecker.UpdateForeignReferenceChecker; import org.postgresql.stado.constraintchecker.UpdatePrimaryKeyChecker; import org.postgresql.stado.engine.Engine; import org.postgresql.stado.engine.ExecutionResult; import org.postgresql.stado.engine.IExecutable; import org.postgresql.stado.engine.XDBSessionContext; import org.postgresql.stado.exception.XDBServerException; import org.postgresql.stado.metadata.DBNode; import org.postgresql.stado.metadata.SysColumn; import org.postgresql.stado.metadata.SysPermission; import org.postgresql.stado.metadata.SysTable; import org.postgresql.stado.misc.RSHelper; import org.postgresql.stado.optimizer.AttributeColumn; import org.postgresql.stado.optimizer.QueryCondition; import org.postgresql.stado.optimizer.QueryTree; import org.postgresql.stado.optimizer.RelationNode; import org.postgresql.stado.optimizer.SqlExpression; import org.postgresql.stado.parser.core.syntaxtree.SetUpdateClause; import org.postgresql.stado.parser.core.syntaxtree.UpdateTable; import org.postgresql.stado.parser.core.syntaxtree.WhereClause; import org.postgresql.stado.parser.handler.IdentifierHandler; import org.postgresql.stado.parser.handler.QueryConditionHandler; import org.postgresql.stado.parser.handler.QueryTreeHandler; import org.postgresql.stado.parser.handler.QueryTreeTracker; import org.postgresql.stado.parser.handler.SQLExpressionHandler; import org.postgresql.stado.parser.handler.TableNameHandler; import org.postgresql.stado.queryproc.QueryProcessor; /** * This class is responsible for holding the information for Update Table * Syntax: UPDATE <target_table> SET <column1> = <value1>, ..., <columnN> = * <valueN> WHERE <condition> Temp Table: INSERT INTO <temp_table> (<column1>_new, * ..., <columnN>_new, <keycol1>_old, <keycolM>_old) SELECT <value1>, ..., * <valueN>, <keycol1>, ..., <keycolM> FROM <target_table> WHERE <condition> * Final statements: Update: UPDATE <target_table> SET <column1> = <temp_table>.<column1>_new, * ..., <columnN> = <temp_table>.<columnN>_new FROM <temp_table> WHERE * <target_table>.<keycol1> = <temp_table>.<keycol1>_old AND ... AND * <target_table>.<keycolM> = <temp_table>.<keycolM>_old [AND <node_id> = i] * Insert: INSERT INTO <target_table> SELECT <temp_table>.<column1>_new,...,<temp_table>.<columnN>_new * FROM <temp_table> LEFT JOIN <target_table> ON <target_table>.<keycol1> = * <temp_table>.<keycol1>_old AND ... AND <target_table>.<keycolM> = * <temp_table>.<keycolM>_old WHERE <target_table>.<keycol1> IS NULL AND ... * AND <target_table>.<keycolM> IS NULL AND <temp_table>.nodeid = <nodeID> * Delete: DELETE FROM <target_table> WHERE EXISTS (SELECT 1 FROM <temp_table> * WHERE <target_table>.<keycol1> = <temp_table>.<keycol1>_old AND ... AND * <target_table>.<keycolM> = <temp_table>.<keycolM>_old AND * <temp_table>.nodeid <> nodeid */ public class SqlUpdateTable extends SqlModifyTable { private static final XLogger logger = XLogger.getLogger(SqlUpdateTable.class); private Map<String, SqlExpression> setClause = new LinkedHashMap<String, SqlExpression>(); // The column name which will be set // private Vector columnNameList = new Vector(); // SQL expression to be evaluated to be set to // private Vector setToExpressionList = new Vector(); // Query Condition according to which the // selection will take place private QueryCondition filterCondition; private QueryTree aQueryTree; /** * List of temp table columns */ private Collection<SysColumn> sysColumnList; /** * List of altered columns of target table */ private Collection<SysColumn> modifiedColumnList; private int maxSuppliedSerial = -1; private boolean doRowMove; private Collection<DBNode> executionNodes = null; public SqlUpdateTable(XDBSessionContext client) { super(client); commandToExecute = new Command(Command.UPDATE, this, new QueryTreeTracker(), client); aQueryTree = new QueryTree(); } // ****************************** // BEGIN GRAMMAR // ****************************** /** * Grammar production: * f0 -> <UPDATE_> * f1 -> TableName(prn) * f2 -> <SET_> * f3 -> SetUpdateClause(prn) * f4 -> ( "," SetUpdateClause(prn) )* * f5 -> [ WhereClause(prn) ] * * @param n * @param argu * @return */ @Override public Object visit(UpdateTable n, Object argu) { Object _ret = null; QueryTreeTracker aQueryTreeTracker = commandToExecute.getaQueryTreeTracker(); aQueryTreeTracker.registerTree(aQueryTree); RelationNode aRelationNode = aQueryTree.newRelationNode(); aRelationNode.setNodeType(RelationNode.TABLE); TableNameHandler aTableNameHandler = new TableNameHandler(client); n.f1.accept(aTableNameHandler, argu); tableName = aTableNameHandler.getTableName(); aRelationNode.setTableName(aTableNameHandler.getTableName()); aRelationNode.setTemporaryTable(aTableNameHandler.isTemporary()); aRelationNode.setClient(commandToExecute.getClientContext()); aRelationNode.setAlias(aTableNameHandler.getReferenceName()); n.f3.accept(this, argu); n.f4.accept(this, argu); n.f5.accept(this, argu); // Add target table's columns needed to apply updates to the query tree // and prepare columns of the temp table expandTargetTable(); QueryTreeHandler.checkAndExpand(aQueryTree.getProjectionList(), aQueryTree.getRelationNodeList(), database, commandToExecute); for (SqlExpression expr : setClause.values()) { QueryTreeHandler.SetBelongsToTree(expr, aQueryTree); } List<SqlExpression> projectionOrphans = QueryTreeHandler.checkAndFillTableNames( aQueryTree.getProjectionList(), aQueryTree.getRelationNodeList(), new ArrayList<SqlExpression>(), QueryTreeHandler.PROJECTION, commandToExecute); aQueryTree.getSelectOrphans().addAll(projectionOrphans); QueryTreeHandler.FillAllExprDataTypes(aQueryTree, commandToExecute); QueryTreeHandler.setOwnerShipColumns(aQueryTree); return _ret; } /** * Grammar production: * f0 -> [ TableName(prn) "." ] * f1 -> Identifier(prn) * f2 -> "=" * f3 -> SQLSimpleExpression(prn) */ @Override public Object visit(SetUpdateClause n, Object argu) { Object _ret = null; if (n.f0.present()) { TableNameHandler aTableNameHandler = new TableNameHandler(client); n.f0.accept(aTableNameHandler, argu); if (!tableName.equalsIgnoreCase(aTableNameHandler.getTableName())) { throw new XDBServerException( "Column prefix does not match to target table name"); } } String columnName = (String) n.f1.accept(new IdentifierHandler(), argu); SQLExpressionHandler aSqlExpressionHandler = new SQLExpressionHandler( commandToExecute); n.f3.accept(aSqlExpressionHandler, aQueryTree); setClause.put(columnName, aSqlExpressionHandler.aroot); aQueryTree.getProjectionList().add(aSqlExpressionHandler.aroot); return _ret; } /** * f0 -> <WHERE_> f1 -> SQLComplexExpression(prn) * * @param n * @param argu * @return */ @Override public Object visit(WhereClause n, Object argu) { Object _ret = null; QueryConditionHandler qch = new QueryConditionHandler(commandToExecute); n.f1.accept(qch, aQueryTree); filterCondition = qch.aRootCondition; if (filterCondition != null) { aQueryTree.setWhereRootCondition(filterCondition); QueryTreeHandler.ProcessWhereCondition(filterCondition, aQueryTree, commandToExecute); } return _ret; } // ****************************** // END GRAMMAR // ****************************** @Override public Collection<SysTable> getReadTables() { Collection<SysTable> readTables = super.getReadTables(); readTables.add(getTargetTable()); return readTables; } /* * (non-Javadoc) * * @see org.postgresql.stado.Parser.SqlModifyTable#getColumnsInvolved() */ @Override protected Collection<SysColumn> getColumnsInvolved() { if (modifiedColumnList == null) { modifiedColumnList = new LinkedHashSet<SysColumn>(); for (String string : setClause.keySet()) { modifiedColumnList.add(getTargetTable().getSysColumn(string)); } } return modifiedColumnList; } /* * (non-Javadoc) * * @see org.postgresql.stado.Parser.SqlModifyTable#getPKChecker(org.postgresql.stado.MetaData.SysTable, * java.util.List) */ @Override protected IConstraintChecker getPKChecker(SysTable targetTable, Collection<SysColumn> columnsInvolved) { IConstraintChecker pkChecker = new UpdatePrimaryKeyChecker( getTargetTable(), client); columnsInvolved.addAll(pkChecker.scanConstraints(getColumnsInvolved())); return pkChecker; } /* * (non-Javadoc) * * @see org.postgresql.stado.Parser.SqlModifyTable#getFKChecker(org.postgresql.stado.MetaData.SysTable, * java.util.List) */ @Override protected IConstraintChecker getFKChecker(SysTable targetTable, Collection<SysColumn> columnsInvolved) { final String method = "getFKChecker"; logger.entering(method, new Object[] {}); try { IConstraintChecker fkChecker = new UpdateForeignKeyChecker( getTargetTable(), client); columnsInvolved.addAll(fkChecker.scanConstraints(getColumnsInvolved())); return fkChecker; } finally { logger.exiting(method); } } /* * (non-Javadoc) * * @see org.postgresql.stado.Parser.SqlModifyTable#getFKChecker(org.postgresql.stado.MetaData.SysTable, * java.util.List) */ @Override protected IConstraintChecker getFRChecker(SysTable targetTable, Collection<SysColumn> columnsInvolved) { final String method = "getFKChecker"; logger.entering(method, new Object[] {}); try { IConstraintChecker frChecker = new UpdateForeignReferenceChecker( getTargetTable(), client); columnsInvolved.addAll(frChecker.scanConstraints(getColumnsInvolved())); return frChecker; } finally { logger.exiting(method); } } /* * (non-Javadoc) * * @see org.postgresql.stado.Parser.SqlModifyTable#createTempTableMetadata(org.postgresql.stado.MetaData.SysTable, * java.util.List) */ @Override protected SysTable createTempTableMetadata(SysTable targetTable, Collection<SysColumn> columnsInvolved) throws Exception { boolean tempTableRequired = true; if (validators.isEmpty()) { tempTableRequired = false; } if (!tempTableRequired) { if (doRowMove || (filterCondition != null && !filterCondition.isSimple())) { tempTableRequired = true; } } if (tempTableRequired) { return super.createTempTableMetadata(targetTable, sysColumnList); } else { return null; } } /* * (non-Javadoc) * * @see org.postgresql.stado.Parser.SqlModifyTable#prepareDataSource(org.postgresql.stado.MetaData.SysTable, * org.postgresql.stado.MetaData.SysTable) */ @Override protected Object prepareDataSource(SysTable targetTable, SysTable tempTable) throws Exception { final String method = "prepareDataSource"; logger.entering(method, new Object[] {}); try { QueryProcessor select = null; if (tempTable != null) { select = new QueryProcessor(client, aQueryTree); select.setSkipPermissionCheck(Collections.singleton(tableName)); select.prepare(); } return select; } finally { logger.exiting(method); } } /** * Partitioning key is not changed: 1: UPDATE <target> SET (<cols>) = * (SELECT <cols>_new FROM <temp> WHERE <target>.<key> = <temp>.<key>_old) * WHERE <key> IN (SELECT <key>_old FROM <temp>) Postgres: UPDATE <target> * SET <col1> = <temp>.<col1>_new, ..., <colN> = <temp_table>.<colN>_new * FROM <temp> WHERE <target>.<keycol1> = <temp>.<keycol1>_old AND ... AND * <target>.<keycolM> = <temp>.<keycolM>_old : UPDATE <target>, * <temp> SET <target>.<col1> = <temp>.<col1>_new, ..., <target>.<colN> = * <temp_table>.<colN>_new WHERE <target>.<keycol1> = <temp>.<keycol1>_old * AND ... AND <target>.<keycolM> = <temp>.<keycolM>_old Partitioning key * is changed: Update: Additonal conditions: 1: WHERE "NODE_ID"_new = * nodeid AND "NODE_ID"_old = nodeid before last ) Postgres : AND * "NODE_ID"_new = nodeid AND "NODE_ID"_old = nodeid * * Delete rows that are gone to another node: DELETE FROM <target> WHERE * EXISTS (SELECT 1 FROM <temp> WHERE <target>.<keycol1> = <temp>.<keycol1> * AND ... AND <target>.<keycolM> = <temp>.<keycolM> AND "NODE_ID"_new <> * nodeid AND "NODE_ID"_old = nodeid) * * INSERT rows that are came from another node: INSERT INTO <target> (<cols>) * SELECT <temp>.<cols> WHERE "NODE_ID"_new = nodeid AND "NODE_ID"_old <> * nodeid * * @see org.postgresql.stado.parser.SqlModifyTable#prepareFinalStatements() */ @Override protected Object prepareFinalStatements(SysTable targetTable, SysTable tempTable) throws Exception { final String method = "prepareFinalStatements"; logger.entering(method, new Object[] {}); try { String targetTableName = targetTable.getTableName(); if (tempTable == null) { // Reconstruct original statement StringBuffer sbUpdate = new StringBuffer("UPDATE "); sbUpdate.append(IdentifierHandler.quote(targetTableName)).append(" SET "); for (Iterator<Map.Entry<String, SqlExpression>> it = setClause.entrySet().iterator(); it.hasNext();) { Map.Entry<String, SqlExpression> entry = it.next(); sbUpdate.append(IdentifierHandler.quote(entry.getKey())).append("="); SqlExpression value = entry.getValue(); SqlExpression.setExpressionResultType(value, commandToExecute); sbUpdate.append(value.rebuildString()).append(", "); } sbUpdate.setLength(sbUpdate.length() - 2); if (filterCondition != null) { sbUpdate.append(" WHERE ").append( filterCondition.rebuildString()); } return sbUpdate.toString(); } else { String tempTableName = tempTable.getTableName(); // Prepare UPDATE statement StringBuffer sbUpdate = new StringBuffer("UPDATE "); if (Props.XDB_SQLCOMMAND_UPDATE_CORELLATEDSTYLE == 1) { // 1 sbUpdate.append(IdentifierHandler.quote(targetTableName)).append(" SET ("); StringBuffer sbTempFieldList = new StringBuffer(); for (String colName : setClause.keySet()) { sbUpdate.append(IdentifierHandler.quote(colName)).append(", "); sbTempFieldList.append(IdentifierHandler.quote(colName + "_new")).append(", "); } sbUpdate.setLength(sbUpdate.length() - 2); sbTempFieldList.setLength(sbTempFieldList.length() - 2); sbUpdate.append(")=(SELECT "); sbUpdate.append(sbTempFieldList).append(" FROM "); sbUpdate.append(IdentifierHandler.quote(tempTableName)); sbUpdate.append(" WHERE "); StringBuffer sbTargetKeyList = new StringBuffer(); StringBuffer sbTempKeyList = new StringBuffer(); for (SysColumn column : targetTable.getRowID()) { String colName = column.getColName(); if (column.isNullable()) { sbUpdate.append("("); } sbUpdate.append(IdentifierHandler.quote(targetTableName)).append("."); sbUpdate.append(IdentifierHandler.quote(colName)).append("="); sbUpdate.append(IdentifierHandler.quote(tempTableName)).append("."); sbUpdate.append(IdentifierHandler.quote(colName)); if (column.isNullable()) { sbUpdate.append(" OR "); sbUpdate.append(IdentifierHandler.quote(targetTableName)).append("."); sbUpdate.append(IdentifierHandler.quote(colName)).append(" IS NULL AND "); sbUpdate.append(IdentifierHandler.quote(tempTableName)).append("."); sbUpdate.append(IdentifierHandler.quote(colName + "_old")).append(" IS NULL"); sbUpdate.append(")"); } sbUpdate.append(" AND "); sbTargetKeyList.append(IdentifierHandler.quote(colName)).append(", "); sbTempKeyList.append(IdentifierHandler.quote(colName + "_old")).append(", "); } sbUpdate.setLength(sbUpdate.length() - 5); sbTargetKeyList.setLength(sbTargetKeyList.length() - 2); sbTempKeyList.setLength(sbTempKeyList.length() - 2); sbUpdate.append(") WHERE "); sbUpdate.append(sbTargetKeyList).append(" IN (SELECT "); sbUpdate.append(sbTempKeyList).append(" FROM "); sbUpdate.append(IdentifierHandler.quote(tempTableName)); if (!doRowMove) { sbUpdate.append(")"); return sbUpdate.toString(); } } else if (Props.XDB_SQLCOMMAND_UPDATE_CORELLATEDSTYLE == 2) { // Postres sbUpdate.append(IdentifierHandler.quote(targetTableName)).append(" SET "); for (String colName : setClause.keySet()) { sbUpdate.append(IdentifierHandler.quote(colName)).append("="); sbUpdate.append(IdentifierHandler.quote(tempTableName)).append("."); sbUpdate.append(IdentifierHandler.quote(colName + "_new")).append(", "); } sbUpdate.setLength(sbUpdate.length() - 2); sbUpdate.append(" FROM ").append(IdentifierHandler.quote(tempTableName)).append( " WHERE "); for (SysColumn column : targetTable.getRowID()) { String colName = column.getColName(); if (column.isNullable()) { sbUpdate.append("("); } sbUpdate.append(IdentifierHandler.quote(targetTableName)).append("."); sbUpdate.append(IdentifierHandler.quote(colName)).append("="); sbUpdate.append(IdentifierHandler.quote(tempTableName)).append("."); sbUpdate.append(IdentifierHandler.quote(colName + "_old")); if (column.isNullable()) { sbUpdate.append(" OR "); sbUpdate.append(IdentifierHandler.quote(targetTableName)).append("."); sbUpdate.append(IdentifierHandler.quote(colName)).append(" IS NULL AND "); sbUpdate.append(IdentifierHandler.quote(tempTableName)).append("."); sbUpdate.append(IdentifierHandler.quote(colName + "_old")).append(" IS NULL"); sbUpdate.append(")"); } sbUpdate.append(" AND "); } if (!doRowMove) { sbUpdate.setLength(sbUpdate.length() - 5); return sbUpdate.toString(); } } else if (Props.XDB_SQLCOMMAND_UPDATE_CORELLATEDSTYLE == 3) { // sbUpdate.append(IdentifierHandler.quote(targetTableName)).append(", "); sbUpdate.append(IdentifierHandler.quote(tempTableName)).append(" SET "); for (String colName : setClause.keySet()) { sbUpdate.append(IdentifierHandler.quote(targetTableName)).append("."); sbUpdate.append(IdentifierHandler.quote(colName)).append("="); sbUpdate.append(IdentifierHandler.quote(tempTableName)).append("."); sbUpdate.append(IdentifierHandler.quote(colName + "_new")).append(", "); } sbUpdate.setLength(sbUpdate.length() - 2); sbUpdate.append(" WHERE "); for (SysColumn column : targetTable.getRowID()) { String colName = column.getColName(); if (column.isNullable()) { sbUpdate.append("("); } sbUpdate.append(IdentifierHandler.quote(targetTableName)).append("."); sbUpdate.append(IdentifierHandler.quote(colName)).append("="); sbUpdate.append(IdentifierHandler.quote(tempTableName)).append("."); sbUpdate.append(IdentifierHandler.quote(colName + "_old")); if (column.isNullable()) { sbUpdate.append(" OR "); sbUpdate.append(IdentifierHandler.quote(targetTableName)).append("."); sbUpdate.append(IdentifierHandler.quote(colName)).append(" IS NULL AND "); sbUpdate.append(IdentifierHandler.quote(tempTableName)).append("."); sbUpdate.append(IdentifierHandler.quote(colName + "_old")).append(" IS NULL"); sbUpdate.append(")"); } sbUpdate.append(" AND "); } if (!doRowMove) { sbUpdate.setLength(sbUpdate.length() - 5); return sbUpdate.toString(); } } else { throw new XDBServerException( "Invalid xdb.sqlcommand.update.correlatedstyle value"); } ArrayList<Map<DBNode, String>> finalStatements = new ArrayList<Map<DBNode, String>>( 3); StringBuffer sbInsert = new StringBuffer("INSERT INTO "); sbInsert.append(IdentifierHandler.quote(targetTableName)).append(" ("); for (SysColumn col : targetTable.getColumns()) { sbInsert.append(IdentifierHandler.quote(col.getColName())).append(", "); } sbInsert.setLength(sbInsert.length() - 2); sbInsert.append(") SELECT "); for (SysColumn col : targetTable.getColumns()) { sbInsert.append(IdentifierHandler.quote(col.getColName() + "_new")).append(", "); } sbInsert.setLength(sbInsert.length() - 2); sbInsert.append(" FROM ").append(IdentifierHandler.quote(tempTableName)); StringBuffer sbDelete = new StringBuffer("DELETE FROM "); sbDelete.append(IdentifierHandler.quote(targetTableName)).append( " WHERE EXISTS (SELECT 1 FROM "); sbDelete.append(IdentifierHandler.quote(tempTableName)).append(" WHERE "); for (SysColumn column : targetTable.getRowID()) { String colName = column.getColName(); if (column.isNullable()) { sbDelete.append("("); } sbDelete.append(IdentifierHandler.quote(tempTableName)).append("."); sbDelete.append(IdentifierHandler.quote(colName + "_old")).append("="); sbDelete.append(IdentifierHandler.quote(targetTableName)).append("."); sbDelete.append(IdentifierHandler.quote(colName)); if (column.isNullable()) { sbDelete.append(" OR "); sbDelete.append(IdentifierHandler.quote(targetTableName)).append("."); sbDelete.append(IdentifierHandler.quote(colName)).append(" IS NULL AND "); sbDelete.append(IdentifierHandler.quote(tempTableName)).append("."); sbDelete.append(IdentifierHandler.quote(colName + "_old")).append(" IS NULL"); sbDelete.append(")"); } sbDelete.append(" AND "); } Map<DBNode, String> insertStatements = new HashMap<DBNode, String>(); Map<DBNode, String> updateStatements = new HashMap<DBNode, String>(); Map<DBNode, String> deleteStatements = new HashMap<DBNode, String>(); for (Object element : targetTable.getNodeList()) { DBNode node = (DBNode) element; int nodeID = node.getNodeId(); String statement = sbInsert.toString() + " WHERE " + IdentifierHandler.quote(NODE_ID + "_new") + " = " + nodeID + " AND " + IdentifierHandler.quote(NODE_ID + "_old") + " <> " + nodeID; insertStatements.put(node, statement); if (Props.XDB_SQLCOMMAND_UPDATE_CORELLATEDSTYLE == 1) { // Mx statement = sbUpdate.toString() + " WHERE " + IdentifierHandler.quote(NODE_ID + "_new") + " = " + nodeID + " AND " + IdentifierHandler.quote(NODE_ID + "_old") + " = " + nodeID + ")"; } else if (Props.XDB_SQLCOMMAND_UPDATE_CORELLATEDSTYLE == 2) { // Postres statement = sbUpdate.toString() + IdentifierHandler.quote(NODE_ID + "_new") + " = " + nodeID + " AND " + IdentifierHandler.quote(NODE_ID + "_old") + " = " + nodeID; } else if (Props.XDB_SQLCOMMAND_UPDATE_CORELLATEDSTYLE == 3) { // (finished with " AND ") statement = sbUpdate.toString() + IdentifierHandler.quote(NODE_ID + "_new") + " = " + nodeID + " AND " + IdentifierHandler.quote(NODE_ID + "_old") + " = " + nodeID; } else { throw new XDBServerException( "Invalid xdb.sqlcommand.update.correlatedstyle value"); } updateStatements.put(node, statement); statement = sbDelete.toString() + IdentifierHandler.quote(NODE_ID + "_new") + " <> " + nodeID + " AND " + IdentifierHandler.quote(NODE_ID + "_old") + " = " + nodeID + ")"; deleteStatements.put(node, statement); } finalStatements.add(deleteStatements); finalStatements.add(updateStatements); finalStatements.add(insertStatements); return finalStatements; } } finally { logger.exiting(method); } } @Override protected int internalExecute(Object finalStatements, Engine engine) throws Exception { if (finalStatements instanceof Collection) { int[] res = new int[3]; int i = 0; for (Iterator it = ((Collection) finalStatements).iterator(); it.hasNext(); i++) { res[i] += super.internalExecute(it.next(), engine); } if (res[0] != res[2]) { logger.log( Level.WARN, "UPDATE TABLE %0%: %1% rows inserted, %2% rows deleted, numbers must be equal\n" + "Final Statements dump:\n%3%", new Object[] { getTableName(), new Integer(res[2]), new Integer(res[0]), finalStatements }); } return res[0] + res[1]; } else { return super.internalExecute(finalStatements, engine); } } @Override protected Collection<DBNode> getExecutionNodes() { if (executionNodes == null) { // default executionNodes = getTargetTable().getNodeList(); if (!doRowMove && getTargetTable().getPartitionedColumn() != null && filterCondition != null) { Collection<DBNode> aNodeList = filterCondition.getPartitionedNode(client); if (aNodeList != null) { executionNodes = aNodeList; } } } return executionNodes; } /* * (non-Javadoc) * * @see org.postgresql.stado.Parser.SqlModifyTable#fillTable(java.lang.Object, * org.postgresql.stado.MetaData.SysTable, org.postgresql.stado.Engine.Engine) */ @Override protected long fillTable(Object source, SysTable tempTable, Engine engine) throws Exception { final String method = "fillTable"; logger.entering(method, new Object[] {}); try { long rowCount = 0; if (source instanceof IExecutable) { ResultSet rs = ((IExecutable) source).execute(engine).getResultSet(); try { int partitionColumnNewIdx = 0; int partitionColumnOldIdx = 0; StringBuffer sbAddUpdate = new StringBuffer("INSERT INTO "); ExpressionType partColType = null; SysColumn partCol = getTargetTable().getPartitionedColumn(); if (partCol != null) { String newAlias = partCol.getColName() + "_new"; String oldAlias = partCol.getColName() + "_old"; int i = 1; for (SqlExpression columnEx : aQueryTree.getProjectionList()) { if (newAlias.equals(columnEx.getAlias())) { partitionColumnNewIdx = i; } else if (oldAlias.equals(columnEx.getAlias())) { partitionColumnOldIdx = i; } i++; } partColType = new ExpressionType(partCol); } SysColumn oldNodeID = null; SysColumn newNodeID = null; sbAddUpdate.append(IdentifierHandler.quote(tempTable.getTableName())).append(" ("); for (SysColumn sysCol : tempTable.getColumns()) { String colName = sysCol.getColName(); sbAddUpdate.append(IdentifierHandler.quote(colName)).append(", "); if (colName.equals(NODE_ID + "_new")) { newNodeID = sysCol; } else if (colName.equals(NODE_ID + "_old")) { oldNodeID = sysCol; } } sbAddUpdate.setLength(sbAddUpdate.length() - 2); sbAddUpdate.append(") VALUES ("); ResultSetMetaData rsmd = rs.getMetaData(); int colCount = rsmd.getColumnCount(); while (rs.next()) { DBNode oldNode = null; DBNode newNode = null; if (partCol != null) { String value = rs.getString(partitionColumnOldIdx); oldNode = getTargetTable().getNode( SqlExpression.createConstantExpression( value, partColType).getNormalizedValue()).iterator().next(); if (partitionColumnNewIdx > 0 && partitionColumnNewIdx <= colCount) { value = rs.getString(partitionColumnNewIdx); if (value == null) { SqlExpression partExpr = partCol.getDefaultExpr(client); if (partExpr != null) { value = partExpr.rebuildString(); } } newNode = getTargetTable().getNode( SqlExpression.createConstantExpression( value, partColType).getNormalizedValue()).iterator().next(); } else { newNode = oldNode; } } int i = 1; StringBuffer sbUpdateValues = new StringBuffer(); for (Iterator<SysColumn> it = tempTable.getColumns().iterator(); it.hasNext(); i++) { SysColumn sysCol = it.next(); if (sysCol == newNodeID) { sbUpdateValues.append(newNode.getNodeId()).append( ", "); continue; } if (sysCol == oldNodeID) { sbUpdateValues.append(oldNode.getNodeId()).append( ", "); continue; } String value = rs.getString(i); if (value != null && RSHelper.getQuoteInfo(sysCol)) { value = "'" + value.replaceAll("'", "''") + "'"; switch (sysCol.getColType()) { case ExpressionType.TIME_TYPE: value = SqlExpression.normalizeTime(value); break; case ExpressionType.TIMESTAMP_TYPE: value = SqlExpression.normalizeTimeStamp(value); break; case ExpressionType.DATE_TYPE: value = SqlExpression.normalizeDate(value); break; default: break; } } sbUpdateValues.append(value).append(", "); if (sysCol == getTargetTable().getSerialColumn()) { maxSuppliedSerial = Math.max(maxSuppliedSerial, Integer.parseInt(value)); } } if (sbUpdateValues.length() > 0) { sbUpdateValues.setLength(sbUpdateValues.length() - 2); String insertStr = sbAddUpdate.toString() + sbUpdateValues.toString() + ")"; if (oldNode == null || validators != null && !validators.isEmpty()) { // Replicated table or need to run constraint // checkers if (engine.addToBatchOnNodes(insertStr, tempTable.getNodeList(), client)) { executeCurrentBatch(engine); } } else { if (engine.addToBatchOnNode(insertStr, oldNode, client)) { executeCurrentBatch(engine); } if (oldNode != newNode) { if (engine.addToBatchOnNode(insertStr, newNode, client)) { executeCurrentBatch(engine); } } } } rowCount++; } } finally { rs.close(); } executeCurrentBatch(engine); } return rowCount; } finally { logger.exiting(method); } } @Override protected short getPrivilege() { return SysPermission.PRIVILEGE_UPDATE; } /** * We try and do a fast manual parse of a simple update statement * * @param cmd - * The update commandToExecute * * @return whether or not it was parseable. */ public boolean manualParse(String cmd) { String token; Lexer aLexer = new Lexer(cmd); if (!aLexer.hasMoreTokens() || !aLexer.nextToken().equalsIgnoreCase("UPDATE")) { return false; } if (!aLexer.hasMoreTokens()) { return false; } tableName = aLexer.nextToken(); // Get tree ready, used in costing QueryTreeTracker aQueryTreeTracker = commandToExecute.getaQueryTreeTracker(); aQueryTreeTracker.registerTree(aQueryTree); RelationNode aRelationNode = aQueryTree.newRelationNode(); aRelationNode.setNodeType(RelationNode.TABLE); aRelationNode.setTableName(tableName); aRelationNode.setTemporaryTable(client.getTempTableName(tableName) != null); aRelationNode.setClient(commandToExecute.getClientContext()); aRelationNode.setAlias(""); if (!aLexer.hasMoreTokens() || !aLexer.nextToken().equalsIgnoreCase("SET")) { return false; } if (!aLexer.hasMoreTokens()) { return false; } token = aLexer.nextToken(); XDBSessionContext client = commandToExecute.getClientContext(); while (true) { String columnName = token; if (!aLexer.hasMoreTokens() || !aLexer.nextToken().equalsIgnoreCase("=")) { return false; } if (!aLexer.hasMoreTokens()) { return false; } SqlExpression aSE = ParserHelper.createSimpleSqlExpression(aLexer, client.getSysDatabase().getSysTable(tableName), aRelationNode, client); if (aSE == null) { // parse error return false; } setClause.put(columnName, aSE); aQueryTree.getProjectionList().add(aSE); if (!aLexer.hasMoreTokens()) { return false; } token = aLexer.nextToken(); if (token.equalsIgnoreCase("WHERE")) { break; } if (!token.equals(",")) { return false; } if (!aLexer.hasMoreTokens()) { return false; } token = aLexer.nextToken(); } // ok, check for where clause if (!token.equalsIgnoreCase("WHERE")) { return false; } // Now process WHERE conditions // see if there is no where clause if (!aLexer.hasMoreTokens()) { return false; } // We assume simple conditions only while (true) { QueryCondition aQC = ParserHelper.getSimpleCondition(aLexer, client.getSysDatabase().getSysTable(tableName), aQueryTree, client); if (aQC == null) { // parser error return false; } if (filterCondition == null) { filterCondition = aQC; } else { filterCondition = ParserHelper.chainQueryConditions( filterCondition, "AND", aQC); } if (!aLexer.hasMoreTokens() || !aLexer.nextToken().equalsIgnoreCase("AND")) { break; } } if (filterCondition != null) { QueryTreeHandler.ProcessWhereCondition(filterCondition, aQueryTree, commandToExecute); } if (aLexer.hasMoreTokens()) { token = aLexer.nextToken(); if (!token.equals(";")) { return false; } if (aLexer.hasMoreTokens()) { return false; } } expandTargetTable(); return true; } /* * (non-Javadoc) * * @see org.postgresql.stado.parser.SqlModifyTable#getResultType() */ @Override public int getResultType() { return ExecutionResult.COMMAND_UPDATE_TABLE; } private void expandTargetTable() { sysColumnList = new LinkedHashSet<SysColumn>(); SysTable targetTable = getTargetTable(); String partColumn = targetTable.getPartitionColumn(); if (partColumn != null && setClause.containsKey(partColumn)) { /* * Partitioning key modified *_new - all table columns */ doRowMove = true; for (SysColumn origin : targetTable.getColumns()) { int pos = origin.getNativeColDef().indexOf(origin.getColName()) + origin.getColName().length(); String nativeDef = origin.getNativeColDef().substring(0, pos) + "_new" + origin.getNativeColDef().substring(pos); SysColumn col = new SysColumn(targetTable, 0, 0, origin.getColName() + "_new", origin.getColType(), origin.getColLength(), origin.getColScale(), origin.getColPrecision(), origin.isNullable(), origin.isSerial(), nativeDef, origin.getSelectivity(), origin.getDefaultExpr()); sysColumnList.add(col); SqlExpression setToExpression = setClause.get(origin .getColName()); if (setToExpression == null) { setToExpression = new SqlExpression(); setToExpression.setExprString(origin.getColName()); setToExpression.setExprType(SqlExpression.SQLEX_COLUMN); setToExpression.setColumn(new AttributeColumn()); setToExpression.getColumn().columnName = origin .getColName(); aQueryTree.getProjectionList().add(setToExpression); } else { // Ensure the expression has proper position in the // projection list aQueryTree.getProjectionList().remove(setToExpression); aQueryTree.getProjectionList().add(setToExpression); } setToExpression.setAlias(col.getColName()); setToExpression.setOuterAlias(col.getColName()); // added for update with typecast issue setToExpression.setExprDataType(new ExpressionType()); setToExpression.getExprDataType().setExpressionType( col.getColType(), col.getColLength(), col.getColPrecision(), col.getColScale()); } } else { /* * Partitioning key unchanged *_new - columns being updated */ for (Entry<String, SqlExpression> setEntry : setClause.entrySet()) { String colName = setEntry.getKey(); SysColumn origin = targetTable.getSysColumn(colName); if (origin == null) { throw new XDBServerException("Column " + colName + " is not found in table " + tableName); } int pos = origin.getNativeColDef().indexOf(colName) + colName.length(); String nativeDef = origin.getNativeColDef().substring(0, pos) + "_new" + origin.getNativeColDef().substring(pos); SysColumn column = new SysColumn(targetTable, 0, 0, origin.getColName() + "_new", origin.getColType(), origin.getColLength(), origin.getColScale(), origin.getColPrecision(), origin.isNullable(), origin.isSerial(), nativeDef, origin.getSelectivity(), origin.getDefaultExpr()); sysColumnList.add(column); } } /* * In any case *_old - key columns + partitioning key */ for (SysColumn origin : targetTable.getRowID()) { int pos = origin.getNativeColDef().indexOf(origin.getColName()) + origin.getColName().length(); String nativeDef = origin.getNativeColDef().substring(0, pos) + "_old" + origin.getNativeColDef().substring(pos); SysColumn col = new SysColumn(targetTable, 0, 0, origin.getColName() + "_old", origin.getColType(), origin.getColLength(), origin.getColScale(), origin.getColPrecision(), origin.isNullable(), origin.isSerial(), nativeDef, origin.getSelectivity(), origin.getDefaultExpr()); sysColumnList.add(col); SqlExpression colExpr = new SqlExpression(); colExpr.setExprString(origin.getColName()); colExpr.setExprType(SqlExpression.SQLEX_COLUMN); colExpr.setColumn(new AttributeColumn()); colExpr.getColumn().columnName = origin.getColName(); colExpr.setAlias(col.getColName()); colExpr.setOuterAlias(col.getColName()); // added for update with typecast issue colExpr.setExprDataType(new ExpressionType()); colExpr.getExprDataType().setExpressionType(col.getColType(), col.getColLength(), col.getColPrecision(), col.getColScale()); aQueryTree.getProjectionList().add(colExpr); } SysColumn origin = targetTable.getPartitionedColumn(); if (origin != null) { int pos = origin.getNativeColDef().indexOf(origin.getColName()) + origin.getColName().length(); String nativeDef = origin.getNativeColDef().substring(0, pos) + "_old" + origin.getNativeColDef().substring(pos); SysColumn col = new SysColumn(targetTable, 0, 0, origin.getColName() + "_old", origin.getColType(), origin.getColLength(), origin.getColScale(), origin.getColPrecision(), origin.isNullable(), origin.isSerial(), nativeDef, origin.getSelectivity(), origin.getDefaultExpr()); if (!sysColumnList.contains(col)) { // sysColumnList.add(col); SqlExpression colExpr = new SqlExpression(); colExpr.setExprString(origin.getColName()); colExpr.setExprType(SqlExpression.SQLEX_COLUMN); colExpr.setColumn(new AttributeColumn()); colExpr.getColumn().columnName = origin.getColName(); colExpr.setAlias(col.getColName()); colExpr.setOuterAlias(col.getColName()); // added for update with typecast issue colExpr.setExprDataType(new ExpressionType()); colExpr.getExprDataType().setExpressionType(col.getColType(), col.getColLength(), col.getColPrecision(), col.getColScale()); aQueryTree.getProjectionList().add(colExpr); } } if (doRowMove) { // Temp table will contain info about node where the row was and // where it will be SysColumn column = new SysColumn( targetTable, 0, 0, NODE_ID + "_new", Types.INTEGER, -1, -1, -1, false, false, IdentifierHandler.quote(NODE_ID + "_new") + " INT NOT NULL", 1, null); sysColumnList.add(column); column = new SysColumn( targetTable, 0, 0, NODE_ID + "_old", Types.INTEGER, -1, -1, -1, false, false, IdentifierHandler.quote(NODE_ID + "_old") + " INT NOT NULL", 1, null); sysColumnList.add(column); } } }