Application-defined non-deterministic functions can be used in CHECK when trusted_schema is OFF
(1) By ChiZhang on 2025-05-08 08:50:46 [source]
Hi,
According to the documentation at https://sqlite.org/appfunc.html#security_implications, application-defined functions cannot be used in CHECK constraints within a table definition under the untrusted schema. However, the following Java code executes successfully without any errors with sqlite-jdbc-3.47.2.0.jar.
In this code, I create a non-deterministic function adf
, which just return 1
.
import org.sqlite.Function;
import java.sql.*;
public class test {
public static void main(String[] args) {
try {
String cmd = "rm ./test.db";
Runtime.getRuntime().exec(cmd);
} catch (Exception e) {
e.printStackTrace();
}
try (Connection conn = DriverManager.getConnection("jdbc:sqlite:test.db")) {
Adf.create(conn);
Statement stmt = conn.createStatement();
stmt.execute("PRAGMA trusted_schema=OFF;");
stmt.execute("CREATE TABLE t1(c0 TEXT CHECK(adf()));");
stmt.execute("INSERT OR IGNORE INTO t1(c0) VALUES(1);");
ResultSet rs = stmt.executeQuery("SELECT c0 FROM t1");
while (rs.next()) {
System.out.println("Results: " + rs.getString("c0"));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class Adf extends Function {
@Override
protected void xFunc() throws SQLException {
StringBuilder output = new StringBuilder();
result("1");
}
public static void create(Connection conn) throws Exception {
// Function.create(conn, "adf", new Adf(), 0x800);
Function.create(conn, "adf", new Adf());
}
}
If I create the function as a deterministic function with 0x800
, then this code can trigger the fellowing error, which is expected:
org.sqlite.SQLiteException: [SQLITE_ERROR] SQL error or missing database (unsafe use of adf())
at org.sqlite.core.DB.newSQLException(DB.java:1179)
at org.sqlite.core.DB.newSQLException(DB.java:1190)
at org.sqlite.core.DB.throwex(DB.java:1150)
at org.sqlite.core.NativeDB.prepare_utf8(Native Method)
at org.sqlite.core.NativeDB.prepare(NativeDB.java:135)
at org.sqlite.core.DB.prepare(DB.java:264)
at org.sqlite.jdbc3.JDBC3Statement.lambda$execute$0(JDBC3Statement.java:55)
at org.sqlite.jdbc3.JDBC3Statement.withConnectionTimeout(JDBC3Statement.java:458)
at org.sqlite.jdbc3.JDBC3Statement.execute(JDBC3Statement.java:44)
at test.main(test.java:19)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at jdk.compiler/com.sun.tools.javac.launcher.Main.execute(Main.java:484)
at jdk.compiler/com.sun.tools.javac.launcher.Main.run(Main.java:208)
at jdk.compiler/com.sun.tools.javac.launcher.Main.main(Main.java:135)
I believe that neither deterministic nor non-deterministic functions should be allowed in CHECK constraints. Am I misunderstanding this?
(2) By Stephan Beal (stephan) on 2025-05-08 14:26:22 in reply to 1 [link] [source]
Function.create(conn, "adf", new Adf(), 0x800);
FYI: a flag of 0x800 is not legal - it's missing the text encoding part (e.g. SQLITE_UTF8).
Here's the repro case ported to C:
#undef NDEBUG #include <stdio.h> #include <assert.h> #include "sqlite3.h" void xFunc(sqlite3_context* c,int argc,sqlite3_value**argv){ } int main(int argc, char const * const * argv){ int rc = 0; sqlite3 * db = 0; sqlite3_stmt *q = 0; int iFlags = SQLITE_UTF8; if( argc>1 ){ /* Pass any argument to add this flag, which will trigger an assert() failure */ iFlags |= SQLITE_DETERMINISTIC; } printf("func flags=%08x\n", iFlags); rc = sqlite3_open(":memory:", &db); assert( 0==rc ); rc = sqlite3_create_function(db, "foo", 0, iFlags, 0, xFunc, 0, 0); assert( 0==rc ); rc = sqlite3_exec(db, "PRAGMA trusted_schema=OFF; " "CREATE TABLE t1(c0 TEXT CHECK(foo()));" "INSERT OR IGNORE INTO t1(c0) VALUES(1)", 0, 0, 0); assert( 0==rc ); rc = sqlite3_prepare(db, "SELECT c0 FROM t1", -1, &q, 0); assert( 0==rc ); while( SQLITE_ROW== (rc = sqlite3_step(q)) ){ printf("row: %s\n", sqlite3_column_text(q, 0)); } sqlite3_finalize(q); assert( SQLITE_DONE==rc ); rc = 0; sqlite3_close(db); return rc; }
Makefile:
udf: udf.c cc -o udf -I. udf.c sqlite3.c -O0
Which results in:
[stephan@nuc:~/tmp]$ m udf cc -o udf -I. udf.c sqlite3.c -O0 [stephan@nuc:~/tmp]$ ./udf func flags=00000001 row: 1 [stephan@nuc:~/tmp]$ ./udf x func flags=00000801 udf: udf.c:28: main: Assertion `0==rc' failed. Aborted (core dumped)
(3) By Richard Hipp (drh) on 2025-05-08 17:08:52 in reply to 1 [link] [source]
Thank you for the report.
Please try again with the latest "Pre-release snapshot" from the Download page and let us know whether or not your issue has been cleared. I think you will find that the latest snapshot works better in this regard, but we would like to have your confirmation.
(4) By ChiZhang on 2025-05-09 11:14:05 in reply to 3 [link] [source]
Hi Richard, thank you for your work in resolving this issue. I can confirm that it has been successfully resolved, both with the C and JDBC drivers.