One of the drawbacks of not being in the SQL team at Microsoft any longer is that I don’t know about all the undocumented features in the next release – I have to hunt around for them like everyone else :-( So I was poking about in SSMS in 2008 CTP-6 and noticed a function called sys.fn_PhysLocCracker that I’d never heard of. Doing an sp_helptext on it gets the following output: — Name: sys.fn_PhysLocCracker declare @page_id binary (4) — Page ID is the first four bytes, then 2 bytes of page ID, then 2 bytes of slot Cool – but something else I’ve never heard of %%physloc%% – what’s that? After playing around for a while, I figured out how to make it work. Just to be confusing, there’s another identical version of the function called sys.fn_PhysLocFormatter – and that’s the only one I could get to work. Here’s an example: CREATE TABLE TEST (c1 INT IDENTITY, c2 CHAR (4000) DEFAULT ‘a’); SELECT sys.fn_PhysLocFormatter (%%physloc%%) AS [Physical RID], * FROM TEST; Physical RID c1 It’s a physical-record locator function! Undocumented and unsupported (obviously), but hey, some of the best features are :-) It gives the database file, page within the file, and slot number on the page in the format (file:page:slot). I can think of a *bunch* of uses for this which I’ll be exploring over the next few months. How cool is that?!?!
—
— Description:
— Cracks the output of %%physloc%% virtual column
—
— Notes:
——————————————————————————-
create function sys.fn_PhysLocCracker (@physical_locator binary (8))
returns @dumploc_table table
(
[file_id] int not null,
[page_id] int not null,
[slot_id] int not null
)
as
begin
declare @file_id binary (2)
declare @slot_id binary (2)
—
select @page_id = convert (binary (4), reverse (substring (@physical_locator, 1, 4)))
select @file_id = convert (binary (2), reverse (substring (@physical_locator, 5, 2)))
select @slot_id = convert (binary (2), reverse (substring (@physical_locator, 7, 2)))
insert into @dumploc_table values (@file_id, @page_id, @slot_id)
return
end
GO
INSERT INTO TEST DEFAULT VALUES;
INSERT INTO TEST DEFAULT VALUES;
INSERT INTO TEST DEFAULT VALUES;
GO
GO
—————– ———–
(1:411:0) 1
(1:411:1) 2
(1:413:0) 3
Black Friday Super Sale!
It’s the time of year again where companies do crazy sales on the day after Thanksgiving in the US, and we’re doing the same! Starting
15 thoughts on “SQL Server 2008: New (undocumented) physical row locator function”
Very coll, its can be very useful.
Thanks for Support! :-)
Very cool, yet the possibility for misuse scares the *** out of me… I -almost- wish you hadn’t blogged it (but on the flip side, it certainly is useful — so thanks!)
In your page in "Create Function" you said "create function sys.fn_PhysLocCracker (@physical_locator binary (8))"
In SQL you said SELECT sys.fn_PhysLocFormatter (%%physloc%%) AS [Physical RID], * FROM TEST;
sys.fn_PhysLocCracker
sys.fn_PhysLocFormatter
Is it Cracker or Formatter?
Actually both of these exist but the only one I could get to work is sys.fn_PhysLocFormatter. I’ve put a comment in the post to that effect.
Thanks!
Another useful bit of T-SQL Code – lists the File_ID, Page_ID, Slot_ID at the end :
select *, %%physloc%%
from AdventureWorks2008.Person.Address
cross apply fn_PhysLocCracker(%%physloc%%)
But, you probably had this code already …
Is there a similar function for Sql Server 2005 ?
No – there’s nothing similar for 2005
I could get the cracker to work using it in the following way:
— get’s the root page of:
SELECT file_id, page_id
FROM sys.system_internals_allocation_units iau
INNER JOIN sys.partitions p ON iau.container_id = p.partition_id
CROSS APPLY sys.fn_PhysLocCracker(iau.root_page)
WHERE p.object_id = OBJECT_ID(@objectName)
AND p.index_id = 1
AND iau.type = 1
Michael Swart already made a similar comment, but once your TEST table is built, you can use sys.fn_PhysLocCracker like this:
SELECT *
FROM dbo.TEST
CROSS APPLY sys.fn_PhysLocCracker(%%physloc%%)
c1 c2 file_id page_id slot_id
———– ——— ———– ———– ———–
1 a 1 7706 0
2 a 1 7706 1
3 a 1 7708 0
4 a 1 7708 1
5 a 1 7709 0
6 a 1 7709 1
Cheers,
Chris
Hello Paul! Thank to the post.
I made some tests and noted the %%physloc%% is not a RID, or at least, the pointer to the row in the heap.
For example, if you run a query like “select col,%%physloc%% from MyHeap” and have a nonclustered index and QP choose use them, then %%physloc%% will return values according to page that belongs to the index.
Do you know some hack like this, that allows us retrieve the stored RID in leaf level of the noncluster index?
Thanks for the attention.
Correct – it’s the RID of the record used to retrieve the column(s) you ask for. If you want to force the heap, use an index hint of 0, if you want to force a nonclustered index, only ask for columns covered by the nonclustered index, and potentially use an index hint with that index ID.
Hi Paul!
And if you cannot access a heap by some reason (e.g it is corrupted)?
I think if I could access the value of RID that is stored on leaf level of index, I would be able to retrieve the values
(for columns in an index) of the pages that were corrupted. Is this correct?
Do you know something like %%physloc%% that returns the RID of a HEAP that is leaf level of a nonclustered index of a heap?
Thanks again for you attention.
Yes – use the function to get the nonclustered index RID and then do DBCC PAGE on that page to get the heap RID. You’ll need to convert from the hex – four byte page, two byte file, two byte slot – all byte reversed within their fields.
haha!
Cool! Thanks!
Paul,
Thanks for sharing. I do Oracle and SQL Server and this was very helpful in confirming the similarity between Oracle’s rowid and SQL Server’s %$physloc%% Here is my article linking back to this post https://wordpress.com/post/markadamsoracleandsqlserver.wordpress.com/53
-Mark Adams