Skip to content

About

Forensic PostgreSQL viewer that reads PGDATA directly - no server, read-only. Recovers deleted, updated, and aborted rows; parses WAL for commit timestamps and rows that live only in the log; carves free space; and surfaces B-tree index traces of long-gone rows.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PostgreSQL Viewer

PostgreSQL 데이터 디렉토리(PGDATA)를 서버 없이 직접 읽는 포렌식 뷰어입니다. 테이블과 행을 보여 줄 뿐 아니라, 삭제된 행 · 수정 전 버전 · WAL 에만 남은 행까지 복구해 시각과 함께 보여 줍니다.

항목 내용
지원 버전 PostgreSQL 17 · 18 (페이지·튜플·카탈로그 해석은 다른 버전도 대체로 동작, WAL 은 17 · 18 전용)
입력 PGDATA 폴더 또는 PGDATA 를 담은 zip (압축을 풀지 않고 읽음)
원본 읽기 전용 — 원본 파일을 쓰거나 서버를 띄우지 않는다
엔진 Python 3.12 표준 라이브러리만 사용
화면 FastAPI + 단일 HTML (로컬 전용, 127.0.0.1)

목차

  1. 왜 서버를 띄우지 않는가
  2. 기능 한눈에 보기
  3. 설치와 실행
  4. 사용 설명서 (화면)
  5. 행의 상태와 출처 읽는 법
  6. 라이브러리로 쓰기 (상위 도구 연동)
  7. 검증 도구와 테스트
  8. 한계와 주의점
  9. 문제 해결
  10. 프로젝트 구조
  11. 라이선스

1. 왜 서버를 띄우지 않는가

압수·수집한 PostgreSQL 폴더를 보는 흔한 방법은 같은 버전의 서버(예: Docker)를 띄워 SQL 로 조회하는 것입니다. 포렌식에서는 이 방법에 문제가 있습니다.

문제 설명 이 뷰어
증거 변형 서버가 시작되면 크래시 복구(WAL 재생)·체크포인트로 파일을 다시 쓴다 파일을 읽기만 한다
삭제 흔적 소실 SQL 은 살아 있는 행만 보여 주고, autovacuum 이 지운 흔적을 정리한다 삭제·수정 전 행을 그대로 보여 준다
WAL 미활용 WAL 에 남은 옛 데이터·커밋 시각을 SQL 로는 볼 수 없다 WAL 을 직접 해석한다
환경 의존 메이저 버전·아키텍처가 맞는 서버가 필요하다 Python 만 있으면 된다

2. 기능 한눈에 보기

기본 열람

  • 데이터베이스 · 스키마 · 테이블 목록: 카탈로그(pg_class · pg_attribute · pg_type …)를 직접 해석
  • 행 보기: 검색 · 정렬 · 페이지 나누기 · 상태/출처 필터 · CSV/JSON 내보내기
  • 타입 디코딩: 정수·실수·문자열·numeric·jsonb·bytea·배열·enum·도메인·날짜/시각·uuid·inet 등
  • 긴 값(TOAST): pglz/lz4 압축 해제, 외부 저장 청크 결합
  • DROP 된 컬럼: 테이블을 다시 쓰기 전까지 남아 있는 원시 바이트 표시

포렌식 복구

기능 무엇을 찾나
삭제·수정 전 행 파일에 아직 남은 deleted · updated(구버전) · aborted(롤백) 튜플
WAL 분석 트랜잭션 커밋/롤백 시각, WAL 에만 남은 행(전체 페이지 이미지 · INSERT/UPDATE 레코드), 파일에서 정리된 TOAST 청크 보충
재활용 세그먼트 이름과 다른 옛 WAL 이 남은 pg_wal 파일까지 페이지 주소로 정렬해 읽음
빈 공간 카빙 라인 포인터가 가리키지 않는 페이지 영역에 남은 옛 튜플
수정 이력 체인 같은 행의 버전들을 xmax = 다음 버전의 xmin 규칙으로 잇고, 바뀐 컬럼만 비교
인덱스 흔적 행은 지워졌는데 B-tree 인덱스에 남은 키 (사라진 행의 ID 등)

