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. |