~ubuntu-branches/ubuntu/precise/koffice/precise

« back to all changes in this revision

Viewing changes to kexi/kexidb/drivers/xbase/xbaseexport.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jonathan Riddell
  • Date: 2010-09-21 15:36:35 UTC
  • mfrom: (1.4.1 upstream) (60.2.11 maverick)
  • Revision ID: james.westby@ubuntu.com-20100921153635-6tejqkiro2u21ydi
Tags: 1:2.2.2-0ubuntu3
Add kubuntu_03_fix-crash-on-closing-sqlite-connection-2.2.2.diff and
kubuntu_04_support-large-memo-values-for-msaccess-2.2.2.diff as
recommended by upstream http://kexi-
project.org/wiki/wikiview/index.php@Kexi2.2_Patches.html#sqlite_stab
ility

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
#include <kdebug.h>
 
26
 
 
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>
 
33
 
 
34
#include <cstring>
 
35
 
 
36
#include "xbase.h"
 
37
 
 
38
using namespace KexiDB;
 
39
 
 
40
class KexiDB::xBaseExportPrivate {
 
41
  public:
 
42
    xBaseExportPrivate() {
 
43
    }
 
44
 
 
45
    //! Converts kexidb field types to xbase types
 
46
    char type(KexiDB::Field::Type fieldType);
 
47
 
 
48
    //! Appends record to xbase table
 
49
    bool appendRecord(const QString& sourceTableName , KexiDB::RecordData* recordData);
 
50
 
 
51
    //! Returns max fieldlengths for xBase table
 
52
    int fieldLength(KexiDB::Field* f );
 
53
 
 
54
    //! converts QVariant data to a format understood by xBase
 
55
    QByteArray fieldData(QVariant data, char type);
 
56
 
 
57
    //! Creates xBase indexes for the table
 
58
    bool createIndexes(const QString& sourceTableName, KexiDB::TableSchema* tableSchema);
 
59
 
 
60
    xbXBase xbase;
 
61
    QHash<QString, QString> tableNamePathMap;
 
62
};
 
63
 
 
64
char xBaseExportPrivate::type(KexiDB::Field::Type fieldType)
 
65
{
 
66
  char xBaseType = '\0';
 
67
 
 
68
  switch( fieldType ) {
 
69
    case KexiDB::Field::Text:
 
70
    case KexiDB::Field::LongText:
 
71
      xBaseType = XB_CHAR_FLD;
 
72
      break;
 
73
 
 
74
    case KexiDB::Field::Boolean:
 
75
      xBaseType = XB_LOGICAL_FLD;
 
76
      break;
 
77
 
 
78
    case KexiDB::Field::Float:
 
79
    case KexiDB::Field::Double:
 
80
      xBaseType = XB_FLOAT_FLD;
 
81
 
 
82
    case KexiDB::Field::ShortInteger:
 
83
    case KexiDB::Field::Integer:
 
84
    case KexiDB::Field::BigInteger:
 
85
      xBaseType = XB_NUMERIC_FLD;
 
86
      break;
 
87
 
 
88
    case KexiDB::Field::DateTime:
 
89
    case KexiDB::Field::Date:
 
90
    case KexiDB::Field::Time:
 
91
      xBaseType = XB_DATE_FLD;
 
92
      break;
 
93
 
 
94
    case KexiDB::Field::BLOB:
 
95
      xBaseType = XB_MEMO_FLD;
 
96
      break;
 
97
 
 
98
    default:
 
99
      xBaseType = '\0';
 
100
  }
 
101
 
 
102
  return xBaseType;
 
103
}
 
104
 
 
105
bool xBaseExportPrivate::appendRecord( const QString& sourceTableName , KexiDB::RecordData* recordData ) {
 
106
 
 
107
//      kDebug()<<recordData->debugString();
 
108
  QString pathName = tableNamePathMap.value( sourceTableName );
 
109
  QByteArray pathNameBa = pathName.toAscii();
 
110
  xbDbf* table = xbase.GetDbfPtr( pathNameBa.constData() );
 
111
 
 
112
  int returnCode;
 
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);
 
117
 
 
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 );
 
122
  #else
 
123
    kDebug()<<"XB_MEMO_FIELDS support disabled during compilation of XBase libraries";
 
124
  #endif
 