분석 편의

  • 시각 변환: epoch 밀리초로 저장된 정수 컬럼 자동 감지 → 날짜로 표시, 행마다 만든/지운 시각
  • ID 링크: 값이 다른 테이블의 기본키와 같으면 클릭해 그 행으로 이동 (돌아가기 지원)
  • 인용 복사: 입력 해시 · 파일 · 오프셋 · 트랜잭션 · 값 · 튜플 SHA-256 을 보고서용 텍스트로
  • 행 상세: 튜플 헤더, 컬럼별 바이트 위치, 헥스 덤프(컬럼에 마우스를 올리면 해당 바이트 강조)
  • 무결성: 입력 SHA-256 기록, 분석 후 재확인

3. 설치와 실행

준비물

  • Windows / macOS / Linux 어디서나 Python 3.12 이상
  • 화면을 쓰려면 FastAPI · Uvicorn (아래 명령으로 설치). 엔진만 쓰면 추가 설치가 필요 없다.

설치 (처음 한 번)

git clone https://github.com/ws1004/postgresql-viewer.git
cd postgresql-viewer
py -m venv .venv
.venv\Scripts\python -m pip install -r requirements.txt

실행

.venv\Scripts\python -X utf8 -m uvicorn pgviewer.api.main:app --host 127.0.0.1 --port 8000

브라우저에서 http://127.0.0.1:8000 을 엽니다. 끝낼 때는 터미널에서 Ctrl+C.

로컬 전용입니다. --host 127.0.0.1 로만 띄우세요. 경로 입력 기능이 이 PC 의 파일을 읽기 때문에, 다른 Host 헤더 · 다른 출처의 요청 · 네트워크(UNC) 경로는 서버가 거부합니다.


4. 사용 설명서 (화면)

4-1. 증거 열기

화면 맨 위 입력칸에 경로를 넣고 [열기] 를 누릅니다.

입력 예
zip C:\cases\case01\postgresql.zip
PGDATA 폴더 D:\case01\var\lib\postgresql\17\main
그 상위 폴더 D:\case01 (네 단계 아래까지 PG_VERSION + global/pg_control 을 찾는다)
  • [zip 업로드] 는 파일을 서버의 work/uploads/ 에 복사해 엽니다. 같은 PC 라면 원본을 복사하지 않는 경로 입력을 권장합니다.
  • 열면 오른쪽 위에 PG 버전 · 클러스터 상태 · SHA-256 이 표시됩니다.
    • shut down (초록) = 정상 종료. 다른 상태는 크래시나 운영 중 복사본일 수 있다.
  • 네트워크 경로(\\서버\공유)와 드라이브 루트(C:\)는 열 수 없습니다. 로컬로 복사한 뒤 여세요.

4-2. 데이터베이스와 테이블 고르기

  • 왼쪽 위 드롭다운: 데이터베이스. 템플릿이 아닌 첫 사용자 DB 가 자동 선택된다.
  • 왼쪽 목록: 스키마별 테이블. 오른쪽 숫자는 파일 크기(0 B = 빈 테이블).
    • 테이블 이름 필터 로 검색
    • 시스템 카탈로그 · TOAST 보기 를 켜면 pg_catalog 등 내부 테이블도 보인다

4-3. 데이터 탭 — 행 보기

