1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <connectivity/TKeys.hxx>
21 #include <TKey.hxx>
22 #include <connectivity/TTableHelper.hxx>
23 #include <com/sun/star/sdb/tools/XKeyAlteration.hpp>
24 #include <com/sun/star/sdbc/XRow.hpp>
25 #include <com/sun/star/sdbc/XResultSet.hpp>
26 #include <com/sun/star/sdbcx/KeyType.hpp>
27 #include <com/sun/star/sdbc/KeyRule.hpp>
28 #include <com/sun/star/sdbc/SQLException.hpp>
29 #include <connectivity/dbtools.hxx>
30 #include <comphelper/types.hxx>
31 #include <TConnection.hxx>
32 
33 namespace connectivity
34 {
35 using namespace comphelper;
36 using namespace connectivity::sdbcx;
37 using namespace dbtools;
38 using namespace ::com::sun::star::uno;
39 using namespace ::com::sun::star::beans;
40 using namespace ::com::sun::star::sdbcx;
41 using namespace ::com::sun::star::sdbc;
42 using namespace ::com::sun::star::container;
43 using namespace ::com::sun::star::lang;
44 
45 
OKeysHelper(OTableHelper * _pTable,::osl::Mutex & _rMutex,const::std::vector<OUString> & _rVector)46 OKeysHelper::OKeysHelper(   OTableHelper* _pTable,
47         ::osl::Mutex& _rMutex,
48         const ::std::vector< OUString>& _rVector
49         ) : OKeys_BASE(*_pTable,true,_rMutex,_rVector,true)
50     ,m_pTable(_pTable)
51 {
52 }
53 
createObject(const OUString & _rName)54 sdbcx::ObjectType OKeysHelper::createObject(const OUString& _rName)
55 {
56     sdbcx::ObjectType xRet;
57 
58     if(!_rName.isEmpty())
59     {
60         xRet = new OTableKeyHelper(m_pTable,_rName,m_pTable->getKeyProperties(_rName));
61     }
62 
63     if(!xRet.is()) // we have a primary key with a system name
64     {
65         xRet = new OTableKeyHelper(m_pTable,_rName,m_pTable->getKeyProperties(_rName));
66     }
67 
68     return xRet;
69 }
70 
impl_refresh()71 void OKeysHelper::impl_refresh()
72 {
73     m_pTable->refreshKeys();
74 }
75 
createDescriptor()76 Reference< XPropertySet > OKeysHelper::createDescriptor()
77 {
78     return new OTableKeyHelper(m_pTable);
79 }
80 
81 /** returns the keyrule string for the primary key
82 */
getKeyRuleString(bool _bUpdate,sal_Int32 _nKeyRule)83 static OUString getKeyRuleString(bool _bUpdate,sal_Int32 _nKeyRule)
84 {
85     const char* pKeyRule = nullptr;
86     switch ( _nKeyRule )
87     {
88         case KeyRule::CASCADE:
89             pKeyRule = _bUpdate ? " ON UPDATE CASCADE " : " ON DELETE CASCADE ";
90             break;
91         case KeyRule::RESTRICT:
92             pKeyRule = _bUpdate ? " ON UPDATE RESTRICT " : " ON DELETE RESTRICT ";
93             break;
94         case KeyRule::SET_NULL:
95             pKeyRule = _bUpdate ? " ON UPDATE SET NULL " : " ON DELETE SET NULL ";
96             break;
97         case KeyRule::SET_DEFAULT:
98             pKeyRule = _bUpdate ? " ON UPDATE SET DEFAULT " : " ON DELETE SET DEFAULT ";
99             break;
100         default:
101             ;
102     }
103     OUString sRet;
104     if ( pKeyRule )
105         sRet = OUString::createFromAscii(pKeyRule);
106     return sRet;
107 }
108 
cloneDescriptorColumns(const sdbcx::ObjectType & _rSourceDescriptor,const sdbcx::ObjectType & _rDestDescriptor)109 void OKeysHelper::cloneDescriptorColumns( const sdbcx::ObjectType& _rSourceDescriptor, const sdbcx::ObjectType& _rDestDescriptor )
110 {
111     Reference< XColumnsSupplier > xColSupp( _rSourceDescriptor, UNO_QUERY_THROW );
112     Reference< XIndexAccess > xSourceCols( xColSupp->getColumns(), UNO_QUERY_THROW );
113 
114     xColSupp.set( _rDestDescriptor, UNO_QUERY_THROW );
115     Reference< XAppend > xDestAppend( xColSupp->getColumns(), UNO_QUERY_THROW );
116 
117     sal_Int32 nCount = xSourceCols->getCount();
118     for ( sal_Int32 i=0; i< nCount; ++i )
119     {
120         Reference< XPropertySet > xColProp( xSourceCols->getByIndex(i), UNO_QUERY );
121         xDestAppend->appendByDescriptor( xColProp );
122     }
123 }
124 
125 // XAppend
appendObject(const OUString & _rForName,const Reference<XPropertySet> & descriptor)126 sdbcx::ObjectType OKeysHelper::appendObject( const OUString& _rForName, const Reference< XPropertySet >& descriptor )
127 {
128     Reference< XConnection> xConnection = m_pTable->getConnection();
129     if ( !xConnection.is() )
130         return nullptr;
131     if ( m_pTable->isNew() )
132     {
133         Reference< XPropertySet > xNewDescriptor( cloneDescriptor( descriptor ) );
134         cloneDescriptorColumns( descriptor, xNewDescriptor );
135         return xNewDescriptor;
136     }
137 
138     const ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap();
139     sal_Int32 nKeyType      = getINT32(descriptor->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_TYPE)));
140     sal_Int32 nUpdateRule = 0, nDeleteRule = 0;
141     OUString sReferencedName;
142 
143     if ( nKeyType == KeyType::FOREIGN )
144     {
145         descriptor->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_REFERENCEDTABLE)) >>= sReferencedName;
146         descriptor->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_UPDATERULE)) >>= nUpdateRule;
147         descriptor->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_DELETERULE)) >>= nDeleteRule;
148     }
149 
150     if ( m_pTable->getKeyService().is() )
151     {
152         m_pTable->getKeyService()->addKey(m_pTable,descriptor);
153     }
154     else
155     {
156         // if we're here, we belong to a table which is not new, i.e. already exists in the database.
157         // In this case, really append the new index.
158         OUStringBuffer aSql;
159         aSql.append("ALTER TABLE ");
160         OUString aQuote  = m_pTable->getConnection()->getMetaData()->getIdentifierQuoteString(  );
161 
162         aSql.append(composeTableName( m_pTable->getConnection()->getMetaData(), m_pTable, ::dbtools::EComposeRule::InTableDefinitions, true ));
163         aSql.append(" ADD ");
164 
165         if ( nKeyType == KeyType::PRIMARY )
166         {
167             aSql.append(" PRIMARY KEY (");
168         }
169         else if ( nKeyType == KeyType::FOREIGN )
170         {
171             aSql.append(" FOREIGN KEY (");
172         }
173         else
174             throw SQLException();
175 
176         Reference<XColumnsSupplier> xColumnSup(descriptor,UNO_QUERY);
177         Reference<XIndexAccess> xColumns(xColumnSup->getColumns(),UNO_QUERY);
178         Reference< XPropertySet > xColProp;
179         for(sal_Int32 i = 0 ; i < xColumns->getCount() ; ++i)
180         {
181             if ( i > 0 )
182                 aSql.append(",");
183             xColProp.set(xColumns->getByIndex(i), css::uno::UNO_QUERY);
184             aSql.append( ::dbtools::quoteName( aQuote,getString(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME)))) );
185 
186         }
187         aSql.append(")");
188 
189         if ( nKeyType == KeyType::FOREIGN )
190         {
191             aSql.append(" REFERENCES ");
192             aSql.append(::dbtools::quoteTableName(m_pTable->getConnection()->getMetaData(),sReferencedName,::dbtools::EComposeRule::InTableDefinitions));
193             aSql.append(" (");
194 
195             for(sal_Int32 i=0;i<xColumns->getCount();++i)
196             {
197                 if ( i > 0 )
198                     aSql.append(",");
199                 xColumns->getByIndex(i) >>= xColProp;
200                 aSql.append(::dbtools::quoteName( aQuote,getString(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_RELATEDCOLUMN)))));
201 
202             }
203             aSql.append(")");
204             aSql.append(getKeyRuleString(true   ,nUpdateRule));
205             aSql.append(getKeyRuleString(false  ,nDeleteRule));
206         }
207 
208         Reference< XStatement > xStmt = m_pTable->getConnection()->createStatement(  );
209         xStmt->execute(aSql.makeStringAndClear());
210     }
211     // find the name which the database gave the new key
212     OUString sNewName( _rForName );
213     try
214     {
215         OUString aSchema,aTable;
216         m_pTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_SCHEMANAME)) >>= aSchema;
217         m_pTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME))       >>= aTable;
218         Reference< XResultSet > xResult;
219         sal_Int32 nColumn = 12;
220         if ( nKeyType == KeyType::FOREIGN )
221             xResult = m_pTable->getMetaData()->getImportedKeys( m_pTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_CATALOGNAME))
222                                                                                     ,aSchema
223                                                                                     ,aTable);
224         else
225         {
226             xResult = m_pTable->getMetaData()->getPrimaryKeys( m_pTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_CATALOGNAME))
227                                                                                     ,aSchema
228                                                                                     ,aTable);
229             nColumn = 6;
230         }
231         if ( xResult.is() )
232         {
233             Reference< XRow > xRow(xResult,UNO_QUERY);
234             while( xResult->next() )
235             {
236                 OUString sName = xRow->getString(nColumn);
237                 if ( !m_pElements->exists(sName) ) // this name wasn't inserted yet so it must be the new one
238                 {
239                     descriptor->setPropertyValue( rPropMap.getNameByIndex( PROPERTY_ID_NAME ), makeAny( sName ) );
240                     sNewName = sName;
241                     break;
242                 }
243             }
244             ::comphelper::disposeComponent(xResult);
245         }
246     }
247     catch(const SQLException&)
248     {
249     }
250 
251     m_pTable->addKey(sNewName,std::make_shared<sdbcx::KeyProperties>(sReferencedName,nKeyType,nUpdateRule,nDeleteRule));
252 
253     return createObject( sNewName );
254 }
255 
getDropForeignKey() const256 OUString OKeysHelper::getDropForeignKey() const
257 {
258     return " DROP CONSTRAINT ";
259 }
260 
261 // XDrop
dropObject(sal_Int32 _nPos,const OUString & _sElementName)262 void OKeysHelper::dropObject(sal_Int32 _nPos, const OUString& _sElementName)
263 {
264     Reference< XConnection> xConnection = m_pTable->getConnection();
265     if ( !xConnection.is() || m_pTable->isNew() )
266         return;
267 
268     Reference<XPropertySet> xKey(getObject(_nPos),UNO_QUERY);
269     if ( m_pTable->getKeyService().is() )
270     {
271         m_pTable->getKeyService()->dropKey(m_pTable,xKey);
272     }
273     else
274     {
275         OUStringBuffer aSql;
276         aSql.append("ALTER TABLE ");
277 
278         aSql.append( composeTableName( m_pTable->getConnection()->getMetaData(), m_pTable,::dbtools::EComposeRule::InTableDefinitions, true ));
279 
280         sal_Int32 nKeyType = KeyType::PRIMARY;
281         if ( xKey.is() )
282         {
283             ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap();
284             xKey->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_TYPE)) >>= nKeyType;
285         }
286         if ( KeyType::PRIMARY == nKeyType )
287         {
288             aSql.append(" DROP PRIMARY KEY");
289         }
290         else
291         {
292             aSql.append(getDropForeignKey());
293             const OUString aQuote    = m_pTable->getConnection()->getMetaData()->getIdentifierQuoteString();
294             aSql.append( ::dbtools::quoteName( aQuote,_sElementName) );
295         }
296 
297         Reference< XStatement > xStmt = m_pTable->getConnection()->createStatement(  );
298         if ( xStmt.is() )
299         {
300             xStmt->execute(aSql.makeStringAndClear());
301             ::comphelper::disposeComponent(xStmt);
302         }
303     }
304 }
305 
306 } // namespace connectivity
307 
308 
309 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
310