SQL Funktionen in SQLite als BeanShell-Scripts

vorhergehende Artikel in: Java Komponenten sQLshell
23.06.2024

Ich habe bereits hin und wieder über Erweiterungen der sQLshell berichtet, die bestimmte spezifische, proprietäre Features des jeweiligen DBMS in der sQLshell verfügbar machen.

Man kann sehr einfach Funktionen zu SQLite hinzufügen. Dabei handelt es sich um Funktionen, die einen skalaren Rückgabewert haben - für Table-valued Funktionen gilt das hier gesagte noch nicht.

Meine Idee war, in der sQLshell das neue Feature der Startup-Scripte zu benutzen, um der jeweiligen Datenbank neue Funktionen hinzuzufügen. In der API existiert ein sehr einfaches Beispiel:

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!"); } });

Mit den entsprechenden Anpassungen an die sQLshell wird daraus folgendes Skript:

conn=sQLshellAPI.getCurrentlyVisibleShell().getConnection();

specs=sQLshellAPI.getCurrentlyVisibleShell().getDBMSInterface().getDatabaseSpecifics().orElse(null);

if(specs!=null) { myFunc=new org.sqlite.Function() { protected void xFunc() { System.out.println("myFunc called!"); } };

specs.create("myFunc", myFunc); }

Dieser Code funktioniert auch, allerdings sollte es schon möglich sein, Funktionen mittels Parametern zu konfigurieren und Ergebnisse zurückzugeben. Dazu existieren Methoden in der Klasse org.sqlite.Function - diese lassen sich aber aus der Implementierung der Funktion xFunc im obigen Beispiel nicht aufrufen: Geskriptete Objekte sind in BeanShell nur als Implementierungen von Interfaces gedacht - nicht um wie oben dargestellt anonyme Klassen abzuleiten.

Daher musste ich zu einer List greifen: Ich definierte ein neues Interface:

public interface Function
{
    void xFunc() throws java.sql.SQLException;
}

und schrieb eine Klasse, um die bestehende SQLite API und die sQLshell zu verbinden:

public class Bridge extends org.sqlite.Function
{
    private final Function function;

public Bridge(Function function) { super(); this.function = function; }

@Override protected void xFunc() throws java.sql.SQLException { function.xFunc(); } /** * Returns the number of arguments passed to the function. Can only be called from * <tt>xFunc()</tt>. */ public int iargs() throws java.sql.SQLException { return super.args(); } /** * Called by <tt>xFunc</tt> to return a value. * * @param value */ public void iresult(byte[] value) throws java.sql.SQLException { super.result(value); }

/** * Called by <tt>xFunc</tt> to return a value. * * @param value */ public void iresult(double value) throws java.sql.SQLException { super.result(value); }

/** * Called by <tt>xFunc</tt> to return a value. * * @param value */ public void iresult(int value) throws java.sql.SQLException { super.result(value); }

/** * Called by <tt>xFunc</tt> to return a value. * * @param value */ public void iresult(long value) throws java.sql.SQLException { super.result(value); }

/** Called by <tt>xFunc</tt> to return a value. */ public void iresult() throws java.sql.SQLException { super.result(); }

/** * Called by <tt>xFunc</tt> to return a value. * * @param value */ public void iresult(String value) throws java.sql.SQLException { super.result(value); }

/** * Called by <tt>xFunc</tt> to throw an error. * * @param err */ public void ierror(String err) throws java.sql.SQLException { super.error(err); }

/** * Called by <tt>xFunc</tt> to access the value of an argument. * * @param arg */ public String ivalue_text(int arg) throws java.sql.SQLException { return super.value_text(arg); }

/** * Called by <tt>xFunc</tt> to access the value of an argument. * * @param arg */ public byte[] ivalue_blob(int arg) throws java.sql.SQLException { return super.value_blob(arg); }

/** * Called by <tt>xFunc</tt> to access the value of an argument. * * @param arg */ public double ivalue_double(int arg) throws java.sql.SQLException { return super.value_double(arg); }

/** * Called by <tt>xFunc</tt> to access the value of an argument. * * @param arg */ public int ivalue_int(int arg) throws java.sql.SQLException { return super.value_int(arg); }

/** * Called by <tt>xFunc</tt> to access the value of an argument. * * @param arg */ public long ivalue_long(int arg) throws java.sql.SQLException { return super.value_long(arg); }

/** * Called by <tt>xFunc</tt> to access the value of an argument. * * @param arg */ public int ivalue_type(int arg) throws java.sql.SQLException { return super.value_type(arg); } }

Mit diesen Hilfsmitteln ausgestattet kann man nun in der sQLshell per BeanShell beliebige SQL-Funktionen schreiben:

conn=sQLshellAPI.getCurrentlyVisibleShell().getConnection();

specs=sQLshellAPI.getCurrentlyVisibleShell().getDBMSInterface().getDatabaseSpecifics().orElse(null);

if(specs!=null) { myFunc=new de.elbosso.db.spi.impl.sqlite.Function() { protected void xFunc() { func.iresult(func.iargs()>0?func.ivalue_text(0):null); } }; func=new de.elbosso.db.spi.impl.sqlite.Bridge(myFunc);

specs.create("myFunc", func); }

Auf die Idee kam ich auf der Suche nach Möglichkeiten, UUIDs in SQLite zu benutzen. Hier einige meiner Suchergebnisse dazu:

Leider funktioniert das nur für normale Funktionen - für Aggregatfunktionen musste ich weit tiefer in die Trickkiste greifen...

Alle Artikel rss Wochenübersicht Monatsübersicht Github Repositories Gitlab Repositories Mastodon Über mich home xmpp


Vor 5 Jahren hier im Blog

Neueste Artikel

  • Spieleengine IX

    Es gibt seit Ewigkeiten mal wieder Neues von meiner eigenen Spieleengine!

    Weiterlesen...
  • Neue Version plantumlinterfaceproxy napkin look

    Es gibt eine neue Version des Projektes plantumlinterfaceproxy - Codename napkin look.

    Weiterlesen...
  • Apache HTTPCore5 funktioniert nicht mit Docker

    Ich habe neulich drei Stunden meines Lebens verschwendet weil ich unbedingt die neueste Version der HTTPCore5 Library von Apache einsetzen wollte.

    Weiterlesen...

Manche nennen es Blog, manche Web-Seite - ich schreibe hier hin und wieder über meine Erlebnisse, Rückschläge und Erleuchtungen bei meinen Hobbies.

Wer daran teilhaben und eventuell sogar davon profitieren möchte, muß damit leben, daß ich hin und wieder kleine Ausflüge in Bereiche mache, die nichts mit IT, Administration oder Softwareentwicklung zu tun haben.

Ich wünsche allen Lesern viel Spaß und hin und wieder einen kleinen AHA!-Effekt...

PS: Meine öffentlichen GitHub-Repositories findet man hier - meine öffentlichen GitLab-Repositories finden sich dagegen hier.