mcc.umod

Udb module script compiler

Synopsis

mcc.umod in_script [out.c|out.cpp] [out.so]

Description

This is the Udb module script compiler. It converts a script written in C/C++ and module commands into a dynamic module for Udb.

This compiler is normally used internally by aq_udb -mod for on-the-fly module generation. However, it can also be used to develop modules manually. Simply install the manually created module (the .so file) in the appropriate location and Udb will be able to use it.

Options

in_script
Required. The module script to compile. If the script come from stdin, set in_script to ‘-‘ (a single dash).
out.c|out.cpp

Save the intermediate source to an output file. This is a C/C++ source file generated based on the input module script. It closely ressembles the original script except for some added support/interface code.

The output file must have a .c or .cpp extension. Only one of the two can be specified. Whether to save this output is optional. Use it for debugging or to help module development as needed.

out.so
Save the final module to an output file. It is the compiled result of the intermediate source. The output file must have a .so extension.

Module Commands

Module commands abstract and hide most of the module API details. They resemble C macros, as in COMMAND(parameters). The commands consist of declaration statements, processing function specifications and module helpers. They tell the module compiler what code to generate before building the final dynamic module.

Module Script Syntax

A module script is primarily a C/C++ source with certain embedded module commands. This is a sample script that normalizes a column:

DECL_LANG(C);
DECL_COLUMN_DYNAMIC(Tab.Col_in_out, F);
DECL_END;
MOD_INIT_FUNC()
{
  if (arg_n != 1) return 0;
  if (!MOD_COLUMN_BIND(Tab.Col_in_out, arg[0])) return 0;
  return 1;
}
MOD_BUCKET_FUNC()
{
  CDAT_F_T sum;
  sum = 0;
  MOD_TABLE_SCAN(Tab) {
    sum += $Tab.Col_in_out;
  }
  if (sum != 0) {
    MOD_TABLE_SCAN(Tab) {
      $Tab.Col_in_out /= sum;
    }
  }
  return 1;
}

Column Datatypes

Columns are type specific. Column types are defined in the data spec. In the module script, a C/C++ variable of the appropriate type must be used when copying or manipulating column values. These are the Udb column types and their corresponding module types/typedefs:

Spec type Program typedef Module typedef Description
S HStr * CDAT_S_T A pointer to a hash string data structure. It represents stored column strings. Not for input strings during imports.
S const StrData * CDAT_SB_T A pointer to a constant temporary string. It represents input strings during imports, see MOD_IMP_CDAT() for details.
F double CDAT_F_T A double precision floating point number.
L u_int64_t CDAT_L_T An unsigned (always positive) 64bit integer.
LS int64_t CDAT_LS_T A 64bit integer.
I u_int32_t CDAT_I_T An unsigned (always positive) 32bit integer.
IS int32_t CDAT_IS_T A 32bit integer.
IP NetIp CDAT_IP_T An IP address data structure.

Declaration Statements

Declaration statements are used to declare variables and options. The compiler interprets these declarations and determines what code to generate. For example, column declarations will result in column handling code, variable declarations will result in variable handling code, and so on.

  • Each declaration must start at the beginning of a line.
  • Each declaration must be given on a single line.
  • One declaration per line.
DECL_LANG(Lang);

Tell the compiler what programming language is being used in the script. Lang can either be C or CPP. Default is C.

Example:

DECL_LANG(C);
  • Specify that C code is being used in the script. C is the default, so this declaration is not strictly necessary.
DECL_BUILD_OPT(Arguments);

Supply custom command line arguments for the compiler. Use cases are:

  • Add a custom include path; e.g., -Imy_include_directory.
  • Add a custom define; e.g., -DMY_DEF=1.
  • Link a custom library with the module; e.g., my_dir/my_lib.a.
  • Add a required runtime library; e.g., -lm for the math library.

Example:

DECL_BUILD_OPT(-DMY_VERSION_STRING='"1.1.1"' -lm);
  • Define the value of “MY_VERSION_STRING”.
  • Indicate the need for the math library.
DECL_COLUMN(TabName.ColName, ColType);

Declare a column for use in the script.

  • TabName.ColName is a column in the database. The given name and type will be verified at run time during module initialization to ensure that the spec is valid.
  • Although table and column names are normally case insensitive, they are case sensitive within the script. This is because table and column names are used to compose variable names in the generated code. For example, if “MyTable” is a valid table, any case insensitive forms of the name (e.g., “mytable”) can be used to reference it in the script. However, once a form is chosen, no other forms should be used to reference the same object.
  • Use multiple declarations as needed.

Example:

DECL_COLUMN(TabName_1.ColName_1, I);
  • TabName_1 and ColName_1 are actual table and column names. They are specified as-is, like a variable (not a string).
DECL_COLUMN_DYNAMIC(TabName.ColName, ColType);

Declare a column for the script just like DECL_COLUMN(), except that the actual target table and column names are not known until run time (hence, dynamic).

  • This statement essentially declares a column variable. MOD_COLUMN_BIND() must be called at run time to bind the column variable to the desired column by name.
  • Use multiple declarations as needed.

Example:

DECL_COLUMN_DYNAMIC(Tab.Col_in_out, F);
MOD_INIT_FUNC()
{
  if (!MOD_COLUMN_BIND(Tab.Col_in_out, "RealTable.RealColumn")) return 0;
  ...
}
  • Declare a dynamic column. Then resolve it at run time during module initialization.
DECL_DATA(VarDecl);

Declare one or more variables as the module’s instance specific data. Unlike global variables which are shared between concurrent instances of the same module, variables declared this way are instance specific (i.e., each instance has its own copies of the variables). This is the recommended way of managing module data.

  • VarDecl is a variable declaration like int num1, num2.
  • Declared variables can later be referenced useing the MOD_DATA() macro; e.g., MOD_DATA(num1) and MOD_DATA(num2) will access the values of those integers.
  • Declared variables are automatically initialized to 0. Initialize them manually in MOD_INIT_FUNC() if a different initial value is desired.
  • Use multiple declarations as needed.

Example:

DECL_DATA(int flag);
DECL_DATA(int num1, num2);
MOD_INIT_FUNC()
{
  if (...) MOD_DATA(flag) = 1; else MOD_DATA(flag) = 2;
  ...
}
MOD_ROW_FUNC(TabName_1)
{
  if (MOD_DATA(flag) == 1) MOD_DATA(num1) += 1;
  else if (MOD_DATA(flag) == 2) MOD_DATA(num2) += 1;
  ...
}
  • Declare 3 instance variables. flag is conditionally initialized to 1 or 2 during module initialization. num1 and num2 are already initialized to 0 automatically.
  • The variables are then used in a row function.
DECL_END;
Mark the end of module declarations. The compiler will generated and insert the module data declaration code. If this is not given, declaration code will be inserted in front of the first processing function.

Processing Functions

The processing functions carry out the intended task of a module. There are several predefined module functions - one optional initialization function, one or more processing functions and one optional wrap up function. If any of them are defined, the compiler will generate code that call these function automatically.

A module function is defined like a C function:

PREDEFINED_FUNCTION_NAME(function_dependent_argument)
{
  code_block
  ...
}
  • The first line is the function name (one of the MOD_*_FUNC()) and argument (function dependent) specification.
  • The function name must start at the beginning of a line.
  • A code block enclosed in “{ ... }” must follow the specification line.
  • The code block can be written in C/C++. It can make use of the helpers described below (and in “etc/include/umod.h”).
MOD_INIT_FUNC()

Define a function for module initialization.

  • It is called once during module preparation.
  • It is called with these implicit arguments:
    • ModCntx *mod - A module instance handle. Pass this to any support functions that use module helpers.
    • const char *const *arg, int arg_n - The parameters passed to the module when it was called on the command line is available here as a string array. Use them to set up run time parameters as necessary.
  • It must return an integer:
    • 1 - Success.
    • 0 - Failure. The relevant Udb action will terminate.

