Merge changes from topic "sqlitez"

am: a920b9c85e

Change-Id: If22eef6e248b9e3bd1f5e22cd5081608494ad733
This commit is contained in:
Jeff Sharkey
2018-12-02 11:32:48 -08:00
committed by android-build-merger
7 changed files with 192 additions and 1050 deletions

View File

@@ -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);

View File

@@ -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>

View File

@@ -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

View File

@@ -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) {

View File

@@ -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));
}
}

View File

@@ -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 }));
}
}