Documentation
¶
Overview ¶
Package yottadb is a Go wrapper for a YottaDB database - a mature, high performance, transactional NoSQL engine with proven speed and stability.
The package requires minimum versions of Go 1.24 and YottaDB r1.34. It uses CGo to interface between this Go wrapper and the YottaDB engine written in C. Its use of the `Node` type to pin memory references to database subscript strings gives it optimal speed. To aid migration of YDBGo v1 to v2, it is possible to use both in one application.
Example ¶
package main
import "fmt"
import "lang.yottadb.com/go/yottadb/v2"
func main() {
defer yottadb.Shutdown(yottadb.MustInit())
conn := yottadb.NewConn()
// Store unicode greeting into node ^hello("world")
greeting := conn.Node("^hello", "world")
greeting.Set("สวัสดี") // Sawadee (hello in Thai)
fmt.Println(greeting.Get())
// Output:
// สวัสดี
}
Prerequisites ¶
Install YottaDB and consider reading the introduction to YottaDB's data model.
Index ¶
- Constants
- Variables
- func ErrorCode(err any) int
- func ErrorIs(err any, code int) bool
- func NotifyYDB(sig os.Signal) bool
- func Shutdown(handle *DB) error
- func ShutdownHard(handle *DB) error
- func ShutdownOnPanic()
- func SignalExitCallback(sigNum os.Signal)
- func SignalNotify(notifyChan chan os.Signal, signals ...os.Signal)
- func SignalReset(signals ...os.Signal)
- func SignalWasFatal() bool
- type CallTable
- type Conn
- func (conn *Conn) CloneConn() *Conn
- func (conn *Conn) CloneNode(n *Node) *Node
- func (conn *Conn) Import(table string) (*MFunctions, error)
- func (conn *Conn) KillAllLocals()
- func (conn *Conn) KillLocalsExcept(exclusions ...string)
- func (conn *Conn) Lock(timeout time.Duration, nodes ...*Node) bool
- func (conn *Conn) MustImport(table string) *MFunctions
- func (conn *Conn) Node(varname string, subscripts ...any) (n *Node)
- func (conn *Conn) Quote(value string) string
- func (conn *Conn) Restart()
- func (conn *Conn) Rollback()
- func (conn *Conn) Str2Zwr(str string) (string, error)
- func (conn *Conn) TimeoutAction(action int)
- func (conn *Conn) Transaction(transID string, localsToRestore []string, callback func()) bool
- func (conn *Conn) TransactionFast(localsToRestore []string, callback func()) bool
- func (conn *Conn) TransactionToken() (tptoken uint64)
- func (conn *Conn) TransactionTokenSet(tptoken uint64)
- func (conn *Conn) Zwr2Str(zstr string) (string, error)
- type DB
- type Error
- type MFunctions
- func (m *MFunctions) Call(rname string, args ...any) any
- func (m *MFunctions) CallErr(rname string, args ...any) (any, error)
- func (m *MFunctions) Wrap(rname string) func(args ...any) any
- func (m *MFunctions) WrapErr(rname string) func(args ...any) (any, error)
- func (m *MFunctions) WrapRetFloat(rname string) func(args ...any) float64
- func (m *MFunctions) WrapRetInt(rname string) func(args ...any) int
- func (m *MFunctions) WrapRetString(rname string) func(args ...any) string
- type Node
- func (n *Node) Child(subscripts ...any) (child *Node)
- func (n *Node) Children() iter.Seq2[*Node, string]
- func (n *Node) ChildrenBackward() iter.Seq2[*Node, string]
- func (n *Node) Clear()
- func (n *Node) Clone() (clone *Node)
- func (n *Node) Dump(args ...int) string
- func (n *Node) Get(defaultValue ...string) string
- func (n *Node) GetBool(defaultValue ...bool) bool
- func (n *Node) GetBytes(defaultValue ...[]byte) []byte
- func (n *Node) GetFloat(defaultValue ...float64) float64
- func (n *Node) GetInt(defaultValue ...int) int
- func (n *Node) GoString() string
- func (n *Node) HasBoth() bool
- func (n *Node) HasNone() bool
- func (n *Node) HasTree() bool
- func (n *Node) HasTreeOnly() bool
- func (n *Node) HasValue() bool
- func (n *Node) HasValueOnly() bool
- func (n *Node) Incr(amount any) string
- func (n *Node) IncrFloat(amount any) float64
- func (n *Node) IncrInt(amount any) int
- func (n *Node) Index(subscripts ...any) *Node
- func (n *Node) IsMutable() bool
- func (n *Node) Kill()
- func (n *Node) Lock(timeout ...time.Duration) bool
- func (n *Node) Lookup() (string, bool)
- func (n *Node) LookupBytes() ([]byte, bool)
- func (n *Node) Next() *Node
- func (n *Node) Prev() *Node
- func (n *Node) Set(val any)
- func (n *Node) String() string
- func (n *Node) Subscript(index int) string
- func (n *Node) Subscripts() []string
- func (n *Node) Tree() iter.Seq[*Node]
- func (n *Node) TreeNext() *Node
- func (n *Node) TreePrev() *Node
- func (n *Node) Unlock()
- type RoutineData
Examples ¶
- Conn.Import
- Conn.Node
- Conn.Restart
- Conn.Str2Zwr
- Conn.TransactionToken
- Conn.TransactionTokenSet
- Conn.Zwr2Str
- ErrorIs
- NewConn
- Node.Children
- Node.Dump
- Node.GetFloat
- Node.GoString
- Node.Index
- Node.Index (Incorrect)
- Node.Kill
- Node.Next
- Node.Next (Varnames)
- Node.String
- Node.Subscripts
- Node.Tree
- Node.TreeNext
- SignalNotify
Constants ¶
const ( TransactionCommit = YDB_OK TransactionRollback = YDB_TP_ROLLBACK TransactionTimeout = ydberr.TPTIMEOUT )
Constants used for TimeoutAction. These just happen to all map to YDB error codes that do something but the new names have a more consistent naming scheme and are more Goish.
const ( // YottaDB Enum constants YDB_DEL_NODE = C.YDB_DEL_NODE YDB_DEL_TREE = C.YDB_DEL_TREE YDB_SEVERITY_ERROR = C.YDB_SEVERITY_ERROR /* Error - Something is definitely incorrect */ YDB_SEVERITY_FATAL = C.YDB_SEVERITY_FATAL /* Fatal - Something happened that is so bad, YottaDB cannot continue */ YDB_SEVERITY_INFORMATIONAL = C.YDB_SEVERITY_INFORMATIONAL /* Informational - won't see these returned as they continue running */ YDB_SEVERITY_SUCCESS = C.YDB_SEVERITY_SUCCESS /* Success */ YDB_SEVERITY_WARNING = C.YDB_SEVERITY_WARNING /* Warning - Something is potentially incorrect */ YDB_DATA_ERROR = C.YDB_DATA_ERROR YDB_DATA_NOVALUE_DESC = C.YDB_DATA_NOVALUE_DESC /* Node has no value but has descendants */ YDB_DATA_UNDEF = C.YDB_DATA_UNDEF /* Node is undefined */ YDB_DATA_VALUE_DESC = C.YDB_DATA_VALUE_DESC /* Node has both value and descendants */ YDB_DATA_VALUE_NODESC = C.YDB_DATA_VALUE_NODESC /* Node has a value but no descendants */ YDB_MAIN_LANG_C = C.YDB_MAIN_LANG_C /* Main routine written in C (or M) or is otherwise C-compatible */ YDB_MAIN_LANG_GO = C.YDB_MAIN_LANG_GO /* Main routine is written in Go so handle signals differently */ // General-purpose constants YDB_DEFER_HANDLER = C.YDB_DEFER_HANDLER /* 0x7ffffffa - defer this signal handler (used in Go wrapper) */ YDB_INT_MAX = C.YDB_INT_MAX YDB_LOCK_TIMEOUT = C.YDB_LOCK_TIMEOUT YDB_MAX_ERRORMSG = C.YDB_MAX_ERRORMSG YDB_MAX_IDENT = C.YDB_MAX_IDENT /* Maximum size of global/local name (not including '^') */ YDB_MAX_M_LINE_LEN = C.YDB_MAX_M_LINE_LEN YDB_MAX_NAMES = C.YDB_MAX_NAMES /* Maximum number of variable names can be specified in a single ydb_*_s() call */ YDB_MAX_PARMS = C.YDB_MAX_PARMS /* Maximum parameters to an M call (call-in) */ YDB_MAX_STR = C.YDB_MAX_STR /* Maximum YottaDB string length */ YDB_MAX_SUBS = C.YDB_MAX_SUBS /* Maximum subscripts currently supported */ YDB_MAX_TIME_NSEC = C.YDB_MAX_TIME_NSEC /* Max specified time in (long long) nanoseconds */ YDB_MAX_YDBERR = C.YDB_MAX_YDBERR /* Maximum (absolute) value for a YottaDB error */ YDB_MIN_YDBERR = C.YDB_MIN_YDBERR /* Minimum (absolute) value for a YottaDB error */ YDB_NODE_END = C.YDB_NODE_END YDB_NOTOK = C.YDB_NOTOK YDB_NOTTP = C.YDB_NOTTP YDB_OK = C.YDB_OK /* Successful return code */ YDB_RELEASE = C.YDB_RELEASE /* Corresponds to YottaDB release r1.24 (i.e. YDB_ZYRELEASE in sr_linux/release_name.h) */ YDB_TP_RESTART = C.YDB_TP_RESTART YDB_TP_ROLLBACK = C.YDB_TP_ROLLBACK )
Define constants exported by YottaDB
Note: YDB_ERR_* error constants are defined in sub-package pkg/lang.yottadb.com/go/yottadb/v2/ydberr.
const WrapperRelease string = "v2.0.6"
WrapperRelease - (string) The Go wrapper release version for YottaDB SimpleAPI. Note the third piece of this version will be even for a production release and odd for a development release. When released, depending on new content, either the third piece of the version will be bumped to an even value or the second piece of the version will be bumped by 1 and the third piece of the version set to 0. On rare occasions, we may bump the first piece of the version and zero the others when the changes are significant. Also, the version numbers may be followed by a hyphen and text, e.g. "v2.0.2-alpha" Note: version descriptions may be seen on the git tags with `git tag -n`
Variables ¶
var ( // MaxPanicExitWait is the maximum wait when a panic caused by a signal has occurred (likely unable to run Exit(). // It specifies the wait in seconds that yottadb.Exit() will wait for ydb_exit() to run before // giving up and forcing the process to exit. Note the normal exit wait is longer as we expect ydb_exit() to be // successful so can afford to wait as long as needed to do the sync but for a signal exit, the rundown is likely // already done (exit handler called by the signal processing itself) but if ydb_exit() is not able to get // the system lock and is likely to hang, 3 seconds is about as much as we can afford to wait. MaxPanicExitWait time.Duration = 3 * time.Second // MaxNormalExitWait is maximum wait for a normal shutdown when no system lock hang in Exit() is likely. MaxNormalExitWait time.Duration = 60 * time.Second // MaxSigShutdownWait is maximum wait to close down signal handling goroutines (shouldn't take this long). MaxSigShutdownWait time.Duration = 5 * time.Second // MaxSigAckWait is maximum wait for notify via acknowledgement channel that a notified signal handler is // done handling the signal. MaxSigAckWait time.Duration = 10 * time.Second )
Set default exit wait times. The user may change these.
var DebugMode atomic.Int64 // increasing values 1, 2 or 3 for increasing log output
DebugMode greater than zero (1, 2, or 3) increases logging output
- DebugMode=0: no debug logging (default)
- DebugMode=1: log at entrypoint of M functions or Go signal callbacks; and don't remove temporary callback table file
- DebugMode=2: in addition, log extra signal processing info
var MaxStacktrace = 4096
MaxStacktrace is the maximum number of stack frames stored in an Error instance
var MinYDBRelease string = "r1.34"
MinYDBRelease - (string) Minimum YottaDB release name required by this wrapper. This is checked on init. It is a var rather than a const so we can change it purely to verify logic that parses it
var YDBSignals = []os.Signal{ syscall.SIGABRT, syscall.SIGALRM, syscall.SIGBUS, syscall.SIGCONT, syscall.SIGFPE, syscall.SIGHUP, syscall.SIGILL, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGSEGV, syscall.SIGTERM, syscall.SIGTRAP, syscall.SIGTSTP, syscall.SIGTTIN, syscall.SIGTTOU, syscall.SIGURG, syscall.SIGUSR1, }
YDBSignals lists all the signals that YottaDB must be notified of.
Functions ¶
func ErrorCode ¶ added in v2.0.4
ErrorCode returns the error code of err if it is an instance of yottadb.Error; otherwise returns ydberr.NotYDBError. Unlike err.Code this works even if err is not an error instance.
func ErrorIs ¶
ErrorIs uses errors.Is() to search an error or chain of wrapped errors for a yottadb.Error with a matching ydberr code. Only the error code is matched, not the message, to support YottaDB error messages that vary. For example, to test for YottaDB INVSTRLEN error:
if yottadb.ErrorIs(err, ydberr.INVSTRLEN) {
is a short equivalent of:
if errors.Is(err, &Error{Code: ydberr.INVSTRLEN}) {
It differs from a simple type test using yottadb.Error.Code in that it searches for a match in the entire chain of wrapped errors
Example ¶
Example checking whether error is a particular YottaDB error:
err := &yottadb.Error{Code: ydberr.INVSTRLEN, Message: "string too long"}
fmt.Println("Error is INVSTRLEN:", yottadb.ErrorIs(err, ydberr.INVSTRLEN))
fmt.Println(" or using longform:", errors.Is(err, &yottadb.Error{Code: ydberr.INVSTRLEN}))
wrapper := fmt.Errorf("wrapped: %w", err)
fmt.Println("Wrapped error is still INVSTRLEN:", yottadb.ErrorIs(wrapper, ydberr.INVSTRLEN))
fmt.Println()
fmt.Println("Or you can grab the error with Error.As():")
var e *yottadb.Error
fmt.Println("Error is type yottadb.Error:", errors.As(err, &e))
if errors.As(err, &e) {
fmt.Println(" and the error is:", e)
}
err2 := fmt.Errorf("string too long")
fmt.Println("Error is type yottadb.Error:", errors.As(err2, &e))
if errors.As(err2, &e) {
fmt.Println("Error is type yottadb.Error:", e)
}
Output: Error is INVSTRLEN: true or using longform: true Wrapped error is still INVSTRLEN: true Or you can grab the error with Error.As(): Error is type yottadb.Error: true and the error is: string too long Error is type yottadb.Error: false
func NotifyYDB ¶
NotifyYDB calls the YottaDB signal handler for sig. If YottaDB deferred handling of the signal, return false; otherwise return true. Panic on YottaDB errors.
func Shutdown ¶
Shutdown invokes YottaDB's rundown function ydb_exit() to shut down the database properly. It MUST be called prior to process termination by any application that calls Init(). It is recommended to defer Shutdown() immediately after calling Init() in the main routine. You should also defer ShutdownOnPanic() from new goroutines to ensure shutdown occurs if they panic.
This is necessary, particularly in Go, because Go does not call the C atexit() handler (unless building with certain test options), so YottaDB itself cannot automatically ensure correct rundown of the database.
If Shutdown() is not called prior to process termination, steps must be taken to ensure database integrity, as documented in Database Integrity and unreleased locks may cause small subsequent delays (see relevant LKE documentation).
Deferring Shutdown() has the side benefit of exiting silently on ydberr.CALLINAFTERXIT panics if they come from a Ctrl-C (SIGINT) panic that has already occurred in another goroutine.
Notes:
- It is the main routine's responsibility to ensure that any goroutines have finished using the database before it calls yottadb.Shutdown(). Otherwise they will receive ydberr.CALLINAFTERXIT errors from YDBGo.
- Avoid Go's os.Exit() function because it bypasses any defers (it is a low-level OS call).
- Shutdown() must be called exactly once for each time Init() was called, and shutdown will not occur until the last time.
Returns ydberr.ShutdownIncomplete if it has to wait longer than MaxNormalExitWait for signal handling goroutines to exit. No other errors are returned. Panics if Shutdown is called more than Init.
func ShutdownHard ¶ added in v2.0.4
ShutdownHard shuts down immediately even if it has not yet been called as many times as Init. It is used before a fatal exit like panic or fatals signals. ShutdownHard may be called any number of times without ill effect (e.g. by different goroutines during an application shutdown).
func ShutdownOnPanic ¶ added in v2.0.4
func ShutdownOnPanic()
ShutdownOnPanic should be deferred at the start of every goroutine to ensure that the database is shut down on panic. Otherwise a panic that occurs within that goroutine will call os.Exit without properly shutting down the database, and MUPIP RUNDOWN will need to be run.
Deferring this function has the side benefit of exiting its goroutine silently on ydberr.CALLINAFTERXIT panics if they come from a fatal signal panic that has already occurred in another goroutine.
- YDBGo's signal handlers already ensure shutdown is called if they panic (i.e. they defer ShutdownOnPanic).
See: Shutdown, SignalWasFatal
func SignalExitCallback ¶ added in v2.0.4
SignalExitCallback is an exported version of signalExitCallback() for use only by FatalSignal test
func SignalNotify ¶
SignalNotify relays incoming signals to notifyChan specifically for signals used by YottaDB. If SignalNotify is used on a specific signal, the user is then responsible to call NotifyYDB() at the start or end of their own handler to allow YottaDB to process the signal. The user can revert behaviour to the YDBGo default with SignalReset(), after which YDBGo will once again call NotifyYDB() itself.
- Users may opt to use the standard library's [Signal.Notify]() instead of this function to be notified of signals, but this will notify them in parallel with YottaDB. However, they must not call Signal.Stop() (see below).
- Do not call [Signal.Stop](), [Signal.Ignore]() or [Signal.Reset]() for any of the YottaDB-specific signals unless you understand that it will prevent NotifyYDB() from being called, and will affect YottaDB timers or other functionality.
- Using SignalNotify to capture SIGSEGV is unreliable. Instead, see standard library function debug.SetPanicOnFault(true)
Goroutines used to handle signals should defer ShutdownOnPanic(). YDBGo's own signal handlers already do so.
YottaDB-specific signals are listed in the source in YDBSignals.
See YottaDB signals.
Example ¶
Example that hooks SIGINT and SIGKILL but only passes SIGKILL on to YottaDB:
ch := make(chan os.Signal, 1) // Create signal notify and signal ack channels
yottadb.SignalNotify(ch, syscall.SIGCONT, syscall.SIGINT)
// A flag just for this test to check when a signal has been handled
signals := atomic.Int64{}
go func(ch chan os.Signal) {
defer yottadb.ShutdownOnPanic() // essential to ensure proper shutdown if there is a panic
for {
sig := <-ch
fmt.Printf("\nSignal %d (%s) received.\n", sig, sig)
if sig == syscall.SIGINT {
fmt.Printf("Not passing signal to YottaDB to avoid exit.\n")
}
if sig == syscall.SIGCONT {
fmt.Printf("Passing signal to YottaDB now.\n")
yottadb.NotifyYDB(sig)
fmt.Printf("Returned from YottaDB signal handler.\n")
}
signals.Add(1)
}
}(ch)
// send SIGCONT to myself
syscall.Kill(os.Getpid(), syscall.SIGCONT)
// ensure the first signal gets processed first so the message order is correct in the output below
for signals.Load() < 1 {
}
// send SIGINT to myself to simulate Ctrl-C
syscall.Kill(os.Getpid(), syscall.SIGINT)
// allow time for the previous message to flush before output is checked
for signals.Load() < 2 {
}
Output: Signal 18 (continued) received. Passing signal to YottaDB now. Returned from YottaDB signal handler. Signal 2 (interrupt) received. Not passing signal to YottaDB to avoid exit.
func SignalReset ¶
SignalReset stops notifying the user of the given signals and reverts to default YDBGo signal behaviour which simply calls NotifyYDB. No error is raised if the signal did not already have a notification request in effect.
func SignalWasFatal ¶ added in v2.0.4
func SignalWasFatal() bool
SignalWasFatal returns whether the currently unwinding panic was caused by a fatal signal like Ctrl-C. May be used in a deferred function like ShutdownOnPanic to check whether a fatal signal caused the current exit procedure.
Types ¶
type CallTable ¶
type CallTable struct {
Filename string
YDBTable string // Table after pre-processing the M-call table into YDB format
// List of M routine metadata structs: one for each routine imported.
Routines map[string]*RoutineData
// contains filtered or unexported fields
}
CallTable stores internal metadata used for calling M and a table of Go functions by name loaded from the M call-in table.
type Conn ¶
type Conn struct {
// contains filtered or unexported fields
}
Conn represents a goroutine-specific 'connection' object for calling the YottaDB API. You must use a different connection for each goroutine.
func NewConn ¶
func NewConn() *Conn
NewConn creates a new database connection. Each goroutine must have its own connection. To prevent deadlocks, this function panics if another Conn has already been created in this goroutine.
Example ¶
Test the example given in the package doc at the top of doc.go
// Note: this example is also used in the README.md
defer yottadb.Shutdown(yottadb.MustInit())
conn := yottadb.NewConn()
// Store unicode greeting into node ^hello("world")
greeting := conn.Node("^hello", "world")
greeting.Set("สวัสดี") // Sawadee (hello in Thai)
fmt.Println(greeting.Get())
Output: สวัสดี
func (*Conn) CloneConn ¶ added in v2.0.4
CloneConn returns a new connection that initially begins with the same transaction token as the original connection conn. This may be used if you absolutely must have activity within one transaction spread across multiple goroutines, in which case each new goroutine will need a new connection that has the same transaction token as the original connection. However, be aware that spreading transaction activity across multiple goroutines is highly discouraged. If it is done, CloneConn should be called in the new goroutine that uses the conn, and it is also the user's responsibility to ensure that the spawned goroutines complete before the parent terminates the transaction. Before doing this the programmer should first read and understand Threads and Transaction Processing.
func (*Conn) CloneNode ¶
CloneNode creates a copy of node associated with conn (in case node was created using a different conn). A node associated with a conn used by another goroutine must not be used by the current goroutine except as a parameter to CloneNode(). If this rule is not obeyed, then the two goroutines could overwrite each others transaction level and error message values. It is the programmer's responsibility to ensure this does not happen by using CloneNode. This does the same as n.Clone() except that it can switch to new conn. Mutable nodes passed to CloneNode will cause a panic. They must be converted to immutable nodes with Node.Clone() Only immutable nodes are returned.
func (*Conn) Import ¶
func (conn *Conn) Import(table string) (*MFunctions, error)
Import loads a call-in table for use by this connection only. The M routines listed in the call-in 'table' (specified below) are each wrapped in a Go function which may be subsequently called using the returned MFunctions.Call(name) or referenced as a Go function using MFunctions.Wrap(name).
If 'table' string contains ":" it is considered to be the call-in table specification itself; otherwise it is treated as the filename of a call-in file to be opened and read.
M-call table format specification ¶
An M-call table specifies M routines which may be called by Go. It may be a string or a file (typically a file with extension .mcalls) and is case-sensitive. The format of an M-call table is a sequence of text lines where each line contains an M routine prototype specifications as follows:
Go_name: [ret_type] M_entrypoint(type, type, ...)
Elements of that line are defined as follows:
- Go_name may be any go string.
- M_entrypoint is any valid M entry reference.
- ret_type may be omitted if an M return value is not supplied. Otherwise it must be *string, *int, *int64, *float64 or omitted (for void return)
- any spaces adjacent to commas, asterisk and square brackets (,*[]) are ignored.
Zero or more parameter type specifications are allowed and must be a Go type specifier: string, int, uint, int32, uint32, int64, uint64, float32, float64, or a pointer version of the same in Go type format (e.g. *int).
- If a pointer type is selected then the parameter is passed by reference so that the M routine can modify the parameter.
- Any *string types and string return values must be followed by a preallocation value in square brackets (e.g. *string[100]).
This allows Go to preallocate enough space for the returned string. If necessary, YottaDB will truncate returned strings so they fit.
Comments begin with // and continue to the end of the line. Blank lines or pure-comment lines are ignored.
Example ¶
Example that imports a call-in table which specifies Go functions that wrap M functions.
// The following table may be supplied from a string or a file
table := `
AddVerbose: string[1024] addVerbose^arithmetic(*string[10], *int, int, string)
Add: int add^arithmetic(int, int)
Sub: int sub^arithmetic(int, int)
AddFloat: float64 add^arithmetic(float64, float64)
Noop: noop^arithmetic()
`
// Import the call table
conn := yottadb.NewConn()
m := conn.MustImport(table)
// Call an imported function directly. Notice that the 'any' return type requires a type assertion
fmt.Printf("Double (5 + 11) is: %d\n", 2*m.Call("Add", 5, 11).(int))
fmt.Printf("Half (5 - 11) is: %d\n", m.Call("Sub", 5, 11).(int)/2)
fmt.Printf("5.5 + 1.2 is: %0.1f\n\n", m.Call("AddFloat", 5.5, 1.2))
// Wrap these same M routines as Go functions for easier and faster calling
add, sub, addFloat := m.WrapRetInt("Add"), m.WrapRetInt("Sub"), m.WrapRetFloat("AddFloat")
fmt.Printf("Double (5 + 11) is: %d\n", 2*add(5, 11))
fmt.Printf("Half (5 - 11) is: %d\n", sub(5, 11)/2)
fmt.Printf("5.5 + 1.2 is: %0.1f\n\n", addFloat(5.5, 1.2))
// Demonstrate calling an M routine that passes variables by reference
s := "test"
n := 3
result := m.Call("AddVerbose", &s, &n, 4, "100").(string)
fmt.Println("Result =", result)
fmt.Println("s =", s)
fmt.Println("n+1 =", n+1)
fmt.Println()
// Call a function that returns nothing in its Go signature
m.Call("Noop")
// Import a different table that invokes the same M routine sub(), but this time
// with a different Go function signature just for fun. YDBGo handles the type conversions.
m2 := conn.MustImport("Sub: string[10] sub^arithmetic(int64, uint64)")
// Test Sub() again but now with a different Go type signature
result = m2.Call("Sub", int64(5), uint64(11)).(string)
fmt.Printf("5 - 11 with negative sign removed is: %s\n", result[1:])
Output: Double (5 + 11) is: 32 Half (5 - 11) is: -3 5.5 + 1.2 is: 6.7 Double (5 + 11) is: 32 Half (5 - 11) is: -3 5.5 + 1.2 is: 6.7 Result = :test:107 s = :test: n+1 = 108 5 - 11 with negative sign removed is: 6
func (*Conn) KillAllLocals ¶
func (conn *Conn) KillAllLocals()
KillAllLocals kills all M 'locals'. It is for clearer source code as it simply calls KillLocalsExcept() without listing any exceptions.
- To kill a specific variable use Node.Kill()
func (*Conn) KillLocalsExcept ¶
KillLocalsExcept kills all M 'locals' except for the ones listed by name in exclusions.
- To kill a specific variable use Node.Kill()
func (*Conn) Lock ¶
Lock releases all existing locks and attempt to acquire locks matching all supplied nodes, waiting up to timeout for availability.
- Equivalent to the M `LOCK` command. See Node.Lock() and Node.Unlock() methods for single-lock usage.
- A timeout of zero means try only once.
- Return true if lock was acquired; otherwise false.
- Panics with error TIME2LONG if the timeout exceeds YDB_MAX_TIME_NSEC or on other panic-worthy errors (e.g. invalid variable names).
func (*Conn) MustImport ¶ added in v2.0.4
func (conn *Conn) MustImport(table string) *MFunctions
MustImport is the same as Conn.Import but panics on errors.
func (*Conn) Node ¶
Node method creates a `Node` type instance that represents a YottaDB database node or M local node, and offers methods that access that YottaDB node.
- The strings and array are stored in C-allocated space to give Node methods fast access to YottaDB API functions.
- The varname and subscripts may be of type string, []byte slice, or an integer or float type; numeric types are converted to a string using the appropriate strconv function.
Example ¶
conn := yottadb.NewConn()
n := conn.Node("var", "sub1", "sub2")
n2 := n.Child("sub3", "sub4")
fmt.Printf("%v\n", n)
fmt.Printf("%v\n", n2)
Output: var("sub1","sub2") var("sub1","sub2","sub3","sub4")
func (*Conn) Quote ¶ added in v2.0.4
Quote adds quotes around strings but not around numbers (just as YottaDB would display them).
- The input value is treated as a string if it cannot be converted, unchanged, to and from float64 using strconv.ParseFloat(value, 64) and strconv.FormatFloat(number, 'f', -1, 64)
- If the string contains unprintable ASCII characters it is converted to YottaDB ZWRITE format using Conn.Str2Zwr.
- This is exported so that the user can validate against the same conversion that is used by YDBGo.
func (*Conn) Restart ¶ added in v2.0.4
func (conn *Conn) Restart()
Restart a transaction immediately (after first rolling back).
Example ¶
package main
import (
"fmt"
yottadbV2 "lang.yottadb.com/go/yottadb/v2"
)
func main() {
conn := yottadbV2.NewConn()
n := conn.Node("^activity")
n.Set(0)
// M locals to demonstrate restoration of M 'local' on restart or not
incr1 := conn.Node("incr1")
incr2 := conn.Node("incr2")
incr1.Set(0)
incr2.Set(0)
restarts := 0 // Go local
run := func() {
n.Incr(1)
incr1.Incr(1)
incr2.Incr(1)
if restarts < 2 {
restarts++
conn.Restart()
}
if restarts > 9 {
// Error condition
conn.Rollback()
}
}
normal := conn.TransactionFast([]string{"incr1"}, run)
fmt.Printf("Database updated %d times; transaction restarted %d times then normal exit was: %v\n", n.GetInt(), restarts, normal)
fmt.Printf("M local 'incr1' was restored so became %d; 'incr2' became %d\n", incr1.GetInt(), incr2.GetInt())
// Run transaction again but give it an error condition so that it rolls back
restarts = 10
rollback := !conn.TransactionFast(nil, run)
fmt.Printf("Transaction rollback %v; restores database value n of 2 to %d\n", rollback, n.GetInt())
}
Output: Database updated 1 times; transaction restarted 2 times then normal exit was: true M local 'incr1' was restored so became 1; 'incr2' became 3 Transaction rollback true; restores database value n of 2 to 1
func (*Conn) Rollback ¶ added in v2.0.4
func (conn *Conn) Rollback()
Rollback and exit a transaction immediately.
func (*Conn) Str2Zwr ¶
Str2Zwr takes the given Go string and converts it to return a ZWRITE-formatted string
- If the input string does not fit within the maximum YottaDB string size, return a *Error with Code=ydberr.InvalidStringLength
- If the output string does not fit within the maximum YottaDB string size, return a *Error with Code=ydberr.INVSTRLEN
- Otherwise, return the ZWRITE-formatted string.
- Note that the length of a string in zwrite format is always greater than or equal to the string in its original, unencoded format.
Panics on other errors because they are are all panic-worthy (e.g. invalid variable names).
Example ¶
Example of converting a Go string to a ZWRITE-formatted string:
conn := yottadb.NewConn()
str, err := conn.Str2Zwr("X\x00ABC")
if err != nil {
panic(err)
}
fmt.Printf("%v", str)
Output: "X"_$C(0)_"ABC"
func (*Conn) TimeoutAction ¶ added in v2.0.4
TimeoutAction sets the action that is performed when [conn.Transaction] times out. The following actions are valid and performed the named function:
- TransactionTimeout (default): rollback and panic with Error.Code = ydberr.TPTIMEOUT
- TransactionRollback: rollback the transaction and return false from the transaction function
- TransactionCommit: commit database activity that was done before the timeout
Notes:
- Timeout only occurs if YottaDB special variable $ZMAXTPTIME is set.
- Although TimeoutAction is specific to the specified conn, $ZMAXTPTIME is global.
- If TimeoutAction has not been called on this Conn, the default action is TransactionTimeout.
func (*Conn) Transaction ¶
Transaction processes database logic inside a database transaction.
- `callback` must be a function that implements the required database logic.
- `transId` has its first 8 bytes recorded in the commit record of journal files for database regions participating in the transaction. Note that a transId of case-insensitive "BATCH" or "BA" are special: see Conn.TransactionFast()
- `localsToRestore` are names of local M variables to be restored to their original values when a transaction is restarted. If localsToRestore[0] equals "*" then all local M locals are restored on restart. Note that since Go has its own local variables it is unlikely that you will need this feature in Go.
- Returns true to indicate that the transaction logic was successful and has been committed to the database, or false if a rollback was necessary.
- Panics on errors because they are are all panic-worthy (e.g. invalid variable names). See yottadb.Error for rationale.
The callback function should:
- Implement the required database logic taking into account key considerations for Transaction Processing code.
- If there are database collisions, `callback` will be called repeatedly, rolling back the database before each call. On the fourth try, YottaDB will resort to calling it with other processes locked out to ensure its success.
- Call Conn.Restart if it needs to rollback and immediately restart the transaction function
- Call Conn.Rollback if it needs to rollback and immediately exit the transaction function
- Finish quickly because database activity in other goroutines will be blocked until it is complete.
- Not create goroutines within the transaction unless absolutely necessary, in which case see Conn.CloneConn.
Transaction nesting level may be determined within the callback function by reading the special variable $tlevel, and the number of restart repetitions by $trestart. These things are documented in more detail in Transaction Processing.
func (*Conn) TransactionFast ¶
TransactionFast is a faster version of Transaction that does not ensure durability, for applications that do not require durability or have alternate durability mechanisms (such as checkpoints). It is implemented by setting the transID to the special name "BATCH" as discussed in Transaction Processing.
- Panics on errors because they are are all panic-worthy (e.g. invalid variable names). See yottadb.Error for rationale.
func (*Conn) TransactionToken ¶ added in v2.0.4
TransactionToken sets the transaction-level token being using by the given connection conn. This is for use only in the unusual situation of mixing YDBGo v1 and v2 code and you have a v2 transaction that needs to call a v1 function (which must therefore be passed the v2 Conn's tptoken). It would be tidier, however, to avoid mixing versions within a transaction, therefore this function is deprecated from its inception and will be removed in a future version once there has been plenty of time to migrate all code to v2. See Conn.TransactionTokenSet
Example ¶
package main
import (
"fmt"
yottadbV1 "lang.yottadb.com/go/yottadb"
yottadbV2 "lang.yottadb.com/go/yottadb/v2"
)
func main() {
defer yottadbV2.Shutdown(yottadbV2.MustInit())
yottadbV1.ForceInit() // Tell v1 that v2 has done the initialization
conn := yottadbV2.NewConn()
conn.TransactionFast(nil, func() {
person := conn.Node("^person")
person.Set("Sally")
// Run a YDBGo v1 function
tptoken := conn.TransactionToken() // without this the v1 function will hang
val, err := yottadbV1.ValE(tptoken, nil, "^person", nil)
if err != nil {
panic(err)
}
fmt.Print(val)
})
}
Output: Sally
func (*Conn) TransactionTokenSet ¶ added in v2.0.4
TransactionTokenSet sets the transaction-level token being using by the given connection conn. This is for use only in the unusual situation of mixing YDBGo v1 and v2 code and you have a v1 transaction that needs to call a v2 function (which must therefore be run on a Conn with the v1 tptoken). It would be tidier, however, to avoid mixing versions within a transaction, therefore this function is deprecated from its inception and will be removed in a future version once there has been plenty of time to migrate all code to v2. See Conn.TransactionToken
Example ¶
package main
import (
"fmt"
yottadbV1 "lang.yottadb.com/go/yottadb"
yottadbV2 "lang.yottadb.com/go/yottadb/v2"
)
func main() {
defer yottadbV2.Shutdown(yottadbV2.MustInit())
yottadbV1.ForceInit() // Tell v1 that v2 has done the initialization
conn := yottadbV2.NewConn()
err := yottadbV1.TpE(yottadbV1.NOTTP, nil, func(tptoken uint64, errstr *yottadbV1.BufferT) int32 {
err := yottadbV1.SetValE(tptoken, nil, "Fred", "^person", nil)
if err != nil {
panic(err)
}
// Run a YDBGo v2 function Node.Dump()
conn.TransactionTokenSet(tptoken) // without this the v2 function will hang
person := conn.Node("^person")
fmt.Print(person.Dump())
return yottadbV2.YDB_OK
}, "BATCH", nil)
if err != nil {
panic(err)
}
// Restore transaction token in conn to its initial value
// without this, subsequent use of conn will hang
conn.TransactionTokenSet(yottadbV1.NOTTP)
}
Output: ^person="Fred"
func (*Conn) Zwr2Str ¶
Zwr2Str takes the given ZWRITE-formatted string and converts it to return as a normal ASCII string.
- If the input string does not fit within the maximum YottaDB string size, return a *Error with Code=ydberr.InvalidStringLength
- If zstr is not in valid zwrite format, return the empty string and a *Error with Code=ydberr.InvalidZwriteFormat.
- Otherwise, return the decoded string.
- Note that the length of a string in zwrite format is always greater than or equal to the string in its original, unencoded format.
Panics on other errors because they are are all panic-worthy (e.g. invalid variable names).
Example ¶
Example of converting a ZWRITE-formatted string to a Go string:
conn := yottadb.NewConn()
str, err := conn.Zwr2Str(`"X"_$C(0)_"ABC"`)
if err != nil {
panic(err)
}
fmt.Printf("%#v", str)
Output: "X\x00ABC"
type DB ¶
type DB struct {
YDBRelease float64 // release version of the installed YottaDB
// contains filtered or unexported fields
}
DB is the type returned by Init() which must be passed to Shutdown(). It is used as a clue to the user that they must not forget to Shutdown()
func Init ¶
Init initializes the YottaDB engine and sets up signal handling. Init may be called multiple times (e.g. by different goroutines) but Shutdown() must be called exactly once for each time Init() was called. See Shutdown for more detail on the fallout from incorrect usage. Although Init could have been made to happen automatically, this more explicit approach clarifies that Shutdown() MUST be called before process exit.
- Be sure to read the cautions at Shutdown.
- Init returns a value of type DB that must be passed to the Shutdown function.
- Returns yottadb.Error with Code=ydberr.Init on failure, which will also wrap any errors from YottaDB in the error chain.
Users should defer Shutdown from their main routine before using other database functions; for example:
db, err := yottadb.Init()
if err != nil {
panic(err)
}
defer yottadb.Shutdown(db)
... user code to use the database ...
type Error ¶
type Error struct {
Message string // The error string - generally from $ZSTATUS when available
Code int // The error value (e.g. ydberr.INVSTRLEN, etc)
// contains filtered or unexported fields
}
Error type holds YDBGo and YottaDB errors including a numeric error code. YDBGo error strategy is as follows. Database setup functions like Init and Conn.Import (and its returned functions) return errors. However, Node functions panic on errors because Node errors are caused by either programmer blunders (like invalid variable name) or system-level events (like out of memory). This approach greatly simplifies the use of Node methods.
If a particular panic needs to be captured this can be done with recover as YDBGo ensures that all its errors and panics are of type yottadb.Error to facilitate capture of the specific cause using the embedded code:
- All YottaDB errors are formated in $ZSTATUS format message and the YottaDB numeric error code with negative value, defined in ydberr/errorscodes.go.
- All YDBGo errors likewise have a message string, but have a positive error code, defined in ydberr.ydberr.go.
The yottadb.Error type implements the Error.Is method to check the entire error chain, matching only against the error code. However, the API function yottadb.ErrorIs wraps this more conveniently to check any error type (even non-YottaDB errors) for a match against a given YottaDB error code: yottadb.ErrorIs(err, ydberr.<ErrorCode>).
func (*Error) Error ¶
Error is a type method of yottadb.Error to return the error message string.
func (*Error) Is ¶
Is lets errors.Is() search an error or chain of wrapped errors for a yottadb.Error with a matching ydberr code. See ErrorIs() for a more practical way to use this capability. Only the error code is matched, not the message: this supports matching errors even when YottaDB messages vary.
func (*Error) Unwrap ¶
Unwrap allows yottadb.Error to wrap other underlying errors. See errors.Unwrap.
type MFunctions ¶
type MFunctions struct {
// Almost-private metadata for testing or specialised access to the imported call table. Public for specialised use.
Table *CallTable
Conn *Conn
}
MFunctions is returned by [Import] to represent an M-call table with some methods that allow the user to call its M routines.
func (*MFunctions) Call ¶
func (m *MFunctions) Call(rname string, args ...any) any
Call calls an M routine rname with parameters args and returns string, int64 or float64. Since the programmer knows the return-type in advance from the M-call table, the return type may be safely forced to that type with an unchecked type assertion. For example:
x = m.Call("add", 1, 2).(int64)
This version panics on errors. See MFunctions.CallErr() for a version that returns errors.
func (*MFunctions) CallErr ¶
func (m *MFunctions) CallErr(rname string, args ...any) (any, error)
CallErr calls an M routine rname with parameters args and returns string, int64 or float64, and any error. This version returns any errors. See MFunctions.Call() for a version that panics.
func (*MFunctions) Wrap ¶
func (m *MFunctions) Wrap(rname string) func(args ...any) any
Wrap returns a function that calls an M routine rname that returns any. This can speed up calling M routines by avoiding the name lookup each invocation. This version creates functions that panic: compare MFunctions.WrapErr that returns errors instead.
func (*MFunctions) WrapErr ¶
func (m *MFunctions) WrapErr(rname string) func(args ...any) (any, error)
WrapErr returns a function that calls an M routine rname that returns any. This can speed up calling M routines by avoiding the name lookup each invocation. This version creates functions that return errors: compare MFunctions.Wrap that panics instead.
func (*MFunctions) WrapRetFloat ¶ added in v2.0.4
func (m *MFunctions) WrapRetFloat(rname string) func(args ...any) float64
WrapRetFloat produces a Go convenience function that wraps an M routine that returns float64. This can avoid messy type assertion and speed up calling M routines by avoiding the name lookup each invocation. This version creates functions that panic: compare MFunctions.WrapErr that returns errors instead.
func (*MFunctions) WrapRetInt ¶ added in v2.0.4
func (m *MFunctions) WrapRetInt(rname string) func(args ...any) int
WrapRetInt produces a Go convenience function that wraps an M routine that returns int. This can avoid messy type assertion and speed up calling M routines by avoiding the name lookup each invocation. Rather than int64 it returns int as the default type in Go so that regular arithmetic can be done on the result without casting. This version creates functions that panic: compare MFunctions.WrapErr that returns errors instead.
func (*MFunctions) WrapRetString ¶ added in v2.0.4
func (m *MFunctions) WrapRetString(rname string) func(args ...any) string
WrapRetString produces a Go convenience function that wraps an M routine that returns string. This can avoid messy type assertion and speed up calling M routines by avoiding the name lookup each invocation. This version creates functions that panic: compare MFunctions.WrapErr that returns errors instead.
type Node ¶
type Node struct {
Conn *Conn // Node.Conn points to the Go conn; Node.cnode.conn will point directly to the C.conn
// contains filtered or unexported fields
}
Node is an object containing strings that represents a YottaDB node.
- Stores all the supplied strings (varname and subscripts) in the Node object along with array of C.ydb_buffer_t structs that point to each successive string, to provide fast access to YottaDB API functions.
- Regular Nodes are immutable. There is a mutable version of Node emitted by Node.Next() and Node iterators, which will change each loop. If you need to take an immutable snapshot of a mutable node this may be done with Node.Clone().
- Concurrency: Do not run database actions on node objects created in another goroutine. If you want to act on a node object passed in from another goroutine, first call Node.Clone(conn) to make a copy of the other goroutine's node object using the current goroutine's connection `conn`. Then perform methods on that.
Node methods panic on errors because they are are all panic-worthy (e.g. invalid variable names). See yottadb.Error for error strategy and rationale.
func (*Node) Child ¶
Child creates a child node of parent that represents parent with subscripts appended.
- Node.Clone() without parameters is equivalent to Node.Child() without parameters.
func (*Node) Children ¶
Children returns an interator over immediate child nodes for use in a FOR-loop. This iterator is a wrapper for Node.Next(). It yields two values:
- a mutable node instance with final subscripts changed to successive subscript names
- the name of the child subscript (optionally assigned with a range statement)
Notes:
- Treats 'null subscripts' (i.e. empty strings) in the same way as M function $ORDER().
- The order of returned nodes matches the collation order of the M database.
- This function never adjusts the supplied node even if it is mutable (it always creates its own mutable copy).
- If you need to take an immutable snapshot of the returned mutable node, use Node.Clone().
See:
- Node.ChildrenBackward().
- Node.Tree() for traversal of nodes in a way that descends into the entire tree.
Example ¶
Example of getting all child nodes
conn := yottadb.NewConn()
n := conn.Node("X", 1)
n.Child(2, 3).Set(123)
n.Child(2, 4).Set(124)
n.Child(2, 3, "person").Set(1237)
// Note that the following person fields will come out in alphabetical order below
n.Child(2, 3, "person", "address").Set("2 Rocklands Rd")
n.Child(2, 3, "person", "address", "postcode").Set(1234)
n.Child(2, 3, "person", "occupation").Set("engineer")
n.Child(2, 3, "person", "age").Set(42)
n.Child(2, 3, "person", "sex").Set("male")
n = conn.Node("X", 1, 2)
for x := range n.Children() {
fmt.Printf("%s=%s\n", x, x.Get())
}
fmt.Println("Do the same in reverse:")
for x := range n.ChildrenBackward() {
fmt.Printf("%s=%s\n", x, x.Get())
}
n = conn.Node("X", 1, 2, 3, "person")
fmt.Printf("Person fields: (")
for _, sub := range n.Children() {
fmt.Printf("%s ", sub)
}
fmt.Println(")")
Output: X(1,2,3)=123 X(1,2,4)=124 Do the same in reverse: X(1,2,4)=124 X(1,2,3)=123 Person fields: (address age occupation sex )
func (*Node) ChildrenBackward ¶
ChildrenBackward is the same as Children but operates in reverse order. See Node.Children().
func (*Node) Clear ¶
func (n *Node) Clear()
Clear deletes the node value, not its child subscripts.
- Equivalent to YottaDB M command ZKILL
func (*Node) Clone ¶
Clone creates an immutable copy of node.
- Node.Clone() is equivalent to calling Node.Child() without parameters.
See Node.IsMutable() for notes on mutability.
func (*Node) Dump ¶ added in v2.0.4
Dump returns a string representation of this database node and subtrees with their contents in YottaDB ZWRITE format. For example output, see Node.GoString. Output lines are formatted as follows:
- Output subscripts and values as unquoted numbers if they convert to float64 and back without change (using [Node.Quote]).
- Output strings in YottaDB ZWRITE format.
- Every line output ends with "\n", including the final line.
- If the receiver is nil, output is "<nil>\n"
- If node has no value and no children, outputs the empty string.
See Node.GoString for an example of the output format. Two optional integers may be supplied to specify maximums where both default to -1:
- first parameter specifies the maximum number of lines to output (not including a file "...\n" line indicating truncation). A maximum of -1 means infinite. If lines are truncated, an additional line "...\n" is added so that the output ends with "\n...\n". A maximum of 0 lines is treated as 1.
- second parameter specifies the maximum number of characters at which to truncate values prior to output where -1 means infinite. Truncated values are output with suffix "..." after any ending quotes. Note that conversion to ZWRITE format may expand this.
Example ¶
Example of traversing a database tree
conn := yottadb.NewConn()
n := conn.Node("tree", 1)
n.Child(6).Set(16)
n.Child(2, 3).Set(123)
n.Child(2, 3, 7).Set("Hello!")
n.Child(2, 4).Set(124)
n.Child(2, 5, 9).Set(1259)
nb := conn.Node("tree", "B")
nb.Child(1).Set("AB")
fmt.Println(n.Dump())
n.Child(2, 3).Set("~ A\x00\x7f" + strings.Repeat("A", 1000))
fmt.Print(n.Dump(2, 8))
Output: tree(1,2,3)=123 tree(1,2,3,7)="Hello!" tree(1,2,4)=124 tree(1,2,5,9)=1259 tree(1,6)=16 tree(1,2,3)="~ A"_$C(0,127)_"AAA"... tree(1,2,3,7)="Hello!" ...
func (*Node) Get ¶
Get fetches and returns the value of a database node or defaultValue[0] if the database node is empty.
- defaultValue defaults to {""}
Return defaultValue[0] if the node's value does not exist.
func (*Node) GetBool ¶ added in v2.0.4
GetBool fetches and returns the value of a database node as a bool.
- defaultValue defaults to {false}
Return true if the node's value is a non-zero integer; false if it is integer zero; otherwise return defaultValue[0] (nonexistant, or is not convertable to an integer)
func (*Node) GetBytes ¶ added in v2.0.4
GetBytes is the same as Node.Get except that it accepts and returns []byte slices rather than strings.
func (*Node) GetFloat ¶ added in v2.0.4
GetFloat fetches and returns the value of a database node as a float64.
- defaultValue defaults to {0}
Return defaultValue[0] if the node's value does not exist or is not convertable to float64.
Example ¶
Calculate the height of 3 oak trees, based on their shadow length and the angle of the sun.
// Note: this example is used in the README.md
defer yottadb.Shutdown(yottadb.MustInit())
conn := yottadb.NewConn()
// capture initial data values into a Go map
data := []map[string]int{
{"shadow": 10, "angle": 30},
{"shadow": 13, "angle": 30},
{"shadow": 15, "angle": 45},
}
// Enter data into the database
trees := conn.Node("^oaks") // node object pointing to YottaDB global ^oaks
for i, items := range data {
for key, value := range items {
trees.Child(i+1, key).Set(value)
}
}
// Iterate data in the database and calculate results
for tree, i := range trees.Children() {
tree.Child("height").Set(tree.Child("shadow").GetFloat() *
math.Tan(tree.Child("angle").GetFloat()*math.Pi/180))
fmt.Printf("Oak %s is %.1fm high\n", i, tree.Child("height").GetFloat())
}
Output: Oak 1 is 5.8m high Oak 2 is 7.5m high Oak 3 is 15.0m high
func (*Node) GetInt ¶ added in v2.0.4
GetInt fetches and returns the value of a database node as an integer.
- defaultValue defaults to {0}
Return defaultValue[0] if the node's value does not exist or is not convertable to an integer.
func (*Node) GoString ¶ added in v2.0.4
GoString makes print("%#v") dump the node and its contents with Node.Dump(30, 80). For example the output format of Node("person",42).GoString() might look like this:
person(42)=1234
person(42)("age")="49"
person(42)("height")("centimeters")=190.5
person(42)("height")("inches")=1234
person(42)("name")="Joe Bloggs"
Example ¶
Example of traversing a database tree
conn := yottadb.NewConn()
n := conn.Node("tree", 1)
n.Child(2, 3).Set(123)
n.Child(2, 3, 7).Set("Hello!")
n.Child(2, 4).Set(124)
fmt.Printf("Dump is:\n%#v", n)
Output: Dump is: tree(1,2,3)=123 tree(1,2,3,7)="Hello!" tree(1,2,4)=124
func (*Node) HasTree ¶
HasTree returns whether the database node has a tree of subscripts containing data.
func (*Node) HasTreeOnly ¶
HasTreeOnly returns whether the database node has no value but does have a tree of subscripts that contain data
func (*Node) HasValueOnly ¶
HasValueOnly returns whether the database node has a value but no tree.
func (*Node) Incr ¶
Incr atomically increments the value of the database node by amount and returns the new value as a string.
- The amount may be an integer, float or string representation of the same.
- YottaDB first converts the value of the node to a canonical number by discarding any trailing non-digits and returning zero if it is still not a number. Then it adds amount to the node, all atomically.
- Return the new value of the node as a string
func (*Node) IncrFloat ¶ added in v2.0.6
IncrFloat atomically increments the value of the database node by amount and returns the new value as a float64. It operates the same as Node.Incr() except that it returns a float64 instead of a string. It panics if the returned value cannot be represented as a float, which should be impossible since prior to the increment, YottaDB first converts the node's value to a canonical number by discarding any trailing non-digits and, if it is still not a number, defaulting to zero.
func (*Node) IncrInt ¶ added in v2.0.6
IncrInt atomically increments the value of the database node by amount and returns the new value as an integer. It operates the same as Node.IncrFloat() except that it returns an int instead of a float64. Any fractional portion in the returned value is truncated by Go's standard int(n) type cast logic, but is retained in the database and may be accessed by Node.GetFloat().
func (*Node) Index ¶ added in v2.0.4
Index allows fast temporary access to subnodes referenced by the given subscripts, but must be used with caution. It is most helpful for high-speed inner loops. If in doubt, use the slower Node.Child. It indexes a node object with the given subscripts and returns a mutable node object that will change next time Index is invoked on the same parent node object (which is thread-safe because each Goroutine has separate node objects). Fast access is achieved by removing the overhead of creating a new child node object every access, e.g. each time around a loop. Node.Children, for example, yields mutable nodes created by Index().
*Caution*: for non-temporary access, the slower Node.Child should be used instead. For example, where nodes will be passed to subroutines (subroutines should be able to assume they have been passed immutable nodes). Node.Index must also be avoided when you wish a Go variable to retain a pointer to a specific node after another use of Node.Index. The second example below illustrates incorrect usage.
- Returns a mutable child of the given node with the given subscripts appended (see Node.IsMutable for mutability details).
Usage is similar to Node.Child except that it typically runs about 4 times as fast and returns mutable instead of immutable nodes.
Example ¶
Example of fast iteration of a node to increment only children with subscripts 0..99999.
conn := yottadb.NewConn()
n := conn.Node("counter")
n.Index(100000).Set("untouched")
for i := range 100000 {
n.Index(i).Incr(1)
}
fmt.Printf("%s: %s\n", n.Index(0), n.Index(0).Get())
fmt.Printf("%s: %s\n", n.Index(99999), n.Index(99999).Get())
fmt.Printf("%s: %s\n", n.Index(100000), n.Index(100000).Get())
Output: counter(0): 1 counter(99999): 1 counter(100000): untouched
Example (Incorrect) ¶
Example of when not to use Node.Index
conn := yottadb.NewConn()
person := conn.Node("person")
first := person.Index("first")
last := person.Index("last") // This overwrites the person index to be 'last' so first now points to the wrong thing
first.Set("Joe")
last.Set("Bloggs")
// Trying to set the first name failed; instead setting the last name,
// which was then overwritten when setting the last name
fmt.Printf("Retrieving the stored names yields first='%s', last='%s'\n", first.Get(), last.Get())
Output: Retrieving the stored names yields first='Bloggs', last='Bloggs'
func (*Node) IsMutable ¶
IsMutable returns whether given node is mutable. Mutable nodes may have their subscripts changed each loop iteration or each call to Node.Index(). This means that the same mutable node object may reference different database nodes and will not always point to the same one.
- Mutable nodes are returned by Node.Index, Node.Next, Node.Prev and iterator Node.Children.
- All standard node methods are valid operations on a mutable node except conn.CloneNode() which will panic.
- If an immutable copy of a mutable node is required, use Node.Clone or Node.Child.
- If you need to take an immutable snapshot of a mutable node this may be done with Node.Clone or Node.Child.
A mutable node object is like a regular node object except that it will change to point to a different database node each time Node.Index is invoked on its originating node object n, so if you store a reference to it, that reference will no longer point to the same database node as it originally did. For example, the following code will print the most recent vehicle, not the heaviest vehicle as intended, because Node.Children() yields mutable nodes.
n := conn.Node("vehicles")
var heaviest *yottadb.Node
var maxWeight float64 = 0
for vehicle := range n.Children() {
if vehicle.Index("weight").GetFloat() > maxWeight {
heaviest = vehicle
maxWeight = vehicle.Index("weight")
}
}
fmt.Print(heaviest.Dump())
func (*Node) Kill ¶
func (n *Node) Kill()
Kill deletes a database node including its value and any subtree.
- To delete only the value of a node use Node.Clear()
Example ¶
Example
// Note: this example is also used in the README.md
defer yottadb.Shutdown(yottadb.MustInit())
conn := yottadb.NewConn()
hi := conn.Node("^howdy") // create Node instance pointing to YottaDB global ^hello
hi.Set("western")
cowboy := hi.Child("cowboy") // new variable pointing to subnode "cowboy" subscript
cowboy.Set("Howdy partner!") // set ^hello("cowboy") to "Howdy partner!"
ranches := cowboy.Child("ranches")
ranches.Incr(2) // Increment empty node by 2 to get 2
fmt.Printf("First dump:\n%#v\n", hi) // %#v produces the same string as hi.Dump()
cowboy.Kill() // delete node, its children, and all values
fmt.Printf("Second dump:\n%#v\n", hi)
hi.Clear() // clear this node's value, too
Output: First dump: ^howdy="western" ^howdy("cowboy")="Howdy partner!" ^howdy("cowboy","ranches")=2 Second dump: ^howdy="western"
func (*Node) Lock ¶
Lock attempts to acquire or increment the count of a lock matching this node, waiting up to timeout for availability. Equivalent to the M `LOCK +lockpath` command.
- If no timeout is supplied, wait forever. A timeout of zero means try only once.
- Return true if lock was acquired; otherwise false.
- Panics with TIME2LONG if the timeout exceeds YDB_MAX_TIME_NSEC or on other panic-worthy errors (e.g. invalid variable names).
func (*Node) Lookup ¶
Lookup returns the value of a database node and true, or if the variable name could not be found, returns the empty string and false.
- If the node's variable name (M local or M global) exists but the subscripted node has no value, Lookup() will return the empty string and true. If you need to distinguish between an empty string and a value-less node you must use Node.HasValue()
- bool false is returned on errors GVUNDEF (undefined M global) or LVUNDEF (undefined M local). Other errors panic.
- You may use Node.Get() to return a default value when an undefined variable is accessed.
func (*Node) LookupBytes ¶ added in v2.0.4
LookupBytes is the same as Node.Lookup except that it returns the value as a []byte slice rather than a string.
func (*Node) Next ¶
Next returns a Node instance pointing to the next subscript at the same depth level. Return a mutable node pointing to the database node with the next subscript after the given node, at the same depth level. Unless you want to start half way through a sequence of subscripts, it's usually tidier to use Node.Iterate() instead.
- Equivalent to the M function $ORDER() and has the same treatment of 'null subscripts' (i.e. empty strings).
- The order of returned nodes matches the collation order of the M database.
- The node path supplied does not need to exist in the database to find the next match.
- If the supplied node n contains only a variable name without subscripts, the next variable (GLVN) name is returned instead of the next subscript.
Returns nil when there are no more subscripts at the level of the supplied node path, or a mutable node as follows:
- if the supplied node is immutable, a mutable clone of n with its final subscript changed to the next node.
- if the supplied node is mutable, the same node n with its final subscript changed to the next node.
If you need to take an immutable snapshot of the returned mutable node this may be done with Node.Clone()
See:
- Compare Node.Prev()
- [Node.Iterate]() for an iterator version and Node.TreeNext() for traversal of nodes in a way that descends into the entire tree.
Example ¶
Example of getting next subscript
conn := yottadb.NewConn()
n := conn.Node("X", 1)
n.Child(2, "3").Set("123")
n.Child(2, 3, 7).Set(1237)
n.Child(2, 4).Set(124)
x := conn.Node("X", 1, "2", "")
x = x.Next()
for x != nil {
fmt.Printf("%s=%s\n", x, x.Get())
x = x.Next()
}
Output: X(1,2,3)=123 X(1,2,4)=124
Example (Varnames) ¶
Example of listing all local database variable names
conn := yottadb.NewConn()
conn.KillAllLocals()
conn.Node("X", 1).Set("X1")
conn.Node("X", 1, 2).Set("X12")
conn.Node("Y", 2).Set("Y2")
fmt.Println("Display all top-level database variable names, starting after '%' (which is the first possible name in sort order)")
x := conn.Node("%")
x = x.Next()
for x != nil {
fmt.Printf("%s\n", x)
x = x.Next()
}
Output: Display all top-level database variable names, starting after '%' (which is the first possible name in sort order) X Y
func (*Node) Set ¶
Set applies val to the value of a database node.
- The val may be a string, []byte slice, or an integer or float type; numeric types are converted to a string using the appropriate strconv function.
func (*Node) String ¶
String returns a string representation of this database node in typical YottaDB format: `varname("sub1")("sub2")`.
- Output subscripts as unquoted numbers if they convert to float64 and back without change (using [Node.Quote]).
- Output strings in YottaDB ZWRITE format
Example ¶
Example of viewing a Node instance as a string:
conn := yottadb.NewConn()
n := conn.Node("var", "sub1", "sub2")
numsubs := conn.Node("var2", 1, 2)
fmt.Println(n)
fmt.Println(numsubs)
Output: var("sub1","sub2") var2(1,2)
func (*Node) Subscript ¶ added in v2.0.4
Subscript returns a string that holds the specified varname or subscript of the given node. An index of zero returns the varname; higher numbers return the respective subscript. A negative index returns a subscript counted from the end (the last is -1). An out-of-range subscript panics.
func (*Node) Subscripts ¶
Subscripts returns a slice of strings that represent the varname and subscript names of the given node.
Example ¶
conn := yottadb.NewConn()
n := conn.Node("var", "sub1", "sub2")
fmt.Println(n)
fmt.Println(n.Subscripts())
Output: var("sub1","sub2") [var sub1 sub2]
func (*Node) Tree ¶
Tree returns an interator over all descendants of node for use in a FOR-loop. This iterator is a wrapper for Node.TreeNext(). It yields immutable node instances.
- The next node is chosen in depth-first order (i.e by descending deeper into the subscript tree before moving to the next node at the same level).
- The order of returned nodes matches the collation order of the M database.
- Nodes that have 'null subscripts' (i.e. empty string) are all returned in their place.
See:
- Node.TreeNext(), Node.TreePrev()
- Node.Children() for traversal of only immediate children.
Example ¶
Example of traversing a database tree
conn := yottadb.NewConn()
n := conn.Node("tree", 1)
n.Child(2, 3).Set(123)
n.Child(2, 3, 7).Set(1237)
n.Child(2, 4).Set(124)
n.Child(2, 5, 9).Set(1259)
n.Child(6).Set(16)
nb := conn.Node("tree", "B")
nb.Child(1).Set("AB")
for x := range n.Child(2).Tree() {
fmt.Printf("%s=%s\n", x, conn.Quote(x.Get()))
}
Output: tree(1,2,3)=123 tree(1,2,3,7)=1237 tree(1,2,4)=124 tree(1,2,5,9)=1259
func (*Node) TreeNext ¶
TreeNext returns the next node in the traversal of a database tree of a database variable. Equivalent to the M function $QUERY().
- It yields immutable nodes.
- The next node is chosen in depth-first order (i.e by descending deeper into the subscript tree before moving to the next node at the same level).
- The order of returned nodes matches the collation order of the M database.
- The node path supplied does not need to exist in the database to find the next match.
- Returns nil when there are no more nodes after the given node path within the given database variable (GLVN).
- Nodes that have 'null subscripts' (i.e. empty string) are all returned in their place except for the top-level GLVN(""), which is never returned.
See:
- Node.TreePrev().
- [Node.LevelNext]() for traversal of nodes at the same level or to move from one database variable (GLVN) to another.
Example ¶
Example of traversing a database tree
conn := yottadb.NewConn()
n := conn.Node("tree", 1)
n.Child(2, 3).Set(123)
n.Child(2, 3, 7).Set(1237)
n.Child(2, 4).Set(124)
n.Child(2, 5, 9).Set("Hello!")
n.Child(6).Set(16)
nb := conn.Node("tree", "B")
nb.Child(1).Set("AB")
x := conn.Node("tree").TreeNext()
for x != nil {
fmt.Printf("%s=%s\n", x, conn.Quote(x.Get()))
x = x.TreeNext()
}
fmt.Println("Re-start half way through and go in reverse order:")
x = conn.Node("tree", 1, 2, 4)
for x != nil {
fmt.Printf("%s=%s\n", x, conn.Quote(x.Get()))
x = x.TreePrev()
}
Output: tree(1,2,3)=123 tree(1,2,3,7)=1237 tree(1,2,4)=124 tree(1,2,5,9)="Hello!" tree(1,6)=16 tree("B",1)="AB" Re-start half way through and go in reverse order: tree(1,2,4)=124 tree(1,2,3,7)=1237 tree(1,2,3)=123
func (*Node) TreePrev ¶
TreePrev is the same as TreeNext but operates in reverse order. See Node.TreeNext().
type RoutineData ¶
type RoutineData struct {
Name string // Go name for the M routine
Entrypoint string // the point to be called in M code
Types []typeSpec // stores type specification for the routine's return value and each parameter
// metadata:
Table *CallTable // call table used to find this routine name
// contains filtered or unexported fields
}
RoutineData stores info used to call an M routine.