1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_features.h>
22 #include <vcl/errinf.hxx>
23 #include <tools/urlobj.hxx>
24 #include <svl/converter.hxx>
25 #include <svl/numformat.hxx>
26 #include <comphelper/processfactory.hxx>
27 #include <comphelper/propertysequence.hxx>
28 #include <comphelper/types.hxx>
29 #include <ucbhelper/content.hxx>
30 #include <svx/txenctab.hxx>
31 #include <unotools/sharedunocomponent.hxx>
32 #include <unotools/charclass.hxx>
33 #include <rtl/character.hxx>
34 #include <rtl/tencinfo.h>
35 #include <sal/log.hxx>
36 #include <osl/diagnose.h>
37 #include <comphelper/diagnose_ex.hxx>
38 #include <o3tl/string_view.hxx>
40 #include <com/sun/star/sdb/CommandType.hpp>
41 #include <com/sun/star/sdbc/DataType.hpp>
42 #include <com/sun/star/sdbc/SQLException.hpp>
43 #include <com/sun/star/sdbc/XConnection.hpp>
44 #include <com/sun/star/sdbc/DriverManager.hpp>
45 #include <com/sun/star/sdbc/XResultSetUpdate.hpp>
46 #include <com/sun/star/sdbc/XRow.hpp>
47 #include <com/sun/star/sdbc/XRowSet.hpp>
48 #include <com/sun/star/sdbc/XRowUpdate.hpp>
49 #include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp>
50 #include <com/sun/star/sdbcx/XAppend.hpp>
51 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
52 #include <com/sun/star/sdbcx/XDataDefinitionSupplier.hpp>
53 #include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp>
54 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
55 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
56 #include <com/sun/star/beans/XPropertySet.hpp>
57 #include <com/sun/star/ucb/NameClash.hpp>
58 #include <com/sun/star/ucb/TransferInfo.hpp>
59 #include <com/sun/star/ucb/XCommandInfo.hpp>
61 #include <scerrors.hxx>
63 #include <progress.hxx>
64 #include <editutil.hxx>
65 #include <cellform.hxx>
66 #include <dbdocutl.hxx>
67 #include <dociter.hxx>
68 #include <globstr.hrc>
69 #include <scresid.hxx>
70 #include <svl/zformat.hxx>
71 #include <svl/intitem.hxx>
72 #include <patattr.hxx>
73 #include <scitems.hxx>
74 #include <docpool.hxx>
75 #include <segmenttree.hxx>
76 #include <docparam.hxx>
77 #include <cellvalue.hxx>
79 #include <unordered_set>
82 using namespace com::sun::star
;
85 #if HAVE_FEATURE_DBCONNECTIVITY
87 constexpr OUString SC_SERVICE_ROWSET
= u
"com.sun.star.sdb.RowSet"_ustr
;
89 //! move to a header file?
90 constexpr OUString SC_DBPROP_ACTIVECONNECTION
= u
"ActiveConnection"_ustr
;
91 constexpr OUString SC_DBPROP_COMMAND
= u
"Command"_ustr
;
92 constexpr OUString SC_DBPROP_COMMANDTYPE
= u
"CommandType"_ustr
;
93 constexpr OUStringLiteral SC_DBPROP_PROPCHANGE_NOTIFY
= u
"PropertyChangeNotificationEnabled";
95 constexpr OUString SC_DBPROP_NAME
= u
"Name"_ustr
;
96 constexpr OUStringLiteral SC_DBPROP_TYPE
= u
"Type";
97 constexpr OUStringLiteral SC_DBPROP_PRECISION
= u
"Precision";
98 constexpr OUStringLiteral SC_DBPROP_SCALE
= u
"Scale";
100 constexpr OUString SC_DBPROP_EXTENSION
= u
"Extension"_ustr
;
101 constexpr OUString SC_DBPROP_CHARSET
= u
"CharSet"_ustr
;
105 ErrCode
lcl_getDBaseConnection(uno::Reference
<sdbc::XDriverManager2
>& _rDrvMgr
, uno::Reference
<sdbc::XConnection
>& _rConnection
, OUString
& _rTabName
, std::u16string_view rFullFileName
, rtl_TextEncoding eCharSet
)
108 aURL
.SetSmartProtocol( INetProtocol::File
);
109 aURL
.SetSmartURL( rFullFileName
);
110 _rTabName
= aURL
.getBase( INetURLObject::LAST_SEGMENT
, true,
111 INetURLObject::DecodeMechanism::Unambiguous
);
112 OUString aExtension
= aURL
.getExtension();
113 aURL
.removeSegment();
114 aURL
.removeFinalSlash();
115 OUString aPath
= aURL
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
116 const uno::Reference
<uno::XComponentContext
>& xContext
= comphelper::getProcessComponentContext();
118 _rDrvMgr
.set( sdbc::DriverManager::create( xContext
) );
122 const OUString aConnUrl
{"sdbc:dbase:" + aPath
};
124 // sdbc:dbase is based on the css.sdbc.FILEConnectionProperties UNOIDL service, so we can
125 // transport the raw rtl_TextEncoding value instead of having to translate it into an IANA
126 // character set name string (which might not exist for certain eCharSet values, like
127 // RTL_TEXTENCODING_MS_950):
128 uno::Sequence
<beans::PropertyValue
> aProps( comphelper::InitPropertySequence({
129 { SC_DBPROP_EXTENSION
, uno::Any(aExtension
) },
130 { SC_DBPROP_CHARSET
, uno::Any(eCharSet
) }
133 _rConnection
= _rDrvMgr
->getConnectionWithInfo( aConnUrl
, aProps
);
138 #endif // HAVE_FEATURE_DBCONNECTIVITY
140 // MoveFile/KillFile/IsDocument: similar to SfxContentHelper
142 bool ScDocShell::MoveFile( const INetURLObject
& rSourceObj
, const INetURLObject
& rDestObj
)
144 bool bMoveData
= true;
145 bool bRet
= true, bKillSource
= false;
146 if ( rSourceObj
.GetProtocol() != rDestObj
.GetProtocol() )
151 OUString aName
= rDestObj
.getName();
152 INetURLObject aDestPathObj
= rDestObj
;
153 aDestPathObj
.removeSegment();
154 aDestPathObj
.setFinalSlash();
158 ::ucbhelper::Content
aDestPath( aDestPathObj
.GetMainURL(INetURLObject::DecodeMechanism::NONE
),
159 uno::Reference
< css::ucb::XCommandEnvironment
>(),
160 comphelper::getProcessComponentContext() );
161 uno::Reference
< css::ucb::XCommandInfo
> xInfo
= aDestPath
.getCommands();
162 OUString aTransferName
= u
"transfer"_ustr
;
163 if ( xInfo
->hasCommandByName( aTransferName
) )
165 aDestPath
.executeCommand( aTransferName
, uno::Any(
166 css::ucb::TransferInfo( bMoveData
, rSourceObj
.GetMainURL(INetURLObject::DecodeMechanism::NONE
), aName
,
167 css::ucb::NameClash::ERROR
) ) );
171 OSL_FAIL( "transfer command not available" );
174 catch( uno::Exception
& )
176 // ucb may throw different exceptions on failure now
181 KillFile( rSourceObj
);
186 bool ScDocShell::KillFile( const INetURLObject
& rURL
)
191 ::ucbhelper::Content
aCnt( rURL
.GetMainURL(INetURLObject::DecodeMechanism::NONE
),
192 uno::Reference
< css::ucb::XCommandEnvironment
>(),
193 comphelper::getProcessComponentContext() );
194 aCnt
.executeCommand( u
"delete"_ustr
, css::uno::Any( true ) );
196 catch( uno::Exception
& )
198 // ucb may throw different exceptions on failure now
205 bool ScDocShell::IsDocument( const INetURLObject
& rURL
)
210 ::ucbhelper::Content
aCnt( rURL
.GetMainURL(INetURLObject::DecodeMechanism::NONE
),
211 uno::Reference
< css::ucb::XCommandEnvironment
>(),
212 comphelper::getProcessComponentContext() );
213 bRet
= aCnt
.isDocument();
215 catch( uno::Exception
& )
217 // ucb may throw different exceptions on failure now - warning only
218 TOOLS_WARN_EXCEPTION( "sc", "Any other exception" );
224 #if HAVE_FEATURE_DBCONNECTIVITY
226 static void lcl_setScalesToColumns(ScDocument
& rDoc
, const vector
<tools::Long
>& rScales
)
228 SvNumberFormatter
* pFormatter
= rDoc
.GetFormatTable();
232 SCCOL nColCount
= static_cast<SCCOL
>(rScales
.size());
233 for (SCCOL i
= 0; i
< nColCount
; ++i
)
238 sal_uInt32 nOldFormat
= rDoc
.GetNumberFormat(i
, 0, 0);
239 const SvNumberformat
* pOldEntry
= pFormatter
->GetEntry(nOldFormat
);
243 LanguageType eLang
= pOldEntry
->GetLanguage();
244 bool bThousand
, bNegRed
;
245 sal_uInt16 nPrecision
, nLeading
;
246 pOldEntry
->GetFormatSpecialInfo(bThousand
, bNegRed
, nPrecision
, nLeading
);
248 nPrecision
= static_cast<sal_uInt16
>(rScales
[i
]);
249 OUString aNewPicture
= pFormatter
->GenerateFormat(nOldFormat
, eLang
,
250 bThousand
, bNegRed
, nPrecision
, nLeading
);
252 sal_uInt32 nNewFormat
= pFormatter
->GetEntryKey(aNewPicture
, eLang
);
253 if (nNewFormat
== NUMBERFORMAT_ENTRY_NOT_FOUND
)
255 sal_Int32 nErrPos
= 0;
256 SvNumFormatType nNewType
= SvNumFormatType::ALL
;
257 bool bOk
= pFormatter
->PutEntry(
258 aNewPicture
, nErrPos
, nNewType
, nNewFormat
, eLang
);
264 ScPatternAttr
aNewAttrs(rDoc
.getCellAttributeHelper());
265 SfxItemSet
& rSet
= aNewAttrs
.GetItemSet();
266 rSet
.Put( SfxUInt32Item(ATTR_VALUE_FORMAT
, nNewFormat
) );
267 rDoc
.ApplyPatternAreaTab(i
, 0, i
, rDoc
.MaxRow(), 0, aNewAttrs
);
271 #endif // HAVE_FEATURE_DBCONNECTIVITY
273 ErrCode
ScDocShell::DBaseImport( const OUString
& rFullFileName
, rtl_TextEncoding eCharSet
,
274 std::map
<SCCOL
, ScColWidthParam
>& aColWidthParam
, ScFlatBoolRowSegments
& rRowHeightsRecalc
)
276 #if !HAVE_FEATURE_DBCONNECTIVITY
277 (void) rFullFileName
;
279 (void) aColWidthParam
;
280 (void) rRowHeightsRecalc
;
282 return ERRCODE_IO_GENERAL
;
285 ErrCode nErr
= ERRCODE_NONE
;
289 sal_Int32 nColCount
= 0;
291 uno::Reference
<sdbc::XDriverManager2
> xDrvMan
;
292 uno::Reference
<sdbc::XConnection
> xConnection
;
293 ErrCode nRet
= lcl_getDBaseConnection(xDrvMan
,xConnection
,aTabName
,rFullFileName
,eCharSet
);
294 if ( !xConnection
.is() || !xDrvMan
.is() )
296 ::utl::DisposableComponent
aConnectionHelper(xConnection
);
298 ScProgress
aProgress( this, ScResId( STR_LOAD_DOC
), 0, true );
299 uno::Reference
<lang::XMultiServiceFactory
> xFactory
= comphelper::getProcessServiceFactory();
300 uno::Reference
<sdbc::XRowSet
> xRowSet( xFactory
->createInstance(SC_SERVICE_ROWSET
),
302 ::utl::DisposableComponent
aRowSetHelper(xRowSet
);
303 uno::Reference
<beans::XPropertySet
> xRowProp( xRowSet
, uno::UNO_QUERY
);
304 OSL_ENSURE( xRowProp
.is(), "can't get RowSet" );
305 if (!xRowProp
.is()) return SCERR_IMPORT_CONNECT
;
307 xRowProp
->setPropertyValue( SC_DBPROP_ACTIVECONNECTION
, uno::Any(xConnection
) );
309 xRowProp
->setPropertyValue( SC_DBPROP_COMMANDTYPE
, uno::Any(sdb::CommandType::TABLE
) );
311 xRowProp
->setPropertyValue( SC_DBPROP_COMMAND
, uno::Any(aTabName
) );
313 xRowProp
->setPropertyValue( SC_DBPROP_PROPCHANGE_NOTIFY
, uno::Any(false) );
317 uno::Reference
<sdbc::XResultSetMetaData
> xMeta
;
318 uno::Reference
<sdbc::XResultSetMetaDataSupplier
> xMetaSupp( xRowSet
, uno::UNO_QUERY
);
319 if ( xMetaSupp
.is() )
320 xMeta
= xMetaSupp
->getMetaData();
322 nColCount
= xMeta
->getColumnCount(); // this is the number of real columns
324 if ( nColCount
> m_pDocument
->MaxCol()+1 )
326 nColCount
= m_pDocument
->MaxCol()+1;
327 nErr
= SCWARN_IMPORT_COLUMN_OVERFLOW
; // warning
330 uno::Reference
<sdbc::XRow
> xRow( xRowSet
, uno::UNO_QUERY
);
331 OSL_ENSURE( xRow
.is(), "can't get Row" );
332 if (!xRow
.is()) return SCERR_IMPORT_CONNECT
;
334 // currency flag is not needed for dBase
335 uno::Sequence
<sal_Int32
> aColTypes( nColCount
); // column types
336 sal_Int32
* pTypeArr
= aColTypes
.getArray();
337 for (sal_Int32 i
=0; i
<nColCount
; i
++)
338 pTypeArr
[i
] = xMeta
->getColumnType( i
+1 );
341 //! add type descriptions
343 aProgress
.SetState( 0 );
345 vector
<tools::Long
> aScales(nColCount
, -1);
346 for (sal_Int32 i
=0; i
<nColCount
; i
++)
348 OUString aHeader
= xMeta
->getColumnLabel( i
+1 );
350 switch ( pTypeArr
[i
] )
352 case sdbc::DataType::BIT
:
355 case sdbc::DataType::DATE
:
358 case sdbc::DataType::LONGVARCHAR
:
361 case sdbc::DataType::VARCHAR
:
362 aHeader
+= ",C," + OUString::number( xMeta
->getColumnDisplaySize( i
+1 ) );
364 case sdbc::DataType::DECIMAL
:
366 tools::Long nPrec
= xMeta
->getPrecision( i
+1 );
367 tools::Long nScale
= xMeta
->getScale( i
+1 );
370 SvDbaseConverter::ConvertPrecisionToDbase(
373 OUString::number( nScale
);
379 m_pDocument
->SetString( static_cast<SCCOL
>(i
), 0, 0, aHeader
);
382 lcl_setScalesToColumns(*m_pDocument
, aScales
);
384 SCROW nRow
= 1; // 0 is column titles
386 while ( !bEnd
&& xRowSet
->next() )
388 if (nRow
<= m_pDocument
->MaxRow())
390 bool bSimpleRow
= true;
392 for (sal_Int32 i
=0; i
<nColCount
; i
++)
394 ScDatabaseDocUtil::StrData aStrData
;
395 ScDatabaseDocUtil::PutData( *m_pDocument
, nCol
, nRow
, 0,
396 xRow
, i
+1, pTypeArr
[i
], false,
399 if (aStrData
.mnStrLength
> aColWidthParam
[nCol
].mnMaxTextLen
)
401 aColWidthParam
[nCol
].mnMaxTextLen
= aStrData
.mnStrLength
;
402 aColWidthParam
[nCol
].mnMaxTextRow
= nRow
;
405 if (!aStrData
.mbSimpleText
)
408 aColWidthParam
[nCol
].mbSimpleText
= false;
414 rRowHeightsRecalc
.setTrue(nRow
, nRow
);
417 else // past the end of the spreadsheet
419 bEnd
= true; // don't continue
420 nErr
= SCWARN_IMPORT_RANGE_OVERFLOW
; // warning message
424 catch ( sdbc::SQLException
& )
426 nErr
= SCERR_IMPORT_CONNECT
;
428 catch ( uno::Exception
& )
430 TOOLS_WARN_EXCEPTION( "sc", "Unexpected exception in database");
431 nErr
= ERRCODE_IO_GENERAL
;
435 #endif // HAVE_FEATURE_DBCONNECTIVITY
438 #if HAVE_FEATURE_DBCONNECTIVITY
442 void lcl_GetColumnTypes(
443 ScDocShell
& rDocShell
, const ScRange
& rDataRange
, bool bHasFieldNames
,
444 OUString
* pColNames
, sal_Int32
* pColTypes
, sal_Int32
* pColLengths
,
445 sal_Int32
* pColScales
, bool& bHasMemo
, rtl_TextEncoding eCharSet
)
447 ScDocument
& rDoc
= rDocShell
.GetDocument();
448 ScInterpreterContext
& rContext
= rDoc
.GetNonThreadedContext();
450 SCTAB nTab
= rDataRange
.aStart
.Tab();
451 SCCOL nFirstCol
= rDataRange
.aStart
.Col();
452 SCROW nFirstRow
= rDataRange
.aStart
.Row();
453 SCCOL nLastCol
= rDataRange
.aEnd
.Col();
454 SCROW nLastRow
= rDataRange
.aEnd
.Row();
456 typedef std::unordered_set
<OUString
> StrSetType
;
457 StrSetType aFieldNames
;
459 tools::Long nField
= 0;
460 SCROW nFirstDataRow
= ( bHasFieldNames
? nFirstRow
+ 1 : nFirstRow
);
461 for ( SCCOL nCol
= nFirstCol
; nCol
<= nLastCol
; nCol
++ )
463 bool bTypeDefined
= false;
464 bool bPrecDefined
= false;
465 sal_Int32 nFieldLen
= 0;
466 sal_Int32 nPrecision
= 0;
467 sal_Int32 nDbType
= sdbc::DataType::SQLNULL
;
470 // Fieldname[,Type[,Width[,Prec]]]
471 // Type etc.: L; D; C[,W]; N[,W[,P]]
472 if ( bHasFieldNames
)
474 OUString aString
{rDoc
.GetString(nCol
, nFirstRow
, nTab
).toAsciiUpperCase()};
476 aFieldName
= aString
.getToken( 0, ',', nIdx
);
479 aString
= aString
.replaceAll(" ", "");
480 switch ( o3tl::getToken(aString
, 0, ',', nIdx
)[0] )
483 nDbType
= sdbc::DataType::BIT
;
489 nDbType
= sdbc::DataType::DATE
;
495 nDbType
= sdbc::DataType::LONGVARCHAR
;
502 nDbType
= sdbc::DataType::VARCHAR
;
507 nDbType
= sdbc::DataType::DECIMAL
;
511 if ( bTypeDefined
&& !nFieldLen
&& nIdx
>0 )
513 nFieldLen
= o3tl::toInt32(o3tl::getToken(aString
, 0, ',', nIdx
));
514 if ( !bPrecDefined
&& nIdx
>0 )
516 OUString
aTmp( aString
.getToken( 0, ',', nIdx
) );
517 if ( CharClass::isAsciiNumeric(aTmp
) )
519 nPrecision
= aTmp
.toInt32();
520 if (nPrecision
&& nFieldLen
< nPrecision
+1)
521 nFieldLen
= nPrecision
+ 1; // include decimal separator
528 // Check field name and generate valid field name if necessary.
529 // First character has to be alphabetical, subsequent characters
530 // have to be alphanumerical or underscore.
531 // "_DBASELOCK" is reserved (obsolete because first character is
532 // not alphabetical).
533 // No duplicated names.
534 if ( !rtl::isAsciiAlpha(aFieldName
[0]) )
535 aFieldName
= "N" + aFieldName
;
536 OUStringBuffer aTmpStr
;
538 for ( const sal_Unicode
* p
= aFieldName
.getStr(); ( c
= *p
) != 0; p
++ )
540 if ( rtl::isAsciiAlpha(c
) || rtl::isAsciiDigit(c
) || c
== '_' )
545 aFieldName
= aTmpStr
.makeStringAndClear();
546 if ( aFieldName
.getLength() > 10 )
547 aFieldName
= aFieldName
.copy(0, 10);
549 if (!aFieldNames
.insert(aFieldName
).second
)
550 { // Duplicated field name, append numeric suffix.
552 OUString
aFixPart( aFieldName
);
556 OUString aVarPart
= OUString::number( nSub
);
557 if ( aFixPart
.getLength() + aVarPart
.getLength() > 10 )
558 aFixPart
= aFixPart
.copy( 0, 10 - aVarPart
.getLength() );
559 aFieldName
= aFixPart
+ aVarPart
;
560 } while (!aFieldNames
.insert(aFieldName
).second
);
565 aFieldName
= "N" + OUString::number(nCol
+1);
570 ScRefCellValue
aCell(rDoc
, ScAddress(nCol
, nFirstDataRow
, nTab
));
571 if (aCell
.isEmpty() || aCell
.hasString())
572 nDbType
= sdbc::DataType::VARCHAR
;
575 sal_uInt32 nFormat
= rDoc
.GetNumberFormat( nCol
, nFirstDataRow
, nTab
);
576 switch (rContext
.NFGetType(nFormat
))
578 case SvNumFormatType::LOGICAL
:
579 nDbType
= sdbc::DataType::BIT
;
582 case SvNumFormatType::DATE
:
583 nDbType
= sdbc::DataType::DATE
;
586 case SvNumFormatType::TIME
:
587 case SvNumFormatType::DATETIME
:
588 nDbType
= sdbc::DataType::VARCHAR
;
591 nDbType
= sdbc::DataType::DECIMAL
;
596 if ( nDbType
== sdbc::DataType::VARCHAR
&& !nFieldLen
)
597 { // Determine maximum field width.
598 nFieldLen
= rDoc
.GetMaxStringLen( nTab
, nCol
, nFirstDataRow
,
599 nLastRow
, eCharSet
);
600 if ( nFieldLen
== 0 )
603 else if ( nDbType
== sdbc::DataType::DECIMAL
)
604 { // Determine maximum field width and precision.
607 nLen
= rDoc
.GetMaxNumberStringLen( nPrec
, nTab
, nCol
,
608 nFirstDataRow
, nLastRow
);
609 // dBaseIII precision limit: 15
610 if ( nPrecision
> 15 )
614 if ( bPrecDefined
&& nPrecision
!= nPrec
)
616 if (nPrecision
< nPrec
)
618 // This is a hairy case. User defined nPrecision but a
619 // number format has more precision. Modifying a dBase
620 // field may as well render the resulting file useless for
621 // an application that relies on its defined structure,
622 // especially if we are resaving an already existing file.
623 // So who's right, the user who (or the loaded file that)
624 // defined the field, or the user who applied the format?
625 // Commit f59e350d1733125055f1144f8b3b1b0a46f6d1ca gave the
626 // format a higher priority, which is debatable.
627 SAL_WARN( "sc", "lcl_GetColumnTypes: conflicting dBase field precision for "
628 << aFieldName
<< " (" << nPrecision
<< "<" << nPrec
<< ")");
630 // Adjust length to larger predefined integer part. There
631 // may be a reason that the field was prepared for larger
633 if (nFieldLen
- nPrecision
> nLen
- nPrec
)
634 nLen
= nFieldLen
- (nPrecision
? nPrecision
+1 : 0) + 1 + nPrec
;
635 // And override precision.
641 // Adjust length to predefined precision.
642 nLen
= nLen
+ ( nPrecision
- nPrec
);
644 /* If the above override for (nPrecision < nPrec) was not in place then
645 * nPrecision could be 0 and this would be the code path to correctly
646 * calculate nLen. But as is, nPrecision is never 0 here, see CID#982304 */
648 // Adjust length to predefined precision.
650 nLen
= nLen
+ ( nPrecision
- nPrec
);
652 nLen
-= nPrec
+1; // also remove the decimal separator
656 if (nFieldLen
< nLen
)
662 // Again a hairy case and conflict. Furthermore, the
663 // larger overall length may be a result of only a higher
664 // precision obtained from formats.
665 SAL_WARN( "sc", "lcl_GetColumnTypes: conflicting dBase field length for "
666 << aFieldName
<< " (" << nFieldLen
<< "<" << nLen
<< ")");
672 if ( nFieldLen
== 0 )
674 else if ( nFieldLen
> 19 )
675 nFieldLen
= 19; // dBaseIII numeric field length limit: 19
676 if ( nPrecision
&& nFieldLen
< nPrecision
+ 2 )
677 nFieldLen
= nPrecision
+ 2; // 0. must fit into
678 // 538 MUST: Sdb internal representation adds 2 to the field length!
679 // To give the user what he wants we must subtract it here.
680 //! CAVEAT! There is no way to define a numeric field with a length
681 //! of 1 and no decimals!
682 nFieldLen
= SvDbaseConverter::ConvertPrecisionToOdbc( nFieldLen
, nPrecision
);
684 if ( nFieldLen
> 254 )
686 if ( nDbType
== sdbc::DataType::VARCHAR
)
687 { // Too long for a normal text field => memo field.
688 nDbType
= sdbc::DataType::LONGVARCHAR
;
693 nFieldLen
= 254; // bad luck...
696 pColNames
[nField
] = aFieldName
;
697 pColTypes
[nField
] = nDbType
;
698 pColLengths
[nField
] = nFieldLen
;
699 pColScales
[nField
] = nPrecision
;
705 void lcl_getLongVarCharEditString( OUString
& rString
,
706 const ScRefCellValue
& rCell
, ScFieldEditEngine
& rEditEngine
)
708 if (!rCell
.getEditText())
711 rEditEngine
.SetTextCurrentDefaults(*rCell
.getEditText());
712 rString
= rEditEngine
.GetText( LINEEND_CRLF
);
715 void lcl_getLongVarCharString(
716 OUString
& rString
, ScDocument
& rDoc
, SCCOL nCol
, SCROW nRow
, SCTAB nTab
, ScInterpreterContext
& rContext
)
719 ScAddress
aPos(nCol
, nRow
, nTab
);
720 sal_uInt32 nFormat
= rDoc
.GetNumberFormat(ScRange(aPos
));
721 rString
= ScCellFormat::GetString(rDoc
, aPos
, nFormat
, &pColor
, &rContext
);
726 #endif // HAVE_FEATURE_DBCONNECTIVITY
728 ErrCodeMsg
ScDocShell::DBaseExport( const OUString
& rFullFileName
, rtl_TextEncoding eCharSet
, bool& bHasMemo
)
730 #if !HAVE_FEATURE_DBCONNECTIVITY
731 (void) rFullFileName
;
735 return ERRCODE_IO_GENERAL
;
737 // remove the file so the dBase driver doesn't find an invalid file
738 INetURLObject
aDeleteObj( rFullFileName
, INetProtocol::File
);
739 KillFile( aDeleteObj
);
741 ErrCodeMsg nErr
= ERRCODE_NONE
;
743 SCCOL nFirstCol
, nLastCol
;
744 SCROW nFirstRow
, nLastRow
;
745 SCTAB nTab
= GetSaveTab();
746 m_pDocument
->GetDataStart( nTab
, nFirstCol
, nFirstRow
);
747 m_pDocument
->GetCellArea( nTab
, nLastCol
, nLastRow
);
748 if ( nFirstCol
> nLastCol
)
749 nFirstCol
= nLastCol
;
750 if ( nFirstRow
> nLastRow
)
751 nFirstRow
= nLastRow
;
752 ScProgress
aProgress( this, ScResId( STR_SAVE_DOC
),
753 nLastRow
- nFirstRow
, true );
754 ScInterpreterContext
& rContext
= m_pDocument
->GetNonThreadedContext();
756 bool bHasFieldNames
= true;
757 for ( SCCOL nDocCol
= nFirstCol
; nDocCol
<= nLastCol
&& bHasFieldNames
; nDocCol
++ )
758 { // only Strings in first row => are field names
759 if ( !m_pDocument
->HasStringData( nDocCol
, nFirstRow
, nTab
) )
760 bHasFieldNames
= false;
763 sal_Int32 nColCount
= nLastCol
- nFirstCol
+ 1;
764 uno::Sequence
<OUString
> aColNames( nColCount
);
765 uno::Sequence
<sal_Int32
> aColTypes( nColCount
);
766 uno::Sequence
<sal_Int32
> aColLengths( nColCount
);
767 uno::Sequence
<sal_Int32
> aColScales( nColCount
);
769 ScRange
aDataRange( nFirstCol
, nFirstRow
, nTab
, nLastCol
, nLastRow
, nTab
);
770 lcl_GetColumnTypes( *this, aDataRange
, bHasFieldNames
,
771 aColNames
.getArray(), aColTypes
.getArray(),
772 aColLengths
.getArray(), aColScales
.getArray(),
773 bHasMemo
, eCharSet
);
774 // also needed for exception catch
776 ScFieldEditEngine
aEditEngine(m_pDocument
.get(), m_pDocument
->GetEditPool());
781 uno::Reference
<sdbc::XDriverManager2
> xDrvMan
;
782 uno::Reference
<sdbc::XConnection
> xConnection
;
784 ErrCode nRet
= lcl_getDBaseConnection(xDrvMan
,xConnection
,aTabName
,rFullFileName
,eCharSet
);
785 if ( !xConnection
.is() || !xDrvMan
.is() )
787 ::utl::DisposableComponent
aConnectionHelper(xConnection
);
790 uno::Reference
< sdbcx::XDataDefinitionSupplier
> xDDSup( xDrvMan
->getDriverByURL( xConnection
->getMetaData()->getURL() ), uno::UNO_QUERY
);
792 return SCERR_EXPORT_CONNECT
;
795 uno::Reference
<sdbcx::XTablesSupplier
> xTablesSupp
=xDDSup
->getDataDefinitionByConnection( xConnection
);
796 OSL_ENSURE( xTablesSupp
.is(), "can't get Data Definition" );
797 if (!xTablesSupp
.is()) return SCERR_EXPORT_CONNECT
;
799 uno::Reference
<container::XNameAccess
> xTables
= xTablesSupp
->getTables();
800 OSL_ENSURE( xTables
.is(), "can't get Tables" );
801 if (!xTables
.is()) return SCERR_EXPORT_CONNECT
;
803 uno::Reference
<sdbcx::XDataDescriptorFactory
> xTablesFact( xTables
, uno::UNO_QUERY
);
804 OSL_ENSURE( xTablesFact
.is(), "can't get tables factory" );
805 if (!xTablesFact
.is()) return SCERR_EXPORT_CONNECT
;
807 uno::Reference
<sdbcx::XAppend
> xTablesAppend( xTables
, uno::UNO_QUERY
);
808 OSL_ENSURE( xTablesAppend
.is(), "can't get tables XAppend" );
809 if (!xTablesAppend
.is()) return SCERR_EXPORT_CONNECT
;
811 uno::Reference
<beans::XPropertySet
> xTableDesc
= xTablesFact
->createDataDescriptor();
812 OSL_ENSURE( xTableDesc
.is(), "can't get table descriptor" );
813 if (!xTableDesc
.is()) return SCERR_EXPORT_CONNECT
;
815 xTableDesc
->setPropertyValue( SC_DBPROP_NAME
, uno::Any(aTabName
) );
819 uno::Reference
<sdbcx::XColumnsSupplier
> xColumnsSupp( xTableDesc
, uno::UNO_QUERY
);
820 OSL_ENSURE( xColumnsSupp
.is(), "can't get columns supplier" );
821 if (!xColumnsSupp
.is()) return SCERR_EXPORT_CONNECT
;
823 uno::Reference
<container::XNameAccess
> xColumns
= xColumnsSupp
->getColumns();
824 OSL_ENSURE( xColumns
.is(), "can't get columns" );
825 if (!xColumns
.is()) return SCERR_EXPORT_CONNECT
;
827 uno::Reference
<sdbcx::XDataDescriptorFactory
> xColumnsFact( xColumns
, uno::UNO_QUERY
);
828 OSL_ENSURE( xColumnsFact
.is(), "can't get columns factory" );
829 if (!xColumnsFact
.is()) return SCERR_EXPORT_CONNECT
;
831 uno::Reference
<sdbcx::XAppend
> xColumnsAppend( xColumns
, uno::UNO_QUERY
);
832 OSL_ENSURE( xColumnsAppend
.is(), "can't get columns XAppend" );
833 if (!xColumnsAppend
.is()) return SCERR_EXPORT_CONNECT
;
835 const OUString
* pColNames
= aColNames
.getConstArray();
836 const sal_Int32
* pColTypes
= aColTypes
.getConstArray();
837 const sal_Int32
* pColLengths
= aColLengths
.getConstArray();
838 const sal_Int32
* pColScales
= aColScales
.getConstArray();
841 for (nCol
=0; nCol
<nColCount
; nCol
++)
843 uno::Reference
<beans::XPropertySet
> xColumnDesc
= xColumnsFact
->createDataDescriptor();
844 OSL_ENSURE( xColumnDesc
.is(), "can't get column descriptor" );
845 if (!xColumnDesc
.is()) return SCERR_EXPORT_CONNECT
;
847 xColumnDesc
->setPropertyValue( SC_DBPROP_NAME
, uno::Any(pColNames
[nCol
]) );
849 xColumnDesc
->setPropertyValue( SC_DBPROP_TYPE
, uno::Any(pColTypes
[nCol
]) );
851 xColumnDesc
->setPropertyValue( SC_DBPROP_PRECISION
, uno::Any(pColLengths
[nCol
]) );
853 xColumnDesc
->setPropertyValue( SC_DBPROP_SCALE
, uno::Any(pColScales
[nCol
]) );
855 xColumnsAppend
->appendByDescriptor( xColumnDesc
);
858 xTablesAppend
->appendByDescriptor( xTableDesc
);
860 // get row set for writing
861 uno::Reference
<lang::XMultiServiceFactory
> xFactory
= comphelper::getProcessServiceFactory();
862 uno::Reference
<sdbc::XRowSet
> xRowSet( xFactory
->createInstance(SC_SERVICE_ROWSET
),
864 ::utl::DisposableComponent
aRowSetHelper(xRowSet
);
865 uno::Reference
<beans::XPropertySet
> xRowProp( xRowSet
, uno::UNO_QUERY
);
866 OSL_ENSURE( xRowProp
.is(), "can't get RowSet" );
867 if (!xRowProp
.is()) return SCERR_EXPORT_CONNECT
;
869 xRowProp
->setPropertyValue( SC_DBPROP_ACTIVECONNECTION
, uno::Any(xConnection
) );
871 xRowProp
->setPropertyValue( SC_DBPROP_COMMANDTYPE
, uno::Any(sal_Int32(sdb::CommandType::TABLE
)) );
873 xRowProp
->setPropertyValue( SC_DBPROP_COMMAND
, uno::Any(aTabName
) );
879 uno::Reference
<sdbc::XResultSetUpdate
> xResultUpdate( xRowSet
, uno::UNO_QUERY
);
880 OSL_ENSURE( xResultUpdate
.is(), "can't get XResultSetUpdate" );
881 if (!xResultUpdate
.is()) return SCERR_EXPORT_CONNECT
;
883 uno::Reference
<sdbc::XRowUpdate
> xRowUpdate( xRowSet
, uno::UNO_QUERY
);
884 OSL_ENSURE( xRowUpdate
.is(), "can't get XRowUpdate" );
885 if (!xRowUpdate
.is()) return SCERR_EXPORT_CONNECT
;
887 SCROW nFirstDataRow
= ( bHasFieldNames
? nFirstRow
+ 1 : nFirstRow
);
890 for ( nDocRow
= nFirstDataRow
; nDocRow
<= nLastRow
; nDocRow
++ )
892 xResultUpdate
->moveToInsertRow();
894 for (nCol
=0; nCol
<nColCount
; nCol
++)
896 SCCOL nDocCol
= sal::static_int_cast
<SCCOL
>( nFirstCol
+ nCol
);
898 switch (pColTypes
[nCol
])
900 case sdbc::DataType::LONGVARCHAR
:
902 ScRefCellValue
aCell(*m_pDocument
, ScAddress(nDocCol
, nDocRow
, nTab
));
903 if (!aCell
.isEmpty())
905 if (aCell
.getType() == CELLTYPE_EDIT
)
906 { // preserve paragraphs
907 lcl_getLongVarCharEditString(aString
, aCell
, aEditEngine
);
911 lcl_getLongVarCharString(
912 aString
, *m_pDocument
, nDocCol
, nDocRow
, nTab
, rContext
);
914 xRowUpdate
->updateString( nCol
+1, aString
);
917 xRowUpdate
->updateNull( nCol
+1 );
921 case sdbc::DataType::VARCHAR
:
922 aString
= m_pDocument
->GetString(nDocCol
, nDocRow
, nTab
);
923 xRowUpdate
->updateString( nCol
+1, aString
);
924 if ( nErr
== ERRCODE_NONE
&& pColLengths
[nCol
] < aString
.getLength() )
925 nErr
= SCWARN_EXPORT_DATALOST
;
928 case sdbc::DataType::DATE
:
930 fVal
= m_pDocument
->GetValue( nDocCol
, nDocRow
, nTab
);
931 // differentiate between 0 with value and 0 no-value
932 bool bIsNull
= (fVal
== 0.0);
934 bIsNull
= !m_pDocument
->HasValueData( nDocCol
, nDocRow
, nTab
);
937 xRowUpdate
->updateNull( nCol
+1 );
938 if ( nErr
== ERRCODE_NONE
&&
939 m_pDocument
->HasStringData( nDocCol
, nDocRow
, nTab
) )
940 nErr
= SCWARN_EXPORT_DATALOST
;
944 Date aDate
= rContext
.NFGetNullDate(); // tools date
945 aDate
.AddDays(fVal
); //! approxfloor?
946 xRowUpdate
->updateDate( nCol
+1, aDate
.GetUNODate() );
951 case sdbc::DataType::DECIMAL
:
952 case sdbc::DataType::BIT
:
953 fVal
= m_pDocument
->GetValue( nDocCol
, nDocRow
, nTab
);
954 if ( fVal
== 0.0 && nErr
== ERRCODE_NONE
&&
955 m_pDocument
->HasStringData( nDocCol
, nDocRow
, nTab
) )
956 nErr
= SCWARN_EXPORT_DATALOST
;
957 if ( pColTypes
[nCol
] == sdbc::DataType::BIT
)
958 xRowUpdate
->updateBoolean( nCol
+1, ( fVal
!= 0.0 ) );
960 xRowUpdate
->updateDouble( nCol
+1, fVal
);
964 OSL_FAIL( "ScDocShell::DBaseExport: unknown FieldType" );
965 if ( nErr
== ERRCODE_NONE
)
966 nErr
= SCWARN_EXPORT_DATALOST
;
967 fVal
= m_pDocument
->GetValue( nDocCol
, nDocRow
, nTab
);
968 xRowUpdate
->updateDouble( nCol
+1, fVal
);
972 xResultUpdate
->insertRow();
974 //! error handling and recovery of old
975 //! ScDocShell::SbaSdbExport is still missing!
977 aProgress
.SetStateOnPercent( nDocRow
- nFirstRow
);
980 comphelper::disposeComponent( xRowSet
);
981 comphelper::disposeComponent( xConnection
);
983 catch ( const sdbc::SQLException
& aException
)
985 sal_Int32 nError
= aException
.ErrorCode
;
986 TOOLS_WARN_EXCEPTION("sc", "ScDocShell::DBaseExport");
988 if (nError
== 22018 || nError
== 22001)
990 // SQL error 22018: Character not in target encoding.
991 // SQL error 22001: String length exceeds field width (after encoding).
992 bool bEncErr
= (nError
== 22018);
993 bool bIsOctetTextEncoding
= rtl_isOctetTextEncoding( eCharSet
);
994 OSL_ENSURE( !bEncErr
|| bIsOctetTextEncoding
, "ScDocShell::DBaseExport: encoding error and not an octet textencoding");
995 SCCOL nDocCol
= nFirstCol
;
996 const sal_Int32
* pColTypes
= aColTypes
.getConstArray();
997 const sal_Int32
* pColLengths
= aColLengths
.getConstArray();
998 ScHorizontalCellIterator
aIter( *m_pDocument
, nTab
, nFirstCol
,
999 nDocRow
, nLastCol
, nDocRow
);
1003 ScRefCellValue
* pCell
= aIter
.GetNext( nDocCol
, nDocRow
);
1006 SCCOL nCol
= nDocCol
- nFirstCol
;
1007 switch (pColTypes
[nCol
])
1009 case sdbc::DataType::LONGVARCHAR
:
1011 if (pCell
->getType() == CELLTYPE_EDIT
)
1012 lcl_getLongVarCharEditString(aString
, *pCell
, aEditEngine
);
1014 lcl_getLongVarCharString(
1015 aString
, *m_pDocument
, nDocCol
, nDocRow
, nTab
, rContext
);
1019 case sdbc::DataType::VARCHAR
:
1020 aString
= m_pDocument
->GetString(nDocCol
, nDocRow
, nTab
);
1023 // NOTE: length of DECIMAL fields doesn't need to be
1024 // checked here, the database driver adjusts the field
1025 // width accordingly.
1033 if (bIsOctetTextEncoding
)
1036 if (!aString
.convertToString( &aOString
, eCharSet
,
1037 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
|
1038 RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR
))
1043 nLen
= aOString
.getLength();
1045 SAL_WARN("sc", "ScDocShell::DBaseExport encoding error, string with default replacements: ``" << aString
<< "''");
1048 nLen
= aString
.getLength() * sizeof(sal_Unicode
);
1050 pColTypes
[nCol
] != sdbc::DataType::LONGVARCHAR
&&
1051 pColLengths
[nCol
] < nLen
)
1054 SAL_INFO("sc", "ScDocShell::DBaseExport: field width: " << pColLengths
[nCol
] << ", encoded length: " << nLen
);
1060 OUString
sPosition(ScAddress(nDocCol
, nDocRow
, nTab
).GetColRowString());
1061 OUString
sEncoding(SvxTextEncodingTable::GetTextString(eCharSet
));
1062 nErr
= ErrCodeMsg( (bEncErr
? SCERR_EXPORT_ENCODING
:
1063 SCERR_EXPORT_FIELDWIDTH
), sPosition
, sEncoding
,
1064 DialogMask::ButtonsOk
| DialogMask::MessageError
);
1066 else if ( !aException
.Message
.isEmpty() )
1067 nErr
= ErrCodeMsg( SCERR_EXPORT_SQLEXCEPTION
, aException
.Message
, DialogMask::ButtonsOk
| DialogMask::MessageError
);
1069 nErr
= SCERR_EXPORT_DATA
;
1071 catch ( uno::Exception
& )
1073 TOOLS_WARN_EXCEPTION( "sc", "Unexpected exception in database");
1074 nErr
= ERRCODE_IO_GENERAL
;
1078 #endif // HAVE_FEATURE_DBCONNECTIVITY
1081 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */