KDb

XbaseExport.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2008 Sharan Rao <sharanrao@gmail.com>
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this program; see the file COPYING. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18*/
19
20#include "XbaseExport.h"
21
22#include <QHash>
23#include <QDir>
24
25
26#include "KDbField.h"
27#include "KDbRecordData.h"
28#include "KDbCursor.h"
29#include "KDbDriverManager.h"
30#include <core/kexi.h>
31#include <migration/keximigratedata.h>
32
33#include <cstring>
34
35#include "xbase.h"
36
37class KDbxBaseExportPrivate {
38 public:
39 xBaseExportPrivate() {
40 }
41
42 //! Converts KDbField types to xbase types
43 char type(KDbField::Type fieldType);
44
45 //! Appends record to xbase table
46 bool appendRecord(const QString& sourceTableName , KDbRecordData* recordData);
47
48 //! Returns max fieldlengths for xBase table
49 int fieldLength(KDbField* f );
50
51 //! converts QVariant data to a format understood by xBase
52 QByteArray fieldData(QVariant data, char type);
53
54 //! Creates xBase indexes for the table
55 bool createIndexes(const QString& sourceTableName, KDbTableSchema* tableSchema);
56
57 xbXBase xbase;
58 QHash<QString, QString> tableNamePathMap;
59};
60
61char xBaseExportPrivate::type(KDbField::Type fieldType)
62{
63 char xBaseType = '\0';
64
65 switch( fieldType ) {
66 case KDbField::Text:
68 xBaseType = XB_CHAR_FLD;
69 break;
70
72 xBaseType = XB_LOGICAL_FLD;
73 break;
74
75 case KDbField::Float:
77 xBaseType = XB_FLOAT_FLD;
78
82 xBaseType = XB_NUMERIC_FLD;
83 break;
84
85 case KDbField::DateTime:
86 case KDbField::Date:
87 case KDbField::Time:
88 xBaseType = XB_DATE_FLD;
89 break;
90
91 case KDbField::BLOB:
92 xBaseType = XB_MEMO_FLD;
93 break;
94
95 default:
96 xBaseType = '\0';
97 }
98
99 return xBaseType;
100}
101
102bool xBaseExportPrivate::appendRecord( const QString& sourceTableName , KDbRecordData* recordData ) {
103
104// xbaseDebug() << recordData->debugString();
105 QString pathName = tableNamePathMap.value( sourceTableName );
106 QByteArray pathNameBa = pathName.toLatin1();
107 xbDbf* table = xbase.GetDbfPtr( pathNameBa.constData() );
108
109 int returnCode;
110 table->BlankRecord();
111 for (int i=0;i < recordData->size();++i) {
112 char fieldType = table->GetFieldType(i);
113 QByteArray stringData = fieldData(recordData->value(i), fieldType);
114
115 if (fieldType == XB_MEMO_FLD) {
116 #ifdef XB_MEMO_FIELDS
117 // we use size()+1 as size to accommodate `\0`
118 table->UpdateMemoData(i, stringData.size()+1, stringData.constData(), F_SETLKW );
119 #else
120 xbaseDebug() << "XB_MEMO_FIELDS support disabled during compilation of XBase libraries";
121 #endif
122 } else {
123 if ((returnCode = table->PutField( i, stringData.constData())) != XB_NO_ERROR ) {
124 switch(returnCode) {
125 case XB_INVALID_FIELDNO:
126 xbaseWarning() << "Invalid field number" << i;
127 return false;
128 case XB_INVALID_DATA:
129 xbaseWarning() << "Invalid data" << stringData;
130 return false;
131 default:
132 xbaseWarning() << "Error number" << returnCode << "has occurred";
133 return false;
134 }
135 }
136 }
137 }
138
139 if((returnCode = table->AppendRecord()) != XB_NO_ERROR) {
140 xbaseWarning() << "xBase Error" << returnCode << "appending data record.";
141 return false;
142 }
143
144// // for debugging purposes only
145// for ( int i=0; i< recordData->size(); ++i ) {
146// xbaseDebug() << table->GetField(i);
147// }
148
149 return true;
150}
151
152int xBaseExportPrivate::fieldLength(KDbField* f)
153{
154 const Field::Type t = f->type(); // cache: evaluating type of expressions can be expensive
155 if (KDbField::isTextType(t)) {
156 return f->maxLength();
157 }
158 // return the max possible (string)length of the types
159 // see https://linux.techass.com/projects/xdb/xbasedocs/xbase_c3.html
160 switch(type(t)) {
161 case XB_CHAR_FLD:
162 return 254;
163 case XB_LOGICAL_FLD:
164 return 1;
165 case XB_FLOAT_FLD:
166 case XB_NUMERIC_FLD:
167 return 17;
168 case XB_DATE_FLD:
169 return 8;
170 case XB_MEMO_FLD:
171 return 10;
172 default:
173 return 0;
174 }
175}
176
177QByteArray xBaseExportPrivate::fieldData(QVariant data, char type) {
178
179 switch(type) {
180 case XB_CHAR_FLD:
181 case XB_FLOAT_FLD:
182 case XB_NUMERIC_FLD:
183 return data.toString().toUtf8();
184
185 case XB_LOGICAL_FLD:
186 if (data.toBool()) {
187 return QString( "t" ).toLatin1();
188 } else
189 return QString( "f" ).toLatin1();
190
191 case XB_DATE_FLD:
192 return data.toDate().toString("yyyyMMdd").toLatin1();
193
194 case XB_MEMO_FLD:
195 return data.toByteArray();
196 default:
197 return QByteArray();
198 }
199}
200
201bool xBaseExportPrivate::createIndexes(const QString& sourceTableName, KDbTableSchema* tableSchema) {
202
203 QString pathName = tableNamePathMap.value( sourceTableName );
204 QByteArray pathNameBa = pathName.toLatin1();
205 xbDbf* table = xbase.GetDbfPtr( pathNameBa.constData() );
206 int fieldCount = tableSchema->fieldCount();
207
208 QString dirName = QFileInfo( pathName ).path();
209
210 for (int i=0; i< fieldCount ; ++i) {
211 KDbField* f = tableSchema->field(i);
212
213 int returnCode;
214 QString fieldName = f->name();
215 QString indexName = dirName + QDir::separator() + sourceTableName + '_' + fieldName + ".ndx";
216 QByteArray indexNameBa = indexName.toLatin1();
217 QByteArray fieldNameBa = fieldName.toLatin1();
218
219 xbNdx index(table);
220 if (f->isUniqueKey() || f->isPrimaryKey()) {
221
222 if ((returnCode = index.CreateIndex(indexNameBa.constData(), fieldNameBa.constData(), XB_UNIQUE, XB_OVERLAY)) != XB_NO_ERROR ) {
223 xbaseWarning() << "Couldn't create unique index for fieldName"
224 << fieldName << "on table" << sourceTableName << "Error Code" << returnCode;
225 return false;
226 }
227 index.CloseIndex();
228
229 } else if ( f->isIndexed() ) {
230
231 if ((returnCode = index.CreateIndex(indexNameBa.constData(), fieldNameBa.constData(), XB_NOT_UNIQUE, XB_OVERLAY)) != XB_NO_ERROR ) {
232 xbaseWarning() << "Couldn't create index for fieldName" << fieldName << "on table"
233 << sourceTableName << "Error Code" << returnCode;
234 return false;
235 }
236 index.CloseIndex();
237
238 }
239 }
240 return true;
241}
242
243
244xBaseExport::xBaseExport()
245: m_migrateData( 0 ),
246d(new xBaseExportPrivate)
247{
248}
249
250void xBaseExport::setData(KexiMigration::Data* migrateData) {
251 m_migrateData = migrateData;
252}
253
254bool xBaseExport::performExport(Kexi::ObjectStatus* result) {
255
256 if (result)
257 result->clearStatus();
258
259
260 KDbDriverManager drvManager;
261
262 if (!m_migrateData) {
263 xbaseWarning() << "Migration Data not set yet";
264 result->setStatus(&drvManager, tr("Data not set for migration."));
265 return false;
266 }
267
268 KDbDriver *sourceDriver = drvManager.driver(m_migrateData->source->driverId);
269 if (!sourceDriver) {
270 result->setStatus(&drvManager,
271 tr("Could not export back to destination database."));
272 return false;
273 }
274
275 // connect to destination database
276 if (!dest_connect()) {
277 xbaseWarning() << "Couldn't connect to destination database";
278 if (result)
279 result->setStatus(tr("Could not connect to data source \"%1\".",
280 m_migrateData->destination->connectionData()->serverInfoString()), "");
281 return false;
282 }
283
284 KDbConnection* sourceConn = sourceDriver->createConnection(*(m_migrateData->source));
285
286 if (!sourceConn || sourceDriver->error()) {
287 xbaseWarning() << "Export failed";
288 return false;
289 }
290 if (!sourceConn->connect()) {
291 xbaseWarning() << "Export failed.Could not connect";
292 return false;
293 }
294
295 if (!sourceConn->useDatabase(m_migrateData->sourceName)) {
296 xbaseWarning() << "Couldn't use database "<<m_migrateData->sourceName;
297 return false;
298 }
299
300 QStringList tables = sourceConn->tableNames();
301
302 // Check if there are any tables
303 if (tables.isEmpty()) {
304 xbaseDebug() << "There were no tables to export";
305 if (result)
306 result->setStatus(
307 tr("No tables to export found in data source \"%1\".",
308 m_migrateData->source->serverInfoString()), "");
309 return false;
310 }
311
312 tables.sort();
313
314 // -- read table schemas and create them in memory (only for non-KDb-compat tables)
315 foreach (const QString& tableCaption, tables) {
316 if (dest_isSystemObjectName( tableCaption )) {
317 return false;
318 }
319
320 KDbTableSchema *tableSchema = sourceConn->tableSchema( tableCaption );
321
322 if (!dest_createTable(tableCaption, tableSchema)) {
323 if (result)
324 result->setStatus(tr("Could not create table in destination \"%1\". Error reading table \"%2\".", m_migrateData->destination->connectionData()->serverInfoString(), tableCaption), "");
325 return false;
326 }
327
328 if (m_migrateData->keepData) {
329 if (!dest_copyTable(tableCaption, sourceConn, tableSchema)) {
330 xbaseWarning() << "Failed to copy table " << tableCaption;
331 if (result)
332 result->setStatus(sourceConn,
333 tr("Could not copy table \"%1\" to destination database.", tableCaption));
334 }
335 }
336
337 }
338
339 if (dest_disconnect()) {
340 bool ok = false;
341 if (sourceConn)
342 ok = sourceConn->disconnect();
343 return ok;
344 }
345
346 // Finally: Error.handling
347 if (result && result->error())
348 result->setStatus(sourceConn,
349 tr("Could not export data to \"%1\".",
350 m_migrateData->source->serverInfoString()));
351 dest_disconnect();
352 if (sourceConn) {
353 sourceConn->disconnect();
354 }
355 return false;
356}
357
358bool xBaseExport::dest_connect() {
359 return true;
360}
361
362bool xBaseExport::dest_disconnect() {
363 QList<QString> pathNameList = d->tableNamePathMap.values();
364 foreach(const QString& pathName, pathNameList) {
365 QByteArray ba = pathName.toLatin1();
366 xbDbf* tablePtr = d->xbase.GetDbfPtr(ba.constData());
367 tablePtr->CloseDatabase();
368 // delete tablePtr ?
369 }
370 return true;
371}
372
373bool xBaseExport::dest_createTable(const QString& originalName, KDbTableSchema* tableSchema) {
374 // Algorithm
375 // 1. For each fields in the table schema.
376 // 2. Create a xbSchema entry and add it to xbSchema array.
377 // 3. End for
378 // 4. Create table in overlay mode ( overwrite )
379
380 int fieldCount = tableSchema->fieldCount();
381 const int arrayLength = fieldCount + 1; // and extra space for the `null`
382 xbSchema xBaseTableSchema[arrayLength];// = new xbSchema[fieldCount+1][4];
383
384 int i = 0;
385 for (i = 0; i < fieldCount ; ++i) {
386 KDbField* f = tableSchema->field(i);
387
388 QByteArray ba = f->name().toLatin1();
389 //! @todo Fieldname can only be 11 characters
390 strcpy(xBaseTableSchema[i].FieldName, ba.data());
391 xBaseTableSchema[i].Type = d->type(f->type());
392 xBaseTableSchema[i].FieldLen = d->fieldLength( f ); //!< @todo Check semantics
393 xBaseTableSchema[i].NoOfDecs = ( xBaseTableSchema[i].Type != XB_CHAR_FLD )? f->scale() : 0 ;
394
395 }
396
397 // last member should be all 0
398 strcpy( xBaseTableSchema[i].FieldName , "" );
399 xBaseTableSchema[i].Type = 0;
400 xBaseTableSchema[i].FieldLen = 0;
401 xBaseTableSchema[i].NoOfDecs = 0;
402
403 const KDbConnectionData* connData = m_migrateData->destination->connectionData();
404 QString dirName = connData->fileName(); // this includes the forward slash after the dir name
405
406 QString pathName = dirName + originalName + ".dbf";
407 d->tableNamePathMap[originalName] = pathName;
408
409 QByteArray pathNameBa = pathName.toLatin1();
410
411 xbDbf* xBaseTable = new xbDbf( &d->xbase );
412 xBaseTable->SetVersion( 4 ); // create dbase IV style files
413 xbShort returnCode;
414 if (( returnCode = xBaseTable->CreateDatabase( pathNameBa.constData() , xBaseTableSchema, XB_OVERLAY )) != XB_NO_ERROR ) {
415 xbaseWarning() << "Error creating table" << originalName << "Error Code" << returnCode;
416 return false;
417 }
418
419 if (!d->createIndexes(originalName, tableSchema)) {
420 return false;
421 }
422
423 return true;
424}
425
426bool xBaseExport::dest_copyTable(const QString& srcTableName, KDbConnection *srcConn,
427 KDbTableSchema* /*srcTable*/) {
428 // Algorithm
429 // 1. pick each row
430 // 2. Insert it into the xBase table
431
432 // using the tableSchema as argument automatically appends rowid
433 // info to the recordData which we don't want. Hence we use SQL query
434 KDbCursor* cursor = srcConn->executeQuery(KDbEscapedString( "SELECT * FROM %1" ).arg(srcTableName));
435
436 if (!cursor)
437 return false;
438
439 if (!cursor->moveFirst() && cursor->error())
440 return false;
441
442 while (!cursor->eof()) {
443 KDbRecordData *record = cursor->storeCurrentRecord();
444 if (!record) {
445 return false;
446 }
447 if (!d->appendRecord(srcTableName, record)) {
448 xbaseWarning() << "Couldn't append record";
449 return false;
450 }
451
452 if (!cursor->moveNext() && cursor->error()) {
453 return false;
454 }
455 }
456 return true;
457}
458
459bool xBaseExport::dest_isSystemObjectName( const QString& /* objectName */ ) {
460 return false;
461}
Database specific connection data, e.g. host, port.
Provides database connection, allowing queries and data modification.
bool connect()
Connects to driver with given parameters.
KDbCursor * executeQuery(const KDbEscapedString &sql, KDbCursor::Options options=KDbCursor::Option::None)
QStringList tableNames(bool alsoSystemTables=false, bool *ok=nullptr)
bool disconnect()
Disconnects from driver with given parameters.
KDbTableSchema * tableSchema(int tableId)
bool useDatabase(const QString &dbName=QString(), bool kexiCompatible=true, bool *cancelled=nullptr, KDbMessageHandler *msgHandler=nullptr)
Opens an existing database specified by dbName.
Provides database cursor functionality.
Definition KDbCursor.h:69
virtual bool moveNext()
KDbRecordData * storeCurrentRecord() const
bool moveFirst()
bool eof() const
Definition KDbCursor.h:147
A driver manager for finding and loading driver plugins.
KDbDriver * driver(const QString &id)
Database driver's abstraction.
Definition KDbDriver.h:50
KDbConnection * createConnection(const KDbConnectionData &connData, const KDbConnectionOptions &options)
Specialized string for escaping.
virtual KDbField * field(int id)
int fieldCount() const
Meta-data for a field.
Definition KDbField.h:72
bool isTextType() const
Definition KDbField.h:353
QString name() const
Definition KDbField.cpp:256
int maxLength() const
Definition KDbField.cpp:665
bool isIndexed() const
Definition KDbField.h:312
int scale() const
Definition KDbField.cpp:291
@ Integer
Definition KDbField.h:90
@ Boolean
Definition KDbField.h:92
@ ShortInteger
Definition KDbField.h:89
@ BigInteger
Definition KDbField.h:91
@ LongText
Definition KDbField.h:99
Type type() const
Definition KDbField.cpp:379
bool isUniqueKey() const
Definition KDbField.h:292
bool isPrimaryKey() const
Definition KDbField.h:287
Structure for storing single record with type information.
QVariant value(int i) const
Type type(const QSqlDatabase &db)
const char * constData() const const
char * data()
qsizetype size() const const
QString toString(QStringView format, QCalendar cal) const const
QChar separator()
QString path() const const
bool isEmpty() const const
QByteArray toLatin1() const const
QByteArray toUtf8() const const
void sort(Qt::CaseSensitivity cs)
bool toBool() const const
QByteArray toByteArray() const const
QDate toDate() const const
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:20:59 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.