View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors.
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   * 
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.jdbc;
25  
26  import java.sql.Connection;
27  import java.sql.ResultSet;
28  import java.sql.SQLException;
29  import java.sql.SQLWarning;
30  import java.sql.Statement;
31  
32  import javax.jcr.RepositoryException;
33  import javax.jcr.query.QueryResult;
34  
35  import org.modeshape.jdbc.delegate.RepositoryDelegate;
36  /**
37   * 
38   */
39  class JcrStatement implements Statement {
40  
41   
42      private final JcrConnection connection;
43      private QueryResult jcrResults;
44      private ResultSet results;
45      private boolean closed;
46      private SQLWarning warning;
47      private int rowLimit = -1;
48      private int fetchDirection = ResultSet.FETCH_FORWARD;
49      private boolean poolable;
50      private int moreResults = 0;
51       
52      private String sqlLanguage = JcrConnection.JCR_SQL2;
53  
54      JcrStatement( JcrConnection connection) {
55          this.connection = connection;
56          assert this.connection != null;
57      }
58  
59      JcrConnection connection() {
60          return this.connection;
61      }
62      
63      public void setJcrSqlLanguage(String jcrSQL) {
64      	this.sqlLanguage = (jcrSQL != null ? jcrSQL : JcrConnection.JCR_SQL2);
65      }
66  
67      /**
68       * {@inheritDoc}
69       * <p>
70       * This driver doesn't have a way to set the fetch size, so this method always returns 0.
71       * </p>
72       * 
73       * @see java.sql.Statement#getFetchSize()
74       */
75      @Override
76      public int getFetchSize() throws SQLException {
77          notClosed();
78          return 0;
79      }
80  
81      /**
82       * {@inheritDoc}
83       * <p>
84       * This driver doesn't have a way to set the fetch size, so this method is ignored and does nothing.
85       * </p>
86       * 
87       * @see java.sql.Statement#setFetchSize(int)
88       */
89      @Override
90      public void setFetchSize( int rows ) throws SQLException {
91          notClosed();
92      }
93  
94      /**
95       * {@inheritDoc}
96       * 
97       * @see java.sql.Statement#getFetchDirection()
98       */
99      @Override
100     public int getFetchDirection() throws SQLException {
101         notClosed();
102         return fetchDirection;
103     }
104 
105     /**
106      * {@inheritDoc}
107      * 
108      * @see java.sql.Statement#setFetchDirection(int)
109      */
110     @Override
111     public void setFetchDirection( int direction ) throws SQLException {
112         notClosed();
113         if (direction != ResultSet.FETCH_FORWARD && direction != ResultSet.FETCH_REVERSE && direction != ResultSet.FETCH_UNKNOWN) {
114             throw new SQLException(JdbcI18n.invalidArgument.text(direction, "" + ResultSet.FETCH_FORWARD + ", "
115                                                                             + ResultSet.FETCH_REVERSE + ", "
116                                                                             + ResultSet.FETCH_UNKNOWN));
117         }
118         fetchDirection = direction;
119     }
120 
121     /**
122      * {@inheritDoc}
123      * <p>
124      * This driver does not support limiting the field size, and always returns 0.
125      * </p>
126      * 
127      * @see java.sql.Statement#getMaxFieldSize()
128      */
129     @Override
130     public int getMaxFieldSize() throws SQLException {
131         notClosed();
132         return 0;
133     }
134 
135     /**
136      * {@inheritDoc}
137      * 
138      * @see java.sql.Statement#setMaxFieldSize(int)
139      */
140     @Override
141     public void setMaxFieldSize( int max ) throws SQLException {
142         notClosed();
143         if (max < 0) {
144             throw new SQLException(JdbcI18n.argumentMayNotBeNegative.text("max", max));
145         }
146         // ignored otherwise
147     }
148 
149     /**
150      * {@inheritDoc}
151      * 
152      * @see java.sql.Statement#getMaxRows()
153      */
154     @Override
155     public int getMaxRows() throws SQLException {
156         notClosed();
157         // need to map ModeShapes -1 rowLimit to 0
158         // because the jdbc spec indicate maxRows must be >= 0 
159         // or an exception should be thrown.
160         return (rowLimit == -1 ? 0 : rowLimit);
161     }
162 
163     /**
164      * {@inheritDoc}
165      * 
166      * @see java.sql.Statement#setMaxRows(int)
167      */
168     @Override
169     public void setMaxRows( int max ) throws SQLException {
170         notClosed();
171         if (max < 0) {
172             throw new SQLException(JdbcI18n.argumentMayNotBeNegative.text("max", max));
173         }
174         rowLimit = max;
175     }
176 
177     /**
178      * {@inheritDoc}
179      * <p>
180      * This method returns 0 since there is currently no timeout with JCR 1.0 or JCR 2.0.
181      * </p>
182      * 
183      * @see java.sql.Statement#getQueryTimeout()
184      */
185     @Override
186     public int getQueryTimeout() throws SQLException {
187         notClosed();
188         return 0;
189     }
190 
191     /**
192      * {@inheritDoc}
193      * 
194      * @see java.sql.Statement#setQueryTimeout(int)
195      */
196     @Override
197     public void setQueryTimeout( int seconds ) throws SQLException {
198         notClosed();
199         if (seconds < 0) {
200             throw new SQLException(JdbcI18n.argumentMayNotBeNegative.text("seconds", seconds));
201         }
202         // Otherwise ignore
203     }
204 
205     /**
206      * {@inheritDoc}
207      * 
208      * @see java.sql.Statement#isPoolable()
209      */
210     @Override
211     public boolean isPoolable() throws SQLException {
212         notClosed();
213         return poolable;
214     }
215 
216     /**
217      * {@inheritDoc}
218      * 
219      * @see java.sql.Statement#setPoolable(boolean)
220      */
221     @Override
222     public void setPoolable( boolean poolable ) throws SQLException {
223         notClosed();
224         this.poolable = poolable;
225     }
226 
227     /**
228      * {@inheritDoc}
229      * 
230      * @see java.sql.Statement#getConnection()
231      */
232     @Override
233     public Connection getConnection() throws SQLException {
234         notClosed();
235         return connection;
236     }
237 
238     /**
239      * {@inheritDoc}
240      * 
241      * @see java.sql.Statement#cancel()
242      */
243     @Override
244     public void cancel() throws SQLException {
245         notClosed();
246         // Unable to cancel a JCR query ...
247     }
248 
249     /**
250      * {@inheritDoc}
251      * 
252      * @see java.sql.Statement#clearWarnings()
253      */
254     @Override
255     public void clearWarnings() throws SQLException {
256         notClosed();
257         warning = null;
258     }
259 
260     /**
261      * {@inheritDoc}
262      * 
263      * @see java.sql.Statement#getWarnings()
264      */
265     @Override
266     public SQLWarning getWarnings() throws SQLException {
267         notClosed();
268         return warning;
269     }
270 
271     /**
272      * {@inheritDoc}
273      * 
274      * @see java.sql.Statement#isClosed()
275      */
276     @Override
277     public boolean isClosed() {
278         return closed || connection.isClosed();
279     }
280 
281     /**
282      * {@inheritDoc}
283      * 
284      * @see java.sql.Statement#close()
285      */
286     @Override
287     public void close() {
288         if (!closed) {
289             closed = true;
290         }
291     }
292 
293     protected final void notClosed() throws SQLException {
294         if (isClosed()) throw new SQLException(JdbcI18n.statementIsClosed.text());
295     }
296 
297     protected final void noUpdates() throws SQLException {
298         throw new SQLException(JdbcI18n.updatesNotSupported.text());
299     }
300 
301     // ----------------------------------------------------------------------------------------------------------------
302     // Updates
303     // ----------------------------------------------------------------------------------------------------------------
304     /**
305      * {@inheritDoc}
306      * 
307      * @see java.sql.Statement#executeUpdate(java.lang.String)
308      */
309     @Override
310     public int executeUpdate( String sql ) throws SQLException {
311         notClosed();
312         noUpdates();
313         return 0;
314     }
315 
316     /**
317      * {@inheritDoc}
318      * 
319      * @see java.sql.Statement#executeUpdate(java.lang.String, int)
320      */
321     @Override
322     public int executeUpdate( String sql,
323                               int autoGeneratedKeys ) throws SQLException {
324         notClosed();
325         noUpdates();
326         return 0;
327     }
328 
329     /**
330      * {@inheritDoc}
331      * 
332      * @see java.sql.Statement#executeUpdate(java.lang.String, int[])
333      */
334     @Override
335     public int executeUpdate( String sql,
336                               int[] columnIndexes ) throws SQLException {
337         notClosed();
338         noUpdates();
339         return 0;
340     }
341 
342     /**
343      * {@inheritDoc}
344      * 
345      * @see java.sql.Statement#executeUpdate(java.lang.String, java.lang.String[])
346      */
347     @Override
348     public int executeUpdate( String sql,
349                               String[] columnNames ) throws SQLException {
350         notClosed();
351         noUpdates();
352         return 0;
353     }
354 
355     /**
356      * {@inheritDoc}
357      * 
358      * @see java.sql.Statement#setCursorName(java.lang.String)
359      */
360     @Override
361     public void setCursorName( String name ) throws SQLException {
362         notClosed();
363         noUpdates();
364     }
365 
366     /**
367      * {@inheritDoc}
368      * 
369      * @see java.sql.Statement#getUpdateCount()
370      */
371     @Override
372     public int getUpdateCount() throws SQLException {
373         notClosed();
374         return -1;
375     }
376 
377     /**
378      * {@inheritDoc}
379      * 
380      * @see java.sql.Statement#addBatch(java.lang.String)
381      */
382     @Override
383     public void addBatch( String sql ) throws SQLException {
384         notClosed();
385         noUpdates();
386     }
387 
388     /**
389      * {@inheritDoc}
390      * 
391      * @see java.sql.Statement#clearBatch()
392      */
393     @Override
394     public void clearBatch() throws SQLException {
395         notClosed();
396         noUpdates();
397     }
398 
399     /**
400      * {@inheritDoc}
401      * 
402      * @see java.sql.Statement#executeBatch()
403      */
404     @Override
405     public int[] executeBatch() throws SQLException {
406         notClosed();
407         noUpdates();
408         return null;
409     }
410 
411     // ----------------------------------------------------------------------------------------------------------------
412     // Queries
413     // ----------------------------------------------------------------------------------------------------------------
414 
415     /**
416      * {@inheritDoc}
417      * 
418      * @see java.sql.Statement#execute(java.lang.String)
419      */
420     @Override
421     public boolean execute( String sql ) throws SQLException {
422         notClosed();
423         warning = null;
424         moreResults = 0;
425         try {
426             // Convert the supplied SQL into JCR-SQL2 ...
427             String jcrSql2 = connection.nativeSQL(sql);
428             // Create the query ...
429             jcrResults = getJcrRepositoryDelegate().execute(jcrSql2, this.sqlLanguage);
430             results = new JcrResultSet(this, jcrResults, null);
431             moreResults = 1;
432         } catch (RepositoryException e) {
433             throw new SQLException(e.getLocalizedMessage(), e);
434         }
435         return true; // always a ResultSet
436     }
437     
438     protected RepositoryDelegate getJcrRepositoryDelegate() {
439     	return this.connection.getRepositoryDelegate();
440     }
441     
442     
443 
444     /**
445      * {@inheritDoc}
446      * 
447      * @see java.sql.Statement#execute(java.lang.String, int)
448      */
449     @Override
450     public boolean execute( String sql,
451                             int autoGeneratedKeys ) throws SQLException {
452         return execute(sql); // ignore the autoGeneratedKeys because we don't support INSERT statements
453     }
454 
455     /**
456      * {@inheritDoc}
457      * 
458      * @see java.sql.Statement#execute(java.lang.String, int[])
459      */
460     @Override
461     public boolean execute( String sql,
462                             int[] columnIndexes ) throws SQLException {
463         return execute(sql); // ignore the columnIndexes because we don't support INSERT statements
464     }
465 
466     /**
467      * {@inheritDoc}
468      * 
469      * @see java.sql.Statement#execute(java.lang.String, java.lang.String[])
470      */
471     @Override
472     public boolean execute( String sql,
473                             String[] columnNames ) throws SQLException {
474         return execute(sql); // ignore the columnNames because we don't support INSERT statements
475     }
476 
477     /**
478      * {@inheritDoc}
479      * 
480      * @see java.sql.Statement#executeQuery(java.lang.String)
481      */
482     @Override
483     public ResultSet executeQuery( String sql ) throws SQLException {
484         execute(sql);
485         return getResultSet();
486     }
487 
488     /**
489      * {@inheritDoc}
490      * 
491      * @see java.sql.Statement#getGeneratedKeys()
492      */
493     @Override
494     public ResultSet getGeneratedKeys() throws SQLException {
495     // TODO:  if and when ModeShape supports providing key information
496     //		then a result set containing the metadata will need to be created.
497         return new JcrResultSet();
498     }
499 
500     /**
501      * {@inheritDoc}
502      * 
503      * @see java.sql.Statement#getMoreResults()
504      */
505     @Override
506     public boolean getMoreResults() throws SQLException {
507         notClosed();
508         return moreResults > 0;
509     }
510 
511     /**
512      * {@inheritDoc}
513      * 
514      * @see java.sql.Statement#getMoreResults(int)
515      */
516     @Override
517     public boolean getMoreResults( int current ) throws SQLException {
518         notClosed();
519         if (current != CLOSE_ALL_RESULTS && current != CLOSE_CURRENT_RESULT && current != KEEP_CURRENT_RESULT) {
520             throw new SQLException(JdbcI18n.invalidArgument.text(current, "" + CLOSE_ALL_RESULTS + ", " + CLOSE_CURRENT_RESULT
521                                                                           + ", " + KEEP_CURRENT_RESULT));
522         }
523         if (KEEP_CURRENT_RESULT != current) {
524             // Close (by nulling) the results ...
525             jcrResults = null;
526             results = null;
527         }
528         if (moreResults > 0) --moreResults;
529         return moreResults > 0;
530     }
531 
532     /**
533      * {@inheritDoc}
534      * 
535      * @see java.sql.Statement#getResultSet()
536      */
537     @Override
538     public ResultSet getResultSet() throws SQLException {
539         notClosed();
540         return results; // may be null
541     }
542 
543     /**
544      * {@inheritDoc}
545      * 
546      * @see java.sql.Statement#getResultSetConcurrency()
547      */
548     @Override
549     public int getResultSetConcurrency() throws SQLException {
550         notClosed();
551         return ResultSet.CONCUR_READ_ONLY;
552     }
553 
554     /**
555      * {@inheritDoc}
556      * 
557      * @see java.sql.Statement#getResultSetHoldability()
558      */
559     @Override
560     public int getResultSetHoldability() throws SQLException {
561         notClosed();
562         return ResultSet.CLOSE_CURSORS_AT_COMMIT;
563     }
564 
565     /**
566      * {@inheritDoc}
567      * 
568      * @see java.sql.Statement#getResultSetType()
569      */
570     @Override
571     public int getResultSetType() throws SQLException {
572         notClosed();
573         return ResultSet.TYPE_SCROLL_INSENSITIVE;
574     }
575 
576     /**
577      * {@inheritDoc}
578      * 
579      * @see java.sql.Statement#setEscapeProcessing(boolean)
580      */
581     @Override
582     public void setEscapeProcessing( boolean enable ) throws SQLException {
583         notClosed();
584         // Ignore for now
585     }
586 
587     /**
588      * {@inheritDoc}
589      * 
590      * @see java.sql.Wrapper#isWrapperFor(java.lang.Class)
591      */
592     @Override
593     public boolean isWrapperFor( Class<?> iface ) /*throws SQLException*/{
594         return iface.isInstance(this) ;
595     }
596 
597     /**
598      * {@inheritDoc}
599      * 
600      * @see java.sql.Wrapper#unwrap(java.lang.Class)
601      */
602     @Override
603     public <T> T unwrap( Class<T> iface ) throws SQLException {
604     	if (!isWrapperFor(iface)) {
605     	       throw new SQLException(JdbcI18n.classDoesNotImplementInterface.text(Statement.class.getSimpleName(),
606                        iface.getName()));
607     	}
608     	
609     	return iface.cast(this);
610      }
611 
612 }