internal Key ResolveForeignKeyReferences(Type type, Key key) { // determine whether we need to translate names if (key.SourceType != null && key.SourceType != type) { // translate column names in key to foreign key name in table used by type Key fkKey = new Key(type, false); ObjectMap map = ObjectFactory.GetMap(this, key.SourceType); ObjectMap fkMap = ObjectFactory.GetMap(this, type); foreach (string name in key.Keys) { string fieldName = key.isPropertyKeys ? name : map.GetPropertyName(name); FieldMap fkfm = fkMap.GetForeignKeyFieldMap(map.Type, fieldName); fkKey[fkfm.ColumnName] = key[name]; } key = fkKey; } return(key); }
private static Type GetColumnType(string columnName, ObjectMap map, SqlResult sr) { Type result = null; if (map != null) { FieldMap fm = map.GetFieldMapFromColumn(columnName); if (fm != null) { result = fm.Type; } } if (result == null && sr.RowsContained > 0) { object obj = sr.GetObject(0, columnName); if (obj != null) { result = obj.GetType(); } } return(result); }
/// <summary> /// <p>Update the statement parameter values using the values from the key.</p> /// <p>Warning! This will erase all parameter values not in the key, so if you /// need to set additional parameters be sure to call this method before /// any calls to SetParameter.</p> /// </summary> /// <param name="key">The key instance containing the parameter values.</param> /// <param name="isUpdateAll">If true all statement parameters will be updated. If the /// key has no value a null value will be assigned.</param> public void SetParameters(Key key, bool isUpdateAll) { Check.VerifyNotNull(key, Error.NullParameter, "key"); GentleSqlFactory sf = broker.GetSqlFactory(); foreach (IDataParameter param in cmd.Parameters) { // strip leading @ character if present string prefix = sf.GetParameterPrefix(); string suffix = sf.GetParameterSuffix(); string paramName = param.ParameterName; if (paramName.StartsWith(prefix)) { paramName = paramName.Substring(prefix.Length, paramName.Length - prefix.Length); } //if needed, strip out the suffix from the paramName/column name if (suffix != "" && paramName.EndsWith(suffix)) { paramName = paramName.Substring(0, paramName.Length - suffix.Length); } FieldMap fm = map.GetFieldMapFromColumn(paramName); // handle special case where parameter is concurrency control column if (fm == null && map.ConcurrencyMap != null && paramName.StartsWith("New") && (paramName.Substring(3, paramName.Length - 3) == map.ConcurrencyMap.ColumnName)) { fm = map.ConcurrencyMap; } // if key contains property names translate parameter name into property name string index = key.isPropertyKeys ? fm.MemberName : paramName; // check if we should set parameter value if (key.ContainsKey(index) || isUpdateAll) { Check.VerifyNotNull(fm, Error.Unspecified, "Could not find a value for the parameter named {0}", index); SetParameter(param, key.ContainsKey(index) ? key[index] : null, fm); } } }
/// <summary> /// This method updated the given ObjectMap instance with metadata obtained /// from the database. /// </summary> /// <param name="map">The ObjectMap to update</param> public virtual void UpdateObjectMap(ObjectMap map) { if (level != AnalyzerLevel.None) { TableMap dbMap = TableMaps[map.TableName.ToLower()] as TableMap; if (dbMap == null && level == AnalyzerLevel.OnDemand) { Analyze(map.TableName); dbMap = TableMaps[map.TableName.ToLower()] as TableMap; } Check.VerifyNotNull(dbMap, Error.UnknownTable, map.TableName, map.Type); // make sure table name is set map.Provider = provider; // transfer table information map.IsView = dbMap.IsView; bool isUseData = !map.IsView; isUseData |= map.IsView && HasCapability(ColumnInformation.IsView); // transfer column information IList processedFields = new ArrayList(map.Fields.Count); foreach (FieldMap dbfm in dbMap.Fields) { try { FieldMap fm = map.Fields.FindColumn(dbfm.ColumnName); if (fm != null) { // view information if (map.IsView) { if (HasCapability(ColumnInformation.ViewType)) { fm.SetDbType(dbfm.DbType); } if (HasCapability(ColumnInformation.ViewSize)) { fm.SetSize(dbfm.Size); } if (HasCapability(ColumnInformation.ViewIsNullable)) { fm.SetIsNullable(dbfm.IsNullable); } if (HasCapability(ColumnInformation.ViewIsPrimaryKey)) { fm.SetIsPrimaryKey(dbfm.IsPrimaryKey); } if (HasCapability(ColumnInformation.ViewIsAutoGenerated)) { fm.SetIsAutoGenerated(dbfm.IsAutoGenerated); } if (HasCapability(ColumnInformation.ViewIsForeignKey) && dbfm.IsForeignKey) { fm.SetForeignKeyTableName(dbfm.ForeignKeyTableName); fm.SetForeignKeyColumnName(dbfm.ForeignKeyColumnName); } } else // table information { if (HasCapability(ColumnInformation.TableType)) { fm.SetDbType(dbfm.DbType); } if (HasCapability(ColumnInformation.TableSize)) { fm.SetSize(dbfm.Size); } if (HasCapability(ColumnInformation.TableIsNullable)) { fm.SetIsNullable(dbfm.IsNullable); } if (HasCapability(ColumnInformation.TableIsPrimaryKey)) { fm.SetIsPrimaryKey(dbfm.IsPrimaryKey); } if (HasCapability(ColumnInformation.TableIsAutoGenerated)) { fm.SetIsAutoGenerated(dbfm.IsAutoGenerated); } if (HasCapability(ColumnInformation.TableIsForeignKey) && dbfm.IsForeignKey) { fm.SetForeignKeyTableName(dbfm.ForeignKeyTableName); fm.SetForeignKeyColumnName(dbfm.ForeignKeyColumnName); } } // remember which fields have been processed (for error reporting later) processedFields.Add(fm); } } catch { // ignore the error but log a warning Check.LogWarning(LogCategories.Metadata, "Type {0} contains no mapping for column {1} in table {2}.", map.Type.Name, dbfm.ColumnName, map.TableName); } } // verify that all defined fields had an existing column in the target table if (isUseData && processedFields.Count < map.Fields.Count) { // construct informative error message StringBuilder sb = new StringBuilder(); bool single = processedFields.Count == map.Fields.Count - 1; sb.AppendFormat("The column{0} ", single ? "" : "s"); bool delimit = false; foreach (FieldMap fm in map.Fields) { if (!processedFields.Contains(fm)) { sb.AppendFormat("{0}{1}", delimit ? ", " : "", fm.ColumnName); delimit = true; } } sb.AppendFormat(" in table {0} do{1} not exist.", map.TableName, single ? "es" : ""); // raise an exception Check.Fail(Error.DeveloperError, sb.ToString()); } } }
/// <summary> /// Please refer to the <see cref="GentleAnalyzer"/> class and the <see cref="IDatabaseAnalyzer"/> /// interface it implements a description of this method. /// </summary> public override void Analyze( string tableName ) { try { bool isSingleRun = tableName != null; string selectSingle = isSingleRun ? String.Format( SELECT_SINGLE, tableName ) : String.Empty; // Check Oracle version and select appropriate SQL-Syntax SqlResult sr = broker.Execute( ORA_VERSION_SELECT ); // string ver = sr.GetString( 0, "Version" ).Substring( 0, 1 ); // int version = Convert.ToInt32( sr.GetString( 0, "Version" ).Substring( 0, 1 ) ); string ver = sr.GetString( 0, "Version" ); int indexOfDot = ver.IndexOf( "." ); if( indexOfDot < 0 ) { throw new GentleException( Error.DeveloperError, "Unable to determine Oracle database version." ); } int version = Convert.ToInt32( ver.Substring( 0, indexOfDot ) ); string select; string selectReferences; if( version < 9 ) { // If Oracle version == '8.1.6' use no-views selectReferences if( ver.Substring( 0, 5 ).CompareTo( "8.1.6" ) == 0 ) { selectReferences = ORA816_SELECT_REFERENCES; } else { selectReferences = ORA8_SELECT_REFERENCES; } select = ORA8_SELECT + selectSingle + ORDER_BY; } else { select = ORA9_SELECT + selectSingle + ORDER_BY; selectReferences = ORA9_SELECT_REFERENCES; } sr = broker.Execute( select ); // process result set using columns: // TableName, ColumnName, Type, Size, IsNullable, DefaultValue, // ConstraintName, ConstraintReference, ConstraintType, UpdateRule, DeleteRule for( int i = 0; i < sr.Rows.Count; i++ ) { try { string dbTableName = sr.GetString( i, "TableName" ); if( ! isSingleRun || tableName.ToLower().Equals( dbTableName.ToLower() ) ) { // get or create TableMap for table TableMap map = GetTableMap( dbTableName ); if( map == null ) { map = new TableMap( provider, dbTableName ); maps[ dbTableName.ToLower() ] = map; } // get or create FieldMap for column string columnName = sr.GetString( i, "ColumnName" ); FieldMap fm = map.GetFieldMapFromColumn( columnName ); if( fm == null ) { fm = new FieldMap( map, columnName ); map.Fields.Add( fm ); } // get basic column information fm.SetDbType( sr.GetString( i, "Type" ), false ); fm.SetIsNullable( GetBoolean( sr.GetString( i, "IsNullable" ) ) ); if( sr[ i, "Size" ] != null ) { if( fm.DbType == (long) OracleType.Clob ) { //Max 4GB //Preferred size 4294967296 fm.SetSize( int.MaxValue ); } else { fm.SetSize( sr.GetInt( i, "Size" ) ); } } // get column constraint infomation if( sr[ i, "ConstraintName" ] != null ) { string typ = sr.GetString( i, "ConstraintType" ); if( typ.ToLower().Equals( "p" ) ) { fm.SetIsPrimaryKey( true ); } else if( typ.ToLower().Equals( "r" ) ) { string conref = sr.GetString( i, "ConstraintReference" ); SqlResult res = broker.Execute( String.Format( selectReferences, conref ) ); fm.SetForeignKeyTableName( res.GetString( 0, "ChildTable" ) ); fm.SetForeignKeyColumnName( res.GetString( 0, "ChildColumn" ) ); } } } } catch( GentleException fe ) { // ignore errors caused by tables found in db but for which no map exists // TODO this should be a config option if( fe.Error != Error.NoObjectMapForTable ) { throw; } } } } catch( Exception e ) { Check.LogInfo( LogCategories.General, "Using provider {0} and connectionString {1}.", provider.Name, provider.ConnectionString ); Check.Fail( e, Error.Unspecified, "An error occurred while analyzing the database schema." ); } }
/// <summary> /// Please refer to the <see cref="GentleAnalyzer"/> class and the <see cref="IDatabaseAnalyzer"/> /// interface it implements a description of this method. /// </summary> public override void Analyze( string tableName ) { GentleSqlFactory sf = provider.GetSqlFactory(); try { bool isSingleRun = tableName != null; // don't quote reserved words here (table name is just a string parameter) string sql = isSingleRun ? select + String.Format( selectSingle, tableName ) : select; SqlResult sr = broker.Execute( sql, null, null ); // process result set using columns: // TableName, ColumnName, Type, Size, IsNullable, DefaultValue, // ConstraintName, ConstraintReference, ConstraintType, // UpdateRule, DeleteRule, TableType for( int i = 0; i < sr.Rows.Count; i++ ) { try { string dbTableName = sr.GetString( i, "tablename" ); if( ! isSingleRun || tableName.ToLower().Equals( dbTableName.ToLower() ) ) { TableMap map = GetTableMap( dbTableName ); if( map == null ) { map = new TableMap( provider, dbTableName ); maps[ dbTableName.ToLower() ] = map; } map.IsView = sr.GetString( i, "TableType" ) == "VIEW"; // get column information for this table string columnName = sr.GetString( i, "ColumnName" ); FieldMap fm = map.GetFieldMapFromColumn( columnName ); if( fm == null ) { fm = new FieldMap( map, columnName ); map.Fields.Add( fm ); } // get basic column information fm.SetDbType( sr.GetString( i, "Type" ), false ); // sql server is always false fm.SetIsNullable( GetBoolean( sr.GetString( i, "IsNullable" ) ) ); fm.SetIsAutoGenerated( sr.GetString( i, "DefaultValue" ).Length > 0 ? true : false ); if( sr[ i, "Size" ] != null && fm.DbType != (long) SqlDbType.Text ) { fm.SetSize( sr.GetInt( i, "Size" ) ); } // get column constraint infomation if( sr[ i, "ConstraintName" ] != null ) { string type = sr.GetString( i, "ConstraintType" ); if( type.ToLower().Equals( "primary key" ) ) { fm.SetIsPrimaryKey( true ); } else if( type.ToLower().Equals( "foreign key" ) ) { string conref = sr.GetString( i, "ConstraintReference" ); if( conref.StartsWith( "IDX" ) ) { string fkRef = sr.GetString( i, "ConstraintName" ); if( fkRef != null && fkRef.StartsWith( "FK" ) ) { conref = fkRef; } } SqlResult res = broker.Execute( String.Format( selectReferences, conref ), null, null ); if( res.ErrorCode == 0 && res.RowsContained == 1 ) { fm.SetForeignKeyTableName( res.GetString( 0, "TableName" ) ); fm.SetForeignKeyColumnName( res.GetString( 0, "ColumnName" ) ); } else { if( res.RowsContained == 0 ) { // see GOPF-155 for additional information Check.LogWarning( LogCategories.Metadata, "Unable to obtain foreign key information for column {0} of table {1}.", fm.ColumnName, map.TableName ); } else { Check.LogWarning( LogCategories.Metadata, "Gentle 1.x does not support composite foreign keys." ); } } } } if( map.IsView ) { // TODO // process backing table members and infer PK/identity info // requires that tables be processed before views! // //string sv = String.Format( selectViewDependencies, map.TableName ); //SqlResult res = broker.Execute( sv ); } } } catch( GentleException fe ) { // ignore errors caused by tables found in db but for which no map exists // TODO this should be a config option if( fe.Error != Error.NoObjectMapForTable ) { throw; } } } } catch( Exception e ) { Check.LogInfo( LogCategories.General, "Using provider {0} and connectionString {1}.", provider.Name, provider.ConnectionString ); Check.Fail( e, Error.Unspecified, "An error occurred while analyzing the database schema." ); } }
private void GetColumnData( TableMap map ) { string sql = String.Format( selectColumns, map.TableId ); SqlResult sr = broker.Execute( sql ); for( int i = 0; i < sr.Rows.Count; i++ ) { string column = sr.GetString( i, "column_name" ); FieldMap fm = map.GetFieldMapFromColumn( column ); if( fm == null ) { fm = new FieldMap( map, column ); map.Fields.Add( fm ); } bool isNullable = sr.GetString( i, "nulls" ).Equals( "Y" ); bool isAutoGenerated = sr.GetString( i, "default" ).ToLower() == "autoincrement"; fm.ColumnId = sr.GetInt( i, "column_id" ); fm.SetDbType( sr.GetString( i, "domain_name" ), false ); fm.SetIsNullable( isNullable ); fm.SetIsAutoGenerated( isAutoGenerated ); fm.SetSize( sr.GetInt( i, "width" ) ); } }
private void InitConstructorMap(object[] row) { columnOrderMap = new int[columnInfo.Names.Length]; ParameterInfo[] pis = constructorInfo.GetParameters(); constructorParameterCount = pis.Length; // use a counter to determine whether we have a column for every parameter int noColumnForParameter = constructorParameterCount; bool isPerfectColumnOrder = true; // process columns for (int i = 0; i < columnInfo.Names.Length; i++) { FieldMap fm = columnInfo.Fields[i]; string columnName = fm != null?fm.ColumnName.ToLower() : columnInfo.Names[i].ToLower(); string memberName = fm != null?fm.MemberName.ToLower() : null; bool foundParam = false; for (int j = 0; j < pis.Length; j++) { string parameterName = pis[j].Name.ToLower(); if (parameterName == columnName || (fm != null && parameterName == memberName)) { noColumnForParameter--; foundParam = true; columnConstructorUsageCount += 1; columnConstructorMask[i] = true; columnOrderMap[i] = j; isPerfectColumnOrder &= i == j; // also flag primary keys if (fm != null && fm.IsPrimaryKey) { columnPrimaryKeyMask[i] = true; } break; } } if (!foundParam) { if (fm != null) { // column not included in constructor but member field is present columnReflectionMask[i] = true; cost += 1000; // also flag primary keys if (fm.IsPrimaryKey) { columnPrimaryKeyMask[i] = true; } } else { // unused column - not in constructor or as member field columnUnusedMask[i] = true; // log a warning Check.LogWarning(LogCategories.Metadata, "The column {0} does not match any member or constructor parameter " + "of type {1}. It will therefore not be used during object construction.", columnInfo.Names[i], objectMap.Type); } } // determine if type conversion is required for column if (fm != null) { // type conversion required for nullable columns mapping to not-nullable system type // or when result set type is different from member/parameter type if (fm.NullValue != null || (row[i] != null && fm.Type != row[i].GetType() && !fm.IsGenericNullableType)) { columnTypeConvertMask[i] = true; cost += 1; } } } // score 100 if parameter and column count differ cost += columnConstructorUsageCount == pis.Length ? 0 : 100; // score 300 if column order does not match parameter order cost += isPerfectColumnOrder ? 0 : 300; // score 600 if type conversion for any column is required cost += AllUnset(columnTypeConvertMask) ? 0 : 600; // determine whether we have a perfect match (can use direct constructor invocation) isPerfectMatch = isPerfectColumnOrder && columnConstructorUsageCount == pis.Length; isPerfectMatch &= AllUnset(columnUnusedMask) && AllUnset(columnTypeConvertMask); isPerfectMatch &= cost == 0; // isValid tells whether this CM can be used with the given columns isValid = noColumnForParameter == 0; }
/// <summary> /// Obtain the column name corresponding to a given property name. /// </summary> /// <param name="propertyName">The name of the property</param> /// <returns>The name of the column</returns> public string GetColumnName(string propertyName) { FieldMap fm = Fields.FindProperty(propertyName); return(fm != null ? fm.ColumnName : null); }
/// <summary> /// Internal helper method used for standard CRUD operations on known types. /// </summary> /// <param name="obj">The object instance being operated on</param> /// <param name="st">The statement type</param> /// <param name="conn">An existing database connection to reuse. This is useful /// when you need to execute statements in the same session as a previous statement.</param> /// <param name="tr">The database transaction for when participating in transactions.</param> /// <returns>The result of the operation</returns> internal SqlResult Execute(object obj, StatementType st, IDbConnection conn, IDbTransaction tr) { ObjectMap map = ObjectFactory.GetMap(this, obj); // perform validations first if (StatementType.Insert == st || StatementType.Update == st) { ValidationBroker.Validate(obj); } if (map.IsSoftDelete && st == StatementType.Delete) { st = StatementType.SoftDelete; } SqlStatement stmt = GetStatement(obj, st); stmt.SetParameters(obj, true); // connections are supplied from outside when in a transaction or executing batch queries conn = tr != null ? tr.Connection : conn ?? stmt.SessionBroker.Provider.GetConnection(); SqlResult sr = stmt.Execute(conn, tr); // throw an error if execution failed Check.Verify(sr.ErrorCode == 0, Error.StatementError, sr.Error, stmt.Sql); // check that statement affected a row if (st == StatementType.Insert || st == StatementType.Update || st == StatementType.Delete || st == StatementType.SoftDelete) { Check.Verify(sr.RowsAffected >= 1, Error.UnexpectedRowCount, sr.RowsAffected, "1+"); } // update identity values for inserts if (st == StatementType.Insert) { if (sr.LastRowId != 0 && map.IdentityMap != null) { map.SetIdentity(obj, sr.LastRowId); } if (obj is IEntity) { (obj as IEntity).IsPersisted = true; } // this is not really necessary but nice for error checking (also used in test cases) if (map.InheritanceMap != null) { map.InheritanceMap.SetValue(obj, obj.GetType().AssemblyQualifiedName); } } // update/invalidate the cache if (GentleSettings.CacheObjects && map.CacheStrategy != CacheStrategy.Never) { if (st == StatementType.Insert) { // insert new object into cache CacheManager.Insert(map.GetInstanceHashKey(obj), obj, map.CacheStrategy); // invalidate query results for select statements for this type, reducing the // effectiveness of the cache (but ensuring correct results) if (GentleSettings.CacheStatements) { CacheManager.ClearQueryResultsByType(map.Type); } } else if (st == StatementType.Delete || st == StatementType.SoftDelete) { // remove invalidated object from the cache CacheManager.Remove(obj); // invalidate query results for select statements for this type, reducing the // effectiveness of the cache (but ensuring correct results) if (GentleSettings.CacheStatements) { CacheManager.ClearQueryResultsByType(map.Type); } } } // update the in-memory version/revision counter for objects under concurrency control if (st == StatementType.Update && GentleSettings.ConcurrencyControl) { if (map.ConcurrencyMap != null) { FieldMap fm = map.ConcurrencyMap; long version = Convert.ToInt64(fm.GetValue(obj)); // handle wrap-around of the version counter if ((fm.Type.Equals(typeof(int)) && version == int.MaxValue) || (fm.Type.Equals(typeof(long)) && version == long.MaxValue)) { version = 1; } else { version += 1; } map.ConcurrencyMap.SetValue(obj, version); } } // update object with database-created values if UpdateAfterWrite is set to true if (map.IsUpdateAfterWrite && (st == StatementType.Insert || st == StatementType.Update)) { if (tr != null) { Refresh(obj, tr); } else { Refresh(obj); } } return(sr); }
/// <summary> /// Obtain the system type of the given property. /// </summary> /// <param name="propertyName">The name of the property</param> /// <returns>The system type of the property</returns> public Type GetPropertyType(string propertyName) { FieldMap fm = Fields.FindProperty(propertyName); return(fm != null ? fm.Type : null); }
/// <summary> /// This method is for adding set membership constraints using either Operator.In or /// Operator.NotIn. If an invalid operator is used this method will throw an exception. /// </summary> /// <param name="op">The operator used for this constraint.</param> /// <param name="fieldMap">The FieldMap instance for the field to constrain. The framework /// assumes property names but /// also checks column names if no property could be found.</param> /// <param name="data">The set of data to constrain on.</param> /// <param name="constraintMap">If the constraint data set holds objects this is the property /// name to gather from all objects. If the data set is an SqlResult this is the column name /// to use from all rows. String-type fields are quoted, all other types are not. This method /// performs no additional type checking and is likely to fail (when the statement is executed) /// for esoteric types such as decimals or dates.</param> public void AddConstraint( Operator op, FieldMap fieldMap, ICollection data, FieldMap constraintMap ) { Check.VerifyNotNull( fieldMap, Error.NullParameter, "field" ); Check.VerifyNotNull( data, Error.NullParameter, "data" ); Check.IsTrue( op == Operator.In || op == Operator.NotIn, Error.DeveloperError, "This AddConstraint method must be used only with the In and NotIn operators." ); // TODO make this a config option (e.g. "strict" behavior) // Check.Verify( data.Count > 0, Error.EmptyListParameter, "data" ); if( data.Count > 0 ) { // convert supplied collection into comma-delimited list of values string[] list = new string[data.Count]; int count = 0; foreach( object obj in data ) { if( constraintMap == null ) { list[ count++ ] = Convert.ToString( obj ); } else { list[ count++ ] = Convert.ToString( constraintMap.GetValue( obj ) ); } } // determine whether field type needs quoting bool needsQuoting = fieldMap.Type.Equals( typeof(string) ); needsQuoting |= fieldMap.Type.Equals( typeof(DateTime) ); // quote GUIDs (only when they are not in binary form) needsQuoting |= fieldMap.Type.Equals( typeof(Guid) ) && (fieldMap.Size == 0 || fieldMap.Size > 16); // add the constraint AddConstraint( String.Format( "{0} {1} {2} {3}", fieldMap.QuotedColumnName, AsOperatorBegin( op, false ), AsCommaList( list, needsQuoting ), AsOperatorEnd( op ) ) ); } }
/// <summary> /// TODO FIXME /// This method breaks use of multiple brokers, as it is static and references Broker (and thus DefaultProvider). /// </summary> internal static object GetParameterValue( object val, ObjectMap map, FieldMap fm, StatementType stmtType ) { Check.VerifyNotNull( map, Error.NullParameter, "map" ); Check.VerifyNotNull( fm, Error.NullParameter, "fm" ); object result; // make a quick exit with the type name if the column is used for inheritance mapping if( map.InheritanceMap != null && fm.ColumnName == map.InheritanceMap.ColumnName ) { return map.Type.AssemblyQualifiedName; } // null handling if( fm.IsNull( val ) ) { // verify that null assignment isn't violating known defintions // dont check value types as they are never nullable, and allow null for autogenerated PKs if( val == null && ! fm.IsValueType && ! (fm.IsPrimaryKey && fm.IsAutoGenerated) ) { Check.Verify( fm.IsNullable, Error.NullProperty, fm.MemberName, map.Type ); } result = DBNull.Value; } else { // clip strings to make sure they fit target column if( fm.Type == typeof(string) && val != null && fm.Size > 0 && ((string) val).Length > fm.Size ) { result = ((string) val).Substring( 0, fm.Size ); } // clip DateTime values else if( fm.Type == typeof(DateTime) ) { // ensure that datetime values are clipped if outside database range result = EnsureValidDate( (DateTime) val ); } // convert enum values to numeric value else if( fm.Type.IsEnum ) { // use the integer or string value of the enum result = fm.HandleEnumAsString ? Convert.ChangeType( val, typeof(string) ) : Convert.ChangeType( val, Enum.GetUnderlyingType( fm.Type ) ); } // if column is autogenerated primary key use DBNull if an integer value of 0 is given else if( fm.IsAutoGeneratedKeyAndSupportedType && val.Equals( 0 ) && stmtType == StatementType.Insert ) { result = DBNull.Value; } else if( fm.Type == typeof(byte[]) || fm.Type == typeof(Byte[]) ) { MemoryStream stream = new MemoryStream( (byte[]) val ); result = stream.ToArray(); } else if( fm.Type == typeof(Guid) && fm.Size == 16 ) { result = TypeConverter.ToBinaryString( (Guid) val ); } else if( fm.Type == typeof(Guid) && Broker.ProviderName.StartsWith( "Sybase" ) ) { // Sybase needs the GUID as a string result = val.ToString(); } else // no special handling for other types/values { result = val; } } return result; }
internal void AddParameter( FieldMap fm ) { AddParameter( null, fm ); }
/// <summary> /// Add a parameter to the current statement. /// </summary> /// <param name="name">The parameter name to use if not the column name defined in the FieldMap</param> /// <param name="fm">The FieldMap describing this parameter</param> internal void AddParameter( string name, FieldMap fm ) { //string paramName = sf.GetParameterPrefix() + (name != null ? name : fm.ColumnName) + sf.GetParameterSuffix(); string paramName = name != null ? name : fm.ColumnName; string paramKey = sf.GetParameterCollectionKey( paramName ); // only add parameters once // Uwe Kitzmann: data providers with positional parameters may need more than one occurence of the same parameter // Uwe Kitzmann: for example "update mytable set field1 = ?, field2 = ? where field1 = ? and field2 = ?" if( ! cmd.Parameters.Contains( paramKey ) || ! sf.HasCapability( Capability.NamedParameters ) ) { if( fm.HasDbType ) { // all insert/update columns must use this method sf.AddParameter( cmd, paramKey, fm.DbType ); } else { sf.AddParameter( cmd, paramKey, fm.Type ); } // also remember the order in which we add parameters (using the original name) parameterOrder.Add( paramName ); } }
/// <summary> /// Obtain the system type of a given column name. /// </summary> /// <param name="columnName">The name of the column</param> /// <returns>The system type of the corresponding property</returns> public Type GetColumnType(string columnName) { FieldMap fm = fields.FindColumn(columnName); return(fm != null ? fm.Type : null); }
protected virtual void InitList(Type viaType, params Type[] relationTypes) { if (listType == GentleListType.StandAlone) { broker.RetrieveList(containedMap.Type, this); } else if (listType == GentleListType.OneToMany) { // no relation objects for 1:n relationships viaInstances = null; mappings = containedMap.GetForeignKeyMappings(parentMap, true); if (mappings.Count == 0) { mappings = viaMap.GetForeignKeyMappings(parentMap, false); } Check.Verify(mappings.Count > 0, Error.DeveloperError, "The type {0} does not contain a foreign key reference to type {1}.", parentMap.Type, containedMap.Type); Check.Verify(mappings.Count == 1, Error.NotImplemented, "GentleList for 1:n relations can not be used with composite keys."); // populate self with any existing entries matching the current parent Key key = new Key(parentMap.Type, true); IDictionaryEnumerator iterator = mappings.GetEnumerator(); while (iterator.MoveNext()) { // construct a key to read the data; first obtain the referenced value from // the parent object (this is the constraint value used in the select) FieldMap fm = iterator.Value as FieldMap; object referencedValue = fm.GetValue(parent); // if class references self make sure to pick the outgoing column if (containedMap.Type == parentMap.Type && !fm.IsForeignKey) { fm = iterator.Key as FieldMap; } key[fm.MemberName] = referencedValue; } broker.RetrieveList(containedMap.Type, key, this); } else if (listType == GentleListType.ManyToMany) { // create relation for n:m management Type[] relatedTypes = Merge(containedMap.Type, relationTypes); viaInstances = new GentleRelation(broker, viaType, parent, relatedTypes); // populate the list with any existing entries matching the current relation entries ObjectMap viaMap = ObjectFactory.GetMap(broker, viaType); SqlBuilder sb = new SqlBuilder(broker, StatementType.Select, containedMap.Type); // assume the relation object is the child, i.e. refers to the contained type mappings = viaMap.GetForeignKeyMappings(containedMap, true); if (mappings.Count == 0) { mappings = viaMap.GetForeignKeyMappings(containedMap, false); } Check.Verify(mappings.Count > 0, Error.DeveloperError, "The type {0} does not contain a foreign key reference to type {1}.", viaMap.Type, containedMap.Type); Check.Verify(mappings.Count == 1, Error.NotImplemented, "GentleList for n:m relations can not be used with composite keys."); // verify that references point to unique instance //Check.Verify( mappings.Count == parentMap.PrimaryKeyCount, Error.DeveloperError, // "The number of fields ({0}) referencing {1} from {2} must equal the primary key count ({3}).", // mappings.Count, parentMap.Type, containedMap.Type, parentMap.PrimaryKeyCount ); if (viaInstances.Count > 0) { foreach (FieldMap remote in mappings.Keys) { FieldMap local = (FieldMap)mappings[remote]; // viaMap.GetForeignKeyFieldMap( containedMap.Type, local.PropertyName ); sb.AddConstraint(Operator.In, local.MemberName, viaInstances, remote.MemberName); } ObjectFactory.GetCollection(containedMap.Type, sb.GetStatement(true).Execute(), this); } } }
public override void Analyze( string tableName ) { GentleSqlFactory sf = provider.GetSqlFactory(); try { bool isSingleRun = tableName != null; // Create foreign key statement SqlStatement fk = broker.GetStatement( StatementType.Select, selectReferences ); sf.AddParameter( fk.Command, "TableName", (long) FbDbType.VarChar ); sf.AddParameter( fk.Command, "ColumnName", (long) FbDbType.VarChar ); // Get tables information SqlResult sr = broker.Execute( select ); // process result set for( int i = 0; i < sr.Rows.Count; i++ ) { try { string dbTableName = sr.GetString( i, "TableName" ).Trim(); if( ! isSingleRun || tableName.ToLower().Equals( dbTableName.ToLower() ) ) { TableMap map = GetTableMap( dbTableName ); if( map == null ) { map = new TableMap( provider, dbTableName ); maps[ dbTableName.ToLower() ] = map; } // get column information for this table string columnName = sr.GetString( i, "ColumnName" ).Trim(); FieldMap fm = map.GetFieldMapFromColumn( columnName ); if( fm == null ) { fm = new FieldMap( map, columnName ); map.Fields.Add( fm ); } FbDbType type = GetFbDbType( sr.GetInt( i, "ColumnDataType" ), sr[ i, "ColumnSubType" ] != null ? sr.GetInt( i, "ColumnSubType" ) : 0, sr[ i, "ColumnScale" ] != null ? sr.GetInt( i, "ColumnScale" ) : 0 ); fm.SetDbType( (long) type ); if( sr[ i, "NullFlag" ] == null ) { fm.SetIsNullable( true ); } else { fm.SetIsNullable( false ); } if( sr[ i, "ColumnSize" ] != null ) { switch( type ) { case FbDbType.Binary: case FbDbType.Text: fm.SetSize( Int32.MaxValue ); break; default: fm.SetSize( sr.GetInt( i, "ColumnSize" ) ); break; } } if( sr.GetInt( i, "PrimaryKey" ) > 0 ) { fm.SetIsPrimaryKey( true ); } if( sr.GetInt( i, "ForeignKey" ) > 0 ) { fk.SetParameter( "TableName", map.TableName.ToUpper() ); fk.SetParameter( "ColumnName", columnName.ToUpper() ); SqlResult res = fk.Execute(); fm.SetForeignKeyTableName( res.GetString( 0, "FKTableName" ).Trim() ); fm.SetForeignKeyColumnName( res.GetString( 0, "FKColumnName" ).Trim() ); } } } catch( GentleException fe ) { // ignore errors caused by tables found in db but for which no map exists // TODO this should be a config option if( fe.Error != Error.NoObjectMapForTable ) { throw fe; } } } } catch( Exception e ) { Check.Fail( e, Error.Unspecified, "An error occurred while analyzing the database schema." ); } }
private void SetParameter( IDataParameter param, object val, FieldMap fm ) { Check.Verify( param.Direction == ParameterDirection.Input || param.Direction == ParameterDirection.InputOutput, "Cannot set value of output parameters!" ); // do additional checking for known types if( map != null && fm != null ) { param.Value = GetParameterValue( val, map, fm, statementType ); } else // unknown type - no clipping or fancy checks, just do it { param.Value = val ?? DBNull.Value; } }
/// <summary> /// Obtain the property name of a given column name. /// </summary> /// <param name="columnName">The name of the column</param> /// <returns>The name of the property</returns> public string GetPropertyName(string columnName) { FieldMap fm = Fields.FindColumn(columnName); return(fm != null ? fm.MemberName : null); }
private object ConvertType( FieldMap fm, object val ) { Check.VerifyNotNull( fm, Error.NullParameter, "fm" ); // convert DBNull to system null if( val != null && val.Equals( DBNull.Value ) ) { val = null; } // perform null handling (NullValue translation) if( val == null && ! fm.IsNullAssignable ) { Check.Verify( fm.NullValue != null, Error.NullWithNoNullValue, fm.ColumnName, fm.Type ); return fm.NullValue; } else { if( val != null ) { Type type = val.GetType(); // trim strings.. otherwise char columns are as wide as their size if( fm.Type == typeof(string) || fm.Type == typeof(Guid) ) { if( fm.Type == typeof(Guid) ) { if( fm.Size == 16 ) // binary compressed version { val = Common.TypeConverter.ToGuid( (string) val ); } else { val = new Guid( val.ToString() ); } } else { string strval = (string) val; // size is 0 for variable width columns // assume we should trim all fixed-width columns val = fm.Size > 0 ? strval.TrimEnd() : strval; } } else if( fm.Type == typeof(bool) && type != typeof(bool) ) { // if property is boolean but database uses integers we need to convert // the type before updating it val = Convert.ToBoolean( val ); } else if( fm.Type.IsEnum && ! type.IsEnum ) { // check whether enum should be stored as string or numeric value // TODO we should check if enum requires 64-bit conversion // val = fm.HandleEnumAsString ? Enum.Parse( fm.Type, Convert.ToString( val ), true ) : Enum.ToObject( fm.Type, Convert.ToInt32( val ) ); val = Common.TypeConverter.Get( fm.Type, val ); } else if( fm.Type == typeof(decimal) && type != typeof(decimal) ) { val = Convert.ToDecimal( val, NumberFormatInfo.InvariantInfo ); } else if( fm.Type != type ) { TypeConverter typeConv = TypeDescriptor.GetConverter( fm.Type ); if( typeConv != null && typeConv.CanConvertFrom( type ) ) { val = typeConv.ConvertFrom( val ); } else { // check for the existence of a TypeConverterAttribute for the field/property object[] attrs = fm.MemberInfo.GetCustomAttributes( typeof(TypeConverterAttribute), false ); if( attrs.Length == 1 ) { TypeConverterAttribute tca = (TypeConverterAttribute) attrs[ 0 ]; TypeConverter typeConverter = (TypeConverter) Activator.CreateInstance( Type.GetType( tca.ConverterTypeName ) ); if( typeConverter != null && typeConverter.CanConvertFrom( val.GetType() ) ) { val = typeConverter.ConvertFrom( val ); } else { val = Convert.ChangeType( val, fm.Type ); } } else { val = Convert.ChangeType( val, fm.Type ); } } } } else { // allow NullValue conversion for null strings if( fm.Type == typeof(string) && fm.NullValue != null ) { val = fm.NullValue; } } return val; } }
private void GetColumnData( TableMap map ) { string sql = String.Format( selectColumns, map.TableName ); SqlStatement stmt = broker.GetStatement( sql ); stmt.StatementType = StatementType.Select; SqlResult sr = stmt.Execute(); for( int i = 0; i < sr.Rows.Count; i++ ) { // returns columns: Field, Type, TypeSize, FieldSize, NotNull, HasDefault, Default string columnName = sr.GetString( i, "field" ); // get or create FieldMap for column FieldMap fm = map.GetFieldMapFromColumn( columnName ); if( fm == null ) { fm = new FieldMap( map, columnName ); map.Fields.Add( fm ); } bool isNullable = sr.GetString( i, "notnull" ).Trim().ToLower().StartsWith( "f" ); if( fm != null ) { bool hasDefault = sr.GetBoolean( i, "hasdefault" ); fm.SetDbType( sr.GetString( i, "type" ), false ); int size = ExtractSize( sr.GetInt( i, "typesize" ), sr.GetInt( i, "fieldsize" ) ); fm.SetSize( size ); fm.SetIsNullable( isNullable ); //fm.SetIsPrimaryKey( sr.GetString( i, "Key" ).Equals( "PRI" ) ); // fm.SetIsForeignKey( sr.GetString( i, "Key" ).Equals( "FOR" ) ); //if( fm.IsPrimaryKey ) // fm.SetIsAutoGenerated( sr.GetString( i, "Key" ).Equals( "auto_increment" ) ); //if( sr.GetString( i, "HasDefault" ).Equals( "t" ) ) // fm.SetMagicValue( sr.GetObject( i, "Default" ) ); } } }
private object ConvertType(FieldMap fm, object val) { Check.VerifyNotNull(fm, Error.NullParameter, "fm"); // convert DBNull to system null if (val != null && val.Equals(DBNull.Value)) { val = null; } // perform null handling (NullValue translation) if (val == null && !fm.IsNullAssignable) { Check.Verify(fm.NullValue != null, Error.NullWithNoNullValue, fm.ColumnName, fm.Type); return(fm.NullValue); } else { if (val != null) { Type type = val.GetType(); // trim strings.. otherwise char columns are as wide as their size if (fm.Type == typeof(string) || fm.Type == typeof(Guid)) { if (fm.Type == typeof(Guid)) { if (fm.Size == 16) // binary compressed version { val = Common.TypeConverter.ToGuid((string)val); } else { val = new Guid(val.ToString()); } } else { string strval = (string)val; // size is 0 for variable width columns // assume we should trim all fixed-width columns val = fm.Size > 0 ? strval.TrimEnd() : strval; } } else if (fm.Type == typeof(bool) && type != typeof(bool)) { // if property is boolean but database uses integers we need to convert // the type before updating it val = Convert.ToBoolean(val); } else if (fm.Type.IsEnum && !type.IsEnum) { // check whether enum should be stored as string or numeric value // TODO we should check if enum requires 64-bit conversion // val = fm.HandleEnumAsString ? Enum.Parse( fm.Type, Convert.ToString( val ), true ) : Enum.ToObject( fm.Type, Convert.ToInt32( val ) ); val = Common.TypeConverter.Get(fm.Type, val); } else if (fm.Type == typeof(decimal) && type != typeof(decimal)) { val = Convert.ToDecimal(val, NumberFormatInfo.InvariantInfo); } else if (fm.Type != type) { TypeConverter typeConv = TypeDescriptor.GetConverter(fm.Type); if (typeConv != null && typeConv.CanConvertFrom(type)) { val = typeConv.ConvertFrom(val); } else { // check for the existence of a TypeConverterAttribute for the field/property object[] attrs = fm.MemberInfo.GetCustomAttributes(typeof(TypeConverterAttribute), false); if (attrs.Length == 1) { TypeConverterAttribute tca = (TypeConverterAttribute)attrs[0]; TypeConverter typeConverter = (TypeConverter) Activator.CreateInstance(Type.GetType(tca.ConverterTypeName)); if (typeConverter != null && typeConverter.CanConvertFrom(val.GetType())) { val = typeConverter.ConvertFrom(val); } else { val = Convert.ChangeType(val, fm.Type); } } else { val = Convert.ChangeType(val, fm.Type); } } } } else { // allow NullValue conversion for null strings if (fm.Type == typeof(string) && fm.NullValue != null) { val = fm.NullValue; } } return(val); } }
private void GetColumnData( TableMap map ) { string sql = String.Format( selectColumns, map.TableName ); SqlStatement stmt = broker.GetStatement( sql ); stmt.StatementType = StatementType.Select; SqlResult sr = stmt.Execute(); for( int i = 0; i < sr.Rows.Count; i++ ) { // returns columns: Field, Type, Null, Key, Default, Extra string columnName = sr.GetString( i, "Field" ); FieldMap fm = map.GetFieldMapFromColumn( columnName ); if( fm == null ) { fm = new FieldMap( map, columnName ); map.Fields.Add( fm ); } if( fm != null ) { string typeinfo = sr.GetString( i, "Type" ); bool isUnsigned; fm.SetDbType( ExtractType( typeinfo, out isUnsigned ), isUnsigned ); if( fm.DbType == (long) MySqlDbType.Enum ) { fm.HandleEnumAsString = true; } fm.SetSize( ExtractSize( typeinfo ) ); fm.SetIsNullable( sr.GetString( i, "Null" ).Equals( "YES" ) ); fm.SetIsPrimaryKey( sr.GetString( i, "Key" ).Equals( "PRI" ) ); if( fm.IsPrimaryKey ) { fm.SetIsAutoGenerated( sr.GetString( i, "Extra" ).Equals( "auto_increment" ) ); } } else // raise an error if we've detected a database/type mismatch { bool hasDefault = sr.GetObject( i, "Default" ) != null; // TODO disabled due to code restructuring // Check.Verify( isNullable || hasDefault, Error.NoPropertyForNotNullColumn, column, map.Type ); } } }
public void RemoveAt(int index) { FieldMap fm = fields[index] as FieldMap; Remove(fm); }
/// <summary> /// TODO FIXME /// This method breaks use of multiple brokers, as it is static and references Broker (and thus DefaultProvider). /// </summary> internal static object GetParameterValue(object val, ObjectMap map, FieldMap fm, StatementType stmtType) { Check.VerifyNotNull(map, Error.NullParameter, "map"); Check.VerifyNotNull(fm, Error.NullParameter, "fm"); object result; // make a quick exit with the type name if the column is used for inheritance mapping if (map.InheritanceMap != null && fm.ColumnName == map.InheritanceMap.ColumnName) { return(map.Type.AssemblyQualifiedName); } // null handling if (fm.IsNull(val)) { // verify that null assignment isn't violating known defintions // dont check value types as they are never nullable, and allow null for autogenerated PKs if (val == null && !fm.IsValueType && !(fm.IsPrimaryKey && fm.IsAutoGenerated)) { Check.Verify(fm.IsNullable, Error.NullProperty, fm.MemberName, map.Type); } result = DBNull.Value; } else { // clip strings to make sure they fit target column if (fm.Type == typeof(string) && val != null && fm.Size > 0 && ((string)val).Length > fm.Size) { result = ((string)val).Substring(0, fm.Size); } // clip DateTime values else if (fm.Type == typeof(DateTime)) { // ensure that datetime values are clipped if outside database range result = EnsureValidDate((DateTime)val); } // convert enum values to numeric value else if (fm.Type.IsEnum) { // use the integer or string value of the enum result = fm.HandleEnumAsString ? Convert.ChangeType(val, typeof(string)) : Convert.ChangeType(val, Enum.GetUnderlyingType(fm.Type)); } // if column is autogenerated primary key use DBNull if an integer value of 0 is given else if (fm.IsAutoGeneratedKeyAndSupportedType && val.Equals(0) && stmtType == StatementType.Insert) { result = DBNull.Value; } else if (fm.Type == typeof(byte[]) || fm.Type == typeof(Byte[])) { MemoryStream stream = new MemoryStream((byte[])val); result = stream.ToArray(); } else if (fm.Type == typeof(Guid) && fm.Size == 16) { result = TypeConverter.ToBinaryString((Guid)val); } else if (fm.Type == typeof(Guid) && Broker.ProviderName.StartsWith("Sybase")) { // Sybase needs the GUID as a string result = val.ToString(); } else // no special handling for other types/values { result = val; } } return(result); }
private void UpdateTableMapWithColumnInformation( TableMap map ) { SqlResult sr = broker.Execute( String.Format( selectColumns, map.TableName ), null, null ); // process result set using columns: cid, name, type, notnull, dflt_value, pk for( int i=0; i<sr.RowsContained; i++ ) { string columnName = sr.GetString( i, "name" ); FieldMap fm = map.GetFieldMapFromColumn( columnName ); if( fm == null ) { fm = new FieldMap( map, columnName ); map.Fields.Add( fm ); } // get basic column information fm.SetDbType( sr.GetString( i, "type" ), false ); fm.SetIsNullable( ! sr.GetBoolean( i, "notnull" ) ); fm.SetIsPrimaryKey( sr.GetBoolean( i, "pk" ) ); fm.SetIsAutoGenerated( fm.IsPrimaryKey && (fm.Type == typeof(int) || fm.Type == typeof(long)) ); } }
/// <summary> /// This method fills the TableMap with information on table columns. /// </summary> private void GetColumnData( TableMap map ) { DataTable dt = GetColumns( map.TableName ); foreach( DataRow row in dt.Rows ) { // result row contains: // COLUMN_NAME, DATA_TYPE, ORDINAL_POSITION, COLUMN_HASDEFAULT, COLUMN_DEFAULT, // COLUMN_FLAGS, IS_NULLABLE, NUMERIC_PRECISION, NUMERIC_SCALE, // CHARACTER_MAXIMUM_LENGTH, CHARACTER_OCTET_LENGTH string columnName = (string) row[ "COLUMN_NAME" ]; FieldMap fm = map.GetFieldMapFromColumn( columnName ); if( fm == null ) { fm = new FieldMap( map, columnName ); map.Fields.Add( fm ); } bool isNullable = Convert.ToBoolean( row[ "IS_NULLABLE" ] ); if( fm != null ) { OleDbType dbType = (OleDbType) row[ "DATA_TYPE" ]; fm.SetDbType( (long) dbType ); // set numeric scale for DBTYPE_DECIMAL, DBTYPE_NUMERIC, DBTYPE_VARNUMERIC if( dbType == OleDbType.Decimal || dbType == OleDbType.Numeric || dbType == OleDbType.VarNumeric ) { fm.SetSize( Convert.ToInt32( row[ "NUMERIC_PRECISION" ] ) ); } if( dbType == OleDbType.LongVarBinary || dbType == OleDbType.LongVarChar || dbType == OleDbType.LongVarWChar || dbType == OleDbType.VarBinary || dbType == OleDbType.VarChar || dbType == OleDbType.VarWChar || dbType == OleDbType.WChar || dbType == OleDbType.Char || dbType == OleDbType.BSTR || dbType == OleDbType.Binary ) { fm.SetSize( Convert.ToInt32( row[ "CHARACTER_MAXIMUM_LENGTH" ] ) ); } fm.SetIsNullable( isNullable ); int columnFlags = Convert.ToInt32( row[ "COLUMN_FLAGS" ] ); // BROKEN (expected value does not match IS_NULLABLE set above) // BROKEN set whether column can contain NULL values int flags = (int) DBCOLUMNFLAGS.ISNULLABLE + (int) DBCOLUMNFLAGS.MAYBENULL; bool isNullableFlag = (columnFlags & flags) != 0; //fm.SetIsNullable( isNullableFlag && fm.IsNullable ); // set whether column is updatable flags = (int) DBCOLUMNFLAGS.WRITE + (int) DBCOLUMNFLAGS.WRITEUNKNOWN; bool isReadOnly = (columnFlags & flags) == 0; fm.IsReadOnly = isReadOnly; // BROKEN (expected bitmask value is never set) // set whether column is auto-generated //flags = (int) DBCOLUMNFLAGS.ISROWID; //bool isAutoGenerated = (columnFlags & flags) != 0; //fm.SetIsAutoGenerated( isAutoGenerated ); } else // raise an error if we've detected a database/type mismatch { bool hasDefault = Convert.ToBoolean( row[ "COLUMN_HASDEFAULT" ] ); // TODO disabled due to code restructuring Check.Verify( isNullable || hasDefault, Error.NoPropertyForNotNullColumn, columnName, map.TableName ); } } }