public.events  table · oid 16569 · filenode 17010   base/16390/17010   수정 이력 7개
[✓ live 79] [✓ updated 14]  │  [✓ 현재 파일 86] [✓ WAL 7]  │  [ ] 수정 이력만
[값 검색 (Enter)]  ✓시스템 정보  ✓삭제된 컬럼  ✓시각 변환               [CSV] [JSON]
조작 동작
상태 칩 체크 해제 그 상태의 행을 숨김 (예: live 를 끄면 삭제·수정 흔적만)
출처 칩 체크 해제 현재 파일 / 빈 공간 / WAL 중 일부만 보기
수정 이력만 버전 체인이 있는 행만, 이력 순서대로
값 검색 모든 컬럼에서 부분 일치 (Enter)
컬럼 머리글 클릭 정렬 (한 번 더 누르면 반대로)
시스템 정보 상태 · 출처 · tid · xmin · xmax · 이력 열 표시
삭제된 컬럼 DROP 된 컬럼(취소선 머리글)의 남은 바이트 표시
시각 변환 epoch ms 컬럼을 날짜로, 만든 시각/지운 시각 열 추가 (UTC)
이력 2/5 클릭 상세 패널에서 버전 비교로 바로 이동
밑줄 친 값 클릭 같은 값을 기본키로 가진 다른 테이블의 행으로 이동
빨간 점선 칸 해석 경고 (마우스를 올리면 이유)

행 배경색: 빨강 = deleted, 주황 = updated, 회색 = aborted/unknown.

4-4. 행 상세 (행 클릭)

오른쪽에 패널이 열립니다. Esc 또는 [닫기] 로 닫습니다.

섹션 내용
위치 / 출처 현재 파일: 파일 경로 · 파일 내 오프셋(헥스 에디터로 바로 찾아갈 수 있는 값) · 블록/라인 포인터 · 페이지 LSN
빈 공간: 페이지 내 오프셋 · 신뢰도 · 판정 메모
WAL: 처음 나타난 LSN · 사용한 기록(전체 페이지 이미지/INSERT/UPDATE) · 결말(언제 DELETE/UPDATE 됐는지) · 헤더가 원본인지 합성인지
트랜잭션 · 튜플 헤더 xmin(만든 트랜잭션)·xmax(지운/바꾼 트랜잭션)와 각각의 커밋 시각, ctid, infomask 플래그
수정 이력 버전 목록과 바뀐 컬럼만 비교 (노란 칸 = 이전 버전과 다른 값). 버전을 누르면 그 버전 상세로
컬럼 컬럼별 튜플 내 위치(오프셋+길이) · 저장 방식(short/plain/compressed/external) · 값. 정수 시각은 날짜 힌트
원시 바이트 튜플 헥스 덤프. 컬럼 행에 마우스를 올리면 해당 바이트가 노랗게

[인용 복사] 를 누르면 다음과 같은 텍스트가 클립보드에 들어갑니다.

[PostgreSQL Viewer 인용]
입력: C:\cases\case01\postgresql.zip (SHA-256 179b8ead…)
PGDATA: 17/main/ · PostgreSQL 17
데이터베이스: appdb (oid 16390) · 테이블: public.events (oid 16569)
행: h:0:22 · 출처 현재 파일 · 상태 live
위치: base/16390/17010 + <파일 내 오프셋> (0x…) · 블록 0 · lp 22
xmin 2888 (committed, 2026-10-23T14:42:35…) · xmax 0 (none) · ctid (0,22)
수정 이력: 2개 버전 중 2번째
값:
  id = …
  title = …
튜플 SHA-256: …

4-5. 컬럼 탭

컬럼 번호 · 이름 · 타입 · 길이 · 정렬 · NOT NULL · 비고(DROP 된 컬럼, 시각으로 보이는 컬럼).

4-6. WAL 기록 탭

선택한 테이블 파일에 대한 WAL 기록을 시간순으로 보여 줍니다.

열 뜻
LSN WAL 위치
트랜잭션 시각 그 레코드를 쓴 트랜잭션의 커밋(또는 롤백) 시각 (UTC)
작업 INSERT · UPDATE · UPDATE(HOT) · DELETE · MULTI_INSERT · 페이지 이미지 · PRUNE …
블록 / 오프셋 대상 위치. UPDATE 는 → (새 블록, 새 오프셋)

위쪽 선택 상자로 작업 종류를 거를 수 있습니다. 많으면 앞 3000건만 표시합니다.

