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, and KeyT which 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.13. 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), download the Go wrapper, install it and test it.

$ go mod init myprogram
$ go get lang.yottadb.com/go/yottadb
$ go build lang.yottadb.com/go/yottadb
$ source /usr/local/etc/ydb_env_set
$ go get -t lang.yottadb.com/go/yottadb
$ chmod +w  ~/go/pkg/mod/lang.yottadb.com/go/yottadb\@v*/m_routines/
$ go test lang.yottadb.com/go/yottadb
ok      lang.yottadb.com/go/yottadb     0.194s
$

There are a number of programs in the go/pkg/mod/lang.yottadb.com/go/yottadb@v* directory that you can look at.

  1. Put your GO program in a directory of your choice, e.g., $ydb_dir directory and change to that directory. As a sample program, you can download the wordfreq.go program, with a reference input file and corresponding reference output file. Compile it thus: go build wordfreq.go.

  2. Run your program and verify that the output matches the reference output. For example:

$ cd $ydb_dir
$ wget https://gitlab.com/YottaDB/DB/YDBTest/raw/master/go/inref/wordfreq.go
$ go build wordfreq.go
$ 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
$ ./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). Whereas, 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.

  • DefaultMaximumNormalExitWait is initial value of MaximumNormalExitWait, with a default of 60 seconds.

  • DefaultMaximumPanicExitWait is the initial value of MaximumPanicExitWait, with a default of 3 seconds.

  • DefaultMaximumSigShutDownWait is initial value of MaximumSigShutDownWait, with a default of 5 seconds.

  • DefaultMaximumSigAckWait is initial value of MaximumSigAckWait, 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:

  • BufferT for data;

  • BufferTArray for a list of subscripts or a set of variable names;

  • KeyT for keys where a key in turn consists of a variable or lock resource name and subscripts, as discussed in Concepts; and

  • CallMDesc references an M routine and caches information to accelerate calls from Go to M.

  • CallMTable to 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.

  • retvallen needs 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.

  • rtnargs refers 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 by CallMT(). 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

For a process that wishes to close YottaDB databases and no longer use YottaDB, the function wraps ydb_exit(). If ydb-exit-fn does not send a return value of YDB_OK, Exit() panics.

