Noah所说的内容已经包含在Firefox的
SQLite Manager插件中。
当您启动此插件时,可以单击标记为
f(x)的图标打开用户定义函数选项卡。从那里,选择一个目录,在该目录下有一个名为smFunctions.sqlite的SQLite数据库,并具有以下架构:
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
);
在表格内,您可以定义自定义函数。参数将作为名为
aValues
的数组传递。例如:
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
,''
);
- 如果
argLength
== -1,则没有参数数量限制。您可以使用aValues.numEntries
获取参数数量。
- 您可以使用
aValues.getTypeOfIndex(i)
来知道参数的类型:0 => NULL,1 => 整数(aValues.getInt64(i)
),2 => 实数(aValues.getDouble(i)
),3 => 字符串,请参见示例。
对于聚合函数,您可以使用this._store
作为最初为空的数组,在onStepBody阶段推送元素,并在onStepFinal
中从中读取以计算最终结果。
下面是一个bash脚本,将创建带有一些自定义函数的smFunctions.sqlite
(这是我自己的smFunctions.sqlite的.dump
):
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