summaryrefslogtreecommitdiffstats
path: root/connectivity/source/drivers/postgresql/pq_xcolumns.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'connectivity/source/drivers/postgresql/pq_xcolumns.cxx')
-rw-r--r--connectivity/source/drivers/postgresql/pq_xcolumns.cxx560
1 files changed, 560 insertions, 0 deletions
diff --git a/connectivity/source/drivers/postgresql/pq_xcolumns.cxx b/connectivity/source/drivers/postgresql/pq_xcolumns.cxx
new file mode 100644
index 000000000..f1d3c0a53
--- /dev/null
+++ b/connectivity/source/drivers/postgresql/pq_xcolumns.cxx
@@ -0,0 +1,560 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * Effective License of whole file:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011:
+ *
+ * The Contents of this file are made available subject to the terms of
+ * the GNU Lesser General Public License Version 2.1
+ *
+ * Copyright: 2000 by Sun Microsystems, Inc.
+ *
+ * Contributor(s): Joerg Budischewski
+ *
+ * All parts contributed on or after August 2011:
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ ************************************************************************/
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/sdbc/ColumnValue.hpp>
+
+#include <cppuhelper/exc_hlp.hxx>
+
+#include "pq_xcolumns.hxx"
+#include "pq_xcolumn.hxx"
+#include "pq_statics.hxx"
+#include "pq_tools.hxx"
+
+using osl::MutexGuard;
+
+
+using com::sun::star::beans::XPropertySet;
+
+using com::sun::star::uno::Any;
+using com::sun::star::uno::UNO_QUERY;
+using com::sun::star::uno::Reference;
+using com::sun::star::uno::RuntimeException;
+
+using com::sun::star::sdbc::XRow;
+using com::sun::star::sdbc::XStatement;
+using com::sun::star::sdbc::XResultSet;
+using com::sun::star::sdbc::XDatabaseMetaData;
+using com::sun::star::sdbc::SQLException;
+
+namespace pq_sdbc_driver
+{
+
+static Any isCurrency( std::u16string_view typeName )
+{
+ return Any( o3tl::equalsIgnoreAsciiCase(typeName, u"money") );
+}
+
+// static sal_Bool isAutoIncrement8( const OUString & typeName )
+// {
+// return typeName.equalsIgnoreAsciiCase("serial8") ||
+// typeName.equalsIgnoreAsciiCase("bigserial");
+// }
+
+static Any isAutoIncrement( std::u16string_view defaultValue )
+{
+ bool ret = o3tl::starts_with( defaultValue, u"nextval(" );
+// printf( "%s %d\n",
+// OUStringToOString(defaultValue, RTL_TEXTENCODING_ASCII_US).getStr(),
+// ret );
+// {
+// static const char * const serials[] =
+// {
+// "serial", "serial4", "serial8", "bigserial", 0
+// };
+// s sal_Bool b = sal_False;
+// for( int i = 0; !b && serials[i] ; i ++ )
+// {
+// b = b || typeName.equalsIgnoreAsciiCaseAscii( serials[i] );
+// }
+ return Any ( ret );
+}
+
+Columns::Columns(
+ const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex,
+ const css::uno::Reference< css::sdbc::XConnection > & origin,
+ ConnectionSettings *pSettings,
+ const OUString &schemaName,
+ const OUString &tableName)
+ : Container( refMutex, origin, pSettings, "COLUMN" ),
+ m_schemaName( schemaName ),
+ m_tableName( tableName )
+{}
+
+Columns::~Columns()
+{}
+
+OUString columnMetaData2SDBCX(
+ ReflectionBase *pBase, const css::uno::Reference< css::sdbc::XRow > &xRow )
+{
+ Statics & st = getStatics();
+
+ // 1. TABLE_CAT string => table catalog (may be NULL)
+ // => not supported
+ // 2. TABLE_SCHEM string => table schema (may be NULL)
+ // => pg_namespace.nspname
+ // 3. TABLE_NAME string => table name
+ // => pg_class.relname
+ // 4. COLUMN_NAME string => column name
+ // => pg_attribute.attname
+ // 5. DATA_TYPE short => SQL type from java.sql.Types
+ // => pg_type.typname => sdbc.DataType
+ // 6. TYPE_NAME string => Data source dependent type name, for a UDT the
+ // type name is fully qualified
+ // => pg_type.typname
+ // 7. COLUMN_SIZE long => column size. For char or date types this is
+ // the maximum number of characters, for numeric
+ // or decimal types this is precision.
+ // => pg_type.typlen ( TODO: What is about variable size ? )
+ // 8. BUFFER_LENGTH is not used.
+ // => not used
+ // 9. DECIMAL_DIGITS long => the number of fractional digits
+ // => don't know ! TODO !
+ // 10. NUM_PREC_RADIX long => Radix (typically either 10 or 2)
+ // => TODO ??
+ // 11. NULLABLE long => is NULL allowed?
+ // NO_NULLS - might not allow NULL values
+ // NULABLE - definitely allows NULL values
+ // NULLABLE_UNKNOWN - nullability unknown
+ // => pg_attribute.attnotnull
+ // 12. REMARKS string => comment describing column (may be NULL )
+ // => Don't know, there does not seem to exist something like
+ // that in postgres
+ // 13. COLUMN_DEF string => default value (may be NULL)
+ // => pg_type.typdefault
+ // 14. SQL_DATA_TYPE long => unused
+ // => empty
+ // 15. SQL_DATETIME_SUB long => unused
+ // => empty
+ // 16. CHAR_OCTET_LENGTH long => for char types the maximum number of
+ // bytes in the column
+ // => pg_type.typlen
+ // 17. ORDINAL_POSITION int => index of column in table (starting at 1)
+ // pg_attribute.attnum
+ // 18. IS_NULLABLE string => "NO" means column definitely does not allow
+ // NULL values; "YES" means the column might
+ // allow NULL values. An empty string means
+ // nobody knows.
+ // => pg_attribute.attnotnull
+
+ static const int COLUMN_NAME = 4;
+ static const int DATA_TYPE = 5;
+ static const int TYPE_NAME = 6;
+ static const int COLUMN_SIZE = 7;
+ static const int DECIMAL_DIGITS = 9;
+ static const int IS_NULLABLE = 11;
+ static const int DESCRIPTION = 12;
+ static const int DEFAULT_VALUE = 13;
+
+ OUString name = xRow->getString( COLUMN_NAME );
+ OUString typeName = xRow->getString( TYPE_NAME );
+
+ pBase->setPropertyValue_NoBroadcast_public(
+ st.NAME, Any( name ) );
+
+ pBase->setPropertyValue_NoBroadcast_public(
+ st.TYPE, Any( xRow->getInt( DATA_TYPE ) ) );
+
+ pBase->setPropertyValue_NoBroadcast_public(
+ st.TYPE_NAME, Any( typeName ) );
+
+ pBase->setPropertyValue_NoBroadcast_public(
+ st.PRECISION, Any( xRow->getInt( COLUMN_SIZE ) ) );
+
+ pBase->setPropertyValue_NoBroadcast_public(
+ st.SCALE, Any( xRow->getInt( DECIMAL_DIGITS ) ) );
+
+ pBase->setPropertyValue_NoBroadcast_public(
+ st.IS_NULLABLE, Any( xRow->getInt( IS_NULLABLE ) ) );
+
+ pBase->setPropertyValue_NoBroadcast_public(
+ st.DEFAULT_VALUE, Any( xRow->getString( DEFAULT_VALUE ) ) );
+
+// pBase->setPropertyValue_NoBroadcast_public(
+// st.DESCRIPTION, makeAny( xRow->getString( DESCRIPTION ) ) );
+
+// if( pBase->getPropertySetInfo()->hasPropertyByName( st.HELP_TEXT ) )
+// pBase->setPropertyValue_NoBroadcast_public(
+// st.HELP_TEXT, makeAny( xRow->getString( DESCRIPTION ) ) );
+// else // for key columns, etc. ...
+ pBase->setPropertyValue_NoBroadcast_public(
+ st.DESCRIPTION, Any( xRow->getString( DESCRIPTION ) ) );
+
+
+ // maybe a better criterion than the type name can be found in future
+ pBase->setPropertyValue_NoBroadcast_public(
+ st.IS_AUTO_INCREMENT, isAutoIncrement(xRow->getString( DEFAULT_VALUE )) );
+
+ pBase->setPropertyValue_NoBroadcast_public(
+ st.IS_CURRENCY, isCurrency( typeName));
+ return name;
+}
+
+
+// class CommentChanger : public cppu::WeakImplHelper< XPropertyChangeListener >
+// {
+// ::rtl::Reference< comphelper::RefCountedMutex > m_xMutex;
+// css::uno::Reference< css::sdbc::XConnection > m_connection;
+// ConnectionSettings *m_pSettings;
+// OUString m_schema;
+// OUString m_table;
+// OUString m_column;
+
+// public:
+// CommentChanger(
+// const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex,
+// const css::uno::Reference< css::sdbc::XConnection > & connection,
+// ConnectionSettings *pSettings,
+// const OUString & schema,
+// const OUString & table,
+// const OUString & column ) :
+// m_xMutex( refMutex ),
+// m_connection( connection ),
+// m_pSettings( pSettings ),
+// m_schema ( schema ),
+// m_table ( table ),
+// m_column ( column )
+// {}
+
+
+// // Methods
+// virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) throw (css::uno::RuntimeException)
+// {
+// osl::MutexGuard guard( m_xMutex->GetMutex() );
+// m_connection.clear();
+// }
+// // Methods
+// virtual void SAL_CALL propertyChange( const css::beans::PropertyChangeEvent& evt ) throw (css::uno::RuntimeException)
+// {
+// osl::MutexGuard guard( m_xMutex->GetMutex() );
+// OUStringBuffer buf( 128 );
+// OUString comment;
+// evt.NewValue >>= comment;
+// buf.append( "COMMENT ON COLUMN" );
+// bufferQuoteQualifiedIdentifier( buf, m_schema, m_table , m_column );
+// buf.append( "IS " );
+// bufferQuoteConstant( buf, comment,m_pSettings->encoding);
+
+// printf( "changing comment of column %s to %s\n",
+// OUStringToOString( m_column, RTL_TEXTENCODING_ASCII_US ).getStr(),
+// OUStringToOString( comment, RTL_TEXTENCODING_ASCII_US ).getStr() );
+
+// m_connection->createStatement()->executeUpdate( buf.makeStringAndClear() );
+// }
+// };
+
+void Columns::refresh()
+{
+ try
+ {
+ SAL_INFO("connectivity.postgresql", "sdbcx.Columns get refreshed for table " << m_schemaName << "." << m_tableName);
+ osl::MutexGuard guard( m_xMutex->GetMutex() );
+
+ Statics &st = getStatics();
+ Reference< XDatabaseMetaData > meta = m_origin->getMetaData();
+
+ Reference< XResultSet > rs =
+ meta->getColumns( Any(), m_schemaName, m_tableName, st.cPERCENT );
+
+ DisposeGuard disposeIt( rs );
+ Reference< XRow > xRow( rs , UNO_QUERY );
+
+ String2IntMap map;
+
+ m_values.clear();
+ int columnIndex = 0;
+ while( rs->next() )
+ {
+ rtl::Reference<Column> pColumn =
+ new Column( m_xMutex, m_origin, m_pSettings );
+ Reference< css::beans::XPropertySet > prop = pColumn;
+
+ OUString name = columnMetaData2SDBCX( pColumn.get(), xRow );
+// pColumn->addPropertyChangeListener(
+// st.HELP_TEXT,
+// new CommentChanger(
+// m_xMutex,
+// m_origin,
+// m_pSettings,
+// m_schemaName,
+// m_tableName,
+// name ) );
+
+ {
+ m_values.push_back( Any( prop ) );
+ map[ name ] = columnIndex;
+ ++columnIndex;
+ }
+ }
+ m_name2index.swap( map );
+ }
+ catch ( css::sdbc::SQLException & e )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException( e.Message,
+ nullptr, anyEx );
+ }
+ fire( RefreshedBroadcaster( *this ) );
+}
+
+
+void alterColumnByDescriptor(
+ std::u16string_view schemaName,
+ std::u16string_view tableName,
+ ConnectionSettings *settings,
+ const Reference< XStatement > &stmt,
+ const css::uno::Reference< css::beans::XPropertySet > & past,
+ const css::uno::Reference< css::beans::XPropertySet > & future)
+{
+ Statics & st = getStatics();
+
+// if( past->getPropertyValue( st.TABLE_NAME ) != future->getPropertyValue( st.TABLE_NAME ) ||
+// past->getPropertyValue( st.SCHEMA_NAME ) != future->getPropertyValue( st.SCHEMA_NAME ))
+// {
+// OUStringBuffer buf(128);
+// buf.append( "Can't move column " );
+// buf.append( extractStringProperty( past, st.COLUMN_NAME ) );
+// buf.append( " from table " );
+// buf.append( extractStringProperty( past, st.TABLE_NAME ) );
+// buf.append( " to table " );
+// buf.append( extractStringProperty( past, st.TABLE_NAME ) );
+// throw SQLException( buf.makeStringAndClear() );
+// }
+
+// OUString tableName = extractStringProperty( past, st.TABLE_NAME );
+// OUString schemaName = extractStringProperty( past, st.SCHEMA_NAME );
+ OUString pastColumnName = extractStringProperty( past, st.NAME );
+ OUString futureColumnName = extractStringProperty( future, st.NAME );
+ OUString pastTypeName = sqltype2string( past );
+ OUString futureTypeName = sqltype2string( future );
+
+ TransactionGuard transaction( stmt );
+
+ OUStringBuffer buf( 128 );
+ if( ! pastColumnName.getLength())
+ {
+ // create a new column
+ buf.append( "ALTER TABLE" );
+ bufferQuoteQualifiedIdentifier( buf, schemaName, tableName, settings );
+ buf.append( "ADD COLUMN" );
+ bufferQuoteIdentifier( buf, futureColumnName, settings );
+ buf.append( futureTypeName );
+ transaction.executeUpdate( buf.makeStringAndClear() );
+ }
+ else
+ {
+ if( pastTypeName != futureTypeName )
+ {
+ throw RuntimeException(
+ "Can't modify column types, drop the column and create a new one" );
+ }
+
+ if( pastColumnName != futureColumnName )
+ {
+ buf.append( "ALTER TABLE" );
+ bufferQuoteQualifiedIdentifier( buf, schemaName, tableName, settings );
+ buf.append( "RENAME COLUMN" );
+ bufferQuoteIdentifier( buf, pastColumnName, settings );
+ buf.append( "TO" );
+ bufferQuoteIdentifier( buf, futureColumnName, settings );
+ transaction.executeUpdate( buf.makeStringAndClear() );
+ }
+ }
+
+ OUString futureDefaultValue = extractStringProperty( future, st.DEFAULT_VALUE );
+ OUString pastDefaultValue = extractStringProperty( past, st.DEFAULT_VALUE );
+ if( futureDefaultValue != pastDefaultValue )
+ {
+ buf.truncate();
+ buf.append( "ALTER TABLE" );
+ bufferQuoteQualifiedIdentifier( buf, schemaName, tableName, settings );
+ buf.append( "ALTER COLUMN" );
+ bufferQuoteIdentifier( buf, futureColumnName, settings );
+ buf.append( "SET DEFAULT " );
+ // LEM TODO: check out
+ // default value is not quoted, caller needs to quote himself (otherwise
+ // how to pass e.g. nextval('something' ) ????
+ buf.append( futureDefaultValue );
+// bufferQuoteConstant( buf, defaultValue, encoding );
+ transaction.executeUpdate( buf.makeStringAndClear() );
+ }
+
+ sal_Int32 futureNullable = extractIntProperty( future, st.IS_NULLABLE );
+ sal_Int32 pastNullable = extractIntProperty( past, st.IS_NULLABLE );
+ if( futureNullable != pastNullable )
+ {
+ buf.truncate();
+ buf.append( "ALTER TABLE" );
+ bufferQuoteQualifiedIdentifier( buf, schemaName, tableName, settings );
+ buf.append( "ALTER COLUMN" );
+ bufferQuoteIdentifier( buf, futureColumnName, settings );
+ if( futureNullable == css::sdbc::ColumnValue::NO_NULLS )
+ {
+ buf.append( "SET" );
+ }
+ else
+ {
+ buf.append( "DROP" );
+ }
+ buf.append( " NOT NULL" );
+ transaction.executeUpdate( buf.makeStringAndClear() );
+ }
+
+// OUString futureComment = extractStringProperty( future, st.HELP_TEXT );
+// OUString pastComment = extractStringProperty( past, st.HELP_TEXT );
+// printf( "past Comment %s, futureComment %s\n",
+// OUStringToOString( pastComment, RTL_TEXTENCODING_ASCII_US ).getStr(),
+// OUStringToOString( futureComment, RTL_TEXTENCODING_ASCII_US ).getStr() );
+ OUString futureComment = extractStringProperty( future, st.DESCRIPTION );
+ OUString pastComment = extractStringProperty( past, st.DESCRIPTION );
+
+ if( futureComment != pastComment )
+ {
+ buf.truncate();
+ buf.append( "COMMENT ON COLUMN" );
+ bufferQuoteQualifiedIdentifier( buf, schemaName, tableName , futureColumnName, settings );
+ buf.append( "IS " );
+ bufferQuoteConstant( buf, futureComment, settings );
+ transaction.executeUpdate( buf.makeStringAndClear() );
+ }
+ transaction.commit();
+}
+
+void Columns::appendByDescriptor(
+ const css::uno::Reference< css::beans::XPropertySet >& future )
+{
+ osl::MutexGuard guard( m_xMutex->GetMutex() );
+ Statics & st = getStatics();
+ Reference< XPropertySet > past = createDataDescriptor();
+ past->setPropertyValue( st.IS_NULLABLE, Any( css::sdbc::ColumnValue::NULLABLE ) );
+ alterColumnByDescriptor(
+ m_schemaName, m_tableName, m_pSettings, m_origin->createStatement() , past, future );
+
+ refresh();
+}
+
+// void Columns::dropByName( const OUString& elementName )
+// throw (css::sdbc::SQLException,
+// css::container::NoSuchElementException,
+// css::uno::RuntimeException)
+// {
+// String2IntMap::const_iterator ii = m_name2index.find( elementName );
+// if( ii == m_name2index.end() )
+// {
+// OUStringBuffer buf( 128 );
+// buf.appendAscii( "Column " );
+// buf.append( elementName );
+// buf.appendAscii( " is unknown in table " );
+// buf.append( m_schemaName );
+// buf.appendAscii( "." );
+// buf.append( m_tableName );
+// buf.appendAscii( ", so it can't be dropped" );
+// throw css::container::NoSuchElementException(
+// buf.makeStringAndClear(), *this );
+// }
+// dropByIndex( ii->second );
+// }
+
+void Columns::dropByIndex( sal_Int32 index )
+{
+ osl::MutexGuard guard( m_xMutex->GetMutex() );
+ if( index < 0 || o3tl::make_unsigned(index) >= m_values.size() )
+ {
+ throw css::lang::IndexOutOfBoundsException(
+ "COLUMNS: Index out of range (allowed 0 to "
+ + OUString::number(m_values.size() -1)
+ + ", got " + OUString::number( index ) + ")",
+ *this );
+ }
+
+ Reference< XPropertySet > set;
+ m_values[index] >>= set;
+ Statics &st = getStatics();
+ OUString name;
+ set->getPropertyValue( st.NAME ) >>= name;
+
+ OUStringBuffer update( 128 );
+ update.append( "ALTER TABLE ONLY");
+ bufferQuoteQualifiedIdentifier( update, m_schemaName, m_tableName, m_pSettings );
+ update.append( "DROP COLUMN" );
+ bufferQuoteIdentifier( update, name, m_pSettings );
+ Reference< XStatement > stmt = m_origin->createStatement( );
+ DisposeGuard disposeIt( stmt );
+ stmt->executeUpdate( update.makeStringAndClear() );
+
+ Container::dropByIndex( index );
+}
+
+
+css::uno::Reference< css::beans::XPropertySet > Columns::createDataDescriptor()
+{
+ return new ColumnDescriptor( m_xMutex, m_origin, m_pSettings );
+}
+
+Reference< css::container::XNameAccess > Columns::create(
+ const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex,
+ const css::uno::Reference< css::sdbc::XConnection > & origin,
+ ConnectionSettings *pSettings,
+ const OUString &schemaName,
+ const OUString &tableName,
+ rtl::Reference<Columns> *ppColumns)
+{
+ *ppColumns = new Columns(
+ refMutex, origin, pSettings, schemaName, tableName );
+ (*ppColumns)->refresh();
+
+ return *ppColumns;
+}
+
+
+ColumnDescriptors::ColumnDescriptors(
+ const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex,
+ const css::uno::Reference< css::sdbc::XConnection > & origin,
+ ConnectionSettings *pSettings )
+ : Container( refMutex, origin, pSettings, "COLUMN-DESCRIPTOR" )
+{}
+
+
+Reference< css::beans::XPropertySet > ColumnDescriptors::createDataDescriptor()
+{
+ return new ColumnDescriptor( m_xMutex, m_origin, m_pSettings );
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */