OriginChainDB docs
examples · tds · 5 / 5

5. T-SQL that is refused

← SQL Server wire examples
what this does

Everything here comes back as an explicit error naming the construct. Nothing runs, nothing is half-applied, and the connection stays open — a refusal is a normal answer on this surface, not a fault. This page is the honest half of the compatibility story, and the half worth reading first if you are costing a migration.

The design rule behind all of it is soundness over coverage: a construct without an exact equivalent is refused rather than approximated, because an approximation returns an answer that looks right.

variables and built-in globals
-- refused
DECLARE @cutoff INT = 100000;
SELECT id FROM invoices WHERE amount_cents > @cutoff;
SELECT @@VERSION;
SELECT @@ROWCOUNT;

-- do this instead: bind the value as a parameter, so the driver
-- carries it and the engine never sees a variable at all.
--   JDBC:  ps.setInt(1, 100000)
--   Go:    db.Query("... amount_cents > @p1", 100000)
-- Row counts come back through the driver's own update-count API.

Note the difference between a T-SQL variable and a driver placeholder. The Go driver writes @p1 for a bind parameter and that is fine — it is rewritten to a positional bind before the statement is parsed. A variable you declared yourself is a different thing, and there is nowhere for it to live.

temporary objects
-- refused
SELECT id, amount_cents INTO #recent FROM invoices WHERE status = 'open';
SELECT * FROM #recent;

-- do this instead: an ordinary table, or a CTE if the scope is one query.
WITH recent AS (
    SELECT id, amount_cents FROM invoices WHERE status = 'open'
)
SELECT * FROM recent;
the GO separator
-- refused: GO is a client-tool directive, not a server statement.
INSERT INTO invoices (id, status) VALUES (1, 'open');
GO
INSERT INTO invoices (id, status) VALUES (2, 'open');
GO

-- do this instead: send one statement per batch. Your driver already
-- does that unless you are pasting a script written for a query tool.

This one is caught before parsing rather than after, because a SQL Server grammar reads a trailing GO as a column alias — so SELECT 1 GO would parse cleanly and mean something you did not write. A standalone GO line is what is rejected; go used as a column name is untouched.

MERGE
-- refused
MERGE invoices AS t
USING staged  AS s ON t.id = s.id
WHEN MATCHED     THEN UPDATE SET t.status = s.status
WHEN NOT MATCHED THEN INSERT (id, status) VALUES (s.id, s.status);

-- do this instead
INSERT INTO invoices (id, status)
SELECT id, status FROM staged
ON CONFLICT (id) DO UPDATE SET status = EXCLUDED.status;
date and time arithmetic
-- refused: there is no calendar timestamp type behind this surface,
-- so there is no sound mapping. Timestamps are ISO-8601 strings.
SELECT DATEADD(day, -7, GETDATE());
SELECT DATEDIFF(day, created_at, GETDATE());
SELECT CONVERT(VARCHAR(10), created_at, 112);   -- style code = formatting

-- do this instead: compute the boundary in your application and bind it.
--   Instant cutoff = Instant.now().minus(7, ChronoUnit.DAYS);
--   ps.setString(1, cutoff.toString());
SELECT id FROM invoices WHERE created_at >= ?;

This is the refusal that catches most reporting queries. There is no calendar timestamp type behind this surface to do the arithmetic on, so rather than invent one the translator declines and you do the arithmetic where a real calendar exists — in your application.

the rest
-- refused: no LIMIT equivalent
SELECT TOP 10 PERCENT * FROM invoices;
SELECT TOP 10 * FROM invoices ORDER BY amount_cents DESC WITH TIES;

-- refused: constrain the statement with a predicate instead
DELETE TOP (100) FROM invoices WHERE status = 'void';

-- refused: the INTO clause would be dropped, and the statement would
-- report a successful table copy that created nothing.
SELECT * INTO invoices_archive FROM invoices;
-- do this instead: declare the target, then fill it.
CREATE TABLE invoices_archive (
    id            INT NOT NULL,
    customer_ref  VARCHAR(64),
    amount_cents  INT,
    status        VARCHAR(16)
);
INSERT INTO invoices_archive SELECT * FROM invoices;

-- refused: ambiguous in T-SQL itself - the column's declared type decides
-- whether this concatenates or adds.
SELECT 'INV-' + some_column;
-- do this instead
SELECT CONCAT('INV-', CAST(some_column AS VARCHAR(64)));
procedures — the expensive one
-- refused: T-SQL procedures are not a surface this engine has.
CREATE PROCEDURE dbo.settle_invoice @id INT AS
BEGIN
    UPDATE invoices SET status = 'settled' WHERE id = @id;
END;
GO
EXEC dbo.settle_invoice @id = 4101;

-- OriginChainDB's procedures are a PL/pgSQL-shaped subset and are created
-- and called over the SQL endpoint or the PostgreSQL wire. This is a
-- rewrite in a different language, not a port.

If your application's logic lives in stored procedures, this is the line item that decides the size of the project, and no amount of wire compatibility reduces it. Read the procedures section of the TDS reference, which shows one procedure written both ways, before you estimate.

Microsoft, SQL Server and T-SQL are trademarks of the Microsoft group of companies. PostgreSQL is a registered trademark of the PostgreSQL Community Association of Canada. Named here only to describe protocol and dialect compatibility. OriginChainDB is not affiliated with, endorsed by, or sponsored by any of them, and does not distribute their software.