4-7. 인덱스 흔적 탭

B-tree 인덱스의 리프 키 중 힙과 맞지 않는 것을 보여 줍니다.

상태 뜻
힙에 없음 가리키는 라인 포인터가 비었다 — 행이 지워지고 정리됐다
힙 슬롯 DEAD 행이 지워지고 정리 직전
값 불일치 그 슬롯을 다른 행이 재사용 중
해석 불가 키나 힙 값을 해석하지 못해 비교할 수 없다

복구된 행 링크를 누르면 같은 위치·같은 키 값으로 복구한 행(WAL/빈 공간)의 상세로 이동합니다. HOT 정리로 생긴 REDIRECT 는 대상 행을 따라가 비교하므로 흔적으로 보고하지 않습니다.

4-8. 클러스터 정보 탭

  • 입력: 경로 · 형식 · SHA-256 · [원본 해시 다시 확인] (분석 후 원본이 바뀌지 않았는지)
  • pg_control: 버전 · CRC · 상태 · 마지막 갱신/체크포인트 시각 · 다음 XID/OID · WAL 레벨 · 블록 크기 · 데이터 체크섬
  • 데이터베이스 목록
  • WAL: 레코드 수(CRC 오류 수) · LSN 범위 · 시각 범위 · 세그먼트 파일별 시작 LSN과 재활용 여부
  • 해석 검증: 카탈로그 자체 검증 결과 (✓/✗)

4-9. 내보내기

[CSV] / [JSON] 은 지금 걸린 필터(상태·출처·검색·수정 이력만)대로 내보냅니다. 앞쪽에 메타 열이 붙습니다: _rid _source _state _tid _xmin _xmin_time _xmax _xmax_time _chain _file _file_offset _wal_lsn.

  • CSV 는 = + - @ 로 시작하는 문자열 앞에 ' 를 붙입니다(스프레드시트 수식 실행 방지). 원본 값 그대로는 JSON 을 쓰세요.
  • JSON 은 WAL 출처 정보(_wal)와 해석 경고(_errors)도 담습니다.

4-10. 주소로 바로 열기

보고서에 링크로 남기거나 상위 도구에서 연결할 때 씁니다.

http://127.0.0.1:8000/?path=C:/cases/pg.zip&db=appdb&rel=public.events&rid=h:0:22&versions=1&time=1
파라미터 뜻
path 입력 경로 (필수)
db 데이터베이스 이름
rel 스키마.테이블
rid 행 ID (아래 5-3) — 상세를 연다
versions=1 상세를 수정 이력 위치로 스크롤
chained=1 수정 이력만 보기
time=1 시각 변환 켜기
tab paneWal · paneIdx · paneInfo · paneCols

5. 행의 상태와 출처 읽는 법

5-1. 상태 (state)

상태 뜻 판정 근거
live 현재 유효한 행 만든 트랜잭션 커밋 + 지운 트랜잭션 없음/롤백/잠금 전용
deleted 삭제된 행 지운 트랜잭션 커밋 + ctid 가 자기 자신
updated UPDATE 이전 버전 지운 트랜잭션 커밋 + ctid 가 새 버전을 가리킴
aborted 롤백된 INSERT 만든 트랜잭션 롤백 (정상 종료 후 커밋 기록이 없는 경우 포함)
in_progress 진행 중 커밋 기록이 없고 판단 불가 (비정상 종료 복사본 등)
unknown 판단 근거 부족 MultiXact, 서브트랜잭션, 파일에서 사라졌는데 삭제 기록이 없는 복구 행 등

커밋 여부는 튜플의 힌트 비트 → pg_xact(커밋 로그) → WAL 의 COMMIT/ABORT 기록 순으로 확인합니다.

5-2. 출처 (source)

