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

前端 未结 4 1763
轮回少年
轮回少年 2020-12-10 19:44

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?

相关标签:
4条回答
  • 2020-12-10 20:23

    Simple way to create a function which accepts arguments and return result:

            Function.create(conn, "addNS", new Function() {
                @Override
                protected void xFunc() {
                    System.out.println("myFunc called!");
                    String arg1;
                    try {
                        arg1 = value_text(0);
                        System.out.println("function arg1:"+arg1);
                        result("NS-"+arg1);
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            },  1, Function.FLAG_DETERMINISTIC);
    
            rs = conn.createStatement().executeQuery("select addNS('xyz');");
            while(rs.next()) {
                String val = rs.getString(1);
                System.out.println("Function return val : " + val);
            }
    
    0 讨论(0)
  • 2020-12-10 20:25

    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.

    0 讨论(0)
  • 2020-12-10 20:25

    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"
    }
    
    0 讨论(0)
  • 2020-12-10 20:25

    I think, this is a simple way to create custom function,

    Class.forName("org.sqlite.JDBC");
    Connection conn = DriverManager.getConnection("jdbc:sqlite:");
    Function.create(conn, "myFunc", new Function() {
               protected void xFunc() {
                   System.out.println("myFunc called!");
               }
           });        
    conn.createStatement().execute("select myFunc();");
    
    0 讨论(0)
提交回复
热议问题