Example:

MOD_INIT_FUNC()
{
  if (arg_n != 1) return 0;
  if (!MOD_COLUMN_BIND(Tab.Col_in_out, arg[0])) return 0;
  return 1;
}
  • Bind the dynamic column``Tab.Col_in_out`` to the name given as the first argument to the module (recall that arg and arg_n are implicit variables in the function).
MOD_BUCKET_FUNC()

Define a function for user bucket processing during an Udb export/count/scan operation.

  • It is called for each user bucket being processed.
  • Use it to scan tables in the current user bucket, examine and/or modify column values, and so on.
  • It is called with this implicit argument:
    • ModCntx *mod - A module instance handle. Pass this to any support functions that use module helpers.
  • It must return an integer that tells Udb what to do:
    • 1 - Success. Udb will continue normal processing.
    • 0 - Failure. Udb will stop processing the current user bucket and skip to the next one.

Example:

MOD_BUCKET_FUNC()
{
  CDAT_F_T sum;
  sum = 0;
  MOD_TABLE_SCAN(Tab) {
    sum += $Tab.Col_in_out;
  }
  if (sum != 0) {
    MOD_TABLE_SCAN(Tab) {
      $Tab.Col_in_out /= sum;
    }
  }
  return 1;
}
  • Convert the value of numeric column Tab.Col_in_out to a per-bucket average.
  • Note the use of $TabName.ColName (or MOD_CDAT()) to address a column’s value.
MOD_ROW_FUNC(TabName)

Define a function for row processing during an Udb export/count/scan operation on TabName.

  • It is called for each row of TabName in each user bucket.
  • Use it examine and/or modify column values of the row being exported/counted/scanned.
  • Within the processing code, tables can be scanned, column values can be examined and/or modified, and so on.
  • On each call, the row iterator of TabName is automatically set to the relevant row. For this reason, do not use MOD_TABLE_SCAN() or MOD_TABLE_SET() on TabName. If a TabName scan is needed, use DECL_COLUMN_DYNAMIC() and MOD_COLUMN_BIND() to bind the same table to another name and scan using that name instead.
  • It is called with this implicit argument:
    • ModCntx *mod - A module instance handle. Pass this to any support functions that use module helpers.
  • It must return an integer that tells Udb what to do:
    • 1 - Success. Udb will continue normal processing.
    • 0 - Failure. Udb will stop processing the current row and skip to the next one.

Example:

MOD_ROW_FUNC(TabName_1)
{
  if ($TabName_1.ColName_1 >= 100 &&
      $TabName_1.ColName_1 <= 199) return 1;
  return 0;
}
  • This demonstrates a simple filter on a column value - keep row if ColName_1 is between 100 and 199, discard otherwise.
  • Note the use of $TabName.ColName (or MOD_CDAT()) to address a column’s value.
MOD_VALUE_FUNC(TabName)

Define a function that checks whether to import the input values of a new row during an Udb import operation on TabName.

  • It is called before a new row is added to TabName in a user bucket.
  • Use it to examine the new input values and determine whether to add a new row. The input values are available via the MOD_IMP_CDAT(). These values should be considered readonly.
  • Within the processing code, tables can be scanned, column values can be examined and/or modified, and so on. However, table access is not applicable if:
    • The user bucket corresponding to the input bucket key does not yet exist.
    • The import is being done on the global Var table.
  • It is called with this implicit argument:
    • ModCntx *mod - A module instance handle. Pass this to any support functions that use module helpers.
  • It must return an integer that tells Udb what to do:
    • 1 - Success. Udb will continue with the import operation.
    • 0 - Reject. Udb will discard the new values.

Example:

