SQLite User Forum

Application-defined non-deterministic functions can be used in CHECK when trusted_schema is OFF
Login

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.