001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.openjpa.jdbc.sql;
020:
021: import java.io.Serializable;
022: import java.sql.CallableStatement;
023: import java.sql.Connection;
024: import java.sql.PreparedStatement;
025: import java.sql.ResultSet;
026: import java.sql.SQLException;
027: import java.util.ArrayList;
028: import java.util.Collections;
029: import java.util.Iterator;
030: import java.util.List;
031:
032: import org.apache.commons.lang.ObjectUtils;
033: import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
034: import org.apache.openjpa.jdbc.kernel.exps.Val;
035: import org.apache.openjpa.jdbc.schema.Column;
036: import org.apache.openjpa.jdbc.schema.Sequence;
037: import org.apache.openjpa.jdbc.schema.Table;
038: import serp.util.Numbers;
039:
040: /**
041: * Buffer for SQL statements that can be used to create
042: * java.sql.PreparedStatements.
043: *
044: * @author Marc Prud'hommeaux
045: * @author Abe White
046: * @since 0.2.4
047: */
048: public final class SQLBuffer implements Serializable, Cloneable {
049:
050: private static final String PARAMETER_TOKEN = "?";
051:
052: private final DBDictionary _dict;
053: private final StringBuffer _sql = new StringBuffer();
054: private List _subsels = null;
055: private List _params = null;
056: private List _cols = null;
057:
058: /**
059: * Default constructor.
060: */
061: public SQLBuffer(DBDictionary dict) {
062: _dict = dict;
063: }
064:
065: /**
066: * Copy constructor.
067: */
068: public SQLBuffer(SQLBuffer buf) {
069: _dict = buf._dict;
070: append(buf);
071: }
072:
073: /**
074: * Perform a shallow clone of this SQL Buffer.
075: */
076: public Object clone() {
077: return new SQLBuffer(this );
078: }
079:
080: /**
081: * Return true if the buffer is emtpy.
082: */
083: public boolean isEmpty() {
084: return _sql.length() == 0;
085: }
086:
087: /**
088: * Append all SQL and parameters of the given buffer.
089: */
090: public SQLBuffer append(SQLBuffer buf) {
091: append(buf, _sql.length(), (_params == null) ? 0 : _params
092: .size(), true);
093: return this ;
094: }
095:
096: /**
097: * Append all SQL and parameters of the given buffer at the given positions.
098: */
099: private void append(SQLBuffer buf, int sqlIndex, int paramIndex,
100: boolean subsels) {
101: if (subsels) {
102: // only allow appending of buffers with subselects, not insertion
103: if (_subsels != null && !_subsels.isEmpty()
104: && sqlIndex != _sql.length())
105: throw new IllegalStateException();
106: if (buf._subsels != null && !buf._subsels.isEmpty()) {
107: if (sqlIndex != _sql.length())
108: throw new IllegalStateException();
109: if (_subsels == null)
110: _subsels = new ArrayList(buf._subsels.size());
111: for (int i = 0; i < buf._subsels.size(); i++)
112: _subsels.add(((Subselect) buf._subsels.get(i))
113: .clone(sqlIndex, paramIndex));
114: }
115: }
116:
117: if (sqlIndex == _sql.length())
118: _sql.append(buf._sql.toString());
119: else
120: _sql.insert(sqlIndex, buf._sql.toString());
121:
122: if (buf._params != null) {
123: if (_params == null)
124: _params = new ArrayList();
125: if (_cols == null && buf._cols != null) {
126: _cols = new ArrayList();
127: while (_cols.size() < _params.size())
128: _cols.add(null);
129: }
130:
131: if (paramIndex == _params.size()) {
132: _params.addAll(buf._params);
133: if (buf._cols != null)
134: _cols.addAll(buf._cols);
135: else if (_cols != null)
136: while (_cols.size() < _params.size())
137: _cols.add(null);
138: } else {
139: _params.addAll(paramIndex, buf._params);
140: if (buf._cols != null)
141: _cols.addAll(paramIndex, buf._cols);
142: else if (_cols != null)
143: while (_cols.size() < _params.size())
144: _cols.add(paramIndex, null);
145: }
146: }
147: }
148:
149: public SQLBuffer append(Table table) {
150: _sql.append(_dict.getFullName(table, false));
151: return this ;
152: }
153:
154: public SQLBuffer append(Sequence seq) {
155: _sql.append(_dict.getFullName(seq));
156: return this ;
157: }
158:
159: public SQLBuffer append(Column col) {
160: _sql.append(col.getName());
161: return this ;
162: }
163:
164: public SQLBuffer append(String s) {
165: _sql.append(s);
166: return this ;
167: }
168:
169: /**
170: * Append a subselect. This delays resolution of the select SQL.
171: */
172: public SQLBuffer append(Select sel, JDBCFetchConfiguration fetch) {
173: return append(sel, fetch, false);
174: }
175:
176: /**
177: * Append a subselect count. This delays resolution of the select SQL.
178: */
179: public SQLBuffer appendCount(Select sel,
180: JDBCFetchConfiguration fetch) {
181: return append(sel, fetch, true);
182: }
183:
184: /**
185: * Append a subselect. This delays resolution of the select SQL.
186: */
187: private SQLBuffer append(Select sel, JDBCFetchConfiguration fetch,
188: boolean count) {
189: _sql.append("(");
190: Subselect sub = new Subselect();
191: sub.select = sel;
192: sub.fetch = fetch;
193: sub.count = count;
194: sub.sqlIndex = _sql.length();
195: sub.paramIndex = (_params == null) ? 0 : _params.size();
196: _sql.append(")");
197:
198: if (_subsels == null)
199: _subsels = new ArrayList(2);
200: _subsels.add(sub);
201: return this ;
202: }
203:
204: /**
205: * Replace a subselect.
206: */
207: public boolean replace(Select old, Select sel) {
208: if (_subsels == null)
209: return false;
210: Subselect sub;
211: for (int i = 0; i < _subsels.size(); i++) {
212: sub = (Subselect) _subsels.get(i);
213: if (sub.select == old) {
214: sub.select = sel;
215: return true;
216: }
217: }
218: return false;
219: }
220:
221: /**
222: * Append a parameter value.
223: */
224: public SQLBuffer appendValue(Object o) {
225: return appendValue(o, null);
226: }
227:
228: /**
229: * Append a parameter value for a specific column.
230: */
231: public SQLBuffer appendValue(Object o, Column col) {
232: if (o == null)
233: _sql.append("NULL");
234: else if (o instanceof Raw)
235: _sql.append(o.toString());
236: else {
237: _sql.append(PARAMETER_TOKEN);
238:
239: // initialize param and col lists; we hold off on col list until
240: // we get the first non-null col
241: if (_params == null)
242: _params = new ArrayList();
243: if (col != null && _cols == null) {
244: _cols = new ArrayList();
245: while (_cols.size() < _params.size())
246: _cols.add(null);
247: }
248:
249: _params.add(o);
250: if (_cols != null)
251: _cols.add(col);
252: }
253: return this ;
254: }
255:
256: /**
257: * Append a parameter value.
258: */
259: public SQLBuffer appendValue(boolean b) {
260: return appendValue(b, null);
261: }
262:
263: /**
264: * Append a parameter value.
265: */
266: public SQLBuffer appendValue(boolean b, Column col) {
267: return appendValue((b) ? Boolean.TRUE : Boolean.FALSE, col);
268: }
269:
270: /**
271: * Append a parameter value.
272: */
273: public SQLBuffer appendValue(byte b) {
274: return appendValue(b, null);
275: }
276:
277: /**
278: * Append a parameter value.
279: */
280: public SQLBuffer appendValue(byte b, Column col) {
281: return appendValue(new Byte(b), col);
282: }
283:
284: /**
285: * Append a parameter value.
286: */
287: public SQLBuffer appendValue(char c) {
288: return appendValue(c, null);
289: }
290:
291: /**
292: * Append a parameter value.
293: */
294: public SQLBuffer appendValue(char c, Column col) {
295: return appendValue(new Character(c), col);
296: }
297:
298: /**
299: * Append a parameter value.
300: */
301: public SQLBuffer appendValue(double d) {
302: return appendValue(d, null);
303: }
304:
305: /**
306: * Append a parameter value.
307: */
308: public SQLBuffer appendValue(double d, Column col) {
309: return appendValue(new Double(d), col);
310: }
311:
312: /**
313: * Append a parameter value.
314: */
315: public SQLBuffer appendValue(float f) {
316: return appendValue(f, null);
317: }
318:
319: /**
320: * Append a parameter value.
321: */
322: public SQLBuffer appendValue(float f, Column col) {
323: return appendValue(new Float(f), col);
324: }
325:
326: /**
327: * Append a parameter value.
328: */
329: public SQLBuffer appendValue(int i) {
330: return appendValue(i, null);
331: }
332:
333: /**
334: * Append a parameter value.
335: */
336: public SQLBuffer appendValue(int i, Column col) {
337: return appendValue(Numbers.valueOf(i), col);
338: }
339:
340: /**
341: * Append a parameter value.
342: */
343: public SQLBuffer appendValue(long l) {
344: return appendValue(l, null);
345: }
346:
347: /**
348: * Append a parameter value.
349: */
350: public SQLBuffer appendValue(long l, Column col) {
351: return appendValue(Numbers.valueOf(l), col);
352: }
353:
354: /**
355: * Append a parameter value.
356: */
357: public SQLBuffer appendValue(short s) {
358: return appendValue(s, null);
359: }
360:
361: /**
362: * Append a parameter value.
363: */
364: public SQLBuffer appendValue(short s, Column col) {
365: return appendValue(new Short(s), col);
366: }
367:
368: /**
369: * Return the list of parameter values.
370: */
371: public List getParameters() {
372: return (_params == null) ? Collections.EMPTY_LIST : _params;
373: }
374:
375: /**
376: * Return the SQL for this buffer.
377: */
378: public String getSQL() {
379: return getSQL(false);
380: }
381:
382: /**
383: * Returns the SQL for this buffer.
384: *
385: * @param replaceParams if true, then replace parameters with the
386: * actual parameter values
387: */
388: public String getSQL(boolean replaceParams) {
389: resolveSubselects();
390: String sql = _sql.toString();
391: if (!replaceParams || _params == null || _params.isEmpty())
392: return sql;
393:
394: StringBuffer buf = new StringBuffer();
395: Iterator pi = _params.iterator();
396: for (int i = 0; i < sql.length(); i++) {
397: if (sql.charAt(i) != '?') {
398: buf.append(sql.charAt(i));
399: continue;
400: }
401:
402: Object param = pi.hasNext() ? pi.next() : null;
403: if (param == null)
404: buf.append("NULL");
405: else if (param instanceof Number
406: || param instanceof Boolean)
407: buf.append(param);
408: else if (param instanceof String
409: || param instanceof Character)
410: buf.append("'").append(param).append("'");
411: else
412: buf.append("?");
413: }
414: return buf.toString();
415: }
416:
417: /**
418: * Resolve our delayed subselects.
419: */
420: private void resolveSubselects() {
421: if (_subsels == null || _subsels.isEmpty())
422: return;
423:
424: // add subsels backwards so that the stored insertion points of
425: // later subsels remain valid
426: Subselect sub;
427: SQLBuffer buf;
428: for (int i = _subsels.size() - 1; i >= 0; i--) {
429: sub = (Subselect) _subsels.get(i);
430: if (sub.count)
431: buf = sub.select.toSelectCount();
432: else
433: buf = sub.select.toSelect(false, sub.fetch);
434: buf.resolveSubselects();
435: append(buf, sub.sqlIndex, sub.paramIndex, false);
436: }
437: _subsels.clear();
438: }
439:
440: /**
441: * Create and populate the parameters of a prepared statement using
442: * the SQL in this buffer.
443: */
444: public PreparedStatement prepareStatement(Connection conn)
445: throws SQLException {
446: return prepareStatement(conn, ResultSet.TYPE_FORWARD_ONLY,
447: ResultSet.CONCUR_READ_ONLY);
448: }
449:
450: /**
451: * Create and populate the parameters of a prepared statement using
452: * the SQL in this buffer.
453: */
454: public PreparedStatement prepareStatement(Connection conn,
455: int rsType, int rsConcur) throws SQLException {
456: return prepareStatement(conn, null, rsType, rsConcur);
457: }
458:
459: /**
460: * Create and populate the parameters of a prepred statement using the
461: * SQL in this buffer and the given fetch configuration.
462: */
463: public PreparedStatement prepareStatement(Connection conn,
464: JDBCFetchConfiguration fetch, int rsType, int rsConcur)
465: throws SQLException {
466: if (rsType == -1 && fetch == null)
467: rsType = ResultSet.TYPE_FORWARD_ONLY;
468: else if (rsType == -1)
469: rsType = fetch.getResultSetType();
470: if (rsConcur == -1)
471: rsConcur = ResultSet.CONCUR_READ_ONLY;
472:
473: PreparedStatement stmnt;
474: if (rsType == ResultSet.TYPE_FORWARD_ONLY
475: && rsConcur == ResultSet.CONCUR_READ_ONLY)
476: stmnt = conn.prepareStatement(getSQL());
477: else
478: stmnt = conn.prepareStatement(getSQL(), rsType, rsConcur);
479: try {
480: setParameters(stmnt);
481: if (fetch != null) {
482: if (fetch.getFetchBatchSize() > 0)
483: stmnt.setFetchSize(fetch.getFetchBatchSize());
484: if (rsType != ResultSet.TYPE_FORWARD_ONLY
485: && fetch.getFetchDirection() != ResultSet.FETCH_FORWARD)
486: stmnt.setFetchDirection(fetch.getFetchDirection());
487: }
488: return stmnt;
489: } catch (SQLException se) {
490: try {
491: stmnt.close();
492: } catch (SQLException se2) {
493: }
494: throw se;
495: }
496: }
497:
498: /**
499: * Create and populate the parameters of a prepared statement using
500: * the SQL in this buffer.
501: */
502: public CallableStatement prepareCall(Connection conn)
503: throws SQLException {
504: return prepareCall(conn, ResultSet.TYPE_FORWARD_ONLY,
505: ResultSet.CONCUR_READ_ONLY);
506: }
507:
508: /**
509: * Create and populate the parameters of a prepared statement using
510: * the SQL in this buffer.
511: */
512: public CallableStatement prepareCall(Connection conn, int rsType,
513: int rsConcur) throws SQLException {
514: return prepareCall(conn, null, rsType, rsConcur);
515: }
516:
517: /**
518: * Create and populate the parameters of a prepred statement using the
519: * SQL in this buffer and the given fetch configuration.
520: */
521: public CallableStatement prepareCall(Connection conn,
522: JDBCFetchConfiguration fetch, int rsType, int rsConcur)
523: throws SQLException {
524: if (rsType == -1 && fetch == null)
525: rsType = ResultSet.TYPE_FORWARD_ONLY;
526: else if (rsType == -1)
527: rsType = fetch.getResultSetType();
528: if (rsConcur == -1)
529: rsConcur = ResultSet.CONCUR_READ_ONLY;
530:
531: CallableStatement stmnt;
532: if (rsType == ResultSet.TYPE_FORWARD_ONLY
533: && rsConcur == ResultSet.CONCUR_READ_ONLY)
534: stmnt = conn.prepareCall(getSQL());
535: else
536: stmnt = conn.prepareCall(getSQL(), rsType, rsConcur);
537: try {
538: setParameters(stmnt);
539: if (fetch != null) {
540: if (fetch.getFetchBatchSize() > 0)
541: stmnt.setFetchSize(fetch.getFetchBatchSize());
542: if (rsType != ResultSet.TYPE_FORWARD_ONLY
543: && fetch.getFetchDirection() != ResultSet.FETCH_FORWARD)
544: stmnt.setFetchDirection(fetch.getFetchDirection());
545: }
546: return stmnt;
547: } catch (SQLException se) {
548: try {
549: stmnt.close();
550: } catch (SQLException se2) {
551: }
552: throw se;
553: }
554: }
555:
556: /**
557: * Populate the parameters of an existing PreparedStatement
558: * with values from this buffer.
559: */
560: public void setParameters(PreparedStatement ps) throws SQLException {
561: if (_params == null)
562: return;
563:
564: Column col;
565: for (int i = 0; i < _params.size(); i++) {
566: col = (_cols == null) ? null : (Column) _cols.get(i);
567: _dict.setUnknown(ps, i + 1, _params.get(i), col);
568: }
569: }
570:
571: public int hashCode() {
572: int hash = _sql.hashCode();
573: return (_params == null) ? hash : hash ^ _params.hashCode();
574: }
575:
576: /**
577: * Compare internal SQL without resolving subselects or stringifying
578: * parameters.
579: */
580: public boolean sqlEquals(String sql) {
581: return _sql.toString().equals(sql);
582: }
583:
584: public boolean equals(Object other) {
585: if (other == this )
586: return true;
587: if (!(other instanceof SQLBuffer))
588: return false;
589:
590: SQLBuffer buf = (SQLBuffer) other;
591: return _sql.equals(buf._sql)
592: && ObjectUtils.equals(_params, buf._params);
593: }
594:
595: /**
596: * Replace SQL '?' with CAST string if required by DB platform
597: * @param oper
598: * @param val
599: */
600: public void addCastForParam(String oper, Val val) {
601: if (_sql.charAt(_sql.length() - 1) == '?') {
602: String castString = _dict.addCastAsType(oper, val);
603: if (castString != null)
604: _sql.replace(_sql.length() - 1, _sql.length(),
605: castString);
606: }
607: }
608:
609: /**
610: * Replace current buffer string with the new string
611: *
612: * @param start replace start position
613: * @param end replace end position
614: * @param newString
615: */
616: public void replaceSqlString(int start, int end, String newString) {
617: _sql.replace(start, end, newString);
618: }
619:
620: /**
621: * Represents a subselect.
622: */
623: private static class Subselect {
624:
625: public Select select;
626: public JDBCFetchConfiguration fetch;
627: public boolean count;
628: public int sqlIndex;
629: public int paramIndex;
630:
631: public Subselect clone(int sqlIndex, int paramIndex) {
632: if (sqlIndex == 0 && paramIndex == 0)
633: return this ;
634:
635: Subselect sub = new Subselect();
636: sub.select = select;
637: sub.fetch = fetch;
638: sub.count = count;
639: sub.sqlIndex = this .sqlIndex + sqlIndex;
640: sub.paramIndex = this .paramIndex + paramIndex;
641: return sub;
642: }
643: }
644:
645: public void setParameters(List params) {
646: _params = params;
647: }
648:
649: public List getColumns() {
650: return _cols;
651: }
652: }
|