|
From: <dav...@us...> - 2008-12-03 19:45:15
|
Revision: 3942
http://nhibernate.svn.sourceforge.net/nhibernate/?rev=3942&view=rev
Author: davybrion
Date: 2008-12-03 19:45:12 +0000 (Wed, 03 Dec 2008)
Log Message:
-----------
included patch from Robert Sosnowski for NH-1592
Modified Paths:
--------------
trunk/nhibernate/src/NHibernate/Dialect/InformixDialect.cs
trunk/nhibernate/src/NHibernate/NHibernate.csproj
Added Paths:
-----------
trunk/nhibernate/src/NHibernate/Dialect/InformixDialect0940.cs
trunk/nhibernate/src/NHibernate/Dialect/InformixDialect1000.cs
trunk/nhibernate/src/NHibernate/Exceptions/ReflectionBasedSqlStateExtracter.cs
trunk/nhibernate/src/NHibernate/Exceptions/SqlStateExtracter.cs
trunk/nhibernate/src/NHibernate/Exceptions/TemplatedViolatedConstraintNameExtracter.cs
trunk/nhibernate/src/NHibernate/SqlCommand/InformixJoinFragment.cs
Modified: trunk/nhibernate/src/NHibernate/Dialect/InformixDialect.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Dialect/InformixDialect.cs 2008-11-30 20:32:19 UTC (rev 3941)
+++ trunk/nhibernate/src/NHibernate/Dialect/InformixDialect.cs 2008-12-03 19:45:12 UTC (rev 3942)
@@ -1,101 +1,490 @@
using System.Data;
-using NHibernate.Cfg;
+using NHibernate.Dialect.Function;
+using NHibernate.SqlCommand;
+using System.Data.Common;
+using NHibernate.Exceptions;
+//using NHibernate.Dialect.Schema;
+using Environment = NHibernate.Cfg.Environment;
+
namespace NHibernate.Dialect
{
- /// <summary>
- /// Summary description for InformixDialect.
- /// </summary>
- /// <remarks>
- /// The InformixDialect defaults the following configuration properties:
- /// <list type="table">
- /// <listheader>
- /// <term>Property</term>
- /// <description>Default Value</description>
- /// </listheader>
- /// <item>
- /// <term>connection.driver_class</term>
- /// <description><see cref="NHibernate.Driver.OdbcDriver" /></description>
- /// </item>
- /// </list>
- /// </remarks>
- public class InformixDialect : Dialect
- {
- /// <summary></summary>
- public InformixDialect() : base()
- {
-// registerFunction( "concat", new VarArgsSQLFunction( Hibernate.STRING, "(", "||", ")" ) );
+ /// <summary>
+ /// Summary description for InformixDialect.
+ /// This dialect is intended to work with IDS version 7.31
+ /// However I can test only version 10.00 as I have only this version at work
+ /// </summary>
+ /// <remarks>
+ /// The InformixDialect defaults the following configuration properties:
+ /// <list type="table">
+ /// <listheader>
+ /// <term>ConnectionDriver</term>
+ /// <description>NHibernate.Driver.OdbcDriver</description>
+ /// <term>PrepareSql</term>
+ /// <description>true</description>
+ /// </listheader>
+ /// <item>
+ /// <term>connection.driver_class</term>
+ /// <description><see cref="NHibernate.Driver.OdbcDriver" /></description>
+ /// </item>
+ /// </list>
+ /// </remarks>
+ public class InformixDialect : Dialect
+ {
+ /// <summary></summary>
+ public InformixDialect()
+ : base()
+ {
+ RegisterColumnType(DbType.AnsiStringFixedLength, "CHAR($l)");
+ RegisterColumnType(DbType.AnsiString, 255, "VARCHAR($l)");
+ RegisterColumnType(DbType.AnsiString, 32739, "LVARCHAR($l)");
+ RegisterColumnType(DbType.AnsiString, 2147483647, "TEXT");
+ RegisterColumnType(DbType.AnsiString, "VARCHAR(255)");
+ RegisterColumnType(DbType.Binary, 2147483647, "BYTE");
+ RegisterColumnType(DbType.Binary, "BYTE");
+ RegisterColumnType(DbType.Boolean, "BOOLEAN");
+ RegisterColumnType(DbType.Currency, "DECIMAL(16,4)");
+ RegisterColumnType(DbType.Byte, "SMALLINT");
+ RegisterColumnType(DbType.Date, "DATE");
+ RegisterColumnType(DbType.DateTime, "datetime year to fraction(5)");
+ RegisterColumnType(DbType.Decimal, "DECIMAL(19, 5)");
+ RegisterColumnType(DbType.Decimal, 19, "DECIMAL($p, $s)");
+ RegisterColumnType(DbType.Double, "DOUBLE");
+ RegisterColumnType(DbType.Int16, "SMALLINT");
+ RegisterColumnType(DbType.Int32, "INTEGER");
+ RegisterColumnType(DbType.Int64, "BIGINT");
+ RegisterColumnType(DbType.Single, "SmallFloat");
+ RegisterColumnType(DbType.Time, "datetime hour to second");
+ RegisterColumnType(DbType.StringFixedLength, "CHAR($l)");
+ RegisterColumnType(DbType.String, 255, "VARCHAR($l)");
+ RegisterColumnType(DbType.String, 32739, "LVARCHAR($l)");
+ RegisterColumnType(DbType.String, 2147483647, "TEXT");
+ RegisterColumnType(DbType.String, "VARCHAR(255)");
- RegisterColumnType(DbType.AnsiStringFixedLength, "CHAR($l)");
- RegisterColumnType(DbType.AnsiString, 255, "VARCHAR($l)");
- RegisterColumnType(DbType.AnsiString, 32739, "LVARCHAR($l)");
- RegisterColumnType(DbType.AnsiString, 2147483647, "CLOB");
- RegisterColumnType(DbType.AnsiString, "VARCHAR(255)");
- RegisterColumnType(DbType.Binary, 2147483647, "BLOB");
- RegisterColumnType(DbType.Binary, "BLOB");
- RegisterColumnType(DbType.Byte, "SMALLINT");
- RegisterColumnType(DbType.Date, "DATE");
- RegisterColumnType(DbType.DateTime, "datetime year to fraction(5)");
- RegisterColumnType(DbType.Decimal, "DECIMAL(19,5)");
- RegisterColumnType(DbType.Decimal, 19, "DECIMAL(19, $l)");
- RegisterColumnType(DbType.Double, "DOUBLE");
- RegisterColumnType(DbType.Int16, "SMALLINT");
- RegisterColumnType(DbType.Int32, "INTEGER");
- RegisterColumnType(DbType.Int64, "BIGINT");
- RegisterColumnType(DbType.Single, "SmallFloat");
- RegisterColumnType(DbType.Time, "datetime hour to second");
- RegisterColumnType(DbType.StringFixedLength, "CHAR($l)");
- RegisterColumnType(DbType.String, 255, "VARCHAR($l)");
- RegisterColumnType(DbType.String, 32739, "LVARCHAR($l)");
- RegisterColumnType(DbType.String, 2147483647, "CLOB");
- RegisterColumnType(DbType.String, "VARCHAR(255)");
+ RegisterFunction("substring", new AnsiSubstringFunction()); // base class override
+ RegisterFunction("substr", new StandardSQLFunction("substr"));
+ // RegisterFunction("trim", new AnsiTrimFunction()); // defined in base class
+ // RegisterFunction("length", new StandardSQLFunction("length", NHibernateUtil.Int32)); // defined in base class
+ RegisterFunction("coalesce", new NvlFunction()); // base class override
+ // RegisterFunction("abs", new StandardSQLFunction("abs"));
+ // RegisterFunction("mod", new StandardSQLFunction("mod", NHibernateUtil.Int32));
+ // RegisterFunction("sqrt", new StandardSQLFunction("sqrt", NHibernateUtil.Double));
+ // RegisterFunction("upper", new StandardSQLFunction("upper"));
+ // RegisterFunction("lower", new StandardSQLFunction("lower"));
+ // RegisterFunction("cast", new CastFunction());
+ // RegisterFunction("concat", new VarArgsSQLFunction(NHibernateUtil.String, "(", "||", ")"));
+ RegisterFunction("current_timestamp", new NoArgSQLFunction("current", NHibernateUtil.DateTime, false));
+ RegisterFunction("sysdate", new NoArgSQLFunction("today", NHibernateUtil.DateTime, false));
+ RegisterFunction("current", new NoArgSQLFunction("current", NHibernateUtil.DateTime, false));
+ RegisterFunction("today", new NoArgSQLFunction("today", NHibernateUtil.DateTime, false));
+ RegisterFunction("day", new StandardSQLFunction("day", NHibernateUtil.Int32));
+ RegisterFunction("month", new StandardSQLFunction("month", NHibernateUtil.Int32));
+ RegisterFunction("year", new StandardSQLFunction("year", NHibernateUtil.Int32));
+ RegisterFunction("date", new StandardSQLFunction("date", NHibernateUtil.DateTime));
+ RegisterFunction("mdy", new SQLFunctionTemplate(NHibernateUtil.DateTime, "mdy(?1, ?2, ?3)"));
+ RegisterFunction("to_char", new StandardSQLFunction("to_char", NHibernateUtil.String));
+ RegisterFunction("to_date", new StandardSQLFunction("to_date", NHibernateUtil.Timestamp));
+ RegisterFunction("instr", new StandardSQLFunction("instr", NHibernateUtil.String));
+ // actually there is no Instr (or equivalent) in Informix; you have to write your own SPL or UDR
- DefaultProperties[Environment.ConnectionDriver] = "NHibernate.Driver.OdbcDriver";
- }
+ DefaultProperties[Environment.ConnectionDriver] = "NHibernate.Driver.OdbcDriver";
+ DefaultProperties[Environment.PrepareSql] = "true";
+ }
- /// <summary></summary>
- public override string AddColumnString
- {
- get { return "add"; }
- }
+ /// <summary>
+ /// The keyword used to insert a generated value into an identity column (or null).
+ /// Need if the dialect does not support inserts that specify no column values.
+ /// </summary>
+ public override string IdentityInsertString
+ {
+ get { return "0"; }
+ }
- public override bool SupportsIdentityColumns
- {
- get { return true; }
- }
+ /// <summary> Command used to create a temporary table. </summary>
+ public override string CreateTemporaryTableString
+ {
+ get { return "create temp table"; }
+ }
- /// <summary>
- /// The syntax that returns the identity value of the last insert, if native
- /// key generation is supported
- /// </summary>
- public override string IdentitySelectString
- {
-// return type==Types.BIGINT ?
-// "select dbinfo('serial8') from systables where tabid=1" :
-// "select dbinfo('sqlca.sqlerrd1') from systables where tabid=1";
- get { return "select dbinfo('sqlca.sqlerrd1') from systables where tabid=1"; }
- }
+ /// <summary>
+ /// Get any fragments needing to be postfixed to the command for
+ /// temporary table creation.
+ /// </summary>
+ public override string CreateTemporaryTablePostfix
+ {
+ get { return "with no log"; }
+ }
+ /// <summary>
+ /// Should the value returned by <see cref="CurrentTimestampSelectString"/>
+ /// be treated as callable. Typically this indicates that JDBC escape
+ /// sytnax is being used...
+ /// </summary>
+ public override bool IsCurrentTimestampSelectStringCallable
+ {
+ get { return true; }
+ }
- /// <summary>
- /// The keyword used to specify an identity column, if native key generation is supported
- /// </summary>
- public override string IdentityColumnString
- {
-// return type==Types.BIGINT ?
-// "serial8 not null" :
-// "serial not null";
- get { return "serial not null"; }
- }
+ /// <summary>
+ /// Retrieve the command used to retrieve the current timestammp from the database.
+ /// </summary>
+ public override string CurrentTimestampSelectString
+ {
+ get { return "select current from systables where tabid=1"; }
+ }
- /// <summary>
- /// Whether this dialect have an Identity clause added to the data type or a
- /// completely seperate identity data type
- /// </summary>
- public override bool HasDataTypeInIdentityColumn
- {
- get { return false; }
- }
- }
+ /// <summary>
+ /// The name of the database-specific SQL function for retrieving the
+ /// current timestamp.
+ /// </summary>
+ public override string CurrentTimestampSQLFunctionName
+ {
+ get { return "current"; }
+ }
+
+ public override IViolatedConstraintNameExtracter ViolatedConstraintNameExtracter
+ {
+ get { return new IfxViolatedConstraintExtracter(); }
+ }
+
+ /// <summary></summary>
+ public override string AddColumnString
+ {
+ get { return "add"; }
+ }
+
+ //public override IDataBaseSchema GetDataBaseSchema(DbConnection connection)
+ //{
+ // if (connection.ToString()=="IBM.Data.Informix.IfxConnection") {
+ // // this driver doesn't have working IfxConnection.GetSchema
+ // // and probably will never have
+ // throw new NotSupportedException();
+ // }
+ // if (connection.ToString()=="System.Data.Odbc.OdbcConnection") {
+ // // waiting for somebody implementing OdbcBaseSchema
+ // // return new OdbcBaseSchema(connection);
+ // }
+ // if (connection.ToString()=="IBM.Data.DB2.IfxConnection") {
+ // // waiting for somebody implementing DB2BaseSchema
+ // return new DB2BaseSchema(connection);
+ // }
+ // throw new NotSupportedException();
+ //}
+
+ /// <summary> Is <tt>FOR UPDATE OF</tt> syntax supported? </summary>
+ /// <value> True if the database supports <tt>FOR UPDATE OF</tt> syntax; false otherwise. </value>
+ public override bool ForUpdateOfColumns
+ {
+ get { return true; }
+ }
+
+ /// <summary>
+ /// Does this dialect support <tt>FOR UPDATE</tt> in conjunction with outer joined rows?
+ /// </summary>
+ /// <value> True if outer joined rows can be locked via <tt>FOR UPDATE</tt>. </value>
+ public override bool SupportsOuterJoinForUpdate
+ {
+ get { return false; }
+ }
+
+ /// <summary>
+ /// Get the <tt>FOR UPDATE OF column_list</tt> fragment appropriate for this
+ /// dialect given the aliases of the columns to be write locked.
+ /// </summary>
+ /// <param name="aliases">The columns to be write locked. </param>
+ /// <returns> The appropriate <tt>FOR UPDATE OF column_list</tt> clause string. </returns>
+ public override string GetForUpdateString(string aliases)
+ {
+ return ForUpdateString + " of " + aliases;
+ }
+
+ /// <summary> Does this dialect support temporary tables? </summary>
+ public override bool SupportsTemporaryTables
+ {
+ get { return true; }
+ }
+
+ /// <summary>
+ /// Does the dialect require that temporary table DDL statements occur in
+ /// isolation from other statements? This would be the case if the creation
+ /// would cause any current transaction to get committed implicitly.
+ /// </summary>
+ /// <returns> see the result matrix above. </returns>
+ /// <remarks>
+ /// JDBC defines a standard way to query for this information via the
+ /// {@link java.sql.DatabaseMetaData#dataDefinitionCausesTransactionCommit()}
+ /// method. However, that does not distinguish between temporary table
+ /// DDL and other forms of DDL; MySQL, for example, reports DDL causing a
+ /// transaction commit via its driver, even though that is not the case for
+ /// temporary table DDL.
+ /// <p/>
+ /// Possible return values and their meanings:<ul>
+ /// <li>{@link Boolean#TRUE} - Unequivocally, perform the temporary table DDL in isolation.</li>
+ /// <li>{@link Boolean#FALSE} - Unequivocally, do <b>not</b> perform the temporary table DDL in isolation.</li>
+ /// <li><i>null</i> - defer to the JDBC driver response in regards to {@link java.sql.DatabaseMetaData#dataDefinitionCausesTransactionCommit()}</li>
+ /// </ul>
+ /// </remarks>
+ public override bool? PerformTemporaryTableDDLInIsolation()
+ {
+ return false;
+ }
+
+ public override int RegisterResultSetOutParameter(DbCommand statement, int position)
+ {
+ return position;
+ }
+
+ public override DbDataReader GetResultSet(DbCommand statement)
+ {
+ return statement.ExecuteReader(CommandBehavior.SingleResult);
+ }
+
+ /// <summary> Does this dialect support a way to retrieve the database's current timestamp value? </summary>
+ public override bool SupportsCurrentTimestampSelection
+ {
+ get { return true; }
+ }
+
+ public override long TimestampResolutionInTicks
+ {
+ get { return 100L; } // Maximum precision (one tick)
+ }
+
+ public override bool SupportsIdentityColumns
+ {
+ get { return true; }
+ }
+
+ /// <summary>
+ /// Whether this dialect have an Identity clause added to the data type or a
+ /// completely seperate identity data type
+ /// </summary>
+ public override bool HasDataTypeInIdentityColumn
+ {
+ get { return false; }
+ }
+
+ /// <summary>
+ /// Get the select command to use to retrieve the last generated IDENTITY
+ /// value for a particular table
+ /// </summary>
+ /// <param name="tableName">The table into which the insert was done </param>
+ /// <param name="identityColumn">The PK column. </param>
+ /// <param name="type">The <see cref="DbType"/> type code. </param>
+ /// <returns> The appropriate select command </returns>
+ public override string GetIdentitySelectString(string identityColumn, string tableName, DbType type)
+ {
+ return type == DbType.Int64 ?
+ "select dbinfo('serial8') from systables where tabid=1" :
+ "select dbinfo('sqlca.sqlerrd1') from systables where tabid=1";
+ }
+
+ /// <summary>
+ /// The syntax that returns the identity value of the last insert, if native
+ /// key generation is supported
+ /// </summary>
+ public override string IdentitySelectString
+ {
+ // return type==Types.BIGINT ?
+ // "select dbinfo('serial8') from systables where tabid=1" :
+ // "select dbinfo('sqlca.sqlerrd1') from systables where tabid=1";
+ get { return "select dbinfo('sqlca.sqlerrd1') from systables where tabid=1"; }
+ }
+
+ /// <summary>
+ /// The syntax used during DDL to define a column as being an IDENTITY of
+ /// a particular type.
+ /// </summary>
+ /// <param name="type">The <see cref="DbType"/> type code. </param>
+ /// <returns> The appropriate DDL fragment. </returns>
+ public override string GetIdentityColumnString(DbType type)
+ {
+ return type == DbType.Int64 ?
+ "serial8 not null" :
+ "serial not null";
+ }
+
+ /// <summary>
+ /// The keyword used to specify an identity column, if native key generation is supported
+ /// </summary>
+ public override string IdentityColumnString
+ {
+ get { return "serial not null"; }
+ }
+
+ /// <summary>
+ /// Does this dialect support sequences?
+ /// </summary>
+ public override bool SupportsSequences
+ {
+ get { return false; }
+ }
+
+ /// <summary>
+ /// Create a <see cref="JoinFragment"/> strategy responsible
+ /// for handling this dialect's variations in how joins are handled.
+ /// </summary>
+ /// <returns> This dialect's <see cref="JoinFragment"/> strategy. </returns>
+ public override JoinFragment CreateOuterJoinFragment()
+ {
+ return new InformixJoinFragment();
+ }
+
+ /// <summary> The SQL literal value to which this database maps boolean values. </summary>
+ /// <param name="value">The boolean value </param>
+ /// <returns> The appropriate SQL literal. </returns>
+ public override string ToBooleanValueString(bool value)
+ {
+ return value ? "t" : "f";
+ }
+ /// <summary>
+ /// Does this Dialect have some kind of <c>LIMIT</c> syntax?
+ /// </summary>
+ /// <value>False, unless overridden.</value>
+ public override bool SupportsLimit
+ {
+ // select first * is supported since 7.31
+ // but it works with unions since 10.00
+ // so it is safer to regard that it is not supported
+ get { return false; }
+ }
+
+ /// <summary>
+ /// Does this Dialect support an offset?
+ /// </summary>
+ public override bool SupportsLimitOffset
+ {
+ get { return false; }
+ }
+ /// <summary>
+ /// Can parameters be used for a statement containing a LIMIT?
+ /// </summary>
+ public override bool SupportsVariableLimit
+ {
+ get { return false; }
+ }
+ /// <summary>
+ /// Does the <c>LIMIT</c> clause come at the start of the
+ /// <c>SELECT</c> statement rather than at the end?
+ /// </summary>
+ /// <value>false, unless overridden</value>
+ public override bool BindLimitParametersFirst
+ {
+ get { return true; }
+ }
+
+
+ /// <summary> Apply s limit clause to the query. </summary>
+ /// <param name="querySqlString">The query to which to apply the limit. </param>
+ /// <param name="hasOffset">Is the query requesting an offset? </param>
+ /// <returns> the modified SQL </returns>
+ /// <remarks>
+ /// Typically dialects utilize <see cref="SupportsVariableLimit"/>
+ /// limit caluses when they support limits. Thus, when building the
+ /// select command we do not actually need to know the limit or the offest
+ /// since we will just be using placeholders.
+ /// <p/>
+ /// Here we do still pass along whether or not an offset was specified
+ /// so that dialects not supporting offsets can generate proper exceptions.
+ /// In general, dialects will override one or the other of this method and
+ /// <see cref="GetLimitString(SqlString,int,int)"/>.
+ /// </remarks>
+ public override SqlString GetLimitString(SqlString querySqlString, int offset, int limit)
+ {
+ /*
+ * "SELECT [SKIP x] FIRST y rest-of-sql-statement"
+ */
+
+ int insertIndex = GetAfterSelectInsertPoint(querySqlString);
+
+ if (offset > 0)
+ {
+ return querySqlString.Insert(insertIndex, " skip " + offset + " first " + limit);
+ }
+
+ return querySqlString.Insert(insertIndex, " first " + limit);
+ }
+ /// <summary>
+ /// Does this dialect support UNION ALL, which is generally a faster variant of UNION?
+ /// True if UNION ALL is supported; false otherwise.
+ /// </summary>
+ public override bool SupportsUnionAll
+ {
+ get { return true; }
+ }
+
+ public override bool SupportsEmptyInList
+ {
+ get { return false; }
+ }
+
+ public override bool SupportsResultSetPositionQueryMethodsOnForwardOnlyCursor
+ {
+ get { return false; }
+ }
+
+ public override bool DoesRepeatableReadCauseReadersToBlockWriters
+ {
+ get { return true; }
+ }
+
+ public override ISQLExceptionConverter BuildSQLExceptionConverter()
+ {
+ // The default SQLExceptionConverter for all dialects is based on SQLState
+ // since SQLErrorCode is extremely vendor-specific. Specific Dialects
+ // may override to return whatever is most appropriate for that vendor.
+ return new SQLStateConverter(ViolatedConstraintNameExtracter);
+ }
+
+ private static int GetAfterSelectInsertPoint(SqlString text)
+ {
+ if (text.StartsWithCaseInsensitive("select"))
+ {
+ return 6;
+ }
+
+ return -1;
+ }
+ }
+
+ public class IfxViolatedConstraintExtracter : TemplatedViolatedConstraintNameExtracter
+ {
+ /// <summary>
+ /// Extract the name of the violated constraint from the given DbException.
+ /// </summary>
+ /// <param name="sqle">The exception that was the result of the constraint violation.</param>
+ /// <returns>The extracted constraint name.</returns>
+ public override string ExtractConstraintName(DbException sqle)
+ {
+ string constraintName = null;
+
+ ReflectionBasedSqlStateExtracter extracter = new ReflectionBasedSqlStateExtracter();
+
+ int errorCode = extracter.ExtractErrorCode(sqle);
+ if (errorCode == -268)
+ {
+ constraintName = ExtractUsingTemplate("Unique constraint (", ") violated.", sqle.Message);
+ }
+ else if (errorCode == -691)
+ {
+ constraintName = ExtractUsingTemplate("Missing key in referenced table for referential constraint (", ").", sqle.Message);
+ }
+ else if (errorCode == -692)
+ {
+ constraintName = ExtractUsingTemplate("Key value for constraint (", ") is still being referenced.", sqle.Message);
+ }
+
+ if (constraintName != null)
+ {
+ // strip table-owner because Informix always returns constraint names as "<table-owner>.<constraint-name>"
+ int i = constraintName.IndexOf('.');
+ if (i != -1)
+ {
+ constraintName = constraintName.Substring(i + 1);
+ }
+ }
+ return constraintName;
+ }
+ };
}
\ No newline at end of file
Added: trunk/nhibernate/src/NHibernate/Dialect/InformixDialect0940.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Dialect/InformixDialect0940.cs (rev 0)
+++ trunk/nhibernate/src/NHibernate/Dialect/InformixDialect0940.cs 2008-12-03 19:45:12 UTC (rev 3942)
@@ -0,0 +1,150 @@
+using System.Data;
+using NHibernate.Cfg;
+using NHibernate.Dialect.Function;
+using NHibernate.SqlCommand;
+using System.Data.Common;
+using NHibernate.Exceptions;
+using NHibernate.Util;
+//using NHibernate.Dialect.Schema;
+using Environment = NHibernate.Cfg.Environment;
+
+
+namespace NHibernate.Dialect
+{
+ /// <summary>
+ /// Summary description for InformixDialect.
+ /// This dialect is intended to work with IDS version 9.40
+ /// </summary>
+ /// <remarks>
+ /// The InformixDialect defaults the following configuration properties:
+ /// <list type="table">
+ /// <listheader>
+ /// <term>ConnectionDriver</term>
+ /// <description>NHibernate.Driver.OdbcDriver</description>
+ /// <term>PrepareSql</term>
+ /// <description>true</description>
+ /// </listheader>
+ /// <item>
+ /// <term>connection.driver_class</term>
+ /// <description><see cref="NHibernate.Driver.OdbcDriver" /></description>
+ /// </item>
+ /// </list>
+ /// </remarks>
+ public class InformixDialect0940 : InformixDialect
+ {
+ /// <summary></summary>
+ public InformixDialect0940()
+ : base()
+ {
+ RegisterColumnType(DbType.AnsiString, 2147483647, "CLOB");
+ RegisterColumnType(DbType.Binary, 2147483647, "BLOB");
+ RegisterColumnType(DbType.Binary, "BLOB");
+ RegisterColumnType(DbType.String, 2147483647, "CLOB");
+ }
+
+
+ /// <summary> Get the select command used retrieve the names of all sequences.</summary>
+ /// <returns> The select command; or null if sequences are not supported. </returns>
+ public override string QuerySequencesString
+ {
+ get
+ {
+ return "select tabname from systables where tabtype='Q'";
+ }
+ }
+
+ /// <summary>
+ /// Does this dialect support sequences?
+ /// </summary>
+ public override bool SupportsSequences
+ {
+ get { return true; }
+ }
+
+ /// <summary>
+ /// Does this dialect support "pooled" sequences. Not aware of a better
+ /// name for this. Essentially can we specify the initial and increment values?
+ /// </summary>
+ /// <returns> True if such "pooled" sequences are supported; false otherwise. </returns>
+ public override bool SupportsPooledSequences
+ {
+ get { return true; }
+ }
+
+ /// <summary>
+ /// Generate the appropriate select statement to to retreive the next value
+ /// of a sequence.
+ /// </summary>
+ /// <param name="sequenceName">the name of the sequence </param>
+ /// <returns> String The "nextval" select string. </returns>
+ /// <remarks>This should be a "stand alone" select statement.</remarks>
+ public override string GetSequenceNextValString(string sequenceName)
+ {
+ return "select " + sequenceName + ".nextval from systables";
+ }
+
+ public override string GetDropSequenceString(string sequenceName)
+ {
+ return "drop sequence " + sequenceName;
+ }
+ /// <summary>
+ /// Generate the select expression fragment that will retrieve the next
+ /// value of a sequence as part of another (typically DML) statement.
+ /// </summary>
+ /// <param name="sequenceName">the name of the sequence </param>
+ /// <returns> The "nextval" fragment. </returns>
+ /// <remarks>
+ /// This differs from <see cref="GetSequenceNextValString"/> in that this
+ /// should return an expression usable within another statement.
+ /// </remarks>
+ public override string GetSelectSequenceNextValString(string sequenceName)
+ {
+ return sequenceName + ".nextval";
+ }
+ public override string GetCreateSequenceString(string sequenceName)
+ {
+ return "create sequence " + sequenceName;
+ // +
+ //" INCREMENT BY 1 START WITH 1 MINVALUE 1 NOCYCLE CACHE 20 NOORDER";
+
+ }
+
+ // in .NET overloaded version cannot be overriden (in Java allowed)
+ //protected override string GetCreateSequenceString(string sequenceName, int initialValue, int incrementSize)
+ //{
+ // return "create sequence " + sequenceName +
+ // " INCREMENT BY " + incrementSize.ToString() +
+ // " START WITH " + initialValue.ToString() +
+ // " MINVALUE 1 NOCYCLE CACHE 20 NOORDER";
+ //}
+
+ /// <summary>
+ /// Create a <see cref="JoinFragment"/> strategy responsible
+ /// for handling this dialect's variations in how joins are handled.
+ /// </summary>
+ /// <returns> This dialect's <see cref="JoinFragment"/> strategy. </returns>
+ public override JoinFragment CreateOuterJoinFragment()
+ {
+ // ANSI join exist from 9.21 but CROSS, RIGHT and FULL were introduced in 9.40;
+ return new ANSIJoinFragment();
+ }
+
+ /// <summary>
+ /// Does this Dialect have some kind of <c>LIMIT</c> syntax?
+ /// </summary>
+ /// <value>False, unless overridden.</value>
+ public override bool SupportsLimit
+ {
+ get { return false; }
+ }
+
+ /// <summary>
+ /// Does this Dialect support an offset?
+ /// </summary>
+ public override bool SupportsLimitOffset
+ {
+ get { return false; }
+ }
+
+ };
+}
\ No newline at end of file
Added: trunk/nhibernate/src/NHibernate/Dialect/InformixDialect1000.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Dialect/InformixDialect1000.cs (rev 0)
+++ trunk/nhibernate/src/NHibernate/Dialect/InformixDialect1000.cs 2008-12-03 19:45:12 UTC (rev 3942)
@@ -0,0 +1,58 @@
+using System.Data;
+using NHibernate.Cfg;
+using NHibernate.Dialect.Function;
+using NHibernate.SqlCommand;
+using System.Data.Common;
+using NHibernate.Exceptions;
+using NHibernate.Util;
+//using NHibernate.Dialect.Schema;
+using Environment = NHibernate.Cfg.Environment;
+
+
+namespace NHibernate.Dialect
+{
+ /// <summary>
+ /// Summary description for InformixDialect.
+ /// This dialect is intended to work with IDS version 10.00
+ /// </summary>
+ /// <remarks>
+ /// The InformixDialect defaults the following configuration properties:
+ /// <list type="table">
+ /// <listheader>
+ /// <term>ConnectionDriver</term>
+ /// <description>NHibernate.Driver.OdbcDriver</description>
+ /// <term>PrepareSql</term>
+ /// <description>true</description>
+ /// </listheader>
+ /// <item>
+ /// <term>connection.driver_class</term>
+ /// <description><see cref="NHibernate.Driver.OdbcDriver" /></description>
+ /// </item>
+ /// </list>
+ /// </remarks>
+ public class InformixDialect1000 : InformixDialect0940
+ {
+ /// <summary></summary>
+ public InformixDialect1000()
+ : base()
+ {
+ }
+
+ /// <summary>
+ /// Does this Dialect have some kind of <c>LIMIT</c> syntax?
+ /// </summary>
+ /// <value>False, unless overridden.</value>
+ public override bool SupportsLimit
+ {
+ get { return true; }
+ }
+
+ /// <summary>
+ /// Does this Dialect support an offset?
+ /// </summary>
+ public override bool SupportsLimitOffset
+ {
+ get { return true; }
+ }
+ }
+}
\ No newline at end of file
Added: trunk/nhibernate/src/NHibernate/Exceptions/ReflectionBasedSqlStateExtracter.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Exceptions/ReflectionBasedSqlStateExtracter.cs (rev 0)
+++ trunk/nhibernate/src/NHibernate/Exceptions/ReflectionBasedSqlStateExtracter.cs 2008-12-03 19:45:12 UTC (rev 3942)
@@ -0,0 +1,64 @@
+using System.Reflection;
+using System.Collections;
+using System.Data.Common;
+
+namespace NHibernate.Exceptions
+{
+ class ReflectionBasedSqlStateExtracter: SqlStateExtracter
+ {
+
+ /* OdbcException, OleDbException, IfxException, Db2Exception, and possible others
+ * have Errors collection which contains fields: NativeError and SQLState
+ * These fields can be extracted using reflection
+ */
+ public override int ExtractSingleErrorCode(DbException sqle)
+ {
+ System.Type type;
+ PropertyInfo pi;
+ int nativeError;
+
+ type = sqle.GetType();
+ pi = type.GetProperty("Errors");
+ if (pi == null) // there is no Errors property
+ {
+ return 0;
+ }
+ nativeError = 0;
+ foreach (object o in (pi.GetValue(sqle, null) as IEnumerable))
+ {
+ pi = o.GetType().GetProperty("NativeError");
+ if (pi == null)
+ return 0;
+ nativeError = (int)pi.GetValue(o, null);
+ if (nativeError != 0)
+ break;
+ }
+ return nativeError;
+ }
+
+ public override string ExtractSingleSqlState(DbException sqle)
+ {
+ System.Type type;
+ PropertyInfo pi;
+ string sqlState;
+
+ type = sqle.GetType();
+ pi = type.GetProperty("Errors");
+ if (pi == null) // there is no Errors property
+ {
+ return null;
+ }
+ sqlState = "";
+ foreach (object o in (pi.GetValue(sqle, null) as IEnumerable))
+ {
+ pi = o.GetType().GetProperty("SQLState");
+ if (pi == null)
+ return null;
+ sqlState = (string)pi.GetValue(o, null);
+ if (sqlState.Length != 0)
+ break;
+ }
+ return sqlState;
+ }
+ }
+}
\ No newline at end of file
Added: trunk/nhibernate/src/NHibernate/Exceptions/SqlStateExtracter.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Exceptions/SqlStateExtracter.cs (rev 0)
+++ trunk/nhibernate/src/NHibernate/Exceptions/SqlStateExtracter.cs 2008-12-03 19:45:12 UTC (rev 3942)
@@ -0,0 +1,51 @@
+using System;
+using System.Data.Common;
+
+namespace NHibernate.Exceptions
+{
+ public abstract class SqlStateExtracter
+ {
+ /* Many drivers provide both SqlState and NativeError in the Exception
+ * Some of them, like OdbcException, have fields SQLState, NativeError
+ * Some of them contain it in Data field, like PsqlException
+ * Some of them have only text message
+ */
+
+ public int ExtractErrorCode(DbException sqle)
+ {
+ int errorCode;
+ Exception nested;
+ errorCode = ExtractSingleErrorCode(sqle);
+ nested = sqle.InnerException;
+ while (errorCode == 0 && nested != null)
+ {
+ if (nested is DbException)
+ {
+ errorCode = ExtractSingleErrorCode(sqle);
+ }
+ nested = sqle.InnerException;
+ }
+ return errorCode;
+ }
+
+ public string ExtractSqlState(DbException sqle)
+ {
+ string sqlState;
+ Exception nested;
+ sqlState = ExtractSingleSqlState(sqle);
+ nested = sqle.InnerException;
+ while (sqlState.Length == 0 && nested != null)
+ {
+ if (nested is DbException)
+ {
+ sqlState = ExtractSingleSqlState(sqle);
+ }
+ nested = sqle.InnerException;
+ }
+ return sqlState;
+ }
+
+ public abstract int ExtractSingleErrorCode(DbException sqle);
+ public abstract string ExtractSingleSqlState(DbException sqle);
+ }
+}
Added: trunk/nhibernate/src/NHibernate/Exceptions/TemplatedViolatedConstraintNameExtracter.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Exceptions/TemplatedViolatedConstraintNameExtracter.cs (rev 0)
+++ trunk/nhibernate/src/NHibernate/Exceptions/TemplatedViolatedConstraintNameExtracter.cs 2008-12-03 19:45:12 UTC (rev 3942)
@@ -0,0 +1,44 @@
+using System.Data.Common;
+
+namespace NHibernate.Exceptions
+{
+ /// <summary>
+ /// Knows how to extract a violated constraint name from an error message based on the
+ /// fact that the constraint name is templated within the message.
+ /// </summary>
+ public abstract class TemplatedViolatedConstraintNameExtracter : IViolatedConstraintNameExtracter
+ {
+
+ /// <summary>
+ /// Extracts the constraint name based on a template (i.e., <i>templateStart</i><b>constraintName</b><i>templateEnd</i>).
+ /// </summary>
+ /// <param name="templateStart">The pattern denoting the start of the constraint name within the message.</param>
+ /// <param name="templateEnd">The pattern denoting the end of the constraint name within the message.</param>
+ /// <param name="message">The templated error message containing the constraint name.</param>
+ /// <returns>The found constraint name, or null.</returns>
+ protected string ExtractUsingTemplate(string templateStart, string templateEnd, string message)
+ {
+ int templateStartPosition = message.IndexOf(templateStart);
+ if (templateStartPosition < 0)
+ {
+ return null;
+ }
+
+ int start = templateStartPosition + templateStart.Length;
+ int end = message.IndexOf(templateEnd, start);
+ if (end < 0)
+ {
+ end = message.Length;
+ }
+
+ return message.Substring(start, end);
+ }
+
+ /// <summary>
+ /// Extract the name of the violated constraint from the given SQLException.
+ /// </summary>
+ /// <param name="sqle">The exception that was the result of the constraint violation. </param>
+ /// <returns> The extracted constraint name. </returns>
+ public abstract string ExtractConstraintName(DbException sqle);
+ }
+}
Modified: trunk/nhibernate/src/NHibernate/NHibernate.csproj
===================================================================
--- trunk/nhibernate/src/NHibernate/NHibernate.csproj 2008-11-30 20:32:19 UTC (rev 3941)
+++ trunk/nhibernate/src/NHibernate/NHibernate.csproj 2008-12-03 19:45:12 UTC (rev 3942)
@@ -443,11 +443,16 @@
<Compile Include="Bytecode\HibernateByteCodeException.cs" />
<Compile Include="Bytecode\ProxyFactoryFactoryNotConfiguredException.cs" />
<Compile Include="Bytecode\UnableToLoadProxyFactoryFactoryException.cs" />
+ <Compile Include="Dialect\InformixDialect0940.cs" />
+ <Compile Include="Dialect\InformixDialect1000.cs" />
<Compile Include="Dialect\Schema\SQLiteMetaData.cs" />
<Compile Include="Dialect\Schema\SybaseAnywhereMetaData.cs" />
<Compile Include="Dialect\SybaseASA10Dialect.cs" />
<Compile Include="Dialect\SybaseASA9Dialect.cs" />
<Compile Include="Driver\IfxDriver.cs" />
+ <Compile Include="Exceptions\ReflectionBasedSqlStateExtracter.cs" />
+ <Compile Include="Exceptions\SqlStateExtracter.cs" />
+ <Compile Include="Exceptions\TemplatedViolatedConstraintNameExtracter.cs" />
<Compile Include="Id\SelectGenerator.cs" />
<Compile Include="Properties\BackFieldStrategy.cs" />
<Compile Include="Bytecode\CodeDom\BytecodeProviderImpl.cs" />
@@ -836,6 +841,7 @@
<Compile Include="Proxy\Poco\BasicLazyInitializer.cs" />
<Compile Include="QueryParameterException.cs" />
<Compile Include="SessionException.cs" />
+ <Compile Include="SqlCommand\InformixJoinFragment.cs" />
<Compile Include="SqlCommand\SubselectClauseExtractor.cs" />
<Compile Include="Engine\SubselectFetch.cs" />
<Compile Include="Criterion\DetachedCriteria.cs" />
Added: trunk/nhibernate/src/NHibernate/SqlCommand/InformixJoinFragment.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/SqlCommand/InformixJoinFragment.cs (rev 0)
+++ trunk/nhibernate/src/NHibernate/SqlCommand/InformixJoinFragment.cs 2008-12-03 19:45:12 UTC (rev 3942)
@@ -0,0 +1,113 @@
+using System;
+using System.Text;
+using Iesi.Collections;
+using NHibernate.Util;
+
+namespace NHibernate.SqlCommand
+{
+ /// <summary>
+ /// An Informix-style (theta) Join
+ /// </summary>
+ public class InformixJoinFragment : JoinFragment
+ {
+ private SqlStringBuilder afterFrom = new SqlStringBuilder();
+ private SqlStringBuilder afterWhere = new SqlStringBuilder();
+
+ public override void AddJoin(string tableName, string alias, string[] fkColumns, string[] pkColumns, JoinType joinType)
+ {
+ string joinString;
+ switch (joinType)
+ {
+ case JoinType.InnerJoin:
+ AddCrossJoin(tableName, alias);
+ break;
+ case JoinType.LeftOuterJoin:
+ afterFrom.Add(StringHelper.CommaSpace)
+ .Add("outer ")
+ .Add(tableName)
+ .Add(" ")
+ .Add(alias);
+ break;
+ case JoinType.RightOuterJoin:
+ int i = GetPrevTableInsertPoint(afterFrom.ToSqlString());
+ afterFrom.Insert(i, "outer ");
+ break;
+ case JoinType.FullJoin:
+ throw new NotSupportedException("join type not supported by Informix");
+ default:
+ throw new AssertionFailure("undefined join type");
+ }
+
+ for (int j = 0; j < fkColumns.Length; j++)
+ {
+ //HasThetaJoins = true;
+ afterWhere.Add(" and " + fkColumns[j]);
+ afterWhere.Add("=" + alias + StringHelper.Dot + pkColumns[j]);
+ }
+ }
+
+ public override void AddJoin(string tableName, string alias, string[] fkColumns, string[] pkColumns, JoinType joinType,
+ string on)
+ {
+ //arbitrary on clause ignored!!
+ AddJoin(tableName, alias, fkColumns, pkColumns, joinType);
+ AddCondition(on);
+ }
+
+ public override SqlString ToFromFragmentString
+ {
+ get { return afterFrom.ToSqlString(); }
+ }
+
+ public override SqlString ToWhereFragmentString
+ {
+ get { return afterWhere.ToSqlString(); }
+ }
+
+ public override void AddJoins(SqlString fromFragment, SqlString whereFragment)
+ {
+ afterFrom.Add(fromFragment);
+ afterWhere.Add(whereFragment);
+ }
+
+ public override void AddCrossJoin(string tableName, string alias)
+ {
+ afterFrom.Add(StringHelper.CommaSpace)
+ .Add(tableName)
+ .Add(" ")
+ .Add(alias);
+ }
+
+ public override bool AddCondition(string condition)
+ {
+ return AddCondition(afterWhere, condition);
+ }
+
+ public override bool AddCondition(SqlString condition)
+ {
+ return AddCondition(afterWhere, condition);
+ }
+
+ public override void AddFromFragmentString(SqlString fromFragmentString)
+ {
+ afterFrom.Add(fromFragmentString);
+ }
+
+ private static int GetPrevTableInsertPoint(SqlString text)
+ {
+ int i, j;
+
+ i = text.LastIndexOfCaseInsensitive("from");
+ j = text.LastIndexOfCaseInsensitive(",");
+ if (i == -1 && j == -1)
+ {
+ return -1;
+ }
+ if (j > i)
+ {
+ return j + 1;
+ }
+ return i + 5;
+ }
+ }
+}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|