- Updates migrator/schema to work with MySQL 5.7

- Fixes unit tests
- Fixes export's SQL query to work with strict ONLY_FULL_GROUP_BY option
This commit is contained in:
Vlad
2016-05-16 20:24:17 -04:00
parent 607a151c23
commit a02b2d3aa0
8 changed files with 49 additions and 46 deletions

View File

@ -60,9 +60,9 @@ class Migrator {
'name varchar(90) NOT NULL,', 'name varchar(90) NOT NULL,',
'type varchar(90) NOT NULL DEFAULT "default",', 'type varchar(90) NOT NULL DEFAULT "default",',
'description varchar(250) NOT NULL DEFAULT "",', 'description varchar(250) NOT NULL DEFAULT "",',
'created_at TIMESTAMP NOT NULL DEFAULT 0,', 'created_at TIMESTAMP NULL,',
'deleted_at TIMESTAMP NULL DEFAULT NULL,', 'deleted_at TIMESTAMP NULL,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'PRIMARY KEY (id),', 'PRIMARY KEY (id),',
'UNIQUE KEY name (name)' 'UNIQUE KEY name (name)'
); );
@ -110,11 +110,11 @@ class Migrator {
'count_processed mediumint(9) NOT NULL DEFAULT 0,', 'count_processed mediumint(9) NOT NULL DEFAULT 0,',
'count_to_process mediumint(9) NOT NULL DEFAULT 0,', 'count_to_process mediumint(9) NOT NULL DEFAULT 0,',
'count_failed mediumint(9) NOT NULL DEFAULT 0,', 'count_failed mediumint(9) NOT NULL DEFAULT 0,',
'scheduled_at TIMESTAMP NOT NULL DEFAULT 0,', 'scheduled_at TIMESTAMP NULL,',
'processed_at TIMESTAMP NOT NULL DEFAULT 0,', 'processed_at TIMESTAMP NULL,',
'created_at TIMESTAMP NOT NULL DEFAULT 0,', 'created_at TIMESTAMP NULL,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'deleted_at TIMESTAMP NULL DEFAULT NULL,', 'deleted_at TIMESTAMP NULL,',
'PRIMARY KEY (id)', 'PRIMARY KEY (id)',
); );
return $this->sqlify(__FUNCTION__, $attributes); return $this->sqlify(__FUNCTION__, $attributes);
@ -128,9 +128,9 @@ class Migrator {
'last_name tinytext NOT NULL DEFAULT "",', 'last_name tinytext NOT NULL DEFAULT "",',
'email varchar(150) NOT NULL,', 'email varchar(150) NOT NULL,',
'status varchar(12) NOT NULL DEFAULT "unconfirmed",', 'status varchar(12) NOT NULL DEFAULT "unconfirmed",',
'created_at TIMESTAMP NOT NULL DEFAULT 0,', 'created_at TIMESTAMP NULL,',
'deleted_at TIMESTAMP NULL DEFAULT NULL,', 'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'deleted_at TIMESTAMP NULL,',
'PRIMARY KEY (id),', 'PRIMARY KEY (id),',
'UNIQUE KEY email (email)' 'UNIQUE KEY email (email)'
); );
@ -143,8 +143,8 @@ class Migrator {
'subscriber_id mediumint(9) NOT NULL,', 'subscriber_id mediumint(9) NOT NULL,',
'segment_id mediumint(9) NOT NULL,', 'segment_id mediumint(9) NOT NULL,',
'status varchar(12) NOT NULL DEFAULT "subscribed",', 'status varchar(12) NOT NULL DEFAULT "subscribed",',
'created_at TIMESTAMP NOT NULL DEFAULT 0,', 'created_at TIMESTAMP NULL,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'PRIMARY KEY (id),', 'PRIMARY KEY (id),',
'UNIQUE KEY subscriber_segment (subscriber_id,segment_id)' 'UNIQUE KEY subscriber_segment (subscriber_id,segment_id)'
); );
@ -157,8 +157,8 @@ class Migrator {
'subscriber_id mediumint(9) NOT NULL,', 'subscriber_id mediumint(9) NOT NULL,',
'custom_field_id mediumint(9) NOT NULL,', 'custom_field_id mediumint(9) NOT NULL,',
'value varchar(255) NOT NULL DEFAULT "",', 'value varchar(255) NOT NULL DEFAULT "",',
'created_at TIMESTAMP NOT NULL DEFAULT 0,', 'created_at TIMESTAMP NULL,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'PRIMARY KEY (id),', 'PRIMARY KEY (id),',
'UNIQUE KEY subscriber_id_custom_field_id (subscriber_id,custom_field_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 "",', 'reply_to_name varchar(150) NOT NULL DEFAULT "",',
'preheader varchar(250) NOT NULL DEFAULT "",', 'preheader varchar(250) NOT NULL DEFAULT "",',
'body longtext,', 'body longtext,',
'created_at TIMESTAMP NOT NULL DEFAULT 0,', 'created_at TIMESTAMP NULL,',
'deleted_at TIMESTAMP NULL DEFAULT NULL,', 'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'deleted_at TIMESTAMP NULL,',
'PRIMARY KEY (id)' 'PRIMARY KEY (id)'
); );
return $this->sqlify(__FUNCTION__, $attributes); return $this->sqlify(__FUNCTION__, $attributes);
@ -192,8 +192,8 @@ class Migrator {
'body LONGTEXT,', 'body LONGTEXT,',
'thumbnail LONGTEXT,', 'thumbnail LONGTEXT,',
'readonly TINYINT(1) DEFAULT 0,', 'readonly TINYINT(1) DEFAULT 0,',
'created_at TIMESTAMP NOT NULL DEFAULT 0,', 'created_at TIMESTAMP NULL,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'PRIMARY KEY (id)' 'PRIMARY KEY (id)'
); );
return $this->sqlify(__FUNCTION__, $attributes); return $this->sqlify(__FUNCTION__, $attributes);
@ -204,8 +204,8 @@ class Migrator {
'id mediumint(9) NOT NULL AUTO_INCREMENT,', 'id mediumint(9) NOT NULL AUTO_INCREMENT,',
'name varchar(90) NOT NULL,', 'name varchar(90) NOT NULL,',
'newsletter_type varchar(90) NOT NULL,', 'newsletter_type varchar(90) NOT NULL,',
'created_at TIMESTAMP NOT NULL DEFAULT 0,', 'created_at TIMESTAMP NULL,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'PRIMARY KEY (id),', 'PRIMARY KEY (id),',
'UNIQUE KEY name_newsletter_type (newsletter_type,name)' 'UNIQUE KEY name_newsletter_type (newsletter_type,name)'
); );
@ -218,8 +218,8 @@ class Migrator {
'newsletter_id mediumint(9) NOT NULL,', 'newsletter_id mediumint(9) NOT NULL,',
'option_field_id mediumint(9) NOT NULL,', 'option_field_id mediumint(9) NOT NULL,',
'value varchar(255) NOT NULL DEFAULT "",', 'value varchar(255) NOT NULL DEFAULT "",',
'created_at TIMESTAMP NOT NULL DEFAULT 0,', 'created_at TIMESTAMP NULL,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'PRIMARY KEY (id),', 'PRIMARY KEY (id),',
'UNIQUE KEY newsletter_id_option_field_id (newsletter_id,option_field_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,', 'id mediumint(9) NOT NULL AUTO_INCREMENT,',
'newsletter_id mediumint(9) NOT NULL,', 'newsletter_id mediumint(9) NOT NULL,',
'segment_id mediumint(9) NOT NULL,', 'segment_id mediumint(9) NOT NULL,',
'created_at TIMESTAMP NOT NULL DEFAULT 0,', 'created_at TIMESTAMP NULL,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'PRIMARY KEY (id),', 'PRIMARY KEY (id),',
'UNIQUE KEY newsletter_segment (newsletter_id,segment_id)' 'UNIQUE KEY newsletter_segment (newsletter_id,segment_id)'
); );
@ -246,8 +246,8 @@ class Migrator {
'queue_id mediumint(9) NOT NULL,', 'queue_id mediumint(9) NOT NULL,',
'url varchar(255) NOT NULL,', 'url varchar(255) NOT NULL,',
'hash varchar(20) NOT NULL,', 'hash varchar(20) NOT NULL,',
'created_at TIMESTAMP NOT NULL DEFAULT 0,', 'created_at TIMESTAMP NULL,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'PRIMARY KEY (id)', 'PRIMARY KEY (id)',
); );
return $this->sqlify(__FUNCTION__, $attributes); return $this->sqlify(__FUNCTION__, $attributes);
@ -260,9 +260,9 @@ class Migrator {
'body longtext,', 'body longtext,',
'settings longtext,', 'settings longtext,',
'styles longtext,', 'styles longtext,',
'created_at TIMESTAMP NOT NULL DEFAULT 0,', 'created_at TIMESTAMP NULL,',
'deleted_at TIMESTAMP NULL DEFAULT NULL,', 'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'deleted_at TIMESTAMP NULL,',
'PRIMARY KEY (id)' 'PRIMARY KEY (id)'
); );
return $this->sqlify(__FUNCTION__, $attributes); return $this->sqlify(__FUNCTION__, $attributes);
@ -274,7 +274,7 @@ class Migrator {
'newsletter_id mediumint(9) NOT NULL,', 'newsletter_id mediumint(9) NOT NULL,',
'subscriber_id mediumint(9) NOT NULL,', 'subscriber_id mediumint(9) NOT NULL,',
'queue_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)', 'PRIMARY KEY (id)',
); );
return $this->sqlify(__FUNCTION__, $attributes); return $this->sqlify(__FUNCTION__, $attributes);
@ -288,8 +288,8 @@ class Migrator {
'queue_id mediumint(9) NOT NULL,', 'queue_id mediumint(9) NOT NULL,',
'link_id mediumint(9) NOT NULL,', 'link_id mediumint(9) NOT NULL,',
'count mediumint(9) NOT NULL,', 'count mediumint(9) NOT NULL,',
'created_at TIMESTAMP NOT NULL DEFAULT 0,', 'created_at TIMESTAMP NULL,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'PRIMARY KEY (id)', 'PRIMARY KEY (id)',
); );
return $this->sqlify(__FUNCTION__, $attributes); return $this->sqlify(__FUNCTION__, $attributes);
@ -301,7 +301,7 @@ class Migrator {
'newsletter_id mediumint(9) NOT NULL,', 'newsletter_id mediumint(9) NOT NULL,',
'subscriber_id mediumint(9) NOT NULL,', 'subscriber_id mediumint(9) NOT NULL,',
'queue_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)', 'PRIMARY KEY (id)',
); );
return $this->sqlify(__FUNCTION__, $attributes); return $this->sqlify(__FUNCTION__, $attributes);
@ -313,7 +313,7 @@ class Migrator {
'newsletter_id mediumint(9) NOT NULL,', 'newsletter_id mediumint(9) NOT NULL,',
'subscriber_id mediumint(9) NOT NULL,', 'subscriber_id mediumint(9) NOT NULL,',
'queue_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)', 'PRIMARY KEY (id)',
); );
return $this->sqlify(__FUNCTION__, $attributes); return $this->sqlify(__FUNCTION__, $attributes);
@ -324,7 +324,7 @@ class Migrator {
'id mediumint(9) NOT NULL AUTO_INCREMENT,', 'id mediumint(9) NOT NULL AUTO_INCREMENT,',
'form_id mediumint(9) NOT NULL,', 'form_id mediumint(9) NOT NULL,',
'subscriber_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),', 'PRIMARY KEY (id),',
'UNIQUE KEY form_subscriber (form_id,subscriber_id)' 'UNIQUE KEY form_subscriber (form_id,subscriber_id)'
); );

