desc: Tests RQL control flow structures table_variable_name: tbl, tbl2 tests: ## FunCall - py: r.expr(1).do(lambda v: v * 2) js: r.expr(1).do(function(v) { return v.mul(2); }) rb: r.expr(1).do{|v| v * 2 } ot: 2 - py: r.expr([0, 1, 2]).do(lambda v: v.append(3)) js: r([0, 1, 2]).do(function(v) { return v.append(3); }) rb: r([0, 1, 2]).do{ |v| v.append(3) } ot: [0, 1, 2, 3] - py: r.do(1, 2, lambda x, y: x + y) js: r.do(1, 2, function(x, y) { return x.add(y); }) rb: r.do(1, 2) {|x, y| x + y} ot: 3 - py: r.do(lambda: 1) js: r.do(function() { return 1; }) rb: r.do{1} ot: 1 # do error cases - py: r.do(1, 2, lambda x: x) js: r.do(1, 2, function(x) { return x; }) rb: r.do(1, 2) {|x| x} ot: err("ReqlQueryLogicError", 'Expected function with 2 arguments but found function with 1 argument.', [1]) - py: r.do(1, 2, 3, lambda x, y: x + y) js: r.do(1, 2, 3, function(x, y) { return x.add(y); }) rb: r.do(1, 2, 3) {|x, y| x + y} ot: err("ReqlQueryLogicError", 'Expected function with 3 arguments but found function with 2 arguments.', [1]) - cd: r.do(1) ot: 1 - js: r.do(1, function(x) {}) ot: err("ReqlDriverCompileError", 'Anonymous function returned `undefined`. Did you forget a `return`?', [1]) - js: r.do(1, function(x) { return undefined; }) ot: err("ReqlDriverCompileError", 'Anonymous function returned `undefined`. Did you forget a `return`?', [1]) - cd: r.do() ot: cd: err("ReqlCompileError", 'Expected 1 or more arguments but found 0.', [1]) # FunCall errors - py: r.expr('abc').do(lambda v: v.append(3)) js: r('abc').do(function(v) { return v.append(3); }) rb: r('abc').do{ |v| v.append(3) } ot: err("ReqlQueryLogicError", "Expected type ARRAY but found STRING.", [1, 0]) - py: r.expr('abc').do(lambda v: v + 3) js: r('abc').do(function(v) { return v.add(3); }) rb: r('abc').do{ |v| v + 3 } ot: err("ReqlQueryLogicError", "Expected type STRING but found NUMBER.", [1, 1]) - py: r.expr('abc').do(lambda v: v + 'def') + 3 js: r('abc').do(function(v) { return v.add('def'); }).add(3) rb: r('abc').do{ |v| v + 'def' } + 3 ot: err("ReqlQueryLogicError", "Expected type STRING but found NUMBER.", [1]) - py: r.expr(0).do(lambda a,b: a + b) js: r(0).do(function(a,b) { return a.add(b); }) rb: r(0).do{ |a, b| a + b } ot: err("ReqlQueryLogicError", 'Expected function with 1 argument but found function with 2 arguments.', [1]) - py: r.do(1, 2, lambda a: a) js: r.do(1,2, function(a) { return a; }) rb: r.do(1, 2) { |a| a } ot: err("ReqlQueryLogicError", 'Expected function with 2 arguments but found function with 1 argument.', [1]) - cd: r.expr(5).do(r.row) rb: r(5).do{ |row| row } ot: 5 ## Branch - cd: r.branch(True, 1, 2) ot: 1 - cd: r.branch(False, 1, 2) ot: 2 - cd: r.branch(1, 'c', False) ot: ("c") - cd: r.branch(null, {}, []) ot: ([]) - cd: r.branch(r.db('test'), 1, 2) ot: err("ReqlQueryLogicError", "Expected type DATUM but found DATABASE:", []) - cd: r.branch(tbl, 1, 2) ot: err("ReqlQueryLogicError", "Expected type DATUM but found TABLE:", []) - cd: r.branch(r.error("a"), 1, 2) ot: err("ReqlUserError", "a", []) - cd: r.branch([], 1, 2) ot: 1 - cd: r.branch({}, 1, 2) ot: 1 - cd: r.branch("a", 1, 2) ot: 1 - cd: r.branch(1.2, 1, 2) ot: 1 - cd: r.branch(True, 1, True, 2, 3) ot: 1 - cd: r.branch(True, 1, False, 2, 3) ot: 1 - cd: r.branch(False, 1, True, 2, 3) ot: 2 - cd: r.branch(False, 1, False, 2, 3) ot: 3 - cd: r.branch(True, 1, True, 2) ot: err("ReqlQueryLogicError", "Cannot call `branch` term with an even number of arguments.") # r.error() - cd: r.error('Hello World') ot: err("ReqlUserError", "Hello World", [0]) - cd: r.error(5) # we might want to allow this eventually ot: err("ReqlQueryLogicError", "Expected type STRING but found NUMBER.", [0]) # r.filter - cd: r.expr([1, 2, 3]).filter() ot: cd: err("ReqlCompileError", "Expected 2 arguments but found 1.", [0]) js: err("ReqlCompileError", "Expected 1 argument (not including options) but found 0.", [0]) - cd: r.expr([1, 2, 3]).filter(1, 2) ot: cd: err("ReqlCompileError", "Expected 2 arguments but found 3.", [0]) js: err("ReqlCompileError", "Expected 1 argument (not including options) but found 2.", [0]) # r.js() - cd: r.js('1 + 1') ot: 2 - cd: r.js('1 + 1; 2 + 2') ot: 4 - cd: r.do(1, 2, r.js('(function(a, b) { return a + b; })')) ot: 3 - cd: r.expr(1).do(r.js('(function(x) { return x + 1; })')) ot: 2 - cd: r.expr('foo').do(r.js('(function(x) { return x + "bar"; })')) ot: 'foobar' # js timeout optarg shouldn't be triggered - cd: r.js('1 + 2', {timeout:1.2}) py: r.js('1 + 2', timeout=1.2) ot: 3 # js error cases - cd: r.js('(function() { return 1; })') ot: err("ReqlQueryLogicError", "Query result must be of type DATUM, GROUPED_DATA, or STREAM (got FUNCTION).", [0]) - cd: r.js('function() { return 1; }') ot: err("ReqlQueryLogicError", "SyntaxError: Unexpected token (", [0]) # Play with the number of arguments in the JS function - cd: r.do(1, 2, r.js('(function(a) { return a; })')) ot: 1 - cd: r.do(1, 2, r.js('(function(a, b, c) { return a; })')) ot: 1 - cd: r.do(1, 2, r.js('(function(a, b, c) { return c; })')) ot: err("ReqlQueryLogicError", "Cannot convert javascript `undefined` to ql::datum_t.", [0]) - cd: r.expr([1, 2, 3]).filter(r.js('(function(a) { return a >= 2; })')) ot: ([2, 3]) - cd: r.expr([1, 2, 3]).map(r.js('(function(a) { return a + 1; })')) ot: ([2, 3, 4]) - cd: r.expr([1, 2, 3]).map(r.js('1')) ot: err("ReqlQueryLogicError", "Expected type FUNCTION but found DATUM:", [0]) - cd: r.expr([1, 2, 3]).filter(r.js('(function(a) {})')) ot: err("ReqlQueryLogicError", "Cannot convert javascript `undefined` to ql::datum_t.", [0]) # What happens if we pass static values to things that expect functions - cd: r.expr([1, 2, 3]).map(1) ot: err("ReqlQueryLogicError", "Expected type FUNCTION but found DATUM:", [0]) - cd: r.expr([1, 2, 3]).filter('foo') ot: ([1, 2, 3]) - cd: r.expr([1, 2, 4]).filter([]) ot: ([1, 2, 4]) - cd: r.expr([1, 2, 3]).filter(null) ot: ([]) - cd: r.expr([1, 2, 4]).filter(False) rb: r([1, 2, 4]).filter(false) ot: ([]) # forEach - cd: tbl.count() ot: 0 # Insert three elements - js: r([1, 2, 3]).forEach(function (row) { return tbl.insert({ id:row }) }) py: r.expr([1, 2, 3]).for_each(lambda row:tbl.insert({ 'id':row })) rb: r([1, 2, 3]).for_each{ |row| tbl.insert({ :id => row }) } ot: ({'deleted':0.0,'replaced':0.0,'unchanged':0.0,'errors':0.0,'skipped':0.0,'inserted':3}) - cd: tbl.count() ot: 3 # Update each row to add additional attribute - js: r([1, 2, 3]).forEach(function (row) { return tbl.update({ foo:row }) }) py: r.expr([1,2,3]).for_each(lambda row:tbl.update({'foo':row})) rb: r.expr([1,2,3]).for_each{ |row| tbl.update({ :foo => row }) } ot: ({'deleted':0.0,'replaced':9,'unchanged':0.0,'errors':0.0,'skipped':0.0,'inserted':0.0}) # Insert three more elements (and error on three) - js: r([1, 2, 3]).forEach(function (row) { return [tbl.insert({ id:row }), tbl.insert({ id:row.mul(10) })] }) py: r.expr([1,2,3]).for_each(lambda row:[tbl.insert({ 'id':row }), tbl.insert({ 'id':row*10 })]) rb: r.expr([1,2,3]).for_each{ |row| [tbl.insert({ :id => row}), tbl.insert({ :id => row*10})] } ot: {'first_error':"Duplicate primary key `id`:\n{\n\t\"foo\":\t3,\n\t\"id\":\t1\n}\n{\n\t\"id\":\t1\n}",'deleted':0.0,'replaced':0.0,'unchanged':0.0,'errors':3,'skipped':0.0,'inserted':3} - cd: tbl.count() ot: 6 - cd: tableCount = tbl2.count() - cd: r.expr([1, 2, 3]).for_each( tbl2.insert({}) ) ot: ({'deleted':0.0,'replaced':0.0,'generated_keys':arrlen(3,uuid()),'unchanged':0.0,'errors':0.0,'skipped':0.0,'inserted':3}) # inserts only a single document per #3700 - cd: tbl2.count() ot: tableCount + 1 # We have six elements, update them 6*2*3=36 times - js: r([1, 2, 3]).forEach(function (row) { return [tbl.update({ foo:row }), tbl.update({ bar:row })] }) py: r.expr([1,2,3]).for_each(lambda row:[tbl.update({'foo':row}), tbl.update({'bar':row})]) rb: r.expr([1,2,3]).for_each{ |row| [tbl.update({:foo => row}), tbl.update({:bar => row})]} ot: ({'deleted':0.0,'replaced':36,'unchanged':0.0,'errors':0.0,'skipped':0.0,'inserted':0.0}) # forEach negative cases - cd: r.expr([1, 2, 3]).for_each( tbl2.insert({ 'id':r.row }) ) rb: r([1, 2, 3]).for_each{ |row| tbl2.insert({ 'id':row }) } ot: ({'deleted':0.0,'replaced':0.0,'unchanged':0.0,'errors':0.0,'skipped':0.0,'inserted':3}) - cd: r.expr([1, 2, 3]).for_each(1) ot: err("ReqlQueryLogicError", "FOR_EACH expects one or more basic write queries. Expected type ARRAY but found NUMBER.", [0]) - py: r.expr([1, 2, 3]).for_each(lambda x:x) js: r([1, 2, 3]).forEach(function (x) { return x; }) rb: r([1, 2, 3]).for_each{ |x| x } ot: err("ReqlQueryLogicError", "FOR_EACH expects one or more basic write queries. Expected type ARRAY but found NUMBER.", [1, 1]) - cd: r.expr([1, 2, 3]).for_each(r.row) rb: r([1, 2, 3]).for_each{ |row| row } ot: err("ReqlQueryLogicError", "FOR_EACH expects one or more basic write queries. Expected type ARRAY but found NUMBER.", [1, 1]) - js: r([1, 2, 3]).forEach(function (row) { return tbl; }) py: r.expr([1, 2, 3]).for_each(lambda row:tbl) rb: r([1, 2, 3]).for_each{ |row| tbl } ot: err("ReqlQueryLogicError", "FOR_EACH expects one or more basic write queries.", [1, 1]) # This is only relevant in JS -- what happens when we return undefined - js: r([1, 2, 3]).forEach(function (row) {}) ot: err("ReqlDriverCompileError", 'Anonymous function returned `undefined`. Did you forget a `return`?', [1]) # Make sure write queries can't be nested into stream ops - cd: r.expr(1).do(tbl.insert({'foo':r.row})) rb: r(1).do{ |row| tbl.insert({ :foo => row }) } ot: ({'deleted':0.0,'replaced':0.0,'generated_keys':arrlen(1,uuid()),'unchanged':0.0,'errors':0.0,'skipped':0.0,'inserted':1}) - py: r.expr([1, 2])[0].do(tbl.insert({'foo':r.row})) js: r.expr([1, 2]).nth(0).do(tbl.insert({'foo':r.row})) rb: r([1, 2])[0].do{ |row| tbl.insert({ :foo => row }) } ot: ({'deleted':0.0,'replaced':0.0,'generated_keys':arrlen(1,uuid()),'unchanged':0.0,'errors':0.0,'skipped':0.0,'inserted':1}) - cd: r.expr([1, 2]).map(tbl.insert({'foo':r.row})) rb: r([1, 2]).map{ |row| tbl.insert({ :foo => row }) } ot: err('ReqlCompileError', 'Cannot nest writes or meta ops in stream operations. Use FOR_EACH instead.', [0]) - cd: r.expr([1, 2]).map(r.db('test').table_create('table_create_failure')) ot: err('ReqlCompileError', 'Cannot nest writes or meta ops in stream operations. Use FOR_EACH instead.', [0]) - cd: r.expr([1, 2]).map(tbl.insert({'foo':r.row}).get_field('inserted')) rb: r.expr([1, 2]).map{|x| tbl.insert({'foo':x}).get_field('inserted')} ot: err('ReqlCompileError', 'Cannot nest writes or meta ops in stream operations. Use FOR_EACH instead.', [0]) - cd: r.expr([1, 2]).map(tbl.insert({'foo':r.row}).get_field('inserted').add(5)) rb: r.expr([1, 2]).map{|x| tbl.insert({'foo':x}).get_field('inserted').add(5)} ot: err('ReqlCompileError', 'Cannot nest writes or meta ops in stream operations. Use FOR_EACH instead.', [0]) - cd: r.expr(1).do(r.db('test').table_create('table_create_success')) ot: partial({'tables_created':1})