| 0 | 1 <# | 
|  | 2 	ConnectionType = "MySql.Data.MySqlClient.MySqlConnection, MySql.Data"; | 
|  | 3 #><#+ | 
|  | 4 | 
|  | 5 private void LoadServerMetadata() | 
|  | 6 { | 
|  | 7 	var tables  = CreateList(new { ID = "", Table  = new Table() }); | 
|  | 8 	var columns = CreateList(new { ID = "", Column = new Column() }); | 
|  | 9 | 
|  | 10 	using (var conn = GetConnection()) | 
|  | 11 	using (var cmd  = conn.CreateCommand()) | 
|  | 12 	{ | 
|  | 13 		string schemasToSkip = "'information_schema', 'cr_debug', 'mysql'"; | 
|  | 14 | 
|  | 15 		// Load tables & views. | 
|  | 16 		// | 
|  | 17 | 
|  | 18 		cmd.CommandText = "SELECT DATABASE()"; | 
|  | 19 		_databaseName = Convert.ToString(cmd.ExecuteScalar()); | 
|  | 20 | 
|  | 21 		cmd.CommandText = string.Format(@" | 
|  | 22 			SELECT | 
|  | 23 				CONCAT(TABLE_SCHEMA, '.', TABLE_NAME), | 
|  | 24 				TABLE_SCHEMA, | 
|  | 25 				TABLE_NAME, | 
|  | 26 				TABLE_TYPE | 
|  | 27 			FROM | 
|  | 28 				INFORMATION_SCHEMA.tables | 
|  | 29 			WHERE | 
|  | 30 				TABLE_SCHEMA NOT IN ({0}) AND | 
|  | 31 				TABLE_TYPE IN ('BASE TABLE', 'VIEW') | 
|  | 32 				{1}", | 
|  | 33 			schemasToSkip, | 
|  | 34 			GetDatabaseSqlFilter("")); | 
|  | 35 | 
|  | 36 		using (var rd = cmd.ExecuteReader()) | 
|  | 37 		{ | 
|  | 38 			while (rd.Read()) | 
|  | 39 			{ | 
|  | 40 				var t = new | 
|  | 41 				{ | 
|  | 42 					ID    = Convert.ToString(rd[0]), | 
|  | 43 					Table = new Table | 
|  | 44 					{ | 
|  | 45 						Owner         			= null, // there is no concept of table owner in MySql | 
|  | 46 						TableName     			= rd[2].ToString(), | 
|  | 47 						ClassName     			= rd[2].ToString(), | 
|  | 48 						DataContextPropertyName	= rd[2].ToString(), | 
|  | 49 						IsView        			= rd[3].ToString() == "VIEW", | 
|  | 50 						BaseClassName 			= BaseEntityClass, | 
|  | 51 					} | 
|  | 52 				}; | 
|  | 53 | 
|  | 54 				tables.Add(t); | 
|  | 55 			} | 
|  | 56 		} | 
|  | 57 | 
|  | 58 		cmd.CommandText = "SELECT @@session.sql_mode"; | 
|  | 59 		string sqlMode = Convert.ToString(cmd.ExecuteScalar()); | 
|  | 60 | 
|  | 61 		// Load columns. | 
|  | 62 		// | 
|  | 63 		cmd.CommandText = string.Format(@" | 
|  | 64 			SELECT | 
|  | 65 				CONCAT(TABLE_SCHEMA, '.', TABLE_NAME)           as id, | 
|  | 66 				CASE WHEN IS_NULLABLE = 'YES' THEN 1 ELSE 0 END as isNullable, | 
|  | 67 				ORDINAL_POSITION         as colid, | 
|  | 68 				COLUMN_NAME              as name, | 
|  | 69 				c.DATA_TYPE              as dataType, | 
|  | 70 				CHARACTER_MAXIMUM_LENGTH as length, | 
|  | 71 				NUMERIC_PRECISION        as prec, | 
|  | 72 				NUMERIC_SCALE            as scale, | 
|  | 73 				EXTRA = 'auto_increment' as isIdentity, | 
|  | 74  				c.COLUMN_TYPE            as columnType, | 
|  | 75 				COLUMN_DEFAULT           as columnDefault, | 
|  | 76 				EXTRA                    as extra | 
|  | 77 			FROM | 
|  | 78 				INFORMATION_SCHEMA.COLUMNS c | 
|  | 79 			WHERE | 
|  | 80 				TABLE_SCHEMA NOT IN ({0}) | 
|  | 81 				{1}", | 
|  | 82 			schemasToSkip, | 
|  | 83 			GetDatabaseSqlFilter("")); | 
|  | 84 | 
|  | 85 		using (var rd = cmd.ExecuteReader()) | 
|  | 86 		{ | 
|  | 87 			while (rd.Read()) | 
|  | 88 			{ | 
|  | 89 				var col = new | 
|  | 90 				{ | 
|  | 91 					ID     = Convert.ToString(rd["id"]), | 
|  | 92 					Column = new Column | 
|  | 93 					{ | 
|  | 94 						ID            = Convert.ToInt16  (rd["colid"]), | 
|  | 95 						ColumnName    = Convert.ToString (rd["name"]), | 
|  | 96 						MemberName    = Convert.ToString (rd["name"]), | 
|  | 97 						ColumnType    = Convert.ToString (rd["dataType"]), | 
|  | 98 						IsNullable    = Convert.ToBoolean(rd["isNullable"]), | 
|  | 99 						IsIdentity    = Convert.ToBoolean(rd["isIdentity"]), | 
|  | 100 						Length        = rd.IsDBNull(rd.GetOrdinal("length")) ? 0 : Convert.ToInt64(rd["length"]), | 
|  | 101 						Precision     = rd.IsDBNull(rd.GetOrdinal("prec"))   ? 0 : Convert.ToInt32(rd["prec"]), | 
|  | 102 						Scale         = rd.IsDBNull(rd.GetOrdinal("scale"))  ? 0 : Convert.ToInt32(rd["scale"]), | 
|  | 103 						ColumnDefault = Convert.ToString(rd["columnDefault"]), | 
|  | 104 						Extra         = Convert.ToString(rd["extra"]).ToUpper() | 
|  | 105 					} | 
|  | 106 				}; | 
|  | 107 | 
|  | 108 				var c           = col.Column; | 
|  | 109 				var columnType  = Convert.ToString(rd["columnType"]).ToLower(); | 
|  | 110 				var unsigned    = columnType.Contains("unsigned"); | 
|  | 111 				var realAsFloat = sqlMode.ToLower().Contains("real_as_float"); | 
|  | 112 | 
|  | 113 				switch (c.ColumnType) | 
|  | 114 				{ | 
|  | 115 					case "longtext"   : | 
|  | 116 					case "mediumtext" : | 
|  | 117 					case "tinytext"   : | 
|  | 118 					case "text"       : c.Type = "string";   c.DbType = DbType.String;   c.SqlDbType = SqlDbType.Text;      break; | 
|  | 119 					case "binary"     : c.Type = "byte[]";   c.DbType = DbType.Binary;   c.SqlDbType = SqlDbType.Binary;    break; | 
|  | 120 					case "date"       : c.Type = "DateTime"; c.DbType = DbType.Date;     c.SqlDbType = SqlDbType.Date;      break; | 
|  | 121 					case "time"       : c.Type = "DateTime"; c.DbType = DbType.Time;     c.SqlDbType = SqlDbType.Time;      break; | 
|  | 122 					case "bit"        : c.Type = "bool";     c.DbType = DbType.Boolean;  c.SqlDbType = SqlDbType.Bit;       break; | 
|  | 123 					case "numeric"    : | 
|  | 124 					case "decimal"    : | 
|  | 125 					case "dec"        : | 
|  | 126 					case "fixed"      : c.Type = "decimal";  c.DbType = DbType.Decimal;  c.SqlDbType = SqlDbType.Decimal;   break; | 
|  | 127 					case "datetime"   : c.Type = "DateTime"; c.DbType = DbType.DateTime; c.SqlDbType = SqlDbType.DateTime;  break; | 
|  | 128 					case "float"      : c.Type = "float";    c.DbType = DbType.Single;   c.SqlDbType = SqlDbType.Float;     break; | 
|  | 129 					case "double"     : c.Type = "double";   c.DbType = DbType.Double;   c.SqlDbType = SqlDbType.Float;     break; | 
|  | 130 					case "varbinary"  : c.Type = "byte[]";   c.DbType = DbType.Binary;   c.SqlDbType = SqlDbType.VarBinary; break; | 
|  | 131 					case "varchar"    : c.Type = "string";   c.DbType = DbType.String;   c.SqlDbType = SqlDbType.VarChar;   break; | 
|  | 132 					case "year"       : c.Type = "DateTime"; c.DbType = DbType.Date;     c.SqlDbType = SqlDbType.Date;      break; | 
|  | 133 					case "enum"       : | 
|  | 134 					case "set"        : c.Type = "string";   c.DbType = DbType.String;   c.SqlDbType = SqlDbType.VarChar;   break; | 
|  | 135 					case "bool"       : | 
|  | 136 					case "boolean"    : c.Type = "bool";     c.DbType = DbType.Boolean;  c.SqlDbType = SqlDbType.Bit;       break; | 
|  | 137 					case "serial"     : c.Type = "ulong";    c.DbType = DbType.UInt64;   c.SqlDbType = SqlDbType.BigInt;    break; | 
|  | 138 					case "mediumblob" : | 
|  | 139 					case "longblob"   : | 
|  | 140 					case "blob"       : c.Type = "byte[]";   c.DbType = DbType.Binary;   c.SqlDbType = SqlDbType.Image;     break; | 
|  | 141 					case "tinyblob"   : c.Type = "byte[]";   c.DbType = DbType.Binary;   c.SqlDbType = SqlDbType.Binary;    break; | 
|  | 142 | 
|  | 143 					case "smallint"   : c.Type = unsigned    ? "ushort" : "short"; c.DbType = unsigned ?    DbType.UInt16 : DbType.Int16;  c.SqlDbType = SqlDbType.SmallInt; break; | 
|  | 144 					case "mediumint"  : | 
|  | 145 					case "int"        : | 
|  | 146 					case "integer"    : c.Type = unsigned    ? "uint"  : "int";    c.DbType = unsigned    ? DbType.UInt32 : DbType.Int32;  c.SqlDbType = SqlDbType.Int;      break; | 
|  | 147 					case "real"       : c.Type = realAsFloat ? "float" : "double"; c.DbType = realAsFloat ? DbType.Single : DbType.Double; c.SqlDbType = SqlDbType.Real;     break; | 
|  | 148 					case "bigint"     : c.Type = unsigned    ? "ulong" : "long";   c.DbType = unsigned    ? DbType.UInt64 : DbType.Int64;  c.SqlDbType = SqlDbType.BigInt;   break; | 
|  | 149 					case "char"       : c.Type = "string";   c.DbType = DbType.StringFixedLength; c.SqlDbType = SqlDbType.Char; break; | 
|  | 150 					case "timestamp"  : | 
|  | 151 						c.Type = "DateTime"; | 
|  | 152 						c.DbType = DbType.DateTime; | 
|  | 153 						c.SqlDbType = SqlDbType.Timestamp; | 
|  | 154 | 
|  | 155 						if(c.ColumnDefault == "CURRENT_TIMESTAMP" || c.Extra.Contains("ON UPDATE CURRENT_TIMESTAMP")) | 
|  | 156 						{ | 
|  | 157 							c.Attributes.Add(string.Format( | 
|  | 158 								"NonUpdatable(OnInsert = {0}, OnUpdate = {1})", | 
|  | 159 								c.ColumnDefault == "CURRENT_TIMESTAMP" ? "true" : "false", | 
|  | 160 								c.Extra.Contains("ON UPDATE CURRENT_TIMESTAMP") ? "true" : "false")); | 
|  | 161 						} | 
|  | 162 						break; | 
|  | 163 					case "tinyint"    : | 
|  | 164 						if(columnType == "tinyint(1)") | 
|  | 165 						{ | 
|  | 166 							c.Type = "bool"; | 
|  | 167 							c.DbType = DbType.Boolean; | 
|  | 168 							c.SqlDbType = SqlDbType.Bit; | 
|  | 169 						} | 
|  | 170 						else | 
|  | 171 						{ | 
|  | 172 							c.Type = unsigned ? "byte" : "sbyte"; | 
|  | 173 							c.DbType = unsigned ? DbType.Byte : DbType.SByte; | 
|  | 174 							c.SqlDbType = SqlDbType.TinyInt; | 
|  | 175 						} | 
|  | 176 						break; | 
|  | 177 					default           : | 
|  | 178 						throw new System.IO.InvalidDataException(string.Format("Unknown column type: {0}.", c.ColumnType)); | 
|  | 179 				} | 
|  | 180 | 
|  | 181 				switch (c.Type) | 
|  | 182 				{ | 
|  | 183 					case "string" : | 
|  | 184 					case "byte[]" : c.IsClass = true; break; | 
|  | 185 				} | 
|  | 186 | 
|  | 187 				if (c.IsNullable && !c.IsClass) | 
|  | 188 					c.Type += "?"; | 
|  | 189 | 
|  | 190 				columns.Add(col); | 
|  | 191 			} | 
|  | 192 		} | 
|  | 193 | 
|  | 194 		// Load PKs. | 
|  | 195 		// | 
|  | 196 		cmd.CommandText = string.Format(@" | 
|  | 197 			SELECT | 
|  | 198 				CONCAT(k.TABLE_SCHEMA, '.', k.TABLE_NAME) as id, | 
|  | 199 				k.CONSTRAINT_NAME                         as name, | 
|  | 200 				k.COLUMN_NAME                             as colname, | 
|  | 201 				k.ORDINAL_POSITION                        as colid | 
|  | 202 			FROM | 
|  | 203 				INFORMATION_SCHEMA.KEY_COLUMN_USAGE k | 
|  | 204 					JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS c ON k.CONSTRAINT_NAME = c.CONSTRAINT_NAME | 
|  | 205 			WHERE | 
|  | 206 				c.CONSTRAINT_TYPE='PRIMARY KEY' AND | 
|  | 207 				k.TABLE_SCHEMA NOT IN ({0}) | 
|  | 208 				{1} | 
|  | 209 			GROUP BY id, colid", | 
|  | 210 			schemasToSkip, | 
|  | 211 			GetDatabaseSqlFilter("k")); | 
|  | 212 | 
|  | 213 		using (var rd = cmd.ExecuteReader()) | 
|  | 214 		{ | 
|  | 215 			while (rd.Read()) | 
|  | 216 			{ | 
|  | 217 				var id      = Convert.ToString(rd["id"]); | 
|  | 218 				var colid   = Convert.ToInt32 (rd["colid"]); | 
|  | 219 				var colname = Convert.ToString(rd["colname"]); | 
|  | 220 | 
|  | 221 				columns.Single(_ => _.ID == id && _.Column.ColumnName == colname).Column.PKIndex = colid; | 
|  | 222 			} | 
|  | 223 		} | 
|  | 224 | 
|  | 225 		// Load FKs. | 
|  | 226 		// | 
|  | 227 		cmd.CommandText = string.Format(@" | 
|  | 228 			SELECT | 
|  | 229 				CONSTRAINT_NAME  as Name, | 
|  | 230 				CONCAT(TABLE_SCHEMA, '.', TABLE_NAME) as ThisTable, | 
|  | 231 				COLUMN_NAME      as ThisColumn, | 
|  | 232 				CONCAT(REFERENCED_TABLE_SCHEMA, '.', REFERENCED_TABLE_NAME) as OtherTable, | 
|  | 233 				REFERENCED_COLUMN_NAME      as OtherColumn, | 
|  | 234 				ORDINAL_POSITION as Ordinal | 
|  | 235 			FROM | 
|  | 236 				INFORMATION_SCHEMA.key_column_usage | 
|  | 237 			WHERE | 
|  | 238 				TABLE_SCHEMA NOT IN ({0}) AND | 
|  | 239 				REFERENCED_TABLE_NAME IS NOT NULL AND | 
|  | 240     			REFERENCED_COLUMN_NAME IS NOT NULL | 
|  | 241     			{1} | 
|  | 242 			ORDER BY | 
|  | 243 				ThisTable, | 
|  | 244 				Ordinal", | 
|  | 245 			schemasToSkip, | 
|  | 246 			GetDatabaseSqlFilter("")); | 
|  | 247 | 
|  | 248 		if(!string.IsNullOrEmpty(DatabaseName)) | 
|  | 249 			cmd.CommandText += string.Format(" AND TABLE_SCHEMA = '{0}' ", DatabaseName); | 
|  | 250 | 
|  | 251 		using (var rd = cmd.ExecuteReader()) | 
|  | 252 		{ | 
|  | 253 			while (rd.Read()) | 
|  | 254 			{ | 
|  | 255 				var name            = Convert.ToString(rd["Name"]); | 
|  | 256 				var thisTableID     = Convert.ToString(rd["ThisTable"]); | 
|  | 257 				var otherTableID    = Convert.ToString(rd["OtherTable"]); | 
|  | 258 				var thisColumnName  = Convert.ToString(rd["ThisColumn"]); | 
|  | 259 				var otherColumnName = Convert.ToString(rd["OtherColumn"]); | 
|  | 260 | 
|  | 261 				var thisTable   = (from t in tables  where t.ID == thisTableID  select t.Table).Single(); | 
|  | 262 				var otherTable  = (from t in tables  where t.ID == otherTableID select t.Table).Single(); | 
|  | 263 				var thisColumn  = (from c in columns where c.ID == thisTableID  && c.Column.ColumnName == thisColumnName  select c.Column).Single(); | 
|  | 264 				var otherColumn = (from c in columns where c.ID == otherTableID && c.Column.ColumnName == otherColumnName select c.Column).Single(); | 
|  | 265 | 
|  | 266 				if (thisTable.ForeignKeys.ContainsKey(name) == false) | 
|  | 267 					thisTable.ForeignKeys.Add(name, new ForeignKey { KeyName = name, MemberName = name, OtherTable = otherTable }); | 
|  | 268 | 
|  | 269 				var key = thisTable.ForeignKeys[name]; | 
|  | 270 | 
|  | 271 				key.ThisColumns. Add(thisColumn); | 
|  | 272 				key.OtherColumns.Add(otherColumn); | 
|  | 273 			} | 
|  | 274 		} | 
|  | 275 	} | 
|  | 276 | 
|  | 277 	var qc = | 
|  | 278 		from c in columns | 
|  | 279 		group c by c.ID into gr | 
|  | 280 		join t in tables on gr.Key equals t.ID | 
|  | 281 		select new { t.Table, gr }; | 
|  | 282 | 
|  | 283 	foreach (var c in qc) | 
|  | 284 	{ | 
|  | 285 		foreach (var col in from col in c.gr orderby col.Column.ID select col.Column) | 
|  | 286 			c.Table.Columns.Add(col.ColumnName, col); | 
|  | 287 | 
|  | 288 		// there is no concept of table owner in MySql so we just use table name | 
|  | 289 		Tables.Add(c.Table.TableName, c.Table); | 
|  | 290 	} | 
|  | 291 } | 
|  | 292 | 
|  | 293 private string _databaseName; | 
|  | 294 private string GetDatabaseSqlFilter(string optionalTablePrefix) | 
|  | 295 { | 
|  | 296 	if(string.IsNullOrEmpty(_databaseName)) return ""; | 
|  | 297 	string tablePrefix = string.IsNullOrEmpty(optionalTablePrefix) ? "" : optionalTablePrefix + "."; | 
|  | 298 	return string.Format(" AND {0}TABLE_SCHEMA = '{1}' ", tablePrefix, _databaseName); | 
|  | 299 } | 
|  | 300 | 
|  | 301 public partial class Column | 
|  | 302 { | 
|  | 303 	public string ColumnDefault; | 
|  | 304 	public string Extra; | 
|  | 305 } | 
|  | 306 | 
|  | 307 #> |