diff --git a/lib/Config/Migrator.php b/lib/Config/Migrator.php index 747598d74b..206cc3d7c1 100644 --- a/lib/Config/Migrator.php +++ b/lib/Config/Migrator.php @@ -60,9 +60,9 @@ class Migrator { 'name varchar(90) NOT NULL,', 'type varchar(90) NOT NULL DEFAULT "default",', 'description varchar(250) NOT NULL DEFAULT "",', - 'created_at TIMESTAMP NOT NULL DEFAULT 0,', - 'deleted_at TIMESTAMP NULL DEFAULT NULL,', - 'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', + 'created_at TIMESTAMP NULL,', + 'deleted_at TIMESTAMP NULL,', + 'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'PRIMARY KEY (id),', 'UNIQUE KEY name (name)' ); @@ -110,11 +110,11 @@ class Migrator { 'count_processed mediumint(9) NOT NULL DEFAULT 0,', 'count_to_process mediumint(9) NOT NULL DEFAULT 0,', 'count_failed mediumint(9) NOT NULL DEFAULT 0,', - 'scheduled_at TIMESTAMP NOT NULL DEFAULT 0,', - 'processed_at TIMESTAMP NOT NULL DEFAULT 0,', - 'created_at TIMESTAMP NOT NULL DEFAULT 0,', - 'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', - 'deleted_at TIMESTAMP NULL DEFAULT NULL,', + 'scheduled_at TIMESTAMP NULL,', + 'processed_at TIMESTAMP NULL,', + 'created_at TIMESTAMP NULL,', + 'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', + 'deleted_at TIMESTAMP NULL,', 'PRIMARY KEY (id)', ); return $this->sqlify(__FUNCTION__, $attributes); @@ -128,9 +128,9 @@ class Migrator { 'last_name tinytext NOT NULL DEFAULT "",', 'email varchar(150) NOT NULL,', 'status varchar(12) NOT NULL DEFAULT "unconfirmed",', - 'created_at TIMESTAMP NOT NULL DEFAULT 0,', - 'deleted_at TIMESTAMP NULL DEFAULT NULL,', - 'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', + 'created_at TIMESTAMP NULL,', + 'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', + 'deleted_at TIMESTAMP NULL,', 'PRIMARY KEY (id),', 'UNIQUE KEY email (email)' ); @@ -143,8 +143,8 @@ class Migrator { 'subscriber_id mediumint(9) NOT NULL,', 'segment_id mediumint(9) NOT NULL,', 'status varchar(12) NOT NULL DEFAULT "subscribed",', - 'created_at TIMESTAMP NOT NULL DEFAULT 0,', - 'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', + 'created_at TIMESTAMP NULL,', + 'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'PRIMARY KEY (id),', 'UNIQUE KEY subscriber_segment (subscriber_id,segment_id)' ); @@ -157,8 +157,8 @@ class Migrator { 'subscriber_id mediumint(9) NOT NULL,', 'custom_field_id mediumint(9) NOT NULL,', 'value varchar(255) NOT NULL DEFAULT "",', - 'created_at TIMESTAMP NOT NULL DEFAULT 0,', - 'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', + 'created_at TIMESTAMP NULL,', + 'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'PRIMARY KEY (id),', 'UNIQUE KEY subscriber_id_custom_field_id (subscriber_id,custom_field_id)' ); @@ -176,9 +176,9 @@ class Migrator { 'reply_to_name varchar(150) NOT NULL DEFAULT "",', 'preheader varchar(250) NOT NULL DEFAULT "",', 'body longtext,', - 'created_at TIMESTAMP NOT NULL DEFAULT 0,', - 'deleted_at TIMESTAMP NULL DEFAULT NULL,', - 'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', + 'created_at TIMESTAMP NULL,', + 'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', + 'deleted_at TIMESTAMP NULL,', 'PRIMARY KEY (id)' ); return $this->sqlify(__FUNCTION__, $attributes); @@ -192,8 +192,8 @@ class Migrator { 'body LONGTEXT,', 'thumbnail LONGTEXT,', 'readonly TINYINT(1) DEFAULT 0,', - 'created_at TIMESTAMP NOT NULL DEFAULT 0,', - 'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', + 'created_at TIMESTAMP NULL,', + 'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'PRIMARY KEY (id)' ); return $this->sqlify(__FUNCTION__, $attributes); @@ -204,8 +204,8 @@ class Migrator { 'id mediumint(9) NOT NULL AUTO_INCREMENT,', 'name varchar(90) NOT NULL,', 'newsletter_type varchar(90) NOT NULL,', - 'created_at TIMESTAMP NOT NULL DEFAULT 0,', - 'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', + 'created_at TIMESTAMP NULL,', + 'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'PRIMARY KEY (id),', 'UNIQUE KEY name_newsletter_type (newsletter_type,name)' ); @@ -218,8 +218,8 @@ class Migrator { 'newsletter_id mediumint(9) NOT NULL,', 'option_field_id mediumint(9) NOT NULL,', 'value varchar(255) NOT NULL DEFAULT "",', - 'created_at TIMESTAMP NOT NULL DEFAULT 0,', - 'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', + 'created_at TIMESTAMP NULL,', + 'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'PRIMARY KEY (id),', 'UNIQUE KEY newsletter_id_option_field_id (newsletter_id,option_field_id)' ); @@ -231,8 +231,8 @@ class Migrator { 'id mediumint(9) NOT NULL AUTO_INCREMENT,', 'newsletter_id mediumint(9) NOT NULL,', 'segment_id mediumint(9) NOT NULL,', - 'created_at TIMESTAMP NOT NULL DEFAULT 0,', - 'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', + 'created_at TIMESTAMP NULL,', + 'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'PRIMARY KEY (id),', 'UNIQUE KEY newsletter_segment (newsletter_id,segment_id)' ); @@ -246,8 +246,8 @@ class Migrator { 'queue_id mediumint(9) NOT NULL,', 'url varchar(255) NOT NULL,', 'hash varchar(20) NOT NULL,', - 'created_at TIMESTAMP NOT NULL DEFAULT 0,', - 'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', + 'created_at TIMESTAMP NULL,', + 'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'PRIMARY KEY (id)', ); return $this->sqlify(__FUNCTION__, $attributes); @@ -260,9 +260,9 @@ class Migrator { 'body longtext,', 'settings longtext,', 'styles longtext,', - 'created_at TIMESTAMP NOT NULL DEFAULT 0,', - 'deleted_at TIMESTAMP NULL DEFAULT NULL,', - 'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', + 'created_at TIMESTAMP NULL,', + 'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', + 'deleted_at TIMESTAMP NULL,', 'PRIMARY KEY (id)' ); return $this->sqlify(__FUNCTION__, $attributes); @@ -274,7 +274,7 @@ class Migrator { 'newsletter_id mediumint(9) NOT NULL,', 'subscriber_id mediumint(9) NOT NULL,', 'queue_id mediumint(9) NOT NULL,', - 'sent_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', + 'sent_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'PRIMARY KEY (id)', ); return $this->sqlify(__FUNCTION__, $attributes); @@ -288,8 +288,8 @@ class Migrator { 'queue_id mediumint(9) NOT NULL,', 'link_id mediumint(9) NOT NULL,', 'count mediumint(9) NOT NULL,', - 'created_at TIMESTAMP NOT NULL DEFAULT 0,', - 'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', + 'created_at TIMESTAMP NULL,', + 'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'PRIMARY KEY (id)', ); return $this->sqlify(__FUNCTION__, $attributes); @@ -301,7 +301,7 @@ class Migrator { 'newsletter_id mediumint(9) NOT NULL,', 'subscriber_id mediumint(9) NOT NULL,', 'queue_id mediumint(9) NOT NULL,', - 'created_at TIMESTAMP NOT NULL DEFAULT 0,', + 'created_at TIMESTAMP NULL,', 'PRIMARY KEY (id)', ); return $this->sqlify(__FUNCTION__, $attributes); @@ -313,7 +313,7 @@ class Migrator { 'newsletter_id mediumint(9) NOT NULL,', 'subscriber_id mediumint(9) NOT NULL,', 'queue_id mediumint(9) NOT NULL,', - 'created_at TIMESTAMP NOT NULL DEFAULT 0,', + 'created_at TIMESTAMP NULL,', 'PRIMARY KEY (id)', ); return $this->sqlify(__FUNCTION__, $attributes); @@ -324,7 +324,7 @@ class Migrator { 'id mediumint(9) NOT NULL AUTO_INCREMENT,', 'form_id mediumint(9) NOT NULL,', 'subscriber_id mediumint(9) NOT NULL,', - 'created_at TIMESTAMP NOT NULL DEFAULT 0,', + 'created_at TIMESTAMP NULL,', 'PRIMARY KEY (id),', 'UNIQUE KEY form_subscriber (form_id,subscriber_id)' ); diff --git a/lib/Subscribers/ImportExport/Export/Export.php b/lib/Subscribers/ImportExport/Export/Export.php index 93e1292e3b..c3d7ebeed4 100644 --- a/lib/Subscribers/ImportExport/Export/Export.php +++ b/lib/Subscribers/ImportExport/Export/Export.php @@ -175,6 +175,7 @@ class Export { } function getSubscribers($offset, $limit) { + // JOIN subscribers on segment and subscriber_segment tables $subscribers = Subscriber:: left_outer_join( SubscriberSegment::$_table, @@ -192,6 +193,8 @@ class Export { )) ->filter('filterWithCustomFieldsForExport'); if($this->subscribers_without_segment !== false) { + // if there are subscribers who do not belong to any segment, use + // a CASE function to group them under "Not In Segment" $subscribers = $subscribers ->selectExpr('CASE WHEN ' . Segment::$_table . '.name IS NOT NULL ' . 'THEN ' . Segment::$_table . '.name ' . @@ -204,21 +207,26 @@ class Export { $this->segments ); } else { + // use an aggregate function to prevent non-deterministic GROUP BY issue + // in MySQL 5.7+ $subscribers = $subscribers - ->select(Segment::$_table . '.name', 'segment_name') + ->selectExpr('CONCAT('.Segment::$_table . '.name) as segment_name') ->whereIn(SubscriberSegment::$_table . '.segment_id', $this->segments); } if(!$this->group_by_segment_option) { + // if grouping by segments, use a GROUP BY clause on subscriber id $subscribers = $subscribers->groupBy(Subscriber::$_table . '.id'); } if($this->export_confirmed_option) { + // select only subscribers with "subscribed" status $subscribers = $subscribers->where(Subscriber::$_table . '.status', 'subscribed'); } $subscribers = $subscribers ->whereNull(Subscriber::$_table . '.deleted_at') - ->limit(sprintf('%d, %d', $offset, $limit)) + ->offset($offset) + ->limit($limit) ->findArray(); return $subscribers; } diff --git a/tests/unit/Models/CustomFieldTest.php b/tests/unit/Models/CustomFieldTest.php index 647fd3cf54..a3e7df2cea 100644 --- a/tests/unit/Models/CustomFieldTest.php +++ b/tests/unit/Models/CustomFieldTest.php @@ -66,7 +66,6 @@ class CustomFieldTest extends MailPoetTest { function testItHasACreatedAtOnCreation() { $custom_field = CustomField::findOne($this->custom_field->id); expect($custom_field->created_at)->notNull(); - expect($custom_field->created_at)->notEquals('0000-00-00 00:00:00'); } function testItHasAnUpdatedAtOnCreation() { diff --git a/tests/unit/Models/FormTest.php b/tests/unit/Models/FormTest.php index 718326cc56..68c4a947a0 100644 --- a/tests/unit/Models/FormTest.php +++ b/tests/unit/Models/FormTest.php @@ -49,7 +49,6 @@ class FormTest extends MailPoetTest { function testItHasACreatedAtOnCreation() { $form = Form::findOne($this->form->id); expect($form->created_at)->notNull(); - expect($form->created_at)->notEquals('0000-00-00 00:00:00'); } function testItHasAnUpdatedAtOnCreation() { diff --git a/tests/unit/Models/NewsletterOptionFieldTest.php b/tests/unit/Models/NewsletterOptionFieldTest.php index b57a280387..1e6ef9dd86 100644 --- a/tests/unit/Models/NewsletterOptionFieldTest.php +++ b/tests/unit/Models/NewsletterOptionFieldTest.php @@ -59,7 +59,6 @@ class NewsletterOptionFieldTest extends MailPoetTest { function testItHasACreatedAtOnCreation() { expect($this->option_field->created_at)->notNull(); - expect($this->option_field->created_at)->notEquals('0000-00-00 00:00:00'); } function testItHasAnUpdatedAtOnCreation() { diff --git a/tests/unit/Models/NewsletterTest.php b/tests/unit/Models/NewsletterTest.php index 4ff9ab77a0..69a544e7cc 100644 --- a/tests/unit/Models/NewsletterTest.php +++ b/tests/unit/Models/NewsletterTest.php @@ -62,7 +62,6 @@ class NewsletterTest extends MailPoetTest { function testItHasACreatedAtOnCreation() { $newsletter = Newsletter::findOne($this->newsletter->id); expect($newsletter->created_at)->notNull(); - expect($newsletter->created_at)->notEquals('0000-00-00 00:00:00'); } function testItHasAnUpdatedAtOnCreation() { diff --git a/tests/unit/Models/SegmentTest.php b/tests/unit/Models/SegmentTest.php index 0f711abc9f..683aba9bc8 100644 --- a/tests/unit/Models/SegmentTest.php +++ b/tests/unit/Models/SegmentTest.php @@ -78,7 +78,6 @@ class SegmentTest extends MailPoetTest { function testItHasACreatedAtOnCreation() { $segment = Segment::findOne($this->segment->id); expect($segment->created_at)->notNull(); - expect($segment->created_at)->notEquals('0000-00-00 00:00:00'); } function testItHasAnUpdatedAtOnCreation() { diff --git a/tests/unit/Subscribers/ImportExport/Export/ExportTest.php b/tests/unit/Subscribers/ImportExport/Export/ExportTest.php index f8aa865e67..8b07008d6a 100644 --- a/tests/unit/Subscribers/ImportExport/Export/ExportTest.php +++ b/tests/unit/Subscribers/ImportExport/Export/ExportTest.php @@ -64,9 +64,9 @@ class ExportTest extends MailPoetTest { $entity->hydrate($subscriber); $entity->save(); } - foreach($this->segments_data as $custom_field) { + foreach($this->segments_data as $segment) { $entity = Segment::create(); - $entity->hydrate($custom_field); + $entity->hydrate($segment); $entity->save(); } foreach($this->custom_fields_data as $custom_field) {