我将BLOB存储在sqlite3数据库表中。 表格相当简单:
create table r (id integer primary_key asc, d blob);
我写了一些用户定义的函数(让我们调用其中一个“udf()”)来读取blob中的特定值。当我使用这样的函数时:
select udf(100,d), udf(200,d) from r;
sqlite3在内部复制数据d(使用EXPLAIN我发现vbde创建OP_COPY而不是OP_SCOPY)没有必要。是否有可能提示解析器重新使用列d来调用udf()而不是复制它(以及所有blob数据))(1)? 我已经在调用sqlite3_create_functionv2时用SQLITE_DETERMINISTIC标记了该函数。
(1)https://www.sqlite.org/opcode.html#Copy表示“每个字符串或blob都有重复。”
答案 0 :(得分:1)
通过比较用户定义的和标准的sqlite函数行为,我怀疑Copy操作代码可能来自其他原因而不是udf(例如分组)。
您没有指定udf
的来源,因此我将继续 reducetio per absurdum ,假设udf(d,i) = substr(d,i,5)
所以:
[SQLiteFunction(Name = "udf", Arguments = 2, FuncType = FunctionType.Scalar)]
public class Udf : SQLiteFunction
{
public override object Invoke(object[] args)
{
string res = args[0].ToString();
int start = int.Parse(args[1].ToString());
res = res.Substring(start, 5);
res = res.ToUpper();
return res;
}
}
来电者演示代码为:
static void Main(string[] args)
{
SQLiteConnection sqLiteConnection = new SQLiteConnection(
@"Data Source=C:\sqlite\test.s3db");
sqLiteConnection.Open();
SQLiteFunction.RegisterFunction(typeof(Udf));
//explain select
SQLiteCommand command = new SQLiteCommand("explain select udf(d,2) from test_blob;", sqLiteConnection);
//SQLiteCommand command = new SQLiteCommand("explain select substr(d,2,5) from test_blob;", sqLiteConnection);
SQLiteDataReader DR = command.ExecuteReader();
while (DR.Read())
{
Console.WriteLine(DR[0].ToString() + ": " + DR[1].ToString() + ": " + DR[2].ToString()
+ ": " + DR[3].ToString() + ": " + DR[4].ToString() + ": " + DR[5].ToString());
}
//sqLiteConnection.UnRegisterFunction(function);
sqLiteConnection.Close();
Console.ReadLine();
}
您在输出中看不到复制
0: Init: 0: 9: 0:
1: OpenRead: 0: 2: 0: 2
2: Rewind: 0: 7: 0:
3: Column: 0: 1: 2:
4: Function: 2: 2: 1: udf(2)
5: ResultRow: 1: 1: 0:
6: Next: 0: 3: 0:
7: Close: 0: 0: 0:
8: Halt: 0: 0: 0:
9: Transaction: 0: 0: 1: 0
10: TableLock: 0: 2: 0: test_blob
11: Integer: 2: 3: 0:
12: Goto: 0: 1: 0:
如果您在选择
中引入a group by substr(d,7,1);
SQLiteCommand command = new SQLiteCommand("explain select udf(d,2) from test_blob group by substr(d,7,1);", sqLiteConnection);
导致复制发生(而不是udf本身,如果你用标准的sqlite函数替换udf就像substr一样)
0: Init: 0: 41: 0:
1: SorterOpen: 1: 2: 0: k(1,B)
2: Integer: 0: 3: 0:
3: Integer: 0: 2: 0:
4: Null: 0: 6: 6:
5: Gosub: 5: 38: 0:
6: OpenRead: 0: 2: 0: 2
7: Rewind: 0: 14: 0:
8: Column: 0: 1: 10:
9: Function: 6: 10: 8: substr(3)
10: Column: 0: 1: 9:
11: MakeRecord: 8: 2: 13:
12: SorterInsert: 1: 13: 0:
13: Next: 0: 8: 0:
14: Close: 0: 0: 0:
15: OpenPseudo: 2: 13: 2:
16: SorterSort: 1: 40: 0:
17: SorterData: 1: 13: 2:
18: Column: 2: 0: 7:
19: Compare: 6: 7: 1: k(1,B)
20: Jump: 21: 25: 21:
21: Move: 7: 6: 1:
22: Gosub: 4: 32: 0:
23: IfPos: 3: 40: 0:
24: Gosub: 5: 38: 0:
25: Column: 2: 1: 1:
26: Integer: 1: 2: 0:
27: SorterNext: 1: 17: 0:
28: Gosub: 4: 32: 0:
29: Goto: 0: 40: 0:
30: Integer: 1: 3: 0:
31: Return: 4: 0: 0:
32: IfPos: 2: 34: 0:
33: Return: 4: 0: 0:
34: Copy: 1: 15: 0:
35: Function: 2: 15: 14: udf(2)
36: ResultRow: 14: 1: 0:
37: Return: 4: 0: 0:
38: Null: 0: 1: 1:
39: Return: 5: 0: 0:
40: Halt: 0: 0: 0:
41: Transaction: 0: 0: 1: 0
42: TableLock: 0: 2: 0: test_blob
43: Integer: 7: 11: 0:
44: Integer: 1: 12: 0:
45: Integer: 2: 16: 0:
46: Goto: 0: 1: 0:
C ++ / Native / unmanaged version
哦......在您的问题中,您提到了sqlite3_create_functionv2
和SQLITE_DETERMINISTIC
。
对于所有.net开发人员:这里停止降压,答案已经完成,你可以跳过这一段。
一切都是不变的但是,如果你更喜欢非托管代码版本,这里是:
IntPtr pointer_db;
string my_db_file = "test.s3db";
int open_ret_code = sqlite3open(my_db_file, out pointer_db);
Console.WriteLine("open ret code:" + open_ret_code.ToString());
Console.ReadLine();
udf_delegate my_delegate = my_udf;
IntPtr pointer_udf = Marshal.GetFunctionPointerForDelegate(my_delegate);
int udf_ret_code = CreateFunction(pointer_db, "udf", 2, 1 | 0x800, IntPtr.Zero, pointer_udf, IntPtr.Zero, IntPtr.Zero);
Console.WriteLine("udf ret code: "+udf_ret_code.ToString());
Console.ReadLine();
string query = "explain select udf(d,100) from test_blob;";
IntPtr lpData = Marshal.StringToHGlobalAuto(query);
IntPtr lpLength = new IntPtr(query.Length);
IntPtr stmHandle;
IntPtr Tail;
int qry_retcode;
try
{
qry_retcode = sqlite3_prepare(pointer_db, query, query.Length,
out stmHandle, out Tail);
}
catch (Exception exc)
{
Console.WriteLine("prepare: " + exc.Message);
return;
}
Console.WriteLine("query ret code: "+qry_retcode.ToString());
Console.ReadLine();
for (int i_step = 0; i_step < 90; i_step++)
{
int the_row = sqlite3_step(stmHandle);
Console.WriteLine("the row: " + the_row.ToString());
Console.ReadLine();
if (the_row != 100)
{
break;
}
IntPtr my_res = sqlite3_column_text(stmHandle, 0);
string my_str_res = Marshal.PtrToStringAnsi(my_res);
Console.WriteLine("query result id: " + my_str_res);
my_res = sqlite3_column_text(stmHandle, 1);
my_str_res = Marshal.PtrToStringAnsi(my_res);
Console.WriteLine("query result Op Code: " + my_str_res);
}
显然,使用相应的sqlite.dll导入以及udf的delegate
。
[DllImport("sqlite3.dll", EntryPoint = "sqlite3_create_function", CallingConvention = CallingConvention.Cdecl)]
private static extern int CreateFunction(
IntPtr dbHandle,
string functionName,
int numArgs,
int textEncoding,
IntPtr pApp,
IntPtr xFunc,
IntPtr xStep, // null
IntPtr xFinal // null
);
[DllImport("Sqlite3.Dll", EntryPoint = "sqlite3_open", CallingConvention = CallingConvention.Cdecl)]
public static extern int sqlite3open(string filename, out IntPtr dbhandle);
[DllImport("Sqlite3.Dll", EntryPoint = "sqlite3_close", CallingConvention = CallingConvention.Cdecl)]
public static extern int sqlite3close(IntPtr dbhandle);
[DllImport("Sqlite3.Dll", EntryPoint = "sqlite3_value_text", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr sqlite3_value_text(IntPtr value);
[DllImport("Sqlite3.Dll", EntryPoint = "sqlite3_value_int", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr sqlite3_value_int(IntPtr value);
[DllImport("Sqlite3.Dll", EntryPoint = "sqlite3_result_text", CallingConvention = CallingConvention.Cdecl)]
public static extern int sqlite3_result_text(IntPtr context, string msg, int len, IntPtr transient);
[DllImport("Sqlite3.Dll", EntryPoint = "sqlite3_prepare_v2", CallingConvention = CallingConvention.Cdecl)]
private static extern int sqlite3_prepare(IntPtr database, string query, int length, out IntPtr statement, out IntPtr tail);
[DllImport("Sqlite3.Dll", EntryPoint = "sqlite3_step", CallingConvention = CallingConvention.Cdecl)]
public static extern int sqlite3_step(IntPtr statement);
[DllImport("Sqlite3.Dll", EntryPoint = "sqlite3_column_text", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr sqlite3_column_text(IntPtr statement, int columnNumber);
delegate void udf_delegate(IntPtr context, int argc, IntPtr[] argv);
static void my_udf(IntPtr context, int argc, IntPtr[] argv)
{
IntPtr text_ptr = sqlite3_value_text(argv[0]);
string text = Marshal.PtrToStringAnsi(text_ptr);
string result = text.Substring(2, 5); //"here is my udf";
sqlite3_result_text(context, result, result.Length, IntPtr.Zero);
return;
}