loop table_name(col1, coll2, ...) function
function_name(param1, param2, ...)
update
(updated_col1, updated_col1, ...)
where
...
Parameters, update clause and where clause are optional
SB will execute 3 C functions
1/ <function_name>_before: optional "before function" called once, where you allocated the object you will need
2/ <function_name>: mandatory "loop function" called once per line fecthed, where you populate the objects
3/ <function_name>_after: optional "after function" called once, where you free memory
See input/output of these functions in common.h
./_SO_CODE/common.h
typedef
struct sb_vals {
 // SB's version of the SB_VALS structure
 int
sb_vals_version;
 // number of columns
 int
count;
 // i_val of each column (loop function only)
 // NULL value: NULL_IVAL (see define here above)
 U_INT *i_vals;
 // num_val of each column (loop function only)
 // NULL value: NAN (C macro) (any float for which isnan evals to true)
 float
*num_vals;
 // str_val of each column (loop function only)
 // NULL value: NULL (C macro) or SB_NULL (see define here above)
 char
**str_vals;
 // type of each column, 2 possible values T (text) or N (number)
 char
*types;
 // obvious
 char
*table_name;
 // obvious
 char
**column_names;
 // obvious
 char
**col_type_names;
 // number of distinct i_val of each column
 U_INT *col_i_val_counts;
 // number of lines in table
 // (where clause is not taken into account)
 // (partitions are taken into account)
 long table_line_count;
 // (in/out) type used to update the columns type of each column,
 // 3 possible values: T (text) or N (number) or I (i_val)
 char
*update_types;
 // (in/out) whatever pointer you write here in the before function will be passed to the loop/after functions
 void
*context;
 // (in/out) response of the loop statement
 char
*result;
 // (in/out) #threads that SB will create, default is 1, of course your code must be thread safe if you put 2+
 int
thread_count; //SB_LIB_VERSION >= 1
 // current thread (loop function only)
 int
thread_pos; //SB_LIB_VERSION >= 1
 // if you need a mutex in the loop function, you can use this one
 // obsolete, do not use (kept for compatibility matters)
 pthread_mutex_t loop_mutex; //SB_LIB_VERSION >= 2
 // parameters passed to the loop function (parameters and columns are not the same thing!)
 char
**params; //SB_LIB_VERSION >= 3
 // #parameters passed to the loop function (parameters and columns are not the same thing!)
 int
params_len; //SB_LIB_VERSION >= 3
 //in out fields (can be modified in before function):
 // thread_count (default 1)
 // (in/out) defines which partition should be fetch (default ALL_PARTITIONS, can be set to DIRTY_PARTITIONS_ONLY)
 int
partitions_scope; //SB_LIB_VERSION >= 4
 // (in/out) defines which lines should be fetch (default ALL_LINES, can be set to LINES_INSERTED_SINCE_LAST_REFRESH_DIRTY_ONLY)
 int
lines_scope; //SB_LIB_VERSION >= 5
 // context field behavior:
 // Note that the context is global (there is only one "loop context" at a given time: "the current loop context")
 // - pass by stormbase to all loop command
 // - the before function can set the context (any non NULL value will be considered as the current loop context)
 // - the after function can set the context to NULL and usually frees memory at the same time (NULL value will be considered as "setting the current loop context to NULL")
 // - any function can manipulate the current loop context, it is standard C manipulation
 // - if the before function sets a name to the current loop context, it can be recall later (in computed columns). Of course in that case the after function should not free it.
 char
*context_name; //SB_LIB_VERSION >= 6
 // see structure definition
 UTILITY *U; //SB_LIB_VERSION >= 7
 // (in/out) optional name of the function to be called to free the context if any (added in v1.16.81)
 // this fn must be t_fn_free_context type
 char
*fn_free_context_name; //SB_LIB_VERSION >= 8
 // (in/out) if update_types uses text update (type T), y means SB will free the pointer, default n
 // Note: many malloc/free will generate memory fragmentation at OS level, so it is better to manage a buffer in your custom code for text updates
 char
text_update_sb_free; //SB_LIB_VERSION >= 10
 // (in/out) whatever pointer you write here in the before function will be passed to the loop/after functions,
 // the work_area can be considered as a second context except that it can't be called after the loop execution
 void
*work_area; //SB_LIB_VERSION >= 13
 // (in) unique line number provide by SB during the fetch, note that line is always < table_line_count
 // the goal in to facilitate multi threading, you don't have to manage a counter with a mutex in your code
 // Note: a given line in a table may receive a different line_fetch number in distinct loop execution
 // Note: in after fn line_fetch will be equal to the number of line fetch
 long line_fetch; //SB_LIB_VERSION >= 14
} SB_VALS;
// before function (returns OK or KO)
typedef
int
(*t_fn_line_before)(SB_VALS *read, SB_VALS *new);
// loop function (returns DELETED or UPDATED or NOT_MODIFIED)
typedef
int
(*t_fn_line)(SB_VALS *read, SB_VALS *new);
// after function (returns OK or KO)
typedef
int
(*t_fn_line_after)(SB_VALS *read, SB_VALS *new);
// free function
typedef
void
(*t_fn_free_context)(void
*context);
//
./_SO_CODE/Z_doc_loop_AA.c
#include
"./common.h"
int
fn1_before(SB_VALS* read, SB_VALS* new) {
 printf("## hello from fn1_before\n"
);
 read->thread_count
= 2;
 return
OK;
}
pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;
int
fn1(SB_VALS* read, SB_VALS* new) {
 printf("## hello from fn1 (thread_pos %d) : "
, read->thread_pos
);
 for (int
i = 0; i < read->
count
; i++) {
   if
(read->types
[i] == 'T') {
     printf("%s=%s (ival %u)"
, read->column_names
[i], read->str_vals
[i], read->i_vals
[i]);
   } else if
(read->types
[i] == 'N') {
     printf("%s=%.2f (ival %u)"
, read->column_names
[i], read->num_vals
[i], read->i_vals
[i]);
   }
   if
(i != read->count
- 1) {
     printf(", "
);
   }
 }
 printf("\n"
);
 return
NOT_MODIFIED;
}
int
fn1_after(SB_VALS* read, SB_VALS* new) {
 printf("## hello from fn1_after\n"
);
 return
OK;
}
set
SO_FILE_NAME='Z_doc_loop_AA.so'
;
refresh
dirty
view
;
loop sales(item_id, sales_qty, line_id) function
fn1;
hello from
fn1_before
hello from
fn1 (thread_pos 0) : item_id=artA (ival 11), sales_qty=5.00 (ival 10), line_id=ID01 (ival 10)
hello from
fn1 (thread_pos 1) : item_id=artB (ival 12), sales_qty=6.00 (ival 11), line_id=ID02 (ival 11)
hello from
fn1 (thread_pos 0) : item_id=artB (ival 12), sales_qty=4.00 (ival 12), line_id=ID03 (ival 12)
hello from
fn1 (thread_pos 1) : item_id=artB (ival 12), sales_qty=7.00 (ival 13), line_id=ID04 (ival 13)
hello from
fn1 (thread_pos 1) : item_id=artC (ival 14), sales_qty=8.00 (ival 14), line_id=ID05 (ival 14)
hello from
fn1 (thread_pos 1) : item_id=artA (ival 11), sales_qty=5.00 (ival 10), line_id=ID06 (ival 15)
hello from
fn1 (thread_pos 0) : item_id=artA (ival 11), sales_qty=5.00 (ival 10), line_id=ID07 (ival 16)
hello from
fn1_after
./_SO_CODE/Z_doc_loop_BB.c
#include
"./common.h"
int
fn1_before(SB_VALS* read, SB_VALS* new) {
 read->thread_count
= 2;
 int
*c = malloc
(sizeof
(int
));
 *c = 99;
 read->context
= c;
 new->update_types
[0] = 'T';
 return
OK;
}
char
txt_th0[20];
char
txt_th1[20];
pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;
int
fn1(SB_VALS* read, SB_VALS* new) {
 int
*c = read->context
;
 char
*txt = read->thread_pos
== 0 ? txt_th0 : txt_th1;
 sprintf(txt, "NEW_ID_%d"
, *c);
 *c = *c - 1;
 new->str_vals
[0] = txt;
 return
UPDATED;
}
int
fn1_after(SB_VALS* read, SB_VALS* new) {
 printf("## hello from fn1_after\n"
);
 free
(read->context
);
 return
OK;
}
set
SO_FILE_NAME='Z_doc_loop_BB.so'
;
refresh
dirty
view
;
select
item_id, sales_qty, line_id from
v_sales cb
sort(1,'asc'
);
item_id | sales_qty | line_id |
# | 8 | ID05 |
artA | 5 | ID01 |
artA | 5 | ID06 |
artA | 5 | ID07 |
artB | 6 | ID02 |
artB | 4 | ID03 |
artB | 7 | ID04 |
loop sales(item_id, sales_qty) function
fn1 update
(line_id) where
item_id='artA'
;
hello from
fn1_after
refresh
dirty
view
;
select
item_id, sales_qty, line_id from
v_sales cb
sort(1,'asc'
);
item_id | sales_qty | line_id |
# | 8 | ID05 |
artA | 5 | NEW_ID_99 |
artA | 5 | NEW_ID_98 |
artA | 5 | NEW_ID_97 |
artB | 6 | ID02 |
artB | 4 | ID03 |
artB | 7 | ID04 |
./_SO_CODE/Z_doc_loop_AB.c
#include
"./common.h"
int
fn1_before(SB_VALS* read, SB_VALS* new) {
 printf("## hello from fn1_before\n"
);
 for(int
i=0;i
params_len
;i++){
   printf("## fn1_before param %d is %s\n"
,i+1,read->params
[i]);
 }
 read->thread_count
= 2;
 return
OK;
}
pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;
int
fn1(SB_VALS* read, SB_VALS* new) {
 return
NOT_MODIFIED;
}
int
fn1_after(SB_VALS* read, SB_VALS* new) {
 printf("## hello from fn1_after\n"
);
 return
OK;
}
set
SO_FILE_NAME='Z_doc_loop_AB.so'
;
refresh
dirty
view
;
loop sales(item_id, sales_qty, line_id) function
fn1('val1'
,'val2'
);
hello from
fn1_before
fn1_before param 1 is val1
fn1_before param 2 is val2
hello from
fn1_after
./_SO_CODE/Z_doc_loop_without_mutex.c
#include
"./common.h"
int
fn1_before(SB_VALS* read, SB_VALS* new) {
 read->thread_count
= 2;
 return
OK;
}
pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;
int
fn1(SB_VALS* read, SB_VALS* new) {
 int
r=rand()%1000000;
 printf("## fn1 A %p %d\n"
,&r,read->thread_pos
);
 usleep(r);
 printf("## fn1 B\n"
);
 return
NOT_MODIFIED;
}
int
fn1_after(SB_VALS* read, SB_VALS* new) {
 printf("## hello from fn1_after\n"
);
 return
OK;
}
set
SO_FILE_NAME='Z_doc_loop_without_mutex.so'
;
refresh
dirty
view
;
loop sales(item_id, sales_qty) function
fn1;
fn1 A 0x700004fddd4c 0
fn1 A 0x700005060d4c 1
fn1 B
fn1 A 0x700004fddd4c 0
fn1 B
fn1 B
fn1 A 0x700004fddd4c 0
fn1 A 0x700005060d4c 1
fn1 B
fn1 A 0x700005060d4c 1
fn1 B
fn1 B
fn1 A 0x700004fddd4c 0
fn1 B
hello from
fn1_after
./_SO_CODE/Z_doc_loop_with_mutex.c
#include
"./common.h"
int
fn1_before(SB_VALS* read, SB_VALS* new) {
 read->thread_count
= 2;
 return
OK;
}
pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;
int
fn1(SB_VALS* read, SB_VALS* new) {
 int
r=rand()%1000000;
 pthread_mutex_lock(&my_mutex);
 printf("## fn1 A %p %d\n"
,&r,read->thread_pos
);
 usleep(r);
 printf("## fn1 B\n"
);
 pthread_mutex_unlock(&my_mutex);
 return
NOT_MODIFIED;
}
int
fn1_after(SB_VALS* read, SB_VALS* new) {
 printf("## hello from fn1_after\n"
);
 return
OK;
}
set
SO_FILE_NAME='Z_doc_loop_with_mutex.so'
;
refresh
dirty
view
;
loop sales(item_id, sales_qty) function
fn1;
fn1 A 0x700004fddd4c 0
fn1 B
fn1 A 0x700004fddd4c 0
fn1 B
fn1 A 0x700005060d4c 1
fn1 B
fn1 A 0x700004fddd4c 0
fn1 B
fn1 A 0x700005060d4c 1
fn1 B
fn1 A 0x700004fddd4c 0
fn1 B
fn1 A 0x700004fddd4c 0
fn1 B
hello from
fn1_after