14 Stimmen

Ist es möglich, ein Javascript zu erstellen Benutzerdefinierte Funktion in Sqlite

HINTERGRUND:

  • Firefox 3 enthält SQLite Version 3.5.9. Firefox erlaubt auch Erweiterungen, die in Javascript geschrieben sind und die eingebettete SQLite-Engine aufrufen können.

  • Wie erwartet, wird die Ausführung der folgenden SQL-Anweisung 'SELECT "TEXT" REGEXP "T*";' führt zu einem Fehler, da es keine REGEXP-Funktion von Haus aus in SQLite enthalten ist.

  • javascript enthält eine eingebaute Regexp-Funktion.

  • SQLite erlaubt das Laden von Erweiterungen über SELECT load_extension('filename');

QUESTION: Ist es möglich, eine Erweiterung in SQLite zu laden, die in Javascript geschrieben ist, die REGEXP tun kann?

10voto

Noah Punkte 14800

Ja, es ist möglich, Javascript-Funktionen aufzurufen

//(thanks to Mirnal Kant, SQLManager)
//Version 2 -- Prevent Firefox crashing 
//          -- Suspect a problem with continual creation of Regex objects

var g_RegExpString = null;
var g_RegExp = null;

//functions to be created for the db
var smDbFunctions = { 
  // (0) = Regex Expression
  // (1) = Column value to test
    regexp: {
        onFunctionCall: function(val) {
            if (g_RegExp == null || val.getString(0) != g_RegExpString) 
            {
                g_RegExpString = val.getString(0);
                g_RegExp = new RegExp(g_RegExpString);
            }
            if (val.getString(1).match(g_RegExp)) return 1;
            else return 0;
        }
    }
}; 

nach der Instanziierung einer SQLite-Instanz:

Database.createFunction("REGEXP", 2, smDbFunctions.regexp);

3voto

Benoit Punkte 72929

Das, wovon Noah spricht, wurde in die SQLite-Manager Add-on für Firefox.

Wenn Sie dieses Add-on starten, können Sie auf das Symbol mit der Aufschrift f(x) um die Registerkarte Benutzerdefinierte Funktionen zu öffnen. Wählen Sie dort ein Verzeichnis, in dem Sie eine SQLite-Datenbank mit dem Namen smFunctions.sqlite und dem folgenden Schema haben:

CREATE TABLE "functions"          ( "name"        TEXT    PRIMARY KEY  NOT NULL
                                  , "body"        TEXT                 NOT NULL
                                  , "argLength"   INTEGER
                                  , "aggregate"   INTEGER              NOT NULL DEFAULT 0
                                  , "enabled"     INTEGER              NOT NULL DEFAULT 1
                                  , "extraInfo"   TEXT
                                  );

CREATE TABLE "aggregateFunctions" ( "name"        TEXT    PRIMARY KEY  NOT NULL
                                  , "argLength"   INTEGER
                                  , "onStepBody"  TEXT
                                  , "onFinalBody" TEXT
                                  , "enabled"     INTEGER              NOT NULL DEFAULT 1
                                  , "extraInfo"   TEXT
                                  );

Innerhalb dieser Tabelle können Sie benutzerdefinierte Funktionen definieren. Die Parameter werden als Array mit dem Namen aValues . Zum Beispiel:

INSERT INTO "functions" ("name", "body", "argLength", "aggregate", "enabled", "extraInfo")
VALUES('regexp_replace'
      ,'// exemple : SELECT regexp_replace(''FOOBAR'',''o+'',''a'',''gi'')
        var input      = new String(aValues.getString(0));
        var regex      = new String(aValues.getString(1));
        var substitute = new String(aValues.getString(2));
        var flags      = new String(aValues.getString(3));

        return input.replace(new RegExp(regex,flags), substitute);
       '
      ,4
      ,0
      ,1
      ,''
      );
  • wenn argLength == -1, dann gibt es keine Begrenzung für die Anzahl der Argumente. Sie können die Anzahl mit aValues.numEntries .
  • Sie können verwenden aValues.getTypeOfIndex(i) um den Typ des Arguments zu kennen: 0 => NULL, 1 => Integer ( aValues.getInt64(i) ), 2 => Real ( aValues.getDouble(i) ), 3 => String, siehe Beispiel.

