package com.ctrip.platform.dal.dao.task;

import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

import com.ctrip.platform.dal.dao.DalHints;
import com.ctrip.platform.dal.dao.StatementParameters;
import com.ctrip.platform.dal.dao.UpdatableEntity;
import com.ctrip.platform.dal.exceptions.DalException;
import com.ctrip.platform.dal.exceptions.ErrorCode;

public class SingleUpdateTask<T> extends TaskAdapter<T> implements SingleTask<T> {
	public static final String TMPL_SQL_UPDATE = "UPDATE %s SET %s WHERE %s";
	
	@Override
	public int execute(DalHints hints, Map<String, ?> fields, T rawPojo) throws SQLException {
		if (fields.size() == 0)
			throw new DalException(ErrorCode.ValidateFieldCount);
		Map<String, ?> pks = getPrimaryKeys(fields);
		
		Object version = getVersion(fields);
		
		Map<String, ?> filted = null;
		
		if(rawPojo instanceof UpdatableEntity)
			filted = filterUpdatableEntity(hints, fields, getUpdatedColumns(rawPojo));
		else
			filted = filterNullColumns(hints, fields);
		
		// If there is no columns changed, we will not perform update
		if(filted.size() == 0)
			return 0;
		
		String updateSql = buildUpdateSql(getTableName(hints, fields), filted, hints);

		StatementParameters parameters = new StatementParameters();
		addParameters(parameters, filted);
		addParameters(parameters, pks);
		addVersion(parameters, version);
		
		return client.update(updateSql, parameters, hints);
	}

	private Object getVersion(Map<String, ?> fields) throws DalException {
		if(!hasVersion)
			return null;

		Object version = fields.get(parser.getVersionColumn());
		if(version == null)
			throw new DalException(ErrorCode.ValidateVersion);

		return version;
	}
	
	private Map<String, ?> filterNullColumns(DalHints hints, Map<String, ?> fields) throws DalException {
		Map<String, Object> filted = new LinkedHashMap<>();
		
		Set<String> updatableColumns = filterColumns(hints);
			
		// Remove null value when hints is not DalHintEnum.updateNullField or
		// primary key or not updatable
		for (String column : parser.getColumnNames()) {
			if ((fields.get(column) == null && !hints.isUpdateNullField())
					|| isPrimaryKey(column) || !updatableColumns.contains(column))
				continue;
			
			filted.put(column, fields.get(column));
		}
		
		return filted;
	}
	
	private Map<String, ?> filterUpdatableEntity(DalHints hints, Map<String, ?> fields, Set<String> updatedColumns) throws DalException {
		Map<String, Object> filted = new LinkedHashMap<>();
		Set<String> updatableColumns = filterColumns(hints);
			
		// Dirty flag is not set but need to update
		// primary key or not updatable
		for (String column : parser.getColumnNames()) {
			if (!updatedColumns.contains(column) && !hints.isUpdateUnchangedField() || 
					isPrimaryKey(column) || !updatableColumns.contains(column))
				continue;
			
			filted.put(column, fields.get(column));
		}
		
		return filted;
	}
	
	private String buildUpdateSql(String tableName, Map<String, ?> fields, DalHints hints) {
		String columns = String.format(
				combine(TMPL_SET_VALUE, fields.size(), COLUMN_SEPARATOR),
				quote(fields.keySet()));

		if(isVersionUpdatable)
			columns += COLUMN_SEPARATOR + setVersionValueTmpl;

		return String.format(TMPL_SQL_UPDATE, tableName, columns, updateCriteriaTmpl);
	}
	
	private void addVersion(StatementParameters parameters, Object version) throws DalException {
		if(!hasVersion)
			return;
		
		addParameter(parameters, parameters.size() + 1, parser.getVersionColumn(), version);
	}
}

@@ -1495,24 +1495,42 @@
 eters, hints
+.setFields(fields)
 );%0D%0A%09%7D%0D%0A%0D%0A%09p

fix locate shard issue