Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for composite primary keys #160

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/SQLite.Net/Orm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ internal static class Orm
public const string ImplicitIndexSuffix = "Id";

internal static string SqlDecl(TableMapping.Column p, bool storeDateTimeAsTicks, IBlobSerializer serializer,
IDictionary<Type, string> extraTypeMappings)
IDictionary<Type, string> extraTypeMappings, bool hasCompositePK = false)
{
var decl = "\"" + p.Name + "\" " + SqlType(p, storeDateTimeAsTicks, serializer, extraTypeMappings) + " ";

if (p.IsPK)
if (p.IsPK && !hasCompositePK)
{
decl += "primary key ";
}
Expand Down
231 changes: 188 additions & 43 deletions src/SQLite.Net/SQLiteConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ public int CreateTable(Type ty, CreateFlags createFlags = CreateFlags.None)
{
var map = GetMapping(ty, createFlags);

var query = "create table if not exists \"" + map.TableName + "\"(\n";
var query = new StringBuilder("create table if not exists \"").Append(map.TableName).Append("\"( \n");

var mapColumns = map.Columns;

Expand All @@ -380,12 +380,24 @@ public int CreateTable(Type ty, CreateFlags createFlags = CreateFlags.None)
throw new Exception("Table has no (public) columns");
}

var decls = mapColumns.Select(p => Orm.SqlDecl(p, StoreDateTimeAsTicks, Serializer, ExtraTypeMappings));
var decl = string.Join(",\n", decls.ToArray());
query += decl;
query += ")";
if (map.HasCompositePK)
{
var compositePK = mapColumns.Where(c => c.IsPK).ToList();

var decls = mapColumns.Select(p => Orm.SqlDecl(p, StoreDateTimeAsTicks, Serializer, ExtraTypeMappings, map.HasCompositePK));
var decl = string.Join(",\n", decls.ToArray());
query.Append(decl).Append(",\n");
query.Append("primary key (").Append(string.Join(",", compositePK.Select(pk => pk.Name))).Append(")");
query.Append(")");
}
else
{
var decls = mapColumns.Select(p => Orm.SqlDecl(p, StoreDateTimeAsTicks, Serializer, ExtraTypeMappings));
var decl = string.Join(",\n", decls.ToArray());
query.Append(decl).Append(")");
}

var count = Execute(query);
var count = Execute(query.ToString());

if (count == 0)
{
Expand Down Expand Up @@ -558,8 +570,13 @@ private void MigrateTable(TableMapping map)

foreach (var p in toBeAdded)
{
if (p.IsPK)
{
throw new NotSupportedException("The new column may not have a PRIMARY KEY constraint.");
}

var addCol = "alter table \"" + map.TableName + "\" add column " +
Orm.SqlDecl(p, StoreDateTimeAsTicks, Serializer, ExtraTypeMappings);
Orm.SqlDecl(p, StoreDateTimeAsTicks, Serializer, ExtraTypeMappings, map.HasCompositePK);
Execute(addCol);
}
}
Expand Down Expand Up @@ -798,7 +815,7 @@ public TableQuery<T> Table<T>() where T : class
/// the given type have a designated PrimaryKey (using the PrimaryKeyAttribute).
/// </summary>
/// <param name="pk">
/// The primary key.
/// The primary key. Needs to be Dictionary<string, object> if table has composite PK.
/// </param>
/// <returns>
/// The object with the given primary key. Throws a not found exception
Expand All @@ -808,7 +825,24 @@ public TableQuery<T> Table<T>() where T : class
public T Get<T>(object pk) where T : class
{
var map = GetMapping(typeof (T));
return Query<T>(map.GetByPrimaryKeySql, pk).First();
if (map.HasCompositePK)
{
IDictionary<string, object> compositePK = pk as Dictionary<string, object>;
if (compositePK == null)
{
throw new NotSupportedException(map.TableName + " table has a composite primary key. Make sure primary key is passed in as Dictionary<string, object>.");
}
var cpk = map.CompositePK;
if (compositePK.Keys.Intersect(cpk.Select(p => p.Name)).Count() < cpk.Length)
{
throw new NotSupportedException("Cannot get from " + map.TableName + ": CompositePK mismatch. Make sure PK names are valid.");
}
return Query<T>(map.GetByPrimaryKeySql, compositePK.Values.ToArray()).First();
}
else
{
return Query<T>(map.GetByPrimaryKeySql, pk).First();
}
}

/// <summary>
Expand All @@ -834,7 +868,7 @@ public T Get<T>(Expression<Func<T, bool>> predicate) where T : class
/// the given type have a designated PrimaryKey (using the PrimaryKeyAttribute).
/// </summary>
/// <param name="pk">
/// The primary key.
/// The primary key. Needs to be Dictionary<string, object> if table has composite PK.
/// </param>
/// <returns>
/// The object with the given primary key or null
Expand All @@ -843,8 +877,25 @@ public T Get<T>(Expression<Func<T, bool>> predicate) where T : class
[PublicAPI]
public T Find<T>(object pk) where T : class
{
var map = GetMapping(typeof (T));
return Query<T>(map.GetByPrimaryKeySql, pk).FirstOrDefault();
var map = GetMapping(typeof(T));
if (map.HasCompositePK)
{
IDictionary<string, object> compositePK = pk as Dictionary<string, object>;
if (compositePK == null)
{
throw new NotSupportedException(map.TableName + " table has a composite primary key. Make sure primary key is passed in as Dictionary<string, object>.");
}
var cpk = map.CompositePK;
if (compositePK.Keys.Intersect(cpk.Select(p => p.Name)).Count() < cpk.Length)
{
throw new NotSupportedException("Cannot find in " + map.TableName + ": CompositePK mismatch. Make sure PK names are valid.");
}
return Query<T>(map.GetByPrimaryKeySql, compositePK.Values.ToArray()).FirstOrDefault();
}
else
{
return Query<T>(map.GetByPrimaryKeySql, pk).FirstOrDefault();
}
}

/// <summary>
Expand Down Expand Up @@ -873,7 +924,7 @@ public T FindWithQuery<T>(string query, params object[] args) where T : class
/// the given type have a designated PrimaryKey (using the PrimaryKeyAttribute).
/// </summary>
/// <param name="pk">
/// The primary key.
/// The primary key. Needs to be Dictionary<string, object> if table has composite PK.
/// </param>
/// <param name="map">
/// The TableMapping used to identify the object type.
Expand All @@ -885,7 +936,24 @@ public T FindWithQuery<T>(string query, params object[] args) where T : class
[PublicAPI]
public object Find(object pk, TableMapping map)
{
return Query(map, map.GetByPrimaryKeySql, pk).FirstOrDefault();
if (map.HasCompositePK)
{
IDictionary<string, object> compositePK = pk as Dictionary<string, object>;
if (compositePK == null)
{
throw new NotSupportedException(map.TableName + " table has a composite primary key. Make sure primary key is passed in as Dictionary<string, object>.");
}
var cpk = map.CompositePK;
if (compositePK.Keys.Intersect(cpk.Select(p => p.Name)).Count() < cpk.Length)
{
throw new NotSupportedException("Cannot find in " + map.TableName + ": CompositePK mismatch. Make sure PK names are valid.");
}
return Query(map, map.GetByPrimaryKeySql, compositePK.Values.ToArray()).FirstOrDefault();
}
else
{
return Query(map, map.GetByPrimaryKeySql, pk).FirstOrDefault();
}
}

/// <summary>
Expand Down Expand Up @@ -1443,10 +1511,23 @@ public int Insert(object obj, string extra, Type objType)
}