125
  } else {
 
126
    if ((returnCode = table->PutField( i, stringData.constData())) != XB_NO_ERROR ) {
 
127
      switch(returnCode) {
 
128
      case XB_INVALID_FIELDNO:
 
129
        kDebug()<<"Invalid field number "<<i;
 
130
        return false;
 
131
      case XB_INVALID_DATA:
 
132
        kDebug()<<"Invalid data "<<stringData;
 
133
        return false;
 
134
      default:
 
135
        kDebug()<<"Error number "<<returnCode<<" has occurred";
 
136
        return false;
 
137
    }
 
138
    }
 
139
  }
 
140
  }
 
141
 
 
142
  if((returnCode = table->AppendRecord()) != XB_NO_ERROR) {
 
143
    kDebug() << "\nxBase Error " << returnCode << " appending data record.";
 
144
    return false;
 
145
  }
 
146
 
 
147
//      // for debugging purposes only
 
148
//      for ( uint i=0; i< (uint)recordData->size(); ++i ) {
 
149
//              kDebug()<<table->GetField(i);
 
150
//      }
 
151
 
 
152
  return true;
 
153
}
 
154
 
 
155
int xBaseExportPrivate::fieldLength(KexiDB::Field* f ) {
 
156
  if ( f->type() == KexiDB::Field::Text ) {
 
157
    return f->length();
 
158
  }
 
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())) {
 
162
    case XB_CHAR_FLD:
 
163
      return 254;
 
164
    case XB_LOGICAL_FLD:
 
165
      return 1;
 
166
    case XB_FLOAT_FLD:
 
167
    case XB_NUMERIC_FLD:
 
168
      return 17;
 
169
    case XB_DATE_FLD:
 
170
      return 8;
 
171
    case XB_MEMO_FLD:
 
172
      return 10;
 
173
    default:
 
174
      return 0;
 
175
  }
 
176
}
 
177
 
 
178
QByteArray xBaseExportPrivate::fieldData(QVariant data, char type) {
 
179
 
 
180
  switch(type) {
 
181
    case XB_CHAR_FLD:
 
182
    case XB_FLOAT_FLD:
 
183
    case XB_NUMERIC_FLD:
 
184
      return data.toString().toUtf8();
 
185
 
 
186
    case XB_LOGICAL_FLD:
 
187
      if (data.toBool()) {
 
188
        return QString( "t" ).toAscii();
 
189
      } else
 
190
        return QString( "f" ).toAscii();
 
191
 
 
192
    case XB_DATE_FLD:
 
193
      return data.toDate().toString("yyyyMMdd").toAscii();
 
194
 
 
195
    case XB_MEMO_FLD:
 
196
      return data.toByteArray();
 
197
    default:
 
198
      return QByteArray();
 
199
  }
 
200
}
 
201
 
 
202
bool xBaseExportPrivate::createIndexes(const QString& sourceTableName, KexiDB::TableSchema* tableSchema) {
 
203
 
 
204
  QString pathName = tableNamePathMap.value( sourceTableName );
 
205
  QByteArray pathNameBa = pathName.toAscii();
 
206
  xbDbf* table = xbase.GetDbfPtr( pathNameBa.constData() );
 
207
  uint fieldCount = tableSchema->fieldCount();
 
208
 
 
209
  QString dirName = QFileInfo( pathName ).path();
 
210
 
 
211
  for (uint i=0; i< (uint)fieldCount ; ++i) {
 
212
    KexiDB::Field* f = tableSchema->field(i);
 
213
 
 
214
    int returnCode;
 
215
    QString fieldName = f->name();
 
216
    QString indexName = dirName + QDir::separator() + sourceTableName + '_' + fieldName + ".ndx";
 
217
    QByteArray indexNameBa = indexName.toAscii();
 
218
    QByteArray fieldNameBa = fieldName.toLatin1();
 
219
 
 
220
    xbNdx index(table);
 
221
    if (f->isUniqueKey() || f->isPrimaryKey()) {
 
222
 
 
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;
 
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
        kDebug()<<"Couldn't create index for fieldName "<<fieldName<<" on table "<<sourceTableName<<" Error Code "<<returnCode;
 
233
        return false;
 
234
      }
 
235
      index.CloseIndex();
 
236
 
 
237
    }
 
238
  }
 
