Quieting the SQL Syntax Warnings

val db: SQLiteDatabase = TODO("open a database")

val st = db.compileStatement("ATTACH DATABASE ? AS plaintext KEY ''")

By default, Android Studio does not like this SQL statement. It puts a red undersquiggle below KEY, complaining that it is expecting a ; instead.

According to SQLite, Android Studio is correct.

This error is coming from a SQL “language injection”. Rebecca Franks wrote a great blog post about language injections a few weeks ago. There is a “SQLiteDatabase methods” language injection set up in my Android Studio 4.1.2 installation, and I assume that it shipped with Studio itself.

However, in my case, I’m right, and Android Studio is wrong.

That is because in this code I am not using plain SQLite — I am using SQLCipher for Android. SQLCipher for Android has an extended form of ATTACH DATABASE that takes a KEY option to supply a passphrase (shown here as the empty string '').

It is unclear why a language injection for android.database.sqlite.SQLiteDatabase is affecting calls to net.sqlcipher.database.SQLiteDatabase. Perhaps it is because net.sqlcipher.database.SQLiteDatabase implements androidx.sqlite.db.SupportSQLiteDatabase, the SQLite indirection API that allows you to use things like SQLCipher for Android with Room and SQLDelight. Or maybe this is a limitation of the scoping rules for language injections, and anything named SQLiteDatabase will be affected.

Regardless, the red undersquiggle was annoying me.

I could disable that language injection, but then I lose syntax highlighting and validation everywhere else. Instead, I want to be able to mark certain statements as being correct and suppress the warning, akin to how @SuppressLint() works. However, this is not a Lint check, so @SuppressLint itself does not help here.

Rebecca’s post gave me an idea, though, which turns out to work, in a couple of different ways.

In her post, she shows that you can use a //language= comment to cause a particular language injection to be applied in a spot it might not normally be applied. So, while this would result in no errors:

private const val ATTACH_THIS = "ATTACH DATABASE ? AS plaintext KEY ''"

…this would complain about KEY:

//language=sql
private const val ATTACH_THIS = "ATTACH DATABASE ? AS plaintext KEY ''"

In the first snippet, the IDE has no idea that this string is a SQL statement and so does not apply any language injections. In the second, we teach it that this statement contains SQL, so the IDE applies the appropriate language injection.

So, one workaround is to pull the offending SQL out of the SQLiteDatabase calls:

val db: SQLiteDatabase = TODO("open a database")

val st = db.compileStatement(ATTACH_THIS)

If we skip the //language=sql comment on the ATTACH_THIS declaration, then we will not get any complaints about KEY.

Another workaround is to override the language injection at the call site:

val db: SQLiteDatabase = TODO("open a database")

//language=text
val st = db.compileStatement("ATTACH DATABASE ? AS plaintext KEY ''")

Here, we tell the IDE “Treat this as plain text. Yes, yes, I know, it looks like it should be SQL. And, yeah, I do not really know if you know what ‘plain text’ is. Just skip any syntax validation on the string, please.”

(fortunately, the comment is shorter)

At least for now, these workarounds give you fine-grained ability to suppress SQL syntax warnings, in cases where Android Studio’s SQL parser either has bugs or is confused by extended SQL syntax.