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 <string.h>
21 #include <mysql/YTable.hxx>
22 #include <mysql/YTables.hxx>
23 #include <com/sun/star/sdbc/XRow.hpp>
24 #include <com/sun/star/sdbc/XResultSet.hpp>
25 #include <com/sun/star/sdbcx/KeyType.hpp>
26 #include <com/sun/star/sdbc/KeyRule.hpp>
27 #include <cppuhelper/typeprovider.hxx>
28 #include <com/sun/star/beans/PropertyAttribute.hpp>
29 #include <com/sun/star/lang/DisposedException.hpp>
30 #include <com/sun/star/sdbc/ColumnValue.hpp>
31 #include <com/sun/star/sdbcx/Privilege.hpp>
32 #include <comphelper/property.hxx>
33 #include <comphelper/servicehelper.hxx>
34 #include <comphelper/types.hxx>
35 #include <connectivity/dbtools.hxx>
36 #include <connectivity/sdbcx/VColumn.hxx>
37 #include <connectivity/TKeys.hxx>
38 #include <connectivity/TIndexes.hxx>
39 #include <connectivity/TColumnsHelper.hxx>
40 #include <mysql/YCatalog.hxx>
41 #include <mysql/YColumns.hxx>
42 #include <TConnection.hxx>
43 
44 using namespace ::comphelper;
45 using namespace connectivity::mysql;
46 using namespace connectivity::sdbcx;
47 using namespace connectivity;
48 using namespace ::com::sun::star::uno;
49 using namespace ::com::sun::star::beans;
50 using namespace ::com::sun::star::sdbcx;
51 using namespace ::com::sun::star::sdbc;
52 using namespace ::com::sun::star::container;
53 using namespace ::com::sun::star::lang;
54 namespace connectivity
55 {
56 namespace mysql
57 {
58 class OMySQLKeysHelper : public OKeysHelper
59 {
60 protected:
getDropForeignKey() const61     virtual OUString getDropForeignKey() const override { return " DROP FOREIGN KEY "; }
62 
63 public:
OMySQLKeysHelper(OTableHelper * _pTable,::osl::Mutex & _rMutex,const::std::vector<OUString> & _rVector)64     OMySQLKeysHelper(OTableHelper* _pTable, ::osl::Mutex& _rMutex,
65                      const ::std::vector<OUString>& _rVector)
66         : OKeysHelper(_pTable, _rMutex, _rVector)
67     {
68     }
69 };
70 }
71 }
72 
OMySQLTable(sdbcx::OCollection * _pTables,const Reference<XConnection> & _xConnection)73 OMySQLTable::OMySQLTable(sdbcx::OCollection* _pTables, const Reference<XConnection>& _xConnection)
74     : OTableHelper(_pTables, _xConnection, true)
75 {
76     // we create a new table here, so we should have all the rights or ;-)
77     m_nPrivileges = Privilege::DROP | Privilege::REFERENCE | Privilege::ALTER | Privilege::CREATE
78                     | Privilege::READ | Privilege::DELETE | Privilege::UPDATE | Privilege::INSERT
79                     | Privilege::SELECT;
80     construct();
81 }
82 
OMySQLTable(sdbcx::OCollection * _pTables,const Reference<XConnection> & _xConnection,const OUString & Name,const OUString & Type,const OUString & Description,const OUString & SchemaName,const OUString & CatalogName,sal_Int32 _nPrivileges)83 OMySQLTable::OMySQLTable(sdbcx::OCollection* _pTables, const Reference<XConnection>& _xConnection,
84                          const OUString& Name, const OUString& Type, const OUString& Description,
85                          const OUString& SchemaName, const OUString& CatalogName,
86                          sal_Int32 _nPrivileges)
87     : OTableHelper(_pTables, _xConnection, true, Name, Type, Description, SchemaName, CatalogName)
88     , m_nPrivileges(_nPrivileges)
89 {
90     construct();
91 }
92 
construct()93 void OMySQLTable::construct()
94 {
95     OTableHelper::construct();
96     if (!isNew())
97         registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRIVILEGES),
98                          PROPERTY_ID_PRIVILEGES, PropertyAttribute::READONLY, &m_nPrivileges,
99                          cppu::UnoType<decltype(m_nPrivileges)>::get());
100 }
101 
createArrayHelper(sal_Int32) const102 ::cppu::IPropertyArrayHelper* OMySQLTable::createArrayHelper(sal_Int32 /*_nId*/) const
103 {
104     return doCreateArrayHelper();
105 }
106 
getInfoHelper()107 ::cppu::IPropertyArrayHelper& OMySQLTable::getInfoHelper()
108 {
109     return *static_cast<OMySQLTable_PROP*>(this)->getArrayHelper(isNew() ? 1 : 0);
110 }
111 
createColumns(const::std::vector<OUString> & _rNames)112 sdbcx::OCollection* OMySQLTable::createColumns(const ::std::vector<OUString>& _rNames)
113 {
114     OMySQLColumns* pColumns = new OMySQLColumns(*this, m_aMutex, _rNames);
115     pColumns->setParent(this);
116     return pColumns;
117 }
118 
createKeys(const::std::vector<OUString> & _rNames)119 sdbcx::OCollection* OMySQLTable::createKeys(const ::std::vector<OUString>& _rNames)
120 {
121     return new OMySQLKeysHelper(this, m_aMutex, _rNames);
122 }
123 
createIndexes(const::std::vector<OUString> & _rNames)124 sdbcx::OCollection* OMySQLTable::createIndexes(const ::std::vector<OUString>& _rNames)
125 {
126     return new OIndexesHelper(this, m_aMutex, _rNames);
127 }
128 
getUnoTunnelId()129 Sequence<sal_Int8> OMySQLTable::getUnoTunnelId()
130 {
131     static ::cppu::OImplementationId implId;
132 
133     return implId.getImplementationId();
134 }
135 
136 // css::lang::XUnoTunnel
137 
getSomething(const Sequence<sal_Int8> & rId)138 sal_Int64 OMySQLTable::getSomething(const Sequence<sal_Int8>& rId)
139 {
140     return (isUnoTunnelId<OMySQLTable>(rId)) ? reinterpret_cast<sal_Int64>(this)
141                                              : OTable_TYPEDEF::getSomething(rId);
142 }
143 
144 // XAlterTable
alterColumnByName(const OUString & colName,const Reference<XPropertySet> & descriptor)145 void SAL_CALL OMySQLTable::alterColumnByName(const OUString& colName,
146                                              const Reference<XPropertySet>& descriptor)
147 {
148     ::osl::MutexGuard aGuard(m_aMutex);
149     checkDisposed(
150 #ifdef __GNUC__
151         ::connectivity::sdbcx::OTableDescriptor_BASE::rBHelper.bDisposed
152 #else
153         rBHelper.bDisposed
154 #endif
155     );
156 
157     if (!m_xColumns || !m_xColumns->hasByName(colName))
158         throw NoSuchElementException(colName, *this);
159 
160     if (!isNew())
161     {
162         // first we have to check what should be altered
163         Reference<XPropertySet> xProp;
164         m_xColumns->getByName(colName) >>= xProp;
165         // first check the types
166         sal_Int32 nOldType = 0, nNewType = 0, nOldPrec = 0, nNewPrec = 0, nOldScale = 0,
167                   nNewScale = 0;
168 
169         ::dbtools::OPropertyMap& rProp = OMetaConnection::getPropMap();
170         xProp->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_TYPE)) >>= nOldType;
171         descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_TYPE)) >>= nNewType;
172         // and precisions and scale
173         xProp->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_PRECISION)) >>= nOldPrec;
174         descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_PRECISION)) >>= nNewPrec;
175         xProp->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_SCALE)) >>= nOldScale;
176         descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_SCALE)) >>= nNewScale;
177         // second: check the "is nullable" value
178         sal_Int32 nOldNullable = 0, nNewNullable = 0;
179         xProp->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_ISNULLABLE)) >>= nOldNullable;
180         descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_ISNULLABLE)) >>= nNewNullable;
181 
182         // check also the auto_increment
183         bool bOldAutoIncrement = false, bAutoIncrement = false;
184         xProp->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_ISAUTOINCREMENT))
185             >>= bOldAutoIncrement;
186         descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_ISAUTOINCREMENT))
187             >>= bAutoIncrement;
188         bool bColumnNameChanged = false;
189         OUString sOldDesc, sNewDesc;
190         xProp->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_DESCRIPTION)) >>= sOldDesc;
191         descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_DESCRIPTION)) >>= sNewDesc;
192 
193         if (nOldType != nNewType || nOldPrec != nNewPrec || nOldScale != nNewScale
194             || nNewNullable != nOldNullable || bOldAutoIncrement != bAutoIncrement
195             || sOldDesc != sNewDesc)
196         {
197             // special handling because they changed the type names to distinguish
198             // if a column should be an auto_increment one
199             if (bOldAutoIncrement != bAutoIncrement)
200             {
201                 OUString sTypeName;
202                 descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_TYPENAME))
203                     >>= sTypeName;
204 
205                 static const char s_sAutoIncrement[] = "auto_increment";
206                 if (bAutoIncrement)
207                 {
208                     if (sTypeName.indexOf(s_sAutoIncrement) == -1)
209                     {
210                         sTypeName += OUStringLiteral(" ") + s_sAutoIncrement;
211                         descriptor->setPropertyValue(rProp.getNameByIndex(PROPERTY_ID_TYPENAME),
212                                                      makeAny(sTypeName));
213                     }
214                 }
215                 else
216                 {
217                     sal_Int32 nIndex = 0;
218                     if (!sTypeName.isEmpty()
219                         && (nIndex = sTypeName.indexOf(s_sAutoIncrement)) != -1)
220                     {
221                         sTypeName = sTypeName.copy(0, nIndex);
222                         descriptor->setPropertyValue(rProp.getNameByIndex(PROPERTY_ID_TYPENAME),
223                                                      makeAny(sTypeName));
224                     }
225                 }
226             }
227             alterColumnType(nNewType, colName, descriptor);
228             bColumnNameChanged = true;
229         }
230 
231         // third: check the default values
232         OUString sNewDefault, sOldDefault;
233         xProp->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_DEFAULTVALUE)) >>= sOldDefault;
234         descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_DEFAULTVALUE))
235             >>= sNewDefault;
236 
237         if (!sOldDefault.isEmpty())
238         {
239             dropDefaultValue(colName);
240             if (!sNewDefault.isEmpty() && sOldDefault != sNewDefault)
241                 alterDefaultValue(sNewDefault, colName);
242         }
243         else if (!sNewDefault.isEmpty())
244             alterDefaultValue(sNewDefault, colName);
245 
246         // now we should look if the name of the column changed
247         OUString sNewColumnName;
248         descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_NAME)) >>= sNewColumnName;
249         if (!sNewColumnName.equalsIgnoreAsciiCase(colName) && !bColumnNameChanged)
250         {
251             const OUString sQuote = getMetaData()->getIdentifierQuoteString();
252             OUString sSql = getAlterTableColumnPart() + " CHANGE "
253                             + ::dbtools::quoteName(sQuote, colName) + " "
254                             + OTables::adjustSQL(::dbtools::createStandardColumnPart(
255                                   descriptor, getConnection(), static_cast<OTables*>(m_pTables),
256                                   getTypeCreatePattern()));
257             executeStatement(sSql);
258         }
259         m_xColumns->refresh();
260     }
261     else
262     {
263         if (m_xColumns)
264         {
265             m_xColumns->dropByName(colName);
266             m_xColumns->appendByDescriptor(descriptor);
267         }
268     }
269 }
270 
alterColumnType(sal_Int32 nNewType,const OUString & _rColName,const Reference<XPropertySet> & _xDescriptor)271 void OMySQLTable::alterColumnType(sal_Int32 nNewType, const OUString& _rColName,
272                                   const Reference<XPropertySet>& _xDescriptor)
273 {
274     const OUString sQuote = getMetaData()->getIdentifierQuoteString();
275     OUString sSql
276         = getAlterTableColumnPart() + " CHANGE " + ::dbtools::quoteName(sQuote, _rColName) + " ";
277 
278     OColumn* pColumn = new OColumn(true);
279     Reference<XPropertySet> xProp = pColumn;
280     ::comphelper::copyProperties(_xDescriptor, xProp);
281     xProp->setPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE),
282                             makeAny(nNewType));
283 
284     sSql += OTables::adjustSQL(::dbtools::createStandardColumnPart(
285         xProp, getConnection(), static_cast<OTables*>(m_pTables), getTypeCreatePattern()));
286     executeStatement(sSql);
287 }
288 
getTypeCreatePattern() const289 OUString OMySQLTable::getTypeCreatePattern() const { return "(M,D)"; }
290 
alterDefaultValue(const OUString & _sNewDefault,const OUString & _rColName)291 void OMySQLTable::alterDefaultValue(const OUString& _sNewDefault, const OUString& _rColName)
292 {
293     const OUString sQuote = getMetaData()->getIdentifierQuoteString();
294     OUString sSql = getAlterTableColumnPart() + " ALTER " + ::dbtools::quoteName(sQuote, _rColName)
295                     + " SET DEFAULT '" + _sNewDefault + "'";
296 
297     executeStatement(sSql);
298 }
299 
dropDefaultValue(const OUString & _rColName)300 void OMySQLTable::dropDefaultValue(const OUString& _rColName)
301 {
302     const OUString sQuote = getMetaData()->getIdentifierQuoteString();
303     OUString sSql = getAlterTableColumnPart() + " ALTER " + ::dbtools::quoteName(sQuote, _rColName)
304                     + " DROP DEFAULT";
305 
306     executeStatement(sSql);
307 }
308 
getAlterTableColumnPart() const309 OUString OMySQLTable::getAlterTableColumnPart() const
310 {
311     OUString sSql("ALTER TABLE ");
312 
313     OUString sComposedName(
314         ::dbtools::composeTableName(getMetaData(), m_CatalogName, m_SchemaName, m_Name, true,
315                                     ::dbtools::EComposeRule::InTableDefinitions));
316     sSql += sComposedName;
317 
318     return sSql;
319 }
320 
executeStatement(const OUString & _rStatement)321 void OMySQLTable::executeStatement(const OUString& _rStatement)
322 {
323     OUString sSQL = _rStatement;
324     if (sSQL.endsWith(","))
325         sSQL = sSQL.replaceAt(sSQL.getLength() - 1, 1, ")");
326 
327     Reference<XStatement> xStmt = getConnection()->createStatement();
328     if (xStmt.is())
329     {
330         xStmt->execute(sSQL);
331         ::comphelper::disposeComponent(xStmt);
332     }
333 }
334 
getRenameStart() const335 OUString OMySQLTable::getRenameStart() const { return "RENAME TABLE "; }
336 
337 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
338