the problematic code snippet in l10n_community/export.inc

function l10n_community_export() {
  $export_string = array()

  while ($string = db_fetch_object($result)) {
    if (!empty($export_string)) {
      //save the string
    }
    $export_string = array('value' => $string->value, ...)
  }
}

it is evident that the last string will not be exported

and I guess this is a duplicate: #231457: Missing strings in exports if version string is missing

Comments

pasqualle’s picture

Status: Active » Needs review
StatusFileSize
new13.88 KB

the change

function l10n_community_export() {
  while ($string = db_fetch_object($result)) {
    $export_string = array('value' => $string->value, ...)
    //save the string
  }
}
zoltán balogh’s picture

This patch is works fine.

gábor hojtsy’s picture

Status: Needs review » Needs work

Sorry that it took some time to review this patch, but it does need some thought. In short: neither the current code, nor your patch is right.

The current logic works this way:

- DO get row from database
  - IF the row represents a new string (new sid)
    - IF the *previous* string was already assembled in $export_string with all location information
      - DO generate data for the *previous* string and add to $output and eventually $string_files
    - DO start the metadata array with the *new* string
  - ELSE append additional location information to *existing* string

What this does is that it keeps building up $export_string with all the location information for a string from multiple database result rows, until the next string comes around, when it saves $export_string, and starts processing of the next string. It does skip saving the last string, it is clear to me. You are right in that.

However, your code does the following (I did not actually try it, was just doing code review):

- DO get row from database
  - ALWAYS start over $export_string as if we are dealing with a new string in all cases
  - IF the row represents an existing string (same sid as previous)
    - DO add the *same* location information again to $export_string 
      (it does not remember any of the old values, since it is always overwritten)
  - DO generate data for the current string and add to $output and eventually $string_files

What this logic disrespects is that data for the same sid comes in a sequence of multiple rows from the database, and those should be aggregated in $export_string before handled. Your code seems to export multiple copies of the same string with bogus (duplicated) location information from the second copy onwards, when the same string appears multiple times in the exported "domain" (eg. Drupal core, module, theme).

What makes this code tricky is that even for the last database row, we don't know in advance whether that will be a new string or just additional location information for the existing one we are already handling. And we don't know when that last row comes, so handling the last row should be deferred until information is available that there are no more rows and we should close up the last string and save into $output / $string_files. So saving the last string is something we can only decide on after we are out of the while() loop.

Maybe we should refactor most of the inside of the while() loop to a function which would take most variables by reference, so we can call it once on each step inside the while() loop and once outside the while loop at the end to save the last string. Any good ideas are appreciated.

gábor hojtsy’s picture

Edited the above flow of your code to say:

- DO get row from database
  - ALWAYS start over $export_string as if we are dealing with a new string in all cases
  - IF the row represents an existing string (same sid as previous)
    - DO add the *same* location information again to $export_string 
      (it does not remember any of the old values, since it is always overwritten)
  - DO generate data for the current string and add to $output and eventually $string_files

Instead of the original:

- DO get row from database
  - ALWAYS start over $export_string as if we are dealing with a new string in all cases
  - IF the row represents an existing string (same sid as previous)
    - DO add the *same* location information again to $export_string 
      (it does not remember any of the old values, since it is always overwritten)
    - DO generate data for the current string and add to $output and eventually $string_files

Which was a mistake in indentation. You always save the string for each new database row, regardless of conditionals, so the last DO should be on the same level as ALWAYS and IF.

pasqualle’s picture

OK, I see the problem

it is possible to write a code where you do not need an extra function to save the string
for example:

prev_sid = 0;

while TRUE {
  row = db_fetch_object(..)

  if ((!row and string) or (row and row->sid != prev_sid and prev_sid > 0)) {
    //save the string
  }
  else
    if (row) {
      //append row data to the string
    }
  }

  if (!row) {
    exit;
  }
  prew_sid = row->sid;
}

but I agree that writing a new function to save the string would result a much cleaner code..

pasqualle’s picture

Status: Needs work » Needs review
StatusFileSize
new6.7 KB

refactor most of the inside of the while() loop to a function which would take most variables by reference, so we can call it once on each step inside the while() loop and once outside the while loop at the end to save the last string

zoltán balogh’s picture

This patch is works fine.

gábor hojtsy’s picture

Status: Needs review » Fixed

Looks great, thanks for the persistence and for the solution. Committed!

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for two weeks with no activity.