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