Merge changes from topic "sqlitez"
am: a920b9c85e
Change-Id: If22eef6e248b9e3bd1f5e22cd5081608494ad733
This commit is contained in:
@@ -12669,6 +12669,7 @@ package android.database.sqlite {
|
||||
method public static void appendColumns(java.lang.StringBuilder, java.lang.String[]);
|
||||
method public void appendWhere(java.lang.CharSequence);
|
||||
method public void appendWhereEscapeString(java.lang.String);
|
||||
method public void appendWhereStandalone(java.lang.CharSequence);
|
||||
method public java.lang.String buildQuery(java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
|
||||
method public deprecated java.lang.String buildQuery(java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String);
|
||||
method public static java.lang.String buildQueryString(boolean, java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.database;
|
||||
|
||||
import android.annotation.UnsupportedAppUsage;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.OperationApplicationException;
|
||||
@@ -35,6 +36,8 @@ import android.os.ParcelFileDescriptor;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.PrintStream;
|
||||
import java.text.Collator;
|
||||
@@ -216,6 +219,92 @@ public class DatabaseUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind the given selection with the given selection arguments.
|
||||
* <p>
|
||||
* Internally assumes that '?' is only ever used for arguments, and doesn't
|
||||
* appear as a literal or escaped value.
|
||||
* <p>
|
||||
* This method is typically useful for trusted code that needs to cook up a
|
||||
* fully-bound selection.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static @Nullable String bindSelection(@Nullable String selection,
|
||||
@Nullable Object... selectionArgs) {
|
||||
if (selection == null) return null;
|
||||
// If no arguments provided, so we can't bind anything
|
||||
if (ArrayUtils.isEmpty(selectionArgs)) return selection;
|
||||
// If no bindings requested, so we can shortcut
|
||||
if (selection.indexOf('?') == -1) return selection;
|
||||
|
||||
// Track the chars immediately before and after each bind request, to
|
||||
// decide if it needs additional whitespace added
|
||||
char before = ' ';
|
||||
char after = ' ';
|
||||
|
||||
int argIndex = 0;
|
||||
final int len = selection.length();
|
||||
final StringBuilder res = new StringBuilder(len);
|
||||
for (int i = 0; i < len; ) {
|
||||
char c = selection.charAt(i++);
|
||||
if (c == '?') {
|
||||
// Assume this bind request is guarded until we find a specific
|
||||
// trailing character below
|
||||
after = ' ';
|
||||
|
||||
// Sniff forward to see if the selection is requesting a
|
||||
// specific argument index
|
||||
int start = i;
|
||||
for (; i < len; i++) {
|
||||
c = selection.charAt(i);
|
||||
if (c < '0' || c > '9') {
|
||||
after = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (start != i) {
|
||||
argIndex = Integer.parseInt(selection.substring(start, i)) - 1;
|
||||
}
|
||||
|
||||
// Manually bind the argument into the selection, adding
|
||||
// whitespace when needed for clarity
|
||||
final Object arg = selectionArgs[argIndex++];
|
||||
if (before != ' ' && before != '=') res.append(' ');
|
||||
switch (DatabaseUtils.getTypeOfObject(arg)) {
|
||||
case Cursor.FIELD_TYPE_NULL:
|
||||
res.append("NULL");
|
||||
break;
|
||||
case Cursor.FIELD_TYPE_INTEGER:
|
||||
res.append(((Number) arg).longValue());
|
||||
break;
|
||||
case Cursor.FIELD_TYPE_FLOAT:
|
||||
res.append(((Number) arg).doubleValue());
|
||||
break;
|
||||
case Cursor.FIELD_TYPE_BLOB:
|
||||
throw new IllegalArgumentException("Blobs not supported");
|
||||
case Cursor.FIELD_TYPE_STRING:
|
||||
default:
|
||||
if (arg instanceof Boolean) {
|
||||
// Provide compatibility with legacy applications which may pass
|
||||
// Boolean values in bind args.
|
||||
res.append(((Boolean) arg).booleanValue() ? 1 : 0);
|
||||
} else {
|
||||
res.append('\'');
|
||||
res.append(arg.toString());
|
||||
res.append('\'');
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (after != ' ') res.append(' ');
|
||||
} else {
|
||||
res.append(c);
|
||||
before = c;
|
||||
}
|
||||
}
|
||||
return res.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns data type of the given object's value.
|
||||
*<p>
|
||||
|
||||
@@ -35,8 +35,7 @@ import java.util.regex.Pattern;
|
||||
* This is a convenience class that helps build SQL queries to be sent to
|
||||
* {@link SQLiteDatabase} objects.
|
||||
*/
|
||||
public class SQLiteQueryBuilder
|
||||
{
|
||||
public class SQLiteQueryBuilder {
|
||||
private static final String TAG = "SQLiteQueryBuilder";
|
||||
private static final Pattern sLimitPattern =
|
||||
Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?");
|
||||
@@ -95,7 +94,7 @@ public class SQLiteQueryBuilder
|
||||
*
|
||||
* @param inWhere the chunk of text to append to the WHERE clause.
|
||||
*/
|
||||
public void appendWhere(CharSequence inWhere) {
|
||||
public void appendWhere(@NonNull CharSequence inWhere) {
|
||||
if (mWhereClause == null) {
|
||||
mWhereClause = new StringBuilder(inWhere.length() + 16);
|
||||
}
|
||||
@@ -115,7 +114,7 @@ public class SQLiteQueryBuilder
|
||||
* @param inWhere the chunk of text to append to the WHERE clause. it will be escaped
|
||||
* to avoid SQL injection attacks
|
||||
*/
|
||||
public void appendWhereEscapeString(String inWhere) {
|
||||
public void appendWhereEscapeString(@NonNull String inWhere) {
|
||||
if (mWhereClause == null) {
|
||||
mWhereClause = new StringBuilder(inWhere.length() + 16);
|
||||
}
|
||||
@@ -125,6 +124,27 @@ public class SQLiteQueryBuilder
|
||||
DatabaseUtils.appendEscapedSQLString(mWhereClause, inWhere);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a standalone chunk to the {@code WHERE} clause of this query.
|
||||
* <p>
|
||||
* This method differs from {@link #appendWhere(CharSequence)} in that it
|
||||
* automatically appends {@code AND} to any existing {@code WHERE} clause
|
||||
* already under construction before appending the given standalone
|
||||
* expression wrapped in parentheses.
|
||||
*
|
||||
* @param inWhere the standalone expression to append to the {@code WHERE}
|
||||
* clause. It will be wrapped in parentheses when it's appended.
|
||||
*/
|
||||
public void appendWhereStandalone(@NonNull CharSequence inWhere) {
|
||||
if (mWhereClause == null) {
|
||||
mWhereClause = new StringBuilder(inWhere.length() + 16);
|
||||
}
|
||||
if (mWhereClause.length() > 0) {
|
||||
mWhereClause.append(" AND ");
|
||||
}
|
||||
mWhereClause.append('(').append(inWhere).append(')');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the projection map for the query. The projection map maps
|
||||
* from column names that the caller passes into query to database
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -309,7 +309,7 @@ public class ArrayUtils {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static @NonNull <T> T[] concat(Class<T> kind, @Nullable T[] a, @Nullable T[] b) {
|
||||
public static @NonNull <T> T[] concatElements(Class<T> kind, @Nullable T[] a, @Nullable T[] b) {
|
||||
final int an = (a != null) ? a.length : 0;
|
||||
final int bn = (b != null) ? b.length : 0;
|
||||
if (an == 0 && bn == 0) {
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.database;
|
||||
|
||||
import static android.database.DatabaseUtils.bindSelection;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class DatabaseUtilsTest {
|
||||
private static final Object[] ARGS = { "baz", 4, null };
|
||||
|
||||
@Test
|
||||
public void testBindSelection_none() throws Exception {
|
||||
assertEquals(null,
|
||||
bindSelection(null, ARGS));
|
||||
assertEquals("",
|
||||
bindSelection("", ARGS));
|
||||
assertEquals("foo=bar",
|
||||
bindSelection("foo=bar", ARGS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindSelection_normal() throws Exception {
|
||||
assertEquals("foo='baz'",
|
||||
bindSelection("foo=?", ARGS));
|
||||
assertEquals("foo='baz' AND bar=4",
|
||||
bindSelection("foo=? AND bar=?", ARGS));
|
||||
assertEquals("foo='baz' AND bar=4 AND meow=NULL",
|
||||
bindSelection("foo=? AND bar=? AND meow=?", ARGS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindSelection_whitespace() throws Exception {
|
||||
assertEquals("BETWEEN 5 AND 10",
|
||||
bindSelection("BETWEEN? AND ?", 5, 10));
|
||||
assertEquals("IN 'foo'",
|
||||
bindSelection("IN?", "foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindSelection_indexed() throws Exception {
|
||||
assertEquals("foo=10 AND bar=11 AND meow=1",
|
||||
bindSelection("foo=?10 AND bar=? AND meow=?1",
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12));
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.internal.util;
|
||||
|
||||
import static com.android.internal.util.ArrayUtils.concatElements;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
@@ -156,23 +158,23 @@ public class ArrayUtilsTest extends TestCase {
|
||||
|
||||
public void testConcatEmpty() throws Exception {
|
||||
assertArrayEquals(new Long[] {},
|
||||
ArrayUtils.concat(Long.class, null, null));
|
||||
concatElements(Long.class, null, null));
|
||||
assertArrayEquals(new Long[] {},
|
||||
ArrayUtils.concat(Long.class, new Long[] {}, null));
|
||||
concatElements(Long.class, new Long[] {}, null));
|
||||
assertArrayEquals(new Long[] {},
|
||||
ArrayUtils.concat(Long.class, null, new Long[] {}));
|
||||
concatElements(Long.class, null, new Long[] {}));
|
||||
assertArrayEquals(new Long[] {},
|
||||
ArrayUtils.concat(Long.class, new Long[] {}, new Long[] {}));
|
||||
concatElements(Long.class, new Long[] {}, new Long[] {}));
|
||||
}
|
||||
|
||||
public void testConcat() throws Exception {
|
||||
public void testconcatElements() throws Exception {
|
||||
assertArrayEquals(new Long[] { 1L },
|
||||
ArrayUtils.concat(Long.class, new Long[] { 1L }, new Long[] {}));
|
||||
concatElements(Long.class, new Long[] { 1L }, new Long[] {}));
|
||||
assertArrayEquals(new Long[] { 1L },
|
||||
ArrayUtils.concat(Long.class, new Long[] {}, new Long[] { 1L }));
|
||||
concatElements(Long.class, new Long[] {}, new Long[] { 1L }));
|
||||
assertArrayEquals(new Long[] { 1L, 2L },
|
||||
ArrayUtils.concat(Long.class, new Long[] { 1L }, new Long[] { 2L }));
|
||||
concatElements(Long.class, new Long[] { 1L }, new Long[] { 2L }));
|
||||
assertArrayEquals(new Long[] { 1L, 2L, 3L, 4L },
|
||||
ArrayUtils.concat(Long.class, new Long[] { 1L, 2L }, new Long[] { 3L, 4L }));
|
||||
concatElements(Long.class, new Long[] { 1L, 2L }, new Long[] { 3L, 4L }));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user