
Programming in Go
There are two wrappers for programming YottaDB in Go, mg_go and the YottaDB Go wrapper (described below). mg_go is developed by Chris Munt of MGateway Ltd. We would like to acknowledge his contribution and thank Chris for the value he adds to the YottaDB community.
mg_go provides the ability to access YottaDB locally as well as remotely over a network.
The documentation below is specific to the YottaDB Go wrapper. Please use the link to mg_go to access its documentation.
The YottaDB Go wrapper wraps the Simple API threaded functions and uses cgo to provide a "yottadb" package for access from Go application code. The wrapper must be installed on a system after YottaDB is installed. There are two Go APIs:
Go Easy API aims to be a straightforward, easy-to-use API to access YottaDB without limiting the functionality of YottaDB. The Go Easy API consists of Go Easy API Functions that use standard Go data types and structures.
Go Simple API aims to improve performance by reducing copying between Go and YottaDB heaps by defining structures
BufferT,BufferTArray, andKeyTwhich contain pointers to structures and data in the YottaDB heap. Go Simple API functionality is provided by Go methods where a method can meaningfully be associated with a structure, and by Go functions where there is no meaningful association with a structure.
As the Go language has important differences from C (for example, it has structures with methods but lacks macros), below are Go-specific sections of the Quick Start, Concepts, Symbolic Constants, Data Structures & Type Definitions, Simple API and Utility Functions sections. The sections that are specific to Go are intended to supplement, but not subsume, their C counterparts.
Go application code must not directly use the YottaDB C API structures and functions (those prefixed by C. or described in the C Simple API) as such usage bypasses important controls, but should instead use the structures, methods and functions exposed by the YottaDB Go wrapper. C. prefixed structures and functions are mentioned only for clarity in documentation and brevity of explanation. For example, C.ydb_buffer_t is the C ydb_buffer_t structure defined in Data Structures & Type Definitions.
All subsections of the Programming in Go section are prefixed with "Go" to ensure unique names for hyperlinking.
As Go implementations are inherently multi-threaded, where the C Simple API provides separate functions for use in multi-threaded applications, e.g., ydb_get_s() / ydb_get_st(), the Go wrapper wraps the function for use in multi-threaded applications. Also, to accommodate Go's multi-threading, calls include an errstr parameter to get the correct $ZSTATUS for each call.
Go Quick Start
The YottaDB Go wrapper requires a minimum YottaDB release of r1.30 and is tested with a minimum Go version of 1.18. If the Golang packages on your operating system are older, and the Go wrapper does not work, please obtain and install a newer Golang implementation.
The Go Quick Start assumes that YottaDB has already been installed as described in the Quick Start section. After completing step 1 (Installing YottaDB), you can immediately start using YottaDB with a Go application that uses YottaDB. To create a Go application that uses YottaDB, see below.
# Sets up a default YottaDB environment
$ source /usr/local/etc/ydb_env_set
# Create an empty directory and cd to it
$ mkdir ydb-example && cd ydb-example
# Initialize a new Go Module
$ go mod init example.com/yottadb-example
# Test program containing the YottaDB Go Import
$ cat > yottadb-example.go << EOF
package main
import (
"lang.yottadb.com/go/yottadb"
)
func main() {
// Set global node ^hello("world")="Sawadee (hello in Thai)"
err := yottadb.SetValE(yottadb.NOTTP, nil, "สวัสดี", "^hello", []string{"world"})
if err != nil {
panic(err)
}
}
EOF
# Download the YottaDB Go Module (you can also use "go mod tidy" here)
$ go get .
# Run the code; alternatively, you can use `go build` to compile it into an exe
$ go run .
$ go build && ./yottadb-example
As a sample program, you can download the wordfreq.go program, with a reference input file and corresponding reference output file.
Run your program and verify that the output matches the reference output. For example:
$ source /usr/local/etc/ydb_env_set
$ mkdir yottadb-wordfreq && cd yottadb-wordfreq
$ go mod init example.com/yottadb-wordfreq
$ wget https://gitlab.com/YottaDB/DB/YDBTest/raw/master/go/inref/wordfreq.go
$ go mod tidy
$ wget https://gitlab.com/YottaDB/DB/YDBTest/raw/master/simpleapi/outref/wordfreq_input.txt
$ wget https://gitlab.com/YottaDB/DB/YDBTest/raw/master/simpleapi/outref/wordfreq_output.txt
$ go build wordfreq.go
$ ./wordfreq <wordfreq_input.txt >wordfreq_output_go.txt
$ diff wordfreq_output_go.txt wordfreq_output.txt
$
Note that the wordfreq.go program randomly uses local or global variables (see Local and Global Variables).
Go Concepts
As the YottaDB wrapper is distributed as a Go package, function calls to YottaDB are prefixed in Go code with yottadb. (e.g., application code to call the ValST() function would be written yottadb.ValST(…).
Go Error Interface
YottaDB has a comprehensive set of error return codes. Each has a unique number and a mnemonic. Thus, for example, to return an error that a buffer allocated for a return value is not large enough, YottaDB uses the INVSTRLEN error code, which has the numeric value yottadb.YDB_ERR_INVSTRLEN. YottaDB attempts to maintain stability of the numeric values and mnemonics from release to release, to ensure applications remain compatible when the underlying YottaDB releases are upgraded. While the Go error interface provides for a call to return an error as a string (with nil for a successful return), applications in other languages, such as C, expect a numeric return value.
The C application code calling YottaDB functions will check the return code. If the return code is not YDB_OK, it will access the intrinsic special variable $ZSTATUS for more detailed information (though the errstr parameter in a multi-threaded application). By contrast, Go application code calling YottaDB methods and functions will check the error interface to determine whether it is nil. This means that Go application code will never see a yottadb.YDB_OK return.
The YottaDB Go error interface has a structure and a method. Sample usage:
_, err := yottadb.ValE(yottadb.NOTTP, nil, "^hello", []string{})
if err != nil {
errcode := yottadb.ErrorCode(err)
}
In the documentation:
Error codes specific to each function are noted. However, common errors can also be returned. For example, while the Go BufferT ValStr() method can return INVSTRLEN, it can also return errors from the YottaDB engine.
An error name such as INVSTRLEN refers to the underlying error, whether application code references the numeric value or the string.
Go Error Return Codes
In addition to the errors discussed in the C Error Return Codes the Go wrapper has additional errors unique to it.
DBRNDWNBYPASS
The DBRNDWNBYPASS message is sent to the syslog.
INVLKNMPAIRLIST
INVLKNMPAIRLIST, Invalid lockname/subscript pair list (uneven number of lockname/subscript parameters)
Compile Time Error: The namesnsubs parameter of Go LockE() is not a series of alternating string and []string parameters.
Action: This is an application syntax bug. Fix the application code.
SIGACKTIMEOUT
The SIGACKTIMEOUT message is sent to the syslog.
SIGGORTNTIMEOUT
The SIGGORTNTIMEOUT message is sent to the syslog.
STRUCTUNALLOCD
STRUCTUNALLOCD, Structure not previously called with Alloc() method
Run Time Error: The corresponding Alloc() method has not been executed for a structure, e.g., a BufferT, passed to a method.
Action: This is an application logic bug. Fix the application code.
Go Constants
The file yottadb.go in the Go wrapper defines a number of constants used to initialize variables that control signal handling.
DefaultMaximumNormalExitWaitis initial value ofMaximumNormalExitWait, with a default of 60 seconds.DefaultMaximumPanicExitWaitis the initial value ofMaximumPanicExitWait, with a default of 3 seconds.DefaultMaximumSigShutDownWaitis initial value ofMaximumSigShutDownWait, with a default of 5 seconds.DefaultMaximumSigAckWaitis initial value ofMaximumSigAckWait, with a default if 10 seconds.
Go Symbolic Constants
YottaDB symbolic constants are available in the YottaDB package, for example, yottadb.YDB_ERR_INVSTRLEN.
NOTTP (yottadb.NOTTP) as a value for parameter tptoken indicates to the invoked YottaDB method or function that the caller is not inside a transaction.
Go Easy API
A global or local variable node, or an intrinsic special variable, is specified using the construct varname string, subary []string. For an intrinsic special variable, subary must be the null array, []string{}, or nil. For a global or local variable, a null array or nil for subary refers to the root node, the entire tree, or both, depending on the function and context.
As the Go Easy API involves more copying of data between the Go and YottaDB runtime systems, it requires the CPU to perform a little more work than the Go Simple API does. Whether or not this has a measurable impact on performance depends on the application and workload.
The length of strings (values and subscripts) in YottaDB is variable, as is the number of subscripts a local or global variable can have. The Go Simple API requires application code to allocate memory for buffers, raising errors when allocated memory (either size or number of buffers) is insufficient. Requiring application code using the Go Easy API to similarly allocate memory would be at odds with our goal of having it "just work". Although YottaDB provides functionality to a priori determine the length of a value in order to allocate required memory, doing this for every call would adversely affect performance. The Go Easy API therefore allocates buffers initially of a size and number we believe to be reasonable. Whenever a result exceeds its allocation and returns an error, YottaDB expands the allocation transparently to the caller, and repeats the operation, remembering the expanded size for future allocations in the process.
Go Easy API Functions
Go Simple API
The Go Simple API consists of Go Data Structures & Type Definitions, Go Simple API BufferT Methods, Go Simple API BufferTArray Methods and Go Simple API KeyT Methods. Each of them wraps a function in the C Simple API - refer to the descriptions of those functions for more detailed information. The majority of the functionality is in Go Simple API KeyT Methods.
Go Data Structures & Type Definitions
The C.ydb_buffer_t structure, which is the ydb_buffer_t structure described in Data Structures & Type Definitions is used to pass values between Go application code and YottaDB. The design pattern is that the ydb_buffer_t structures are in memory managed by YottaDB. Go structures contain pointers to the YottaDB structures so that when the Go garbage collector moves Go structures, the pointers they contain remain valid.
There are five structures for the interface between YottaDB and Go:
BufferTfor data;BufferTArrayfor a list of subscripts or a set of variable names;KeyTfor keys where a key in turn consists of a variable or lock resource name and subscripts, as discussed in Concepts; andCallMDescreferences an M routine and caches information to accelerate calls from Go to M.CallMTableto reference an M code call-in table.
Go Wrapper Functions
Go CallMT()
func CallMT(tptoken uint64, errstr *BufferT, retvallen uint32, rtnname string, rtnargs ...interface{}) (string, error)
As a wrapper for the C function ydb_ci_t(), the CallMT() function is used to call M routines from Go, used when a single call to the function is anticipated.
retvallenneeds to be of sufficient size to hold any value returned by the call. If the output value exceeds the buffer size, a SIG-11 failure is likely as it will overwrite adjacently allocated memory, damaging storage management headers.If a return value is specified but has not been configured in the call-in descriptor file or vice-versa, a parameter mismatch situation is created.
rtnargsrefers to a list of 0 or more arguments passed to the called routine. As all arguments are passed as strings after conversion by fmt.Sprintf("%v", parm), any argument that can be so converted can be used here. Any error returned by fmt.Sprintf() is returned as an error byCallMT(). Note that passing an array will generate a string containing an entire array, which may be unexpected. The number of parameters possible is restricted to 34 (for 64-bit systems) or 33 (for 32-bit systems).Note that functions that are defined in the call-in table (refer call-in table in the M Programmer's Guide) that have IO (input/output) or O (output) parameters can only be defined as string variables (and not literals) as the wrapper will try to put the updated values back into these variables. The parameter values need to be passed by reference otherwise it will result in an error.
Example:
fmt.Println("Golang: Invoking HelloWorld")
retval, err := yottadb.CallMT(yottadb.NOTTP, nil, 1024, "HelloWorld", "English", "USA")
if nil != err {
panic(fmt.Sprintf("CallMT() call failed: %s", err))
}
fmt.Println("Golang: retval =", retval)
The HelloWorld program in the example returns a "HelloWorld" string in a language "English" and a location "USA" specified in the two parameters. retvallen is set to be 1024 bytes.
Note that a call-in table is required when calling from Go into M. A call-in table can be specified at process startup with the environment variable ydb_ci or using the functions Go CallMTable CallMTableOpenT() and Go CallMTable CallMTableSwitchT().
Go DataE()
func DataE(tptoken uint64, errstr *BufferT, varname string, subary []string) (uint32, error)
Matching Go KeyT DataST(), DataE() function wraps and returns the result of ydb_data_s() / ydb_data_st() (0, 1, 10, or 11). In the event of an error, the return value is unspecified.
Go DeleteE()
func DeleteE(tptoken uint64, errstr *BufferT, deltype int, varname string, subary []string) error
Matching Go KeyT DeleteST(), DeleteE() wraps ydb_delete_s() / ydb_delete_st() to delete a local or global variable node or (sub)tree, with a value of yottadb.YDB_DEL_NODE for deltype specifying that only the node should be deleted, leaving the (sub)tree untouched, and a value of yottadb.YDB_DEL_TREE specifying that the node as well as the (sub)tree are to be deleted.
Go DeleteExclE()
func DeleteExclE(tptoken uint64, errstr *BufferT, varnames []string) error
Matching Go BufferTArray DeleteExclST(), DeleteExclE() wraps ydb_delete_excl_s() / ydb_delete_excl_st() to delete all local variables except those specified. In the event varnames has no elements (i.e., []string{}), DeleteExclE() deletes all local variables.
In the event that the number of variable names in varnames exceeds yottadb.YDB_MAX_NAMES, the error return is ERRNAMECOUNT2HI. Otherwise, if ydb_delete_excl_s() / ydb_delete_excl_st() returns an error, the function returns the error.
As mixing M and Go application code in the same process is now supported, make sure you understand what (sub)trees are being deleted when you use ydb_delete_excl_s() / ydb_delete_excl_st().
Go ErrorCode()
func ErrorCode(err error) int
ErrorCode() is a function used to find the error return code.
Go Exit()
func Exit() error
This function invokes YottaDB's exit handler ydb_exit() to shut down the database properly. It MUST be called prior to process termination by any application that modifies the database. 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 Exit() 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).
Recommended behaviour is for your main routine to defer yottadb.Exit() early in the main routine's initialization, and then for the main routine
to confirm that all goroutines have stopped or have completely finished accessing the database before returning.
If Go routines that access the database are spawned, it is the main routine's responsibility to ensure that all such threads have finished using the database before it calls
yottadb.Exit().The application must not call Go's os.Exit() function which is a very low-level function that bypasses any defers.
Care must be taken with any signal notifications (see Go Using Signals) to prevent them from causing premature exit.
Note that Go will run defers on panic, but not on fatal signals such as
SIGSEGV.
Exit() may be called multiple times by different threads during an application shutdown.
Go IncrE()
func IncrE(tptoken uint64, errstr *BufferT, incr, varname string, subary []string) (string, error)
Matching Go KeyT IncrST(), IncrE() wraps ydb_incr_s() / ydb_incr_st() to atomically increment the referenced global or local variable node coerced to a number with incr coerced to a number, with the result stored in the node and returned by the function.
If ydb_incr_s() / ydb_incr_st() returns an error such as NUMOFLOW or INVSTRLEN, the function returns the error.
Otherwise, it returns the incremented value of the node.
With a nil value for incr, the default increment is 1. Note that the value of the empty string coerced to an integer is zero, but 1 is a more useful default value for an omitted parameter in this case.
Go Init()
func Init()
The first invocation of any EasyAPI and SimpleAPI function or method automatically initializes the YottaDB runtime system. Applications that need to initialize YottaDB prior to that, can call Init(). Calling Init() when the YottaDB runtime system is already initialized is a no-op.
Go IsLittleEndian()
func IsLittleEndian() bool
The function returns true if the underlying computing infrastructure is little endian and false otherwise.
Go LockDecrE()
func LockDecrE(tptoken uint64, errstr *BufferT, varname string, subary []string) error
Matching Go KeyT LockDecrST() LockDecrE() wraps ydb_lock_decr_s() / ydb_lock_decr_st() to decrement the count of the lock name referenced, releasing it if the count goes to zero or ignoring the invocation if the process does not hold the lock.
Go LockE()
func LockE(tptoken uint64, errstr *BufferT, timeoutNsec uint64, namesnsubs ... interface{}) error
Matching Go LockST(), LockE() releases all lock resources currently held and then attempts to acquire the named lock resources referenced. If no lock resources are specified, it simply releases all lock resources currently held and returns.
interface{} is a series of pairs of varname string and subary []string parameters, where a null subary parameter ([]string{}) specifies the unsubscripted lock resource name.
If lock resources are specified, upon return, the process will have acquired all of the named lock resources or none of the named lock resources.
If
timeoutNsecexceedsyottadb.YDB_MAX_TIME_NSEC, the function returns with an error return of TIME2LONG.If the lock resource names exceeds the maximum number supported (currently 11), the function returns a PARMOFLOW error.
If
namesnsubsis not a series of alternatingstringand[]stringparameters, the function returns the INVLKNMPAIRLIST error.If it is able to aquire the lock resource(s) within
timeoutNsecnanoseconds, the function returns holding the lock resource(s); otherwise it returns LOCKTIMEOUT. IftimeoutNsecis zero, the function makes exactly one attempt to acquire the lock resource(s).
Go LockIncrE()
func LockIncrE(tptoken uint64, errstr *BufferT, timeoutNsec uint64, varname string, subary []string) error
Matching Go KeyT LockIncrST(), LockIncrE() wraps ydb_lock_incr_s() / ydb_lock_incr_st() to attempt to acquire the referenced lock resource name without releasing any locks the process already holds.
If the process already holds the named lock resource, the function increments its count and returns.
If
timeoutNsecexceedsyottadb.YDB_MAX_TIME_NSEC, the function returns with an error return TIME2LONG.If it is able to aquire the lock resource within
timeoutNsecnanoseconds, it returns holding the lock, otherwise it returns LOCKTIMEOUT. IftimeoutNsecis zero, the function makes exactly one attempt to acquire the lock.
Go LockST()
func LockST(tptoken uint64, errstr *BufferT, timeoutNsec uint64, lockname ... *KeyT) error
Matching Go LockE(), LockST() wraps ydb_lock_s() / ydb_lock_st() to release all lock resources currently held and then attempt to acquire the named lock resources referenced. If no lock resources are specified, it simply releases all lock resources currently held and returns.
If lock resources are specified, upon return, the process will have acquired all of the named lock resources or none of the named lock resources.
If
timeoutNsecexceedsyottadb.YDB_MAX_TIME_NSEC, the method returns with a TIME2LONG error.If the number of lock resource names exceeds the maximum number supported (currently eleven), the function returns a PARMOFLOW error.
If it is able to aquire the lock resource(s) within
timeoutNsecnanoseconds, it returns holding the lock resource(s); otherwise it returns LOCKTIMEOUT. IftimeoutNsecis zero, the method makes exactly one attempt to acquire the lock resource(s).
Go MessageT()
func MessageT(tptoken uint64, errstr *BufferT, status int) (string, error)
MessageT() returns the text template for the error number specified by status.
If
statusdoes not correspond to an error that YottaDB recognizes, it returns the error UNKNOWNSYSERR.Otherwise, it returns the error message text template for the error number specified by
status.
Go NewError()
func NewError(tptoken uint64, errstr *BufferT, errnum int) error
NewError() is a function to create a new YDBError from errstr and errnum, setting the two private fields in the returned YDBError to the provided values.
Go NodeNextE()
func NodeNextE(tptoken uint64, errstr *BufferT, varname string, subary []string) ([]string, error)
Matching Go KeyT NodeNextST(), NodeNextE() wraps ydb_node_next_s() / ydb_node_next_st() to facilitate traversal of a local or global variable tree. A node or subtree does not have to exist at the specified key.
If there is a next node, it returns the subscripts of that next node.
If there is no node following the specified node, the function returns the NODEEND error.
Go NodePrevE()
func NodePrevE(tptoken uint64, errstr *BufferT, varname string, subary []string) ([]string, error)
Matching Go KeyT NodePrevST(), NodePrevE() wraps ydb_node_previous_s() / ydb_node_previous_st() to facilitate reverse traversal of a local or global variable tree. A node or subtree does not have to exist at the specified key.
If there is a previous node, it returns the subscripts of that previous node; an empty string array if that previous node is the root.
If there is no node preceding the specified node, the function returns the NODEEND error.
Go RegisterSignalNotify()
func RegisterSignalNotify(sig syscall.Signal, notifyChan, ackChan chan bool, notifyWhen YDBHandlerFlag) error
Requests that when the wrapper receives the notification that a given signal (sig) has occurred, then if a registration has been done with this function for that signal, we also notify a user channel that the signal has occurred. How and when that notification is sent (relative to YottaDB's own handling of the signal) is controlled by the YDBHandlerFlag setting. The flag descriptions are described in the Go Using Signals subsection of the Go Programming Notes.
Both notifyChan and ackChan are channels passed in by the caller. The notifyChan is the channel the caller wishes to be notified on when the specified signal occurs. The ackChan is the channel that, once the notified routine is done doing whatever they were going to do, the notify routine should post something (any bool) on this channel to notify the wrapper they are done. Signal processing then proceeds depending on when the user notification occurred. Note that before the dispatcher notifies the notifyChan user channel, the ackChan channel is drained.
Go ReleaseT()
func ReleaseT(tptoken uint64, errstr *BufferT) string
Returns a string consisting of six space separated pieces to provide version information for the Go wrapper and underlying YottaDB release:
The first piece is always "gowr" to identify the Go wrapper.
The Go wrapper release number, which starts with "v" and is followed by three numbers separated by a period ("."), e.g., "v0.90.0" mimicking Semantic Versioning. The first is a major release number, the second is a minor release number under the major release and the third is a patch level. Even minor and patch release numbers indicate formally released software. Odd minor release numbers indicate software builds from "in flight" code under development, between releases. Note that although they follow the same format, Go wrapper release numbers are different from the release numbers of the underlying YottaDB release as reported by $ZYRELEASE.
The third through sixth pieces are $ZYRELEASE from the underlying YottaDB release.
Go SetValE()
func SetValE(tptoken uint64, errstr *BufferT, value, varname string, subary []string) error
Matching Go KeyT SetValST(), at the referenced local or global variable node, or the intrinsic special variable, SetValE() wraps ydb_set_s() / ydb_set_st() to set the value specified by value.
Go SubNextE()
func SubNextE(tptoken uint64, errstr *BufferT, varname string, subary []string) (string, error)
Matching Go KeyT SubNextST(), SubNextE() wraps ydb_subscript_next_s() / ydb_subscript_next_st() to facilitate traversal of a local or global variable sub-tree. A node or subtree does not have to exist at the specified key.
At the level of the last subscript, if there is a next subscript with a node and/or a subtree, it returns that subscript.
If there is no next node or subtree at that level of the subtree, the function returns the NODEEND error.
In the special case where subary is the null array, SubNextE() returns the name of the next global or local variable, and the NODEEND error if there is no global or local variable following varname.
Go SubPrevE()
func SubPrevE(tptoken uint64, errstr *BufferT, varname string, subary []string) (string, error)
Matching Go KeyT SubPrevST(), SubPrevE() wraps ydb_subscript_previous_s() / ydb_subscript_previous_st() to facilitate reverse traversal of a local or global variable sub-tree. A node or subtree does not have to exist at the specified key.
At the level of the last subscript, if there is a previous subscript with a node and/or a subtree, it returns that subscript.
If there is no previous node or subtree at that level of the subtree, the function returns the NODEEND error.
In the special case where subary is the null array SubPrevE() returns the name of the previous global or local variable, and the NODEEND error if there is no global or local variable preceding varname.
Go TpE()
func TpE(tptoken uint64, errstr *BufferT, tpfn func(uint64, *BufferT) int32, transid string, varnames []string) error
Matching Go BufferTArray TpST(), TpE() wraps ydb-tp-s-st-fn to implement Transaction Processing.
Refer to Go BufferTArray TpST() for a more detailed discussion of YottaDB Go transaction processing.
Go UnRegisterSignalNotify()
func UnRegisterSignalNotify(sig syscall.Signal) error
Requests a halt to signal notifications for the specified signal. If the signal is a signal that YottaDB does not allow, currently, the wrapper raises a panic (like it does for all other wrapper errors) though this is likely to change in a subsequent release. If the signal is a valid signal but is not being monitored, no error results. In that case, the call is a no-op.
Go ValE()
func ValE(tptoken uint64, errstr *BufferT, varname string, subary []string) (string, error)
Matching Go KeyT ValST(), ValE() wraps ydb_get_s() / ydb_get_st() to return the value at the referenced global or local variable node, or intrinsic special variable.
If ydb_get_s() / ydb_get_st() returns an error such as GVUNDEF, INVSVN, LVUNDEF, the function returns the error.
Otherwise, it returns the value at the node.
Go BufferT Alloc()
func (buft *BufferT) Alloc(nBytes uint32)
Allocate a buffer in YottaDB heap space of size nBytes; and set BufferT structure to provide access to that buffer.
Go BufferT Dump()
func (buft *BufferT) Dump()
For debugging purposes, dump on stdout:
buftas a hexadecimal address;for the
C.ydb_buffer_tstructure referenced bybuft:buf_addras a hexadecimal address, andlen_allocandlen_usedas integers; and
at the address
buf_addr, the lower oflen_usedorlen_allocbytes in Zwrite Format.
As this function is intended for debugging and provides details of internal structures, its output is likely to change as internal implementations change, even when stability of the external API is maintained.
Go BufferT DumpToWriter()
func (buft *BufferT) DumpToWriter(writer io.writer)
For debugging purposes, dump on writer:
buftas a hexadecimal address;for the
C.ydb_buffer_tstructure referenced bybuft:buf_addras a hexadecimal address, andlen_allocandlen_usedas integers; and
at the address
buf_addr, the lower oflen_usedorlen_allocbytes in Zwrite Format.
As this function is intended for debugging and provides details of internal structures, its output is likely to change as internal implementations change, even when stability of the external API is maintained.
Go BufferT Free()
func (buft *BufferT) Free()
The inverse of the Alloc() method: release the buffer in YottaDB heap space referenced by the C.ydb_buffer_t structure, release the C.ydb_buffer_t, and set buft in the BufferT structure to nil.
Go BufferT LenAlloc()
func (buft *BufferT) LenAlloc(tptoken uint64, errstr *BufferT) (uint32, error)
If the underlying structures have not yet been allocated, return the STRUCTUNALLOCD error.
Otherwise, return the
len_allocfield of theC.ydb_buffer_tstructure referenced bybuft.
Go BufferT LenUsed()
func (buft *BufferT) LenUsed(tptoken uint64, errstr *BufferT) (uint32, error)
If the underlying structures have not yet been allocated, return the STRUCTUNALLOCD error.
If the
len_usedfield of theC.ydb_buffer_tstructure is greater than itslen_allocfield (owing to a prior INVSTRLEN error), return an INVSTRLEN error and thelen_usedfield of theC.ydb_buffer_tstructure referenced bybuft.Otherwise, return the
len_usedfield of theC.ydb_buffer_tstructure referenced bybuft.
Go BufferT SetLenUsed()
func (buft *BufferT) SetLenUsed(tptoken uint64, errstr *BufferT, newLen uint32) error
Use this method to change the length of a used substring of the contents of the buffer referenced by the buf_addr field of the referenced C.ydb_buffer_t.
If the underlying structures have not yet been allocated, return the STRUCTUNALLOCD error.
If
newLenis greater than thelen_allocfield of the referencedC.ydb_buffer_t, make no changes and return with an error return of INVSTRLEN.Otherwise, set the
len_usedfield of the referencedC.ydb_buffer_ttonewLen.
Note that even if newLen is not greater than the value of len_alloc, setting a len_used value greater than the number of meaningful bytes in the buffer will likely lead to hard-to-debug errors.
Go BufferT SetValBAry()
func (buft *BufferT) SetValBAry(tptoken uint64, errstr *BufferT, value []byte) error
If the underlying structures have not yet been allocated, return the STRUCTUNALLOCD error.
If the length of
valueis greater than thelen_allocfield of theC.ydb_buffer_tstructure referenced bybuft, make no changes and return INVSTRLEN.Otherwise, copy the bytes of
valueto the referenced buffer and set thelen_usedfield to the length ofvalue.
Go BufferT SetValStr()
func (buft *BufferT) SetValStr(tptoken uint64, errstr *BufferT, value string) error
If the underlying structures have not yet been allocated, return the STRUCTUNALLOCD error.
If the length of
valueis greater than thelen_allocfield of theC.ydb_buffer_tstructure referenced bybuft, make no changes and return INVSTRLEN.Otherwise, copy the bytes of
valueto the referenced buffer and set thelen_usedfield to the length ofvalue.
Go BufferT Str2ZwrST()
func (buft *BufferT) Str2ZwrST(tptoken uint64, errstr *BufferT, zwr *BufferT) error
The method wraps ydb_str2zwr_s() / ydb_str2zwr_st() to provide the string in Zwrite Format.
If the underlying structures have not yet been allocated, return the STRUCTUNALLOCD error.
If
len_allocis not large enough, setlen_usedto the required length, and return an INVSTRLEN error. In this case,len_usedwill be greater thanlen_allocuntil corrected by application code, and the value referenced byzwris unspecified.Otherwise, set the buffer referenced by
buf_addrto the Zwrite Format string, and setlen_usedto the length.
Note that the length of a string in Zwrite Format is always greater than or equal to the string in its original, unencoded format.
Go BufferT ValBAry()
func (buft *BufferT) ValBAry(tptoken uint64, errstr *BufferT) ([]byte, error)
If the underlying structures have not yet been allocated, return the STRUCTUNALLOCD error.
If the
len_usedfield of theC.ydb_buffer_tstructure is greater than itslen_allocfield (owing to a prior INVSTRLEN error), return an INVSTRLEN error andlen_allocbytes as a byte array.Otherwise, return
len_usedbytes of the buffer as a byte array.
Go BufferT ValStr()
func (buft *BufferT) ValStr(tptoken uint64, errstr *BufferT) (string, error)
If the underlying structures have not yet been allocated, return the STRUCTUNALLOCD error.
If the
len_usedfield of theC.ydb_buffer_tstructure is greater than itslen_allocfield (owing to a prior INVSTRLEN error), return an INVSTRLEN error andlen_allocbytes as a string.Otherwise, return
len_usedbytes of the buffer as a string.
Go BufferT Zwr2StrST()
func (buft *BufferT) Zwr2StrST(tptoken uint64, errstr *BufferT, str *BufferT) error
This method wraps ydb_zwr2str_s() / ydb_zwr2str_st() and is the inverse of Go BufferT Str2ZwrST().
If the underlying structures have not yet been allocated, return the STRUCTUNALLOCD error.
If
len_allocis not large enough, setlen_usedto the required length, and return an INVSTRLEN error. In this case,len_usedwill be greater thanlen_allocuntil corrected by application code.If
strhas errors and is not in valid Zwrite Format, setlen_usedto zero, and return the error code returned by ydb_zwr2str_s() / ydb_zwr2str_st() e.g., INVZWRITECHAR.Otherwise, set the buffer referenced by
buf_addrto the unencoded string, setlen_usedto the length.
Go BufferTArray Alloc()
func (buftary *BufferTArray) Alloc(numSubs, bufSiz uint32)
Allocate an array of numSubs buffers in YottaDB heap space, each of of size bufSiz, referenced by the BufferTArray structure.
Go BufferTArray DeleteExclST()
func (buftary *BufferTArray) DeleteExclST(tptoken uint64, errstr *BufferT) error
DeleteExclST() wraps ydb_delete_excl_s() / ydb_delete_excl_st() to delete all local variable trees except those of local variables whose names are specified in the BufferTArray structure. In the special case where elemsUsed is zero, the method deletes all local variable trees.
In the event that the elemsUsed exceeds yottadb.YDB_MAX_NAMES, the error return is ERRNAMECOUNT2HI.
As mixing M and Go application code in the same process is now supported, make sure you understand what (sub)trees are being deleted when you use ydb_delete_excl_s() / ydb_delete_excl_st().
Go BufferTArray Dump()
func (buftary *BufferTArray) Dump()
For debugging purposes, dump on stdout:
buftaryas a hexadecimal address;elemsAllocandelemsUsedas integers;for each element of the smaller of
elemsAllocandelemsUsedelements of theC.ydb_buffer_tarray referenced bybuftary:buf_addras a hexadecimal address, andlen_allocandlen_usedas integers; andthe smaller of
len_usedandlen_allocbytes at the addressbuf_addr, in Zwrite Format.
As this function is intended for debugging and provides details of internal structures, its output is likely to change as internal implementations change, even when stability of the external API is maintained.
Go BufferTArray DumpToWriter()
func (buftary *BufferTArray) DumpToWriter(writer io.writer)
For debugging purposes, dump on writer:
buftaryas a hexadecimal address;elemsAllocandelemsUsedas integers;for each element of the smaller of
elemsAllocandelemsUsedelements of theC.ydb_buffer_tarray referenced bybuftary:buf_addras a hexadecimal address, andlen_allocandlen_usedas integers; andthe smaller of
len_usedandlen_allocbytes at the addressbuf_addr, in Zwrite Format.
As this function is intended for debugging and provides details of internal structures, its output is likely to change as internal implementations change, even when stability of the external API is maintained.
Go BufferTArray ElemAlloc()
func (buftary *BufferTArray) ElemAlloc() uint32
If the underlying structures have not yet been allocated, return the STRUCTUNALLOCD error.
Otherwise, return the number of allocated buffers.
Go BufferTArray ElemLenAlloc()
func (buftary *BufferTArray) ElemLenAlloc(tptoken uint64) uint32
If the underlying structures have not yet been allocated, return the STRUCTUNALLOCD error.
Otherwise, return the
len_allocfrom theC.ydb_buffer_tstructures referenced bybuftary, all of which have the same value.
Go BufferTArray ElemLenUsed()
func (buftary *BufferTArray) ElemLenUsed(tptoken uint64, errstr *BufferT, idx uint32) (uint32, error)
If the underlying structures have not yet been allocated, return the STRUCTUNALLOCD error.
If
idxis greater than or equal to theelemsAllocof theBufferTArraystructure, return with an error return of INSUFFSUBS.Otherwise, return the
len_usedfield of the array element specifed byidxof theC.ydb_buffer_tarray referenced bybuftary.
Go BufferTArray ElemUsed()
func (buftary *BufferTArray) ElemUsed() uint32
If the underlying structures have not yet been allocated, return the STRUCTUNALLOCD error.
Otherwise, return the value of the
elemsUsedfield.
Go BufferTArray Free()
func (buftary *BufferTArray) Free()
The inverse of the Alloc() method: release the numSubs buffers and the C.ydb_buffer_t array. Set buftary to nil, and elemsAlloc and elemsUsed to zero.
Go BufferTArray SetElemLenUsed()
func (buftary *BufferTArray) SetElemLenUsed(tptoken uint64, errstr *BufferT, idx, newLen uint32) error
Use this method to set the number of bytes in C.ydb_buffer_t structure referenced by buft of the array element specified by idx, for example to change the length of a used substring of the contents of the buffer referenced by the buf_addr field of the referenced C.ydb_buffer_t.
If the underlying structures have not yet been allocated, return the STRUCTUNALLOCD error.
If
idxis greater than or equal toelemsAlloc, make no changes and return an INSUFFSUBS error.If
newLenis greater than thelen_allocfield of the referencedC.ydb_buffer_t, make no changes and return an INVSTRLEN error.Otherwise, set the
len_usedfield of the referencedC.ydb_buffer_ttonewLen.
Note that even if newLen is not greater than the value of len_alloc, using a len_used value greater than the number of meaningful bytes in the buffer will likely lead to hard-to-debug errors.
Go BufferTArray SetElemUsed()
func (buftary *BufferTArray) SetElemUsed(tptoken uint64, errstr *BufferT, newUsed uint32) error
Use this method to set the current number of valid strings (subscripts or variable names) in the BufferTArray.
If the underlying structures have not yet been allocated, return the STRUCTUNALLOCD error.
If
newUsedis greater thanelemsAlloc, make no changes and return with an error return of INSUFFSUBS.Otherwise, set
elemsUsedtonewUsed.
Note that even if newUsed is not greater than the value of elemsAlloc, using an elemsUsed value greater than the number of valid values in the array will likely lead to hard-to-debug errors.
Go BufferTArray SetValBAry()
func (buftary *BufferTArray) SetValBAry(tptoken uint64, errstr *BufferT, idx uint32, value []byte) error
If the underlying structures have not yet been allocated, return the STRUCTUNALLOCD error.
If
idxis greater than or equal toelemsAllocmake no changes and return with an error return of INSUFFSUBS.If the length of
valueis greater than thelen_allocfield of the array element specified byidx, make no changes, and return INVSTRLEN.Otherwise, copy the bytes of
valueto the referenced buffer and set thelen_usedfield to the length ofvalue.
Go BufferTArray SetValStr()
func (buftary *BufferTArray) SetValStr(tptoken uint64, errstr *BufferT, idx uint32, value string) error
If the underlying structures have not yet been allocated, return the STRUCTUNALLOCD error.
If
idxis greater than or equal toelemsAllocmake no changes and return with an error return of INSUFFSUBS.If the length of
valueis greater than thelen_allocfield of the array element specified byidx, make no changes, and return INVSTRLEN.Otherwise, copy the bytes of
valueto the referenced buffer and set thelen_usedfield to the length ofvalue.
Go BufferTArray TpST()
func (buftary *BufferTArray) TpST(tptoken uint64, errstr *BufferT, tpfn func(uint64, *BufferT) int32, transid string) error
TpST() wraps ydb_tp_s() / ydb_tp_st() to implement Transaction Processing. tpfn is a function with two parameters, the first of which is a tptoken and the second is an errstr.
As an alternative to parameters for tpfn, create closures.
A function implementing logic for a transaction should return int32 with one of the following:
A normal return (
YDB_OK) to indicate that per application logic, the transaction can be committed. The YottaDB database engine will commit the transaction if it is able to, as discussed in Transaction Processing, and if not, will call the function again.YDB_TP_RESTARTto indicate that the transaction should restart, either because application logic has so determined or because a YottaDB function called by the function has returned TPRESTART.YDB_TP_ROLLBACKto indicate thatTpST()should not commit the transaction, and should return ROLLBACK to the caller.
The BufferTArray receiving the TpST() method is a list of local variables whose values should be saved, and restored to their original values when the transaction restarts. If the buftary structures have not been allocated or elemsUsed is zero, no local variables are saved and restored; and if elemsUsed is 1, and that sole element references the string "*" all local variables are saved and restored.
A case-insensitive value of "BA" or "BATCH" for transid indicates to YottaDB that it need not ensure Durability for this transaction (it continues to ensure Atomicity, Consistency, and Isolation), as discussed under ydb_tp_s() / ydb_tp_st().
Please see both the description of ydb_tp_s() / ydb_tp_st() and the sections on Transaction Processing and Threads and Transaction Processing for details.
Note
If the transaction logic receives a YDB_TP_RESTART or YDB_TP_ROLLBACK from a YottaDB function or method that it calls, it must return that value to the calling TpE() or TpST(). Failure to do so could result in application level data inconsistencies and hard to debug application code.
Go BufferTArray ValBAry()
func (buftary *BufferTArray) ValBAry(tptoken uint64, errstr *BufferT, idx uint32) ([]byte, error)
If the underlying structures have not yet been allocated, return the STRUCTUNALLOCD error.
If
idxis greater than or equal toelemsAlloc, return a zero length byte array and an error return of INSUFFSUBS.If the
len_usedfield of theC.ydb_buffer_tstructure specified byidxis greater than itslen_allocfield (owing to a previous INVSTRLEN error), return a byte array containing thelen_allocbytes atbuf_addrand an INVSTRLEN error.Otherwise, return a byte array containing the
len_usedbytes atbuf_addr.
Go BufferTArray ValStr()
func (buftary *BufferTArray) ValStr(tptoken uint64, errstr *BufferT, idx uint32) (string, error)
If the underlying structures have not yet been allocated, return the STRUCTUNALLOCD error.
If
idxis greater than or equal toelemsAlloc, return a zero length string and an error return of INSUFFSUBS.If the
len_usedfield of theC.ydb_buffer_tstructure specified byidxis greater than itslen_allocfield (owing to a previous INVSTRLEN error), return a string containing thelen_allocbytes atbuf_addrand the INVSTRLEN error.Otherwise, return a string containing the
len_usedbytes atbuf_addr.
Go CallMDesc CallMDescT()
func (mdesc *CallMDesc) CallMDescT(tptoken uint64, errstr *BufferT, retvallen uint32, rtnargs ...interface{}) (string, error)
As a wrapper for the C function ydb_cip_t(), the CallMDescT() is a method of the CallMDesc (call descriptor) structure which, during the first call, saves information in the CallMDesc structure that makes all following calls using the same descriptor structure able to run much faster by bypassing a lookup of the function name and going straight to the M routine being called.
CallMDescT()requires aCallMDescstructure to have been created and initialized with theSetRtnName()method.
Example:
var mrtn yottadb.CallMDesc
fmt.Println("Golang: Invoking HelloWorld")
mrtn.SetRtnName("HelloWorld")
retval, err := mrtn.CallMDescT(yottadb.NOTTP, nil, 1024, "English", "USA")
if nil != err { panic(fmt.Sprintf("CallMDescT() call failed: %s", err)) }
fmt.Println("Golang: retval =", retval)
Go CallMDesc Free()
func (mdesc *CallMDesc) Free()
Frees a CallMDesc structure previously allocated.
Go CallMDesc SetRtnName()
func (mdesc *CallMDesc) SetRtnName(rtnname string)
Allocates and initializes a structure to cache information to accelarate Go to M calls made by Go CallMDesc CallMDescT(). rtnname is a <c-call-name> in a Call-in table. The structure can then be used by the Go CallMDesc CallMDescT() method to call an M function. YottaDB looks for rtnname in the current call-in table.
Go CallMTable CallMTableOpenT()
func CallMTableOpenT(tptoken uint64, errstr *BufferT, tablename string) (*CallMTable, error)
If the environment variable ydb_ci does not specify an M code call-in table at process startup, function CallMTableOpenT() can be used to open an initial call-in table. tablename is the filename of a call-in table, and the function opens the file and initializes a CallMTable structure. Processes use the zroutines intrinsic special variable intrinsic special variable to locate M routines to execute, and $zroutines is initialized at process startup from the ydb_routines environment variable.
Go CallMTable CallMTableSwitchT()
func (newcmtable *CallMTable) CallMTableSwitchT(tptoken uint64, errstr *BufferT) (*CallMTable, error)
Method CallMTableSwitchT() enables switching of call-in tables. newcmtable is the new call-in table to be used, which should have previously been opened with Go CallMTable CallMTableOpenT(). *CallMTable returns the previously open call-in table, *nil if there was none. As switching the call-in table does not change $zroutines, if the new call-in table requires a different M routine search path, code will need to change $zroutines appropriately.
Go KeyT Alloc()
func (key *KeyT) Alloc(varSiz, numSubs, subSiz uint32)
Invoke Varnm.Alloc(varSiz) (see Go BufferT Alloc()) and SubAry.Alloc(numSubs, subSiz) (see Go BufferTArray Alloc()).
Go KeyT DataST()
func (key *KeyT) DataST(tptoken uint64, errstr *BufferT) (uint32, error)
Matching Go DataE(), DataST() returns the result of ydb_data_s() / ydb_data_st() (0, 1, 10, or 11). In the event of an error, the return value is unspecified.
Go KeyT DeleteST()
func (key *KeyT) DeleteST(tptoken uint64, errstr *BufferT, deltype int) error
Matching Go DeleteE(), DeleteST() wraps ydb_delete_s() / ydb_delete_st() to delete a local or global variable node or (sub)tree, with a value of yottadb.YDB_DEL_NODE for deltype specifying that only the node should be deleted, leaving the (sub)tree untouched, and a value of yottadb.YDB_DEL_TREE specifying that the node as well as the (sub)tree are to be deleted.
Go KeyT Dump()
func (key *KeyT) Dump()
Invoke Varnm.Dump() (see Go BufferT Dump()) and SubAry.Dump() (see Go BufferTArray Dump()).
Go KeyT DumpToWriter()
func (key *KeyT) DumpToWriter(writer io.writer)
Invoke Varnm.Dump() (see Go BufferT Dump()) and SubAry.Dump() (see Go BufferTArray Dump()), sending the output to writer.
Go KeyT Free()
func (key *KeyT) Free()
Invoke Varnm.Free() (see Go BufferT Free()) and SubAry.Free() (see Go BufferTArray Free()).
Go KeyT IncrST()
func (key *KeyT) IncrST(tptoken uint64, errstr *BufferT, incr, retval *BufferT) error
Matching Go IncrE(), IncrST() wraps ydb_incr_s() / ydb_incr_st() to atomically increment the referenced global or local variable node coerced to a number, with incr coerced to a number. It stores the result in the node and also returns it through the BufferT structure referenced by retval.
If ydb_incr_s() / ydb_incr_st() returns an error such as NUMOFLOW, the method makes no changes to the structures under
retvaland returns the error.If the length of the data to be returned exceeds
retval.lenAlloc, the method sets thelen_usedof theC.ydb_buffer_treferenced byretvalto the required length, and returns an INVSTRLEN error. The value referenced byretvalis unspecified.Otherwise, it copies the data to the buffer referenced by the
retval.buf_addr, setsretval.lenUsedto its length.
With a nil value for incr, the default increment is 1. Note that the value of the empty string coerced to an integer is zero, but 1 is a more useful default value for an omitted parameter in this case.
Go KeyT LockDecrST()
func (key *KeyT) LockDecrS(tptoken uint64, errstr *BufferT) error
Matching Go LockDecrE() LockDecrST() wraps ydb_lock_decr_s() / ydb_lock_decr_st() to decrement the count of the lock name referenced, releasing it if the count goes to zero or ignoring the invocation if the process does not hold the lock.
Go KeyT LockIncrST()
func (key *KeyT) LockIncrST(tptoken uint64, errstr *BufferT, timeoutNsec uint64) error
Matching Go LockIncrE(), LockIncrST() wraps ydb_lock_incr_s() / ydb_lock_incr_st() to attempt to acquire the referenced lock resource name without releasing any locks the process already holds.
If the process already holds the named lock resource, the method increments its count and returns.
If
timeoutNsecexceedsyottadb.YDB_MAX_TIME_NSEC, the method returns with an error return TIME2LONG.If it is able to aquire the lock resource within
timeoutNsecnanoseconds, it returns holding the lock, otherwise it returns LOCK_TIMEOUT. IftimeoutNsecis zero, the method makes exactly one attempt to acquire the lock.
Go KeyT NodeNextST()
func (key *KeyT) NodeNextST(tptoken uint64, errstr *BufferT, next *BufferTArray) error
Matching Go NodeNextE(), NodeNextST() wraps ydb_node_next_s() / ydb_node_next_st() to facilitate traversal of a local or global variable tree. A node or subtree does not have to exist at the specified key.
If there is a subsequent node:
If the number of subscripts of that next node exceeds
next.elemsAlloc, the method setsnext.elemsUsedto the number of subscripts required, and returns an INSUFFSUBS error. In this case theelemsUsedis greater thanelemsAlloc.If one of the
C.ydb_buffer_tstructures referenced bynext(call the first or only elementn) has insufficient space for the corresponding subscript, the method setsnext.elemsUsedton, and thelen_allocof thatC.ydb_buffer_tstructure to the actual space required. The method returns an INVSTRLEN error. In this case thelen_usedof that structure is greater than itslen_alloc.Otherwise, it sets the structure
nextto reference the subscripts of that next node, andnext.elemsUsedto the number of subscripts.
If there is no subsequent node, the method returns the NODEEND error (
yottadb.YDB_ERR_NODEEND), making no changes to the structures belownext.
Go KeyT NodePrevST()
func (key *KeyT) NodePrevST(tptoken uint64, errstr *BufferT, prev *BufferTArray) error
Matching Go NodePrevE(), NodePrevST() wraps ydb_node_previous_s() / ydb_node_previous_st() to facilitate reverse traversal of a local or global variable tree. A node or subtree does not have to exist at the specified key.
If there is a previous node:
If the number of subscripts of that previous node exceeds
prev.elemsAlloc, the method setsprev.elemsUsedto the number of subscripts required, and returns an INSUFFSUBS error. In this case theelemsUsedis greater thanelemsAlloc.If one of the
C.ydb_buffer_tstructures referenced byprev(call the first or only elementn) has insufficient space for the corresponding subscript, the method setsprev.elemsUsedton, and thelen_allocof thatC.ydb_buffer_tstructure to the actual space required. The method returns an INVSTRLEN error. In this case thelen_usedof that structure is greater than itslen_alloc.Otherwise, it sets the structure
prevto reference the subscripts of that prev node, andprev.elemsUsedto the number of subscripts.
If there is no previous node, the method returns the NODEEND error making no changes to the structures below
prev.
Go KeyT SetValST()
func (key *KeyT) SetValST(tptoken uint64, errstr *BufferT, value *BufferT) error
Matching Go SetValE(), at the referenced local or global variable node, or the intrinsic special variable, SetValST() wraps ydb_set_s() / ydb_set_st() to set the value specified by value.
Go KeyT SubNextST()
func (key *KeyT) SubNextST(tptoken uint64, errstr *BufferT, retval *BufferT) error
Matching Go SubNextE(), SubNextST() wraps ydb_subscript_next_s() / ydb_subscript_next_st() to facilitate traversal of a local or global variable sub-tree. A node or subtree does not have to exist at the specified key.
At the level of the last subscript, if there is a next subscript with a node and/or a subtree:
If the length of that next subscript exceeds
sub.len_alloc, the method setssub.len_usedto the actual length of that subscript, and returns an INVSTRLEN error. In this casesub.len_usedis greater thansub.len_alloc.Otherwise, it copies that subscript to the buffer referenced by
sub.buf_addr, and setssub.len_usedto its length.
If there is no next node or subtree at that level of the subtree, the method returns the NODEEND error.
Go KeyT SubPrevST()
func (key *KeyT) SubPrevST(tptoken uint64, errstr *BufferT, retval *BufferT) error
Matching Go SubPrevE(), SubPrevST() wraps ydb_subscript_previous_s() / ydb_subscript_previous_st() to facilitate reverse traversal of a local or global variable sub-tree. A node or subtree does not have to exist at the specified key.
At the level of the last subscript, if there is a previous subscript with a node and/or a subtree:
If the length of that previous subscript exceeds
sub.len_alloc, the method setssub.len_usedto the actual length of that subscript, and returns an INVSTRLEN error. In this casesub.len_usedis greater thansub.len_alloc.Otherwise, it copies that subscript to the buffer referenced by
sub.buf_addr, and setsbuf.len_usedto its length.
If there is no previous node or subtree at that level of the subtree, the method returns the NODEEND error.
Go KeyT ValST()
func (key *KeyT) ValST(tptoken uint64, errstr *BufferT, retval *BufferT) error
Matching Go ValE(), ValST() wraps ydb_get_s() / ydb_get_st() to return the value at the referenced global or local variable node, or intrinsic special variable, in the buffer referenced by retval.
If ydb_get_s() / ydb_get_st() returns an error such as GVUNDEF, INVSVN, LVUNDEF, the method makes no changes to the structures under
retvaland returns the error.If the length of the data to be returned exceeds
retval.GetLenAlloc(), the method sets thelen_usedof theC.ydb_buffer_treferenced byretvalto the required length, and returns an INVSTRLEN error.Otherwise, it copies the data to the buffer referenced by the
retval.buf_addr, and setsretval.lenUsedto the length of the returned value.
Go Error()
func (err *YDBError) Error() string
Error() is a method to return the expected error message string.
Go Programming Notes
These Go Programming Notes supplement rather than supplant the more general Programming Notes (Avoiding Common Pitfalls) for C.
Goroutines
In order to avoid restricting Go applications to calling the single-threaded YottaDB engine from a single goroutine (which would be unnatural to a Go programmer), the YottaDB Go wrapper calls the functions of the C Simple API that support multi-threaded applications, and includes logic to maintain the integrity of the engine.
Directly calling YottaDB C API functions bypasses this protection, and may result in unpredictable results (Murphy says that unpredictable results will occur when you least expect them). Therefore, Go application code should only call the YottaDB API exposed in this Programming in Go section.
Goroutines in a process are dynamically mapped by the Go implementation to run on threads within that process. Since YottaDB resources are held by the process rather than by the thread or the Goroutine, refer to the Threads discussion about the need for applications to avoid race conditions when accessing YottaDB resources.
Go Using Signals
As discussed in Signals, the YottaDB runtime system uses signals. When the Go wrapper is in use, the YottaDB runtime system receives signals from the Go runtime system through the Go wrapper, instead of directly from the operating system. The signals for which the wrapper registers handlers with the Go runtime system are the following (fatal signals marked with '*'):
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
In addition, the following two signals have the same signal numbers as other handled signals:
syscall.SIGIO is a duplicate of syscall.SIGURG.
syscall.SIGIOT is a duplicate of syscall.SIGABRT.
We recommend against use of signal.Notify() by applications that need to be notified of signal receipt especially for fatal signals. If application logic uses signal.Notify() to be so notified, both it and YottaDB are notified concurrently about the signal. The YottaDB signal handler will cleanly shut the YottaDB engine and terminate the process with a panic(), potentially before the application signal handling logic finishes. Conversely, if the application signal handler finishes its logic and terminates the process with a panic() that may leave the YottaDB database in an unclean state, potentially with damage to its internal structures.
Instead, the YottaDB Go wrapper provides a notification facility (yottadb.RegisterSignalNotify()) for coordination between the YottaDB signal handler and that of the application code. An application can control both when, and whether, the YottaDB signal handler executes, in relation to the application signal handler. There are two functions:
func RegisterSignalNotify(sig syscall.Signal, notifyChan, ackChan chan bool, notifyWhen YDBHandlerFlag) error
func UnRegisterSignalNotify(sig syscall.Signal) error
The parameters are:
sig- the signal being registered or unregistered.notifyChan- the channel to which YottaDB posts, to notify application code of receipt of the signal.ackChan- the channel to which the application code posts, to notify YottaDB that it has completed its work.notifyWhen- specifies when or if the YottaDB signal hander runs, with respect to the application signal handler. Choices are:yottadb.NotifyBeforeYDBSigHandler- YottaDB notifies the application handler BEFORE the YottaDB signal handler runs. YottaDB expects the application to post toackChanwhen it completes, after which the YottaDB signal handler runs.yottadb.NotifyAfterYDBSigHandler- YottaDB notifies the application handler AFTER the YottaDB signal handler runs. When it completes, YottaDB posts tonotifyChan, and waits for the application to post toackChan, indicating that YottaDB can continue. Note since YDB processing for fatal signals issues apanic(),yottadb.NotifyAfterYDBSigHandleris unsuitable for fatal signals.yottadb.NotifyAsyncYDBSigHandler- YottaDB notifies the application signal handler and runs its signal handler concurrently. For practical purposes, this is equivalent to application code usingsignal.Notify().ackChanis ignored in this case.yottadb.NotifyInsteadOfYDBSigHandler- The application handler is notified but the YottaDB handler is not run. This parameter value should never be used for SIGALRM since YottaDB requires that signal internally for correct database operation. For other signals, we recommend against use of this parameter value unless you either (a) know what you are doing, or (b) are following a recommendation made by YottaDB for your specific situtation.
Note that input/output flow control signals (SIGTSTP, SIGTTIN, SIGTTOU) are excluded from this support as the YottaDB runtime performs no terminal IO. Applications that include logic coded in multiple languages (e.g., Go and C) require careful design of IO flow control signal handling whether or not the YottaDB runtime is active.
YottaDB has a strong need to carefully shutdown databases at process end making sure its buffers are all flushed out, releasing held M locks, etc. To satisfy this requirement, use the following safe programming practices:
Always put a
defer yottadb.Exit()in the main program before invoking YottaDB, which allows YottaDB to shutdown correctly. This takes care of the normal exit case where goroutines terminate and control reverts to the main program, which eventually terminates when it exits.Avoid application exits that use "out-of-band" exits (e.g.
os.Exit()). This bypasses orderly rundown of all goroutines which we have found to be important for a clean shutdown.Ensure that all goroutines that have called YottaDB have completed and shutdown terminating the application process. Failure to do so may cause YottaDB rundown procedures to be cut short or bypassed, resulting in potential database damage.
Do not use
signal.Notify()for any fatal signal. Instead useyottadb.RegisterSignalNotify().Do not use SIGUSR2 which YottaDB uses internally, and will replace any application handler for SIGUSR2 with its own.
Note
This discussion applies only to asynchronous signals as defined by Go, i.e., signals that are sent to the process by itself, e.g., with syscall.Kill(). It does not apply to synchronous signals that are signals that are raised by the hardware itself or by the OS as a consequence of executing code, i.e., SIGBUS, SIGFPE, SIGILL and SIGSEGV. A process that receives a synchronous signal will terminate without shutting down the database properly. Should such an event occur, you should verify database integrity when convenient (see MUPIP INTEG), and take appropriate action if damage is encountered (see MUPIP JOURNAL RECOVER BACKWARD / MUPIP JOURNAL ROLLBACK BACKWARD).
Database Integrity
When a process terminates, the YottaDB runtime system drives an exit handler to assure that all databases are rundown, and that proper cleanup of storage is completed. YottaDB does this by using an atexit() handler that it defines during initialization. However, this is unreliable with Go. Therefore, our strong recommendation is to add a defer yottadb.Exit() statement to the Go main routine. When the main routine exits, this drives the exit handler to do database rundown and clean up.
The above works well in application processes that terminate normally through the main routine. However, when a process terminates abnormally, e.g., with a fatal error, the exit handler is not always driven, and database cleanup does not always occur. For this reason, we also strongly recommend a MUPIP RUNDOWN of an application database after the last process that has it open terminates, especially if it is not assured that all processes terminated normally. If all processes terminate normally, this is not required.
Note
Fatal error handlers in the YottaDB runtime system use a panic() call to unwind the process through the main program, which calls yottadb.Exit(). While this does not guarantee that yottadb.Exit() is called in the highly concurrent Go runtime environment, it makes that call likely.