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