diff --git a/vlib/builtin/string.v b/vlib/builtin/string.v index ab3673b7cb..2d9f6d7aa0 100644 --- a/vlib/builtin/string.v +++ b/vlib/builtin/string.v @@ -190,6 +190,101 @@ pub fn (s string) replace(rep, with string) string { return tos(b, new_len) } +struct RepIndex { + idx int + val_idx int +} + +fn (a mut []RepIndex) sort() { + a.sort_with_compare(compare_rep_index) +} + + +// TODO +/* +fn (a RepIndex) < (b RepIndex) bool { + return a.idx < b.idx +} +*/ + +fn compare_rep_index(a, b &RepIndex) int { + if a.idx < b.idx { + return -1 + } + if a.idx > b.idx { + return 1 + } + return 0 +} + +pub fn (s string) replace_each(vals []string) string { + if s.len == 0 || vals.len == 0 { + return s + } + if vals.len % 2 != 0 { + println('string.replace_many(): odd number of strings') + return s + } + // `rep` - string to replace + // `with` - string to replace with + // Remember positions of all rep strings, and calculate the length + // of the new string to do just one allocation. + mut new_len := s.len + mut idxs := []RepIndex + mut idx := 0 + for rep_i := 0; rep_i < vals.len; rep_i+=2 { + // vals: ['rep1, 'with1', 'rep2', 'with2'] + rep := vals[rep_i] + with := vals[rep_i+1] + for { + idx = s.index_after(rep, idx) + if idx == -1 { + break + } + // We need to remember both the position in the string, + // and which rep/with pair it refers to. + idxs << RepIndex{idx, rep_i} + idx++ + new_len += with.len - rep.len + } + } + // Dont change the string if there's nothing to replace + if idxs.len == 0 { + return s + } + idxs.sort() + mut b := malloc(new_len + 1)// add a \0 just in case + // Fill the new string + mut idx_pos := 0 + mut cur_idx := idxs[idx_pos] + mut b_i := 0 + for i := 0; i < s.len; i++ { + // Reached the location of rep, replace it with "with" + if i == cur_idx.idx { + rep := vals[cur_idx.val_idx] + with := vals[cur_idx.val_idx+1] + for j := 0; j < with.len; j++ { + b[b_i] = with[j] + b_i++ + } + // Skip the length of rep, since we just replaced it with "with" + i += rep.len - 1 + // Go to the next index + idx_pos++ + if idx_pos < idxs.len { + cur_idx = idxs[idx_pos] + } + } + // Rep doesnt start here, just copy + else { + b[b_i] = s[i] + b_i++ + } + } + b[new_len] = `\0` + return tos(b, new_len) +} + pub fn (s string) bool() bool { return s == 'true' || s == 't' // TODO t for pg, remove } diff --git a/vlib/builtin/string_test.v b/vlib/builtin/string_test.v index ecc1dd72f7..9bb7124db9 100644 --- a/vlib/builtin/string_test.v +++ b/vlib/builtin/string_test.v @@ -219,6 +219,22 @@ fn test_replace() { assert c.replace('','-') == c } +fn test_replace_each() { + s := 'hello man man :)' + q := s.replace_each([ + 'man', 'dude', + 'hello', 'hey' + ]) + assert q == 'hey dude dude :)' + bb := '[b]bold[/b] [code]code[/code]' + assert bb.replace_each([ + '[b]', '', + '[/b]', '', + '[code]', '', + '[/code]', '' + ]) == 'bold code' +} + fn test_itoa() { num := 777 assert num.str() == '777' diff --git a/vlib/compiler/parser.v b/vlib/compiler/parser.v index e5bd2e58ee..f6cfd7770a 100644 --- a/vlib/compiler/parser.v +++ b/vlib/compiler/parser.v @@ -1764,7 +1764,11 @@ fn (p mut Parser) var_expr(v Var) string { name := p.tokens[p.token_idx].lit if !name.contains('exec') && !name.starts_with('q_') { p.next() - p.insert_query(fn_ph) + if name == 'insert' { + p.insert_query(fn_ph) + } else if name == 'update' { + p.update_query(fn_ph) + } return 'void' } } diff --git a/vlib/compiler/query.v b/vlib/compiler/query.v index 690865bc58..7bfd8937e9 100644 --- a/vlib/compiler/query.v +++ b/vlib/compiler/query.v @@ -245,3 +245,63 @@ fn (p mut Parser) insert_query(fn_ph int) { 0, params, 0, 0, 0)') } +// `db.update User set nr_orders=nr_orders+1` +fn (p mut Parser) update_query(fn_ph int) { + println('update query') + p.check_name() + table_name := p.check_name() + typ := p.table.find_type(table_name) + if typ.name == '' { + p.error('unknown type `$table_name`') + } + set := p.check_name() + if set != 'set' { + p.error('expected `set`') + } + if typ.fields.len == 0 { + p.error('V orm: update: empty fields in `$typ.name`') + } + if typ.fields[0].name != 'id' { + p.error('V orm: `id int` must be the first field in `$typ.name`') + } + field := p.check_name() + p.check(.assign) + for f in typ.fields { + if !(f.typ in ['string', 'int', 'bool']) { + println('orm: skipping $f.name') + continue + } + p.register_var({ f | is_mut: true, is_used:true, is_changed:true }) + } + mut q := 'update ${typ.name}s set $field=' + p.is_sql = true + set_typ, expr := p.tmp_expr() + p.is_sql = false + // TODO this hack should not be necessary + if set_typ == 'bool' { + if expr.trim_space() == '1' { + q += 'true' + } + else { + q += 'false' + } + } else { + q += expr + } + // where + if p.tok == .name && p.lit == 'where' { + p.next() + p.is_sql = true + _, wexpr := p.tmp_expr() + p.is_sql = false + q += ' where ' + wexpr + } + + + nr_vals := 0 + p.cgen.insert_before('char* params[$nr_vals];')// + params) + p.cgen.set_placeholder(fn_ph, 'PQexecParams( ') + println('update q="$q"') + p.genln('.conn, "$q", $nr_vals, 0, params, 0, 0, 0)') +} + diff --git a/vlib/vweb/vweb.v b/vlib/vweb/vweb.v index 087b6730f2..a422b6fbab 100644 --- a/vlib/vweb/vweb.v +++ b/vlib/vweb/vweb.v @@ -196,6 +196,7 @@ pub fn run(app mut T, port int) { // } // Call the right action + println('action=$action') app.$action() or { conn.write(HTTP_404) or {} }