blob: b9a5798b5e159965fffa65702a9d8cbf083630d4 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// ReSharper disable UnusedAutoPropertyAccessor.Local
// ReSharper disable NonReadonlyMemberInGetHashCode
namespace Apache.Ignite.Core.Tests.Cache.Query
{
using System;
using System.Linq;
using Apache.Ignite.Core.Binary;
using Apache.Ignite.Core.Cache.Configuration;
using Apache.Ignite.Core.Cache.Query;
using Apache.Ignite.Core.Common;
using NUnit.Framework;
/// <summary>
/// Tests Data Manipulation Language queries.
/// </summary>
public class CacheDmlQueriesTest
{
/// <summary>
/// Sets up test fixture.
/// </summary>
[TestFixtureSetUp]
public void FixtureSetUp()
{
var cfg = new IgniteConfiguration(TestUtils.GetTestConfiguration())
{
// Type registration is required because we call DML first and cache.Get after.
BinaryConfiguration = new BinaryConfiguration(typeof(Foo), typeof(Key), typeof(Key2), typeof(KeyAll))
{
NameMapper = GetNameMapper()
}
};
Ignition.Start(cfg);
}
/// <summary>
/// Gets the name mapper.
/// </summary>
protected virtual IBinaryNameMapper GetNameMapper()
{
return new BinaryBasicNameMapper {IsSimpleName = false};
}
/// <summary>
/// Tears down test fixture.
/// </summary>
[TestFixtureTearDown]
public void FixtureTearDown()
{
Ignition.StopAll(true);
}
/// <summary>
/// Tests primitive key.
/// </summary>
[Test]
public void TestPrimitiveKey()
{
var cfg = new CacheConfiguration("primitive_key", new QueryEntity(typeof(int), typeof(Foo)));
var cache = Ignition.GetIgnite().CreateCache<int, Foo>(cfg);
// Test insert.
var res = cache.Query(new SqlFieldsQuery("insert into foo(_key, id, name) " +
"values (?, ?, ?), (?, ?, ?)",
1, 2, "John", 3, 4, "Mary")).GetAll();
Assert.AreEqual(1, res.Count);
Assert.AreEqual(1, res[0].Count);
Assert.AreEqual(2, res[0][0]); // 2 affected rows
var foos = cache.OrderBy(x => x.Key).ToArray();
Assert.AreEqual(2, foos.Length);
Assert.AreEqual(1, foos[0].Key);
Assert.AreEqual(2, foos[0].Value.Id);
Assert.AreEqual("John", foos[0].Value.Name);
Assert.AreEqual(3, foos[1].Key);
Assert.AreEqual(4, foos[1].Value.Id);
Assert.AreEqual("Mary", foos[1].Value.Name);
// Test key existence.
Assert.IsTrue(cache.ContainsKey(1));
Assert.IsTrue(cache.ContainsKey(3));
}
/// <summary>
/// Tests the NotNull constraint.
/// </summary>
[Test]
public void TestNotNull()
{
var cfg = new CacheConfiguration("not_null", new QueryEntity(typeof(int), typeof(Foo))
{
Fields = new[]
{
new QueryField("id", typeof(int)) {NotNull = true},
new QueryField("name", typeof(string))
}
});
var cache = Ignition.GetIgnite().CreateCache<int, Foo>(cfg);
var ex = Assert.Throws<IgniteException>(() => cache.Query(new SqlFieldsQuery(
"insert into foo(_key, name) values (?, ?)", 1, "bar")).GetAll());
Assert.AreEqual("Null value is not allowed for column 'ID'", ex.Message);
}
/// <summary>
/// Tests the NotNull constraint.
/// </summary>
[Test]
public void TestNotNullAttribute()
{
var cfg = new CacheConfiguration("not_null_attr", new QueryEntity(typeof(int), typeof(Foo)));
var cache = Ignition.GetIgnite().CreateCache<int, Foo>(cfg);
var ex = Assert.Throws<IgniteException>(() => cache.Query(new SqlFieldsQuery(
"insert into foo(_key, id) values (?, ?)", 1, 2)).GetAll());
Assert.AreEqual("Null value is not allowed for column 'NAME'", ex.Message);
}
/// <summary>
/// Tests all primitive key types.
/// </summary>
[Test]
public void TestPrimitiveKeyAllTypes()
{
TestKey(byte.MinValue, byte.MaxValue);
TestKey(sbyte.MinValue, sbyte.MaxValue);
TestKey(short.MinValue, short.MaxValue);
TestKey(int.MinValue, int.MaxValue);
TestKey(uint.MinValue, uint.MaxValue);
TestKey(long.MinValue, long.MaxValue);
TestKey(ulong.MinValue, ulong.MaxValue);
TestKey(float.MinValue, float.MaxValue);
TestKey(double.MinValue, double.MaxValue);
TestKey(decimal.MinValue, decimal.MaxValue);
TestKey(Guid.NewGuid());
}
/// <summary>
/// Tests the key.
/// </summary>
private static void TestKey<T>(params T[] vals)
{
var cfg = new CacheConfiguration("primitive_key_dotnet_" + typeof(T),
new QueryEntity(typeof(T), typeof(string)));
var cache = Ignition.GetIgnite().CreateCache<T, string>(cfg);
foreach (var val in vals)
{
var res = cache.Query(new SqlFieldsQuery(
"insert into string(_key, _val) values (?, ?)", val, val.ToString())).GetAll();
Assert.AreEqual(1, res.Count);
Assert.AreEqual(1, res[0].Count);
Assert.AreEqual(1, res[0][0]);
Assert.AreEqual(val.ToString(), cache[val]);
}
}
/// <summary>
/// Tests composite key (which requires QueryField.IsKeyField).
/// </summary>
[Test]
public void TestCompositeKey()
{
var cfg = new CacheConfiguration("composite_key_arr", new QueryEntity(typeof(Key), typeof(Foo)));
var cache = Ignition.GetIgnite().CreateCache<Key, Foo>(cfg);
// Test insert.
var res = cache.Query(new SqlFieldsQuery("insert into foo(hi, lo, id, name) " +
"values (-1, 65500, 3, 'John'), (255, 128, 6, 'Mary')")).GetAll();
Assert.AreEqual(1, res.Count);
Assert.AreEqual(1, res[0].Count);
Assert.AreEqual(2, res[0][0]); // 2 affected rows
var foos = cache.OrderByDescending(x => x.Key.Lo).ToArray();
Assert.AreEqual(2, foos.Length);
Assert.AreEqual(-1, foos[0].Key.Hi);
Assert.AreEqual(65500, foos[0].Key.Lo);
Assert.AreEqual(3, foos[0].Value.Id);
Assert.AreEqual("John", foos[0].Value.Name);
Assert.AreEqual(255, foos[1].Key.Hi);
Assert.AreEqual(128, foos[1].Key.Lo);
Assert.AreEqual(6, foos[1].Value.Id);
Assert.AreEqual("Mary", foos[1].Value.Name);
// Existence tests check that hash codes are consistent.
var binary = cache.Ignite.GetBinary();
var binCache = cache.WithKeepBinary<IBinaryObject, IBinaryObject>();
Assert.IsTrue(cache.ContainsKey(new Key(65500, -1)));
Assert.IsTrue(cache.ContainsKey(foos[0].Key));
Assert.IsTrue(binCache.ContainsKey(
binary.GetBuilder(typeof(Key)).SetField("hi", -1).SetField("lo", 65500).Build()));
Assert.IsTrue(binCache.ContainsKey( // Fields are sorted.
binary.GetBuilder(typeof(Key)).SetField("lo", 65500).SetField("hi", -1).Build()));
Assert.IsTrue(cache.ContainsKey(new Key(128, 255)));
Assert.IsTrue(cache.ContainsKey(foos[1].Key));
Assert.IsTrue(binCache.ContainsKey(
binary.GetBuilder(typeof(Key)).SetField("hi", 255).SetField("lo", 128).Build()));
Assert.IsTrue(binCache.ContainsKey( // Fields are sorted.
binary.GetBuilder(typeof(Key)).SetField("lo", 128).SetField("hi", 255).Build()));
}
/// <summary>
/// Tests composite key (which requires QueryField.IsKeyField).
/// </summary>
[Test]
public void TestCompositeKey2()
{
var cfg = new CacheConfiguration("composite_key_fld", new QueryEntity(typeof(Key2), typeof(Foo)));
var cache = Ignition.GetIgnite().CreateCache<Key2, Foo>(cfg);
// Test insert.
var res = cache.Query(new SqlFieldsQuery("insert into foo(hi, lo, str, id, name) " +
"values (1, 2, 'Фу', 3, 'John'), (4, 5, 'Бар', 6, 'Mary')")).GetAll();
Assert.AreEqual(1, res.Count);
Assert.AreEqual(1, res[0].Count);
Assert.AreEqual(2, res[0][0]); // 2 affected rows
var foos = cache.OrderBy(x => x.Key.Lo).ToArray();
Assert.AreEqual(2, foos.Length);
Assert.AreEqual(1, foos[0].Key.Hi);
Assert.AreEqual(2, foos[0].Key.Lo);
Assert.AreEqual("Фу", foos[0].Key.Str);
Assert.AreEqual(3, foos[0].Value.Id);
Assert.AreEqual("John", foos[0].Value.Name);
Assert.AreEqual(4, foos[1].Key.Hi);
Assert.AreEqual(5, foos[1].Key.Lo);
Assert.AreEqual("Бар", foos[1].Key.Str);
Assert.AreEqual(6, foos[1].Value.Id);
Assert.AreEqual("Mary", foos[1].Value.Name);
// Existence tests check that hash codes are consistent.
Assert.IsTrue(cache.ContainsKey(new Key2(2, 1, "Фу")));
Assert.IsTrue(cache.ContainsKey(foos[0].Key));
Assert.IsTrue(cache.ContainsKey(new Key2(5, 4, "Бар")));
Assert.IsTrue(cache.ContainsKey(foos[1].Key));
}
/// <summary>
/// Tests the composite key without IsKeyField configuration.
/// </summary>
[Test]
public void TestInvalidCompositeKey()
{
var cfg = new CacheConfiguration("invalid_composite_key", new QueryEntity
{
KeyTypeName = "Key",
ValueTypeName = "Foo",
Fields = new[]
{
// Next two fields belong to the <see cref="Key"/> object, so should have been marked with <see cref="QueryField.IsKeyField"/>
// But if we forgot to do this - all fields are treated as value fields. Key fields have default values and second insert fails.
new QueryField("Lo", typeof(int)),
new QueryField("Hi", typeof(int)),
new QueryField("Id", typeof(int)),
new QueryField("Name", typeof(string))
}
});
var cache = Ignition.GetIgnite().CreateCache<Key, Foo>(cfg);
var ex = Assert.Throws<IgniteException>(
() => cache.Query(new SqlFieldsQuery("insert into foo(lo, hi, id, name) " +
"values (1, 2, 3, 'John'), (4, 5, 6, 'Mary')")));
StringAssert.StartsWith("Failed to INSERT some keys because they are already in cache", ex.Message);
}
/// <summary>
/// Tests DML with pure binary cache mode, without classes.
/// </summary>
[Test]
public void TestBinaryMode()
{
var cfg = new CacheConfiguration("binary_only", new QueryEntity
{
KeyTypeName = "CarKey",
ValueTypeName = "Car",
Fields = new[]
{
new QueryField("VIN", typeof(string)) {IsKeyField = true},
new QueryField("Id", typeof(int)) {IsKeyField = true},
new QueryField("Make", typeof(string)),
new QueryField("Year", typeof(int))
}
});
var cache = Ignition.GetIgnite().CreateCache<object, object>(cfg)
.WithKeepBinary<IBinaryObject, IBinaryObject>();
var res = cache.Query(new SqlFieldsQuery("insert into car(VIN, Id, Make, Year) " +
"values ('DLRDMC', 88, 'DeLorean', 1982)")).GetAll();
Assert.AreEqual(1, res.Count);
Assert.AreEqual(1, res[0].Count);
Assert.AreEqual(1, res[0][0]);
var car = cache.Single();
Assert.AreEqual("CarKey", car.Key.GetBinaryType().TypeName);
Assert.AreEqual("Car", car.Value.GetBinaryType().TypeName);
}
/// <summary>
/// Tests the composite key with fields of all data types.
/// </summary>
[Test]
public void TestCompositeKeyAllDataTypes()
{
var cfg = new CacheConfiguration("composite_key_all", new QueryEntity(typeof(KeyAll), typeof(string)));
var cache = Ignition.GetIgnite().CreateCache<KeyAll, string>(cfg);
var key = new KeyAll
{
Byte = byte.MaxValue,
SByte = sbyte.MaxValue,
Short = short.MaxValue,
UShort = ushort.MaxValue,
Int = int.MaxValue,
UInt = uint.MaxValue,
Long = long.MaxValue,
ULong = ulong.MaxValue,
Float = float.MaxValue,
Double = double.MaxValue,
Decimal = decimal.MaxValue,
Guid = Guid.NewGuid(),
String = "привет",
Key = new Key(255, 65555)
};
// Test insert.
var res = cache.Query(new SqlFieldsQuery(
"insert into string(byte, sbyte, short, ushort, int, uint, long, ulong, float, double, decimal, " +
"guid, string, key, _val) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
key.Byte, key.SByte, key.Short, key.UShort, key.Int, key.UInt, key.Long, key.ULong, key.Float,
key.Double, key.Decimal, key.Guid, key.String, key.Key, "VALUE"))
.GetAll();
Assert.AreEqual(1, res.Count);
Assert.AreEqual(1, res[0].Count);
Assert.AreEqual(1, res[0][0]);
// Compare resulting keys.
Assert.AreEqual(key, cache.Single().Key);
// Compare keys in binary form.
var binKey = cache.Ignite.GetBinary().ToBinary<IBinaryObject>(key);
var binKeyRes = cache.WithKeepBinary<IBinaryObject, string>().Single().Key;
Assert.AreEqual(binKey, binKeyRes);
// Get by key to verify identity.
Assert.AreEqual("VALUE", cache[key]);
}
/// <summary>
/// Tests the QueryField.DefaultValue functionality.
/// </summary>
[Test]
public void TestDefaultValue()
{
// Attribute-based config.
var cfg = new CacheConfiguration("def_value_attr", new QueryEntity(typeof(int), typeof(Foo)));
Assert.AreEqual(-1, cfg.QueryEntities.Single().Fields.Single(x => x.Name == "Id").DefaultValue);
var cache = Ignition.GetIgnite().CreateCache<int, Foo>(cfg);
Assert.AreEqual(-1,
cache.GetConfiguration().QueryEntities.Single().Fields.Single(x => x.Name == "Id").DefaultValue);
cache.Query(new SqlFieldsQuery("insert into foo(_key, id, name) values (?, ?, ?)", 1, 2, "John")).GetAll();
cache.Query(new SqlFieldsQuery("insert into foo(_key, name) values (?, ?)", 3, "Mary")).GetAll();
Assert.AreEqual(2, cache[1].Id);
Assert.AreEqual(-1, cache[3].Id);
// QueryEntity-based config.
cfg = new CacheConfiguration("def_value_binary", new QueryEntity
{
KeyType = typeof(int),
ValueTypeName = "DefValTest",
Fields = new[]
{
new QueryField("Name", typeof(string)) {DefaultValue = "foo"}
}
});
var cache2 = Ignition.GetIgnite().CreateCache<int, int>(cfg).WithKeepBinary<int, IBinaryObject>();
cache2.Query(new SqlFieldsQuery("insert into DefValTest(_key, name) values (?, ?)", 1, "John")).GetAll();
cache2.Query(new SqlFieldsQuery("insert into DefValTest(_key) values (?)", 2)).GetAll();
Assert.AreEqual("John", cache2[1].GetField<string>("Name"));
Assert.AreEqual("foo", cache2[2].GetField<string>("Name"));
}
/// <summary>
/// Key.
/// </summary>
private struct Key
{
public Key(int lo, int hi) : this()
{
Lo = lo;
Hi = hi;
}
[QuerySqlField] public int Lo { get; private set; }
[QuerySqlField] public int Hi { get; private set; }
}
/// <summary>
/// Key.
/// </summary>
private struct Key2
{
public Key2(int lo, int hi, string str) : this()
{
Lo = lo;
Hi = hi;
Str = str;
}
[QuerySqlField] public int Lo { get; private set; }
[QuerySqlField] public int Hi { get; private set; }
[QuerySqlField] public string Str { get; private set; }
}
/// <summary>
/// Value.
/// </summary>
private class Foo
{
[QuerySqlField(DefaultValue = -1)] public int Id { get; set; }
[QuerySqlField(NotNull = true)] public string Name { get; set; }
}
/// <summary>
/// Key with all kinds of fields.
/// </summary>
private class KeyAll
{
[QuerySqlField] public byte Byte { get; set; }
[QuerySqlField] public sbyte SByte { get; set; }
[QuerySqlField] public short Short { get; set; }
[QuerySqlField] public ushort UShort { get; set; }
[QuerySqlField] public int Int { get; set; }
[QuerySqlField] public uint UInt { get; set; }
[QuerySqlField] public long Long { get; set; }
[QuerySqlField] public ulong ULong { get; set; }
[QuerySqlField] public float Float { get; set; }
[QuerySqlField] public double Double { get; set; }
[QuerySqlField] public decimal Decimal { get; set; }
[QuerySqlField] public Guid Guid { get; set; }
[QuerySqlField] public string String { get; set; }
[QuerySqlField] public Key Key { get; set; }
private bool Equals(KeyAll other)
{
return Byte == other.Byte && SByte == other.SByte && Short == other.Short &&
UShort == other.UShort && Int == other.Int && UInt == other.UInt && Long == other.Long &&
ULong == other.ULong && Float.Equals(other.Float) && Double.Equals(other.Double) &&
Decimal == other.Decimal && Guid.Equals(other.Guid) && string.Equals(String, other.String) &&
Key.Equals(other.Key);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((KeyAll) obj);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = Byte.GetHashCode();
hashCode = (hashCode * 397) ^ SByte.GetHashCode();
hashCode = (hashCode * 397) ^ Short.GetHashCode();
hashCode = (hashCode * 397) ^ UShort.GetHashCode();
hashCode = (hashCode * 397) ^ Int;
hashCode = (hashCode * 397) ^ (int) UInt;
hashCode = (hashCode * 397) ^ Long.GetHashCode();
hashCode = (hashCode * 397) ^ ULong.GetHashCode();
hashCode = (hashCode * 397) ^ Float.GetHashCode();
hashCode = (hashCode * 397) ^ Double.GetHashCode();
hashCode = (hashCode * 397) ^ Decimal.GetHashCode();
hashCode = (hashCode * 397) ^ Guid.GetHashCode();
hashCode = (hashCode * 397) ^ String.GetHashCode();
hashCode = (hashCode * 397) ^ Key.GetHashCode();
return hashCode;
}
}
}
}
}