Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing user defined SQL functions for SQLite using Java or Groovy?

With SQLite, user defined SQL functions can easily be added using the C api or PHP. But is it also possible using Java or Groovy?

like image 968
otto.poellath Avatar asked Jul 07 '10 06:07

otto.poellath


2 Answers

Since the question is about solution in Java or Groovy, someone can look here for Java solution (just like i did)

Below you can see simple function that validates, if provided dates are in same day:

public class IsSameDay extends Function {

    @Override
    protected void xFunc() throws SQLException {
        if (args() != 2) {
            throw new SQLException("IsSameDay(date1,date2): Invalid argument count. Requires 2, but found " + args());
        }
        try {
            DateTime t1 = DateTime.parse(value_text(0).replace(" ", "T"));
            DateTime t2 = DateTime.parse(value_text(1).replace(" ", "T"));
            if (t1.getYear() == t2.getYear() && t1.getDayOfYear() == t2.getDayOfYear()) {
                result(1);
            } else {
                result(0);
            }
        } catch (Exception exception) {
            throw new SQLDataException("IsSameDay(date1,date2): One of Arguments is invalid: " + exception.getLocalizedMessage());
        }
    }
}

and if someone needs to do an aggregation function, then might find this example useful:

public class MyMax extends Function.Aggregate {

    private long buff = 0;

    public MyMax() {
    }

    @Override
    protected void xStep() throws SQLException {
        long current = value_long(0);
        if (current > buff) {
            buff = current;
        }

    }

    @Override
    protected void xFinal() throws SQLException {
        result(buff);
    }
}

accumulator works because in each query MyMax instantion is cloned, so start value can be provided when instantiating object.

at the end example showing how to attach functions:

Function.create(c, IsSameDay.class.getSimpleName(), new IsSameDay());
Function.create(c, MyMax.class.getSimpleName(), new MyMax());

Hope that someone will find it useful.

like image 105
T.G Avatar answered Oct 09 '22 02:10

T.G


It turns out writing a user defined function is actually quite easy using SQLiteJDBC. Here's a Groovy example:

@GrabConfig(systemClassLoader=true)
@Grab('org.xerial:sqlite-jdbc:3.6.16')
import org.sqlite.*
import java.sql.*

db = groovy.sql.Sql.newInstance("jdbc:sqlite::memory:","org.sqlite.JDBC")

// a distance function using the spherical law of cosines
Function.create(db.getConnection(), "distance", new Function() {
    protected void xFunc() throws SQLException {
        def lat1 = value_double(0)
        def lon1 = value_double(1)
        def lat2 = value_double(2)
        def lon2 = value_double(3)

        double theta = lon1 - lon2;
        double dist = (Math.sin(deg2rad(lat1)) * Math.sin(deg2rad(lat2))) + 
            (Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.cos(deg2rad(theta)))
        dist = Math.acos(dist)
        dist = rad2deg(dist)
        dist = dist * 60 * 1.1515
        dist = dist * 1.609344
        result(dist);
    }

    def deg2rad(deg) {
      deg * Math.PI / 180.0
    }

    def rad2deg(rad) {
      rad * 180.0 / Math.PI
    }
})

db.execute("CREATE TABLE city(name, lat, lon)")
db.execute("INSERT INTO city(name, lat, lon) VALUES('New York City', 40.7143, -74.0060)")
db.execute("INSERT INTO city(name, lat, lon) VALUES('San Francisco', 37.7749, -122.4194)")
db.execute("INSERT INTO city(name, lat, lon) VALUES('Paris', 48.8567, 2.3510)")
db.execute("INSERT INTO city(name, lat, lon) VALUES('Cologne', 50.9407, 6.9599)")

db.eachRow("SELECT a.name as a, b.name as b, distance(a.lat, a.lon, b.lat, b.lon) as d FROM city a, city b WHERE a.name != b.name ORDER BY d;") {
    println "Distance from ${it.a} to ${it.b}: ${it.d}km"
}
like image 38
otto.poellath Avatar answered Oct 09 '22 01:10

otto.poellath