a few functions to explore DB indexes
GroupMembers(GroupId, MemberId) index for SQLite
indexes action in rt-setup-database

This is a separate file that is excuted as perl code
with $dbh connected as DBA. Goal is to give rope to
explore existing indexes during upgrade.
indexes on Groups table

All queries on Groups table should involve Domain column.
If Domain is not filtered then something is wrong.

We decided to go with case insensitive searches even on
columns where case is "controlled" - it's easier to implement
and isn't slower with proper indexes, so indexes are wrapped
into LOWER() functions for case sensitive DBs.

Type is deprecated and Name should be used, so we index it after
Domain. This covers loading of user defined (Instantless) groups
that were not covered before by any index longer than (Domain).

Log of all queries from an active instance showed that we do queries
like:

    Name = A AND ((Domain = B AND Instance = C) OR (Domain = D))

For such queries (Domain, Name) index doesn't provide enough
selectivity. Index on (Instance) can not be used. After all the query
is very selective as Domain = D is part that selects system role group
(4 records top). With (Domain, Name) index DB spends a lot of time
switching between the index and the table to re-check Instance = C
condition. If we have (Domain, Name, Instance) index then, at least
mysql, can skip jumping and just use index to check all conditions
without looking into table data.

Test were performed with SQL from 4.0 instance, but in master (4.2 to be)
we widened use of 1 as Instance value for system groups. This means
that in above query Instance is defined for every ORed branch and more
DBs can use full length of the index.

Instance is very selective column and short (like id), so we just index
it for situations when Type/Name is not defined, but instance is.

Domain, Type, Instance is kept for backwards compatibility and will go
away with Type column.
unique case insensitive indexes on Queues and Users

make sure we have indexes on Queues(Name) and Users(Name)
and those are unique and are set up for case insensitive
searches (LOWER(...)).
indexes on cached group members

CachedGroupMembers (CGM) table is mostly pass through
table when we either have GroupId and need MemberId or
have MemberId and need GroupId.

We also filter by Disabled column in most cases and don't
in some, but selectivity of Disabled column is very small,
so we put it as last.

Some DBs (mysql, newer Pg, most probably Oracle) can use
index to complete select without looking into table itself,
so we create index for both case mentioned above with Disabled
on the tail.

Also, we make sure (MemberId, ImmediateParentId) is indexed
which is used when we delete members from Groups.
drop indexes that start from id

indexes that start from PK are very useless.

Mysql just don't use any such index, but primary key. Pg acts
similary. It's harder to check Oracle. Even if SQLite behaves
differently (hardly so) it doesn't matter much, for we this
for consistency.
Specify Instance even for RT::System groups

This allows more specific indexes to be used.
->id can be called as a class method on RT::System; use eval rather than ref() and can()

This mirrors the code in RT::Principal::_HasRoleRightQuery.
Refactor duplicated role group code
move functions into RT::Handle from upgrade script
de-duplicate Name in Users/Queues

It's possible that an old setup has non unique values
in the columns, so we have to go over and get rid of
duplicates by changing Name to 'xxx-dup-#'.

I've tried to use RT's API, but not all objects are
available during 'indexes' upgrade action.
Adjust log levels to reduce noise and not hide true errors
Merge branch '4.2/upgrading-indexes-and-review'