The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#include "parse_int.h"

int csv_start_insert(
	csv_parse_t *parse, Expr *name, ExprList *cols, ExprList *values
) {
	csv_table_def_t *table;
	csv_insert_t *insert = NULL;
	size_t i, j;
	table = csv_open_table( parse,
		name->var.sv, name->var.sv_len, NULL, 0, "r+" );
	if( table == NULL )
		goto error;
	Newxz( insert, 1, csv_insert_t );
	Newxz( insert->values, table->column_count, Expr * );
	if( cols != NULL ) {
		if( values->expr_count != cols->expr_count ||
			values->expr_count > table->column_count
		) {
			csv_error_msg( parse, CSV_ERR_COLUMNCOUNT,
				"Number of columns differs to number of fields" );
			goto error;
		}
		for( i = 0; i < cols->expr_count; i ++ ) {
			if( CSV_OK != expr_bind_columns(
				parse, cols->expr[i], &table, 1, "insert clause" ) )
					goto error;
			if( CSV_OK != expr_bind_columns(
				parse, values->expr[i], &table, 1, "insert clause" ) )
					goto error;
			j = cols->expr[i]->column - table->columns;
			insert->values[j] = values->expr[i];
			values->expr[i] = NULL;
		}
	}
	else {
		if( values->expr_count != table->column_count ) {
			csv_error_msg( parse, CSV_ERR_COLUMNCOUNT,
				"Number of columns differs to number of fields" );
			goto error;
		}
		for( i = 0; i < values->expr_count; i ++ ) {
			if( CSV_OK != expr_bind_columns(
				parse, values->expr[i], &table, 1, "insert clause" ) )
					goto error;
			insert->values[i] = values->expr[i];
			values->expr[i] = NULL;
		}
	}
	insert->table = table;
	parse->type = PARSE_TYPE_INSERT;
	parse->insert = insert;
	csv_expr_free( name );
	csv_exprlist_free( cols );
	csv_exprlist_free( values );
	return CSV_OK;
error:
	csv_expr_free( name );
	csv_exprlist_free( cols );
	csv_exprlist_free( values );
	csv_table_def_free( table );
	Safefree( insert );
	return CSV_ERROR;
}

int csv_insert_run( csv_parse_t *parse ) {
	char *val, qf[1], qt[2], tmp[128], *p2;
	size_t i, j, len;
	PerlIO *pfile;
	csv_insert_t *insert = parse->insert;
	csv_table_def_t *table = insert->table;
	csv_column_def_t *col;
	Expr *x1;
	int ch;
	csv_var_t vt1;
	Zero( &vt1, 1, csv_var_t );
	pfile = table->dstream;
	PerlIO_seek( pfile, 0, SEEK_END );
	qf[0] = qt[1] = qt[2] = table->quote;
	for( i = 0; i < table->column_count; i ++ ) {
		if( i > 0 )
			PerlIO_putc( pfile, table->delimiter );
		col = &table->columns[i];
		x1 = insert->values[i];
		if( x1 != NULL && x1->op == TK_ISNULL ) {
			PerlIO_write( pfile, "NULL", 4 );
			continue;
		}
		switch( col->type ) {
		case FIELD_TYPE_CHAR:
			if( x1 != NULL ) {
				expr_eval( parse, x1 );
				VAR_EVAL_SV( x1->var );
				val = x1->var.sv;
				len = MIN( x1->var.sv_len, col->size );
			}
			else {
				val = col->defval;
				len = MIN( col->defval_length, col->size );
			}
#ifdef CSV_DEBUG
			_debug( "set %u char %u %s\n", i + 1, len, val );
#endif
			if( val != NULL ) {
				if( table->charset != parse->csv->client_charset ) {
					vt1.sv_len = charset_convert(
						val, len, parse->csv->client_charset,
						table->charset, &vt1.sv
					);
					val = vt1.sv;
					len = vt1.sv_len;
				}
				WRITE_QUOTED( pfile, val, len, table->quote );
			}
			else {
				PerlIO_write( pfile, "NULL", 4 );
			}
			break;
		case FIELD_TYPE_INTEGER:
			if( x1 != NULL ) {
				expr_eval( parse, x1 );
				VAR_EVAL_IV( x1->var );
				val = my_itoa( tmp, x1->var.iv, 10 );
			}
			else if( col->defval != NULL )
				val = my_itoa( tmp, atoi( col->defval ), 10 );
			else
				val = my_strcpy( tmp, "NULL" );
#ifdef CSV_DEBUG
			_debug( "set %u integer %s\n", i + 1, tmp );
#endif
			PerlIO_write( pfile, tmp, val - tmp );
			break;
		case FIELD_TYPE_DOUBLE:
			if( x1 != NULL ) {
				expr_eval( parse, x1 );
				VAR_EVAL_NV( x1->var );
				val = my_ftoa( tmp, x1->var.nv );
			}
			else if( col->defval != NULL ) {
				if( table->decimal_symbol != '.' )
					if( (p2 = strchr( col->defval, table->decimal_symbol )) != NULL )
						*p2 = '.';
				val = my_ftoa( tmp, atof( col->defval ) );
			}
			else
				val = my_strcpy( tmp, "NULL" );
#ifdef CSV_DEBUG
			_debug( "set %u integer %s\n", i + 1, tmp );
#endif
			if( table->decimal_symbol != '.' )
				if( (p2 = strchr( tmp, '.' )) != NULL )
					*p2 = table->decimal_symbol;
			PerlIO_write( pfile, tmp, val - tmp );
			break;
		case FIELD_TYPE_BLOB:
			if( x1 != NULL ) {
				expr_eval( parse, x1 );
				VAR_EVAL_SV( x1->var );
				val = x1->var.sv;
				len = MIN( x1->var.sv_len, col->size );
			}
			else {
				val = col->defval;
				len = MIN( col->defval_length, col->size );
			}
#ifdef CSV_DEBUG
			_debug( "set %u blob %s\n", i + 1, val );
#endif
			if( val != NULL ) {
				// convert to hex
				PerlIO_write( pfile, "0x", 2 );
				for( j = 0; j < len; j ++ ) {
					ch = (unsigned char) *val ++;
					PerlIO_putc( pfile, HEX_FROM_CHAR[ch / 16] );
					PerlIO_putc( pfile, HEX_FROM_CHAR[ch % 16] );
				}
			}
			else {
				PerlIO_write( pfile, "NULL", 4 );
			}
			break;
		}
	}
	PerlIO_putc( pfile, '\n' );
	parse->csv->affected_rows = 1;
	Safefree( vt1.sv );
	return CSV_OK;
}

void csv_insert_free( csv_insert_t *insert ) {
	size_t i;
	if( insert == NULL )
		return;
	for( i = 0; i < insert->table->column_count; i ++ ) {
		if( insert->values[i] != NULL )
			csv_expr_free( insert->values[i] );
	}
	csv_table_def_free( insert->table );
	Safefree( insert->values );
	Safefree( insert );
}