Although in theory typical processes should not need to call Exit() because normal process termination should close databases cleanly, in practice, thread shutdown may not always ensure that databases are closed cleanly, especially since the C atexit() functionality does not reliably work in Go's multi-threaded environment. Application code should invoke Exit() prior to process exit, or when an application intends to continue with other work beyond use of YottaDB, to ensure that databases are closed cleanly. To accomplish this, you should use a "defer yottadb.Exit()" statement early in the main routine's initialization.

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 timeoutNsec exceeds yottadb.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 namesnsubs is not a series of alternating string and []string parameters, the function returns the INVLKNMPAIRLIST error.

  • If it is able to aquire the lock resource(s) within timeoutNsec nanoseconds, the function returns holding the lock resource(s); otherwise it returns LOCKTIMEOUT. If timeoutNsec is 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 timeoutNsec exceeds yottadb.YDB_MAX_TIME_NSEC, the function returns with an error return TIME2LONG.

  • If it is able to aquire the lock resource within timeoutNsec nanoseconds, it returns holding the lock, otherwise it returns LOCKTIMEOUT. If timeoutNsec is 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 timeoutNsec exceeds yottadb.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 timeoutNsec nanoseconds, it returns holding the lock resource(s); otherwise it returns LOCKTIMEOUT. If timeoutNsec is 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 status does 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:

  • buft as a hexadecimal address;

  • for the C.ydb_buffer_t structure referenced by buft:

    • buf_addr as a hexadecimal address, and

    • len_alloc and len_used as integers; and

  • at the address buf_addr, the lower of len_used or len_alloc bytes 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:

  • buft as a hexadecimal address;

  • for the C.ydb_buffer_t structure referenced by buft:

    • buf_addr as a hexadecimal address, and

    • len_alloc and len_used as integers; and

  • at the address buf_addr, the lower of len_used or len_alloc bytes 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_alloc field of the C.ydb_buffer_t structure referenced by buft.

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_used field of the C.ydb_buffer_t structure is greater than its len_alloc field (owing to a prior INVSTRLEN error), return an INVSTRLEN error and the len_used field of the C.ydb_buffer_t structure referenced by buft.

  • Otherwise, return the len_used field of the C.ydb_buffer_t structure referenced by buft.

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 newLen is greater than the len_alloc field of the referenced C.ydb_buffer_t, make no changes and return with an error return of INVSTRLEN.

  • Otherwise, set the len_used field of the referenced C.ydb_buffer_t to newLen.

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 value is greater than the len_alloc field of the C.ydb_buffer_t structure referenced by buft, make no changes and return INVSTRLEN.

  • Otherwise, copy the bytes of value to the referenced buffer and set the len_used field to the length of value.

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 value is greater than the len_alloc field of the C.ydb_buffer_t structure referenced by buft, make no changes and return INVSTRLEN.

  • Otherwise, copy the bytes of value to the referenced buffer and set the len_used field to the length of value.

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_alloc is not large enough, set len_used to the required length, and return an INVSTRLEN error. In this case, len_used will be greater than len_alloc until corrected by application code, and the value referenced by zwr is unspecified.

  • Otherwise, set the buffer referenced by buf_addr to the Zwrite Format string, and set len_used to 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_used field of the C.ydb_buffer_t structure is greater than its len_alloc field (owing to a prior INVSTRLEN error), return an INVSTRLEN error and len_alloc bytes as a byte array.

  • Otherwise, return len_used bytes 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_used field of the C.ydb_buffer_t structure is greater than its len_alloc field (owing to a prior INVSTRLEN error), return an INVSTRLEN error and len_alloc bytes as a string.

  • Otherwise, return len_used bytes 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_alloc is not large enough, set len_used to the required length, and return an INVSTRLEN error. In this case, len_used will be greater than len_alloc until corrected by application code.

  • If str has errors and is not in valid Zwrite Format, set len_used to zero, and return the error code returned by ydb_zwr2str_s() / ydb_zwr2str_st() e.g., INVZWRITECHAR.

  • Otherwise, set the buffer referenced by buf_addr to the unencoded string, set len_used to 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:

  • buftary as a hexadecimal address;

  • elemsAlloc and elemsUsed as integers;

  • for each element of the smaller of elemsAlloc and elemsUsed elements of the C.ydb_buffer_t array referenced by buftary:

    • buf_addr as a hexadecimal address, and

    • len_alloc and len_used as integers; and

    • the smaller of len_used and len_alloc bytes at the address buf_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:

  • buftary as a hexadecimal address;

  • elemsAlloc and elemsUsed as integers;

  • for each element of the smaller of elemsAlloc and elemsUsed elements of the C.ydb_buffer_t array referenced by buftary:

    • buf_addr as a hexadecimal address, and

    • len_alloc and len_used as integers; and

    • the smaller of len_used and len_alloc bytes at the address buf_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_alloc from the C.ydb_buffer_t structures referenced by buftary, 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 idx is greater than or equal to the elemsAlloc of the BufferTArray structure, return with an error return of INSUFFSUBS.

  • Otherwise, return the len_used field of the array element specifed by idx of the C.ydb_buffer_t array referenced by buftary.

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 elemsUsed field.

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 idx is greater than or equal to elemsAlloc, make no changes and return an INSUFFSUBS error.

  • If newLen is greater than the len_alloc field of the referenced C.ydb_buffer_t, make no changes and return an INVSTRLEN error.

  • Otherwise, set the len_used field of the referenced C.ydb_buffer_t to newLen.

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 newUsed is greater than elemsAlloc, make no changes and return with an error return of INSUFFSUBS.

  • Otherwise, set elemsUsed to newUsed.

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 idx is greater than or equal to elemsAlloc make no changes and return with an error return of INSUFFSUBS.

  • If the length of value is greater than the len_alloc field of the array element specified by idx, make no changes, and return INVSTRLEN.

  • Otherwise, copy the bytes of value to the referenced buffer and set the len_used field to the length of value.

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 idx is greater than or equal to elemsAlloc make no changes and return with an error return of INSUFFSUBS.

  • If the length of value is greater than the len_alloc field of the array element specified by idx, make no changes, and return INVSTRLEN.

  • Otherwise, copy the bytes of value to the referenced buffer and set the len_used field to the length of value.

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_RESTART to 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_ROLLBACK to indicate that TpST() 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 idx is greater than or equal to elemsAlloc, return a zero length byte array and an error return of INSUFFSUBS.

  • If the len_used field of the C.ydb_buffer_t structure specified by idx is greater than its len_alloc field (owing to a previous INVSTRLEN error), return a byte array containing the len_alloc bytes at buf_addr and an INVSTRLEN error.

  • Otherwise, return a byte array containing the len_used bytes at buf_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 idx is greater than or equal to elemsAlloc, return a zero length string and an error return of INSUFFSUBS.

  • If the len_used field of the C.ydb_buffer_t structure specified by idx is greater than its len_alloc field (owing to a previous INVSTRLEN error), return a string containing the len_alloc bytes at buf_addr and the INVSTRLEN error.

  • Otherwise, return a string containing the len_used bytes at buf_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 a CallMDesc structure to have been created and initialized with the SetRtnName() 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 retval and returns the error.

  • If the length of the data to be returned exceeds retval.lenAlloc, the method sets the len_used of the C.ydb_buffer_t referenced by retval to the required length, and returns an INVSTRLEN error. The value referenced by retval is unspecified.

  • Otherwise, it copies the data to the buffer referenced by the retval.buf_addr, sets retval.lenUsed to 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 timeoutNsec exceeds yottadb.YDB_MAX_TIME_NSEC, the method returns with an error return TIME2LONG.

  • If it is able to aquire the lock resource within timeoutNsec nanoseconds, it returns holding the lock, otherwise it returns LOCK_TIMEOUT. If timeoutNsec is 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 sets next.elemsUsed to the number of subscripts required, and returns an INSUFFSUBS error. In this case the elemsUsed is greater than elemsAlloc.

    • If one of the C.ydb_buffer_t structures referenced by next (call the first or only element n) has insufficient space for the corresponding subscript, the method sets next.elemsUsed to n, and the len_alloc of that C.ydb_buffer_t structure to the actual space required. The method returns an INVSTRLEN error. In this case the len_used of that structure is greater than its len_alloc.

    • Otherwise, it sets the structure next to reference the subscripts of that next node, and next.elemsUsed to 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 below next.

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 sets prev.elemsUsed to the number of subscripts required, and returns an INSUFFSUBS error. In this case the elemsUsed is greater than elemsAlloc.

    • If one of the C.ydb_buffer_t structures referenced by prev (call the first or only element n) has insufficient space for the corresponding subscript, the method sets prev.elemsUsed to n, and the len_alloc of that C.ydb_buffer_t structure to the actual space required. The method returns an INVSTRLEN error. In this case the len_used of that structure is greater than its len_alloc.

    • Otherwise, it sets the structure prev to reference the subscripts of that prev node, and prev.elemsUsed to 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 sets sub.len_used to the actual length of that subscript, and returns an INVSTRLEN error. In this case sub.len_used is greater than sub.len_alloc.

    • Otherwise, it copies that subscript to the buffer referenced by sub.buf_addr, and sets sub.len_used to 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 sets sub.len_used to the actual length of that subscript, and returns an INVSTRLEN error. In this case sub.len_used is greater than sub.len_alloc.

    • Otherwise, it copies that subscript to the buffer referenced by sub.buf_addr, and sets buf.len_used to 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 the BufferT structure 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 retval and returns the error.

  • If the length of the data to be returned exceeds retval.GetLenAlloc(), the method sets the len_used of the C.ydb_buffer_t referenced by retval to the required length, and returns an INVSTRLEN error.

  • Otherwise, it copies the data to the buffer referenced by the retval.buf_addr, and sets retval.lenUsed to 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 to ackChan when 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 to notifyChan, and waits for the application to post to ackChan, indicating that YottaDB can continue. Note since YDB processing for fatal signals issues a panic(), yottadb.NotifyAfterYDBSigHandler is 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 using signal.Notify(). ackChan is 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:

  1. 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.

  2. 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.

  3. 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.

  4. Do not use signal.Notify() for any fatal signal. Instead use yottadb.RegisterSignalNotify().

  5. 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.