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 "xbasemigrate.h"
21
#include <migration/keximigratedata.h>
27
#include <KDbDriverManager>
36
using namespace KexiMigration;
38
/* This is the implementation for the xBase specific import routines. */
39
KEXI_PLUGIN_FACTORY(xBaseMigrate, "keximigrate_xbase.json")
41
//! Constructor (needed for trading interface)
42
xBaseMigrate::xBaseMigrate(QObject *parent, const QVariantList& args) :
43
KexiMigrate(parent, args)
45
KDbDriverManager manager;
46
setDriver(manager.driver("xbase"));
49
/* ************************************************************************** */
51
xBaseMigrate::~xBaseMigrate()
56
/* ************************************************************************** */
57
/*! Connect to the db backend */
58
bool xBaseMigrate::drv_connect()
60
// Get the xbase directory path
61
Data* migrationData = data();
62
KDbConnectionData* dataSource = migrationData->source;
63
QString dbPath = dataSource->dbPath();
65
QDir xBaseDirectory( dbPath );
68
QStringList dbfFilters;
69
//! @todo use application/x-dbase mime type as soon as share mime db provides info on file extensions
70
dbfFilters<<"*.dbf"<<"*.DBF";
72
xBaseDirectory.setNameFilters( dbfFilters );
73
QStringList dbfFiles = xBaseDirectory.entryList(); // set a readable files filter here ?
75
foreach( const QString& fileName, dbfFiles ) {
76
xbDbf* table = new xbDbf( this );
77
// Calling OpenDatabase, will automatically add the pointer `table`
78
// to the dbfList of xbXBase class ( if there is no error )
79
QString absoluteFileName = xBaseDirectory.filePath( fileName );
81
// remove the letters '.dbf'. Hence the -4
82
QString choppedFileName = fileName.left( fileName.length() - 4 ).toLower();
83
m_tableNamePathMap[choppedFileName] = absoluteFileName;
84
qDebug()<<choppedFileName<<" Path:"<<absoluteFileName;
87
QByteArray ba = absoluteFileName.toUtf8();
88
if ( ( returnCode = table->OpenDatabase( ba.constData() ) ) != XB_NO_ERROR ) {
89
switch( returnCode ) {
91
qDebug()<<"Couldn't open "<<absoluteFileName<<".Skipping it.";
94
qDebug()<<"Memory allocation error in XBase library";
97
qDebug()<<absoluteFileName<<" is not a DBF file.Skipping it.";
100
qDebug()<<"Error code "<<returnCode;
106
qDebug()<<"Successfully processed all the dbf files in the directory";
111
/*! Disconnect from the db backend */
112
bool xBaseMigrate::drv_disconnect()
114
// delete all dbf pointers here ?
115
xbDbList* tempDbfList = DbfList;
117
while ( tempDbfList != NULL ) {
118
xbDbf* currentDbf = tempDbfList->dbf;
119
tempDbfList = tempDbfList->NextDbf;
120
if (currentDbf->CloseDatabase() != XB_NO_ERROR) {
121
// File not open error
122
qDebug()<<"File Not Open";
124
// delete currentDbf here ?
131
/* ************************************************************************** */
132
/*! Get the types and properties for each column. */
133
bool xBaseMigrate::drv_readTableSchema(
134
const QString& originalName, KDbTableSchema *tableSchema)
137
// 1. Get the number of fields
138
// 2. for i = 1 to no_of_fields
139
// 3. Get the fieldName of the i th field
140
// 4. Generate a fieldId
141
// 5. Create a KDbField object, using the fieldId and the fieldType ( you may need to write a type conversion function here )
142
// 6. Examine enum fields of any
143
// 7. Set the caption of the field
144
// 8. Set other properties of the field ( pertaining to constraints like pkey, unique etc, and AutoIncrement etc )
145
// 9. Add the field to the KDbTableSchema
149
QString tablePath = m_tableNamePathMap.value( originalName );
150
// get dbf pointer for table
151
xbDbf* tableDbf = GetDbfPtr(qPrintable(tablePath));
153
xbLong numFlds = tableDbf->FieldCount();
156
for( xbShort i = 0; i < numFlds; ++i ) {
157
QString fldName = QString::fromLatin1( tableDbf->GetFieldName( i ) );
158
QString fldID( KDb::stringToIdentifier( fldName.toLower() ) );
161
new KDbField( fldID, type( tableDbf->GetFieldType( i ) ) );
163
if ( fld->type() == KDbField::Text ) {
164
int len = tableDbf->GetFieldLen(i);
165
if (len < 255) { // limit for small lengths only
166
fld->setMaxLength(len);
170
if ( fld->isFPNumericType() ) {
171
fld->setScale( tableDbf->GetFieldDecimal(i) );
174
getConstraints(originalName, fld);
176
if (!tableSchema->addField(fld)) {
178
tableSchema->clear();
187
/*! Get a list of tables and put into the supplied string list */
188
bool xBaseMigrate::drv_tableNames(QStringList *tableNames)
190
// Get the names from the map directly
191
tableNames->append(m_tableNamePathMap.keys());
192
//qDebug()<<"Tables "<<tableNames;
196
//! Copy xBase table to KDb table
197
bool xBaseMigrate::drv_copyTable(const QString& srcTable, KDbConnection *destConn,
198
KDbTableSchema* dstTable, const RecordFilter *recordFilter)
201
// 1. for all records in the table
202
// 2. num_fields = number of fields in the table
203
// 3. for each field in the table
204
// 4. get the length of the field in the table
205
// 5. Append the field to the variant list ( vals )
207
// 7. Insert the record into the destinationConnection into the destinationTable
210
// get dbf pointer for table
211
QString tablePath = m_tableNamePathMap.value( srcTable );
212
xbDbf* tableDbf = GetDbfPtr(qPrintable(tablePath));
214
xbLong numRecords = tableDbf->NoOfRecords();
216
const KDbQueryColumnInfo::Vector fieldsExpanded( dstTable->query()->fieldsExpanded() );
217
// records are indexed from 1
218
for ( xbULong i = 1; i <= (xbULong)numRecords ; ++i ) {
219
tableDbf->GetRecord( i );
220
QList<QVariant> vals;
222
xbLong numFlds = tableDbf->FieldCount();
223
// fields are indexed from 0
224
for( xbShort j = 0; j < numFlds; ++j ) {
226
tableDbf->GetField(j, data);
229
#ifdef XB_MEMO_FIELDS
231
char* memoBuffer = 0;
235
switch ( type( tableDbf->GetFieldType( j ) ) ) {
237
val = QDate::fromString( data, "yyyyMMdd" );
239
case KDbField::Boolean:
241
case 'Y': case 'y': case 'T': case 't':
244
case 'N': case 'n': case 'F' : case'f':
253
#ifdef XB_MEMO_FIELDS
254
blobFieldLength = tableDbf->GetMemoFieldLen(j);
255
memoBuffer = new char[blobFieldLength];
258
tableDbf->LockMemoFile( F_SETLK, F_RDLCK );
261
if ( ( returnCode = tableDbf->GetMemoField( j , blobFieldLength, memoBuffer, F_SETLKW ) ) != XB_NO_ERROR ) {
262
qDebug()<<"Error reading blob field. Error code: "<<returnCode; // make error message more verbose
265
val = KDb::cstringToVariant( memoBuffer, fieldsExpanded.at(j)->field->type(), 0, blobFieldLength );
268
tableDbf->LockMemoFile( F_SETLK, F_UNLCK );
273
qDebug()<<"XB_MEMO_FIELDS support disabled during compilation of XBase libraries";
277
val = KDb::cstringToVariant(data, fieldsExpanded.at(j)->field->type());
282
if (recordFilter && !(*recordFilter)(vals)) {
285
if (!destConn->insertRecord(*dstTable, vals)) {
293
KDbField::Type KexiMigration::xBaseMigrate::type(char xBaseColumnType)
295
KDbField::Type kexiType = KDbField::InvalidType;
297
switch( xBaseColumnType ) {
299
kexiType = KDbField::Text;
302
kexiType = KDbField::Boolean;
305
kexiType = KDbField::Float;
308
kexiType = KDbField::Date;
311
kexiType = KDbField::BLOB;
314
kexiType = KDbField::Double;
317
kexiType = KDbField::InvalidType;
324
void KexiMigration::xBaseMigrate::getConstraints(const QString& tableName, KDbField* fld)
326
// 1. Get the names of the index files
327
// 2. Create appropriate xbIndex type object ( xbNdx or xbNtx ) depending on extension
328
// 3. Open the index file
329
// 4. Check the expression of the index to crosscheck whether this is indeed the index file on the required field.
330
// 5. Determine the index type ( unique or not )
331
// 6. Set appropriate properties to the field
333
// Create a base class pointer to an xbIndex
336
QStringList indexFileNames = getIndexFileNames(tableName, fld->name());
338
if ( indexFileNames.isEmpty() ) {
339
// no index files exist for this field
343
foreach( const QString& indexFileName, indexFileNames ) {
345
// get dbf pointer for table
346
QString tablePath = m_tableNamePathMap.value( tableName );
347
xbDbf* tableDbf = GetDbfPtr(qPrintable(tablePath));
349
// determine type of indexFile
350
// currently done by checking extension.
351
//! @TODO Check mimetype instead
352
QString fileExtension = indexFileName.right( 3 );
354
if ( fileExtension.toLower() == "ndx" ) {
355
index = new xbNdx( tableDbf );
356
} else if ( fileExtension.toLower() == "ntx" ) {
357
index = new xbNtx( tableDbf );
359
// couldn't recognize extension
360
qDebug()<<"Couldn't recognize extension";
364
if ( index->OpenIndex(qPrintable(indexFileName)) != XB_NO_ERROR ) {
365
qDebug()<<"Couldn't open index file"<<indexFileName;
369
// verfiy if this index is on the required field
371
index->GetExpression( buf, 256 );
372
QString expressionName = QString::fromLatin1( buf );
374
if ( expressionName.toLower() != fld->name() ) {
375
qDebug()<<"Expression mismatch in "<<indexFileName;
379
// all is well, set the index
380
if ( index->UniqueIndex() == XB_UNIQUE ) {
381
fld->setUniqueKey( true );
382
qDebug()<<"Unique Index on "<<fld->name();
383
} else { // index->UniqueIndex() == XB_NOT_UNIQUE
384
fld->setIndexed( true );
385
qDebug()<<"Normal Index on "<<fld->name();
388
// ok, moving through the loop is fairly useless as we can only set a single index on a field anyway
389
// does any one use multiple indexes on the same field ?
390
// well anyway, when Kexi supports it, we'll use IndexSchemas till then ...
394
QStringList KexiMigration::xBaseMigrate::getIndexFileNames(const QString& tableName, const QString& fieldName)
396
// this function needs to return a lits of index files corresponding to the given tablename and field.
397
// The current policy uses the xbsql ( http://www.quaking.demon.co.uk/xbsql/ ) semantics for determining
398
// the filenames of the index files
399
// index files are assumed to be of the type <tablename>_<fieldname>.ndx or .ntx
401
// Though the current semantics allows only one index on a field ( actually two, considering we can
402
// have both .ndx and .ntx index, there can be multiple indices, hence a list of filenames is returned
403
// (Note: Kexi fields support only a single index. But we have a separate KDbIndexSchema class ...)
405
QString dbPath = data()->source->dbPath();
406
QDir xBaseDirectory( dbPath );
408
QString fileName = tableName + '_' + fieldName;
410
QStringList indexFilters;
411
indexFilters<<fileName+'*'; // filter all files of the form <tableName>_<fieldName>
413
xBaseDirectory.setNameFilters( indexFilters );
414
QStringList fileNameList = xBaseDirectory.entryList();
416
QStringList absolutePathNames;
417
foreach( const QString& fileName, fileNameList ) {
418
absolutePathNames<<xBaseDirectory.filePath( fileName );
421
return absolutePathNames;