File: xbasemigrate.cpp

package info (click to toggle)
calligra 1:2.8.5+dfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 397,516 kB
  • ctags: 185,064
  • sloc: cpp: 1,505,875; python: 45,987; ansic: 28,421; xml: 26,472; sh: 23,986; java: 18,214; objc: 3,965; makefile: 2,816; perl: 2,792; yacc: 2,601; lex: 1,410; sql: 903; ruby: 737; asm: 236; lisp: 121
file content (419 lines) | stat: -rw-r--r-- 13,560 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
/* This file is part of the KDE project
   Copyright (C) 2008 Sharan Rao <sharanrao@gmail.com>

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this program; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
*/

#include "xbasemigrate.h"

#include <QString>
#include <QRegExp>
#include <QFile>
#include <QVariant>
#include <QList>
#include <QDir>
#include <kdebug.h>

#include <migration/keximigratedata.h>
#include <db/cursor.h>
#include <db/field.h>
#include <db/utils.h>
#include <db/drivermanager.h>
#include <kexiutils/identifier.h>

using namespace KexiMigration;

/* This is the implementation for the xBase specific import routines. */

K_EXPORT_KEXIMIGRATE_DRIVER(xBaseMigrate, "xbase")

//! Constructor (needed for trading interface)
xBaseMigrate::xBaseMigrate(QObject *parent, const QVariantList& args) :
  KexiMigrate(parent, args)
{
  KexiDB::DriverManager manager;
  setDriver(manager.driver("xbase"));
}

/* ************************************************************************** */
//! Destructor
xBaseMigrate::~xBaseMigrate()
{
}


/* ************************************************************************** */
/*! Connect to the db backend */
bool xBaseMigrate::drv_connect()
{
  // Get the xbase directory path
  Data* migrationData = data();
  KexiDB::ConnectionData* dataSource = migrationData->source;
  QString dbPath = dataSource->dbPath();

  QDir xBaseDirectory( dbPath );

  // set filters
  QStringList dbfFilters;
//! @todo use application/x-dbase mime type as soon as share mime db provides info on file extensions
  dbfFilters<<"*.dbf"<<"*.DBF";

  xBaseDirectory.setNameFilters( dbfFilters );
  QStringList dbfFiles = xBaseDirectory.entryList(); // set a readable files filter here ?

  foreach( const QString& fileName, dbfFiles ) {
    xbDbf* table = new xbDbf( this );
    // Calling OpenDatabase, will automatically add the pointer `table`
    // to the dbfList of xbXBase class ( if there is no error )
    QString absoluteFileName = xBaseDirectory.filePath( fileName );

    // remove the letters '.dbf'. Hence the -4
    QString choppedFileName = fileName.left( fileName.length() - 4 ).toLower();
    m_tableNamePathMap[choppedFileName] = absoluteFileName;
    kDebug()<<choppedFileName<<" Path:"<<absoluteFileName;

    int returnCode;
    QByteArray ba = absoluteFileName.toUtf8();
    if (  ( returnCode = table->OpenDatabase( ba.constData() ) ) != XB_NO_ERROR ) {
      switch( returnCode ) {
        case XB_OPEN_ERROR:
          kDebug()<<"Couldn't open "<<absoluteFileName<<".Skipping it.";
          break;
        case XB_NO_MEMORY:
          kDebug()<<"Memory allocation error in XBase library";
          return false;
        case XB_NOT_XBASE:
          kDebug()<<absoluteFileName<<" is not a DBF file.Skipping it.";
          break;
        default:
          kDebug()<<"Error code "<<returnCode;
          return false;
      }
    }
  }

  kDebug()<<"Successfully processed all the dbf files in the directory";
  return true;
}


/*! Disconnect from the db backend */
bool xBaseMigrate::drv_disconnect()
{
  // delete all dbf pointers here ?
  xbDbList* tempDbfList = DbfList;

  while ( tempDbfList != NULL ) {
    xbDbf* currentDbf = tempDbfList->dbf;
    tempDbfList = tempDbfList->NextDbf;
    int returnCode;
    if ( ( returnCode = currentDbf->CloseDatabase() ) != XB_NO_ERROR ) {
      // File not open error
      kDebug()<<"File Not Open";
    }
    // delete currentDbf here ?
  }

  return true;
}