MOD_VALUE_FUNC(TabName_1)
{
  if (MOD_IMP_CDAT(TabName_1.ColName_1) >= 100 &&
      MOD_IMP_CDAT(TabName_1.ColName_1) <= 199) return 1;
  return 0;
}
  • This demonstrates a simple filter on an input value - keep new values if ColName_1 is between 100 and 199, discard otherwise.
  • Note the use of MOD_IMP_CDAT() to address a column’s input value.
MOD_MERGE_FUNC(TabName)

Define a function that checks whether to merge the input values of a new row into an existing data row during an Udb import operation on TabName.

  • It is called before the input values are merged into an existing row in TabName in a user bucket.
  • Use it to examine the new input values as well as existing column values and determine whether to merge in the new values. The input values are available via MOD_IMP_CDAT(). These values should be considered readonly. The existing column values are available via MOD_CDAT().
  • Within the processing code, tables can be scanned, column values can be examined and/or modified, and so on.
  • On each call, the row iterator of TabName is automatically set to the existing row. For this reason, do not use MOD_TABLE_SCAN() or MOD_TABLE_SET() on TabName. If a TabName scan is needed, use DECL_COLUMN_DYNAMIC() and MOD_COLUMN_BIND() to bind the same table to another name and scan using that name instead.
  • It is called with this implicit argument:
    • ModCntx *mod - A module instance handle. Pass this to any support functions that use module helpers.
  • It must return an integer that tells Udb what to do:
    • 1 - Success. Udb will proceed with the merge operation.
    • 0 - No further action needed. This could mean that the function has performed the merge by itself or that the input values are not desired.

Example:

MOD_MERGE_FUNC(TabName_1)
{
  if (MOD_IMP_CDAT(TabName_1.ColName_1) == $TabName_1.ColName_1) return 1;
  return 0;
}
  • This demonstrates a simple test - keep new values if the new ColName_1 is the same as the existing one, discard otherwise.
  • Note the use of MOD_IMP_CDAT() to address a column’s input value and $TabName.ColName (or MOD_CDAT()) to address a column’s existing value.
MOD_DONE_FUNC()

Define a function that performs module wrap up related tasks. Udb unloads the module.

  • It is called once right before Udb unloads the module.
  • Use it for reporting and data cleanup.
  • It is called with this implicit argument:
    • ModCntx *mod - A module instance handle. Pass this to any support functions that use module helpers.
  • This is a void function, no return value is needed.

Example:

MOD_DONE_FUNC()
{
  ModLog("%s done\n", MOD_NAME);
}
  • Print a message to the Udb server log at module completion.

Module Helpers

These are helpers that are designed specifically for module processing tasks. They can be used in any processing functions or subroutines called from these functions (these subroutines must be given a ModCntx *mod argument).

int MOD_COLUMN_BIND(TabName.ColName, const char *real_name)

Dynamic column setup function.

  • TabName.ColName must ba a column declared via DECL_COLUMN_DYNAMIC().
  • real_name is a C string containing the actual table dot column name.
  • Returns 1 if successful, 0 otherwise.
  • It should be called before the desired column is used, usually during module initialization.
  • See MOD_INIT_FUNC() for an usage example.
MOD_TABLE_SCAN(TabName) { ... }

A macro that expands to a for loop over all rows of the given table.

  • TabName must be a table declared via DECL_COLUMN() or DECL_COLUMN_DYNAMIC().
  • There is an implicit row iterator. References to any column values within the loop implicitly refer to the row iterator’s values.
  • Usually followed by the loop content - a code block enclosed in “{ ... }”.
  • See MOD_BUCKET_FUNC() for an usage example.
MOD_TABLE_SET(TabName)

A macro that sets the row iterator of the given table to the first row of the table. No return value.

Example:

DECL_COLUMN(TabName_1.ColName_1, I);
DECL_COLUMN(VecName_2.ColName_1, I);
MOD_BUCKET_FUNC()
{
  CDAT_I_T sum;
  sum = 0;
  MOD_TABLE_SCAN(TabName_1) {
    sum += $TabName_1.ColName_1;
  }
  MOD_TABLE_SET(VecName_2);
  $VecName_2.ColName_1 = sum;
  ...
}
  • Save the sum of TabName_1.ColName_1 over all rows of TabName_1 to vector column VecName_2.ColName_1.
RowData *MOD_ROW(TabName)

A macro that returns the current row iterator of the given table.

Example:

DECL_COLUMN(TabName_1.ColName_1, I);
MOD_BUCKET_FUNC()
{
  MOD_TABLE_SET(TabName_1);
  if (!MOD_ROW(TabName_1)) return 0;
  ...
}
  • With this logic, a user bucket is skipped if it’s TabName_1 is empty.
CDAT_*_T MOD_CDAT(TabName.ColName), CDAT_*_T $TabName.ColName

Use either form like a program variable to address the value of a column declared via DECL_COLUMN() or DECL_COLUMN_DYNAMIC().

Example:

DECL_COLUMN(TabName_1.InNumColumn, I);
DECL_COLUMN_DYNAMIC(TabName_1.OutNumColumn, I);
MOD_INIT_FUNC()
{
  MOD_COLUMN_BIND(TabName_1.OutNumColumn, "TabName_1.RealColumn");
  ...
}
MOD_ROW_FUNC(TabName_1)
{
  if ($TabName_1.InNumColumn == 4321) $TabName_1.OutNumColumn += 1;
  ...
}
  • Examine and change column values.
CDAT_S_T MOD_CDAT(PKEY), CDAT_S_T $PKEY

Use either form like a CDAT_S_T variable to address the current bucket key.

Example:

MOD_BUCKET_FUNC()
{
  if (ModDifHStrPat($PKEY, "a*c", 3, 0) != 0) return 0;
  ...
}
  • Skip bucket if its key does not match the given pattern.
CDAT_*_T MOD_IMP_CDAT(TabName.ColName)

Use this like a program variable to address the input value of a column declared via DECL_COLUMN() or DECL_COLUMN_DYNAMIC().

  • The variable will have a CDAT_*_T type (see column datatypes) derived from the ColType in the declaration. The only exception is that the input value of a string column will be CDAT_SB_T instead of CDAT_S_T. The reason is that input strings are held in temporary buffers until they have actually been imported.
  • Only applicable within MOD_VALUE_FUNC() and MOD_MERGE_FUNC().

Example:

MOD_VALUE_FUNC(TabName_1)
{
  if (MOD_IMP_CDAT(TabName_1.ColName_1) < 100) return 0;
  ...
}
  • Test an input column value to determine whether to import.
CDAT_SB_T MOD_IMP_CDAT(PKEY)

Use this like a CDAT_SB_T variable to address the input bucket key.

Example:

MOD_VALUE_FUNC()
{
  CDAT_SB_T pkey_new;
  pkey_new = MOD_IMP_CDAT(PKEY);
  if (pkey_new->n == 0) return 0;
  ...
}
  • Test input PKEY value to determine whether to import.
void MOD_CDAT_S_SET(TabName.ColName, CDAT_S_T hs)

Set the value of a string column represented by TabName.ColName to hash string hs.

  • hs can be the value of another string column (e.g., $TabName.StrColumn) or a hash string created using HStrNAdd().

Example:

DECL_COLUMN(TabName_1.StrColumn_1, S);
DECL_COLUMN(TabName_1.StrColumn_2, S);
DECL_COLUMN(TabName_1.StrColumn_3, S);
MOD_ROW_FUNC(TabName_1)
{
  CDAT_S_T str;
  str = HStrNAdd("abc", 3);
  MOD_CDAT_S_SET(TabName_1.StrColumn_1, str);
  MOD_CDAT_S_SET(TabName_1.StrColumn_2, $TabName_1.StrColumn_3);
  ...
}
  • Alter the value of two string columns.
