yottadb

package module
v2.0.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 7, 2026 License: GPL-3.0, LGPL-3.0 Imports: 25 Imported by: 0

README

YottaDB Go Wrapper v2

Go Report Card | Go Doc | Coverage report

YDBGo v2 is the latest and recommended Go API for YottaDB. Reference documentation is on the Go packages website.

Quick Start

Install YottaDB, a C compiler, and Go, before building YDBGo projects. See Get Started for instructions for installing YottaDB. YDBGo supports versions YottaDB r1.34 or later.

A YottaDB database must be set up and the environment variables configured. This can be done by sourcing /path/to/yottadb/ydb_env_set. For example:

source $(pkg-config --variable=prefix yottadb)/ydb_env_set

Once this is done, existing Go programs written for YDBGo should build without further setup.

Creating your own Go program

  1. Create an empty directory anywhere on your file system and go there.
mkdir ydb-example
cd ydb-example
  1. Use go mod init to create a package for your code in a unique namespace:
go mod init example.com/yottadb-example
  1. Create a Go program (e.g. helloworld.go) containing your main program with an import of lang.yottadb.com/go/yottadb/v2, e.g. the example script in the YDBGo documentation, which is:
package main

import "fmt"
import "lang.yottadb.com/go/yottadb/v2"

func main() {
	defer yottadb.Shutdown(yottadb.MustInit())
	conn := yottadb.NewConn()

	// Store unicode greeting into node ^hello("world")
	greeting := conn.Node("^hello", "world")
	greeting.Set("สวัสดี") // Sawadee (hello in Thai)
	fmt.Println(greeting.Get())

	// Output:
	// สวัสดี
}
  1. Download the YottaDB module by using go get .

  2. Run the code using go run .

  3. You can verify that the data got saved by running mupip extract -sel=^hello -stdout

  4. go build will create an executable for you (yottadb-example in this case). You can run that directly (e.g. ./yottadb-example).

  5. go install will install the executable for you in your Go path ($GOPATH/bin or ~/go by default).

Intro by example

To see how to increment or clear database node values, or kill entire node trees, replace main() above with:

defer yottadb.Shutdown(yottadb.MustInit())
conn := yottadb.NewConn()

hi := conn.Node("^howdy")            // create Node instance pointing to YottaDB global ^hello
hi.Set("western")
cowboy := hi.Child("cowboy")         // new variable pointing to subnode "cowboy" subscript
cowboy.Set("Howdy partner!")         // set ^hello("cowboy") to "Howdy partner!"
ranches := cowboy.Child("ranches")
ranches.Incr(2)                      // Increment empty node by 2 to get 2

fmt.Printf("First dump:\n%#v\n", hi) // %#v produces the same string as hi.Dump()
cowboy.Kill()                        // delete node, its children, and all values
fmt.Printf("Second dump:\n%#v\n", hi)
hi.Clear()                           // clear this node's value, too

// Output:
// First dump:
// ^howdy="western"
// ^howdy("cowboy")="Howdy partner!"
// ^howdy("cowboy","ranches")=2
//
// Second dump:
// ^howdy="western"

Doing something useful

Let's use Go to calculate the height of 3 oak trees, based on their shadow length and the angle of the sun. Replace main() with the following code. You will also need to import "math":

defer yottadb.Shutdown(yottadb.MustInit())
conn := yottadb.NewConn()

// capture initial data values into a Go map
data := []map[string]int{
    {"shadow": 10, "angle": 30},
    {"shadow": 13, "angle": 30},
    {"shadow": 15, "angle": 45},
}

// Enter data into the database
trees := conn.Node("^oaks") // node object pointing to YottaDB global ^oaks
for i, items := range data {
    for key, value := range items {
        trees.Child(i+1, key).Set(value)
    }
}

// Iterate data in the database and calculate results
for tree, i := range trees.Children() {
    tree.Child("height").Set(tree.Child("shadow").GetFloat() *
        math.Tan(tree.Child("angle").GetFloat()*math.Pi/180))
    fmt.Printf("Oak %s is %.1fm high\n", i, tree.Child("height").GetFloat())
}

// Output:
// Oak 1 is 5.8m high
// Oak 2 is 7.5m high
// Oak 3 is 15.0m high

Advanced Configuration

Installing pkg-config

Go's installation of this package uses pkg-config to find libyottadb.h. This requires installation of the yottadb.pc file, which is generated by default when YottaDB is installed with the recommended ydbinstall / ydbinstall.sh script.

If you installed YottaDB in some other way that did not create a yottadb.pc file, the contents should look something like this:

prefix=/usr/local/lib/yottadb/r202

exec_prefix=${prefix}
includedir=${prefix}
libdir=${exec_prefix}

Name: YottaDB
Description: YottaDB database library
Version: r2.02
Cflags: -I${includedir}
Libs: -L${libdir} -lyottadb -Wl,-rpath,${libdir}

Change the prefix to the correct path for your environment.

NOTE: you cannot use environment variables for the prefix path (e.g. $ydb_dist); it must be a fully qualified path.

You can also override the path used by pkg-config to find yottadb.pc with the environment variable PKG_CONFIG_PATH and a path to where the yottadb.pc file resides. You will need to use this method if you have more than one YottaDB release installed on your system.

Migrating v1 to v2

Applications that use YDBGo v1 will continue to operate without change. To aid migration of large applications from YDBGo v1 to v2, it is possible to use v1 and v2 APIs in the same application code. However, the the two versions cannot use each other's data types so this will only make sense where the old and new functionality is fairly modular. All signal handling will need to be migrated to v2 since v1 signal handlers will no longer be called.

To use both v1 and v2 APIs in one application you will need to add a named import like this:

import v1 "lang.yottadb.com/go/yottadb"

You must remove any calls to v1 Init() and Exit() and instead add the following line immediately after calling v2 Init():

v1.ForceInit()  // only available from v1.2.8

This will let v1 know that v2 has already done the initialization. You will need to upgrade your go.mod to specify at least v1.2.8 for yottadb v1:

require lang.yottadb.com/go/yottadb v1.2.8

Programming Notes

These notes supplement rather than replace the more general Programming Notes (Avoiding Common Pitfalls) for C.

Goroutines

Goroutines are supported since YDBGo is implemented using YottaDB's multi-threading API, including mechanisms to maintain engine integrity during concurrent access. Direct calls to the YottaDB C API should be avoided as they may bypass this protection.

Goroutines are subject to the requirements stated in the documentation for C Threads. However, its requirements for error buffers and transaction tokens are fully managed by the YDBGo wrapper, placing no burden on the programmer except that each goroutine must have its own instances of yottadb.Conn, and yottadb.Node.

Although Goroutines are fully supported, you should be aware that if the goroutines are database-intensive rather than computationally intensive, they will run faster as parallel processes than as parallel goroutines. This is because the database engine itself is not multi-threaded, so all goroutine access to the database is internally gated and queued through a single thread: only computational activity in Go is done in parallel.

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. YDBGo registers handlers with the Go runtime system for the following signals:

  • fatal signals: SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGINT, SIGQUIT, SIGSEGV, SIGTERM, SIGTRAP
  • non-fatal signals: SIGALRM, SIGCONT, SIGHUP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGUSR1

In addition, the following two signals have the same signal numbers as other handled signals:

  • SIGIO is a duplicate of SIGURG
  • SIGIOT is a duplicate of SIGABRT

By default these signals simply call yottadb.NotifyYDB() to let YottaDB handle the signal. Users may set up their own channel to be notified of a signal instead of YottaDB by calling yottadb.SignalNotify(), which has the same function signature as Go's signal.Notify(). The user's handler must then notify YottaDB of the signal, as appropriate, by calling yottadb.NotifyYDB() before, during, or after the user's own handling of the signal, depending on the application's needs. If you omit notification of YottaDB, be sure that you know what you know what you are doing, as YottaDB expects to be notified of signals.

Use yottadb.SignalNotify() in preference to Go's signal.Notify(), especially for fatal signals. If an application uses signal.Notify(), both it and YottaDB are notified concurrently about the signal -- whichever handler finishes first may exit with a panic, which will shutdown the database. Although this shutdown will be clean (assuming adherence to Database Integrity below), the shutdown may cause the unfinished handler to receive ydberr.CALLINAFTERXIT errors from the database.

Note: Although it is possible to capture program execution errors such as SIGBUS, SIGFPE, SIGILL, and SIGSEGV, recovery is not always possible, and they may indicate that corruption has already occurred. In these cases you should best to 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

YDBGo v2 has more robust handling of abnormal program termination. As in v1, before a program exits it must call yottadb.Shutdown() to flush database buffers and preserve database integrity. Since Go does not support atexit(), you must achieve this by having the main goroutine invoke defer yottadb.Shutdown(), and all other goroutines must defer ShutdownOnPanic(), a new function in v2. This is documented in more detail at yottadb.Shutdown().

If these instructions were not followed and a panic occurs when multiple goroutines were updating the database, you will need to follow the repair action in the note above.

Contributing

Last, if you plan to commit, you should set-up pre-commit hooks.

ln -s ../../pre-commit .git/hooks
go install honnef.co/go/tools/cmd/staticcheck@latest

To develop the YottaDB Go wrapper itself you may wish to import a local version of the wrapper instead of the public wrapper on the internet. To do this, clone the wrapper, then in a separate directory create your application that uses the wrapper and use go work commands to point it to the wrapper on your local file system rather than the internet repository.

To do so, run the following commands in the client app directory:

go work init
go work use . /your/local/path/to/YDBGo/v2  # Set this path to your YDBGo clone
git ignore go.work

