CharlesKWON 8472928102 feat(pgserver): Phase 4 — Extended Protocol (Parse/Bind/Execute)
pgx and most drivers default to PostgreSQL's Extended Protocol
(named prepared statements). Phase 2 only handled Simple Query,
so every pgx caller had to force `QueryExecModeSimpleProtocol` —
unworkable for a production deployment. This commit lands the
full Parse → Bind → Describe → Execute → Sync state machine,
enough that pgx (and any other libpq-protocol-v3 client) works
without any client-side knobs.

Implementation lives in `hbrtl/pgserver/extended.go`:

* Per-session caches `stmts map[string]*preparedStmt` and
  `portals map[string]*portal`, lazily allocated on first use.
  Stored as fields on `session` so they don't leak across
  connections.

* Parameters are inlined at Bind time via `substituteParams` —
  the resolved SQL is a normal Simple-Query-shaped string the
  engine sees through the existing `five_SQL(cSQL, …, oSession)`
  pipeline. Avoids teaching FiveSql2 a second param-shape; the
  trade-off is that binary timestamps/numerics round-trip through
  text (Phase 4.1 will plumb `?`-params through aParams for the
  binary fast path).

* `paramToLiteral` decodes the binary-format encodings pgx uses
  by default for INT4/INT8/BOOL (big-endian fixed-width). Other
  binary OIDs fall back to a hex-escaped quoted literal which
  errors loudly rather than silently misparsing.

* `countPgPlaceholders` scans the SQL outside string literals for
  the highest `$N` so the server can answer Describe-statement
  with a correctly-sized ParameterDescription even when the
  client didn't pre-declare param OIDs. Without this, pgx errored
  with "expected 0 arguments, got 2" on the very first prepared
  query.

