Changeset: 7cf718a87aee for MonetDB
URL: http://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=7cf718a87aee
Modified Files:
sql/backends/monet5/bam/bam_loader.c
sql/backends/monet5/bam/bam_loader.h
Branch: DVframework_bam
Log Message:
Improved _process_bam_alignment method greatly. First, Samtools was used to
transform an alignment structure into an ASCII string. The bam_loader would
then parse this string. Since this approach was way too lame, I now retrieve
all the values from the alignment structure directly and tested the approach on
a toy file.
diffs (truncated from 426 to 300 lines):
diff --git a/sql/backends/monet5/bam/bam_loader.c
b/sql/backends/monet5/bam/bam_loader.c
--- a/sql/backends/monet5/bam/bam_loader.c
+++ b/sql/backends/monet5/bam/bam_loader.c
@@ -1,4 +1,6 @@
#include "monetdb_config.h"
+#include "bam.h"
+#include "kstring.h"
#include "bam_loader.h"
/*
@@ -92,8 +94,8 @@ static bit _append_option_to_bat(_temp_c
static str _read_bam_header_line(str *header, _bam_header_line *ret_hl, bit
*eof);
static void _free_bam_header_line(_bam_header_line *hl);
static str _process_bam_alignment(sht file_id, lng virtual_offset,
bam_header_t *header, bam1_t *alignment, _temp_container *ret_tc, sht schema);
-static bit _parse_alignment_str(str *sam_alig, str *dest);
-static bit _parse_alignment_lng(str *sam_alig, lng *dest);
+/*static bit _parse_alignment_str(str *sam_alig, str *dest);
+static bit _parse_alignment_lng(str *sam_alig, lng *dest);*/
/* Generic functions */
static str _init_temp_subcontainer(_temp_subcontainer *ret_tsc, str
*col_names, int *col_types, sht num_cols);
@@ -230,7 +232,7 @@ static str
if(ret_tc->table_names == NULL || ret_tc->num_columns == NULL)
throw(MAL, "init_temp_container", MAL_MALLOC_FAIL);
- for(t = 0; t < num_tables; t++)
+ for(t = 0; t < num_tables; ++t)
{
ret_tc->table_names[t] = table_names[t];
ret_tc->num_columns[t] = num_cols[t];
@@ -301,7 +303,7 @@ static str
{
if((err = _process_bam_alignment(file_id, voffset, header, alignment,
ret_tc, dbschema)) != MAL_SUCCEED)
throw(MAL, "loadfile", "Error processing bam alignment: %s\n",
err);
- num_alignments++;
+ ++num_alignments;
voffset = bam_tell(input);
}
duration_sec = (GDKms() - start) / 1000.0f;
@@ -359,7 +361,7 @@ static str
/* read and interpret the header tag */
if(strcmp(hl->header_tag, "HD") == 0)
{
- num_hd_lines++;
+ ++num_hd_lines;
if(num_hd_lines > 1)
{
_free_bam_header_line(hl);
@@ -369,7 +371,7 @@ static str
hd_fields_found[0] = FALSE;
hd_fields_found[1] = FALSE;
- for(o = 0; o < hl->num_options; o++)
+ for(o = 0; o < hl->num_options; ++o)
{
if(_append_option_to_bat_cond_flt(ret_tc, &hl->options[o],
"VN", 0, 2, &appendErr, &hd_fields_found[0])) continue;
if(_append_option_to_bat_cond_str(ret_tc, &hl->options[o],
"SO", 0, 3, &appendErr, &hd_fields_found[1])) continue;
@@ -399,10 +401,10 @@ static str
throw(MAL, "_process_bam_header", "Appending file_id to SQ BAT
failed\n");
}
- for(i=0; i<6; i++)
+ for(i=0; i<6; ++i)
sq_fields_found[i] = FALSE;
- for(o = 0; o < hl->num_options; o++)
+ for(o = 0; o < hl->num_options; ++o)
{
if(_append_option_to_bat_cond_str(ret_tc, &hl->options[o],
"SN", 1, 0, &appendErr, &sq_fields_found[0])) continue;
if(_append_option_to_bat_cond_lng(ret_tc, &hl->options[o],
"LN", 1, 2, &appendErr, &sq_fields_found[1])) continue;
@@ -446,10 +448,10 @@ static str
throw(MAL, "_process_bam_header", "Appending file_id to RG BAT
failed\n");
}
- for(i=0; i<12; i++)
+ for(i=0; i<12; ++i)
rg_fields_found[i] = FALSE;
- for(o = 0; o < hl->num_options; o++)
+ for(o = 0; o < hl->num_options; ++o)
{
if(_append_option_to_bat_cond_str(ret_tc, &hl->options[o],
"ID", 2, 0, &appendErr, &rg_fields_found[0])) continue;
if(_append_option_to_bat_cond_str(ret_tc, &hl->options[o],
"CN", 2, 2, &appendErr, &rg_fields_found[1])) continue;
@@ -501,10 +503,10 @@ static str
throw(MAL, "_process_bam_header", "Appending file_id to PG BAT
failed\n");
}
- for(i=0; i<5; i++)
+ for(i=0; i<5; ++i)
pg_fields_found[i] = FALSE;
- for(o = 0; o < hl->num_options; o++)
+ for(o = 0; o < hl->num_options; ++o)
{
if(_append_option_to_bat_cond_str(ret_tc, &hl->options[o],
"ID", 3, 0, &appendErr, &pg_fields_found[0])) continue;
if(_append_option_to_bat_cond_str(ret_tc, &hl->options[o],
"PN", 3, 2, &appendErr, &pg_fields_found[1])) continue;
@@ -698,7 +700,7 @@ static void
if(hl == NULL)
return;
- for(o=0; o<hl->num_options; o++)
+ for(o=0; o<hl->num_options; ++o)
{
/* for some reason, freeing hl->options[o].value causes segmentation
fault, while these
strings are created with GDKmalloc... */
@@ -708,36 +710,109 @@ static void
GDKfree(hl);
}
+/*
+* Given a Samtools native structure bam1_t, retrieve all information from it
and append it to the _temp_container.
+* Retrieval code is inspired by the code found in bam.c::bam_format1_core
+*
+* Old implementation actually called bam.c::bam_format1_core and then parsed
the resulting SAM-string. This new and less lame
+* implementation has been compared with the old implementation on the file
toy.bam and resulted in the exact same database contents.
+*/
static str
_process_bam_alignment(sht file_id, lng virtual_offset, bam_header_t *header,
bam1_t *alignment, _temp_container *ret_tc, sht dbschema)
{
- str err = NULL;
str qname, rname, cigar, rnext, seq, qual;
- lng flag, pos, mapq, pnext, tlen;
+ sht flag, mapq;
+ int pos, pnext, tlen;
- str tag = NULL, type = NULL, value = NULL; /* auxiliary options */
-
- str sam_alig;
- str parse_err = NULL;
+ uint8_t *s;
+ int i;
(void)dbschema;
- sam_alig = bam_format1(header, alignment);
- if( !_parse_alignment_str(&sam_alig, &qname ))
parse_err = "QNAME";
- if(parse_err == NULL && !_parse_alignment_lng(&sam_alig, &flag ))
parse_err = "FLAG" ;
- if(parse_err == NULL && !_parse_alignment_str(&sam_alig, &rname ))
parse_err = "RNAME";
- if(parse_err == NULL && !_parse_alignment_lng(&sam_alig, &pos ))
parse_err = "POS" ;
- if(parse_err == NULL && !_parse_alignment_lng(&sam_alig, &mapq ))
parse_err = "MAPQ" ;
- if(parse_err == NULL && !_parse_alignment_str(&sam_alig, &cigar ))
parse_err = "CIGAR";
- if(parse_err == NULL && !_parse_alignment_str(&sam_alig, &rnext ))
parse_err = "RNEXT";
- if(parse_err == NULL && !_parse_alignment_lng(&sam_alig, &pnext ))
parse_err = "PNEXT";
- if(parse_err == NULL && !_parse_alignment_lng(&sam_alig, &tlen ))
parse_err = "TLEN" ;
- if(parse_err == NULL && !_parse_alignment_str(&sam_alig, &seq ))
parse_err = "SEQ" ;
- if(parse_err == NULL && !_parse_alignment_str(&sam_alig, &qual ))
parse_err = "QUAL" ;
+ //qname
+ qname = bam1_qname(alignment);
+
+ //flag
+ flag = alignment->core.flag;
+
+ //rname
+ if(alignment->core.tid < 0)
+ rname = "*";
+ else
+ rname = header->target_name[alignment->core.tid];
+
+ //pos
+ pos = alignment->core.pos + 1;
+
+ //mapq
+ mapq = alignment->core.qual;
+
+ //cigar
+ if(alignment->core.n_cigar == 0)
+ cigar = "*";
+ else
+ {
+ uint32_t *cigar_bin = bam1_cigar(alignment);
+
+ /* Use kstring library as is done in Samtools, to conveniently
reconstruct cigar string */
+ kstring_t cigar_stream;
+ cigar_stream.l = cigar_stream.m = 0; cigar_stream.s = 0;
+
+ for (i=0; i<alignment->core.n_cigar; ++i)
+ {
+ kputw(cigar_bin[i]>>BAM_CIGAR_SHIFT, &cigar_stream);
+ kputc(bam_cigar_opchr(cigar_bin[i]), &cigar_stream);
+ }
+ cigar = (str)GDKmalloc(alignment->core.n_cigar * sizeof(char));
+ if(cigar == NULL)
+ throw(MAL, "_process_bam_alignment", MAL_MALLOC_FAIL);
+ cigar = cigar_stream.s;
+ }
+
+ //rnext
+ if(alignment->core.mtid < 0)
+ rnext = "*";
+ else if(alignment->core.mtid == alignment->core.tid)
+ rnext = "=";
+ else
+ rnext = header->target_name[alignment->core.mtid];
+
+ //pnext
+ pnext = alignment->core.mpos + 1;
+
+ //tlen
+ tlen = alignment->core.isize;
+
+ //seq and qual
+ if(alignment->core.l_qseq)
+ {
+ s = bam1_seq(alignment);
+ seq = (str)GDKmalloc((alignment->core.l_qseq + 1) * sizeof(char)); /*
+1 for \0 character */
+ if(seq == NULL)
+ throw(MAL, "_process_bam_alignment", MAL_MALLOC_FAIL);
+ for (i=0; i<alignment->core.l_qseq; ++i)
+ seq[i] = bam_nt16_rev_table[bam1_seqi(s, i)];
+ seq[alignment->core.l_qseq] = '\0';
+ s = bam1_qual(alignment);
+ if (s[0] == 0xff)
+ qual = "*";
+ else
+ {
+ qual = (str)GDKmalloc((alignment->core.l_qseq + 1) *
sizeof(char)); /* +1 for \0 character */
+ if(qual == NULL)
+ throw(MAL, "_process_bam_alignment", MAL_MALLOC_FAIL);
+ for (i=0; i<alignment->core.l_qseq; ++i)
+ qual[i] = s[i] + 33;
+ qual[alignment->core.l_qseq] = '\0';
+ }
+ }
+ else
+ {
+ seq = "*";
+ qual = "*";
+ }
- if(parse_err != NULL)
- throw(MAL, "_process_bam_alignment", "Parse error on field: %s", err);
if(BUNappend(ret_tc->tables_columns[4].column_bats[0] , (ptr)
&virtual_offset , TRUE) == NULL
|| BUNappend(ret_tc->tables_columns[4].column_bats[1] , (ptr) &file_id
, TRUE) == NULL
@@ -759,48 +834,68 @@ static str
/* parse auxiliary data */
- /* tabs have been stripped already, so we are directly faced with an
optional field, a \n or a \0 */
- while(TRUE)
- {
- if(*sam_alig == '\n' || *sam_alig == '\0') break;
-
- if(_read_string_until_delim(&sam_alig, &tag, ":\t\n\0", 4) != 2)
- throw(MAL, "_process_bam_alignment", "Auxiliary data tag length !=
2 found in alignment: %s", tag);
- if(*sam_alig != ':')
- throw(MAL, "_process_bam_alignment", "Auxiliary data tag contains
one of '\\t\\n\\0'");
- sam_alig++;
-
- if(_read_string_until_delim(&sam_alig, &type, ":\t\n\0", 4) != 1)
- throw(MAL, "_process_bam_alignment", "Auxiliary data type length
!= 1 found in alignment: %s:%s", tag, type);
- if(*sam_alig != ':')
- throw(MAL, "_process_bam_alignment", "Auxiliary data type contains
one of '\\t\\n\\0'");
- sam_alig++;
-
- if(_read_string_until_delim(&sam_alig, &value, "\t\n\0", 3) == -1)
- throw(MAL, "_process_bam_alignment", MAL_MALLOC_FAIL);
-
- if(BUNappend(ret_tc->tables_columns[5].column_bats[0] , (ptr) tag
, TRUE) == NULL
- || BUNappend(ret_tc->tables_columns[5].column_bats[1] , (ptr)
&virtual_offset , TRUE) == NULL
- || BUNappend(ret_tc->tables_columns[5].column_bats[2] , (ptr) &file_id
, TRUE) == NULL
- || BUNappend(ret_tc->tables_columns[5].column_bats[3] , (ptr) type
, TRUE) == NULL
- || BUNappend(ret_tc->tables_columns[5].column_bats[4] , (ptr) value
, TRUE) == NULL
+ s = bam1_aux(alignment);
+ while (s < alignment->data + alignment->data_len) {
+ uint8_t type, tag[2];
+ kstring_t aux_value_stream;
+ aux_value_stream.l = aux_value_stream.m = 0; aux_value_stream.s = 0;
+ tag[0] = s[0];
+ tag[1] = s[1];
+ s += 2;
+ type = *s;
+ ++s;
+ if (type == 'A') { kputc(*s
, &aux_value_stream) ; ++s; }
+ else if (type == 'C') { type = 'i'; kputw(*s
, &aux_value_stream) ; ++s; }
+ else if (type == 'c') { type = 'i';
kputw(*(int8_t*)s , &aux_value_stream) ; ++s; }
+ else if (type == 'S') { type = 'i';
kputw(*(uint16_t*)s , &aux_value_stream) ; s += 2; }
+ else if (type == 's') { type = 'i';
kputw(*(int16_t*)s , &aux_value_stream) ; s += 2; }
+ else if (type == 'I') { type = 'i';
kputuw(*(uint32_t*)s, &aux_value_stream) ; s += 4; }
+ else if (type == 'i') { type = 'i';
kputw(*(int32_t*)s , &aux_value_stream) ; s += 4; }
+ else if (type == 'f') {
ksprintf(&aux_value_stream, "%g", *(float*)s) ; s += 4; }
+ else if (type == 'd') {
ksprintf(&aux_value_stream, "%lg", *(double*)s); s += 8; }
+ else if (type == 'Z' || type == 'H') { while (*s)
kputc(*s++, &aux_value_stream) ; ++s; }
+ else if (type == 'B')
+ {
+ uint8_t sub_type = *(s++);
+ int32_t n;
+ memcpy(&n, s, 4);
+ s += 4; // no point to the start of the array
+ kputc(sub_type, &aux_value_stream); // write the typing
+ for (i = 0; i < n; ++i)
+ {
+ kputc(',', &aux_value_stream);
+ if ('c' == sub_type) { kputw(*(int8_t*)s,
&aux_value_stream) ; ++s; }
+ else if ('C' == sub_type) { kputw(*(uint8_t*)s,
&aux_value_stream) ; ++s; }
+ else if ('s' == sub_type) { kputw(*(int16_t*)s,
&aux_value_stream) ; s += 2; }
+ else if ('S' == sub_type) {
kputw(*(uint16_t*)s, &aux_value_stream) ; s += 2; }
+ else if ('i' == sub_type) { kputw(*(int32_t*)s,
&aux_value_stream) ; s += 4; }
+ else if ('I' == sub_type) {
kputuw(*(uint32_t*)s, &aux_value_stream) ; s += 4; }
_______________________________________________
checkin-list mailing list
[email protected]
http://mail.monetdb.org/mailman/listinfo/checkin-list