<  *  | *** >

Loop/update data 

  • SB C interface
  • basic read
  • basic update
  • passing parameters
  • mutex
  • standard update functions
  • skip

-- ###########################
-- RUNNING stop_on_error.sql
stop_on_error;
reponse
success

-- ###########################
-- RUNNING clear.sh

/*
sure mode, no confirmation prompt
clearing directory INTERNAL_FILES_DIR (../STORAGE/INT) ...
done
clearing directory MAX_PERF_FILES_DIR (../STORAGE/MAX_PERF) ...
done
clearing directory TRANSAC_FILES_DIR (../STORAGE/TRANSAC) ...
done
*/

-- ###########################
-- RUNNING doc_data_init.sql
create col_type t_site_id as text;
reponse
success

create col_type t_dept_id as text;
reponse
success

create col_type t_item_id as text;
reponse
success

create col_type t_customer_id as text;
reponse
success

create col_type t_date as text;
reponse
success

create col_type t_customer_info as text;
reponse
success

create col_type end_user as text;
reponse
success

create merge table items( item_id t_item_id, art_label text, dept t_dept_id, avg_week_sales number, sales_price number);
reponse
success

create merge table customers( customer_id t_customer_id, customer_name text);
reponse
success

create table item_tags( item_id t_item_id, tag text);
reponse
success

create table fidelity_cards( customer_id t_customer_id, card_label t_customer_info, valid_from t_date, valid_until t_date);
reponse
success

create table item_customer_infos( customer_id t_customer_id, item_id t_item_id, info t_customer_info, valid_from t_date, valid_until t_date);
reponse
success

create big table sales( item_id t_item_id, customer_id t_customer_id, sales_date t_date, sales_qty number, line_id text, packaging_id t_item_id);
reponse
success

create big table inventory( item_id t_item_id, inv_qty number);
reponse
success

create view v_items as select * from items;
reponse
success

create view v_item_tags as select * from item_tags;
reponse
success

create view v_fidelity_cards as select * from fidelity_cards;
reponse
success

create view v_item_customer_infos as select * from item_customer_infos;
reponse
success

create view v_sales as select * from sales, items, customers where items.item_id=sales.item_id and customers.customer_id=sales.customer_id;
reponse
success

create view v_inventory as select * from inventory, items where items.item_id=inventory.item_id;
reponse
success

insert into items values('artA','the article A','dept #1',10,1.5);
reponse
success

insert into items values('artB','the article B','dept #2',10,3.2);
reponse
success

insert into items values('box1','a box','packaging',10,0);
reponse
success

insert into customers values('C1','customer #1')('C2','customer #2');
reponse
success

insert into item_tags values('artA','tag #1');
reponse
success

insert into item_tags values('artA','tag #2');
reponse
success

insert into fidelity_cards values('C1','SILVER','20191201','20191231');
reponse
success

insert into fidelity_cards values('C1','GOLD','20201201','20201231');
reponse
success

insert into item_customer_infos values('C1','artA','FREQUENT BUYER of artA in 2019','20190101','20191231');
reponse
success

insert into item_customer_infos values('C1','artB','FREQUENT BUYER of artB in 2020','20200101','20201231');
reponse
success

insert into item_customer_infos values('C2','artB','FREQUENT BUYER of artB in 2020','20200101','20201231');
reponse
success

insert into sales values('artA','C1','20191231',5,'ID01','box1');
reponse
success

insert into sales values('artB','C2','20200102',6,'ID02','');
reponse
success

insert into sales values('artB','C1','20191231',4,'ID03','');
reponse
success

insert into sales values('artB','C2','20200102',7,'ID04','box1');
reponse
success

insert into sales values('artC','C1','20200102',8,'ID05','');
reponse
success

insert into sales values('artA','C1','20191231',5,'ID06','box1');
reponse
success

insert into sales values('artA','C2','20191231',5,'ID07','box1');
reponse
success

insert into inventory values('artA',32);
reponse
success

insert into inventory values('artC',12);
reponse
success

refresh dirty view;
reponse
success

-- ###########################
-- RUNNING doc_data_update.sql
--

Loop/update data

data in stormbase can be read/updated using loop statements and C programs

SB C interface

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

} 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);
//

basic read

./_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';
reponse
success

refresh dirty view;
reponse
success

--ACCEPT_DIFF_START
loop sales(item_id, sales_qty, line_id) function fn1;
hello from fn1_before
hello from fn1 (thread_pos 1) : item_id=artA (ival 11), sales_qty=5.00 (ival 10), line_id=ID01 (ival 10)
hello from fn1 (thread_pos 0) : item_id=artB (ival 12), sales_qty=6.00 (ival 11), line_id=ID02 (ival 11)
hello from fn1 (thread_pos 1) : item_id=artB (ival 12), sales_qty=4.00 (ival 12), line_id=ID03 (ival 12)
hello from fn1 (thread_pos 0) : item_id=artB (ival 12), sales_qty=7.00 (ival 13), line_id=ID04 (ival 13)
hello from fn1 (thread_pos 0) : item_id=artC (ival 14), sales_qty=8.00 (ival 14), line_id=ID05 (ival 14)
hello from fn1 (thread_pos 0) : 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
LOOP_RESULT
DONE

