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,
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>
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 )
86 // return typeName.equalsIgnoreAsciiCase("serial8") ||
87 // typeName.equalsIgnoreAsciiCase("bigserial");
90 static Any
isAutoIncrement( std::u16string_view defaultValue
)
92 bool ret
= o3tl::starts_with( defaultValue
, u
"nextval(" );
94 // OUStringToOString(defaultValue, RTL_TEXTENCODING_ASCII_US).getStr(),
97 // static const char * const serials[] =
99 // "serial", "serial4", "serial8", "bigserial", 0
101 // s sal_Bool b = sal_False;
102 // for( int i = 0; !b && serials[i] ; i ++ )
104 // b = b || typeName.equalsIgnoreAsciiCaseAscii( serials[i] );
110 const ::rtl::Reference
< comphelper::RefCountedMutex
> & refMutex
,
111 const css::uno::Reference
< css::sdbc::XConnection
> & origin
,
112 ConnectionSettings
*pSettings
,
115 : Container( refMutex
, origin
, pSettings
, u
"COLUMN"_ustr
),
116 m_schemaName(std::move( schemaName
)),
117 m_tableName(std::move( tableName
))
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)
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.
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)
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
159 // 13. COLUMN_DEF string => default value (may be NULL)
160 // => pg_type.typdefault
161 // 14. SQL_DATA_TYPE long => unused
163 // 15. SQL_DATETIME_SUB long => unused
165 // 16. CHAR_OCTET_LENGTH long => for char types the maximum number of
166 // bytes in the column
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
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
));
230 // class CommentChanger : public cppu::WeakImplHelper< XPropertyChangeListener >
232 // ::rtl::Reference< comphelper::RefCountedMutex > m_xMutex;
233 // css::uno::Reference< css::sdbc::XConnection > m_connection;
234 // ConnectionSettings *m_pSettings;
235 // OUString m_schema;
237 // OUString m_column;
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 )
257 // virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) throw (css::uno::RuntimeException)
259 // osl::MutexGuard guard( m_xMutex->GetMutex() );
260 // m_connection.clear();
263 // virtual void SAL_CALL propertyChange( const css::beans::PropertyChangeEvent& evt ) throw (css::uno::RuntimeException)
265 // osl::MutexGuard guard( m_xMutex->GetMutex() );
266 // OUStringBuffer buf( 128 );
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() );
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
);
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(
311 // new CommentChanger(
320 m_values
.emplace_back(prop
);
321 map
[ name
] = 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
,
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 ))
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() );
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() );
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
);
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
)
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
)
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
)
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
)
449 buf
.append( "COMMENT ON COLUMN" );
450 bufferQuoteQualifiedIdentifier( buf
, schemaName
, tableName
, futureColumnName
, settings
);
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
);
471 // void Columns::dropByName( const OUString& elementName )
472 // throw (css::sdbc::SQLException,
473 // css::container::NoSuchElementException,
474 // css::uno::RuntimeException)
476 // String2IntMap::const_iterator ii = m_name2index.find( elementName );
477 // if( ii == m_name2index.end() )
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 );
490 // dropByIndex( ii->second );
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
) + ")",
505 Reference
< XPropertySet
> set
;
506 m_values
[index
] >>= set
;
507 Statics
&st
= getStatics();
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();
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: */