diff options
Diffstat (limited to 'connectivity/source/drivers/postgresql/pq_tools.cxx')
-rw-r--r-- | connectivity/source/drivers/postgresql/pq_tools.cxx | 1247 |
1 files changed, 1247 insertions, 0 deletions
diff --git a/connectivity/source/drivers/postgresql/pq_tools.cxx b/connectivity/source/drivers/postgresql/pq_tools.cxx new file mode 100644 index 000000000..bece8a806 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_tools.cxx @@ -0,0 +1,1247 @@ +/* -*- 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 <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> + +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::uno::makeAny; + +using com::sun::star::container::XEnumeration; +using com::sun::star::container::XEnumerationAccess; + +namespace pq_sdbc_driver +{ + +OUString concatQualified( const OUString & a, const OUString &b) +{ + return a + "." + b; +} + +static OString iOUStringToOString( const OUString& str, ConnectionSettings const *settings) { + OSL_ENSURE(settings, "pgsql-sdbc: OUStringToOString got NULL settings"); + return OUStringToOString( str, ConnectionSettings::encoding ); +} + +OString OUStringToOString( const OUString& str, ConnectionSettings const *settings) { + return iOUStringToOString( str, settings ); +} + +void bufferEscapeConstant( OUStringBuffer & buf, const OUString & 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, + "22018", + -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.makeStringAndClear(), RTL_TEXTENCODING_UTF8 ) ); +} + +static void ibufferQuoteConstant( OUStringBuffer & buf, const OUString & value, ConnectionSettings *settings ) +{ + buf.append( "'" ); + bufferEscapeConstant( buf, value, settings ); + buf.append( "'" ); +} + +void bufferQuoteConstant( OUStringBuffer & buf, const OUString & 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, const OUString &toQuote, ConnectionSettings *settings ) +{ + OSL_ENSURE(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, + "22018", + -1, + Any()); + } + buf.append( OStringToOUString( cstr, RTL_TEXTENCODING_UTF8 ) ); + PQfreemem( cstr ); +} + +void bufferQuoteIdentifier( OUStringBuffer & buf, const OUString &toQuote, ConnectionSettings *settings ) +{ + return ibufferQuoteIdentifier(buf, toQuote, settings); +} + + +void bufferQuoteQualifiedIdentifier( + OUStringBuffer & buf, const OUString &schema, const OUString &table, ConnectionSettings *settings ) +{ + ibufferQuoteIdentifier(buf, schema, settings); + buf.append( "." ); + ibufferQuoteIdentifier(buf, table, settings); +} + +void bufferQuoteQualifiedIdentifier( + OUStringBuffer & buf, + const OUString &schema, + const OUString &table, + const OUString &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( + "PQSDBC: Couldn't retrieve connection from statement", + 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( const OUString & sql ) +{ + OUString ret; + int i = 0; + while (i < sql.getLength() && isWhitespace(sql[i])) { i++; } + + if( sql.matchIgnoreAsciiCase("insert", i) ) + { + i += 6; + while (i < sql.getLength() && isWhitespace(sql[i])) { i++; } + if( sql.matchIgnoreAsciiCase("into", i) ) + { + i +=4; + while (i < sql.getLength() && isWhitespace(sql[i])) { i++; } + int start = i; + bool quote = (sql[i] == '"'); + for( i++ ; i < sql.getLength() ; i ++ ) + { + if( quote && sql[i] == '"' ) + { + while (i < sql.getLength() && isWhitespace(sql[i])) { i++; } + if( '.' == sql[i] ) + { + while (i < sql.getLength() && 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 = sql.copy(start, i - start ).trim(); +// 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.push_back( OString( &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.push_back( OString( &sql.getStr()[start], i - start +1 ) ); + start = i + 1; // leave single quotes ! + singleQuote = false; + } + } + else + { + if( '"' == c ) + { + vec.push_back( OString( &sql.getStr()[start], i - start ) ); + doubleQuote = true; + start = i; + } + else if( '\'' == c ) + { + vec.push_back( OString( &sql.getStr()[start], i - start ) ); + singleQuote = true; + start = i; + } + } + } + if( start < i ) + vec.push_back( OString( &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.push_back( OString( &sql.getStr()[start], i-start ) ); + start = i + 1; + doubleQuote = false; + } + } + else if( singleQuote ) + { + if( '\'' == c ) + { + vec.push_back( OString( &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.push_back( OString( &sql.getStr()[start], i - start ) ); + start = i +1; + } + } + else if( ',' == c || isOperator( c ) || '(' == c || ')' == c ) + { + if( i - start ) + vec.push_back( OString( &sql.getStr()[start], i - start ) ); + vec.push_back( OString( &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.push_back( OString( &sql.getStr()[start], i - start ) ); + vec.push_back( OString( "." ) ); + start = i + 1; + } + } + } + } + if( start < i ) + vec.push_back( OString( &sql.getStr()[start] , i - start ) ); + +// for( i = 0 ; i < vec.size() ; i ++ ) +// printf( "%s!" , vec[i].getStr() ); +// printf( "\n" ); +} + + +void splitConcatenatedIdentifier( const OUString & source, OUString *first, OUString *second) +{ + std::vector< OString > vec; + tokenizeSQL( 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( const OUString & str ) +{ + int len = str.getLength(); + bool doubleQuote = false; + int brackets = 0; + int 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.push_back( makeAny( 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.push_back( makeAny( 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( + "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=?" ); + + 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)? + OUString("pg_attrdef.adsrc"): + OUString("pg_get_expr(pg_attrdef.adbin, pg_attrdef.adrelid, true)"); +} + +css::uno::Sequence< sal_Int32 > string2intarray( const OUString & str ) +{ + css::uno::Sequence< sal_Int32 > ret; + const sal_Int32 strlen = str.getLength(); + if( str.getLength() > 1 ) + { + sal_Int32 start = 0; + sal_uInt32 c; + for (;;) + { + c = str.iterateCodePoints(&start); + if (!iswspace(c)) + break; + if ( start == strlen) + return ret; + } + if ( c != L'{' ) + return ret; + for (;;) + { + c = str.iterateCodePoints(&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=str.iterateCodePoints(&start); + } while ( c ); + do + { + if (!iswdigit(c)) + break; + if ( start == strlen) + return ret; + digits.append(OUString(&c, 1)); + c = str.iterateCodePoints(&start); + } while ( c ); + vec.push_back( digits.makeStringAndClear().toInt32() ); + do + { + if(!iswspace(c)) + break; + if ( start == strlen) + return ret; + c = str.iterateCodePoints(&start); + } while ( c ); + if ( c == L'}' ) + break; + if ( str.iterateCodePoints(&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() ); + for( int i = 0; i < intArray.getLength() ; i ++ ) + { + Int2StringMap::const_iterator ii = map.find( intArray[i] ); + if( ii != map.end() ) + ret[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( "(" ); + typeName.append( precision ); + typeName.append( ")" ); + break; + } + case css::sdbc::DataType::DECIMAL: + case css::sdbc::DataType::NUMERIC: + { + typeName.append( "(" ); + typeName.append( precision ); + typeName.append( "," ); + typeName.append( extractIntProperty( desc, getStatics().SCALE ) ); + typeName.append( ")" ); + 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: */ |