summaryrefslogtreecommitdiffstats
path: root/connectivity/source/drivers/postgresql/pq_tools.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /connectivity/source/drivers/postgresql/pq_tools.cxx
parentInitial commit. (diff)
downloadlibreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz
libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'connectivity/source/drivers/postgresql/pq_tools.cxx')
-rw-r--r--connectivity/source/drivers/postgresql/pq_tools.cxx1249
1 files changed, 1249 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..eb90c24ce
--- /dev/null
+++ b/connectivity/source/drivers/postgresql/pq_tools.cxx
@@ -0,0 +1,1249 @@
+/* -*- 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,
+ "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, 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 )
+{
+ 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, 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(
+ "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 = o3tl::trim(sql.subView(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.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( 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( 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( Any( 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( Any( 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() );
+ 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( "(" );
+ 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: */