diff --git a/.gitignore~ b/.gitignore~ new file mode 100644 index 00000000..4d988644 --- /dev/null +++ b/.gitignore~ @@ -0,0 +1,2 @@ +-make.bat +-_build/ diff --git a/MultiLangProgGuide/MultiLangProgGuide.rst b/MultiLangProgGuide/MultiLangProgGuide.rst index 75fd8df0..5a1d6f36 100644 --- a/MultiLangProgGuide/MultiLangProgGuide.rst +++ b/MultiLangProgGuide/MultiLangProgGuide.rst @@ -235,9 +235,11 @@ become apparent: `canonical number`_, YottaDB internally converts it to, and stores it as, a number. When ordering subscripts: - - Empty string subscripts precede all numeric subscripts. *Note: - YottaDB recommends against applications that use empty string - subscripts.* [#]_ + - Empty string subscripts precede all numeric subscripts. By + default, YottaDB prohibits empty string subscripts for global + variables but permits them for local variables (see `Local and + Global Variables`_). *Note: YottaDB recommends against the + practice of using empty string subscripts in applications.* [#]_ - Numeric subscripts precede string subscripts. Numeric subscripts are in numeric order. - String subscripts follow numeric subscripts and collate in byte @@ -463,6 +465,23 @@ substrings. - The remainder is more detailed information about the error, and may contain commas within. +---------- +$zyrelease +---------- + +:code:`$zyrelease` identifies the YottaDB release in use. It consists +of four space separated pieces: + +1. Always “YottaDB”. +#. The release number, which starts with “r” and is followed by two + numbers separated by a period (“.”), e.g., “r1.22”. The first is a + major release number and the second is a minor release number under + the major release. Even minor release numbers indicate formally + released software. Odd minor release numbers indicate software + builds from “in flight” code under development, between releases. +#. The operating system. e.g., “Linux”. +#. The CPU architecture, e.g., “x86_64”. + .. _transaction processing: Transaction Processing @@ -1821,6 +1840,1238 @@ If the requested :code:`timeout_nsec` exceeds :code:`YDB_MAX_TIME_NSEC`, the function returns :code:`YDB_ERR_TIME2LONG`; otherwise it returns :code:`YDB_OK`. +================= +Programming in Go +================= + +Programming YottaDB in the `Go language `_ is +accomplished through a wrapper that uses `cgo +`_ to provide a “yottadb” package for +access from Go application code to YottaDB releases installed on a +system. YottaDB C functions are wrapped by Go methods where a method +can meaningfully be associated with a Go structure, and by Go +functions otherwise. + +Note: the YotaDB Go wrapper does not implement direct calls from Go +to M. To call an M function from Go, create a C function that calls +the M function, and call the C function from Go. + +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 above. The sections below that are specific to Go +are intended to supplement, but not subsume their general (C) +counterparts. + +Go application code *must not* directly use the YottaDB C API +structures and functions (those prefixed by :code:`C.`) as such usage +bypasses important controls, but should instead use the structures, +methods and functions exposed by the YottaDB Go wrapper. :code:`C.` +prefixed structures and functions are mentioned only for clarity in +documentation and brevity of explanation. For example, +:code:`C.ydb_buffer_t` is the C :code:`ydb_buffer_t` structure defined +in `Data Structures & Type Definitions`_ and :code:`C.ydb_lock_s()` is +the Simple API `ydb_lock_s()`_ function. + +All subsections of the `Programming in Go` section are prefixed with +“Go” to ensure unique names for hyperlinking. + +Go Quick Start +============== + +The YottaDB Go wrapper requires a minimum YottaDB version of r1.20 and +is tested with a minimum Go version of 1.6.2. 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 following +additional Go packages are also required: XYZ [currently no additional +packages are believed to be required, but need to document one way or +the other]. + +The Go Quick Start assumes that YottaDB has already been installed as +described in the `Quick Start`_ section. After completing step 1 +(*Installing YottaDB*), install the Go wrapper: + +Download the Go wrapper from XYZ [provide URL when released]. Unpack +the contents in its own directory (e.g, :code:`$HOME/go/src/yottadb`), +and ensure that directory is in the search path for packages. + +Then after step 2 (*Choose a directory for your default +environment and initialize it*) in the `Quick Start`_ section: + +3. Put your GO program in the :code:`$ydb_dir` directory, XYZ + (instructions to include headers). As a sample program, you can + download the wordfreq.go program [XYZ – provide actual URL for + wordfreq.go program when ready], with a `reference input file + `_ + and `corresponding reference output file + `_. Compile + it thus: [XYZ compilation instructions / command]. + +#. Run your program and verify that the output matches the reference output. For example: + +.. code-block:: bash + + $ cd $ydb_dir + $ # XYZ instructions to compile wordfreq.go to executable + $ ./wordfreq wordfreq_output_go.txt + $ diff wordfreq_output_go.txt wordfreq_output.txt + $ + +Note that the :code:`wordfreq.go` program randomly uses local or +global variables (see `Local and Global Variables`_). + +Go Concepts +=========== + +XYZ - Add anything special that a Go programmer should know before +using YottaDB. + +YottaDB Go methods and functions return an :code:`int` return +code. Return codes have values identical to C return codes (see `Go +Symbolic Constants`_). Where a method or function returns two values, +as permitted by Go, the second is the return code. + +As the YottaDB wrapper is packaged as a Go package, functions calls to +YottaDB must be prefixed in Go code with :code:`yottadb.`. + +Go Symbolic Constants +===================== + +For modules that use `cgo `_ to pull-in +:code:`$ydb_dist/libyottadb.h`, Go symbolic constants are the C +`Symbolic Constants`_ with each C symbolic constant prefixed with +:code:`C.`. For example, the normal C return code :code:`YDB_OK` is +:code:`C.YDB_OK` in Go, and C error return code +:code:`YDB_ERR_INVSTRLEN` is :code:`C.YDB_ERR_INVSTRLEN` in Go. + +Go Data Structures & Type Definitions +===================================== + +The :code:`C.ydb_buffer_t` structure, which is the +:code:`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 :code:`ydb_buffer_t` +structures are in memory managed by YottaDB. Go structures contain +pointers to the YottaDB structures so that if and when the Go garbage +collector moves Go structures, the pointers they contain remain valid. + +There are three structures for the interface between YottaDB and Go: +:code:`BufferT` for data, :code:`BufferTArray` for a list of +subscripts or a set of variable or lock resource names, :code:`KeyT` +for keys where a key in turn consists of a variable or lock resource +name and subscripts, as discussed in `Concepts`_. + +.. code-block:: go + + type BufferT struct { + cbuft *C.ydb_buffer_t // Pointer to C structure describing data + } + + type BufferTArray struct { + elemsAlloc uint // Number of elements allocated in array + elemsUsed uint // Number of elements in use + cbuftarray *C.ydb_buffer_t // Pointer to start of array of C structures describing data + } + + type KeyT struct { + Varnm BufferT // Pointer to variable name struct + SubAry BufferTArray // Pointer to subscript struct + } + +As these structures contain pointers to storage allocated by YottaDB, +allowing a structure to go out of scope without first driving its +:code:`Free()` method introduces a storage leak. Where possible, use +the Golang :code:`defer` statement to automatically drive the +appropriate free methods when these blocks go out of scope. + +For those fields in the structures described here that are not +directly accessible (because they start with lower case letters), +there are methods associated with their containing structures to +access and modify them. + +Methods for each structure are classified as either `Go Access +Methods`_ or `Go Simple API`_ methods. `Go Access Methods`_ are +methods implemented in the Go wrapper for managing the structures for +data interchange. `Go Simple API`_ methods that wrap functionality +exposed by the YottaDB API. + +Go Access Methods +================= + +----------------------------- +Go Access Methods for BufferT +----------------------------- + +Go BufferT Alloc() +------------------ + +.. code-block:: go + + Alloc(nBytes uint) + +Allocate: + +- a buffer in YottaDB heap space of size :code:`nBytes`; and +- a :code:`C.ydb_buffer_t` structure, also in YottaDB heap space, with + its :code:`buf_addr` referencing the buffer, its :code:`len_alloc` + set to :code:`nBytes` and its :code:`len_used` set to zero. + +Set :code:`cbuft` in the :code:`BufferT` +structure to reference the :code:`C.ydb_buffer_t` structure. + +Go BufferT Dump() +----------------- + +.. code-block:: go + + Dump() + +For debugging purposes, dump on stdout: + +- :code:`cbuft` as a hexadecimal address; +- for the :code:`C.ydb_buffer_t` structure referenced by + :code:`cbuft`: + + - :code:`buf_addr` as a hexadecimal address, and + - :code:`len_alloc` and :code:`len_used` as integers; and + +- at the address :code:`buf_addr`, the lower of :code:`len_used` or + :code:`len_alloc` bytes in `zwrite format`_. + +Go BufferT Free() +----------------- + +.. code-block:: go + + Free() + +The inverse of the :code:`Alloc()` method: release the buffer in +YottaDB heap space referenced by the :code:`C.ydb_buffer_t` structure, +release the :code:`C.ydb_buffer_t`, and set :code:`cbuft` in the +:code:`BufferT` structure to :code:`nil`. + +Go BufferT GetLenAlloc() +------------------------ + +.. code-block:: go + + GetLenAlloc() uint + +Return the :code:`len_alloc` field of the :code:`C.ydb_buffer_t` +structure referenced by :code:`cbuft` (zero if the structure has not +yet been allocated, i.e., :code:`cbuft` is :code:`nil`). + +Go BufferT GetLenUsed() +----------------------- + +.. code-block:: go + + GetLenUsed() uint, int + +Return the :code:`len_used` field of the :code:`C.ydb_buffer_t` +structure referenced by :code:`cbuft` as the first (:code:`uint`) +return value (zero if the structure has not yet been allocated, i.e., +:code:`cbuft` is :code:`nil`). + +If the :code:`len_used` field of the :code:`C.ydb_buffer_t` structure +is greater than its :code:`len_alloc` field (owing to a prior +:code:`C.YDB_ERR_INVSTRLEN` error), the return code is +:code:`C.YDB_ERR_INVSTRLEN`; otherwise, it is :code:`C.YDB_OK`. + +Go BufferT GetValBAry() +----------------------- + +.. code-block:: go + + GetValBAry() *[]byte, int + +If the :code:`len_used` field of the :code:`C.ydb_buffer_t` structure +referenced by :code:`cbuft` is greater than its :code:`len_alloc` +field (owing to a prior :code:`C.YDB_ERR_INVSTRLEN` error), return +:code:`len_alloc` bytes of the buffer referenced by the +:code:`C.ydb_buffer_t` structure referenced by :code:`cbuft` as a byte +array, and a return code of :code:`C.YDB_ERR_INVSTRLEN`. + +Otherwise, return :code:`len_used` bytes of the buffer as a byte array +(a zero length array if the structure has not yet been allocated, +i.e., :code:`cbuft` is :code:`nil`), and a return code of +:code:`C.YDB_OK`. + +Go BufferT GetValStr() +---------------------- + +.. code-block:: go + + GetValStr() *string, int + +If the :code:`len_used` field of the :code:`C.ydb_buffer_t` structure +referenced by :code:`cbuft` is greater than its :code:`len_alloc` +field (owing to a prior :code:`C.YDB_ERR_INVSTRLEN` error), return +:code:`len_alloc` bytes of the buffer referenced by the +:code:`C.ydb_buffer_t` structure referenced by :code:`cbuft` as a +string, and a return code of :code:`C.YDB_ERR_INVSTRLEN`. + +Otherwise, return :code:`len_used` bytes of the buffer as a string (a +zero length string if the structure has not yet been allocated, i.e., +:code:`cbuft` is :code:`nil`), and a return code of :code:`C.YDB_OK`. + +Go BufferT SetValBAry() +----------------------- + +.. code-block:: go + + SetValBAry(val *[]byte) int + + +If the length of :code:`val` is greater than the :code:`len_alloc` +field of the :code:`C.ydb_buffer_t` structure referenced by +:code:`cbuft`, make no changes and return :code:`C.YDB_ERR_INVSTRLEN`. + +Otherwise, copy the bytes of :code:`val` to the location referenced by +the :code:`buf_addr` field of the :code:`C.ydbbuffer_t` structure, set +the :code:`len_used` field to the length of :code:`val` and return +with a return code of :code:`C.YDB_OK`. + +Go BufferT SetValStr() +---------------------- + +.. code-block:: go + + SetVarStr(val *string) int + +If the length of :code:`val` is greater than the :code:`len_alloc` +field of the :code:`C.ydb_buffer_t` structure referenced by +:code:`cbuft`, make no changes and return :code:`C.YDB_ERR_INVSTRLEN`. + +Otherwise, copy the bytes of :code:`val` to the location referenced by +the :code:`buf_addr` field of the :code:`C.ydbbuffer_t` structure, set +the :code:`len_used` field to the length if :code:`val` and return +with a return code of :code:`C.YDB_OK`. + +---------------------------------- +Go Access Methods for BufferTArray +---------------------------------- + +Go BufferTArray Alloc() +----------------------- + +.. code-block:: go + + Alloc(numSubs, bufSiz uint) + +Allocate: + +- :code:`numSubs` buffers in YottaDB heap space, each of of size + :code:`bufSiz`; and +- an array of :code:`numSubs` :code:`C.ydb_buffer_t` structures, also + in YottaDB heap space. + +Set: + +- In each :code:`C.ydb_buffer_t` structure: + + - :code:`buf_addr` to the address of a buffer; + - :code:`len_alloc` to :code:`bufSiz`; and + - :code:`len_used` to zero. + +- In the :code:`BufferTArray` structure: + + - :code:`cbuftary` to reference the beginning of the :code:`C.ydb_buffer_t` array; + - :code:`elemsAlloc` field to :code:`numSubs`; and + - :code:`elemsUsed` is to zero. + +Go BufferTArray Dump() +---------------------- + +.. code-block:: go + + Dump() + +For debugging purposes, dump on stdout: + +- :code:`cbuftary` as a hexadecimal address; +- :code:`elemsAlloc` and :code:`elemsUsed` as integers; +- for each element of the smaller of :code:`elemsAlloc` and + :code:`elemsUsed` elements of the :code:`C.ydb_buffer_t` array + referenced by :code:`cbuftary`: + + - :code:`buf_addr` as a hexadecimal address, and + - :code:`len_alloc` and :code:`len_used` as integers; and + - the smaller of :code:`len_used` and :code:`len_alloc` bytes at the + address :code:`buf_addr`, in `zwrite format`_. + +Go BufferTArray Free() +---------------------- + +.. code-block:: go + + Free() + +The inverse of the :code:`Alloc()` method: release the :code:`numSubs` +buffers and the :code:`C.ydb_buffer_t` array. Set :code:`cbuftary` to +:code:`nil`, and :code:`elemsAlloc` and :code:`elemsUsed` to zero. + +Go BufferTArray GetAlloc() +-------------------------- + +.. code-block:: go + + GetAlloc() uint + +Return the :code:`elemsAlloc` field. + +Go BufferTArray GetLenAlloc() +----------------------------- + +.. code-block:: go + + GetLenAlloc() uint + +Return the :code:`len_alloc` from the :code:`C.ydb_buffer_t` structures +referenced by :code:`cbuftary`, all of which have the same value (zero +if the structure has not yet been allocated). + +Go BufferTArray GetLenUsed() +---------------------------- + +.. code-block:: go + + GetLenUsed(idx uint) uint, int + +- If :code:`idx` is greater than the :code:`elemsAlloc` of the + :code:`BufferTArray` structure, return with a return code of + :code:`C.YDB_ERR_INSUFFSUBS`. In this case, the return value (the + :code:`uint` returned) is not meaningful. +- Otherwise, return the :code:`len_used` field of the array element + specifed by :code:`idx` of the :code:`C.ydb_buffer_t` array referenced + by :code:`cbuftary` with a return code of :code:`C.YDB_OK`. + +Go BufferTArray GetUsed() +------------------------- + +.. code-block:: go + + GetUsed() uint + +Return the value of the :code:`elemsUsed` field. + +Go BufferTArray GetValBAry() +---------------------------- + +.. code-block:: go + + GetValBAry(idx uint) *[]byte, int + +- If :code:`idx` is greater than :code:`elemsAlloc`, return a zero + length byte array and a return code of :code:`C.YDB_ERR_INSUFFSUBS`. +- If the :code:`len_used` field of the :code:`C.ydb_buffer_t` + structure specified by :code:`idx` is greater than its + :code:`len_alloc` field (owing to a previous + :code:`C.YDB_ERR_INVSTRLEN` error), return a byte array + containing the :code:`len_alloc` bytes at :code:`buf_addr` and a + return code of :code:`C.YDB_ERR_INVSTRLEN`. +- Otherwise, return a byte array containing the :code:`len_used` bytes + at :code:`buf_addr` and a return code of :code:`C.YDB_OK`. + +Go BufferTArray GetValStr() +--------------------------- + +.. code-block:: go + + GetValStr(idx uint) *string, int + +- If :code:`idx` is greater than :code:`elemsAlloc`, return a zero + length string and a return code of :code:`C.YDB_ERR_INSUFFSUBS`. +- If the :code:`len_used` field of the :code:`C.ydb_buffer_t` + structure specified by :code:`idx` is greater than its + :code:`len_alloc` field (owing to a previous + :code:`C.YDB_ERR_INVSTRLEN` error), return a string + containing the :code:`len_alloc` bytes at :code:`buf_addr` and a + return code of :code:`C.YDB_ERR_INVSTRLEN`. +- Otherwise, return a string containing the :code:`len_used` bytes at + :code:`buf_addr` and a return code of :code:`C.YDB_OK`. + +Go BufferTArray SetUsed() +------------------------- + +.. code-block:: go + + SetUsed(newUsed uint) int + +Use this method to set the current number of valid strings (subscripts +or variable names) in the :code:`BufferTArray`. + +- If :code:`newUsed` is greater than :code:`elemsAlloc`, make no + changes and return with a return code of + :code:`C.YDB_ERR_INSUFFSUBS`. +- Otherwise, set :code:`elemsUsed` to :code:`newUsed` and return with a + return code of :code:`C.YDB_OK`. + +Note that even if :code:`newUsed` is not greater than the value of +:code:`elemsAlloc`, using an :code:`elemsUsed` value greater than the +number of valid values in the array will likely lead to hard-to-debug +errors. + +Go BufferTArray SetValBAry() +---------------------------- + +.. code-block:: go + + SetValBAry(idx int, val *[]byte) int + +- If :code:`idx` is greater than :code:`elemsAlloc` make no changes + and return with a return code of :code:`C.YDB_ERR_INSUFFSUBS`. +- Otherwise, if the length of :code:`val` is greater than the + :code:`len_alloc` field of the array element specified by :code:`idx`, + set the :code:`len_used` field of that array element to the required + length, and return :code:`C.YDB_ERR_INVSTRLEN`. +- Otherwise, copy the bytes of :code:`val` to the location referenced + by the :code:`buf_addr` field of the :code:`C.ydb_buffer_t` + structure referenced, set its :code:`len_used` field to the number + of bytes copied and return :code:`C.YDB_OK`. + +Go BufferTArray SetValStr() +--------------------------- + +.. code-block:: go + + SetValStr(idx int, val *string) int + +- If :code:`idx` is greater than :code:`elemsAlloc` make no changes + and return with a return code of :code:`C.YDB_ERR_INSUFFSUBS`. +- Otherwise, if the length of :code:`val` is greater than the + :code:`len_alloc` field of the array element specified by :code:`idx`, + set the :code:`len_used` field of that array element to the required + length, and return :code:`C.YDB_ERR_INVSTRLEN`. +- Otherwise, copy the bytes of :code:`val` to the location referenced + by the :code:`buf_addr` field of the :code:`C.ydb_buffer_t` + structure referenced, set its :code:`len_used` field to the number + of bytes copied and return :code:`C.YDB_OK`. + +-------------------------- +Go Access Methods for KeyT +-------------------------- + +As the members of :code:`KeyT` are visible to Go programs (they start +with upper-case letters), and application code can call the +:code:`BufferT` methods on :code:`Varnm` and :code:`BufferTArray` +methods on :code:`SubAry`, the `Go KeyT Alloc()`_, `Go KeyT Dump()`_ +and `Go KeyT Free()`_ methods are available for programming +convenience. + +Go KeyT Alloc() +--------------- + +.. code-block:: go + + Alloc(varSiz, numSubs, subSiz uint) + +Invoke :code:`Varnm.Alloc(varSiz)` (see `Go BufferT Alloc()`_) and +:code:`SubAry.Alloc(numSubs, subSiz)` (see `Go BufferTArray +Alloc()`_). + +Go KeyT Dump() +-------------- + +.. code-block:: go + + Dump() + +Invoke :code:`Varnm.Dump()` (see `Go BufferT Dump()`_) and +:code:`SubAry.Dump()` (see `Go BufferTArray Dump()`_). + +Go KeyT Free() +-------------- + +.. code-block:: go + + Free() + +Invoke :code:`Varnm.Free()` (see `Go BufferT Free()`_) and +:code:`SubAry.Free()` (see `Go BufferTArray Free()`_). + + +Go Simple API +============= + +The Go Simple API consists of `Go Simple API BufferT Methods`_, `Go +Simple API BufferTArray Methods`_, `Go Simple API KeyT Methods`_ and +`Go Simple API Functions`_. 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 Simple API BufferT Methods +----------------------------- + +Go Str2ZwrS() +------------- + +.. code-block:: go + + Str2ZwrS(zwr *BufferT) int + +The method wraps `ydb_str2zwr_s()`_ to provide the string in `zwrite +format`_. + +- If :code:`len_alloc` is not large enough, set :code:`len_used` to + the required length, and return with a return code of + :code:`C.YDB_ERR_INVSTRLEN`. In this case, :code:`len_used` will be + greater than :code:`len_alloc` until corrected by application code. +- Otherwise, set the buffer referenced by :code:`buf_addr` to the + `zwrite format`_ string, set :code:`len_used` to the length, and + return with a return code of :code:`C.YDB_OK`. + +Go Zwr2StrS() +------------- + +.. code-block:: go + + Zwr2StrS(str *BufferT) int + +This method wraps `ydb_zwr2str_s()`_ and is the inverse of `Go +Str2ZwrS()`_. + +- If :code:`len_alloc` is not large enough, set :code:`len_used` to + the required length, and return with a return code of + :code:`C.YDB_ERR_INVSTRLEN`. In this case, :code:`len_used` will be + greater than :code:`len_alloc` until corrected by application code. +- If :code:`str` has errors and is not in valid `zwrite format`_,, set + :code:`len_used` to zero, and return the error code returned by + `ydb_zwr2str_s()`_ prefixed by :code:`C.`, e.g., + :code:`C.YDB_ERR_INVZWRITECHAR`. +- Otherwise, set the buffer referenced by :code:`buf_addr` to the + unencoded string, set :code:`len_used` to the length, and return + with a return code of :code:`C.YDB_OK`. + +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 Simple API BufferTArray Methods +---------------------------------- + +Go DeleteExclS() +---------------- + +.. code-block:: go + + DeleteExclS() int + +:code:`DeleteExclS()` wraps `ydb_delete_excl_s()`_ to delete all local +variable trees except those of local variables whose names are +specified in the :code:`BufferTArray` structure, with a return code of +:code:`C.YDB_OK`. In the special case where :code:`elemsUsed` is zero, +the method deletes all local variable trees. If your application mixes +M and Go code, be sure to read and understand the warning in the +description of `ydb_delete_excl_s()`_. + +In the unlikely event that the :code:`elemsUsed` exceeds +:code:`C.YDB_MAX_NAMES`, the return code is +:code:`C.YDB_ERRNAMECOUNT2HI`. Any other error results in an `error +return code`_ prefixed with :code:`C.`. + +Go TpS() +-------- + +.. code-block:: go + + TpS(tpfn unsafe pointer, tpfnparm unsafe.pointer, transid *string) int + +:code:`TpS()` wraps `ydb_tp_s()`_ to implement `Transaction +Processing`_. :code:`tpfn` is a pointer to a C function with one +parameter, :code:`tpfnparm`, a pointer to an arbitrary data structure +in YottaDB heap space. Please see both the description of +`ydb_tp_s()`_ and the section on `Transaction Processing`_ for +details. + +Since Go does not permit a pointer to a Go function to be passed as a +parameter to a C function, :code:`tpfn` is required to be a pointer to +a C function. For a pure Go application, the C function is a glue +routine that in turn calls the Go function. The YottaDB Go wrapper +provides a shell script `GenYDBGlueRoutine.sh`_ to generate glue +routine functions. + +A function implementing logic for a transaction should return one of +the following: + +- :code:`C.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. +- :code:`C.YDB_TPRESTART` 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 + :code:`C.YDB_TPRESTART`. +- :code:`C.YDB_ROLLBACK` to indicate that :code:`TpS()` should not + commit the transaction, and should return :code:`C.YDB_ROLLBACK` to + the caller. + +In order to provide the function implementing the transaction logic +with a parameter or parameters, :code:`tpfnparm` is passed to the glue +routine, in turn be passed to the Go function called by the glue +routine. As :code:`tpfnparm` is passed from Go to YottaDB and back to +Go, the memory it references should be allocated using +`Go Malloc()`_ to protect it from the Go garbage collector. + +The :code:`BufferTArray` receiving the :code:`TpS()` method is a list +of local variables whose values should be saved, and restored to their +original values when the transaction restarts. If :code:`elemsUsed` is +zero, no local variables are saved and restored; and if +:code:`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 :code:`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()`_. + +A special note: as the definition and implementation of Go protect +against dangling pointers in pure Go code, Go application code may not +be designed and coded with the same level of defensiveness against +dangling pointers that C applications are. In the case of +:code:`TpS()`, owing to the need to use :code:`unsafe pointer` +parameters, please take additional care in designing and coding your +application to ensure the validity of the pointers passed to +:code:`TpS()`. + +-------------------------- +Go Simple API KeyT Methods +-------------------------- + +Go DataS() +---------- + +.. code-block:: go + + DataS() uint, int + +:code:`DataS()` returns the result of `ydb_data_s()`_ with a return +code of :code:`C.YDB_ERR_OK` or an `error return code`_ prefixed with +:code:`C.`. In the latter case, the return value is unspecified. + +Go DeleteS() +------------ + +.. code-block:: go + + DeleteS(deltype int) int + +:code:`DeleteS()` wraps `ydb_delete_s()`_ to delete a local or global +variable tree, with a value of :code:`C.YDB_DEL_NODE` for +:code:`deltype` specifying that only the node should be deleted, +leaving the tree untouched, and a value of :code:`C.YDB_DEL_TREE` +specifying that the node as well as the tree are to be deleted. The +return code is :code:`C.YDB_OK` or an `error return code`_ prefixed +with :code:`C.`. + +Go GetS() +---------- + +.. code-block:: go + + GetS(retval *BufferT) int + +:code:`GetS()` wraps `ydb_get_s()`_ to return the value at the +referenced global or local variable node, or intrinsic special +variable, in the buffer referenced by the :code:`BufferT` structure +referenced by :code:`retval`. + +- If `ydb_get_s()`_ returns an error code such as + :code:`C.YDB_ERR_GVUNDEF`, :code:`C.YDB_ERR_INVSVN`, + :code:`C.YDB_ERR_LVUNDEF`, or another `error return code`_ prefixed + with :code:`C.`, the method makes no changes to the structures under + :code:`retval` and returns the error code. +- Otherwise, if the length of the data to be returned exceeds + :code:`retval.getLenAlloc()`, the method sets the :code:`len_used` + of the :code:`C.ydb_buffer_t` referenced by :code:`retval` + to the required length, and returns with a return code of + :code:`C.YDB_ERR_INVSTRLEN`. +- Otherwise, it copies the data to the buffer referenced by the + :code:`retval.buf_addr`, sets :code:`retval.lenUsed` to its + length and returns with a return code of :code:`C.YDB_OK`. + +Go IncrS() +---------- + +.. code-block:: go + + IncrS(incr, retval *BufferT) int + +:code:`IncrS()` wraps `ydb_incr_s()`_ to atomically increment the +referenced global or local variable node coerced to a number with +:code:`incr` coerced to a number, with the result stored in the node +and returned through the :code:`BufferT` structure referenced by +:code:`retval`. + +- If `ydb_incr_s()`_ returns an error codes such as + :code:`C.YDB_ERR_NUMOFLOW`, :code:`C.YDB_ERR_INVSTRLEN`, or another + `error return code`_ prefixed with :code:`C.`, the method makes no + changes to the structures under :code:`retval` and returns the error + code. +- Otherwise, if the length of the data to be returned exceeds + :code:`retval.lenAlloc`, the method sets the :code:`len_used` + of the :code:`C.ydb_buffer_t` referenced by :code:`retval` + to the required length, and returns with a return code of + :code:`C.YDB_ERR_INVSTRLEN`. +- Otherwise, it copies the data to the buffer referenced by the + :code:`retval.buf_addr`, sets :code:`retval.lenUsed` to its + length, and returns with a return code of :code:`C.YDB_OK`. + +Go LockDecrS() +-------------- + +.. code-block:: go + + LockDecrS() int + +:code:`LockDecrS()` wraps `ydb_lock_decr_s()`_ 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. The +return code is :code:`C.YDB_OK` unless there is an invalid lock +resource name or other error, resulting in an `error return code`_ +prefixed with :code:`C.`. + +Go LockIncrS() +-------------- + +.. code-block:: go + + LockIncrS(timeoutNsec uint64) int + +The :code:`LockIncrS()` method wraps `ydb_lock_incr_s()`_ to attempt +to acquire the referenced lock resource name without releasing any +locks the process already holds. + +- If the process already holds the lock resource named, the method + increments the count and returns with a return code of + :code:`C.YDB_OK`. +- If :code:`timeoutNsec` exceeds :code:`C.YDB_MAX_TIME_NSEC`, the + method returns with an error return code of + :code:`C.YDB_ERR_TIME2LONG`. +- If it is able to aquire the lock resource within :code:`timeoutNsec` + nanoseconds, it returns with a return code of :code:`C.YDB_OK`; + otherwise it returns with a return code of + :code:`C.YDB_LOCK_TIMEOUT`. If :code:`timeoutNsec` is zero, the + method makes exactly one attempt to acquire the lock. +- Other errors return an appropriate `error return code`_ prefixed + with :code:`C.`. + +Go NodeNextS() +-------------- + +.. code-block:: go + + NodeNextS(next *BufferTArray) int + +:code:`NodeNext()` wraps `ydb_node_next_s()`_ to facilitate depth +first traversal of a local or global variable tree. + +- If there is a next node: + + - If the number of subscripts of that next node exceeds + :code:`next.elemsAlloc`, the method sets + :code:`next.elemsUsed` to the number of subscripts + required, and returns with a return code of + :code:`C.YDB_ERR_INSUFFSUBS`. In this case the :code:`elemsUsed` + is greater than :code:`elemsAlloc`. + - If one of the :code:`C.ydb_buffer_t` structures referenced by + :code:`next` (call the first or only element :code:`n`) has + insufficient space for the corresponding subscript, the method sets + :code:`next.elemsUsed` to :code:`n`, and the + :code:`len_alloc` of that :code:`C.ydb_buffer_t` structure to the + actual space required. The method returns with a return code of + :code:`C.YDB_ERR_INVSTRLEN`. In this case the :code:`len_used` of + that structure is greater than its :code:`len_alloc`. + - Otherwise, it sets the structure :code:`next` to reference the + subscripts of that next node, and returns with a return code of + :code:`C.YDB_OK`. + +- If the node is the last in the tree, the method returns with a + return code of :code:`C.YDB_NODE_END`, making no changes to the + structures below :code:`next`. + +:code:`NodeNextS()` may also report another `error return code`_ +prefixed with :code:`C.`. + +Go NodePrevS() +-------------- + +.. code-block:: go + + NodePrevS(prev *BufferTArray) int + +:code:`NodePrevS()` wraps `ydb_node_previous_s()`_ to facilitate +reverse depth first traversal of a local or global variable tree. + +- If there is a previous node: + + - If the number of subscripts of that previous node exceeds + :code:`prev.elemsAlloc`, the method sets + :code:`prev.elemsUsed` to the number of subscripts + required, and returns with a return code of + :code:`C.YDB_ERR_INSUFFSUBS`. In this case the :code:`elemsUsed` + is greater than :code:`elemsAlloc`. + - If one of the :code:`C.ydb_buffer_t` structures referenced by + :code:`prev` (call the first or only element :code:`n`) has + insufficient space for the corresponding subscript, the method sets + :code:`prev.elemsUsed` to :code:`n`, and the :code:`len_alloc` of + that :code:`C.ydb_buffer_t` structure to the actual space + required. The method returns with a return code of + :code:`C.YDB_ERR_INVSTRLEN`. In this case the :code:`len_used` of + that structure is greater than its :code:`len_alloc`. + - Otherwise, it sets the structure :code:`prev` to reference the + subscripts of that prev node, and returns with a return code of + :code:`C.YDB_OK`. + +- If the node is the first in the tree, the method returns with a + return code of :code:`C.YDB_NODE_END`, making no changes to the + structures below :code:`prev`. + +:code:`NodePrevS()` may also report another `error return code`_ +prefixed with :code:`C.`. + +Go SetS() +------------ + +.. code-block:: go + + SetS(val *BufferT) int + +At the referenced local or global variable node, or the intrinsic +special variable, :code:`SetS()` wraps `ydb_set_s()`_ to set +the value specified by :code:`val`, returning a return code of +:code:`C.YDB_OK` or another `error return code`_ prefixed with +:code:`C.`. + +Go SubNextS() +------------- + +.. code-block:: go + + SubNextS(sub *BufferT) int + +:code:`SubNextS()` wraps `ydb_subscript_next_s()`_ to facilitate +breadth-first traversal of a local or global variable sub-tree. + +- 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 + :code:`sub.len_alloc`, the method sets :code:`sub.len_used` to + the actual length of that subscript, and returns with a return + code of :code:`C.YDB_ERR_INVSTRLEN`. In this case + :code:`sub.len_used` is greater than :code:`sub.len_alloc`. + - Otherwise, it copies that subscript to the buffer referenced by + :code:`sub.buf_addr`, sets :code:`buf.len_used` to its length, and + returns with a return code of :code:`C.YDB_OK`. + +- If there is no next node or subtree at that level of the subtree, + the method returns with a return code of :code:`C.YDB_OK` and + sets :code:`sub.len_used` to zero. + +:code:`SubNextS()` may also report an `error return code`_ prefixed +with :code:`C.`. + +Go SubPrevS() +------------- + +.. code-block:: go + + SubPrevS(sub *BufferT) int + +:code:`SubPrevS()` wraps `ydb_subscript_previous_s()`_ to facilitate +reverse breadth-first traversal of a local or global variable sub-tree. + +- 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 + :code:`sub.len_alloc`, the method sets :code:`sub.len_used` to + the actual length of that subscript, and returns with a return + code of :code:`C.YDB_ERR_INVSTRLEN`. In this case + :code:`sub.len_used` is greater than :code:`sub.len_alloc`. + - Otherwise, it copies that subscript to the buffer referenced by + :code:`sub.buf_addr`, sets :code:`buf.len_used` to its length, and + returns with a return code of :code:`C.YDB_OK`. + +- If there is no previous node or subtree at that level of the subtree, + the method returns with a return code of :code:`C.YDB_OK` and + sets :code:`sub.len_used` to zero. + +:code:`SubPrevS()` may also report an `error return code`_ prefixed +with :code:`C.`. + +----------------------- +Go Simple API Functions +----------------------- + +Go LockS() +---------- + +.. code-block:: go + + yottadb.LockS(timeoutNsec uint64, lockName ... *KeyT) int + +The :code:`LockS()` function wraps `ydb_lock_s()`_ to release all lock +resources currently held and then attempt to acquire the named lock +resources referenced. Upon return, the process will have acquired all +of the named lock resources or none of the named lock resources. + +- If :code:`timeoutNsec` exceeds :code:`C.YDB_MAX_TIME_NSEC`, the + method returns with an error return code of + :code:`C.YDB_ERR_TIME2LONG`. +- If it is able to aquire the lock resources within :code:`timeoutNsec` + nanoseconds, it returns with a return code of :code:`C.YDB_OK`; + otherwise it returns with a return code of + :code:`C.YDB_LOCK_TIMEOUT`. If :code:`timeoutNsec` is zero, the + method makes exactly one attempt to acquire the lock resources. +- Other errors return an appropriate `error return code`_ prefixed + with :code:`C.`. + +Go Comprehensive API +==================== + +The Go Comprehensive API is a project for the future, to follow the C +`Comprehensive API`_ + +Go Utility Functions +==================== + +--------- +Go Exit() +--------- + +.. code-block:: go + + yottadb.Exit() int + +For a process that wishes to close YottaDB databases and no longer use +YottaDB, the function wraps `ydb_exit()`_ so that any further calls to +YottaDB result in a :code:`C.YDB_ERR_CALLINAFTEREXIT` error. + +Typical processes will not need to call :code:`Exit()` because +normal process termination closes databases cleanly. However, a +process that has completed its use of YottaDB, but intends to continue +with other work for some non-trivial period of time, should call +:code:`Exit()`. + +See also `Go ForkExec()`_. + +------------- +Go ForkExec() +------------- + +.. code-block:: go + + yottadb.ForkExec(argv0 string, argv []string, attr *ProcAttr) int, int + +The function has the same signature as the `Go +syscall.ForkExec() `_, which +it wraps. The YottaDB `Go ForkExec()`_ ensures that the child process +is safely disconnected from YottaDB interprocess communication +resources and database files. + +Application code that must use :code:`syscall.Forkexec()` should call +`Go Exit()`_ first, which means that after calling +:code:`syscall.ForkExec()`, a process can no longer call the YottaDB +runtime system. + +-------------- +Go ForkNCore() +-------------- + +.. code-block:: go + + yottadb.ForkNCore() + +The function wraps `ydb_fork_n_core()`_ to generate a core file +snapshot of a process for debugging purposes. See `ydb_fork_n_core()`_ +for more information. + +:code:`ForkNCore()` has no parameters and returns no return code. + +--------- +Go Free() +--------- + +.. code-block:: go + + yottadb.Free(ptr unsafe.pointer) + +The function wraps `ydb_free()`_ to release memory previously +allocated using :code:`Malloc()`. As passing a :code:`ptr` not +previously allocated using :code:`Malloc()` will result in +unpredictable behavior, application code should be written with an +appropriate level of diligence when calling :code:`Free()`. + +--------- +Go Init() +--------- + +.. code-block:: go + + yottadb.Init() int + +The function wraps `ydb_init()`_ to initialize the YottaDB runtime +system. This call is normally not required as YottaDB initializes +itself on its first call, the exception being when an application +wishes to set its own signal handlers (see +`Signals`_). :code:`Init()` returns :code:`C.YDB_OK` or an `error +return code`_ prefixed with :code:`C.`. + +------------ +Go Release() +------------ + +.. code-block:: go + + yottadb.Release() 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 idenfify the Go wrapper. +- The Go wrapper release number, which starts with “r” and is followed + by two numbers separated by a period (“.”), e.g., “r1.02”. The first + is a major release number and the second is a minor release number + under the major release. Even minor release numbers indicate + formally released software. Odd minor release numbers indicate + software builds from “in flight” code under development, between + releases. +- The fourth through sixth pieces are `$zyrelease`_ from the underlying + YottaDB relase. + +----------- +Go Malloc() +----------- + +.. code-block:: go + + yottadb.Malloc(size uint64) unsafe.pointer + +The function wraps `ydb_malloc()`_ to allocate :code:`size` bytes of +storage managed by YottaDB. Use of :code:`Malloc()` to allocate +storage provides debugging tools. Using any function other than +:code:`Free()` to release storage allocated with +:code:`Malloc()` has unpredictable results. + +As the definition and implementation of Go protect against dangling +pointers in pure Go code, Go application code may not be designed and +coded with the same level of defensiveness against dangling pointers +that C applications are. Please take additional care in designing and +coding your application to ensure the correct use of application +storage allocated using :code:`Malloc()`. + +---------------- +Go TimerCancel() +---------------- + +.. code-block:: go + + yottadb.TimerCancel(timerid uintptr) + +The function wraps `ydb_timer_cancel()`_ to cancel a timer previously +established using :code:`TimerStart()`. :code:`timerid` is the id +of the timer. The function returns no return code. + +--------------- +Go TimerStart() +--------------- + +.. code-block:: go + + yottadb.TimerStart(timerid uintptr, + limitNsec uint64, + handler unsafe.pointer, + handlerDataLen uint, + handlerData unsafe.pointer) int + +The function wraps `ydb_timer_start()`_ to start a timer. Unless +canceled, after the timer expires, YottaDB invokes the handler +function referenced by :code:`handler`, passing it :code:`handlerData` +as a parameter. + +Since Go does not permit a pointer to a Go function to be passed as a +parameter to a C function, :code:`handler` is required to be a pointer +to a C function. For a pure Go application, the C function is a glue +routine that in turn calls the Go function. YottaDB provides a shell +script `GenYDBGlueRoutine.sh`_ to generate glue routine functions. The +:code:`handlerData` structure should be in memory allocated with `Go +Malloc()`_ to protect it from Go garbage collection. + +Owing the need to use :code:`unsafe pointer` parameters, please take +additional care in designing and coding your application to ensure the +validity of the pointers passed to :code:`TimerStart()`. + +Go Programming Notes +==================== + +These `Go Programming Notes`_ supplement rather than supplant the more +general `Programming Notes`_ for C. + +---------- +Goroutines +---------- + +As the YottaDB runtime system is not thread-safe, and because the +internal data structures and operating system interfaces of Go are not +part of a stable API, out of an abundance of caution, the initial +implementation of the YottaDB Go Wrapper is restricted to executing +its logic in a single goroutine. + +In order to avoid restricting Go applications from calling the YottaDB +API from a single goroutine (which would be unnatural to a Go +programmer), the YottaDB Go wrapper includes logic that coerces the +execution of the YottaDB runtime system to a single +goroutine. 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. + +-------------------- +GenYDBGlueRoutine.sh +-------------------- + +As discussed in `Go TpS()`_ and `Go TimerStart()`_, as Go does not +permit a pointer to a Go function to be passed as a parameter to a C +function, Go functions that encapsulate logic to be executed as an +ACID transaction, and Go functions that serve as handlers for +expired timers cannot be passed as parameters to :code:`TpS()` and +:code:`yottadb.TimerStart()`. Instead, each Go function must have a C +“glue” routine whose address is passed to :code:`TpS()` or +:code:`yottadb.TimerStart()`. + +If an application has a callback routine called :code:`CallBackRtn()`, +executing :code:`GenYDBGlueRoutine.sh CallBackRtn` will generate a +routine :code:`CallBackRtn_cgo.go`. In the following example, +:code:`/usr/lib/yottadb/r122` is the directory where YottaDB r1.22 +(the YottaDB release to be used) resides. + +.. code-block:: go + + // #cgo CFLAGS: -I/usr/lib/yottadb/r122 + // #include "libyottadb.h" + // #cgo LDFLAGS: -L/usr/lib/yottadb/r122 -lyottadb -Wl,-rpath,/usr/lib/yottadb/r122 + // int CallBackRtn_cgo(uintptr_t in); // Forward declaration + import "C" + +The module in which :code:`CallBackRtn()` is defined should be +structured thus: + +.. code-block:: go + + //export CallBackRtn // no space between "//" and "export" + func CallBackRtn(parm uintptr) int { + // code for function + } + +:code:`CallBackRtn_cgo.go` and the module defining +:code:`CallBackRtn()` should reside in the same directory. + ================ Programming in M ================