The git ignore line prevents you from committing this local change to the public who will not have your local wrapper clone.

Now you can modify the YottaDB Go wrapper elsewhere on your local file system, and it will be immediately used by your client application, even before you commit the wrapper changes.

If you are developing YDBGo itself, you may find it useful to increase debugMode to increase debug logging as documented in yottadb.go. This is a private variable that cannot be set by the YDBGo user. It is only intended for use by developers of YDBGo. Coverage tests turn it on to ensure coverage of that debug portion of the code.

Testing

To test this wrapper:

  • go build only does a test compilation; it does not produce any files; go install has no effect.

  • To run tests, run make test

  • To run benchmarks, run make bench

    • Some CPUs gradually warm up during benchmarking, making the first few benchmark tests unfairly faster. Benchmark results are a lot more accurate and fair if you install perflock, which make bench will then invoke it automatically.

    • Perflock is apparently less effective on some CPUs. You can test whether it's working to produce consistent results by running make check which will run each benchmark several times so that you can see whether you're experiencing warm-up effects as the multiple tests run.

Advanced Testing

More rigorous tests can be performed with the go test -asan to catch illegal memory access errors, and it may even be run against a full ASAN build of YottaDB, in which case the -asan option is required.

If you wish to run against a YottaDB build that is not in the default location (e.g. an ASAN build), you will need to do the following to switch YottaDB build:

export ydb_dist=/path/to/yottadb
export PKG_CONFIG_PATH=$ydb_dist
go clean -r -cache -testcache   # remove artifacts from previous builds
go test [-asan] [-v] [-x]
  • Include -asan to catch illegal memory access errors.
  • Include -v to list each test as it is performed.
  • Include -x if you wish to list all linked files to ensure you're building against the correct libraries (e.g. an ASAN build of YottaDB).
Docker Container

The Dockerfile included in this repository creates an Ubuntu image with YottaDB, Go, and some basic development tools (git, gcc, make, vim, etc).

To build the Docker container run:

docker build . -t ydbgo

To use the container to develop a YDBGo project in your ~/goprojects directory, from the YDBGo directory, run:

docker run --rm -it -v ~/goprojects:/goprojects -v .:/YDBGo -w /goprojects ydbgo bash

Then follow the instructions for usage and setting up for development above.

Alternatively, may be able to use your local YDBGo directory from within the docker container. But you might have to add -e ydb_chset=UTF-8 to the docker command line to match docker's expectations of the files built in your local YDBGo directory.

Documentation

Overview

Package yottadb is a Go wrapper for a YottaDB database - a mature, high performance, transactional NoSQL engine with proven speed and stability.

The package requires minimum versions of Go 1.24 and YottaDB r1.34. It uses CGo to interface between this Go wrapper and the YottaDB engine written in C. Its use of the `Node` type to pin memory references to database subscript strings gives it optimal speed. To aid migration of YDBGo v1 to v2, it is possible to use both in one application.

Example

package main

import "fmt"
import "lang.yottadb.com/go/yottadb/v2"

func main() {
	defer yottadb.Shutdown(yottadb.MustInit())
	conn := yottadb.NewConn()

	// Store unicode greeting into node ^hello("world")
	greeting := conn.Node("^hello", "world")
	greeting.Set("สวัสดี") // Sawadee (hello in Thai)
	fmt.Println(greeting.Get())

	// Output:
	// สวัสดี
}

Prerequisites

Install YottaDB and consider reading the introduction to YottaDB's data model.

Index

Examples

Constants

View Source
const (
	TransactionCommit   = YDB_OK
	TransactionRollback = YDB_TP_ROLLBACK
	TransactionTimeout  = ydberr.TPTIMEOUT
)

Constants used for TimeoutAction. These just happen to all map to YDB error codes that do something but the new names have a more consistent naming scheme and are more Goish.

View Source
const (
	// YottaDB Enum constants
	YDB_DEL_NODE               = C.YDB_DEL_NODE
	YDB_DEL_TREE               = C.YDB_DEL_TREE
	YDB_SEVERITY_ERROR         = C.YDB_SEVERITY_ERROR         /* Error - Something is definitely incorrect */
	YDB_SEVERITY_FATAL         = C.YDB_SEVERITY_FATAL         /* Fatal - Something happened that is so bad, YottaDB cannot continue */
	YDB_SEVERITY_INFORMATIONAL = C.YDB_SEVERITY_INFORMATIONAL /* Informational - won't see these returned as they continue running */
	YDB_SEVERITY_SUCCESS       = C.YDB_SEVERITY_SUCCESS       /* Success */
	YDB_SEVERITY_WARNING       = C.YDB_SEVERITY_WARNING       /* Warning - Something is potentially incorrect */
	YDB_DATA_ERROR             = C.YDB_DATA_ERROR
	YDB_DATA_NOVALUE_DESC      = C.YDB_DATA_NOVALUE_DESC /* Node has no value but has descendants */
	YDB_DATA_UNDEF             = C.YDB_DATA_UNDEF        /* Node is undefined */
	YDB_DATA_VALUE_DESC        = C.YDB_DATA_VALUE_DESC   /* Node has both value and descendants */
	YDB_DATA_VALUE_NODESC      = C.YDB_DATA_VALUE_NODESC /* Node has a value but no descendants */
	YDB_MAIN_LANG_C            = C.YDB_MAIN_LANG_C       /* Main routine written in C (or M) or is otherwise C-compatible */
	YDB_MAIN_LANG_GO           = C.YDB_MAIN_LANG_GO      /* Main routine is written in Go so handle signals differently */

	// General-purpose constants
	YDB_DEFER_HANDLER  = C.YDB_DEFER_HANDLER /* 0x7ffffffa - defer this signal handler (used in Go wrapper) */
	YDB_INT_MAX        = C.YDB_INT_MAX
	YDB_LOCK_TIMEOUT   = C.YDB_LOCK_TIMEOUT
	YDB_MAX_ERRORMSG   = C.YDB_MAX_ERRORMSG
	YDB_MAX_IDENT      = C.YDB_MAX_IDENT /* Maximum size of global/local name (not including '^') */
	YDB_MAX_M_LINE_LEN = C.YDB_MAX_M_LINE_LEN
	YDB_MAX_NAMES      = C.YDB_MAX_NAMES     /* Maximum number of variable names can be specified in a single ydb_*_s() call */
	YDB_MAX_PARMS      = C.YDB_MAX_PARMS     /* Maximum parameters to an M call (call-in) */
	YDB_MAX_STR        = C.YDB_MAX_STR       /* Maximum YottaDB string length */
	YDB_MAX_SUBS       = C.YDB_MAX_SUBS      /* Maximum subscripts currently supported */
	YDB_MAX_TIME_NSEC  = C.YDB_MAX_TIME_NSEC /* Max specified time in (long long) nanoseconds */
	YDB_MAX_YDBERR     = C.YDB_MAX_YDBERR    /* Maximum (absolute) value for a YottaDB error */
	YDB_MIN_YDBERR     = C.YDB_MIN_YDBERR    /* Minimum (absolute) value for a YottaDB error */
	YDB_NODE_END       = C.YDB_NODE_END
	YDB_NOTOK          = C.YDB_NOTOK
	YDB_NOTTP          = C.YDB_NOTTP
	YDB_OK             = C.YDB_OK      /* Successful return code */
	YDB_RELEASE        = C.YDB_RELEASE /* Corresponds to YottaDB release r1.24 (i.e. YDB_ZYRELEASE in sr_linux/release_name.h) */
	YDB_TP_RESTART     = C.YDB_TP_RESTART
	YDB_TP_ROLLBACK    = C.YDB_TP_ROLLBACK
)

Define constants exported by YottaDB

Note: YDB_ERR_* error constants are defined in sub-package pkg/lang.yottadb.com/go/yottadb/v2/ydberr.

View Source
const WrapperRelease string = "v2.0.6"

WrapperRelease - (string) The Go wrapper release version for YottaDB SimpleAPI. Note the third piece of this version will be even for a production release and odd for a development release. When released, depending on new content, either the third piece of the version will be bumped to an even value or the second piece of the version will be bumped by 1 and the third piece of the version set to 0. On rare occasions, we may bump the first piece of the version and zero the others when the changes are significant. Also, the version numbers may be followed by a hyphen and text, e.g. "v2.0.2-alpha" Note: version descriptions may be seen on the git tags with `git tag -n`

Variables

View Source
var (
	// MaxPanicExitWait is the maximum wait when a panic caused by a signal has occurred (likely unable to run Exit().
	// It specifies the wait in seconds that yottadb.Exit() will wait for ydb_exit() to run before
	// giving up and forcing the process to exit. Note the normal exit wait is longer as we expect ydb_exit() to be
	// successful so can afford to wait as long as needed to do the sync but for a signal exit, the rundown is likely
	// already done (exit handler called by the signal processing itself) but if ydb_exit() is not able to get
	// the system lock and is likely to hang, 3 seconds is about as much as we can afford to wait.
	MaxPanicExitWait time.Duration = 3 * time.Second

	// MaxNormalExitWait is maximum wait for a normal shutdown when no system lock hang in Exit() is likely.
	MaxNormalExitWait time.Duration = 60 * time.Second

	// MaxSigShutdownWait is maximum wait to close down signal handling goroutines (shouldn't take this long).
	MaxSigShutdownWait time.Duration = 5 * time.Second

	// MaxSigAckWait is maximum wait for notify via acknowledgement channel that a notified signal handler is
	// done handling the signal.
	MaxSigAckWait time.Duration = 10 * time.Second
)