* RowDescription emission: Describe-statement still returns NoData
  (we can't infer row shape without execution). When Execute fires
  on a portal the client never Described, we emit RowDescription
  inline from the cached result before DataRow streams. pgx and
  psql both tolerate this ordering.

* Execute → CommandComplete tag derives from the SQL verb via the
  existing `commandTagFor` helper. Row counts in the tag remain
  "VERB 0" for v1.0; threading real counters through the engine
  is Phase 5.

Wire dispatch in `session.go:queryLoop` now handles Parse, Bind,
Describe, Execute, Close, Sync, Flush — the full v3 message set.

Verification
------------

End-to-end pgx (default mode, no SimpleProtocol flag) successfully
runs:
  SELECT $1 AS n, $2 AS s with 42 + "hi" → [42 hi]
  Same statement re-executed with different bound values → reuses
    the cached prepared statement
  SELECT $1 AS b, $2 AS s with true + "binary-bool" → [t binary-bool]

`tests/pgserver/run.sh` expanded from 1 → 3 integration assertions:

  PASS  Simple Query: SELECT 1, 'hello'
  PASS  Multi-statement Simple Query
  PASS  Transaction control: BEGIN/COMMIT round-trip

(Extended Protocol can't be driven from psql's -c CLI directly
because psql's PREPARE/EXECUTE is a separate SQL-level feature
that FiveSql2 doesn't parse; the pgx-driven path verifies it
manually, and a self-contained Go integration that drives pgx
from inside a process bootstrap is Phase 7 work.)

All six release gates green:
  go test ./...                       ✓
  FiveSql2 SQL:1999 43/43             ✓
  Harbour compat 56/56                ✓
  std.ch 17/17                        ✓
  FRB 7/7                             ✓
  pgserver integration 3/3            ✓

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 12:55:41 +09:00

Five — Harbour + Go Fusion Language

Harbour PRG 코드를 네이티브 Go 바이너리로 컴파일하는 퓨전 언어.

30년간 축적된 Harbour/xBase 비즈니스 로직을 Go의 성능, 동시성, 크로스 플랫폼 위에서 실행합니다.

employees.prg  →  five build  →  employees (단일 실행파일, 18MB)

주요 특징

  • Harbour 문법 100% 지원 (CLASS, CODE BLOCK, BEGIN SEQUENCE, ...)
  • Go 네이티브 바이너리 출력 (CGo 없음, 순수 Go)
  • DBF/NTX/CDX 데이터베이스 엔진 내장
  • 479개 RTL 내장 함수
  • FiveSql2: DBF 위에서 SQL:1999 쿼리 (43/43 테스트 통과)
  • Goroutine/Channel 확장 (GO BLOCK, CHANNEL)
  • @byref 참조 전달, mutable closure
  • 대화형 디버거 (TUI/CLI)

빌드 방법

1. Go 설치

Five는 Go 1.21 이상이 필요합니다.

Linux/WSL:

# 이미 설치되어 있는지 확인
go version

# 없으면 설치
wget https://go.dev/dl/go1.22.5.linux-amd64.tar.gz
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.22.5.linux-amd64.tar.gz
echo 'export PATH=$PATH:/usr/local/go/bin:$HOME/go/bin' >> ~/.bashrc
source ~/.bashrc
go version

macOS (Apple Silicon — M1/M2/M3/M4):

brew install go
# 또는 직접 다운로드:
wget https://go.dev/dl/go1.22.5.darwin-arm64.tar.gz
sudo tar -C /usr/local -xzf go1.22.5.darwin-arm64.tar.gz
echo 'export PATH=$PATH:/usr/local/go/bin:$HOME/go/bin' >> ~/.zshrc
source ~/.zshrc

macOS (Intel):

brew install go
# 또는 직접 다운로드:
wget https://go.dev/dl/go1.22.5.darwin-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.22.5.darwin-amd64.tar.gz
echo 'export PATH=$PATH:/usr/local/go/bin:$HOME/go/bin' >> ~/.zshrc
source ~/.zshrc

Windows:

https://go.dev/dl/ 에서 .msi 설치

2. Five 컴파일러 빌드

git clone https://gitea.fivego.org/fivedev/five.git
cd five
go build -o five ./cmd/five

빌드 확인:

./five version

3. PRG 프로그램 컴파일 및 실행

단일 파일:

# 컴파일
./five build examples/hello.prg -o hello

# 실행
./hello

다중 파일 (FiveSql2 등):

./five build _FiveSql2/test/test_sql1999.prg _FiveSql2/src/*.prg -o test_sql
./test_sql

4. 테스트 실행

# Go 유닛 테스트
go test ./...

# FiveSql2 SQL 테스트 (43/43)
./five build _FiveSql2/test/test_sql1999.prg _FiveSql2/src/*.prg -o /tmp/test_sql
cd /tmp && ./test_sql

# Harbour 호환 테스트 (51/51)
./five build tests/compat_harbour.prg -o /tmp/test_compat
/tmp/test_compat

Five 명령어

five run <file.prg>              컴파일 후 즉시 실행
five build <file.prg> [-o out]   네이티브 바이너리 생성
five gen <file.prg>              생성된 Go 코드 출력 (디버깅용)
five debug <file.prg>            대화형 디버거 실행
five version                     버전 정보

Hello World

// hello.prg
PROCEDURE Main()
   ? "Hello, Five!"
   ? "Date:", Date()
   ? "Time:", Time()
RETURN
./five build hello.prg -o hello && ./hello

SQL 예제

// sql_demo.prg
#include "FiveSqlDef.ch"

PROCEDURE Main()
   // 테이블 생성
   five_SQL("CREATE TABLE employees (id INTEGER, name CHAR(30), salary NUMERIC(12,2))")

   // 데이터 삽입
   five_SQL("INSERT INTO employees (id, name, salary) VALUES (1, 'Alice', 8000)")
   five_SQL("INSERT INTO employees (id, name, salary) VALUES (2, 'Bob', 7000)")

   // SQL 쿼리
   LOCAL aR := five_SQL("SELECT name, salary FROM employees WHERE salary > 6000 ORDER BY salary DESC")

   ? "Results:", Len(aR[2]), "rows"
   LOCAL i
   FOR i := 1 TO Len(aR[2])
      ? "  ", aR[2][i][1], aR[2][i][2]
   NEXT
RETURN
./five build sql_demo.prg _FiveSql2/src/*.prg -o sql_demo && ./sql_demo

프로젝트 구조

five/
├── cmd/five/          Five CLI (main entry point)
├── compiler/          PRG → Go 컴파일러
│   ├── lexer/         토크나이저
│   ├── parser/        구문 분석기
│   ├── analyzer/      의미 분석기
│   ├── gengo/         Go 코드 생성기
│   └── pp/            전처리기 (#include, #define)
├── hbrt/              런타임 (VM, Stack, Value, Class)
├── hbrtl/             RTL 표준 라이브러리 (479개 함수)
├── hbrdd/             RDD 데이터베이스 엔진
│   ├── dbf/           DBF 파일 드라이버
│   ├── ntx/           NTX 인덱스 드라이버
│   ├── cdx/           CDX 인덱스 드라이버
│   └── mem/           메모리 RDD
├── _FiveSql2/         SQL:1999 엔진 (PRG)
│   ├── src/           SQL 엔진 소스 (14 파일, 10,458 LOC)
│   └── test/          SQL 테스트 스위트
├── tests/             호환성 테스트
├── examples/          예제 프로그램
└── docs/              기술 문서

현재 상태

항목 수치
Go 프로덕션 코드 ~36,000 LOC
RTL 내장 함수 479개
RDD 드라이버 4종 (DBF, NTX, CDX, Memory)
FiveSql2 테스트 43/43 (100%)
호환 테스트 51/51 (100%)
Go 테스트 ALL PASS

라이선스

Copyright (c) 2025-2026 Charles KWON OhJun (charleskwonohjun@gmail.com) All rights reserved.

Description
Five — Harbour+Go Fusion Language (PRG→Go native binary)
Readme 64 MiB
Languages
Go 57.9%
xBase 22%
C 19.5%
Shell 0.5%
Makefile 0.1%