001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.amber.query;
031:
032: import com.caucho.amber.entity.AmberEntityHome;
033: import com.caucho.amber.entity.TableInvalidateCompletion;
034: import com.caucho.amber.expr.AmberExpr;
035: import com.caucho.amber.expr.JoinExpr;
036: import com.caucho.amber.manager.AmberConnection;
037: import com.caucho.amber.table.Column;
038: import com.caucho.amber.type.SubEntityType;
039: import com.caucho.amber.type.EntityType;
040: import com.caucho.jdbc.JdbcMetaData;
041: import com.caucho.jdbc.PostgresMetaData;
042: import com.caucho.util.CharBuffer;
043:
044: import java.sql.SQLException;
045: import java.util.ArrayList;
046:
047: /**
048: * Represents an Amber select query
049: */
050: public class UpdateQuery extends AbstractQuery {
051: private ArrayList<AmberExpr> _fieldList;
052: private ArrayList<AmberExpr> _valueList;
053:
054: private String _sql;
055:
056: UpdateQuery(String query, JdbcMetaData metaData) {
057: super (query, metaData);
058: }
059:
060: /**
061: * Sets the field list.
062: */
063: void setFieldList(ArrayList<AmberExpr> fieldList) {
064: _fieldList = fieldList;
065: }
066:
067: /**
068: * Returns the field list.
069: */
070: public ArrayList<AmberExpr> getFieldList() {
071: return _fieldList;
072: }
073:
074: /**
075: * Sets the field expr list.
076: */
077: void setValueList(ArrayList<AmberExpr> exprList) {
078: _valueList = exprList;
079: }
080:
081: /**
082: * Returns the field list.
083: */
084: public ArrayList<AmberExpr> getValueList() {
085: return _valueList;
086: }
087:
088: /**
089: * Sets the where expression
090: */
091: void setWhere(AmberExpr expr) {
092: _where = expr;
093: }
094:
095: /**
096: * Returns the id load sql
097: */
098: public String getSQL() {
099: return _sql;
100: }
101:
102: /**
103: * Initialize
104: */
105: void init() throws QueryParseException {
106: super .init();
107:
108: CharBuffer cb = new CharBuffer();
109:
110: cb.append("update ");
111:
112: FromItem item = _fromList.get(0);
113:
114: // Postgres 8.2.x accepts UPDATE statements with
115: // table alias name, but Postgres 8.0.x does not.
116: //
117: // Also, adds portability for MySql 3.23 vs MySql 4.x/5.x
118: // In MySql 3.23, UPDATE with joins are not supported.
119: //
120: // jpa/1201 vs jpa/1202 vs jpa/1203
121: if (getMetaData().supportsUpdateTableAlias()
122: && (_fromList.size() > 1)) {
123: if (getMetaData().supportsUpdateTableList()) {
124: // MySql: jpa/1202
125: generateFromList(cb, false);
126: } else { // Oracle: jpa/1203
127: cb.append(item.getTable().getName());
128: cb.append(" ");
129: cb.append(item.getName());
130: }
131: } else {
132: // Postgres: jpa/1201
133: cb.append(item.getTable().getName());
134: }
135:
136: cb.append(" set ");
137:
138: for (int i = 0; i < _fieldList.size(); i++) {
139: if (i != 0)
140: cb.append(", ");
141:
142: // cb.append(_fieldList.get(i).generateSelect(null));
143:
144: AmberExpr expr = _fieldList.get(i);
145:
146: // jpa/1202, jpa/0k15
147: if (getMetaData().supportsUpdateTableAlias()
148: && (_fromList.size() > 1))
149: expr.generateWhere(cb);
150: else
151: expr.generateUpdateWhere(cb);
152:
153: cb.append("=");
154:
155: // jpa/1231
156: if (getMetaData().supportsUpdateTableAlias()
157: || hasSubQuery())
158: _valueList.get(i).generateWhere(cb);
159: else
160: _valueList.get(i).generateUpdateWhere(cb);
161: }
162:
163: String updateJoin = null;
164:
165: if (_where != null) {
166: // jpa/1200 vs jpa/1201 and jpa/1231
167: if (_fromList.size() == 1 && !hasSubQuery()) {
168: cb.append(" where ");
169:
170: _where.generateUpdateWhere(cb);
171: } else {
172: boolean isFirst = true;
173:
174: // jpa/1201: postgres 8.0.x/8.2.x compatibility
175: if (getMetaData() instanceof PostgresMetaData) {
176: item = _fromList.get(0);
177:
178: EntityType type = item.getEntityType();
179:
180: String targetId = type.getId().generateSelect(
181: item.getName());
182:
183: cb.append(" FROM ");
184:
185: String tableName = item.getTable().getName();
186:
187: cb.append(tableName);
188: cb.append(' ');
189: cb.append(item.getName());
190:
191: isFirst = false;
192:
193: cb.append(" where ");
194:
195: cb.append(targetId);
196: cb.append(" = ");
197: cb.append(type.getId().generateSelect(
198: item.getTable().getName()));
199: }
200:
201: // jpa/1231, jpa/1202 vs jpa/1201 and jpa/1203
202: if (_fromList.size() > 1
203: && !getMetaData().supportsUpdateTableList()) {
204: // Postgres: jpa/1201 and Oracle: jpa/1203
205: item = _fromList.get(1);
206:
207: EntityType type = item.getEntityType();
208:
209: String relatedId = type.getId().generateSelect(
210: item.getName());
211:
212: if (isFirst) {
213: isFirst = false;
214: cb.append(" where ");
215: } else
216: cb.append(" and ");
217:
218: cb.append("exists (select ");
219:
220: cb.append(relatedId);
221:
222: cb.append(" from ");
223:
224: generateFromList(cb, true);
225:
226: // jpa/1231
227: isFirst = true;
228: }
229:
230: for (int i = 0; i < _fromList.size(); i++) {
231: item = _fromList.get(i);
232:
233: AmberExpr expr = item.getJoinExpr();
234:
235: if (expr != null && !item.isOuterJoin()) {
236: // jpa/1231
237: if (isFirst) {
238: isFirst = false;
239: cb.append(" where ");
240: } else
241: cb.append(" and ");
242:
243: expr.generateJoin(cb);
244: }
245:
246: EntityType entityType = item.getEntityType();
247:
248: // jpa/0l44
249: if (entityType != null) {
250: Column discriminator = entityType
251: .getDiscriminator();
252:
253: // jpa/0l43
254: if (entityType instanceof SubEntityType
255: && discriminator != null) {
256: // jpa/0l12, jpa/0l4b
257:
258: if (item.getTable() == discriminator
259: .getTable()) {
260: if (isFirst) {
261: isFirst = false;
262: cb.append(" where ");
263: } else
264: cb.append(" and ");
265:
266: cb.append("(" + item.getName() + "."
267: + discriminator.getName()
268: + " = ");
269: cb
270: .append("'"
271: + entityType
272: .getDiscriminatorValue()
273: + "')");
274: }
275: }
276: }
277: }
278:
279: if (isFirst) {
280: isFirst = false;
281: cb.append(" where ");
282: } else
283: cb.append(" and ");
284:
285: _where.generateWhere(cb);
286: }
287: } // end if (_where != null)
288:
289: // jpa/1201 vs jpa/1202
290: if (_fromList.size() > 1
291: && !getMetaData().supportsUpdateTableList()) {
292: cb.append(")");
293: }
294:
295: _sql = cb.close();
296: }
297:
298: /**
299: * Adds any completion info.
300: */
301: public void prepare(UserQuery userQuery, AmberConnection aConn)
302: throws SQLException {
303: aConn.flushNoChecks();
304: }
305:
306: /**
307: * Adds any completion info.
308: */
309: public void complete(UserQuery userQuery, AmberConnection aConn)
310: throws SQLException {
311: aConn.expire();
312:
313: FromItem item = _fromList.get(0);
314:
315: aConn.addCompletion(new TableInvalidateCompletion(item
316: .getEntityType().getTable().getName()));
317: }
318:
319: /**
320: * Debug view.
321: */
322: public String toString() {
323: return "UpdateQuery[" + getQueryString() + "]";
324: }
325:
326: /**
327: * Generates the FROM list.
328: */
329: private void generateFromList(CharBuffer cb, boolean skipFirst) {
330: FromItem item;
331:
332: // jpa/114f: reorder from list for left outer join
333: for (int i = 1; i < _fromList.size(); i++) {
334: item = _fromList.get(i);
335:
336: if (item.isOuterJoin()) {
337: JoinExpr join = item.getJoinExpr();
338:
339: if (join == null)
340: continue;
341:
342: FromItem parent = join.getJoinParent();
343:
344: int index = _fromList.indexOf(parent);
345:
346: if (index < 0)
347: continue;
348:
349: _fromList.remove(i);
350:
351: if (index < i)
352: index++;
353:
354: _fromList.add(index, item);
355: }
356: }
357:
358: boolean hasJoinExpr = false;
359: boolean isFirst = true;
360:
361: int i = 0;
362:
363: // 1201: skip the first from item since it is
364: // available in the UPDATE clause
365: if (skipFirst)
366: i++;
367:
368: for (; i < _fromList.size(); i++) {
369: item = _fromList.get(i);
370:
371: // jpa/1178
372: if (getParentQuery() != null) {
373: ArrayList<FromItem> fromList = getParentQuery()
374: .getFromList();
375: if (fromList != null) {
376: if (fromList.contains(item)) {
377: hasJoinExpr = true;
378: continue;
379: }
380: }
381: }
382:
383: if (isFirst) {
384: isFirst = false;
385: } else {
386: if (item.isOuterJoin())
387: cb.append(" left outer join ");
388: else {
389: cb.append(", ");
390:
391: if (item.getJoinExpr() != null)
392: hasJoinExpr = true;
393: }
394: }
395:
396: cb.append(item.getTable().getName());
397: cb.append(" ");
398: cb.append(item.getName());
399:
400: if (item.getJoinExpr() != null && item.isOuterJoin()) {
401: cb.append(" on ");
402: item.getJoinExpr().generateJoin(cb);
403: }
404: }
405: }
406: }
|