/* q should be set when joining */ static int binding_row_compare(fs_query *q, fs_binding *b1, fs_binding *b2, int p1, int p2, int length1, int length2) { if (p1 >= length1 && p2 >= length2) { return 0; } if (p1 >= length1) { #ifdef DEBUG_COMPARE printf("CMP from past end\n"); #endif return 1; } else if (p2 >= length2) { #ifdef DEBUG_COMPARE printf("CMP to past end\n"); #endif return -1; } for (int i=1; b1[i].name; i++) { if (!b1[i].sort) continue; const fs_rid b1v = table_value(b1, i, p1); const fs_rid b2v = table_value(b2, i, p2); if (b1v == FS_RID_NULL) { if (b2v == FS_RID_NULL) { /* both bindings are null, assume equality */ continue; } /* b1v is null, b2v is not, assume null < b2v */ return -2; } if (b2v == FS_RID_NULL) { /* b2v is null, b1v is not, assume b1v > null */ return 2; } if (b1v > b2v) { #ifdef DEBUG_COMPARE printf("CMP %llx > %llx\n", b1v, b2v); #endif return 1; } if (b1v < b2v) { #ifdef DEBUG_COMPARE printf("CMP %llx < %llx\n", b1v, b2v); #endif return -1; } } return 0; }
/* * Return code: * 0 - End Of Table * -1 - Error * -2 - Last change changed - again * +1 - ok, continue */ static int table_check_response(struct tabwork *work, const struct snmp_pdu *resp) { const struct snmp_value *b; struct entry *e; if (resp->error_status != SNMP_ERR_NOERROR) { if (snmp_client.version == SNMP_V1 && resp->error_status == SNMP_ERR_NOSUCHNAME && resp->error_index == (work->descr->last_change.len == 0) ? 1 : 2) /* EOT */ return (0); /* Error */ seterr(&snmp_client, "error fetching table: status=%d index=%d", resp->error_status, resp->error_index); return (-1); } for (b = resp->bindings; b < resp->bindings + resp->nbindings; b++) { if (work->descr->last_change.len != 0 && b == resp->bindings) { if (!asn_is_suboid(&work->descr->last_change, &b->var) || b->var.len != work->descr->last_change.len + 1 || b->var.subs[work->descr->last_change.len] != 0) { seterr(&snmp_client, "last_change: bad response"); return (-1); } if (b->syntax != SNMP_SYNTAX_TIMETICKS) { seterr(&snmp_client, "last_change: bad syntax %u", b->syntax); return (-1); } if (work->first) { work->last_change = b->v.uint32; work->first = 0; } else if (work->last_change != b->v.uint32) { if (++work->iter >= work->descr->max_iter) { seterr(&snmp_client, "max iteration count exceeded"); return (-1); } table_free(work, 1); return (-2); } continue; } if (!asn_is_suboid(&work->descr->table, &b->var) || b->syntax == SNMP_SYNTAX_ENDOFMIBVIEW) return (0); if ((e = table_find(work, &b->var)) == NULL) return (-1); if (table_value(work->descr, e, b)) return (-1); } return (+1); }
void fs_binding_uniq(fs_binding *bi) { if (fs_binding_length(bi) < 2) { /* we don't need to do anything, code below assumes >= 1 row */ return; } fs_binding *b = fs_binding_copy_and_clear(bi); bi[0].vals->length = 0; #ifdef DEBUG_MERGE double then = fs_time(); #endif int length = fs_binding_length(b); int outrow = 1; for (int column = 1; b[column].name; column++) { fs_rid_vector_append(bi[column].vals, table_value(b, column, 0)); bi[column].bound = b[column].bound; b[column].sort = b[column].bound; } for (int row = 1; row < length; row++) { if (binding_row_compare(NULL, b, b, row, row-1, length, length) == 0) { continue; } for (int column = 1; b[column].name; column++) { fs_rid_vector_append(bi[column].vals, table_value(b, column, row)); } outrow++; } #ifdef DEBUG_MERGE double now = fs_time(); printf("uniq took %fs (%d->%d rows)\n", now-then, length, outrow); fs_binding_print(bi, stdout); #endif fs_binding_free(b); }
fs_binding *fs_binding_join(fs_query *q, fs_binding *a, fs_binding *b, fs_join_type join) { if (a == NULL) { return fs_binding_copy(b); } if (b == NULL) { return fs_binding_copy(a); } fs_binding *c = fs_binding_copy(a); int inter = 0; /* do the tables intersect */ for (int i=0; a[i].name; i++) { a[i].sort = 0; b[i].sort = 0; c[i].sort = 0; c[i].vals->length = 0; } int bound_a = 0; int bound_b = 0; for (int i=1; a[i].name; i++) { if (a[i].bound) bound_a++; if (b[i].bound) bound_b++; if (a[i].bound || b[i].bound) { c[i].bound = 1; } if (a[i].bound && b[i].bound) { inter = 1; a[i].sort = 1; b[i].sort = 1; #ifdef DEBUG_MERGE printf("joining on %s\n", a[i].name); #endif } } /* a and b bound variables do not intersect, we can just dump results */ if (!inter) { int length_a = fs_binding_length(a); int length_b = fs_binding_length(b); for (int i=1; a[i].name; i++) { if (!a[i].bound) { for (int j=0; j<length_a; j++) { fs_rid_vector_append(c[i].vals, FS_RID_NULL); } } else { fs_rid_vector_append_vector(c[i].vals, a[i].vals); } if (!b[i].bound) { for (int j=0; j<length_b; j++) { fs_rid_vector_append(c[i].vals, FS_RID_NULL); } } else { fs_rid_vector_append_vector(c[i].vals, b[i].vals); } } #ifdef DEBUG_MERGE printf("append all, result:\n"); fs_binding_print(c, stdout); #endif return c; } int length_a = fs_binding_length(a); int length_b = fs_binding_length(b); /* sort the two sets of bindings so they can be merged linearly */ fs_binding_sort(a); fs_binding_sort(b); #ifdef DEBUG_MERGE printf("a: %d bindings\n", fs_binding_length(a)); fs_binding_print(a, stdout); printf("b: %d bindings\n", fs_binding_length(b)); fs_binding_print(b, stdout); #endif /* If were running in restricted mode, truncate the binding tables */ if (q->flags & FS_QUERY_RESTRICTED) { int restricted = 0; fs_binding_truncate(a, q->soft_limit); if (length_a > fs_binding_length(a)) { length_a = fs_binding_length(a); restricted = 1; } fs_binding_truncate(b, q->soft_limit); if (length_b > fs_binding_length(b)) { length_b = fs_binding_length(b); restricted = 1; } if (restricted) { char *msg = "some results have been dropped to prevent overunning effort allocation"; q->warnings = g_slist_prepend(q->warnings, msg); } } int apos = 0; int bpos = 0; int cmp; while (apos < length_a) { if (join == FS_INNER && bpos >= length_b) break; cmp = binding_row_compare(q, a, b, apos, bpos, length_a, length_b); if (cmp == -1) { /* A and B aren't compatible, A sorts lower, skip A or left join */ #if DEBUG_MERGE > 1 printf("[L] Ar=%d, Br=%d", apos, bpos); #endif if (join == FS_LEFT) { for (int col=0; a[col].name; col++) { if (!c[col].need_val) { continue; } else if (a[col].bound) { #if DEBUG_MERGE > 1 printf(" %s=%016llx", c[col].name, table_value(a, col, apos)); #endif fs_rid_vector_append(c[col].vals, table_value(a, col, apos)); } else { #if DEBUG_MERGE > 1 printf(" %s=null", c[col].name); #endif fs_rid_vector_append(c[col].vals, FS_RID_NULL); } } } apos++; } else if (cmp == 0 || cmp == -2 || cmp == 2) { /* Both rows are equal (cmp == 0), or one row is null (cmp == -2, 2) */ /* Both rows match, find out what combinations bind and produce them */ #if DEBUG_MERGE > 1 printf("[I] Ar=%d, Br=%d", apos, bpos); #endif int range_a = apos+1; int range_b = bpos+1; while (binding_row_compare(q, a, a, apos, range_a, length_a, length_a) == 0) range_a++; while (binding_row_compare(q, b, b, bpos, range_b, length_b, length_b) == 0) range_b++; int start_a = apos; int start_b = bpos; for (apos = start_a; apos<range_a; apos++) { for (bpos = start_b; bpos<range_b; bpos++) { for (int col=0; a[col].name; col++) { if (!c[col].need_val) { continue; } else if (!a[col].bound && !b[col].bound) { #if DEBUG_MERGE > 1 printf(" %s=null", c[col].name); #endif fs_rid_vector_append(c[col].vals, FS_RID_NULL); } else if (a[col].bound) { /* if were left joining and A is NULL, we want the * value from B */ if (join == FS_LEFT && table_value(a, col, apos) == FS_RID_NULL && b[col].bound) { #if DEBUG_MERGE > 1 printf(" %s=%016llx", c[col].name, table_value(b, col, bpos)); #endif fs_rid_vector_append(c[col].vals, table_value(b, col, bpos)); } else { #if DEBUG_MERGE > 1 printf(" %s=%016llx", c[col].name, table_value(a, col, apos)); #endif fs_rid_vector_append(c[col].vals, table_value(a, col, apos)); } } else { #if DEBUG_MERGE > 1 printf(" %s=%016llx", c[col].name, table_value(b, col, bpos)); #endif fs_rid_vector_append(c[col].vals, table_value(b, col, bpos)); } } } } /* this is actually unneccesary because the for loop will do the * same thing, but it's clearer */ apos = range_a; bpos = range_b; } else if (cmp == +1) { /* A and B aren't compatible, B sorts lower, skip B */ bpos++; } else { fs_error(LOG_ERR, "cmp=%d, value out of range", cmp); } #if DEBUG_MERGE > 1 printf("\n"); #endif } /* clear the _ord columns */ a[0].vals->length = 0; b[0].vals->length = 0; #ifdef DEBUG_MERGE printf("result: %d bindings\n", fs_binding_length(c)); fs_binding_print(c, stdout); #endif return c; }
/* return to = from [X] to, this is used to perform joins inside blocks, it * saves allocations by doing most operations inplace, unlike fs_binding_join */ void fs_binding_merge(fs_query *q, int block, fs_binding *from, fs_binding *to) { fs_binding *inter_f = NULL; /* the intersecting column */ fs_binding *inter_t = NULL; /* the intersecting column */ for (int i=0; from[i].name; i++) { from[i].sort = 0; to[i].sort = 0; } int used = 0; for (int i=1; from[i].name; i++) { if (!from[i].bound || !to[i].bound) continue; if (from[i].used) used++; if (from[i].bound && to[i].bound) { inter_f = from+i; inter_t = to+i; from[i].sort = 1; to[i].sort = 1; #ifdef DEBUG_MERGE printf("@@ join on %s\n", to[i].name); #endif } } /* from and to bound variables do not intersect, we can just dump results, under some circustances we need to do a combinatorial explosion */ if (!inter_f && (fs_binding_length(from) == 0)) { const int length_f = fs_binding_length(from); const int length_t = fs_binding_length(to); for (int i=1; from[i].name; i++) { if (to[i].bound && !from[i].bound) { if (from[i].vals) { fs_rid_vector_free(from[i].vals); } from[i].vals = fs_rid_vector_new(length_f); for (int d=0; d<length_f; d++) { from[i].vals->data[d] = FS_RID_NULL; } from[i].bound = 1; } if (!from[i].bound) continue; if (!to[i].bound) { if (to[i].vals) { fs_rid_vector_free(to[i].vals); } to[i].vals = fs_rid_vector_new(length_t); for (int d=0; d<length_t; d++) { to[i].vals->data[d] = FS_RID_NULL; } } fs_rid_vector_append_vector(to[i].vals, from[i].vals); to[i].bound = 1; } #ifdef DEBUG_MERGE printf("append all, result:\n"); fs_binding_print(to, stdout); #endif return; } /* If were running in restricted mode, truncate the binding tables */ if (q->flags & FS_QUERY_RESTRICTED) { fs_binding_truncate(from, q->soft_limit); fs_binding_truncate(to, q->soft_limit); } int length_t = fs_binding_length(to); int length_f = fs_binding_length(from); /* ms8: this list keeps track of the vars to replace */ GList *rep_list = NULL; for (int i=1; to[i].name; i++) { if (to+i == inter_t || to[i].used || to[i].bound) { /* do nothing */ #if DEBUG_MERGE > 1 printf("@@ preserve %s\n", to[i].name); #endif } else if (from[i].bound && !to[i].bound) { #if DEBUG_MERGE > 1 printf("@@ replace %s\n", from[i].name); #endif to[i].bound = 1; if (to[i].vals) { if (to[i].vals->length != length_t) { fs_rid_vector_free(to[i].vals); to[i].vals = fs_rid_vector_new(length_t); } } else { to[i].vals = fs_rid_vector_new(length_t); } for (int d=0; d<length_t; d++) { to[i].vals->data[d] = FS_RID_NULL; } rep_list = g_list_append(rep_list, GINT_TO_POINTER(i)); } } /* sort the two sets of bindings so they can be merged linearly */ if (inter_f) { fs_binding_sort(from); fs_binding_sort(to); } else { /* make sure the tables are not marked sorted */ from[0].vals->length = 0; to[0].vals->length = 0; } #ifdef DEBUG_MERGE printf("old: %d bindings\n", fs_binding_length(from)); fs_binding_print(from, stdout); printf("new: %d bindings\n", fs_binding_length(to)); fs_binding_print(to, stdout); #endif int fpos = 0; int tpos = 0; while (fpos < length_f || tpos < length_t) { if (q->flags & FS_QUERY_RESTRICTED && fs_binding_length(to) >= q->soft_limit) { char *msg = g_strdup("some results have been dropped to prevent overunning time allocation"); q->warnings = g_slist_prepend(q->warnings, msg); break; } int cmp; cmp = binding_row_compare(q, from, to, fpos, tpos, length_f, length_t); if (cmp == 0) { /* both rows match */ int fp, tp = tpos; for (fp = fpos; binding_row_compare(q, from, to, fp, tpos, length_f, length_t) == 0; fp++) { #if DEBUG_MERGE > 1 if (fp == DEBUG_CUTOFF) { printf("...\n"); } #endif for (tp = tpos; 1; tp++) { if (binding_row_compare(q, from, to, fp, tp, length_f, length_t) == 0) { #if DEBUG_MERGE > 1 if (fp < DEBUG_CUTOFF) { printf("STEP %d, %d ", fp-fpos, tp-tpos); } #endif if (fp == fpos) { #if DEBUG_MERGE > 1 if (fp < DEBUG_CUTOFF) { if (inter_f) { printf("REPL %llx\n", inter_f->vals->data[fp]); } else { printf("REPL ???\n"); } } #endif for (int c=1; to[c].name; c++) { if (!from[c].bound && !to[c].bound) continue; if (from[c].bound && table_value(from, c, fp) == FS_RID_NULL) { continue; } if (from[c].bound && fp < from[c].vals->length) { long wrow = to[0].vals->length ? to[0].vals->data[tp] : tp; to[c].vals->data[wrow] = table_value(from, c, fp); if (to[c].vals->length <= tp) { to[c].vals->length = tp+1; } } } } else { #if DEBUG_MERGE > 1 if (fp < DEBUG_CUTOFF) { printf("ADD\n"); } #endif for (int c=1; to[c].name; c++) { if (!from[c].bound && !to[c].bound) continue; if (from[c].bound && fp < from[c].vals->length) { fs_rid_vector_append(to[c].vals, table_value(from, c, fp)); } else { fs_rid_vector_append(to[c].vals, table_value(to, c, tp)); } } } } else { break; } } } tpos = tp; fpos = fp; } else if (cmp <= -1) { fpos++; } else if (cmp >= 1) { tpos++; } else { fs_error(LOG_CRIT, "unknown compare state %d in binding", cmp); } } /* clear the _ord columns */ from[0].vals->length = 0; to[0].vals->length = 0; /* ms8: INIT code to clean up rows that where not replaced */ if (rep_list) { unsigned char *to_del = fs_new_bit_array(length_t); int to_del_count = 0; while(rep_list) { int col_r = GPOINTER_TO_INT(rep_list->data); rep_list = g_list_next(rep_list); for (int d=0; d<length_t; d++) { if (to[col_r].vals->data[d] == FS_RID_NULL) { fs_bit_array_set(to_del, d, 0); to_del_count++; } } } g_list_free(rep_list); if (to_del_count) { int vars = 0; for (int i=1; to[i].name; i++) vars++; fs_rid_vector **clean = calloc(vars, sizeof(fs_rid_vector *)); for (int i=0;i<vars;i++) clean[i] = fs_rid_vector_new(0); for (int d = 0;d<length_t;d++) { if (fs_bit_array_get(to_del,d)) { for (int i=0;i<vars;i++) { fs_rid_vector_append(clean[i],to[i+1].vals->data[d]); } } } for (int i=1;i<=vars;i++) { free(to[i].vals->data); to[i].vals->data = clean[i-1]->data; to[i].vals->length = clean[i-1]->length; to[i].vals->size = clean[i-1]->size; free(clean[i-1]); } free(clean); } fs_bit_array_destroy(to_del); } /* ms8: END code to clean up rows that where not replaced */ #ifdef DEBUG_MERGE printf("result: %d bindings\n", fs_binding_length(to)); fs_binding_print(to, stdout); #endif }
fs_binding *fs_binding_minus(fs_query *q, fs_binding *a, fs_binding *b) { if (a == NULL) { return NULL; } if (b == NULL) { /* a - 0 = a */ return fs_binding_copy(a); } fs_binding *c = fs_binding_copy(a); int inter = 0; /* do the tables intersect */ for (int i=0; a[i].name; i++) { a[i].sort = 0; b[i].sort = 0; c[i].sort = 0; c[i].vals->length = 0; } int bound_a = 0; int bound_b = 0; for (int i=1; a[i].name; i++) { if (a[i].bound) bound_a++; if (b[i].bound) bound_b++; if (a[i].bound || b[i].bound) { c[i].bound = 1; } if (a[i].bound && b[i].bound) { inter = 1; a[i].sort = 1; b[i].sort = 1; #ifdef DEBUG_MERGE printf("joining on %s\n", a[i].name); #endif } } /* a and b bound variables do not intersect, return c (copy of a) */ if (!inter) { #ifdef DEBUG_MERGE printf("remove nothing, result:\n"); fs_binding_print(c, stdout); #endif return c; } int length_a = fs_binding_length(a); int length_b = fs_binding_length(b); /* sort the two sets of bindings so they can be merged linearly */ fs_binding_sort(a); fs_binding_sort(b); #ifdef DEBUG_MERGE printf("a: %d bindings\n", fs_binding_length(a)); fs_binding_print(a, stdout); printf("b: %d bindings\n", fs_binding_length(b)); fs_binding_print(b, stdout); #endif int apos = 0; int bpos = 0; int cmp; while (apos < length_a) { cmp = binding_row_compare(q, a, b, apos, bpos, length_a, length_b); if (cmp == -1 || cmp == -2) { /* A and B aren't compatible, keep A row */ for (int col=0; a[col].name; col++) { if (!c[col].need_val) { continue; } else if (a[col].bound) { fs_rid_vector_append(c[col].vals, table_value(a, col, apos)); } else { fs_rid_vector_append(c[col].vals, FS_RID_NULL); } } apos++; } else if (cmp == 0) { /* Both rows are equal (cmp == 0), skip A row in result */ #if DEBUG_MERGE > 1 printf("[I] Ar=%d, Br=%d", apos, bpos); #endif int range_a = apos+1; int range_b = bpos+1; while (binding_row_compare(q, a, a, apos, range_a, length_a, length_a) == 0) range_a++; while (binding_row_compare(q, b, b, bpos, range_b, length_b, length_b) == 0) range_b++; apos = range_a; bpos = range_b; } else if (cmp == +1 || cmp == +2) { /* A and B aren't compatible, B sorts lower, skip B or B row is NULL */ bpos++; } else { fs_error(LOG_ERR, "cmp=%d, value out of range", cmp); } } /* clear the _ord columns */ a[0].vals->length = 0; b[0].vals->length = 0; #ifdef DEBUG_MERGE printf("result: %d bindings\n", fs_binding_length(c)); fs_binding_print(c, stdout); #endif return c; }