/*
* Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source 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 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source 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, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.amber.query;
import com.caucho.amber.entity.AmberEntityHome;
import com.caucho.amber.entity.TableInvalidateCompletion;
import com.caucho.amber.expr.AmberExpr;
import com.caucho.amber.expr.JoinExpr;
import com.caucho.amber.manager.AmberConnection;
import com.caucho.amber.table.AmberColumn;
import com.caucho.amber.type.SubEntityType;
import com.caucho.amber.type.EntityType;
import com.caucho.jdbc.JdbcMetaData;
import com.caucho.jdbc.PostgresMetaData;
import com.caucho.util.CharBuffer;
import java.sql.SQLException;
import java.util.ArrayList;
/**
* Represents an Amber select query
*/
public class UpdateQuery extends AbstractQuery {
private ArrayList<AmberExpr> _fieldList;
private ArrayList<AmberExpr> _valueList;
private String _sql;
UpdateQuery(String query, JdbcMetaData metaData)
{
super(query, metaData);
}
/**
* Sets the field list.
*/
void setFieldList(ArrayList<AmberExpr> fieldList)
{
_fieldList = fieldList;
}
/**
* Returns the field list.
*/
public ArrayList<AmberExpr> getFieldList()
{
return _fieldList;
}
/**
* Sets the field expr list.
*/
void setValueList(ArrayList<AmberExpr> exprList)
{
_valueList = exprList;
}
/**
* Returns the field list.
*/
public ArrayList<AmberExpr> getValueList()
{
return _valueList;
}
/**
* Sets the where expression
*/
void setWhere(AmberExpr expr)
{
_where = expr;
}
/**
* Returns the id load sql
*/
public String getSQL()
{
return _sql;
}
/**
* Initialize
*/
void init()
throws QueryParseException
{
super.init();
CharBuffer cb = new CharBuffer();
cb.append("update ");
FromItem item = _fromList.get(0);
// Postgres 8.2.x accepts UPDATE statements with
// table alias name, but Postgres 8.0.x does not.
//
// Also, adds portability for MySql 3.23 vs MySql 4.x/5.x
// In MySql 3.23, UPDATE with joins are not supported.
//
// jpa/1201 vs jpa/1202 vs jpa/1203
if (getMetaData().supportsUpdateTableAlias()
&& (_fromList.size() > 1)) {
if (getMetaData().supportsUpdateTableList()) {
// MySql: jpa/1202
generateFromList(cb, false);
}
else { // Oracle: jpa/1203
cb.append(item.getTable().getName());
cb.append(" ");
cb.append(item.getName());
}
}
else {
// Postgres: jpa/1201
cb.append(item.getTable().getName());
}
cb.append(" set ");
for (int i = 0; i < _fieldList.size(); i++) {
if (i != 0)
cb.append(", ");
// cb.append(_fieldList.get(i).generateSelect(null));
AmberExpr expr = _fieldList.get(i);
// jpa/1202, jpa/0k15
if (getMetaData().supportsUpdateTableAlias()
&& (_fromList.size() > 1))
expr.generateWhere(cb);
else
expr.generateUpdateWhere(cb);
cb.append("=");
// jpa/1231
if (getMetaData().supportsUpdateTableAlias() || hasSubQuery())
_valueList.get(i).generateWhere(cb);
else
_valueList.get(i).generateUpdateWhere(cb);
}
String updateJoin = null;
if (_where != null) {
// jpa/1200 vs jpa/1201 and jpa/1231
if (_fromList.size() == 1 && ! hasSubQuery()) {
cb.append(" where ");
_where.generateUpdateWhere(cb);
}
else {
boolean isFirst = true;
// jpa/1201: postgres 8.0.x/8.2.x compatibility
if (getMetaData() instanceof PostgresMetaData) {
item = _fromList.get(0);
EntityType type = item.getEntityType();
String targetId = type.getId().generateSelect(item.getName());
cb.append(" FROM ");
String tableName = item.getTable().getName();
cb.append(tableName);
cb.append(' ');
cb.append(item.getName());
isFirst = false;
cb.append(" where ");
cb.append(targetId);
cb.append(" = ");
cb.append(type.getId().generateSelect(item.getTable().getName()));
}
// jpa/1231, jpa/1202 vs jpa/1201 and jpa/1203
if (_fromList.size() > 1
&& ! getMetaData().supportsUpdateTableList()) {
// Postgres: jpa/1201 and Oracle: jpa/1203
item = _fromList.get(1);
EntityType type = item.getEntityType();
String relatedId = type.getId().generateSelect(item.getName());
if (isFirst) {
isFirst = false;
cb.append(" where ");
}
else
cb.append(" and ");
cb.append("exists (select ");
cb.append(relatedId);
cb.append(" from ");
generateFromList(cb, true);
// jpa/1231
isFirst = true;
}
for (int i = 0; i < _fromList.size(); i++) {
item = _fromList.get(i);
AmberExpr expr = item.getJoinExpr();
if (expr != null && ! item.isOuterJoin()) {
// jpa/1231
if (isFirst) {
isFirst = false;
cb.append(" where ");
}
else
cb.append(" and ");
expr.generateJoin(cb);
}
EntityType entityType = item.getEntityType();
// jpa/0l44
if (entityType != null) {
AmberColumn discriminator = entityType.getDiscriminator();
// jpa/0l43
if (entityType instanceof SubEntityType &&
discriminator != null) {
// jpa/0l12, jpa/0l4b
if (item.getTable() == discriminator.getTable()) {
if (isFirst) {
isFirst = false;
cb.append(" where ");
}
else
cb.append(" and ");
cb.append("(" + item.getName() + "." + discriminator.getName() + " = ");
cb.append("'" + entityType.getDiscriminatorValue() + "')");
}
}
}
}
if (isFirst) {
isFirst = false;
cb.append(" where ");
}
else
cb.append(" and ");
_where.generateWhere(cb);
}
} // end if (_where != null)
// jpa/1201 vs jpa/1202
if (_fromList.size() > 1
&& ! getMetaData().supportsUpdateTableList()) {
cb.append(")");
}
_sql = cb.close();
}
/**
* Adds any completion info.
*/
public void prepare(UserQuery userQuery, AmberConnection aConn)
throws SQLException
{
aConn.flushNoChecks();
}
/**
* Adds any completion info.
*/
public void complete(UserQuery userQuery, AmberConnection aConn)
throws SQLException
{
aConn.expire();
FromItem item = _fromList.get(0);
aConn.addCompletion(new TableInvalidateCompletion(item.getEntityType().getTable().getName()));
}
/**
* Debug view.
*/
public String toString()
{
return "UpdateQuery[" + getQueryString() + "]";
}
/**
* Generates the FROM list.
*/
private void generateFromList(CharBuffer cb, boolean skipFirst)
{
FromItem item;
// jpa/114f: reorder from list for left outer join
for (int i = 1; i < _fromList.size(); i++) {
item = _fromList.get(i);
if (item.isOuterJoin()) {
JoinExpr join = item.getJoinExpr();
if (join == null)
continue;
FromItem parent = join.getJoinParent();
int index = _fromList.indexOf(parent);
if (index < 0)
continue;
_fromList.remove(i);
if (index < i)
index++;
_fromList.add(index, item);
}
}
boolean hasJoinExpr = false;
boolean isFirst = true;
int i = 0;
// 1201: skip the first from item since it is
// available in the UPDATE clause
if (skipFirst)
i++;
for (; i < _fromList.size(); i++) {
item = _fromList.get(i);
// jpa/1178
if (getParentQuery() != null) {
ArrayList<FromItem> fromList = getParentQuery().getFromList();
if (fromList != null) {
if (fromList.contains(item)) {
hasJoinExpr = true;
continue;
}
}
}
if (isFirst) {
isFirst = false;
}
else {
if (item.isOuterJoin())
cb.append(" left outer join ");
else {
cb.append(", ");
if (item.getJoinExpr() != null)
hasJoinExpr = true;
}
}
cb.append(item.getTable().getName());
cb.append(" ");
cb.append(item.getName());
if (item.getJoinExpr() != null && item.isOuterJoin()) {
cb.append(" on ");
item.getJoinExpr().generateJoin(cb);
}
}
}
}