출처 뜻 주의
현재 파일 (heap) 테이블 파일에서 라인 포인터가 가리키는 튜플 가장 신뢰도가 높다
WAL (wal) WAL 의 전체 페이지 이미지나 INSERT/UPDATE 레코드에만 남은 튜플 INSERT/UPDATE 레코드에서 온 행은 헤더 23바이트가 합성(xmin = 레코드 트랜잭션, ctid = 자기 위치). UPDATE 의 생략된 앞뒤 바이트를 WAL 안의 옛 튜플로 채운 행은 "재구성"으로 표시
빈 공간 (carved) 페이지에서 라인 포인터가 가리키지 않는 영역에서 찾은 튜플 원래 라인 포인터를 모른다. 삭제/수정 구분이 WAL 로 확인되지 않으면 "불확실" 메모. 문자열 등 가변 길이 컬럼이 없는 테이블은 신뢰도 "낮음"

같은 튜플이 여러 곳에 있으면 현재 파일 → WAL → 빈 공간 순으로 하나만 남깁니다.

5-3. 행 ID (rid)

형식 예 뜻
h:블록:lp h:0:22 현재 파일, 블록 0 · 라인 포인터 22
c:블록:오프셋 c:3:4096 빈 공간, 블록 3 · 페이지 내 오프셋 4096
w:블록:lp:xmin w:0:21:2884 WAL, 원래 위치 (0,21) · 만든 트랜잭션 2884

5-4. 수정 이력의 연결 표시

  • exact: 다음 버전의 위치와 xmin 이 정확히 맞는 연결
  • (추정): 빈 공간 행이라 위치(lp)를 모르지만 후보가 하나뿐이라 이은 연결. 후보가 여럿이면 잇지 않는다.

5-5. 시각

모든 시각은 UTC 입니다. 트랜잭션 시각은 WAL 의 커밋/롤백 기록에서 오므로, WAL 이 남아 있는 범위의 트랜잭션에만 붙습니다. "시각 변환"이 바꿔 보여 주는 epoch 정수 컬럼은 표시만 바꾸며 원래 값은 마우스를 올리면 보입니다.


6. 라이브러리로 쓰기 (상위 도구 연동)

엔진(pgviewer/engine/postgres)은 웹 없이 단독으로 import 할 수 있고 표준 라이브러리만 씁니다.

from pgviewer.engine.postgres import open_cluster

c = open_cluster(r"C:\cases\case01\postgresql.zip")   # 폴더도 된다
print(c.info()["control"]["state_name"])               # 'shut down'

for d in c.databases():                                # pg_database
    print(d["oid"], d["name"], d["has_dir"])

db = c.database(16390)                                 # 데이터베이스 하나
events = db.relation_by_name("public", "events")
cols = [a.name for a in events.attrs]

for row in db.rows(events.oid):                        # 모든 출처 · 모든 상태
    rec = dict(zip(cols, row["values"]))
    print(row["rid"], row["source"], row["state"], row["xmin_time"], rec["title"])
    if row.get("chain"):                               # {id, pos, len, prev, next, link}
        print("  버전", row["chain"]["pos"], "/", row["chain"]["len"])

db.rows(events.oid, {"heap"})                          # 현재 파일의 행만
db.row_detail(events.oid, "h:0:22")                    # 헤더 · 컬럼 위치 · 헥스 · 커밋 시각 · 버전 목록
db.index_traces(events.oid)                            # 인덱스 흔적
db.wal_events(events.oid)                              # 이 테이블의 WAL 기록
c.wal_summary()                                        # WAL 범위 · 레코드 수 · CRC 오류
c.close()

row 의 주요 필드:

필드 뜻
rid · source · state · tid 행 ID · 출처 · 상태 · (블록, lp)
xmin · xmax · xmin_status · xmax_status 트랜잭션 번호와 커밋 상태
xmin_time · xmax_time 커밋 시각 (WAL, UTC ISO 문자열)
values · errors 컬럼 값 목록(attrs 순서) · 컬럼별 해석 경고
file · offset · length 세그먼트 파일 · 파일 내 오프셋 · 튜플 길이 (현재 파일 · 빈 공간)
chain 수정 이력 정보
wal WAL 출처 정보 {first_lsn, lsn, sources, fate, synthetic_header, reconstructed}
carve 빈 공간 정보 {page_offset, confidence, note}

