/***************************************************************************** * 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.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import org.postgresql.stado.common.util.XLogger; import org.postgresql.stado.constraintchecker.DeleteForeignReferenceChecker; import org.postgresql.stado.constraintchecker.IConstraintChecker; 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.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.Delete; 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.TableNameHandler; import org.postgresql.stado.queryproc.QueryProcessor; /** * */ public class SqlDeleteTable extends SqlModifyTable { private static final XLogger logger = XLogger.getLogger(SqlDeleteTable.class); private QueryCondition whereCondition; private Collection<SysColumn> columnsInvolved; private QueryTree aQueryTree = null; private Collection<DBNode> executionNodes = null; public SqlDeleteTable(XDBSessionContext client) { super(client); commandToExecute = new Command(Command.DELETE, this, new QueryTreeTracker(), client); aQueryTree = new QueryTree(); } // ****************************** // BEGIN GRAMMAR // ****************************** /** * f0 -> <DELETE_> f1 -> "FROM" f2 -> TableName(prn) f3 -> [ * WhereClause(prn) ] */ @Override public Object visit(Delete 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.f2.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, aQueryTree); return _ret; } /** * Grammar production: f0 -> <WHERE_> f1 -> SQLComplexExpression(prn) * * @param n * @param argu * @return */ @Override public Object visit(WhereClause n, Object argu) { QueryConditionHandler aQueryConditionHandler = new QueryConditionHandler( commandToExecute); n.f1.accept(aQueryConditionHandler, aQueryTree); whereCondition = aQueryConditionHandler.aRootCondition; if (whereCondition != null) { // verifyQueryCondition(whereCondition); QueryTreeHandler.ProcessWhereCondition(whereCondition, aQueryTree, commandToExecute); } return argu; } // ****************************** // 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 (columnsInvolved == null) { columnsInvolved = new HashSet<SysColumn>( getTargetTable().getRowID()); } return columnsInvolved; } /* * (non-Javadoc) * * @see org.postgresql.stado.Parser.SqlModifyTable#getPKChecker(org.postgresql.stado.MetaData.SysTable, * java.util.List) */ @Override protected IConstraintChecker getPKChecker(SysTable targetTable, Collection columnsInvolved) { // DELETE can not violate primary or unique key return null; } /* * (non-Javadoc) * * @see org.postgresql.stado.Parser.SqlModifyTable#getFKChecker(org.postgresql.stado.MetaData.SysTable, * java.util.List) */ @Override protected IConstraintChecker getFKChecker(SysTable targetTable, Collection columnsInvolved) { final String method = "getFKChecker"; logger.entering(method, new Object[] {}); try { return null; } 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 columnsInvolved) { final String method = "getFKChecker"; logger.entering(method, new Object[] {}); try { IConstraintChecker frChecker = new DeleteForeignReferenceChecker( getTargetTable(), whereCondition, 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 { if (whereCondition == null || whereCondition.isSimple()) { return null; } return super.createTempTableMetadata(targetTable, columnsInvolved); } /* * (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) { for (SysColumn column : getColumnsInvolved()) { SqlExpression rowidExpr = new SqlExpression(); rowidExpr.setExprString(column.getColName()); rowidExpr.setExprType(SqlExpression.SQLEX_COLUMN); rowidExpr.setColumn(new AttributeColumn()); rowidExpr.getColumn().columnName = column.getColName(); rowidExpr.setAlias(column.getColName()); rowidExpr.setOuterAlias(column.getColName()); aQueryTree.getProjectionList().add(rowidExpr); } QueryTreeHandler.checkAndExpand(aQueryTree.getProjectionList(), aQueryTree.getRelationNodeList(), database, commandToExecute); QueryTreeHandler.checkAndFillTableNames(aQueryTree.getProjectionList(), aQueryTree.getRelationNodeList(), new ArrayList<SqlExpression>(), QueryTreeHandler.PROJECTION, commandToExecute); QueryTreeHandler.FillAllExprDataTypes(aQueryTree, commandToExecute); QueryTreeHandler.setOwnerShipColumns(aQueryTree); select = new QueryProcessor(client, aQueryTree); select.setSkipPermissionCheck(Collections.singleton(tableName)); select.prepare(); } return select; } finally { logger.exiting(method); } } /* * (non-Javadoc) * * @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 { StringBuffer sbFinalDelete = new StringBuffer("DELETE FROM "); sbFinalDelete.append(IdentifierHandler.quote(targetTable.getTableName())); if (tempTable == null) { if (whereCondition != null) { sbFinalDelete.append(" WHERE ").append( whereCondition.rebuildString()); } } else { sbFinalDelete.append(" WHERE EXISTS (SELECT 1 FROM "); sbFinalDelete.append(IdentifierHandler.quote(tempTable.getTableName())).append(" WHERE "); for (SysColumn sysColumn : targetTable.getRowID()) { if (sysColumn.isNullable()) { sbFinalDelete.append("("); sbFinalDelete.append(IdentifierHandler.quote(targetTable.getTableName())).append("."); sbFinalDelete.append(IdentifierHandler.quote(sysColumn.getColName())).append("="); sbFinalDelete.append(IdentifierHandler.quote(tempTable.getTableName())).append("."); sbFinalDelete.append(IdentifierHandler.quote(sysColumn.getColName())).append(" or "); sbFinalDelete.append(IdentifierHandler.quote(targetTable.getTableName())).append("."); sbFinalDelete.append(IdentifierHandler.quote(sysColumn.getColName())).append(" IS NULL AND "); sbFinalDelete.append(IdentifierHandler.quote(tempTable.getTableName())).append("."); sbFinalDelete.append(IdentifierHandler.quote(sysColumn.getColName())).append(" IS NULL) AND "); } else { sbFinalDelete.append("("); sbFinalDelete.append(IdentifierHandler.quote(targetTable.getTableName())).append("."); sbFinalDelete.append(IdentifierHandler.quote(sysColumn.getColName())).append("="); sbFinalDelete.append(IdentifierHandler.quote(tempTable.getTableName())).append("."); sbFinalDelete.append(IdentifierHandler.quote(sysColumn.getColName())).append(") AND "); } } sbFinalDelete.setLength(sbFinalDelete.length() - 5); sbFinalDelete.append(")"); } return sbFinalDelete.toString(); } finally { logger.exiting(method); } } /* * (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 { StringBuffer sbInsert = new StringBuffer("INSERT INTO "); sbInsert.append(IdentifierHandler.quote(tempTable.getTableName())).append(" ("); for (SysColumn sysCol : tempTable.getColumns()) { String colName = sysCol.getColName(); sbInsert.append(IdentifierHandler.quote(colName)).append(", "); } sbInsert.setLength(sbInsert.length() - 2); sbInsert.append(") VALUES ("); ResultSetMetaData rsmd = rs.getMetaData(); int colCount = rsmd.getColumnCount(); while (rs.next()) { StringBuffer sbValues = new StringBuffer(); int i = 1; for (SysColumn sysCol : tempTable.getColumns()) { if (i <= colCount) { String value = rs.getString(i); if (value != null && RSHelper.getQuoteInfo(sysCol)) { sbValues.append("'").append( value.replaceAll("'", "''")).append( "'"); } else { sbValues.append(value); } } sbValues.append(", "); i++; } sbValues.setLength(sbValues.length() - 2); sbValues.append(")"); String insertStr = sbInsert.toString() + sbValues.toString(); if (!engine.addToBatchOnNodes(insertStr, tempTable.getNodeList(), client)) { executeCurrentBatch(engine); } rowCount++; } } finally { rs.close(); } executeCurrentBatch(engine); } return rowCount; } finally { logger.exiting(method); } } @Override protected Collection<DBNode> getExecutionNodes() { if (executionNodes == null) { // default executionNodes = getTargetTable().getNodeList(); if (getTempTable() == null && getTargetTable().getPartitionedColumn() != null && whereCondition != null) { Collection<DBNode> aNodeList = whereCondition.getPartitionedNode(client); if (aNodeList != null) { executionNodes = aNodeList; } } } return executionNodes; } @Override protected short getPrivilege() { return SysPermission.PRIVILEGE_DELETE; } /* * (non-Javadoc) * * @see org.postgresql.stado.MetaData.Scheduler.ILockCost#needCoordinatorConnection() */ @Override public boolean needCoordinatorConnection() { return false; } /** * We try and do a fast manual parse of a simple update statement * * @param cmd - * The delete 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("DELETE")) { return false; } if (!aLexer.hasMoreTokens() || !aLexer.nextToken().equalsIgnoreCase("FROM")) { return false; } if (!aLexer.hasMoreTokens()) { return false; } tableName = aLexer.nextToken(); if (!aLexer.hasMoreTokens()) { return true; // dangerous, no WHERE clause } // Now process WHERE conditions // 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(""); token = aLexer.nextToken(); // ok, check for where clause if (!token.equalsIgnoreCase("WHERE")) { return false; } if (!aLexer.hasMoreTokens()) { return false; } // We assume simple conditions only: column = value while (true) { QueryCondition aQC = ParserHelper.getSimpleCondition(aLexer, getTargetTable(), aQueryTree, client); if (aQC == null) { // parser error return false; } if (whereCondition == null) { whereCondition = aQC; } else { whereCondition = ParserHelper.chainQueryConditions( whereCondition, "AND", aQC); } if (!aLexer.hasMoreTokens() || !aLexer.nextToken().equalsIgnoreCase("AND")) { break; } } if (whereCondition != null) { QueryTreeHandler.ProcessWhereCondition(whereCondition, aQueryTree, commandToExecute); } if (aLexer.hasMoreTokens()) { token = aLexer.nextToken(); if (!token.equals(";")) { return false; } if (aLexer.hasMoreTokens()) { return false; } } return true; } /* * (non-Javadoc) * * @see org.postgresql.stado.parser.SqlModifyTable#getResultType() */ @Override public int getResultType() { return ExecutionResult.COMMAND_DELETE_TABLE; } }