/* ************************************************************************** */
/*! Get the types and properties for each column. */
bool xBaseMigrate::drv_readTableSchema(
  const QString& originalName, KexiDB::TableSchema& tableSchema)
{
  // Steps
  // 1. Get the number of fields
  // 2. for i = 1 to no_of_fields
  // 3.	 Get the fieldName of the i th	field
  // 4.	 Generate a fieldId
  // 5.	 Create a KexiDB::Field object, using the fieldId and the fieldType ( you may need to write a type conversion function here )
  // 6.	 Examine enum fields of any
  // 7.	 Set the caption of the field
  // 8.	 Set other properties of the field ( pertaining to constraints like pkey, unique etc, and  AutoIncrement etc )
  // 9.	 Add the field to the TableSchema
  // 10. end for

  // Get table path
  QString tablePath = m_tableNamePathMap.value( originalName );
  // get dbf pointer for table
  xbDbf* tableDbf = GetDbfPtr( tablePath.toLatin1().constData() );

  xbLong numFlds = tableDbf->FieldCount();

  for( xbShort i = 0; i < numFlds; ++i ) {
    QString fldName = QString::fromLatin1( tableDbf->GetFieldName( i ) );
    QString fldID( KexiUtils::stringToIdentifier( fldName.toLower() ) );

    KexiDB::Field *fld =
        new KexiDB::Field( fldID, type( tableDbf->GetFieldType( i ) ) );

    if ( fld->type() == KexiDB::Field::Text ) {
      uint len = tableDbf->GetFieldLen(i);
      if (len < 255) { // limit for small lengths only
          fld->setMaxLength(len);
      }
    }

    if ( fld->isFPNumericType() ) {
      fld->setScale( tableDbf->GetFieldDecimal(i) );
    }

    getConstraints(originalName, fld);

    tableSchema.addField(fld);
  }
  return true;
}


/*! Get a list of tables and put into the supplied string list */
bool xBaseMigrate::drv_tableNames(QStringList& tableNames)
{
  // Get the names from the map directly
  tableNames<<m_tableNamePathMap.keys();

  kDebug()<<"Tables "<<tableNames;

  return true;
}

/*! Copy xBase table to KexiDB database */
bool xBaseMigrate::drv_copyTable(const QString& srcTable, KexiDB::Connection *destConn,
  KexiDB::TableSchema* dstTable)
{
  // Steps
  // 1. for all records in the table
  // 2.	num_fields = number of fields in the table
  // 3.	for each field in the table
  // 4.	    get the length of the field in the table
  // 5.	    Append the field to the variant list ( vals )
  // 6.	end for
  // 7.	Insert the record into the destinationConnection into the destinationTable
  // 8. end for

  // get dbf pointer for table
  QString tablePath = m_tableNamePathMap.value( srcTable );
  xbDbf* tableDbf = GetDbfPtr( tablePath.toLatin1().constData() );

  xbLong numRecords = tableDbf->NoOfRecords();

  const KexiDB::QueryColumnInfo::Vector fieldsExpanded( dstTable->query()->fieldsExpanded() );
  // records are indexed from 1
  for ( xbULong i = 1; i <= (xbULong)numRecords ; ++i ) {
    tableDbf->GetRecord( i );
    QList<QVariant> vals;

    xbLong numFlds = tableDbf->FieldCount();
    // fields are indexed from 0
    for( xbShort j = 0; j < numFlds; ++j ) {
      char data[1024];
      tableDbf->GetField(j, data);
      QVariant val;

      #ifdef XB_MEMO_FIELDS
        int blobFieldLength;
        char* memoBuffer = 0;
        int returnCode;
      #endif
  
      switch ( type( tableDbf->GetFieldType( j ) ) ) {
        case KexiDB::Field::Date:
          val = QDate::fromString( data, "yyyyMMdd" );
          break;
        case KexiDB::Field::Boolean:
          switch(data[0]) {
            case 'Y': case 'y': case 'T': case 't':
              val = true;
              break;
            case 'N': case 'n': case 'F' : case'f':
              val = false;
              break;
            default:
              val = false;
              break;
          }
          break;
        case KexiDB::Field::BLOB:
        #ifdef XB_MEMO_FIELDS
          blobFieldLength = tableDbf->GetMemoFieldLen(j);
          memoBuffer = new char[blobFieldLength];

          #ifdef XB_LOCKING_ON
            // tableDbf->LockMemoFile( F_SETLK, F_RDLCK );
          #endif

          if ( ( returnCode = tableDbf->GetMemoField( j , blobFieldLength, memoBuffer, F_SETLKW ) ) != XB_NO_ERROR ) {
            kDebug()<<"Error reading blob field. Error code: "<<returnCode; // make error message more verbose
          } else {
            val = KexiDB::cstringToVariant( memoBuffer, fieldsExpanded.at(j)->field, blobFieldLength );
          }
          #ifdef XB_LOCKING_ON
            // tableDbf->LockMemoFile( F_SETLK, F_UNLCK );
          #endif

          break;
        #else
          kDebug()<<"XB_MEMO_FIELDS support disabled during compilation of XBase libraries";
        #endif

        default:
          val = KexiDB::cstringToVariant(data, fieldsExpanded.at(j)->field, strlen( data ) ) ;
          break;
      }
      vals.append( val );
    }
    if (!destConn->insertRecord(*dstTable, vals)) {
      return false;
    }
  }

  return true;
}

