001    /**
002     * Copyright (c) 2010 Yahoo! Inc. All rights reserved.
003     * Licensed under the Apache License, Version 2.0 (the "License");
004     * you may not use this file except in compliance with the License.
005     * You may obtain a copy of the License at
006     *
007     *   http://www.apache.org/licenses/LICENSE-2.0
008     *
009     *  Unless required by applicable law or agreed to in writing, software
010     *  distributed under the License is distributed on an "AS IS" BASIS,
011     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012     *  See the License for the specific language governing permissions and
013     *  limitations under the License. See accompanying LICENSE file.
014     */
015    package org.apache.oozie.util.db;
016    
017    import java.io.BufferedInputStream;
018    import java.io.BufferedOutputStream;
019    import java.io.ByteArrayOutputStream;
020    import java.io.IOException;
021    import java.sql.Blob;
022    import java.sql.Connection;
023    import java.sql.PreparedStatement;
024    import java.sql.ResultSet;
025    import java.sql.SQLException;
026    import java.sql.Timestamp;
027    import java.util.ArrayList;
028    import java.util.HashSet;
029    import java.util.List;
030    import java.util.Map;
031    import java.util.Set;
032    
033    import org.apache.commons.logging.LogFactory;
034    import org.apache.oozie.util.XLog;
035    import org.apache.oozie.util.db.Schema.Column;
036    import org.apache.oozie.util.db.Schema.Table;
037    
038    /**
039     * The <code>SqlStatement</code> is used to generate SQL Statements. Prepare the generated Statements and also to parse
040     * the resultSets
041     */
042    public abstract class SqlStatement {
043    
044        private static XLog log = new XLog(LogFactory.getLog(SqlStatement.class));
045        protected boolean forUpdate = false;
046    
047        /**
048         * <code>ResultSetReader</code> is used to parse the result set and gives methods for getting appropriate type of
049         * data given the column name
050         */
051        public static class ResultSetReader {
052            final ResultSet rSet;
053    
054            private ResultSetReader(ResultSet rSet) {
055                this.rSet = rSet;
056            }
057    
058            /**
059             * Move the Result Set to next record
060             *
061             * @return true if there is a next record
062             * @throws SQLException
063             */
064            public boolean next() throws SQLException {
065                return rSet.next();
066            }
067    
068            /**
069             * Close the Result Set
070             *
071             * @throws SQLException
072             */
073            public void close() throws SQLException {
074                rSet.close();
075            }
076    
077            /**
078             * Get the Column data given its type and name
079             *
080             * @param <T> Type of the column
081             * @param clazz Class of the Type
082             * @param col Column name
083             * @return Column data
084             * @throws SQLException
085             */
086            @SuppressWarnings("unchecked")
087            public <T> T get(Class<T> clazz, Column col) throws SQLException {
088                if (clazz.isAssignableFrom(col.getType())) {
089                    return (T) rSet.getObject(col.asLabel());
090                }
091                else {
092                    if (String.class.equals(clazz)) {
093                        return (T) ("" + rSet.getObject(col.asLabel()));
094                    }
095                    else {
096                        throw new RuntimeException("Column Error : Actual Type [" + col.getType() + "]," + " Requested Type ["
097                                + clazz + "] !!");
098                    }
099                }
100            }
101    
102            /**
103             * Get the data for columns with blob type
104             *
105             * @param col Column name
106             * @return Column data
107             * @throws SQLException
108             */
109            public byte[] getByteArray(Column col) throws SQLException {
110                byte[] bArray = null;
111                if (Blob.class.equals(col.getType())) {
112                    BufferedInputStream bStream = new BufferedInputStream(rSet.getBinaryStream(col.asLabel()));
113                    byte[] temp = new byte[1024];
114                    int num = 0;
115                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
116                    BufferedOutputStream bOut = new BufferedOutputStream(baos);
117                    try {
118                        while ((num = bStream.read(temp)) != -1) {
119                            bOut.write(temp, 0, num);
120                        }
121                        bOut.flush();
122                        bOut.close();
123                    }
124                    catch (IOException e) {
125                        throw new SQLException(e);
126                    }
127                    bArray = baos.toByteArray();
128                }
129                else {
130                    throw new RuntimeException("Column Error : Actual Type [" + col.getType() + "]," + " Requested Type ["
131                            + Blob.class + "] !!");
132                }
133                return bArray;
134            }
135    
136            /**
137             * Get a String Column
138             *
139             * @param col Column Name
140             * @return Column data
141             * @throws SQLException
142             */
143            public String getString(Column col) throws SQLException {
144                return get(String.class, col);
145            }
146    
147            /**
148             * Get the Timestamp Column
149             *
150             * @param col Column name
151             * @return Column data
152             * @throws SQLException
153             */
154            public Timestamp getTimestamp(Column col) throws SQLException {
155                return get(Timestamp.class, col);
156            }
157    
158            /**
159             * Get the Boolean Column
160             *
161             * @param col Column name
162             * @return Column data
163             * @throws SQLException
164             */
165            public Boolean getBoolean(Column col) throws SQLException {
166                return get(Boolean.class, col);
167            }
168    
169            /**
170             * Get the Numeric data
171             *
172             * @param col Column name
173             * @return Column data
174             * @throws SQLException
175             */
176            public Long getLong(Column col) throws SQLException {
177                return get(Long.class, col);
178            }
179    
180        }
181    
182        /**
183         * Construct the condition statement that will be used in the where clause
184         */
185        public static class Condition {
186    
187            final Column column;
188            protected StringBuilder sb = new StringBuilder("( ");
189            protected List<Object> values = new ArrayList<Object>();
190    
191            private Condition(Column column) {
192                this.column = column;
193                if (column != null) {
194                    sb.append(column);
195                }
196            }
197    
198            /**
199             * Return the Condition string
200             */
201            public String toString() {
202                return sb.toString();
203            }
204        }
205    
206        /**
207         * NULL/NOT NULL Condition Generator
208         */
209        static class Null extends Condition {
210            Null(boolean isInvert, Column column) {
211                super(column);
212                sb.append(" IS");
213                sb.append(isInvert ? " NOT" : "");
214                sb.append(" NULL ");
215                sb.append(" )");
216            }
217        }
218    
219        /**
220         * Generate condition statement for IS NULL
221         *
222         * @param column column name
223         * @return IS NULL condition statement
224         */
225        public static Condition isNull(Column column) {
226            return new Null(false, column);
227        }
228    
229        /**
230         * Generate condition statement for IS NOT NULL
231         *
232         * @param column column name
233         * @return IS NOT NULL condition statement
234         */
235        public static Condition isNotNull(Column column) {
236            return new Null(true, column);
237        }
238    
239        /**
240         * LIKE/NOT LIKE Condition Generator
241         */
242        static class Like extends Condition {
243            Like(boolean isInvert, Column column, String value) {
244                super(column);
245                sb.append(isInvert ? " NOT" : "").append(" LIKE ").append("?").append(" )");
246                values.add(value);
247            }
248        }
249    
250        /**
251         * Generate condition statement for IS LIKE
252         *
253         * @param column column name
254         * @param value value to be checked
255         * @return IS LIKE condition statement
256         */
257        public static Condition isLike(Column column, String value) {
258            return new Like(false, column, value);
259        }
260    
261        /**
262         * Generates condition statement for IS NOT LIKE
263         *
264         * @param column column name
265         * @param value value to be checked
266         * @return IS NOT LIKE condition statement
267         */
268        public static Condition isNotLike(Column column, String value) {
269            return new Like(true, column, value);
270        }
271    
272        /**
273         * Comparison condition statement generator
274         */
275        static class Compare extends Condition {
276            Compare(String oper, Column column, Object value) {
277                super(column);
278                if (value instanceof Column) {
279                    sb.append(oper).append(value).append(" )");
280                }
281                else {
282                    sb.append(oper).append("?").append(" )");
283                    values.add(value);
284                }
285            }
286        }
287    
288        /**
289         * Generate Condition statement for equality check
290         *
291         * @param column
292         * @param value
293         * @return Equality Condition statement
294         */
295        public static Condition isEqual(Column column, Object value) {
296            return new Compare(" = ", column, value);
297        }
298    
299        /**
300         * Generate InEquality Condition statement
301         *
302         * @param column
303         * @param value
304         * @return Inequality Condition statement
305         */
306        public static Condition isNotEqual(Column column, Object value) {
307            return new Compare(" <> ", column, value);
308        }
309    
310        /**
311         * Generate Condition statement for LESS THAN condition checking
312         *
313         * @param column
314         * @param value
315         * @return less than condition statement
316         */
317        public static Condition lessThan(Column column, Object value) {
318            return new Compare(" < ", column, value);
319        }
320    
321        /**
322         * Generate Condition statement for GREATER THAN condition checking
323         *
324         * @param column
325         * @param value
326         * @return greater than condition statement
327         */
328        public static Condition greaterThan(Column column, Object value) {
329            return new Compare(" > ", column, value);
330        }
331    
332        /**
333         * Generate Condition statement for LESS THAN OR EQUAL condition checking
334         *
335         * @param column
336         * @param value
337         * @return less than or equal condition statement
338         */
339        public static Condition lessThanOrEqual(Column column, Object value) {
340            return new Compare(" <= ", column, value);
341        }
342    
343        /**
344         * Generate Condition statement for GREATER THAN OR EQUAL condition checking
345         *
346         * @param column
347         * @param value
348         * @return greater than or equal condition statement
349         */
350        public static Condition greaterThanOrEqual(Column column, Object value) {
351            return new Compare(" >= ", column, value);
352        }
353    
354        /**
355         * IN/NOT IN condition statement generator for checking multiple values and for sub queries
356         */
357        static class In extends Condition {
358            In(boolean isInvert, Column column, Object... values) {
359                super(column);
360                sb.append(isInvert ? " NOT" : "").append(" IN (");
361                for (Object value : values) {
362                    sb.append(" ? ").append(",");
363                    this.values.add(value);
364                }
365                // remove last comma
366                sb.setLength(sb.length() - 1);
367                sb.append(") )");
368            }
369    
370            In(boolean isInvert, Column column, Select select) {
371                super(column);
372                sb.append(isInvert ? " NOT" : "").append(" IN (");
373                for (Object value : select.values) {
374                    this.values.add(value);
375                }
376                sb.append(select.toString());
377                sb.append(") )");
378            }
379        }
380    
381        /**
382         * IN Condition for checking multiple values
383         *
384         * @param column
385         * @param values
386         * @return In condition statement
387         */
388        public static Condition in(Column column, Object... values) {
389            return new In(false, column, values);
390        }
391    
392        /**
393         * NOT IN Condition for checking multiple values
394         *
395         * @param column
396         * @param values
397         * @return not in condition statement
398         */
399        public static Condition notIn(Column column, Object... values) {
400            return new In(true, column, values);
401        }
402    
403        /**
404         * Sub query with IN condition
405         *
406         * @param column
407         * @param select
408         * @return Sub query using in
409         */
410        public static Condition in(Column column, Select select) {
411            return new In(false, column, select);
412        }
413    
414        /**
415         * Sub query with NOT IN condition
416         *
417         * @param column
418         * @param select
419         * @return sub query using not in
420         */
421        public static Condition notIn(Column column, Select select) {
422            return new In(true, column, select);
423        }
424    
425        /**
426         * Generate the Range checking statements
427         */
428        static class Between extends Condition {
429            Between(boolean isInvert, Column column, Object lVal, Object rVal) {
430                super(column);
431                sb.append(isInvert ? " NOT" : "").append(" BETWEEN ");
432                sb.append(" ? ");
433                values.add(lVal);
434                sb.append(" AND ");
435                sb.append(" ? ").append(" )");
436                values.add(rVal);
437            }
438        }
439    
440        /**
441         * BETWEEN range checking statement
442         *
443         * @param column
444         * @param lVal min value for range checking
445         * @param rVal max value for range checking
446         * @return between condition statement
447         */
448        public static Condition between(Column column, Object lVal, Object rVal) {
449            return new Between(false, column, lVal, rVal);
450        }
451    
452        /**
453         * NOT BETWEEN range checking statement
454         *
455         * @param column
456         * @param lVal min value for range checking
457         * @param rVal max value for range checking
458         * @return not between condition statement
459         */
460        public static Condition notBetween(Column column, Object lVal, Object rVal) {
461            return new Between(true, column, lVal, rVal);
462        }
463    
464        /**
465         * Logical AND condition Generator
466         *
467         * @param conds list of conditions for AND
468         * @return AND statement
469         */
470        public static Condition and(Condition... conds) {
471            Condition retVal = new Condition(null);
472            if (conds.length >= 2) {
473                for (int i = 0; i < conds.length; i++) {
474                    if (i > 0) {
475                        retVal.sb.append(" AND ");
476                    }
477                    retVal.sb.append(conds[i].sb);
478                    retVal.values.addAll(conds[i].values);
479                }
480            }
481            else {
482                if (conds.length == 1) {
483                    return conds[0];
484                }
485            }
486            retVal.sb.append(" )");
487            return retVal;
488        }
489    
490        /**
491         * Logical OR condition generator
492         *
493         * @param conds list of conditions for OR
494         * @return OR statement
495         */
496        public static Condition or(Condition... conds) {
497            Condition retVal = new Condition(null);
498            if (conds.length >= 2) {
499                for (int i = 0; i < conds.length; i++) {
500                    if (i > 0) {
501                        retVal.sb.append(" OR ");
502                    }
503                    retVal.sb.append(conds[i].sb);
504                    retVal.values.addAll(conds[i].values);
505                }
506            }
507            else {
508                if (conds.length == 1) {
509                    return conds[0];
510                }
511            }
512            retVal.sb.append(" )");
513            return retVal;
514        }
515    
516        protected StringBuilder sb = new StringBuilder("");
517        protected List<Object> values = new ArrayList<Object>();
518    
519        /**
520         * Select Statement generator. Generate the SQL Statement for select statements. Provide methods to add WHERE
521         * clause, ORDER BY clause, FOR UPDATE clause.
522         */
523        public static class Select extends SqlStatement {
524            private Condition condition;
525            private boolean isOdered = false;
526            private Column[] orderby = null;
527            private boolean[] isAscending = null;
528            private boolean isLimitSet = false;
529    
530            private Select(Select other) {
531                this.condition = other.condition;
532                this.sb.append(other.sb);
533                this.values.addAll(other.values);
534                this.isOdered = other.isOdered;
535                this.isLimitSet = other.isLimitSet;
536                this.orderby = other.orderby;
537                this.isAscending = other.isAscending;
538                this.forUpdate = other.forUpdate;
539            }
540    
541            Select(boolean count, Table... tables) {
542                String temp = count ? "COUNT(*)" : "*";
543                this.sb.append("SELECT " + temp + " FROM");
544                if ((tables != null) && (tables.length > 0)) {
545                    for (Table table : tables) {
546                        this.sb.append(" " + table + " ,");
547                    }
548                    // remove comma
549                    this.sb.setLength(sb.length() - 1);
550                }
551                else {
552                    throw new RuntimeException("Need atleast 1 Table !!");
553                }
554            }
555    
556            Select(Column... columns) {
557                this.sb.append("SELECT");
558                if ((columns != null) && (columns.length > 0)) {
559                    Set<Table> tables = new HashSet<Table>();
560                    for (Column column : columns) {
561                        tables.add(column.table());
562                        this.sb.append(" " + column + " AS " + column.asLabel() + " ,");
563                    }
564                    // remove comma
565                    this.sb.setLength(sb.length() - 1);
566                    this.sb.append(" FROM");
567                    for (Table table : tables) {
568                        this.sb.append(" " + table + " ,");
569                    }
570                    // remove comma
571                    this.sb.setLength(sb.length() - 1);
572                }
573                else {
574                    throw new RuntimeException("Need atleast 1 Column !!");
575                }
576            }
577    
578            /**
579             * Set the condition for where clause
580             *
581             * @param condition condition for where clause
582             * @return <code>Select</code> for cascading
583             */
584            public Select where(Condition condition) {
585                Select retVal = new Select(this);
586                retVal.condition = condition;
587                retVal.values.addAll(condition.values);
588                return retVal;
589            }
590    
591            /**
592             * Sets the column to sort and the order of sort
593             *
594             * @param column column to sort
595             * @param order true = ascending
596             * @return <code>Select</code> for cascading
597             */
598            public Select orderBy(Column column, boolean order) {
599                if (!isOdered) {
600                    Select retVal = new Select(this);
601                    retVal.orderby = new Column[]{column};
602                    retVal.isAscending = new boolean[]{order};
603                    retVal.isOdered = true;
604                    return retVal;
605                }
606                return this;
607            }
608    
609            /**
610             * To sort 2 columns
611             *
612             * @param column0 First column to be sorted
613             * @param order0 true = ascending
614             * @param column1 Second column to be sorted
615             * @param order1 true = ascending
616             * @return <code>Select</code> for cascading
617             */
618            public Select orderBy(Column column0, boolean order0, Column column1, boolean order1) {
619                if (!isOdered) {
620                    Select retVal = new Select(this);
621                    retVal.orderby = new Column[]{column0, column1};
622                    retVal.isAscending = new boolean[]{order0, order1};
623                    retVal.isOdered = true;
624                    return retVal;
625                }
626                return this;
627            }
628    
629            /**
630             * Setting the offset and limit for LIMIT clause
631             *
632             * @param offset
633             * @param limit
634             * @return <code>Select</code> for cascading
635             */
636            public Select limit(int offset, int limit) {
637                if (isOdered) {
638                    Select retVal = new Select(this);
639                    retVal.values.add(limit);
640                    retVal.values.add(offset);
641                    retVal.isLimitSet = true;
642                    return retVal;
643                }
644                return this;
645            }
646    
647            /**
648             * Set the "for update" flag to lock the rows for updating
649             *
650             * @return <code>Select</code> for cascading
651             */
652            // TODO Not working for hsql
653            public Select forUpdate() {
654                Select retVal = new Select(this);
655                retVal.forUpdate = true;
656                return retVal;
657            }
658    
659            /**
660             * Generate the SQL Select Statement with conditions and other clauses that were set
661             */
662            public String toString() {
663                String oBy = "";
664                if ((orderby != null) && (isAscending != null) && (orderby.length == isAscending.length)) {
665                    StringBuffer osb = new StringBuffer(" ORDER BY ");
666                    int i = 0;
667                    for (Column column : orderby) {
668                        osb.append(column.asLabel()).append(isAscending[i] ? " ASC ," : " DESC ,");
669                    }
670                    osb.setLength(osb.length() - 1);
671                    if (isLimitSet) {
672                        osb.append("LIMIT ").append("?").append(" OFFSET ").append("?").append(" ");
673                    }
674                    oBy = osb.toString();
675                }
676                return sb.toString() + ((condition != null) ? "WHERE " + condition.toString() : "") + oBy;
677            }
678        }
679    
680        /**
681         * SQL Statement generator for DELETE Statements
682         */
683        public static class Delete extends SqlStatement {
684            Condition condition;
685            final Table table;
686    
687            Delete(Table table) {
688                this.table = table;
689                this.sb.append("DELETE FROM " + table + " ");
690            }
691    
692            private Delete(Delete other) {
693                this.table = other.table;
694                this.condition = other.condition;
695                this.sb.append(other.sb);
696                this.values.addAll(other.values);
697            }
698    
699            /**
700             * Set the where clause for DELETE
701             *
702             * @param condition condition for where clause
703             * @return <code>Delete</code> for cascading
704             */
705            public Delete where(Condition condition) {
706                Delete retVal = new Delete(this);
707                retVal.condition = condition;
708                retVal.values.addAll(condition.values);
709                return retVal;
710            }
711    
712            /**
713             * Return the DELETE Statement
714             */
715            public String toString() {
716                return sb.toString() + ((condition != null) ? "WHERE " + condition.toString() : "");
717            }
718        }
719    
720        /**
721         * UPDATE SQL Statement generator
722         */
723        public static class Update extends SqlStatement {
724            Condition condition;
725            final Table table;
726    
727            Update(Table table) {
728                this.table = table;
729                this.sb.append("UPDATE " + table + " SET ");
730            }
731    
732            private Update(Update other) {
733                this.table = other.table;
734                this.condition = other.condition;
735                this.sb.append(other.sb);
736                this.values.addAll(other.values);
737            }
738    
739            /**
740             * SET clause for update statement
741             *
742             * @param column column name
743             * @param value A temporary place holder which can be replaced while preparing
744             * @return <code>Update</code> for cascading
745             */
746            public Update set(Column column, Object value) {
747                Update retVal = new Update(this);
748                retVal.sb.append((values.isEmpty() ? "" : ", ") + column + " = ? ");
749                retVal.values.add(value);
750                return retVal;
751            }
752    
753            /**
754             * Set condition for updating
755             *
756             * @param condition condition for where clause
757             * @return <code>Update</code> for cascading
758             */
759            public Update where(Condition condition) {
760                Update retVal = new Update(this);
761                retVal.condition = condition;
762                retVal.values.addAll(condition.values);
763                return retVal;
764            }
765    
766            /**
767             * Return the UPDATE statement
768             */
769            public String toString() {
770                return sb.toString() + ((condition != null) ? " WHERE " + condition.toString() : "");
771            }
772        }
773    
774        /**
775         * INSERT Statement generator
776         */
777        public static class Insert extends SqlStatement {
778            StringBuilder sbCol = new StringBuilder("");
779            StringBuilder sbVal = new StringBuilder("");
780            boolean isFirst = true;
781            final Table table;
782    
783            Insert(Table table) {
784                this.table = table;
785                this.sbCol.append("INSERT INTO " + table + " ( )");
786                this.sbVal.append("VALUES ( )");
787            }
788    
789            private Insert(Insert other) {
790                this.table = other.table;
791                this.sbCol.append(other.sbCol);
792                this.sbVal.append(other.sbVal);
793                this.values.addAll(other.values);
794                if (other.isFirst) {
795                    this.isFirst = false;
796                }
797            }
798    
799            /**
800             * Set the VALUES that are to be inserted
801             *
802             * @param column
803             * @param value A temporary place holder which will be replaced while preparing
804             * @return
805             */
806            public Insert value(Column column, Object value) {
807                Insert retVal = new Insert(this);
808                retVal.sbCol.setLength(retVal.sbCol.length() - 1);
809                retVal.sbVal.setLength(retVal.sbVal.length() - 1);
810                retVal.values.add(value);
811                retVal.sbCol.append(((isFirst) ? "" : ", ") + column + " )");
812                retVal.sbVal.append(((isFirst) ? "" : ", ") + "? )");
813                retVal.isFirst = false;
814                return retVal;
815            }
816    
817            /**
818             * Return the INSERT Statement
819             */
820            public String toString() {
821                return sbCol.toString() + " " + sbVal.toString();
822            }
823        }
824    
825        /**
826         * Prepare the SQL Statement that is generated and assign the values to prepared statement. setValues should be
827         * called to set the Real Values for place holders
828         *
829         * @param conn Connection
830         * @return Prepared SQL Statement
831         * @throws SQLException
832         */
833        public PreparedStatement prepareAndSetValues(Connection conn) throws SQLException {
834            String stmt = toString();
835            if (forUpdate && !Schema.isHsqlConnection(conn)) {
836                stmt += " FOR UPDATE";
837            }
838            PreparedStatement pStmt = conn.prepareStatement(stmt);
839            int i = 1;
840            for (Object value : this.values) {
841                pStmt.setObject(i, value);
842                i++;
843            }
844            log.trace(XLog.Info.get().createPrefix() + " Preparing : " + stmt);
845            log.trace(XLog.Info.get().createPrefix() + " Values : " + values);
846            return pStmt;
847        }
848    
849        /**
850         * Assign the values to Prepared Statement. setValues should be called to set the Real Values for place holders
851         *
852         * @param pStmt Prepared Statement
853         * @return PreparedStatement with values set
854         * @throws SQLException
855         */
856        public PreparedStatement prepare(PreparedStatement pStmt) throws SQLException {
857            int i = 1;
858            pStmt.clearParameters();
859            for (Object value : this.values) {
860                pStmt.setObject(i, value);
861                i++;
862            }
863            return pStmt;
864        }
865    
866        /**
867         * Prepare the SQL Statement. Doesn't set the values.
868         *
869         * @param conn Connection
870         * @return PreparedStatement
871         * @throws SQLException
872         */
873        public PreparedStatement prepare(Connection conn) throws SQLException {
874            String stmt = toString();
875            if (forUpdate && !Schema.isHsqlConnection(conn)) {
876                stmt += " FOR UPDATE";
877            }
878            return conn.prepareStatement(stmt);
879        }
880    
881        /**
882         * Preparing Multiple statements for batch execution.
883         *
884         * @param conn Connection
885         * @param values A list of maps that contains the actual values
886         * @return Prepared Statement
887         * @throws SQLException
888         */
889        public PreparedStatement prepareForBatch(Connection conn, List<? extends Map<Object, Object>> values,
890                                                 PreparedStatement pStmt) throws SQLException {
891            String stmt = toString();
892            if (forUpdate && !Schema.isHsqlConnection(conn)) {
893                stmt += " FOR UPDATE";
894            }
895            // PreparedStatement pStmt = conn.prepareStatement(stmt);
896            for (Map<Object, Object> map : values) {
897                getNewStatementWithValues(map).prepare(pStmt);
898                pStmt.addBatch();
899            }
900            return pStmt;
901        }
902    
903        /**
904         * Replace the place holders with actual values in the sql statement
905         *
906         * @param oldVal Place holder
907         * @param newVal Actual Value
908         * @return SQL Statement with oldVal(place holder) replaced with newVal(actual value)
909         */
910        public SqlStatement setValue(Object oldVal, Object newVal) {
911            ArrayList<Object> temp = new ArrayList<Object>(values);
912            if (values.contains(oldVal)) {
913                int i = temp.indexOf(oldVal);
914                temp.set(i, newVal);
915            }
916            SqlStatement retVal = create(temp);
917            return retVal;
918        }
919    
920        /**
921         * Replace the keys(newValues) which are place holders in the sql statements with the corresponding new values. And
922         * Gives back a new SQL Statement so that the actual statement can be re-used
923         *
924         * @param newValues
925         * @return A New SQL Statement object with actual values set in its member
926         */
927        public SqlStatement getNewStatementWithValues(Map<Object, Object> newValues) {
928            ArrayList<Object> temp = new ArrayList<Object>();
929            for (Object value : values) {
930                if (newValues.containsKey(value)) {
931                    temp.add(newValues.get(value));
932                }
933                else {
934                    temp.add(value);
935                }
936            }
937            SqlStatement retVal = create(temp);
938            return retVal;
939        }
940    
941        /**
942         * Create the Appropriate SQL Statement with the given values
943         *
944         * @param temp
945         * @return
946         */
947        private SqlStatement create(ArrayList<Object> temp) {
948            SqlStatement retVal = null;
949            if (this instanceof Select) {
950                retVal = new Select((Select) this);
951            }
952            else {
953                if (this instanceof Insert) {
954                    retVal = new Insert((Insert) this);
955                }
956                else {
957                    if (this instanceof Update) {
958                        retVal = new Update((Update) this);
959                    }
960                    else {
961                        if (this instanceof Delete) {
962                            retVal = new Delete((Delete) this);
963                        }
964                    }
965                }
966            }
967            retVal.values.clear();
968            retVal.values.addAll(temp);
969            return retVal;
970        }
971    
972        /**
973         * Create the <code>ResultSetReader</code> object that has the methods to access the data from the result set
974         *
975         * @param rSet Result Set
976         * @return ResultSet Reader
977         */
978        public static ResultSetReader parse(ResultSet rSet) {
979            return new ResultSetReader(rSet);
980        }
981    
982        /**
983         * Return a new Insert Statement
984         *
985         * @param table
986         * @return Insert statement
987         */
988        public static Insert insertInto(Table table) {
989            return new Insert(table);
990        }
991    
992        /**
993         * Return a new Update Statement
994         *
995         * @param table
996         * @return Update statement
997         */
998        public static Update update(Table table) {
999            return new Update(table);
1000        }
1001    
1002        /**
1003         * Return a new Delete Statement
1004         *
1005         * @param table
1006         * @return Delete Statement
1007         */
1008        public static Delete deleteFrom(Table table) {
1009            return new Delete(table);
1010        }
1011    
1012        /**
1013         * Return a Select All Statement
1014         *
1015         * @param tables
1016         * @return Select * statement
1017         */
1018        public static Select selectAllFrom(Table... tables) {
1019            return new Select(false, tables);
1020        }
1021    
1022        /**
1023         * Return a Select Statement
1024         *
1025         * @param columns columns to select
1026         * @return select statement
1027         */
1028        public static Select selectColumns(Column... columns) {
1029            return new Select(columns);
1030        }
1031    
1032        /**
1033         * Select count(*) Statement generator.
1034         *
1035         * @param tables
1036         * @return "select count(*) from tables" statement
1037         */
1038        public static Select getCount(Table... tables) {
1039            return new Select(true, tables);
1040        }
1041    }