void MOD_CDAT_S_NADD(TabName.ColName, const char *b, unsigned int n)

Set the value of a string column represented by TabName.ColName to a hash string based on string buffer b of length n.

Example:

DECL_COLUMN(TabName_1.StrColumn_1, S);
MOD_ROW_FUNC(TabName_1)
{
  MOD_CDAT_S_NADD(TabName_1.StrColumn_1, "abc", 3);
  ...
}
  • Alter the value of a string column.
const ColDefn *MOD_CDEF(TabName.ColName)
A macro that returns the column definition of the given column.
MOD_DATA(variable)
Access a variable previously defined with DECL_DATA(). See DECL_DATA() for an usage example.
const char *MOD_NAME
A marco respresenting the module name string. See ModLog() for an usage example.
MOD_LOG_ERR(const char *format, ...)

Print a message to the Udb server log. If it is called during module initialization, the same message will be returned to the client.

Example:

MOD_INIT_FUNC()
{
  if (arg_n != 1) {
    MOD_LOG_ERR("missing module argument");
    return 0;
  }
  ...
}
  • Report module initialization error to the server log and client.

General Helpers

Generic programming supports and convenient functions for module specific datatype handling. Note that any memory allocated by the module must be deallocated with free() before the module is unloaded (see MOD_DONE_FUNC()).

int ModDifHStr(const CDAT_S_T hs1, const CDAT_S_T hs2, int dif_flag)

Compare the values of 2 hash strings.

  • Returns 0 if they are the same, 1 if hs1 is greater, and -1 otherwise.
  • dif_flag is either 0 (case sensitive comparision) or DIF_A_NCAS (case insensitive comparison).

Example:

DECL_COLUMN(TabName_1.StrColumn_1, S);
DECL_COLUMN(TabName_1.StrColumn_2, S);
MOD_ROW_FUNC(TabName_1)
{
  if (ModDifHStr($TabName_1.StrColumn_1, $TabName_1.StrColumn_2, 0) == 0) ...
  ...
}
  • Compare (case sensitive) the values of 2 string columns.
int ModDifHStrStr(const CDAT_S_T hs, const char *b, int n, int dif_flag)

Compare the value of hash string hs to string buffer b of length n.

  • Returns 0 if they are the same, 1 if hs is greater, and -1 otherwise.
  • dif_flag is either 0 (case sensitive comparision) or DIF_A_NCAS (case insensitive comparison).

Example:

DECL_COLUMN(TabName_1.StrColumn_1, S);
MOD_ROW_FUNC(TabName_1)
{
  if (ModDifHStrStr($TabName_1.StrColumn_1, "abc", 3, 0) == 0) ...
  ...
}
  • Compare (case sensitive) the value of a string column to a known value.
int ModDifHStrPat(const CDAT_S_T hs, const char *pat, int n, int dif_flag)

Compare the value of hash string hs to pattern buffer pat of length n.

  • pat may contain ‘*’ (for any number of bytes) and ‘?’ (for any 1 byte). Use a ‘’ to escape literal ‘*’, ‘?’ and ‘\’ in the pattern. If the pattern is given as a literal, any backslashes in it must be backslash escaped one more time for the C/C++ interpreter.
  • Returns 0 if they matches, non-zero otherwise.
  • dif_flag can have these values:
    • DIF_A_NCAS - Do case insensitive instead of case sensitive comparison.
    • DIF_A_LIKE - Use ‘%’ and ‘_’ instead of ‘*’ and ‘?’ as the wildcard characters.

Example:

DECL_COLUMN(TabName_1.StrColumn_1, S);
MOD_ROW_FUNC(TabName_1)
{
  if (ModDifHStrPat($TabName_1.StrColumn_1, "a*c", 3, 0) == 0) ...
  ...
}
  • Compare (case sensitive) the value of a string column to a pattern.
int ModDifIp(const CDAT_IP_T *ip1, const CDAT_IP_T *ip2)

