DBCommandBuilder.cs 62 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541
  1. //------------------------------------------------------------------------------
  2. // <copyright file="CommandBuilder.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. // <owner current="true" primary="true">Microsoft</owner>
  6. // <owner current="true" primary="false">Microsoft</owner>
  7. //------------------------------------------------------------------------------
  8. namespace System.Data.Common {
  9. using System;
  10. using System.Collections;
  11. using System.ComponentModel;
  12. using System.Data;
  13. using System.Diagnostics;
  14. using System.Globalization;
  15. using System.Text;
  16. using System.Text.RegularExpressions;
  17. public abstract class DbCommandBuilder : Component { // V1.2.3300
  18. private class ParameterNames {
  19. private const string DefaultOriginalPrefix = "Original_";
  20. private const string DefaultIsNullPrefix = "IsNull_";
  21. // we use alternative prefix if the default prefix fails parametername validation
  22. private const string AlternativeOriginalPrefix = "original";
  23. private const string AlternativeIsNullPrefix = "isnull";
  24. private const string AlternativeOriginalPrefix2 = "ORIGINAL";
  25. private const string AlternativeIsNullPrefix2 = "ISNULL";
  26. private string _originalPrefix;
  27. private string _isNullPrefix;
  28. private Regex _parameterNameParser;
  29. private DbCommandBuilder _dbCommandBuilder;
  30. private string[] _baseParameterNames;
  31. private string[] _originalParameterNames;
  32. private string[] _nullParameterNames;
  33. private bool[] _isMutatedName;
  34. private int _count;
  35. private int _genericParameterCount;
  36. private int _adjustedParameterNameMaxLength;
  37. internal ParameterNames(DbCommandBuilder dbCommandBuilder, DbSchemaRow[] schemaRows) {
  38. _dbCommandBuilder = dbCommandBuilder;
  39. _baseParameterNames = new string[schemaRows.Length];
  40. _originalParameterNames = new string[schemaRows.Length];
  41. _nullParameterNames = new string[schemaRows.Length];
  42. _isMutatedName = new bool[schemaRows.Length];
  43. _count = schemaRows.Length;
  44. _parameterNameParser = new Regex(_dbCommandBuilder.ParameterNamePattern, RegexOptions.ExplicitCapture | RegexOptions.Singleline);
  45. SetAndValidateNamePrefixes();
  46. _adjustedParameterNameMaxLength = GetAdjustedParameterNameMaxLength();
  47. // Generate the baseparameter names and remove conflicting names
  48. // No names will be generated for any name that is rejected due to invalid prefix, regex violation or
  49. // name conflict after mutation.
  50. // All null values will be replaced with generic parameter names
  51. //
  52. for (int i = 0; i < schemaRows.Length; i++) {
  53. if (null == schemaRows[i]) {
  54. continue;
  55. }
  56. bool isMutatedName = false;
  57. string columnName = schemaRows[i].ColumnName;
  58. // all names that start with original- or isNullPrefix are invalid
  59. if (null != _originalPrefix) {
  60. if (columnName.StartsWith(_originalPrefix, StringComparison.OrdinalIgnoreCase)) {
  61. continue;
  62. }
  63. }
  64. if (null != _isNullPrefix) {
  65. if (columnName.StartsWith(_isNullPrefix, StringComparison.OrdinalIgnoreCase)) {
  66. continue;
  67. }
  68. }
  69. // Mutate name if it contains space(s)
  70. if (columnName.IndexOf(' ') >= 0) {
  71. columnName = columnName.Replace(' ', '_');
  72. isMutatedName = true;
  73. }
  74. // Validate name against regular expression
  75. if (!_parameterNameParser.IsMatch(columnName)) {
  76. continue;
  77. }
  78. // Validate name against adjusted max parametername length
  79. if (columnName.Length > _adjustedParameterNameMaxLength) {
  80. continue;
  81. }
  82. _baseParameterNames[i] = columnName;
  83. _isMutatedName[i] = isMutatedName;
  84. }
  85. EliminateConflictingNames();
  86. // Generate names for original- and isNullparameters
  87. // no names will be generated if the prefix failed parametername validation
  88. for (int i = 0; i < schemaRows.Length; i++) {
  89. if (null != _baseParameterNames[i]) {
  90. if (null != _originalPrefix) {
  91. _originalParameterNames[i] = _originalPrefix + _baseParameterNames[i];
  92. }
  93. if (null != _isNullPrefix) {
  94. // don't bother generating an 'IsNull' name if it's not used
  95. if (schemaRows[i].AllowDBNull) {
  96. _nullParameterNames[i] = _isNullPrefix + _baseParameterNames[i];
  97. }
  98. }
  99. }
  100. }
  101. ApplyProviderSpecificFormat();
  102. GenerateMissingNames(schemaRows);
  103. }
  104. private void SetAndValidateNamePrefixes() {
  105. if (_parameterNameParser.IsMatch(DefaultIsNullPrefix)) {
  106. _isNullPrefix = DefaultIsNullPrefix;
  107. }
  108. else if (_parameterNameParser.IsMatch(AlternativeIsNullPrefix)) {
  109. _isNullPrefix = AlternativeIsNullPrefix;
  110. }
  111. else if (_parameterNameParser.IsMatch(AlternativeIsNullPrefix2)) {
  112. _isNullPrefix = AlternativeIsNullPrefix2;
  113. }
  114. else {
  115. _isNullPrefix = null;
  116. }
  117. if (_parameterNameParser.IsMatch(DefaultOriginalPrefix)) {
  118. _originalPrefix = DefaultOriginalPrefix;
  119. }
  120. else if (_parameterNameParser.IsMatch(AlternativeOriginalPrefix)) {
  121. _originalPrefix = AlternativeOriginalPrefix;
  122. }
  123. else if (_parameterNameParser.IsMatch(AlternativeOriginalPrefix2)) {
  124. _originalPrefix = AlternativeOriginalPrefix2;
  125. }
  126. else {
  127. _originalPrefix = null;
  128. }
  129. }
  130. private void ApplyProviderSpecificFormat() {
  131. for (int i = 0; i < _baseParameterNames.Length; i++) {
  132. if (null != _baseParameterNames[i]) {
  133. _baseParameterNames[i] = _dbCommandBuilder.GetParameterName(_baseParameterNames[i]);
  134. }
  135. if (null != _originalParameterNames[i]) {
  136. _originalParameterNames[i] = _dbCommandBuilder.GetParameterName(_originalParameterNames[i]);
  137. }
  138. if (null != _nullParameterNames[i]) {
  139. _nullParameterNames[i] = _dbCommandBuilder.GetParameterName(_nullParameterNames[i]);
  140. }
  141. }
  142. }
  143. private void EliminateConflictingNames() {
  144. //
  145. for (int i = 0; i < _count - 1; i++) {
  146. string name = _baseParameterNames[i];
  147. if (null != name) {
  148. for (int j = i + 1; j < _count; j++) {
  149. if (ADP.CompareInsensitiveInvariant(name, _baseParameterNames[j])) {
  150. // found duplicate name
  151. // the name unchanged name wins
  152. int iMutatedName = _isMutatedName[j] ? j : i;
  153. Debug.Assert(_isMutatedName[iMutatedName], String.Format(CultureInfo.InvariantCulture, "{0} expected to be a mutated name", _baseParameterNames[iMutatedName]));
  154. _baseParameterNames[iMutatedName] = null; // null out the culprit
  155. }
  156. }
  157. }
  158. }
  159. }
  160. // Generates parameternames that couldn't be generated from columnname
  161. internal void GenerateMissingNames(DbSchemaRow[] schemaRows) {
  162. // foreach name in base names
  163. // if base name is null
  164. // for base, original and nullnames (null names only if nullable)
  165. // do
  166. // generate name based on current index
  167. // increment index
  168. // search name in base names
  169. // loop while name occures in base names
  170. // end for
  171. // end foreach
  172. string name;
  173. for (int i = 0; i < _baseParameterNames.Length; i++) {
  174. name = _baseParameterNames[i];
  175. if (null == name) {
  176. _baseParameterNames[i] = GetNextGenericParameterName();
  177. _originalParameterNames[i] = GetNextGenericParameterName();
  178. // don't bother generating an 'IsNull' name if it's not used
  179. if ((null != schemaRows[i]) && schemaRows[i].AllowDBNull) {
  180. _nullParameterNames[i] = GetNextGenericParameterName();
  181. }
  182. }
  183. }
  184. }
  185. private int GetAdjustedParameterNameMaxLength() {
  186. int maxPrefixLength = Math.Max(
  187. (null != _isNullPrefix ? _isNullPrefix.Length : 0),
  188. (null != _originalPrefix ? _originalPrefix.Length : 0)
  189. ) + _dbCommandBuilder.GetParameterName("").Length;
  190. return _dbCommandBuilder.ParameterNameMaxLength - maxPrefixLength;
  191. }
  192. private string GetNextGenericParameterName() {
  193. string name;
  194. bool nameExist;
  195. do {
  196. nameExist = false;
  197. _genericParameterCount++;
  198. name = _dbCommandBuilder.GetParameterName(_genericParameterCount);
  199. for (int i = 0; i < _baseParameterNames.Length; i++) {
  200. if (ADP.CompareInsensitiveInvariant(_baseParameterNames[i], name)) {
  201. nameExist = true;
  202. break;
  203. }
  204. }
  205. } while (nameExist);
  206. return name;
  207. }
  208. internal string GetBaseParameterName(int index) {
  209. return (_baseParameterNames[index]);
  210. }
  211. internal string GetOriginalParameterName(int index) {
  212. return (_originalParameterNames[index]);
  213. }
  214. internal string GetNullParameterName(int index) {
  215. return (_nullParameterNames[index]);
  216. }
  217. }
  218. private const string DeleteFrom = "DELETE FROM ";
  219. private const string InsertInto = "INSERT INTO ";
  220. private const string DefaultValues = " DEFAULT VALUES";
  221. private const string Values = " VALUES ";
  222. private const string Update = "UPDATE ";
  223. private const string Set = " SET ";
  224. private const string Where = " WHERE ";
  225. private const string SpaceLeftParenthesis = " (";
  226. private const string Comma = ", ";
  227. private const string Equal = " = ";
  228. private const string LeftParenthesis = "(";
  229. private const string RightParenthesis = ")";
  230. private const string NameSeparator = ".";
  231. private const string IsNull = " IS NULL";
  232. private const string EqualOne = " = 1";
  233. private const string And = " AND ";
  234. private const string Or = " OR ";
  235. private DbDataAdapter _dataAdapter;
  236. private DbCommand _insertCommand;
  237. private DbCommand _updateCommand;
  238. private DbCommand _deleteCommand;
  239. private MissingMappingAction _missingMappingAction;
  240. private ConflictOption _conflictDetection = ConflictOption.CompareAllSearchableValues;
  241. private bool _setAllValues = false;
  242. private bool _hasPartialPrimaryKey = false;
  243. private DataTable _dbSchemaTable;
  244. private DbSchemaRow[] _dbSchemaRows;
  245. private string[] _sourceColumnNames;
  246. private ParameterNames _parameterNames = null;
  247. private string _quotedBaseTableName;
  248. // quote strings to use around SQL object names
  249. private CatalogLocation _catalogLocation = CatalogLocation.Start;
  250. private string _catalogSeparator = NameSeparator;
  251. private string _schemaSeparator = NameSeparator;
  252. private string _quotePrefix = "";
  253. private string _quoteSuffix = "";
  254. private string _parameterNamePattern = null;
  255. private string _parameterMarkerFormat = null;
  256. private int _parameterNameMaxLength = 0;
  257. protected DbCommandBuilder() : base() { // V1.2.3300
  258. }
  259. [
  260. DefaultValueAttribute(ConflictOption.CompareAllSearchableValues),
  261. ResCategoryAttribute(Res.DataCategory_Update),
  262. ResDescriptionAttribute(Res.DbCommandBuilder_ConflictOption),
  263. ]
  264. virtual public ConflictOption ConflictOption { // V1.2.3300
  265. get {
  266. return _conflictDetection;
  267. }
  268. set {
  269. switch(value) {
  270. case ConflictOption.CompareAllSearchableValues:
  271. case ConflictOption.CompareRowVersion:
  272. case ConflictOption.OverwriteChanges:
  273. _conflictDetection = value;
  274. break;
  275. default:
  276. throw ADP.InvalidConflictOptions(value);
  277. }
  278. }
  279. }
  280. [
  281. DefaultValueAttribute(CatalogLocation.Start),
  282. ResCategoryAttribute(Res.DataCategory_Schema),
  283. ResDescriptionAttribute(Res.DbCommandBuilder_CatalogLocation),
  284. ]
  285. virtual public CatalogLocation CatalogLocation { // V1.2.3300, MDAC 79449
  286. get {
  287. return _catalogLocation;
  288. }
  289. set {
  290. if (null != _dbSchemaTable) {
  291. throw ADP.NoQuoteChange();
  292. }
  293. switch(value) {
  294. case CatalogLocation.Start:
  295. case CatalogLocation.End:
  296. _catalogLocation = value;
  297. break;
  298. default:
  299. throw ADP.InvalidCatalogLocation(value);
  300. }
  301. }
  302. }
  303. [
  304. DefaultValueAttribute(DbCommandBuilder.NameSeparator),
  305. ResCategoryAttribute(Res.DataCategory_Schema),
  306. ResDescriptionAttribute(Res.DbCommandBuilder_CatalogSeparator),
  307. ]
  308. virtual public string CatalogSeparator { // V1.2.3300, MDAC 79449
  309. get {
  310. string catalogSeparator = _catalogSeparator;
  311. return (((null != catalogSeparator) && (0 < catalogSeparator.Length)) ? catalogSeparator : NameSeparator);
  312. }
  313. set {
  314. if (null != _dbSchemaTable) {
  315. throw ADP.NoQuoteChange();
  316. }
  317. _catalogSeparator = value;
  318. }
  319. }
  320. [
  321. Browsable(false),
  322. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
  323. ResDescriptionAttribute(Res.DbCommandBuilder_DataAdapter),
  324. ]
  325. public DbDataAdapter DataAdapter { // V1.2.3300
  326. get {
  327. return _dataAdapter;
  328. }
  329. set {
  330. if (_dataAdapter != value) {
  331. RefreshSchema();
  332. if (null != _dataAdapter) {
  333. // derived should remove event handler from old adapter
  334. SetRowUpdatingHandler(_dataAdapter);
  335. _dataAdapter = null;
  336. }
  337. if (null != value) {
  338. // derived should add event handler to new adapter
  339. SetRowUpdatingHandler(value);
  340. _dataAdapter = value;
  341. }
  342. }
  343. }
  344. }
  345. internal int ParameterNameMaxLength {
  346. get {
  347. return _parameterNameMaxLength;
  348. }
  349. }
  350. internal string ParameterNamePattern {
  351. get {
  352. return _parameterNamePattern;
  353. }
  354. }
  355. private string QuotedBaseTableName {
  356. get {
  357. return _quotedBaseTableName;
  358. }
  359. }
  360. [
  361. DefaultValueAttribute(""),
  362. ResCategoryAttribute(Res.DataCategory_Schema),
  363. ResDescriptionAttribute(Res.DbCommandBuilder_QuotePrefix),
  364. ]
  365. virtual public string QuotePrefix { // V1.2.3300, XXXCommandBuilder V1.0.3300
  366. get {
  367. string quotePrefix = _quotePrefix;
  368. return ((null != quotePrefix) ? quotePrefix : ADP.StrEmpty);
  369. }
  370. set {
  371. if (null != _dbSchemaTable) {
  372. throw ADP.NoQuoteChange();
  373. }
  374. _quotePrefix = value;
  375. }
  376. }
  377. [
  378. DefaultValueAttribute(""),
  379. ResCategoryAttribute(Res.DataCategory_Schema),
  380. ResDescriptionAttribute(Res.DbCommandBuilder_QuoteSuffix),
  381. ]
  382. virtual public string QuoteSuffix { // V1.2.3300, XXXCommandBuilder V1.0.3300
  383. get {
  384. string quoteSuffix = _quoteSuffix;
  385. return ((null != quoteSuffix) ? quoteSuffix : ADP.StrEmpty);
  386. }
  387. set {
  388. if (null != _dbSchemaTable) {
  389. throw ADP.NoQuoteChange();
  390. }
  391. _quoteSuffix = value;
  392. }
  393. }
  394. [
  395. DefaultValueAttribute(DbCommandBuilder.NameSeparator),
  396. ResCategoryAttribute(Res.DataCategory_Schema),
  397. ResDescriptionAttribute(Res.DbCommandBuilder_SchemaSeparator),
  398. ]
  399. virtual public string SchemaSeparator { // V1.2.3300, MDAC 79449
  400. get {
  401. string schemaSeparator = _schemaSeparator;
  402. return (((null != schemaSeparator) && (0 < schemaSeparator.Length)) ? schemaSeparator : NameSeparator);
  403. }
  404. set {
  405. if (null != _dbSchemaTable) {
  406. throw ADP.NoQuoteChange();
  407. }
  408. _schemaSeparator = value;
  409. }
  410. }
  411. [
  412. DefaultValueAttribute(false),
  413. ResCategoryAttribute(Res.DataCategory_Schema),
  414. ResDescriptionAttribute(Res.DbCommandBuilder_SetAllValues),
  415. ]
  416. public bool SetAllValues {
  417. get {
  418. return _setAllValues;
  419. }
  420. set {
  421. _setAllValues = value;
  422. }
  423. }
  424. private DbCommand InsertCommand {
  425. get {
  426. return _insertCommand;
  427. }
  428. set {
  429. _insertCommand = value;
  430. }
  431. }
  432. private DbCommand UpdateCommand {
  433. get {
  434. return _updateCommand;
  435. }
  436. set {
  437. _updateCommand = value;
  438. }
  439. }
  440. private DbCommand DeleteCommand {
  441. get {
  442. return _deleteCommand;
  443. }
  444. set {
  445. _deleteCommand = value;
  446. }
  447. }
  448. private void BuildCache(bool closeConnection, DataRow dataRow, bool useColumnsForParameterNames) { // V1.2.3300
  449. // Don't bother building the cache if it's done already; wait for
  450. // the user to call RefreshSchema first.
  451. if ((null != _dbSchemaTable) && (!useColumnsForParameterNames || (null != _parameterNames))) {
  452. return;
  453. }
  454. DataTable schemaTable = null;
  455. DbCommand srcCommand = GetSelectCommand();
  456. DbConnection connection = srcCommand.Connection;
  457. if (null == connection) {
  458. throw ADP.MissingSourceCommandConnection();
  459. }
  460. try {
  461. if (0 == (ConnectionState.Open & connection.State)) {
  462. connection.Open();
  463. }
  464. else {
  465. closeConnection = false;
  466. }
  467. if (useColumnsForParameterNames) {
  468. DataTable dataTable = connection.GetSchema(DbMetaDataCollectionNames.DataSourceInformation);
  469. if (dataTable.Rows.Count == 1) {
  470. _parameterNamePattern = dataTable.Rows[0][DbMetaDataColumnNames.ParameterNamePattern] as string;
  471. _parameterMarkerFormat = dataTable.Rows[0][DbMetaDataColumnNames.ParameterMarkerFormat] as string;
  472. object oParameterNameMaxLength = dataTable.Rows[0][DbMetaDataColumnNames.ParameterNameMaxLength];
  473. _parameterNameMaxLength = (oParameterNameMaxLength is int) ? (int)oParameterNameMaxLength : 0;
  474. // note that we protect against errors in the xml file!
  475. if (0 == _parameterNameMaxLength || null == _parameterNamePattern || null == _parameterMarkerFormat) {
  476. useColumnsForParameterNames = false;
  477. }
  478. }
  479. else {
  480. Debug.Assert(false, "Rowcount expected to be 1");
  481. useColumnsForParameterNames = false;
  482. }
  483. }
  484. schemaTable = GetSchemaTable(srcCommand);
  485. }
  486. finally {
  487. if (closeConnection) {
  488. connection.Close();
  489. }
  490. }
  491. if (null == schemaTable) {
  492. throw ADP.DynamicSQLNoTableInfo();
  493. }
  494. #if DEBUG
  495. //if (AdapterSwitches.DbCommandBuilder.TraceVerbose) {
  496. // ADP.TraceDataTable("DbCommandBuilder", schemaTable);
  497. //}
  498. #endif
  499. BuildInformation(schemaTable);
  500. _dbSchemaTable = schemaTable;
  501. DbSchemaRow[] schemaRows = _dbSchemaRows;
  502. string[] srcColumnNames = new string[schemaRows.Length];
  503. for (int i = 0; i < schemaRows.Length; ++i) {
  504. if (null != schemaRows[i]) {
  505. srcColumnNames[i] = schemaRows[i].ColumnName;
  506. }
  507. }
  508. _sourceColumnNames = srcColumnNames;
  509. if (useColumnsForParameterNames) {
  510. _parameterNames = new ParameterNames(this, schemaRows);
  511. }
  512. ADP.BuildSchemaTableInfoTableNames(srcColumnNames);
  513. }
  514. virtual protected DataTable GetSchemaTable (DbCommand sourceCommand) {
  515. using (IDataReader dataReader = sourceCommand.ExecuteReader(CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo)){
  516. return dataReader.GetSchemaTable();
  517. }
  518. }
  519. private void BuildInformation(DataTable schemaTable) {
  520. DbSchemaRow[] rows = DbSchemaRow.GetSortedSchemaRows(schemaTable, false); // MDAC 60609
  521. if ((null == rows) || (0 == rows.Length)) {
  522. throw ADP.DynamicSQLNoTableInfo();
  523. }
  524. string baseServerName = ""; // MDAC 72721, 73599
  525. string baseCatalogName = "";
  526. string baseSchemaName = "";
  527. string baseTableName = null;
  528. for (int i = 0; i < rows.Length; ++i) {
  529. DbSchemaRow row = rows[i];
  530. string tableName = row.BaseTableName;
  531. if ((null == tableName) || (0 == tableName.Length)) {
  532. rows[i] = null;
  533. continue;
  534. }
  535. string serverName = row.BaseServerName;
  536. string catalogName = row.BaseCatalogName;
  537. string schemaName = row.BaseSchemaName;
  538. if (null == serverName) {
  539. serverName = "";
  540. }
  541. if (null == catalogName) {
  542. catalogName = "";
  543. }
  544. if (null == schemaName) {
  545. schemaName = "";
  546. }
  547. if (null == baseTableName) {
  548. baseServerName = serverName;
  549. baseCatalogName = catalogName;
  550. baseSchemaName = schemaName;
  551. baseTableName = tableName;
  552. }
  553. else if ( (0 != ADP.SrcCompare(baseTableName, tableName))
  554. || (0 != ADP.SrcCompare(baseSchemaName, schemaName))
  555. || (0 != ADP.SrcCompare(baseCatalogName, catalogName))
  556. || (0 != ADP.SrcCompare(baseServerName, serverName))) {
  557. throw ADP.DynamicSQLJoinUnsupported();
  558. }
  559. }
  560. if (0 == baseServerName.Length) {
  561. baseServerName = null;
  562. }
  563. if (0 == baseCatalogName.Length) {
  564. baseServerName = null;
  565. baseCatalogName = null;
  566. }
  567. if (0 == baseSchemaName.Length) {
  568. baseServerName = null;
  569. baseCatalogName = null;
  570. baseSchemaName = null;
  571. }
  572. if ((null == baseTableName) || (0 == baseTableName.Length)) {
  573. throw ADP.DynamicSQLNoTableInfo();
  574. }
  575. CatalogLocation location = CatalogLocation;
  576. string catalogSeparator = CatalogSeparator;
  577. string schemaSeparator = SchemaSeparator;
  578. string quotePrefix = QuotePrefix;
  579. string quoteSuffix = QuoteSuffix;
  580. if (!ADP.IsEmpty(quotePrefix) && (-1 != baseTableName.IndexOf(quotePrefix, StringComparison.Ordinal))) {
  581. throw ADP.DynamicSQLNestedQuote(baseTableName, quotePrefix);
  582. }
  583. if (!ADP.IsEmpty(quoteSuffix) && (-1 != baseTableName.IndexOf(quoteSuffix, StringComparison.Ordinal))) {
  584. throw ADP.DynamicSQLNestedQuote(baseTableName, quoteSuffix);
  585. }
  586. System.Text.StringBuilder builder = new System.Text.StringBuilder();
  587. if (CatalogLocation.Start == location) {
  588. // MDAC 79449
  589. if (null != baseServerName) {
  590. builder.Append(ADP.BuildQuotedString(quotePrefix, quoteSuffix, baseServerName));
  591. builder.Append(catalogSeparator);
  592. }
  593. if (null != baseCatalogName) {
  594. builder.Append(ADP.BuildQuotedString(quotePrefix, quoteSuffix, baseCatalogName));
  595. builder.Append(catalogSeparator);
  596. }
  597. //
  598. }
  599. if (null != baseSchemaName) {
  600. builder.Append(ADP.BuildQuotedString(quotePrefix, quoteSuffix, baseSchemaName));
  601. builder.Append(schemaSeparator);
  602. }
  603. //
  604. builder.Append(ADP.BuildQuotedString(quotePrefix, quoteSuffix, baseTableName));
  605. if (CatalogLocation.End == location) {
  606. // MDAC 79449
  607. if (null != baseServerName) {
  608. builder.Append(catalogSeparator);
  609. builder.Append(ADP.BuildQuotedString(quotePrefix, quoteSuffix, baseServerName));
  610. }
  611. if (null != baseCatalogName) {
  612. builder.Append(catalogSeparator);
  613. builder.Append(ADP.BuildQuotedString(quotePrefix, quoteSuffix, baseCatalogName));
  614. }
  615. }
  616. _quotedBaseTableName = builder.ToString();
  617. _hasPartialPrimaryKey = false;
  618. foreach(DbSchemaRow row in rows) {
  619. if ((null != row) && (row.IsKey || row.IsUnique) && !row.IsLong && !row.IsRowVersion && row.IsHidden) {
  620. _hasPartialPrimaryKey = true;
  621. break;
  622. }
  623. }
  624. _dbSchemaRows = rows;
  625. }
  626. private DbCommand BuildDeleteCommand(DataTableMapping mappings, DataRow dataRow) {
  627. DbCommand command = InitializeCommand(DeleteCommand);
  628. StringBuilder builder = new StringBuilder();
  629. int parameterCount = 0;
  630. Debug.Assert (!ADP.IsEmpty(_quotedBaseTableName), "no table name");
  631. builder.Append(DeleteFrom);
  632. builder.Append(QuotedBaseTableName);
  633. parameterCount = BuildWhereClause(mappings, dataRow, builder, command, parameterCount, false);
  634. command.CommandText = builder.ToString();
  635. RemoveExtraParameters(command, parameterCount);
  636. DeleteCommand = command;
  637. return command;
  638. }
  639. private DbCommand BuildInsertCommand(DataTableMapping mappings, DataRow dataRow) {
  640. DbCommand command = InitializeCommand(InsertCommand);
  641. StringBuilder builder = new StringBuilder();
  642. int parameterCount = 0;
  643. string nextSeparator = SpaceLeftParenthesis;
  644. Debug.Assert (!ADP.IsEmpty(_quotedBaseTableName), "no table name");
  645. builder.Append(InsertInto);
  646. builder.Append(QuotedBaseTableName);
  647. // search for the columns in that base table, to be the column clause
  648. DbSchemaRow[] schemaRows = _dbSchemaRows;
  649. string[] parameterName = new string[schemaRows.Length];
  650. for (int i = 0; i < schemaRows.Length; ++i) {
  651. DbSchemaRow row = schemaRows[i];
  652. if ( (null == row) || (0 == row.BaseColumnName.Length) || !IncludeInInsertValues(row) )
  653. continue;
  654. object currentValue = null;
  655. string sourceColumn = _sourceColumnNames[i];
  656. // If we're building a statement for a specific row, then check the
  657. // values to see whether the column should be included in the insert
  658. // statement or not
  659. if ((null != mappings) && (null != dataRow)) {
  660. DataColumn dataColumn = GetDataColumn(sourceColumn, mappings, dataRow);
  661. if (null == dataColumn)
  662. continue;
  663. // Don't bother inserting if the column is readonly in both the data
  664. // set and the back end.
  665. if (row.IsReadOnly && dataColumn.ReadOnly)
  666. continue;
  667. currentValue = GetColumnValue(dataRow, dataColumn, DataRowVersion.Current);
  668. // If the value is null, and the column doesn't support nulls, then
  669. // the user is requesting the server-specified default value, so don't
  670. // include it in the set-list.
  671. if ( !row.AllowDBNull && (null == currentValue || Convert.IsDBNull(currentValue)) )
  672. continue;
  673. }
  674. builder.Append(nextSeparator);
  675. nextSeparator = Comma;
  676. builder.Append(QuotedColumn(row.BaseColumnName));
  677. parameterName[parameterCount] = CreateParameterForValue(
  678. command,
  679. GetBaseParameterName(i),
  680. sourceColumn,
  681. DataRowVersion.Current,
  682. parameterCount,
  683. currentValue,
  684. row, StatementType.Insert, false
  685. );
  686. parameterCount++;
  687. }
  688. if (0 == parameterCount)
  689. builder.Append(DefaultValues);
  690. else {
  691. builder.Append(RightParenthesis);
  692. builder.Append(Values);
  693. builder.Append(LeftParenthesis);
  694. builder.Append(parameterName[0]);
  695. for (int i = 1; i < parameterCount; ++i) {
  696. builder.Append(Comma);
  697. builder.Append(parameterName[i]);
  698. }
  699. builder.Append(RightParenthesis);
  700. }
  701. command.CommandText = builder.ToString();
  702. RemoveExtraParameters(command, parameterCount);
  703. InsertCommand = command;
  704. return command;
  705. }
  706. private DbCommand BuildUpdateCommand(DataTableMapping mappings, DataRow dataRow) {
  707. DbCommand command = InitializeCommand(UpdateCommand);
  708. StringBuilder builder = new StringBuilder();
  709. string nextSeparator = Set;
  710. int parameterCount = 0;
  711. Debug.Assert (!ADP.IsEmpty(_quotedBaseTableName), "no table name");
  712. builder.Append(Update);
  713. builder.Append(QuotedBaseTableName);
  714. // search for the columns in that base table, to build the set clause
  715. DbSchemaRow[] schemaRows = _dbSchemaRows;
  716. for (int i = 0; i < schemaRows.Length; ++i) {
  717. DbSchemaRow row = schemaRows[i];
  718. if ((null == row) || (0 == row.BaseColumnName.Length) || !IncludeInUpdateSet(row))
  719. continue;
  720. object currentValue = null;
  721. string sourceColumn = _sourceColumnNames[i];
  722. // If we're building a statement for a specific row, then check the
  723. // values to see whether the column should be included in the update
  724. // statement or not
  725. if ((null != mappings) && (null != dataRow)) {
  726. DataColumn dataColumn = GetDataColumn(sourceColumn, mappings, dataRow);
  727. if (null == dataColumn)
  728. continue;
  729. // Don't bother updating if the column is readonly in both the data
  730. // set and the back end.
  731. if (row.IsReadOnly && dataColumn.ReadOnly)
  732. continue;
  733. // Unless specifically directed to do so, we will not automatically update
  734. // a column with it's original value, which means that we must determine
  735. // whether the value has changed locally, before we send it up.
  736. currentValue = GetColumnValue(dataRow, dataColumn, DataRowVersion.Current);
  737. if (!SetAllValues) {
  738. object originalValue = GetColumnValue(dataRow, dataColumn, DataRowVersion.Original);
  739. if ((originalValue == currentValue)
  740. || ((null != originalValue) && originalValue.Equals(currentValue))) {
  741. continue;
  742. }
  743. }
  744. }
  745. builder.Append(nextSeparator);
  746. nextSeparator = Comma;
  747. builder.Append(QuotedColumn(row.BaseColumnName));
  748. builder.Append(Equal);
  749. builder.Append(
  750. CreateParameterForValue(
  751. command,
  752. GetBaseParameterName(i),
  753. sourceColumn,
  754. DataRowVersion.Current,
  755. parameterCount,
  756. currentValue,
  757. row, StatementType.Update, false
  758. )
  759. );
  760. parameterCount++;
  761. }
  762. // It is an error to attempt an update when there's nothing to update;
  763. bool skipRow = (0 == parameterCount);
  764. parameterCount = BuildWhereClause(mappings, dataRow, builder, command, parameterCount, true);
  765. command.CommandText = builder.ToString();
  766. RemoveExtraParameters(command, parameterCount);
  767. UpdateCommand = command;
  768. return (skipRow) ? null : command;
  769. }
  770. private int BuildWhereClause(
  771. DataTableMapping mappings,
  772. DataRow dataRow,
  773. StringBuilder builder,
  774. DbCommand command,
  775. int parameterCount,
  776. bool isUpdate
  777. ) {
  778. string beginNewCondition = string.Empty;
  779. int whereCount = 0;
  780. builder.Append(Where);
  781. builder.Append(LeftParenthesis);
  782. DbSchemaRow[] schemaRows = _dbSchemaRows;
  783. for (int i = 0; i < schemaRows.Length; ++i) {
  784. DbSchemaRow row = schemaRows[i];
  785. if ((null == row) || (0 == row.BaseColumnName.Length) || !IncludeInWhereClause(row, isUpdate)) {
  786. continue;
  787. }
  788. builder.Append(beginNewCondition);
  789. beginNewCondition = And;
  790. object value = null;
  791. string sourceColumn = _sourceColumnNames[i];
  792. string baseColumnName = QuotedColumn(row.BaseColumnName);
  793. if ((null != mappings) && (null != dataRow))
  794. value = GetColumnValue(dataRow, sourceColumn, mappings, DataRowVersion.Original);
  795. if (!row.AllowDBNull) {
  796. // (<baseColumnName> = ?)
  797. builder.Append(LeftParenthesis);
  798. builder.Append(baseColumnName);
  799. builder.Append(Equal);
  800. builder.Append(
  801. CreateParameterForValue(
  802. command,
  803. GetOriginalParameterName(i),
  804. sourceColumn,
  805. DataRowVersion.Original,
  806. parameterCount,
  807. value,
  808. row, (isUpdate ? StatementType.Update : StatementType.Delete), true
  809. )
  810. );
  811. parameterCount++;
  812. builder.Append(RightParenthesis);
  813. }
  814. else {
  815. // ((? = 1 AND <baseColumnName> IS NULL) OR (<baseColumnName> = ?))
  816. builder.Append(LeftParenthesis);
  817. builder.Append(LeftParenthesis);
  818. builder.Append(
  819. CreateParameterForNullTest(
  820. command,
  821. GetNullParameterName(i),
  822. sourceColumn,
  823. DataRowVersion.Original,
  824. parameterCount,
  825. value,
  826. row, (isUpdate ? StatementType.Update : StatementType.Delete), true
  827. )
  828. );
  829. parameterCount++;
  830. builder.Append(EqualOne);
  831. builder.Append(And);
  832. builder.Append(baseColumnName);
  833. builder.Append(IsNull);
  834. builder.Append(RightParenthesis);
  835. builder.Append(Or);
  836. builder.Append(LeftParenthesis);
  837. builder.Append(baseColumnName);
  838. builder.Append(Equal);
  839. builder.Append(
  840. CreateParameterForValue(
  841. command,
  842. GetOriginalParameterName(i),
  843. sourceColumn,
  844. DataRowVersion.Original,
  845. parameterCount,
  846. value,
  847. row, (isUpdate ? StatementType.Update : StatementType.Delete), true
  848. )
  849. );
  850. parameterCount++;
  851. builder.Append(RightParenthesis);
  852. builder.Append(RightParenthesis);
  853. }
  854. if (IncrementWhereCount(row)) {
  855. whereCount++;
  856. }
  857. }
  858. builder.Append(RightParenthesis);
  859. if (0 == whereCount) {
  860. if (isUpdate) {
  861. if (ConflictOption.CompareRowVersion == ConflictOption) {
  862. throw ADP.DynamicSQLNoKeyInfoRowVersionUpdate();
  863. }
  864. throw ADP.DynamicSQLNoKeyInfoUpdate();
  865. }
  866. else {
  867. if (ConflictOption.CompareRowVersion == ConflictOption) {
  868. throw ADP.DynamicSQLNoKeyInfoRowVersionDelete();
  869. }
  870. throw ADP.DynamicSQLNoKeyInfoDelete();
  871. }
  872. }
  873. return parameterCount;
  874. }
  875. private string CreateParameterForNullTest(
  876. DbCommand command,
  877. string parameterName,
  878. string sourceColumn,
  879. DataRowVersion version,
  880. int parameterCount,
  881. object value,
  882. DbSchemaRow row,
  883. StatementType statementType,
  884. bool whereClause
  885. ) {
  886. DbParameter p = GetNextParameter(command, parameterCount);
  887. Debug.Assert(!ADP.IsEmpty(sourceColumn), "empty source column");
  888. if (null == parameterName) {
  889. p.ParameterName = GetParameterName(1 + parameterCount);
  890. }
  891. else {
  892. p.ParameterName = parameterName;
  893. }
  894. p.Direction = ParameterDirection.Input;
  895. p.SourceColumn = sourceColumn;
  896. p.SourceVersion = version;
  897. p.SourceColumnNullMapping = true;
  898. p.Value = value;
  899. p.Size = 0; // don't specify parameter.Size so that we don't silently truncate to the metadata size
  900. ApplyParameterInfo(p, row.DataRow, statementType, whereClause);
  901. p.DbType = DbType.Int32;
  902. p.Value = ADP.IsNull(value) ? DbDataAdapter.ParameterValueNullValue : DbDataAdapter.ParameterValueNonNullValue;
  903. if (!command.Parameters.Contains(p)) {
  904. command.Parameters.Add(p);
  905. }
  906. if (null == parameterName) {
  907. return GetParameterPlaceholder(1 + parameterCount);
  908. }
  909. else {
  910. Debug.Assert(null != _parameterNames, "How can we have a parameterName without a _parameterNames collection?");
  911. Debug.Assert(null != _parameterMarkerFormat, "How can we have a _parameterNames collection but no _parameterMarkerFormat?");
  912. return String.Format(CultureInfo.InvariantCulture, _parameterMarkerFormat, parameterName);
  913. }
  914. }
  915. private string CreateParameterForValue(
  916. DbCommand command,
  917. string parameterName,
  918. string sourceColumn,
  919. DataRowVersion version,
  920. int parameterCount,
  921. object value,
  922. DbSchemaRow row,
  923. StatementType statementType,
  924. bool whereClause
  925. ) {
  926. DbParameter p = GetNextParameter(command, parameterCount);
  927. if (null == parameterName) {
  928. p.ParameterName = GetParameterName(1 + parameterCount);
  929. }
  930. else {
  931. p.ParameterName = parameterName;
  932. }
  933. p.Direction = ParameterDirection.Input;
  934. p.SourceColumn = sourceColumn;
  935. p.SourceVersion = version;
  936. p.SourceColumnNullMapping = false;
  937. p.Value = value;
  938. p.Size = 0; // don't specify parameter.Size so that we don't silently truncate to the metadata size
  939. ApplyParameterInfo(p, row.DataRow, statementType, whereClause);
  940. if (!command.Parameters.Contains(p)) {
  941. command.Parameters.Add(p);
  942. }
  943. if (null == parameterName) {
  944. return GetParameterPlaceholder(1 + parameterCount);
  945. }
  946. else {
  947. Debug.Assert(null != _parameterNames, "How can we have a parameterName without a _parameterNames collection?");
  948. Debug.Assert(null != _parameterMarkerFormat, "How can we have a _parameterNames collection but no _parameterMarkerFormat?");
  949. return String.Format(CultureInfo.InvariantCulture, _parameterMarkerFormat, parameterName);
  950. }
  951. }
  952. override protected void Dispose(bool disposing) { // V1.2.3300, XXXCommandBuilder V1.0.3300
  953. // MDAC 65459
  954. if (disposing) {
  955. // release mananged objects
  956. DataAdapter = null;
  957. }
  958. //release unmanaged objects
  959. base.Dispose(disposing); // notify base classes
  960. }
  961. private DataTableMapping GetTableMapping(DataRow dataRow ) {
  962. DataTableMapping tableMapping = null;
  963. if (null != dataRow) {
  964. DataTable dataTable = dataRow.Table;
  965. if (null != dataTable) {
  966. DbDataAdapter adapter = DataAdapter;
  967. if (null != adapter) {
  968. tableMapping = adapter.GetTableMapping(dataTable);
  969. }
  970. else {
  971. string tableName = dataTable.TableName;
  972. tableMapping = new DataTableMapping(tableName, tableName);
  973. }
  974. }
  975. }
  976. return tableMapping;
  977. }
  978. private string GetBaseParameterName(int index) {
  979. if (null != _parameterNames) {
  980. return (_parameterNames.GetBaseParameterName(index));
  981. }
  982. else {
  983. return null;
  984. }
  985. }
  986. private string GetOriginalParameterName(int index) {
  987. if (null != _parameterNames) {
  988. return (_parameterNames.GetOriginalParameterName(index));
  989. }
  990. else {
  991. return null;
  992. }
  993. }
  994. private string GetNullParameterName(int index) {
  995. if (null != _parameterNames) {
  996. return (_parameterNames.GetNullParameterName(index));
  997. }
  998. else {
  999. return null;
  1000. }
  1001. }
  1002. private DbCommand GetSelectCommand() { // V1.2.3300
  1003. DbCommand select = null;
  1004. DbDataAdapter adapter = DataAdapter;
  1005. if (null != adapter) {
  1006. if (0 == _missingMappingAction) {
  1007. _missingMappingAction = adapter.MissingMappingAction;
  1008. }
  1009. select = (DbCommand)adapter.SelectCommand;
  1010. }
  1011. if (null == select) {
  1012. throw ADP.MissingSourceCommand();
  1013. }
  1014. return select;
  1015. }
  1016. // open connection is required by OleDb/OdbcCommandBuilder.QuoteIdentifier and UnquoteIdentifier
  1017. // to get literals quotes from the driver
  1018. internal DbConnection GetConnection() {
  1019. DbDataAdapter adapter = DataAdapter;
  1020. if (adapter != null) {
  1021. DbCommand select = (DbCommand)adapter.SelectCommand;
  1022. if (select != null) {
  1023. return select.Connection;
  1024. }
  1025. }
  1026. return null;
  1027. }
  1028. public DbCommand GetInsertCommand() { // V1.2.3300, XXXCommandBuilder V1.0.3300
  1029. return GetInsertCommand((DataRow)null, false);
  1030. }
  1031. public DbCommand GetInsertCommand(bool useColumnsForParameterNames) {
  1032. return GetInsertCommand((DataRow)null, useColumnsForParameterNames);
  1033. }
  1034. internal DbCommand GetInsertCommand(DataRow dataRow, bool useColumnsForParameterNames) {
  1035. BuildCache(true, dataRow, useColumnsForParameterNames);
  1036. BuildInsertCommand(GetTableMapping(dataRow), dataRow);
  1037. return InsertCommand;
  1038. }
  1039. public DbCommand GetUpdateCommand() { // V1.2.3300, XXXCommandBuilder V1.0.3300
  1040. return GetUpdateCommand((DataRow)null, false);
  1041. }
  1042. public DbCommand GetUpdateCommand(bool useColumnsForParameterNames) {
  1043. return GetUpdateCommand((DataRow)null, useColumnsForParameterNames);
  1044. }
  1045. internal DbCommand GetUpdateCommand(DataRow dataRow, bool useColumnsForParameterNames) {
  1046. BuildCache(true, dataRow, useColumnsForParameterNames);
  1047. BuildUpdateCommand(GetTableMapping(dataRow), dataRow);
  1048. return UpdateCommand;
  1049. }
  1050. public DbCommand GetDeleteCommand() { // V1.2.3300, XXXCommandBuilder V1.0.3300
  1051. return GetDeleteCommand((DataRow)null, false);
  1052. }
  1053. public DbCommand GetDeleteCommand(bool useColumnsForParameterNames) {
  1054. return GetDeleteCommand((DataRow)null, useColumnsForParameterNames);
  1055. }
  1056. internal DbCommand GetDeleteCommand(DataRow dataRow, bool useColumnsForParameterNames) {
  1057. BuildCache(true, dataRow, useColumnsForParameterNames);
  1058. BuildDeleteCommand(GetTableMapping(dataRow), dataRow);
  1059. return DeleteCommand;
  1060. }
  1061. private object GetColumnValue(DataRow row, String columnName, DataTableMapping mappings, DataRowVersion version) {
  1062. return GetColumnValue(row, GetDataColumn(columnName, mappings, row), version);
  1063. }
  1064. private object GetColumnValue(DataRow row, DataColumn column, DataRowVersion version) {
  1065. object value = null;
  1066. if (null != column) {
  1067. value = row[column, version];
  1068. }
  1069. return value;
  1070. }
  1071. private DataColumn GetDataColumn(string columnName, DataTableMapping tablemapping, DataRow row) {
  1072. DataColumn column = null;
  1073. if (!ADP.IsEmpty(columnName)) {
  1074. column = tablemapping.GetDataColumn(columnName, null, row.Table, _missingMappingAction, MissingSchemaAction.Error);
  1075. }
  1076. return column;
  1077. }
  1078. static private DbParameter GetNextParameter(DbCommand command, int pcount) {
  1079. DbParameter p;
  1080. if (pcount < command.Parameters.Count) {
  1081. p = command.Parameters[pcount];
  1082. }
  1083. else {
  1084. p = command.CreateParameter();
  1085. /*if (null == p) {
  1086. //
  1087. */
  1088. }
  1089. Debug.Assert(null != p, "null CreateParameter");
  1090. return p;
  1091. }
  1092. private bool IncludeInInsertValues(DbSchemaRow row) {
  1093. //
  1094. return (!row.IsAutoIncrement && !row.IsHidden && !row.IsExpression && !row.IsRowVersion && !row.IsReadOnly);
  1095. }
  1096. private bool IncludeInUpdateSet(DbSchemaRow row) {
  1097. //
  1098. return (!row.IsAutoIncrement && !row.IsRowVersion && !row.IsHidden && !row.IsReadOnly);
  1099. }
  1100. private bool IncludeInWhereClause(DbSchemaRow row, bool isUpdate) {
  1101. bool flag = IncrementWhereCount(row);
  1102. if (flag && row.IsHidden) { // MDAC 52564
  1103. if (ConflictOption.CompareRowVersion == ConflictOption) {
  1104. throw ADP.DynamicSQLNoKeyInfoRowVersionUpdate();
  1105. }
  1106. throw ADP.DynamicSQLNoKeyInfoUpdate();
  1107. }
  1108. if (!flag && (ConflictOption.CompareAllSearchableValues == ConflictOption)) {
  1109. // include other searchable values
  1110. flag = !row.IsLong && !row.IsRowVersion && !row.IsHidden;
  1111. }
  1112. return flag;
  1113. }
  1114. private bool IncrementWhereCount(DbSchemaRow row) {
  1115. ConflictOption value = ConflictOption;
  1116. switch(value) {
  1117. case ConflictOption.CompareAllSearchableValues:
  1118. case ConflictOption.OverwriteChanges:
  1119. // find the primary key
  1120. return (row.IsKey || row.IsUnique) && !row.IsLong && !row.IsRowVersion;
  1121. case ConflictOption.CompareRowVersion:
  1122. // or the row version
  1123. return (((row.IsKey || row.IsUnique) && !_hasPartialPrimaryKey) || row.IsRowVersion) && !row.IsLong;
  1124. default:
  1125. throw ADP.InvalidConflictOptions(value);
  1126. }
  1127. }
  1128. virtual protected DbCommand InitializeCommand(DbCommand command) { // V1.2.3300
  1129. if (null == command) {
  1130. DbCommand select = GetSelectCommand();
  1131. command = select.Connection.CreateCommand();
  1132. /*if (null == command) {
  1133. //
  1134. */
  1135. // the following properties are only initialized when the object is created
  1136. // all other properites are reinitialized on every row
  1137. /*command.Connection = select.Connection;*/ // initialized by CreateCommand
  1138. command.CommandTimeout = select.CommandTimeout;
  1139. command.Transaction = select.Transaction;
  1140. }
  1141. command.CommandType = CommandType.Text;
  1142. command.UpdatedRowSource = UpdateRowSource.None; // no select or output parameters expected
  1143. return command;
  1144. }
  1145. private string QuotedColumn(string column) {
  1146. return ADP.BuildQuotedString(QuotePrefix, QuoteSuffix, column);
  1147. }
  1148. public virtual string QuoteIdentifier(string unquotedIdentifier ) {
  1149. throw ADP.NotSupported();
  1150. }
  1151. virtual public void RefreshSchema() { // V1.2.3300, XXXCommandBuilder V1.0.3300
  1152. _dbSchemaTable = null;
  1153. _dbSchemaRows = null;
  1154. _sourceColumnNames = null;
  1155. _quotedBaseTableName = null;
  1156. DbDataAdapter adapter = DataAdapter;
  1157. if (null != adapter) { // MDAC 66016
  1158. if (InsertCommand == adapter.InsertCommand) {
  1159. adapter.InsertCommand = null;
  1160. }
  1161. if (UpdateCommand == adapter.UpdateCommand) {
  1162. adapter.UpdateCommand = null;
  1163. }
  1164. if (DeleteCommand == adapter.DeleteCommand) {
  1165. adapter.DeleteCommand = null;
  1166. }
  1167. }
  1168. DbCommand command;
  1169. if (null != (command = InsertCommand)) {
  1170. command.Dispose();
  1171. }
  1172. if (null != (command = UpdateCommand)) {
  1173. command.Dispose();
  1174. }
  1175. if (null != (command = DeleteCommand)) {
  1176. command.Dispose();
  1177. }
  1178. InsertCommand = null;
  1179. UpdateCommand = null;
  1180. DeleteCommand = null;
  1181. }
  1182. static private void RemoveExtraParameters(DbCommand command, int usedParameterCount) {
  1183. for (int i = command.Parameters.Count-1; i >= usedParameterCount; --i) {
  1184. command.Parameters.RemoveAt(i);
  1185. }
  1186. }
  1187. protected void RowUpdatingHandler(RowUpdatingEventArgs rowUpdatingEvent) {
  1188. if (null == rowUpdatingEvent) {
  1189. throw ADP.ArgumentNull("rowUpdatingEvent");
  1190. }
  1191. try {
  1192. if (UpdateStatus.Continue == rowUpdatingEvent.Status) {
  1193. StatementType stmtType = rowUpdatingEvent.StatementType;
  1194. DbCommand command = (DbCommand)rowUpdatingEvent.Command;
  1195. if (null != command) {
  1196. switch(stmtType) {
  1197. case StatementType.Select:
  1198. Debug.Assert(false, "how did we get here?");
  1199. return; // don't mess with it
  1200. case StatementType.Insert:
  1201. command = InsertCommand;
  1202. break;
  1203. case StatementType.Update:
  1204. command = UpdateCommand;
  1205. break;
  1206. case StatementType.Delete:
  1207. command = DeleteCommand;
  1208. break;
  1209. default:
  1210. throw ADP.InvalidStatementType(stmtType);
  1211. }
  1212. if (command != rowUpdatingEvent.Command) {
  1213. command = (DbCommand)rowUpdatingEvent.Command;
  1214. if ((null != command) && (null == command.Connection)) { // MDAC 87649
  1215. DbDataAdapter adapter = DataAdapter;
  1216. DbCommand select = ((null != adapter) ? ((DbCommand)adapter.SelectCommand) : null);
  1217. if (null != select) {
  1218. command.Connection = (DbConnection)select.Connection;
  1219. }
  1220. }
  1221. // user command, not a command builder command
  1222. }
  1223. else command = null;
  1224. }
  1225. if (null == command) {
  1226. RowUpdatingHandlerBuilder(rowUpdatingEvent);
  1227. }
  1228. }
  1229. }
  1230. catch(Exception e) {
  1231. //
  1232. if (!ADP.IsCatchableExceptionType(e)) {
  1233. throw;
  1234. }
  1235. ADP.TraceExceptionForCapture(e);
  1236. rowUpdatingEvent.Status = UpdateStatus.ErrorsOccurred;
  1237. rowUpdatingEvent.Errors = e;
  1238. }
  1239. }
  1240. private void RowUpdatingHandlerBuilder(RowUpdatingEventArgs rowUpdatingEvent) {
  1241. // MDAC 58710 - unable to tell Update method that Event opened connection and Update needs to close when done
  1242. // HackFix - the Update method will close the connection if command was null and returned command.Connection is same as SelectCommand.Connection
  1243. DataRow datarow = rowUpdatingEvent.Row;
  1244. BuildCache(false, datarow, false);
  1245. DbCommand command;
  1246. switch(rowUpdatingEvent.StatementType) {
  1247. case StatementType.Insert:
  1248. command = BuildInsertCommand(rowUpdatingEvent.TableMapping, datarow);
  1249. break;
  1250. case StatementType.Update:
  1251. command = BuildUpdateCommand(rowUpdatingEvent.TableMapping, datarow);
  1252. break;
  1253. case StatementType.Delete:
  1254. command = BuildDeleteCommand(rowUpdatingEvent.TableMapping, datarow);
  1255. break;
  1256. #if DEBUG
  1257. case StatementType.Select:
  1258. Debug.Assert(false, "how did we get here?");
  1259. goto default;
  1260. #endif
  1261. default:
  1262. throw ADP.InvalidStatementType(rowUpdatingEvent.StatementType);
  1263. }
  1264. if (null == command) {
  1265. if (null != datarow) {
  1266. datarow.AcceptChanges();
  1267. }
  1268. rowUpdatingEvent.Status = UpdateStatus.SkipCurrentRow;
  1269. }
  1270. rowUpdatingEvent.Command = command;
  1271. }
  1272. public virtual string UnquoteIdentifier(string quotedIdentifier ) {
  1273. throw ADP.NotSupported();
  1274. }
  1275. abstract protected void ApplyParameterInfo(DbParameter parameter, DataRow row, StatementType statementType, bool whereClause); // V1.2.3300
  1276. abstract protected string GetParameterName(int parameterOrdinal); // V1.2.3300
  1277. abstract protected string GetParameterName(string parameterName);
  1278. abstract protected string GetParameterPlaceholder(int parameterOrdinal); // V1.2.3300
  1279. abstract protected void SetRowUpdatingHandler(DbDataAdapter adapter); // V1.2.3300
  1280. //
  1281. static internal string[] ParseProcedureName(string name, string quotePrefix, string quoteSuffix) {
  1282. // Procedure may consist of up to four parts:
  1283. // 0) Server
  1284. // 1) Catalog
  1285. // 2) Schema
  1286. // 3) ProcedureName
  1287. //
  1288. // Parse the string into four parts, allowing the last part to contain '.'s.
  1289. // If less than four period delimited parts, use the parts from procedure backwards.
  1290. //
  1291. const string Separator = ".";
  1292. string[] qualifiers = new string[4];
  1293. if (!ADP.IsEmpty(name)) {
  1294. bool useQuotes = !ADP.IsEmpty(quotePrefix) && !ADP.IsEmpty(quoteSuffix);
  1295. int currentPos = 0, parts;
  1296. for(parts = 0; (parts < qualifiers.Length) && (currentPos < name.Length); ++parts) {
  1297. int startPos = currentPos;
  1298. // does the part begin with a quotePrefix?
  1299. if (useQuotes && (name.IndexOf(quotePrefix, currentPos, quotePrefix.Length, StringComparison.Ordinal) == currentPos)) {
  1300. currentPos += quotePrefix.Length; // move past the quotePrefix
  1301. // search for the quoteSuffix (or end of string)
  1302. while (currentPos < name.Length) {
  1303. currentPos = name.IndexOf(quoteSuffix, currentPos, StringComparison.Ordinal);
  1304. if (currentPos < 0) {
  1305. // error condition, no quoteSuffix
  1306. currentPos = name.Length;
  1307. break;
  1308. }
  1309. else {
  1310. currentPos += quoteSuffix.Length; // move past the quoteSuffix
  1311. // is this a double quoteSuffix?
  1312. if ((currentPos < name.Length) && (name.IndexOf(quoteSuffix, currentPos, quoteSuffix.Length, StringComparison.Ordinal) == currentPos)) {
  1313. // a second quoteSuffix, continue search for terminating quoteSuffix
  1314. currentPos += quoteSuffix.Length; // move past the second quoteSuffix
  1315. }
  1316. else {
  1317. // found the terminating quoteSuffix
  1318. break;
  1319. }
  1320. }
  1321. }
  1322. }
  1323. // search for separator (either no quotePrefix or already past quoteSuffix)
  1324. if (currentPos < name.Length) {
  1325. currentPos = name.IndexOf(Separator, currentPos, StringComparison.Ordinal);
  1326. if ((currentPos < 0) || (parts == qualifiers.Length-1)) {
  1327. // last part that can be found
  1328. currentPos = name.Length;
  1329. }
  1330. }
  1331. qualifiers[parts] = name.Substring(startPos, currentPos-startPos);
  1332. currentPos += Separator.Length;
  1333. }
  1334. // allign the qualifiers if we had less than MaxQualifiers
  1335. for(int j = qualifiers.Length-1; 0 <= j; --j) {
  1336. qualifiers[j] = ((0 < parts) ? qualifiers[--parts] : null);
  1337. }
  1338. }
  1339. return qualifiers;
  1340. }
  1341. }
  1342. }