라이브러리는 Python 값을 그대로 돌려줍니다(큰 정수도 int). 웹 API 는 JavaScript 정밀도 때문에 2^53 이상 정수와 NaN 을 문자열로 보냅니다.


7. 검증 도구와 테스트

프로젝트 폴더에서 실행합니다.

py -X utf8 -m unittest discover -s tests -t .            # 단위 테스트 + 샘플 검증 (35개)
py -X utf8 tools/validate.py [입력]                       # 모든 DB · 모든 테이블 디코딩 전수 점검
py -X utf8 tools/inspect_rel.py appdb public.events 10            # 테이블 행을 터미널에 출력
py -X utf8 tools/smoke.py [입력]                          # 빠른 동작 확인

서버 없이 정답과 비교할 수 없어서, 서로 독립적인 출처가 일치하는지로 검증합니다. PG 17 샘플 데이터로 확인한 것:

검증 결과
pg_control CRC-32C 일치
카탈로그 자기 검증 (pg_attribute 22개 컬럼 정렬) 일치
모든 테이블 relnatts = 컬럼 수 일치
디코딩 (5개 DB, 12만여 행, 복구 행 포함) 오류 0, TOAST 소실 0
WAL 레코드 CRC-32C (64,910개) 오류 0
WAL 최대 xid + 1 = pg_control nextXid 일치
인덱스 흔적의 키 = 같은 위치에서 WAL 로 복구한 행의 키 50건 이상 일치
수정 이력의 모든 연결이 xmax = 다음 xmin 규칙 만족 일치

구조 해석의 근거(오프셋·상수·PostgreSQL 소스 위치)는 각 엔진 모듈의 주석에 [검증] / [문서] / [추정] 으로 구분해 적어 두었습니다.


8. 한계와 주의점

  • WAL 은 PostgreSQL 17 · 18 형식만 해석합니다. 그 외 메이저의 WAL 은 읽지 않고 클러스터 정보 탭에 이유를 표시합니다.
  • 압축 방식: pglz · lz4 지원, zstd 로 압축된 전체 페이지 이미지는 미지원.
  • 2단계 커밋(PREPARE/COMMIT PREPARED)의 시각은 붙이지 않습니다.
  • MultiXact 상태는 해석하지 않습니다 (해당 행은 unknown).
  • 빈 공간 복구는 일부만 남은(잘린) 튜플 조각은 찾지 않습니다.
  • 다른 테이블스페이스(pg_tblspc/)는 폴더 입력에서 링크가 살아 있을 때만 읽습니다.
  • 성능: 테이블과 WAL 을 메모리에 올려 처리합니다. 수 GB 급 테이블은 느리거나 메모리가 많이 들 수 있습니다.
  • ID 링크는 단일 컬럼 기본키 값(8자 이상 문자열)의 일치로 잇습니다. 우연히 같은 값이면 무관한 행이 이어질 수 있습니다.
  • 판정은 근거와 함께 읽으세요. 상세 패널의 출처 · 신뢰도 · 판정 메모 · 합성 헤더 표시를 보고서에 함께 남기는 것을 권장합니다.

9. 문제 해결

