Wednesday 24 March 2010

Customising NHibernate to work with Visual FoxPro

So far I have added a new class to create a dialect for VFP9 and altered some of the Nhibernate code to allow for generation of basic VFP tables.. Problems you might face include locking since NHibernate uses a SELECT ... FOR UPDATE to lock rows. Since I am using optimistic locking throughout I shall have to design the system without using LockMode.Upgrade. The VFP9Dialect class is below, if you have any improvements let me know!

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Text;
using System.Text.RegularExpressions;
using NHibernate.Dialect.Function;
using NHibernate.Dialect.Schema;
using NHibernate.Engine;
using NHibernate.Mapping;
using NHibernate.SqlCommand;
using NHibernate.Type;
using NHibernate.Util;
using Environment=NHibernate.Cfg.Environment;

namespace NHibernate.Dialect
{
///
/// An SQL dialect compatible with Microsoft Visual Foxpro 9.
///

///
/// The VFP9Dialect defaults the following configuration properties:
///
///
/// Property
/// Default Value
///

///
/// use_outer_join
///
///

///
/// connection.driver_class
///
///

///
/// prepare_sql
///
///

///

///

public class VFP9Dialect : Dialect
{
///
public VFP9Dialect()
{
// standard sql92 functions (can be overridden by subclasses)
RegisterFunction("substring", new SQLFunctionTemplate(NHibernateUtil.String, "substr(?1, ?2, ?3)"));
RegisterFunction("length", new StandardSQLFunction("len", NHibernateUtil.Int32));

// the syntax of current_timestamp is extracted from H3.2 tests
// - test\hql\ASTParserLoadingTest.java
// - test\org\hibernate\test\hql\HQLTest.java
RegisterFunction("current_timestamp", new NoArgSQLFunction("datetime()", NHibernateUtil.DateTime, true));
RegisterFunction("sysdate", new NoArgSQLFunction("date()", NHibernateUtil.DateTime, false));

//map second/minute/hour/day/month/year to ANSI extract(), override on subclasses
RegisterFunction("second", new SQLFunctionTemplate(NHibernateUtil.Int32, "sec(?1)"));
RegisterFunction("minute", new SQLFunctionTemplate(NHibernateUtil.Int32, "minute(?1)"));
RegisterFunction("hour", new SQLFunctionTemplate(NHibernateUtil.Int32, "hour(?1)"));
RegisterFunction("day", new SQLFunctionTemplate(NHibernateUtil.Int32, "day(?1)"));
RegisterFunction("month", new SQLFunctionTemplate(NHibernateUtil.Int32, "month(?1)"));
RegisterFunction("year", new SQLFunctionTemplate(NHibernateUtil.Int32, "year(?1)"));

RegisterColumnType(DbType.AnsiStringFixedLength, "C(254)");
RegisterColumnType(DbType.AnsiStringFixedLength, 254, "C($l)");
RegisterColumnType(DbType.AnsiString, "V(254)");
RegisterColumnType(DbType.AnsiString, 254, "V($l)");
RegisterColumnType(DbType.AnsiString, 2147483647, "M");
RegisterColumnType(DbType.Binary, "Q(254)");
RegisterColumnType(DbType.Binary, 254, "Q($l)");
RegisterColumnType(DbType.Binary, 2147483647, "W");
RegisterColumnType(DbType.Boolean, "L");
RegisterColumnType(DbType.Byte, "I");
RegisterColumnType(DbType.Currency, "Y");
RegisterColumnType(DbType.Date, "D");
RegisterColumnType(DbType.DateTime, "T");
RegisterColumnType(DbType.Decimal, "N(19,5)");
RegisterColumnType(DbType.Decimal, 19, "N($p, $s)");
RegisterColumnType(DbType.Double, "B"); //synonym for FLOAT(53)
RegisterColumnType(DbType.Guid, "C(40)");
RegisterColumnType(DbType.Int16, "I");
RegisterColumnType(DbType.Int32, "I");
RegisterColumnType(DbType.Int64, "I");
RegisterColumnType(DbType.Single, "B"); //synonym for FLOAT(24)
RegisterColumnType(DbType.StringFixedLength, "C(254)");
RegisterColumnType(DbType.StringFixedLength, 254, "C($l)");
RegisterColumnType(DbType.String, "V(254)");
RegisterColumnType(DbType.String, 254, "V($l)");
RegisterColumnType(DbType.String, 1073741823, "M");
RegisterColumnType(DbType.Time, "T");
}

public static Random random = new Random();

public override char OpenQuote
{
get { return ' '; }
}

///
/// The closing quote for a quoted identifier.
///

public override char CloseQuote
{
get { return ' '; }
}

public override string ForUpdateString
{
get { return string.Empty; }
}

public override bool QualifyIndexName
{
get { return false ; }
}

public override string QuoteForColumnName(string columnName)
{
return columnName;
}
public override bool SupportsOuterJoinForUpdate
{
get { return false; }
}

public override bool SupportsUniqueConstraintInCreateAlterTable
{
get { return false; }
}

///
/// Does this dialect support the UNIQUE column syntax?
///

public override bool SupportsUnique
{
get { return false ; }
}

public override bool SupportsColumnCheck
{
get { return false; }
}

/// Does this dialect support table-level check constraints?
/// True if table-level CHECK constraints are supported; false otherwise.
public override bool SupportsTableCheck
{
get { return false; }
}

public override bool DropConstraints
{
get { return false; }
}

public override bool SupportsCascadeDelete
{
get { return false; }
}

public override bool SupportsNotNullUnique
{
get { return false; }
}

public override bool SupportsLimit
{
get { return false; }
}
public override string PrimaryKeyOpenParen
{
get { return ""; }
}
public override string PrimaryKeyCloseParen
{
get {
return " TAG PK" + random.Next(10000000, 99999999).ToString();
}
}

public override string GetAddForeignKeyConstraintString(string constraintName, string[] foreignKey,
string referencedTable, string[] primaryKey, bool referencesPrimaryKey)
{
var res = new StringBuilder(200);

res.Append(" add foreign key ")
.Append(StringHelper.Join(StringHelper.CommaSpace, foreignKey))
.Append(" TAG FK")
.Append(random.Next(10000000, 99999999).ToString())
.Append(" references ")
.Append(referencedTable);

if (!referencesPrimaryKey)
{
res.Append(" TAG ")
.Append(StringHelper.Join(StringHelper.CommaSpace, primaryKey));
}

return res.ToString();
}

public override string GetAddPrimaryKeyConstraintString(string constraintName)
{
return " add primary key ";
}
public override string DropForeignKeyString
{
get { return " drop Foreign Key Tag "; }
}

}
}

No comments:

Post a Comment