Merge pull request #4676 from jmarshallnz/dont_set_scraper_on_tvshow_on_nfo
[vuplus_xbmc] / xbmc / dbwrappers / sqlitedataset.cpp
1 /**********************************************************************
2  * Copyright (c) 2004, Leo Seib, Hannover
3  *
4  * Project:SQLiteDataset C++ Dynamic Library
5  * Module: SQLiteDataset class realisation file
6  * Author: Leo Seib      E-Mail: leoseib@web.de 
7  * Begin: 5/04/2002
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a copy
10  * of this software and associated documentation files (the "Software"), to deal
11  * in the Software without restriction, including without limitation the rights
12  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13  * copies of the Software, and to permit persons to whom the Software is
14  * furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included in
17  * all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
22  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25  * THE SOFTWARE.
26  *
27  **********************************************************************/
28
29 #include <iostream>
30 #include <string>
31
32 #include "sqlitedataset.h"
33 #include "utils/log.h"
34 #include "system.h" // for Sleep(), OutputDebugString() and GetLastError()
35 #include "utils/URIUtils.h"
36
37 #ifdef TARGET_WINDOWS
38 #pragma comment(lib, "sqlite3.lib")
39 #endif
40
41 using namespace std;
42
43 namespace dbiplus {
44 //************* Callback function ***************************
45
46 int callback(void* res_ptr,int ncol, char** reslt,char** cols)
47 {
48   result_set* r = (result_set*)res_ptr;
49
50   if (!r->record_header.size())
51   {
52     r->record_header.reserve(ncol);
53     for (int i=0; i < ncol; i++) {
54       field_prop header;
55       header.name = cols[i];
56       r->record_header.push_back(header);
57     }
58   }
59
60   if (reslt != NULL)
61   {
62     sql_record *rec = new sql_record;
63     rec->resize(ncol);
64     for (int i=0; i<ncol; i++)
65     { 
66       field_value &v = rec->at(i);
67       if (reslt[i] == NULL)
68       {
69         v.set_asString("");
70         v.set_isNull();
71       }
72       else
73       {
74         v.set_asString(reslt[i]);
75       }
76     }
77     r->records.push_back(rec);
78   }
79   return 0;  
80 }
81
82 static int busy_callback(void*, int busyCount)
83 {
84   Sleep(100);
85   OutputDebugString("SQLite collision\n");
86   return 1;
87 }
88
89 //************* SqliteDatabase implementation ***************
90
91 SqliteDatabase::SqliteDatabase() {
92
93   active = false;  
94   _in_transaction = false;    // for transaction
95
96   error = "Unknown database error";//S_NO_CONNECTION;
97   host = "localhost";
98   port = "";
99   db = "sqlite.db";
100   login = "root";
101   passwd = "";
102 }
103
104 SqliteDatabase::~SqliteDatabase() {
105   disconnect();
106 }
107
108
109 Dataset* SqliteDatabase::CreateDataset() const {
110   return new SqliteDataset((SqliteDatabase*)this); 
111 }
112
113 void SqliteDatabase::setHostName(const char *newHost) {
114   host = newHost;
115
116   // hostname is the relative folder to the database, ensure it's slash terminated
117   if (host[host.length()-1] != '/' && host[host.length()-1] != '\\')
118     host += "/";
119
120   // ensure the fully qualified path has slashes in the correct direction
121   if ( (host[1] == ':') && isalpha(host[0]))
122   {
123     size_t pos = 0;
124     while ( (pos = host.find("/", pos)) != string::npos )
125       host.replace(pos++, 1, "\\");
126   }
127   else
128   {
129     size_t pos = 0;
130     while ( (pos = host.find("\\", pos)) != string::npos )
131       host.replace(pos++, 1, "/");
132   }
133 }
134
135 void SqliteDatabase::setDatabase(const char *newDb) {
136   db = newDb;
137
138   // db is the filename for the database, ensure it's not slash prefixed
139   if (newDb[0] == '/' || newDb[0] == '\\')
140     db = db.substr(1);
141
142   // ensure the ".db" extension is appended to the end
143   if ( db.find(".db") != (db.length()-3) )
144     db += ".db";
145 }
146
147 int SqliteDatabase::status(void) {
148   if (active == false) return DB_CONNECTION_NONE;
149   return DB_CONNECTION_OK;
150 }
151
152 int SqliteDatabase::setErr(int err_code, const char * qry){
153   switch (err_code) {
154   case SQLITE_OK: error ="Successful result";
155     break;
156   case SQLITE_ERROR: error = "SQL error or missing database";
157     break;
158   case SQLITE_INTERNAL: error = "An internal logic error in SQLite";
159     break;
160   case SQLITE_PERM: error ="Access permission denied";
161     break;
162   case SQLITE_ABORT: error = "Callback routine requested an abort";
163     break;
164   case SQLITE_BUSY: error = "The database file is locked";
165     break;
166   case SQLITE_LOCKED: error = "A table in the database is locked";
167     break;
168   case SQLITE_NOMEM: error = "A malloc() failed";
169     break;
170   case SQLITE_READONLY: error = "Attempt to write a readonly database";
171     break;
172   case SQLITE_INTERRUPT: error = "Operation terminated by sqlite_interrupt()";
173     break;
174   case  SQLITE_IOERR: error = "Some kind of disk I/O error occurred";
175     break;
176   case  SQLITE_CORRUPT: error = "The database disk image is malformed";
177     break;
178   case SQLITE_NOTFOUND: error = "(Internal Only) Table or record not found";
179     break;
180   case SQLITE_FULL: error = "Insertion failed because database is full";
181     break;
182   case SQLITE_CANTOPEN: error = "Unable to open the database file";
183     break;
184   case SQLITE_PROTOCOL: error = "Database lock protocol error";
185     break;
186   case SQLITE_EMPTY:  error = "(Internal Only) Database table is empty";
187     break;
188   case SQLITE_SCHEMA: error = "The database schema changed";
189     break;
190   case SQLITE_TOOBIG: error = "Too much data for one row of a table";
191     break;
192   case SQLITE_CONSTRAINT: error = "Abort due to constraint violation";
193     break;
194   case SQLITE_MISMATCH:  error = "Data type mismatch";
195     break;
196   default : error = "Undefined SQLite error";
197   }
198   error += "\nQuery: ";
199   error += qry;
200   error += "\n";
201   return err_code;
202 }
203
204 const char *SqliteDatabase::getErrorMsg() {
205    return error.c_str();
206 }
207
208 int SqliteDatabase::connect(bool create) {
209   if (host.empty() || db.empty())
210     return DB_CONNECTION_NONE;
211
212   //CLog::Log(LOGDEBUG, "Connecting to sqlite:%s:%s", host.c_str(), db.c_str());
213
214   CStdString db_fullpath = URIUtils::AddFileToFolder(host, db);
215
216   try
217   {
218     disconnect();
219     int flags = SQLITE_OPEN_READWRITE;
220     if (create)
221       flags |= SQLITE_OPEN_CREATE;
222     if (sqlite3_open_v2(db_fullpath.c_str(), &conn, flags, NULL)==SQLITE_OK)
223     {
224       sqlite3_busy_handler(conn, busy_callback, NULL);
225       char* err=NULL;
226       if (setErr(sqlite3_exec(getHandle(),"PRAGMA empty_result_callbacks=ON",NULL,NULL,&err),"PRAGMA empty_result_callbacks=ON") != SQLITE_OK)
227       {
228         throw DbErrors(getErrorMsg());
229       }
230       active = true;
231       return DB_CONNECTION_OK;
232     }
233
234     return DB_CONNECTION_NONE;
235   }
236   catch(...)
237   {
238   }
239   return DB_CONNECTION_NONE;
240 }
241
242 bool SqliteDatabase::exists(void)
243 {
244   bool bRet = false;
245   if (!active) return bRet;
246   result_set res;
247   char sqlcmd[512];
248
249   // performing a select all on the sqlite_master will return rows if there are tables
250   // defined indicating it's not empty and therefore must "exist".
251   sprintf(sqlcmd,"SELECT * FROM sqlite_master");
252   if ((last_err = sqlite3_exec(getHandle(),sqlcmd, &callback, &res,NULL)) == SQLITE_OK)
253   {
254     bRet = (res.records.size() > 0);
255   }
256
257   return bRet;
258 }
259
260 void SqliteDatabase::disconnect(void) {
261   if (active == false) return;
262   sqlite3_close(conn);
263   active = false;
264 }
265
266 int SqliteDatabase::create() {
267   return connect(true);
268 }
269
270 int SqliteDatabase::copy(const char *backup_name) {
271   if (active == false)
272     throw DbErrors("Can't copy database: no active connection...");
273
274   CLog::Log(LOGDEBUG, "Copying from %s to %s at %s", db.c_str(), backup_name, host.c_str());
275
276   int rc;
277   string backup_db = backup_name;
278
279   sqlite3 *pFile;           /* Database connection opened on zFilename */
280   sqlite3_backup *pBackup;  /* Backup object used to copy data */
281
282   //
283   if (backup_name[0] == '/' || backup_name[0] == '\\')
284     backup_db = backup_db.substr(1);
285
286   // ensure the ".db" extension is appended to the end
287   if ( backup_db.find(".db") != (backup_db.length()-3) )
288     backup_db += ".db";
289
290   string backup_path = host + backup_db;
291
292   /* Open the database file identified by zFilename. Exit early if this fails
293   ** for any reason. */
294   rc = sqlite3_open(backup_path.c_str(), &pFile);
295   if( rc==SQLITE_OK )
296   {
297     pBackup = sqlite3_backup_init(pFile, "main", getHandle(), "main");
298
299     if( pBackup )
300     {
301       (void)sqlite3_backup_step(pBackup, -1);
302       (void)sqlite3_backup_finish(pBackup);
303     }
304
305     rc = sqlite3_errcode(pFile);
306   }
307
308   (void)sqlite3_close(pFile);
309
310   if( rc != SQLITE_OK )
311     throw DbErrors("Can't copy database. (%d)", rc);
312
313   return rc;
314 }
315
316 int SqliteDatabase::drop_analytics(void) {
317   // SqliteDatabase::copy used a full database copy, so we have a new version
318   // with all the analytics stuff. We should clean database from everything but data
319   if (active == false)
320     throw DbErrors("Can't drop extras database: no active connection...");
321
322   char sqlcmd[4096];
323   result_set res;
324
325   CLog::Log(LOGDEBUG, "Cleaning indexes from database %s at %s", db.c_str(), host.c_str());
326   sprintf(sqlcmd, "SELECT name FROM sqlite_master WHERE type == 'index'");
327   if ((last_err = sqlite3_exec(conn, sqlcmd, &callback, &res, NULL)) != SQLITE_OK) return DB_UNEXPECTED_RESULT;
328
329   for (size_t i=0; i < res.records.size(); i++) {
330     sprintf(sqlcmd,"DROP INDEX '%s'", res.records[i]->at(0).get_asString().c_str());
331     if ((last_err = sqlite3_exec(conn, sqlcmd, NULL, NULL, NULL) != SQLITE_OK)) return DB_UNEXPECTED_RESULT;
332   }
333   res.clear();
334
335   CLog::Log(LOGDEBUG, "Cleaning views from database %s at %s", db.c_str(), host.c_str());
336   sprintf(sqlcmd, "SELECT name FROM sqlite_master WHERE type == 'view'");
337   if ((last_err = sqlite3_exec(conn, sqlcmd, &callback, &res, NULL)) != SQLITE_OK) return DB_UNEXPECTED_RESULT;
338
339   for (size_t i=0; i < res.records.size(); i++) {
340     sprintf(sqlcmd,"DROP VIEW '%s'", res.records[i]->at(0).get_asString().c_str());
341     if ((last_err = sqlite3_exec(conn, sqlcmd, NULL, NULL, NULL) != SQLITE_OK)) return DB_UNEXPECTED_RESULT;
342   }
343   res.clear();
344
345   CLog::Log(LOGDEBUG, "Cleaning triggers from database %s at %s", db.c_str(), host.c_str());
346   sprintf(sqlcmd, "SELECT name FROM sqlite_master WHERE type == 'trigger'");
347   if ((last_err = sqlite3_exec(conn, sqlcmd, &callback, &res, NULL)) != SQLITE_OK) return DB_UNEXPECTED_RESULT;
348
349   for (size_t i=0; i < res.records.size(); i++) {
350     sprintf(sqlcmd,"DROP TRIGGER '%s'", res.records[i]->at(0).get_asString().c_str());
351     if ((last_err = sqlite3_exec(conn, sqlcmd, NULL, NULL, NULL) != SQLITE_OK)) return DB_UNEXPECTED_RESULT;
352   }
353   // res would be cleared on destruct
354
355   return DB_COMMAND_OK;
356 }
357
358 int SqliteDatabase::drop() {
359   if (active == false) throw DbErrors("Can't drop database: no active connection...");
360   disconnect();
361   if (!unlink(db.c_str())) {
362      throw DbErrors("Can't drop database: can't unlink the file %s,\nError: %s",db.c_str(),strerror(errno));
363      }
364   return DB_COMMAND_OK;
365 }
366
367
368 long SqliteDatabase::nextid(const char* sname) {
369   if (!active) return DB_UNEXPECTED_RESULT;
370   int id;/*,nrow,ncol;*/
371   result_set res;
372   char sqlcmd[512];
373   sprintf(sqlcmd,"select nextid from %s where seq_name = '%s'",sequence_table.c_str(), sname);
374   if ((last_err = sqlite3_exec(getHandle(),sqlcmd,&callback,&res,NULL)) != SQLITE_OK) {
375     return DB_UNEXPECTED_RESULT;
376     }
377   if (res.records.size() == 0) {
378     id = 1;
379     sprintf(sqlcmd,"insert into %s (nextid,seq_name) values (%d,'%s')",sequence_table.c_str(),id,sname);
380     if ((last_err = sqlite3_exec(conn,sqlcmd,NULL,NULL,NULL)) != SQLITE_OK) return DB_UNEXPECTED_RESULT;
381     return id;
382   }
383   else {
384     id = res.records[0]->at(0).get_asInt()+1;
385     sprintf(sqlcmd,"update %s set nextid=%d where seq_name = '%s'",sequence_table.c_str(),id,sname);
386     if ((last_err = sqlite3_exec(conn,sqlcmd,NULL,NULL,NULL) != SQLITE_OK)) return DB_UNEXPECTED_RESULT;
387     return id;
388   }
389   return DB_UNEXPECTED_RESULT;
390 }
391
392
393 // methods for transactions
394 // ---------------------------------------------
395 void SqliteDatabase::start_transaction() {
396   if (active) {
397     sqlite3_exec(conn,"begin IMMEDIATE",NULL,NULL,NULL);
398     _in_transaction = true;
399   }
400 }
401
402 void SqliteDatabase::commit_transaction() {
403   if (active) {
404     sqlite3_exec(conn,"commit",NULL,NULL,NULL);
405     _in_transaction = false;
406   }
407 }
408
409 void SqliteDatabase::rollback_transaction() {
410   if (active) {
411     sqlite3_exec(conn,"rollback",NULL,NULL,NULL);
412     _in_transaction = false;
413   }  
414 }
415
416
417 // methods for formatting
418 // ---------------------------------------------
419 string SqliteDatabase::vprepare(const char *format, va_list args)
420 {
421   string strFormat = format;
422   string strResult = "";
423   char *p;
424   size_t pos;
425
426   //  %q is the sqlite format string for %s.
427   //  Any bad character, like "'", will be replaced with a proper one
428   pos = 0;
429   while ( (pos = strFormat.find("%s", pos)) != string::npos )
430     strFormat.replace(pos++, 2, "%q");
431
432   //  the %I64 enhancement is not supported by sqlite3_vmprintf
433   //  must be %ll instead
434   pos = 0;
435   while ( (pos = strFormat.find("%I64", pos)) != string::npos )
436     strFormat.replace(pos++, 4, "%ll");
437
438   p = sqlite3_vmprintf(strFormat.c_str(), args);
439   if ( p )
440   {
441     strResult = p;
442     sqlite3_free(p);
443   }
444
445   return strResult;
446 }
447
448
449 //************* SqliteDataset implementation ***************
450
451 SqliteDataset::SqliteDataset():Dataset() {
452   haveError = false;
453   db = NULL;
454   errmsg = NULL;
455   autorefresh = false;
456 }
457
458
459 SqliteDataset::SqliteDataset(SqliteDatabase *newDb):Dataset(newDb) {
460   haveError = false;
461   db = newDb;
462   errmsg = NULL;
463   autorefresh = false;
464 }
465
466  SqliteDataset::~SqliteDataset(){
467    if (errmsg) sqlite3_free(errmsg);
468  }
469
470
471 void SqliteDataset::set_autorefresh(bool val){
472     autorefresh = val;
473 }
474
475
476
477 //--------- protected functions implementation -----------------//
478
479 sqlite3* SqliteDataset::handle(){
480   if (db != NULL){
481     return static_cast<SqliteDatabase*>(db)->getHandle();
482       }
483   else return NULL;
484 }
485
486 void SqliteDataset::make_query(StringList &_sql) {
487   string query;
488   if (db == NULL) throw DbErrors("No Database Connection");
489
490  try {
491
492   if (autocommit) db->start_transaction();
493
494
495   for (list<string>::iterator i =_sql.begin(); i!=_sql.end(); i++) {
496   query = *i;
497   char* err=NULL; 
498   Dataset::parse_sql(query);
499   if (db->setErr(sqlite3_exec(this->handle(),query.c_str(),NULL,NULL,&err),query.c_str())!=SQLITE_OK) {
500     throw DbErrors(db->getErrorMsg());
501   }
502   } // end of for
503
504
505   if (db->in_transaction() && autocommit) db->commit_transaction();
506
507   active = true;
508   ds_state = dsSelect;    
509   if (autorefresh)
510     refresh();
511
512  } // end of try
513  catch(...) {
514   if (db->in_transaction()) db->rollback_transaction();
515   throw;
516  }
517
518 }
519
520
521 void SqliteDataset::make_insert() {
522   make_query(insert_sql);
523   last();
524 }
525
526
527 void SqliteDataset::make_edit() {
528   make_query(update_sql);
529 }
530
531
532 void SqliteDataset::make_deletion() {
533   make_query(delete_sql);
534 }
535
536
537 void SqliteDataset::fill_fields() {
538   //cout <<"rr "<<result.records.size()<<"|" << frecno <<"\n";
539   if ((db == NULL) || (result.record_header.size() == 0) || (result.records.size() < (unsigned int)frecno)) return;
540
541   if (fields_object->size() == 0) // Filling columns name
542   {
543     const unsigned int ncols = result.record_header.size();
544     fields_object->resize(ncols);
545     for (unsigned int i = 0; i < ncols; i++)
546       (*fields_object)[i].props = result.record_header[i];
547   }
548
549   //Filling result
550   if (result.records.size() != 0)
551   {
552     const sql_record *row = result.records[frecno];
553     if (row)
554     {
555       const unsigned int ncols = row->size();
556       fields_object->resize(ncols);
557       for (unsigned int i = 0; i < ncols; i++)
558         (*fields_object)[i].val = row->at(i);
559       return;
560     }
561   }
562   const unsigned int ncols = result.record_header.size();
563   fields_object->resize(ncols);
564   for (unsigned int i = 0; i < ncols; i++)
565     (*fields_object)[i].val = "";
566 }
567
568
569 //------------- public functions implementation -----------------//
570 bool SqliteDataset::dropIndex(const char *table, const char *index)
571 {
572   string sql;
573
574   sql = static_cast<SqliteDatabase*>(db)->prepare("DROP INDEX IF EXISTS %s", index);
575
576   return (exec(sql) == SQLITE_OK);
577 }
578
579
580 int SqliteDataset::exec(const string &sql) {
581   if (!handle()) throw DbErrors("No Database Connection");
582   string qry = sql;
583   int res;
584   exec_res.clear();
585
586   // Strip size constraints from indexes (not supported in sqlite)
587   //
588   // Example:
589   //   before: CREATE UNIQUE INDEX ixPath ON path ( strPath(255) )
590   //   after:  CREATE UNIQUE INDEX ixPath ON path ( strPath )
591   //
592   // NOTE: unexpected results occur if brackets are not matched
593   if ( qry.find("CREATE UNIQUE INDEX") != string::npos ||
594       (qry.find("CREATE INDEX") != string::npos))
595   {
596     size_t pos = 0;
597     size_t pos2 = 0;
598
599     if ( (pos = qry.find("(")) != string::npos )
600     {
601       pos++;
602       while ( (pos = qry.find("(", pos)) != string::npos )
603       {
604         if ( (pos2 = qry.find(")", pos)) != string::npos )
605         {
606           qry.replace(pos, pos2-pos+1, "");
607           pos = pos2;
608         }
609       }
610     }
611   }
612   // Strip ON table from DROP INDEX statements:
613   // before: DROP INDEX foo ON table
614   // after:  DROP INDEX foo
615   size_t pos = qry.find("DROP INDEX ");
616   if ( pos != string::npos )
617   {
618     pos = qry.find(" ON ", pos+1);
619
620     if ( pos != string::npos )
621       qry = qry.substr(0, pos);
622   }
623
624   if((res = db->setErr(sqlite3_exec(handle(),qry.c_str(),&callback,&exec_res,&errmsg),qry.c_str())) == SQLITE_OK)
625     return res;
626   else
627     {
628       throw DbErrors(db->getErrorMsg());
629     }
630 }
631
632 int SqliteDataset::exec() {
633   return exec(sql);
634 }
635
636 const void* SqliteDataset::getExecRes() {
637   return &exec_res;
638 }
639
640
641 bool SqliteDataset::query(const char *query) {
642     if(!handle()) throw DbErrors("No Database Connection");
643     std::string qry = query;
644     int fs = qry.find("select");
645     int fS = qry.find("SELECT");
646     if (!( fs >= 0 || fS >=0))                                 
647          throw DbErrors("MUST be select SQL!"); 
648
649   close();
650
651   sqlite3_stmt *stmt = NULL;
652   if (db->setErr(sqlite3_prepare_v2(handle(),query,-1,&stmt, NULL),query) != SQLITE_OK)
653     throw DbErrors(db->getErrorMsg());
654
655   // column headers
656   const unsigned int numColumns = sqlite3_column_count(stmt);
657   result.record_header.resize(numColumns);
658   for (unsigned int i = 0; i < numColumns; i++)
659     result.record_header[i].name = sqlite3_column_name(stmt, i);
660
661   // returned rows
662   while (sqlite3_step(stmt) == SQLITE_ROW)
663   { // have a row of data
664     sql_record *res = new sql_record;
665     res->resize(numColumns);
666     for (unsigned int i = 0; i < numColumns; i++)
667     {
668       field_value &v = res->at(i);
669       switch (sqlite3_column_type(stmt, i))
670       {
671       case SQLITE_INTEGER:
672         v.set_asInt64(sqlite3_column_int64(stmt, i));
673         break;
674       case SQLITE_FLOAT:
675         v.set_asDouble(sqlite3_column_double(stmt, i));
676         break;
677       case SQLITE_TEXT:
678         v.set_asString((const char *)sqlite3_column_text(stmt, i));
679         break;
680       case SQLITE_BLOB:
681         v.set_asString((const char *)sqlite3_column_text(stmt, i));
682         break;
683       case SQLITE_NULL:
684       default:
685         v.set_asString("");
686         v.set_isNull();
687         break;
688       }
689     }
690     result.records.push_back(res);
691   }
692   if (db->setErr(sqlite3_finalize(stmt),query) == SQLITE_OK)
693   {
694     active = true;
695     ds_state = dsSelect;
696     this->first();
697     return true;
698   }
699   else
700   {
701     throw DbErrors(db->getErrorMsg());
702   }  
703 }
704
705 bool SqliteDataset::query(const string &q){
706   return query(q.c_str());
707 }
708
709 void SqliteDataset::open(const string &sql) {
710   set_select_sql(sql);
711   open();
712 }
713
714 void SqliteDataset::open() {
715   if (select_sql.size()) {
716     query(select_sql.c_str()); 
717   }
718   else {
719     ds_state = dsInactive;
720   }
721 }
722
723
724 void SqliteDataset::close() {
725   Dataset::close();
726   result.clear();
727   edit_object->clear();
728   fields_object->clear();
729   ds_state = dsInactive;
730   active = false;
731 }
732
733
734 void SqliteDataset::cancel() {
735   if ((ds_state == dsInsert) || (ds_state==dsEdit)) {
736     if (result.record_header.size())
737       ds_state = dsSelect;
738     else
739       ds_state = dsInactive;
740   }
741 }
742
743
744 int SqliteDataset::num_rows() {
745   return result.records.size();
746 }
747
748
749 bool SqliteDataset::eof() {
750   return feof;
751 }
752
753
754 bool SqliteDataset::bof() {
755   return fbof;
756 }
757
758
759 void SqliteDataset::first() {
760   Dataset::first();
761   this->fill_fields();
762 }
763
764 void SqliteDataset::last() {
765   Dataset::last();
766   fill_fields();
767 }
768
769 void SqliteDataset::prev(void) {
770   Dataset::prev();
771   fill_fields();
772 }
773
774 void SqliteDataset::next(void) {
775   Dataset::next();
776   if (!eof()) 
777       fill_fields();
778 }
779
780 void SqliteDataset::free_row(void)
781 {
782   if (frecno < 0 || (unsigned int)frecno >= result.records.size())
783     return;
784
785   sql_record *row = result.records[frecno];
786   if (row)
787   {
788     delete row;
789     result.records[frecno] = NULL;
790   }
791 }
792
793 bool SqliteDataset::seek(int pos) {
794   if (ds_state == dsSelect) {
795     Dataset::seek(pos);
796     fill_fields();
797     return true;  
798     }
799   return false;
800 }
801
802 int64_t SqliteDataset::lastinsertid()
803 {
804   if(!handle()) throw DbErrors("No Database Connection");
805   return sqlite3_last_insert_rowid(handle());
806 }
807
808
809 long SqliteDataset::nextid(const char *seq_name) {
810   if (handle()) return db->nextid(seq_name);
811   else return DB_UNEXPECTED_RESULT;
812 }
813
814 void SqliteDataset::interrupt() {
815   sqlite3_interrupt(handle());
816 }
817 }//namespace