1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include "MacabStatement.hxx"
22 #include "sqlbison.hxx"
23 #include "MacabConnection.hxx"
24 #include "MacabAddressBook.hxx"
25 #include "MacabDriver.hxx"
26 #include "MacabResultSet.hxx"
27 #include "MacabResultSetMetaData.hxx"
28 #include "macabcondition.hxx"
29 #include "macaborder.hxx"
30 #include "TConnection.hxx"
31 #include <connectivity/dbexception.hxx>
32 #include "resource/sharedresources.hxx"
33 #include "resource/macab_res.hrc"
35 #if OSL_DEBUG_LEVEL > 0
36 # define OUtoCStr( x ) ( OUStringToOString ( (x), RTL_TEXTENCODING_ASCII_US).getStr())
37 #else /* OSL_DEBUG_LEVEL */
38 # define OUtoCStr( x ) ("dummy")
39 #endif /* OSL_DEBUG_LEVEL */
41 using namespace connectivity::macab
;
42 using namespace com::sun::star::uno
;
43 using namespace com::sun::star::lang
;
44 using namespace com::sun::star::beans
;
45 using namespace com::sun::star::sdbc
;
46 using namespace com::sun::star::sdbcx
;
47 using namespace com::sun::star::container
;
48 using namespace com::sun::star::io
;
49 using namespace com::sun::star::util
;
51 namespace connectivity
55 void impl_throwError(sal_uInt16 _nErrorId
)
57 ::connectivity::SharedResources aResources
;
58 const OUString
sError( aResources
.getResourceString(_nErrorId
) );
59 ::dbtools::throwGenericSQLException(sError
,nullptr);
64 IMPLEMENT_SERVICE_INFO(MacabStatement
, "com.sun.star.sdbc.drivers.MacabStatement", "com.sun.star.sdbc.Statement");
66 MacabCommonStatement::MacabCommonStatement(MacabConnection
* _pConnection
)
67 : MacabCommonStatement_BASE(m_aMutex
),
68 OPropertySetHelper(rBHelper
),
69 m_aParser(_pConnection
->getDriver()->getComponentContext()),
70 m_aSQLIterator(_pConnection
, _pConnection
->createCatalog()->getTables(), m_aParser
),
71 m_pParseTree(nullptr),
72 m_pConnection(_pConnection
)
74 m_pConnection
->acquire();
77 MacabCommonStatement::~MacabCommonStatement()
81 void MacabCommonStatement::resetParameters() const throw(css::sdbc::SQLException
)
85 void MacabCommonStatement::getNextParameter(OUString
&) const throw(css::sdbc::SQLException
)
87 impl_throwError(STR_PARA_ONLY_PREPARED
);
90 MacabCondition
*MacabCommonStatement::analyseWhereClause(const OSQLParseNode
*pParseNode
) const throw(SQLException
)
92 if (pParseNode
->count() == 3)
94 const OSQLParseNode
*pLeft
= pParseNode
->getChild(0),
95 *pMiddle
= pParseNode
->getChild(1),
96 *pRight
= pParseNode
->getChild(2);
99 if (SQL_ISPUNCTUATION(pLeft
, "(") && SQL_ISPUNCTUATION(pRight
, ")"))
101 return analyseWhereClause(pMiddle
);
103 else if (SQL_ISRULE(pParseNode
, comparison_predicate
))
105 if (pLeft
->isToken() && pRight
->isToken())
107 switch (pMiddle
->getNodeType())
109 case SQLNodeType::Equal
:
111 return new MacabConditionConstant(pLeft
->getTokenValue() == pRight
->getTokenValue());
113 case SQLNodeType::NotEqual
:
115 // (might not be correct SQL... don't care, handling anyway)
116 return new MacabConditionConstant(pLeft
->getTokenValue() != pRight
->getTokenValue());
122 else if (SQL_ISRULE(pLeft
, column_ref
))
124 OUString sColumnName
,
127 m_aSQLIterator
.getColumnRange(pLeft
, sColumnName
, sTableRange
);
129 if (pRight
->isToken() || SQL_ISRULE(pRight
, parameter
))
131 OUString sMatchString
;
133 if (pRight
->isToken()) // WHERE Name = 'Doe'
134 sMatchString
= pRight
->getTokenValue();
135 else if (SQL_ISRULE(pRight
, parameter
)) // WHERE Name = ?
136 getNextParameter(sMatchString
);
138 switch (pMiddle
->getNodeType())
140 case SQLNodeType::Equal
:
141 // WHERE Name = 'Smith'
142 return new MacabConditionEqual(m_pHeader
, sColumnName
, sMatchString
);
144 case SQLNodeType::NotEqual
:
145 // WHERE Name <> 'Jones'
146 return new MacabConditionDifferent(m_pHeader
, sColumnName
, sMatchString
);
154 else if (SQL_ISRULE(pParseNode
, search_condition
))
156 if (SQL_ISTOKEN(pMiddle
, OR
))
158 // WHERE Name = 'Smith' OR Name = 'Jones'
159 return new MacabConditionOr(
160 analyseWhereClause(pLeft
),
161 analyseWhereClause(pRight
));
164 else if (SQL_ISRULE(pParseNode
, boolean_term
))
166 if (SQL_ISTOKEN(pMiddle
, AND
))
168 // WHERE Name = 'Smith' AND "Given Name" = 'Peter'
169 return new MacabConditionAnd(
170 analyseWhereClause(pLeft
),
171 analyseWhereClause(pRight
));
175 else if (SQL_ISRULE(pParseNode
, test_for_null
) || SQL_ISRULE(pParseNode
, like_predicate
))
177 const OSQLParseNode
*pLeft
= pParseNode
->getChild(0);
178 const OSQLParseNode
* pPart2
= pParseNode
->getChild(1);
179 const OSQLParseNode
*pMiddleLeft
= pPart2
->getChild(0),
180 *pMiddleRight
= pPart2
->getChild(1),
181 *pRight
= pPart2
->getChild(2);
183 if (SQL_ISRULE(pParseNode
, test_for_null
))
185 if (SQL_ISRULE(pLeft
, column_ref
) &&
186 SQL_ISTOKEN(pMiddleLeft
, IS
) &&
187 SQL_ISTOKEN(pRight
, NULL
))
189 OUString sColumnName
,
192 m_aSQLIterator
.getColumnRange(pLeft
, sColumnName
, sTableRange
);
194 if (SQL_ISTOKEN(pMiddleRight
, NOT
))
196 // WHERE "Mobile Phone" IS NOT NULL
197 return new MacabConditionNotNull(m_pHeader
, sColumnName
);
201 // WHERE "Mobile Phone" IS NULL
202 return new MacabConditionNull(m_pHeader
, sColumnName
);
206 else if (SQL_ISRULE(pParseNode
, like_predicate
))
208 if (SQL_ISRULE(pLeft
, column_ref
))
210 OUString sColumnName
,
213 m_aSQLIterator
.getColumnRange(pLeft
, sColumnName
, sTableRange
);
215 if (pMiddleRight
->isToken() || SQL_ISRULE(pMiddleRight
, parameter
))
217 OUString sMatchString
;
219 if (pMiddleRight
->isToken()) // WHERE Name LIKE 'Sm%'
220 sMatchString
= pMiddleRight
->getTokenValue();
221 else if (SQL_ISRULE(pMiddleRight
, parameter
)) // WHERE Name LIKE ?
222 getNextParameter(sMatchString
);
224 return new MacabConditionSimilar(m_pHeader
, sColumnName
, sMatchString
);
229 impl_throwError(STR_QUERY_TOO_COMPLEX
);
235 MacabOrder
*MacabCommonStatement::analyseOrderByClause(const OSQLParseNode
*pParseNode
) const throw(SQLException
)
237 if (SQL_ISRULE(pParseNode
, ordering_spec_commalist
))
239 MacabComplexOrder
*list
= new MacabComplexOrder();
240 sal_uInt32 n
= pParseNode
->count();
242 // Iterate through the ordering columns
243 for (sal_uInt32 i
= 0; i
< n
; i
++)
246 (analyseOrderByClause(pParseNode
->getChild(i
)));
251 else if (SQL_ISRULE(pParseNode
, ordering_spec
))
253 if (pParseNode
->count() == 2)
255 OSQLParseNode
* pColumnRef
= pParseNode
->getChild(0);
256 OSQLParseNode
* pAscendingDescending
= pParseNode
->getChild(1);
258 if (SQL_ISRULE(pColumnRef
, column_ref
))
260 if (pColumnRef
->count() == 3)
261 pColumnRef
= pColumnRef
->getChild(2);
263 if (pColumnRef
->count() == 1)
265 OUString sColumnName
=
266 pColumnRef
->getChild(0)->getTokenValue();
268 !SQL_ISTOKEN(pAscendingDescending
, DESC
);
270 return new MacabSimpleOrder(m_pHeader
, sColumnName
, bAscending
);
275 impl_throwError(STR_QUERY_TOO_COMPLEX
);
281 OUString
MacabCommonStatement::getTableName() const
283 const OSQLTables
& xTabs
= m_aSQLIterator
.getTables();
288 // can only deal with one table at a time
289 if(xTabs
.size() > 1 || m_aSQLIterator
.hasErrors() )
292 return xTabs
.begin()->first
;
295 void MacabCommonStatement::setMacabFields(MacabResultSet
*pResult
) const throw(SQLException
)
297 ::rtl::Reference
<connectivity::OSQLColumns
> xColumns
; // selected columns
298 MacabResultSetMetaData
*pMeta
; // meta information - holds the list of AddressBook fields
300 xColumns
= m_aSQLIterator
.getSelectColumns();
303 ::connectivity::SharedResources aResources
;
304 const OUString
sError( aResources
.getResourceString(
305 STR_INVALID_COLUMN_SELECTION
307 ::dbtools::throwGenericSQLException(sError
,nullptr);
309 pMeta
= static_cast<MacabResultSetMetaData
*>(pResult
->getMetaData().get());
310 pMeta
->setMacabFields(xColumns
);
313 void MacabCommonStatement::selectRecords(MacabResultSet
*pResult
) const throw(SQLException
)
315 const OSQLParseNode
*pParseNode
;
317 pParseNode
= m_aSQLIterator
.getWhereTree();
318 if (pParseNode
!= nullptr)
320 if (SQL_ISRULE(pParseNode
, where_clause
))
323 pParseNode
= pParseNode
->getChild(1);
324 MacabCondition
*pCondition
= analyseWhereClause(pParseNode
);
325 if (pCondition
->isAlwaysTrue())
326 pResult
->allMacabRecords();
328 pResult
->someMacabRecords(pCondition
);
334 // no WHERE clause: get all rows
335 pResult
->allMacabRecords();
338 void MacabCommonStatement::sortRecords(MacabResultSet
*pResult
) const throw(SQLException
)
340 const OSQLParseNode
*pParseNode
;
342 pParseNode
= m_aSQLIterator
.getOrderTree();
343 if (pParseNode
!= nullptr)
345 if (SQL_ISRULE(pParseNode
, opt_order_by_clause
))
347 pParseNode
= pParseNode
->getChild(2);
348 MacabOrder
*pOrder
= analyseOrderByClause(pParseNode
);
349 pResult
->sortMacabRecords(pOrder
);
355 Any SAL_CALL
MacabCommonStatement::queryInterface( const Type
& rType
) throw(RuntimeException
, std::exception
)
357 Any aRet
= MacabCommonStatement_BASE::queryInterface(rType
);
358 if (!aRet
.hasValue())
359 aRet
= OPropertySetHelper::queryInterface(rType
);
363 Sequence
< Type
> SAL_CALL
MacabCommonStatement::getTypes( ) throw(RuntimeException
, std::exception
)
365 ::cppu::OTypeCollection
aTypes( cppu::UnoType
<XMultiPropertySet
>::get(),
366 cppu::UnoType
<XFastPropertySet
>::get(),
367 cppu::UnoType
<XPropertySet
>::get());
369 return comphelper::concatSequences(aTypes
.getTypes(),MacabCommonStatement_BASE::getTypes());
372 void SAL_CALL
MacabCommonStatement::cancel( ) throw(RuntimeException
)
374 ::osl::MutexGuard
aGuard( m_aMutex
);
376 checkDisposed(rBHelper
.bDisposed
);
377 // cancel the current sql statement
380 void SAL_CALL
MacabCommonStatement::close( ) throw(SQLException
, RuntimeException
)
383 ::osl::MutexGuard
aGuard( m_aMutex
);
384 checkDisposed(rBHelper
.bDisposed
);
390 sal_Bool SAL_CALL
MacabCommonStatement::execute(
391 const OUString
& sql
) throw(SQLException
, RuntimeException
)
393 ::osl::MutexGuard
aGuard( m_aMutex
);
394 checkDisposed(rBHelper
.bDisposed
);
396 Reference
< XResultSet
> xRS
= executeQuery(sql
);
401 Reference
< XResultSet
> SAL_CALL
MacabCommonStatement::executeQuery(
402 const OUString
& sql
) throw(SQLException
, RuntimeException
)
404 ::osl::MutexGuard
aGuard( m_aMutex
);
405 checkDisposed(rBHelper
.bDisposed
);
407 OSL_TRACE("Mac OS Address book - SQL Request: %s", OUtoCStr(sql
));
409 MacabResultSet
* pResult
= new MacabResultSet(this);
410 Reference
< XResultSet
> xRS
= pResult
;
413 m_pParseTree
= m_aParser
.parseTree(aErr
, sql
);
414 if (m_pParseTree
== nullptr)
415 throw SQLException(aErr
, *this, aErr
, 0, Any());
417 m_aSQLIterator
.setParseTree(m_pParseTree
);
418 m_aSQLIterator
.traverseAll();
419 switch (m_aSQLIterator
.getStatementType())
421 case OSQLStatementType::Select
:
423 OUString sTableName
= getTableName(); // FROM which table ?
424 if (sTableName
.getLength() != 0) // a match
426 MacabRecords
*aRecords
;
427 aRecords
= m_pConnection
->getAddressBook()->getMacabRecords(sTableName
);
429 // In case, somehow, we don't have anything with the name m_sTableName
430 if(aRecords
== nullptr)
432 impl_throwError(STR_NO_TABLE
);
436 m_pHeader
= aRecords
->getHeader();
438 pResult
->setTableName(sTableName
);
440 setMacabFields(pResult
); // SELECT which columns ?
441 selectRecords(pResult
); // WHERE which condition ?
442 sortRecords(pResult
); // ORDER BY which columns ?
444 // To be continued: DISTINCT
451 // To be continued: UPDATE
454 impl_throwError(STR_QUERY_TOO_COMPLEX
);
457 m_xResultSet
= Reference
<XResultSet
>(pResult
);
461 Reference
< XConnection
> SAL_CALL
MacabCommonStatement::getConnection( ) throw(SQLException
, RuntimeException
)
463 ::osl::MutexGuard
aGuard( m_aMutex
);
464 checkDisposed(rBHelper
.bDisposed
);
466 // just return our connection here
467 return m_pConnection
;
470 sal_Int32 SAL_CALL
MacabCommonStatement::executeUpdate( const OUString
& ) throw(SQLException
, RuntimeException
)
472 ::osl::MutexGuard
aGuard( m_aMutex
);
473 checkDisposed(rBHelper
.bDisposed
);
475 // the return values gives information about how many rows are affected by executing the sql statement
479 Any SAL_CALL
MacabCommonStatement::getWarnings( ) throw(SQLException
, RuntimeException
)
481 ::osl::MutexGuard
aGuard( m_aMutex
);
482 checkDisposed(rBHelper
.bDisposed
);
484 return makeAny(m_aLastWarning
);
487 void SAL_CALL
MacabCommonStatement::clearWarnings( ) throw(SQLException
, RuntimeException
)
489 ::osl::MutexGuard
aGuard( m_aMutex
);
490 checkDisposed(rBHelper
.bDisposed
);
492 m_aLastWarning
= SQLWarning();
495 ::cppu::IPropertyArrayHelper
* MacabCommonStatement::createArrayHelper( ) const
497 // this properties are defined by the service statement
498 // they must be in alphabetic order
499 Sequence
< Property
> aProps(10);
500 Property
* pProperties
= aProps
.getArray();
502 pProperties
[nPos
++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_CURSORNAME
),
503 PROPERTY_ID_CURSORNAME
, cppu::UnoType
<OUString
>::get(), 0);
504 pProperties
[nPos
++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ESCAPEPROCESSING
),
505 PROPERTY_ID_ESCAPEPROCESSING
, cppu::UnoType
<bool>::get(), 0);
506 pProperties
[nPos
++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHDIRECTION
),
507 PROPERTY_ID_FETCHDIRECTION
, cppu::UnoType
<sal_Int32
>::get(), 0);
508 pProperties
[nPos
++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHSIZE
),
509 PROPERTY_ID_FETCHSIZE
, cppu::UnoType
<sal_Int32
>::get(), 0);
510 pProperties
[nPos
++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_MAXFIELDSIZE
),
511 PROPERTY_ID_MAXFIELDSIZE
, cppu::UnoType
<sal_Int32
>::get(), 0);
512 pProperties
[nPos
++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_MAXROWS
),
513 PROPERTY_ID_MAXROWS
, cppu::UnoType
<sal_Int32
>::get(), 0);
514 pProperties
[nPos
++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_QUERYTIMEOUT
),
515 PROPERTY_ID_QUERYTIMEOUT
, cppu::UnoType
<sal_Int32
>::get(), 0);
516 pProperties
[nPos
++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETCONCURRENCY
),
517 PROPERTY_ID_RESULTSETCONCURRENCY
, cppu::UnoType
<sal_Int32
>::get(), 0);
518 pProperties
[nPos
++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETTYPE
),
519 PROPERTY_ID_RESULTSETTYPE
, cppu::UnoType
<sal_Int32
>::get(), 0);
520 pProperties
[nPos
++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_USEBOOKMARKS
),
521 PROPERTY_ID_USEBOOKMARKS
, cppu::UnoType
<bool>::get(), 0);
523 return new ::cppu::OPropertyArrayHelper(aProps
);
526 ::cppu::IPropertyArrayHelper
& MacabCommonStatement::getInfoHelper()
528 return *getArrayHelper();
531 sal_Bool
MacabCommonStatement::convertFastPropertyValue(
535 const Any
&) throw (css::lang::IllegalArgumentException
)
537 bool bConverted
= false;
538 // here we have to try to convert
542 void MacabCommonStatement::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle
,const Any
&) throw (Exception
)
544 // set the value to whatever is necessary
547 case PROPERTY_ID_QUERYTIMEOUT
:
548 case PROPERTY_ID_MAXFIELDSIZE
:
549 case PROPERTY_ID_MAXROWS
:
550 case PROPERTY_ID_CURSORNAME
:
551 case PROPERTY_ID_RESULTSETCONCURRENCY
:
552 case PROPERTY_ID_RESULTSETTYPE
:
553 case PROPERTY_ID_FETCHDIRECTION
:
554 case PROPERTY_ID_FETCHSIZE
:
555 case PROPERTY_ID_ESCAPEPROCESSING
:
556 case PROPERTY_ID_USEBOOKMARKS
:
562 void MacabCommonStatement::getFastPropertyValue(Any
&,sal_Int32 nHandle
) const
566 case PROPERTY_ID_QUERYTIMEOUT
:
567 case PROPERTY_ID_MAXFIELDSIZE
:
568 case PROPERTY_ID_MAXROWS
:
569 case PROPERTY_ID_CURSORNAME
:
570 case PROPERTY_ID_RESULTSETCONCURRENCY
:
571 case PROPERTY_ID_RESULTSETTYPE
:
572 case PROPERTY_ID_FETCHDIRECTION
:
573 case PROPERTY_ID_FETCHSIZE
:
574 case PROPERTY_ID_ESCAPEPROCESSING
:
575 case PROPERTY_ID_USEBOOKMARKS
:
581 void SAL_CALL
MacabCommonStatement::acquire() throw()
583 MacabCommonStatement_BASE::acquire();
586 void SAL_CALL
MacabCommonStatement::release() throw()
588 MacabCommonStatement_BASE::release();
591 Reference
< css::beans::XPropertySetInfo
> SAL_CALL
MacabCommonStatement::getPropertySetInfo( ) throw(RuntimeException
)
593 return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper());
596 MacabStatement::MacabStatement(MacabConnection
* _pConnection
)
597 : MacabStatement_BASE(_pConnection
)
601 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */