确保 Tcl 的 SQLite 中的变量替换始终是所需的类型?

Ensuring that variable substituion in SQLite from Tcl will always be of the desired type?

提问人:Gary 提问时间:11/11/2023 最后编辑:Gary 更新时间:11/11/2023 访问量:73

问:

在SQLite中确保Tcl变量被视为正确类型的正确方法是什么?

此活动会话似乎是设置为 的最简单的方案,但它似乎 SQLite 将其视为字符串和数字。number50:number:number+0$number

如果改为使用 SQLite,则SQLite将被视为一个数字。incr nbr 50:nbr

有没有办法在 Tcl 中“声明”变量,以便 SQLite 根据需要处理它;还是应该在 SQL 表达式中强制转换每个值?

我想知道如何确保查询始终按预期工作,而不是仅仅使用“失败”的变量,以便在测试时被识别为所需的变量;我还没有认识到一些模式或最佳实践。cast

如果所有变量替换都不被视为字符串并转换为 SQL 中的数字(除非测试显示问题),那么未来的 SQLite 版本是否会导致它“正常工作”的实例更改为需要强制转换?

谢谢。

% package require sqlite3
3.43.2
% sqlite3 db
% set number 50
50
% db eval {select min(300, :number)}
300
% db eval {select min(300,:number+0)}
50
% db eval "select min(300,$number)"   
50
% db eval {select min(300, cast(:number as integer))}
50
% incr nbr 50
50
% db eval {select min(300,:nbr)}
50
% db eval "select min(300,$nbr)"
50

% db eval {create table numbers (id integer, value integer);
 insert into numbers values (1,300);}
% db eval {select min(value,:number) from numbers;} 
300
SQLite 三氯化科技

评论


答:

1赞 Donal Fellows 11/11/2023 #1

我记得,如果你运行这个值,那么它会得到SQLite的数字解释,但你需要小心一点,阻止Tcl在测试时只是将值作为文字分解出来:expr

set number 50
set number [expr {$number}]
db eval {select min(300, :number)}

在执行更多面向生产的代码时,这往往不是一个大问题,因为您在 SQL 代码中放置常量并且只参数化更改的值(无论如何,这些值很可能具有数字性质)。

如果你真的担心它,在你的 SQL 中放一个。CAST


在字节码级别,通过调用的操作发送值,如果值可以有,则为值提供数字解释。文本默认为没有解释。从形式上讲,我们更喜欢 C 代码不依赖于现有的值解释,而只是要求他们想要的值,但 tclsqlite 扩展(称为它以将其与底层库区分开来)没有这样做,也从未这样做过。exprtryCvtToNumeric

在处理数据时,这一点更为重要。那是你使用而不是......BLOB@var:var

评论

0赞 mrcalvin 11/11/2023
嗅探 Tcl 可变的内部值表示是 SQLite 的一个值得商榷的设计决策。就我个人而言,我总是会明确并使用.CAST
0赞 Gary 11/12/2023
谢谢。如果所有通过 和 ' 查询写入的变量都是(或使用的严格表),那么在执行可用于数值或字符串值的比较时(我假设几乎是所有值),数据库列应该也是还是只有 Tcl 变量?INSERTUPDATECASTCAST
1赞 Cyan Ogilvie 11/11/2023 #2

据我所知,sqlite 窥探了它收到的 Tcl 值的 objtype,并据此改变它的行为,我认为这是对系统的滥用,或者至少是对它的误解。该“类型”实际上只是一个缓存值,反映了它上次被处理的内容,并且实际上对该值是什么没有任何意义(就像在数据库类型意义上一样)。也就是说,sqlite 本身更多地将数据库类型视为准则而不是规则,并且会很乐意存储列声明以外的内容,因此如果您眯着眼睛看得恰到好处,它应该大部分工作正常。

您的示例演示了为什么窥探上次使用的 objtype 不是一个有效的策略:

set number 50
puts "at this stage, number's objtype is: [tcl::unsupported::representation $number]"
# Programmer likely thinks of $number as containing a number,
# but it's actually a string

set number [expr {$number}]
puts "after coercion by expr: [tcl::unsupported::representation $number]"
# Now it's been used as a number, and has an integer objtype

puts "digits in number: [string length $number]"
# Programmer still thinks it's a number, but now it's a string again:
puts "after string length: [tcl::unsupported::representation $number]"

# And, just for fun, let's view it as a list:
puts "llength: [llength $number]"
# now it's a list:
puts "after llength: [tcl::unsupported::representation $number]"

输出:

at this stage, number's objtype is: value is a pure string with a refcount of 2, object pointer at 0xffffffffdba1d820, string representation "50"
after coercion by expr: value is a int with a refcount of 2, object pointer at 0xffffffffdba1e270, internal representation 0x32:0x0, no string representation
digits in number: 2
after string length: value is a utf32string with a refcount of 2, object pointer at 0xffffffffdba1e270, internal representation 0xffffffffdba505b0:0x0, string representation "50"
llength: 1
after llength: value is a list with a refcount of 2, object pointer at 0xffffffffdba1e270, internal representation 0xffffffffdba48c10:0x0, string representation "50"

这些年来,我与一些人进行过很多讨论,他们无法接受 Tcl 值真的、真的没有字符串以外的形式类型,并坚持假装他们有,每个案例都有这个问题。

更进一步,允许 Tcl 值具有多个缓存的 objtype 的工作进展缓慢(提示 445 隐藏了实现与值关联的 objtype 的内部细节,以便可以对其进行更改以支持此想法)。几年前,我做了一个实验,使用TIP(通常称为“Hydra”——一个多头Tcl值系统)实现了这个想法,所以我有具体的证据证明它工作和执行正确。这主要是为了解决一个性能问题,即一个值被快速连续地反复使用,并有两种或多种解释,称为“闪烁”(尽管我开始看到人们使用该术语来指代 obtype 的变化,甚至一次。为此,我使用“converted”)。在 Hydra 值模型下,上述代码将累积它所引用的所有 objtypes,从而使后续访问成为这些 objtypes 中的任何一个都很快。应该清楚的是,通过窥探 objtype 来假装 Tcl 值具有类型的方法在 Hydra 世界中完全崩溃了。

评论

0赞 Gary 11/12/2023
谢谢你的解释和例子。我不知道在 Tcl 中查询有关 reprsentation 的信息如此容易。从您的示例中,我认为 SQLite 将我的整数视为字符串的原因是,除了首先声明它们或从传入请求或其他数据库表中提取它们之外,我从未在 Tcl 中对它们做任何事情,因此它们是纯字符串表示。
0赞 Gary 11/12/2023
也许,当列/类亲和力有机会工作时,它可以与 Hydra 设置一起使用,因为 SQLite 可以从 Hydra 中选择适合的类型而无需重新转换它。除了最后一个之外,我的例子有点不公平,因为没有向SQLite提供任何信息来确定亲和力。但是,如果函数像比较运算符那样应用亲和力,那就太好了;则 -substitued variabe 将被强制转换为列的数据类,而不是其最新的 Tcl 表示形式。至少它似乎正在这样做。min():
0赞 Cyan Ogilvie 11/13/2023
是的,正确的做法是让 c 代码(此处为 sqlite)从 Tcl 值中请求特定类型,由类/列亲和力/等告知。然后,该类型的处理程序将返回该缓存的内部 rep(如果它已经有该值的内部 rep),或者尝试从该值的字符串 rep(或其他类型的现有 intrep)生成一个新 rep。这样一来,该类型由请求该值的代码提供,并且与它是否生活在九头蛇世界中无关,并且不受基于该值的先前使用状态的滞后性的影响。