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 <rtl/ustrbuf.hxx>
38 #include <rtl/strbuf.hxx>
40 #include <com/sun/star/sdbc/XRow.hpp>
41 #include <com/sun/star/sdbc/XParameters.hpp>
42 #include <com/sun/star/sdbc/DataType.hpp>
43 #include <com/sun/star/sdbc/ColumnValue.hpp>
45 #include <cppuhelper/implbase1.hxx>
47 #include "pq_xcolumns.hxx"
48 #include "pq_xcolumn.hxx"
49 #include "pq_statics.hxx"
50 #include "pq_tools.hxx"
52 using osl::MutexGuard
;
55 using com::sun::star::beans::XPropertySet
;
56 using com::sun::star::beans::XPropertyChangeListener
;
57 using com::sun::star::beans::PropertyChangeEvent
;
59 using com::sun::star::uno::Any
;
60 using com::sun::star::uno::makeAny
;
61 using com::sun::star::uno::UNO_QUERY
;
62 using com::sun::star::uno::Type
;
63 using com::sun::star::uno::XInterface
;
64 using com::sun::star::uno::Reference
;
65 using com::sun::star::uno::Sequence
;
66 using com::sun::star::uno::RuntimeException
;
68 using com::sun::star::container::NoSuchElementException
;
69 using com::sun::star::lang::WrappedTargetException
;
71 using com::sun::star::sdbc::XRow
;
72 using com::sun::star::sdbc::XCloseable
;
73 using com::sun::star::sdbc::XStatement
;
74 using com::sun::star::sdbc::XResultSet
;
75 using com::sun::star::sdbc::XParameters
;
76 using com::sun::star::sdbc::XPreparedStatement
;
77 using com::sun::star::sdbc::XDatabaseMetaData
;
78 using com::sun::star::sdbc::SQLException
;
80 namespace pq_sdbc_driver
83 static Any
isCurrency( const OUString
& typeName
)
85 sal_Bool b
= typeName
.equalsIgnoreAsciiCase("money");
86 return Any( &b
, getBooleanCppuType() );
89 // static sal_Bool isAutoIncrement8( const OUString & typeName )
91 // return typeName.equalsIgnoreAsciiCase("serial8") ||
92 // typeName.equalsIgnoreAsciiCase("bigserial");
95 static Any
isAutoIncrement( const OUString
& defaultValue
)
97 sal_Bool ret
= defaultValue
.startsWith( "nextval(" );
99 // OUStringToOString(defaultValue, RTL_TEXTENCODING_ASCII_US).getStr(),
102 // static const char * const serials[] =
104 // "serial", "serial4", "serial8", "bigserial", 0
106 // s sal_Bool b = sal_False;
107 // for( int i = 0; !b && serials[i] ; i ++ )
109 // b = b || typeName.equalsIgnoreAsciiCaseAscii( serials[i] );
111 return Any ( &ret
, getBooleanCppuType() );
115 const ::rtl::Reference
< RefCountedMutex
> & refMutex
,
116 const ::com::sun::star::uno::Reference
< com::sun::star::sdbc::XConnection
> & origin
,
117 ConnectionSettings
*pSettings
,
118 const OUString
&schemaName
,
119 const OUString
&tableName
)
120 : Container( refMutex
, origin
, pSettings
, "COLUMN" ),
121 m_schemaName( schemaName
),
122 m_tableName( tableName
)
128 OUString
columnMetaData2SDBCX(
129 ReflectionBase
*pBase
, const com::sun::star::uno::Reference
< com::sun::star::sdbc::XRow
> &xRow
)
131 Statics
& st
= getStatics();
133 // 1. TABLE_CAT string => table catalog (may be NULL)
135 // 2. TABLE_SCHEM string => table schema (may be NULL)
136 // => pg_namespace.nspname
137 // 3. TABLE_NAME string => table name
138 // => pg_class.relname
139 // 4. COLUMN_NAME string => column name
140 // => pg_attribure.attname
141 // 5. DATA_TYPE short => SQL type from java.sql.Types
142 // => pg_type.typname => sdbc.DataType
143 // 6. TYPE_NAME string => Data source dependent type name, for a UDT the
144 // type name is fully qualified
145 // => pg_type.typname
146 // 7. COLUMN_SIZE long => column size. For char or date types this is
147 // the maximum number of characters, for numeric
148 // or decimal types this is precision.
149 // => pg_type.typlen ( TODO: What is about variable size ? )
150 // 8. BUFFER_LENGTH is not used.
152 // 9. DECIMAL_DIGITS long => the number of fractional digits
153 // => don't know ! TODO !
154 // 10. NUM_PREC_RADIX long => Radix (typically either 10 or 2)
156 // 11. NULLABLE long => is NULL allowed?
157 // NO_NULLS - might not allow NULL values
158 // NULABLE - definitely allows NULL values
159 // NULLABLE_UNKNOWN - nullability unknown
160 // => pg_attribute.attnotnull
161 // 12. REMARKS string => comment describing column (may be NULL )
162 // => Don't know, there does not seem to exist something like
164 // 13. COLUMN_DEF string => default value (may be NULL)
165 // => pg_type.typdefault
166 // 14. SQL_DATA_TYPE long => unused
168 // 15. SQL_DATETIME_SUB long => unused
170 // 16. CHAR_OCTET_LENGTH long => for char types the maximum number of
171 // bytes in the column
173 // 17. ORDINAL_POSITION int => index of column in table (starting at 1)
174 // pg_attribute.attnum
175 // 18. IS_NULLABLE string => "NO" means column definitely does not allow
176 // NULL values; "YES" means the column might
177 // allow NULL values. An empty string means
179 // => pg_attribute.attnotnull
181 static const int COLUMN_NAME
= 4;
182 static const int DATA_TYPE
= 5;
183 static const int TYPE_NAME
= 6;
184 static const int COLUMN_SIZE
= 7;
185 static const int DECIMAL_DIGITS
= 9;
186 static const int IS_NULLABLE
= 11;
187 static const int DESCRIPTION
= 12;
188 static const int DEFAULT_VALUE
= 13;
190 OUString name
= xRow
->getString( COLUMN_NAME
);
191 OUString typeName
= xRow
->getString( TYPE_NAME
);
193 pBase
->setPropertyValue_NoBroadcast_public(
194 st
.NAME
, makeAny( name
) );
196 pBase
->setPropertyValue_NoBroadcast_public(
197 st
.TYPE
, makeAny( xRow
->getInt( DATA_TYPE
) ) );
199 pBase
->setPropertyValue_NoBroadcast_public(
200 st
.TYPE_NAME
, makeAny( typeName
) );
202 pBase
->setPropertyValue_NoBroadcast_public(
203 st
.PRECISION
, makeAny( xRow
->getInt( COLUMN_SIZE
) ) );
205 pBase
->setPropertyValue_NoBroadcast_public(
206 st
.SCALE
, makeAny( xRow
->getInt( DECIMAL_DIGITS
) ) );
208 pBase
->setPropertyValue_NoBroadcast_public(
209 st
.IS_NULLABLE
, makeAny( xRow
->getInt( IS_NULLABLE
) ) );
211 pBase
->setPropertyValue_NoBroadcast_public(
212 st
.DEFAULT_VALUE
, makeAny( xRow
->getString( DEFAULT_VALUE
) ) );
214 // pBase->setPropertyValue_NoBroadcast_public(
215 // st.DESCRIPTION, makeAny( xRow->getString( DESCRIPTION ) ) );
217 // if( pBase->getPropertySetInfo()->hasPropertyByName( st.HELP_TEXT ) )
218 // pBase->setPropertyValue_NoBroadcast_public(
219 // st.HELP_TEXT, makeAny( xRow->getString( DESCRIPTION ) ) );
220 // else // for key columns, etc. ...
221 pBase
->setPropertyValue_NoBroadcast_public(
222 st
.DESCRIPTION
, makeAny( xRow
->getString( DESCRIPTION
) ) );
225 // maybe a better criterion than the type name can be found in future
226 pBase
->setPropertyValue_NoBroadcast_public(
227 st
.IS_AUTO_INCREMENT
, isAutoIncrement(xRow
->getString( DEFAULT_VALUE
)) );
229 pBase
->setPropertyValue_NoBroadcast_public(
230 st
.IS_CURRENCY
, isCurrency( typeName
));
235 // class CommentChanger : public cppu::WeakImplHelper1< XPropertyChangeListener >
237 // ::rtl::Reference< RefCountedMutex > m_refMutex;
238 // ::com::sun::star::uno::Reference< com::sun::star::sdbc::XConnection > m_connection;
239 // ConnectionSettings *m_pSettings;
240 // OUString m_schema;
242 // OUString m_column;
246 // const ::rtl::Reference< RefCountedMutex > & refMutex,
247 // const ::com::sun::star::uno::Reference< com::sun::star::sdbc::XConnection > & connection,
248 // ConnectionSettings *pSettings,
249 // const OUString & schema,
250 // const OUString & table,
251 // const OUString & column ) :
252 // m_refMutex( refMutex ),
253 // m_connection( connection ),
254 // m_pSettings( pSettings ),
255 // m_schema ( schema ),
256 // m_table ( table ),
257 // m_column ( column )
262 // virtual void SAL_CALL disposing( const ::com::sun::star::lang::EventObject& Source ) throw (::com::sun::star::uno::RuntimeException)
264 // osl::MutexGuard guard( m_refMutex->mutex );
265 // m_connection.clear();
268 // virtual void SAL_CALL propertyChange( const ::com::sun::star::beans::PropertyChangeEvent& evt ) throw (::com::sun::star::uno::RuntimeException)
270 // osl::MutexGuard guard( m_refMutex->mutex );
271 // OUStringBuffer buf( 128 );
273 // evt.NewValue >>= comment;
274 // buf.append( "COMMENT ON COLUMN" );
275 // bufferQuoteQualifiedIdentifier( buf, m_schema, m_table , m_column );
276 // buf.append( "IS " );
277 // bufferQuoteConstant( buf, comment,m_pSettings->encoding);
279 // printf( "changing comment of column %s to %s\n",
280 // OUStringToOString( m_column, RTL_TEXTENCODING_ASCII_US ).getStr(),
281 // OUStringToOString( comment, RTL_TEXTENCODING_ASCII_US ).getStr() );
283 // m_connection->createStatement()->executeUpdate( buf.makeStringAndClear() );
287 void Columns::refresh()
288 throw (::com::sun::star::uno::RuntimeException
, std::exception
)
292 if( isLog( m_pSettings
, LogLevel::INFO
) )
295 buf
.append( "sdbcx.Columns get refreshed for table " );
296 buf
.append( OUStringToOString( m_schemaName
, m_pSettings
->encoding
) );
298 buf
.append( OUStringToOString( m_tableName
, m_pSettings
->encoding
) );
299 log( m_pSettings
, LogLevel::INFO
, buf
.makeStringAndClear().getStr() );
301 osl::MutexGuard
guard( m_refMutex
->mutex
);
303 Statics
&st
= getStatics();
304 Reference
< XDatabaseMetaData
> meta
= m_origin
->getMetaData();
306 Reference
< XResultSet
> rs
=
307 meta
->getColumns( Any(), m_schemaName
, m_tableName
, st
.cPERCENT
);
309 DisposeGuard
disposeIt( rs
);
310 Reference
< XRow
> xRow( rs
, UNO_QUERY
);
314 m_values
= Sequence
< com::sun::star::uno::Any
> ();
319 new Column( m_refMutex
, m_origin
, m_pSettings
);
320 Reference
< com::sun::star::beans::XPropertySet
> prop
= pColumn
;
322 OUString name
= columnMetaData2SDBCX( pColumn
, xRow
);
323 // pColumn->addPropertyChangeListener(
325 // new CommentChanger(
334 const int currentColumnIndex
= columnIndex
++;
335 assert(currentColumnIndex
== m_values
.getLength());
336 m_values
.realloc( columnIndex
);
337 m_values
[currentColumnIndex
] = makeAny( prop
);
338 map
[ name
] = currentColumnIndex
;
341 m_name2index
.swap( map
);
343 catch ( com::sun::star::sdbc::SQLException
& e
)
345 throw RuntimeException( e
.Message
, e
.Context
);
347 fire( RefreshedBroadcaster( *this ) );
351 void alterColumnByDescriptor(
352 const OUString
& schemaName
,
353 const OUString
& tableName
,
354 ConnectionSettings
*settings
,
355 const Reference
< XStatement
> &stmt
,
356 const com::sun::star::uno::Reference
< com::sun::star::beans::XPropertySet
> & past
,
357 const com::sun::star::uno::Reference
< com::sun::star::beans::XPropertySet
> & future
)
359 Statics
& st
= getStatics();
361 // if( past->getPropertyValue( st.TABLE_NAME ) != future->getPropertyValue( st.TABLE_NAME ) ||
362 // past->getPropertyValue( st.SCHEMA_NAME ) != future->getPropertyValue( st.SCHEMA_NAME ))
364 // OUStringBuffer buf(128);
365 // buf.append( "Can't move column " );
366 // buf.append( extractStringProperty( past, st.COLUMN_NAME ) );
367 // buf.append( " from table " );
368 // buf.append( extractStringProperty( past, st.TABLE_NAME ) );
369 // buf.append( " to table " );
370 // buf.append( extractStringProperty( past, st.TABLE_NAME ) );
371 // throw SQLException( buf.makeStringAndClear(), Reference< XInterface > () );
374 // OUString tableName = extractStringProperty( past, st.TABLE_NAME );
375 // OUString schemaName = extractStringProperty( past, st.SCHEMA_NAME );
376 OUString pastColumnName
= extractStringProperty( past
, st
.NAME
);
377 OUString futureColumnName
= extractStringProperty( future
, st
.NAME
);
378 OUString pastTypeName
= sqltype2string( past
);
379 OUString futureTypeName
= sqltype2string( future
);
381 TransactionGuard
transaction( stmt
);
383 OUStringBuffer
buf( 128 );
384 if( ! pastColumnName
.getLength())
386 // create a new column
387 buf
.append( "ALTER TABLE" );
388 bufferQuoteQualifiedIdentifier( buf
, schemaName
, tableName
, settings
);
389 buf
.append( "ADD COLUMN" );
390 bufferQuoteIdentifier( buf
, futureColumnName
, settings
);
391 buf
.append( futureTypeName
);
392 transaction
.executeUpdate( buf
.makeStringAndClear() );
396 if( pastTypeName
!= futureTypeName
)
398 throw RuntimeException(
399 "Can't modify column types, drop the column and create a new one",
400 Reference
< XInterface
> () );
403 if( pastColumnName
!= futureColumnName
)
405 buf
.append( "ALTER TABLE" );
406 bufferQuoteQualifiedIdentifier( buf
, schemaName
, tableName
, settings
);
407 buf
.append( "RENAME COLUMN" );
408 bufferQuoteIdentifier( buf
, pastColumnName
, settings
);
410 bufferQuoteIdentifier( buf
, futureColumnName
, settings
);
411 transaction
.executeUpdate( buf
.makeStringAndClear() );
415 OUString futureDefaultValue
= extractStringProperty( future
, st
.DEFAULT_VALUE
);
416 OUString pastDefaultValue
= extractStringProperty( past
, st
.DEFAULT_VALUE
);
417 if( futureDefaultValue
!= pastDefaultValue
)
419 buf
= OUStringBuffer( 128 );
420 buf
.append( "ALTER TABLE" );
421 bufferQuoteQualifiedIdentifier( buf
, schemaName
, tableName
, settings
);
422 buf
.append( "ALTER COLUMN" );
423 bufferQuoteIdentifier( buf
, futureColumnName
, settings
);
424 buf
.append( "SET DEFAULT " );
425 // LEM TODO: check out
426 // default value is not quoted, caller needs to quote himself (otherwise
427 // how to pass e.g. nextval('something' ) ????
428 buf
.append( futureDefaultValue
);
429 // bufferQuoteConstant( buf, defaultValue, encoding );
430 transaction
.executeUpdate( buf
.makeStringAndClear() );
433 sal_Int32 futureNullable
= extractIntProperty( future
, st
.IS_NULLABLE
);
434 sal_Int32 pastNullable
= extractIntProperty( past
, st
.IS_NULLABLE
);
435 if( futureNullable
!= pastNullable
)
437 buf
= OUStringBuffer( 128 );
438 buf
.append( "ALTER TABLE" );
439 bufferQuoteQualifiedIdentifier( buf
, schemaName
, tableName
, settings
);
440 buf
.append( "ALTER COLUMN" );
441 bufferQuoteIdentifier( buf
, futureColumnName
, settings
);
442 if( futureNullable
== com::sun::star::sdbc::ColumnValue::NO_NULLS
)
448 buf
.append( "DROP" );
450 buf
.append( " NOT NULL" );
451 transaction
.executeUpdate( buf
.makeStringAndClear() );
454 // OUString futureComment = extractStringProperty( future, st.HELP_TEXT );
455 // OUString pastComment = extractStringProperty( past, st.HELP_TEXT );
456 // printf( "past Comment %s, futureComment %s\n",
457 // OUStringToOString( pastComment, RTL_TEXTENCODING_ASCII_US ).getStr(),
458 // OUStringToOString( futureComment, RTL_TEXTENCODING_ASCII_US ).getStr() );
459 OUString futureComment
= extractStringProperty( future
, st
.DESCRIPTION
);
460 OUString pastComment
= extractStringProperty( past
, st
.DESCRIPTION
);
462 if( futureComment
!= pastComment
)
464 buf
= OUStringBuffer( 128 );
465 buf
.append( "COMMENT ON COLUMN" );
466 bufferQuoteQualifiedIdentifier( buf
, schemaName
, tableName
, futureColumnName
, settings
);
468 bufferQuoteConstant( buf
, futureComment
, settings
);
469 transaction
.executeUpdate( buf
.makeStringAndClear() );
471 transaction
.commit();
474 void Columns::appendByDescriptor(
475 const ::com::sun::star::uno::Reference
< ::com::sun::star::beans::XPropertySet
>& future
)
476 throw (::com::sun::star::sdbc::SQLException
,
477 ::com::sun::star::container::ElementExistException
,
478 ::com::sun::star::uno::RuntimeException
, std::exception
)
480 osl::MutexGuard
guard( m_refMutex
->mutex
);
481 Statics
& st
= getStatics();
482 Reference
< XPropertySet
> past
= createDataDescriptor();
483 past
->setPropertyValue( st
.IS_NULLABLE
, makeAny( com::sun::star::sdbc::ColumnValue::NULLABLE
) );
484 alterColumnByDescriptor(
485 m_schemaName
, m_tableName
, m_pSettings
, m_origin
->createStatement() , past
, future
);
490 // void Columns::dropByName( const OUString& elementName )
491 // throw (::com::sun::star::sdbc::SQLException,
492 // ::com::sun::star::container::NoSuchElementException,
493 // ::com::sun::star::uno::RuntimeException)
495 // String2IntMap::const_iterator ii = m_name2index.find( elementName );
496 // if( ii == m_name2index.end() )
498 // OUStringBuffer buf( 128 );
499 // buf.appendAscii( "Column " );
500 // buf.append( elementName );
501 // buf.appendAscii( " is unknown in table " );
502 // buf.append( m_schemaName );
503 // buf.appendAscii( "." );
504 // buf.append( m_tableName );
505 // buf.appendAscii( ", so it can't be dropped" );
506 // throw com::sun::star::container::NoSuchElementException(
507 // buf.makeStringAndClear(), *this );
509 // dropByIndex( ii->second );
512 void Columns::dropByIndex( sal_Int32 index
)
513 throw (::com::sun::star::sdbc::SQLException
,
514 ::com::sun::star::lang::IndexOutOfBoundsException
,
515 ::com::sun::star::uno::RuntimeException
, std::exception
)
517 osl::MutexGuard
guard( m_refMutex
->mutex
);
518 if( index
< 0 || index
>= m_values
.getLength() )
520 OUStringBuffer
buf( 128 );
521 buf
.appendAscii( "COLUMNS: Index out of range (allowed 0 to " );
522 buf
.append((sal_Int32
)(m_values
.getLength() -1) );
523 buf
.appendAscii( ", got " );
525 buf
.appendAscii( ")" );
526 throw com::sun::star::lang::IndexOutOfBoundsException(
527 buf
.makeStringAndClear(), *this );
530 Reference
< XPropertySet
> set
;
531 m_values
[index
] >>= set
;
532 Statics
&st
= getStatics();
534 set
->getPropertyValue( st
.NAME
) >>= name
;
536 OUStringBuffer
update( 128 );
537 update
.appendAscii( "ALTER TABLE ONLY");
538 bufferQuoteQualifiedIdentifier( update
, m_schemaName
, m_tableName
, m_pSettings
);
539 update
.appendAscii( "DROP COLUMN" );
540 bufferQuoteIdentifier( update
, name
, m_pSettings
);
541 Reference
< XStatement
> stmt
= m_origin
->createStatement( );
542 DisposeGuard
disposeIt( stmt
);
543 stmt
->executeUpdate( update
.makeStringAndClear() );
545 Container::dropByIndex( index
);
549 ::com::sun::star::uno::Reference
< ::com::sun::star::beans::XPropertySet
> Columns::createDataDescriptor()
550 throw (::com::sun::star::uno::RuntimeException
, std::exception
)
552 return new ColumnDescriptor( m_refMutex
, m_origin
, m_pSettings
);
555 Reference
< com::sun::star::container::XNameAccess
> Columns::create(
556 const ::rtl::Reference
< RefCountedMutex
> & refMutex
,
557 const ::com::sun::star::uno::Reference
< com::sun::star::sdbc::XConnection
> & origin
,
558 ConnectionSettings
*pSettings
,
559 const OUString
&schemaName
,
560 const OUString
&tableName
,
563 *ppColumns
= new Columns(
564 refMutex
, origin
, pSettings
, schemaName
, tableName
);
565 Reference
< com::sun::star::container::XNameAccess
> ret
= *ppColumns
;
566 (*ppColumns
)->refresh();
573 ColumnDescriptors::ColumnDescriptors(
574 const ::rtl::Reference
< RefCountedMutex
> & refMutex
,
575 const ::com::sun::star::uno::Reference
< com::sun::star::sdbc::XConnection
> & origin
,
576 ConnectionSettings
*pSettings
)
577 : Container( refMutex
, origin
, pSettings
, "COLUMN-DESCRIPTOR" )
581 Reference
< ::com::sun::star::beans::XPropertySet
> ColumnDescriptors::createDataDescriptor()
582 throw (::com::sun::star::uno::RuntimeException
, std::exception
)
584 return new ColumnDescriptor( m_refMutex
, m_origin
, m_pSettings
);
589 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */