Files
five/examples/go_sqlite.prg
Charles KWON OhJun 59568f3301 Five v0.9 — Harbour + Go fusion language
- Compiler: PP → Lexer → Parser → Analyzer → Gengo pipeline
- Parser: 232/236 (98%) Harbour compatibility, registry-based dispatch
- RTL: 351 Harbour-compatible functions
- RDD: DBF/NTX/CDX engines with Rushmore bitmap optimization
- Go Interop: IMPORT + pkg.Func() + obj:Method() with FastPath (15M calls/sec)
- HB_FUNC API: Full Harbour C API compatible Go bridge
- Concurrency: SPAWN/LAUNCH/GOROUTINE, <-, WATCH, PARALLEL FOR, ASYNC/AWAIT
- Extensions: Multi-return, DEFER, Slice, f-string, Nil-safe ?:, CONST
- Macro Compiler: Runtime AST parsing and evaluation
- Debugger: TUI debugger with source display, breakpoints, stepping
- FRB: Native + Pcode dual mode runtime binary
- Tests: 13 packages ALL PASS

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 09:41:50 +09:00

205 lines
4.6 KiB
Plaintext

// Five Example: SQLite Database with Go's database/sql
//
// Harbour's xBase syntax + Go's SQL ecosystem = modern database apps.
// Traditional Harbour: limited to DBF/NTX/CDX
// Five: any database Go supports (SQLite, PostgreSQL, MySQL, etc.)
PROCEDURE Main()
LOCAL aRows, aSummary, aSearch, i
? "=== Five + SQLite Demo ==="
?
GoDbOpen(":memory:")
GoDbExec("CREATE TABLE customers (" + ;
" id INTEGER PRIMARY KEY AUTOINCREMENT," + ;
" name TEXT NOT NULL," + ;
" city TEXT," + ;
" balance REAL DEFAULT 0" + ;
")")
? "Inserting records..."
GoDbExec("INSERT INTO customers (name, city, balance) VALUES ('Charles Kwon', 'Seoul', 15000.50)")
GoDbExec("INSERT INTO customers (name, city, balance) VALUES ('John Smith', 'New York', 8200.00)")
GoDbExec("INSERT INTO customers (name, city, balance) VALUES ('Maria Garcia', 'Madrid', 12300.75)")
GoDbExec("INSERT INTO customers (name, city, balance) VALUES ('Yuki Tanaka', 'Tokyo', 9800.25)")
GoDbExec("INSERT INTO customers (name, city, balance) VALUES ('Hans Mueller', 'Berlin', 6500.00)")
? "5 records inserted."
?
aRows := GoDbQuery("SELECT * FROM customers ORDER BY balance DESC")
? "All customers (sorted by balance):"
? PadR("ID", 4), PadR("Name", 20), PadR("City", 15), "Balance"
? Replicate("-", 55)
FOR i := 1 TO Len(aRows)
? PadR(aRows[i]["id"], 4), ;
PadR(aRows[i]["name"], 20), ;
PadR(aRows[i]["city"], 15), ;
aRows[i]["balance"]
NEXT
?
aSummary := GoDbQuery("SELECT COUNT(*) as cnt, SUM(balance) as total, AVG(balance) as avg FROM customers")
? "Summary:"
? " Count: ", aSummary[1]["cnt"]
? " Total: ", aSummary[1]["total"]
? " Average:", aSummary[1]["avg"]
?
aSearch := GoDbQueryP("SELECT name, city FROM customers WHERE balance > ?", 10000)
? "Customers with balance > 10000:"
FOR i := 1 TO Len(aSearch)
? " ", aSearch[i]["name"], "-", aSearch[i]["city"]
NEXT
GoDbClose()
?
? "Done."
RETURN
#pragma BEGINDUMP
import (
"database/sql"
"five/hbrt"
"fmt"
_ "modernc.org/sqlite"
)
var db *sql.DB
func init() {
hbrt.HB_FUNC("GODBOPEN", goDbOpen)
hbrt.HB_FUNC("GODBCLOSE", goDbClose)
hbrt.HB_FUNC("GODBEXEC", goDbExec)
hbrt.HB_FUNC("GODBQUERY", goDbQuery)
hbrt.HB_FUNC("GODBQUERYP", goDbQueryP)
}
func goDbOpen(ctx *hbrt.HBContext) {
dsn := ctx.ParC(1)
if dsn == "" {
dsn = ":memory:"
}
var err error
db, err = sql.Open("sqlite", dsn)
if err != nil {
ctx.RetL(false)
return
}
ctx.RetL(true)
}
func goDbClose(ctx *hbrt.HBContext) {
if db != nil {
db.Close()
db = nil
}
ctx.RetNil()
}
func goDbExec(ctx *hbrt.HBContext) {
sqlStr := ctx.ParC(1)
if db == nil || sqlStr == "" {
ctx.RetL(false)
return
}
_, err := db.Exec(sqlStr)
if err != nil {
fmt.Printf("SQL Error: %v\n", err)
ctx.RetL(false)
return
}
ctx.RetL(true)
}
func goDbQuery(ctx *hbrt.HBContext) {
sqlStr := ctx.ParC(1)
if db == nil || sqlStr == "" {
ctx.RetArray(nil)
return
}
rows, err := db.Query(sqlStr)
if err != nil {
fmt.Printf("SQL Error: %v\n", err)
ctx.RetArray(nil)
return
}
defer rows.Close()
ctx.RetVal(rowsToHarbour(ctx, rows))
}
func goDbQueryP(ctx *hbrt.HBContext) {
sqlStr := ctx.ParC(1)
if db == nil || sqlStr == "" {
ctx.RetArray(nil)
return
}
var args []interface{}
for i := 2; i <= ctx.PCount(); i++ {
v := ctx.Param(i)
switch {
case v.IsString():
args = append(args, v.AsString())
case v.IsNumeric():
args = append(args, v.AsNumDouble())
case v.IsLogical():
args = append(args, v.AsBool())
default:
args = append(args, nil)
}
}
rows, err := db.Query(sqlStr, args...)
if err != nil {
fmt.Printf("SQL Error: %v\n", err)
ctx.RetArray(nil)
return
}
defer rows.Close()
ctx.RetVal(rowsToHarbour(ctx, rows))
}
func rowsToHarbour(ctx *hbrt.HBContext, rows *sql.Rows) hbrt.Value {
cols, _ := rows.Columns()
var result []hbrt.Value
for rows.Next() {
values := make([]interface{}, len(cols))
ptrs := make([]interface{}, len(cols))
for i := range values {
ptrs[i] = &values[i]
}
rows.Scan(ptrs...)
hash := ctx.HashNew()
for i, col := range cols {
key := hbrt.MakeString(col)
var val hbrt.Value
switch v := values[i].(type) {
case int64:
val = hbrt.MakeInt(int(v))
case float64:
val = hbrt.MakeDouble(v, 0, 0)
case string:
val = hbrt.MakeString(v)
case []byte:
val = hbrt.MakeString(string(v))
case bool:
val = hbrt.MakeBool(v)
default:
val = hbrt.MakeNil()
}
ctx.HashAdd(hash, key, val)
}
result = append(result, hash)
}
return hbrt.MakeArrayFrom(result)
}
#pragma ENDDUMP