1
/* This file is part of the KDE project
2
Copyright (C) 2008 Sharan Rao <sharanrao@gmail.com>
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.
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.
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.
20
#include "xbaseexport.h"
27
#include <kexidb/field.h>
28
#include <kexidb/RecordData.h>
29
#include <kexidb/cursor.h>
30
#include <kexidb/drivermanager.h>
31
#include <core/kexi.h>
32
#include <migration/keximigratedata.h>
38
using namespace KexiDB;
40
class KexiDB::xBaseExportPrivate {
42
xBaseExportPrivate() {
45
//! Converts kexidb field types to xbase types
46
char type(KexiDB::Field::Type fieldType);
48
//! Appends record to xbase table
49
bool appendRecord(const QString& sourceTableName , KexiDB::RecordData* recordData);
51
//! Returns max fieldlengths for xBase table
52
int fieldLength(KexiDB::Field* f );
54
//! converts QVariant data to a format understood by xBase
55
QByteArray fieldData(QVariant data, char type);
57
//! Creates xBase indexes for the table
58
bool createIndexes(const QString& sourceTableName, KexiDB::TableSchema* tableSchema);
61
QHash<QString, QString> tableNamePathMap;
64
char xBaseExportPrivate::type(KexiDB::Field::Type fieldType)
66
char xBaseType = '\0';
69
case KexiDB::Field::Text:
70
case KexiDB::Field::LongText:
71
xBaseType = XB_CHAR_FLD;
74
case KexiDB::Field::Boolean:
75
xBaseType = XB_LOGICAL_FLD;
78
case KexiDB::Field::Float:
79
case KexiDB::Field::Double:
80
xBaseType = XB_FLOAT_FLD;
82
case KexiDB::Field::ShortInteger:
83
case KexiDB::Field::Integer:
84
case KexiDB::Field::BigInteger:
85
xBaseType = XB_NUMERIC_FLD;
88
case KexiDB::Field::DateTime:
89
case KexiDB::Field::Date:
90
case KexiDB::Field::Time:
91
xBaseType = XB_DATE_FLD;
94
case KexiDB::Field::BLOB:
95
xBaseType = XB_MEMO_FLD;
105
bool xBaseExportPrivate::appendRecord( const QString& sourceTableName , KexiDB::RecordData* recordData ) {
107
// kDebug()<<recordData->debugString();
108
QString pathName = tableNamePathMap.value( sourceTableName );
109
QByteArray pathNameBa = pathName.toAscii();
110
xbDbf* table = xbase.GetDbfPtr( pathNameBa.constData() );
113
table->BlankRecord();
114
for (int i=0;i < recordData->size();++i) {
115
char fieldType = table->GetFieldType(i);
116
QByteArray stringData = fieldData(recordData->value(i), fieldType);
118
if (fieldType == XB_MEMO_FLD) {
119
#ifdef XB_MEMO_FIELDS
120
// we use size()+1 as size to accommodate `\0`
121
table->UpdateMemoData(i, stringData.size()+1, stringData.constData(), F_SETLKW );
123
kDebug()<<"XB_MEMO_FIELDS support disabled during compilation of XBase libraries";
126
if ((returnCode = table->PutField( i, stringData.constData())) != XB_NO_ERROR ) {
128
case XB_INVALID_FIELDNO:
129
kDebug()<<"Invalid field number "<<i;
131
case XB_INVALID_DATA:
132
kDebug()<<"Invalid data "<<stringData;
135
kDebug()<<"Error number "<<returnCode<<" has occurred";
142
if((returnCode = table->AppendRecord()) != XB_NO_ERROR) {
143
kDebug() << "\nxBase Error " << returnCode << " appending data record.";
147
// // for debugging purposes only
148
// for ( uint i=0; i< (uint)recordData->size(); ++i ) {
149
// kDebug()<<table->GetField(i);
155
int xBaseExportPrivate::fieldLength(KexiDB::Field* f ) {
156
if ( f->type() == KexiDB::Field::Text ) {
159
// return the max possible (string)length of the types
160
// see http://linux.techass.com/projects/xdb/xbasedocs/xbase_c3.html
161
switch(type( f->type())) {
178
QByteArray xBaseExportPrivate::fieldData(QVariant data, char type) {
184
return data.toString().toUtf8();
188
return QString( "t" ).toAscii();
190
return QString( "f" ).toAscii();
193
return data.toDate().toString("yyyyMMdd").toAscii();
196
return data.toByteArray();
202
bool xBaseExportPrivate::createIndexes(const QString& sourceTableName, KexiDB::TableSchema* tableSchema) {
204
QString pathName = tableNamePathMap.value( sourceTableName );
205
QByteArray pathNameBa = pathName.toAscii();
206
xbDbf* table = xbase.GetDbfPtr( pathNameBa.constData() );
207
uint fieldCount = tableSchema->fieldCount();
209
QString dirName = QFileInfo( pathName ).path();
211
for (uint i=0; i< (uint)fieldCount ; ++i) {
212
KexiDB::Field* f = tableSchema->field(i);
215
QString fieldName = f->name();
216
QString indexName = dirName + QDir::separator() + sourceTableName + '_' + fieldName + ".ndx";
217
QByteArray indexNameBa = indexName.toAscii();
218
QByteArray fieldNameBa = fieldName.toLatin1();
221
if (f->isUniqueKey() || f->isPrimaryKey()) {
223
if ((returnCode = index.CreateIndex(indexNameBa.constData(), fieldNameBa.constData(), XB_UNIQUE, XB_OVERLAY)) != XB_NO_ERROR ) {
224
kDebug()<<"Couldn't create unique index for fieldName "<<fieldName<<" on table "<<sourceTableName<<" Error Code "<<returnCode;
229
} else if ( f->isIndexed() ) {
231
if ((returnCode = index.CreateIndex(indexNameBa.constData(), fieldNameBa.constData(), XB_NOT_UNIQUE, XB_OVERLAY)) != XB_NO_ERROR ) {
232
kDebug()<<"Couldn't create index for fieldName "<<fieldName<<" on table "<<sourceTableName<<" Error Code "<<returnCode;
243
xBaseExport::xBaseExport()
244
: m_migrateData( 0 ),
245
d(new xBaseExportPrivate)
249
void xBaseExport::setData(KexiMigration::Data* migrateData) {
250
m_migrateData = migrateData;
253
bool xBaseExport::performExport(Kexi::ObjectStatus* result) {
256
result->clearStatus();
259
KexiDB::DriverManager drvManager;
261
if (!m_migrateData) {
262
kDebug()<<"Migration Data not set yet !!";
263
result->setStatus(&drvManager, i18n("Data not set for migration"));
267
KexiDB::Driver *sourceDriver = drvManager.driver(
268
m_migrateData->source->driverName);
270
result->setStatus(&drvManager,
271
i18n("Could not export back to destination database"));
275
// connect to destination database
276
if (!dest_connect()) {
277
kDebug()<<"Couldn't connect to destination database";
279
result->setStatus(i18n("Could not connect to data source \"%1\".",
280
m_migrateData->destination->connectionData()->serverInfoString()), "");
284
KexiDB::Connection* sourceConn = sourceDriver->createConnection(*(m_migrateData->source));
286
if (!sourceConn || sourceDriver->error()) {
287
kDebug()<<"Export failed";
290
if (!sourceConn->connect()) {
291
kDebug()<<"Export failed.Could not connect";
295
if (!sourceConn->useDatabase(m_migrateData->sourceName)) {
296
kDebug()<<"Couldn't use database "<<m_migrateData->sourceName;
300
QStringList tables = sourceConn->tableNames();
302
// Check if there are any tables
303
if (tables.isEmpty()) {
304
kDebug() << "There were no tables to export";
307
i18n("No tables to export found in data source \"%1\".",
308
m_migrateData->source->serverInfoString()), "");
314
// -- read table schemas and create them in memory (only for non-KexiDB-compat tables)
315
foreach (const QString& tableCaption, tables) {
316
if (dest_isSystemObjectName( tableCaption )) {
320
KexiDB::TableSchema *tableSchema = sourceConn->tableSchema( tableCaption );
322
if (!dest_createTable(tableCaption, tableSchema)) {
324
result->setStatus(i18n("Could not create table in destination \"%1\". Error reading table \"%2\".", m_migrateData->destination->connectionData()->serverInfoString(), tableCaption), "");
328
if (m_migrateData->keepData) {
329
if (!dest_copyTable(tableCaption, sourceConn, tableSchema)) {
330
kDebug() << "Failed to copy table " << tableCaption;
332
result->setStatus(sourceConn,
333
i18n("Could not copy table \"%1\" to destination database.", tableCaption));
339
if (dest_disconnect()) {
342
ok = sourceConn->disconnect();
346
// Finally: error handling
347
if (result && result->error())
348
result->setStatus(sourceConn,
349
i18n("Could not export data to \"%1\".",
350
m_migrateData->source->serverInfoString()));
353
sourceConn->disconnect();
358
bool xBaseExport::dest_connect() {
362
bool xBaseExport::dest_disconnect() {
363
QList<QString> pathNameList = d->tableNamePathMap.values();
364
foreach(const QString& pathName, pathNameList) {
365
QByteArray ba = pathName.toAscii();
366
xbDbf* tablePtr = d->xbase.GetDbfPtr(ba.constData());
367
tablePtr->CloseDatabase();
373
bool xBaseExport::dest_createTable(const QString& originalName, KexiDB::TableSchema* tableSchema) {
375
// 1. For each fields in the table schema.
376
// 2. Create a xbSchema entry and add it to xbSchema array.
378
// 4. Create table in overlay mode ( overwrite )
380
uint fieldCount = tableSchema->fieldCount();
381
const int arrayLength = fieldCount + 1; // and extra space for the `null`
382
xbSchema xBaseTableSchema[arrayLength];// = new xbSchema[fieldCount+1][4];
385
for (i = 0; i < fieldCount ; ++i) {
386
KexiDB::Field* f = tableSchema->field(i);
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 ;
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;
403
const KexiDB::ConnectionData* connData = m_migrateData->destination->connectionData();
404
QString dirName = connData->fileName(); // this includes the forward slash after the dir name
406
QString pathName = dirName + originalName + ".dbf";
407
d->tableNamePathMap[originalName] = pathName;
409
QByteArray pathNameBa = pathName.toAscii();
411
xbDbf* xBaseTable = new xbDbf( &d->xbase );
412
xBaseTable->SetVersion( 4 ); // create dbase IV style files
414
if (( returnCode = xBaseTable->CreateDatabase( pathNameBa.constData() , xBaseTableSchema, XB_OVERLAY )) != XB_NO_ERROR ) {
415
kDebug()<<"Error creating table "<<originalName<<" Error Code "<<returnCode;
419
if (!d->createIndexes(originalName, tableSchema)) {
426
bool xBaseExport::dest_copyTable(const QString& srcTableName, KexiDB::Connection *srcConn,
427
KexiDB::TableSchema* /*srcTable*/) {
430
// 2. Insert it into the xBase table
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
KexiDB::Cursor* cursor = srcConn->executeQuery(QString( "Select * from %1" ).arg(srcTableName));
439
if (!cursor->moveFirst() && cursor->error())
442
while (!cursor->eof()) {
443
KexiDB::RecordData *record = cursor->storeCurrentRow();
447
if (!d->appendRecord(srcTableName, record)) {
448
kDebug()<<"Couldn't append record";
452
if (!cursor->moveNext() && cursor->error()) {
459
bool xBaseExport::dest_isSystemObjectName( const QString& /* objectName */ ) {