require File.expand_path("../spec_basic_helper", __FILE__) describe "OrientDB" do describe "SQL" do describe "Common" do describe "quote" do it "should quote numeric values correctly" do OrientDB::SQL::Query.quote(1).should == "1" OrientDB::SQL::Query.quote(1.1).should == "1.1" OrientDB::SQL::Query.quote(10_000_000).should == "10000000" end it "should quote symbols correctly" do OrientDB::SQL::Query.quote(:name).should == "name" end it "should quote strings correctly" do OrientDB::SQL::Query.quote("name").should == "'name'" OrientDB::SQL::Query.quote("'name'").should == "'name'" OrientDB::SQL::Query.quote("O'Brien").should == "'O\\'Brien'" OrientDB::SQL::Query.quote("'O\\'Brien'").should == "'O\\'Brien'" OrientDB::SQL::Query.quote("O'Brien & O'Malley").should == "'O\\'Brien & O\\'Malley'" end it "should quote arrays correctly" do OrientDB::SQL::Query.quote([:a, 'b', 1]).should == "[a, 'b', 1]" end it "should quote regular expressions correctly" do OrientDB::SQL::Query.quote(/\d{1,3}\.\d{1,3}/).should == "'\\d{1,3}\\.\\d{1,3}'" end it "should quote record attributes properly" do OrientDB::SQL::Query.quote(:@this).should == "@this" OrientDB::SQL::Query.quote(:@rid).should == "@rid" OrientDB::SQL::Query.quote(:@class).should == "@class" OrientDB::SQL::Query.quote(:@version).should == "@version" OrientDB::SQL::Query.quote(:@size).should == "@size" OrientDB::SQL::Query.quote(:@type).should == "@type" end it "should quote literal_expressions properly" do value = OrientDB::SQL::LiteralExpression.new "@this" OrientDB::SQL::Query.quote(:@this).should == "@this" value = OrientDB::SQL::LiteralExpression.new :@this OrientDB::SQL::Query.quote(:@this).should == "@this" end end describe "select_single_string" do it "should split extend and alias strings correctly" do OrientDB::SQL::Query.select_single_string('a__b___c').should == 'a.b AS c' end it "should split extend strings correctly" do OrientDB::SQL::Query.select_single_string('a__b').should == 'a.b' end it "should split extended strings correctly" do OrientDB::SQL::Query.select_single_string('a__b__c').should == 'a.b.c' end it "should split alias strings correctly" do OrientDB::SQL::Query.select_single_string('a___c').should == 'a AS c' end end describe "field_name" do it "should split simple names correctly" do OrientDB::SQL::Query.field_name(:a__b).should == 'a.b' end it "should split longer names correctly" do OrientDB::SQL::Query.field_name(:a__b__c).should == 'a.b.c' end end end describe "Query" do it "should do a blank query" do @q = OrientDB::SQL::Query.new @q.should be_a_kind_of OrientDB::SQL::Query @q.to_s.should == 'SELECT FROM' end describe "SELECT" do before :each do @q = OrientDB::SQL::Query.new end it "should select simple string columns" do @q.select('name').to_s.should == 'SELECT name FROM' end it "should select with extended dots on __ separated strings" do @q.select('name__a').to_s.should == 'SELECT name.a FROM' end it "should select as alias on ___ separated strings" do @q.select('name___a').to_s.should == 'SELECT name AS a FROM' end it "should select as mixed alias/dots strings" do @q.select('name__a___b').to_s.should == 'SELECT name.a AS b FROM' end it "should select simple symbol columns" do @q.select(:name).to_s.should == 'SELECT name FROM' end it "should select simple integer columns" do @q.select(1).to_s.should == 'SELECT 1 FROM' end it "should select simple hashes" do @q.select(:name => :a).to_s.should == 'SELECT name AS a FROM' end it "should select simple two column arrays as aliases" do @q.select(['name', 'a']).to_s.should == 'SELECT name AS a FROM' end it "should select simple n-column arrays as entries" do @q.select(['name', 'age', 'code']).to_s.should == 'SELECT name, age, code FROM' end it "should select simple multiple parameters as entries" do @q.select('name', :age, 1, [:a, :b], :x => :y).to_s.should == 'SELECT name, age, 1, a AS b, x AS y FROM' end it "should select simple multiple parameters as entries using columns" do @q.columns('name', :age, 1, [:a, :b], :x => :y).to_s.should == 'SELECT name, age, 1, a AS b, x AS y FROM' end it "should handle concatenation" do @q.select('name').select(:age).to_s.should == 'SELECT name, age FROM' end it "should handle overriding" do @q.select('name').select!(:age).to_s.should == 'SELECT age FROM' end end describe "FROM" do before :each do @q = OrientDB::SQL::Query.new end it "should use a simple string target" do @q.from('Developer').to_s.should == 'SELECT FROM Developer' end it "should use a simple symbol target" do @q.from(:Developer).to_s.should == 'SELECT FROM Developer' end it "should use a simple object target" do @q.from(Developer).to_s.should == 'SELECT FROM Developer' end it "should use multiple simple string targets" do @q.from('5:1', '5:3', '5:5').to_s.should == 'SELECT FROM [5:1, 5:3, 5:5]' end it "should handle concatenation" do @q.from('5:1').from('5:3').from('5:5').to_s.should == 'SELECT FROM [5:1, 5:3, 5:5]' end it "should handle overriding" do @q.from('5:1').from!('5:5').to_s.should == 'SELECT FROM 5:5' end end describe "WHERE" do before :each do @q = OrientDB::SQL::Query.new end it "should handle simple hashes" do @q.where(:a => 1).to_s.should == 'SELECT FROM WHERE a = 1' end it "should handle simple arrays" do @q.where(['a > 1', 'b < 3', 'c = 5']).to_s.should == 'SELECT FROM WHERE a > 1 AND b < 3 AND c = 5' end it "should handle concatenation" do @q.where(:a => 1).where(:b => 2).to_s.should == 'SELECT FROM WHERE a = 1 AND b = 2' end it "should handle overriding" do @q.where(:a => 1).where!(:b => 2).to_s.should == 'SELECT FROM WHERE b = 2' end it "should handle simple joined AND conditions" do @q.where(:a => 1).and(:b => 2).to_s.should == 'SELECT FROM WHERE a = 1 AND b = 2' end it "should handle simple joined OR conditions" do @q.where(:a => 1).or(:b => 2).to_s.should == 'SELECT FROM WHERE a = 1 OR b = 2' end it "should handle simple joined AND NOT conditions" do @q.where(:a => 1).and_not(:b => 2).to_s.should == 'SELECT FROM WHERE a = 1 AND NOT b = 2' end it "should handle simple joined OR NOT conditions" do @q.where(:a => 1).or_not(:b => 2).to_s.should == 'SELECT FROM WHERE a = 1 OR NOT b = 2' end it "should handle complex joined AND conditions (1)" do @q.where(:a => 1, :b => 2).and(:c => 3).to_s.should == 'SELECT FROM WHERE (a = 1 AND b = 2) AND c = 3' end it "should handle complex joined AND conditions (2)" do @q.where(:a => 1, :b => 2).and(:c => 3, :d => 4).to_s.should == 'SELECT FROM WHERE (a = 1 AND b = 2) AND (c = 3 AND d = 4)' end it "should handle complex joined OR conditions (1)" do @q.where(:a => 1, :b => 2).or(:c => 3).to_s.should == 'SELECT FROM WHERE (a = 1 AND b = 2) OR c = 3' end it "should handle complex joined OR conditions (2)" do @q.where(:a => 1, :b => 2).or(:c => 3, :d => 4).to_s.should == 'SELECT FROM WHERE (a = 1 AND b = 2) OR (c = 3 AND d = 4)' end it "should handle complex joined AND NOT conditions (1)" do @q.where(:a => 1, :b => 2).and_not(:c => 3).to_s.should == 'SELECT FROM WHERE (a = 1 AND b = 2) AND NOT c = 3' end it "should handle complex joined AND NOT conditions (2)" do @q.where(:a => 1, :b => 2).and_not(:c => 3, :d => 4).to_s.should == 'SELECT FROM WHERE (a = 1 AND b = 2) AND NOT (c = 3 AND d = 4)' end it "should handle complex joined OR NOT conditions (1)" do @q.where(:a => 1, :b => 2).or_not(:c => 3).to_s.should == 'SELECT FROM WHERE (a = 1 AND b = 2) OR NOT c = 3' end it "should handle complex joined OR NOT conditions (2)" do @q.where(:a => 1, :b => 2).or_not(:c => 3, :d => 4).to_s.should == 'SELECT FROM WHERE (a = 1 AND b = 2) OR NOT (c = 3 AND d = 4)' end end describe "ORDER BY" do before :each do @q = OrientDB::SQL::Query.new end it "should handle simple hashes" do @q.order(:a => :desc).to_s.should == 'SELECT FROM ORDER BY a DESC' end it "should handle two column arrays" do @q.order([:a, :desc]).to_s.should == 'SELECT FROM ORDER BY a DESC' end it "should handle n column arrays" do @q.order([:a, :b, :c]).to_s.should == 'SELECT FROM ORDER BY a, b, c' end it "should handle simple entries" do @q.order(:a, 'b').to_s.should == 'SELECT FROM ORDER BY a, b' end it "should default to ASC on unrecognized directions" do @q.order(:a => :unknown).to_s.should == 'SELECT FROM ORDER BY a ASC' end it "should handle concatenation" do @q.order(:a).order(:b).to_s.should == 'SELECT FROM ORDER BY a, b' end it "should handle overriding" do @q.order(:a).order!(:b).to_s.should == 'SELECT FROM ORDER BY b' end end describe "LIMIT" do before :each do @q = OrientDB::SQL::Query.new end it "should handle integers" do @q.limit(1).to_s.should == 'SELECT FROM LIMIT 1' end it "should handle strings" do @q.limit('1').to_s.should == 'SELECT FROM LIMIT 1' end it "should handle other types" do @q.limit(:'1').to_s.should == 'SELECT FROM LIMIT 1' end it "should override on concatenation" do @q.limit(2).limit(1).to_s.should == 'SELECT FROM LIMIT 1' end it "should override on bang (alias)" do @q.limit!(2).limit!(1).to_s.should == 'SELECT FROM LIMIT 1' end end describe "RANGE" do before :each do @q = OrientDB::SQL::Query.new end it "should handle lower only" do @q.range('1:1').to_s.should == 'SELECT FROM RANGE 1:1' end it "should handle lower and higher" do @q.range('1:1', '1:5').to_s.should == 'SELECT FROM RANGE 1:1, 1:5' end it "should override on concatenation" do @q.range('2:1', '2:5').range('1:1').to_s.should == 'SELECT FROM RANGE 1:1' end it "should override on bang (alias)" do @q.range!('2:1', '2:5').range!('1:1').to_s.should == 'SELECT FROM RANGE 1:1' end end describe "Monkey Patching" do describe Symbol do OrientDB::SQL.monkey_patch! Symbol describe "Order" do it "#asc should work" do :name.asc.should == 'name ASC' end it "#desc should work" do :name.desc.should == 'name DESC' end end describe "Conditional" do it "should work" do :name.lit.should == OrientDB::SQL::LiteralExpression.new("name") end it "#like should work" do :name.like("test%").should == "name LIKE 'test%'" end it "#eq should work" do :age.eq(35).should == "age = 35" end it "#ne should work" do :age.ne(35).should == "age <> 35" end it "#lt should work" do :age.lt(35).should == "age < 35" end it "#lte should work" do :age.lte(35).should == "age <= 35" end it "#gt should work" do :age.gt(35).should == "age > 35" end it "#gte should work" do :age.gte(35).should == "age >= 35" end it "#is_null should work" do :name.is_null.should == "name IS null" end it "#is_not_null should work" do :name.is_not_null.should == "name IS NOT null" end it "#in should work" do :age.in(34, 36, 38).should == "age IN [34, 36, 38]" end it "#contains should work" do :name.contains(:name, "tester").should == "name contains (name = 'tester')" end it "#contains_all should work" do :name.contains_all(:name, "tester").should == "name containsAll (name = 'tester')" end it "#contains_key should work" do :name.contains_key("tester").should == "name containsKey 'tester'" end it "#contains_value should work" do :name.contains_value("tester").should == "name containsValue 'tester'" end it "#contains_text should work" do :name.contains_text("tester").should == "name containsText 'tester'" end it "#matches should work" do :name.matches(/(john|mark)/).should == "name matches '(john|mark)'" end end describe "Field Operators" do it "#odb_length should work" do :name.odb_length.should == "name.length()" end it "#odb_trim should work" do :name.odb_trim.should == "name.trim()" end it "#to_upper_case should work" do :name.to_upper_case.should == "name.toUpperCase()" end it "#to_lower_case should work" do :name.to_lower_case.should == "name.toLowerCase()" end it "#odb_left should work" do :name.odb_left(5).should == "name.left(5)" end it "#odb_right should work" do :name.odb_right(5).should == "name.right(5)" end it "#sub_string should work" do :name.sub_string(3).should == "name.subString(3)" :name.sub_string(3, 5).should == "name.subString(3, 5)" end it "#char_at should work" do :name.char_at(3).should == "name.charAt(3)" end it "#index_of should work" do :name.index_of("test").should == "name.indexOf('test')" :name.index_of("test", 3).should == "name.indexOf('test', 3)" end it "#odb_format should work" do :name.odb_format('%-20.20s').should == "name.format('%-20.20s')" end it "#odb_size should work" do :name.odb_size.should == "name.size()" end it "#as_string should work" do :name.as_string.should == "name.asString()" end it "#as_integer should work" do :name.as_integer.should == "name.asInteger()" end it "#as_float should work" do :name.as_float.should == "name.asFloat()" end it "#as_date should work" do :name.as_date.should == "name.asDate()" end it "#as_date_time should work" do :name.as_date_time.should == "name.asDateTime()" end it "#as_boolean should work" do :name.as_boolean.should == "name.asBoolean()" end end describe "Bundled Functions" do it "#odb_count should work" do :name.odb_count.should == "count(name)" end it "#odb_min should work" do :name.odb_min.should == "min(name)" end it "#odb_max should work" do :name.odb_max.should == "max(name)" end it "#odb_avg should work" do :name.odb_avg.should == "avg(name)" end it "#odb_sum should work" do :name.odb_sum.should == "sum(name)" end it "#sysdate should work" do :'yyyy.MM.dd'.sysdate.should == "sysdate('yyyy.MM.dd')" end it "#odb_format_str should work" do :'%d - Mr. %s %s (%s)'.odb_format_str(:id, :name, :surname, :address).should == "format('%d - Mr. %s %s (%s)', id, name, surname, address)" end end end describe String do OrientDB::SQL.monkey_patch! String describe "Order" do it "#asc should work" do 'name'.asc.should == 'name ASC' end it "#desc should work" do 'name'.desc.should == 'name DESC' end end describe "Conditional" do it "should work" do 'name'.lit.should == OrientDB::SQL::LiteralExpression.new("name") end it "#like should work" do 'name'.like("test%").should == "name LIKE 'test%'" end it "#eq should work" do 'age'.eq(35).should == "age = 35" end it "#ne should work" do 'age'.ne(35).should == "age <> 35" end it "#lt should work" do 'age'.lt(35).should == "age < 35" end it "#lte should work" do 'age'.lte(35).should == "age <= 35" end it "#gt should work" do 'age'.gt(35).should == "age > 35" end it "#gte should work" do 'age'.gte(35).should == "age >= 35" end it "#is_null should work" do 'name'.is_null.should == "name IS null" end it "#is_not_null should work" do 'name'.is_not_null.should == "name IS NOT null" end it "#in should work" do 'age'.in(34, 36, 38).should == "age IN [34, 36, 38]" end it "#contains should work" do 'name'.contains('name', "tester").should == "name contains (name = 'tester')" end it "#contains_all should work" do 'name'.contains_all('name', "tester").should == "name containsAll (name = 'tester')" end it "#contains_key should work" do 'name'.contains_key("tester").should == "name containsKey 'tester'" end it "#contains_value should work" do 'name'.contains_value("tester").should == "name containsValue 'tester'" end it "#contains_text should work" do 'name'.contains_text("tester").should == "name containsText 'tester'" end it "#matches should work" do 'name'.matches(/(john|mark)/).should == "name matches '(john|mark)'" end end describe "Field Operators" do it "#odb_length should work" do 'name'.odb_length.should == "name.length()" end it "#odb_trim should work" do 'name'.odb_trim.should == "name.trim()" end it "#to_upper_case should work" do 'name'.to_upper_case.should == "name.toUpperCase()" end it "#to_lower_case should work" do 'name'.to_lower_case.should == "name.toLowerCase()" end it "#odb_left should work" do 'name'.odb_left(5).should == "name.left(5)" end it "#odb_right should work" do 'name'.odb_right(5).should == "name.right(5)" end it "#sub_string should work" do 'name'.sub_string(3).should == "name.subString(3)" 'name'.sub_string(3, 5).should == "name.subString(3, 5)" end it "#char_at should work" do 'name'.char_at(3).should == "name.charAt(3)" end it "#index_of should work" do 'name'.index_of("test").should == "name.indexOf('test')" 'name'.index_of("test", 3).should == "name.indexOf('test', 3)" end it "#odb_format should work" do 'name'.odb_format('%-20.20s').should == "name.format('%-20.20s')" end it "#odb_size should work" do 'name'.odb_size.should == "name.size()" end it "#as_string should work" do 'name'.as_string.should == "name.asString()" end it "#as_integer should work" do 'name'.as_integer.should == "name.asInteger()" end it "#as_float should work" do 'name'.as_float.should == "name.asFloat()" end it "#as_date should work" do 'name'.as_date.should == "name.asDate()" end it "#as_date_time should work" do 'name'.as_date_time.should == "name.asDateTime()" end it "#as_boolean should work" do 'name'.as_boolean.should == "name.asBoolean()" end end describe "Bundled Functions" do it "#odb_count should work" do 'name'.odb_count.should == "count(name)" end it "#odb_min should work" do 'name'.odb_min.should == "min(name)" end it "#odb_max should work" do 'name'.odb_max.should == "max(name)" end it "#odb_avg should work" do 'name'.odb_avg.should == "avg(name)" end it "#odb_sum should work" do 'name'.odb_sum.should == "sum(name)" end it "#sysdate should work" do 'yyyy.MM.dd'.sysdate.should == "sysdate('yyyy.MM.dd')" end it "#odb_format_str should work" do '%d - Mr. %s %s (%s)'.odb_format_str(:id, :name, :surname, :address).should == "format('%d - Mr. %s %s (%s)', id, name, surname, address)" end end end end end end describe "INSERT" do before :each do @q = OrientDB::SQL::Insert.new end it "should handle basic oclass inserts" do @q.oclass('Person').values(:name => "Martin").to_s.should == "INSERT INTO Person (name) VALUES ('Martin')" end it "should handle basic oclass inserts" do @q.cluster('person').values(:name => "Martin").to_s.should == "INSERT INTO cluster:person (name) VALUES ('Martin')" end it "should handle complex oclass inserts" do @q.oclass('Person').fields(:name => "Martin").values(:age => 39).to_s.should == "INSERT INTO Person (name, age) VALUES ('Martin', 39)" end it "should handle array oclass inserts" do @q.oclass('Person').fields(:name, :age).values("Martin", 39).to_s.should == "INSERT INTO Person (name, age) VALUES ('Martin', 39)" end end describe "UPDATE" do before :each do @q = OrientDB::SQL::Update.new end it "should handle basic oclass SET update" do @q.oclass('Person').values(:name => "Martin").to_s.should == "UPDATE Person SET name = 'Martin'" end it "should handle basic cluster SET update" do @q.cluster('person').values(:name => "Martin").to_s.should == "UPDATE cluster:person SET name = 'Martin'" end it "should handle complex oclass SET updates" do @q.oclass('Person').fields(:name => "Martin").values(:age => 39).to_s.should == "UPDATE Person SET name = 'Martin', age = 39" end it "should handle array oclass REMOVE a field updates" do @q.oclass('Person').action(:remove).fields(:name).to_s.should == "UPDATE Person REMOVE name" end it "should handle array oclass REMOVE many fields updates" do @q.oclass('Person').action(:remove).fields(:name, :age).to_s.should == "UPDATE Person REMOVE name, age" end it "should handle basic oclass REMOVE a value from a collection update" do @q.oclass('Person').action(:remove).values(:name => "Martin").to_s.should == "UPDATE Person REMOVE name = 'Martin'" end it "should handle basic oclass ADD a value into a collection update" do @q.oclass('Person').action(:add).values(:name => "Martin").to_s.should == "UPDATE Person ADD name = 'Martin'" end it "should handle basic oclass ADD many values into a collection update" do @q.oclass('Person').action(:add).values(:name => "Martin").values(:age => 39).to_s.should == "UPDATE Person ADD name = 'Martin', age = 39" end it "should handle simple hashes for conditions" do @q.oclass('Person').values(:age => 39).where(:a => 1).to_s.should == 'UPDATE Person SET age = 39 WHERE a = 1' end it "should handle simple arrays for conditions" do @q.oclass('Person').values(:age => 39).where(['a > 1', 'b < 3', 'c = 5']).to_s.should == 'UPDATE Person SET age = 39 WHERE a > 1 AND b < 3 AND c = 5' end it "should handle concatenation for conditions" do @q.oclass('Person').values(:age => 39).where(:a => 1).where(:b => 2).to_s.should == 'UPDATE Person SET age = 39 WHERE a = 1 AND b = 2' end it "should handle overriding for conditions" do @q.oclass('Person').values(:age => 39).where(:a => 1).where!(:b => 2).to_s.should == 'UPDATE Person SET age = 39 WHERE b = 2' end it "should handle simple joined AND conditions for conditions" do @q.oclass('Person').values(:age => 39).where(:a => 1).and(:b => 2).to_s.should == 'UPDATE Person SET age = 39 WHERE a = 1 AND b = 2' end it "should handle simple joined OR conditions for conditions" do @q.oclass('Person').values(:age => 39).where(:a => 1).or(:b => 2).to_s.should == 'UPDATE Person SET age = 39 WHERE a = 1 OR b = 2' end end describe "DELETE" do before :each do @q = OrientDB::SQL::Delete.new end it "should handle basic oclass" do @q.oclass('Person').to_s.should == "DELETE FROM Person" end it "should handle basic cluster" do @q.cluster('person').to_s.should == "DELETE FROM cluster:person" end it "should handle simple hashes for conditions" do @q.oclass('Person').where(:a => 1).to_s.should == 'DELETE FROM Person WHERE a = 1' end it "should handle simple arrays for conditions" do @q.oclass('Person').where(['a > 1', 'b < 3', 'c = 5']).to_s.should == 'DELETE FROM Person WHERE a > 1 AND b < 3 AND c = 5' end it "should handle concatenation for conditions" do @q.oclass('Person').where(:a => 1).where(:b => 2).to_s.should == 'DELETE FROM Person WHERE a = 1 AND b = 2' end it "should handle overriding for conditions" do @q.oclass('Person').where(:a => 1).where!(:b => 2).to_s.should == 'DELETE FROM Person WHERE b = 2' end it "should handle simple joined AND conditions for conditions" do @q.oclass('Person').where(:a => 1).and(:b => 2).to_s.should == 'DELETE FROM Person WHERE a = 1 AND b = 2' end it "should handle simple joined OR conditions for conditions" do @q.oclass('Person').where(:a => 1).or(:b => 2).to_s.should == 'DELETE FROM Person WHERE a = 1 OR b = 2' end end end