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:
204
examples/go_sqlite.prg
Normal file
204
examples/go_sqlite.prg
Normal file
@@ -0,0 +1,204 @@
|
||||
// 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
|
||||
Reference in New Issue
Block a user