////////////////////////////////////////////////////////////////////// // dict: user privileges, relocation, index removal. #include "TSql.h" #pragma hdrstop NAMESPACE_UPP #ifndef NOAPPSQL int SqlUserGetRightsTo(const char* table) { return SqlUser().GetRightsTo(table); } #endif #ifndef NOAPPSQL bool SqlUserCanWrite(const char* table) { return SqlUser().CanWrite(table); } #endif #ifndef NOAPPSQL bool SqlUserCanRead(const char* table) { return SqlUser().CanRead(table); } #endif SqlAnyTable::SqlAnyTable() { } SqlAnyTable::SqlAnyTable(const char *table_name, SqlSession& session) { const char *n = strchr(table_name, '.'); if(n) { // full specification owner = ToUpper(String(table, n - table_name)); table = ToUpper(n + 1); } else { // check table existence static VectorMap table_map; static String user; if(session.GetUser() != user) { // another user - clear cache user = session.GetUser(); table_map.Clear(); } int ix = table_map.Find(table_name); if(ix >= 0) { *this = table_map[ix]; return; } table = ToUpper(table_name); Sql cursor(session); owner = cursor.Select("OWNER from ALL_TABLES " "where OWNER = USER and TABLE_NAME = ?", table); if(IsNull(owner)) { // table not found - look up synonym table if(!cursor.Execute("select TABLE_OWNER, TABLE_NAME from ALL_SYNONYMS " "where SYNONYM_NAME = ? " "order by DECODE(OWNER, USER, 0, 'PUBLIC', 1, 2)", table)) throw SqlExc(session); if(!cursor.Fetch(owner, table)) owner = table = String(); } if(!IsNull(owner) && !IsNull(table)) table_map.FindAdd(table_name, *this); } } SqlAnyTable::SqlAnyTable(const char* owner, const char* table) : owner(owner), table(table) { } String SqlAnyTable::Dot() const { String result; if(!IsNull(owner)) result << owner << '.'; result += table; return result; } bool SqlAnyTable::operator == (const SqlAnyTable& t) const { return owner == t.owner && table == t.table; } SqlAnyColumn::SqlAnyColumn() { } SqlAnyColumn::SqlAnyColumn(const char* column, const char* table_name, SqlSession& session) : SqlAnyTable(table_name, session) , column(ToUpper(column)) { } SqlAnyColumn::SqlAnyColumn(const char* column, const char* owner, const char* table) : SqlAnyTable(owner, table) , column(ToUpper(column)) { } SqlAnyColumn::SqlAnyColumn(const char* column, const SqlAnyTable& table) : SqlAnyTable(table) , column(ToUpper(column)) { } String SqlAnyColumn::DotColumn() const { return Dot() + '.' + column; } bool SqlAnyColumn::operator == (const SqlAnyColumn& c) const { return owner == c.owner && table == c.table && column == c.column; } #ifndef NOAPPSQL GLOBAL_VAR(SqlUserRights, SqlUser) #endif void SqlUserRights::Clear() { user = Null; roles.Clear(); rights.Clear(); error = Null; } void SqlUserRights::Sync() { try { SqlSession& ss = GetSession(); if(!ss) throw SqlExc(t_("User is not connected to the database.")); String new_user = ss.GetUser(); if(user == new_user) // user has not changed, rights are up to date return; Clear(); user = new_user; Sql cursor(ss); (Select(SqlId("GRANTED_ROLE")).From(SqlId("USER_ROLE_PRIVS")) | Select(SqlId("GRANTED_ROLE")).From(SqlId("ROLE_ROLE_PRIVS"))).Force(cursor); String temp; while(cursor.Fetch(temp)) roles.FindAdd(temp); // fetch rights SqlId OWNER("OWNER"); SqlId TABLE_NAME("TABLE_NAME"); SqlId PRIVILEGE("PRIVILEGE"); Select(SqlCol("OWNER, TABLE_NAME, " "SUM(distinct DECODE(PRIVILEGE, 'SELECT', 1, 'UPDATE', 2, " "'INSERT', 4, 'DELETE', 8, 0))")) .From(Select(OWNER, TABLE_NAME, PRIVILEGE).From(SqlId("USER_TAB_PRIVS")) | Select(OWNER, TABLE_NAME, PRIVILEGE).From(SqlId("ROLE_TAB_PRIVS"))) // "(select * from USER_TAB_PRIVS union select * from ROLE_TAB_PRIVS)") .GroupBy(SqlSet(SqlId("OWNER"), SqlId("TABLE_NAME"))) .Force(cursor); SqlAnyTable t; int privs; while(cursor.Fetch(t.owner, t.table, privs)) rights.Add(t.Dot(), privs); } catch(Exc e) { error = e; } } int SqlUserRights::GetRightsTo(const char* table) { try { SqlSession& session = GetSession(); SqlAnyTable tbl(table, session); if(tbl.owner == session.GetUser()) return 15; // user has always full access to his/her own schema Sync(); int ix = rights.Find(tbl.Dot()); return (ix >= 0 ? rights[ix] : 0); } catch(Exc e) { error = e; return 0; } } bool SqlUserRights::HasRole(const char* role) { Sync(); return roles.Find(role) >= 0; } SqlIndex::SqlIndex() { session = NULL; } void SqlIndex::Open(String ind, String own, SqlSession& session) { SqlBool exp = SqlId("INDEX_NAME") == ind; if(!IsNull(own)) exp &= SqlCol("OWNER") == own; Sql cursor(session); Select(SqlId("OWNER"), SqlId("INDEX_NAME"), SqlId("TABLE_OWNER"), SqlId("TABLE_NAME")) .From(SqlId("ALL_INDEXES")) .Where(exp).Force(cursor); if(cursor.Fetch(owner, index, table.owner, table.table)) FetchContainerExc(columns, Select(SqlId("COLUMN_NAME")) .From(SqlId("ALL_IND_COLUMNS")) .Where(SqlId("INDEX_OWNER") == owner && SqlId("INDEX_NAME") == index), session); dropped = false; } SqlIndex::SqlIndex(const SqlIndex& another, int) { table = another.table; index = another.index; columns <<= another.columns; dropped = another.dropped; session = another.session; } SqlIndex::~SqlIndex() { } String SqlIndex::Dot() const { if(!owner.IsEmpty()) return owner + '.' + index; else return index; } void SqlIndex::Drop() { ASSERT(session); Sql cursor(*session); if(!index.IsEmpty() && cursor.Execute("drop index " + Dot())) dropped = true; } void SqlIndex::Create() { if(index.IsEmpty() || columns.IsEmpty() || !dropped) return; String s; s << "create index " << Dot() << " on " << table.Dot() << '(' << columns[0]; for(int i = 1; i < columns.GetCount(); i++) s << ", " << columns[i]; s << ')'; ASSERT(session); Sql cursor(*session); if(cursor.Execute(s)) dropped = false; } SqlIndexMap::SqlIndexMap() { } SqlIndexMap::~SqlIndexMap() { } void SqlIndexMap::Add(const char* _table, SqlSession& session) { SqlAnyTable table(_table, session); Sql cursor(session); if(!cursor.Execute("select OWNER, INDEX_NAME from ALL_INDEXES " "where UNIQUENESS = 'NONUNIQUE' " "and TABLE_OWNER = ? and TABLE_NAME = ?", table.owner, table.table)) throw SqlExc(session); String owner, index; while(cursor.Fetch(owner, index)) AddPick(SqlIndex(index, owner, session)); } void SqlIndexMap::Drop(Gate2 progress) { for(int i = 0; i < map.GetCount(); i++) { map[i].Drop(); if(progress(i, map.GetCount())) throw AbortExc(); } } void SqlIndexMap::Create(Gate2 progress) { for(int i = 0; i < map.GetCount(); i++) { map[i].Create(); if(progress(i, map.GetCount())) throw AbortExc(); } } SqlRelocate::SqlRelocate() { session = NULL; } SqlRelocate::~SqlRelocate() { } void SqlRelocate::Find(SqlId table, SqlId column, SqlSession& session) { Find(~table.ToString(), ~column.ToString(), session); } void SqlRelocate::Find(const char* table, const char* column, SqlSession& sess) { session = &sess; // todo: CWaitCursor without GUI / MFC? // CWaitCursor wait; error = Null; reference.Clear(); canceled = false; try { SqlAnyColumn col(column, table, *session); static VectorMap > reference_map; int i = reference_map.Find(col); if(i >= 0) { reference <<= reference_map[i]; return; } Vector positions; Sql cursor(*session); if(!cursor.Execute("select distinct POSITION from ALL_CONS_COLUMNS " "where OWNER = ? and TABLE_NAME = ? " "and COLUMN_NAME = ? and POSITION is not NULL", col.owner, col.table, col.column)) throw SqlExc(*session); int temp; if(cursor.Fetch(temp)) { // positions exist do positions.Add(temp); while(cursor.Fetch(temp)); for(int pos = 0; pos < positions.GetCount(); pos++) { if(!cursor.Execute( "select OWNER, TABLE_NAME, COLUMN_NAME from ALL_CONS_COLUMNS " "where (OWNER, CONSTRAINT_NAME, POSITION) in " "(select OWNER, CONSTRAINT_NAME, ? from ALL_CONSTRAINTS " "where CONSTRAINT_TYPE = 'R' and (OWNER, R_CONSTRAINT_NAME) in " "(select OWNER, CONSTRAINT_NAME from ALL_CONS_COLUMNS " "where OWNER = ? and TABLE_NAME = ? " "and COLUMN_NAME = ? and POSITION = ?))", positions[pos], col.owner, col.table, col.column, positions[pos])) throw SqlExc(*session); SqlAnyColumn rcol; while(cursor.Fetch(rcol.owner, rcol.table, rcol.column)) reference.Add(rcol); } } reference_map.Add(col, reference); } catch(Exc e) { error = e; } ClearResult(); } void SqlRelocate::Move(const Vector& old_seq, const Vector& new_seq, int options, Gate2 progress) { ASSERT(session); if(!error.IsEmpty()) // don't do anything when Find() failed return; ASSERT(old_seq.GetCount() == new_seq.GetCount()); Vector old_sub, new_sub, dec_sub; int c = 0; for(int i = 0; i < old_seq.GetCount(); i++) if(!IsNull(old_seq[i]) && new_seq[i] != old_seq[i]) { if(c >= 50 || old_sub.IsEmpty()) { old_sub.Add(); new_sub.Add(); dec_sub.Add(); c = 0; } c++; old_sub.Top().Cat(old_seq[i]); new_sub.Top().Cat(new_seq[i]); dec_sub.Top().Cat(old_seq[i]).Cat(new_seq[i]); } if(old_sub.IsEmpty()) return; canceled = false; ClearResult(); // todo: CWaitCursor without GUI / MFC? // CWaitCursor wait; try { SqlBlock block(*session); Sql cursor(*session); if(progress(0, GetCount()) && !(options & NO_CANCEL)) { canceled = true; throw AbortExc(); } for(int i = 0; i < GetCount(); i++) { const SqlAnyColumn& column = reference[i]; SqlId tbl(column.Dot()); SqlId col(column.column); for(int s = 0; s < old_sub.GetCount(); s++) { if(!IsNull(cursor % Select(1).From(tbl).Where(col == old_sub[s] && SqlId("ROWNUM") == 1))) { SqlSet dec_set = dec_sub[s]; dec_set.Cat(col); if(!Update(tbl)(col, Decode(col, dec_set)) .Where(col == old_sub[s]).Execute(cursor)) { // update failed if(!(options & TRY_ALL)) throw SqlExc(*session); else if(error.IsEmpty()) error = SqlExc(*session); // just store error & continue trying other tables } else result[i] = true; } else result[i] = true; } // progress.SetText( // Format("Upravuji %s", column.column), false); if(progress(i, GetCount()) && !(options & NO_CANCEL)) { // set 'canceled' flag & exit canceled = true; throw AbortExc(); } } block.Commit(); } catch(Exc e) { error = e; } } void SqlRelocate::Move(int old_seq, int new_seq, int options, Gate2 progress) { Vector src, dest; src << old_seq; dest << new_seq; Move(src, dest, options, progress); } void SqlRelocate::ClearResult() { result.SetCount(reference.GetCount()); Fill(result.Begin(), result.End(), false); } Vector SqlRelocate::GetReferences(int seq) const { ASSERT(session); Vector result; if(IsNull(seq)) return result; Sql cursor(*session); for(int i = 0; i < GetCount(); i++) { const SqlAnyColumn& column = reference[i]; if(seq == (int)cursor.Select(column.column + " from " + column.Dot() + " where " + column.column + " = ? and ROWNUM = 1", seq)) result.Add(column); } return result; } unsigned GetHashValue(const SqlAnyTable& t) { return GetHashValue(t.owner) ^ GetHashValue(t.table); } unsigned GetHashValue(const SqlAnyColumn& t) { return GetHashValue(static_cast(t)) ^ GetHashValue(t.column); } END_UPP_NAMESPACE