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>
This commit is contained in:
213
examples/go_sql_direct.prg
Normal file
213
examples/go_sql_direct.prg
Normal file
@@ -0,0 +1,213 @@
|
||||
// Five Example: Direct Go SQL — the simplest possible way
|
||||
//
|
||||
// #pragma BEGINDUMP registers Go functions via HB_FUNC.
|
||||
// PRG calls them like regular Harbour functions.
|
||||
// Go objects flow as Harbour values — : for methods.
|
||||
//
|
||||
// Pattern: IMPORT declares Go packages
|
||||
// HB_FUNC bridges Go → Harbour
|
||||
// PRG code stays clean xBase style
|
||||
|
||||
IMPORT "database/sql"
|
||||
IMPORT _ "modernc.org/sqlite"
|
||||
|
||||
PROCEDURE Main()
|
||||
LOCAL db, aRows, aSum, i
|
||||
|
||||
? "=== Five SQL Demo ==="
|
||||
?
|
||||
|
||||
db := SqlOpen("sqlite", ":memory:")
|
||||
IF db == NIL
|
||||
? "Failed to open database"
|
||||
RETURN
|
||||
ENDIF
|
||||
|
||||
SqlExec(db, "CREATE TABLE customers (" + ;
|
||||
" id INTEGER PRIMARY KEY AUTOINCREMENT," + ;
|
||||
" name TEXT NOT NULL," + ;
|
||||
" city TEXT," + ;
|
||||
" balance REAL DEFAULT 0)")
|
||||
|
||||
SqlExec(db, "INSERT INTO customers (name, city, balance) VALUES ('Charles Kwon', 'Seoul', 15000.50)")
|
||||
SqlExec(db, "INSERT INTO customers (name, city, balance) VALUES ('John Smith', 'New York', 8200.00)")
|
||||
SqlExec(db, "INSERT INTO customers (name, city, balance) VALUES ('Maria Garcia', 'Madrid', 12300.75)")
|
||||
SqlExec(db, "INSERT INTO customers (name, city, balance) VALUES ('Yuki Tanaka', 'Tokyo', 9800.25)")
|
||||
SqlExec(db, "INSERT INTO customers (name, city, balance) VALUES ('Hans Mueller', 'Berlin', 6500.00)")
|
||||
? "5 records inserted."
|
||||
?
|
||||
|
||||
aRows := SqlQuery(db, "SELECT * FROM customers ORDER BY balance DESC")
|
||||
? 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
|
||||
?
|
||||
|
||||
aSum := SqlQuery(db, "SELECT COUNT(*) as cnt, SUM(balance) as total, AVG(balance) as avg FROM customers")
|
||||
? "Count:", aSum[1]["cnt"], " Total:", aSum[1]["total"], " Avg:", aSum[1]["avg"]
|
||||
?
|
||||
|
||||
aRows := SqlQueryP(db, "SELECT name, city FROM customers WHERE balance > ?", 10000)
|
||||
? "Balance > 10000:"
|
||||
FOR i := 1 TO Len(aRows)
|
||||
? " ", aRows[i]["name"], "-", aRows[i]["city"]
|
||||
NEXT
|
||||
|
||||
SqlClose(db)
|
||||
? "Done."
|
||||
|
||||
RETURN
|
||||
|
||||
#pragma BEGINDUMP
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"five/hbrt"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func init() {
|
||||
hbrt.HB_FUNC("SQLOPEN", sqlOpen)
|
||||
hbrt.HB_FUNC("SQLCLOSE", sqlClose)
|
||||
hbrt.HB_FUNC("SQLEXEC", sqlExec)
|
||||
hbrt.HB_FUNC("SQLQUERY", sqlQuery)
|
||||
hbrt.HB_FUNC("SQLQUERYP", sqlQueryP)
|
||||
}
|
||||
|
||||
// SqlOpen(cDriver, cDSN) → oDb or NIL
|
||||
func sqlOpen(ctx *hbrt.HBContext) {
|
||||
driver := ctx.ParC(1)
|
||||
dsn := ctx.ParC(2)
|
||||
db, err := sql.Open(driver, dsn)
|
||||
if err != nil {
|
||||
ctx.RetNil()
|
||||
return
|
||||
}
|
||||
if err = db.Ping(); err != nil {
|
||||
ctx.RetNil()
|
||||
return
|
||||
}
|
||||
ctx.RetVal(hbrt.WrapGo(db))
|
||||
}
|
||||
|
||||
// SqlClose(oDb)
|
||||
func sqlClose(ctx *hbrt.HBContext) {
|
||||
if db := getDB(ctx, 1); db != nil {
|
||||
db.Close()
|
||||
}
|
||||
ctx.RetNil()
|
||||
}
|
||||
|
||||
// SqlExec(oDb, cSQL) → lSuccess
|
||||
func sqlExec(ctx *hbrt.HBContext) {
|
||||
db := getDB(ctx, 1)
|
||||
if db == nil {
|
||||
ctx.RetL(false)
|
||||
return
|
||||
}
|
||||
_, err := db.Exec(ctx.ParC(2))
|
||||
if err != nil {
|
||||
fmt.Printf("SQL Error: %v\n", err)
|
||||
ctx.RetL(false)
|
||||
return
|
||||
}
|
||||
ctx.RetL(true)
|
||||
}
|
||||
|
||||
// SqlQuery(oDb, cSQL) → aRows (array of hashes)
|
||||
func sqlQuery(ctx *hbrt.HBContext) {
|
||||
db := getDB(ctx, 1)
|
||||
if db == nil {
|
||||
ctx.RetArray(nil)
|
||||
return
|
||||
}
|
||||
rows, err := db.Query(ctx.ParC(2))
|
||||
if err != nil {
|
||||
fmt.Printf("SQL Error: %v\n", err)
|
||||
ctx.RetArray(nil)
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
ctx.RetArray(scanRows(ctx, rows))
|
||||
}
|
||||
|
||||
// SqlQueryP(oDb, cSQL, xParam1, ...) → aRows with parameters
|
||||
func sqlQueryP(ctx *hbrt.HBContext) {
|
||||
db := getDB(ctx, 1)
|
||||
if db == nil {
|
||||
ctx.RetArray(nil)
|
||||
return
|
||||
}
|
||||
var args []interface{}
|
||||
for i := 3; 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(ctx.ParC(2), args...)
|
||||
if err != nil {
|
||||
fmt.Printf("SQL Error: %v\n", err)
|
||||
ctx.RetArray(nil)
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
ctx.RetArray(scanRows(ctx, rows))
|
||||
}
|
||||
|
||||
// --- internal helpers ---
|
||||
|
||||
func getDB(ctx *hbrt.HBContext, n int) *sql.DB {
|
||||
obj := hbrt.UnwrapGo(ctx.Param(n))
|
||||
db, _ := obj.(*sql.DB)
|
||||
return db
|
||||
}
|
||||
|
||||
func scanRows(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 result
|
||||
}
|
||||
|
||||
#pragma ENDDUMP
|
||||
Reference in New Issue
Block a user