2012-03-25 20 views
12

En Android, android.database.sqlite.SQLiteStatement me permite usar declaraciones preparadas en SQLite para evitar ataques de inyección. Su método execute es adecuado para operaciones de creación/actualización/eliminación, pero no parece haber ningún método para consultas que devuelva un cursor o similar.Consultas con declaraciones preparadas en Android?

Ahora en iOS puedo crear declaraciones preparadas de tipo sqlite3_stmt* y usarlas para consultas, así que sé que esto no es una limitación de SQLite. ¿Cómo puedo realizar consultas con declaraciones preparadas en Android?

Respuesta

39

una declaración preparada le permite hacer dos cosas

  • acelerar el rendimiento, ya que la base de datos no tiene que analizar la instrucción cada vez
  • unen & argumentos de escape en la cuenta de lo que son salvación contra ataques de inyección

que no saben exactamente dónde/cuándo aplicación androides SQLite utiliza realmente sqlite3_prepare (afiak no sqlite3_prepare_v2 - ver here) pero t lo usa de lo contrario no podría obtener Reached MAX size for compiled-sql statement cache errors.

Así que si quiere consultar la base de datos debe confiar en la implementación, no hay manera de que yo sepa hacerlo con SQLiteStatement.

Con respecto a la seguridad de inyección, cada método de consulta, inserción, etc. de la base de datos tiene versiones (a veces alternativas) que le permiten vincular argumentos.

E.g.si usted desea conseguir un Cursor de

SELECT * FROM table WHERE column1='value1' OR column2='value2' 

Cursor SQLiteDatabase#rawQuery(

  • String sql,: completa SELECT DECLARACIÓN que puede incluir ? todas partes
  • String[] selectionArgs: lista de valores que reemplazan ?, en Para que aparezcan

)

Cursor c1 = db.rawQuery(
    "SELECT * FROM table WHERE column1=? OR column2=?", 
    new String[] {"value1", "value2"} 
); 

Cursor SQLiteDatabase#query (

  • String table,: nombre de la tabla, puede incluir JOIN etc
  • String[] columns,: lista de las columnas se requiere, null = *
  • String selection,: WHERE cláusula withouth WHERE puede/debe incluir ?
  • String[] selectionArgs,: lista de valores que reemplazan ?, en el orden en que aparecen
  • String groupBy,: GROUP BY cláusula w/o GROUP BY
  • String having,: HAVING cláusula w/o HAVING
  • String orderBy: ORDER BY cláusula w/o ORDER BY

)

Cursor c2 = db.query("table", null, 
    "column1=? OR column2=?", 
     new String[] {"value1", "value2"}, 
     null, null, null); 

Via ContentProviders - ese caso es un poco diferente, ya que interactúa con un proveedor abstracta, no es una base de datos. No hay garantía de que exista una base de datos sqlite que respalde el ContentProvider. Entonces, a menos que sepa qué columnas hay/cómo el proveedor funciona internamente, debe apegarse a lo que dice la documentación.

Cursor ContentResolver#query(

  • Uri uri,: un URI que representa la fuente de datos (traducido internamente a una mesa)
  • String[] projection,: lista de las columnas se requiere, null = *
  • String selection,: WHERE cláusula withouth WHERE puede/debe incluir ?
  • String[] selectionArgs,: lista de valores que reemplazan ?, en el orden en que aparecen
  • String sortOrder: ORDER BY cláusula w/o ORDER BY

)

Cursor c3 = getContentResolver().query(
    Uri.parse("content://provider/table"), null, 
    "column=? OR column2=?", 
     new String[] {"value1", "value2"}, 
     null); 

Consejo: si quieres LIMIT Aquí puede añadir a la ORDER BY cláusula:

String sortOrder = "somecolumn LIMIT 5"; 

o dependiendo de la aplicación de la ContentProvider añadirlo como un parámetro a la Uri:

Uri.parse("content://provider/table?limit=5"); 
// or better via buildUpon() 
Uri audio = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; 
audio.buildUpon().appendQueryParameter("limit", "5"); 

En todos los casos ? será reemplazado por la versión escapado de lo que se pone en el argumento se unen.

? + "hack'me" = 'hack''me'

+1

posterior excepcional, gracias, me ha ayudado mucho. Tiempos como estos me hacen pensar que necesitamos poder modificar más de una vez. – Louis

+1

Incluso si esta respuesta es muy antigua, ¿hay alguna solución para enlazar algo más que una Cadena? Mis consultas solicitan un valor numérico para ser vinculante, pero todos los métodos de SqliteDatabase usan una matriz String. Y si yo mismo creo el PreparedStatement, puedo ejecutar la consulta para recuperar el Cursor ... No puedo creer que no haya solución, pero no lo he encontrado todavía. – AxelH

+0

@AxelH La solución es que todo se puede expresar como una cadena. Por ejemplo, use 'String.valueOf (yourNumericThingHere)'. – zapl

Cuestiones relacionadas