COOPY » Guide  version 0.6.5
/home/paulfitz/cvs/coopy_scm/coopy/src/libsqlite/SqliteSheet.cpp
Go to the documentation of this file.
00001 #include <coopy/Dbg.h>
00002 #include <coopy/SqliteSheet.h>
00003 #include <coopy/Stringer.h>
00004 #include <sqlite3.h>
00005 #include <coopy/SqlCompare.h>
00006 
00007 using namespace coopy::store;
00008 using namespace coopy::store::sqlite;
00009 using namespace coopy::cmp;
00010 using namespace std;
00011 
00012 namespace coopy {
00013   namespace store {
00014     class SqliteSchema;
00015     //class SqlSheetRow;
00016   }
00017 }
00018 
00019 static const char *sql_keywords[] = {
00020 "ABORT",
00021 "ACTION",
00022 "ADD",
00023 "AFTER",
00024 "ALL",
00025 "ALTER",
00026 "ANALYZE",
00027 "AND",
00028 "AS",
00029 "ASC",
00030 "ATTACH",
00031 "AUTOINCREMENT",
00032 "BEFORE",
00033 "BEGIN",
00034 "BETWEEN",
00035 "BY",
00036 "CASCADE",
00037 "CASE",
00038 "CAST",
00039 "CHECK",
00040 "COLLATE",
00041 "COLUMN",
00042 "COMMIT",
00043 "CONFLICT",
00044 "CONSTRAINT",
00045 "CREATE",
00046 "CROSS",
00047 "CURRENT_DATE",
00048 "CURRENT_TIME",
00049 "CURRENT_TIMESTAMP",
00050 "DATABASE",
00051 "DEFAULT",
00052 "DEFERRABLE",
00053 "DEFERRED",
00054 "DELETE",
00055 "DESC",
00056 "DETACH",
00057 "DISTINCT",
00058 "DROP",
00059 "EACH",
00060 "ELSE",
00061 "END",
00062 "ESCAPE",
00063 "EXCEPT",
00064 "EXCLUSIVE",
00065 "EXISTS",
00066 "EXPLAIN",
00067 "FAIL",
00068 "FOR",
00069 "FOREIGN",
00070 "FROM",
00071 "FULL",
00072 "GLOB",
00073 "GROUP",
00074 "HAVING",
00075 "IF",
00076 "IGNORE",
00077 "IMMEDIATE",
00078 "IN",
00079 "INDEX",
00080 "INDEXED",
00081 "INITIALLY",
00082 "INNER",
00083 "INSERT",
00084 "INSTEAD",
00085 "INTERSECT",
00086 "INTO",
00087 "IS",
00088 "ISNULL",
00089 "JOIN",
00090 "KEY",
00091 "LEFT",
00092 "LIKE",
00093 "LIMIT",
00094 "MATCH",
00095 "NATURAL",
00096 "NO",
00097 "NOT",
00098 "NOTNULL",
00099 "NULL",
00100 "OF",
00101 "OFFSET",
00102 "ON",
00103 "OR",
00104 "ORDER",
00105 "OUTER",
00106 "PLAN",
00107 "PRAGMA",
00108 "PRIMARY",
00109 "QUERY",
00110 "RAISE",
00111 "REFERENCES",
00112 "REGEXP",
00113 "REINDEX",
00114 "RELEASE",
00115 "RENAME",
00116 "REPLACE",
00117 "RESTRICT",
00118 "RIGHT",
00119 "ROLLBACK",
00120 "ROW",
00121 "SAVEPOINT",
00122 "SELECT",
00123 "SET",
00124 "TABLE",
00125 "TEMP",
00126 "TEMPORARY",
00127 "THEN",
00128 "TO",
00129 "TRANSACTION",
00130 "TRIGGER",
00131 "UNION",
00132 "UNIQUE",
00133 "UPDATE",
00134 "USING",
00135 "VACUUM",
00136 "VALUES",
00137 "VIEW",
00138 "VIRTUAL",
00139 "WHEN",
00140 "WHERE",
00141 NULL
00142 };
00143 
00144 class coopy::store::SqliteSchema {
00145 public:
00146   string preamble;
00147   vector<string> parts;
00148   sqlite3 *db;
00149   string table_name;
00150 
00151   SqliteSchema() {
00152     db = NULL;
00153   }
00154 
00155   string fetch(sqlite3 *db, const char *table);
00156 
00157   void parse(const char *str);
00158 
00159   string toString() const;
00160 
00161   bool apply(const vector<ColumnInfo>& names, const string& novel = "");
00162 };
00163 
00164 
00165 #define DB(x) ((sqlite3 *)(x))
00166 
00167 SqliteSheet::SqliteSheet(void *db1, const char *name, const char *prefix) {
00168   implementation = db1;
00169   this->name = name;
00170   this->prefix = prefix;
00171   char *query;
00172   if (this->prefix == "") {
00173     query = sqlite3_mprintf("%Q", this->name.c_str());
00174   } else {
00175     query = sqlite3_mprintf("%Q.%Q", this->prefix.c_str(),
00176                             this->name.c_str());
00177   }
00178   this->quoted_name = query;
00179   sqlite3_free(query);
00180   prefix_dot = "";
00181   if (this->prefix != "") {
00182     query = sqlite3_mprintf("%Q.", this->prefix.c_str());
00183     prefix_dot = query;
00184     sqlite3_free(query);
00185   }
00186   w = h = 0;
00187 
00188   schema = new SqliteSheetSchema;
00189   COOPY_MEMORY(schema);
00190   schema->sheet = this;
00191 }
00192 
00193 bool SqliteSheet::isReserved(const std::string& name) {
00194   static efficient_map<std::string,int> keywordMap;
00195   if (keywordMap.size()==0) {
00196     const char **k = sql_keywords;
00197     while (*k!=NULL) {
00198       string s = *k;
00199       keywordMap[s] = 1;
00200       k++;
00201     }
00202   }
00203   std::string _name = name;
00204   for (int i=0; i<(int)_name.length(); i++) {
00205     _name[i] = toupper(_name[i]);
00206     char ch = _name[i];
00207     if (ch=='['||ch==']'||ch=='.') {
00208       return true;
00209     }
00210   }
00211   return keywordMap.find(_name)!=keywordMap.end();
00212 }
00213 
00214 std::string SqliteSheet::_quoted(const std::string& x, char ch, bool force) {
00215   return quoteSql(x,ch,force);
00216 }
00217 
00218 std::string SqliteSheet::_quoted_double(const std::string& x) {
00219   return _quoted(x,'\"',isReserved(x));
00220 }
00221 
00222 std::string SqliteSheet::_quoted_single(const std::string& x) {
00223   return _quoted(x,'\'',isReserved(x));
00224 }
00225 
00226 
00227 bool SqliteSheet::connect() {
00228 
00229   dbg_printf("Loading SqliteSheet\n");
00230 
00231   sqlite3 *db = DB(implementation);
00232   if (db==NULL) return false;
00233 
00234   sqlite3_stmt *statement = NULL;
00235   char *query = NULL;
00236   int iresult = 0;
00237 
00238 
00240   // Check column names
00241 
00242   query = sqlite3_mprintf("PRAGMA %stable_info(%Q)",
00243                           this->prefix_dot.c_str(),
00244                           this->name.c_str());
00245 
00246   iresult = sqlite3_prepare_v2(db, query, -1, 
00247                                &statement, NULL);
00248   if (iresult!=SQLITE_OK) {
00249     const char *msg = sqlite3_errmsg(db);
00250     if (msg!=NULL) {
00251       fprintf(stderr,"Error: %s\n", msg);
00252       fprintf(stderr,"Query was: %s\n", query);
00253     }
00254     sqlite3_finalize(statement);
00255     sqlite3_free(query);
00256   } 
00257 
00258   col2sql.clear();
00259   while (sqlite3_step(statement) == SQLITE_ROW) {
00260     const char *col = (const char *)sqlite3_column_text(statement,1);
00261     const char *kind = (const char *)sqlite3_column_text(statement,2);
00262     ColumnInfo info(col);
00263     //printf("col is %s kind is %s\n", col, kind);
00264     info.setType(kind,"sqlite");
00265     col2sql.push_back(info);
00266   }
00267   sqlite3_finalize(statement);
00268   sqlite3_free(query);
00269 
00271   // Check dimensions
00272 
00273   query = sqlite3_mprintf("SELECT COUNT(*), * FROM %s",
00274                           quoted_name.c_str());
00275  
00276   iresult = sqlite3_prepare_v2(db, query, -1, 
00277                                    &statement, NULL);
00278   if (iresult!=SQLITE_OK) {
00279     const char *msg = sqlite3_errmsg(db);
00280     if (msg!=NULL) {
00281       fprintf(stderr,"Error: %s\n", msg);
00282       fprintf(stderr,"Query was: %s\n", query);
00283     }
00284     sqlite3_finalize(statement);
00285     sqlite3_free(query);
00286     return false;
00287   } 
00288   if (sqlite3_step(statement) == SQLITE_ROW) {
00289     h = sqlite3_column_int(statement,0);
00290     w = sqlite3_column_count(statement)-1;
00291   }
00292   //printf("Dimensions %dx%d\n", w, h);
00293   sqlite3_finalize(statement);
00294   sqlite3_free(query);
00295 
00297   // Check ROWIDs
00298 
00299   query = sqlite3_mprintf("SELECT ROWID FROM %s ORDER BY ROWID",
00300                           this->quoted_name.c_str());
00301 
00302   iresult = sqlite3_prepare_v2(db, query, -1, 
00303                                &statement, NULL);
00304   if (iresult!=SQLITE_OK) {
00305     const char *msg = sqlite3_errmsg(db);
00306     if (msg!=NULL) {
00307       fprintf(stderr,"Error: %s\n", msg);
00308       fprintf(stderr,"Query was: %s\n", query);
00309     }
00310     sqlite3_finalize(statement);
00311     sqlite3_free(query);
00312     return false;
00313   } 
00314 
00315   while (sqlite3_step(statement) == SQLITE_ROW) {
00316     row2sql.push_back(sqlite3_column_int(statement,0));
00317   }
00318   sqlite3_finalize(statement);
00319   sqlite3_free(query);
00320 
00321 
00322   checkPrimaryKeys();
00323   checkForeignKeys();
00324 
00325   cache.resize(w,h,"");
00326   cacheFlag.resize(w,h,0);
00327 
00328   pending_load = true;
00329 
00330   dbg_printf("Preloaded SqliteSheet\n");
00331 
00332   return true;
00333 }
00334 
00335 std::string SqliteSheet::getRawHash() const {
00336   char *query = NULL;
00337   int iresult = 0;
00338   sqlite3 *db = DB(implementation);
00339   sqlite3_stmt *statement = NULL;
00340 
00341   string column_list = "";
00342   for (int i=0; i<(int)col2sql.size(); i++) {
00343     string base = col2sql[i].getName().c_str();
00344     string add = base;
00345     if (add.find('\"')!=string::npos) {
00346       printf("Quoting of sql column names not done yet\n");
00347       exit(1);
00348     }
00349     add = string("\"") + add + "\"";
00350     if (i>0) {
00351       column_list += ',';
00352     }
00353     column_list += add;
00354   }
00355 
00356   sqlite3_exec(db, "SELECT coopy_set(0)", NULL, NULL, NULL);
00357   query = sqlite3_mprintf("SELECT coopy_add(%s) FROM %s ORDER BY ROWID",
00358                           column_list.c_str(),
00359                           quoted_name.c_str());
00360   iresult = sqlite3_exec(db, query, NULL, NULL, NULL);
00361   if (iresult!=SQLITE_OK) {
00362     const char *msg = sqlite3_errmsg(db);
00363     if (msg!=NULL) {
00364       fprintf(stderr,"Error: %s\n", msg);
00365       fprintf(stderr,"Query was: %s\n", query);
00366     }
00367     sqlite3_free(query);
00368     return "";
00369   } 
00370   sqlite3_free(query);
00371 
00372   query = sqlite3_mprintf("SELECT coopy_set(1)"); 
00373   iresult = sqlite3_prepare_v2(db, query, -1, 
00374                                &statement, NULL);
00375   if (iresult!=SQLITE_OK) {
00376     const char *msg = sqlite3_errmsg(db);
00377     if (msg!=NULL) {
00378       fprintf(stderr,"Error: %s\n", msg);
00379       fprintf(stderr,"Query was: %s\n", query);
00380     }
00381     sqlite3_finalize(statement);
00382     sqlite3_free(query);
00383     return "";
00384   } 
00385   string key;
00386   if (sqlite3_step(statement) == SQLITE_ROW) {
00387     key = (char *)sqlite3_column_text(statement,0);
00388     //printf("HASH IS %s\n", key.c_str());
00389   }
00390   sqlite3_finalize(statement);
00391   sqlite3_free(query);
00392   return key;
00393 }
00394 
00395 
00396 void SqliteSheet::check() {
00397   if (!pending_load) return;
00398   pending_load = false;
00399 
00400   char *query = NULL;
00401   int iresult = 0;
00402   sqlite3 *db = DB(implementation);
00403   sqlite3_stmt *statement = NULL;
00404   {
00405     query = sqlite3_mprintf("SELECT * FROM %s ORDER BY ROWID",
00406                             this->quoted_name.c_str());
00407     
00408     iresult = sqlite3_prepare_v2(db, query, -1, 
00409                                  &statement, NULL);
00410     if (iresult!=SQLITE_OK) {
00411       const char *msg = sqlite3_errmsg(db);
00412       if (msg!=NULL) {
00413         fprintf(stderr,"Error: %s\n", msg);
00414         fprintf(stderr,"Query was: %s\n", query);
00415       }
00416       sqlite3_finalize(statement);
00417       sqlite3_free(query);
00418       return;
00419     } 
00420 
00421     int yy = 0;
00422     while (sqlite3_step(statement) == SQLITE_ROW) {
00423       for (int xx=0; xx<w; xx++) {
00424         const unsigned char *r = sqlite3_column_text(statement,xx);
00425         if (r==NULL) {
00426           cacheFlag.cell(xx,yy) = 1;
00427         } else {
00428           //printf("[%s]\n", r);
00429           cache.cell(xx,yy) = (char *)r;
00430         }
00431       }
00432       yy++;
00433     }
00434     sqlite3_finalize(statement);
00435     sqlite3_free(query);
00436   }
00437 
00438   dbg_printf("Loaded SqliteSheet\n");
00439 }
00440 
00441 bool SqliteSheet::create(const SheetSchema& schema) {
00442   sqlite3 *db = DB(implementation);
00443   if (db==NULL) return false;
00444 
00445   name = schema.getSheetName();
00446   dbg_printf("sqlitesheet::create %s\n", name.c_str());
00447 
00448   sqlite3_stmt *statement = NULL;
00449   char *query = NULL;
00450 
00451   string cols = "";
00452   vector<string> keys;
00453   //if (schema.providesPrimaryKeys()) {
00454   for (int i=0; i<schema.getColumnCount(); i++) {
00455     ColumnInfo ci = schema.getColumnInfo(i);
00456     if (ci.isPrimaryKey()) {
00457       keys.push_back(ci.getName());
00458     }
00459   }
00460   //}
00461 
00462   for (int i=0; i<schema.getColumnCount(); i++) {
00463     ColumnInfo ci = schema.getColumnInfo(i);
00464     string cname = ci.getName();
00465     if (cname=="" || cname=="*") {
00466       fprintf(stderr,"Invalid/unknown column name\n");
00467       return false;
00468     }
00469     if (cols!="") {
00470       cols += ',';
00471     }
00472     cols += _quoted_single(cname);
00473     if (ci.hasType()) {
00474       string t = ci.getColumnType().asSqlite(keys.size()<=1);
00475       dbg_printf("TYPE %s (%s)\n", t.c_str(), ci.getColumnType().src_name.c_str());
00476       if (t!="") {
00477         cols += " ";
00478         cols += t;
00479       }
00480     }
00481   }
00482   if (keys.size()>1) {
00483     cols += ", PRIMARY KEY(";
00484     for (int i=0; i<(int)keys.size(); i++) {
00485       if (i>0) {
00486         cols += ", ";
00487       }
00488       cols += _quoted_single(keys[i]);
00489     }
00490     cols += ")";
00491   }
00492   for (int i=0; i<schema.getColumnCount(); i++) {
00493     ColumnInfo ci = schema.getColumnInfo(i);
00494     ColumnType ct = ci.getColumnType();
00495     if (ct.foreignKeySet) {
00496       //printf("Should add foreign key\n");
00497       cols += ", FOREIGN KEY(";
00498       cols += _quoted_single(ci.getName());
00499       cols += ") REFERENCES ";      
00500       cols += _quoted_single(ct.foreignTable);
00501       cols += "(";
00502       cols += _quoted_single(ct.foreignKey);
00503       cols += ")";
00504     }
00505   }
00506 
00507 
00508   dbg_printf("Creating table: %s\n", cols.c_str());
00509   query = sqlite3_mprintf("CREATE TABLE %s (%s)", 
00510                           _quoted_single(name).c_str(),
00511                           cols.c_str());
00512 
00513   int iresult = sqlite3_exec(db, query, NULL, NULL, NULL);
00514   if (iresult!=SQLITE_OK) {
00515     const char *msg = sqlite3_errmsg(db);
00516     if (msg!=NULL) {
00517       fprintf(stderr,"Error: %s\n", msg);
00518       fprintf(stderr,"Query was: %s\n", query);
00519     }
00520     sqlite3_free(query);
00521     return false;
00522   }
00523   sqlite3_free(query);
00524 
00525   return connect();
00526 }
00527 
00528 
00529 SqliteSheet::~SqliteSheet() {
00530   if (schema!=NULL) delete schema;
00531   schema = NULL;
00532 }
00533 
00534 SheetSchema *SqliteSheet::getSchema() const {
00535   return schema;
00536 }
00537 
00538 
00539 std::string SqliteSheet::cellString(int x, int y) const {
00540   bool tmp;
00541   return cellString(x,y,tmp);
00542 }
00543 
00544 std::string SqliteSheet::cellString(int x, int y, bool& escaped) const {
00545   ((SqliteSheet *)this)->check();
00546   escaped = false;
00547   const unsigned char *f = cacheFlag.pcell_const(x,y);
00548   if (f!=NULL) {
00549     escaped = true;
00550     return "NULL";
00551   }
00552   const string *c = cache.pcell_const(x,y);
00553   if (c!=NULL) {
00554     return *c;
00555   }
00556 
00557   // starting with a COMPLETELY brain-dead implementation
00558 
00559   sqlite3 *db = DB(implementation);
00560   escaped = true;
00561   if (db==NULL) return "NULL";
00562 
00563   sqlite3_stmt *statement = NULL;
00564   char *query = NULL;
00565 
00566   query = sqlite3_mprintf("SELECT * FROM %s WHERE ROWID = %d", 
00567                           quoted_name.c_str(),
00568                           row2sql[y]);
00569 
00570   //printf("LOOKUP %s\n", query);
00571  
00572   int iresult = sqlite3_prepare_v2(db, query, -1, 
00573                                    &statement, NULL);
00574   if (iresult!=SQLITE_OK) {
00575     sqlite3_finalize(statement);
00576     sqlite3_free(query);
00577     return "";
00578   }
00579   string result = "";
00580   if (sqlite3_step(statement) == SQLITE_ROW) {
00581     char *txt = (char *)sqlite3_column_text(statement,x);
00582     //printf("Got field %s\n", txt);
00583     if (txt!=NULL) {
00584       result = txt;
00585       escaped = false;
00586     } else { 
00587       result = "NULL";
00588       escaped = true;
00589     }
00590   }
00591   sqlite3_finalize(statement);
00592   sqlite3_free(query);
00593   
00594   return result;
00595 }
00596 
00597 
00598 bool SqliteSheet::cellString(int x, int y, const std::string& str, bool escaped) {
00599   check();
00600   // starting with a COMPLETELY brain-dead implementation
00601 
00602   cache.cell(x,y) = str;
00603   if (escaped) {
00604     cacheFlag.cell(x,y) = true;
00605   } else {
00606     cacheFlag.remove(x,y);
00607   }
00608 
00609   sqlite3 *db = DB(implementation);
00610   if (db==NULL) return false;
00611 
00612   sqlite3_stmt *statement = NULL;
00613   char *query = NULL;
00614 
00615   const char *tstr = str.c_str();
00616   if (escaped) { 
00617     tstr = NULL;
00618   }
00619   query = sqlite3_mprintf("UPDATE %s SET %Q = %Q WHERE ROWID = %d", 
00620                           quoted_name.c_str(),
00621                           col2sql[x].getName().c_str(),
00622                           tstr,
00623                           row2sql[y]);
00624   //printf(">>> %s\n", query);
00625 
00626   int iresult = sqlite3_exec(db, query, NULL, NULL, NULL);
00627   if (iresult!=SQLITE_OK) {
00628     const char *msg = sqlite3_errmsg(db);
00629     if (msg!=NULL) {
00630       fprintf(stderr,"Error: %s\n", msg);
00631       fprintf(stderr,"Query was: %s\n", query);
00632     }
00633     sqlite3_free(query);
00634     return false;
00635   }
00636   sqlite3_free(query);
00637   
00638   return true;
00639 }
00640 
00641 
00642 ColumnRef SqliteSheet::moveColumn(const ColumnRef& src, 
00643                                     const ColumnRef& base) {
00644   clearCache();
00645   
00646   int src_index = src.getIndex();
00647   int base_index = base.getIndex();
00648 
00649   sqlite3 *db = DB(implementation);
00650   if (db==NULL) return ColumnRef();
00651 
00652   SqliteSchema ischema;
00653   string sql_pre = ischema.fetch(db,name.c_str());
00654   ischema.parse(sql_pre.c_str());
00655   string cache_sql = ischema.parts[src_index];
00656   ColumnInfo cache_info = col2sql[src_index];
00657   string cache_name = cache_info.getName();
00658   ischema.parts.erase(ischema.parts.begin()+src_index);
00659   col2sql.erase(col2sql.begin()+src_index);
00660   if (base_index>src_index) {
00661     base_index--;
00662   }
00663   if (base_index==-1) {
00664     ischema.parts.push_back(cache_sql);
00665     col2sql.push_back(cache_info);
00666   } else {
00667     ischema.parts.insert(ischema.parts.begin()+base_index,cache_sql);
00668     col2sql.insert(col2sql.begin()+base_index,cache_info);
00669   }
00670   
00671   return ischema.apply(col2sql)?ColumnRef(base_index):ColumnRef();
00672 }
00673 
00674 
00675 bool SqliteSheet::deleteColumn(const ColumnRef& column) {
00676   clearCache();
00677 
00678   int deadbeat_index = column.getIndex();
00679 
00680   sqlite3 *db = DB(implementation);
00681   if (db==NULL) return false;
00682 
00683   SqliteSchema ischema;
00684   string sql_pre = ischema.fetch(db,name.c_str());
00685   ischema.parse(sql_pre.c_str());
00686   ischema.parts.erase(ischema.parts.begin()+deadbeat_index);
00687   col2sql.erase(col2sql.begin()+deadbeat_index);
00688   w--;
00689   
00690   return ischema.apply(col2sql);
00691 }
00692 
00693 
00694 ColumnRef SqliteSheet::insertColumn(const ColumnRef& base) {
00695   return insertColumn(base,ColumnInfo());
00696 }
00697 
00698 bool SqliteSheet::modifyColumn(const ColumnRef& base, const ColumnInfo& kind) {
00699   int idx = base.getIndex();
00700   bool ok = insertColumn(base,kind).getIndex()!=-1;
00701   if (!ok) return false;
00702   for (int i=0; i<height(); i++) {
00703     cellSummary(idx,i,cellSummary(idx+1,i));
00704   }
00705   return deleteColumn(ColumnRef(idx+1));
00706 }
00707 
00708 
00709 ColumnRef SqliteSheet::insertColumn(const ColumnRef& base, 
00710                                     const ColumnInfo& kind) {
00711   clearCache();
00712 
00713   // we will need to pass in type hints in future, for better merges.
00714   // also, hint for column name.
00715 
00716   sqlite3 *db = DB(implementation);
00717   if (db==NULL) return ColumnRef();
00718 
00719   int index = base.getIndex();
00720 
00721   string suggest_base = "ins";
00722   if (kind.hasName()) suggest_base = kind.getName();
00723   string suggest = suggest_base;
00724   bool found = false;
00725   for (int i=0; i<(int)col2sql.size(); i++) {
00726     string n = col2sql[i].getName();
00727     if (n.substr(0,suggest_base.length())==suggest_base) {
00728       if (n.length()>=suggest.length()) {
00729         suggest = n;
00730         found = true;
00731       }
00732     }
00733   }
00734   if (found) {
00735     suggest += "_";
00736   }
00737   string col_name = suggest;
00738 
00739   ColumnInfo info = kind;
00740   info.setName(col_name);
00741   
00742   if (index==-1) {
00743     char *query = sqlite3_mprintf("ALTER TABLE %s ADD COLUMN %Q",
00744                                   quoted_name.c_str(), col_name.c_str());
00745 
00746     int iresult = sqlite3_exec(db, query, NULL, NULL, NULL);
00747     if (iresult!=SQLITE_OK) {
00748       const char *msg = sqlite3_errmsg(db);
00749       if (msg!=NULL) {
00750         fprintf(stderr,"Error: %s\n", msg);
00751         fprintf(stderr,"Query was: %s\n", query);
00752       }
00753       sqlite3_free(query);
00754       return ColumnRef();
00755     }
00756     sqlite3_free(query);
00757     col2sql.push_back(info);
00758     w++;
00759     return ColumnRef(w-1);
00760   }
00761 
00762   string col_sql = _quoted_single(suggest);
00763 
00764   SqliteSchema ischema;
00765   string sql_pre = ischema.fetch(db,name.c_str());
00766   ischema.parse(sql_pre.c_str());
00767   if (index<0) {
00768     ischema.parts.push_back(col_sql);
00769     col2sql.push_back(info);
00770     index = w;
00771   } else {
00772     ischema.parts.insert(ischema.parts.begin()+index,col_sql);
00773     col2sql.insert(col2sql.begin()+index,info);
00774   }
00775   if (ischema.apply(col2sql,col_name)) {
00776     w++;
00777     return ColumnRef(index);
00778   }
00779   return ColumnRef();
00780 }
00781 
00782 RowRef SqliteSheet::insertRow(const RowRef& base) {
00783   clearCache();
00784 
00785   // Relies on having default values, to insert "blank row".
00786   // This is suboptimal.
00787 
00788   sqlite3 *db = DB(implementation);
00789   if (db==NULL) return RowRef(-1);
00790   int index = base.getIndex();
00791   
00792   if (index!=-1) {
00793     fprintf(stderr,"*** WARNING: Row insertion order ignored for Sqlite\n");
00794   }
00795 
00796   char *query = sqlite3_mprintf("INSERT INTO %s DEFAULT VALUES",
00797                                 quoted_name.c_str());
00798 
00799   int iresult = sqlite3_exec(db, query, NULL, NULL, NULL);
00800   if (iresult!=SQLITE_OK) {
00801     const char *msg = sqlite3_errmsg(db);
00802     if (msg!=NULL) {
00803       fprintf(stderr,"Error: %s\n", msg);
00804       fprintf(stderr,"Query was: %s\n", query);
00805     }
00806     sqlite3_free(query);
00807     return false;
00808   }
00809   sqlite3_free(query);
00810 
00811   int rid = (int)sqlite3_last_insert_rowid(db);
00812 
00813   // inconsistent ordering
00814   row2sql.push_back(rid);
00815   h++;
00816   return RowRef(h-1);
00817 }
00818 
00819 bool SqliteSheet::deleteRow(const RowRef& src) {
00820   clearCache();
00821 
00822   sqlite3 *db = DB(implementation);
00823   if (db==NULL) return false;
00824 
00825   int index = src.getIndex();
00826   if (index==-1) return false;
00827   int rid = row2sql[index];
00828   char *query = sqlite3_mprintf("DELETE FROM %s WHERE ROWID=%d",
00829                                 quoted_name.c_str(), rid);
00830 
00831   int iresult = sqlite3_exec(db, query, NULL, NULL, NULL);
00832   if (iresult!=SQLITE_OK) {
00833     const char *msg = sqlite3_errmsg(db);
00834     if (msg!=NULL) {
00835       fprintf(stderr,"Error: %s\n", msg);
00836       fprintf(stderr,"Query was: %s\n", query);
00837     }
00838     sqlite3_free(query);
00839     return false;
00840   }
00841   sqlite3_free(query);
00842   row2sql.erase(row2sql.begin()+index);
00843   h--;
00844   return true;
00845 }
00846 
00847 
00848 bool SqliteSheet::applyRowCache(const RowCache& cache, int row,
00849                                 SheetCell *result) {
00850   check();
00851 
00852   sqlite3 *db = DB(implementation);
00853   if (db==NULL) return false;
00854   
00855   if (row!=-1 && row!=height()) {
00856     fprintf(stderr,"*** WARNING: Row insertion order ignored for Sqlite\n");
00857   }
00858 
00859   string cols = "";
00860   string vals = "";
00861   for (int i=0; i<(int)col2sql.size(); i++) {
00862     if (cache.flags[i]) {
00863       string cname = col2sql[i].getName();
00864       if (cname=="" || cname=="*") {
00865         fprintf(stderr,"Invalid/unknown column name\n");
00866         return false;
00867       }
00868       if (cols!="") {
00869         cols += ',';
00870         vals += ',';
00871       }
00872       char *squery = NULL;
00873       squery = sqlite3_mprintf("%Q", cname.c_str());
00874       cols += squery;
00875       sqlite3_free(squery);
00876       squery = sqlite3_mprintf("%Q", cache.cells[i].escaped?NULL:cache.cells[i].text.c_str());
00877       vals += squery;
00878       sqlite3_free(squery);
00879     }
00880   }
00881 
00882   if (cols=="") {
00883     RowRef r = insertRow(RowRef(-1));
00884     return r.isValid();
00885   }
00886 
00887   char *query = sqlite3_mprintf("INSERT INTO %s (%s) VALUES (%s)",
00888                                 quoted_name.c_str(),
00889                                 cols.c_str(),
00890                                 vals.c_str());
00891 
00892   dbg_printf("INSERT QUERY: %s\n", query);
00893   int iresult = sqlite3_exec(db, query, NULL, NULL, NULL);
00894   if (iresult!=SQLITE_OK) {
00895     const char *msg = sqlite3_errmsg(db);
00896     if (msg!=NULL) {
00897       fprintf(stderr,"Error: %s\n", msg);
00898       fprintf(stderr,"Query was: %s\n", query);
00899     }
00900     sqlite3_free(query);
00901     return false;
00902   }
00903   sqlite3_free(query);
00904 
00905   int rid = (int)sqlite3_last_insert_rowid(db);
00906 
00907   for (int i=0; i<(int)col2sql.size(); i++) {
00908     if (cache.invent[i]) {
00909       SheetCell c(rid);
00910       this->cache.cell(i,h) = c.text;
00911       cacheFlag.remove(i,h);
00912     }
00913   }
00914 
00915   if (result) {
00916     *result = SheetCell(h);
00917   }
00918 
00919   // inconsistent ordering
00920   row2sql.push_back(rid);
00921   h++;
00922   return true;
00923 }
00924 
00925 
00926 bool SqliteSheet::deleteData(int offset) {
00927   check();
00928   if (offset!=0) DataSheet::deleteData(offset);
00929 
00930   clearCache();
00931 
00932   sqlite3 *db = DB(implementation);
00933   if (db==NULL) return false;
00934 
00935   char *query = sqlite3_mprintf("DELETE FROM %s WHERE 1", quoted_name.c_str());
00936 
00937   int iresult = sqlite3_exec(db, query, NULL, NULL, NULL);
00938   if (iresult!=SQLITE_OK) {
00939     const char *msg = sqlite3_errmsg(db);
00940     if (msg!=NULL) {
00941       fprintf(stderr,"Error: %s\n", msg);
00942       fprintf(stderr,"Query was: %s\n", query);
00943     }
00944     sqlite3_free(query);
00945     return false;
00946   }
00947   sqlite3_free(query);
00948   row2sql.clear();
00949   h = 0;
00950   return true;
00951 }
00952 
00953 
00954 void SqliteSheet::checkPrimaryKeys() {
00955   sqlite3 *db = DB(implementation);
00956   if (db==NULL) return;
00957 
00958   sqlite3_stmt *statement = NULL;
00959   char *query = NULL;
00960 
00961   query = sqlite3_mprintf("PRAGMA %stable_info(%Q)", 
00962                           prefix_dot.c_str(),
00963                           name.c_str());
00964  
00965   int iresult = sqlite3_prepare_v2(db, query, -1, 
00966                                    &statement, NULL);
00967   if (iresult!=SQLITE_OK) {
00968     sqlite3_finalize(statement);
00969     sqlite3_free(query);
00970     return;
00971   }
00972   int i = 0;
00973   while (sqlite3_step(statement) == SQLITE_ROW) {
00974     char *col = (char *)sqlite3_column_text(statement,1);
00975     int pk = sqlite3_column_int(statement,5);
00976     if (pk) {
00977       primaryKeys.push_back(string(col));
00978     }
00979     col2sql[i].setPk(pk!=0);
00980     bool ai = false;
00981     if (pk) {
00982       if (col2sql[i].getColumnType().family == ColumnType::COLUMN_FAMILY_INTEGER) {
00983         ai = true;
00984       }
00985     }
00986     col2sql[i].setAutoIncrement(ai);
00987     i++;
00988   }
00989   sqlite3_finalize(statement);
00990   sqlite3_free(query);
00991 }
00992 
00993 
00994 
00995 void SqliteSheet::checkForeignKeys() {
00996   sqlite3 *db = DB(implementation);
00997   if (db==NULL) return;
00998 
00999   sqlite3_stmt *statement = NULL;
01000   char *query = NULL;
01001 
01002   query = sqlite3_mprintf("PRAGMA %sforeign_key_list(%Q)",
01003                           this->prefix_dot.c_str(),
01004                           this->name.c_str());
01005 
01006   int iresult = sqlite3_prepare_v2(db, query, -1, 
01007                                    &statement, NULL);
01008   if (iresult!=SQLITE_OK) {
01009     const char *msg = sqlite3_errmsg(db);
01010     if (msg!=NULL) {
01011       fprintf(stderr,"Error: %s\n", msg);
01012       fprintf(stderr,"Query was: %s\n", query);
01013     }
01014     sqlite3_finalize(statement);
01015     sqlite3_free(query);
01016   } 
01017 
01018   while (sqlite3_step(statement) == SQLITE_ROW) {
01019     const char *ref_table = (const char *)sqlite3_column_text(statement,2);
01020     const char *col = (const char *)sqlite3_column_text(statement,3);
01021     const char *ref_col = (const char *)sqlite3_column_text(statement,4);
01022     //printf("FOREIGN %s: %s %s\n", col, ref_table, ref_col);
01023     for (int i=0; i<(int)col2sql.size(); i++) {
01024       if (col2sql[i].getName()==col) {
01025         col2sql[i].setReference(ref_table,ref_col?ref_col:"");
01026       }
01027     }
01028   }
01029   sqlite3_finalize(statement);
01030   sqlite3_free(query);
01031 }
01032 
01033 
01034 void SqliteSchema::parse(const char *str) {
01035   string sql = str;
01036   preamble = "";
01037   parts.clear();
01038 
01039   bool double_quote = false;
01040   bool single_quote = false;
01041   string token = "";
01042   int nesting = 0;
01043   for (int i=0; i<(int)sql.length(); i++) {
01044     char ch = sql[i];
01045     if (double_quote||single_quote) {
01046       if (double_quote) {
01047         if (ch=='\"') double_quote = false;
01048       }
01049       if (single_quote) {
01050         if (ch=='\'') single_quote = false;
01051       }
01052       token += ch;
01053       continue;
01054     }
01055     bool brk = false;
01056     if (ch=='(') {
01057       nesting++;
01058       if (nesting==1) {
01059         brk = true;
01060       }
01061     } else if (ch==')') {
01062       nesting--;
01063       if (nesting==0) {
01064         brk = true;
01065       }
01066     }
01067     if (ch==',') {
01068       brk = true;
01069       if (nesting==1) {
01070       }
01071     }
01072     if (brk) {
01073       if (token[0]==' ') {
01074         token = token.substr(1,token.length());
01075       }
01076       if (preamble=="") {
01077         preamble = token;
01078       } else {
01079         parts.push_back(token);
01080       }
01081       token = "";
01082     } else {
01083       token += ch;
01084     }
01085   }
01086 
01087   dbg_printf("preamble %s\n", preamble.c_str());
01088   for (int i=0; i<(int)parts.size(); i++) {
01089     dbg_printf(" part %s\n", parts[i].c_str());
01090   }
01091 }
01092 
01093 
01094 string SqliteSchema::toString() const {
01095   string result = preamble;
01096   result += "(";
01097   for (int i=0; i<(int)parts.size(); i++) {
01098     if (i>0) {
01099       result += ", ";
01100     }
01101     result += parts[i];
01102   }
01103   result += ")";
01104   dbg_printf("modified SQL is %s\n", result.c_str());
01105   return result;
01106 }
01107 
01108 
01109 string SqliteSchema::fetch(sqlite3 *db, const char *table) {
01110   this->db = db;
01111   this->table_name = table;
01112   string sql = "";
01113   sqlite3_stmt *statement = NULL;
01114   char *query = NULL;
01115 
01116   query = sqlite3_mprintf("SELECT sql FROM SQLITE_MASTER WHERE name = %Q",
01117                           table);
01118  
01119   int iresult = sqlite3_prepare_v2(db, query, -1, 
01120                                    &statement, NULL);
01121   if (iresult!=SQLITE_OK) {
01122     const char *msg = sqlite3_errmsg(db);
01123     if (msg!=NULL) {
01124       fprintf(stderr,"Error: %s\n", msg);
01125       fprintf(stderr,"Query was: %s\n", query);
01126     }
01127     sqlite3_finalize(statement);
01128     sqlite3_free(query);
01129     return "";
01130   } 
01131   if (sqlite3_step(statement) == SQLITE_ROW) {
01132     sql = (const char *)sqlite3_column_text(statement,0);
01133   }
01134   sqlite3_finalize(statement);
01135   sqlite3_free(query);
01136   return sql;
01137 }
01138 
01139 
01140 bool SqliteSchema::apply(const vector<ColumnInfo>& names, const string& novel) {
01141   string sql_mod = toString();
01142 
01144   // Apply schema
01145 
01146   string new_column_list = "";
01147   string ins_column_list = "";
01148   for (int i=0; i<(int)names.size(); i++) {
01149     string base = names[i].getName().c_str();
01150     string add = base;
01151     if (add.find('\"')!=string::npos) {
01152       printf("Quoting of sql column names not done yet\n");
01153       exit(1);
01154     }
01155     add = string("\"") + add + "\"";
01156     if (i>0) {
01157       new_column_list += ',';
01158     }
01159     new_column_list += add;
01160     if (base!=novel) {
01161       if (ins_column_list!="") {
01162         ins_column_list += ',';
01163       }
01164       ins_column_list += add;
01165     }
01166   }
01167 
01168   char *query = sqlite3_mprintf("BEGIN TRANSACTION; \
01169 CREATE TEMPORARY TABLE __coopy_backup(%s);          \
01170 INSERT INTO __coopy_backup (%s) SELECT %s FROM %Q;   \
01171 DROP TABLE %Q;                                      \
01172 %s;                                                 \
01173 INSERT INTO %Q (%s) SELECT * FROM __coopy_backup;   \
01174 DROP TABLE __coopy_backup;                          \
01175 COMMIT;                                             \
01176 ", 
01177                                 ins_column_list.c_str(),
01178                                 ins_column_list.c_str(),
01179                                 ins_column_list.c_str(),
01180                                 table_name.c_str(),
01181                                 table_name.c_str(),
01182                                 sql_mod.c_str(),
01183                                 table_name.c_str(),
01184                                 ins_column_list.c_str());
01185 
01186   dbg_printf("Operation %s\n", query);
01187 
01188   int iresult = sqlite3_exec(db, query, NULL, NULL, NULL);
01189   if (iresult!=SQLITE_OK) {
01190     const char *msg = sqlite3_errmsg(db);
01191     if (msg!=NULL) {
01192       fprintf(stderr,"Error: %s\n", msg);
01193       fprintf(stderr,"Query was: %s\n", query);
01194     }
01195     sqlite3_free(query);
01196     return false;
01197   }
01198 
01199   sqlite3_free(query);
01200   return true;
01201 }
01202 
01203 
01204 class SqliteDbiSqlWrapper : public DbiSqlWrapper {
01205 public:
01206   sqlite3 *db;  
01207   sqlite3_stmt *statement;
01208 
01209   SqliteDbiSqlWrapper() {
01210     statement = NULL;
01211     db = NULL;
01212   }
01213 
01214   ~SqliteDbiSqlWrapper() {
01215     end();
01216   }
01217 
01218   // put quoting functions here
01219 
01220   std::string getQuotedColumnName(const std::string& name) {
01221     string tname ;
01222     char *query = NULL;
01223     query = sqlite3_mprintf("`%q`", name.c_str());
01224     tname = query;
01225     sqlite3_free(query);
01226     return tname;
01227   }
01228 
01229   std::string getQuotedTableName(const SqlTableName& name) {
01230     string tname;
01231     char *query = NULL;
01232     if (name.prefix == "") {
01233       query = sqlite3_mprintf("`%q`", name.name.c_str());
01234     } else {
01235       query = sqlite3_mprintf("`%q`.`%q`", name.prefix.c_str(),
01236                               name.name.c_str());
01237     }
01238     tname = query;
01239     sqlite3_free(query);
01240     return tname;
01241   }
01242 
01243   std::vector<ColumnInfo> getColumns(const SqlTableName& name) {
01244     std::vector<ColumnInfo> col2sql;
01245 
01246     if (db==NULL) return col2sql;
01247 
01248     string tname = name.name;
01249     string prefix_dot = "";
01250     char *query = NULL;
01251     if (name.prefix!="") {
01252       query = sqlite3_mprintf("%Q.", 
01253                               name.prefix.c_str());
01254       prefix_dot = query;
01255       sqlite3_free(query);
01256     }
01257 
01258     sqlite3_stmt *statement = NULL;
01259 
01260     query = sqlite3_mprintf("PRAGMA %stable_info(%Q)", 
01261                             prefix_dot.c_str(),
01262                             tname.c_str());
01263     
01264     int iresult = sqlite3_prepare_v2(db, query, -1, 
01265                                      &statement, NULL);
01266     if (iresult!=SQLITE_OK) {
01267       sqlite3_finalize(statement);
01268       sqlite3_free(query);
01269       col2sql.clear();
01270       return col2sql;
01271     }
01272     int i = 0;
01273     while (sqlite3_step(statement) == SQLITE_ROW) {
01274       char *col = (char *)sqlite3_column_text(statement,1);
01275       ColumnInfo info(col);
01276       col2sql.push_back(info);
01277       int pk = sqlite3_column_int(statement,5);
01278       col2sql[i].setPk(pk!=0);
01279       bool ai = false;
01280       if (pk) {
01281         if (col2sql[i].getColumnType().family == ColumnType::COLUMN_FAMILY_INTEGER) {
01282           ai = true;
01283         }
01284       }
01285       col2sql[i].setAutoIncrement(ai);
01286       i++;
01287     }
01288     sqlite3_finalize(statement);
01289     sqlite3_free(query);
01290 
01291     return col2sql;
01292   }
01293 
01294   virtual bool begin(const std::string& query) {
01295     end();
01296     int result = sqlite3_prepare_v2(db, query.c_str(), -1, 
01297                                     &statement, NULL);
01298     if (result!=SQLITE_OK) {
01299       const char *msg = sqlite3_errmsg(db);
01300       if (msg!=NULL) {
01301         fprintf(stderr,"Error: %s\n", msg);
01302         fprintf(stderr,"Query was: %s\n", query.c_str());
01303       }
01304       end();
01305       return false;
01306     }
01307     return true;
01308   }
01309 
01310   virtual bool read() {
01311     return (sqlite3_step(statement) == SQLITE_ROW);
01312   }
01313 
01314   virtual SheetCell get(int index) {
01315     char *result = (char*)sqlite3_column_text(statement,index);
01316     if (!result) return SheetCell();
01317     return SheetCell(result,false);
01318   }
01319 
01320   virtual bool end() {
01321     if (!statement) return false;
01322     sqlite3_finalize(statement);
01323     statement = NULL;
01324     return true;
01325   }
01326   virtual int width() {
01327     return sqlite3_column_count(statement);
01328   }
01329 };
01330 
01331 int SqliteSheet::compare(coopy::store::DataSheet& pivot, 
01332                          coopy::store::DataSheet& local, 
01333                          coopy::store::DataSheet& remote, 
01334                          coopy::cmp::Patcher& output, 
01335                          const coopy::cmp::CompareFlags& flags) {
01336   //printf("sqlite compare anyone?\n");
01337   SqliteSheet *spivot = dynamic_cast<SqliteSheet *>(&(pivot.tail()));
01338   SqliteSheet *slocal = dynamic_cast<SqliteSheet *>(&(local.tail()));
01339   SqliteSheet *sremote = dynamic_cast<SqliteSheet *>(&(remote.tail()));
01340   if (spivot==NULL || slocal==NULL || sremote==NULL) return -1;
01341   if (spivot->prefix != slocal->prefix) return -1;
01342   if (spivot->name != slocal->name) return -1;
01343   //printf("name %s %s\n", slocal->name.c_str(), sremote->name.c_str());
01344   //printf("prefix |%s| |%s|\n", slocal->prefix.c_str(), sremote->prefix.c_str());
01345 
01346   SqliteDbiSqlWrapper wrap;
01347   wrap.db = DB(slocal->implementation);
01348   SqlTableName t1, t2;
01349   t1.prefix = slocal->prefix;
01350   t1.name = slocal->name;
01351   t2.prefix = sremote->prefix;
01352   t2.name = sremote->name;
01353   if (t1.prefix=="") t1.prefix = "main";
01354   if (t2.prefix=="") t2.prefix = "main";
01355   SqlCompare cmp(&wrap,t1,t2,output);
01356 
01357   return cmp.apply()?0:-1;
01358 }
01359 
01360 
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines