PostgresDBTraits.CheckConstraint, GetColumnOptions and GetTableOptions implemented; ReferentialAction.Restrict implemented

main
Inga 🏳‍🌈 13 years ago
parent 426f687b51
commit ca126e368a
  1. 2
      Builder/IISMainHandler/build.txt
  2. 1
      Patcher/DB/ForeignKeyConstraint.cs
  3. 148
      Patcher/DB/PostgresDBTraits.cs
  4. 3
      Patcher/DB/SQLQueryManager.cs
  5. 1
      Patcher/Data/Patch/AtomicPatch.cs
  6. 2
      Patcher/Data/XMLParser.cs
  7. 3
      Patcher/Resources/IPatch.xsd

@ -24,6 +24,7 @@ namespace Patcher.DB
Cascade,
SetNull,
SetDefault,
Restrict,
}
public readonly string column;

@ -176,67 +176,161 @@ namespace Patcher.DB {
private static string GetStringRepresentation(ForeignKeyConstraint.ReferentialAction action) {
switch(action) {
case ForeignKeyConstraint.ReferentialAction.NoAction:
return "NO ACTION";
return "a";
case ForeignKeyConstraint.ReferentialAction.Cascade:
return "CASCADE";
return "c";
case ForeignKeyConstraint.ReferentialAction.SetNull:
return "SET NULL";
return "n";
case ForeignKeyConstraint.ReferentialAction.SetDefault:
return "SET DEFAULT";
return "d";
case ForeignKeyConstraint.ReferentialAction.Restrict:
return "r";
default:
throw new ApplicationException("Unknown referential action");
}
}
private void CheckConstraint(Func<DbCommand> commandCreator, ForeignKeyConstraint constraint) {
Int64 referencedTableOid;
Int16[] attnums;
Int16[] fattnums;
using(DbCommand command = commandCreator()) {
command.CommandText = string.Format("\\d {0}", _EscapeName(constraint.name));
command.CommandText = "select c.* from pg_constraint c join pg_class r on c.conrelid = r.oid where r.relname = :ptable and c.conname = :pconstraint";
AddParam(command, "ptable", DbType.String, constraint.table);
AddParam(command, "pconstraint", DbType.String, constraint.name);
using(var reader = ExecuteReader(command)) {
int row = 0;
while(reader.Read()) {
Logger.instance.Log("Row #" + row);
for(int j=0; j<reader.FieldCount; j++) {
Logger.instance.Log(reader.GetName(j) + "='" + reader.GetValue(j) + "'");
if(!reader.Read()) {
throw new FormattableException("Constraint {0}.{1} not found", constraint.table, constraint.name);
}
row++;
if(reader.GetValue("contype").ToString() != "f") {
throw new FormattableException("Constraint {0}.{1} wrong type: expected {2}, got {3}", constraint.table, constraint.name, "C", reader.GetValue("contype"));
}
if(reader.GetValue("confdeltype").ToString() != GetStringRepresentation(constraint.onDelete)) {
throw new FormattableException("Constraint {0}.{1} wrong on delete action: expected {2}, got {3}", constraint.table, constraint.name, GetStringRepresentation(constraint.onDelete), reader.GetValue("confdeltype"));
}
if(reader.GetValue("confupdtype").ToString() != GetStringRepresentation(constraint.onUpdate)) {
throw new FormattableException("Constraint {0}.{1} wrong on update action: expected {2}, got {3}", constraint.table, constraint.name, GetStringRepresentation(constraint.onUpdate), reader.GetValue("confupdtype"));
}
attnums = (Int16[])reader.GetValue("conkey");
fattnums = (Int16[])reader.GetValue("confkey");
referencedTableOid = (Int64)reader.GetValue("confrelid");
}
if(attnums.Length != 1 || fattnums.Length != 1) {
throw new FormattableException("More than one field participating in {0}.{1}", constraint.table, constraint.name);
}
}
using(DbCommand command = commandCreator()) {
command.CommandText = "select c.*, r.relname relname from pg_constraint c join pg_class r on c.conrelid = r.oid where r.oid = :prelid and c.contype = :ptype";
AddParam(command, "prelid", DbType.Int64, referencedTableOid);
AddParam(command, "ptype", DbType.String, "p");
using(var reader = ExecuteReader(command)) {
reader.Read();
if(reader.GetValue("relname").ToString() != constraint.referencedTable) {
throw new FormattableException("Constraint {0}.{1} wrong referenced table: expected {2}, got {3}", constraint.table, constraint.name, reader.GetValue("relname"), constraint.referencedTable);
}
Int16[] rattnums = (Int16[])reader.GetValue("conkey");
if(rattnums.Length != 1) {
throw new FormattableException("Referenced table {0} primary key consists of {1} columns", constraint.referencedTable, rattnums.Length);
}
if(rattnums[0] != fattnums[0]) {
throw new FormattableException("Referenced column {2} for constraint {0}.{1} differs from column {3} used in a primary key of referenced table", constraint.table, constraint.name, fattnums[0], rattnums[0]);
}
}
}
using(DbCommand command = commandCreator()) {
command.CommandText = "SELECT a.attnum, a.attname AS field, t.typname AS type, a.attlen AS length, a.atttypmod AS lengthvar, a.attnotnull AS notnull, d.adsrc AS defaultvalue FROM pg_attribute a JOIN pg_class c ON a.attrelid = c.oid JOIN pg_type t ON a.atttypid = t.oid LEFT JOIN pg_attrdef d ON c.oid = d.adrelid AND a.attnum = d.adnum WHERE c.relname = :ptable and a.attnum = :pattnum";
AddParam(command, "ptable", DbType.String, constraint.table);
AddParam(command, "pattnum", DbType.Int16, attnums[0]);
using(var reader = ExecuteReader(command)) {
reader.Read();
if(reader.GetValue("field").ToString() != constraint.column) {
throw new FormattableException("Constraint {0}.{1} wrong column: expected {2}, got {3}", constraint.table, constraint.name, constraint.column, reader.GetValue("field"));
}
}
}
throw new NotImplementedException();
}
private void CheckConstraint(Func<DbCommand> commandCreator, UniqueConstraint constraint) {
Int16[] attnums = null;
using(DbCommand command = commandCreator()) {
command.CommandText = "select c.* from pg_constraint c join pg_class r on c.conrelid = r.oid where r.relname = :ptable and c.conname = :pconstraint";
AddParam(command, "ptable", DbType.String, constraint.table);
AddParam(command, "pconstraint", DbType.String, constraint.name);
using(var reader = ExecuteReader(command)) {
if(!reader.Read()) {
throw new FormattableException("Constraint {0}.{1} not found", constraint.table, constraint.name);
}
if(reader.GetValue("contype").ToString() != "u") {
throw new FormattableException("Constraint {0}.{1} wrong type: expected {2}, got {3}", constraint.table, constraint.name, "C", reader.GetValue("contype"));
}
attnums = (Int16[])reader.GetValue("conkey");
}
}
using(DbCommand command = commandCreator()) {
command.CommandText = string.Format("\\d {0}", _EscapeName(constraint.name));
command.CommandText = string.Format("SELECT a.attnum, a.attname AS field, t.typname AS type, a.attlen AS length, a.atttypmod AS lengthvar, a.attnotnull AS notnull, d.adsrc AS defaultvalue FROM pg_attribute a JOIN pg_class c ON a.attrelid = c.oid JOIN pg_type t ON a.atttypid = t.oid LEFT JOIN pg_attrdef d ON c.oid = d.adrelid AND a.attnum = d.adnum WHERE c.relname = :ptable and a.attnum in ({0})", string.Join(", ", (from i in Enumerable.Range(0, attnums.Length) select ":pattnum" + i).ToArray()));
AddParam(command, "ptable", DbType.String, constraint.table);
for(int i=0; i<attnums.Length; i++) {
AddParam(command, "pattnum" + i, DbType.Int16, attnums[i]);
}
using(var reader = ExecuteReader(command)) {
int row = 0;
HashSet<string> dbColumns = new HashSet<string>();
while(reader.Read()) {
Logger.instance.Log("Row #" + row);
for(int j=0; j<reader.FieldCount; j++) {
Logger.instance.Log(reader.GetName(j) + "='" + reader.GetValue(j) + "'");
dbColumns.Add(reader.GetValue("field").ToString());
}
row++;
if(!dbColumns.IsSubsetOf(constraint.columns)) {
throw new FormattableException("Some columns are not mentioned in constraint definition: {0}", string.Join(",", dbColumns.Except(constraint.columns).ToArray()));
}
if(!dbColumns.IsSupersetOf(constraint.columns)) {
throw new FormattableException("Some columns are missed in DB: {0}", string.Join(",", constraint.columns.Except(dbColumns).ToArray()));
}
}
}
throw new NotImplementedException();
}
private void CheckConstraint(Func<DbCommand> commandCreator, CheckConstraint constraint) {
using(DbCommand command = commandCreator()) {
command.CommandText = string.Format("\\d {0}", _EscapeName(constraint.name));
command.CommandText = "select c.* from pg_constraint c join pg_class r on c.conrelid = r.oid where r.relname = :ptable and c.conname = :pconstraint";
AddParam(command, "ptable", DbType.String, constraint.table);
AddParam(command, "pconstraint", DbType.String, constraint.name);
using(var reader = ExecuteReader(command)) {
int row = 0;
while(reader.Read()) {
Logger.instance.Log("Row #" + row);
for(int j=0; j<reader.FieldCount; j++) {
Logger.instance.Log(reader.GetName(j) + "='" + reader.GetValue(j) + "'");
if(!reader.Read()) {
throw new FormattableException("Constraint {0}.{1} not found", constraint.table, constraint.name);
}
row++;
if(reader.GetValue("contype").ToString() != "c") {
throw new FormattableException("Constraint {0}.{1} wrong type: expected {2}, got {3}", constraint.table, constraint.name, "C", reader.GetValue("contype"));
}
if(reader.GetValue("consrc").ToString() != constraint.condition) {
throw new FormattableException("Constraint {0}.{1} wrong condition: expected {2}, got {3}", constraint.table, constraint.name, constraint.condition, reader.GetValue("consrc"));
}
}
}
throw new NotImplementedException();
}
private void CheckConstraint(Func<DbCommand> commandCreator, AbstractConstraint constraint) {

@ -95,6 +95,7 @@ namespace Patcher.DB
/// <summary>
/// SQL-92, page 274
/// SQL:1999 added a support for ON DELETE RESTRICT, and both Postgres and Oracle support it in their current versions
/// </summary>
private static string _ReferentialAction(ForeignKeyConstraint.ReferentialAction action)
{
@ -108,6 +109,8 @@ namespace Patcher.DB
return "SET NULL";
case ForeignKeyConstraint.ReferentialAction.SetDefault:
return "SET DEFAULT";
case ForeignKeyConstraint.ReferentialAction.Restrict:
return "RESTRICT";
default:
throw new ApplicationException(string.Format("Unknown action {0}", action));
}

@ -51,6 +51,7 @@ namespace Patcher.Data.Patch
}
rollbackInfos.Add(new XElement("command", new XAttribute("num", i), commandRollbackInfoContent));
}
throw new ApplicationException("safety switch");
return new XDocument(new XElement("rollbackInfo", rollbackInfos.ToArray()));
}

@ -25,6 +25,8 @@ namespace Patcher.Data
return ForeignKeyConstraint.ReferentialAction.SetNull;
case "setDefault":
return ForeignKeyConstraint.ReferentialAction.SetDefault;
case "restrict":
return ForeignKeyConstraint.ReferentialAction.Restrict;
default:
throw new FormattableException("Unknown referential action {0}", element.Value);
}

@ -15,7 +15,7 @@
<xs:simpleType name="dbName">
<xs:restriction base="longDbName">
<xs:pattern value=".{2,30}"/>
<xs:pattern value=".{2,300}"/>
</xs:restriction>
</xs:simpleType>
@ -38,6 +38,7 @@
<xs:enumeration value="cascade"/>
<xs:enumeration value="setNull"/>
<xs:enumeration value="setDefault"/>
<xs:enumeration value="restrict"/>
</xs:restriction>
</xs:simpleType>

Loading…
Cancel
Save