str ITRbunNext(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) { BATiter bi; BAT *b; oid *head; bat *bid; ValPtr tail; (void) cntxt; (void) mb; head = (oid *) getArgReference(stk, pci, 0); tail = getArgReference(stk,pci,1); bid = (bat *) getArgReference(stk, pci, 2); if ((b = BATdescriptor(*bid)) == NULL) { throw(MAL, "iterator.nextChunk", INTERNAL_BAT_ACCESS); } *head = (BUN)*head + 1; if (*head >= BUNlast(b)) { *head = oid_nil; BBPunfix(b->batCacheid); return MAL_SUCCEED; } bi = bat_iterator(b); VALinit(tail, b->ttype, BUNtail(bi, *(BUN*) head)); BBPunfix(b->batCacheid); return MAL_SUCCEED; }
static str gsl_bat_chisqprob_cst(bat * retval, bat chi2, dbl datapoints) { BAT *b, *bn; BATiter bi; BUN p,q; dbl r; char *msg = NULL; if (datapoints == dbl_nil) { throw(MAL, "GSLbat_chisqprob_cst", "Parameter datapoints should not be nil"); } if (datapoints < 0) throw(MAL, "gsl.chi2prob", "Wrong value for datapoints"); if ((b = BATdescriptor(chi2)) == NULL) { throw(MAL, "chisqprob", "Cannot access descriptor"); } bi = bat_iterator(b); bn = BATnew(TYPE_void, TYPE_dbl, BATcount(b), TRANSIENT); if (bn == NULL){ BBPunfix(b->batCacheid); throw(MAL, "gsl.chisqprob", MAL_MALLOC_FAIL); } BATseqbase(bn, b->hseqbase); BATloop(b,p,q) { dbl d = *(dbl*)BUNtail(bi,p); if ((d == dbl_nil) || (d < 0)) throw(MAL, "gsl.chi2prob", "Wrong value for chi2"); r = gsl_cdf_chisq_Q(d, datapoints); BUNappend(bn, &r, FALSE); }
/* * @- * The BUN- and BAT-stream manipulate a long handle, i.e. * the destination variable. It assumes it has been set to * zero as part of runtime stack initialization. Subsequently, * it fetches a bun and returns the increment to the control * variable. If it returns zero the control variable has been reset * to zero and end of stream has been reached. */ str ITRbunIterator(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) { BATiter bi; BAT *b; oid *head; bat *bid; ValPtr tail; (void) cntxt; (void) mb; head = getArgReference_oid(stk, pci, 0); tail = &stk->stk[pci->argv[1]]; bid = getArgReference_bat(stk, pci, 2); if ((b = BATdescriptor(*bid)) == NULL) { throw(MAL, "iterator.nextChunk", INTERNAL_BAT_ACCESS); } if (BATcount(b) == 0) { *head = oid_nil; BBPunfix(b->batCacheid); return MAL_SUCCEED; } *head = BUNfirst(b); bi = bat_iterator(b); VALinit(tail, b->ttype, BUNtail(bi, *(BUN*) head)); BBPunfix(b->batCacheid); return MAL_SUCCEED; }
str MRgetCloud(int *ret, str *mrcluster) { str msg; BAT *cloud; BUN p, q; BATiter bi; char nodes[BUFSIZ]; char *n = nodes; int mapcount = 0; snprintf(nodes, sizeof(nodes), "*/%s/node/*", *mrcluster); if ((msg = RMTresolve(ret, &n)) != MAL_SUCCEED) return msg; MT_lock_set(&mal_contextLock, "mapreduce"); cloud = BATdescriptor(*ret); /* should succeed */ mapnodes = (mapnode*)GDKzalloc(sizeof(mapnode) * (BATcount(cloud) + 1)); if (mapnodes == NULL) { BBPreleaseref(*ret); throw(MAL, "mapreduce.getCloud", MAL_MALLOC_FAIL); } bi = bat_iterator(cloud); BATloop(cloud, p, q) { str t = (str)BUNtail(bi, p); mapnodes[mapcount].uri = GDKstrdup(t); mapnodes[mapcount].user = GDKstrdup("monetdb"); mapnodes[mapcount].pass = GDKstrdup("monetdb"); mapcount++; }
/** * Function returns the highest file id that currently exists in the * bam.files table and stores this number + 1 in the next_file_id * variable. Function doesn't use a mutex to guarantee that the * returned value is up to date at return time. If this is required, * the calling function should activate a mutex. */ str next_file_id(mvc * m, sql_table * files, lng * next_file_id) { sql_column *c; BAT *b = NULL; BATiter li; BUN p = 0, q = 0; lng max_file_id = 0; sht i; assert(m != NULL); assert(files != NULL); /* Try to bind the file_id column of the bam.files table */ if ((c = mvc_bind_column(m, files, "file_id")) == NULL) { throw(MAL, "next_file_id", "Could not retrieve the next file id: Error binding file_id column of 'files' table"); } /* Loop through BATs for this column and find the maximum file_id */ for(i=0; i<3; ++i) { b = store_funcs.bind_col(m->session->tr, c, i); li = bat_iterator(b); BATloop(b, p, q) { lng t = *(lng *) BUNtail(li, p); max_file_id = MAX(max_file_id, t); } BBPunfix(b->batCacheid); }
str BATXMLdocument(bat *ret, const bat *bid) { BAT *b, *bn; BUN p, q; BATiter bi; size_t size = BUFSIZ; str buf = GDKmalloc(size); const char *err = OPERATION_FAILED; if (buf == NULL) throw(MAL,"xml.document",MAL_MALLOC_FAIL); if ((b = BATdescriptor(*bid)) == NULL) { GDKfree(buf); throw(MAL, "xml.document", INTERNAL_BAT_ACCESS); } prepareResult(bn, b, TYPE_xml, "document", GDKfree(buf)); bi = bat_iterator(b); BATloop(b, p, q) { const char *t = (const char *) BUNtail(bi, p); xmlDocPtr doc; int len; xmlChar *s; if (strNil(t)) { bunfastapp(bn, str_nil); bn->T->nonil = 0; continue; } len = (int) strlen(t); doc = xmlParseMemory(t, len); if (doc == NULL) { err = OPERATION_FAILED XML_PARSE_ERROR; goto bunins_failed; } xmlDocDumpMemory(doc, &s, &len); xmlFreeDoc(doc); if ((size_t) len + 2 >= size) { GDKfree(buf); size = (size_t) len + 128; buf = GDKmalloc(size); if (buf == NULL) { err= MAL_MALLOC_FAIL; goto bunins_failed; } } buf[0] = 'D'; strcpy(buf + 1, (char *) s); bunfastapp(bn, buf); } GDKfree(buf); finalizeResult(ret, bn, b); return MAL_SUCCEED; bunins_failed: GDKfree(buf); BBPunfix(b->batCacheid); BBPunfix(bn->batCacheid); throw(MAL, "xml.document", "%s", err); }
str BATXMLcomment(bat *ret, const bat *bid) { BAT *b, *bn; BUN p, q; size_t size = BUFSIZ; str buf = GDKmalloc(size); BATiter bi; const char *err= OPERATION_FAILED; if (buf == NULL) throw(MAL, "xml.comment", MAL_MALLOC_FAIL); if ((b = BATdescriptor(*bid)) == NULL) { GDKfree(buf); throw(MAL, "xml.comment", INTERNAL_BAT_ACCESS); } prepareResult(bn, b, TYPE_xml, "comment", GDKfree(buf)); bi = bat_iterator(b); BATloop(b, p, q) { const char *t = (const char *) BUNtail(bi, p); size_t len; if (strNil(t)) { bunfastapp(bn, str_nil); bn->T->nonil = 0; continue; } if (strstr(t, "--") != NULL) { err = XML_COMMENT_ERROR; goto bunins_failed; } len = strlen(t); if (len + 9 >= size) { /* make sure there is enough space */ size = len + 128; /* free/malloc so we don't copy */ GDKfree(buf); buf = GDKmalloc(size); if (buf == NULL) { err = MAL_MALLOC_FAIL; goto bunins_failed; } } snprintf(buf, size, "C<!--%s-->", t); bunfastapp(bn, buf); } GDKfree(buf); finalizeResult(ret, bn, b); return MAL_SUCCEED; bunins_failed: BBPunfix(b->batCacheid); BBPunfix(bn->batCacheid); if (buf != NULL) GDKfree(buf); throw(MAL, "xml.comment", "%s", err); }
/* actual implementation */ static char * UDFBATreverse_(BAT **ret, BAT *src) { BATiter li; BAT *bn = NULL; BUN p = 0, q = 0; /* assert calling sanity */ assert(ret != NULL); /* handle NULL pointer */ if (src == NULL) throw(MAL, "batudf.reverse", RUNTIME_OBJECT_MISSING); /* check tail type */ if (src->ttype != TYPE_str) { throw(MAL, "batudf.reverse", "tail-type of input BAT must be TYPE_str"); } /* allocate result BAT */ bn = BATnew(src->htype, TYPE_str, BATcount(src)); if (bn == NULL) { throw(MAL, "batudf.reverse", MAL_MALLOC_FAIL); } BATseqbase(bn, src->hseqbase); /* create BAT iterator */ li = bat_iterator(src); /* the core of the algorithm, expensive due to malloc/frees */ BATloop(src, p, q) { char *tr = NULL, *err = NULL; /* get original head & tail value */ ptr h = BUNhead(li, p); const char *t = (const char *) BUNtail(li, p); /* revert tail value */ err = UDFreverse_(&tr, t); if (err != MAL_SUCCEED) { /* error -> bail out */ BBPreleaseref(bn->batCacheid); return err; } /* assert logical sanity */ assert(tr != NULL); /* insert original head and reversed tail in result BAT */ /* BUNins() takes care of all necessary administration */ BUNins(bn, h, tr, FALSE); /* free memory allocated in UDFreverse_() */ GDKfree(tr); }
char * GDKgetenv(const char *name) { BUN b = BUNfnd(BATmirror(GDKkey), (ptr) name); if (b != BUN_NONE) { BATiter GDKenvi = bat_iterator(GDKval); return BUNtail(GDKenvi, b); } return NULL; }
/* * The core of the activity is str2xml, where the actual strings * are constructed. * To avoid repetitive copying we make sure that the garbage * collector does not remove the xml intermediates. * This way, we know that as long as the xml-variables are not * reused, the complete structure of the xml document(s) are available. * We merely have to collect the pieces. * [FOR LATER, FIRST GO FOR THE EASY IMPLEMENTATION] * XML values are represented by strings already. */ str BATXMLstr2xml(bat *ret, const bat *bid) { BAT *b, *bn; BUN p, q; size_t size = BUFSIZ; str buf; const char *err= OPERATION_FAILED; BATiter bi; buf = GDKmalloc(size); if (buf == NULL) throw(MAL,"xml.str2xml",MAL_MALLOC_FAIL); if ((b = BATdescriptor(*bid)) == NULL) { GDKfree(buf); throw(MAL, "xml.xml", INTERNAL_BAT_ACCESS); } prepareResult(bn, b, TYPE_xml, "xml", GDKfree(buf)); bi = bat_iterator(b); BATloop(b, p, q) { const char *t = (const char *) BUNtail(bi, p); size_t len; if (strNil(t)) { bunfastapp(bn, str_nil); bn->T->nonil = 0; continue; } len = strlen(t) * 6 + 1; if (size < len) { size = len + 128; GDKfree(buf); buf = GDKmalloc(size); if (buf == NULL) { err = MAL_MALLOC_FAIL; goto bunins_failed; } } buf[0] = 'C'; XMLquotestring(t, buf + 1, size - 1); bunfastapp(bn, buf); } GDKfree(buf); finalizeResult(ret, bn, b); return MAL_SUCCEED; bunins_failed: BBPunfix(b->batCacheid); BBPunfix(bn->batCacheid); if (buf != NULL) GDKfree(buf); throw(MAL, "xml.xml", "%s", err); }
BUN append_inserted(BAT *b, BAT *i ) { BUN nr = 0, r; BATiter ii = bat_iterator(i); for (r = i->batInserted; r < BUNlast(i); r++) { BUNappend(b, BUNtail(ii,r), TRUE); nr++; } return nr; }
str BATXMLgroup(xml *ret, const bat *bid) { BAT *b; BUN p, q; const char *t; size_t len, size = BUFSIZ, offset; str buf = GDKmalloc(size); BATiter bi; const char *err = NULL; if (buf == NULL) throw(MAL, "xml.aggr",MAL_MALLOC_FAIL); if ((b = BATdescriptor(*bid)) == NULL) { GDKfree(buf); throw(MAL, "xml.aggr", RUNTIME_OBJECT_MISSING); } strcpy(buf, str_nil); offset = 0; bi = bat_iterator(b); BATloop(b, p, q) { int n; t = (const char *) BUNtail(bi, p); if (strNil(t)) continue; len = strlen(t) + 1; if (len >= size - offset) { size += len + 128; buf = GDKrealloc(buf, size); if (buf == NULL) { err= MAL_MALLOC_FAIL; goto failed; } } if (offset == 0) n = snprintf(buf, size, "%s", t); else if (buf[0] != *t) { err = "incompatible values in group"; goto failed; } else if (buf[0] == 'A') n = snprintf(buf + offset, size - offset, " %s", t + 1); else if (buf[0] == 'C') n = snprintf(buf + offset, size - offset, "%s", t + 1); else { err = "can only group attributes and element content"; goto failed; } offset += n; }
BUN copy_inserted(BAT *b, BAT *i ) { BUN nr = 0; BUN r; BATiter ii = bat_iterator(i); for (r = i->batInserted; r < BUNlast(i); r++) { BUNins(b, BUNhead(ii,r), BUNtail(ii,r), TRUE); nr++; } return nr; }
static int bat_max_strlength(BAT *b) { BUN p, q; int l = 0; int max = 0; BATiter bi = bat_iterator(b); BATloop(b, p, q) { str v = (str) BUNtail(bi, p); strLength(&l, v); if (l == int_nil) l = 0; if (l > max) max = l; }
static oid column_find_row(sql_trans *tr, sql_column *c, void *value, ...) { va_list va; BUN q; BAT *b = NULL, *s = NULL, *r = NULL, *d = NULL; oid rid = oid_nil; sql_column *nc; void *nv; sql_dbat *bat = c->t->data; if (bat->dbid) d = store_funcs.bind_del(tr, c->t, RDONLY); va_start(va, value); while ((nc = va_arg(va, sql_column *)) != NULL) { nv = va_arg(va, void *); b = full_column(c, d, s); if (s) bat_destroy(s); s = BATselect(b, value, value); bat_destroy(b); c = nc; value = nv; } va_end(va); b = full_column(c, d, s); if (s) bat_destroy(s); if (d) bat_destroy(d); r = BATmirror(b); q = BUNfnd(r, value); if (q != BUN_NONE) { BATiter ri = bat_iterator(r); rid = *(oid *) BUNtail(ri, q); } bat_destroy(b); return rid; }
str batnil_2_timestamp(bat *res, const bat *bid) { BAT *b, *dst; BATiter bi; BUN p, q; if ((b = BATdescriptor(*bid)) == NULL) { throw(SQL, "batcalc.nil_2_timestamp", "Cannot access descriptor"); } bi = bat_iterator(b); dst = BATnew(b->htype, TYPE_timestamp, BATcount(b), TRANSIENT); if (dst == NULL) { BBPunfix(b->batCacheid); throw(SQL, "sql.2_timestamp", MAL_MALLOC_FAIL); } BATseqbase(dst, b->hseqbase); BATloop(b, p, q) { timestamp r = *timestamp_nil; BUNins(dst, BUNhead(bi, p), &r, FALSE); }
str BATXMLisdocument(bat *ret, const bat *bid) { BAT *b, *bn; BUN p, q; BATiter bi; if ((b = BATdescriptor(*bid)) == NULL) throw(MAL, "xml.isdocument", INTERNAL_BAT_ACCESS); prepareResult(bn, b, TYPE_bit, "isdocument", (void) 0); bi = bat_iterator(b); BATloop(b, p, q) { const char *t = (const char *) BUNtail(bi, p); xmlDocPtr doc; bit val; if (strNil(t)) { val = bit_nil; bn->T->nonil = 0; } else { doc = xmlParseMemory(t, (int) strlen(t)); if (doc == NULL) { val = 0; } else { xmlFreeDoc(doc); val = 1; } } bunfastapp(bn, &val); } finalizeResult(ret, bn, b); return MAL_SUCCEED; bunins_failed: BBPunfix(b->batCacheid); BBPunfix(bn->batCacheid); throw(MAL, "xml.isdocument", OPERATION_FAILED " During bulk processing"); }
static str JSONrenderRowObject(BAT **bl, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci, BUN idx) { int i, tpe; char *row, *name = 0, *val = 0; size_t len, lim, l; void *p; BATiter bi; row = (char *) GDKmalloc(lim = BUFSIZ); row[0] = '{'; row[1] = 0; len = 1; val = (char *) GDKmalloc(BUFSIZ); for (i = pci->retc; i < pci->argc; i += 2) { name = stk->stk[getArg(pci, i)].val.sval; bi = bat_iterator(bl[i + 1]); p = BUNtail(bi, BUNfirst(bl[i + 1]) + idx); tpe = getColumnType(getArgType(mb, pci, i + 1)); ATOMformat(tpe, p, &val); if (strncmp(val, "nil", 3) == 0) strcpy(val, "null"); l = strlen(name) + strlen(val); while (l > lim - len) row = (char *) GDKrealloc(row, lim += BUFSIZ); snprintf(row + len, lim - len, "\"%s\":%s,", name, val); len += l + 4; } if (row[1]) row[len - 1] = '}'; else { row[1] = '}'; row[2] = 0; } GDKfree(val); return row; }
gdk_return TMsubcommit(BAT *b) { int cnt = 1; gdk_return ret = GDK_FAIL; bat *subcommit; BUN p, q; BATiter bi = bat_iterator(b); subcommit = (bat *) GDKmalloc((BATcount(b) + 1) * sizeof(bat)); if (subcommit == NULL) return GDK_FAIL; subcommit[0] = 0; /* BBP artifact: slot 0 in the array will be ignored */ /* collect the list and save the new bats outside any * locking */ BATloop(b, p, q) { bat bid = BBPindex((str) BUNtail(bi, p)); if (bid < 0) bid = -bid; if (bid) subcommit[cnt++] = bid; }
/* * The remainder are utilities to manipulate the BAT view and not to * forget some details in the process. It expects a position range in * the underlying BAT and compensates for outliers. */ void VIEWbounds(BAT *b, BAT *view, BUN l, BUN h) { BUN cnt; BATiter bi = bat_iterator(b); if (b == NULL || view == NULL) { GDKerror("VIEWbounds: bat argument missing"); return; } if (h > BATcount(b)) h = BATcount(b); if (h < l) h = l; l += BUNfirst(b); view->batFirst = view->batDeleted = view->batInserted = 0; cnt = h - l; view->H->heap.base = (view->htype) ? BUNhloc(bi, l) : NULL; view->T->heap.base = (view->ttype) ? BUNtloc(bi, l) : NULL; view->H->heap.size = headsize(view, cnt); view->T->heap.size = tailsize(view, cnt); BATsetcount(view, cnt); BATsetcapacity(view, cnt); }
static str JSONrenderRowArray(BAT **bl, MalBlkPtr mb, InstrPtr pci, BUN idx) { int i, tpe; char *row, *val = 0; size_t len, lim, l; void *p; BATiter bi; row = (char *) GDKmalloc(lim = BUFSIZ); row[0] = '['; row[1] = 0; len = 1; val = (char *) GDKmalloc(BUFSIZ); for (i = pci->retc; i < pci->argc; i++) { bi = bat_iterator(bl[i]); p = BUNtail(bi, BUNfirst(bl[i]) + idx); tpe = getColumnType(getArgType(mb, pci, i)); ATOMformat(tpe, p, &val); if (strncmp(val, "nil", 3) == 0) strcpy(val, "null"); l = strlen(val); while (l > lim - len) row = (char *) GDKrealloc(row, lim += BUFSIZ); snprintf(row + len, lim - len, "%s,", val); len += l + 1; } if (row[1]) row[len - 1] = ']'; else { row[1] = '}'; row[2] = 0; } GDKfree(val); return row; }
/* * The standard supports specific mappings for * NULL values,i.e. {null,absent,empty,nil,niloncontent) * in the context of an element and forest construction. * The standard should be studied in more detail, because * the syntax(rules) seem ambiguous. * It applies to all components of an element or their * concatenation. * * For the time being, the variaton on XMLtag seems the * most reasonable interpretation. */ str BATXMLoptions(bat *ret, const char * const *name, const char * const *options, const bat *bid) { BAT *b, *bn; BUN p, q; str buf = GDKmalloc(BUFSIZ); str val = GDKmalloc(BUFSIZ); size_t size = BUFSIZ, len = strlen(*name); BATiter bi; const char *err = OPERATION_FAILED " During bulk options analysis"; if (val == NULL || buf == NULL) { if (val != NULL) GDKfree(val); if (buf != NULL) GDKfree(buf); throw(MAL, "batxml.options", MAL_MALLOC_FAIL); } if ((b = BATdescriptor(*bid)) == NULL) { GDKfree(val); GDKfree(buf); throw(MAL, "xml.options", INTERNAL_BAT_ACCESS); } prepareResult(bn, b, TYPE_xml, "options", GDKfree(val); GDKfree(buf)); if (strcmp(*options, "absent") == 0) buf[0] = 0; else if (strcmp(*options, "empty") == 0) snprintf(buf, size, "<%s></%s>", *name, *name); else if (strcmp(*options, "null") == 0) snprintf(buf, size, "null"); else if (strcmp(*options, "nil") == 0) snprintf(buf, size, "nil"); else { /*if(strcmp(*options,"niloncontent")==0) */ err = PROGRAM_NYI; goto bunins_failed; } snprintf(val, size, "<%s>", *name); bi = bat_iterator(b); BATloop(b, p, q) { const char *t = (const char *) BUNtail(bi, p); if (strNil(t)) { bunfastapp(bn, buf); } else { if (strlen(t) > size - 2 * len - 6) { size += strlen(t); val = (char *) GDKrealloc(val, size + strlen(t)); if (val == NULL) { err = MAL_MALLOC_FAIL; goto bunins_failed; } } snprintf(val + len + 2, size - len, "%s</%s>", t, *name); bunfastapp(bn, val); } } GDKfree(val); GDKfree(buf); finalizeResult(ret, bn, b); return MAL_SUCCEED; bunins_failed: BBPunfix(b->batCacheid); BBPunfix(bn->batCacheid); if (buf != NULL) GDKfree(buf); if (val != NULL) GDKfree(val); throw(MAL, "batxml.options", "%s", err); }
str MKEYbathash(bat *res, const bat *bid) { BAT *b, *dst; wrd *r; BUN n; if ((b = BATdescriptor(*bid)) == NULL) throw(SQL, "mkey.bathash", RUNTIME_OBJECT_MISSING); assert(BAThvoid(b) || BAThrestricted(b)); n = BATcount(b); dst = BATnew(TYPE_void, TYPE_wrd, n, TRANSIENT); if (dst == NULL) { BBPunfix(b->batCacheid); throw(SQL, "mkey.bathash", MAL_MALLOC_FAIL); } BATseqbase(dst, b->hseqbase); BATsetcount(dst, n); r = (wrd *) Tloc(dst, BUNfirst(dst)); switch (ATOMstorage(b->ttype)) { case TYPE_void: { oid o = b->tseqbase; if (o == oid_nil) while (n-- > 0) *r++ = wrd_nil; else while (n-- > 0) *r++ = (wrd) o++; break; } case TYPE_bte: { bte *v = (bte *) Tloc(b, BUNfirst(b)); while (n-- > 0) { *r++ = MKEYHASH_bte(v); v++; } break; } case TYPE_sht: { sht *v = (sht *) Tloc(b, BUNfirst(b)); while (n-- > 0) { *r++ = MKEYHASH_sht(v); v++; } break; } case TYPE_int: case TYPE_flt: { int *v = (int *) Tloc(b, BUNfirst(b)); while (n-- > 0) { *r++ = MKEYHASH_int(v); v++; } break; } case TYPE_lng: case TYPE_dbl: { lng *v = (lng *) Tloc(b, BUNfirst(b)); while (n-- > 0) { *r++ = MKEYHASH_lng(v); v++; } break; } #ifdef HAVE_HGE case TYPE_hge: { hge *v = (hge *) Tloc(b, BUNfirst(b)); while (n-- > 0) { *r++ = MKEYHASH_hge(v); v++; } break; } #endif default: { BATiter bi = bat_iterator(b); BUN (*hash)(const void *) = BATatoms[b->ttype].atomHash; int (*cmp)(const void *, const void *) = ATOMcompare(b->ttype); void *nil = ATOMnilptr(b->ttype); BUN i; const void *v; BATloop(b, i, n) { v = BUNtail(bi, i); if ((*cmp)(v, nil) == 0) *r++ = wrd_nil; else *r++ = (wrd) (*hash)(v); } break; } }
/* * The prime routine for the BAT layer is to create a new hash index. * Its argument is the element type and the maximum number of BUNs be * stored under the hash function. */ BAT * BAThash(BAT *b, BUN masksize) { BAT *o = NULL; lng t0,t1; (void) t0; (void) t1; if (VIEWhparent(b)) { bat p = VIEWhparent(b); o = b; b = BATdescriptor(p); if (!ALIGNsynced(o, b) || BUNfirst(o) != BUNfirst(b)) { BBPunfix(b->batCacheid); b = o; o = NULL; } } MT_lock_set(&GDKhashLock(ABS(b->batCacheid)), "BAThash"); if (b->H->hash == NULL) { unsigned int tpe = ATOMstorage(b->htype); BUN cnt = BATcount(b); BUN mask; BUN p = BUNfirst(b), q = BUNlast(b), r; Hash *h = NULL; Heap *hp = NULL; str nme = BBP_physical(b->batCacheid); BATiter bi = bat_iterator(b); ALGODEBUG fprintf(stderr, "#BAThash: create hash(" BUNFMT ");\n", BATcount(b)); /* cnt = 0, hopefully there is a proper capacity from * which we can derive enough information */ if (!cnt) cnt = BATcapacity(b); if (b->htype == TYPE_void) { if (b->hseqbase == oid_nil) { MT_lock_unset(&GDKhashLock(ABS(b->batCacheid)), "BAThash"); ALGODEBUG fprintf(stderr, "#BAThash: cannot create hash-table on void-NIL column.\n"); return NULL; } ALGODEBUG fprintf(stderr, "#BAThash: creating hash-table on void column..\n"); tpe = TYPE_void; } /* determine hash mask size p = first; then no dynamic * scheme */ if (masksize > 0) { mask = HASHmask(masksize); } else if (ATOMsize(ATOMstorage(tpe)) == 1) { mask = (1 << 8); } else if (ATOMsize(ATOMstorage(tpe)) == 2) { mask = (1 << 12); } else if (b->hkey) { mask = HASHmask(cnt); } else { /* dynamic hash: we start with * HASHmask(cnt/64); if there are too many * collisions we try HASHmask(cnt/16), then * HASHmask(cnt/4), and finally * HASHmask(cnt). */ mask = HASHmask(cnt >> 6); p += (cnt >> 2); /* try out on first 25% of b */ if (p > q) p = q; } if (mask < 1024) mask = 1024; t0 = GDKusec(); do { BUN nslots = mask >> 3; /* 1/8 full is too full */ r = BUNfirst(b); if (hp) { HEAPfree(hp); GDKfree(hp); } if (h) { ALGODEBUG fprintf(stderr, "#BAThash: retry hash construction\n"); GDKfree(h); } /* create the hash structures */ hp = (Heap *) GDKzalloc(sizeof(Heap)); if (hp && (hp->filename = GDKmalloc(strlen(nme) + 12)) != NULL) sprintf(hp->filename, "%s.%chash", nme, b->batCacheid > 0 ? 'h' : 't'); if (hp == NULL || hp->filename == NULL || (h = HASHnew(hp, ATOMtype(b->htype), BATcapacity(b), mask)) == NULL) { MT_lock_unset(&GDKhashLock(ABS(b->batCacheid)), "BAThash"); if (hp != NULL) { GDKfree(hp->filename); GDKfree(hp); } return NULL; } switch (tpe) { case TYPE_bte: starthash(bte); break; case TYPE_sht: starthash(sht); break; case TYPE_int: case TYPE_flt: starthash(int); break; case TYPE_dbl: case TYPE_lng: starthash(lng); break; default: for (; r < p; r++) { ptr v = BUNhead(bi, r); BUN c = (BUN) heap_hash_any(b->H->vheap, h, v); if ( HASHget(h,c) == HASHnil(h) && nslots-- == 0) break; /* mask too full */ HASHputlink(h,r, HASHget(h,c)); HASHput(h,c, r); } break; } } while (r < p && mask < cnt && (mask <<= 2)); /* finish the hashtable with the current mask */ p = r; switch (tpe) { case TYPE_bte: finishhash(bte); break; case TYPE_sht: finishhash(sht); break; case TYPE_int: case TYPE_flt: finishhash(int); break; case TYPE_dbl: case TYPE_lng: finishhash(lng); break; default: for (; p < q; p++) { ptr v = BUNhead(bi, p); BUN c = (BUN) heap_hash_any(b->H->vheap, h, v); HASHputlink(h,p, HASHget(h,c)); HASHput(h,c,p); } break; } b->H->hash = h; t1 = GDKusec(); ALGODEBUG fprintf(stderr, "#BAThash: hash construction "LLFMT" usec\n", t1-t0); ALGODEBUG HASHcollisions(b,b->H->hash); }
str BATXMLcontent(bat *ret, const bat *bid) { BAT *b, *bn; BUN p, q; BATiter bi; xmlDocPtr doc; xmlNodePtr root; size_t size = BUFSIZ; str buf = GDKmalloc(size); const char *err = OPERATION_FAILED; xmlBufferPtr xbuf; if (buf == NULL) throw(MAL,"xml.content",MAL_MALLOC_FAIL); if ((b = BATdescriptor(*bid)) == NULL) { GDKfree(buf); throw(MAL, "xml.content", INTERNAL_BAT_ACCESS); } doc = xmlParseMemory("<doc/>", 6); root = xmlDocGetRootElement(doc); prepareResult(bn, b, TYPE_xml, "content", GDKfree(buf)); bi = bat_iterator(b); xbuf = xmlBufferCreate(); BATloop(b, p, q) { const char *t = (const char *) BUNtail(bi, p); size_t len; xmlNodePtr elem; xmlParserErrors xerr; const xmlChar *s; if (strNil(t)) { bunfastapp(bn, str_nil); bn->T->nonil = 0; continue; } len = strlen(t); xerr = xmlParseInNodeContext(root, t, (int) len, 0, &elem); if (xerr != XML_ERR_OK) { err = XML_PARSE_ERROR; goto bunins_failed; } xmlNodeDump(xbuf, doc, elem, 0, 0); s = xmlBufferContent(xbuf); len = strlen((const char *) s); if (len + 2 >= size) { GDKfree(buf); size = len + 128; buf = GDKmalloc(size); if (buf == NULL) { err = MAL_MALLOC_FAIL; goto bunins_failed; } } buf[0] = 'C'; strcpy(buf + 1, (const char *) s); bunfastapp(bn, buf); xmlBufferEmpty(xbuf); xmlFreeNodeList(elem); } xmlBufferFree(xbuf); xmlFreeDoc(doc); GDKfree(buf); finalizeResult(ret, bn, b); return MAL_SUCCEED; bunins_failed: xmlBufferFree(xbuf); xmlFreeDoc(doc); if (buf != NULL) GDKfree(buf); BBPunfix(b->batCacheid); BBPunfix(bn->batCacheid); throw(MAL, "xml.document", "%s", err); }
str RAPIeval(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci, bit grouped) { sql_func * sqlfun = NULL; str exprStr = *getArgReference_str(stk, pci, pci->retc + 1); SEXP x, env, retval; SEXP varname = R_NilValue; SEXP varvalue = R_NilValue; ParseStatus status; int i = 0; char argbuf[64]; char *argnames = NULL; size_t argnameslen; size_t pos; char* rcall = NULL; size_t rcalllen; int ret_cols = 0; /* int because pci->retc is int, too*/ str *args; int evalErr; char *msg = MAL_SUCCEED; BAT *b; node * argnode; int seengrp = FALSE; rapiClient = cntxt; if (!RAPIEnabled()) { throw(MAL, "rapi.eval", "Embedded R has not been enabled. Start server with --set %s=true", rapi_enableflag); } if (!rapiInitialized) { throw(MAL, "rapi.eval", "Embedded R initialization has failed"); } if (!grouped) { sql_subfunc *sqlmorefun = (*(sql_subfunc**) getArgReference(stk, pci, pci->retc)); if (sqlmorefun) sqlfun = (*(sql_subfunc**) getArgReference(stk, pci, pci->retc))->func; } else { sqlfun = *(sql_func**) getArgReference(stk, pci, pci->retc); } args = (str*) GDKzalloc(sizeof(str) * pci->argc); if (args == NULL) { throw(MAL, "rapi.eval", SQLSTATE(HY001) MAL_MALLOC_FAIL); } // get the lock even before initialization of the R interpreter, as this can take a second and must be done only once. MT_lock_set(&rapiLock); env = PROTECT(eval(lang1(install("new.env")), R_GlobalEnv)); assert(env != NULL); // first argument after the return contains the pointer to the sql_func structure // NEW macro temporarily renamed to MNEW to allow including sql_catalog.h if (sqlfun != NULL && sqlfun->ops->cnt > 0) { int carg = pci->retc + 2; argnode = sqlfun->ops->h; while (argnode) { char* argname = ((sql_arg*) argnode->data)->name; args[carg] = GDKstrdup(argname); carg++; argnode = argnode->next; } } // the first unknown argument is the group, we don't really care for the rest. argnameslen = 2; for (i = pci->retc + 2; i < pci->argc; i++) { if (args[i] == NULL) { if (!seengrp && grouped) { args[i] = GDKstrdup("aggr_group"); seengrp = TRUE; } else { snprintf(argbuf, sizeof(argbuf), "arg%i", i - pci->retc - 1); args[i] = GDKstrdup(argbuf); } } argnameslen += strlen(args[i]) + 2; /* extra for ", " */ } // install the MAL variables into the R environment // we can basically map values to int ("INTEGER") or double ("REAL") for (i = pci->retc + 2; i < pci->argc; i++) { int bat_type = getBatType(getArgType(mb,pci,i)); // check for BAT or scalar first, keep code left if (!isaBatType(getArgType(mb,pci,i))) { b = COLnew(0, getArgType(mb, pci, i), 0, TRANSIENT); if (b == NULL) { msg = createException(MAL, "rapi.eval", SQLSTATE(HY001) MAL_MALLOC_FAIL); goto wrapup; } if ( getArgType(mb,pci,i) == TYPE_str) { if (BUNappend(b, *getArgReference_str(stk, pci, i), false) != GDK_SUCCEED) { BBPreclaim(b); b = NULL; msg = createException(MAL, "rapi.eval", SQLSTATE(HY001) MAL_MALLOC_FAIL); goto wrapup; } } else { if (BUNappend(b, getArgReference(stk, pci, i), false) != GDK_SUCCEED) { BBPreclaim(b); b = NULL; msg = createException(MAL, "rapi.eval", SQLSTATE(HY001) MAL_MALLOC_FAIL); goto wrapup; } } } else { b = BATdescriptor(*getArgReference_bat(stk, pci, i)); if (b == NULL) { msg = createException(MAL, "rapi.eval", SQLSTATE(HY001) MAL_MALLOC_FAIL); goto wrapup; } } // check the BAT count, if it is bigger than RAPI_MAX_TUPLES, fail if (BATcount(b) > RAPI_MAX_TUPLES) { msg = createException(MAL, "rapi.eval", "Got "BUNFMT" rows, but can only handle "LLFMT". Sorry.", BATcount(b), (lng) RAPI_MAX_TUPLES); BBPunfix(b->batCacheid); goto wrapup; } varname = PROTECT(Rf_install(args[i])); varvalue = bat_to_sexp(b, bat_type); if (varvalue == NULL) { msg = createException(MAL, "rapi.eval", "unknown argument type "); goto wrapup; } BBPunfix(b->batCacheid); // install vector into R environment Rf_defineVar(varname, varvalue, env); UNPROTECT(2); } /* we are going to evaluate the user function within an anonymous function call: * ret <- (function(arg1){return(arg1*2)})(42) * the user code is put inside the {}, this keeps our environment clean (TM) and gives * a clear path for return values, namely using the builtin return() function * this is also compatible with PL/R */ pos = 0; argnames = malloc(argnameslen); if (argnames == NULL) { msg = createException(MAL, "rapi.eval", SQLSTATE(HY001) MAL_MALLOC_FAIL); goto wrapup; } argnames[0] = '\0'; for (i = pci->retc + 2; i < pci->argc; i++) { pos += snprintf(argnames + pos, argnameslen - pos, "%s%s", args[i], i < pci->argc - 1 ? ", " : ""); } rcalllen = 2 * pos + strlen(exprStr) + 100; rcall = malloc(rcalllen); if (rcall == NULL) { msg = createException(MAL, "rapi.eval", SQLSTATE(HY001) MAL_MALLOC_FAIL); goto wrapup; } snprintf(rcall, rcalllen, "ret <- as.data.frame((function(%s){%s})(%s), nm=NA, stringsAsFactors=F)\n", argnames, exprStr, argnames); free(argnames); argnames = NULL; #ifdef _RAPI_DEBUG_ printf("# R call %s\n",rcall); #endif x = R_ParseVector(mkString(rcall), 1, &status, R_NilValue); if (LENGTH(x) != 1 || status != PARSE_OK) { msg = createException(MAL, "rapi.eval", "Error parsing R expression '%s'. ", exprStr); goto wrapup; } retval = R_tryEval(VECTOR_ELT(x, 0), env, &evalErr); if (evalErr != FALSE) { char* errormsg = strdup(R_curErrorBuf()); size_t c; if (errormsg == NULL) { msg = createException(MAL, "rapi.eval", "Error running R expression."); goto wrapup; } // remove newlines from error message so it fits into a MAPI error (lol) for (c = 0; c < strlen(errormsg); c++) { if (errormsg[c] == '\r' || errormsg[c] == '\n') { errormsg[c] = ' '; } } msg = createException(MAL, "rapi.eval", "Error running R expression: %s", errormsg); free(errormsg); goto wrapup; } // ret should be a data frame with exactly as many columns as we need from retc ret_cols = LENGTH(retval); if (ret_cols != pci->retc) { msg = createException(MAL, "rapi.eval", "Expected result of %d columns, got %d", pci->retc, ret_cols); goto wrapup; } // collect the return values for (i = 0; i < pci->retc; i++) { SEXP ret_col = VECTOR_ELT(retval, i); int bat_type = getBatType(getArgType(mb,pci,i)); if (bat_type == TYPE_any || bat_type == TYPE_void) { getArgType(mb,pci,i) = bat_type; msg = createException(MAL, "rapi.eval", "Unknown return value, possibly projecting with no parameters."); goto wrapup; } // hand over the vector into a BAT b = sexp_to_bat(ret_col, bat_type); if (b == NULL) { msg = createException(MAL, "rapi.eval", "Failed to convert column %i", i); goto wrapup; } // bat return if (isaBatType(getArgType(mb,pci,i))) { *getArgReference_bat(stk, pci, i) = b->batCacheid; } else { // single value return, only for non-grouped aggregations BATiter li = bat_iterator(b); if (VALinit(&stk->stk[pci->argv[i]], bat_type, BUNtail(li, 0)) == NULL) { // TODO BUNtail here msg = createException(MAL, "rapi.eval", SQLSTATE(HY001) MAL_MALLOC_FAIL); goto wrapup; } } msg = MAL_SUCCEED; } /* unprotect environment, so it will be eaten by the GC. */ UNPROTECT(1); wrapup: MT_lock_unset(&rapiLock); if (argnames) free(argnames); if (rcall) free(rcall); for (i = 0; i < pci->argc; i++) GDKfree(args[i]); GDKfree(args); return msg; }
str BATXMLelement(bat *ret, const char * const *name, xml *nspace, xml *attr, const bat *bid) { BAT *b, *bn; BUN p, q; size_t size = BUFSIZ; str buf; BATiter bi; size_t elemlen, namelen; const char *err = OPERATION_FAILED; if (strNil(*name)) throw(MAL, "xml.element", XML_NO_ELEMENT); if (xmlValidateName((xmlChar *) *name, 0) != 0) throw(MAL, "xml.element", XML_ATTRIBUTE_INVALID); if (nspace && !strNil(*nspace) && **nspace) throw(MAL, "xml.element", XML_NO_NAMESPACE); namelen = strlen(*name); elemlen = namelen + 5; if (nspace && !strNil(*nspace)) { if (**nspace != 'A') throw(MAL, "xml.element", XML_ILLEGAL_NAMESPACE); elemlen += strlen(*nspace); /* " " + nspace (nspace contains initial 'A' which is replaced by space) */ } if (attr && !strNil(*attr)) { if (**attr != 'A') throw(MAL, "xml.element", XML_ILLEGAL_ATTRIBUTE); elemlen += strlen(*attr); /* " " + attr (attr contains initial 'A' which is replaced by space) */ } buf = GDKmalloc(size); if (buf == NULL) throw(MAL, "xml.attribute", MAL_MALLOC_FAIL); if ((b = BATdescriptor(*bid)) == NULL) { GDKfree(buf); throw(MAL, "xml.element", INTERNAL_BAT_ACCESS); } prepareResult(bn, b, TYPE_xml, "element", GDKfree(buf)); bi = bat_iterator(b); BATloop(b, p, q) { const char *t = (const char *) BUNtail(bi, p); size_t len; len = elemlen; if (!strNil(t)) { if (*t != 'C') { err = XML_ILLEGAL_CONTENT; goto bunins_failed; } len += strlen(t + 1) + namelen + 2; /* extra "<", ">", and name ("/" already counted) */ } if (len >= size) { /* make sure there is enough space */ size = len + 128; /* free/malloc so we don't copy */ GDKfree(buf); buf = GDKmalloc(size); if (buf == NULL) { err = MAL_MALLOC_FAIL; goto bunins_failed; } } if (strNil(t) && (!attr || strNil(*attr))) { strcpy(buf, str_nil); bn->T->nonil = 0; } else { int i = snprintf(buf, size, "C<%s", *name); if (nspace && !strNil(*nspace)) i += snprintf(buf + i, size - i, " %s", *nspace + 1); if (attr && !strNil(*attr)) i += snprintf(buf + i, size - i, " %s", *attr + 1); if (!strNil(t)) i += snprintf(buf + i, size - i, ">%s</%s>", t + 1, *name); else i += snprintf(buf + i, size - i, "/>"); } bunfastapp(bn, buf); } GDKfree(buf); finalizeResult(ret, bn, b); return MAL_SUCCEED; bunins_failed: BBPunfix(b->batCacheid); BBPunfix(bn->batCacheid); if (buf != NULL) GDKfree(buf); throw(MAL, "xml.element", "%s", err); }
str BATXMLattribute(bat *ret, const char * const *name, const bat *bid) { BAT *b, *bn; BUN p, q; size_t size = BUFSIZ; str buf; BATiter bi; size_t attrlen; const char *err = OPERATION_FAILED; if (strNil(*name)) throw(MAL, "xml.attribute", XML_ATTRIBUTE_ERROR); if (xmlValidateName((xmlChar *) *name, 0) != 0) throw(MAL, "xml.attribute", XML_ATTRIBUTE_INVALID); attrlen = strlen(*name) + 5; buf = GDKmalloc(size); if (buf == NULL) throw(MAL, "xml.attribute", MAL_MALLOC_FAIL); if ((b = BATdescriptor(*bid)) == NULL) { GDKfree(buf); throw(MAL, "xml.attribute", INTERNAL_BAT_ACCESS); } prepareResult(bn, b, TYPE_xml, "attribute", GDKfree(buf)); bi = bat_iterator(b); BATloop(b, p, q) { const char *t = (const char *) BUNtail(bi, p); size_t len; len = attrlen; if (!strNil(t)) len += strlen(t) * 6 + 1; if (len >= size) { /* make sure there is enough space */ size = len + 128; /* free/malloc so we don't copy */ GDKfree(buf); buf = GDKmalloc(size); if (buf == NULL) { err = MAL_MALLOC_FAIL; goto bunins_failed; } } if (strNil(t)) { strcpy(buf, str_nil); bn->T->nonil = 0; } else { int n = snprintf(buf, size, "A%s = \"", *name); size_t m = XMLquotestring(t, buf + n, size - n); strcpy(buf + n + m, "\""); } bunfastapp(bn, buf); } GDKfree(buf); finalizeResult(ret, bn, b); return MAL_SUCCEED; bunins_failed: BBPunfix(b->batCacheid); BBPunfix(bn->batCacheid); if (buf != NULL) GDKfree(buf); throw(MAL, "xml.attribute", "%s", err); }
str BATXMLroot(bat *ret, const bat *bid, const char * const *version, const char * const *standalone) { BAT *b, *bn; BUN p, q; size_t size = BUFSIZ; str buf; BATiter bi; size_t hdrlen; const char *err = OPERATION_FAILED; hdrlen = 8; if (!strNil(*version) && **version) { if (strcmp(*version, "1.0") != 0 && strcmp(*version, "1.1") != 0) throw(MAL, "xml.root", XML_VERSION_ERROR); hdrlen += 11 + strlen(*version); /* strlen(" version=\"\"") */ } if (!strNil(*standalone) && **standalone) { if (strcmp(*standalone, "yes") != 0 && strcmp(*standalone, "no") != 0) throw(MAL, "xml.root", XML_STANDALONE_ERROR "illegal XML standalone value"); hdrlen += 14 + strlen(*standalone); /* strlen(" standalone=\"\"") */ } buf = GDKmalloc(size); if (buf == NULL) throw(MAL, "xml.root", MAL_MALLOC_FAIL); if ((b = BATdescriptor(*bid)) == NULL) { GDKfree(buf); throw(MAL, "xml.pi", INTERNAL_BAT_ACCESS); } prepareResult(bn, b, TYPE_xml, "pi", GDKfree(buf)); bi = bat_iterator(b); BATloop(b, p, q) { const char *t = (const char *) BUNtail(bi, p); size_t len, i; bit isdoc; len = hdrlen; if (!strNil(t)) len += strlen(t); if (len >= size) { /* make sure there is enough space */ size = len + 128; /* free/malloc so we don't copy */ GDKfree(buf); buf = GDKmalloc(size); if (buf == NULL) { err = MAL_MALLOC_FAIL; goto bunins_failed; } } if (strNil(t)) { strcpy(buf, str_nil); bn->T->nonil = 0; } else { strcpy(buf, "D<?xml"); i = strlen(buf); if (!strNil(*version) && **version) i += snprintf(buf + i, len - i, " version=\"%s\"", *version); if (!strNil(*standalone) && **standalone) i += snprintf(buf + i, len - i, " standalone=\"%s\"", *standalone); snprintf(buf + i, len - i, "?>%s", t + 1); buf++; XMLisdocument(&isdoc, &buf); /* check well-formedness */ buf--; if (!isdoc) { err = XML_NOT_WELL_FORMED; goto bunins_failed; } } bunfastapp(bn, buf); } GDKfree(buf); finalizeResult(ret, bn, b); return MAL_SUCCEED; bunins_failed: BBPunfix(b->batCacheid); BBPunfix(bn->batCacheid); if (buf != NULL) GDKfree(buf); throw(MAL, "xml.root", "%s", err); }
str BATXMLpi(bat *ret, const char * const *target, const bat *bid) { BAT *b, *bn; BUN p, q; size_t size = BUFSIZ; str buf; BATiter bi; size_t tgtlen; const char *err = OPERATION_FAILED; if (strNil(*target)) throw(MAL, "xml.pi", XML_PI_ERROR); buf = GDKmalloc(size); if (buf == NULL) throw(MAL, "xml.pi", MAL_MALLOC_FAIL); tgtlen = strlen(*target) + 6; if ((b = BATdescriptor(*bid)) == NULL) { GDKfree(buf); throw(MAL, "xml.pi", INTERNAL_BAT_ACCESS); } prepareResult(bn, b, TYPE_xml, "pi", GDKfree(buf)); bi = bat_iterator(b); BATloop(b, p, q) { const char *t = (const char *) BUNtail(bi, p); size_t len; len = tgtlen; if (!strNil(t)) len += strlen(t) * 6 + 1; if (len >= size) { /* make sure there is enough space */ size = len + 128; /* free/malloc so we don't copy */ GDKfree(buf); buf = GDKmalloc(size); if (buf == NULL) { err = MAL_MALLOC_FAIL; goto bunins_failed; } } if (strNil(t)) snprintf(buf, size, "C<?%s?>", *target); else { int n = snprintf(buf, size, "C<?%s ", *target); size_t m = XMLquotestring(t, buf + n, size - n); strcpy(buf + n + m, "?>"); } bunfastapp(bn, buf); } GDKfree(buf); finalizeResult(ret, bn, b); return MAL_SUCCEED; bunins_failed: BBPunfix(b->batCacheid); BBPunfix(bn->batCacheid); if (buf != NULL) GDKfree(buf); throw(MAL, "xml.pi", "%s", err); }