Compare the values of 2 IP addresses. Note that the arguments are pointers to IP address structures.

  • Returns 0 if they are the same, 1 if ip1 is greater, and -1 otherwise.

Example:

DECL_COLUMN(TabName_1.IPColumn_1, IP);
DECL_COLUMN(TabName_1.IPColumn_2, IP);
MOD_ROW_FUNC(TabName_1)
{
  if (ModDifIp(&$TabName_1.IPColumn_1, &$TabName_1.IPColumn_2) == 0) ...
  ...
}
  • Compare the values of two IP columns. Note that pointers to the column values are passed to the function.
void ModLog(const char *format, ...)

Print a message to the Udb server log.

Example:

MOD_INIT_FUNC()
{
  if (arg_n != 1) {
    ModLog("%s: missing module argument\n", MOD_NAME);
    return 0;
  }
  ...
}
  • Report a message to server log during module initialization.
void *ZAlloc(size_t size)
Allocate size bytes of memory. This is the same as the C function malloc() except that the returned memory is initialized to zero.
Type *ZALLOC_TYPE(Type)
Allocate an object of type Type. This is a macro based on ZAlloc().
Type *ZALLOC_TYPE_N(Type, int num)
Allocate num object of type Type. This is a macro based on ZAlloc().
int ReAlloc(void *orig_mem, size_t new_size)

This function works like a combination of the C functions malloc() and realloc() - it allocates new_size bytes if the original memory address is NULL or reallocates to new_size otherwise.

  • orig_mem is the address of the original memory address (i.e., an address of an address).
  • Returns 1 if successful, 0 otherwise. The original memory is not altered on failure.
char *StrNDup(const char *b, int n)

Duplicate a data buffer b of length n (i.e., allocate memory and copy data).

  • The resulting string is null terminated.
  • Special cases:
    • If b is NULL, NULL is returned regardless of the value of n.
    • If n is greater than or equal to 0, b needs not be null terminated.
    • If n is less than 0, b must be null terminated. The string length of b will be used as the data length.
BUF_INIT(BufData *buf)
This is a macro that initializes (i.e., zeroes out) a BufData structure. This should be done on any uninitialized BufData structure before it is used for the first time.
BUF_CLEAR(BufData *buf)
This is a macro that clears (i.e., frees) the memory used by the buffer in a BufData structure. Do this before destroying a BufData structure.
int BufNCat(BufData *buf, const char *b, int n)

Append data buffer b of length n to the buffer in BufData structure buf.

  • Returns 1 if successful, 0 otherwise.
  • The resulting buf->s string is null terminated.
  • Special cases:
    • If b is NULL, the size of buf->s will be increased by n (if necessary), but no data will be copied. In other words, buf->s and buf->z may change, but buf->n will not.
    • If n is greater than or equal to 0, b needs not be null terminated.
    • If n is less than 0, b must be null terminated. The string length of b will be used as the data length.
CDAT_S_T HStrNAdd(const char *b, unsigned int n)

Create/retrieve a hash string based on string buffer b of length n.

Example:

DECL_DATA(CDAT_S_T my_str);
MOD_INIT_FUNC()
{
  MOD_DATA(my_str) = HStrNAdd("abc", 3);
  ...
}
  • Set a global variable’s value to a hash string.

Additional Supports

Additional resources can be found in the low level include file “etc/include/umod.h”.

Name versus $Name

The ability to address columns by their names is a key feature of the module script API. Both TabName.ColName and $TabName.ColName are designed to address columns, but they differ in these ways:

  • TabName.ColName (without the leading dollar sign) refers to an abstract column reference. It is only valid in module helpers.
  • $TabName.ColName (with the leading dollar sign) is a shorthand for MOD_CDAT(TabName.ColName). It refers to a column’s value. It acts like a program variable of type CDAT_*_T (see column datatypes). It can be used anywhere program variables are appropriate.

See Also