Für Aggregatfunktionen können Sie verwenden this._store als anfänglich leeres Array, um die Elemente während der onStepBody-Phase zu schieben, und lesen Sie daraus in onStepFinal um das Endergebnis zu berechnen.

Im Folgenden finden Sie ein Bash-Skript, das Folgendes erstellt smFunctions.sqlite mit einigen benutzerdefinierten Funktionen (dies ist eine .dump meiner eigenen smFunctions.sqlite):

sqlite smFunctions.sqlite << EOF
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE "functions" ("name" TEXT PRIMARY KEY  NOT NULL, "body" TEXT NOT NULL, "argLength" INTEGER, "aggregate" INTEGER NOT NULL  DEFAULT 0, "enabled" INTEGER NOT NULL  DEFAULT 1, "extraInfo" TEXT);
INSERT INTO "functions" VALUES('accumulate','var sum = 0;
for (var j = 0; j < aValues.numEntries; j++) {
    sum += aValues.getInt32(j);
}
return sum;
',-1,0,1,NULL);
INSERT INTO "functions" VALUES('concatenate','var valArr = [];
var delim = new String(aValues.getString(0));
for (var j = 1; j < aValues.numEntries; j++) {
    switch (aValues.getTypeOfIndex(j)) {
    case 0:
        //NULL
        valArr.push(null);
        break;
    case 1:
        //INTEGER
        valArr.push(aValues.getInt64(j));
        break;
    case 2:
        //REAL
        valArr.push(aValues.getDouble(j));
        break;
    case 3:
        //TEXT
    default:
        valArr.push(aValues.getString(j));
    }
}
return valArr.join(delim);',-1,0,1,NULL);
INSERT INTO "functions" VALUES('regexp_match','var regExp = new RegExp(aValues.getString(0));
var strVal = new String(aValues.getString(1));

if (strVal.match(regExp)) {
    return 1;
}
else {
    return 0;
}

',2,0,1,NULL);
INSERT INTO "functions" VALUES('regexp_replace','// exemple : regexp_replace(''toto'',''o+'',''a'',''g'')
var input      = new String(aValues.getString(0));
var regex      = new String(aValues.getString(1));
var substitute = new String(aValues.getString(2));
var flags      = new String(aValues.getString(3));

return input.replace(new RegExp(regex,flags), substitute);
',4,0,1,NULL);
INSERT INTO "functions" VALUES('instr','var char = new String(aValues.getString(0));
var str  = new String(aValues.getString(1));
return str.indexOf(char, 0) + 1;',2,0,1,NULL);
INSERT INTO "functions" VALUES('rinstr','var char = new String(aValues.getString(0));
var str  = new String(aValues.getString(1));
return str.lastIndexOf(char) + 1;
',2,0,1,NULL);
CREATE TABLE "aggregateFunctions" ("name" TEXT PRIMARY KEY  NOT NULL, "argLength" INTEGER, "onStepBody" TEXT, "onFinalBody" TEXT, "enabled" INTEGER NOT NULL DEFAULT 1, "extraInfo" TEXT);
INSERT INTO "aggregateFunctions" VALUES('stdDev',1,'this._store.push(aValues.getInt32(0));','var iLength = this._store.length;
let total = 0;
this._store.forEach(function(elt) { total += elt });
let mean = total / iLength;
let data = this._store.map(function(elt) {
  let value = elt - mean;
  return value * value;
});
total = 0;
data.forEach(function(elt) { total += elt });
this._store = [];
return Math.sqrt(total / iLength);',1,NULL);
INSERT INTO "aggregateFunctions" VALUES('longest_prefix',1,'this._store.push(aValues.getString(0));','if (this._store.length == 0) {
        return "";
}
var prefix = this._store[0];
var prefixLen = prefix.length;
for (var i = 1; i < this._store.length && prefixLen > 0; i++) {
        var word = this._store[i];
        // The next line assumes 1st char of word and prefix always match.
        // Initialize matchLen to -1 to test entire word.
        var matchLen = 0;
        var maxMatchLen = Math.min(word.length, prefixLen);
        while (++matchLen < maxMatchLen) {
                if (word.charAt(matchLen) != prefix.charAt(matchLen)) {
                        break;
                }
        }
        prefixLen = matchLen;
}
return prefix.substring(0, prefixLen);',1,NULL);
COMMIT;
EOF

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X