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

c90pui9n  于 2023-11-21  发布在  SQLite
关注(0)|答案(2)|浏览(162)

在SQLite中,确保Tcl变量被视为正确类型的正确方法是什么?
这个活动会话似乎是将number设置为50的最简单的场景,但SQLite似乎将:number视为字符串,将:number+0$number视为数字。
如果改用incr nbr 50,则SQLite会将: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

字符串

wj8zmpe1

wj8zmpe11#

我记得,如果你通过expr运行这个值,那么它会得到SQLite的数值解释,但是你需要小心一点,不要在测试时让Tcl把这个值分解为一个文字:

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

字符串
当执行更多面向生产的代码时,这往往不是一个大问题,因为您将常量放在SQL代码中,并且只参数化更改的值(并且这些值很可能具有数值性质)。
如果你真的担心这个问题,可以在SQL中输入CAST
在字节码级别,expr变成通过一个名为tryCvtToNumeric的操作发送值,如果值可以有一个数值解释,该操作将为值提供一个数值解释。字面量默认为没有解释。形式上,我们更喜欢C代码不依赖于现有的值解释,而是只要求他们想要的解释,但是tclsqlite扩展(这样命名是为了将其与底层库区分开来)不这样做,而且从来没有这样做过。
当处理BLOB数据时,这一点更重要。这是当你使用@var而不是:var时。

9q78igpj

9q78igpj2#

据我所知,sqlite会监听它接收到的Tcl值的objtype,并根据它来改变它的行为,我觉得这是对系统的滥用,或者至少是对系统的误解。“type”实际上只是一个缓存值,反映了它上次被当作什么来处理,并没有任何关于该值 * 是什么 * 的意义(就像在数据库类型的意义上)。也就是说,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值除了string之外没有其他正式类型的想法,并坚持假装它们有,每个案例都有这个问题。
更进一步,允许Tcl值具有 * 多个 * 缓存对象类型的工作进展缓慢(tip 445隐藏了实现与值关联的objtype的内部细节,因此可以对其进行更改以支持此想法)。(通常称为“Hydra”--一个多头Tcl值系统),因此我有具体的证据证明它可以正常工作并执行。这主要是为了解决一个性能问题,即一个值在快速连续的两次或两次以上的解释中反复使用,被称为“ Flink ”(尽管我已经开始看到人们使用这个术语来表示obtype的更改,即使是一次。我使用“converted”来表示)。在Hydra值模型下,上面的代码将累积它所引用的所有objtype,通过窥探objtype来假装Tcl值具有类型的方法在Hydra世界中完全福尔斯。

相关问题