KexiDB::Field::Type KexiMigration::xBaseMigrate::type(char xBaseColumnType)
{
  KexiDB::Field::Type kexiType = KexiDB::Field::InvalidType;

  switch( xBaseColumnType ) {
    case XB_CHAR_FLD:
      kexiType = KexiDB::Field::Text;
      break;
    case XB_LOGICAL_FLD:
      kexiType = KexiDB::Field::Boolean;
      break;
    case XB_NUMERIC_FLD:
      kexiType = KexiDB::Field::Float;
      break;
    case XB_DATE_FLD:
      kexiType = KexiDB::Field::Date;
      break;
    case XB_MEMO_FLD:
      kexiType = KexiDB::Field::BLOB;
      break;
    case XB_FLOAT_FLD:
      kexiType = KexiDB::Field::Double;
      break;
    default:
      kexiType = KexiDB::Field::InvalidType;
      break;
  }

  return kexiType;
}

void KexiMigration::xBaseMigrate::getConstraints(const QString& tableName, KexiDB::Field* fld)
{
  // 1. Get the names of the index files
  // 2. Create appropriate xbIndex type object ( xbNdx or xbNtx ) depending on extension
  // 3. Open the index file
  // 4. Check the expression of the index to crosscheck whether this is indeed the index file on the required field.
  // 5. Determine the index type ( unique or not )
  // 6. Set appropriate properties to the field
  
  // Create a base class pointer to an xbIndex
  xbIndex* index = 0;
  
  QStringList indexFileNames = getIndexFileNames(tableName, fld->name());
  
  if ( indexFileNames.isEmpty() ) {
    // no index files exist for this field
    return;
  }
  
  foreach( const QString& indexFileName, indexFileNames ) {
  
    // get dbf pointer for table
    QString tablePath = m_tableNamePathMap.value( tableName );
    xbDbf* tableDbf = GetDbfPtr( tablePath.toLatin1().constData() );

    // determine type of indexFile
    // currently done by checking extension.
    //! @TODO Check mimetype instead
    QString fileExtension = indexFileName.right( 3 );
  
    if ( fileExtension.toLower() == "ndx" ) {
      index = new xbNdx( tableDbf );
    } else if ( fileExtension.toLower() == "ntx" ) {
      index = new xbNtx( tableDbf );
    } else {
      // couldn't recognize extension
      kDebug()<<"Couldn't recognize extension";
      return;
    }
  
    if ( index->OpenIndex( indexFileName.toLatin1().constData() ) != XB_NO_ERROR ) {
      kDebug()<<"Couldn't open index file"<<indexFileName;
      return;
    }
  
    // verfiy if this index is on the required field
    char buf[256];
    index->GetExpression( buf, 256 );
    QString expressionName = QString::fromLatin1( buf );
  
    if ( expressionName.toLower() != fld->name() ) {
      kDebug()<<"Expression mismatch in "<<indexFileName;
      continue;
    }
  
    // all is well, set the index
    if ( index->UniqueIndex() == XB_UNIQUE ) {
      fld->setUniqueKey( true );
      kDebug()<<"Unique Index on "<<fld->name();
    } else {  // index->UniqueIndex() == XB_NOT_UNIQUE
      fld->setIndexed( true );
      kDebug()<<"Normal Index on "<<fld->name();
    }

    // ok, moving through the loop is fairly useless as we can only set a single index on a field anyway
    // does any one use multiple indexes on the same field ?
    // well anyway, when Kexi supports it, we'll use IndexSchemas till then ...
  }
}

QStringList KexiMigration::xBaseMigrate::getIndexFileNames(const QString& tableName, const QString& fieldName)
{
  // this function needs to return a lits of index files corresponding to the given tablename and field.
  // The current policy uses the xbsql ( http://www.quaking.demon.co.uk/xbsql/ ) semantics for determining
  // the filenames of the index files
  // index files are assumed to be of the type <tablename>_<fieldname>.ndx or .ntx
  
  // Though the current semantics allows only one index on a field ( actually two, considering we can
  // have both .ndx and .ntx index, there can be multiple indices, hence a list of filenames is returned
  // (Note: Kexi fields support only a single index. But we have a separate IndexSchema class ...)
  
  QString dbPath = data()->source->dbPath();
  QDir xBaseDirectory( dbPath );
  
  QString fileName = tableName + '_' + fieldName;
  
  QStringList indexFilters;
  indexFilters<<fileName+'*'; // filter all files of the form <tableName>_<fieldName>
  
  xBaseDirectory.setNameFilters( indexFilters );
  QStringList fileNameList = xBaseDirectory.entryList();
  
  QStringList absolutePathNames;
  foreach( const QString& fileName, fileNameList ) {
    absolutePathNames<<xBaseDirectory.filePath( fileName );
  }
  
  return absolutePathNames;
}

#include "xbasemigrate.moc"