View File

@ -175,6 +175,7 @@ class Export {
} }
function getSubscribers($offset, $limit) { function getSubscribers($offset, $limit) {
// JOIN subscribers on segment and subscriber_segment tables
$subscribers = Subscriber:: $subscribers = Subscriber::
left_outer_join( left_outer_join(
SubscriberSegment::$_table, SubscriberSegment::$_table,
@ -192,6 +193,8 @@ class Export {
)) ))
->filter('filterWithCustomFieldsForExport'); ->filter('filterWithCustomFieldsForExport');
if($this->subscribers_without_segment !== false) { 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 $subscribers = $subscribers
->selectExpr('CASE WHEN ' . Segment::$_table . '.name IS NOT NULL ' . ->selectExpr('CASE WHEN ' . Segment::$_table . '.name IS NOT NULL ' .
'THEN ' . Segment::$_table . '.name ' . 'THEN ' . Segment::$_table . '.name ' .
@ -204,21 +207,26 @@ class Export {
$this->segments $this->segments
); );
} else { } else {
// use an aggregate function to prevent non-deterministic GROUP BY issue
// in MySQL 5.7+
$subscribers = $subscribers $subscribers = $subscribers
->select(Segment::$_table . '.name', 'segment_name') ->selectExpr('CONCAT('.Segment::$_table . '.name) as segment_name')
->whereIn(SubscriberSegment::$_table . '.segment_id', $this->segments); ->whereIn(SubscriberSegment::$_table . '.segment_id', $this->segments);
} }
if(!$this->group_by_segment_option) { if(!$this->group_by_segment_option) {
// if grouping by segments, use a GROUP BY clause on subscriber id
$subscribers = $subscribers =
$subscribers->groupBy(Subscriber::$_table . '.id'); $subscribers->groupBy(Subscriber::$_table . '.id');
} }
if($this->export_confirmed_option) { if($this->export_confirmed_option) {
// select only subscribers with "subscribed" status
$subscribers = $subscribers =
$subscribers->where(Subscriber::$_table . '.status', 'subscribed'); $subscribers->where(Subscriber::$_table . '.status', 'subscribed');
} }
$subscribers = $subscribers $subscribers = $subscribers
->whereNull(Subscriber::$_table . '.deleted_at') ->whereNull(Subscriber::$_table . '.deleted_at')
->limit(sprintf('%d, %d', $offset, $limit)) ->offset($offset)
->limit($limit)
->findArray(); ->findArray();
return $subscribers; return $subscribers;
} }

