Use correct object
[LibreOffice.git] / connectivity / source / drivers / postgresql / pq_xcolumns.cxx
blob962449368aafe253e39d8698eb3b900cb6b5936d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * Effective License of whole file:
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License version 2.1, as published by the Free Software Foundation.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
18 * MA 02111-1307 USA
20 * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011:
22 * The Contents of this file are made available subject to the terms of
23 * the GNU Lesser General Public License Version 2.1
25 * Copyright: 2000 by Sun Microsystems, Inc.
27 * Contributor(s): Joerg Budischewski
29 * All parts contributed on or after August 2011:
31 * This Source Code Form is subject to the terms of the Mozilla Public
32 * License, v. 2.0. If a copy of the MPL was not distributed with this
33 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
35 ************************************************************************/
37 #include <sal/config.h>
39 #include <string_view>
41 #include <o3tl/safeint.hxx>
42 #include <o3tl/string_view.hxx>
43 #include <rtl/ref.hxx>
44 #include <rtl/ustrbuf.hxx>
45 #include <sal/log.hxx>
46 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
47 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
48 #include <com/sun/star/sdbc/SQLException.hpp>
49 #include <com/sun/star/sdbc/XRow.hpp>
50 #include <com/sun/star/sdbc/ColumnValue.hpp>
52 #include <cppuhelper/exc_hlp.hxx>
53 #include <utility>
55 #include "pq_xcolumns.hxx"
56 #include "pq_xcolumn.hxx"
57 #include "pq_statics.hxx"
58 #include "pq_tools.hxx"
60 using osl::MutexGuard;
63 using com::sun::star::beans::XPropertySet;
65 using com::sun::star::uno::Any;
66 using com::sun::star::uno::UNO_QUERY;
67 using com::sun::star::uno::Reference;
68 using com::sun::star::uno::RuntimeException;
70 using com::sun::star::sdbc::XRow;
71 using com::sun::star::sdbc::XStatement;
72 using com::sun::star::sdbc::XResultSet;
73 using com::sun::star::sdbc::XDatabaseMetaData;
74 using com::sun::star::sdbc::SQLException;
76 namespace pq_sdbc_driver
79 static Any isCurrency( std::u16string_view typeName )
81 return Any( o3tl::equalsIgnoreAsciiCase(typeName, u"money") );
84 // static sal_Bool isAutoIncrement8( const OUString & typeName )
85 // {
86 // return typeName.equalsIgnoreAsciiCase("serial8") ||
87 // typeName.equalsIgnoreAsciiCase("bigserial");
88 // }
90 static Any isAutoIncrement( std::u16string_view defaultValue )
92 bool ret = o3tl::starts_with( defaultValue, u"nextval(" );
93 // printf( "%s %d\n",
94 // OUStringToOString(defaultValue, RTL_TEXTENCODING_ASCII_US).getStr(),
95 // ret );
96 // {
97 // static const char * const serials[] =
98 // {
99 // "serial", "serial4", "serial8", "bigserial", 0
100 // };
101 // s sal_Bool b = sal_False;
102 // for( int i = 0; !b && serials[i] ; i ++ )
103 // {
104 // b = b || typeName.equalsIgnoreAsciiCaseAscii( serials[i] );
105 // }
106 return Any ( ret );
109 Columns::Columns(
110 const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex,
111 const css::uno::Reference< css::sdbc::XConnection > & origin,
112 ConnectionSettings *pSettings,
113 OUString schemaName,
114 OUString tableName)
115 : Container( refMutex, origin, pSettings, u"COLUMN"_ustr ),
116 m_schemaName(std::move( schemaName )),
117 m_tableName(std::move( tableName ))
120 Columns::~Columns()
123 OUString columnMetaData2SDBCX(
124 ReflectionBase *pBase, const css::uno::Reference< css::sdbc::XRow > &xRow )
126 Statics & st = getStatics();
128 // 1. TABLE_CAT string => table catalog (may be NULL)
129 // => not supported
130 // 2. TABLE_SCHEM string => table schema (may be NULL)
131 // => pg_namespace.nspname
132 // 3. TABLE_NAME string => table name
133 // => pg_class.relname
134 // 4. COLUMN_NAME string => column name
135 // => pg_attribute.attname
136 // 5. DATA_TYPE short => SQL type from java.sql.Types
137 // => pg_type.typname => sdbc.DataType
138 // 6. TYPE_NAME string => Data source dependent type name, for a UDT the
139 // type name is fully qualified
140 // => pg_type.typname
141 // 7. COLUMN_SIZE long => column size. For char or date types this is
142 // the maximum number of characters, for numeric
143 // or decimal types this is precision.
144 // => pg_type.typlen ( TODO: What is about variable size ? )
145 // 8. BUFFER_LENGTH is not used.
146 // => not used
147 // 9. DECIMAL_DIGITS long => the number of fractional digits
148 // => don't know ! TODO !
149 // 10. NUM_PREC_RADIX long => Radix (typically either 10 or 2)
150 // => TODO ??
151 // 11. NULLABLE long => is NULL allowed?
152 // NO_NULLS - might not allow NULL values
153 // NULABLE - definitely allows NULL values
154 // NULLABLE_UNKNOWN - nullability unknown
155 // => pg_attribute.attnotnull
156 // 12. REMARKS string => comment describing column (may be NULL )
157 // => Don't know, there does not seem to exist something like
158 // that in postgres
159 // 13. COLUMN_DEF string => default value (may be NULL)
160 // => pg_type.typdefault
161 // 14. SQL_DATA_TYPE long => unused
162 // => empty
163 // 15. SQL_DATETIME_SUB long => unused
164 // => empty
165 // 16. CHAR_OCTET_LENGTH long => for char types the maximum number of
166 // bytes in the column
167 // => pg_type.typlen
168 // 17. ORDINAL_POSITION int => index of column in table (starting at 1)
169 // pg_attribute.attnum
170 // 18. IS_NULLABLE string => "NO" means column definitely does not allow
171 // NULL values; "YES" means the column might
172 // allow NULL values. An empty string means
173 // nobody knows.
174 // => pg_attribute.attnotnull
176 static const int COLUMN_NAME = 4;
177 static const int DATA_TYPE = 5;
178 static const int TYPE_NAME = 6;
179 static const int COLUMN_SIZE = 7;
180 static const int DECIMAL_DIGITS = 9;
181 static const int IS_NULLABLE = 11;
182 static const int DESCRIPTION = 12;
183 static const int DEFAULT_VALUE = 13;
185 OUString name = xRow->getString( COLUMN_NAME );
186 OUString typeName = xRow->getString( TYPE_NAME );
188 pBase->setPropertyValue_NoBroadcast_public(
189 st.NAME, Any( name ) );
191 pBase->setPropertyValue_NoBroadcast_public(
192 st.TYPE, Any( xRow->getInt( DATA_TYPE ) ) );
194 pBase->setPropertyValue_NoBroadcast_public(
195 st.TYPE_NAME, Any( typeName ) );
197 pBase->setPropertyValue_NoBroadcast_public(
198 st.PRECISION, Any( xRow->getInt( COLUMN_SIZE ) ) );
200 pBase->setPropertyValue_NoBroadcast_public(
201 st.SCALE, Any( xRow->getInt( DECIMAL_DIGITS ) ) );
203 pBase->setPropertyValue_NoBroadcast_public(
204 st.IS_NULLABLE, Any( xRow->getInt( IS_NULLABLE ) ) );
206 pBase->setPropertyValue_NoBroadcast_public(
207 st.DEFAULT_VALUE, Any( xRow->getString( DEFAULT_VALUE ) ) );
209 // pBase->setPropertyValue_NoBroadcast_public(
210 // st.DESCRIPTION, makeAny( xRow->getString( DESCRIPTION ) ) );
212 // if( pBase->getPropertySetInfo()->hasPropertyByName( st.HELP_TEXT ) )
213 // pBase->setPropertyValue_NoBroadcast_public(
214 // st.HELP_TEXT, makeAny( xRow->getString( DESCRIPTION ) ) );
215 // else // for key columns, etc. ...
216 pBase->setPropertyValue_NoBroadcast_public(
217 st.DESCRIPTION, Any( xRow->getString( DESCRIPTION ) ) );
220 // maybe a better criterion than the type name can be found in future
221 pBase->setPropertyValue_NoBroadcast_public(
222 st.IS_AUTO_INCREMENT, isAutoIncrement(xRow->getString( DEFAULT_VALUE )) );
224 pBase->setPropertyValue_NoBroadcast_public(
225 st.IS_CURRENCY, isCurrency( typeName));
226 return name;
230 // class CommentChanger : public cppu::WeakImplHelper< XPropertyChangeListener >
231 // {
232 // ::rtl::Reference< comphelper::RefCountedMutex > m_xMutex;
233 // css::uno::Reference< css::sdbc::XConnection > m_connection;
234 // ConnectionSettings *m_pSettings;
235 // OUString m_schema;
236 // OUString m_table;
237 // OUString m_column;
239 // public:
240 // CommentChanger(
241 // const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex,
242 // const css::uno::Reference< css::sdbc::XConnection > & connection,
243 // ConnectionSettings *pSettings,
244 // const OUString & schema,
245 // const OUString & table,
246 // const OUString & column ) :
247 // m_xMutex( refMutex ),
248 // m_connection( connection ),
249 // m_pSettings( pSettings ),
250 // m_schema ( schema ),
251 // m_table ( table ),
252 // m_column ( column )
253 // {}
256 // // Methods
257 // virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) throw (css::uno::RuntimeException)
258 // {
259 // osl::MutexGuard guard( m_xMutex->GetMutex() );
260 // m_connection.clear();
261 // }
262 // // Methods
263 // virtual void SAL_CALL propertyChange( const css::beans::PropertyChangeEvent& evt ) throw (css::uno::RuntimeException)
264 // {
265 // osl::MutexGuard guard( m_xMutex->GetMutex() );
266 // OUStringBuffer buf( 128 );
267 // OUString comment;
268 // evt.NewValue >>= comment;
269 // buf.append( "COMMENT ON COLUMN" );
270 // bufferQuoteQualifiedIdentifier( buf, m_schema, m_table , m_column );
271 // buf.append( "IS " );
272 // bufferQuoteConstant( buf, comment,m_pSettings->encoding);
274 // printf( "changing comment of column %s to %s\n",
275 // OUStringToOString( m_column, RTL_TEXTENCODING_ASCII_US ).getStr(),
276 // OUStringToOString( comment, RTL_TEXTENCODING_ASCII_US ).getStr() );
278 // m_connection->createStatement()->executeUpdate( buf.makeStringAndClear() );
279 // }
280 // };
282 void Columns::refresh()
286 SAL_INFO("connectivity.postgresql", "sdbcx.Columns get refreshed for table " << m_schemaName << "." << m_tableName);
287 osl::MutexGuard guard( m_xMutex->GetMutex() );
289 Statics &st = getStatics();
290 Reference< XDatabaseMetaData > meta = m_origin->getMetaData();
292 Reference< XResultSet > rs =
293 meta->getColumns( Any(), m_schemaName, m_tableName, st.cPERCENT );
295 DisposeGuard disposeIt( rs );
296 Reference< XRow > xRow( rs , UNO_QUERY );
298 String2IntMap map;
300 m_values.clear();
301 int columnIndex = 0;
302 while( rs->next() )
304 rtl::Reference<Column> pColumn =
305 new Column( m_xMutex, m_origin, m_pSettings );
306 Reference< css::beans::XPropertySet > prop = pColumn;
308 OUString name = columnMetaData2SDBCX( pColumn.get(), xRow );
309 // pColumn->addPropertyChangeListener(
310 // st.HELP_TEXT,
311 // new CommentChanger(
312 // m_xMutex,
313 // m_origin,
314 // m_pSettings,
315 // m_schemaName,
316 // m_tableName,
317 // name ) );
320 m_values.emplace_back(prop);
321 map[ name ] = columnIndex;
322 ++columnIndex;
325 m_name2index.swap( map );
327 catch ( css::sdbc::SQLException & e )
329 css::uno::Any anyEx = cppu::getCaughtException();
330 throw css::lang::WrappedTargetRuntimeException( e.Message,
331 nullptr, anyEx );
333 fire( RefreshedBroadcaster( *this ) );
337 void alterColumnByDescriptor(
338 std::u16string_view schemaName,
339 std::u16string_view tableName,
340 ConnectionSettings *settings,
341 const Reference< XStatement > &stmt,
342 const css::uno::Reference< css::beans::XPropertySet > & past,
343 const css::uno::Reference< css::beans::XPropertySet > & future)
345 Statics & st = getStatics();
347 // if( past->getPropertyValue( st.TABLE_NAME ) != future->getPropertyValue( st.TABLE_NAME ) ||
348 // past->getPropertyValue( st.SCHEMA_NAME ) != future->getPropertyValue( st.SCHEMA_NAME ))
349 // {
350 // OUStringBuffer buf(128);
351 // buf.append( "Can't move column " );
352 // buf.append( extractStringProperty( past, st.COLUMN_NAME ) );
353 // buf.append( " from table " );
354 // buf.append( extractStringProperty( past, st.TABLE_NAME ) );
355 // buf.append( " to table " );
356 // buf.append( extractStringProperty( past, st.TABLE_NAME ) );
357 // throw SQLException( buf.makeStringAndClear() );
358 // }
360 // OUString tableName = extractStringProperty( past, st.TABLE_NAME );
361 // OUString schemaName = extractStringProperty( past, st.SCHEMA_NAME );
362 OUString pastColumnName = extractStringProperty( past, st.NAME );
363 OUString futureColumnName = extractStringProperty( future, st.NAME );
364 OUString pastTypeName = sqltype2string( past );
365 OUString futureTypeName = sqltype2string( future );
367 TransactionGuard transaction( stmt );
369 OUStringBuffer buf( 128 );
370 if( ! pastColumnName.getLength())
372 // create a new column
373 buf.append( "ALTER TABLE" );
374 bufferQuoteQualifiedIdentifier( buf, schemaName, tableName, settings );
375 buf.append( "ADD COLUMN" );
376 bufferQuoteIdentifier( buf, futureColumnName, settings );
377 buf.append( futureTypeName );
378 transaction.executeUpdate( buf.makeStringAndClear() );
380 else
382 if( pastTypeName != futureTypeName )
384 throw RuntimeException(
385 u"Can't modify column types, drop the column and create a new one"_ustr );
388 if( pastColumnName != futureColumnName )
390 buf.append( "ALTER TABLE" );
391 bufferQuoteQualifiedIdentifier( buf, schemaName, tableName, settings );
392 buf.append( "RENAME COLUMN" );
393 bufferQuoteIdentifier( buf, pastColumnName, settings );
394 buf.append( "TO" );
395 bufferQuoteIdentifier( buf, futureColumnName, settings );
396 transaction.executeUpdate( buf.makeStringAndClear() );
400 OUString futureDefaultValue = extractStringProperty( future, st.DEFAULT_VALUE );
401 OUString pastDefaultValue = extractStringProperty( past, st.DEFAULT_VALUE );
402 if( futureDefaultValue != pastDefaultValue )
404 buf.truncate();
405 buf.append( "ALTER TABLE" );
406 bufferQuoteQualifiedIdentifier( buf, schemaName, tableName, settings );
407 buf.append( "ALTER COLUMN" );
408 bufferQuoteIdentifier( buf, futureColumnName, settings );
409 // LEM TODO: check out
410 // default value is not quoted, caller needs to quote himself (otherwise
411 // how to pass e.g. nextval('something' ) ????
412 buf.append( "SET DEFAULT " + futureDefaultValue );
413 // bufferQuoteConstant( buf, defaultValue, encoding );
414 transaction.executeUpdate( buf.makeStringAndClear() );
417 sal_Int32 futureNullable = extractIntProperty( future, st.IS_NULLABLE );
418 sal_Int32 pastNullable = extractIntProperty( past, st.IS_NULLABLE );
419 if( futureNullable != pastNullable )
421 buf.truncate();
422 buf.append( "ALTER TABLE" );
423 bufferQuoteQualifiedIdentifier( buf, schemaName, tableName, settings );
424 buf.append( "ALTER COLUMN" );
425 bufferQuoteIdentifier( buf, futureColumnName, settings );
426 if( futureNullable == css::sdbc::ColumnValue::NO_NULLS )
428 buf.append( "SET" );
430 else
432 buf.append( "DROP" );
434 buf.append( " NOT NULL" );
435 transaction.executeUpdate( buf.makeStringAndClear() );
438 // OUString futureComment = extractStringProperty( future, st.HELP_TEXT );
439 // OUString pastComment = extractStringProperty( past, st.HELP_TEXT );
440 // printf( "past Comment %s, futureComment %s\n",
441 // OUStringToOString( pastComment, RTL_TEXTENCODING_ASCII_US ).getStr(),
442 // OUStringToOString( futureComment, RTL_TEXTENCODING_ASCII_US ).getStr() );
443 OUString futureComment = extractStringProperty( future, st.DESCRIPTION );
444 OUString pastComment = extractStringProperty( past, st.DESCRIPTION );
446 if( futureComment != pastComment )
448 buf.truncate();
449 buf.append( "COMMENT ON COLUMN" );
450 bufferQuoteQualifiedIdentifier( buf, schemaName, tableName , futureColumnName, settings );
451 buf.append( "IS " );
452 bufferQuoteConstant( buf, futureComment, settings );
453 transaction.executeUpdate( buf.makeStringAndClear() );
455 transaction.commit();
458 void Columns::appendByDescriptor(
459 const css::uno::Reference< css::beans::XPropertySet >& future )
461 osl::MutexGuard guard( m_xMutex->GetMutex() );
462 Statics & st = getStatics();
463 Reference< XPropertySet > past = createDataDescriptor();
464 past->setPropertyValue( st.IS_NULLABLE, Any( css::sdbc::ColumnValue::NULLABLE ) );
465 alterColumnByDescriptor(
466 m_schemaName, m_tableName, m_pSettings, m_origin->createStatement() , past, future );
468 refresh();
471 // void Columns::dropByName( const OUString& elementName )
472 // throw (css::sdbc::SQLException,
473 // css::container::NoSuchElementException,
474 // css::uno::RuntimeException)
475 // {
476 // String2IntMap::const_iterator ii = m_name2index.find( elementName );
477 // if( ii == m_name2index.end() )
478 // {
479 // OUStringBuffer buf( 128 );
480 // buf.appendAscii( "Column " );
481 // buf.append( elementName );
482 // buf.appendAscii( " is unknown in table " );
483 // buf.append( m_schemaName );
484 // buf.appendAscii( "." );
485 // buf.append( m_tableName );
486 // buf.appendAscii( ", so it can't be dropped" );
487 // throw css::container::NoSuchElementException(
488 // buf.makeStringAndClear(), *this );
489 // }
490 // dropByIndex( ii->second );
491 // }
493 void Columns::dropByIndex( sal_Int32 index )
495 osl::MutexGuard guard( m_xMutex->GetMutex() );
496 if( index < 0 || o3tl::make_unsigned(index) >= m_values.size() )
498 throw css::lang::IndexOutOfBoundsException(
499 "COLUMNS: Index out of range (allowed 0 to "
500 + OUString::number(m_values.size() -1)
501 + ", got " + OUString::number( index ) + ")",
502 *this );
505 Reference< XPropertySet > set;
506 m_values[index] >>= set;
507 Statics &st = getStatics();
508 OUString name;
509 set->getPropertyValue( st.NAME ) >>= name;
511 OUStringBuffer update( 128 );
512 update.append( "ALTER TABLE ONLY");
513 bufferQuoteQualifiedIdentifier( update, m_schemaName, m_tableName, m_pSettings );
514 update.append( "DROP COLUMN" );
515 bufferQuoteIdentifier( update, name, m_pSettings );
516 Reference< XStatement > stmt = m_origin->createStatement( );
517 DisposeGuard disposeIt( stmt );
518 stmt->executeUpdate( update.makeStringAndClear() );
520 Container::dropByIndex( index );
524 css::uno::Reference< css::beans::XPropertySet > Columns::createDataDescriptor()
526 return new ColumnDescriptor( m_xMutex, m_origin, m_pSettings );
529 Reference< css::container::XNameAccess > Columns::create(
530 const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex,
531 const css::uno::Reference< css::sdbc::XConnection > & origin,
532 ConnectionSettings *pSettings,
533 const OUString &schemaName,
534 const OUString &tableName,
535 rtl::Reference<Columns> *ppColumns)
537 *ppColumns = new Columns(
538 refMutex, origin, pSettings, schemaName, tableName );
539 (*ppColumns)->refresh();
541 return *ppColumns;
545 ColumnDescriptors::ColumnDescriptors(
546 const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex,
547 const css::uno::Reference< css::sdbc::XConnection > & origin,
548 ConnectionSettings *pSettings )
549 : Container( refMutex, origin, pSettings, u"COLUMN-DESCRIPTOR"_ustr )
553 Reference< css::beans::XPropertySet > ColumnDescriptors::createDataDescriptor()
555 return new ColumnDescriptor( m_xMutex, m_origin, m_pSettings );
560 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */