70 if ( !isset($lang) ) $lang = $this->app->_conf[
'lang'];
79 import(
'SQL/Parser.php');
80 $this->_parser =
new SQL_Parser( null,
'MySQL');
81 import(
'SQL/Compiler.php');
82 $this->_compiler =& SQL_Compiler::newInstance(
'mysql');
83 $this->_compiler->version = 2;
100 $this->parentContext =& $translator;
105 if ( !is_array($query) ){
108 $query = trim($query);
109 $this->_query = $query;
110 $command = strtolower(substr($query, 0, strpos($query,
' ')));
112 $command = $query[
'command'];
117 unset($this->_tableNames);
118 unset($this->_tableAliases);
119 unset($this->_tableAliasTranslationMap);
120 unset($this->_columnTranslationMap);
122 unset($this->_talbeNames_tr);
123 unset($this->_tableAliases_tr);
124 $this->_tableNames = array();
125 $this->_tableAliases = array();
126 $this->_tableAliasTranslationMap = array();
127 $this->_columnTranslationMap = array();
128 if ( isset($lang) ) $this->_lang = $lang;
136 if ( !@$this->app->_conf[
'default_language_no_fallback'] and ($this->_lang == $this->app->_conf[
'default_language']) ){
138 return array($query);
140 if ( is_array($query) ){
141 $this->_data = $query;
143 $this->_data = $this->_parser->parse($query);
146 $this->_tableNames_tr = array();
147 $this->_tableAliases_tr = array();
160 'scripts.Dataface.QueryTranslator.translateQuery.ERROR_INVALID_QUERY',
161 "Invalid query attempted to be translated. Expected select, update, or insert query but received: ".$query,
162 array(
'query'=>$query)
175 $numTables = count($d[
'tables']);
176 for ($i=0; $i<$numTables; $i++){
177 if ( $d[
'tables'][$i][
'type'] ==
'ident' ){
178 $tname = $d[
'tables'][$i][
'value'];
179 $talias = $d[
'tables'][$i][
'alias'];
180 if ( !$talias ) $talias = $tname;
184 $d[
'tables'][$i][
'value'] = $translator->translateQuery($d[
'tables'][$i][
'value'], null,
false);
188 $this->_tableNames[$talias] = $tname;
189 $this->_tableAliases[$tname] = $talias;
190 if (!isset( $this->_tables[$tname] ) ){
195 if ( isset( $this->parentContext) ){
196 foreach ( array_keys($this->parentContext->_tables) as
$tablename ){
197 if ( !isset($this->_tables[$tablename]) ){
208 $this->_data_translated = $d;
210 $this->_data_translated[
'columns'] = array();
211 if ( !isset( $d[
'table_join'] ) ) $this->_data_translated[
'table_join'] = array();
212 foreach ($this->_data_translated[
'table_join'] as $k=>$v){
215 $this->_data_translated[
'table_join'][$k] =
"inner join";
221 if ( isset( $d[
'columns'] ) and is_array($d[
'columns']) ) {
222 $numCols = count($d[
'columns']);
223 for ($i=0; $i<$numCols; $i++){
224 $currColumn = $d[
'columns'][$i];
234 if ( isset($d[
'where_clause']) ){
239 if ( isset($d[
'table_join_clause']) ){
240 $numClauses = count($d[
'table_join_clause']);
241 for ($i=0; $i<$numClauses; $i++){
243 $this->
translateClause($this->_data_translated[
'table_join_clause'][$i]);
248 if ( isset($d[
'sort_order']) ){
249 $numClauses = count($d[
'sort_order']);
250 for ( $i=0; $i<$numClauses;$i++){
256 $out = array($this->_compiler->compile( $this->_data_translated));
278 if ( is_array($col) ){
288 switch ( $columnInfo[
'type'] ){
291 $col = $columnInfo[
'value'];
292 if ( $columnInfo[
'table'] ) $col = $columnInfo[
'table'].
'.'.$col;
293 $column_alias = $columnInfo[
'alias'];
300 $this->_data_translated[
'columns'][] =& $columnInfo;
308 if ( $update && preg_match(
'/\.{0,1}\*$/', $col) ){
311 foreach ( $expandedGlob as $globCol){
312 $currColumnTable = null;
313 $currColumnName = null;
314 $currColumnArr = explode(
'.', $globCol);
315 if ( count($currColumnArr) > 1){
316 $currColumnName = $currColumnArr[1];
317 $currColumnTable = $currColumnArr[0];
319 $currColumnName = $currColumnArr[0];
320 $currColumnTable =
'';
322 $currColumn = array(
'type'=>
'ident',
'table'=>$currColumnTable,
'value'=>$currColumnName,
'alias'=>
'');
330 $originalColumnName = $col;
332 if ( strpos($col,
'.') !==
false ) {
334 list($alias,$col) = explode(
'.', $col);
336 if ( isset($this->_tableNames[$alias]) ){
341 foreach ( array_keys($this->_tables) as $tableKey){
342 if ( isset($this->_tables[$tableKey]->_fields[$col]) ){
343 $tablename = $this->_tables[$tableKey]->tablename;
360 return array(
'column_names'=>array(($alias ? $alias.
'.' :
'').$col),
'column_aliases'=>array($column_alias));
362 if ( isset( $columnInfo ) ){
363 $this->_data_translated[
'columns'][] = $columnInfo;
367 }
else if ( isset( $this->_tableAliases[
$tablename]) and !$alias ){
372 $translation =
$table->getTranslation($this->_lang);
373 if ( !isset($translation) or !in_array($col, $translation) or in_array($col, array_keys(
$table->keys()) ) ){
379 if ( isset($columnInfo) ){
380 if ( !$columnInfo[
'table'] ) $columnInfo[
'table'] =
$tablename;
381 if ( $alias ) $columnInfo[
'table'] = $alias;
382 $this->_data_translated[
'columns'][] = $columnInfo;
386 return array(
'column_names'=>array(($alias ? $alias : $tablename).
'.'.$col),
'column_aliases'=>array($column_alias));
399 if ( !isset($this->_tableNames_tr[$alias] ) ){
401 $this->_tableNames_tr[$alias] = $tablename.
'_'.
$this->_lang;
403 if ( !isset($this->_tableAliases_tr[$tablename.
'_'.$this->_lang]) ){
406 if ( !isset($this->_tableAliasTranslationMap[$alias]) ){
407 $this->_tableAliasTranslationMap[$alias] = $old_alias;
411 if ( !in_array( $alias, $this->_data_translated[
'table_aliases'] ) ){
412 $this->_data_translated[
'table_names'][] = $tablename.
'_'.
$this->_lang;
413 $this->_data_translated[
'table_aliases'][] = $alias;
414 $this->_data_translated[
'tables'][] = array(
'type'=>
'ident',
'value'=>$tablename.
'_'.$this->_lang,
'alias'=>$alias);
415 $this->_data_translated[
'table_join'][] =
'left join';
417 foreach ( array_keys($this->_tables[$tablename]->keys()) as $keyName){
420 'value'=> ( $old_alias ? $old_alias : $tablename).
'.'.$keyName,
425 'value'=> $alias.
'.'.$keyName,
429 if ( !isset($join_clause) ){
430 $join_clause =& $temp;
432 $temp2 =& $join_clause;
434 $join_clause = array(
446 $this->_data_translated[
'table_join_clause'][] = $join_clause;
460 'value'=>$alias.
'.'.$col
464 'value'=>($old_alias ? $old_alias : $tablename).
'.'.$col
467 'alias'=>($column_alias ? $column_alias : $col)
469 if ( $update && !isset( $this->_columnTranslationMap[$originalColumnName] ) ) {
470 $this->_columnTranslationMap[$originalColumnName] = $func_struct[
'alias'];
473 if ( isset($columnInfo) ){
474 $columnInfo[
'type'] =
'func';
475 $columnInfo[
'table'] =
'';
476 $columnInfo[
'value'] = $func_struct;
477 $columnInfo[
'alias'] = $func_struct[
'alias'];
478 $this->_data_translated[
'columns'][] = $columnInfo;
482 $this->_data_translated[
'set_function'][] = $func_struct;
486 return array(
'set_function'=>array($func_struct));
500 if ( !isset( $func[
'args'] ) )
return false;
501 if ( !$update ) $new_func = $func;
502 foreach ( array_keys($func[
'args']) as $key){
503 $arg =& $func[
'args'][$key];
504 switch( $arg[
'type'] ){
506 $new_value = $this->
translateColumn($func[
'args'][$key][
'value'], null,
false);
508 if ( isset($new_value[
'set_function']) ) {
509 $new_arg = array(
'type'=>
'function',
'value'=>$new_value[
'set_function'][0]);
510 }
else if ( isset( $new_value[
'column_names'] ) ){
511 $new_arg = array(
'type'=>
'ident',
'value'=>$new_value[
'column_names'][0]);
514 $new_func[
'args'][$key] = $new_arg;
516 $func[
'args'][$key] = $new_arg;
553 if ( !is_array($clause) )
return;
554 foreach ( array(
'arg_1',
'arg_2') as $arg ){
555 if ( !isset($clause[$arg]) )
continue;
561 if ( !isset($clause[
'type']) ){
564 switch( $clause[
'type'] ){
567 if ( is_array($new_value) and isset($new_value[
'set_function']) ) {
569 $clause[
'type'] =
'function';
570 $clause[
'value'] = $new_value[
'set_function'][0];
572 $clause[
'value'] = $new_value[
'column_names'][0];
588 $translator->setParentContext($this);
589 $clause[
'value'] = $translator->translateQuery($clause[
'value'], $this->_lang,
false);
619 $numTables = count($this->_tableNames);
620 if ( strpos($glob,
'.') !==
false ){
622 list($alias, $glob) = explode(
'.', $glob);
623 if ( isset( $this->_tableNames[$alias]) ){
624 $out = array_keys($this->_tables[ $this->_tableNames[$alias] ]->fields() );
626 foreach (
$out as $col){
627 $out2[] = $alias.
'.'.$col;
633 'scripts.Dataface.QueryTranslator.expandGlob.ERROR_NONEXISTENT_TABLE',
634 "Attempt to expand glob for non-existent table '$alias'",
635 array(
'table'=>$alias)
640 foreach ( array_keys($this->_tableNames) as $alias ){
641 $newfields = array_keys($this->_tables[ $this->_tableNames[ $alias ] ]->fields());
642 foreach ( $newfields as $newfield ){
644 $fields[] = $alias.
'.'.$newfield;
671 $tableName = $d[
'table_names'][0];
672 if ( count($d[
'table_names']) > 1 ){
675 'scripts.Dataface.QueryTranslator.translateUpdateQuery.ERROR_MULTI_TABLE_UPDATE',
676 'Failed to translate update query because the translator does not support multiple-table update syntax.'
684 $translation =
$table->getTranslation($this->_lang);
688 if ( !isset( $translation ) ) $translation = array();
697 $this->_data_translated = $d;
698 $this->_data_translated[
'column_names'] = array();
699 $this->_data_translated[
'values'] = array();
700 $this->_data_translated[
'table_names'] = array($tableName.($translation?
'_'.$this->_lang:
''));
705 $new_data[
'column_names'] = array();
706 $new_data[
'values'] = array();
711 $keyChange_data = $d;
712 $keyChange_data[
'column_names'] = array();
713 $keyChange_data[
'values'] = array();
717 $numCols = count($d[
'column_names']);
718 $translationRequired =
false;
719 $originalRequired =
false;
720 $keysChanged =
false;
722 for ($i=0; $i<$numCols; $i++ ){
723 $col = $d[
'column_names'][$i];
724 $value = $d[
'values'][$i];
725 if ( in_array($col,
$keys) ){
726 $originalRequired =
true;
728 $this->_data_translated[
'column_names'][] = $col;
729 $this->_data_translated[
'values'][] = $value;
730 $new_data[
'column_names'][] = $col;
731 $new_data[
'values'][] = $value;
732 $keyChange_data[
'column_names'][] = $col;
733 $keyChange_data[
'values'][] = $value;
734 }
else if ( in_array($col, $translation) ){
735 $translationRequired =
true;
736 $this->_data_translated[
'column_names'][] = $col;
737 $this->_data_translated[
'values'][] = $value;
739 $originalRequired =
true;
740 $new_data[
'column_names'][] = $col;
741 $new_data[
'values'][] = $value;
745 if (!$translationRequired and !$keysChanged){
746 return array($query);
749 if ( $translationRequired ){
750 $out = array(
'insert ignore into `'.$tableName.
'_'.$this->_lang.
'` (`'.implode(
'`,`', array_keys($queryKeys)).
'`) values (\''.implode(
'\',\
'', array_values($queryKeys)).
'\')
');
754 if ( $originalRequired ) $out[] = $this->_compiler->compile($new_data);
755 $out[] = $this->_compiler->compile($this->_data_translated);
758 $translations = array_keys($table->getTranslations());
759 foreach ( $translations as $tr ){
760 if ( $tr == $this->_lang ) continue;
761 $keyChange_data['table_names
'] = array($tableName.'_
'.$tr);
762 $out[] = $this->_compiler->compile($keyChange_data);
779 function extractQuery(&$data){
780 $w =& $data['where_clause
'];
782 $this->extractQuery_rec($w, $out);
786 function extractQuery_rec($clause, &$out){
787 if ( !is_array($clause)) return;
789 if ( isset($clause['arg_1
'] ) ){
790 switch ($clause['arg_1
']['type
']){
792 $this->extractQuery_rec($clause['arg_1
'], $out);
796 if ( in_array($clause['arg_2
']['type
'], array('int_val
','real_val
','text_val
','null
') ) and $clause['op
'] == '=
'){
797 $out[$clause['arg_1
']['value
']] = $clause['arg_2
']['value
'];
816 function translateInsertQuery($query){
819 * This method translates an update query to be multilingual.
820 * It tries to be non-obtrusive in that only column names in the
821 * update list are converted. The where clause is left alone.
822 * As a consequence, this does not support multi-table updates.
825 $tableName = $d['table_names
'][0];
826 if ( count($d['table_names
']) > 1 ){
827 return PEAR::raiseError(
829 'scripts.Dataface.QueryTranslator.translateUpdateQuery.ERROR_MULTI_TABLE_UPDATE
',
830 'Failed to translate update query because the translator does not support multiple-table update syntax.
'
834 $table = Dataface_Table::loadTable($tableName, null, false, true);
835 if ( PEAR::isError($table) ){
836 return array($query);
839 $translation = $table->getTranslation($this->_lang);
840 // Array of column names that have a translation in this language.
842 if ( !isset( $translation ) ) return array($query);
843 // there are no translations for this table, so we just return the query.
845 // We initialize the data structure to store the update to the translation
847 $this->_data_translated = $d; // to store update query to translation table
848 $this->_data_translated['column_names
'] = array();
849 $this->_data_translated['values
'] = array();
850 $this->_data_translated['table_names
'] = array($tableName.'_
'.$this->_lang);
852 // Initialize the data structure to store the update to the original table
853 // after translated columns are removed.
854 $new_data = $d; // to store update query to base table
855 $new_data['column_names
'] = array();
856 $new_data['values
'] = array();
860 $numCols = count($d['column_names
']);
861 $translationRequired = false;
862 $originalRequired = true; // for inserts the original is always required!
864 if ( ($aif = $table->getAutoIncrementField()) ){
865 $new_data['column_names
'][] = $this->_data_translated['column_names
'][] = $aif;
866 $new_data['values
'][] = $this->_data_translated['values
'][] = array('type
'=>'text_val
', 'value
'=>'%%%%%__MYSQL_INSERT_ID__%%%%%
');
870 for ($i=0; $i<$numCols; $i++ ){
871 $col = $d['column_names
'][$i];
872 $value = $d['values
'][$i];
873 if ( in_array($col, $translation)){
874 $translationRequired = true;
875 $this->_data_translated['column_names
'][] = $col;
876 $this->_data_translated['values
'][] = $value;
879 $new_data['column_names
'][] = $col;
880 $new_data['values
'][] = $value;
884 if (!$translationRequired){
885 $out = array($query);
889 if ( $originalRequired ) $out[] = $this->_compiler->compile($new_data);
890 $out[] = $this->_compiler->compile($this->_data_translated);
903 function translateDeleteQuery($query){
905 $out = array($this->_compiler->compile($d));
906 $table = Dataface_Table::loadTable($d['table_names
'][0]);
907 foreach ( array_keys($table->getTranslations()) as $lang){
908 $d['table_names
'][0] = $table->tablename.'_
'.$lang;
909 $out[] = $this->_compiler->compile($d);