증상 확인할 것
"PGDATA 를 찾지 못했습니다" 입력 안에 PG_VERSION 과 global/pg_control 이 같이 있는지. zip 안에 PGDATA 가 여러 개면 하나만 담아서 다시
"네트워크(UNC) 경로는 열지 않습니다" 로컬 디스크로 복사한 뒤 열기
"로컬 전용 도구입니다 (Host 불일치)" 주소를 127.0.0.1 또는 localhost 로 열기
처음 테이블을 열 때 느림 처음 한 번 WAL 전체를 읽는다 (샘플 32MB 기준 약 2초). 이후는 캐시
빨간 점선 칸 "TOAST 청크가 남아 있지 않음" 긴 값의 조각이 파일과 WAL 모두에서 정리됨 — 복구 불가
WAL 탭에 "읽지 못했습니다" 클러스터 정보 탭의 WAL 카드에서 이유 확인 (버전, 파일 없음 등)
한글이 \xNN 으로 보임 DB 인코딩이 지원 목록(UTF8 · EUC_KR · EUC_JP · EUC_CN · LATIN1) 밖이거나 손상된 바이트
서버 종료 실행한 터미널에서 Ctrl+C

10. 프로젝트 구조

PostgreSQL Viewer/
├── README.md · LICENSE
├── requirements.txt          # 웹 계층 의존성 (엔진은 표준 라이브러리만)
├── pgviewer/
│   ├── source.py             # 입력: 폴더 / zip (읽기 전용)
│   ├── engine/postgres/      # 저장소 엔진
│   │   ├── control.py        #   global/pg_control
│   │   ├── relmap.py         #   pg_filenode.map
│   │   ├── clog.py           #   pg_xact (커밋 상태)
│   │   ├── heap.py           #   페이지 · 튜플 헤더 · 상태 판정
│   │   ├── varlena.py        #   varlena · TOAST · pglz · lz4
│   │   ├── types.py          #   타입 디코더
│   │   ├── wal.py            #   WAL 레코드 · 커밋 시각 · WAL 튜플
│   │   ├── carve.py          #   빈 공간 카빙
│   │   ├── btree.py          #   B-tree 리프 (인덱스 흔적)
│   │   ├── recovery.py       #   행 조립 · 수정 이력 · 인덱스 흔적 · 키 링크
│   │   ├── crc32c.py
│   │   └── cluster.py        #   카탈로그 부트스트랩 · 공개 API
│   └── api/main.py           # FastAPI (엔진 호출만)
├── web/index.html            # 화면 (단일 HTML, 모든 증거 값은 textContent 로만 표시)
├── tests/test_sample.py      # 단위 테스트 + 샘플 불변식
├── tools/                    # validate · inspect_rel · smoke
└── work/                     # 업로드 사본 (git 제외)

동작 원리 요약

  1. 입력 — 폴더 또는 zip 에서 PGDATA 를 찾고 SHA-256 을 기록한다.
  2. 컨트롤 파일 — global/pg_control 에서 버전 · 블록 크기 · 상태 · 다음 XID 를 읽고 CRC 로 확인한다.
  3. 카탈로그 부트스트랩 — pg_attribute 앞 5개 컬럼만 고정 정의로 읽고, pg_attribute 가 자기 자신을 설명하는 행에서 나머지 구조를 알아낸 뒤 정렬 값을 자체 검증한다. 이후 모든 테이블은 카탈로그에 적힌 정의로 읽는다.
  4. 힙 페이지 — 8KB 페이지 → 라인 포인터 → 튜플 헤더(xmin/xmax/infomask) → 컬럼 분해 → 타입 변환.
  5. 상태 판정 — 힌트 비트 · pg_xact · WAL 커밋 기록으로 live/deleted/updated/aborted 를 정한다.
  6. 복구 — WAL(페이지 주소 순, CRC 검증) · 빈 공간 카빙 · 인덱스 흔적을 더하고, 같은 튜플은 하나로 합친다.
  7. 관계 — 수정 이력 체인, 기본키 링크, 커밋 시각을 붙인다.

라이선스

MIT License. 자유롭게 사용·수정·재배포할 수 있으며, 저작권 표시와 라이선스 전문을 함께 유지하면 됩니다. 무보증(as-is)으로 제공됩니다.

About

Forensic PostgreSQL viewer that reads PGDATA directly - no server, read-only. Recovers deleted, updated, and aborted rows; parses WAL for commit timestamps and rows that live only in the log; carves free space; and surfaces B-tree index traces of long-gone rows.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages