/* -*- 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 <o3tl/any.hxx>
#include <o3tl/string_view.hxx>
#include <rtl/strbuf.hxx>
#include <rtl/ustrbuf.hxx>
#include <sal/log.hxx>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/container/XEnumerationAccess.hpp>
#include <com/sun/star/lang/XComponent.hpp>
#include <com/sun/star/sdbc/SQLException.hpp>
#include <com/sun/star/sdbc/XRow.hpp>
#include <com/sun/star/sdbc/XParameters.hpp>
#include <com/sun/star/sdbc/DataType.hpp>
#include <com/sun/star/sdbc/KeyRule.hpp>
#include <com/sun/star/sdbcx/KeyType.hpp>
#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
#include "pq_tools.hxx"
#include "pq_statics.hxx"
#include <libpq-fe.h>
#include <string.h>
#include <string_view>
using com::sun::star::beans::XPropertySet;
using com::sun::star::lang::XComponent;
using com::sun::star::sdbc::SQLException;
using com::sun::star::sdbc::XStatement;
using com::sun::star::sdbc::XConnection;
using com::sun::star::sdbc::XPreparedStatement;
using com::sun::star::sdbc::XParameters;
using com::sun::star::sdbc::XResultSet;
using com::sun::star::sdbc::XRow;
using com::sun::star::sdbcx::XColumnsSupplier;
using com::sun::star::uno::UNO_QUERY;
using com::sun::star::uno::UNO_QUERY_THROW;
using com::sun::star::uno::Reference;
using com::sun::star::uno::Sequence;
using com::sun::star::uno::XInterface;
using com::sun::star::uno::Any;
using com::sun::star::container::XEnumeration;
using com::sun::star::container::XEnumerationAccess;
namespace pq_sdbc_driver
{
OUString concatQualified( std::u16string_view a, std::u16string_view b)
{
return OUString::Concat(a) + "." + b;
}
static OString iOUStringToOString( std::u16string_view str, ConnectionSettings const *settings) {
OSL_ENSURE(settings, "pgsql-sdbc: OUStringToOString got NULL settings");
return rtl::OUStringToOString( str, ConnectionSettings::encoding );
}
OString OUStringToOString( std::u16string_view str, ConnectionSettings const *settings) {
return iOUStringToOString( str, settings );
}
void bufferEscapeConstant( OUStringBuffer & buf, std::u16string_view value, ConnectionSettings *settings )
{
OString y = iOUStringToOString( value, settings );
OStringBuffer strbuf( y.getLength() * 2 + 2 );
int error;
int len = PQescapeStringConn(settings->pConnection, const_cast<char*>(strbuf.getStr()), y.getStr() , y.getLength(), &error );
if ( error )
{
char *errstr = PQerrorMessage(settings->pConnection);
// As of PostgreSQL 9.1, the only possible errors "involve invalid multibyte encoding"
// According to https://www2.opengroup.org/ogsys/jsp/publications/PublicationDetails.jsp?publicationid=11216
// (X/Open SQL CLI, March 1995, ISBN: 1-85912-081-4, X/Open Document Number: C451)
// 22018 is for "Invalid character value" and seems to be the best match.
// We have no good XInterface Reference to pass here, so just give NULL
throw SQLException(OUString(errstr, strlen(errstr), ConnectionSettings::encoding),
nullptr,
u"22018"_ustr,
-1,
Any());
}
strbuf.setLength( len );
// Previously here RTL_TEXTENCODING_ASCII_US; as we set the PostgreSQL client_encoding to UTF8,
// we get UTF8 here, too. I'm not sure why it worked well before...
buf.append( OStringToOUString( strbuf, RTL_TEXTENCODING_UTF8 ) );
}
static void ibufferQuoteConstant( OUStringBuffer & buf, std::u16string_view value, ConnectionSettings *settings )
{
buf.append( "'" );
bufferEscapeConstant( buf, value, settings );
buf.append( "'" );
}
void bufferQuoteConstant( OUStringBuffer & buf, std::u16string_view value, ConnectionSettings *settings )
{
return ibufferQuoteConstant( buf, value, settings );
}
void bufferQuoteAnyConstant( OUStringBuffer & buf, const Any &val, ConnectionSettings *settings )
{
if( val.hasValue() )
{
OUString str;
val >>= str;
bufferQuoteConstant( buf, str, settings );
}
else
buf.append( "NULL" );
}
static void ibufferQuoteIdentifier( OUStringBuffer & buf, std::u16string_view toQuote, ConnectionSettings *settings )
{
assert(settings && "pgsql-sdbc: bufferQuoteIdentifier got NULL settings");
OString y = iOUStringToOString( toQuote, settings );
char *cstr = PQescapeIdentifier(settings->pConnection, y.getStr(), y.getLength());
if ( cstr == nullptr )
{
char *errstr = PQerrorMessage(settings->pConnection);
// Implementation-defined SQLACCESS error
throw SQLException(OUString(errstr, strlen(errstr), ConnectionSettings::encoding),
nullptr,
u"22018"_ustr,
-1,
Any());
}
buf.append( OStringToOUString( cstr, RTL_TEXTENCODING_UTF8 ) );
PQfreemem( cstr );
}
void bufferQuoteIdentifier( OUStringBuffer & buf, std::u16string_view toQuote, ConnectionSettings *settings )
{
return ibufferQuoteIdentifier(buf, toQuote, settings);
}
void bufferQuoteQualifiedIdentifier(
OUStringBuffer & buf, std::u16string_view schema, std::u16string_view table, ConnectionSettings *settings )
{
ibufferQuoteIdentifier(buf, schema, settings);
buf.append( "." );
ibufferQuoteIdentifier(buf, table, settings);
}
void bufferQuoteQualifiedIdentifier(
OUStringBuffer & buf,
std::u16string_view schema,
std::u16string_view table,
std::u16string_view col,
ConnectionSettings *settings)
{
ibufferQuoteIdentifier(buf, schema, settings);
buf.append( "." );
ibufferQuoteIdentifier(buf, table, settings);
buf.append( "." );
ibufferQuoteIdentifier(buf, col, settings);
}
OUString extractStringProperty(
const Reference< XPropertySet > & descriptor, const OUString &name )
{
OUString value;
descriptor->getPropertyValue( name ) >>= value;
return value;
}
bool extractBoolProperty(
const Reference< XPropertySet > & descriptor, const OUString &name )
{
bool value = false;
descriptor->getPropertyValue( name ) >>= value;
return value;
}
sal_Int32 extractIntProperty(
const Reference< XPropertySet > & descriptor, const OUString &name )
{
sal_Int32 ret = 0;
descriptor->getPropertyValue( name ) >>= ret;
return ret;
}
void disposeObject( const css::uno::Reference< css::uno::XInterface > & r )
{
Reference< XComponent > comp( r, UNO_QUERY );
if( comp.is() )
comp->dispose();
}
void disposeNoThrow( const css::uno::Reference< css::uno::XInterface > & r )
{
try
{
disposeObject( r );
}
catch( SQLException & )
{
// ignore this
}
}
Reference< XConnection > extractConnectionFromStatement( const Reference< XInterface > & stmt )
{
Reference< XConnection > ret;
Reference< css::sdbc::XStatement > owner( stmt, UNO_QUERY );
if( owner.is() )
ret = owner->getConnection();
else
{
Reference< css::sdbc::XPreparedStatement > myowner( stmt, UNO_QUERY );
if( myowner.is() )
ret = myowner->getConnection();
if( ! ret.is() )
throw SQLException(
u"PQSDBC: Couldn't retrieve connection from statement"_ustr,
Reference< XInterface > () , OUString(), 0 , css::uno::Any() );
}
return ret;
}
DisposeGuard::DisposeGuard( const Reference< XInterface > & r )
: d( r )
{}
DisposeGuard::~DisposeGuard()
{
disposeNoThrow( d );
}
TransactionGuard::TransactionGuard( const Reference< XStatement > &stmt )
: m_stmt( stmt ),
m_commited( false )
{
m_stmt->executeUpdate( getStatics().BEGIN );
}
void TransactionGuard::commit()
{
m_stmt->executeUpdate( getStatics().COMMIT );
m_commited = true;
}
void TransactionGuard::executeUpdate( const OUString & sql )
{
m_stmt->executeUpdate( sql );
}
TransactionGuard::~TransactionGuard()
{
try
{
if( ! m_commited )
m_stmt->executeUpdate( getStatics().ROLLBACK );
}
catch( css::uno::Exception & )
{
// ignore, we are within a dtor
}
disposeNoThrow( m_stmt );
}
bool isWhitespace( sal_Unicode c )
{
return ' ' == c || 9 == c || 10 == c || 13 == c;
}
OUString extractTableFromInsert( std::u16string_view sql )
{
OUString ret;
size_t i = 0;
while (i < sql.size() && isWhitespace(sql[i])) { i++; }
if( o3tl::matchIgnoreAsciiCase(sql, u"insert", i) )
{
i += 6;
while (i < sql.size() && isWhitespace(sql[i])) { i++; }
if( o3tl::matchIgnoreAsciiCase(sql, u"into", i) )
{
i +=4;
while (i < sql.size() && isWhitespace(sql[i])) { i++; }
int start = i;
bool quote = (sql[i] == '"');
for( i++ ; i < sql.size() ; i ++ )
{
if( quote && sql[i] == '"' )
{
while (i < sql.size() && isWhitespace(sql[i])) { i++; }
if( '.' == sql[i] )
{
while (i < sql.size() && isWhitespace(sql[i])) { i++; }
if( '"' == sql[i] )
{
// the second part of the table name does not use quotes
// parse on
quote = false;
}
}
else
{
// end quoted name, ok
break;
}
}
else
{
if( isWhitespace( sql[i] ) )
{
// found the end of an unquoted name
break;
}
}
}
ret = o3tl::trim(sql.substr(start, i - start ));
// printf( "pq_statement: parsed table name %s from insert\n" ,
// OUStringToOString( ret, RTL_TEXTENCODING_ASCII_US).getStr() );
}
}
return ret;
}
static bool isOperator( char c )
{
bool ret;
switch(c)
{
case '+':
case '-':
case '*':
case '/':
case '<':
case '>':
case '=':
case '~':
case '!':
case '@':
case '#':
case '%':
case '^':
case '&':
case '|':
case '`':
case '?':
case '$':
ret = true;
break;
default:
ret = false;
}
return ret;
}
void splitSQL( const OString & sql, std::vector< OString > &vec )
{
int length = sql.getLength();
int i = 0;
bool singleQuote = false;
bool doubleQuote = false;
int start = 0;
for( ; i < length ; i ++ )
{
char c = sql[i];
if( doubleQuote )
{
if( '"' == c )
{
vec.emplace_back( &sql.getStr()[start], i-start+1 );
start = i + 1;
doubleQuote = false;
}
}
else if( singleQuote )
{
if( '\'' == c && (i+1) < length && '\'' == sql[i+1] )
{
// two subsequent single quotes within a quoted string
// mean a single quote within the string
i ++;
}
else if( '\'' == c )
{
vec.emplace_back( &sql.getStr()[start], i - start +1 );
start = i + 1; // leave single quotes !
singleQuote = false;
}
}
else
{
if( '"' == c )
{
vec.emplace_back( &sql.getStr()[start], i - start );
doubleQuote = true;
start = i;
}
else if( '\'' == c )
{
vec.emplace_back( &sql.getStr()[start], i - start );
singleQuote = true;
start = i;
}
}
}
if( start < i )
vec.emplace_back( &sql.getStr()[start] , i - start );
// for( i = 0 ; i < vec.size() ; i ++ )
// printf( "%s!" , vec[i].getStr() );
// printf( "\n" );
}
void tokenizeSQL( const OString & sql, std::vector< OString > &vec )
{
int length = sql.getLength();
int i = 0;
bool singleQuote = false;
bool doubleQuote = false;
int start = 0;
for( ; i < length ; i ++ )
{
char c = sql[i];
if( doubleQuote )
{
if( '"' == c )
{
vec.emplace_back( &sql.getStr()[start], i-start );
start = i + 1;
doubleQuote = false;
}
}
else if( singleQuote )
{
if( '\'' == c )
{
vec.emplace_back( &sql.getStr()[start], i - start +1 );
start = i + 1; // leave single quotes !
singleQuote = false;
}
}
else
{
if( '"' == c )
{
doubleQuote = true;
start = i +1; // skip double quotes !
}
else if( '\'' == c )
{
singleQuote = true;
start = i; // leave single quotes
}
else if( isWhitespace( c ) )
{
if( i == start )
start ++; // skip additional whitespace
else
{
vec.emplace_back( &sql.getStr()[start], i - start );
start = i +1;
}
}
else if( ',' == c || isOperator( c ) || '(' == c || ')' == c )
{
if( i - start )
vec.emplace_back( &sql.getStr()[start], i - start );
vec.emplace_back( &sql.getStr()[i], 1 );
start = i + 1;
}
else if( '.' == c )
{
if( ( i > start && sql[start] >= '0' && sql[start] <= '9' ) ||
( i == start && i > 1 && isWhitespace( sql[i-1] ) ) )
{
// ignore, is a literal
}
else
{
if( i - start )
vec.emplace_back( &sql.getStr()[start], i - start );
vec.emplace_back( "." );
start = i + 1;
}
}
}
}
if( start < i )
vec.emplace_back( &sql.getStr()[start] , i - start );
// for( i = 0 ; i < vec.size() ; i ++ )
// printf( "%s!" , vec[i].getStr() );
// printf( "\n" );
}
void splitConcatenatedIdentifier( std::u16string_view source, OUString *first, OUString *second)
{
std::vector< OString > vec;
tokenizeSQL( rtl::OUStringToOString( source, RTL_TEXTENCODING_UTF8 ), vec );
switch (vec.size())
{
case 1:
first->clear();
*second = OStringToOUString( vec[0], RTL_TEXTENCODING_UTF8 );
break;
case 3:
*first = OStringToOUString( vec[0], RTL_TEXTENCODING_UTF8 );
*second = OStringToOUString( vec[2], RTL_TEXTENCODING_UTF8 );
break;
default:
SAL_WARN("connectivity.postgresql",
"pq_tools::splitConcatenatedIdentifier unexpected number of tokens in identifier: "
<< vec.size());
}
}
OUString array2String( const css::uno::Sequence< Any > &seq )
{
OUStringBuffer buf(128);
int len = seq.getLength();
buf.append( "{" );
for( int i = 0 ; i < len ; i ++ )
{
OUString element;
seq[i] >>= element;
if( i > 0 )
buf.append( "," );
int strLength = element.getLength();
buf.append( "\"" );
for( int j = 0 ; j < strLength ; j ++ )
{
sal_Unicode c = element[j];
if( c == '\\' || c == '"' || c == '{' || c == '}' )
{
buf.append( "\\" );
}
buf.append( c );
}
buf.append( "\"" );
}
buf.append( "}" );
return buf.makeStringAndClear();
}
std::vector< Any > parseArray( std::u16string_view str )
{
size_t len = str.size();
bool doubleQuote = false;
int brackets = 0;
size_t i = 0;
OUStringBuffer current;
std::vector<Any> elements;
bool doubleQuotedValue = false;
while( i < len )
{
sal_Unicode c = str[i];
sal_Unicode cnext = str[i+1];
if( doubleQuote )
{
if( '\\' == c )
{
i ++;
current.append( cnext );
}
else if( '"' == c )
{
doubleQuote = false;
doubleQuotedValue = true; // signal, that there was an empty element
}
else
{
current.append( c );
}
}
else if ( '{' == c )
{
brackets ++;
}
else if( '}' == c )
{
brackets --;
if( brackets < 0 )
{
throw SQLException(
"error during array parsing, didn't expect a } at position "
+ OUString::number(i) + " ('" + str + "')",
Reference< XInterface > (), OUString(), 1, Any() );
}
if( brackets == 0 )
{
if( !current.isEmpty() || doubleQuotedValue )
elements.emplace_back( current.makeStringAndClear() );
}
else
{
current.append( c );
}
}
else if( '"' == c )
{
// if( current.getLength() != 0 )
// {
// OUStringBuffer buf;
// buf.appendAscii( "error during array parsing, didn't expect a \" at position " );
// buf.append( i );
// buf.append( " ('" );
// buf.append( str );
// buf.append( "')" );
// throw SDBCException(
// buf.makeStringAndClear(),
// Reference< XInterface > (), 1, Any() );
// }
// else
// {
doubleQuote = true;
// }
}
else if( ',' == c && brackets == 1)
{
doubleQuotedValue = false;
elements.emplace_back( current.makeStringAndClear() );
}
else if( isWhitespace( c ) )
{
// ignore whitespace without quotes
}
else
{
current.append( c );
}
i++;
}
return elements;
}
std::vector< sal_Int32 > parseIntArray( const OUString & str )
{
sal_Int32 start = 0;
std::vector<sal_Int32> vec;
// printf( ">%s<\n" , OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
for( sal_Int32 i = str.indexOf( ' ' ) ; i != -1 ; i = str.indexOf( ' ', start) )
{
vec.push_back( rtl_ustr_toInt32( &str.pData->buffer[start], 10 ) );
// printf( "found %d\n" , rtl_ustr_toInt32( &str.pData->buffer[start], 10 ));
start = i + 1;
}
vec.push_back( rtl_ustr_toInt32( &str.pData->buffer[start], 10 ) );
// printf( "found %d\n" , rtl_ustr_toInt32( &str.pData->buffer[start], 10 ));
return vec;
}
void fillAttnum2attnameMap(
Int2StringMap &map,
const Reference< css::sdbc::XConnection > &conn,
const OUString &schema,
const OUString &table )
{
Reference< XPreparedStatement > prep = conn->prepareStatement(
u"SELECT attname,attnum "
"FROM pg_attribute "
"INNER JOIN pg_class ON attrelid = pg_class.oid "
"INNER JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid "
"WHERE relname=? AND nspname=?"_ustr );
Reference< XParameters > paras( prep, UNO_QUERY_THROW );
paras->setString( 1 , table );
paras->setString( 2 , schema );
Reference< XResultSet > rs = prep->executeQuery();
Reference< XRow > xRow( rs , UNO_QUERY_THROW );
while( rs->next() )
{
map[ xRow->getInt(2) ] = xRow->getString(1);
}
}
OString extractSingleTableFromSelect( const std::vector< OString > &vec )
{
OString ret;
if( 0 == rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
vec[0].pData->buffer, vec[0].pData->length, "select" , 6 , 6 ) )
{
size_t token = 0;
for( token = 1; token < vec.size() ; token ++ )
{
if( 0 == rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
vec[token].getStr(), vec[token].getLength(), "from" , 4 , 4 ) )
{
// found from
break;
}
}
token ++;
if( token < vec.size() && 0 == rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
vec[token].pData->buffer, vec[token].pData->length, "only " , 4 , 4 ) )
{
token ++;
}
if( token < vec.size() && vec[token] != "(" )
{
// it is a table or a function name
OStringBuffer buf(128);
if( '"' == vec[token][0] )
buf.append( &(vec[token].getStr()[1]) , vec[token].getLength() -2 );
else
buf.append( vec[token] );
token ++;
if( token < vec.size() )
{
if( vec[token] == "." )
{
buf.append( vec[token] );
token ++;
if( token < vec.size() )
{
if( '"' == vec[token][0] )
buf.append( &(vec[token].getStr()[1]) , vec[token].getLength() -2 );
else
buf.append( vec[token] );
token ++;
}
}
}
ret = buf.makeStringAndClear();
// now got my table candidate
if( token < vec.size() && vec[token] == "(" )
{
// whoops, it is a function
ret.clear();
}
else
{
if( token < vec.size() )
{
if( 0 == rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
vec[token].pData->buffer, vec[token].pData->length, "as" , 2, 2 ) )
{
token += 2; // skip alias
}
}
if( token < vec.size() )
{
if( vec[token] == "," )
{
// whoops, multiple tables are used
ret.clear();
}
else
{
static const char * forbiddenKeywords[] =
{ "join", "natural", "outer", "inner", "left", "right", "full" , nullptr };
for( int i = 0 ; forbiddenKeywords[i] ; i ++ )
{
size_t nKeywordLen = strlen(forbiddenKeywords[i]);
if( 0 == rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
vec[token].pData->buffer, vec[token].pData->length,
forbiddenKeywords[i], nKeywordLen,
nKeywordLen ) )
{
// whoops, it is a join
ret.clear();
}
}
}
}
}
}
}
return ret;
}
OUString getColExprForDefaultSettingVal(ConnectionSettings const *settings)
{
return (PQserverVersion( settings->pConnection ) < 80000)?
u"pg_attrdef.adsrc"_ustr:
u"pg_get_expr(pg_attrdef.adbin, pg_attrdef.adrelid, true)"_ustr;
}
css::uno::Sequence< sal_Int32 > string2intarray( std::u16string_view str )
{
css::uno::Sequence< sal_Int32 > ret;
const sal_Int32 strlen = str.size();
if( strlen > 1 )
{
sal_Int32 start = 0;
sal_uInt32 c;
for (;;)
{
c = o3tl::iterateCodePoints(str, &start);
if (!iswspace(c))
break;
if ( start == strlen)
return ret;
}
if ( c != L'{' )
return ret;
for (;;)
{
c = o3tl::iterateCodePoints(str, &start);
if ( !iswspace(c) )
break;
if ( start == strlen)
return ret;
}
if ( c == L'}' )
return ret;
std::vector< sal_Int32 > vec;
do
{
OUStringBuffer digits;
do
{
if(!iswspace(c))
break;
if ( start == strlen)
return ret;
c = o3tl::iterateCodePoints(str, &start);
} while ( c );
do
{
if (!iswdigit(c))
break;
if ( start == strlen)
return ret;
digits.append(OUString(&c, 1));
c = o3tl::iterateCodePoints(str, &start);
} while ( c );
vec.push_back( o3tl::toInt32(digits) );
do
{
if(!iswspace(c))
break;
if ( start == strlen)
return ret;
c = o3tl::iterateCodePoints(str, &start);
} while ( c );
if ( c == L'}' )
break;
if ( o3tl::iterateCodePoints(str, &start) != L',' )
return ret;
if ( start == strlen)
return ret;
} while( true );
// vec is guaranteed non-empty
assert(vec.size() > 0);
ret = css::uno::Sequence< sal_Int32 > ( vec.data() , vec.size() );
}
return ret;
}
Sequence< OUString > convertMappedIntArray2StringArray(
const Int2StringMap &map, const Sequence< sal_Int32 > &intArray )
{
Sequence< OUString > ret( intArray.getLength() );
auto retRange = asNonConstRange(ret);
for( int i = 0; i < intArray.getLength() ; i ++ )
{
Int2StringMap::const_iterator ii = map.find( intArray[i] );
if( ii != map.end() )
retRange[i] = ii->second;
}
return ret;
}
OUString sqltype2string( const Reference< XPropertySet > & desc )
{
OUStringBuffer typeName;
typeName.append( extractStringProperty( desc, getStatics().TYPE_NAME ) );
sal_Int32 precision = extractIntProperty( desc, getStatics().PRECISION );
if( precision )
{
switch( extractIntProperty( desc, getStatics().TYPE ) )
{
case css::sdbc::DataType::VARBINARY:
case css::sdbc::DataType::VARCHAR:
case css::sdbc::DataType::CHAR:
{
typeName.append( "(" + OUString::number(precision) + ")" );
break;
}
case css::sdbc::DataType::DECIMAL:
case css::sdbc::DataType::NUMERIC:
{
typeName.append( "("
+ OUString::number(precision)
+ ","
+ OUString::number(extractIntProperty( desc, getStatics().SCALE ))
+ ")" );
break;
}
default:
((void)0);
}
}
return typeName.makeStringAndClear();
}
static void keyType2String( OUStringBuffer & buf, sal_Int32 keyType )
{
if( css::sdbc::KeyRule::CASCADE == keyType )
{
buf.append( "CASCADE " );
}
else if( css::sdbc::KeyRule::RESTRICT == keyType )
{
buf.append( "RESTRICT " );
}
else if( css::sdbc::KeyRule::SET_DEFAULT == keyType )
{
buf.append( "SET DEFAULT " );
}
else if( css::sdbc::KeyRule::SET_NULL == keyType )
{
buf.append( "SET NULL " );
}
else //if( css::sdbc::KeyRule::NO_ACTION == keyType )
{
buf.append( "NO ACTION " );
}
}
void bufferKey2TableConstraint(
OUStringBuffer &buf, const Reference< XPropertySet > &key, ConnectionSettings *settings )
{
Statics &st = getStatics();
sal_Int32 type = extractIntProperty( key, st.TYPE );
OUString referencedTable = extractStringProperty( key, st.REFERENCED_TABLE );
sal_Int32 updateRule = extractIntProperty( key, st.UPDATE_RULE );
sal_Int32 deleteRule = extractIntProperty( key, st.DELETE_RULE );
bool foreign = false;
if( type == css::sdbcx::KeyType::UNIQUE )
{
buf.append( "UNIQUE( " );
}
else if( type == css::sdbcx::KeyType::PRIMARY )
{
buf.append( "PRIMARY KEY( " );
}
else if( type == css::sdbcx::KeyType::FOREIGN )
{
foreign = true;
buf.append( "FOREIGN KEY( " );
}
Reference< XColumnsSupplier > columns( key, UNO_QUERY );
if( columns.is() )
{
Reference< XEnumerationAccess > colEnumAccess( columns->getColumns(), UNO_QUERY );
if( colEnumAccess.is() )
{
Reference< XEnumeration > colEnum = colEnumAccess->createEnumeration();
bool first = true;
while(colEnum.is() && colEnum->hasMoreElements() )
{
if( first )
{
first = false;
}
else
{
buf.append( ", " );
}
Reference< XPropertySet > keyColumn( colEnum->nextElement(), UNO_QUERY_THROW );
bufferQuoteIdentifier(buf, extractStringProperty( keyColumn, st.NAME ), settings );
}
}
}
buf.append( ") " );
if( !foreign )
return;
buf.append( "REFERENCES " );
OUString schema;
OUString tableName;
splitConcatenatedIdentifier( referencedTable, &schema, &tableName );
bufferQuoteQualifiedIdentifier(buf , schema, tableName, settings );
if(columns.is() )
{
Reference< XEnumerationAccess > colEnumAccess( columns->getColumns(), UNO_QUERY);
if( colEnumAccess.is() )
{
buf.append( " (" );
Reference< XEnumeration > colEnum(colEnumAccess->createEnumeration());
bool first = true;
while(colEnum.is() && colEnum->hasMoreElements() )
{
if( first )
{
first = false;
}
else
{
buf.append( ", " );
}
Reference< XPropertySet > keyColumn( colEnum->nextElement(), UNO_QUERY_THROW );
bufferQuoteIdentifier(
buf, extractStringProperty( keyColumn, st.RELATED_COLUMN ), settings );
}
buf.append( ") " );
}
}
buf.append( "ON DELETE " );
keyType2String( buf, deleteRule );
buf.append( " ON UPDATE " );
keyType2String( buf, updateRule );
}
void extractNameValuePairsFromInsert( String2StringMap & map, const OString & lastQuery )
{
std::vector< OString > vec;
tokenizeSQL( lastQuery, vec );
int nSize = vec.size();
// printf( "1 %d\n", nSize );
if( !(nSize > 6 &&
vec[0].equalsIgnoreAsciiCase( "insert" ) &&
vec[1].equalsIgnoreAsciiCase( "into" )) )
return;
int n = 2;
// printf( "1a\n" );
// skip table name
if( vec[n+1].equalsIgnoreAsciiCase( "." ) )
{
n +=2;
}
n ++;
if( !vec[n].equalsIgnoreAsciiCase( "(" ) )
return;
std::vector< OString> names;
// printf( "2\n" );
// extract names
n++;
while( nSize > n && ! vec[n].equalsIgnoreAsciiCase( ")" ) )
{
names.push_back( vec[n] );
if( nSize > n+1 && vec[n+1].equalsIgnoreAsciiCase( "," ) )
{
n ++;
}
n++;
}
n++;
// now read the values
if( !(nSize > n +1 && vec[n].equalsIgnoreAsciiCase("VALUES") &&
vec[n+1].equalsIgnoreAsciiCase( "(" )) )
return;
n +=2;
// printf( "3\n" );
for (auto& name : names)
{
if (n >= nSize)
break;
map[name] = vec[n];
if( nSize > n+1 && vec[n+1].equalsIgnoreAsciiCase(",") )
{
n ++;
}
n++;
}
}
OUString querySingleValue(
const css::uno::Reference< css::sdbc::XConnection > &connection,
const OUString &query )
{
OUString ret;
Reference< XStatement > stmt = connection->createStatement();
DisposeGuard guard( stmt );
Reference< XResultSet > rs = stmt->executeQuery( query );
Reference< XRow > xRow( rs, UNO_QUERY );
if( rs->next() )
ret = xRow->getString( 1 );
return ret;
}
// copied from connectivity/source/dbtools, can't use the function directly
bool implSetObject( const Reference< XParameters >& _rxParameters,
const sal_Int32 _nColumnIndex, const Any& _rValue)
{
bool bSuccessfullyReRouted = true;
switch (_rValue.getValueTypeClass())
{
case css::uno::TypeClass_HYPER:
{
_rxParameters->setLong( _nColumnIndex, sal_Int64(0) );
}
break;
case css::uno::TypeClass_VOID:
_rxParameters->setNull(_nColumnIndex,css::sdbc::DataType::VARCHAR);
break;
case css::uno::TypeClass_STRING:
_rxParameters->setString(_nColumnIndex, *o3tl::forceAccess<OUString>(_rValue));
break;
case css::uno::TypeClass_BOOLEAN:
_rxParameters->setBoolean(_nColumnIndex, *o3tl::forceAccess<bool>(_rValue));
break;
case css::uno::TypeClass_BYTE:
_rxParameters->setByte(_nColumnIndex, *o3tl::forceAccess<sal_Int8>(_rValue));
break;
case css::uno::TypeClass_UNSIGNED_SHORT:
case css::uno::TypeClass_SHORT:
_rxParameters->setShort(_nColumnIndex, *o3tl::forceAccess<sal_Int16>(_rValue));
break;
case css::uno::TypeClass_CHAR:
_rxParameters->setString(_nColumnIndex, OUString(*o3tl::forceAccess<sal_Unicode>(_rValue)));
break;
case css::uno::TypeClass_UNSIGNED_LONG:
case css::uno::TypeClass_LONG:
_rxParameters->setInt(_nColumnIndex, *o3tl::forceAccess<sal_Int32>(_rValue));
break;
case css::uno::TypeClass_FLOAT:
_rxParameters->setFloat(_nColumnIndex, *o3tl::forceAccess<float>(_rValue));
break;
case css::uno::TypeClass_DOUBLE:
_rxParameters->setDouble(_nColumnIndex, *o3tl::forceAccess<double>(_rValue));
break;
case css::uno::TypeClass_SEQUENCE:
if (auto s = o3tl::tryAccess<Sequence< sal_Int8 >>(_rValue))
{
_rxParameters->setBytes(_nColumnIndex, *s);
}
else
bSuccessfullyReRouted = false;
break;
case css::uno::TypeClass_STRUCT:
if (auto s1 = o3tl::tryAccess<css::util::DateTime>(_rValue))
_rxParameters->setTimestamp(_nColumnIndex, *s1);
else if (auto s2 = o3tl::tryAccess<css::util::Date>(_rValue))
_rxParameters->setDate(_nColumnIndex, *s2);
else if (auto s3 = o3tl::tryAccess<css::util::Time>(_rValue))
_rxParameters->setTime(_nColumnIndex, *s3);
else
bSuccessfullyReRouted = false;
break;
case css::uno::TypeClass_INTERFACE:
{
Reference< css::io::XInputStream > xStream;
if (_rValue >>= xStream)
{
_rValue >>= xStream;
_rxParameters->setBinaryStream(_nColumnIndex, xStream, xStream->available());
break;
}
[[fallthrough]];
}
default:
bSuccessfullyReRouted = false;
}
return bSuccessfullyReRouted;
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V666 Consider inspecting fifth argument of the function 'rtl_str_shortenedCompareIgnoreAsciiCase_WithLength'. It is possible that the value does not correspond with the length of a string which was passed with the third argument.
↑ V666 Consider inspecting fourth argument of the function 'rtl_str_shortenedCompareIgnoreAsciiCase_WithLength'. It is possible that the value does not correspond with the length of a string which was passed with the third argument.