void tds_check_column_extra(const TDSCOLUMN * column) { int size; TDSCONNECTION conn; int varint_ok; int column_varint_size; assert(column); column_varint_size = column->column_varint_size; /* 8 is for varchar(max) or similar */ assert(column_varint_size == 8 || (column_varint_size <= 5 && column_varint_size != 3)); assert(column->column_scale <= column->column_prec); assert(column->column_prec <= MAXPRECISION); /* I don't like this that much... freddy77 */ if (column->column_type == 0) return; assert(column->funcs); assert(column->column_type > 0); /* check type and server type same or SQLNCHAR -> SQLCHAR */ #define SPECIAL(ttype, server_type, varint) \ if (column->column_type == ttype && column->on_server.column_type == server_type && column_varint_size == varint) {} else SPECIAL(SYBTEXT, XSYBVARCHAR, 8) SPECIAL(SYBTEXT, XSYBNVARCHAR, 8) SPECIAL(SYBIMAGE, XSYBVARBINARY, 8) assert(tds_get_cardinal_type(column->on_server.column_type, column->column_usertype) == column->column_type || (tds_get_conversion_type(column->on_server.column_type, column->column_size) == column->column_type && column_varint_size == 1 && is_fixed_type(column->column_type))); varint_ok = 0; if (column_varint_size == 8) { assert(column->on_server.column_type == XSYBVARCHAR || column->on_server.column_type == XSYBVARBINARY || column->on_server.column_type == XSYBNVARCHAR || column->on_server.column_type == SYBMSXML || column->on_server.column_type == SYBMSUDT); varint_ok = 1; } else if (is_blob_type(column->column_type)) { assert(column_varint_size >= 4); } else if (column->column_type == SYBVARIANT) { assert(column_varint_size == 4); } conn.tds_version = 0x500; varint_ok = varint_ok || tds_get_varint_size(&conn, column->on_server.column_type) == column_varint_size; conn.tds_version = 0x700; varint_ok = varint_ok || tds_get_varint_size(&conn, column->on_server.column_type) == column_varint_size; assert(varint_ok); /* check current size <= size */ if (is_numeric_type(column->column_type)) { /* I don't like that much this difference between numeric and not numeric - freddy77 */ /* TODO what should be the size ?? */ assert(column->column_prec >= 1 && column->column_prec <= MAXPRECISION); assert(column->column_scale <= column->column_prec); /* assert(column->column_cur_size == tds_numeric_bytes_per_prec[column->column_prec] + 2 || column->column_cur_size == -1); */ } else { assert(column->column_cur_size <= column->column_size); } /* check size of fixed type correct */ size = tds_get_size_by_type(column->column_type); assert(size != 0 || column->column_type == SYBVOID); if (size >= 0 && column->column_type != SYBBITN) { /* check macro */ assert(is_fixed_type(column->column_type)); /* check current size */ if (column->column_type != SYBMSDATE) assert(size == column->column_size); /* check cases where server need nullable types */ if (column->column_type != column->on_server.column_type && (column->column_type != SYBINT8 || column->on_server.column_type != SYB5INT8)) { assert(!is_fixed_type(column->on_server.column_type)); assert(column_varint_size == 1); assert(column->column_size == column->column_cur_size || column->column_cur_size == -1); } else { assert(column_varint_size == 0 || (column->column_type == SYBUNIQUE && column_varint_size == 1) || (column->column_type == SYBMSDATE && column_varint_size == 1)); assert(column->column_size == column->column_cur_size || (column->column_type == SYBUNIQUE && column->column_cur_size == -1) || (column->column_type == SYBMSDATE && column->column_cur_size == -1)); } assert(column->column_size == column->on_server.column_size); } else { assert(!is_fixed_type(column->column_type)); assert(is_char_type(column->column_type) || (column->on_server.column_size == column->column_size || column->on_server.column_size == 0)); assert(column_varint_size != 0); } /* check size of nullable types (ie intN) it's supported */ if (tds_get_conversion_type(column->column_type, 4) != column->column_type) { /* check macro */ assert(is_nullable_type(column->column_type)); /* check that size it's correct for this type of nullable */ assert(tds_get_conversion_type(column->column_type, column->column_size) != column->column_type); /* check current size */ assert(column->column_size >= column->column_cur_size || column->column_cur_size == -1); /* check same type and size on server */ assert(column->column_type == column->on_server.column_type); assert(column->column_size == column->on_server.column_size); } }
/** * Add fixed size columns to the row * \param bcpinfo BCP information * \param get_col_data function to call to retrieve data to be sent * \param ignored function to call if we try to send NULL if not allowed (not used) * \param offset passed to get_col_data and null_error to specify the row to get * \param rowbuffer row buffer to write to * \param start row buffer last end position * \returns new row length or -1 on error. */ static int tds_bcp_add_fixed_columns(TDSBCPINFO *bcpinfo, tds_bcp_get_col_data get_col_data, tds_bcp_null_error null_error, int offset, unsigned char * rowbuffer, int start) { TDS_NUMERIC *num; int row_pos = start; TDSCOLUMN *bcpcol; int cpbytes; int i, j; int bitleft = 0, bitpos; assert(bcpinfo); assert(rowbuffer); tdsdump_log(TDS_DBG_FUNC, "tds_bcp_add_fixed_columns(%p, %p, ignored, %d, %p, %d)\n", bcpinfo, get_col_data, offset, rowbuffer, start); for (i = 0; i < bcpinfo->bindinfo->num_cols; i++) { bcpcol = bcpinfo->bindinfo->columns[i]; if (is_nullable_type(bcpcol->on_server.column_type) || bcpcol->column_nullable) continue; tdsdump_log(TDS_DBG_FUNC, "tds_bcp_add_fixed_columns column %d is a fixed column\n", i + 1); if (TDS_FAILED(get_col_data(bcpinfo, bcpcol, offset))) { tdsdump_log(TDS_DBG_INFO1, "get_col_data (column %d) failed\n", i + 1); return -1; } if (bcpcol->bcp_column_data->is_null && null_error) { /* No value or default value available and NULL not allowed. */ null_error(bcpinfo, i, offset); return -1; } if (is_numeric_type(bcpcol->column_type)) { num = (TDS_NUMERIC *) bcpcol->bcp_column_data->data; cpbytes = tds_numeric_bytes_per_prec[num->precision]; memcpy(&rowbuffer[row_pos], num->array, cpbytes); } else if (bcpcol->column_type == SYBBIT) { /* all bit are collapsed together */ if (!bitleft) { bitpos = row_pos++; bitleft = 8; rowbuffer[bitpos] = 0; } if (bcpcol->bcp_column_data->data[0]) rowbuffer[bitpos] |= 256 >> bitleft; --bitleft; continue; } else { cpbytes = bcpcol->bcp_column_data->datalen > bcpcol->column_size ? bcpcol->column_size : bcpcol->bcp_column_data->datalen; memcpy(&rowbuffer[row_pos], bcpcol->bcp_column_data->data, cpbytes); /* CHAR data may need padding out to the database length with blanks */ /* TODO check binary !!! */ if (bcpcol->column_type == SYBCHAR && cpbytes < bcpcol->column_size) { for (j = cpbytes; j < bcpcol->column_size; j++) rowbuffer[row_pos + j] = ' '; } } row_pos += bcpcol->column_size; }
static void start_element(void *data, const char *element_name, const char **atts) { struct parse_context *ctx = data; struct interface *interface; struct message *message; struct arg *arg; struct enumeration *enumeration; struct entry *entry; struct description *description = NULL; const char *name = NULL; const char *type = NULL; const char *interface_name = NULL; const char *value = NULL; const char *summary = NULL; const char *since = NULL; const char *allow_null = NULL; const char *enumeration_name = NULL; const char *bitfield = NULL; int i, version = 0; ctx->loc.line_number = XML_GetCurrentLineNumber(ctx->parser); for (i = 0; atts[i]; i += 2) { if (strcmp(atts[i], "name") == 0) name = atts[i + 1]; if (strcmp(atts[i], "version") == 0) { version = strtouint(atts[i + 1]); if (version == -1) fail(&ctx->loc, "wrong version (%s)", atts[i + 1]); } if (strcmp(atts[i], "type") == 0) type = atts[i + 1]; if (strcmp(atts[i], "value") == 0) value = atts[i + 1]; if (strcmp(atts[i], "interface") == 0) interface_name = atts[i + 1]; if (strcmp(atts[i], "summary") == 0) summary = atts[i + 1]; if (strcmp(atts[i], "since") == 0) since = atts[i + 1]; if (strcmp(atts[i], "allow-null") == 0) allow_null = atts[i + 1]; if (strcmp(atts[i], "enum") == 0) enumeration_name = atts[i + 1]; if (strcmp(atts[i], "bitfield") == 0) bitfield = atts[i + 1]; } ctx->character_data_length = 0; if (strcmp(element_name, "protocol") == 0) { if (name == NULL) fail(&ctx->loc, "no protocol name given"); ctx->protocol->name = xstrdup(name); ctx->protocol->uppercase_name = uppercase_dup(name); } else if (strcmp(element_name, "copyright") == 0) { } else if (strcmp(element_name, "interface") == 0) { if (name == NULL) fail(&ctx->loc, "no interface name given"); if (version == 0) fail(&ctx->loc, "no interface version given"); interface = create_interface(ctx->loc, name, version); ctx->interface = interface; wl_list_insert(ctx->protocol->interface_list.prev, &interface->link); } else if (strcmp(element_name, "request") == 0 || strcmp(element_name, "event") == 0) { if (name == NULL) fail(&ctx->loc, "no request name given"); message = create_message(ctx->loc, name); if (strcmp(element_name, "request") == 0) wl_list_insert(ctx->interface->request_list.prev, &message->link); else wl_list_insert(ctx->interface->event_list.prev, &message->link); if (type != NULL && strcmp(type, "destructor") == 0) message->destructor = 1; version = version_from_since(ctx, since); if (version < ctx->interface->since) warn(&ctx->loc, "since version not increasing\n"); ctx->interface->since = version; message->since = version; if (strcmp(name, "destroy") == 0 && !message->destructor) fail(&ctx->loc, "destroy request should be destructor type"); ctx->message = message; } else if (strcmp(element_name, "arg") == 0) { if (name == NULL) fail(&ctx->loc, "no argument name given"); arg = create_arg(name); if (!set_arg_type(arg, type)) fail(&ctx->loc, "unknown type (%s)", type); switch (arg->type) { case NEW_ID: ctx->message->new_id_count++; /* fallthrough */ case OBJECT: if (interface_name) arg->interface_name = xstrdup(interface_name); break; default: if (interface_name != NULL) fail(&ctx->loc, "interface attribute not allowed for type %s", type); break; } if (allow_null) { if (strcmp(allow_null, "true") == 0) arg->nullable = 1; else if (strcmp(allow_null, "false") != 0) fail(&ctx->loc, "invalid value for allow-null attribute (%s)", allow_null); if (!is_nullable_type(arg)) fail(&ctx->loc, "allow-null is only valid for objects, strings, and arrays"); } if (enumeration_name == NULL || strcmp(enumeration_name, "") == 0) arg->enumeration_name = NULL; else arg->enumeration_name = xstrdup(enumeration_name); if (summary) arg->summary = xstrdup(summary); wl_list_insert(ctx->message->arg_list.prev, &arg->link); ctx->message->arg_count++; } else if (strcmp(element_name, "enum") == 0) { if (name == NULL) fail(&ctx->loc, "no enum name given"); enumeration = create_enumeration(name); if (bitfield == NULL || strcmp(bitfield, "false") == 0) enumeration->bitfield = false; else if (strcmp(bitfield, "true") == 0) enumeration->bitfield = true; else fail(&ctx->loc, "invalid value (%s) for bitfield attribute (only true/false are accepted)", bitfield); wl_list_insert(ctx->interface->enumeration_list.prev, &enumeration->link); ctx->enumeration = enumeration; } else if (strcmp(element_name, "entry") == 0) { if (name == NULL) fail(&ctx->loc, "no entry name given"); entry = create_entry(name, value); version = version_from_since(ctx, since); if (version < ctx->enumeration->since) warn(&ctx->loc, "since version not increasing\n"); ctx->enumeration->since = version; entry->since = version; if (summary) entry->summary = xstrdup(summary); else entry->summary = NULL; wl_list_insert(ctx->enumeration->entry_list.prev, &entry->link); } else if (strcmp(element_name, "description") == 0) { if (summary == NULL) fail(&ctx->loc, "description without summary"); description = xzalloc(sizeof *description); description->summary = xstrdup(summary); if (ctx->message) ctx->message->description = description; else if (ctx->enumeration) ctx->enumeration->description = description; else if (ctx->interface) ctx->interface->description = description; else ctx->protocol->description = description; ctx->description = description; } }
/** * Send one row of data to server * \tds * \param bcpinfo BCP information * \param get_col_data function to call to retrieve data to be sent * \param ignored function to call if we try to send NULL if not allowed (not used) * \param offset passed to get_col_data and null_error to specify the row to get * \return TDS_SUCCESS or TDS_FAIL. */ TDSRET tds_bcp_send_record(TDSSOCKET *tds, TDSBCPINFO *bcpinfo, tds_bcp_get_col_data get_col_data, tds_bcp_null_error null_error, int offset) { TDSCOLUMN *bindcol; int i, start_col = bcpinfo->next_col; TDSRET rc; tdsdump_log(TDS_DBG_FUNC, "tds_bcp_send_bcp_record(%p, %p, %p, %p, %d)\n", tds, bcpinfo, get_col_data, null_error, offset); if (tds->out_flag != TDS_BULK || tds_set_state(tds, TDS_WRITING) != TDS_WRITING) return TDS_FAIL; if (start_col > 0) { bindcol = bcpinfo->bindinfo->columns[start_col - 1]; *bindcol->column_lenbind = MIN((TDS_INT) bindcol->column_bindlen - bcpinfo->text_sent, *bindcol->column_lenbind); tds_put_n(tds, bindcol->column_varaddr, *bindcol->column_lenbind); bcpinfo->text_sent += *bindcol->column_lenbind; if ((TDS_UINT) bcpinfo->text_sent < bindcol->column_bindlen) { return TDS_SUCCESS; /* That's all for now. */ } else if (!IS_TDS7_PLUS(tds->conn)) { bcpinfo->blob_cols++; } bcpinfo->next_col = 0; bcpinfo->text_sent = 0; } if (IS_TDS7_PLUS(tds->conn)) { if (start_col == 0) { tds_put_byte(tds, TDS_ROW_TOKEN); /* 0xd1 */ } for (i = start_col; i < bcpinfo->bindinfo->num_cols; i++) { TDS_INT save_size; unsigned char *save_data; TDSBLOB blob; int /* bool */ has_text = 0; bindcol = bcpinfo->bindinfo->columns[i]; /* * Don't send the (meta)data for timestamp columns or * identity columns unless indentity_insert is enabled. */ if ((!bcpinfo->identity_insert_on && bindcol->column_identity) || bindcol->column_timestamp || !tds_bcp_is_bound(bcpinfo, bindcol)) { continue; } rc = get_col_data(bcpinfo, bindcol, offset); if (rc == TDS_FAIL) { tdsdump_log(TDS_DBG_INFO1, "get_col_data (column %d) failed\n", i + 1); goto cleanup; } else if (rc != TDS_SUCCESS) { /* CS_BLK_HAS_TEXT? */ has_text = 1; } tdsdump_log(TDS_DBG_INFO1, "gotten column %d length %d null %d\n", i + 1, bindcol->bcp_column_data->datalen, bindcol->bcp_column_data->is_null); save_size = bindcol->column_cur_size; save_data = bindcol->column_data; assert(bindcol->column_data == NULL); if (bindcol->bcp_column_data->is_null) { if ( !bindcol->column_nullable && !is_nullable_type(bindcol->on_server.column_type) ) { return TDS_FAIL; } bindcol->column_cur_size = -1; } else if (has_text) { bindcol->column_cur_size = bindcol->bcp_column_data->datalen; } else if (is_blob_col(bindcol)) { bindcol->column_cur_size = bindcol->bcp_column_data->datalen; memset(&blob, 0, sizeof(blob)); blob.textvalue = (TDS_CHAR *) bindcol->bcp_column_data->data; bindcol->column_data = (unsigned char *) &blob; } else { bindcol->column_cur_size = bindcol->bcp_column_data->datalen; bindcol->column_data = bindcol->bcp_column_data->data; } rc = bindcol->funcs->put_data(tds, bindcol, 1); bindcol->column_cur_size = save_size; bindcol->column_data = save_data; if (TDS_FAILED(rc)) goto cleanup; else if (has_text) { bcpinfo->next_col = i + 1; /* bcpinfo->text_sent = 0; */ break; } } } /* IS_TDS7_PLUS */ else { if (start_col == 0) { int row_pos; int row_sz_pos; int var_cols_written = 0; TDS_INT old_record_size = bcpinfo->bindinfo->row_size; unsigned char *record = bcpinfo->bindinfo->current_row; memset(record, '\0', old_record_size); /* zero the rowbuffer */ /* * offset 0 = number of var columns * offset 1 = row number. zeroed (datasever assigns) */ row_pos = 2; rc = TDS_FAIL; if ((row_pos = tds_bcp_add_fixed_columns(bcpinfo, get_col_data, null_error, offset, record, row_pos)) < 0) goto cleanup; row_sz_pos = row_pos; /* potential variable columns to write */ if ((row_pos = tds_bcp_add_variable_columns(bcpinfo, get_col_data, null_error, offset, record, row_pos, &var_cols_written)) < 0) goto cleanup; if (var_cols_written) { TDS_PUT_UA2(&record[row_sz_pos], row_pos); record[0] = var_cols_written; } tdsdump_log(TDS_DBG_INFO1, "old_record_size = %d new size = %d \n", old_record_size, row_pos); tds_put_smallint(tds, row_pos); tds_put_n(tds, record, row_pos); /* row is done, now handle any text/image data */ bcpinfo->blob_cols = 0; } for (i = start_col; i < bcpinfo->bindinfo->num_cols; i++) { bindcol = bcpinfo->bindinfo->columns[i]; if (is_blob_type(bindcol->column_type)) { /* Elide trailing NULLs */ if (bindcol->bcp_column_data->is_null) { int j; for (j = i + 1; j < bcpinfo->bindinfo->num_cols; ++j) { TDSCOLUMN *bindcol2 = bcpinfo->bindinfo->columns[j]; if (is_blob_type(bindcol2->column_type) && !bindcol2->bcp_column_data->is_null) { break; } } if (j == bcpinfo->bindinfo->num_cols) { i = j; break; } } rc = get_col_data(bcpinfo, bindcol, offset); if (rc == TDS_FAIL) goto cleanup; /* unknown but zero */ tds_put_smallint(tds, 0); tds_put_byte(tds, (unsigned char) bindcol->column_type); tds_put_byte(tds, 0xff - bcpinfo->blob_cols); /* * offset of txptr we stashed during variable * column processing */ tds_put_smallint(tds, bindcol->column_textpos); tds_put_int(tds, bindcol->bcp_column_data->datalen); if (rc != TDS_SUCCESS) { /* CS_BLK_HAS_TEXT? */ bcpinfo->next_col = i + 1; /* bcpinfo->text_sent = 0; */ break; } tds_put_n(tds, bindcol->bcp_column_data->data, bindcol->bcp_column_data->datalen); bcpinfo->blob_cols++; } } } if (i == bcpinfo->bindinfo->num_cols) { tds_set_state(tds, TDS_SENDING); bcpinfo->next_col = 0; } return TDS_SUCCESS; cleanup: tds_set_state(tds, TDS_SENDING); return rc; }
void tds_check_column_extra(const TDSCOLUMN * column) { int size; TDSCONNECTION conn; int varint_ok; int column_varint_size; assert(column); column_varint_size = column->column_varint_size; /* 8 is for varchar(max) or similar */ assert(column_varint_size == 8 || (column_varint_size <= 5 && column_varint_size != 3)); assert(column->column_scale <= column->column_prec); assert(column->column_prec <= MAXPRECISION); /* I don't like this that much... freddy77 */ if (column->column_type == 0) return; assert(column->funcs); assert(column->column_type > 0); /* specific checks, if true fully checked */ if (column->funcs->check(column)) return; /* check type and server type same or SQLNCHAR -> SQLCHAR */ #define SPECIAL(ttype, server_type, varint) \ if (column->column_type == ttype && column->on_server.column_type == server_type && column_varint_size == varint) {} else SPECIAL(SYBTEXT, XSYBVARCHAR, 8) SPECIAL(SYBTEXT, XSYBNVARCHAR, 8) SPECIAL(SYBIMAGE, XSYBVARBINARY, 8) assert(tds_get_cardinal_type(column->on_server.column_type, column->column_usertype) == column->column_type || (tds_get_conversion_type(column->on_server.column_type, column->column_size) == column->column_type && column_varint_size == 1 && is_fixed_type(column->column_type))); varint_ok = 0; if (column_varint_size == 8) { if (column->on_server.column_type == XSYBVARCHAR || column->on_server.column_type == XSYBVARBINARY || column->on_server.column_type == XSYBNVARCHAR) varint_ok = 1; } else if (is_blob_type(column->column_type)) { assert(column_varint_size >= 4); } else if (column->column_type == SYBVARIANT) { assert(column_varint_size == 4); } conn.tds_version = 0x500; varint_ok = varint_ok || tds_get_varint_size(&conn, column->on_server.column_type) == column_varint_size; conn.tds_version = 0x700; varint_ok = varint_ok || tds_get_varint_size(&conn, column->on_server.column_type) == column_varint_size; assert(varint_ok); assert(!is_numeric_type(column->column_type)); assert(column->column_cur_size <= column->column_size); /* check size of fixed type correct */ size = tds_get_size_by_type(column->column_type); assert(size != 0 || column->column_type == SYBVOID); /* these peculiar types are variable but have only a possible size */ if (size >= 0 && (column->column_type != SYBBITN && column->column_type != SYBDATEN && column->column_type != SYBTIMEN)) { /* check macro */ assert(is_fixed_type(column->column_type)); /* check current size */ assert(size == column->column_size); /* check cases where server need nullable types */ if (column->column_type != column->on_server.column_type && (column->column_type != SYBINT8 || column->on_server.column_type != SYB5INT8)) { assert(!is_fixed_type(column->on_server.column_type)); assert(column_varint_size == 1); assert(column->column_size == column->column_cur_size || column->column_cur_size == -1); } else { assert(column_varint_size == 0 || (column->column_type == SYBUNIQUE && column_varint_size == 1)); assert(column->column_size == column->column_cur_size || (column->column_type == SYBUNIQUE && column->column_cur_size == -1)); } assert(column->column_size == column->on_server.column_size); } else { assert(!is_fixed_type(column->column_type)); assert(is_char_type(column->column_type) || (column->on_server.column_size == column->column_size || column->on_server.column_size == 0)); assert(column_varint_size != 0); } /* check size of nullable types (ie intN) it's supported */ if (tds_get_conversion_type(column->column_type, 4) != column->column_type) { /* check macro */ assert(is_nullable_type(column->column_type)); /* check that size it's correct for this type of nullable */ assert(tds_get_conversion_type(column->column_type, column->column_size) != column->column_type); /* check current size */ assert(column->column_size >= column->column_cur_size || column->column_cur_size == -1); /* check same type and size on server */ assert(column->column_type == column->on_server.column_type); assert(column->column_size == column->on_server.column_size); } }