View File

@ -66,7 +66,6 @@ class CustomFieldTest extends MailPoetTest {
function testItHasACreatedAtOnCreation() { function testItHasACreatedAtOnCreation() {
$custom_field = CustomField::findOne($this->custom_field->id); $custom_field = CustomField::findOne($this->custom_field->id);
expect($custom_field->created_at)->notNull(); expect($custom_field->created_at)->notNull();
expect($custom_field->created_at)->notEquals('0000-00-00 00:00:00');
} }
function testItHasAnUpdatedAtOnCreation() { function testItHasAnUpdatedAtOnCreation() {

View File

@ -49,7 +49,6 @@ class FormTest extends MailPoetTest {
function testItHasACreatedAtOnCreation() { function testItHasACreatedAtOnCreation() {
$form = Form::findOne($this->form->id); $form = Form::findOne($this->form->id);
expect($form->created_at)->notNull(); expect($form->created_at)->notNull();
expect($form->created_at)->notEquals('0000-00-00 00:00:00');
} }
function testItHasAnUpdatedAtOnCreation() { function testItHasAnUpdatedAtOnCreation() {

View File

@ -59,7 +59,6 @@ class NewsletterOptionFieldTest extends MailPoetTest {
function testItHasACreatedAtOnCreation() { function testItHasACreatedAtOnCreation() {
expect($this->option_field->created_at)->notNull(); expect($this->option_field->created_at)->notNull();
expect($this->option_field->created_at)->notEquals('0000-00-00 00:00:00');
} }
function testItHasAnUpdatedAtOnCreation() { function testItHasAnUpdatedAtOnCreation() {

View File

@ -62,7 +62,6 @@ class NewsletterTest extends MailPoetTest {
function testItHasACreatedAtOnCreation() { function testItHasACreatedAtOnCreation() {
$newsletter = Newsletter::findOne($this->newsletter->id); $newsletter = Newsletter::findOne($this->newsletter->id);
expect($newsletter->created_at)->notNull(); expect($newsletter->created_at)->notNull();
expect($newsletter->created_at)->notEquals('0000-00-00 00:00:00');
} }
function testItHasAnUpdatedAtOnCreation() { function testItHasAnUpdatedAtOnCreation() {

View File

@ -78,7 +78,6 @@ class SegmentTest extends MailPoetTest {
function testItHasACreatedAtOnCreation() { function testItHasACreatedAtOnCreation() {
$segment = Segment::findOne($this->segment->id); $segment = Segment::findOne($this->segment->id);
expect($segment->created_at)->notNull(); expect($segment->created_at)->notNull();
expect($segment->created_at)->notEquals('0000-00-00 00:00:00');
} }
function testItHasAnUpdatedAtOnCreation() { function testItHasAnUpdatedAtOnCreation() {

View File

@ -64,9 +64,9 @@ class ExportTest extends MailPoetTest {
$entity->hydrate($subscriber); $entity->hydrate($subscriber);
$entity->save(); $entity->save();
} }
foreach($this->segments_data as $custom_field) { foreach($this->segments_data as $segment) {
$entity = Segment::create(); $entity = Segment::create();
$entity->hydrate($custom_field); $entity->hydrate($segment);
$entity->save(); $entity->save();
} }
foreach($this->custom_fields_data as $custom_field) { foreach($this->custom_fields_data as $custom_field) {