1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * Effective License of whole file:
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License version 2.1, as published by the Free Software Foundation.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20 * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011:
22 * The Contents of this file are made available subject to the terms of
23 * the GNU Lesser General Public License Version 2.1
25 * Copyright: 2000 by Sun Microsystems, Inc.
27 * Contributor(s): Joerg Budischewski
29 * All parts contributed on or after August 2011:
31 * This Source Code Form is subject to the terms of the Mozilla Public
32 * License, v. 2.0. If a copy of the MPL was not distributed with this
33 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
35 ************************************************************************/
37 #include <sal/log.hxx>
38 #include "pq_preparedstatement.hxx"
39 #include "pq_tools.hxx"
40 #include "pq_statics.hxx"
41 #include "pq_statement.hxx"
43 #include <o3tl/deleter.hxx>
44 #include <rtl/strbuf.hxx>
45 #include <rtl/ustrbuf.hxx>
48 #include <comphelper/sequence.hxx>
50 #include <com/sun/star/sdbc/DataType.hpp>
51 #include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
52 #include <com/sun/star/sdbc/ResultSetType.hpp>
53 #include <com/sun/star/sdbc/SQLException.hpp>
57 #include <string_view>
59 #include <connectivity/dbconversion.hxx>
61 using osl::MutexGuard
;
64 using com::sun::star::uno::Any
;
65 using com::sun::star::uno::Type
;
66 using com::sun::star::uno::Sequence
;
67 using com::sun::star::uno::Reference
;
68 using com::sun::star::uno::UNO_QUERY
;
70 using com::sun::star::lang::IllegalArgumentException
;
72 using com::sun::star::sdbc::XCloseable
;
73 using com::sun::star::sdbc::XResultSet
;
74 using com::sun::star::sdbc::XRef
;
75 using com::sun::star::sdbc::XBlob
;
76 using com::sun::star::sdbc::XClob
;
77 using com::sun::star::sdbc::XArray
;
78 using com::sun::star::sdbc::XConnection
;
79 using com::sun::star::sdbc::SQLException
;
81 using com::sun::star::beans::Property
;
82 using com::sun::star::beans::XPropertySetInfo
;
84 using namespace dbtools
;
86 namespace pq_sdbc_driver
88 static ::cppu::IPropertyArrayHelper
& getPreparedStatementPropertyArrayHelper()
90 static ::cppu::OPropertyArrayHelper
arrayHelper(
94 ::cppu::UnoType
<OUString
>::get() , 0 ),
96 "EscapeProcessing", 1,
97 cppu::UnoType
<bool>::get() , 0 ),
100 ::cppu::UnoType
<sal_Int32
>::get() , 0 ),
103 ::cppu::UnoType
<sal_Int32
>::get() , 0 ),
106 ::cppu::UnoType
<sal_Int32
>::get() , 0 ),
109 ::cppu::UnoType
<sal_Int32
>::get() , 0 ),
112 ::cppu::UnoType
<sal_Int32
>::get() , 0 ),
114 "ResultSetConcurrency", 7,
115 ::cppu::UnoType
<sal_Int32
>::get() , 0 ),
118 ::cppu::UnoType
<sal_Int32
>::get() , 0 )},
120 static ::cppu::IPropertyArrayHelper
*pArrayHelper
= &arrayHelper
;
122 return *pArrayHelper
;
125 static bool isOperator( char c
)
127 static const char * const operators
= "<>=()!/&%.,;";
129 const char * w
= operators
;
130 while (*w
&& *w
!= c
)
137 static bool isNamedParameterStart( std::string_view o
, int index
)
139 return o
[index
] == ':' && (
140 isWhitespace( o
[index
-1] ) || isOperator(o
[index
-1]) );
143 static bool isQuoted( std::string_view str
)
145 return str
[0] == '"' || str
[0] == '\'';
148 PreparedStatement::PreparedStatement(
149 const ::rtl::Reference
< comphelper::RefCountedMutex
> & refMutex
,
150 const Reference
< XConnection
> & conn
,
151 struct ConnectionSettings
*pSettings
,
152 const OString
& stmt
)
153 : PreparedStatement_BASE(refMutex
->GetMutex())
154 , OPropertySetHelper(PreparedStatement_BASE::rBHelper
)
156 , m_pSettings(pSettings
)
159 , m_multipleResultAvailable(false)
160 , m_multipleResultUpdateCount(0)
161 , m_lastOidInserted( InvalidOid
)
163 m_props
[PREPARED_STATEMENT_QUERY_TIME_OUT
] <<= sal_Int32(0);
164 m_props
[PREPARED_STATEMENT_MAX_ROWS
] <<= sal_Int32(0);
165 m_props
[PREPARED_STATEMENT_RESULT_SET_CONCURRENCY
] <<=
166 css::sdbc::ResultSetConcurrency::READ_ONLY
;
167 m_props
[PREPARED_STATEMENT_RESULT_SET_TYPE
] <<=
168 css::sdbc::ResultSetType::SCROLL_INSENSITIVE
;
170 splitSQL( m_stmt
, m_splittedStatement
);
172 for(const OString
& str
: m_splittedStatement
)
174 // ignore quoted strings...
175 if( ! isQuoted( str
) )
177 // the ':' cannot be the first or the last part of the
179 // the ? cannot be the first part of the token , so we start
181 for( int index
= 1 ; index
< str
.getLength() ; index
++ )
183 if( str
[index
] == '?' ||
184 isNamedParameterStart( str
, index
)
192 m_vars
= std::vector
< OString
>( elements
);
195 PreparedStatement::~PreparedStatement()
199 void PreparedStatement::checkColumnIndex( sal_Int32 parameterIndex
)
201 if( parameterIndex
< 1 || parameterIndex
> static_cast<sal_Int32
>(m_vars
.size()) )
204 "pq_preparedstatement: parameter index out of range (expected 1 to "
205 + OUString::number( m_vars
.size() )
206 + ", got " + OUString::number( parameterIndex
)
207 + ", statement '" + OStringToOUString( m_stmt
, ConnectionSettings::encoding
)
209 *this, OUString(), 1, Any () );
212 void PreparedStatement::checkClosed()
214 if( ! m_pSettings
|| ! m_pSettings
->pConnection
)
216 "pq_driver: PreparedStatement or connection has already been closed !",
217 *this, OUString(),1,Any());
220 Any
PreparedStatement::queryInterface( const Type
& rType
)
222 Any aRet
= PreparedStatement_BASE::queryInterface(rType
);
223 return aRet
.hasValue() ? aRet
: OPropertySetHelper::queryInterface(rType
);
227 Sequence
< Type
> PreparedStatement::getTypes()
229 static Sequence
< Type
> collection(
230 ::comphelper::concatSequences(
231 OPropertySetHelper::getTypes(),
232 PreparedStatement_BASE::getTypes()));
237 Sequence
< sal_Int8
> PreparedStatement::getImplementationId()
239 return css::uno::Sequence
<sal_Int8
>();
242 void PreparedStatement::close( )
244 // let the connection die without acquired mutex !
245 Reference
< XConnection
> r
;
246 Reference
< XCloseable
> resultSet
;
248 MutexGuard
guard( m_xMutex
->GetMutex() );
249 m_pSettings
= nullptr;
251 m_connection
.clear();
253 resultSet
= m_lastResultset
;
254 m_lastResultset
.clear();
262 void PreparedStatement::raiseSQLException( const char * errorMsg
)
264 OUStringBuffer
buf(128);
265 buf
.append( "pq_driver: ");
267 OUString( errorMsg
, strlen(errorMsg
) , ConnectionSettings::encoding
) );
268 buf
.append( " (caused by statement '" );
269 buf
.appendAscii( m_executedStatement
.getStr() );
271 OUString error
= buf
.makeStringAndClear();
272 SAL_WARN("connectivity.postgresql", error
);
273 throw SQLException( error
, *this, OUString(), 1, Any() );
276 Reference
< XResultSet
> PreparedStatement::executeQuery( )
280 raiseSQLException( "not a query" );
282 return Reference
< XResultSet
> ( m_lastResultset
, css::uno::UNO_QUERY
);
285 sal_Int32
PreparedStatement::executeUpdate( )
289 raiseSQLException( "not a command" );
291 return m_multipleResultUpdateCount
;
294 sal_Bool
PreparedStatement::execute( )
296 osl::MutexGuard
guard( m_xMutex
->GetMutex() );
298 OStringBuffer
buf( m_stmt
.getLength() *2 );
300 std::vector
< OString
>::size_type vars
= 0;
301 for(const OString
& str
: m_splittedStatement
)
303 // LEM TODO: instead of this manual mucking with SQL
304 // could we use PQexecParams / PQExecPrepared / ...?
305 // Only snafu is giving the types of the parameters and
306 // that it needs $1, $2, etc instead of "?"
308 // printf( "Split %d %s\n" , i , str.getStr() );
309 if( isQuoted( str
) )
316 for( index
= 1 ; index
< str
.getLength() ; index
++ )
318 if( str
[index
] == '?' )
320 buf
.append( str
.getStr()+start
, index
- start
);
321 buf
.append( m_vars
[vars
] );
327 if ( isNamedParameterStart( str
, index
) )
329 buf
.append( str
.getStr()+start
, index
-start
);
330 buf
.append( m_vars
[vars
] );
332 // skip to the end of the named parameter
333 while ( index
< str
.getLength()
334 && !( isWhitespace(str
[index
])
335 || isOperator (str
[index
])))
344 // if( index +1 >= str.getLength() )
346 buf
.append( str
.getStr() + start
, index
-start
);
351 m_executedStatement
= buf
.makeStringAndClear();
353 Reference
< XCloseable
> lastResultSet
= m_lastResultset
;
354 if( lastResultSet
.is() )
355 lastResultSet
->close();
357 m_lastResultset
.clear();
358 m_lastTableInserted
.clear();
360 struct CommandData data
;
361 data
.refMutex
= m_xMutex
;
362 data
.ppSettings
= &m_pSettings
;
363 data
.pLastOidInserted
= &m_lastOidInserted
;
364 data
.pLastQuery
= &m_lastQuery
;
365 data
.pMultipleResultUpdateCount
= &m_multipleResultUpdateCount
;
366 data
.pMultipleResultAvailable
= &m_multipleResultAvailable
;
367 data
.pLastTableInserted
= &m_lastTableInserted
;
368 data
.pLastResultset
= &m_lastResultset
;
370 data
.tableSupplier
.set( m_connection
, UNO_QUERY
);
371 data
.concurrency
= extractIntProperty( this, getStatics().RESULT_SET_CONCURRENCY
);
373 return executePostgresCommand( m_executedStatement
, &data
); // see pq_statement.cxx
376 Reference
< XConnection
> PreparedStatement::getConnection( )
378 Reference
< XConnection
> ret
;
380 MutexGuard
guard( m_xMutex
->GetMutex() );
388 void PreparedStatement::setNull( sal_Int32 parameterIndex
, sal_Int32
)
390 MutexGuard
guard( m_xMutex
->GetMutex() );
392 checkColumnIndex( parameterIndex
);
393 m_vars
[parameterIndex
-1] = OString( "NULL" );
396 void PreparedStatement::setObjectNull(
397 sal_Int32 parameterIndex
, sal_Int32
, const OUString
& )
399 MutexGuard
guard( m_xMutex
->GetMutex() );
401 checkColumnIndex( parameterIndex
);
402 m_vars
[parameterIndex
-1] = OString( "NULL" );
406 void PreparedStatement::setBoolean( sal_Int32 parameterIndex
, sal_Bool x
)
408 MutexGuard
guard(m_xMutex
->GetMutex() );
410 checkColumnIndex( parameterIndex
);
412 m_vars
[parameterIndex
-1] = OString( "'t'" );
414 m_vars
[parameterIndex
-1] = OString( "'f'" );
417 void PreparedStatement::setByte( sal_Int32 parameterIndex
, sal_Int8 x
)
419 setInt(parameterIndex
,x
);
422 void PreparedStatement::setShort( sal_Int32 parameterIndex
, sal_Int16 x
)
424 setInt(parameterIndex
, x
);
427 void PreparedStatement::setInt( sal_Int32 parameterIndex
, sal_Int32 x
)
429 // printf( "setString %d %d\n ", parameterIndex, x);
430 MutexGuard
guard(m_xMutex
->GetMutex() );
432 checkColumnIndex( parameterIndex
);
433 m_vars
[parameterIndex
-1] = "'" + OString::number(x
) + "'";
436 void PreparedStatement::setLong( sal_Int32 parameterIndex
, sal_Int64 x
)
438 MutexGuard
guard(m_xMutex
->GetMutex() );
440 checkColumnIndex( parameterIndex
);
441 m_vars
[parameterIndex
-1] = "'" + OString::number(x
) + "'";
444 void PreparedStatement::setFloat( sal_Int32 parameterIndex
, float x
)
446 MutexGuard
guard(m_xMutex
->GetMutex() );
448 checkColumnIndex( parameterIndex
);
449 m_vars
[parameterIndex
-1] = "'" + OString::number(x
) + "'";
452 void PreparedStatement::setDouble( sal_Int32 parameterIndex
, double x
)
454 MutexGuard
guard(m_xMutex
->GetMutex() );
456 checkColumnIndex( parameterIndex
);
457 m_vars
[parameterIndex
-1] = "'" + OString::number(x
) + "'";
460 void PreparedStatement::setString( sal_Int32 parameterIndex
, const OUString
& x
)
462 // printf( "setString %d %s\n ", parameterIndex,
463 // OUStringToOString( x , RTL_TEXTENCODING_ASCII_US ).getStr());
464 MutexGuard
guard(m_xMutex
->GetMutex() );
466 checkColumnIndex( parameterIndex
);
467 OStringBuffer
buf( 20 );
469 OString y
= OUStringToOString( x
, ConnectionSettings::encoding
);
470 buf
.ensureCapacity( y
.getLength() * 2 + 2 );
471 int len
= PQescapeString( const_cast<char*>(buf
.getStr())+1, y
.getStr() , y
.getLength() );
472 buf
.setLength( 1 + len
);
474 m_vars
[parameterIndex
-1] = buf
.makeStringAndClear();
477 void PreparedStatement::setBytes(
478 sal_Int32 parameterIndex
, const Sequence
< sal_Int8
>& x
)
480 MutexGuard
guard(m_xMutex
->GetMutex() );
482 checkColumnIndex( parameterIndex
);
484 const std::unique_ptr
<unsigned char, deleter_from_fn
<PQfreemem
>> escapedString(
485 PQescapeBytea( reinterpret_cast<unsigned char const *>(x
.getConstArray()), x
.getLength(), &len
));
486 if( ! escapedString
)
489 "pq_preparedstatement.setBytes: Error during converting bytesequence to an SQL conform string",
490 *this, OUString(), 1, Any() );
492 m_vars
[parameterIndex
-1]
493 = OString::Concat("'") + std::string_view(reinterpret_cast<char *>(escapedString
.get()), len
-1) + "'";
497 void PreparedStatement::setDate( sal_Int32 parameterIndex
, const css::util::Date
& x
)
499 setString( parameterIndex
, DBTypeConversion::toDateString( x
) );
502 void PreparedStatement::setTime( sal_Int32 parameterIndex
, const css::util::Time
& x
)
504 setString( parameterIndex
, DBTypeConversion::toTimeString( x
) );
507 void PreparedStatement::setTimestamp(
508 sal_Int32 parameterIndex
, const css::util::DateTime
& x
)
510 setString( parameterIndex
, DBTypeConversion::toDateTimeString( x
) );
513 void PreparedStatement::setBinaryStream(
515 const Reference
< css::io::XInputStream
>&,
519 "pq_preparedstatement: setBinaryStream not implemented",
520 *this, OUString(), 1, Any () );
523 void PreparedStatement::setCharacterStream(
525 const Reference
< css::io::XInputStream
>&,
529 "pq_preparedstatement: setCharacterStream not implemented",
530 *this, OUString(), 1, Any () );
533 void PreparedStatement::setObject( sal_Int32 parameterIndex
, const Any
& x
)
535 if( ! implSetObject( this, parameterIndex
, x
))
538 "pq_preparedstatement::setObject: can't convert value of type " + x
.getValueTypeName(),
539 *this, OUString(), 1, Any () );
543 void PreparedStatement::setObjectWithInfo(
544 sal_Int32 parameterIndex
,
546 sal_Int32 targetSqlType
,
549 if( css::sdbc::DataType::DECIMAL
== targetSqlType
||
550 css::sdbc::DataType::NUMERIC
== targetSqlType
)
552 double myDouble
= 0.0;
556 myString
= OUString::number( myDouble
);
562 if( myString
.isEmpty() )
565 "pq_preparedstatement::setObjectWithInfo: can't convert value of type "
566 + x
.getValueTypeName() + " to type DECIMAL or NUMERIC",
567 *this, OUString(), 1, Any () );
570 setString( parameterIndex
, myString
);
574 setObject( parameterIndex
, x
);
579 void PreparedStatement::setRef(
581 const Reference
< XRef
>& )
584 "pq_preparedstatement: setRef not implemented",
585 *this, OUString(), 1, Any () );
588 void PreparedStatement::setBlob(
590 const Reference
< XBlob
>& )
593 "pq_preparedstatement: setBlob not implemented",
594 *this, OUString(), 1, Any () );
597 void PreparedStatement::setClob(
599 const Reference
< XClob
>& )
602 "pq_preparedstatement: setClob not implemented",
603 *this, OUString(), 1, Any () );
606 void PreparedStatement::setArray(
607 sal_Int32 parameterIndex
,
608 const Reference
< XArray
>& x
)
610 setString( parameterIndex
, array2String( x
->getArray( nullptr ) ) );
613 void PreparedStatement::clearParameters( )
615 MutexGuard
guard(m_xMutex
->GetMutex() );
616 m_vars
= std::vector
< OString
>( m_vars
.size() );
619 Any
PreparedStatement::getWarnings( )
624 void PreparedStatement::clearWarnings( )
628 Reference
< css::sdbc::XResultSetMetaData
> PreparedStatement::getMetaData()
630 Reference
< css::sdbc::XResultSetMetaData
> ret
;
631 Reference
< css::sdbc::XResultSetMetaDataSupplier
> supplier( m_lastResultset
, UNO_QUERY
);
633 ret
= supplier
->getMetaData();
637 ::cppu::IPropertyArrayHelper
& PreparedStatement::getInfoHelper()
639 return getPreparedStatementPropertyArrayHelper();
643 sal_Bool
PreparedStatement::convertFastPropertyValue(
644 Any
& rConvertedValue
, Any
& rOldValue
, sal_Int32 nHandle
, const Any
& rValue
)
647 rOldValue
= m_props
[nHandle
];
650 case PREPARED_STATEMENT_CURSOR_NAME
:
653 bRet
= ( rValue
>>= val
);
654 rConvertedValue
<<= val
;
657 case PREPARED_STATEMENT_ESCAPE_PROCESSING
:
660 bRet
= ( rValue
>>= val
);
661 rConvertedValue
<<= val
;
664 case PREPARED_STATEMENT_FETCH_DIRECTION
:
665 case PREPARED_STATEMENT_FETCH_SIZE
:
666 case PREPARED_STATEMENT_MAX_FIELD_SIZE
:
667 case PREPARED_STATEMENT_MAX_ROWS
:
668 case PREPARED_STATEMENT_QUERY_TIME_OUT
:
669 case PREPARED_STATEMENT_RESULT_SET_CONCURRENCY
:
670 case PREPARED_STATEMENT_RESULT_SET_TYPE
:
673 bRet
= ( rValue
>>= val
);
674 rConvertedValue
<<= val
;
679 throw IllegalArgumentException(
680 "pq_statement: Invalid property handle ("
681 + OUString::number( nHandle
) + ")",
689 void PreparedStatement::setFastPropertyValue_NoBroadcast(
690 sal_Int32 nHandle
,const Any
& rValue
)
692 m_props
[nHandle
] = rValue
;
695 void PreparedStatement::getFastPropertyValue( Any
& rValue
, sal_Int32 nHandle
) const
697 rValue
= m_props
[nHandle
];
700 Reference
< XPropertySetInfo
> PreparedStatement::getPropertySetInfo()
702 return OPropertySetHelper::createPropertySetInfo( getPreparedStatementPropertyArrayHelper() );
705 void PreparedStatement::disposing()
711 Reference
< XResultSet
> PreparedStatement::getResultSet( )
713 return Reference
< XResultSet
> ( m_lastResultset
, css::uno::UNO_QUERY
);
715 sal_Int32
PreparedStatement::getUpdateCount( )
717 return m_multipleResultUpdateCount
;
719 sal_Bool
PreparedStatement::getMoreResults( )
721 Reference
< XCloseable
> lastResultSet
= m_lastResultset
;
722 if( lastResultSet
.is() )
723 lastResultSet
->close();
724 m_multipleResultUpdateCount
= -1;
728 Reference
< XResultSet
> PreparedStatement::getGeneratedValues( )
730 osl::MutexGuard
guard( m_xMutex
->GetMutex() );
731 return getGeneratedValuesFromLastInsert(
732 m_pSettings
, m_connection
, m_lastOidInserted
, m_lastTableInserted
, m_lastQuery
);
738 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */