"Fossies" - the Fresh Open Source Software Archive

Member "sqlupdate-1.6.6/sqlupdate.cc" (25 Feb 2012, 78625 Bytes) of package /linux/privat/old/sqlupdate-1.6.6.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 #include <sstream>
    2 #include <cctype>
    3 #include <cstdio>
    4 #include <string>
    5 #include <vector>
    6 #include <list>
    7 #include <map>
    8 #include <algorithm>
    9 
   10 #undef SUPPORT_INSERTS
   11 
   12 /*
   13  TODO: add support for CURRENT_TIMESTAMP , used with (DEFAULT,ON UPDATE)
   14 */
   15 
   16 using namespace std;
   17 
   18 #ifdef SUPPORT_INSERTS
   19 static bool nodata = true;
   20 #endif
   21 static bool UseCreateIndex = false;
   22 static bool AddComments = false;
   23 static bool HandleCharsets = true;
   24 
   25 static bool startwith(const char *s2, const string &s, size_t ind=0)
   26 {
   27     size_t a;
   28     for(a=ind; s2 && *s2; ++a)
   29     {
   30         if(a >= s.size())return false;
   31         if(toupper(s[a]) != *s2++)
   32             return false;
   33     }
   34     return true;
   35 }
   36 
   37 static const string ucase(const string &s)
   38 {
   39     string t;
   40     size_t a, b=s.size();
   41     for(a=0; a<b; ++a) t += (char)toupper(s[a]);
   42     return t;
   43 }
   44 static bool ischar(char c)
   45 {
   46     return c=='_' || isalnum(c);
   47 }
   48 static const string GetWord(const string &s, size_t &a)
   49 {
   50     // Upon return, a points 1 char after the value returned.
   51     
   52     string result;
   53     const size_t b = s.size();
   54     if(a < b && s[a] == '`')
   55     {
   56         ++a;
   57         while(a < b && s[a] != '`')
   58         {
   59             if(s[a] == '\\') ++a;
   60             result += s[a++];
   61         }
   62         if(a < b && s[a] == '`') ++a;
   63     }
   64     else
   65     {
   66         while(a < b && ischar(s[a]))
   67             result += s[a++];
   68     }
   69     return result;
   70 }
   71 static const string GetUcaseWord(const string &s, size_t &a)
   72 {
   73     return ucase(GetWord(s, a));
   74 }
   75 static const std::string GetStringValue(const string& s, size_t& a, bool& was_quoted)
   76 {
   77     std::string result = "";
   78     const size_t b = s.size();
   79     
   80     was_quoted = false;
   81     if(s[a]=='\'' || s[a]=='"')
   82     {
   83         char sep = s[a++];
   84         was_quoted = true;
   85         while(a<b)
   86         {
   87             if(s[a]==sep) { ++a; break; }
   88             if(s[a]=='\\')
   89             {
   90                 ++a;
   91                 switch(s[a])
   92                 {
   93                     case 'n': result += '\n'; break;
   94                     case 'r': result += '\r'; break;
   95                     case 't': result += '\t'; break;
   96                     default: result += s[a]; // covers \\ and \" and \'
   97                 }
   98             }
   99             else
  100                 result += s[a];
  101             ++a;
  102         }
  103     }
  104     else
  105     {
  106         if(s[a] == '-' || s[a] == '+') result += s[a++];
  107         while(a<b && (isalnum(s[a]) || s[a]=='.' || s[a]=='_')) result += s[a++];
  108     }
  109     return result;
  110 }
  111 static const std::string QuoteWord(const std::string& s)
  112 {
  113     // Quotes are ugly, so we only use them if necessary.
  114 static const char* const ReservedWords[] =
  115 {
  116 "ADD","ALL","ALTER",
  117 "ANALYZE","AND","AS",
  118 "ASC","ASENSITIVE","BEFORE",
  119 "BETWEEN","BIGINT","BINARY","BIT",
  120 "BLOB","BOTH","BY",
  121 "CALL","CASCADE","CASE",
  122 "CHANGE","CHAR","CHARACTER","CHARSET",
  123 "CHECK","COLLATE","COLUMN","COMMENT",
  124 "CONDITION","CONNECTION","CONSTRAINT",
  125 "CONTINUE","CONVERT","CREATE",
  126 "CROSS","CURRENT_DATE","CURRENT_TIME",
  127 "CURRENT_TIMESTAMP","CURRENT_USER","CURSOR",
  128 "DATABASE","DATABASES","DAY_HOUR",
  129 "DAY_MICROSECOND","DAY_MINUTE","DAY_SECOND",
  130 "DEC","DECIMAL","DECLARE",
  131 "DEFAULT","DELAYED","DELETE",
  132 "DESC","DESCRIBE","DETERMINISTIC",
  133 "DISTINCT","DISTINCTROW","DIV",
  134 "DOUBLE","DROP","DUAL",
  135 "EACH","ELSE","ELSEIF",
  136 "ENCLOSED","ESCAPED","EXISTS",
  137 "EXIT","EXPLAIN","FALSE",
  138 "FETCH","FLOAT","FOR",
  139 "FORCE","FOREIGN","FROM",
  140 "FULLTEXT","GEOMETRY","GEOMETRYCOLLECTION",
  141 "GOTO","GRANT",
  142 "GROUP","HAVING","HIGH_PRIORITY",
  143 "HOUR_MICROSECOND","HOUR_MINUTE","HOUR_SECOND",
  144 "IF","IGNORE","IN",
  145 "INDEX","INFILE","INNER",
  146 "INOUT","INSENSITIVE","INSERT",
  147 "INT","INTEGER","INTERVAL",
  148 "INTO","IS","ITERATE",
  149 "JOIN","KEY","KEYS",
  150 "KILL","LEADING","LEAVE",
  151 "LEFT","LIKE",
  152 "LINESTRING",
  153 "LIMIT",
  154 "LINES","LOAD","LOCALTIME",
  155 "LOCALTIMESTAMP","LOCK","LONG",
  156 "LONGBLOB","LONGTEXT","LOOP",
  157 "LOW_PRIORITY","MATCH","MEDIUMBLOB",
  158 "MEDIUMINT","MEDIUMTEXT","MIDDLEINT",
  159 "MINUTE_MICROSECOND","MINUTE_SECOND","MOD",
  160 "MODIFIES",
  161 "MULTILINESTRING","MULTIPOINT","MULTIPOLYGON",
  162 "NATURAL","NOT",
  163 "NO_WRITE_TO_BINLOG","NULL","NUMERIC",
  164 "ON","OPTIMIZE","OPTION",
  165 "OPTIONALLY","OR","ORDER",
  166 "OUT","OUTER","OUTFILE",
  167 "POINT","POLYGON",
  168 "PRECISION","PRIMARY","PROCEDURE",
  169 "PURGE","READ","READS",
  170 "REAL","REFERENCES","REGEXP",
  171 "RELEASE","RENAME","REPEAT",
  172 "REPLACE","REQUIRE","RESTRICT",
  173 "RETURN","REVOKE","RIGHT",
  174 "RLIKE","SCHEMA","SCHEMAS",
  175 "SECOND_MICROSECOND","SELECT","SENSITIVE",
  176 "SEPARATOR","SET","SHOW",
  177 "SMALLINT","SONAME","SPATIAL",
  178 "SPECIFIC","SQL","SQLEXCEPTION",
  179 "SQLSTATE","SQLWARNING","SQL_BIG_RESULT",
  180 "SQL_CALC_FOUND_ROWS","SQL_SMALL_RESULT","SSL",
  181 "STARTING","STRAIGHT_JOIN","TABLE",
  182 "TERMINATED","THEN","TINYBLOB",
  183 "TINYINT","TINYTEXT","TO",
  184 "TRAILING","TRIGGER","TRUE",
  185 "UNDO","UNION","UNIQUE",
  186 "UNLOCK","UNSIGNED","UPDATE",
  187 "USAGE","USE","USING",
  188 "UTC_DATE","UTC_TIME","UTC_TIMESTAMP",
  189 "VALUES","VARBINARY","VARCHAR",
  190 "VARCHARACTER","VARYING","WHEN",
  191 "WHERE","WHILE","WITH",
  192 "WRITE","XOR","YEAR_MONTH",
  193 "ZEROFILL"
  194 };
  195 const size_t nwords = sizeof(ReservedWords) / sizeof(*ReservedWords);
  196     
  197     bool needs_quoting = std::binary_search(ReservedWords, ReservedWords+nwords, ucase(s));
  198     
  199     if(!needs_quoting)
  200     {
  201         // Check for special characters
  202         for(size_t a=0; a<s.size(); ++a)
  203             if(!ischar(s[a])) { needs_quoting = true; break; }
  204     }
  205     if(needs_quoting)
  206     {
  207         std::string result = "`";
  208         for(size_t a=0; a<s.size(); ++a)
  209             if(s[a] == '`') result += "\\`";
  210             else result += s[a];
  211         result += '`';
  212         return result;
  213     }
  214     return s;
  215 }
  216 static const std::string GetDefaultCollate(const std::string& cset)
  217 {
  218     static const struct{const char*cset; const char* collate; }
  219     SHOW_CHARSETS[] = {
  220 {"ARMSCII8","ARMSCII8_GENERAL_CI" },
  221 {"ASCII","ASCII_GENERAL_CI" },
  222 {"BIG5","BIG5_CHINESE_CI" },
  223 {"BINARY","BINARY" },
  224 {"CP1250","CP1250_GENERAL_CI" },
  225 {"CP1251","CP1251_GENERAL_CI" },
  226 {"CP1256","CP1256_GENERAL_CI" },
  227 {"CP1257","CP1257_GENERAL_CI" },
  228 {"CP850","CP850_GENERAL_CI" },
  229 {"CP852","CP852_GENERAL_CI" },
  230 {"CP866","CP866_GENERAL_CI" },
  231 {"CP932","CP932_JAPANESE_CI" },
  232 {"DEC8","DEC8_SWEDISH_CI" },
  233 {"EUCJPMS","EUCJPMS_JAPANESE_CI" },
  234 {"EUCKR","EUCKR_KOREAN_CI" },
  235 {"GB2312","GB2312_CHINESE_CI" },
  236 {"GBK","GBK_CHINESE_CI" },
  237 {"GEOSTD8","GEOSTD8_GENERAL_CI" },
  238 {"GREEK","GREEK_GENERAL_CI" },
  239 {"HEBREW","HEBREW_GENERAL_CI" },
  240 {"HP8","HP8_ENGLISH_CI" },
  241 {"KEYBCS2","KEYBCS2_GENERAL_CI" },
  242 {"KOI8R","KOI8R_GENERAL_CI" },
  243 {"KOI8U","KOI8U_GENERAL_CI" },
  244 {"LATIN1","LATIN1_SWEDISH_CI" },
  245 {"LATIN2","LATIN2_GENERAL_CI" },
  246 {"LATIN5","LATIN5_TURKISH_CI" },
  247 {"LATIN7","LATIN7_GENERAL_CI" },
  248 {"MACCE","MACCE_GENERAL_CI" },
  249 {"MACROMAN","MACROMAN_GENERAL_CI" },
  250 {"SJIS","SJIS_JAPANESE_CI" },
  251 {"SWE7","SWE7_SWEDISH_CI" },
  252 {"TIS620","TIS620_THAI_CI" },
  253 {"UCS2","UCS2_GENERAL_CI" },
  254 {"UJIS","UJIS_JAPANESE_CI" },
  255 {"UTF8","UTF8_GENERAL_CI" }
  256     };
  257     /* FIXME: use binary search */
  258     const size_t max = sizeof(SHOW_CHARSETS) / sizeof(*SHOW_CHARSETS);
  259     for(size_t a=0; a<max; ++a)
  260         if(cset == SHOW_CHARSETS[a].cset) return SHOW_CHARSETS[a].collate;
  261     return "";
  262 }
  263 
  264 static const std::string GetDefaultCharset(const std::string& coll)
  265 {
  266     static const struct{const char*collate; const char* cset; }
  267     SHOW_COLLATES[] = {
  268 { "BIG5_CHINESE_CI","BIG5" },
  269 { "BIG5_BIN","BIG5" },
  270 { "DEC8_SWEDISH_CI","DEC8" },
  271 { "DEC8_BIN","DEC8" },
  272 { "CP850_GENERAL_CI","CP850" },
  273 { "CP850_BIN","CP850" },
  274 { "HP8_ENGLISH_CI","HP8" },
  275 { "HP8_BIN","HP8" },
  276 { "KOI8R_GENERAL_CI","KOI8R" },
  277 { "KOI8R_BIN","KOI8R" },
  278 { "LATIN1_GERMAN1_CI","LATIN1" },
  279 { "LATIN1_SWEDISH_CI","LATIN1" },
  280 { "LATIN1_DANISH_CI","LATIN1" },
  281 { "LATIN1_GERMAN2_CI","LATIN1" },
  282 { "LATIN1_BIN","LATIN1" },
  283 { "LATIN1_GENERAL_CI","LATIN1" },
  284 { "LATIN1_GENERAL_CS","LATIN1" },
  285 { "LATIN1_SPANISH_CI","LATIN1" },
  286 { "LATIN2_CZECH_CS","LATIN2" },
  287 { "LATIN2_GENERAL_CI","LATIN2" },
  288 { "LATIN2_HUNGARIAN_CI","LATIN2" },
  289 { "LATIN2_CROATIAN_CI","LATIN2" },
  290 { "LATIN2_BIN","LATIN2" },
  291 { "SWE7_SWEDISH_CI","SWE7" },
  292 { "SWE7_BIN","SWE7" },
  293 { "ASCII_GENERAL_CI","ASCII" },
  294 { "ASCII_BIN","ASCII" },
  295 { "UJIS_JAPANESE_CI","UJIS" },
  296 { "UJIS_BIN","UJIS" },
  297 { "SJIS_JAPANESE_CI","SJIS" },
  298 { "SJIS_BIN","SJIS" },
  299 { "HEBREW_GENERAL_CI","HEBREW" },
  300 { "HEBREW_BIN","HEBREW" },
  301 { "TIS620_THAI_CI","TIS620" },
  302 { "TIS620_BIN","TIS620" },
  303 { "EUCKR_KOREAN_CI","EUCKR" },
  304 { "EUCKR_BIN","EUCKR" },
  305 { "KOI8U_GENERAL_CI","KOI8U" },
  306 { "KOI8U_BIN","KOI8U" },
  307 { "GB2312_CHINESE_CI","GB2312" },
  308 { "GB2312_BIN","GB2312" },
  309 { "GREEK_GENERAL_CI","GREEK" },
  310 { "GREEK_BIN","GREEK" },
  311 { "CP1250_GENERAL_CI","CP1250" },
  312 { "CP1250_CZECH_CS","CP1250" },
  313 { "CP1250_CROATIAN_CI","CP1250" },
  314 { "CP1250_BIN","CP1250" },
  315 { "GBK_CHINESE_CI","GBK" },
  316 { "GBK_BIN","GBK" },
  317 { "LATIN5_TURKISH_CI","LATIN5" },
  318 { "LATIN5_BIN","LATIN5" },
  319 { "ARMSCII8_GENERAL_CI","ARMSCII8" },
  320 { "ARMSCII8_BIN","ARMSCII8" },
  321 { "UTF8_GENERAL_CI","UTF8" },
  322 { "UTF8_BIN","UTF8" },
  323 { "UTF8_UNICODE_CI","UTF8" },
  324 { "UTF8_ICELANDIC_CI","UTF8" },
  325 { "UTF8_LATVIAN_CI","UTF8" },
  326 { "UTF8_ROMANIAN_CI","UTF8" },
  327 { "UTF8_SLOVENIAN_CI","UTF8" },
  328 { "UTF8_POLISH_CI","UTF8" },
  329 { "UTF8_ESTONIAN_CI","UTF8" },
  330 { "UTF8_SPANISH_CI","UTF8" },
  331 { "UTF8_SWEDISH_CI","UTF8" },
  332 { "UTF8_TURKISH_CI","UTF8" },
  333 { "UTF8_CZECH_CI","UTF8" },
  334 { "UTF8_DANISH_CI","UTF8" },
  335 { "UTF8_LITHUANIAN_CI","UTF8" },
  336 { "UTF8_SLOVAK_CI","UTF8" },
  337 { "UTF8_SPANISH2_CI","UTF8" },
  338 { "UTF8_ROMAN_CI","UTF8" },
  339 { "UTF8_PERSIAN_CI","UTF8" },
  340 { "UTF8_ESPERANTO_CI","UTF8" },
  341 { "UTF8_HUNGARIAN_CI","UTF8" },
  342 { "UCS2_GENERAL_CI","UCS2" },
  343 { "UCS2_BIN","UCS2" },
  344 { "UCS2_UNICODE_CI","UCS2" },
  345 { "UCS2_ICELANDIC_CI","UCS2" },
  346 { "UCS2_LATVIAN_CI","UCS2" },
  347 { "UCS2_ROMANIAN_CI","UCS2" },
  348 { "UCS2_SLOVENIAN_CI","UCS2" },
  349 { "UCS2_POLISH_CI","UCS2" },
  350 { "UCS2_ESTONIAN_CI","UCS2" },
  351 { "UCS2_SPANISH_CI","UCS2" },
  352 { "UCS2_SWEDISH_CI","UCS2" },
  353 { "UCS2_TURKISH_CI","UCS2" },
  354 { "UCS2_CZECH_CI","UCS2" },
  355 { "UCS2_DANISH_CI","UCS2" },
  356 { "UCS2_LITHUANIAN_CI","UCS2" },
  357 { "UCS2_SLOVAK_CI","UCS2" },
  358 { "UCS2_SPANISH2_CI","UCS2" },
  359 { "UCS2_ROMAN_CI","UCS2" },
  360 { "UCS2_PERSIAN_CI","UCS2" },
  361 { "UCS2_ESPERANTO_CI","UCS2" },
  362 { "UCS2_HUNGARIAN_CI","UCS2" },
  363 { "CP866_GENERAL_CI","CP866" },
  364 { "CP866_BIN","CP866" },
  365 { "KEYBCS2_GENERAL_CI","KEYBCS2" },
  366 { "KEYBCS2_BIN","KEYBCS2" },
  367 { "MACCE_GENERAL_CI","MACCE" },
  368 { "MACCE_BIN","MACCE" },
  369 { "MACROMAN_GENERAL_CI","MACROMAN" },
  370 { "MACROMAN_BIN","MACROMAN" },
  371 { "CP852_GENERAL_CI","CP852" },
  372 { "CP852_BIN","CP852" },
  373 { "LATIN7_ESTONIAN_CS","LATIN7" },
  374 { "LATIN7_GENERAL_CI","LATIN7" },
  375 { "LATIN7_GENERAL_CS","LATIN7" },
  376 { "LATIN7_BIN","LATIN7" },
  377 { "CP1251_BULGARIAN_CI","CP1251" },
  378 { "CP1251_UKRAINIAN_CI","CP1251" },
  379 { "CP1251_BIN","CP1251" },
  380 { "CP1251_GENERAL_CI","CP1251" },
  381 { "CP1251_GENERAL_CS","CP1251" },
  382 { "CP1256_GENERAL_CI","CP1256" },
  383 { "CP1256_BIN","CP1256" },
  384 { "CP1257_LITHUANIAN_CI","CP1257" },
  385 { "CP1257_BIN","CP1257" },
  386 { "CP1257_GENERAL_CI","CP1257" },
  387 { "BINARY","BINARY" },
  388 { "GEOSTD8_GENERAL_CI","GEOSTD8" },
  389 { "GEOSTD8_BIN","GEOSTD8" },
  390 { "CP932_JAPANESE_CI","CP932" },
  391 { "CP932_BIN","CP932" },
  392 { "EUCJPMS_JAPANESE_CI","EUCJPMS" },
  393 { "EUCJPMS_BIN","EUCJPMS" }
  394     };
  395     /* FIXME: use binary search */
  396     const size_t max = sizeof(SHOW_COLLATES) / sizeof(*SHOW_COLLATES);
  397     for(size_t a=0; a<max; ++a)
  398         if(coll == SHOW_COLLATES[a].collate) return SHOW_COLLATES[a].cset;
  399     return "";
  400 }
  401 
  402 
  403 static bool isempty(char c)
  404 {
  405     return c=='\n' || c=='\r' || c=='\t' || c==' ';
  406 }
  407 static void SkipBlank(const std::string& s, size_t& a)
  408 {
  409     const size_t b = s.size();
  410     while(a < b && isempty(s[a])) ++a;
  411 }
  412 
  413 static const string sqlfix(const string &s, bool must_quote=false)
  414 {
  415     if(!must_quote) // Check if it's an integral value, can omit quotes then
  416     {
  417         bool is_int_without_zerolead = true;
  418         if(s.empty()) is_int_without_zerolead = false;
  419         for(size_t zeroes=0, nonzeroes=0, a=0; a<s.size(); ++a)
  420         {
  421             if((s[a]<'0' || s[a]>'9')
  422             || (s[a]=='0' && zeroes>0 && nonzeroes==0))
  423             {
  424                 is_int_without_zerolead = false;
  425                 break;
  426             }
  427             if(!a && s[a]=='0') ++zeroes; else ++nonzeroes;
  428         }
  429         if(is_int_without_zerolead) return s;
  430     }
  431 
  432     string t;
  433     size_t a, b=s.size();
  434     for(a=0; a<b; ++a)
  435     {
  436         if(s[a]=='\\' || s[a]=='\'')t += '\\';
  437         t += s[a];
  438     }
  439     return "'" + t + "'";
  440 }
  441 
  442 class fieldtype
  443 {
  444     string type;
  445     size_t width, prec;
  446     string enumparams;
  447     bool autoincrement;
  448     bool zerofill;
  449     bool unsig;
  450     bool null;
  451     string deftext;
  452     string content;
  453     string collate,charset;
  454     string comment;
  455     bool binary, unique, fulltext;
  456 
  457     void RebuildContent()
  458     {
  459         content = type;
  460         if(type=="ENUM" || type=="SET") { content += '('; content += enumparams; content += ')'; }
  461         if(width)
  462         {
  463             char Buf[64];
  464             content += '(';
  465             sprintf(Buf, "%u", (unsigned)width); content += Buf;
  466             if(prec) { sprintf(Buf, ",%u", (unsigned)prec); content += Buf; }
  467             content += ')';
  468         }
  469         content += ' ';
  470         if(zerofill)content += "ZEROFILL ";
  471         if(unsig)content += "UNSIGNED ";
  472         if(binary)content += "BINARY ";
  473         if(unique)content += "UNIQUE ";
  474         if(HandleCharsets)
  475         {
  476             if(!charset.empty()) content += "CHARSET " + charset + " ";
  477             if(!collate.empty()) content += "COLLATE " + collate + " ";
  478         }
  479         if(fulltext)content += "FULLTEXT ";
  480 
  481         if(null)content += "NULL "; else { content += "NOT NULL "; forcedefault(); }
  482 
  483         if(!comment.empty())
  484             content += "COMMENT '" + comment + "' ";
  485 
  486         if(autoincrement)
  487         {
  488             content += "AUTO_INCREMENT ";
  489         }
  490         //  fprintf(stderr, "'%s' \n", content.c_str());
  491     }
  492 
  493     void FixCharset()
  494     {
  495         if(!HandleCharsets)
  496         {
  497             charset = collate = "";
  498             return;
  499         }
  500         if(!charset.empty() && collate.empty()) collate = GetDefaultCollate(charset);
  501         if(charset.empty() && !collate.empty()) charset = GetDefaultCharset(collate);
  502     }
  503 public:
  504     inline bool operator!= (const fieldtype &s) const { return !operator==(s); }
  505     bool operator== (const fieldtype &s) const
  506     {
  507         return s.width==width
  508            &&  s.prec==prec
  509            &&  s.autoincrement==autoincrement
  510            &&  s.zerofill==zerofill
  511            &&  s.unsig==unsig
  512            &&  s.null==null
  513            &&  s.type==type
  514            &&  s.binary==binary
  515            &&  s.charset==charset
  516            &&  s.collate==collate;
  517     }
  518     const char *c_str() const { return content.c_str(); }
  519     const string& defval() const { return deftext; }
  520     void forcedefault()
  521     {
  522         if(!deftext.empty()) return;
  523         if(autoincrement) return;
  524         
  525         if(type=="INT" || type=="TINYINT" || type=="SMALLINT" || type=="BIGINT")
  526         {
  527             deftext = sqlfix("0");
  528         }
  529         else if(type=="DECIMAL" || type=="DOUBLE" || type=="FLOAT")
  530         {
  531             deftext = "0";
  532             if(prec) deftext += '.';
  533             for(size_t a=0; a<prec; ++a) deftext += '0';
  534             deftext = sqlfix(deftext);
  535         }
  536         else if(type=="BIT")
  537             {} // no default
  538         else if((type=="ENUM") || (type=="SET"))
  539             {} // no default
  540         else
  541             deftext = sqlfix("");
  542     }
  543     const string& gettype() const { return type; }
  544     const string& getcollate() const { return collate; }
  545     const string& getcharset() const { return charset; }
  546     const string& getcomment() const { return comment; }
  547     const string& getenumparams() const { return enumparams; }
  548 
  549     size_t getwidth() const { return width; }
  550     void settype(const string &t) { type=t; RebuildContent(); }
  551     void setcharset(const string &t) { charset=t; FixCharset(); RebuildContent(); }
  552     void setcollate(const string &t) { collate=t; FixCharset(); RebuildContent(); }
  553     void setcomment(const string &t) { comment=t; }
  554     void setenumparams(const string &t) { enumparams=t; }
  555     
  556     void clearcharsetandcollate() { charset=collate=""; RebuildContent(); }
  557 
  558     explicit fieldtype(const string &s) :
  559         type(),
  560         width(0), prec(0),
  561         enumparams(),
  562         autoincrement(false),
  563         zerofill(false), unsig(false), null(true),
  564         deftext(), content(s),
  565         collate(), charset(),
  566         comment(),
  567         binary(false), unique(false), fulltext(false)
  568     {
  569         size_t b=s.size(), c=0;
  570         SkipBlank(s, c);
  571         size_t c_before = c;
  572         type = GetUcaseWord(s, c);
  573         if(type.empty())
  574         {
  575             throw("Can't get type from '" + s.substr(c_before) + "'");
  576         }
  577         
  578         // Resolve aliases
  579         if(type=="INTEGER")type="INT";
  580         else if(type=="REAL")type="DOUBLE";
  581         else if(type=="NUMERIC")type="DECIMAL";
  582 
  583         // Resolve default parameters AND bail out if unknown types
  584         if(type=="INT")width=11;
  585         else if(type=="SMALLINT")width=6;
  586         else if(type=="TINYINT")width=4;
  587         else if(type=="MEDIUMINT")width=9;
  588         else if(type=="BIGINT")width=20;
  589         else if(type=="DOUBLE")width=16, prec=4;
  590         else if(type=="FLOAT")width=10, prec=2;
  591         else if(type=="TIMESTAMP")width=14;
  592         else if(type=="DECIMAL"){}
  593 
  594         else if(type=="CHAR")width=1;
  595         else if(type=="VARCHAR"){}
  596         else if(type=="TEXT"){}
  597         else if(type=="TINYTEXT"){}
  598         else if(type=="MEDIUMTEXT"){}
  599         else if(type=="LONGTEXT"){}
  600         else if(type=="BLOB"){}
  601         else if(type=="MEDIUMBLOB"){}
  602         else if(type=="LONGBLOB"){}
  603         else if(type=="DATETIME"){}
  604         else if(type=="DATE"){}
  605         else if(type=="TIME"){}
  606         else if(type=="ENUM"){}
  607         else if(type=="SET"){}
  608 
  609         // Spatial datatypes
  610         else if(type=="GEOMETRY"){}
  611         else if(type=="POLYGON"){}
  612         else if(type=="POINT"){}
  613         else if(type=="LINESTRING"){}
  614         else if(type=="MULTIPOINT"){}
  615         else if(type=="MULTILINESTRING"){}
  616         else if(type=="MULTIPOLYGON"){}
  617         else if(type=="GEOMETRYCOLLECTION"){}
  618         
  619         else
  620         {
  621             fprintf(stderr, "Warning: Unknown type '%s'\n", type.c_str());
  622         }
  623         SkipBlank(s, c);
  624         if(c<b)
  625         {
  626             // Parse parameters of the type
  627             if(s[c]=='(')
  628             {
  629                 ++c; SkipBlank(s, c);
  630                 if((type == "ENUM") || (type == "SET"))
  631                 {
  632                     while(c<b)
  633                     {
  634                         SkipBlank(s, c);
  635                         bool was_quoted=false;
  636                         enumparams += sqlfix(GetStringValue(s, c, was_quoted),true);
  637                         if(!was_quoted)
  638                             fprintf(stderr, "Syntax error in ENUM/SET params: ENUM/SET values must be quoted.");
  639                         SkipBlank(s, c);
  640                         if(c<b && s[c]==',') { enumparams += s[c++]; continue; }
  641                         break;
  642                     }
  643                 }
  644                 else
  645                 {
  646                     for(width=0; c<b && isdigit(s[c]); ++c)width=width*10+s[c]-'0';
  647                     SkipBlank(s, c);
  648                     if(c<b && s[c]==',')
  649                     {
  650                         ++c; SkipBlank(s, c);
  651                         for(prec=0; c<b && isdigit(s[c]); ++c)prec=prec*10+s[c]-'0';
  652                         SkipBlank(s, c);
  653                     }
  654                 }
  655                 if(c>=b || s[c]!=')')
  656                 {
  657                     throw("Expected ')' in '" + s + "'");
  658                 }
  659                 ++c;
  660             }
  661             
  662             // Parse the parameters of the type
  663             while(c<b)
  664             {
  665                 string t;
  666                 SkipBlank(s, c);
  667                 //fprintf(stderr, "Analyzing '%s'...\n", s.substr(c).c_str());
  668                 if(c>=b)break;
  669                 t = GetUcaseWord(s, c);
  670                 if(t.empty())
  671                 {
  672                     throw("Can't get a word from '" + s + "' at '" + s.substr(c) + "'");
  673                 }
  674                 if(t=="AUTO_INCREMENT")autoincrement = true;
  675                 else if(t=="UNSIGNED")unsig = true;
  676                 else if(t=="BINARY")binary = true;
  677                 else if(t=="UNIQUE")unique = true;
  678                 else if(t=="ZEROFILL")zerofill = true;
  679                 else if(t=="FULLTEXT")fulltext = true;
  680                 else if(t=="NULL")null = true;
  681                 else if(t=="PRIMARY")
  682                 {
  683                     SkipBlank(s, c);
  684                     t = GetUcaseWord(s, c);
  685                     if(t!="KEY")throw("PRIMARY "+t+"? KEY expected.");
  686                     /* ignore */
  687                 }
  688                 else if(t=="NOT")
  689                 {
  690                     SkipBlank(s, c);
  691                     t = GetUcaseWord(s, c);
  692                     if(t!="NULL")throw("NOT "+t+"? NULL expected.");
  693                     null = false;
  694                 }
  695                 else if(t=="DEFAULT")
  696                 {
  697                     SkipBlank(s, c);
  698                     bool was_quoted=false;
  699                     deftext = GetStringValue(s, c, was_quoted);
  700                     if(!was_quoted && deftext == "NULL")
  701                         deftext = "";
  702                     else if(!was_quoted && deftext == "CURRENT_TIMESTAMP")
  703                         deftext = "CURRENT_TIMESTAMP";
  704                     else
  705                         deftext = sqlfix(deftext);
  706                 }
  707                 else if(t=="CHARSET")
  708                 {
  709                 GotCharSet:
  710                     SkipBlank(s, c);
  711                     bool was_quoted=false;
  712                     charset = ucase(GetStringValue(s, c, was_quoted));
  713                 }
  714                 else if(t=="CHARACTER")
  715                 {
  716                     SkipBlank(s,c);
  717                     std::string word = GetUcaseWord(s,c);
  718                     if(word == "SET") goto GotCharSet;
  719                 }
  720                 else if(t=="COLLATE")
  721                 {
  722                     SkipBlank(s, c);
  723                     bool was_quoted=false;
  724                     collate = ucase(GetStringValue(s, c, was_quoted));
  725                 }
  726                 else if(t=="COMMENT")
  727                 {
  728                     SkipBlank(s, c);
  729                     bool was_quoted=true;
  730                     comment = ucase(GetStringValue(s, c, was_quoted));
  731                     //fprintf(stderr, "'%s' \n", comment.c_str());
  732                 }
  733                 else
  734                     throw("What is '" + t + "' of '" + s + "'? Type is '" + type + "'");
  735             }
  736             //fprintf(stderr, "(Done)\n");
  737         }
  738         
  739         // Discard defaults and rename the type if necessary.
  740                 
  741         if(type=="INT") { if(width==11)width=0; }
  742         else if(type=="BIGINT") { if(width==20)width=0; }
  743         else if(type=="TINYINT") { if(width==4)width=0; }
  744         else if(type=="SMALLINT") { if(width==6)width=0; }
  745         else if(type=="DOUBLE") { if(width==16 && prec==4)width=prec=0; }
  746         else if(type=="FLOAT") { if(width==10 && prec==2)width=prec=0; }
  747         else if(type=="CHAR") { if(width==1)width=0; }
  748         else if(type=="VARCHAR" && width<4)type="CHAR";
  749         else if(type=="DATETIME" && deftext.empty()) deftext=sqlfix("0000-00-00 00:00:00");
  750         else if(type=="DATE" && deftext.empty()) deftext=sqlfix("0000-00-00");
  751         else if(type=="TIME" && deftext.empty()) deftext=sqlfix("00:00:00");
  752         else if(type=="TIMESTAMP") { width=(width+1)&~1; if(width>=14)width=0; }
  753         
  754         FixCharset();
  755         RebuildContent();
  756     }
  757 };
  758 struct ForeignKeyData
  759 {
  760     string ConstraintName;
  761     string IndexFields;
  762     string ReferenceTable;
  763     string ReferenceFields;
  764     string OnDelete;
  765     string OnUpdate;
  766     
  767     ForeignKeyData()
  768         : ConstraintName(), IndexFields(),
  769           ReferenceTable(), ReferenceFields(),
  770           OnDelete(), OnUpdate()
  771     {
  772     }
  773     
  774     const std::string MakeString() const
  775     {
  776         std::string result;
  777         result = "(" + IndexFields + ")REFERENCES "+QuoteWord(ReferenceTable)
  778                + "(" + ReferenceFields + ")";
  779         if(!OnDelete.empty()) result += "ON DELETE " + OnDelete + " ";
  780         if(!OnUpdate.empty()) result += "ON UPDATE " + OnUpdate + " ";
  781         return result;
  782     }
  783     
  784     bool operator!= (const ForeignKeyData& b) const { return !(*this == b); }
  785     bool operator== (const ForeignKeyData& b) const
  786     {
  787         return IndexFields    == b.IndexFields
  788             && ReferenceTable == b.ReferenceTable
  789             && ReferenceFields == b.ReferenceFields
  790             && OnDelete == b.OnDelete
  791             && OnUpdate == b.OnUpdate;
  792     }
  793 };
  794 
  795 struct SimulateFieldOrder
  796 {
  797     std::list<std::string> fields;
  798 public:
  799     SimulateFieldOrder() : fields() {}
  800 
  801     void Add(const std::string& s)
  802     {
  803         fields.push_back(s);
  804     }
  805     void Drop(const std::string& s)
  806     {
  807         std::list<std::string>::iterator i;
  808         for(i=fields.begin(); i!=fields.end(); ++i)
  809             if(*i == s) { fields.erase(i); break; }
  810     }
  811     void Add_After(const std::string& prev, const std::string& s)
  812     {
  813         if(prev.empty()) { fields.push_front(s); return; }
  814         
  815         std::list<std::string>::iterator i;
  816         for(i=fields.begin(); i!=fields.end(); ++i)
  817             if(*i==prev)
  818             {
  819                 ++i;
  820                 fields.insert(i, s);
  821                 return;
  822             }        
  823         fields.push_back(s);
  824     }
  825     void Move_After(const std::string& prev, const std::string& s)
  826     {
  827         Drop(s);
  828         Add_After(prev, s);
  829     }
  830     const std::string FindPrecedent(const std::string& s) const
  831     {
  832         std::string result = "";
  833         std::list<std::string>::const_iterator i;
  834         for(i=fields.begin(); i!=fields.end(); ++i)
  835         {
  836             if(*i == s) break;
  837             result = *i;
  838         }
  839         return result;
  840     }
  841 };
  842 
  843 typedef pair<string, fieldtype> fieldpairtype;
  844 typedef vector<fieldpairtype> fieldlisttype;
  845 typedef pair<string, string> keypairtype;
  846 typedef vector<keypairtype> keylisttype;
  847 typedef pair<string, ForeignKeyData> foreignkeypairtype;
  848 typedef vector<foreignkeypairtype> foreignkeylisttype;
  849 
  850 string database = "winnie3";
  851 
  852 class InputFailedError { public: bool pip; InputFailedError(bool p) : pip(p) { } };
  853 
  854 static bool CaseEqual(const string &a, const string &b)
  855 {
  856     size_t aasi = a.size();
  857     size_t beesi= b.size();
  858     if(aasi != beesi)return false;
  859     for(size_t p=0; p<aasi; ++p)
  860         if(toupper(a[p]) != toupper(b[p]))
  861             return false;
  862     return true;
  863 }
  864 
  865 class Table
  866 {
  867     string nam;
  868     string typ;
  869     string primarykey;
  870     string primarykeytype;
  871     string charset;
  872     string collate;
  873     
  874 public:
  875     fieldlisttype fields;
  876     keylisttype keys;
  877     foreignkeylisttype fks;
  878     
  879 #ifdef SUPPORT_INSERTS
  880     vector<string> inserts;
  881 #endif
  882     
  883     Table(const string &name, const std::string& type="")
  884         : nam(name), typ(type),
  885           primarykey(), primarykeytype(),
  886           charset(), collate(),
  887           fields(), keys(), fks()
  888 #ifdef SUPPORT_INSERTS
  889           , inserts()
  890 #endif
  891     {
  892     }
  893     void settype(const std::string& p) { typ = p; }
  894     void setcharset(const std::string& p) { charset=p; }
  895     void setcollate(const std::string& p) { collate=p; }
  896     void add(const string &name, const string &type)
  897     {
  898         //fprintf(stderr, "*** Table '%s': '%s' = '%s'\n", nam.c_str(), name.c_str(), type.c_str());
  899         fields.push_back(fieldpairtype(name, fieldtype(type)));
  900     }
  901     void addkey(const string &name, const string &type)
  902     {
  903         keys.push_back(keypairtype(name, type));
  904     }
  905     void addforeign(const string& indexcols, const ForeignKeyData& details)
  906     {
  907         fks.push_back(foreignkeypairtype(indexcols, details));
  908     }
  909     void setprimary(const string &n, const string& indextype)
  910     {
  911         primarykey = n;
  912         primarykeytype = indextype;
  913         for(;;)
  914         {
  915             string::size_type a = primarykey.find(", ");
  916             if(a==primarykey.npos)break;
  917             primarykey.erase(a+1, 1); // convert ", " into ","
  918         }
  919         // fprintf(stderr, "*** Table '%s': primary = '%s'\n", nam.c_str(), n.c_str());
  920     }
  921     inline const string &name() const { return nam; }
  922     inline const string &type() const { return typ; }
  923     inline const string &pkey() const { return primarykey; }
  924     inline const string &pkeytype() const { return primarykeytype; }
  925     bool hasfield(const string &n) const
  926     {
  927         size_t a, b=fields.size();
  928         for(a=0; a<b; ++a)
  929             if(CaseEqual(fields[a].first, n))return true;
  930         return false;
  931     }
  932     const fieldpairtype &getfield(const string &n) const
  933     {
  934         size_t a, b=fields.size();
  935         for(a=0; a<b; ++a)
  936             if(CaseEqual(fields[a].first, n))
  937                 return fields[a];
  938         return fields[0]; // error
  939     }
  940     
  941     const string getkey(const string &search) const
  942     {
  943         size_t a, b=keys.size();
  944         for(a=0; a<b; ++a)
  945             if(CaseEqual(keys[a].first, search))
  946                 return keys[a].second;
  947         return "";
  948     }
  949     const ForeignKeyData getfk(const string &search) const
  950     {
  951         size_t a, b=fks.size();
  952         for(a=0; a<b; ++a)
  953             if(CaseEqual(fks[a].first, search))
  954                 return fks[a].second;
  955         return ForeignKeyData();
  956     }
  957 #ifdef SUPPORT_INSERTS
  958     void Insert(const string &s)
  959     {
  960         inserts.push_back(s);
  961     }
  962     void FlushInserts() const
  963     {
  964         size_t a, b=inserts.size();
  965         for(a=0; a<b; ++a)
  966             printf("insert into %s %s;\n", nam.c_str(), inserts[a].c_str());
  967     }
  968     inline bool tyhjataulu() const { return inserts.size() == 0; }
  969 #endif
  970     void postfix()
  971     {
  972         size_t a;
  973         /* If the table has variable-length fields, then
  974          * all CHAR fields longer than 3 characters are
  975          * automatically turned into VARCHAR fields by MySQL.
  976          */
  977         bool hasvar = false;
  978         for(a=0; a<fields.size(); ++a)
  979         {
  980             if(fields[a].second.gettype()=="VARCHAR"
  981             || fields[a].second.gettype()=="TEXT"
  982             || fields[a].second.gettype()=="TINYTEXT"
  983             || fields[a].second.gettype()=="MEDIUMTEXT"
  984             || fields[a].second.gettype()=="LONGTEXT"
  985             || fields[a].second.gettype()=="LONGBLOB"
  986             || fields[a].second.gettype()=="MEDIUMBLOB"
  987             || fields[a].second.gettype()=="BLOB")
  988                 hasvar = true;
  989         }
  990         if(hasvar)
  991         {
  992             for(a=0; a<fields.size(); ++a)
  993             {
  994                 if(fields[a].second.gettype() == "CHAR"
  995                 && fields[a].second.getwidth() > 3)
  996                     fields[a].second.settype("VARCHAR");
  997             }
  998         }
  999         /* If the table has a collate or charset set, force
 1000          * it into every column where it is not specified */
 1001         for(a=0; a<fields.size(); ++a)
 1002         {
 1003             if(fields[a].second.getcollate().empty() && !collate.empty())
 1004                 fields[a].second.setcollate(collate);
 1005             if(fields[a].second.getcharset().empty() && !charset.empty())
 1006                 fields[a].second.setcharset(charset);
 1007         }
 1008     }
 1009 };
 1010 
 1011 typedef pair<string, Table> tabletype;
 1012 typedef map<string, Table> tablelisttype;
 1013 
 1014 class sqldump
 1015 {
 1016     tablelisttype tables;
 1017     
 1018     void load(const string &s) // analyzes the result
 1019     {
 1020         string Constraint;
 1021         
 1022         size_t a, b=s.size();
 1023         for(a=0; a<b; ++a)
 1024         {
 1025             if(s[a]=='#')
 1026             {
 1027                 while(a+1<b && s[++a]!='\n') {}
 1028                 continue;
 1029             }
 1030             if((a+1) < b && s[a]=='-' && s[a+1]=='-')
 1031             {
 1032                 while(a < b && s[a] != '\n')++a;
 1033                 continue;
 1034             }
 1035             
 1036             if(isempty(s[a]))continue;
 1037             //fprintf(stderr, "*** Testing: '%s'\n", s.substr(a,50).c_str());
 1038             if(startwith("CREATE ", s, a))
 1039             {
 1040                 a += 7;
 1041                 SkipBlank(s, a);
 1042                 if(startwith("INDEX ", s, a))
 1043                 {
 1044                     GetWord(s,a); // Eat "INDEX"
 1045                     SkipBlank(s,a);
 1046                     std::string indexname = GetWord(s,a); // Eat key name
 1047                     SkipBlank(s,a);
 1048                     std::string onword = GetUcaseWord(s,a);
 1049                     if(onword != "ON")
 1050                     {
 1051                         throw("CREATE INDEX "+indexname+"... '"+onword+"'? I expected 'ON'");
 1052                     }
 1053                     SkipBlank(s,a);
 1054                     std::string tablename = GetWord(s,a);
 1055                     SkipBlank(s,a);
 1056 
 1057                     string content;
 1058                     int sku=0;
 1059                     while(a < b)
 1060                     {
 1061                         SkipBlank(s, a);
 1062                         if(a >= b) break;
 1063                         
 1064                         size_t c = a;
 1065                         content += QuoteWord(GetWord(s, a));
 1066                         if(a != c) continue;
 1067                         content += s[a];
 1068                         
 1069                         if(s[a] == '(')++sku;
 1070                         else if(s[a] == ')')
 1071                         {
 1072                             if(!--sku)break;
 1073                         }
 1074                         ++a;
 1075                     }
 1076                     ++a; // skip ')';
 1077                     tablelisttype::iterator
 1078                         i = tables.find(tablename);
 1079                     if(i == tables.end())
 1080                     {
 1081                         throw "No table '"+tablename+"' to add key '"+indexname+"' into";
 1082                     }
 1083                     i->second.addkey(indexname, content);
 1084                     goto SkipCmd;
 1085                 }
 1086                 if(!startwith("TABLE ", s, a))goto SkipCmd;
 1087                 a += 6;
 1088                 Table t(GetWord(s, a));
 1089                 SkipBlank(s, a);
 1090                 if(a < b)
 1091                 {
 1092                     if(s[a] == '(') ++a;
 1093                     else
 1094                         throw "Parse error: TABLE "+t.name()+" was not followed by '('";
 1095                 }
 1096                 while(a < b)
 1097                 {
 1098                     SkipBlank(s, a);
 1099                     if(s[a]=='#')
 1100                     {
 1101                         while(a+1<b && s[++a]!='\n') {}
 1102                         continue;
 1103                     }
 1104                     string fldname = GetWord(s, a);
 1105                     
 1106                     if(fldname.empty())
 1107                     {
 1108                         if(a < b && s[a] == ')')
 1109                         {
 1110                             throw "Error: Table "+t.name()+" ends with comma.";
 1111                         }
 1112                         throw "Parse error: Was not able to get a field name in "+t.name()+".";
 1113                     }
 1114                     
 1115                     SkipBlank(s, a);
 1116                     if(ucase(fldname) == "PRIMARY")
 1117                     {
 1118                         if(GetUcaseWord(s, a) != "KEY")
 1119                         {
 1120                             throw string("Primary what?");
 1121                         }
 1122                         
 1123                         string content;
 1124                         SkipBlank(s, a);
 1125                         
 1126                         string indextype;
 1127                         if(GetUcaseWord(s, a) == "USING")
 1128                         {
 1129                             SkipBlank(s, a);
 1130                             indextype = GetUcaseWord(s, a);
 1131                             SkipBlank(s, a);
 1132                         }
 1133                         
 1134                         if(s[a]=='(')++a;
 1135                         while(a < b)
 1136                         {
 1137                             SkipBlank(s, a);
 1138                             size_t c=a;
 1139                             content += QuoteWord(GetWord(s, a));
 1140                             if(a == c)
 1141                             {
 1142                                 throw string("Invalid parameters for PRIMARY KEY (got so far: " + content + ")");
 1143                             }
 1144                             if(s[a]=='(')
 1145                             {
 1146                                 while(a < b && s[a]!=')') content += s[a++];
 1147                                 if(a < b && s[a]==')') { content += s[a++]; }
 1148                             }
 1149                             if(a<b && s[a]==',') { content += s[a++]; continue; }
 1150                             break;
 1151                         }
 1152                         if(a >= b || s[a] != ')')
 1153                         {
 1154                             throw string("Expected ')' after PRIMARY KEY("+content+")");
 1155                         }
 1156                         ++a;
 1157                         t.setprimary(content, indextype);
 1158                     }
 1159                     else if(ucase(fldname) == "KEY")
 1160                     {
 1161                         string indexname = GetWord(s, a);
 1162                         string content;
 1163                         int sku=0;
 1164                         while(a < b)
 1165                         {
 1166                             SkipBlank(s, a);
 1167                             if(a >= b) break;
 1168                             
 1169                             size_t c = a;
 1170                             content += QuoteWord(GetWord(s, a));
 1171                             if(a != c) continue;
 1172                             content += s[a];
 1173                             
 1174                             if(s[a] == '(')++sku;
 1175                             else if(s[a] == ')')
 1176                             {
 1177                                 if(!--sku)break;
 1178                             }
 1179                             ++a;
 1180                         }
 1181                         ++a;
 1182                         t.addkey(indexname, content);
 1183                     }
 1184                     else if(ucase(fldname) == "UNIQUE"
 1185                          || ucase(fldname) == "FULLTEXT"
 1186                          || ucase(fldname) == "SPATIAL")
 1187                     {
 1188                     RetryUnique:
 1189                         string k = ucase(fldname) == "UNIQUE"
 1190                                  ? "#"
 1191                                  : ucase(fldname) == "SPATIAL"
 1192                                  ? "&"
 1193                                  : "*";
 1194                         k += GetWord(s, a);
 1195                         if(ucase(k.substr(1)) == "KEY")
 1196                         {
 1197                             SkipBlank(s, a);
 1198                             goto RetryUnique;
 1199                         }
 1200                         
 1201                         int sku=0;
 1202                         string content;
 1203                         while(a < b)
 1204                         {
 1205                             SkipBlank(s, a);
 1206                             if(a >= b) break;
 1207                             
 1208                             size_t c = a;
 1209                             content += QuoteWord(GetWord(s, a));
 1210                             if(a != c) continue;
 1211                             content += s[a];
 1212                             
 1213                             if(s[a] == '(')++sku;
 1214                             if(s[a] == ')')
 1215                             {
 1216                                 if(!--sku)break;
 1217                             }
 1218                             ++a;
 1219                         }
 1220                         if(a < b) ++a;
 1221                         t.addkey(k, content);
 1222                     }
 1223                     else if(ucase(fldname) == "CONSTRAINT")
 1224                     {
 1225                         /* remember until the next FK definition */
 1226                         SkipBlank(s, a);
 1227                         Constraint = GetWord(s, a);
 1228                     }
 1229                     else if(ucase(fldname) == "FOREIGN")
 1230                     {
 1231                         ForeignKeyData fk;
 1232                         fk.ConstraintName = Constraint;
 1233 
 1234                         SkipBlank(s, a);
 1235                         if(GetUcaseWord(s, a) != "KEY")
 1236                         {
 1237                             throw string("Foreign what?");
 1238                         }
 1239 
 1240                         SkipBlank(s, a);
 1241                         if(a<b && s[a]=='(')
 1242                             ++a;
 1243                         else
 1244                             throw string("Expected '(' after FOREIGN KEY");
 1245                         while(a < b && s[a] != ')')
 1246                         {
 1247                             if(s[a] == ',') { fk.IndexFields += s[a++]; SkipBlank(s, a);; continue; }
 1248                             string part = GetWord(s, a);
 1249                             if(part.empty())
 1250                             {
 1251                                 throw string("Couldn't get FK part (got so far: "+fk.IndexFields+")");
 1252                             }
 1253                             fk.IndexFields += QuoteWord(part);
 1254                             SkipBlank(s, a);
 1255                         }
 1256                         if(a < b && s[a] == ')')
 1257                             ++a;
 1258                         else
 1259                             throw string("Expected ')' after FOREIGN KEY("+fk.IndexFields+")");
 1260                         
 1261                         SkipBlank(s, a);
 1262                         if(GetUcaseWord(s, a) != "REFERENCES")
 1263                         {
 1264                             throw string("Expected: 'REFERENCES'");
 1265                         }
 1266                         
 1267                         SkipBlank(s, a);
 1268                         fk.ReferenceTable = GetWord(s, a);
 1269                         SkipBlank(s, a);
 1270                         if(s[a]=='(')
 1271                             ++a;
 1272                         else
 1273                             throw string("Expected '(' after REFERENCES");
 1274                         while(a < b && s[a] != ')')
 1275                         {
 1276                             if(s[a] == ',') { fk.ReferenceFields += s[a++]; SkipBlank(s, a);; continue; }
 1277                             string part = GetWord(s, a);
 1278                             if(part.empty())
 1279                             {
 1280                                 throw string("Couldn't get FK REF part ("+fk.IndexFields+")ref=("+fk.ReferenceFields+")");
 1281                             }
 1282                             fk.ReferenceFields += QuoteWord(part);
 1283                             SkipBlank(s, a);
 1284                         }
 1285                         if(a < b && s[a] == ')')
 1286                             ++a;
 1287                         else
 1288                             throw string("Expected ')' after REFERENCES("+fk.ReferenceFields+")");
 1289                         
 1290                         for(;;)
 1291                         {
 1292                             SkipBlank(s, a);
 1293                             if(startwith("ON DELETE", s, a))
 1294                             {
 1295                                 a += 2+1+6; SkipBlank(s, a);
 1296                                 string part = GetUcaseWord(s, a);
 1297                                 if(part == "NO" || part == "SET")
 1298                                 {
 1299                                     SkipBlank(s, a);
 1300                                     part += " " + GetWord(s, a);
 1301                                 }
 1302                                 fk.OnDelete = part;
 1303                                 
 1304                                 if(fk.OnDelete == "RESTRICT") fk.OnDelete = "";                                
 1305                                 continue;
 1306                             }
 1307                             if(startwith("ON UPDATE", s, a))
 1308                             {
 1309                                 a += 2+1+6; SkipBlank(s, a);
 1310                                 string part = GetUcaseWord(s, a);
 1311                                 if(part == "NO" || part == "SET")
 1312                                 {
 1313                                     SkipBlank(s, a);
 1314                                     part += " " + GetWord(s, a);
 1315                                 }
 1316                                 fk.OnUpdate = part;
 1317                                 continue;
 1318                             }
 1319                             break;
 1320                         }
 1321                         
 1322                         if(fk.ConstraintName.empty())
 1323                         {
 1324                             static size_t autonum = 1;
 1325                             stringstream tmp;
 1326                             tmp << "autogenerated_constraint_";
 1327                             tmp << autonum++;
 1328                             fk.ConstraintName = tmp.str();
 1329                         }
 1330                         
 1331                         t.addforeign(fk.IndexFields, fk);
 1332                         
 1333                         Constraint = "";
 1334                     }
 1335                     else 
 1336                     {
 1337                         // Use a very simplistic parser to eat the parameters of this variable
 1338                         char quotechar = 0;
 1339                         bool empti = true;
 1340                         int depth = 0;
 1341                         string content;
 1342                         while(a < b && (depth || (s[a] != ',' && s[a] != ')') || quotechar))
 1343                         {
 1344                             if(s[a]=='#' && !quotechar)
 1345                             {
 1346                                 while(a+1<b && s[++a]!='\n') {}
 1347                                 continue;
 1348                             }
 1349                             if(isempty(s[a]))
 1350                                 empti = true;
 1351                             else
 1352                             {
 1353                                 if(empti && !content.empty())content += ' ';
 1354                                 content += quotechar ? s[a] : (char)toupper(s[a]);
 1355                                 empti = false;
 1356                             }
 1357                             if(s[a] == '(')++depth;
 1358                             else if(s[a] == ')')--depth;
 1359                             if(s[a] == '\\')content += s[++a];
 1360                             else if(quotechar && s[a] == quotechar) quotechar=0;
 1361                             else if(!quotechar && s[a] == '\'')  quotechar=s[a];
 1362                             else if(!quotechar && s[a] == '"')  quotechar=s[a];
 1363                             ++a;
 1364                         }
 1365                         if(content.find("PRIMARY KEY") != content.npos)
 1366                         {
 1367                             t.setprimary(QuoteWord(fldname), "");
 1368                         }
 1369                         if(content.empty())
 1370                         {
 1371                             throw string("Parse error: Got empty type for field '" + fldname + "' in "+t.name());
 1372                         }
 1373                         t.add(fldname, content);
 1374                     }
 1375                 rechar_table:
 1376                     SkipBlank(s, a);
 1377                     if(s[a] == ',') { ++a; continue; }
 1378                     if(s[a] == ')')break;
 1379                     if(s[a] == '#')
 1380                     {
 1381                         while(a+1<b && s[++a]!='\n') {}
 1382                         goto rechar_table;
 1383                     }
 1384                     
 1385                     // We don't hold a comma necessary so that CONSTRAINT will work.
 1386                     if(!ischar(s[a]))
 1387                     {
 1388                         std::string tmp; tmp += s[a];
 1389                         throw string("Parse error: Unrecognized character '" + tmp + "' in table "+t.name()
 1390                                     +": "+s.substr(a,10)+"...");
 1391                     }
 1392                 }
 1393                 while(a<b)
 1394                 {
 1395                     SkipBlank(s, a);
 1396                     std::string word = GetUcaseWord(s, a);
 1397                     if(word == "CHARSET")
 1398                     {
 1399                     GotCharSet:
 1400                         SkipBlank(s,a);
 1401                         if(a<b&&s[a] == '=')
 1402                         {
 1403                             ++a;
 1404                             std::string type = GetUcaseWord(s,a);
 1405                             t.setcharset(type);
 1406                         }
 1407                         continue;
 1408                     }
 1409                     if(word == "CHARACTER")
 1410                     {
 1411                         SkipBlank(s,a);
 1412                         word = GetUcaseWord(s,a);
 1413                         if(word == "SET") goto GotCharSet;
 1414                     }
 1415                     if(word == "COLLATE")
 1416                     {
 1417                         SkipBlank(s,a);
 1418                         if(a<b&&s[a] == '=')
 1419                         {
 1420                             ++a;
 1421                             std::string type = GetUcaseWord(s,a);
 1422                             t.setcollate(type);
 1423                         }
 1424                         continue;
 1425                     }
 1426                     if(word == "TYPE")
 1427                     {
 1428                         SkipBlank(s,a);
 1429                         if(a<b&&s[a] == '=')
 1430                         {
 1431                             ++a;
 1432                             std::string type = GetUcaseWord(s,a);
 1433                             t.settype(type);
 1434                         }
 1435                         continue;
 1436                     }
 1437                     if(s[a] == ';') break;
 1438                     ++a;
 1439                 }
 1440                 t.postfix();
 1441                 tables.insert(tabletype(t.name(), t));
 1442                 
 1443                 continue;
 1444             }
 1445 #ifdef SUPPORT_INSERTS
 1446             if(startwith("INSERT INTO ", s, a))
 1447             {
 1448                 a += 11;
 1449                 SkipBlank(s, a);;
 1450                 string tabname = GetWord(s, a);
 1451                 SkipBlank(s, a);;
 1452                 bool quotechar=false;
 1453                 string content;
 1454                 while(a < b && (s[a] != ';' || quotechar))
 1455                 {
 1456                     content += s[a];
 1457                     if(s[a] == '\\')content += s[++a];
 1458                     else if(s[a] == '\'')quotechar = !quotechar;
 1459                     ++a;
 1460                 }
 1461                 if(!nodata)
 1462                     tables.find(tabname)->second.Insert(content);
 1463             }
 1464 #endif
 1465 SkipCmd:    bool quotechar=false;
 1466             while(a < b && (s[a] != ';' || quotechar))
 1467             {
 1468                 if(s[a] == '\\')++a;
 1469                 else if(s[a] == '\'')quotechar = !quotechar;
 1470                 ++a;
 1471             }
 1472         }
 1473     }
 1474 public:
 1475     sqldump(FILE *fp, bool ispipe=false)
 1476         : tables()
 1477     {
 1478         if(!fp)
 1479         {
 1480             throw InputFailedError(ispipe);
 1481         }
 1482         fprintf(stderr, "*** Reading%s...\n", ispipe?" pipe":" file");
 1483         char Buf[4096];
 1484         string s;
 1485         for(;;)
 1486         {
 1487             size_t n = fread(Buf, 1, sizeof Buf, fp);
 1488             if(n <= 0)break;
 1489             s += string(Buf, n);
 1490         }
 1491         if(ispipe)
 1492             pclose(fp);
 1493         else
 1494             fclose(fp);
 1495         fprintf(stderr, "*** Analyzing...\n");
 1496         load(s);
 1497     }
 1498     
 1499     
 1500 #if 1
 1501     class alter_table_data
 1502     {
 1503     public:
 1504         std::vector<std::string/*field name*/> DROP_FIELDS;
 1505         std::vector<std::string/*field name*/> DROP_DEFAULTS;
 1506         std::vector<std::string/*index name*/> DROP_INDEXES;
 1507         std::vector<std::string/*fk name*/> DROP_FOREIGN_KEYS;
 1508         bool DROP_PRIMARY_KEY;
 1509 
 1510         std::vector<std::pair<std::string/*field name*/, std::string/*field def*/> > CHANGE_FIELDS;
 1511         std::vector<std::pair<std::string/*field name*/, std::string/*field def*/> > ADD_FIELDS;
 1512         std::vector<std::pair<std::string/*index name*/, std::string/*index def*/> > ADD_INDEXES;
 1513         std::vector<std::string/*fk content*/> ADD_FOREIGN_KEYS;
 1514         std::pair<std::string, std::string> ADD_PRIMARY;
 1515     public:
 1516         alter_table_data()
 1517             : DROP_FIELDS(), DROP_DEFAULTS(),
 1518               DROP_INDEXES(), DROP_FOREIGN_KEYS(),
 1519               DROP_PRIMARY_KEY(), CHANGE_FIELDS(),
 1520               ADD_FIELDS(), ADD_INDEXES(),
 1521               ADD_FOREIGN_KEYS(), ADD_PRIMARY()
 1522         {
 1523         }
 1524     };
 1525 #endif
 1526     class alter_db_data
 1527     {
 1528     public:
 1529 #if 1
 1530         std::vector<std::string/*table name*/> DROP_TABLES;
 1531         std::vector<std::string/*table content*/> CREATE_TABLES;
 1532         std::map<std::string/*table name*/, alter_table_data> ALTER_TABLES;
 1533 #endif
 1534     public:
 1535         alter_db_data()
 1536             : DROP_TABLES(), CREATE_TABLES(), ALTER_TABLES()
 1537         {
 1538         }
 1539         
 1540         void Flush()
 1541         {
 1542             typedef std::vector<std::string>::const_iterator seti;
 1543             typedef std::map<std::string, std::string>::const_iterator mapi;
 1544             typedef std::vector<std::pair<std::string, std::string> >::const_iterator veci;
 1545             
 1546             /* First drop foreign keys to prevent innodb from complaining
 1547              * when tables/keys are dropped */
 1548             std::map<std::string, alter_table_data>::const_iterator i;
 1549             for(i=ALTER_TABLES.begin(); i!=ALTER_TABLES.end(); ++i)
 1550             {
 1551                 const std::string& tablename = i->first;
 1552                 const alter_table_data& data = i->second;
 1553 
 1554                 std::vector<std::string> alter_list;
 1555 
 1556                 for(seti j=data.DROP_FOREIGN_KEYS.begin(); j!=data.DROP_FOREIGN_KEYS.end(); ++j)
 1557                     alter_list.push_back("DROP FOREIGN KEY " + *j);
 1558                 FlushAlterList(tablename, alter_list);
 1559             }
 1560 
 1561             for(size_t a=0; a<DROP_TABLES.size(); ++a)
 1562                 printf("DROP TABLE %s;\n", QuoteWord(DROP_TABLES[a]).c_str());
 1563             
 1564             for(size_t a=0; a<CREATE_TABLES.size(); ++a)
 1565                 printf("CREATE TABLE %s;\n", CREATE_TABLES[a].c_str());
 1566             
 1567             for(i=ALTER_TABLES.begin(); i!=ALTER_TABLES.end(); ++i)
 1568             {
 1569                 const std::string& tablename = i->first;
 1570                 const alter_table_data& data = i->second;
 1571                 
 1572                 std::vector<std::string> alter_list;
 1573                 
 1574                 // Drop fields.
 1575                 for(seti j=data.DROP_FIELDS.begin(); j!=data.DROP_FIELDS.end(); ++j)
 1576                     alter_list.push_back("DROP " + QuoteWord(*j));
 1577                 for(seti j=data.DROP_DEFAULTS.begin(); j!=data.DROP_DEFAULTS.end(); ++j)
 1578                     alter_list.push_back("ALTER " + QuoteWord(*j) + " DROP DEFAULT");
 1579                 for(seti j=data.DROP_INDEXES.begin(); j!=data.DROP_INDEXES.end(); ++j)
 1580                 {
 1581                     if(UseCreateIndex)
 1582                     {
 1583                         printf("DROP INDEX %s ON %s;\n",
 1584                             j->c_str(),
 1585                             tablename.c_str());
 1586                     }
 1587                     else
 1588                     {
 1589                         alter_list.push_back("DROP INDEX " + *j);
 1590                     }
 1591                 }
 1592                 if(data.DROP_PRIMARY_KEY)
 1593                     alter_list.push_back("DROP PRIMARY KEY");
 1594                 
 1595                 FlushAlterList(tablename, alter_list);
 1596                 
 1597                 // Add fields.
 1598                 for(veci j=data.ADD_FIELDS.begin(); j!=data.ADD_FIELDS.end(); ++j)
 1599                     alter_list.push_back("ADD " + QuoteWord(j->first) + " " + j->second);
 1600                 
 1601                 if(!data.ADD_FIELDS.empty() && !data.CHANGE_FIELDS.empty())
 1602                 {
 1603                     // Flush here so that field orders are proper
 1604                     FlushAlterList(tablename, alter_list);
 1605                 }
 1606                 
 1607                 // Last change fields.
 1608                 // It's done last so that changing the field order will work properly.
 1609                 for(veci j=data.CHANGE_FIELDS.begin(); j!=data.CHANGE_FIELDS.end(); ++j)
 1610                     alter_list.push_back("CHANGE " + QuoteWord(j->first) + " " + j->second);
 1611                     
 1612                 FlushAlterList(tablename, alter_list);
 1613                 
 1614                 for(veci j=data.ADD_INDEXES.begin(); j!=data.ADD_INDEXES.end(); ++j)
 1615                 {
 1616                     const char *mkey = j->first.c_str();
 1617                     if(UseCreateIndex)
 1618                     {
 1619                         printf("CREATE %sINDEX %s ON %s%s;\n",
 1620                             (mkey[0]=='#' ? "UNIQUE "
 1621                             :mkey[0]=='&' ? "SPATIAL "
 1622                             :mkey[0]=='*' ? "FULLTEXT "
 1623                             : ""),
 1624                              QuoteWord((mkey[0]=='#' || mkey[0]=='&' || mkey[0]=='*') ? mkey+1 : mkey).c_str(),
 1625                              QuoteWord(tablename).c_str(),
 1626                              j->second.c_str());
 1627                     }
 1628                     else
 1629                     {
 1630                         alter_list.push_back("ADD " + std::string
 1631                             (mkey[0]=='#' ? "UNIQUE"
 1632                             :mkey[0]=='&' ? "SPATIAL"
 1633                             :mkey[0]=='*' ? "FULLTEXT"
 1634                             : "KEY")
 1635                             + " "
 1636                             + QuoteWord((mkey[0]=='#' || mkey[0]=='&' || mkey[0]=='*') ? mkey+1 : mkey)
 1637                             + j->second);
 1638                     }
 1639                 }
 1640                 
 1641                 if(!data.ADD_PRIMARY.first.empty())
 1642                 {
 1643                     std::string cmd = "ADD PRIMARY KEY";
 1644                     if(!data.ADD_PRIMARY.second.empty())
 1645                         cmd += " USING " + data.ADD_PRIMARY.second;
 1646                     alter_list.push_back(cmd + "(" + data.ADD_PRIMARY.first + ")");
 1647                 }
 1648 
 1649                 FlushAlterList(tablename, alter_list);
 1650             }
 1651             
 1652             /* Last add foreign keys. It must be done only after the keys are there. */
 1653             for(i=ALTER_TABLES.begin(); i!=ALTER_TABLES.end(); ++i)
 1654             {
 1655                 const std::string& tablename = i->first;
 1656                 const alter_table_data& data = i->second;
 1657                 
 1658                 std::vector<std::string> alter_list;
 1659                 
 1660                 for(seti j=data.ADD_FOREIGN_KEYS.begin(); j!=data.ADD_FOREIGN_KEYS.end(); ++j)
 1661                 {
 1662                     alter_list.push_back("ADD FOREIGN KEY" + *j);
 1663                 }
 1664 
 1665                 FlushAlterList(tablename, alter_list);
 1666             }
 1667         }
 1668     private:
 1669         void FlushAlterList(const std::string& tablename,
 1670                             std::vector<std::string>& alter_list)
 1671         {
 1672             if(!alter_list.empty())
 1673             {
 1674                 //printf("-- Table %s --\n", tablename.c_str());
 1675                 
 1676                 printf("ALTER TABLE %s ", tablename.c_str());
 1677                 for(size_t a=0; a<alter_list.size(); ++a)
 1678                 {
 1679                     if(a>0)printf(", ");
 1680                     printf("%s", alter_list[a].c_str());
 1681                 }
 1682                 printf(";\n");
 1683                 alter_list.clear();
 1684             }
 1685         }
 1686     };
 1687     
 1688     void updatewith(const sqldump &newdesc)
 1689     {
 1690         fprintf(stderr, "*** Combining...\n");
 1691         
 1692         if(tables.empty())
 1693             printf("CREATE DATABASE /*!32312 IF NOT EXISTS*/ %s;\n", QuoteWord(database).c_str());
 1694         
 1695         printf("USE %s;\n", QuoteWord(database).c_str());
 1696         
 1697         alter_db_data changes;
 1698         
 1699         /* Find things that no longer apply */
 1700         for(tablelisttype::const_iterator i=tables.begin(); i!=tables.end(); ++i)
 1701         {
 1702             const Table& oldtable = i->second;
 1703             
 1704             const std::string& tablename = i->first;
 1705             
 1706             tablelisttype::const_iterator j = newdesc.tables.find(tablename);
 1707             if(j == newdesc.tables.end())
 1708             {
 1709                 /* If the table no longer exists */
 1710                 changes.DROP_TABLES.push_back(tablename);
 1711                 continue;
 1712             }
 1713             
 1714             const Table& newtable = j->second;
 1715             
 1716             changes.ALTER_TABLES[tablename].DROP_PRIMARY_KEY=false;
 1717             
 1718             // Find out which fields to drop
 1719             fieldlisttype::const_iterator k;
 1720             for(k = oldtable.fields.begin(); k != oldtable.fields.end(); ++k)
 1721             {
 1722                 const string& fieldname = k->first;
 1723                 if(!newtable.hasfield(fieldname))
 1724                     changes.ALTER_TABLES[tablename].DROP_FIELDS.push_back(fieldname);
 1725             }
 1726             
 1727             /* Drop extinct indices */
 1728             keylisttype::const_iterator m;
 1729             for(m = oldtable.keys.begin(); m != oldtable.keys.end(); ++m)
 1730             {
 1731                 const string& keyname = m->first;
 1732                 const string oldkey   = newtable.getkey(keyname);
 1733                 if(oldkey.empty())
 1734                 {
 1735                     const std::string Fixedname = (keyname[0]=='#' || keyname[0]=='&' || keyname[0]=='*') ? keyname.substr(1) : keyname;
 1736                     std::string def = QuoteWord(Fixedname);
 1737                     if(AddComments)
 1738                         def += "\n -- Index was: " + m->second + "\n";
 1739                     changes.ALTER_TABLES[tablename].DROP_INDEXES.push_back(def);
 1740                 }
 1741             }
 1742             
 1743             /* Drop extinct foreign keys */
 1744             foreignkeylisttype::const_iterator f;
 1745             for(f = oldtable.fks.begin(); f != oldtable.fks.end(); ++f)
 1746             {
 1747                 const string& keyname       = f->first;
 1748                 const ForeignKeyData oldkey = newtable.getfk(keyname);
 1749                 if(oldkey.IndexFields.empty())
 1750                 {
 1751                     std::string def = QuoteWord(f->second.ConstraintName);
 1752                     if(def.empty()) def = keyname;
 1753                     if(AddComments)
 1754                         def += "\n -- FK was: " + f->second.MakeString() + "\n";
 1755                     changes.ALTER_TABLES[tablename].DROP_FOREIGN_KEYS.push_back(def);
 1756                 }
 1757             }
 1758         }
 1759         
 1760         /* Find all things that apply now */
 1761         for(tablelisttype::const_iterator i=newdesc.tables.begin(); i!=newdesc.tables.end(); ++i)
 1762         {
 1763             const Table& newtable = i->second;
 1764             
 1765             bool create_table;
 1766             std::string create_table_data;
 1767             
 1768             const std::string& tablename = i->first;
 1769             
 1770             tablelisttype::const_iterator j = tables.find(tablename);
 1771             
 1772             create_table = (j == tables.end());
 1773             
 1774             if(create_table)
 1775             {
 1776                 /* If the table didn't exist before */
 1777             }
 1778             else
 1779             {
 1780                 const Table& oldtable = j->second;
 1781             
 1782                 if(oldtable.pkey() != newtable.pkey()
 1783                 || oldtable.pkeytype() != newtable.pkeytype())
 1784                 {
 1785                     /*Drop the primary key if it has changed */
 1786                     changes.ALTER_TABLES[tablename].DROP_PRIMARY_KEY = true;
 1787                 }
 1788             }
 1789             
 1790             bool primarydone = false;
 1791             bool eka = true;
 1792             string ed = "";
 1793             
 1794             /* Simulation for creating proper AFTER/FIRST defs for field reordering */
 1795             SimulateFieldOrder simulation;
 1796             
 1797             if(!create_table)
 1798             {
 1799                 const Table& oldtable = j->second;
 1800 
 1801                 // Add all old fields that weren't deleted
 1802                 fieldlisttype::const_iterator k;
 1803                 for(k = oldtable.fields.begin(); k != oldtable.fields.end(); ++k)
 1804                     if(newtable.hasfield(k->first))
 1805                         simulation.Add(k->first);
 1806                 // Next, add all new fields
 1807                 std::string prev="";
 1808                 for(k = newtable.fields.begin(); k != newtable.fields.end(); prev = (k++)->first)
 1809                     if(!oldtable.hasfield(k->first))
 1810                         simulation.Add_After(prev, k->first);
 1811                 // The simulation is ready.
 1812             }
 1813             
 1814             /* Add new fields (drop & change already done) */
 1815             fieldlisttype::const_iterator k;
 1816             for(k = newtable.fields.begin(); k != newtable.fields.end(); ++k)
 1817             {
 1818                 if(create_table)
 1819                 {
 1820                     if(eka)eka=false;else create_table_data += ",\n";
 1821                     
 1822                     create_table_data += "  " + QuoteWord(k->first) + " " + k->second.c_str();
 1823                     
 1824                     const string defval = k->second.defval();
 1825                     if(!defval.empty())
 1826                         create_table_data += " DEFAULT " + defval;
 1827                     
 1828                     if(k->first == newtable.pkey()
 1829                     && newtable.pkeytype().empty())
 1830                     {
 1831                         create_table_data += " PRIMARY KEY";
 1832                         primarydone = true;
 1833                     }
 1834                 }
 1835                 else
 1836                 {
 1837                     const std::string& fieldname = k->first;
 1838                     const Table& oldtable = j->second;
 1839             
 1840                     if(!oldtable.hasfield(fieldname))
 1841                     {
 1842                         // Add the field
 1843                         
 1844                         std::string def = k->second.c_str();
 1845                         const string defval = k->second.defval();
 1846                         if(!defval.empty())
 1847                             def += " DEFAULT " + defval;
 1848 
 1849                         if(fieldname == newtable.pkey() && newtable.pkeytype().empty())
 1850                         {
 1851                             primarydone = true;
 1852                             def += " PRIMARY KEY ";
 1853                         }
 1854                         if(!ed.empty())
 1855                             def += " AFTER " + QuoteWord(ed);
 1856                         else
 1857                             def += " FIRST";
 1858 
 1859                         changes.ALTER_TABLES[tablename].ADD_FIELDS.
 1860                             push_back(std::make_pair(fieldname, def));
 1861                     }
 1862                     else
 1863                     {
 1864                         // Change the field if required.
 1865                         
 1866                         /* old field */
 1867                         const fieldpairtype &oldfp = oldtable.getfield(fieldname);
 1868                         const fieldtype     &oldf  = oldfp.second;
 1869                         const string olddefault = oldf.defval();
 1870                         const std::string   oldprev = simulation.FindPrecedent(fieldname);
 1871                         const std::string   oldcomment = oldf.getcomment();
 1872                         const std::string   oldenumparams = oldf.getenumparams();
 1873 
 1874                         /* new field */
 1875                         const fieldpairtype &newfp = *k;
 1876                         const fieldtype     &newf  = newfp.second;
 1877                         const string newdefault = newf.defval();
 1878                         const std::string   &newprev = ed;
 1879                         const std::string   newcomment = newf.getcomment();
 1880                         const std::string   newenumparams = newf.getenumparams();
 1881 
 1882                         
 1883                         bool changed = false;
 1884                         bool only_order = false;
 1885                         
 1886                         if(oldf != newf)
 1887                         {
 1888                             changed = true;
 1889                             /* If the new field has no collate/charset and the old one
 1890                              * has, ignore them */
 1891                             fieldtype oldftemp = oldf;
 1892                             oldftemp.clearcharsetandcollate();
 1893                             if(oldftemp == newf) changed = false;
 1894 
 1895                             /* If the new field has no default and the old one has,
 1896                              * and the new is "not null", ignore the default change */
 1897                             //if(changed)fprintf(stderr, "%s changed #1\n", fieldname.c_str());
 1898                         }
 1899 
 1900                         if (newcomment != oldcomment
 1901                         ||  newenumparams != oldenumparams)
 1902                         {
 1903                             changed = true;
 1904                         }
 1905 
 1906                         if(newdefault != olddefault && !newdefault.empty())
 1907                         {
 1908                             changed = true;
 1909                         }
 1910 
 1911                         if(oldprev != newprev)
 1912                         {
 1913                             if(!changed) only_order = true;
 1914                             changed = true;
 1915                             //fprintf(stderr, "%s changed bcs order\n", fieldname.c_str());
 1916                             simulation.Move_After(newprev, fieldname);
 1917                         }
 1918                         
 1919                         if(changed)
 1920                         {
 1921                             std::string def = QuoteWord(newfp.first) + " " + newf.c_str();
 1922                             if(!newdefault.empty())
 1923                             {
 1924                                 def += " DEFAULT " + newdefault;
 1925                                 if(newdefault != olddefault && AddComments)
 1926                                     def += " -- Default was: " + olddefault + "\n";
 1927                             }
 1928                             if(!only_order && AddComments)
 1929                                 def += string("\n -- Field was: ") + oldf.c_str() + "\n";
 1930                             if(oldprev != newprev)
 1931                             {
 1932                                 if(!newprev.empty())
 1933                                     def += " AFTER " + QuoteWord(newprev);
 1934                                 else
 1935                                     def += " FIRST";
 1936                                 if(only_order && AddComments)
 1937                                     def += string("\n -- Old precedent: '" + oldprev + "'\n");
 1938                             }
 1939                             
 1940                             changes.ALTER_TABLES[tablename].CHANGE_FIELDS.
 1941                                 push_back(std::make_pair(fieldname, def));
 1942                         }
 1943                         
 1944                         if(newdefault.empty() && !olddefault.empty())
 1945                         {
 1946                             std::string f = fieldname;
 1947                             if(AddComments)
 1948                                 f += " -- Default was: " + olddefault + "\n";
 1949                             changes.ALTER_TABLES[tablename].DROP_DEFAULTS.push_back(f);
 1950                         }
 1951                     }
 1952                 }
 1953                 ed = k->first.c_str();
 1954             }
 1955             
 1956             /* Update/add indices */
 1957             keylisttype::const_iterator m;
 1958             for(m = newtable.keys.begin(); m != newtable.keys.end(); ++m)
 1959             {
 1960                 const string& keyname = m->first;
 1961                 const char *mkey = keyname.c_str();
 1962                 if(create_table)
 1963                 {
 1964                     if(eka)eka=false;else create_table_data += ",\n";
 1965                     
 1966                     create_table_data += std::string
 1967                         (mkey[0]=='#' ? "UNIQUE"
 1968                         :mkey[0]=='&' ? "SPATIAL"
 1969                         :mkey[0]=='*' ? "FULLTEXT"
 1970                         : "KEY")
 1971                         + " "
 1972                         + QuoteWord((mkey[0]=='#' || mkey[0]=='&' || mkey[0]=='*') ? mkey+1 : mkey)
 1973                         + m->second;
 1974                 }
 1975                 else
 1976                 {
 1977                     const Table& oldtable = j->second;
 1978             
 1979                     const string oldkey = oldtable.getkey(keyname);
 1980                     if(oldkey != m->second)
 1981                     {
 1982                         if(!oldkey.empty())
 1983                         {
 1984                             const std::string Fixedname = (keyname[0]=='#' || keyname[0]=='&' || keyname[0]=='*') ? keyname.substr(1) : keyname;
 1985                             std::string def = QuoteWord(Fixedname);
 1986                             if(AddComments)
 1987                                 def += "\n -- Index was: " + oldkey + "\n";
 1988                             changes.ALTER_TABLES[tablename].DROP_INDEXES.push_back(def);
 1989                         }
 1990                         // m->second shouldn't be empty
 1991                         changes.ALTER_TABLES[tablename].ADD_INDEXES.push_back(std::make_pair(keyname, m->second));
 1992                     }
 1993                 }
 1994             }
 1995             
 1996             /* Update/add foreign keys */
 1997             foreignkeylisttype::const_iterator f;
 1998             for(f = newtable.fks.begin(); f != newtable.fks.end(); ++f)
 1999             {
 2000                 const string& keyname = f->first;
 2001                 
 2002                 std::string FKdef = f->second.MakeString();
 2003                 if(create_table)
 2004                 {
 2005                     if(eka)eka=false;else create_table_data += ",\n";
 2006                     create_table_data += "FOREIGN KEY" + FKdef;
 2007                 }
 2008                 else
 2009                 {
 2010                     const Table& oldtable = j->second;
 2011             
 2012                     const ForeignKeyData oldkey = oldtable.getfk(keyname);
 2013                     if(oldkey != f->second)
 2014                     {
 2015                         if(!oldkey.IndexFields.empty())
 2016                         {
 2017                             std::string def = QuoteWord(oldkey.ConstraintName);
 2018                             if(def.empty()) def = keyname;
 2019                             if(AddComments)
 2020                                 def += "\n -- FK Was: " + oldkey.MakeString() + "\n";
 2021                             changes.ALTER_TABLES[tablename].DROP_FOREIGN_KEYS.push_back(def);
 2022                         }
 2023                         // f->second shouldn't be empty
 2024                         changes.ALTER_TABLES[tablename].ADD_FOREIGN_KEYS.push_back(FKdef);
 2025                     }
 2026                 }
 2027             }
 2028             
 2029             if(create_table)
 2030             {
 2031                 if(!newtable.pkey().empty() && !primarydone)
 2032                 {
 2033                     if(!eka) create_table_data += ",\n";
 2034                     create_table_data += "  PRIMARY KEY";
 2035                     if(!newtable.pkeytype().empty())
 2036                         create_table_data += " USING "+newtable.pkeytype();
 2037                     create_table_data += "(" + newtable.pkey() + ")";
 2038                     primarydone = true;
 2039                     eka = false;
 2040                 }
 2041                 if(!eka) create_table_data += "\n";
 2042                 
 2043                 std::string type_data;
 2044                 if(!newtable.type().empty())
 2045                     type_data += "TYPE=" + newtable.type();
 2046                 changes.CREATE_TABLES.push_back(QuoteWord(tablename) + "\n(\n" + create_table_data + ")" + type_data);
 2047             }
 2048             else
 2049             {
 2050                 const Table& oldtable = j->second;
 2051             
 2052                 if(!primarydone && (oldtable.pkey() != newtable.pkey()
 2053                                  || oldtable.pkeytype() != newtable.pkeytype()))
 2054                 {
 2055                     changes.ALTER_TABLES[j->first].ADD_PRIMARY =
 2056                        std::make_pair(newtable.pkey(), newtable.pkeytype());
 2057                 }
 2058             }
 2059             
 2060 #ifdef SUPPORT_INSERTS
 2061             if(create_table || oldtable.tyhjataulu())
 2062             {
 2063                 newtable.FlushInserts();
 2064             }
 2065 #endif
 2066         }
 2067         changes.Flush();
 2068     }
 2069 };
 2070 
 2071 static void Usage(void)
 2072 {
 2073     printf(
 2074         "sqlupdate v"VERSION" - Copyright (C) Joel Yliluoma (http://iki.fi/bisqwit/)\n"
 2075         "\n"
 2076         "Usage:\n"
 2077         "    sqlupdate [options] >changes.sql\n"
 2078         "       (Creates an update script)\n"
 2079         "\n"
 2080         "Options:\n"
 2081         "    -t tablefile          Describes the file containing\n"
 2082         "                          the new sql layout. Default: tables.sql\n"
 2083         "    -d database           Default: winnie3\n"
 2084         "    -h host               Default: localhost\n"
 2085         "    -u user               Default: root\n"
 2086         "    -i                    Use CREATE INDEX instead of ALTER..ADD KEY\n"
 2087         "    -m                    Add comments explaining the differences\n"
 2088         "    -c                    Ignore character set differences\n"
 2089         "    -p pass\n"
 2090         "    -r                    Reverse operation. (new->old)\n"
 2091 #ifdef SUPPORT_INSERTS
 2092         "    -D                    Insert new data\n"
 2093 #endif
 2094         "\n"
 2095         "Example:\n"
 2096         "  ./sqlupdate -t etimgr.sql -d etimgr | mysql -uroot\n"
 2097         "\n"
 2098         "This program does not update a database. It only\n"
 2099         "produces update scripts (which show the differences).\n\n");
 2100 }
 2101 
 2102 int main(int argc, const char *const *argv)
 2103 {
 2104     string newdescfile = "tables.sql";
 2105     string host = "localhost";
 2106     string user = "root";
 2107     string pass = ""; 
 2108     bool vanhaksi = false;
 2109     
 2110     while(--argc > 0)
 2111     {
 2112         const char *s = *++argv;
 2113         if(*s=='-')
 2114             while(*++s)
 2115                 switch(*s)
 2116                 {
 2117                     #define stringparm(svar) \
 2118                         if(*++s){svar = s;s=" ";}else{if(!--argc){Usage();return -1;};svar=*++argv;s=" ";}
 2119                     
 2120                     case 't': stringparm(newdescfile); break;
 2121                     case 'd': stringparm(database); break;
 2122                     case 'h': stringparm(host); break;
 2123                     case 'u': stringparm(user); break;
 2124                     case 'p': stringparm(pass); break;
 2125                     case 'i': UseCreateIndex = true; break;
 2126                     case 'm': AddComments = true; break;
 2127                     case 'c': HandleCharsets = false; break;
 2128 #ifdef SUPPORT_INSERTS
 2129                     case 'D':
 2130                         nodata = false;
 2131                         break;
 2132 #endif
 2133                     case 'r':
 2134                         vanhaksi = true;
 2135                         break;
 2136                     default:
 2137                         Usage();
 2138                         return -1;
 2139                 }
 2140         else
 2141         {
 2142             Usage();
 2143             return -1;
 2144         }
 2145     }
 2146         
 2147     string vanhakomento = "/usr/bin/mysqldump -e -u'" + user + "' -h'" + host + "'";
 2148 #ifdef SUPPORT_INSERTS
 2149     if(nodata)
 2150 #endif
 2151         vanhakomento += " -d";
 2152     
 2153     if(!pass.empty())
 2154         vanhakomento += " -p'" + pass + "'";
 2155     
 2156     vanhakomento += " '";
 2157     vanhakomento += database;
 2158     vanhakomento += "'";
 2159     
 2160     try
 2161     {
 2162         sqldump vanha(popen(vanhakomento.c_str(), "r"), true);
 2163         sqldump newdesc(fopen(newdescfile.c_str(), "r"));
 2164     
 2165         printf("SET FOREIGN_KEY_CHECKS=0;\n");
 2166         if(vanhaksi)
 2167             newdesc.updatewith(vanha);
 2168         else
 2169             vanha.updatewith(newdesc);
 2170         printf("SET FOREIGN_KEY_CHECKS=1;\n");
 2171     }
 2172     catch(const InputFailedError &p)
 2173     {
 2174         fprintf(stderr, "*** %s failed!\n", p.pip ? database.c_str() : newdescfile.c_str());
 2175     }
 2176     catch(const string &s)
 2177     {
 2178         fprintf(stderr, "*** Oops.\n*** %s\n", s.c_str());
 2179     }
 2180         
 2181     fprintf(stderr, "*** Done\n");
 2182     
 2183     return 0;
 2184 }