Set default exit wait times. The user may change these.

View Source
var DebugMode atomic.Int64 // increasing values 1, 2 or 3 for increasing log output

DebugMode greater than zero (1, 2, or 3) increases logging output

  • DebugMode=0: no debug logging (default)
  • DebugMode=1: log at entrypoint of M functions or Go signal callbacks; and don't remove temporary callback table file
  • DebugMode=2: in addition, log extra signal processing info
View Source
var MaxStacktrace = 4096

MaxStacktrace is the maximum number of stack frames stored in an Error instance

View Source
var MinYDBRelease string = "r1.34"

MinYDBRelease - (string) Minimum YottaDB release name required by this wrapper. This is checked on init. It is a var rather than a const so we can change it purely to verify logic that parses it

YDBSignals lists all the signals that YottaDB must be notified of.

Functions

func ErrorCode added in v2.0.4

func ErrorCode(err any) int

ErrorCode returns the error code of err if it is an instance of yottadb.Error; otherwise returns ydberr.NotYDBError. Unlike err.Code this works even if err is not an error instance.

func ErrorIs

func ErrorIs(err any, code int) bool

ErrorIs uses errors.Is() to search an error or chain of wrapped errors for a yottadb.Error with a matching ydberr code. Only the error code is matched, not the message, to support YottaDB error messages that vary. For example, to test for YottaDB INVSTRLEN error:

if yottadb.ErrorIs(err, ydberr.INVSTRLEN) {

is a short equivalent of:

if errors.Is(err, &Error{Code: ydberr.INVSTRLEN}) {

It differs from a simple type test using yottadb.Error.Code in that it searches for a match in the entire chain of wrapped errors

Example

Example checking whether error is a particular YottaDB error:

err := &yottadb.Error{Code: ydberr.INVSTRLEN, Message: "string too long"}

fmt.Println("Error is INVSTRLEN:", yottadb.ErrorIs(err, ydberr.INVSTRLEN))
fmt.Println(" or using longform:", errors.Is(err, &yottadb.Error{Code: ydberr.INVSTRLEN}))

wrapper := fmt.Errorf("wrapped: %w", err)
fmt.Println("Wrapped error is still INVSTRLEN:", yottadb.ErrorIs(wrapper, ydberr.INVSTRLEN))

fmt.Println()
fmt.Println("Or you can grab the error with Error.As():")
var e *yottadb.Error
fmt.Println("Error is type yottadb.Error:", errors.As(err, &e))
if errors.As(err, &e) {
	fmt.Println("  and the error is:", e)
}

err2 := fmt.Errorf("string too long")
fmt.Println("Error is type yottadb.Error:", errors.As(err2, &e))
if errors.As(err2, &e) {
	fmt.Println("Error is type yottadb.Error:", e)
}
Output:
Error is INVSTRLEN: true
 or using longform: true
Wrapped error is still INVSTRLEN: true

Or you can grab the error with Error.As():
Error is type yottadb.Error: true
  and the error is: string too long
Error is type yottadb.Error: false

func NotifyYDB

func NotifyYDB(sig os.Signal) bool

NotifyYDB calls the YottaDB signal handler for sig. If YottaDB deferred handling of the signal, return false; otherwise return true. Panic on YottaDB errors.

func Shutdown

func Shutdown(handle *DB) error

Shutdown invokes YottaDB's rundown function ydb_exit() to shut down the database properly. It MUST be called prior to process termination by any application that calls Init(). It is recommended to defer Shutdown() immediately after calling Init() in the main routine. You should also defer ShutdownOnPanic() from new goroutines to ensure shutdown occurs if they panic.

This is necessary, particularly in Go, because Go does not call the C atexit() handler (unless building with certain test options), so YottaDB itself cannot automatically ensure correct rundown of the database.

If Shutdown() is not called prior to process termination, steps must be taken to ensure database integrity, as documented in Database Integrity and unreleased locks may cause small subsequent delays (see relevant LKE documentation).

Deferring Shutdown() has the side benefit of exiting silently on ydberr.CALLINAFTERXIT panics if they come from a Ctrl-C (SIGINT) panic that has already occurred in another goroutine.

Notes:

  • It is the main routine's responsibility to ensure that any goroutines have finished using the database before it calls yottadb.Shutdown(). Otherwise they will receive ydberr.CALLINAFTERXIT errors from YDBGo.
  • Avoid Go's os.Exit() function because it bypasses any defers (it is a low-level OS call).
  • Shutdown() must be called exactly once for each time Init() was called, and shutdown will not occur until the last time.

Returns ydberr.ShutdownIncomplete if it has to wait longer than MaxNormalExitWait for signal handling goroutines to exit. No other errors are returned. Panics if Shutdown is called more than Init.

func ShutdownHard added in v2.0.4

func ShutdownHard(handle *DB) error

ShutdownHard shuts down immediately even if it has not yet been called as many times as Init. It is used before a fatal exit like panic or fatals signals. ShutdownHard may be called any number of times without ill effect (e.g. by different goroutines during an application shutdown).

func ShutdownOnPanic added in v2.0.4

func ShutdownOnPanic()

ShutdownOnPanic should be deferred at the start of every goroutine to ensure that the database is shut down on panic. Otherwise a panic that occurs within that goroutine will call os.Exit without properly shutting down the database, and MUPIP RUNDOWN will need to be run.

Deferring this function has the side benefit of exiting its goroutine silently on ydberr.CALLINAFTERXIT panics if they come from a fatal signal panic that has already occurred in another goroutine.

  • YDBGo's signal handlers already ensure shutdown is called if they panic (i.e. they defer ShutdownOnPanic).

See: Shutdown, SignalWasFatal

func SignalExitCallback added in v2.0.4

func SignalExitCallback(sigNum os.Signal)

SignalExitCallback is an exported version of signalExitCallback() for use only by FatalSignal test

func SignalNotify

func SignalNotify(notifyChan chan os.Signal, signals ...os.Signal)

SignalNotify relays incoming signals to notifyChan specifically for signals used by YottaDB. If SignalNotify is used on a specific signal, the user is then responsible to call NotifyYDB() at the start or end of their own handler to allow YottaDB to process the signal. The user can revert behaviour to the YDBGo default with SignalReset(), after which YDBGo will once again call NotifyYDB() itself.

  • Users may opt to use the standard library's [Signal.Notify]() instead of this function to be notified of signals, but this will notify them in parallel with YottaDB. However, they must not call Signal.Stop() (see below).
  • Do not call [Signal.Stop](), [Signal.Ignore]() or [Signal.Reset]() for any of the YottaDB-specific signals unless you understand that it will prevent NotifyYDB() from being called, and will affect YottaDB timers or other functionality.
  • Using SignalNotify to capture SIGSEGV is unreliable. Instead, see standard library function debug.SetPanicOnFault(true)

Goroutines used to handle signals should defer ShutdownOnPanic(). YDBGo's own signal handlers already do so.

YottaDB-specific signals are listed in the source in YDBSignals.

See YottaDB signals.

Example

Example that hooks SIGINT and SIGKILL but only passes SIGKILL on to YottaDB:

ch := make(chan os.Signal, 1) // Create signal notify and signal ack channels
yottadb.SignalNotify(ch, syscall.SIGCONT, syscall.SIGINT)

// A flag just for this test to check when a signal has been handled
signals := atomic.Int64{}

go func(ch chan os.Signal) {
	defer yottadb.ShutdownOnPanic() // essential to ensure proper shutdown if there is a panic

	for {
		sig := <-ch

		fmt.Printf("\nSignal %d (%s) received.\n", sig, sig)
		if sig == syscall.SIGINT {
			fmt.Printf("Not passing signal to YottaDB to avoid exit.\n")
		}
		if sig == syscall.SIGCONT {
			fmt.Printf("Passing signal to YottaDB now.\n")
			yottadb.NotifyYDB(sig)
			fmt.Printf("Returned from YottaDB signal handler.\n")
		}
		signals.Add(1)
	}
}(ch)

// send SIGCONT to myself
syscall.Kill(os.Getpid(), syscall.SIGCONT)
// ensure the first signal gets processed first so the message order is correct in the output below
for signals.Load() < 1 {
}

// send SIGINT to myself to simulate Ctrl-C
syscall.Kill(os.Getpid(), syscall.SIGINT)
// allow time for the previous message to flush before output is checked
for signals.Load() < 2 {
}
Output:

Signal 18 (continued) received.
Passing signal to YottaDB now.
Returned from YottaDB signal handler.

Signal 2 (interrupt) received.
Not passing signal to YottaDB to avoid exit.

func SignalReset

func SignalReset(signals ...os.Signal)

SignalReset stops notifying the user of the given signals and reverts to default YDBGo signal behaviour which simply calls NotifyYDB. No error is raised if the signal did not already have a notification request in effect.

func SignalWasFatal added in v2.0.4

func SignalWasFatal() bool

SignalWasFatal returns whether the currently unwinding panic was caused by a fatal signal like Ctrl-C. May be used in a deferred function like ShutdownOnPanic to check whether a fatal signal caused the current exit procedure.

Types

type CallTable

type CallTable struct {
	Filename string
	YDBTable string // Table after pre-processing the M-call table into YDB format
	// List of M routine metadata structs: one for each routine imported.
	Routines map[string]*RoutineData
	// contains filtered or unexported fields
}

CallTable stores internal metadata used for calling M and a table of Go functions by name loaded from the M call-in table.

type Conn

type Conn struct {
	// contains filtered or unexported fields
}

Conn represents a goroutine-specific 'connection' object for calling the YottaDB API. You must use a different connection for each goroutine.

func NewConn

func NewConn() *Conn

NewConn creates a new database connection. Each goroutine must have its own connection. To prevent deadlocks, this function panics if another Conn has already been created in this goroutine.

Example

Test the example given in the package doc at the top of doc.go

// Note: this example is also used in the README.md
defer yottadb.Shutdown(yottadb.MustInit())
conn := yottadb.NewConn()

// Store unicode greeting into node ^hello("world")
greeting := conn.Node("^hello", "world")
greeting.Set("สวัสดี") // Sawadee (hello in Thai)
fmt.Println(greeting.Get())
Output:
สวัสดี

func (*Conn) CloneConn added in v2.0.4

func (conn *Conn) CloneConn() *Conn

CloneConn returns a new connection that initially begins with the same transaction token as the original connection conn. This may be used if you absolutely must have activity within one transaction spread across multiple goroutines, in which case each new goroutine will need a new connection that has the same transaction token as the original connection. However, be aware that spreading transaction activity across multiple goroutines is highly discouraged. If it is done, CloneConn should be called in the new goroutine that uses the conn, and it is also the user's responsibility to ensure that the spawned goroutines complete before the parent terminates the transaction. Before doing this the programmer should first read and understand Threads and Transaction Processing.

func (*Conn) CloneNode

func (conn *Conn) CloneNode(n *Node) *Node

CloneNode creates a copy of node associated with conn (in case node was created using a different conn). A node associated with a conn used by another goroutine must not be used by the current goroutine except as a parameter to CloneNode(). If this rule is not obeyed, then the two goroutines could overwrite each others transaction level and error message values. It is the programmer's responsibility to ensure this does not happen by using CloneNode. This does the same as n.Clone() except that it can switch to new conn. Mutable nodes passed to CloneNode will cause a panic. They must be converted to immutable nodes with Node.Clone() Only immutable nodes are returned.

func (*Conn) Import

func (conn *Conn) Import(table string) (*MFunctions, error)

Import loads a call-in table for use by this connection only. The M routines listed in the call-in 'table' (specified below) are each wrapped in a Go function which may be subsequently called using the returned MFunctions.Call(name) or referenced as a Go function using MFunctions.Wrap(name).

If 'table' string contains ":" it is considered to be the call-in table specification itself; otherwise it is treated as the filename of a call-in file to be opened and read.

M-call table format specification

An M-call table specifies M routines which may be called by Go. It may be a string or a file (typically a file with extension .mcalls) and is case-sensitive. The format of an M-call table is a sequence of text lines where each line contains an M routine prototype specifications as follows:

Go_name: [ret_type] M_entrypoint(type, type, ...)

Elements of that line are defined as follows:

  • Go_name may be any go string.
  • M_entrypoint is any valid M entry reference.
  • ret_type may be omitted if an M return value is not supplied. Otherwise it must be *string, *int, *int64, *float64 or omitted (for void return)
  • any spaces adjacent to commas, asterisk and square brackets (,*[]) are ignored.

Zero or more parameter type specifications are allowed and must be a Go type specifier: string, int, uint, int32, uint32, int64, uint64, float32, float64, or a pointer version of the same in Go type format (e.g. *int).

  • If a pointer type is selected then the parameter is passed by reference so that the M routine can modify the parameter.
  • Any *string types and string return values must be followed by a preallocation value in square brackets (e.g. *string[100]).

This allows Go to preallocate enough space for the returned string. If necessary, YottaDB will truncate returned strings so they fit.

Comments begin with // and continue to the end of the line. Blank lines or pure-comment lines are ignored.

Example

Example that imports a call-in table which specifies Go functions that wrap M functions.

// The following table may be supplied from a string or a file
table := `
		AddVerbose: string[1024] addVerbose^arithmetic(*string[10], *int, int, string)
		Add: int add^arithmetic(int, int)
		Sub: int sub^arithmetic(int, int)
		AddFloat: float64 add^arithmetic(float64, float64)
		Noop: noop^arithmetic()
	`

// Import the call table
conn := yottadb.NewConn()
m := conn.MustImport(table)

// Call an imported function directly. Notice that the 'any' return type requires a type assertion
fmt.Printf("Double (5 + 11) is: %d\n", 2*m.Call("Add", 5, 11).(int))
fmt.Printf("Half (5 - 11) is: %d\n", m.Call("Sub", 5, 11).(int)/2)
fmt.Printf("5.5 + 1.2 is: %0.1f\n\n", m.Call("AddFloat", 5.5, 1.2))

// Wrap these same M routines as Go functions for easier and faster calling
add, sub, addFloat := m.WrapRetInt("Add"), m.WrapRetInt("Sub"), m.WrapRetFloat("AddFloat")
fmt.Printf("Double (5 + 11) is: %d\n", 2*add(5, 11))
fmt.Printf("Half (5 - 11) is: %d\n", sub(5, 11)/2)
fmt.Printf("5.5 + 1.2 is: %0.1f\n\n", addFloat(5.5, 1.2))

// Demonstrate calling an M routine that passes variables by reference
s := "test"
n := 3
result := m.Call("AddVerbose", &s, &n, 4, "100").(string)
fmt.Println("Result =", result)
fmt.Println("s =", s)
fmt.Println("n+1 =", n+1)
fmt.Println()

// Call a function that returns nothing in its Go signature
m.Call("Noop")

// Import a different table that invokes the same M routine sub(), but this time
// with a different Go function signature just for fun. YDBGo handles the type conversions.
m2 := conn.MustImport("Sub: string[10] sub^arithmetic(int64, uint64)")
// Test Sub() again but now with a different Go type signature
result = m2.Call("Sub", int64(5), uint64(11)).(string)
fmt.Printf("5 - 11 with negative sign removed is: %s\n", result[1:])
Output:

Double (5 + 11) is: 32
Half (5 - 11) is: -3
5.5 + 1.2 is: 6.7

Double (5 + 11) is: 32
Half (5 - 11) is: -3
5.5 + 1.2 is: 6.7

Result = :test:107
s = :test:
n+1 = 108

5 - 11 with negative sign removed is: 6

func (*Conn) KillAllLocals

func (conn *Conn) KillAllLocals()

KillAllLocals kills all M 'locals'. It is for clearer source code as it simply calls KillLocalsExcept() without listing any exceptions.

  • To kill a specific variable use Node.Kill()

func (*Conn) KillLocalsExcept

func (conn *Conn) KillLocalsExcept(exclusions ...string)

KillLocalsExcept kills all M 'locals' except for the ones listed by name in exclusions.

  • To kill a specific variable use Node.Kill()

func (*Conn) Lock

func (conn *Conn) Lock(timeout time.Duration, nodes ...*Node) bool

Lock releases all existing locks and attempt to acquire locks matching all supplied nodes, waiting up to timeout for availability.

  • Equivalent to the M `LOCK` command. See Node.Lock() and Node.Unlock() methods for single-lock usage.
  • A timeout of zero means try only once.
  • Return true if lock was acquired; otherwise false.
  • Panics with error TIME2LONG if the timeout exceeds YDB_MAX_TIME_NSEC or on other panic-worthy errors (e.g. invalid variable names).

func (*Conn) MustImport added in v2.0.4

func (conn *Conn) MustImport(table string) *MFunctions

MustImport is the same as Conn.Import but panics on errors.

func (*Conn) Node

func (conn *Conn) Node(varname string, subscripts ...any) (n *Node)

Node method creates a `Node` type instance that represents a YottaDB database node or M local node, and offers methods that access that YottaDB node.

  • The strings and array are stored in C-allocated space to give Node methods fast access to YottaDB API functions.
  • The varname and subscripts may be of type string, []byte slice, or an integer or float type; numeric types are converted to a string using the appropriate strconv function.
Example
conn := yottadb.NewConn()
n := conn.Node("var", "sub1", "sub2")
n2 := n.Child("sub3", "sub4")
fmt.Printf("%v\n", n)
fmt.Printf("%v\n", n2)
Output:
var("sub1","sub2")
var("sub1","sub2","sub3","sub4")

func (*Conn) Quote added in v2.0.4

func (conn *Conn) Quote(value string) string

Quote adds quotes around strings but not around numbers (just as YottaDB would display them).

  • The input value is treated as a string if it cannot be converted, unchanged, to and from float64 using strconv.ParseFloat(value, 64) and strconv.FormatFloat(number, 'f', -1, 64)
  • If the string contains unprintable ASCII characters it is converted to YottaDB ZWRITE format using Conn.Str2Zwr.
  • This is exported so that the user can validate against the same conversion that is used by YDBGo.

func (*Conn) Restart added in v2.0.4

func (conn *Conn) Restart()

Restart a transaction immediately (after first rolling back).

Example
package main

import (
	"fmt"

	yottadbV2 "lang.yottadb.com/go/yottadb/v2"
)

func main() {
	conn := yottadbV2.NewConn()
	n := conn.Node("^activity")
	n.Set(0)
	// M locals to demonstrate restoration of M 'local' on restart or not
	incr1 := conn.Node("incr1")
	incr2 := conn.Node("incr2")
	incr1.Set(0)
	incr2.Set(0)
	restarts := 0 // Go local
	run := func() {
		n.Incr(1)
		incr1.Incr(1)
		incr2.Incr(1)
		if restarts < 2 {
			restarts++
			conn.Restart()
		}
		if restarts > 9 {
			// Error condition
			conn.Rollback()
		}
	}
	normal := conn.TransactionFast([]string{"incr1"}, run)
	fmt.Printf("Database updated %d times; transaction restarted %d times then normal exit was: %v\n", n.GetInt(), restarts, normal)
	fmt.Printf("M local 'incr1' was restored so became %d; 'incr2' became %d\n", incr1.GetInt(), incr2.GetInt())

	// Run transaction again but give it an error condition so that it rolls back
	restarts = 10
	rollback := !conn.TransactionFast(nil, run)
	fmt.Printf("Transaction rollback %v; restores database value n of 2 to %d\n", rollback, n.GetInt())
}
Output:
Database updated 1 times; transaction restarted 2 times then normal exit was: true
M local 'incr1' was restored so became 1; 'incr2' became 3
Transaction rollback true; restores database value n of 2 to 1

func (*Conn) Rollback added in v2.0.4

func (conn *Conn) Rollback()

Rollback and exit a transaction immediately.

func (*Conn) Str2Zwr

func (conn *Conn) Str2Zwr(str string) (string, error)

Str2Zwr takes the given Go string and converts it to return a ZWRITE-formatted string

  • If the input string does not fit within the maximum YottaDB string size, return a *Error with Code=ydberr.InvalidStringLength
  • If the output string does not fit within the maximum YottaDB string size, return a *Error with Code=ydberr.INVSTRLEN
  • Otherwise, return the ZWRITE-formatted string.
  • Note that the length of a string in zwrite format is always greater than or equal to the string in its original, unencoded format.

Panics on other errors because they are are all panic-worthy (e.g. invalid variable names).

Example

Example of converting a Go string to a ZWRITE-formatted string:

conn := yottadb.NewConn()
str, err := conn.Str2Zwr("X\x00ABC")
if err != nil {
	panic(err)
}
fmt.Printf("%v", str)
Output:
"X"_$C(0)_"ABC"

func (*Conn) TimeoutAction added in v2.0.4

func (conn *Conn) TimeoutAction(action int)

TimeoutAction sets the action that is performed when [conn.Transaction] times out. The following actions are valid and performed the named function:

  • TransactionTimeout (default): rollback and panic with Error.Code = ydberr.TPTIMEOUT
  • TransactionRollback: rollback the transaction and return false from the transaction function
  • TransactionCommit: commit database activity that was done before the timeout

Notes:

  • Timeout only occurs if YottaDB special variable $ZMAXTPTIME is set.
  • Although TimeoutAction is specific to the specified conn, $ZMAXTPTIME is global.
  • If TimeoutAction has not been called on this Conn, the default action is TransactionTimeout.

func (*Conn) Transaction

func (conn *Conn) Transaction(transID string, localsToRestore []string, callback func()) bool

Transaction processes database logic inside a database transaction.

  • `callback` must be a function that implements the required database logic.
  • `transId` has its first 8 bytes recorded in the commit record of journal files for database regions participating in the transaction. Note that a transId of case-insensitive "BATCH" or "BA" are special: see Conn.TransactionFast()
  • `localsToRestore` are names of local M variables to be restored to their original values when a transaction is restarted. If localsToRestore[0] equals "*" then all local M locals are restored on restart. Note that since Go has its own local variables it is unlikely that you will need this feature in Go.
  • Returns true to indicate that the transaction logic was successful and has been committed to the database, or false if a rollback was necessary.
  • Panics on errors because they are are all panic-worthy (e.g. invalid variable names). See yottadb.Error for rationale.

The callback function should:

  • Implement the required database logic taking into account key considerations for Transaction Processing code.
  • If there are database collisions, `callback` will be called repeatedly, rolling back the database before each call. On the fourth try, YottaDB will resort to calling it with other processes locked out to ensure its success.
  • Call Conn.Restart if it needs to rollback and immediately restart the transaction function
  • Call Conn.Rollback if it needs to rollback and immediately exit the transaction function
  • Finish quickly because database activity in other goroutines will be blocked until it is complete.
  • Not create goroutines within the transaction unless absolutely necessary, in which case see Conn.CloneConn.

Transaction nesting level may be determined within the callback function by reading the special variable $tlevel, and the number of restart repetitions by $trestart. These things are documented in more detail in Transaction Processing.

func (*Conn) TransactionFast

func (conn *Conn) TransactionFast(localsToRestore []string, callback func()) bool

TransactionFast is a faster version of Transaction that does not ensure durability, for applications that do not require durability or have alternate durability mechanisms (such as checkpoints). It is implemented by setting the transID to the special name "BATCH" as discussed in Transaction Processing.

  • Panics on errors because they are are all panic-worthy (e.g. invalid variable names). See yottadb.Error for rationale.

func (*Conn) TransactionToken added in v2.0.4

func (conn *Conn) TransactionToken() (tptoken uint64)

TransactionToken sets the transaction-level token being using by the given connection conn. This is for use only in the unusual situation of mixing YDBGo v1 and v2 code and you have a v2 transaction that needs to call a v1 function (which must therefore be passed the v2 Conn's tptoken). It would be tidier, however, to avoid mixing versions within a transaction, therefore this function is deprecated from its inception and will be removed in a future version once there has been plenty of time to migrate all code to v2. See Conn.TransactionTokenSet

Example
package main

import (
	"fmt"

	yottadbV1 "lang.yottadb.com/go/yottadb"
	yottadbV2 "lang.yottadb.com/go/yottadb/v2"
)

func main() {
	defer yottadbV2.Shutdown(yottadbV2.MustInit())
	yottadbV1.ForceInit() // Tell v1 that v2 has done the initialization

	conn := yottadbV2.NewConn()
	conn.TransactionFast(nil, func() {
		person := conn.Node("^person")
		person.Set("Sally")

		// Run a YDBGo v1 function
		tptoken := conn.TransactionToken() // without this the v1 function will hang
		val, err := yottadbV1.ValE(tptoken, nil, "^person", nil)
		if err != nil {
			panic(err)
		}
		fmt.Print(val)
	})
}
Output:
Sally

func (*Conn) TransactionTokenSet added in v2.0.4

func (conn *Conn) TransactionTokenSet(tptoken uint64)

TransactionTokenSet sets the transaction-level token being using by the given connection conn. This is for use only in the unusual situation of mixing YDBGo v1 and v2 code and you have a v1 transaction that needs to call a v2 function (which must therefore be run on a Conn with the v1 tptoken). It would be tidier, however, to avoid mixing versions within a transaction, therefore this function is deprecated from its inception and will be removed in a future version once there has been plenty of time to migrate all code to v2. See Conn.TransactionToken

Example
package main

import (
	"fmt"

	yottadbV1 "lang.yottadb.com/go/yottadb"
	yottadbV2 "lang.yottadb.com/go/yottadb/v2"
)

func main() {
	defer yottadbV2.Shutdown(yottadbV2.MustInit())
	yottadbV1.ForceInit() // Tell v1 that v2 has done the initialization

	conn := yottadbV2.NewConn()
	err := yottadbV1.TpE(yottadbV1.NOTTP, nil, func(tptoken uint64, errstr *yottadbV1.BufferT) int32 {
		err := yottadbV1.SetValE(tptoken, nil, "Fred", "^person", nil)
		if err != nil {
			panic(err)
		}

		// Run a YDBGo v2 function Node.Dump()
		conn.TransactionTokenSet(tptoken) // without this the v2 function will hang
		person := conn.Node("^person")
		fmt.Print(person.Dump())
		return yottadbV2.YDB_OK
	}, "BATCH", nil)
	if err != nil {
		panic(err)
	}
	// Restore transaction token in conn to its initial value
	// without this, subsequent use of conn will hang
	conn.TransactionTokenSet(yottadbV1.NOTTP)

}
Output:
^person="Fred"

func (*Conn) Zwr2Str

func (conn *Conn) Zwr2Str(zstr string) (string, error)

Zwr2Str takes the given ZWRITE-formatted string and converts it to return as a normal ASCII string.

  • If the input string does not fit within the maximum YottaDB string size, return a *Error with Code=ydberr.InvalidStringLength
  • If zstr is not in valid zwrite format, return the empty string and a *Error with Code=ydberr.InvalidZwriteFormat.
  • Otherwise, return the decoded string.
  • Note that the length of a string in zwrite format is always greater than or equal to the string in its original, unencoded format.

Panics on other errors because they are are all panic-worthy (e.g. invalid variable names).

Example

Example of converting a ZWRITE-formatted string to a Go string:

conn := yottadb.NewConn()
str, err := conn.Zwr2Str(`"X"_$C(0)_"ABC"`)
if err != nil {
	panic(err)
}
fmt.Printf("%#v", str)
Output:
"X\x00ABC"

type DB

type DB struct {
	YDBRelease float64 // release version of the installed YottaDB
	// contains filtered or unexported fields
}

DB is the type returned by Init() which must be passed to Shutdown(). It is used as a clue to the user that they must not forget to Shutdown()

func Init

func Init() (*DB, error)

Init initializes the YottaDB engine and sets up signal handling. Init may be called multiple times (e.g. by different goroutines) but Shutdown() must be called exactly once for each time Init() was called. See Shutdown for more detail on the fallout from incorrect usage. Although Init could have been made to happen automatically, this more explicit approach clarifies that Shutdown() MUST be called before process exit.

  • Be sure to read the cautions at Shutdown.
  • Init returns a value of type DB that must be passed to the Shutdown function.
  • Returns yottadb.Error with Code=ydberr.Init on failure, which will also wrap any errors from YottaDB in the error chain.

Users should defer Shutdown from their main routine before using other database functions; for example:

db, err := yottadb.Init()
if err != nil {
  panic(err)
}
defer yottadb.Shutdown(db)
  ... user code to use the database ...

func MustInit

func MustInit() *DB

MustInit calls Init() and panics on errors. It is purely to shorten example code.

type Error

type Error struct {
	Message string // The error string - generally from $ZSTATUS when available
	Code    int    // The error value (e.g. ydberr.INVSTRLEN, etc)
	// contains filtered or unexported fields
}

Error type holds YDBGo and YottaDB errors including a numeric error code. YDBGo error strategy is as follows. Database setup functions like Init and Conn.Import (and its returned functions) return errors. However, Node functions panic on errors because Node errors are caused by either programmer blunders (like invalid variable name) or system-level events (like out of memory). This approach greatly simplifies the use of Node methods.

If a particular panic needs to be captured this can be done with recover as YDBGo ensures that all its errors and panics are of type yottadb.Error to facilitate capture of the specific cause using the embedded code:

  • All YottaDB errors are formated in $ZSTATUS format message and the YottaDB numeric error code with negative value, defined in ydberr/errorscodes.go.
  • All YDBGo errors likewise have a message string, but have a positive error code, defined in ydberr.ydberr.go.

The yottadb.Error type implements the Error.Is method to check the entire error chain, matching only against the error code. However, the API function yottadb.ErrorIs wraps this more conveniently to check any error type (even non-YottaDB errors) for a match against a given YottaDB error code: yottadb.ErrorIs(err, ydberr.<ErrorCode>).

func (*Error) Error

func (err *Error) Error() string

Error is a type method of yottadb.Error to return the error message string.

func (*Error) Is

func (err *Error) Is(target error) bool

Is lets errors.Is() search an error or chain of wrapped errors for a yottadb.Error with a matching ydberr code. See ErrorIs() for a more practical way to use this capability. Only the error code is matched, not the message: this supports matching errors even when YottaDB messages vary.

func (*Error) Unwrap

func (err *Error) Unwrap() []error

Unwrap allows yottadb.Error to wrap other underlying errors. See errors.Unwrap.

type MFunctions

type MFunctions struct {
	// Almost-private metadata for testing or specialised access to the imported call table. Public for specialised use.
	Table *CallTable
	Conn  *Conn
}

MFunctions is returned by [Import] to represent an M-call table with some methods that allow the user to call its M routines.

func (*MFunctions) Call

func (m *MFunctions) Call(rname string, args ...any) any

Call calls an M routine rname with parameters args and returns string, int64 or float64. Since the programmer knows the return-type in advance from the M-call table, the return type may be safely forced to that type with an unchecked type assertion. For example:

x = m.Call("add", 1, 2).(int64)

This version panics on errors. See MFunctions.CallErr() for a version that returns errors.

func (*MFunctions) CallErr

func (m *MFunctions) CallErr(rname string, args ...any) (any, error)

CallErr calls an M routine rname with parameters args and returns string, int64 or float64, and any error. This version returns any errors. See MFunctions.Call() for a version that panics.

func (*MFunctions) Wrap

func (m *MFunctions) Wrap(rname string) func(args ...any) any

Wrap returns a function that calls an M routine rname that returns any. This can speed up calling M routines by avoiding the name lookup each invocation. This version creates functions that panic: compare MFunctions.WrapErr that returns errors instead.

func (*MFunctions) WrapErr

func (m *MFunctions) WrapErr(rname string) func(args ...any) (any, error)

WrapErr returns a function that calls an M routine rname that returns any. This can speed up calling M routines by avoiding the name lookup each invocation. This version creates functions that return errors: compare MFunctions.Wrap that panics instead.

func (*MFunctions) WrapRetFloat added in v2.0.4

func (m *MFunctions) WrapRetFloat(rname string) func(args ...any) float64

WrapRetFloat produces a Go convenience function that wraps an M routine that returns float64. This can avoid messy type assertion and speed up calling M routines by avoiding the name lookup each invocation. This version creates functions that panic: compare MFunctions.WrapErr that returns errors instead.

func (*MFunctions) WrapRetInt added in v2.0.4

func (m *MFunctions) WrapRetInt(rname string) func(args ...any) int

WrapRetInt produces a Go convenience function that wraps an M routine that returns int. This can avoid messy type assertion and speed up calling M routines by avoiding the name lookup each invocation. Rather than int64 it returns int as the default type in Go so that regular arithmetic can be done on the result without casting. This version creates functions that panic: compare MFunctions.WrapErr that returns errors instead.

func (*MFunctions) WrapRetString added in v2.0.4

func (m *MFunctions) WrapRetString(rname string) func(args ...any) string

WrapRetString produces a Go convenience function that wraps an M routine that returns string. This can avoid messy type assertion and speed up calling M routines by avoiding the name lookup each invocation. This version creates functions that panic: compare MFunctions.WrapErr that returns errors instead.

type Node

type Node struct {
	Conn *Conn // Node.Conn points to the Go conn; Node.cnode.conn will point directly to the C.conn
	// contains filtered or unexported fields
}

Node is an object containing strings that represents a YottaDB node.

  • Stores all the supplied strings (varname and subscripts) in the Node object along with array of C.ydb_buffer_t structs that point to each successive string, to provide fast access to YottaDB API functions.
  • Regular Nodes are immutable. There is a mutable version of Node emitted by Node.Next() and Node iterators, which will change each loop. If you need to take an immutable snapshot of a mutable node this may be done with Node.Clone().
  • Concurrency: Do not run database actions on node objects created in another goroutine. If you want to act on a node object passed in from another goroutine, first call Node.Clone(conn) to make a copy of the other goroutine's node object using the current goroutine's connection `conn`. Then perform methods on that.

Node methods panic on errors because they are are all panic-worthy (e.g. invalid variable names). See yottadb.Error for error strategy and rationale.

func (*Node) Child

func (n *Node) Child(subscripts ...any) (child *Node)

Child creates a child node of parent that represents parent with subscripts appended.

func (*Node) Children

func (n *Node) Children() iter.Seq2[*Node, string]

Children returns an interator over immediate child nodes for use in a FOR-loop. This iterator is a wrapper for Node.Next(). It yields two values:

  • a mutable node instance with final subscripts changed to successive subscript names
  • the name of the child subscript (optionally assigned with a range statement)

Notes:

  • Treats 'null subscripts' (i.e. empty strings) in the same way as M function $ORDER().
  • The order of returned nodes matches the collation order of the M database.
  • This function never adjusts the supplied node even if it is mutable (it always creates its own mutable copy).
  • If you need to take an immutable snapshot of the returned mutable node, use Node.Clone().

See:

Example

Example of getting all child nodes

conn := yottadb.NewConn()
n := conn.Node("X", 1)
n.Child(2, 3).Set(123)
n.Child(2, 4).Set(124)
n.Child(2, 3, "person").Set(1237)

// Note that the following person fields will come out in alphabetical order below
n.Child(2, 3, "person", "address").Set("2 Rocklands Rd")
n.Child(2, 3, "person", "address", "postcode").Set(1234)
n.Child(2, 3, "person", "occupation").Set("engineer")
n.Child(2, 3, "person", "age").Set(42)
n.Child(2, 3, "person", "sex").Set("male")

n = conn.Node("X", 1, 2)
for x := range n.Children() {
	fmt.Printf("%s=%s\n", x, x.Get())
}

fmt.Println("Do the same in reverse:")
for x := range n.ChildrenBackward() {
	fmt.Printf("%s=%s\n", x, x.Get())
}

n = conn.Node("X", 1, 2, 3, "person")
fmt.Printf("Person fields: (")
for _, sub := range n.Children() {
	fmt.Printf("%s ", sub)
}
fmt.Println(")")
Output:
X(1,2,3)=123
X(1,2,4)=124
Do the same in reverse:
X(1,2,4)=124
X(1,2,3)=123
Person fields: (address age occupation sex )

func (*Node) ChildrenBackward

func (n *Node) ChildrenBackward() iter.Seq2[*Node, string]

ChildrenBackward is the same as Children but operates in reverse order. See Node.Children().

func (*Node) Clear

func (n *Node) Clear()

Clear deletes the node value, not its child subscripts.

  • Equivalent to YottaDB M command ZKILL

func (*Node) Clone

func (n *Node) Clone() (clone *Node)

Clone creates an immutable copy of node.

See Node.IsMutable() for notes on mutability.

func (*Node) Dump added in v2.0.4

func (n *Node) Dump(args ...int) string

Dump returns a string representation of this database node and subtrees with their contents in YottaDB ZWRITE format. For example output, see Node.GoString. Output lines are formatted as follows:

  • Output subscripts and values as unquoted numbers if they convert to float64 and back without change (using [Node.Quote]).
  • Output strings in YottaDB ZWRITE format.
  • Every line output ends with "\n", including the final line.
  • If the receiver is nil, output is "<nil>\n"
  • If node has no value and no children, outputs the empty string.

See Node.GoString for an example of the output format. Two optional integers may be supplied to specify maximums where both default to -1:

  • first parameter specifies the maximum number of lines to output (not including a file "...\n" line indicating truncation). A maximum of -1 means infinite. If lines are truncated, an additional line "...\n" is added so that the output ends with "\n...\n". A maximum of 0 lines is treated as 1.
  • second parameter specifies the maximum number of characters at which to truncate values prior to output where -1 means infinite. Truncated values are output with suffix "..." after any ending quotes. Note that conversion to ZWRITE format may expand this.
Example

Example of traversing a database tree

conn := yottadb.NewConn()
n := conn.Node("tree", 1)
n.Child(6).Set(16)
n.Child(2, 3).Set(123)
n.Child(2, 3, 7).Set("Hello!")
n.Child(2, 4).Set(124)
n.Child(2, 5, 9).Set(1259)
nb := conn.Node("tree", "B")
nb.Child(1).Set("AB")

fmt.Println(n.Dump())

n.Child(2, 3).Set("~ A\x00\x7f" + strings.Repeat("A", 1000))
fmt.Print(n.Dump(2, 8))
Output:
tree(1,2,3)=123
tree(1,2,3,7)="Hello!"
tree(1,2,4)=124
tree(1,2,5,9)=1259
tree(1,6)=16

tree(1,2,3)="~ A"_$C(0,127)_"AAA"...
tree(1,2,3,7)="Hello!"
...

func (*Node) Get

func (n *Node) Get(defaultValue ...string) string

Get fetches and returns the value of a database node or defaultValue[0] if the database node is empty.

  • defaultValue defaults to {""}

Return defaultValue[0] if the node's value does not exist.

func (*Node) GetBool added in v2.0.4

func (n *Node) GetBool(defaultValue ...bool) bool

GetBool fetches and returns the value of a database node as a bool.

  • defaultValue defaults to {false}

Return true if the node's value is a non-zero integer; false if it is integer zero; otherwise return defaultValue[0] (nonexistant, or is not convertable to an integer)

func (*Node) GetBytes added in v2.0.4

func (n *Node) GetBytes(defaultValue ...[]byte) []byte

GetBytes is the same as Node.Get except that it accepts and returns []byte slices rather than strings.

func (*Node) GetFloat added in v2.0.4

func (n *Node) GetFloat(defaultValue ...float64) float64

GetFloat fetches and returns the value of a database node as a float64.

  • defaultValue defaults to {0}

Return defaultValue[0] if the node's value does not exist or is not convertable to float64.

Example

Calculate the height of 3 oak trees, based on their shadow length and the angle of the sun.

// Note: this example is used in the README.md
defer yottadb.Shutdown(yottadb.MustInit())
conn := yottadb.NewConn()

// capture initial data values into a Go map
data := []map[string]int{
	{"shadow": 10, "angle": 30},
	{"shadow": 13, "angle": 30},
	{"shadow": 15, "angle": 45},
}

// Enter data into the database
trees := conn.Node("^oaks") // node object pointing to YottaDB global ^oaks
for i, items := range data {
	for key, value := range items {
		trees.Child(i+1, key).Set(value)
	}
}

// Iterate data in the database and calculate results
for tree, i := range trees.Children() {
	tree.Child("height").Set(tree.Child("shadow").GetFloat() *
		math.Tan(tree.Child("angle").GetFloat()*math.Pi/180))
	fmt.Printf("Oak %s is %.1fm high\n", i, tree.Child("height").GetFloat())
}
Output:
Oak 1 is 5.8m high
Oak 2 is 7.5m high
Oak 3 is 15.0m high

func (*Node) GetInt added in v2.0.4

func (n *Node) GetInt(defaultValue ...int) int

GetInt fetches and returns the value of a database node as an integer.

  • defaultValue defaults to {0}

Return defaultValue[0] if the node's value does not exist or is not convertable to an integer.

func (*Node) GoString added in v2.0.4

func (n *Node) GoString() string

GoString makes print("%#v") dump the node and its contents with Node.Dump(30, 80). For example the output format of Node("person",42).GoString() might look like this:

person(42)=1234
person(42)("age")="49"
person(42)("height")("centimeters")=190.5
person(42)("height")("inches")=1234
person(42)("name")="Joe Bloggs"
Example

Example of traversing a database tree

conn := yottadb.NewConn()
n := conn.Node("tree", 1)
n.Child(2, 3).Set(123)
n.Child(2, 3, 7).Set("Hello!")
n.Child(2, 4).Set(124)

fmt.Printf("Dump is:\n%#v", n)
Output:
Dump is:
tree(1,2,3)=123
tree(1,2,3,7)="Hello!"
tree(1,2,4)=124

func (*Node) HasBoth

func (n *Node) HasBoth() bool

HasBoth returns whether the database node has both tree and value.

func (*Node) HasNone

func (n *Node) HasNone() bool

HasNone returns whether the database node has neither tree nor value.

func (*Node) HasTree

func (n *Node) HasTree() bool

HasTree returns whether the database node has a tree of subscripts containing data.

func (*Node) HasTreeOnly

func (n *Node) HasTreeOnly() bool

HasTreeOnly returns whether the database node has no value but does have a tree of subscripts that contain data

func (*Node) HasValue

func (n *Node) HasValue() bool

HasValue returns whether the database node has a value.

func (*Node) HasValueOnly

func (n *Node) HasValueOnly() bool

HasValueOnly returns whether the database node has a value but no tree.

func (*Node) Incr

func (n *Node) Incr(amount any) string

Incr atomically increments the value of the database node by amount and returns the new value as a string.

  • The amount may be an integer, float or string representation of the same.
  • YottaDB first converts the value of the node to a canonical number by discarding any trailing non-digits and returning zero if it is still not a number. Then it adds amount to the node, all atomically.
  • Return the new value of the node as a string

func (*Node) IncrFloat added in v2.0.6

func (n *Node) IncrFloat(amount any) float64

IncrFloat atomically increments the value of the database node by amount and returns the new value as a float64. It operates the same as Node.Incr() except that it returns a float64 instead of a string. It panics if the returned value cannot be represented as a float, which should be impossible since prior to the increment, YottaDB first converts the node's value to a canonical number by discarding any trailing non-digits and, if it is still not a number, defaulting to zero.

func (*Node) IncrInt added in v2.0.6

func (n *Node) IncrInt(amount any) int

IncrInt atomically increments the value of the database node by amount and returns the new value as an integer. It operates the same as Node.IncrFloat() except that it returns an int instead of a float64. Any fractional portion in the returned value is truncated by Go's standard int(n) type cast logic, but is retained in the database and may be accessed by Node.GetFloat().

func (*Node) Index added in v2.0.4

func (n *Node) Index(subscripts ...any) *Node

Index allows fast temporary access to subnodes referenced by the given subscripts, but must be used with caution. It is most helpful for high-speed inner loops. If in doubt, use the slower Node.Child. It indexes a node object with the given subscripts and returns a mutable node object that will change next time Index is invoked on the same parent node object (which is thread-safe because each Goroutine has separate node objects). Fast access is achieved by removing the overhead of creating a new child node object every access, e.g. each time around a loop. Node.Children, for example, yields mutable nodes created by Index().

*Caution*: for non-temporary access, the slower Node.Child should be used instead. For example, where nodes will be passed to subroutines (subroutines should be able to assume they have been passed immutable nodes). Node.Index must also be avoided when you wish a Go variable to retain a pointer to a specific node after another use of Node.Index. The second example below illustrates incorrect usage.

  • Returns a mutable child of the given node with the given subscripts appended (see Node.IsMutable for mutability details).

Usage is similar to Node.Child except that it typically runs about 4 times as fast and returns mutable instead of immutable nodes.

Example

Example of fast iteration of a node to increment only children with subscripts 0..99999.

conn := yottadb.NewConn()
n := conn.Node("counter")
n.Index(100000).Set("untouched")
for i := range 100000 {
	n.Index(i).Incr(1)
}

fmt.Printf("%s: %s\n", n.Index(0), n.Index(0).Get())
fmt.Printf("%s: %s\n", n.Index(99999), n.Index(99999).Get())
fmt.Printf("%s: %s\n", n.Index(100000), n.Index(100000).Get())
Output:
counter(0): 1
counter(99999): 1
counter(100000): untouched
Example (Incorrect)

Example of when not to use Node.Index

conn := yottadb.NewConn()
person := conn.Node("person")
first := person.Index("first")
last := person.Index("last") // This overwrites the person index to be 'last' so first now points to the wrong thing
first.Set("Joe")
last.Set("Bloggs")
// Trying to set the first name failed; instead setting the last name,
// which was then overwritten when setting the last name
fmt.Printf("Retrieving the stored names yields first='%s', last='%s'\n", first.Get(), last.Get())
Output:
Retrieving the stored names yields first='Bloggs', last='Bloggs'

func (*Node) IsMutable

func (n *Node) IsMutable() bool

IsMutable returns whether given node is mutable. Mutable nodes may have their subscripts changed each loop iteration or each call to Node.Index(). This means that the same mutable node object may reference different database nodes and will not always point to the same one.

A mutable node object is like a regular node object except that it will change to point to a different database node each time Node.Index is invoked on its originating node object n, so if you store a reference to it, that reference will no longer point to the same database node as it originally did. For example, the following code will print the most recent vehicle, not the heaviest vehicle as intended, because Node.Children() yields mutable nodes.

n := conn.Node("vehicles")
var heaviest *yottadb.Node
var maxWeight float64 = 0
for vehicle := range n.Children() {
  if vehicle.Index("weight").GetFloat() > maxWeight {
    heaviest = vehicle
    maxWeight = vehicle.Index("weight")
  }
}
fmt.Print(heaviest.Dump())

func (*Node) Kill

func (n *Node) Kill()

Kill deletes a database node including its value and any subtree.

  • To delete only the value of a node use Node.Clear()
Example

Example

// Note: this example is also used in the README.md
defer yottadb.Shutdown(yottadb.MustInit())
conn := yottadb.NewConn()

hi := conn.Node("^howdy") // create Node instance pointing to YottaDB global ^hello
hi.Set("western")
cowboy := hi.Child("cowboy") // new variable pointing to subnode "cowboy" subscript
cowboy.Set("Howdy partner!") // set ^hello("cowboy") to "Howdy partner!"
ranches := cowboy.Child("ranches")
ranches.Incr(2) // Increment empty node by 2 to get 2

fmt.Printf("First dump:\n%#v\n", hi) // %#v produces the same string as hi.Dump()
cowboy.Kill()                        // delete node, its children, and all values
fmt.Printf("Second dump:\n%#v\n", hi)
hi.Clear() // clear this node's value, too
Output:
First dump:
^howdy="western"
^howdy("cowboy")="Howdy partner!"
^howdy("cowboy","ranches")=2

Second dump:
^howdy="western"

func (*Node) Lock

func (n *Node) Lock(timeout ...time.Duration) bool

Lock attempts to acquire or increment the count of a lock matching this node, waiting up to timeout for availability. Equivalent to the M `LOCK +lockpath` command.

  • If no timeout is supplied, wait forever. A timeout of zero means try only once.
  • Return true if lock was acquired; otherwise false.
  • Panics with TIME2LONG if the timeout exceeds YDB_MAX_TIME_NSEC or on other panic-worthy errors (e.g. invalid variable names).

func (*Node) Lookup

func (n *Node) Lookup() (string, bool)

Lookup returns the value of a database node and true, or if the variable name could not be found, returns the empty string and false.

  • If the node's variable name (M local or M global) exists but the subscripted node has no value, Lookup() will return the empty string and true. If you need to distinguish between an empty string and a value-less node you must use Node.HasValue()
  • bool false is returned on errors GVUNDEF (undefined M global) or LVUNDEF (undefined M local). Other errors panic.
  • You may use Node.Get() to return a default value when an undefined variable is accessed.

func (*Node) LookupBytes added in v2.0.4

func (n *Node) LookupBytes() ([]byte, bool)

LookupBytes is the same as Node.Lookup except that it returns the value as a []byte slice rather than a string.

func (*Node) Next

func (n *Node) Next() *Node

Next returns a Node instance pointing to the next subscript at the same depth level. Return a mutable node pointing to the database node with the next subscript after the given node, at the same depth level. Unless you want to start half way through a sequence of subscripts, it's usually tidier to use Node.Iterate() instead.

  • Equivalent to the M function $ORDER() and has the same treatment of 'null subscripts' (i.e. empty strings).
  • The order of returned nodes matches the collation order of the M database.
  • The node path supplied does not need to exist in the database to find the next match.
  • If the supplied node n contains only a variable name without subscripts, the next variable (GLVN) name is returned instead of the next subscript.

Returns nil when there are no more subscripts at the level of the supplied node path, or a mutable node as follows:

  • if the supplied node is immutable, a mutable clone of n with its final subscript changed to the next node.
  • if the supplied node is mutable, the same node n with its final subscript changed to the next node.

If you need to take an immutable snapshot of the returned mutable node this may be done with Node.Clone()

See:

  • Compare Node.Prev()
  • [Node.Iterate]() for an iterator version and Node.TreeNext() for traversal of nodes in a way that descends into the entire tree.
Example

Example of getting next subscript

conn := yottadb.NewConn()
n := conn.Node("X", 1)
n.Child(2, "3").Set("123")
n.Child(2, 3, 7).Set(1237)
n.Child(2, 4).Set(124)

x := conn.Node("X", 1, "2", "")
x = x.Next()
for x != nil {
	fmt.Printf("%s=%s\n", x, x.Get())
	x = x.Next()
}
Output:
X(1,2,3)=123
X(1,2,4)=124
Example (Varnames)

Example of listing all local database variable names

conn := yottadb.NewConn()
conn.KillAllLocals()
conn.Node("X", 1).Set("X1")
conn.Node("X", 1, 2).Set("X12")
conn.Node("Y", 2).Set("Y2")

fmt.Println("Display all top-level database variable names, starting after '%' (which is the first possible name in sort order)")
x := conn.Node("%")
x = x.Next()
for x != nil {
	fmt.Printf("%s\n", x)
	x = x.Next()
}
Output:
Display all top-level database variable names, starting after '%' (which is the first possible name in sort order)
X
Y

func (*Node) Prev

func (n *Node) Prev() *Node

Prev is the same as Next but operates in the reverse order. See Node.Next()

func (*Node) Set

func (n *Node) Set(val any)

Set applies val to the value of a database node.

  • The val may be a string, []byte slice, or an integer or float type; numeric types are converted to a string using the appropriate strconv function.

func (*Node) String

func (n *Node) String() string

String returns a string representation of this database node in typical YottaDB format: `varname("sub1")("sub2")`.

  • Output subscripts as unquoted numbers if they convert to float64 and back without change (using [Node.Quote]).
  • Output strings in YottaDB ZWRITE format
Example

Example of viewing a Node instance as a string:

conn := yottadb.NewConn()
n := conn.Node("var", "sub1", "sub2")
numsubs := conn.Node("var2", 1, 2)
fmt.Println(n)
fmt.Println(numsubs)
Output:
var("sub1","sub2")
var2(1,2)

func (*Node) Subscript added in v2.0.4

func (n *Node) Subscript(index int) string

Subscript returns a string that holds the specified varname or subscript of the given node. An index of zero returns the varname; higher numbers return the respective subscript. A negative index returns a subscript counted from the end (the last is -1). An out-of-range subscript panics.

func (*Node) Subscripts

func (n *Node) Subscripts() []string

Subscripts returns a slice of strings that represent the varname and subscript names of the given node.

Example
conn := yottadb.NewConn()
n := conn.Node("var", "sub1", "sub2")
fmt.Println(n)
fmt.Println(n.Subscripts())
Output:
var("sub1","sub2")
[var sub1 sub2]

func (*Node) Tree

func (n *Node) Tree() iter.Seq[*Node]

Tree returns an interator over all descendants of node for use in a FOR-loop. This iterator is a wrapper for Node.TreeNext(). It yields immutable node instances.

  • The next node is chosen in depth-first order (i.e by descending deeper into the subscript tree before moving to the next node at the same level).
  • The order of returned nodes matches the collation order of the M database.
  • Nodes that have 'null subscripts' (i.e. empty string) are all returned in their place.

See:

Example

Example of traversing a database tree

conn := yottadb.NewConn()
n := conn.Node("tree", 1)
n.Child(2, 3).Set(123)
n.Child(2, 3, 7).Set(1237)
n.Child(2, 4).Set(124)
n.Child(2, 5, 9).Set(1259)
n.Child(6).Set(16)
nb := conn.Node("tree", "B")
nb.Child(1).Set("AB")

for x := range n.Child(2).Tree() {
	fmt.Printf("%s=%s\n", x, conn.Quote(x.Get()))
}
Output:
tree(1,2,3)=123
tree(1,2,3,7)=1237
tree(1,2,4)=124
tree(1,2,5,9)=1259

func (*Node) TreeNext

func (n *Node) TreeNext() *Node

TreeNext returns the next node in the traversal of a database tree of a database variable. Equivalent to the M function $QUERY().

  • It yields immutable nodes.
  • The next node is chosen in depth-first order (i.e by descending deeper into the subscript tree before moving to the next node at the same level).
  • The order of returned nodes matches the collation order of the M database.
  • The node path supplied does not need to exist in the database to find the next match.
  • Returns nil when there are no more nodes after the given node path within the given database variable (GLVN).
  • Nodes that have 'null subscripts' (i.e. empty string) are all returned in their place except for the top-level GLVN(""), which is never returned.

See:

  • Node.TreePrev().
  • [Node.LevelNext]() for traversal of nodes at the same level or to move from one database variable (GLVN) to another.
Example

Example of traversing a database tree

conn := yottadb.NewConn()
n := conn.Node("tree", 1)
n.Child(2, 3).Set(123)
n.Child(2, 3, 7).Set(1237)
n.Child(2, 4).Set(124)
n.Child(2, 5, 9).Set("Hello!")
n.Child(6).Set(16)
nb := conn.Node("tree", "B")
nb.Child(1).Set("AB")

x := conn.Node("tree").TreeNext()
for x != nil {
	fmt.Printf("%s=%s\n", x, conn.Quote(x.Get()))
	x = x.TreeNext()
}

fmt.Println("Re-start half way through and go in reverse order:")
x = conn.Node("tree", 1, 2, 4)
for x != nil {
	fmt.Printf("%s=%s\n", x, conn.Quote(x.Get()))
	x = x.TreePrev()
}
Output:
tree(1,2,3)=123
tree(1,2,3,7)=1237
tree(1,2,4)=124
tree(1,2,5,9)="Hello!"
tree(1,6)=16
tree("B",1)="AB"
Re-start half way through and go in reverse order:
tree(1,2,4)=124
tree(1,2,3,7)=1237
tree(1,2,3)=123

func (*Node) TreePrev

func (n *Node) TreePrev() *Node

TreePrev is the same as TreeNext but operates in reverse order. See Node.TreeNext().

func (*Node) Unlock

func (n *Node) Unlock()

Unlock decrements the count of a lock matching this node, releasing it if zero. Equivalent to the M `LOCK -lockpath` command.

  • Returns nothing since releasing a lock cannot fail.

type RoutineData

type RoutineData struct {
	Name       string     // Go name for the M routine
	Entrypoint string     // the point to be called in M code
	Types      []typeSpec // stores type specification for the routine's return value and each parameter

	// metadata:
	Table *CallTable // call table used to find this routine name
	// contains filtered or unexported fields
}

RoutineData stores info used to call an M routine.

Directories

Path Synopsis
ydberr contains YottaDB constants generated from YottaDB source.
ydberr contains YottaDB constants generated from YottaDB source.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL