bump product version to 6.3.0.0.beta1
[LibreOffice.git] / connectivity / source / drivers / postgresql / pq_tools.cxx
blobfd691f2ec079db0716e5fddc0035afbdb3c98d74
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * Effective License of whole file:
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License version 2.1, as published by the Free Software Foundation.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
18 * MA 02111-1307 USA
20 * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011:
22 * The Contents of this file are made available subject to the terms of
23 * the GNU Lesser General Public License Version 2.1
25 * Copyright: 2000 by Sun Microsystems, Inc.
27 * Contributor(s): Joerg Budischewski
29 * All parts contributed on or after August 2011:
31 * This Source Code Form is subject to the terms of the Mozilla Public
32 * License, v. 2.0. If a copy of the MPL was not distributed with this
33 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
35 ************************************************************************/
37 #include <sal/config.h>
39 #include <o3tl/any.hxx>
40 #include <rtl/strbuf.hxx>
41 #include <rtl/ustrbuf.hxx>
42 #include <sal/log.hxx>
44 #include <com/sun/star/beans/XPropertySet.hpp>
45 #include <com/sun/star/lang/XComponent.hpp>
46 #include <com/sun/star/sdbc/SQLException.hpp>
47 #include <com/sun/star/sdbc/XRow.hpp>
48 #include <com/sun/star/sdbc/XParameters.hpp>
49 #include <com/sun/star/sdbc/DataType.hpp>
50 #include <com/sun/star/sdbc/KeyRule.hpp>
51 #include <com/sun/star/sdbcx/KeyType.hpp>
52 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
54 #include "pq_xcontainer.hxx"
55 #include "pq_tools.hxx"
56 #include "pq_statics.hxx"
58 #include <libpq-fe.h>
59 #include <string.h>
61 using com::sun::star::beans::XPropertySet;
63 using com::sun::star::lang::XComponent;
65 using com::sun::star::sdbc::SQLException;
66 using com::sun::star::sdbc::XStatement;
67 using com::sun::star::sdbc::XConnection;
68 using com::sun::star::sdbc::XPreparedStatement;
69 using com::sun::star::sdbc::XParameters;
70 using com::sun::star::sdbc::XResultSet;
71 using com::sun::star::sdbc::XRow;
73 using com::sun::star::sdbcx::XColumnsSupplier;
75 using com::sun::star::uno::UNO_QUERY;
76 using com::sun::star::uno::UNO_QUERY_THROW;
77 using com::sun::star::uno::Reference;
78 using com::sun::star::uno::Sequence;
79 using com::sun::star::uno::XInterface;
80 using com::sun::star::uno::Any;
81 using com::sun::star::uno::makeAny;
83 using com::sun::star::container::XEnumeration;
84 using com::sun::star::container::XEnumerationAccess;
86 namespace pq_sdbc_driver
89 OUString concatQualified( const OUString & a, const OUString &b)
91 OUStringBuffer buf( a.getLength() + 2 + b.getLength() );
92 buf.append( a );
93 buf.append( "." );
94 buf.append( b );
95 return buf.makeStringAndClear();
98 static OString iOUStringToOString( const OUString& str, ConnectionSettings const *settings) {
99 OSL_ENSURE(settings, "pgsql-sdbc: OUStringToOString got NULL settings");
100 return OUStringToOString( str, ConnectionSettings::encoding );
103 OString OUStringToOString( const OUString& str, ConnectionSettings const *settings) {
104 return iOUStringToOString( str, settings );
107 void bufferEscapeConstant( OUStringBuffer & buf, const OUString & value, ConnectionSettings *settings )
110 OString y = iOUStringToOString( value, settings );
111 OStringBuffer strbuf( y.getLength() * 2 + 2 );
112 int error;
113 int len = PQescapeStringConn(settings->pConnection, const_cast<char*>(strbuf.getStr()), y.getStr() , y.getLength(), &error );
114 if ( error )
116 char *errstr = PQerrorMessage(settings->pConnection);
117 // As of PostgreSQL 9.1, the only possible errors "involve invalid multibyte encoding"
118 // According to https://www2.opengroup.org/ogsys/jsp/publications/PublicationDetails.jsp?publicationid=11216
119 // (X/Open SQL CLI, March 1995, ISBN: 1-85912-081-4, X/Open Document Number: C451)
120 // 22018 is for "Invalid character value" and seems to be the best match.
121 // We have no good XInterface Reference to pass here, so just give NULL
122 throw SQLException(OUString(errstr, strlen(errstr), ConnectionSettings::encoding),
123 nullptr,
124 "22018",
126 Any());
128 strbuf.setLength( len );
129 // Previously here RTL_TEXTENCODING_ASCII_US; as we set the PostgreSQL client_encoding to UTF8,
130 // we get UTF8 here, too. I'm not sure why it worked well before...
131 buf.append( OStringToOUString( strbuf.makeStringAndClear(), RTL_TEXTENCODING_UTF8 ) );
134 static void ibufferQuoteConstant( OUStringBuffer & buf, const OUString & value, ConnectionSettings *settings )
136 buf.append( "'" );
137 bufferEscapeConstant( buf, value, settings );
138 buf.append( "'" );
141 void bufferQuoteConstant( OUStringBuffer & buf, const OUString & value, ConnectionSettings *settings )
143 return ibufferQuoteConstant( buf, value, settings );
146 void bufferQuoteAnyConstant( OUStringBuffer & buf, const Any &val, ConnectionSettings *settings )
148 if( val.hasValue() )
150 OUString str;
151 val >>= str;
152 bufferQuoteConstant( buf, str, settings );
154 else
155 buf.append( "NULL" );
158 static void ibufferQuoteIdentifier( OUStringBuffer & buf, const OUString &toQuote, ConnectionSettings *settings )
160 OSL_ENSURE(settings, "pgsql-sdbc: bufferQuoteIdentifier got NULL settings");
162 OString y = iOUStringToOString( toQuote, settings );
163 char *cstr = PQescapeIdentifier(settings->pConnection, y.getStr(), y.getLength());
164 if ( cstr == nullptr )
166 char *errstr = PQerrorMessage(settings->pConnection);
167 // Implementation-defined SQLACCESS error
168 throw SQLException(OUString(errstr, strlen(errstr), ConnectionSettings::encoding),
169 nullptr,
170 "22018",
172 Any());
174 buf.append( OStringToOUString( cstr, RTL_TEXTENCODING_UTF8 ) );
175 PQfreemem( cstr );
178 void bufferQuoteIdentifier( OUStringBuffer & buf, const OUString &toQuote, ConnectionSettings *settings )
180 return ibufferQuoteIdentifier(buf, toQuote, settings);
184 void bufferQuoteQualifiedIdentifier(
185 OUStringBuffer & buf, const OUString &schema, const OUString &table, ConnectionSettings *settings )
187 ibufferQuoteIdentifier(buf, schema, settings);
188 buf.append( "." );
189 ibufferQuoteIdentifier(buf, table, settings);
192 void bufferQuoteQualifiedIdentifier(
193 OUStringBuffer & buf,
194 const OUString &schema,
195 const OUString &table,
196 const OUString &col,
197 ConnectionSettings *settings)
199 ibufferQuoteIdentifier(buf, schema, settings);
200 buf.append( "." );
201 ibufferQuoteIdentifier(buf, table, settings);
202 buf.append( "." );
203 ibufferQuoteIdentifier(buf, col, settings);
207 OUString extractStringProperty(
208 const Reference< XPropertySet > & descriptor, const OUString &name )
210 OUString value;
211 descriptor->getPropertyValue( name ) >>= value;
212 return value;
215 bool extractBoolProperty(
216 const Reference< XPropertySet > & descriptor, const OUString &name )
218 bool value = false;
219 descriptor->getPropertyValue( name ) >>= value;
220 return value;
223 sal_Int32 extractIntProperty(
224 const Reference< XPropertySet > & descriptor, const OUString &name )
226 sal_Int32 ret = 0;
227 descriptor->getPropertyValue( name ) >>= ret;
228 return ret;
231 void disposeObject( const css::uno::Reference< css::uno::XInterface > & r )
233 Reference< XComponent > comp( r, UNO_QUERY );
234 if( comp.is() )
235 comp->dispose();
238 void disposeNoThrow( const css::uno::Reference< css::uno::XInterface > & r )
242 disposeObject( r );
244 catch( SQLException & )
246 // ignore this
251 Reference< XConnection > extractConnectionFromStatement( const Reference< XInterface > & stmt )
253 Reference< XConnection > ret;
255 Reference< css::sdbc::XStatement > owner( stmt, UNO_QUERY );
256 if( owner.is() )
257 ret = owner->getConnection();
258 else
260 Reference< css::sdbc::XPreparedStatement > myowner( stmt, UNO_QUERY );
261 if( myowner.is() )
262 ret = myowner->getConnection();
263 if( ! ret.is() )
264 throw SQLException(
265 "PQSDBC: Couldn't retrieve connection from statement",
266 Reference< XInterface > () , OUString(), 0 , css::uno::Any() );
269 return ret;
273 DisposeGuard::DisposeGuard( const Reference< XInterface > & r )
274 : d( r )
277 DisposeGuard::~DisposeGuard()
279 disposeNoThrow( d );
282 TransactionGuard::TransactionGuard( const Reference< XStatement > &stmt )
283 : m_stmt( stmt ),
284 m_commited( false )
286 m_stmt->executeUpdate( getStatics().BEGIN );
289 void TransactionGuard::commit()
291 m_stmt->executeUpdate( getStatics().COMMIT );
292 m_commited = true;
295 void TransactionGuard::executeUpdate( const OUString & sql )
297 m_stmt->executeUpdate( sql );
300 TransactionGuard::~TransactionGuard()
304 if( ! m_commited )
305 m_stmt->executeUpdate( getStatics().ROLLBACK );
307 catch( css::uno::Exception & )
309 // ignore, we are within a dtor
312 disposeNoThrow( m_stmt );
316 bool isWhitespace( sal_Unicode c )
318 return ' ' == c || 9 == c || 10 == c || 13 == c;
321 OUString extractTableFromInsert( const OUString & sql )
323 OUString ret;
324 int i = 0;
325 while (i < sql.getLength() && isWhitespace(sql[i])) { i++; }
327 if( sql.matchIgnoreAsciiCase("insert", i) )
329 i += 6;
330 while (i < sql.getLength() && isWhitespace(sql[i])) { i++; }
331 if( sql.matchIgnoreAsciiCase("into", i) )
333 i +=4;
334 while (i < sql.getLength() && isWhitespace(sql[i])) { i++; }
335 int start = i;
336 bool quote = (sql[i] == '"');
337 for( i++ ; i < sql.getLength() ; i ++ )
339 if( quote && sql[i] == '"' )
341 while (i < sql.getLength() && isWhitespace(sql[i])) { i++; }
342 if( '.' == sql[i] )
344 while (i < sql.getLength() && isWhitespace(sql[i])) { i++; }
345 if( '"' == sql[i] )
347 // the second part of the table name does not use quotes
348 // parse on
349 quote = false;
352 else
354 // end quoted name, ok
355 break;
358 else
360 if( isWhitespace( sql[i] ) )
362 // found the end of an unquoted name
363 break;
367 ret = sql.copy(start, i - start ).trim();
368 // printf( "pq_statement: parsed table name %s from insert\n" ,
369 // OUStringToOString( ret, RTL_TEXTENCODING_ASCII_US).getStr() );
372 return ret;
376 static bool isOperator( char c )
378 bool ret;
379 switch(c)
381 case '+':
382 case '-':
383 case '*':
384 case '/':
385 case '<':
386 case '>':
387 case '=':
388 case '~':
389 case '!':
390 case '@':
391 case '#':
392 case '%':
393 case '^':
394 case '&':
395 case '|':
396 case '`':
397 case '?':
398 case '$':
399 ret = true;
400 break;
401 default:
402 ret = false;
404 return ret;
407 void splitSQL( const OString & sql, std::vector< OString > &vec )
409 int length = sql.getLength();
411 int i = 0;
412 bool singleQuote = false;
413 bool doubleQuote = false;
414 int start = 0;
415 for( ; i < length ; i ++ )
417 char c = sql[i];
418 if( doubleQuote )
420 if( '"' == c )
422 vec.push_back( OString( &sql.getStr()[start], i-start+1 ) );
423 start = i + 1;
424 doubleQuote = false;
427 else if( singleQuote )
429 if( '\'' == c && (i+1) < length && '\'' == sql[i+1] )
431 // two subsequent single quotes within a quoted string
432 // mean a single quote within the string
433 i ++;
435 else if( '\'' == c )
437 vec.push_back( OString( &sql.getStr()[start], i - start +1 ) );
438 start = i + 1; // leave single quotes !
439 singleQuote = false;
442 else
444 if( '"' == c )
446 vec.push_back( OString( &sql.getStr()[start], i - start ) );
447 doubleQuote = true;
448 start = i;
450 else if( '\'' == c )
452 vec.push_back( OString( &sql.getStr()[start], i - start ) );
453 singleQuote = true;
454 start = i;
458 if( start < i )
459 vec.push_back( OString( &sql.getStr()[start] , i - start ) );
461 // for( i = 0 ; i < vec.size() ; i ++ )
462 // printf( "%s!" , vec[i].getStr() );
463 // printf( "\n" );
467 void tokenizeSQL( const OString & sql, std::vector< OString > &vec )
469 int length = sql.getLength();
471 int i = 0;
472 bool singleQuote = false;
473 bool doubleQuote = false;
474 int start = 0;
475 for( ; i < length ; i ++ )
477 char c = sql[i];
478 if( doubleQuote )
480 if( '"' == c )
482 vec.push_back( OString( &sql.getStr()[start], i-start ) );
483 start = i + 1;
484 doubleQuote = false;
487 else if( singleQuote )
489 if( '\'' == c )
491 vec.push_back( OString( &sql.getStr()[start], i - start +1 ) );
492 start = i + 1; // leave single quotes !
493 singleQuote = false;
496 else
498 if( '"' == c )
500 doubleQuote = true;
501 start = i +1; // skip double quotes !
503 else if( '\'' == c )
505 singleQuote = true;
506 start = i; // leave single quotes
508 else if( isWhitespace( c ) )
510 if( i == start )
511 start ++; // skip additional whitespace
512 else
514 vec.push_back( OString( &sql.getStr()[start], i - start ) );
515 start = i +1;
518 else if( ',' == c || isOperator( c ) || '(' == c || ')' == c )
520 if( i - start )
521 vec.push_back( OString( &sql.getStr()[start], i - start ) );
522 vec.push_back( OString( &sql.getStr()[i], 1 ) );
523 start = i + 1;
525 else if( '.' == c )
527 if( ( i > start && sql[start] >= '0' && sql[start] <= '9' ) ||
528 ( i == start && i > 1 && isWhitespace( sql[i-1] ) ) )
530 // ignore, is a literal
532 else
534 if( i - start )
535 vec.push_back( OString( &sql.getStr()[start], i - start ) );
536 vec.push_back( OString( "." ) );
537 start = i + 1;
542 if( start < i )
543 vec.push_back( OString( &sql.getStr()[start] , i - start ) );
545 // for( i = 0 ; i < vec.size() ; i ++ )
546 // printf( "%s!" , vec[i].getStr() );
547 // printf( "\n" );
551 void splitConcatenatedIdentifier( const OUString & source, OUString *first, OUString *second)
553 std::vector< OString > vec;
554 tokenizeSQL( OUStringToOString( source, RTL_TEXTENCODING_UTF8 ), vec );
555 switch (vec.size())
557 case 1:
558 first->clear();
559 *second = OStringToOUString( vec[0], RTL_TEXTENCODING_UTF8 );
560 break;
561 case 3:
562 *first = OStringToOUString( vec[0], RTL_TEXTENCODING_UTF8 );
563 *second = OStringToOUString( vec[2], RTL_TEXTENCODING_UTF8 );
564 break;
565 default:
566 SAL_WARN("connectivity.postgresql",
567 "pq_tools::splitConcatenatedIdentifier unexpected number of tokens in identifier: "
568 << vec.size());
572 OUString array2String( const css::uno::Sequence< Any > &seq )
574 OUStringBuffer buf(128);
575 int len = seq.getLength();
576 buf.append( "{" );
577 for( int i = 0 ; i < len ; i ++ )
579 OUString element;
580 seq[i] >>= element;
582 if( i > 0 )
583 buf.append( "," );
584 int strLength = element.getLength();
585 buf.append( "\"" );
586 for( int j = 0 ; j < strLength ; j ++ )
588 sal_Unicode c = element[j];
589 if( c == '\\' || c == '"' || c == '{' || c == '}' )
591 buf.append( "\\" );
593 buf.append( c );
595 buf.append( "\"" );
597 buf.append( "}" );
598 return buf.makeStringAndClear();
602 std::vector< Any > parseArray( const OUString & str )
604 int len = str.getLength();
605 bool doubleQuote = false;
606 int brackets = 0;
607 int i = 0;
609 OUStringBuffer current;
610 std::vector<Any> elements;
611 bool doubleQuotedValue = false;
612 while( i < len )
614 sal_Unicode c = str[i];
615 sal_Unicode cnext = str[i+1];
616 if( doubleQuote )
618 if( '\\' == c )
620 i ++;
621 current.append( cnext );
623 else if( '"' == c )
625 doubleQuote = false;
626 doubleQuotedValue = true; // signal, that there was an empty element
628 else
630 current.append( c );
633 else if ( '{' == c )
635 brackets ++;
637 else if( '}' == c )
639 brackets --;
640 if( brackets < 0 )
642 throw SQLException(
643 "error during array parsing, didn't expect a } at position "
644 + OUString::number(i) + " ('" + str + "')",
645 Reference< XInterface > (), OUString(), 1, Any() );
647 if( brackets == 0 )
649 if( !current.isEmpty() || doubleQuotedValue )
650 elements.push_back( makeAny( current.makeStringAndClear() ) );
652 else
654 current.append( c );
657 else if( '"' == c )
659 // if( current.getLength() != 0 )
660 // {
661 // OUStringBuffer buf;
662 // buf.appendAscii( "error during array parsing, didn't expect a \" at position " );
663 // buf.append( i );
664 // buf.append( " ('" );
665 // buf.append( str );
666 // buf.append( "')" );
667 // throw SDBCException(
668 // buf.makeStringAndClear(),
669 // Reference< XInterface > (), 1, Any() );
670 // }
671 // else
672 // {
673 doubleQuote = true;
674 // }
676 else if( ',' == c && brackets == 1)
678 doubleQuotedValue = false;
679 elements.push_back( makeAny( current.makeStringAndClear() ) );
681 else if( isWhitespace( c ) )
683 // ignore whitespace without quotes
685 else
687 current.append( c );
689 i++;
691 return elements;
694 std::vector< sal_Int32 > parseIntArray( const OUString & str )
696 sal_Int32 start = 0;
697 std::vector<sal_Int32> vec;
698 // printf( ">%s<\n" , OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
699 for( sal_Int32 i = str.indexOf( ' ' ) ; i != -1 ; i = str.indexOf( ' ', start) )
701 vec.push_back( rtl_ustr_toInt32( &str.pData->buffer[start], 10 ) );
702 // printf( "found %d\n" , rtl_ustr_toInt32( &str.pData->buffer[start], 10 ));
703 start = i + 1;
705 vec.push_back( rtl_ustr_toInt32( &str.pData->buffer[start], 10 ) );
706 // printf( "found %d\n" , rtl_ustr_toInt32( &str.pData->buffer[start], 10 ));
707 return vec;
710 void fillAttnum2attnameMap(
711 Int2StringMap &map,
712 const Reference< css::sdbc::XConnection > &conn,
713 const OUString &schema,
714 const OUString &table )
716 Reference< XPreparedStatement > prep = conn->prepareStatement(
717 "SELECT attname,attnum "
718 "FROM pg_attribute "
719 "INNER JOIN pg_class ON attrelid = pg_class.oid "
720 "INNER JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid "
721 "WHERE relname=? AND nspname=?" );
723 Reference< XParameters > paras( prep, UNO_QUERY_THROW );
724 paras->setString( 1 , table );
725 paras->setString( 2 , schema );
726 Reference< XResultSet > rs = prep->executeQuery();
728 Reference< XRow > xRow( rs , UNO_QUERY_THROW );
729 while( rs->next() )
731 map[ xRow->getInt(2) ] = xRow->getString(1);
735 OString extractSingleTableFromSelect( const std::vector< OString > &vec )
737 OString ret;
739 if( 0 == rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
740 vec[0].pData->buffer, vec[0].pData->length, "select" , 6 , 6 ) )
742 size_t token = 0;
744 for( token = 1; token < vec.size() ; token ++ )
746 if( 0 == rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
747 vec[token].getStr(), vec[token].getLength(), "from" , 4 , 4 ) )
749 // found from
750 break;
753 token ++;
755 if( token < vec.size() && 0 == rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
756 vec[token].pData->buffer, vec[token].pData->length, "only " , 4 , 4 ) )
758 token ++;
761 if( token < vec.size() && vec[token] != "(" )
763 // it is a table or a function name
764 OStringBuffer buf(128);
765 if( '"' == vec[token][0] )
766 buf.append( &(vec[token].getStr()[1]) , vec[token].getLength() -2 );
767 else
768 buf.append( vec[token] );
769 token ++;
771 if( token < vec.size() )
773 if( vec[token] == "." )
775 buf.append( vec[token] );
776 token ++;
777 if( token < vec.size() )
779 if( '"' == vec[token][0] )
780 buf.append( &(vec[token].getStr()[1]) , vec[token].getLength() -2 );
781 else
782 buf.append( vec[token] );
783 token ++;
788 ret = buf.makeStringAndClear();
789 // now got my table candidate
791 if( token < vec.size() && vec[token] == "(" )
793 // whoops, it is a function
794 ret.clear();
796 else
798 if( token < vec.size() )
800 if( 0 == rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
801 vec[token].pData->buffer, vec[token].pData->length, "as" , 2, 2 ) )
803 token += 2; // skip alias
807 if( token < vec.size() )
809 if( vec[token] == "," )
811 // whoops, multiple tables are used
812 ret.clear();
814 else
816 static const char * forbiddenKeywords[] =
817 { "join", "natural", "outer", "inner", "left", "right", "full" , nullptr };
818 for( int i = 0 ; forbiddenKeywords[i] ; i ++ )
820 size_t nKeywordLen = strlen(forbiddenKeywords[i]);
821 if( 0 == rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
822 vec[token].pData->buffer, vec[token].pData->length,
823 forbiddenKeywords[i], nKeywordLen,
824 nKeywordLen ) )
826 // whoops, it is a join
827 ret.clear();
835 return ret;
839 css::uno::Sequence< sal_Int32 > string2intarray( const OUString & str )
841 css::uno::Sequence< sal_Int32 > ret;
842 const sal_Int32 strlen = str.getLength();
843 if( str.getLength() > 1 )
845 sal_Int32 start = 0;
846 sal_uInt32 c;
847 while ( iswspace( (c=str.iterateCodePoints(&start)) ) )
848 if ( start == strlen)
849 return ret;
850 if ( c != L'{' )
851 return ret;
852 while ( iswspace( c=str.iterateCodePoints(&start) ) )
853 if ( start == strlen)
854 return ret;
855 if ( c == L'}' )
856 return ret;
858 std::vector< sal_Int32 > vec;
861 OUStringBuffer digits;
864 if(!iswspace(c))
865 break;
866 if ( start == strlen)
867 return ret;
868 c=str.iterateCodePoints(&start);
869 } while ( c );
872 if (!iswdigit(c))
873 break;
874 if ( start == strlen)
875 return ret;
876 digits.append(OUString(&c, 1));
877 c = str.iterateCodePoints(&start);
878 } while ( c );
879 vec.push_back( digits.makeStringAndClear().toInt32() );
882 if(!iswspace(c))
883 break;
884 if ( start == strlen)
885 return ret;
886 c = str.iterateCodePoints(&start);
887 } while ( c );
888 if ( c == L'}' )
889 break;
890 if ( str.iterateCodePoints(&start) != L',' )
891 return ret;
892 if ( start == strlen)
893 return ret;
894 } while( true );
895 // vec is guaranteed non-empty
896 assert(vec.size() > 0);
897 ret = css::uno::Sequence< sal_Int32 > ( vec.data() , vec.size() );
899 return ret;
903 Sequence< OUString > convertMappedIntArray2StringArray(
904 const Int2StringMap &map, const Sequence< sal_Int32 > &intArray )
906 Sequence< OUString > ret( intArray.getLength() );
907 for( int i = 0; i < intArray.getLength() ; i ++ )
909 Int2StringMap::const_iterator ii = map.find( intArray[i] );
910 if( ii != map.end() )
911 ret[i] = ii->second;
913 return ret;
917 OUString sqltype2string( const Reference< XPropertySet > & desc )
919 OUStringBuffer typeName;
920 typeName.append( extractStringProperty( desc, getStatics().TYPE_NAME ) );
921 sal_Int32 precision = extractIntProperty( desc, getStatics().PRECISION );
923 if( precision )
925 switch( extractIntProperty( desc, getStatics().TYPE ) )
927 case css::sdbc::DataType::VARBINARY:
928 case css::sdbc::DataType::VARCHAR:
929 case css::sdbc::DataType::CHAR:
931 typeName.append( "(" );
932 typeName.append( precision );
933 typeName.append( ")" );
934 break;
936 case css::sdbc::DataType::DECIMAL:
937 case css::sdbc::DataType::NUMERIC:
939 typeName.append( "(" );
940 typeName.append( precision );
941 typeName.append( "," );
942 typeName.append( extractIntProperty( desc, getStatics().SCALE ) );
943 typeName.append( ")" );
944 break;
946 default:
947 ((void)0);
950 return typeName.makeStringAndClear();
954 static void keyType2String( OUStringBuffer & buf, sal_Int32 keyType )
956 if( css::sdbc::KeyRule::CASCADE == keyType )
958 buf.append( "CASCADE " );
960 else if( css::sdbc::KeyRule::RESTRICT == keyType )
962 buf.append( "RESTRICT " );
964 else if( css::sdbc::KeyRule::SET_DEFAULT == keyType )
966 buf.append( "SET DEFAULT " );
968 else if( css::sdbc::KeyRule::SET_NULL == keyType )
970 buf.append( "SET NULL " );
972 else //if( css::sdbc::KeyRule::NO_ACTION == keyType )
974 buf.append( "NO ACTION " );
978 void bufferKey2TableConstraint(
979 OUStringBuffer &buf, const Reference< XPropertySet > &key, ConnectionSettings *settings )
981 Statics &st = getStatics();
982 sal_Int32 type = extractIntProperty( key, st.TYPE );
983 OUString referencedTable = extractStringProperty( key, st.REFERENCED_TABLE );
984 sal_Int32 updateRule = extractIntProperty( key, st.UPDATE_RULE );
985 sal_Int32 deleteRule = extractIntProperty( key, st.DELETE_RULE );
986 bool foreign = false;
987 if( type == css::sdbcx::KeyType::UNIQUE )
989 buf.append( "UNIQUE( " );
991 else if( type == css::sdbcx::KeyType::PRIMARY )
993 buf.append( "PRIMARY KEY( " );
995 else if( type == css::sdbcx::KeyType::FOREIGN )
997 foreign = true;
998 buf.append( "FOREIGN KEY( " );
1001 Reference< XColumnsSupplier > columns( key, UNO_QUERY );
1002 if( columns.is() )
1004 Reference< XEnumerationAccess > colEnumAccess( columns->getColumns(), UNO_QUERY );
1005 if( colEnumAccess.is() )
1007 Reference< XEnumeration > colEnum = colEnumAccess->createEnumeration();
1008 bool first = true;
1009 while(colEnum.is() && colEnum->hasMoreElements() )
1011 if( first )
1013 first = false;
1015 else
1017 buf.append( ", " );
1019 Reference< XPropertySet > keyColumn( colEnum->nextElement(), UNO_QUERY_THROW );
1020 bufferQuoteIdentifier(buf, extractStringProperty( keyColumn, st.NAME ), settings );
1024 buf.append( ") " );
1026 if( foreign )
1028 buf.append( "REFERENCES " );
1029 OUString schema;
1030 OUString tableName;
1031 splitConcatenatedIdentifier( referencedTable, &schema, &tableName );
1032 bufferQuoteQualifiedIdentifier(buf , schema, tableName, settings );
1033 if(columns.is() )
1035 Reference< XEnumerationAccess > colEnumAccess( columns->getColumns(), UNO_QUERY);
1036 if( colEnumAccess.is() )
1038 buf.append( " (" );
1039 Reference< XEnumeration > colEnum(colEnumAccess->createEnumeration());
1040 bool first = true;
1041 while(colEnum.is() && colEnum->hasMoreElements() )
1043 if( first )
1045 first = false;
1047 else
1049 buf.append( ", " );
1051 Reference< XPropertySet > keyColumn( colEnum->nextElement(), UNO_QUERY_THROW );
1052 bufferQuoteIdentifier(
1053 buf, extractStringProperty( keyColumn, st.RELATED_COLUMN ), settings );
1055 buf.append( ") " );
1059 buf.append( "ON DELETE " );
1060 keyType2String( buf, deleteRule );
1061 buf.append( " ON UPDATE " );
1062 keyType2String( buf, updateRule );
1067 void extractNameValuePairsFromInsert( String2StringMap & map, const OString & lastQuery )
1069 std::vector< OString > vec;
1070 tokenizeSQL( lastQuery, vec );
1072 int nSize = vec.size();
1073 // printf( "1 %d\n", nSize );
1074 if( nSize > 6 &&
1075 vec[0].equalsIgnoreAsciiCase( "insert" ) &&
1076 vec[1].equalsIgnoreAsciiCase( "into" ) )
1078 int n = 2;
1080 // printf( "1a\n" );
1081 // skip table name
1082 if( vec[n+1].equalsIgnoreAsciiCase( "." ) )
1084 n +=2;
1087 n ++;
1088 if( vec[n].equalsIgnoreAsciiCase( "(" ) )
1090 std::vector< OString> names;
1091 // printf( "2\n" );
1092 // extract names
1093 n++;
1094 while( nSize > n && ! vec[n].equalsIgnoreAsciiCase( ")" ) )
1096 names.push_back( vec[n] );
1097 if( nSize > n+1 && vec[n+1].equalsIgnoreAsciiCase( "," ) )
1099 n ++;
1101 n++;
1103 n++;
1105 // now read the values
1106 if( nSize > n +1 && vec[n].equalsIgnoreAsciiCase("VALUES") &&
1107 vec[n+1].equalsIgnoreAsciiCase( "(" ) )
1109 n +=2;
1110 // printf( "3\n" );
1111 for (auto& name : names)
1113 if (n >= nSize)
1114 break;
1116 map[name] = vec[n];
1117 if( nSize > n+1 && vec[n+1].equalsIgnoreAsciiCase(",") )
1119 n ++;
1121 n++;
1128 OUString querySingleValue(
1129 const css::uno::Reference< css::sdbc::XConnection > &connection,
1130 const OUString &query )
1132 OUString ret;
1133 Reference< XStatement > stmt = connection->createStatement();
1134 DisposeGuard guard( stmt );
1135 Reference< XResultSet > rs = stmt->executeQuery( query );
1136 Reference< XRow > xRow( rs, UNO_QUERY );
1137 if( rs->next() )
1138 ret = xRow->getString( 1 );
1139 return ret;
1143 // copied from connectivity/source/dbtools, can't use the function directly
1144 bool implSetObject( const Reference< XParameters >& _rxParameters,
1145 const sal_Int32 _nColumnIndex, const Any& _rValue)
1147 bool bSuccessfullyReRouted = true;
1148 switch (_rValue.getValueTypeClass())
1150 case css::uno::TypeClass_HYPER:
1152 _rxParameters->setLong( _nColumnIndex, sal_Int64(0) );
1154 break;
1156 case css::uno::TypeClass_VOID:
1157 _rxParameters->setNull(_nColumnIndex,css::sdbc::DataType::VARCHAR);
1158 break;
1160 case css::uno::TypeClass_STRING:
1161 _rxParameters->setString(_nColumnIndex, *o3tl::forceAccess<OUString>(_rValue));
1162 break;
1164 case css::uno::TypeClass_BOOLEAN:
1165 _rxParameters->setBoolean(_nColumnIndex, *o3tl::forceAccess<bool>(_rValue));
1166 break;
1168 case css::uno::TypeClass_BYTE:
1169 _rxParameters->setByte(_nColumnIndex, *o3tl::forceAccess<sal_Int8>(_rValue));
1170 break;
1172 case css::uno::TypeClass_UNSIGNED_SHORT:
1173 case css::uno::TypeClass_SHORT:
1174 _rxParameters->setShort(_nColumnIndex, *o3tl::forceAccess<sal_Int16>(_rValue));
1175 break;
1177 case css::uno::TypeClass_CHAR:
1178 _rxParameters->setString(_nColumnIndex, OUString(*o3tl::forceAccess<sal_Unicode>(_rValue)));
1179 break;
1181 case css::uno::TypeClass_UNSIGNED_LONG:
1182 case css::uno::TypeClass_LONG:
1183 _rxParameters->setInt(_nColumnIndex, *o3tl::forceAccess<sal_Int32>(_rValue));
1184 break;
1186 case css::uno::TypeClass_FLOAT:
1187 _rxParameters->setFloat(_nColumnIndex, *o3tl::forceAccess<float>(_rValue));
1188 break;
1190 case css::uno::TypeClass_DOUBLE:
1191 _rxParameters->setDouble(_nColumnIndex, *o3tl::forceAccess<double>(_rValue));
1192 break;
1194 case css::uno::TypeClass_SEQUENCE:
1195 if (auto s = o3tl::tryAccess<Sequence< sal_Int8 >>(_rValue))
1197 _rxParameters->setBytes(_nColumnIndex, *s);
1199 else
1200 bSuccessfullyReRouted = false;
1201 break;
1202 case css::uno::TypeClass_STRUCT:
1203 if (auto s1 = o3tl::tryAccess<css::util::DateTime>(_rValue))
1204 _rxParameters->setTimestamp(_nColumnIndex, *s1);
1205 else if (auto s2 = o3tl::tryAccess<css::util::Date>(_rValue))
1206 _rxParameters->setDate(_nColumnIndex, *s2);
1207 else if (auto s3 = o3tl::tryAccess<css::util::Time>(_rValue))
1208 _rxParameters->setTime(_nColumnIndex, *s3);
1209 else
1210 bSuccessfullyReRouted = false;
1211 break;
1213 case css::uno::TypeClass_INTERFACE:
1215 Reference< css::io::XInputStream > xStream;
1216 if (_rValue >>= xStream)
1218 _rValue >>= xStream;
1219 _rxParameters->setBinaryStream(_nColumnIndex, xStream, xStream->available());
1220 break;
1222 [[fallthrough]];
1224 default:
1225 bSuccessfullyReRouted = false;
1229 return bSuccessfullyReRouted;
1234 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */