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 {}
}