InvalidDataTest.php 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. <?php
  2. namespace Drupal\KernelTests\Core\Database;
  3. use Drupal\Core\Database\Database;
  4. use Drupal\Core\Database\IntegrityConstraintViolationException;
  5. /**
  6. * Tests handling of some invalid data.
  7. *
  8. * @group Database
  9. */
  10. class InvalidDataTest extends DatabaseTestBase {
  11. /**
  12. * Tests aborting of traditional SQL database systems with invalid data.
  13. */
  14. public function testInsertDuplicateData() {
  15. // Try to insert multiple records where at least one has bad data.
  16. try {
  17. db_insert('test')
  18. ->fields(['name', 'age', 'job'])
  19. ->values([
  20. 'name' => 'Elvis',
  21. 'age' => 63,
  22. 'job' => 'Singer',
  23. ])->values([
  24. // Duplicate value on unique field.
  25. 'name' => 'John',
  26. 'age' => 17,
  27. 'job' => 'Consultant',
  28. ])
  29. ->values([
  30. 'name' => 'Frank',
  31. 'age' => 75,
  32. 'job' => 'Singer',
  33. ])
  34. ->execute();
  35. $this->fail('Insert succeeded when it should not have.');
  36. }
  37. catch (IntegrityConstraintViolationException $e) {
  38. // Check if the first record was inserted.
  39. $name = db_query('SELECT name FROM {test} WHERE age = :age', [':age' => 63])->fetchField();
  40. if ($name == 'Elvis') {
  41. if (!Database::getConnection()->supportsTransactions()) {
  42. // This is an expected fail.
  43. // Database engines that don't support transactions can leave partial
  44. // inserts in place when an error occurs. This is the case for MySQL
  45. // when running on a MyISAM table.
  46. $this->pass("The whole transaction has not been rolled-back when a duplicate key insert occurs, this is expected because the database doesn't support transactions");
  47. }
  48. else {
  49. $this->fail('The whole transaction is rolled back when a duplicate key insert occurs.');
  50. }
  51. }
  52. else {
  53. $this->pass('The whole transaction is rolled back when a duplicate key insert occurs.');
  54. }
  55. // Ensure the other values were not inserted.
  56. $record = db_select('test')
  57. ->fields('test', ['name', 'age'])
  58. ->condition('age', [17, 75], 'IN')
  59. ->execute()->fetchObject();
  60. $this->assertFalse($record, 'The rest of the insert aborted as expected.');
  61. }
  62. }
  63. /**
  64. * Tests inserting with invalid data from a select query.
  65. */
  66. public function testInsertDuplicateDataFromSelect() {
  67. // Insert multiple records in 'test_people' where one has bad data
  68. // (duplicate key). A 'Meredith' record has already been inserted
  69. // in ::setUp.
  70. db_insert('test_people')
  71. ->fields(['name', 'age', 'job'])
  72. ->values([
  73. 'name' => 'Elvis',
  74. 'age' => 63,
  75. 'job' => 'Singer',
  76. ])->values([
  77. // Duplicate value on unique field 'name' for later INSERT in 'test'
  78. // table.
  79. 'name' => 'John',
  80. 'age' => 17,
  81. 'job' => 'Consultant',
  82. ])
  83. ->values([
  84. 'name' => 'Frank',
  85. 'age' => 75,
  86. 'job' => 'Bass',
  87. ])
  88. ->execute();
  89. try {
  90. // Define the subselect query. Add ORDER BY to ensure we have consistent
  91. // order in results. Will return:
  92. // 0 => [name] => Elvis, [age] => 63, [job] => Singer
  93. // 1 => [name] => Frank, [age] => 75, [job] => Bass
  94. // 2 => [name] => John, [age] => 17, [job] => Consultant
  95. // 3 => [name] => Meredith, [age] => 30, [job] => Speaker
  96. // Records 0 and 1 should pass, record 2 should lead to integrity
  97. // constraint violation.
  98. $query = db_select('test_people', 'tp')
  99. ->fields('tp', ['name', 'age', 'job'])
  100. ->orderBy('name');
  101. // Try inserting from the subselect.
  102. db_insert('test')
  103. ->from($query)
  104. ->execute();
  105. $this->fail('Insert succeeded when it should not have.');
  106. }
  107. catch (IntegrityConstraintViolationException $e) {
  108. // Check if the second record was inserted.
  109. $name = db_query('SELECT name FROM {test} WHERE age = :age', [':age' => 75])->fetchField();
  110. if ($name == 'Frank') {
  111. if (!Database::getConnection()->supportsTransactions()) {
  112. // This is an expected fail.
  113. // Database engines that don't support transactions can leave partial
  114. // inserts in place when an error occurs. This is the case for MySQL
  115. // when running on a MyISAM table.
  116. $this->pass("The whole transaction has not been rolled-back when a duplicate key insert occurs, this is expected because the database doesn't support transactions");
  117. }
  118. else {
  119. $this->fail('The whole transaction is rolled back when a duplicate key insert occurs.');
  120. }
  121. }
  122. else {
  123. $this->pass('The whole transaction is rolled back when a duplicate key insert occurs.');
  124. }
  125. // Ensure the values for records 2 and 3 were not inserted.
  126. $record = db_select('test')
  127. ->fields('test', ['name', 'age'])
  128. ->condition('age', [17, 30], 'IN')
  129. ->execute()->fetchObject();
  130. $this->assertFalse($record, 'The rest of the insert aborted as expected.');
  131. }
  132. }
  133. }