Repository navigation
v2.1.0
Decimals keep their places as text, five more refusals carry a real
server's number, and both batteries now run over RPC as well.
pip install --upgrade pysqlbridge
SELECT CONCAT(1234567.89, '') AS label, -- '1234567.89', was '1.23457e+006'
CAST(CAST(12.567 AS money) AS varchar(10)) AS price, -- '12.57'
NTILE((SELECT 2)) OVER (ORDER BY id) AS half
FROM peopleA decimal is written with the places it carries. A float has no places
of its own, so 1.50 and 1.5 were one value here and every decimal became
text as a float's six significant digits: CONCAT(1234567.89, '') was
1.23457e+006 and LEN(1234567.89) was 12. A number now carries the places
it was written with, so the text is 1234567.89 and the length 10.
CONCAT(1.50, '') keeps its trailing nought, a cast to decimal(5,3) shows
three places, and a variable declared as one keeps what it was declared
with. Money is the one type whose text is narrower than its value: it holds
four places and shows two, rounded, so CAST(CAST(12.567 AS money) AS varchar(30)) is 12.57. Every source this serves declares its numbers
float, so a report concatenating a price into a label was reading seventeen
digits where a real server shows the two it wrote.
The places are only ever carried, never worked out. Arithmetic over two
decimals leaves a float here, which is the old divergence the README sets
out: closing it needs the declared type of every operand.
A value where a condition belongs is msg 4145. T-SQL has no boolean to
read one as, so IIF(1, 'a', 'b') is refused where this answered 'a'; a
real server reads an IIF as the CASE it stands for. A batch cut short
inside a condition says the same, wherever it stops afterwards, so WHERE rank and WHERE rank ORDER are 4145 while WHERE rank = 1 stays 102 for
the statement being unfinished.
An expression with nothing in it to take a type from is refused. A
CASE whose every result is the word NULL is msg 8133, IIF with both
results NULL the same, COALESCE with every argument one is 4127, and
NULLIF whose first argument is one is 4151. All four answered NULL here.
A NULL that carries a type is not the constant, so a cast, a declared
variable and a column are all fine.
A window function's argument may be a subquery. NTILE((SELECT 2)),
LAG(score, (SELECT 1)), FIRST_VALUE((SELECT 5)) and SUM((SELECT 1)) OVER (...) all answer on a real server and all were refused here, because
the arguments were never lifted the way a select list's are. An aggregate
that reduces the rows is the other way about: SUM((SELECT 2)),
SUM(SUM(score)) and SUM(score + (SELECT 2)) are msg 130, settled while
compiling, where this answered them.
Messages name the text they were given. A real server types 'ab' as
varchar and N'ab' as nvarchar and says which, so CAST('ab' AS int) now
says varchar where everything here used to say nvarchar. A cast to
varchar, char or text makes narrow text and the next message about it
says so. Every column this server serves is nvarchar and is named that way.
A batch of nothing but a DECLARE or a SET runs. DECLARE @x int = 'abc'
on its own is msg 245, DECLARE @x decimal(3,1) = 12345.6 is 8115 and
DECLARE @x int = 5/0 is 8134. This answered nothing at all, because a
batch with no statement that reads was passed over. Found by the truncation
sweep, which sends the prefixes of a battery query and had never had one
that ended at a DECLARE before.
Both batteries now also run the way a client sends a parameterised
query. A parameterised statement arrives as an RPC call to sp_executesql
rather than as a batch, which is a different path through this server and
the one a client uses for half of what it sends: SSMS sends 50 of its 93
queries that way. differential.ps1 -AsRpc and batches.ps1 -AsRpc send
the same batteries over it, and the switch checks that an RPC really went
out before it reports agreement.
1,236 queries answer as SQL Server 2025 does, with the same column types and
the same message numbers, and so do 146 whole batches, statement by
statement, over batches and RPC alike. Of 8,211 truncated prefixes, none is
answered here that a real server refuses and none is refused here that it
answers; 73 differ by error number alone, down from 135.
A decimal is still held as a float, so 0.1 + 0.2 is 0.30000000000000004
here and 0.3 there. The README says what closing that would take.