COOPY » Guide
version 0.6.5
|
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