#include "Sql.h" namespace Upp { void SqlSchema::FlushColumn() { if(column.IsEmpty()) return; for(int i = items ? 0 : Null; items ? i < items : IsNull(i); i++) { String cn = Format("\t%-20s ", ~Expand("@c", i)) + CurrentType(); if(dialect == SQLITE3) cn = Format(" %s ", ~Expand("@c", i)) + CurrentType(); String attr = Expand(attribute, i); String cd = cn + attr; if(firstcolumn) { if (dialect == SQLITE3) Upgrade() << Expand("create table @t ( ") << cd << " )" << table_suffix << ";\n"; else Upgrade() << Expand("create table @t (\n") << cd << "\n)" << table_suffix << ";\n\n"; } else { if(dialect == MSSQL) { Upgrade() << Expand("alter table @t add ") << cn << ";\n"; Upgrade() << Expand("alter table @t alter column ") << cd << ";\n"; } else if(dialect == MY_SQL) { Upgrade() << Expand("alter table @t add ") << cn << ";\n"; Upgrade() << Expand("alter table @t modify ") << cd << ";\n"; } else if (dialect == SQLITE3 || dialect == MY_SQL) Upgrade() << Expand("alter table @t add ") << cd << ";\n"; else if (dialect == PGSQL) Upgrade() << Expand("alter table @t add \n") << cd << "\n;\n\n"; else Upgrade() << Expand("alter table @t add (\n") << cd << "\n);\n\n"; Schema() << ",\n"; } Schema() << cd; firstcolumn = false; } column.Clear(); } void SqlSchema::FlushTable() { FlushColumn(); if(!table.IsEmpty()) Schema() << "\n)" << table_suffix << ";\n\n"; table.Clear(); } void SqlSchema::Object(const char *text, const char *drop) { FlushTable(); Schema() << text << '\n'; SchemaDrop() << drop << '\n'; } void SqlSchema::Upgrade(const char *text) { Upgrade() << text << '\n'; } void SqlSchema::Table(const char *name) { FlushTable(); table = name; table_suffix = Null; Schema() << Expand("create table @t (\n"); SchemaDrop() << Expand("drop table @t;\n"); Attributes() << '\n'; AttributesDrop() << '\n'; firstcolumn = true; } void SqlSchema::TableSuffix(const char *suffix) { table_suffix << suffix; } void SqlSchema::Column(const char *_type, const char *name) { FlushColumn(); items = 0; type = _type; column = prefix + name; attribute.Clear(); } void SqlSchema::ColumnArray(const char *type, const char *name, int _items) { Column(type, name); items = _items; } void SqlSchema::Attribute(const char *attr, const char *drop) { for(int i = items ? 0 : Null; items ? i < items : IsNull(i); i++) { Attributes() << Expand(attr, i) << '\n'; AttributesDrop() << Expand(drop, i) << '\n'; } } void SqlSchema::InlineAttribute(const char *attr) { attribute << (*attr == ',' ? "" : " ") << Expand(attr); } void SqlSchema::Config(const char *config, const char *drop) { Config() << config; ConfigDrop() << drop; } String SqlSchema::CurrentTable() const { return IsNull(schemaname) ? table : schemaname + '.' + table; } String SqlSchema::Expand(const char *txt, int i) const { String n, r; if(!IsNull(i)) n = Format("%d", i); String c = CurrentColumn() + n; String v = table + '$' + c; v = v.Mid(max(0, min(v.GetLength(), v.GetLength() - maxidlen))); while(*txt) { if(*txt == '@' && txt[1]) { switch(*++txt) { case 'i': r.Cat(n); break; case 't': r.Cat(CurrentTable()); break; case 'u': r.Cat(table); break; case 'c': r.Cat(c); break; case 'C': r.Cat(CurrentColumn()); break; case 'T': r.Cat(CurrentType()); break; case 'x': r.Cat(v); break; case 's': if(!IsNull(schemaname)) { r.Cat(schemaname); r.Cat('.'); } break; default: r.Cat(*txt); break; } } else r.Cat(*txt); txt++; } return r; } void SqlSchema::Var(void (*type)(SqlSchema& s), const char *name) { String p = prefix; prefix << name << '$'; (*type)(*this); prefix = p; } String& SqlSchema::Script(int si) { return script.At(si); } String SqlSchema::Script(int si) const { return si < script.GetCount() ? script[si] : String(); } String& SqlSchema::Schema() { return Script(SCHEMA); } String& SqlSchema::SchemaDrop() { return Script(DROPSCHEMA); } String& SqlSchema::Attributes() { return Script(ATTRIBUTES); } String& SqlSchema::AttributesDrop() { return Script(DROPATTRIBUTES); } String& SqlSchema::Upgrade() { return Script(UPGRADE); } String& SqlSchema::UpgradeDrop() { return Script(DROPUPGRADE); } String& SqlSchema::Config() { return Script(CONFIG); } String& SqlSchema::ConfigDrop() { return Script(DROPCONFIG); } String SqlSchema::NormalFileName(int i, const char *dir, const char *name) const { ASSERT(i >= 0 && i <= DROPCONFIG); String n; if(name) n = name; else #ifdef PLATFORM_WIN32 n = ForceExt(GetFileName(GetExeFilePath()), ".sql"); #endif #ifdef PLATFORM_POSIX n = "script.sql"; #endif const char *pfx[] = { "S_", "SD_", "A_", "AD_", "U_", "UD_", "C_", "CD_" }; return dir ? AppendFileName(dir, pfx[i] + n) : ConfigFile(pfx[i] + n); } bool SqlSchema::ScriptChanged(int i, const char *dir, const char *name) const { String fn = NormalFileName(i, dir, name); return LoadFile(fn) != Script(i); } bool SqlSchema::UpdateNormalFile(int i, const char *dir, const char *name) const { String fn = NormalFileName(i, dir, name); if(LoadFile(fn) != Script(i)) { DeleteFile(fn); SaveFile(fn, Script(i)); return true; } return false; } void SqlSchema::SaveNormal(const char *dir, const char *name) const { for(int i = SCHEMA; i <= DROPCONFIG; i++) UpdateNormalFile(i, dir, name); } SqlSchema::SqlSchema(int dialect_) { dialect = dialect_; maxidlen = 63; if(dialect == MSSQL) maxidlen = 128; if(dialect == ORACLE) maxidlen = 24; } void operator*(SqlSchema& schema, const SqlInsert& insert) { schema.Config() << SqlStatement(insert).Get(schema.GetDialect()) << ";\n"; schema.ConfigDrop() << SqlStatement(Delete(insert.GetTable()) .Where(insert.GetKeyColumn() == insert.GetKeyValue())) .Get(schema.GetDialect()) << ";\n"; } }