Version 4.3.0.0.beta1, tag libreoffice-4.3.0.0.beta1
[LibreOffice.git] / connectivity / source / drivers / postgresql / pq_xcolumns.cxx
blob11762484efcdca4f35f49055ad15554cf78cb38e
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 <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 )
90 // {
91 // return typeName.equalsIgnoreAsciiCase("serial8") ||
92 // typeName.equalsIgnoreAsciiCase("bigserial");
93 // }
95 static Any isAutoIncrement( const OUString & defaultValue )
97 sal_Bool ret = defaultValue.startsWith( "nextval(" );
98 // printf( "%s %d\n",
99 // OUStringToOString(defaultValue, RTL_TEXTENCODING_ASCII_US).getStr(),
100 // ret );
101 // {
102 // static const char * const serials[] =
103 // {
104 // "serial", "serial4", "serial8", "bigserial", 0
105 // };
106 // s sal_Bool b = sal_False;
107 // for( int i = 0; !b && serials[i] ; i ++ )
108 // {
109 // b = b || typeName.equalsIgnoreAsciiCaseAscii( serials[i] );
110 // }
111 return Any ( &ret, getBooleanCppuType() );
114 Columns::Columns(
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 )
125 Columns::~Columns()
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)
134 // => not supported
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.
151 // => 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)
155 // => TODO ??
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
163 // that in postgres
164 // 13. COLUMN_DEF string => default value (may be NULL)
165 // => pg_type.typdefault
166 // 14. SQL_DATA_TYPE long => unused
167 // => empty
168 // 15. SQL_DATETIME_SUB long => unused
169 // => empty
170 // 16. CHAR_OCTET_LENGTH long => for char types the maximum number of
171 // bytes in the column
172 // => pg_type.typlen
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
178 // nobody knows.
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));
231 return name;
235 // class CommentChanger : public cppu::WeakImplHelper1< XPropertyChangeListener >
236 // {
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;
241 // OUString m_table;
242 // OUString m_column;
244 // public:
245 // CommentChanger(
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 )
258 // {}
261 // // Methods
262 // virtual void SAL_CALL disposing( const ::com::sun::star::lang::EventObject& Source ) throw (::com::sun::star::uno::RuntimeException)
263 // {
264 // osl::MutexGuard guard( m_refMutex->mutex );
265 // m_connection.clear();
266 // }
267 // // Methods
268 // virtual void SAL_CALL propertyChange( const ::com::sun::star::beans::PropertyChangeEvent& evt ) throw (::com::sun::star::uno::RuntimeException)
269 // {
270 // osl::MutexGuard guard( m_refMutex->mutex );
271 // OUStringBuffer buf( 128 );
272 // OUString comment;
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() );
284 // }
285 // };
287 void Columns::refresh()
288 throw (::com::sun::star::uno::RuntimeException, std::exception)
292 if( isLog( m_pSettings, LogLevel::INFO ) )
294 OStringBuffer buf;
295 buf.append( "sdbcx.Columns get refreshed for table " );
296 buf.append( OUStringToOString( m_schemaName, m_pSettings->encoding ) );
297 buf.append( "." );
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 );
312 String2IntMap map;
314 m_values = Sequence< com::sun::star::uno::Any > ();
315 int columnIndex = 0;
316 while( rs->next() )
318 Column * pColumn =
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(
324 // st.HELP_TEXT,
325 // new CommentChanger(
326 // m_refMutex,
327 // m_origin,
328 // m_pSettings,
329 // m_schemaName,
330 // m_tableName,
331 // name ) );
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 ))
363 // {
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 > () );
372 // }
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() );
394 else
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 );
409 buf.append( "TO" );
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 )
444 buf.append( "SET" );
446 else
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 );
467 buf.append( "IS " );
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 );
487 refresh();
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)
494 // {
495 // String2IntMap::const_iterator ii = m_name2index.find( elementName );
496 // if( ii == m_name2index.end() )
497 // {
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 );
508 // }
509 // dropByIndex( ii->second );
510 // }
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 " );
524 buf.append( index );
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();
533 OUString name;
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,
561 Columns **ppColumns)
563 *ppColumns = new Columns(
564 refMutex, origin, pSettings, schemaName, tableName );
565 Reference< com::sun::star::container::XNameAccess > ret = *ppColumns;
566 (*ppColumns)->refresh();
568 return ret;
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: */