通过ContentProvider实现groupby查询数据(转)及ICS后的修改(原)

本文介绍了如何在ContentProvider的query方法中使用groupby查询,通过构造特定的selection字符串来模拟GROUP BY语句。然而,这种技巧在Android ICS之后的版本中已不再适用,因为系统对此进行了安全性修复。文中提到了ICS中的CalllogProvider和SQLiteQueryBuilder类的改动,并建议需要在ICS下兼容的应用进行相应调整。解决方案包括直接使用SQLiteQueryBuilder提供的带groupby的query接口。
摘要由CSDN通过智能技术生成


     很多人想在Contentprovider的query方法中使用groupby查询,奈何SDK里面不提供这样的功能,于是有牛人从黑客的注入式破解中得到灵感,做法如下:

比如要用实现这么一个sql语句:
SELECT _id, number, date, duration, type, name, numbertype,
numberlabel, COUNT(*) FROM calls  GROUP BY number,type,date/8640000
ORDER BY date DESC
这个如果在一般的SQL编译工具里都能正常运行,在ContentRosolver中有些不一样。

用ContentRosolver中的query这么写:

private static String CALLS_COUNT = "calls_count";
static final String[] CALL_LOG_PROJECTION = new String[] {
           Calls._ID,
           Calls.NUMBER,
           Calls.DATE,
           Calls.DURATION,
           Calls.TYPE,
           Calls.CACHED_NAME,
           Calls.CACHED_NUMBER_TYPE,
           Calls.CACHED_NUMBER_LABEL,
           "COUNT(*) AS " + CALLS_COUNT
   };

String selection = "0==0) GROUP BY ("+
           Calls.NUMBER+"),("+
           Calls.TYPE+"),("+
           Calls.DATE+"/86400000";

rosolver.query(QUERY_TOKEN, null, Calls.CONTENT_URI,
               CALL_LOG_PROJECTION, selection, null, Calls.DEFAULT_SORT_ORDER);

注意事项:
1 关键字“COUNT, AS, GROUP BY”的大小写
2 COUNT(*) 后需要跟AS ***
3 Android会将query中的参数整合成一条sql语句,其中会将selection的字符串自动加一个括号,形成 “WHERE
(*******)”的形式,所以要特别注意selection中有括号的情况
4 GROUP BY后面的字段应该加括号,用逗号隔开。

转化为sql语句正确的形式应该如下:
SELECT _id, number, date, duration, type, name, numbertype,
numberlabel, COUNT(*) FROM calls WHERE (0==0) GROUP BY
(number),(type),(date/8640000) ORDER BY date DESC

=================分割线=======================

可惜!在ICS里面,这个方法又不适用了!(为什么是又。。。)

经过跟踪源码,ICS中的对应的Provider(比如CalllogProvider)和Framework中的SQLiteQueryBuilder类,已经将这个漏洞补上

首先增加了一个参数mStrict,在CalllogProvider的query方法中被设置为true

并在SQLiteQueryBuilder中进行了检测:

 private String[] computeProjection(String[] projectionIn) {
        if (projectionIn != null && projectionIn.length > 0) {
            if (mProjectionMap != null) {
                String[] projection = new String[projectionIn.length];
                int length = projectionIn.length;


                for (int i = 0; i < length; i++) {
                    String userColumn = projectionIn[i];
                    String column = mProjectionMap.get(userColumn);


                    if (column != null) {
                        projection[i] = column;
                        continue;
                    }

		 
                    if (!mStrict &&
                            ( userColumn.contains(" AS ") || userColumn.contains(" as "))) {
                        /* A column alias already exist */
                        projection[i] = userColumn;
                        continue;
                    }


                    throw new IllegalArgumentException("Invalid column "
                            + projectionIn[i]);
                }
                return projection;
            } else {
                return projectionIn;
            }
        } else if (mProjectionMap != null) {
            // Return all columns in projection map.
            Set<Entry<String, String>> entrySet = mProjectionMap.entrySet();
            String[] projection = new String[entrySet.size()];
            Iterator<Entry<String, String>> entryIter = entrySet.iterator();
            int i = 0;


            while (entryIter.hasNext()) {
                Entry<String, String> entry = entryIter.next();


                // Don't include the _count column when people ask for no projection.
                if (entry.getKey().equals(BaseColumns._COUNT)) {
                    continue;
                }
                projection[i++] = entry.getValue();
            }
            return projection;
        }
         return null;
    }
从标红前后代码可以看见,自己定义的projection中的column,直接就被抛异常了


就算采用了什么猥琐的方法,将mStrict设为了false,seleciton还是会被在接下来的拼接过程中被加上括号弥补掉可能被inject的可能。

简单看了下,这个修改,没有在ICS的变更说明中有描述,毕竟对于google来说只是改了个漏洞

如果有同学的app是用了这样的方法,要在ICS下兼容估计要改改了。


回到问题本身,如何在ContentProvider中实现groupby,如果,这个ContentProvider是自己实现或者可以修改系统的provider,是可以做到的,SQLiteQueryBuilder类提供了带groupby的query接口:

public Cursor query(SQLiteDatabase db, String[] projectionIn,
            String selection, String[] selectionArgs, String groupBy,
            String having, String sortOrder, String limit) 
如果,是使用系统的 ContentProvider如CalllogProvider、ContactsProvider,现在据我所知,是没有直接查询的办法了。代替的办法是使用MatrixCursor类,先查询到所有数据,然后遍历Cursor,自己处理拼接一个MatrixCursor使用,这个需要注意的是MatrixCursor的拼接,效率偏低,当数据量较大时,会很耗时,需要做好算法优化和异步处理。

参考文档:

http://yelinsen.iteye.com/blog/836935

http://www.eoeandroid.com/thread-31662-1-1.html



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值