1
0
Fork 0
libreoffice/connectivity/source/drivers/postgresql/pq_resultsetmetadata.cxx
Daniel Baumann 8e63e14cf6
Adding upstream version 4:25.2.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 16:20:04 +02:00

441 lines
12 KiB
C++

/* -*- 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 <rtl/ustrbuf.hxx>
#include "pq_resultsetmetadata.hxx"
#include "pq_resultset.hxx"
#include "pq_tools.hxx"
#include "pq_statics.hxx"
#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
#include <com/sun/star/sdbc/ColumnValue.hpp>
#include <com/sun/star/sdbc/DataType.hpp>
#include <com/sun/star/sdbc/SQLException.hpp>
#include <com/sun/star/sdbc/XRow.hpp>
#include <utility>
#include <string.h>
using osl::MutexGuard;
using com::sun::star::uno::Any;
using com::sun::star::uno::Exception;
using com::sun::star::uno::Reference;
using com::sun::star::uno::UNO_QUERY;
using com::sun::star::sdbc::SQLException;
using com::sun::star::sdbc::XStatement;
using com::sun::star::sdbc::XRow;
using com::sun::star::sdbc::XResultSet;
using com::sun::star::sdbcx::XColumnsSupplier;
using com::sun::star::sdbcx::XTablesSupplier;
using com::sun::star::beans::XPropertySet;
using com::sun::star::container::XNameAccess;
namespace pq_sdbc_driver
{
// struct ColumnMetaData
// {
// OUString tableName;
// OUString schemaTableName;
// OUString typeName;
// css::sdbc::DataType type;
// sal_Int32 precision;
// sal_Int32 scale;
// sal_Bool isCurrency;
// sal_Bool isNullable;
// sal_Bool isAutoIncrement;
// sal_Bool isReadOnly;
// sal_Bool isSigned;
// };
// is not exported by the postgres header
const int PQ_VARHDRSZ = sizeof( sal_Int32 );
static void extractPrecisionAndScale( sal_Int32 atttypmod, sal_Int32 *precision, sal_Int32 *scale )
{
if( atttypmod < PQ_VARHDRSZ )
{
*precision = 0;
*scale = 0;
}
else
{
if( atttypmod & 0xffff0000 )
{
*precision = ( ( atttypmod - PQ_VARHDRSZ ) >> 16 ) & 0xffff;
*scale = (atttypmod - PQ_VARHDRSZ ) & 0xffff;
}
else
{
*precision = atttypmod - PQ_VARHDRSZ;
*scale = 0;
}
}
}
ResultSetMetaData::ResultSetMetaData(
::rtl::Reference< comphelper::RefCountedMutex > refMutex,
css::uno::Reference< css::sdbc::XResultSet > origin,
ResultSet * pResultSet,
ConnectionSettings **ppSettings,
PGresult const *pResult,
OUString schemaName,
OUString tableName ) :
m_xMutex(std::move( refMutex )),
m_ppSettings( ppSettings ),
m_origin(std::move( origin )),
m_tableName(std::move( tableName )),
m_schemaName(std::move( schemaName )),
m_colDesc( PQnfields( pResult ) ),
m_pResultSet( pResultSet ),
m_checkedForTable( false ),
m_checkedForTypes( false ),
m_colCount( PQnfields( pResult ) )
{
// extract all needed information from the result object, so that we don't
// need it anymore after this call !
for( int col = 0; col < m_colCount ; col ++ )
{
sal_Int32 size = PQfsize( pResult, col );
size = -1 == size ? 25 : size;
m_colDesc[col].displaySize = size;
extractPrecisionAndScale(
PQfmod( pResult, col ),
& ( m_colDesc[col].precision ),
& ( m_colDesc[col].scale ) );
char *name = PQfname( pResult, col );
m_colDesc[col].name = OUString( name, strlen(name) , ConnectionSettings::encoding );
m_colDesc[col].typeOid = PQftype( pResult, col );
m_colDesc[col].type = css::sdbc::DataType::LONGVARCHAR;
}
}
void ResultSetMetaData::checkForTypes()
{
if( m_checkedForTypes )
return;
Reference< XStatement > stmt =
extractConnectionFromStatement( m_origin->getStatement() )->createStatement();
DisposeGuard guard( stmt );
OUStringBuffer buf(128);
buf.append( "SELECT oid, typname, typtype FROM pg_type WHERE ");
for( int i = 0 ; i < m_colCount ; i ++ )
{
if( i > 0 )
buf.append( " OR " );
int oid = m_colDesc[i].typeOid;
buf.append( "oid=" + OUString::number(static_cast<sal_Int32>(oid)) );
}
Reference< XResultSet > rs = stmt->executeQuery( buf.makeStringAndClear() );
Reference< XRow > xRow( rs, UNO_QUERY );
while( rs->next() )
{
Oid oid = xRow->getInt( 1 );
OUString typeName = xRow->getString( 2 );
OUString typType = xRow->getString( 3 );
sal_Int32 type = typeNameToDataType( typeName, typType );
for( sal_Int32 j = 0; j < m_colCount ; j ++ )
{
if( m_colDesc[j].typeOid == oid )
{
m_colDesc[j].typeName = typeName;
m_colDesc[j].type = type;
}
}
}
m_checkedForTypes = true;
}
void ResultSetMetaData::checkTable()
{
if( m_checkedForTable )
return;
m_checkedForTable = true;
if( !m_tableName.getLength() )
return;
Reference< css::container::XNameAccess > tables = (*m_ppSettings)->tables;
if( ! tables.is() )
{
Reference< XTablesSupplier > supplier(
extractConnectionFromStatement( m_origin->getStatement() ), UNO_QUERY);
if( supplier.is() )
tables = supplier->getTables();
}
if( tables.is() )
{
const OUString name (getTableName ( 1 ));
const OUString schema (getSchemaName( 1 ));
const OUString composedName( schema.isEmpty() ? name : (schema + "." + name) );
tables->getByName( composedName ) >>= m_table;
}
}
sal_Int32 ResultSetMetaData::getIntColumnProperty( const OUString & name, int index, int def )
{
sal_Int32 ret = def; // give defensive answers, when data is not available
try
{
MutexGuard guard( m_xMutex->GetMutex() );
checkColumnIndex( index );
Reference< XPropertySet > set = getColumnByIndex( index );
if( set.is() )
{
set->getPropertyValue( name ) >>= ret;
}
}
catch( css::uno::Exception & )
{
}
return ret;
}
bool ResultSetMetaData::getBoolColumnProperty( const OUString & name, int index, bool def )
{
bool ret = def;
try
{
MutexGuard guard( m_xMutex->GetMutex() );
checkColumnIndex( index );
Reference< XPropertySet > set = getColumnByIndex( index );
if( set.is() )
{
set->getPropertyValue( name ) >>= ret;
}
}
catch( css::uno::Exception & )
{
}
return ret;
}
Reference< css::beans::XPropertySet > ResultSetMetaData::getColumnByIndex( int index )
{
Reference< XPropertySet > ret;
checkTable();
if( m_table.is() )
{
OUString columnName = getColumnName( index );
Reference< XColumnsSupplier > supplier( m_table, UNO_QUERY );
if( supplier.is() )
{
Reference< XNameAccess > columns = supplier->getColumns();
if( columns.is() && columns->hasByName( columnName ) )
{
columns->getByName( columnName ) >>= ret;
}
}
}
return ret;
}
// Methods
sal_Int32 ResultSetMetaData::getColumnCount( )
{
return m_colCount;
}
sal_Bool ResultSetMetaData::isAutoIncrement( sal_Int32 column )
{
bool ret = getBoolColumnProperty( getStatics().IS_AUTO_INCREMENT, column, false );
return ret;
}
sal_Bool ResultSetMetaData::isCaseSensitive( sal_Int32 )
{
return true; // ??? hmm, numeric types or
}
sal_Bool ResultSetMetaData::isSearchable( sal_Int32 )
{
return true; // mmm, what types are not searchable ?
}
sal_Bool ResultSetMetaData::isCurrency( sal_Int32 column )
{
return getBoolColumnProperty( getStatics().IS_CURRENCY, column, false );
}
sal_Int32 ResultSetMetaData::isNullable( sal_Int32 column )
{
return getIntColumnProperty(
getStatics().IS_NULLABLE, column, css::sdbc::ColumnValue::NULLABLE_UNKNOWN );
}
sal_Bool ResultSetMetaData::isSigned( sal_Int32 )
{
return true;
}
sal_Int32 ResultSetMetaData::getColumnDisplaySize( sal_Int32 column )
{
MutexGuard guard( m_xMutex->GetMutex() );
checkColumnIndex( column );
return m_colDesc[column-1].displaySize;
}
OUString ResultSetMetaData::getColumnLabel( sal_Int32 column )
{
return getColumnName( column);
}
OUString ResultSetMetaData::getColumnName( sal_Int32 column )
{
MutexGuard guard( m_xMutex->GetMutex() );
checkColumnIndex( column );
return m_colDesc[column-1].name;
}
OUString ResultSetMetaData::getSchemaName( sal_Int32 )
{
return m_schemaName;
}
sal_Int32 ResultSetMetaData::getPrecision( sal_Int32 column )
{
MutexGuard guard( m_xMutex->GetMutex() );
checkColumnIndex( column );
return m_colDesc[column-1].precision;
}
sal_Int32 ResultSetMetaData::getScale( sal_Int32 column )
{
MutexGuard guard( m_xMutex->GetMutex() );
checkColumnIndex( column );
return m_colDesc[column-1].scale;
}
OUString ResultSetMetaData::getTableName( sal_Int32 )
{
// LEM TODO This is very fishy... Should probably return the table to which that column belongs!
return m_tableName;
}
OUString ResultSetMetaData::getCatalogName( sal_Int32 )
{
// can do this through XConnection.getCatalog() !
return OUString();
}
sal_Int32 ResultSetMetaData::getColumnType( sal_Int32 column )
{
int ret = getIntColumnProperty( getStatics().TYPE, column, -100 );
if( -100 == ret )
{
checkForTypes();
if( css::sdbc::DataType::LONGVARCHAR == m_colDesc[column-1].type && m_pResultSet )
m_colDesc[column-1].type = m_pResultSet->guessDataType( column );
ret = m_colDesc[column-1].type;
}
return ret;
}
OUString ResultSetMetaData::getColumnTypeName( sal_Int32 column )
{
OUString ret; // give defensive answers, when data is not available
try
{
MutexGuard guard( m_xMutex->GetMutex() );
checkColumnIndex( column );
Reference< XPropertySet > set = getColumnByIndex( column );
if( set.is() )
{
set->getPropertyValue( getStatics().TYPE_NAME ) >>= ret;
}
else
{
checkForTypes();
ret = m_colDesc[column-1].typeName;
}
}
catch( css::uno::Exception & )
{
}
return ret;
}
sal_Bool ResultSetMetaData::isReadOnly( sal_Int32 )
{
return false;
}
sal_Bool ResultSetMetaData::isWritable( sal_Int32 column )
{
return ! isReadOnly( column ); // what's the sense if this method ?
}
sal_Bool ResultSetMetaData::isDefinitelyWritable( sal_Int32 column )
{
return isWritable(column); // uhh, now it becomes really esoteric...
}
OUString ResultSetMetaData::getColumnServiceName( sal_Int32 )
{
return OUString();
}
void ResultSetMetaData::checkColumnIndex(sal_Int32 columnIndex)
{
if( columnIndex < 1 || columnIndex > m_colCount )
{
throw SQLException(
"pq_resultsetmetadata: index out of range (expected 1 to "
+ OUString::number( m_colCount ) + ", got " + OUString::number( columnIndex ),
*this, OUString(), 1, Any() );
}
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */