1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 package com.sun.star.comp.sdbc;
22 
23 import java.io.ByteArrayInputStream;
24 import java.io.InputStreamReader;
25 import java.io.UnsupportedEncodingException;
26 import java.math.BigDecimal;
27 
28 import org.apache.openoffice.comp.sdbc.dbtools.util.DBTypeConversion;
29 import org.apache.openoffice.comp.sdbc.dbtools.util.DbTools;
30 import org.apache.openoffice.comp.sdbc.dbtools.util.ORowSetValue;
31 import org.apache.openoffice.comp.sdbc.dbtools.util.Resources;
32 import org.apache.openoffice.comp.sdbc.dbtools.util.SharedResources;
33 import org.apache.openoffice.comp.sdbc.dbtools.util.StandardSQLState;
34 
35 import com.sun.star.io.IOException;
36 import com.sun.star.io.XInputStream;
37 import com.sun.star.lang.IllegalArgumentException;
38 import com.sun.star.lang.XServiceInfo;
39 import com.sun.star.logging.LogLevel;
40 import com.sun.star.sdbc.DataType;
41 import com.sun.star.sdbc.SQLException;
42 import com.sun.star.sdbc.XArray;
43 import com.sun.star.sdbc.XBlob;
44 import com.sun.star.sdbc.XClob;
45 import com.sun.star.sdbc.XConnection;
46 import com.sun.star.sdbc.XParameters;
47 import com.sun.star.sdbc.XPreparedBatchExecution;
48 import com.sun.star.sdbc.XPreparedStatement;
49 import com.sun.star.sdbc.XRef;
50 import com.sun.star.sdbc.XResultSet;
51 import com.sun.star.sdbc.XResultSetMetaData;
52 import com.sun.star.sdbc.XResultSetMetaDataSupplier;
53 import com.sun.star.uno.Any;
54 import com.sun.star.uno.AnyConverter;
55 import com.sun.star.util.Date;
56 import com.sun.star.util.DateTime;
57 import com.sun.star.util.Time;
58 
59 public class JavaSQLPreparedStatement extends JavaSQLStatementBase
60         implements XPreparedStatement, XResultSetMetaDataSupplier, XParameters, XPreparedBatchExecution, XServiceInfo {
61 
62     private static final String[] services = {
63             "com.sun.star.sdbc.PreparedStatement"
64     };
65 
JavaSQLPreparedStatement(JavaSQLConnection connection, String sqlStatement)66     public JavaSQLPreparedStatement(JavaSQLConnection connection, String sqlStatement) {
67         super(connection);
68         this.sqlStatement = sqlStatement;
69     }
70 
71     @Override
createStatement()72     protected void createStatement() throws SQLException {
73         checkDisposed();
74         if (jdbcStatement == null) {
75             try {
76                 try {
77                     jdbcStatement = connection.getJDBCConnection().prepareStatement(
78                             sqlStatement, resultSetType, resultSetConcurrency);
79                 } catch (NoSuchMethodError noSuchMethodError) {
80                     jdbcStatement = connection.getJDBCConnection().prepareStatement(sqlStatement);
81                 }
82             } catch (java.sql.SQLException sqlException) {
83                 throw Tools.toUnoExceptionLogged(this, logger, sqlException);
84             }
85         }
86     }
87 
88     // XServiceInfo
89 
90     @Override
getImplementationName()91     public String getImplementationName() {
92         return "com.sun.star.sdbcx.JPreparedStatement";
93     }
94 
95     @Override
getSupportedServiceNames()96     public String[] getSupportedServiceNames() {
97         return services.clone();
98     }
99 
100     @Override
supportsService(String serviceName)101     public boolean supportsService(String serviceName) {
102         for (String service : services) {
103             if (service.equals(serviceName)) {
104                 return true;
105             }
106         }
107         return false;
108     }
109 
110     // XPreparedStatement
111 
112     @Override
execute()113     public synchronized boolean execute() throws SQLException {
114         createStatement();
115         logger.log(LogLevel.FINE, Resources.STR_LOG_EXECUTING_PREPARED);
116         try {
117             return ((java.sql.PreparedStatement)jdbcStatement).execute();
118         } catch (java.sql.SQLException sqlException) {
119             throw Tools.toUnoExceptionLogged(this, logger, sqlException);
120         }
121     }
122 
123     @Override
executeUpdate()124     public synchronized int executeUpdate() throws SQLException {
125         createStatement();
126         logger.log(LogLevel.FINE, Resources.STR_LOG_EXECUTING_PREPARED_UPDATE);
127         try {
128             return ((java.sql.PreparedStatement)jdbcStatement).executeUpdate();
129         } catch (java.sql.SQLException sqlException) {
130             throw Tools.toUnoExceptionLogged(this, logger, sqlException);
131         }
132     }
133 
134     @Override
executeQuery()135     public synchronized XResultSet executeQuery() throws SQLException {
136         createStatement();
137         logger.log(LogLevel.FINE, Resources.STR_LOG_EXECUTING_PREPARED_QUERY);
138         try {
139             java.sql.ResultSet jdbcResults = ((java.sql.PreparedStatement)jdbcStatement).executeQuery();
140             if (jdbcResults != null) {
141                 return new JavaSQLResultSet(jdbcResults, connection);
142             }
143             return null;
144         } catch (java.sql.SQLException sqlException) {
145             throw Tools.toUnoExceptionLogged(this, logger, sqlException);
146         }
147     }
148 
149     @Override
getConnection()150     public XConnection getConnection() throws SQLException {
151         return connection;
152     }
153 
154     // XParameters
155 
156     @Override
setArray(int index, XArray x)157     public synchronized void setArray(int index, XArray x) throws SQLException {
158         String error = SharedResources.getInstance().getResourceStringWithSubstitution(
159                 Resources.STR_UNSUPPORTED_FEATURE, "$featurename$", "XParameters::setArray");
160         throw new SQLException(error, this, StandardSQLState.SQL_FEATURE_NOT_IMPLEMENTED.text(), 0, Any.VOID);
161     }
162 
163     @Override
setBinaryStream(int index, XInputStream x, int length)164     public synchronized void setBinaryStream(int index, XInputStream x, int length) throws SQLException {
165         logger.log(LogLevel.FINER, Resources.STR_LOG_BINARYSTREAM_PARAMETER, index);
166         createStatement();
167         try {
168             // FIXME: why did the C++ implementation copy the stream here? It's a huge waste of memory.
169             byte[][] bytesReference = new byte[1][0];
170             int bytesRead = x.readBytes(bytesReference, length);
171             ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytesReference[0], 0, bytesRead);
172             ((java.sql.PreparedStatement)jdbcStatement).setBinaryStream(index, byteArrayInputStream, bytesRead);
173         } catch (java.sql.SQLException | IOException exception) {
174             throw Tools.toUnoExceptionLogged(this, logger, exception);
175         }
176     }
177 
178     @Override
setBlob(int index, XBlob x)179     public synchronized void setBlob(int index, XBlob x) throws SQLException {
180         String error = SharedResources.getInstance().getResourceStringWithSubstitution(
181                 Resources.STR_UNSUPPORTED_FEATURE, "$featurename$", "XParameters::setBlob");
182         throw new SQLException(error, this, StandardSQLState.SQL_FEATURE_NOT_IMPLEMENTED.text(), 0, Any.VOID);
183     }
184 
185     @Override
setBoolean(int index, boolean x)186     public synchronized void setBoolean(int index, boolean x) throws SQLException {
187         createStatement();
188         logger.log(LogLevel.FINE, Resources.STR_LOG_BOOLEAN_PARAMETER, index, x);
189         try {
190             ((java.sql.PreparedStatement)jdbcStatement).setBoolean(index, x);
191         } catch (java.sql.SQLException sqlException) {
192             throw Tools.toUnoExceptionLogged(this, logger, sqlException);
193         }
194     }
195 
196     @Override
setByte(int index, byte x)197     public synchronized void setByte(int index, byte x) throws SQLException {
198         createStatement();
199         logger.log(LogLevel.FINE, Resources.STR_LOG_BYTE_PARAMETER, index, x);
200         try {
201             ((java.sql.PreparedStatement)jdbcStatement).setByte(index, x);
202         } catch (java.sql.SQLException sqlException) {
203             throw Tools.toUnoExceptionLogged(this, logger, sqlException);
204         }
205     }
206 
207     @Override
setBytes(int index, byte[] x)208     public synchronized void setBytes(int index, byte[] x) throws SQLException {
209         createStatement();
210         logger.log(LogLevel.FINE, Resources.STR_LOG_BYTES_PARAMETER, index);
211         try {
212             ((java.sql.PreparedStatement)jdbcStatement).setBytes(index, x.clone());
213         } catch (java.sql.SQLException sqlException) {
214             throw Tools.toUnoExceptionLogged(this, logger, sqlException);
215         }
216     }
217 
218     @Override
setCharacterStream(int index, XInputStream x, int length)219     public synchronized void setCharacterStream(int index, XInputStream x, int length) throws SQLException {
220         createStatement();
221         logger.log(LogLevel.FINER, Resources.STR_LOG_CHARSTREAM_PARAMETER, index);
222         try {
223             // FIXME: why did the C++ implementation copy the stream here? It's a huge waste of memory.
224             byte[][] bytesReference = new byte[1][0];
225             int bytesRead = x.readBytes(bytesReference, length);
226             ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytesReference[0], 0, bytesRead);
227             InputStreamReader inputStreamReader = new InputStreamReader(byteArrayInputStream, "UTF-16LE");
228             ((java.sql.PreparedStatement)jdbcStatement).setCharacterStream(index, inputStreamReader, length);
229         } catch (java.sql.SQLException | IOException | UnsupportedEncodingException exception) {
230             throw Tools.toUnoExceptionLogged(this, logger, exception);
231         }
232     }
233 
234     @Override
setClob(int index, XClob x)235     public synchronized void setClob(int index, XClob x) throws SQLException {
236         String error = SharedResources.getInstance().getResourceStringWithSubstitution(
237                 Resources.STR_UNSUPPORTED_FEATURE, "$featurename$", "XParameters::setClob");
238         throw new SQLException(error, this, StandardSQLState.SQL_FEATURE_NOT_IMPLEMENTED.text(), 0, Any.VOID);
239     }
240 
241     @Override
setDate(int index, Date x)242     public synchronized void setDate(int index, Date x) throws SQLException {
243         createStatement();
244         logger.log(LogLevel.FINE, Resources.STR_LOG_DATE_PARAMETER, index, x);
245         try {
246             ((java.sql.PreparedStatement)jdbcStatement).setDate(
247                     index, java.sql.Date.valueOf(DBTypeConversion.toDateString(x)));
248         } catch (java.sql.SQLException sqlException) {
249             throw Tools.toUnoExceptionLogged(this, logger, sqlException);
250         }
251     }
252 
253     @Override
setDouble(int index, double x)254     public synchronized void setDouble(int index, double x) throws SQLException {
255         createStatement();
256         logger.log(LogLevel.FINE, Resources.STR_LOG_DOUBLE_PARAMETER, index, x);
257         try {
258             ((java.sql.PreparedStatement)jdbcStatement).setDouble(index, x);
259         } catch (java.sql.SQLException sqlException) {
260             throw Tools.toUnoExceptionLogged(this, logger, sqlException);
261         }
262     }
263 
264     @Override
setFloat(int index, float x)265     public synchronized void setFloat(int index, float x) throws SQLException {
266         createStatement();
267         logger.log(LogLevel.FINE, Resources.STR_LOG_FLOAT_PARAMETER, index, x);
268         try {
269             ((java.sql.PreparedStatement)jdbcStatement).setFloat(index, x);
270         } catch (java.sql.SQLException sqlException) {
271             throw Tools.toUnoExceptionLogged(this, logger, sqlException);
272         }
273     }
274 
275     @Override
setInt(int index, int x)276     public synchronized void setInt(int index, int x) throws SQLException {
277         createStatement();
278         logger.log(LogLevel.FINE, Resources.STR_LOG_INT_PARAMETER, index, x);
279         try {
280             ((java.sql.PreparedStatement)jdbcStatement).setInt(index, x);
281         } catch (java.sql.SQLException sqlException) {
282             throw Tools.toUnoExceptionLogged(this, logger, sqlException);
283         }
284     }
285 
286     @Override
setLong(int index, long x)287     public synchronized void setLong(int index, long x) throws SQLException {
288         createStatement();
289         logger.log(LogLevel.FINE, Resources.STR_LOG_LONG_PARAMETER, index, x);
290         try {
291             ((java.sql.PreparedStatement)jdbcStatement).setLong(index, x);
292         } catch (java.sql.SQLException sqlException) {
293             throw Tools.toUnoExceptionLogged(this, logger, sqlException);
294         }
295     }
296 
297     @Override
setNull(int index, int sqlType)298     public synchronized void setNull(int index, int sqlType) throws SQLException {
299         createStatement();
300         logger.log(LogLevel.FINE, Resources.STR_LOG_NULL_PARAMETER, index, sqlType);
301         try {
302             ((java.sql.PreparedStatement)jdbcStatement).setNull(index, sqlType);
303         } catch (java.sql.SQLException sqlException) {
304             throw Tools.toUnoExceptionLogged(this, logger, sqlException);
305         }
306     }
307 
308     @Override
setObject(int index, Object x)309     public void setObject(int index, Object x) throws SQLException {
310         if (!DbTools.setObject(this, index, x)) {
311             String error = SharedResources.getInstance().getResourceStringWithSubstitution(
312                     Resources.STR_UNKNOWN_PARA_TYPE, "$position$", Integer.toString(index));
313             throw new SQLException(error, this, StandardSQLState.SQL_GENERAL_ERROR.text(), 0, Any.VOID);
314         }
315     }
316 
317     @Override
setObjectNull(int index, int sqlType, String typeName)318     public synchronized void setObjectNull(int index, int sqlType, String typeName) throws SQLException {
319         createStatement();
320         logger.log(LogLevel.FINER, Resources.STR_LOG_OBJECT_NULL_PARAMETER, index);
321         try {
322             ((java.sql.PreparedStatement)jdbcStatement).setObject(index, null);
323         } catch (java.sql.SQLException sqlException) {
324             throw Tools.toUnoExceptionLogged(this, logger, sqlException);
325         }
326     }
327 
328     @Override
setObjectWithInfo(int index, Object x, int targetSqlType, int scale)329     public synchronized void setObjectWithInfo(int index, Object x, int targetSqlType, int scale) throws SQLException {
330         createStatement();
331         logger.log(LogLevel.FINER, Resources.STR_LOG_OBJECT_NULL_PARAMETER, index);
332         try {
333             if (targetSqlType == DataType.DECIMAL || targetSqlType == DataType.NUMERIC) {
334                 BigDecimal bigDecimal;
335                 if (AnyConverter.isDouble(x)) {
336                     bigDecimal = new BigDecimal(AnyConverter.toDouble(x));
337                 } else {
338                     ORowSetValue rowSetValue = new ORowSetValue();
339                     rowSetValue.fill(x);
340                     String value = rowSetValue.toString();
341                     if (value.isEmpty()) {
342                         bigDecimal = new BigDecimal(0.0);
343                     } else {
344                         bigDecimal = new BigDecimal(value);
345                     }
346                 }
347                 ((java.sql.PreparedStatement)jdbcStatement).setObject(index, bigDecimal, targetSqlType, scale);
348             } else {
349                 ((java.sql.PreparedStatement)jdbcStatement).setObject(index, AnyConverter.toString(x), targetSqlType, scale);
350             }
351         } catch (java.sql.SQLException | IllegalArgumentException exception) {
352             throw Tools.toUnoExceptionLogged(this, logger, exception);
353         }
354     }
355 
356     @Override
setRef(int index, XRef x)357     public void setRef(int index, XRef x) throws SQLException {
358         String error = SharedResources.getInstance().getResourceStringWithSubstitution(
359                 Resources.STR_UNSUPPORTED_FEATURE, "$featurename$", "XParameters::setRef");
360         throw new SQLException(error, this, StandardSQLState.SQL_FEATURE_NOT_IMPLEMENTED.text(), 0, Any.VOID);
361     }
362 
363     @Override
setShort(int index, short x)364     public synchronized void setShort(int index, short x) throws SQLException {
365         createStatement();
366         logger.log(LogLevel.FINE, Resources.STR_LOG_SHORT_PARAMETER, index, x);
367         try {
368             ((java.sql.PreparedStatement)jdbcStatement).setShort(index, x);
369         } catch (java.sql.SQLException sqlException) {
370             throw Tools.toUnoExceptionLogged(this, logger, sqlException);
371         }
372     }
373 
374     @Override
setString(int index, String x)375     public synchronized void setString(int index, String x) throws SQLException {
376         createStatement();
377         logger.log(LogLevel.FINE, Resources.STR_LOG_STRING_PARAMETER, index, x);
378         try {
379             ((java.sql.PreparedStatement)jdbcStatement).setString(index, x);
380         } catch (java.sql.SQLException sqlException) {
381             throw Tools.toUnoExceptionLogged(this, logger, sqlException);
382         }
383     }
384 
385     @Override
setTime(int index, Time x)386     public synchronized void setTime(int index, Time x) throws SQLException {
387         createStatement();
388         logger.log(LogLevel.FINE, Resources.STR_LOG_TIME_PARAMETER, index, x);
389         try {
390             ((java.sql.PreparedStatement)jdbcStatement).setTime(
391                     index, java.sql.Time.valueOf(DBTypeConversion.toTimeString(x)));
392         } catch (java.sql.SQLException sqlException) {
393             throw Tools.toUnoExceptionLogged(this, logger, sqlException);
394         }
395     }
396 
397     @Override
setTimestamp(int index, DateTime x)398     public synchronized void setTimestamp(int index, DateTime x) throws SQLException {
399         createStatement();
400         logger.log(LogLevel.FINE, Resources.STR_LOG_TIMESTAMP_PARAMETER, index, x);
401         try {
402             ((java.sql.PreparedStatement)jdbcStatement).setTimestamp(
403                     index, java.sql.Timestamp.valueOf(DBTypeConversion.toDateTimeString(x)));
404         } catch (java.sql.SQLException sqlException) {
405             throw Tools.toUnoExceptionLogged(this, logger, sqlException);
406         }
407     }
408 
409     @Override
clearParameters()410     public synchronized void clearParameters() throws SQLException {
411         createStatement();
412         logger.log(LogLevel.FINE, Resources.STR_LOG_CLEAR_PARAMETERS);
413         try {
414             ((java.sql.PreparedStatement)jdbcStatement).clearParameters();
415         } catch (java.sql.SQLException sqlException) {
416             throw Tools.toUnoExceptionLogged(this, logger, sqlException);
417         }
418     }
419 
420     // XPreparedBatchExecution
421 
422     @Override
clearBatch()423     public synchronized void clearBatch() throws SQLException {
424         createStatement();
425         try {
426             ((java.sql.PreparedStatement)jdbcStatement).clearBatch();
427         } catch (java.sql.SQLException sqlException) {
428             throw Tools.toUnoExceptionLogged(this, logger, sqlException);
429         }
430     }
431 
432     @Override
addBatch()433     public synchronized void addBatch() throws SQLException {
434         createStatement();
435         try {
436             ((java.sql.PreparedStatement)jdbcStatement).addBatch();
437         } catch (java.sql.SQLException sqlException) {
438             throw Tools.toUnoExceptionLogged(this, logger, sqlException);
439         }
440     }
441 
442     @Override
executeBatch()443     public synchronized int[] executeBatch() throws SQLException {
444         createStatement();
445         try {
446             return ((java.sql.PreparedStatement)jdbcStatement).executeBatch();
447         } catch (java.sql.SQLException sqlException) {
448             throw Tools.toUnoExceptionLogged(this, logger, sqlException);
449         }
450     }
451 
452     // XResultSetMetaDataSupplier
453 
454     @Override
getMetaData()455     public synchronized XResultSetMetaData getMetaData() throws SQLException {
456         createStatement();
457         try {
458             java.sql.ResultSetMetaData jdbcMetadata = ((java.sql.PreparedStatement)jdbcStatement).getMetaData();
459             if (jdbcMetadata != null) {
460                 return new JavaSQLResultSetMetaData(connection, jdbcMetadata);
461             } else {
462                 return null;
463             }
464         } catch (java.sql.SQLException sqlException) {
465             throw Tools.toUnoExceptionLogged(this, logger, sqlException);
466         }
467     }
468 }
469