home

-- Data page structure and dbcc page.
Author Nigel Rivett

-- create a table to test with
create table test (s char(10), t varchar(10))
insert test select 'abc', 'def'
insert test select 'ghi', 'jkl'
insert test select 'ghi', null

/*
Now read the data page.
format for dbcc page is
dbcc page (dbname/id, file number, page number, option)	- option = 2 for headers
the file number and page number for the table can be found from column "first" in the root entry in sysindexes.
*/
select * from sysindexes where id = object_id('test') and indid in (1,0)
/*
first = 0xB64738000100
the first four bytes are the page numver - the last two the file number
These entries are byte reversed and must be converted to integers for the dbcc page command
so 
PageNum = 003847B6
FileNum = 0001
*/

declare @first binary(6)
select @first = first from sysindexes where id = object_id('test') and indid = 0
declare @PageNum int
select @PageNum = convert(int, substring(@first,4,1) + substring(@first,3,1) + substring(@first,2,1) + substring(@first,1,1) )
declare @FileNum int
select @FileNum = convert(int, substring(@first,6,1) + substring(@first,5,1))
select @FileNum, @PageNum

/*
now we can run the dbcc page command to get the data page of the table.
*/
declare @sql varchar(1000)
select @sql = 'dbcc page (''' + db_name() + ''', ' + convert(varchar(10),@FileNum) + ', ' +  convert(varchar(10),@PageNum) + ', 2)'
select @sql
dbcc traceon(3604)
exec (@sql)

/*
I omit the header info - but it is informative to look at.

DATA:
-----

Memory Dump @0x45F8A000
-----------------------
45F8A000:  01010000 00800000 00000000 00000e00  ................
45F8A010:  00000000 00000200 59d1f14f 6c1f9000  ........Y..Ol...
45F8A020:  b6473800 01000000 55340000 b2bb0000  .G8.....U4......
45F8A030:  02000000 00000000 00000000 00000000  ................
45F8A040:  00000000 00000000 00000000 00000000  ................
45F8A050:  00000000 00000000 00000000 00000000  ................
45F8A060:  30000e00 61626320 20202020 20200200  0...abc       ..
45F8A070:  00010018 00646566 30000e00 67686920  .....def0...ghi 
45F8A080:  20202020 20200200 00010018 006a6b6c        .......jkl
45F8A090:  0000f845 79007300 63006f00 6d000000  ...Ey.s.c.o.m...
45F8A0A0:  90a0f845 00000000 00000000 00000000  ...E............
45F8A0B0:  a0a0f845 00000000 28000000 f8a0f845  ...E....(......E
45F8A0C0:  b0a0f845 00000000 00000000 00000000  ...E............
45F8A0D0:  c0a0f845 00000000 0000ca00 ffff0000  ...E............

Now to decode the page.
size = 8Mb
Header = 96b

Header
PageID		6b		= b64738000100 (byte reversed)
Next page ID	6b		= 000055340000
Prev page ID	6b		= b2bb00000200


data row
start location 45F8A060
StatusBitsA	1b		= 30 	(10 for all fixed data 30 for some variable length)
StatusBitsB	1b		= 00
LenFixedBytes	2b		= 0e00	byte reversed - to Noofcols inc 1st byte
FixedLenData			= 67686920202020202020	-- this is in coloffset order in syscolumns
NoOfCols	1b + 00		= 02 + 00
NullBitMap	NoCols/8 b	= 00
NoVarLenCols	1b + 00		= 01 + 00
ColOffsettArray	2b per varcol	= 18 00		pos where col ends - byte reversed col ends at 0018 in page from 
VarLenColData	nb		= 646566

To find the column allocation look at syscolumns.
The columns are stored in the order of coloffset.
Positive values for fixed length data, negative for variable length.
For the fixed length data the lengths can be obtained from syscolumns.
For the variable length use the ColOffsettArray and NullBitMap.

Note trailing variable length cols which contain null are  not included in the data row.
The NoOfCols is set to exclude them.
This is why an alter table to add a null column does not affect any data rows.
*/

drop table test

home