KDb

XbaseExport.cpp
1 /* This file is part of the KDE project
2  Copyright (C) 2008 Sharan Rao <[email protected]>
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 
37 class 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 
61 char xBaseExportPrivate::type(KDbField::Type fieldType)
62 {
63  char xBaseType = '\0';
64 
65  switch( fieldType ) {
66  case KDbField::Text:
67  case KDbField::LongText:
68  xBaseType = XB_CHAR_FLD;
69  break;
70 
71  case KDbField::Boolean:
72  xBaseType = XB_LOGICAL_FLD;
73  break;
74 
75  case KDbField::Float:
76  case KDbField::Double:
77  xBaseType = XB_FLOAT_FLD;
78 
80  case KDbField::Integer:
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 
102 bool 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 
152 int 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 
177 QByteArray 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 
201 bool 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 
244 xBaseExport::xBaseExport()
245 : m_migrateData( 0 ),
246 d(new xBaseExportPrivate)
247 {
248 }
249 
250 void xBaseExport::setData(KexiMigration::Data* migrateData) {
251  m_migrateData = migrateData;
252 }
253 
254 bool 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 
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.toLatin1();
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, 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 
426 bool 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 
459 bool xBaseExport::dest_isSystemObjectName( const QString& /* objectName */ ) {
460  return false;
461 }
QString path() const const
Provides database cursor functionality.
Definition: KDbCursor.h:68
bool useDatabase(const QString &dbName=QString(), bool kexiCompatible=true, bool *cancelled=nullptr, KDbMessageHandler *msgHandler=nullptr)
Opens an existing database specified by dbName.
virtual bool moveNext()
Definition: KDbCursor.cpp:351
bool isPrimaryKey() const
Definition: KDbField.h:287
bool isIndexed() const
Definition: KDbField.h:312
bool moveFirst()
Definition: KDbCursor.cpp:285
Type type(const QSqlDatabase &db)
virtual KDbField * field(int id)
QChar separator()
@ Text
Definition: KDbField.h:98
Specialized string for escaping.
QStringList tableNames(bool alsoSystemTables=false, bool *ok=nullptr)
Database driver's abstraction.
Definition: KDbDriver.h:49
QByteArray toLatin1() const const
bool isUniqueKey() const
Definition: KDbField.h:292
QByteArray toByteArray() const const
int scale() const
Definition: KDbField.cpp:291
QDate toDate() const const
KDbRecordData * storeCurrentRecord() const
Definition: KDbCursor.cpp:183
bool eof() const
Definition: KDbCursor.h:147
bool isTextType() const
Definition: KDbField.h:353
bool connect()
Connects to driver with given parameters.
KDbConnection * createConnection(const KDbConnectionData &connData, const KDbConnectionOptions &options)
Definition: KDbDriver.cpp:137
@ Integer
Definition: KDbField.h:90
QByteArray toUtf8() const const
int maxLength() const
Definition: KDbField.cpp:665
bool isEmpty() const const
QVariant value(int i) const
@ BigInteger
Definition: KDbField.h:91
Type type() const
Definition: KDbField.cpp:379
KDbCursor * executeQuery(const KDbEscapedString &sql, KDbCursor::Options options=KDbCursor::Option::None)
@ Double
Definition: KDbField.h:97
KDbTableSchema * tableSchema(int tableId)
bool toBool() const const
@ LongText
Definition: KDbField.h:99
@ Boolean
Definition: KDbField.h:92
Structure for storing single record with type information.
Definition: KDbRecordData.h:36
const char * constData() const const
Database specific connection data, e.g. host, port.
bool disconnect()
Disconnects from driver with given parameters.
Meta-data for a field.
Definition: KDbField.h:71
QString toString(Qt::DateFormat format) const const
int fieldCount() const
int size() const const
@ Float
Definition: KDbField.h:96
A driver manager for finding and loading driver plugins.
Provides database connection, allowing queries and data modification.
Definition: KDbConnection.h:51
QString name() const
Definition: KDbField.cpp:256
@ ShortInteger
Definition: KDbField.h:89
char * data()
KDbDriver * driver(const QString &id)
void sort(Qt::CaseSensitivity cs)
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Sat Jun 25 2022 06:21:40 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.