online - sqlite local javascript
¿Es posible crear una función javascript definida por el usuario en sqlite? (2)
Sí. Es posible llamar a las funciones de JavaScript
//(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;
}
}
};
después de instanciar una instancia de SQLite:
Database.createFunction("REGEXP", 2, smDbFunctions.regexp);
FONDO:
Firefox 3 incluye SQLite versión 3.5.9. Firefox también permite extensiones, que están escritas en javascript y pueden llamar al motor SQLite incorporado.
Como era de esperar, ejecutando la siguiente instrucción SQL ''SELECT'' TEXT "REGEXP" T * "; '' da un error, ya que no hay una función REGEXP incluida de forma nativa en SQLite.
JavaScript incluye una función integrada de expresiones regulares.
SQLite permite extensiones cargables a través de SELECT load_extension (''filename'');
PREGUNTA: ¿Es posible cargar una extensión en SQLite que está escrita en javascript que puede hacer REGEXP?
De lo que Noah está hablando se ha incluido en el add-on de SQLite Manager para Firefox.
Cuando inicie este complemento, puede hacer clic en el icono etiquetado como f (x) para abrir la pestaña de funciones definidas por el usuario. A partir de ahí, seleccione un directorio en el que tenga una base de datos SQLite llamada smFunctions.sqlite, con el siguiente esquema:
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
);
Dentro de esa tabla puedes definir funciones personalizadas. Los parámetros se pasarán como una matriz llamada aValues
. Por ejemplo:
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
,''''
);
- si
argLength
== -1, entonces no hay límite en la cantidad de argumentos. Puede obtener el conteo conaValues.numEntries
. - Puede usar
aValues.getTypeOfIndex(i)
para conocer el tipo de argumento: 0 => NULL, 1 => Integer (aValues.getInt64(i)
), 2 => Real (aValues.getDouble(i)
), 3 = > Cadena, ver ejemplo.
Para las funciones agregadas puede usar this._store
como una matriz inicialmente vacía para empujar los elementos durante la fase onStepBody, y leer desde allí onStepFinal
para calcular el resultado final.
A continuación se muestra un script bash que creará smFunctions.sqlite
con algunas funciones personalizadas (este es un .dump
de mi propio 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