1 //--------------------------------------------------------------------- 2 // <copyright file="FunctionDetailsReader.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 // 6 // @owner Microsoft 7 // @backupOwner Microsoft 8 //--------------------------------------------------------------------- 9 using System; 10 using System.Collections.Generic; 11 using System.Text; 12 using System.Data.Common; 13 using System.Data.EntityClient; 14 using System.Diagnostics; 15 using System.Data.Metadata.Edm; 16 17 namespace System.Data.Entity.Design.SsdlGenerator 18 { 19 /// <summary> 20 /// The purpose of this class is to give us strongly typed access to the results of the reader. 21 /// NOTE: this class will dispose of the command when the reader is disposed. 22 /// </summary> 23 internal abstract class FunctionDetailsReader : IDisposable 24 { 25 private DbDataReader _reader; 26 private EntityCommand _command; 27 private EntityConnection _connection; 28 private object[] _currentRow; 29 Create(EntityConnection connection, IEnumerable<EntityStoreSchemaFilterEntry> filters, Version storeSchemaModelVersion)30 public static FunctionDetailsReader Create(EntityConnection connection, IEnumerable<EntityStoreSchemaFilterEntry> filters, Version storeSchemaModelVersion) 31 { 32 Debug.Assert(connection != null, "the parameter connection is null"); 33 Debug.Assert(connection.State == ConnectionState.Open, "the connection is not Open"); 34 35 if (storeSchemaModelVersion >= EntityFrameworkVersions.Version3) 36 { 37 return new FunctionDetailsReaderV3(connection, filters); 38 } 39 else 40 { 41 return new FunctionDetailsReaderV1(connection, filters); 42 } 43 } 44 InitializeReader(EntityConnection connection, IEnumerable<EntityStoreSchemaFilterEntry> filters)45 protected void InitializeReader(EntityConnection connection, IEnumerable<EntityStoreSchemaFilterEntry> filters) 46 { 47 _connection = connection; 48 49 _command = EntityStoreSchemaGeneratorDatabaseSchemaLoader.CreateFilteredCommand( 50 _connection, 51 FunctionDetailSql, 52 FunctionOrderByClause, 53 EntityStoreSchemaFilterObjectTypes.Function, 54 new List<EntityStoreSchemaFilterEntry>(filters), 55 new string[] { FunctionDetailAlias }); 56 _reader = _command.ExecuteReader(CommandBehavior.SequentialAccess); 57 } 58 Read()59 internal bool Read() 60 { 61 Debug.Assert(_reader != null, "don't Read() when it is created from a memento"); 62 bool haveRow = _reader.Read(); 63 if (haveRow) 64 { 65 if (_currentRow == null) 66 { 67 _currentRow = new object[ColumnCount]; 68 } 69 _reader.GetValues(_currentRow); 70 } 71 else 72 { 73 _currentRow = null; 74 } 75 return haveRow; 76 } 77 Dispose()78 public void Dispose() 79 { 80 // Technically, calling GC.SuppressFinalize is not required because the class does not 81 // have a finalizer, but it does no harm, protects against the case where a finalizer is added 82 // in the future, and prevents an FxCop warning. 83 GC.SuppressFinalize(this); 84 Debug.Assert(_reader != null, "don't Dispose() when it is created from a memento"); 85 _reader.Dispose(); 86 _command.Dispose(); 87 } 88 89 internal abstract int ColumnCount { get; } 90 91 public abstract string Catalog { get; } 92 93 public abstract string Schema { get; } 94 95 public abstract string ProcedureName { get; } 96 97 public abstract string ReturnType { get; } 98 99 public abstract bool IsIsAggregate { get; } 100 101 public abstract bool IsBuiltIn { get; } 102 103 public abstract bool IsComposable { get; } 104 105 public abstract bool IsNiladic { get; } 106 107 public abstract bool IsTvf { get; } 108 109 public abstract string ParameterName { get; } 110 111 public abstract bool IsParameterNameNull { get; } 112 113 public abstract string ParameterType { get; } 114 115 public abstract bool IsParameterTypeNull { get; } 116 117 public abstract string ProcParameterMode { get; } 118 119 public abstract bool IsParameterModeNull { get; } 120 TryGetParameterMode(out ParameterMode mode)121 public bool TryGetParameterMode(out ParameterMode mode) 122 { 123 if (IsParameterModeNull) 124 { 125 mode = (ParameterMode)(-1); 126 return false; 127 } 128 129 switch (ProcParameterMode) 130 { 131 case "IN": 132 mode = ParameterMode.In; 133 return true; 134 case "OUT": 135 mode = ParameterMode.Out; 136 return true; 137 case "INOUT": 138 mode = ParameterMode.InOut; 139 return true; 140 default: 141 mode = (ParameterMode)(-1); 142 return false; 143 } 144 } 145 CreateDbObjectKey()146 internal EntityStoreSchemaGenerator.DbObjectKey CreateDbObjectKey() 147 { 148 Debug.Assert(_currentRow != null, "don't call this method when you not reading"); 149 return new EntityStoreSchemaGenerator.DbObjectKey(this.Catalog, this.Schema, this.ProcedureName, EntityStoreSchemaGenerator.DbObjectType.Function); 150 } 151 ConvertDBNull(object value)152 protected static T ConvertDBNull<T>(object value) 153 { 154 return Convert.IsDBNull(value) ? default(T) : (T)value; 155 } 156 Attach(Memento memento)157 public abstract void Attach(Memento memento); 158 CreateMemento()159 public abstract Memento CreateMemento(); 160 161 private static readonly string FunctionDetailAlias = "sp"; 162 protected abstract string FunctionDetailSql { get; } 163 private static readonly string FunctionOrderByClause = @" 164 ORDER BY 165 sp.SchemaName 166 , sp.Name 167 , sp.Ordinal 168 "; 169 170 internal sealed class FunctionDetailsReaderV1 : FunctionDetailsReader 171 { FunctionDetailsReaderV1(MementoV1 memento)172 internal FunctionDetailsReaderV1(MementoV1 memento) 173 { 174 _currentRow = memento.Values; 175 } 176 FunctionDetailsReaderV1(EntityConnection connection, IEnumerable<EntityStoreSchemaFilterEntry> filters)177 public FunctionDetailsReaderV1(EntityConnection connection, IEnumerable<EntityStoreSchemaFilterEntry> filters) 178 { 179 InitializeReader(connection, filters); 180 } 181 Attach(Memento memento)182 public override void Attach(Memento memento) 183 { 184 Debug.Assert(memento != null, "the parameter memento is null"); 185 Debug.Assert(memento.Values != null, "the values in the memento are null"); 186 Debug.Assert(memento is MementoV1, "the memento is for a different version"); 187 Debug.Assert(_reader == null, "don't attach to a real reader"); 188 _currentRow = memento.Values; 189 } 190 CreateMemento()191 public override Memento CreateMemento() 192 { 193 Debug.Assert(_currentRow != null, "don't call this method when you not reading"); 194 return new MementoV1((object[])_currentRow.Clone()); 195 } 196 197 const int PROC_SCHEMA_INDEX = 0; 198 const int PROC_NAME_INDEX = 1; 199 const int PROC_RET_TYPE_INDEX = 2; 200 const int PROC_ISAGGREGATE_INDEX = 3; 201 const int PROC_ISCOMPOSABLE_INDEX = 4; 202 const int PROC_ISBUILTIN_INDEX = 5; 203 const int PROC_ISNILADIC_INDEX = 6; 204 const int PARAM_NAME_INDEX = 7; 205 const int PARAM_TYPE_INDEX = 8; 206 const int PARAM_DIRECTION_INDEX = 9; 207 208 internal override int ColumnCount { get { return 10; } } 209 210 public override string Catalog { get { return null; } } 211 212 public override string Schema 213 { 214 get { return ConvertDBNull<string>(_currentRow[PROC_SCHEMA_INDEX]); } 215 } 216 217 public override string ProcedureName 218 { 219 get { return ConvertDBNull<string>(_currentRow[PROC_NAME_INDEX]); } 220 } 221 222 public override string ReturnType 223 { 224 get { return ConvertDBNull<string>(_currentRow[PROC_RET_TYPE_INDEX]); } 225 } 226 227 public override bool IsIsAggregate 228 { 229 get { return ConvertDBNull<bool>(_currentRow[PROC_ISAGGREGATE_INDEX]); } 230 } 231 232 public override bool IsBuiltIn 233 { 234 get { return ConvertDBNull<bool>(_currentRow[PROC_ISBUILTIN_INDEX]); } 235 } 236 237 public override bool IsComposable 238 { 239 get { return ConvertDBNull<bool>(_currentRow[PROC_ISCOMPOSABLE_INDEX]); } 240 } 241 242 public override bool IsNiladic 243 { 244 get { return ConvertDBNull<bool>(_currentRow[PROC_ISNILADIC_INDEX]); } 245 } 246 247 public override bool IsTvf { get { return false; } } 248 249 public override string ParameterName 250 { 251 get { return (string)_currentRow[PARAM_NAME_INDEX]; } 252 } 253 254 public override bool IsParameterNameNull 255 { 256 get { return Convert.IsDBNull(_currentRow[PARAM_NAME_INDEX]); } 257 } 258 259 public override string ParameterType 260 { 261 get { return (string)_currentRow[PARAM_TYPE_INDEX]; } 262 } 263 264 public override bool IsParameterTypeNull 265 { 266 get { return Convert.IsDBNull(_currentRow[PARAM_TYPE_INDEX]); } 267 } 268 269 public override string ProcParameterMode 270 { 271 get { return (string)_currentRow[PARAM_DIRECTION_INDEX]; } 272 } 273 274 public override bool IsParameterModeNull 275 { 276 get { return Convert.IsDBNull(_currentRow[PARAM_DIRECTION_INDEX]); } 277 } 278 279 protected override string FunctionDetailSql 280 { 281 get 282 { 283 return @" 284 SELECT 285 sp.SchemaName 286 , sp.Name 287 , sp.ReturnTypeName 288 , sp.IsAggregate 289 , sp.IsComposable 290 , sp.IsBuiltIn 291 , sp.IsNiladic 292 , sp.ParameterName 293 , sp.ParameterType 294 , sp.Mode 295 FROM ( 296 (SELECT 297 r.CatalogName as CatalogName 298 , r.SchemaName as SchemaName 299 , r.Name as Name 300 , r.ReturnType.TypeName as ReturnTypeName 301 , r.IsAggregate as IsAggregate 302 , true as IsComposable 303 , r.IsBuiltIn as IsBuiltIn 304 , r.IsNiladic as IsNiladic 305 , p.Name as ParameterName 306 , p.ParameterType.TypeName as ParameterType 307 , p.Mode as Mode 308 , p.Ordinal as Ordinal 309 FROM 310 OfType(SchemaInformation.Functions, Store.ScalarFunction) as r 311 OUTER APPLY 312 r.Parameters as p) 313 UNION ALL 314 (SELECT 315 r.CatalogName as CatalogName 316 , r.SchemaName as SchemaName 317 , r.Name as Name 318 , CAST(NULL as string) as ReturnTypeName 319 , false as IsAggregate 320 , false as IsComposable 321 , false as IsBuiltIn 322 , false as IsNiladic 323 , p.Name as ParameterName 324 , p.ParameterType.TypeName as ParameterType 325 , p.Mode as Mode 326 , p.Ordinal as Ordinal 327 FROM 328 SchemaInformation.Procedures as r 329 OUTER APPLY 330 r.Parameters as p)) as sp 331 "; 332 } 333 } 334 } 335 336 internal sealed class FunctionDetailsReaderV3 : FunctionDetailsReader 337 { FunctionDetailsReaderV3(MementoV3 memento)338 internal FunctionDetailsReaderV3(MementoV3 memento) 339 { 340 _currentRow = memento.Values; 341 } 342 FunctionDetailsReaderV3(EntityConnection connection, IEnumerable<EntityStoreSchemaFilterEntry> filters)343 public FunctionDetailsReaderV3(EntityConnection connection, IEnumerable<EntityStoreSchemaFilterEntry> filters) 344 { 345 InitializeReader(connection, filters); 346 } 347 Attach(Memento memento)348 public override void Attach(Memento memento) 349 { 350 Debug.Assert(memento != null, "the parameter memento is null"); 351 Debug.Assert(memento.Values != null, "the values in the memento are null"); 352 Debug.Assert(memento is MementoV3, "the memento is for a different version"); 353 Debug.Assert(_reader == null, "don't attach to a real reader"); 354 _currentRow = memento.Values; 355 } 356 CreateMemento()357 public override Memento CreateMemento() 358 { 359 Debug.Assert(_currentRow != null, "don't call this method when you not reading"); 360 return new MementoV3((object[])_currentRow.Clone()); 361 } 362 363 const int PROC_CATALOG_INDEX = 0; 364 const int PROC_SCHEMA_INDEX = 1; 365 const int PROC_NAME_INDEX = 2; 366 const int PROC_RET_TYPE_INDEX = 3; 367 const int PROC_ISAGGREGATE_INDEX = 4; 368 const int PROC_ISCOMPOSABLE_INDEX = 5; 369 const int PROC_ISBUILTIN_INDEX = 6; 370 const int PROC_ISNILADIC_INDEX = 7; 371 const int PROC_ISTVF_INDEX = 8; 372 const int PARAM_NAME_INDEX = 9; 373 const int PARAM_TYPE_INDEX = 10; 374 const int PARAM_DIRECTION_INDEX = 11; 375 376 internal override int ColumnCount { get { return 12; } } 377 378 public override string Catalog 379 { 380 get { return ConvertDBNull<string>(_currentRow[PROC_CATALOG_INDEX]); } 381 } 382 383 public override string Schema 384 { 385 get { return ConvertDBNull<string>(_currentRow[PROC_SCHEMA_INDEX]); } 386 } 387 388 public override string ProcedureName 389 { 390 get { return ConvertDBNull<string>(_currentRow[PROC_NAME_INDEX]); } 391 } 392 393 public override string ReturnType 394 { 395 get { return ConvertDBNull<string>(_currentRow[PROC_RET_TYPE_INDEX]); } 396 } 397 398 public override bool IsIsAggregate 399 { 400 get { return ConvertDBNull<bool>(_currentRow[PROC_ISAGGREGATE_INDEX]); } 401 } 402 403 public override bool IsBuiltIn 404 { 405 get { return ConvertDBNull<bool>(_currentRow[PROC_ISBUILTIN_INDEX]); } 406 } 407 408 public override bool IsComposable 409 { 410 get { return ConvertDBNull<bool>(_currentRow[PROC_ISCOMPOSABLE_INDEX]); } 411 } 412 413 public override bool IsNiladic 414 { 415 get { return ConvertDBNull<bool>(_currentRow[PROC_ISNILADIC_INDEX]); } 416 } 417 418 public override bool IsTvf 419 { 420 get { return ConvertDBNull<bool>(_currentRow[PROC_ISTVF_INDEX]); } 421 } 422 423 public override string ParameterName 424 { 425 get { return (string)_currentRow[PARAM_NAME_INDEX]; } 426 } 427 428 public override bool IsParameterNameNull 429 { 430 get { return Convert.IsDBNull(_currentRow[PARAM_NAME_INDEX]); } 431 } 432 433 public override string ParameterType 434 { 435 get { return (string)_currentRow[PARAM_TYPE_INDEX]; } 436 } 437 438 public override bool IsParameterTypeNull 439 { 440 get { return Convert.IsDBNull(_currentRow[PARAM_TYPE_INDEX]); } 441 } 442 443 public override string ProcParameterMode 444 { 445 get { return (string)_currentRow[PARAM_DIRECTION_INDEX]; } 446 } 447 448 public override bool IsParameterModeNull 449 { 450 get { return Convert.IsDBNull(_currentRow[PARAM_DIRECTION_INDEX]); } 451 } 452 453 protected override string FunctionDetailSql 454 { 455 get 456 { 457 return @" 458 Function IsTvf(f Store.Function) as (f is of (Store.TableValuedFunction)) 459 SELECT 460 sp.CatalogName 461 , sp.SchemaName 462 , sp.Name 463 , sp.ReturnTypeName 464 , sp.IsAggregate 465 , sp.IsComposable 466 , sp.IsBuiltIn 467 , sp.IsNiladic 468 , sp.IsTvf 469 , sp.ParameterName 470 , sp.ParameterType 471 , sp.Mode 472 FROM ( 473 (SELECT 474 r.CatalogName as CatalogName 475 , r.SchemaName as SchemaName 476 , r.Name as Name 477 , TREAT(r as Store.ScalarFunction).ReturnType.TypeName as ReturnTypeName 478 , TREAT(r as Store.ScalarFunction).IsAggregate as IsAggregate 479 , true as IsComposable 480 , r.IsBuiltIn as IsBuiltIn 481 , r.IsNiladic as IsNiladic 482 , IsTvf(r) as IsTvf 483 , p.Name as ParameterName 484 , p.ParameterType.TypeName as ParameterType 485 , p.Mode as Mode 486 , p.Ordinal as Ordinal 487 FROM 488 SchemaInformation.Functions as r 489 OUTER APPLY 490 r.Parameters as p) 491 UNION ALL 492 (SELECT 493 r.CatalogName as CatalogName 494 , r.SchemaName as SchemaName 495 , r.Name as Name 496 , CAST(NULL as string) as ReturnTypeName 497 , false as IsAggregate 498 , false as IsComposable 499 , false as IsBuiltIn 500 , false as IsNiladic 501 , false as IsTvf 502 , p.Name as ParameterName 503 , p.ParameterType.TypeName as ParameterType 504 , p.Mode as Mode 505 , p.Ordinal as Ordinal 506 FROM 507 SchemaInformation.Procedures as r 508 OUTER APPLY 509 r.Parameters as p)) as sp 510 "; 511 } 512 } 513 } 514 515 internal abstract class Memento 516 { 517 protected object[] _values; 518 519 internal object[] Values 520 { 521 get { return _values; } 522 } 523 CreateReader()524 public abstract FunctionDetailsReader CreateReader(); 525 } 526 527 internal sealed class MementoV1 : Memento 528 { MementoV1(object[] values)529 internal MementoV1(object[] values) 530 { 531 _values = values; 532 } 533 CreateReader()534 public override FunctionDetailsReader CreateReader() 535 { 536 return new FunctionDetailsReaderV1(this); 537 } 538 } 539 540 internal sealed class MementoV3 : Memento 541 { MementoV3(object[] values)542 internal MementoV3(object[] values) 543 { 544 _values = values; 545 } 546 CreateReader()547 public override FunctionDetailsReader CreateReader() 548 { 549 return new FunctionDetailsReaderV3(this); 550 } 551 } 552 } 553 } 554