--ACCEPT_DIFF_END

basic update

./_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';
reponse
success

refresh dirty view;
reponse
success

select item_id, sales_qty, line_id from v_sales cb sort(1,'asc');
item_idsales_qtyline_id
#8ID05
artA5ID01
artA5ID06
artA5ID07
artB6ID02
artB4ID03
artB7ID04

loop sales(item_id, sales_qty) function fn1 update(line_id) where item_id='artA';
hello from fn1_after
LOOP_RESULT
DONE

refresh dirty view;
reponse
success

select item_id, sales_qty, line_id from v_sales cb sort(1,'asc');
item_idsales_qtyline_id
#8ID05
artA5NEW_ID_99
artA5NEW_ID_98
artA5NEW_ID_97
artB6ID02
artB4ID03
artB7ID04

passing parameters

./_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';
reponse
success

refresh dirty view;
reponse
success

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
LOOP_RESULT
DONE

--ACCEPT_DIFF_START

mutex

./_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';
reponse
success

refresh dirty view;
reponse
success

loop sales(item_id, sales_qty) function fn1;
fn1 A 0x70000b269cfc 0
fn1 A 0x70000b2eccfc 1
fn1 B
fn1 B
fn1 A 0x70000b269cfc 0
fn1 A 0x70000b2eccfc 1
fn1 B
fn1 B
fn1 A 0x70000b269cfc 0
fn1 A 0x70000b2eccfc 1
fn1 B
fn1 B
fn1 A 0x70000b269cfc 0
fn1 B
hello from fn1_after
LOOP_RESULT
DONE

./_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';
reponse
success

refresh dirty view;
reponse
success

loop sales(item_id, sales_qty) function fn1;
fn1 A 0x70000b269cfc 0
fn1 B
fn1 A 0x70000b2eccfc 1
fn1 B
fn1 A 0x70000b269cfc 0
fn1 B
fn1 A 0x70000b2eccfc 1
fn1 B
fn1 A 0x70000b269cfc 0
fn1 B
fn1 A 0x70000b2eccfc 1
fn1 B
fn1 A 0x70000b269cfc 0
fn1 B
hello from fn1_after
LOOP_RESULT
DONE

--ACCEPT_DIFF_END

standard update functions

select item_id,sales#customer_id from v_sales cb sort(1,'asc');
item_idsales#customer_id
#C1
artAC1
artAC1
artAC2
artBC2
artBC1
artBC2

create table customer_update(item_id t_item_id, new_customer_id t_customer_id,new_sales_qty number,item_id_new t_item_id);
reponse
success

insert into customer_update values('artA','C_artA',123.45,'??');
reponse
success

--fn_build_idx1
loop customer_update(item_id,new_customer_id) function fn_build_idx1;
LOOP_RESULT
DONE

desc context;
context_name
IDX1#item_id->new_customer_id

loop sales(item_id) function fn_update_idx1('IDX1#item_id->new_customer_id') update(customer_id);
LOOP_RESULT
DONE

refresh dirty view;
reponse
success

select item_id,sales#customer_id from v_sales cb sort(1,'asc');
item_idsales#customer_id
#C1
artAC_artA
artAC_artA
artAC_artA
artBC2
artBC1
artBC2

--fn_build_idx1_num
loop customer_update(item_id,new_sales_qty) function fn_build_idx1_num;
LOOP_RESULT
DONE

desc context;
context_name
IDX1#item_id->new_customer_id
IDX1_NUM#item_id->new_sales_qty

loop sales(item_id) function fn_update_idx1_num('IDX1_NUM#item_id->new_sales_qty') update(sales_qty);
LOOP_RESULT
DONE

refresh dirty view;
reponse
success

select item_id,sales_qty from v_sales cb sort(1,'asc');
item_idsales_qty
#0
artA123.450
artA123.450
artA123.450
artB0
artB0
artB0

--fn_copy_col
create view v_customer_update as select * from customer_update;
reponse
success

refresh dirty view;
reponse
success

select item_id,item_id_new from v_customer_update;
item_iditem_id_new
artA??

loop customer_update(item_id) function fn_copy_col update(item_id_new);
LOOP_RESULT
DONE

refresh dirty view;
reponse
success

select item_id,item_id_new from v_customer_update;
item_iditem_id_new
artAartA

skip

If before fn returns SKIP, the rest of the loop is skip
loop sales(item_id, sales_qty) function fn_skip;
reponse
loop skip

-- ###########################
-- RUNNING shutdown.sql
shutdown;