239
  return true;
 
240
}
 
241
 
 
242
 
 
243
xBaseExport::xBaseExport()
 
244
: m_migrateData( 0 ),
 
245
d(new xBaseExportPrivate)
 
246
{
 
247
}
 
248
 
 
249
void xBaseExport::setData(KexiMigration::Data* migrateData) {
 
250
  m_migrateData = migrateData;
 
251
}
 
252
 
 
253
bool xBaseExport::performExport(Kexi::ObjectStatus* result) {
 
254
 
 
255
  if (result)
 
256
    result->clearStatus();
 
257
 
 
258
 
 
259
  KexiDB::DriverManager drvManager;
 
260
 
 
261
  if (!m_migrateData) {
 
262
    kDebug()<<"Migration Data not set yet !!";
 
263
    result->setStatus(&drvManager, i18n("Data not set for migration"));
 
264
    return false;
 
265
  }
 
266
 
 
267
  KexiDB::Driver *sourceDriver = drvManager.driver(
 
268
    m_migrateData->source->driverName);
 
269
  if (!sourceDriver) {
 
270
    result->setStatus(&drvManager,
 
271
      i18n("Could not export back to destination database"));
 
272
    return false;
 
273
  }
 
274
 
 
275
  // connect to destination database
 
276
  if (!dest_connect()) {
 
277
    kDebug()<<"Couldn't connect to destination database";
 
278
    if (result)
 
279
      result->setStatus(i18n("Could not connect to data source \"%1\".",
 
280
        m_migrateData->destination->connectionData()->serverInfoString()), "");
 
281
    return false;
 
282
  }
 
283
 
 
284
  KexiDB::Connection* sourceConn = sourceDriver->createConnection(*(m_migrateData->source));
 
285
 
 
286
  if (!sourceConn || sourceDriver->error()) {
 
287
    kDebug()<<"Export failed";
 
288
    return false;
 
289
  }
 
290
  if (!sourceConn->connect()) {
 
291
    kDebug()<<"Export failed.Could not connect";
 
292
    return false;
 
293
  }
 
294
 
 
295
  if (!sourceConn->useDatabase(m_migrateData->sourceName)) {
 
296
    kDebug()<<"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
    kDebug() << "There were no tables to export";
 
305
    if (result)
 
306
      result->setStatus(
 
307
        i18n("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-KexiDB-compat tables)
 
315
  foreach (const QString& tableCaption, tables) {
 
316
    if (dest_isSystemObjectName( tableCaption )) {
 
317
      return false;
 
318
    }
 
319
 
 
320
    KexiDB::TableSchema *tableSchema = sourceConn->tableSchema( tableCaption );
 
321
 
 
322
    if (!dest_createTable(tableCaption, tableSchema)) {
 
323
      if (result)
 
324
        result->setStatus(i18n("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
        kDebug() << "Failed to copy table " << tableCaption;
 
331
        if (result)
 
332
          result->setStatus(sourceConn,
 
333
              i18n("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
      i18n("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
 
 
358
bool xBaseExport::dest_connect() {
 
359
  return true;
 
360
}
 
361
 
 
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();
 
368
    // delete tablePtr ?
 
369
  }
 
370
  return true;
 
371
}
 
372
 
 
373
bool xBaseExport::dest_createTable(const QString& originalName, KexiDB::TableSchema* 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
  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];
 
383
 
 
384
  uint i = 0;
 
385
  for (i = 0; i < fieldCount ; ++i) {
 
386
    KexiDB::Field* 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 KexiDB::ConnectionData* 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.toAscii();
 
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
    kDebug()<<"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
 
 
426
bool xBaseExport::dest_copyTable(const QString& srcTableName, KexiDB::Connection *srcConn,
 
427
        KexiDB::TableSchema* /*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
  KexiDB::Cursor* cursor = srcConn->executeQuery(QString( "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
    KexiDB::RecordData *record = cursor->storeCurrentRow();
 
444
    if (!record) {
 
445
      return false;
 
446
    }
 
447
    if (!d->appendRecord(srcTableName, record)) {
 
448
      kDebug()<<"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
 
 
459
bool xBaseExport::dest_isSystemObjectName( const QString& /* objectName */ ) {
 
460
  return false;
 
461
}