var map = GetMapping(objType);
TableMapping.Column pk = null;

if (map.PK != null && map.PK.IsAutoGuid)
if (map.HasCompositePK)
{
var prop = objType.GetRuntimeProperty(map.PK.PropertyName);
pk = map.CompositePK.FirstOrDefault(p => p.IsAutoGuid);
}
else
{
if (map.PK != null && map.PK.IsAutoGuid)
{
pk = map.PK;
}
}

if (pk != null)
{
var prop = objType.GetRuntimeProperty(pk.PropertyName);
if (prop != null)
{
if (prop.GetValue(obj, null).Equals(Guid.Empty))
Expand Down Expand Up @@ -1551,29 +1632,57 @@ public int Update(object obj, Type objType)
}

var map = GetMapping(objType);
string q = null;
object[] ps = null;

if (map.HasCompositePK)
{
var compositePK = map.CompositePK;
var cols = from p in map.Columns
where !compositePK.Any(pk => pk == p)
select p;

var pslist = (from c in cols
select c.GetValue(obj)).ToList();

pslist.AddRange(compositePK.Select(pk => pk.GetValue(obj)));

var pk = map.PK;
q = string.Format("update \"{0}\" set {1} where {2}", map.TableName,
string.Join(",", (from c in cols
select "\"" + c.Name + "\" = ? ").ToArray()), string.Join(" and ", compositePK.Select(pk => "\"" + pk.Name + "\" = ? ")));

if (pk == null)
ps = pslist.ToArray();
}
else
{
throw new NotSupportedException("Cannot update " + map.TableName + ": it has no PK");
var pk = map.PK;

if (pk == null)
{
throw new NotSupportedException("Cannot update " + map.TableName + ": it has no PK");
}

var cols = from p in map.Columns
where p != pk
select p;

var vals = from c in cols
select c.GetValue(obj);
var pslist = new List<object>(vals)
{
pk.GetValue(obj)
};

q = string.Format("update \"{0}\" set {1} where {2} = ? ", map.TableName,
string.Join(",", (from c in cols
select "\"" + c.Name + "\" = ? ").ToArray()), pk.Name);

ps = pslist.ToArray();
}

var cols = from p in map.Columns
where p != pk
select p;
var vals = from c in cols
select c.GetValue(obj);
var ps = new List<object>(vals)
{
pk.GetValue(obj)
};
var q = string.Format("update \"{0}\" set {1} where {2} = ? ", map.TableName,
string.Join(",", (from c in cols
select "\"" + c.Name + "\" = ? ").ToArray()), pk.Name);
try
{
rowsAffected = Execute(q, ps.ToArray());
rowsAffected = Execute(q, ps);
}
catch (SQLiteException ex)
{
Expand Down Expand Up @@ -1635,20 +1744,35 @@ public int UpdateAll(IEnumerable objects, bool runInTransaction = true)
public int Delete(object objectToDelete)
{
var map = GetMapping(objectToDelete.GetType());
var pk = map.PK;
if (pk == null)
string q = null;
object[] ps = null;

if (map.HasCompositePK)
{
var compositePK = map.CompositePK;
q = string.Format("delete from \"{0}\" where {1}", map.TableName, string.Join(" and ", compositePK.Select(pk => "\"" + pk.Name + "\" = ? ")));
ps = (from pk in compositePK
select pk.GetValue(objectToDelete)).ToArray();
}
else
{
throw new NotSupportedException("Cannot delete " + map.TableName + ": it has no PK");
var pk = map.PK;
if (pk == null)
{
throw new NotSupportedException("Cannot delete " + map.TableName + ": it has no PK");
}
q = string.Format("delete from \"{0}\" where \"{1}\" = ?", map.TableName, pk.Name);
ps = new object[] { pk.GetValue(objectToDelete) };
}
var q = string.Format("delete from \"{0}\" where \"{1}\" = ?", map.TableName, pk.Name);
return Execute(q, pk.GetValue(objectToDelete));

return Execute(q, ps);
}

/// <summary>
/// Deletes the object with the specified primary key.
/// </summary>
/// <param name="primaryKey">
/// The primary key of the object to delete.
/// The primary key of the object to delete. Needs to be Dictionary<string, object> if table has composite PK.
/// </param>
/// <returns>
/// The number of objects deleted.
Expand All @@ -1660,13 +1784,34 @@ public int Delete(object objectToDelete)
public int Delete<T>(object primaryKey)
{
var map = GetMapping(typeof (T));
var pk = map.PK;
if (pk == null)

if (map.HasCompositePK)
{
throw new NotSupportedException("Cannot delete " + map.TableName + ": it has no PK");
var cpk = map.CompositePK;
IDictionary<string, object> compositePK = primaryKey as Dictionary<string, object>;
if (compositePK == null)
{
throw new NotSupportedException(map.TableName + " table has a composite primary key. Make sure primary key is passed in as Dictionary<string, object>.");
}
if (compositePK.Keys.Intersect(cpk.Select(p => p.Name)).Count() < cpk.Length)
{
throw new NotSupportedException("Cannot delete " + map.TableName + ": CompositePK mismatch. Make sure PK names are valid.");
}
var q = string.Format("delete from \"{0}\" where {1}", map.TableName, string.Join(" and ", compositePK.Keys.Select(pk => "\"" + pk + "\" = ? ")));
var ps = (from pk in compositePK.Values
select pk).ToArray();
return Execute(q, ps);
}
else
{
var pk = map.PK;
if (pk == null)
{
throw new NotSupportedException("Cannot delete " + map.TableName + ": it has no PK");
}
var q = string.Format("delete from \"{0}\" where \"{1}\" = ?", map.TableName, pk.Name);
return Execute(q, primaryKey);
}
var q = string.Format("delete from \"{0}\" where \"{1}\" = ?", map.TableName, pk.Name);
return Execute(q, primaryKey);
}

/// <summary>
Expand Down
Loading