xref: /AOO41X/main/connectivity/source/commontools/predicateinput.cxx (revision 9b5730f6ddef7eb82608ca4d31dc0d7678e652cf)
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 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_connectivity.hxx"
26 #include <connectivity/predicateinput.hxx>
27 #include <comphelper/types.hxx>
28 #include <connectivity/dbtools.hxx>
29 #include <com/sun/star/sdbc/DataType.hpp>
30 #include <com/sun/star/sdbc/ColumnValue.hpp>
31 #include <osl/diagnose.h>
32 #include <connectivity/sqlnode.hxx>
33 #include <connectivity/PColumn.hxx>
34 #include <comphelper/numbers.hxx>
35 
36 //.........................................................................
37 namespace dbtools
38 {
39 //.........................................................................
40 
41     using ::com::sun::star::sdbc::XConnection;
42     using ::com::sun::star::lang::XMultiServiceFactory;
43     using ::com::sun::star::util::XNumberFormatsSupplier;
44     using ::com::sun::star::util::XNumberFormatter;
45     using ::com::sun::star::uno::UNO_QUERY;
46     using ::com::sun::star::beans::XPropertySet;
47     using ::com::sun::star::beans::XPropertySetInfo;
48     using ::com::sun::star::lang::Locale;
49     using ::com::sun::star::uno::Exception;
50     using ::com::sun::star::i18n::XLocaleData;
51     using ::com::sun::star::i18n::LocaleDataItem;
52 
53     using namespace ::com::sun::star::sdbc;
54     using namespace ::connectivity;
55 
56     using ::connectivity::OSQLParseNode;
57 
58     #define Reference ::com::sun::star::uno::Reference
59 
60     //=====================================================================
61     //---------------------------------------------------------------------
lcl_getSeparatorChar(const::rtl::OUString & _rSeparator,sal_Unicode _nFallback)62     static sal_Unicode lcl_getSeparatorChar( const ::rtl::OUString& _rSeparator, sal_Unicode _nFallback )
63     {
64         OSL_ENSURE( 0 < _rSeparator.getLength(), "::lcl_getSeparatorChar: invalid separator string!" );
65 
66         sal_Unicode nReturn( _nFallback );
67         if ( _rSeparator.getLength() )
68             nReturn = static_cast< sal_Char >( _rSeparator.getStr()[0] );
69         return nReturn;
70     }
71 
72     //=====================================================================
73     //= OPredicateInputController
74     //=====================================================================
75     //---------------------------------------------------------------------
getSeparatorChars(const Locale & _rLocale,sal_Unicode & _rDecSep,sal_Unicode & _rThdSep) const76     sal_Bool OPredicateInputController::getSeparatorChars( const Locale& _rLocale, sal_Unicode& _rDecSep, sal_Unicode& _rThdSep ) const
77     {
78         _rDecSep = '.';
79         _rThdSep = ',';
80         try
81         {
82             LocaleDataItem aLocaleData;
83             if ( m_xLocaleData.is() )
84             {
85                 aLocaleData = m_xLocaleData->getLocaleItem( _rLocale );
86                 _rDecSep = lcl_getSeparatorChar( aLocaleData.decimalSeparator, _rDecSep );
87                 _rThdSep = lcl_getSeparatorChar( aLocaleData.decimalSeparator, _rThdSep );
88                 return sal_True;
89             }
90         }
91         catch( const Exception& )
92         {
93             OSL_ENSURE( sal_False, "OPredicateInputController::getSeparatorChars: caught an exception!" );
94         }
95         return sal_False;
96     }
97 
98     //---------------------------------------------------------------------
OPredicateInputController(const Reference<XMultiServiceFactory> & _rxORB,const Reference<XConnection> & _rxConnection,const IParseContext * _pParseContext)99     OPredicateInputController::OPredicateInputController(
100         const Reference< XMultiServiceFactory >& _rxORB, const Reference< XConnection >& _rxConnection, const IParseContext* _pParseContext )
101         :m_xORB( _rxORB )
102         ,m_xConnection( _rxConnection )
103         ,m_aParser( m_xORB, _pParseContext )
104     {
105         try
106         {
107             // create a number formatter / number formats supplier pair
108             OSL_ENSURE( m_xORB.is(), "OPredicateInputController::OPredicateInputController: need a service factory!" );
109             if ( m_xORB.is() )
110             {
111                 m_xFormatter = Reference< XNumberFormatter >( m_xORB->createInstance(
112                     ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.util.NumberFormatter" ) ) ),
113                     UNO_QUERY
114                 );
115             }
116 
117             Reference< XNumberFormatsSupplier >  xNumberFormats = ::dbtools::getNumberFormats( m_xConnection, sal_True );
118             if ( !xNumberFormats.is() )
119                 ::comphelper::disposeComponent( m_xFormatter );
120             else if ( m_xFormatter.is() )
121                 m_xFormatter->attachNumberFormatsSupplier( xNumberFormats );
122 
123             // create the locale data
124             if ( m_xORB.is() )
125             {
126                 m_xLocaleData = m_xLocaleData.query( m_xORB->createInstance(
127                     ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.i18n.LocaleData" ) ) )
128                 );
129             }
130         }
131         catch( const Exception& )
132         {
133             OSL_ENSURE( sal_False, "OPredicateInputController::OPredicateInputController: caught an exception!" );
134         }
135     }
136 
137     //---------------------------------------------------------------------
implPredicateTree(::rtl::OUString & _rErrorMessage,const::rtl::OUString & _rStatement,const Reference<XPropertySet> & _rxField) const138     OSQLParseNode* OPredicateInputController::implPredicateTree(::rtl::OUString& _rErrorMessage, const ::rtl::OUString& _rStatement, const Reference< XPropertySet > & _rxField) const
139     {
140         OSQLParseNode* pReturn = const_cast< OSQLParser& >( m_aParser ).predicateTree( _rErrorMessage, _rStatement, m_xFormatter, _rxField );
141         if ( !pReturn )
142         {   // is it a text field ?
143             sal_Int32 nType = DataType::OTHER;
144             _rxField->getPropertyValue( ::rtl::OUString::createFromAscii( "Type" ) ) >>= nType;
145 
146             if  (   ( DataType::CHAR        == nType )
147                 ||  ( DataType::VARCHAR     == nType )
148                 ||  ( DataType::LONGVARCHAR == nType )
149                 ||  ( DataType::CLOB        == nType )
150                 )
151             {   // yes -> force a quoted text and try again
152                 ::rtl::OUString sQuoted( _rStatement );
153                 if  (   sQuoted.getLength()
154                     &&  (   (sQuoted.getStr()[0] != '\'')
155                         ||  (sQuoted.getStr()[ sQuoted.getLength() - 1 ] != '\'' )
156                         )
157                     )
158                 {
159                     static const ::rtl::OUString sSingleQuote( RTL_CONSTASCII_USTRINGPARAM( "'" ) );
160                     static const ::rtl::OUString sDoubleQuote( RTL_CONSTASCII_USTRINGPARAM( "''" ) );
161 
162                     sal_Int32 nIndex = -1;
163                     sal_Int32 nTemp = 0;
164                     while ( -1 != ( nIndex = sQuoted.indexOf( '\'',nTemp ) ) )
165                     {
166                         sQuoted = sQuoted.replaceAt( nIndex, 1, sDoubleQuote );
167                         nTemp = nIndex+2;
168                     }
169 
170                     ::rtl::OUString sTemp( sSingleQuote );
171                     ( sTemp += sQuoted ) += sSingleQuote;
172                     sQuoted = sTemp;
173                 }
174                 pReturn = const_cast< OSQLParser& >( m_aParser ).predicateTree( _rErrorMessage, sQuoted, m_xFormatter, _rxField );
175             }
176 
177             // one more fallback: for numeric fields, and value strings containing a decimal/thousands separator
178             // problem which is to be solved with this:
179             // * a system locale "german"
180             // * a column formatted with an english number format
181             // => the output is german (as we use the system locale for this), i.e. "3,4"
182             // => the input does not recognize the german text, as predicateTree uses the number format
183             //    of the column to determine the main locale - the locale on the context is only a fallback
184             if  (   ( DataType::FLOAT == nType )
185                 ||  ( DataType::REAL == nType )
186                 ||  ( DataType::DOUBLE == nType )
187                 ||  ( DataType::NUMERIC == nType )
188                 ||  ( DataType::DECIMAL == nType )
189                 )
190             {
191                 const IParseContext& rParseContext = m_aParser.getContext();
192                 // get the separators for the locale of our parse context
193                 sal_Unicode nCtxDecSep;
194                 sal_Unicode nCtxThdSep;
195                 getSeparatorChars( rParseContext.getPreferredLocale(), nCtxDecSep, nCtxThdSep );
196 
197                 // determine the locale of the column we're building a predicate string for
198                 sal_Unicode nFmtDecSep( nCtxDecSep );
199                 sal_Unicode nFmtThdSep( nCtxThdSep );
200                 try
201                 {
202                     Reference< XPropertySetInfo > xPSI( _rxField->getPropertySetInfo() );
203                     if ( xPSI.is() && xPSI->hasPropertyByName( ::rtl::OUString::createFromAscii( "FormatKey" ) ) )
204                     {
205                         sal_Int32 nFormatKey = 0;
206                         _rxField->getPropertyValue( ::rtl::OUString::createFromAscii( "FormatKey" ) ) >>= nFormatKey;
207                         if ( nFormatKey && m_xFormatter.is() )
208                         {
209                             Locale aFormatLocale;
210                             ::comphelper::getNumberFormatProperty(
211                                 m_xFormatter,
212                                 nFormatKey,
213                                 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Locale" ) )
214                             ) >>= aFormatLocale;
215 
216                             // valid locale
217                             if ( aFormatLocale.Language.getLength() )
218                             {
219                                 getSeparatorChars( aFormatLocale, nFmtDecSep, nCtxThdSep );
220                             }
221                         }
222                     }
223                 }
224                 catch( const Exception& )
225                 {
226                     OSL_ENSURE( sal_False, "OPredicateInputController::implPredicateTree: caught an exception while dealing with the formats!" );
227                 }
228 
229                 sal_Bool bDecDiffers = ( nCtxDecSep != nFmtDecSep );
230                 sal_Bool bFmtDiffers = ( nCtxThdSep != nFmtThdSep );
231                 if ( bDecDiffers || bFmtDiffers )
232                 {   // okay, at least one differs
233                     // "translate" the value into the "format locale"
234                     ::rtl::OUString sTranslated( _rStatement );
235                     const sal_Unicode nIntermediate( '_' );
236                     sTranslated = sTranslated.replace( nCtxDecSep,  nIntermediate );
237                     sTranslated = sTranslated.replace( nCtxThdSep,  nFmtThdSep );
238                     sTranslated = sTranslated.replace( nIntermediate, nFmtDecSep );
239 
240                     pReturn = const_cast< OSQLParser& >( m_aParser ).predicateTree( _rErrorMessage, sTranslated, m_xFormatter, _rxField );
241                 }
242             }
243         }
244         return pReturn;
245     }
246 
247     //---------------------------------------------------------------------
normalizePredicateString(::rtl::OUString & _rPredicateValue,const Reference<XPropertySet> & _rxField,::rtl::OUString * _pErrorMessage) const248     sal_Bool OPredicateInputController::normalizePredicateString(
249         ::rtl::OUString& _rPredicateValue, const Reference< XPropertySet > & _rxField, ::rtl::OUString* _pErrorMessage ) const
250     {
251         OSL_ENSURE( m_xConnection.is() && m_xFormatter.is() && _rxField.is(),
252             "OPredicateInputController::normalizePredicateString: invalid state or params!" );
253 
254         sal_Bool bSuccess = sal_False;
255         if ( m_xConnection.is() && m_xFormatter.is() && _rxField.is() )
256         {
257             // parse the string
258             ::rtl::OUString sError;
259             ::rtl::OUString sTransformedText( _rPredicateValue );
260             OSQLParseNode* pParseNode = implPredicateTree( sError, sTransformedText, _rxField );
261             if ( _pErrorMessage ) *_pErrorMessage = sError;
262 
263             if ( pParseNode )
264             {
265                 const IParseContext& rParseContext = m_aParser.getContext();
266                 sal_Unicode nDecSeparator, nThousandSeparator;
267                 getSeparatorChars( rParseContext.getPreferredLocale(), nDecSeparator, nThousandSeparator );
268 
269                 // translate it back into a string
270                 sTransformedText = ::rtl::OUString();
271                 pParseNode->parseNodeToPredicateStr(
272                     sTransformedText, m_xConnection, m_xFormatter, _rxField,
273                     rParseContext.getPreferredLocale(), (sal_Char)nDecSeparator, &rParseContext
274                 );
275                 _rPredicateValue = sTransformedText;
276                 delete pParseNode;
277 
278                 bSuccess = sal_True;
279             }
280         }
281 
282         return bSuccess;
283     }
284 
285     //---------------------------------------------------------------------
getPredicateValue(const::rtl::OUString & _rPredicateValue,const Reference<XPropertySet> & _rxField,sal_Bool _bForStatementUse,::rtl::OUString * _pErrorMessage) const286     ::rtl::OUString OPredicateInputController::getPredicateValue(
287         const ::rtl::OUString& _rPredicateValue, const Reference< XPropertySet > & _rxField,
288         sal_Bool _bForStatementUse, ::rtl::OUString* _pErrorMessage ) const
289     {
290         OSL_ENSURE( _rxField.is(), "OPredicateInputController::getPredicateValue: invalid params!" );
291         ::rtl::OUString sReturn;
292         if ( _rxField.is() )
293         {
294             ::rtl::OUString sValue( _rPredicateValue );
295 
296             // a little problem : if the field is a text field, the normalizePredicateString added two
297             // '-characters to the text. If we would give this to predicateTree this would add
298             // two  additional '-characters which we don't want. So check the field format.
299             // FS - 06.01.00 - 71532
300             sal_Bool bValidQuotedText = ( sValue.getLength() >= 2 )
301                                     &&  ( sValue.getStr()[0] == '\'' )
302                                     &&  ( sValue.getStr()[ sValue.getLength() - 1 ] == '\'' );
303                 // again : as normalizePredicateString always did a conversion on the value text,
304                 // bValidQuotedText == sal_True implies that we have a text field, as no other field
305                 // values will be formatted with the quote characters
306             if ( bValidQuotedText )
307             {
308                 sValue = sValue.copy( 1, sValue.getLength() - 2 );
309                 static const ::rtl::OUString sSingleQuote( RTL_CONSTASCII_USTRINGPARAM( "'" ) );
310                 static const ::rtl::OUString sDoubleQuote( RTL_CONSTASCII_USTRINGPARAM( "''" ) );
311 
312                 sal_Int32 nIndex = -1;
313                 sal_Int32 nTemp = 0;
314                 while ( -1 != ( nIndex = sValue.indexOf( sDoubleQuote,nTemp ) ) )
315                 {
316                     sValue = sValue.replaceAt( nIndex, 2, sSingleQuote );
317                     nTemp = nIndex+2;
318                 }
319             }
320 
321             // The following is mostly stolen from the former implementation in the parameter dialog
322             // (dbaccess/source/ui/dlg/paramdialog.cxx). I do not fully understand this .....
323 
324             ::rtl::OUString sError;
325             OSQLParseNode* pParseNode = implPredicateTree( sError, sValue, _rxField );
326             if ( _pErrorMessage )
327                 *_pErrorMessage = sError;
328 
329             sReturn = implParseNode(pParseNode,_bForStatementUse);
330         }
331 
332         return sReturn;
333     }
334 
getPredicateValue(const::rtl::OUString & _sField,const::rtl::OUString & _rPredicateValue,sal_Bool _bForStatementUse,::rtl::OUString * _pErrorMessage) const335     ::rtl::OUString OPredicateInputController::getPredicateValue(
336         const ::rtl::OUString& _sField, const ::rtl::OUString& _rPredicateValue, sal_Bool _bForStatementUse, ::rtl::OUString* _pErrorMessage ) const
337     {
338         ::rtl::OUString sReturn = _rPredicateValue;
339         ::rtl::OUString sError;
340         ::rtl::OUString sField = _sField;
341         sal_Int32 nIndex = 0;
342         sField = sField.getToken(0,'(',nIndex);
343         if(nIndex == -1)
344             sField = _sField;
345         sal_Int32 nType = ::connectivity::OSQLParser::getFunctionReturnType(sField,&m_aParser.getContext());
346         if ( nType == DataType::OTHER || !sField.getLength() )
347         {
348             // first try the international version
349             ::rtl::OUString sSql;
350             sSql += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("SELECT * "));
351             sSql += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" FROM x WHERE "));
352             sSql += sField;
353             sSql += _rPredicateValue;
354             ::std::auto_ptr<OSQLParseNode> pParseNode( const_cast< OSQLParser& >( m_aParser ).parseTree( sError, sSql, sal_True ) );
355             nType = DataType::DOUBLE;
356             if ( pParseNode.get() )
357             {
358                 OSQLParseNode* pColumnRef = pParseNode->getByRule(OSQLParseNode::column_ref);
359                 if ( pColumnRef )
360                 {
361                 }
362             }
363         }
364 
365         Reference<XDatabaseMetaData> xMeta = m_xConnection->getMetaData();
366         parse::OParseColumn* pColumn = new parse::OParseColumn( sField,
367                                                                 ::rtl::OUString(),
368                                                                 ::rtl::OUString(),
369                                                                 ::rtl::OUString(),
370                                                                 ColumnValue::NULLABLE_UNKNOWN,
371                                                                 0,
372                                                                 0,
373                                                                 nType,
374                                                                 sal_False,
375                                                                 sal_False,
376                                                                 xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers());
377         Reference<XPropertySet> xColumn = pColumn;
378         pColumn->setFunction(sal_True);
379         pColumn->setRealName(sField);
380 
381         OSQLParseNode* pParseNode = implPredicateTree( sError, _rPredicateValue, xColumn );
382         if ( _pErrorMessage )
383             *_pErrorMessage = sError;
384         return pParseNode ? implParseNode(pParseNode,_bForStatementUse) : sReturn;
385     }
386 
implParseNode(OSQLParseNode * pParseNode,sal_Bool _bForStatementUse) const387     ::rtl::OUString OPredicateInputController::implParseNode(OSQLParseNode* pParseNode,sal_Bool _bForStatementUse) const
388     {
389         ::rtl::OUString sReturn;
390         if ( pParseNode )
391         {
392             ::std::auto_ptr<OSQLParseNode> pTemp(pParseNode);
393             OSQLParseNode* pOdbcSpec = pParseNode->getByRule( OSQLParseNode::odbc_fct_spec );
394             if ( pOdbcSpec )
395             {
396                 if ( _bForStatementUse )
397                 {
398                     OSQLParseNode* pFuncSpecParent = pOdbcSpec->getParent();
399                     OSL_ENSURE( pFuncSpecParent, "OPredicateInputController::getPredicateValue: an ODBC func spec node without parent?" );
400                     if ( pFuncSpecParent )
401                         pFuncSpecParent->parseNodeToStr(sReturn, m_xConnection, &m_aParser.getContext(), sal_False, sal_True);
402                 }
403                 else
404                 {
405                     OSQLParseNode* pValueNode = pOdbcSpec->getChild(1);
406                     if ( SQL_NODE_STRING == pValueNode->getNodeType() )
407                         sReturn = pValueNode->getTokenValue();
408                     else
409                         pValueNode->parseNodeToStr(sReturn, m_xConnection, &m_aParser.getContext(), sal_False, sal_True);
410                     // sReturn = pOdbcSpec->getChild(1)->getTokenValue();
411                 }
412             }
413             else
414             {
415                 if  ( pParseNode->count() >= 3 )
416                 {
417                     OSQLParseNode* pValueNode = pParseNode->getChild(2);
418                     OSL_ENSURE( pValueNode, "OPredicateInputController::getPredicateValue: invalid node child!" );
419                     if ( !_bForStatementUse )
420                     {
421                         if ( SQL_NODE_STRING == pValueNode->getNodeType() )
422                             sReturn = pValueNode->getTokenValue();
423                         else
424                             pValueNode->parseNodeToStr(
425                                 sReturn, m_xConnection, &m_aParser.getContext(), sal_False, sal_True
426                             );
427                     }
428                     else
429                         pValueNode->parseNodeToStr(
430                             sReturn, m_xConnection, &m_aParser.getContext(), sal_False, sal_True
431                         );
432                 }
433                 else
434                     OSL_ENSURE( sal_False, "OPredicateInputController::getPredicateValue: unknown/invalid structure (noodbc)!" );
435             }
436         }
437         return sReturn;
438     }
439 //.........................................................................
440 }   // namespace dbtools
441 //.........................................................................
442 
443 
444