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
, andKeyT
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.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). 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 ofMaximumNormalExitWait
, with a default of 60 seconds.DefaultMaximumPanicExitWait
is the initial value ofMaximumPanicExitWait
, with a default of 3 seconds.DefaultMaximumSigShutDownWait
is initial value ofMaximumSigShutDownWait
, with a default of 5 seconds.DefaultMaximumSigAckWait
is 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:
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; andCallMDesc
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 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
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
exceedsyottadb.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 alternatingstring
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. IftimeoutNsec
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
exceedsyottadb.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. IftimeoutNsec
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
exceedsyottadb.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. IftimeoutNsec
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 bybuft
:buf_addr
as a hexadecimal address, andlen_alloc
andlen_used
as integers; and
at the address
buf_addr
, the lower oflen_used
orlen_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 bybuft
:buf_addr
as a hexadecimal address, andlen_alloc
andlen_used
as integers; and
at the address
buf_addr
, the lower oflen_used
orlen_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 theC.ydb_buffer_t
structure 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_used
field of theC.ydb_buffer_t
structure is greater than itslen_alloc
field (owing to a prior INVSTRLEN error), return an INVSTRLEN error and thelen_used
field of theC.ydb_buffer_t
structure referenced bybuft
.Otherwise, return the
len_used
field of theC.ydb_buffer_t
structure 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
newLen
is greater than thelen_alloc
field of the referencedC.ydb_buffer_t
, make no changes and return with an error return of INVSTRLEN.Otherwise, set the
len_used
field of the referencedC.ydb_buffer_t
tonewLen
.
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 thelen_alloc
field of theC.ydb_buffer_t
structure referenced bybuft
, make no changes and return INVSTRLEN.Otherwise, copy the bytes of
value
to the referenced buffer and set thelen_used
field 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
value
is greater than thelen_alloc
field of theC.ydb_buffer_t
structure referenced bybuft
, make no changes and return INVSTRLEN.Otherwise, copy the bytes of
value
to the referenced buffer and set thelen_used
field 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_alloc
is not large enough, setlen_used
to the required length, and return an INVSTRLEN error. In this case,len_used
will be greater thanlen_alloc
until corrected by application code, and the value referenced byzwr
is unspecified.Otherwise, set the buffer referenced by
buf_addr
to the Zwrite Format string, and setlen_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 theC.ydb_buffer_t
structure is greater than itslen_alloc
field (owing to a prior INVSTRLEN error), return an INVSTRLEN error andlen_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 theC.ydb_buffer_t
structure is greater than itslen_alloc
field (owing to a prior INVSTRLEN error), return an INVSTRLEN error andlen_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, setlen_used
to the required length, and return an INVSTRLEN error. In this case,len_used
will be greater thanlen_alloc
until corrected by application code.If
str
has errors and is not in valid Zwrite Format, setlen_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, setlen_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
andelemsUsed
as integers;for each element of the smaller of
elemsAlloc
andelemsUsed
elements of theC.ydb_buffer_t
array referenced bybuftary
:buf_addr
as a hexadecimal address, andlen_alloc
andlen_used
as integers; andthe smaller of
len_used
andlen_alloc
bytes 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
:
buftary
as a hexadecimal address;elemsAlloc
andelemsUsed
as integers;for each element of the smaller of
elemsAlloc
andelemsUsed
elements of theC.ydb_buffer_t
array referenced bybuftary
:buf_addr
as a hexadecimal address, andlen_alloc
andlen_used
as integers; andthe smaller of
len_used
andlen_alloc
bytes 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_alloc
from theC.ydb_buffer_t
structures 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
idx
is greater than or equal to theelemsAlloc
of theBufferTArray
structure, return with an error return of INSUFFSUBS.Otherwise, return the
len_used
field of the array element specifed byidx
of theC.ydb_buffer_t
array 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
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 toelemsAlloc
, make no changes and return an INSUFFSUBS error.If
newLen
is greater than thelen_alloc
field of the referencedC.ydb_buffer_t
, make no changes and return an INVSTRLEN error.Otherwise, set the
len_used
field of the referencedC.ydb_buffer_t
tonewLen
.
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 thanelemsAlloc
, make no changes and return with an error return of INSUFFSUBS.Otherwise, set
elemsUsed
tonewUsed
.
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 toelemsAlloc
make no changes and return with an error return of INSUFFSUBS.If the length of
value
is greater than thelen_alloc
field of the array element specified byidx
, make no changes, and return INVSTRLEN.Otherwise, copy the bytes of
value
to the referenced buffer and set thelen_used
field 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
idx
is greater than or equal toelemsAlloc
make no changes and return with an error return of INSUFFSUBS.If the length of
value
is greater than thelen_alloc
field of the array element specified byidx
, make no changes, and return INVSTRLEN.Otherwise, copy the bytes of
value
to the referenced buffer and set thelen_used
field 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_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 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
idx
is greater than or equal toelemsAlloc
, return a zero length byte array and an error return of INSUFFSUBS.If the
len_used
field of theC.ydb_buffer_t
structure specified byidx
is greater than itslen_alloc
field (owing to a previous INVSTRLEN error), return a byte array containing thelen_alloc
bytes atbuf_addr
and an INVSTRLEN error.Otherwise, return a byte array containing the
len_used
bytes 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
idx
is greater than or equal toelemsAlloc
, return a zero length string and an error return of INSUFFSUBS.If the
len_used
field of theC.ydb_buffer_t
structure specified byidx
is greater than itslen_alloc
field (owing to a previous INVSTRLEN error), return a string containing thelen_alloc
bytes atbuf_addr
and the INVSTRLEN error.Otherwise, return a string containing the
len_used
bytes 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 aCallMDesc
structure 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
retval
and returns the error.If the length of the data to be returned exceeds
retval.lenAlloc
, the method sets thelen_used
of theC.ydb_buffer_t
referenced byretval
to the required length, and returns an INVSTRLEN error. The value referenced byretval
is unspecified.Otherwise, it copies the data to the buffer referenced by the
retval.buf_addr
, setsretval.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
exceedsyottadb.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. IftimeoutNsec
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 setsnext.elemsUsed
to the number of subscripts required, and returns an INSUFFSUBS error. In this case theelemsUsed
is greater thanelemsAlloc
.If one of the
C.ydb_buffer_t
structures referenced bynext
(call the first or only elementn
) has insufficient space for the corresponding subscript, the method setsnext.elemsUsed
ton
, and thelen_alloc
of thatC.ydb_buffer_t
structure to the actual space required. The method returns an INVSTRLEN error. In this case thelen_used
of that structure is greater than itslen_alloc
.Otherwise, it sets the structure
next
to reference the subscripts of that next node, andnext.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 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.elemsUsed
to the number of subscripts required, and returns an INSUFFSUBS error. In this case theelemsUsed
is greater thanelemsAlloc
.If one of the
C.ydb_buffer_t
structures referenced byprev
(call the first or only elementn
) has insufficient space for the corresponding subscript, the method setsprev.elemsUsed
ton
, and thelen_alloc
of thatC.ydb_buffer_t
structure to the actual space required. The method returns an INVSTRLEN error. In this case thelen_used
of that structure is greater than itslen_alloc
.Otherwise, it sets the structure
prev
to reference the subscripts of that prev node, andprev.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 setssub.len_used
to the actual length of that subscript, and returns an INVSTRLEN error. In this casesub.len_used
is greater thansub.len_alloc
.Otherwise, it copies that subscript to the buffer referenced by
sub.buf_addr
, and setssub.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 setssub.len_used
to the actual length of that subscript, and returns an INVSTRLEN error. In this casesub.len_used
is greater thansub.len_alloc
.Otherwise, it copies that subscript to the buffer referenced by
sub.buf_addr
, and setsbuf.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 thelen_used
of theC.ydb_buffer_t
referenced byretval
to the required length, and returns an INVSTRLEN error.Otherwise, it copies the data to the buffer referenced by the
retval.buf_addr
, and setsretval.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 toackChan
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 tonotifyChan
, and waits for the application to post toackChan
, indicating that YottaDB can continue. Note since YDB processing for fatal signals issues apanic()
,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 usingsignal.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:
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.