|
![]() |
#1 |
Участник
|
![]()
В пору обновления приложения Axapta 3.0 до AX 2009 я столкнулся с серьезной проблемой при подъеме модификаций для таблиц, поля которых в стандарте используют идентификаторы из диапазона usr-слоя (см. тему DAX2009: поля таблиц стандартного приложения с идентификаторами из диапазона usr-слоя). Попытки синхронизации таких таблиц неизменно заканчивались ошибками, а поскольку происходило все на пустой разрабоческой базе, было решено обойти придурь ядра с помощью "программной эмуляции": удалить вообще записи в SqlDictionary для проблемных таблиц и воссоздать их, "как положено". Ниже приводится использовавшийся для этого код, немного сдобренный комментариями, - авось еще кому пригодится. В коде поля типа UtcDateTime обрабатываются специальным образом, обсуждение см. в теме Зачем нужно поле для хранения временной зоны для значений полей типа UtcDateTime?
X++: public static server void sqlDictionaryFill4Table(tableId _tableId) { #macrolib.DictField #define.RecIdBaseType (49) // для полей с типом RecId/RefRecId/createdTransactionId/modifiedTransactionId используется не Types::Int64, а этот тип #define.TZIDsuffix ('_TZID') // для несистемных полей типа UtcDateTime в БД создается дополнительное поле с кодом временной зоны, в которой было записано значение SqlDictionary sqlDict; SysdictType dictType; DictTable dictTable = new DictTable( _tableId ); DictField dictField; ArrayIdx arrIdx; Counter numOfSqlFields; // сколько записей для полей таблицы должно быть в SqlDictionary fieldName fieldName; fieldId fieldId; boolean processTableField(DictField _dictField, ArrayIdx _arrIdx, boolean _isTzIdField = false) { ArrayIdx dictArrIdx; str infoName; // это имя поля сугубо для сообщений FieldName sqlName; boolean ret; ; if (_isTzIdField) { if ( _dictField.baseType() != Types::UtcDateTime || _dictField.id() == fieldnum(Common, createdDateTime) || _dictField.id() == fieldnum(Common, modifiedDateTime) ) { throw error( Error::wrongUseOfFunction( funcname() ) ); } dictArrIdx = _dictField.arraySize() + _arrIdx; sqlName = _dictField.dateTimeTimeZoneRuleFieldName( _arrIdx - 1 ); infoName = _dictField.name() + #TZIDsuffix; } else { dictArrIdx = _arrIdx; sqlName = _dictField.name( DbBackend::Sql, _arrIdx ); infoName = _dictField.name(); } select firstonly sqlDict where sqlDict.tabId == _dictField.tableid() && sqlDict.fieldId == _dictField.id() && sqlDict.array == dictArrIdx ; if (!sqlDict) { sqlDict.clear(); sqlDict.initValue(); sqlDict.tabId = _dictField.tableid(); sqlDict.fieldId = _dictField.id(); sqlDict.array = dictArrIdx; sqlDict.name = strupr( _dictField.name( DbBackend::Native, _arrIdx ) ); sqlDict.sqlName = sqlName; dictType = new SysDictType( _dictField.typeId() ); if (_isTzIdField) { sqlDict.fieldType = Types::Integer; } else if ( _dictField.id() == fieldnum(Common, RecId) || _dictField.id() == fieldnum(Common, createdTransactionId) || _dictField.id() == fieldnum(Common, modifiedTransactionId) || _dictField.typeId() == typeid(RecId) || _dictField.typeId() == typeid(RefRecId) || ( dictType && dictType.isExtending( extendedtypenum(RecId) ) ) ) { // для RecId в поле fieldType прописывается не Types::Int64, а число 49 sqlDict.fieldType = #RecIdBaseType; } else { sqlDict.fieldType = _dictField.baseType(); } sqlDict.strSize = _dictField.stringLen(); sqlDict.shadow = bitTest( _dictField.flags(), #DBF_SHADOW ); sqlDict.rightJustify = bitTest( _dictField.flags(), #DBF_RIGHT ); sqlDict.nullable = _dictField.baseType() == Types::Container || _dictField.baseType() == Types::VarString; sqlDict.flags = sqlDict.shadow; // а вот ни фига не _dictField.flags(); if (sqlDict.validateWrite()) { sqlDict.insert(); ret = true; info( strfmt( @"Создана запись для поля %1.%2%3", dictTable.name(), infoName, _dictField.arraySize() > 1 ? strfmt( @"[%1]", _arrIdx ) : '' )); // для всех несистемных полей UtcDateTime создаем также связанное поле TZID if ( !_isTzIdField && _dictField.baseType() == Types::UtcDateTime && _dictField.id() != fieldnum(Common, createdDateTime) && _dictField.id() != fieldnum(Common, modifiedDateTime) ) { processTableField( _dictField, _arrIdx, true ); } } else { ret = checkFailed( strfmt( @"Запись в %1 для поля %2.%3 не была создана", tablestr(SqlDictionary), dictTable.name(), infoName ) ); } } return ret; } ; if (!dictTable) { throw error( strfmt( @"Не удалось создать объект %1 для таблицы '%2' (%3)", classstr(DictTable), tableid2name( _tableId ), _tableId ) ); } if (dictTable.isSystemTable()) { throw error( strfmt( @"Таблица '%1' - системная, на это я пойтить не могу", dictTable.name() ) ); } if (!dictTable.isSql()) { throw error( strfmt( @"Таблицы '%1' вообще не должно быть в базе", dictTable.name() ) ); } for (fieldId = dictTable.fieldNext( 0 ); fieldId; fieldId = dictTable.fieldNext( fieldId )) { dictField = dictTable.fieldObject( fieldId ); if (dictField && (dictField.flags() & #DBF_STORE)) { fieldName = dictField.name(); for (arrIdx = 1; arrIdx <= dictField.arraySize(); arrIdx++) { numOfSqlFields++; processTableField( dictField, arrIdx ); } } } select firstonly sqlDict where sqlDict.tabId == _tableId && sqlDict.fieldId == 0 ; if (!sqlDict) { sqlDict.clear(); sqlDict.initValue(); sqlDict.tabId = _tableId; sqlDict.name = strupr( dictTable.name() ); sqlDict.sqlName = dictTable.name( DbBackend::Sql ); sqlDict.strSize = numOfSqlFields; // для "заголовка" таблицы тут указывается, сколько у нее полей в БД sqlDict.flags = dictTable.isView(); // "мой дедуктивный метод..." sqlDict.insert(); info( strfmt( @"Создана запись для таблицы %1", dictTable.name() ) ); } } |
|
|
За это сообщение автора поблагодарили: raz (15), sukhanchik (10), propeller (1). |
![]() |
#2 |
Участник
|
![]()
Как показали полевые испытания, скрипт учитывал не все. Ниже - исправленная версия
X++: public static server void sqlDictionaryFill4Table(tableId _tableId) { #macrolib.DictField #define.RecIdBaseType (49) // для полей с типом RecId/RefRecId/createdTransactionId/modifiedTransactionId используется не Types::Int64, а этот тип #define.TZIDsuffix ('_TZID') // для несистемных полей типа UtcDateTime в БД создается дополнительное поле с кодом временной зоны, в которой было записано значение SqlDictionary sqlDict; SysdictType dictType; DictTable dictTable; DictField dictField; ArrayIdx arrIdx; Counter numOfSqlFields; // сколько записей для полей таблицы должно быть в SqlDictionary fieldName fieldName; fieldId fieldId; boolean processTableField(DictField _dictField, ArrayIdx _arrIdx, boolean _isTzIdField = false) { ArrayIdx dictArrIdx; str infoName; // это имя поля сугубо для сообщений FieldName sqlName; boolean ret; ; if (_isTzIdField) { if ( _dictField.baseType() != Types::UtcDateTime || _dictField.id() == fieldnum(Common, createdDateTime) || _dictField.id() == fieldnum(Common, modifiedDateTime) ) { throw error(Error::wrongUseOfFunction(funcname())); } dictArrIdx = _dictField.arraySize() + _arrIdx; sqlName = _dictField.dateTimeTimeZoneRuleFieldName(_arrIdx - 1); infoName = _dictField.name() + #TZIDsuffix; } else { dictArrIdx = _arrIdx; sqlName = _dictField.name(DbBackend::Sql, _arrIdx); infoName = _dictField.name(); } select firstonly sqlDict where sqlDict.tabId == _dictField.tableid() && sqlDict.fieldId == _dictField.id() && sqlDict.array == dictArrIdx ; if (!sqlDict) { sqlDict.clear(); sqlDict.initValue(); sqlDict.tabId = _dictField.tableid(); sqlDict.fieldId = _dictField.id(); sqlDict.array = dictArrIdx; sqlDict.name = strupr(_dictField.name(DbBackend::Native, _arrIdx)); sqlDict.sqlName = sqlName; dictType = new SysDictType( _dictField.typeId() ); if (_isTzIdField) { sqlDict.fieldType = Types::Integer; } else if ( _dictField.id() == fieldnum(Common, RecId) || _dictField.id() == fieldnum(Common, createdTransactionId) || _dictField.id() == fieldnum(Common, modifiedTransactionId) || _dictField.typeId() == extendedtypenum(RecId) || _dictField.typeId() == extendedtypenum(RefRecId) || _dictField.typeId() == extendedtypenum(createdTransactionId) || _dictField.typeId() == extendedtypenum(modifiedTransactionId) || ( dictType && ( dictType.isExtending(extendedtypenum(RecId)) || dictType.isExtending(extendedtypenum(createdTransactionId)) || dictType.isExtending(extendedtypenum(modifiedTransactionId)) ) ) ) { // для RecId в поле fieldType прописывается не Types::Int64, а число 49 sqlDict.fieldType = #RecIdBaseType; } else { sqlDict.fieldType = _dictField.baseType(); } sqlDict.strSize = _dictField.stringLen(); sqlDict.shadow = bitTest( _dictField.flags(), #DBF_SHADOW ); sqlDict.rightJustify = bitTest( _dictField.flags(), #DBF_RIGHT ); sqlDict.flags = sqlDict.shadow; // а вот ни фига не _dictField.flags(); sqlDict.nullable = _dictField.baseType() == Types::Container || _dictField.baseType() == Types::VarString ; if (sqlDict.validateWrite()) { sqlDict.insert(); ret = true; info(strfmt(@"Создана запись для поля %1.%2%3", dictTable.name(), infoName, _dictField.arraySize() > 1 ? strfmt(@"[%1]", _arrIdx) : '' )); // для всех несистемных полей UtcDateTime создаем также связанное поле TZID if ( !_isTzIdField && _dictField.baseType() == Types::UtcDateTime && _dictField.id() != fieldnum(Common, createdDateTime) && _dictField.id() != fieldnum(Common, modifiedDateTime) ) { processTableField(_dictField, _arrIdx, true); } } else { ret = checkFailed(strfmt(@"Запись в %1 для поля %2.%3 не была создана", tablestr(SqlDictionary), dictTable.name(), infoName)); } } return ret; } ; dictTable = new DictTable( _tableId ); if (!dictTable) { throw error(strfmt(@"Не удалось создать объект %1 для таблицы '%2' (%3)", classstr(DictTable), tableid2name( _tableId ), _tableId)); } if (dictTable.isSystemTable()) { throw error(strfmt(@"Таблица '%1' - системная, на это я пойтить не могу", dictTable.name())); } if (!dictTable.isSql()) { throw error(strfmt(@"Таблицы '%1' вообще не должно быть в базе", dictTable.name())); } for (fieldId = dictTable.fieldNext( 0 ); fieldId; fieldId = dictTable.fieldNext( fieldId )) { dictField = dictTable.fieldObject( fieldId ); if (dictField && dictField.isSql()) { fieldName = dictField.name(); for (arrIdx = 1; arrIdx <= dictField.arraySize(); arrIdx++) { numOfSqlFields++; processTableField( dictField, arrIdx ); } } } select firstonly sqlDict where sqlDict.tabId == _tableId && sqlDict.fieldId == 0 ; if (!sqlDict) { sqlDict.clear(); sqlDict.initValue(); sqlDict.tabId = _tableId; sqlDict.name = strupr(dictTable.name()); sqlDict.sqlName = dictTable.name(DbBackend::Sql); sqlDict.strSize = numOfSqlFields; // для "заголовка" таблицы тут указывается, сколько у нее полей в БД sqlDict.flags = dictTable.isView(); // "мой дедуктивный метод..." sqlDict.insert(); info(strfmt(@"Создана запись для таблицы %1", dictTable.name())); } } |
|
|
За это сообщение автора поблагодарили: KiselevSA (5), Владимир Максимов (10), Logger (10), alex55 (5), GBH (1). |
![]() |
#3 |
Участник
|
Цитата:
Сообщение от gl00mie
![]() В пору обновления приложения Axapta 3.0 до AX 2009 я столкнулся с серьезной проблемой при подъеме модификаций для таблиц, поля которых в стандарте используют идентификаторы из диапазона usr-слоя (см. тему DAX2009: поля таблиц стандартного приложения с идентификаторами из диапазона usr-слоя). Попытки синхронизации таких таблиц неизменно заканчивались ошибками, а поскольку происходило все на пустой разрабоческой базе, было решено обойти придурь ядра с помощью "программной эмуляции": удалить вообще записи в SqlDictionary для проблемных таблиц и воссоздать их, "как положено".
[/XPP]
__________________
-Ты в гномиков веришь? -Нет. -А они в тебя верят, смотри, не подведи их. |
|
![]() |
#4 |
Злыдни
|
Джобик отработал на AX2012R3 с одной ошибкой: для поля Partition присвоился тип Int64 вместо типа для RecId (49). Пришлось менять через SQL. Может найдется время для модификации job-а под AX2012?
__________________
люди...считают, что если техника не ломается, то ее не нужно ремонтировать. Инженеры считают, что если она не ломается, то нуждается в совершенствовании. |
|
![]() |
#5 |
Участник
|
Цитата:
Еще для гуидов хранится 45 вместо types::Guid X++: // [url=https://axforum.info/forums/showthread.php?t=39005&highlight=sqlDictionary]Программное воссоздание записей SqlDictionary для определенной таблицы[/url] public static server void sqlDictionaryFill4Table(tableId _tableId) { #macrolib.DictField #define.RecIdBaseType (49) // для полей с типом RecId/RefRecId/createdTransactionId/modifiedTransactionId используется не Types::Int64, а этот тип #define.TZIDsuffix ('_TZID') // для несистемных полей типа UtcDateTime в БД создается дополнительное поле с кодом временной зоны, в которой было записано значение SqlDictionary sqlDict; SysdictType dictType; DictTable dictTable; DictField dictField; ArrayIdx arrIdx; Counter numOfSqlFields; // сколько записей для полей таблицы должно быть в SqlDictionary fieldName fieldName; fieldId fieldId; boolean processTableField(DictField _dictField, ArrayIdx _arrIdx, boolean _isTzIdField = false) { ArrayIdx dictArrIdx; str infoName; // это имя поля сугубо для сообщений FieldName sqlName; boolean ret; ; if (_isTzIdField) { if ( _dictField.baseType() != Types::UtcDateTime || _dictField.id() == fieldnum(Common, createdDateTime) || _dictField.id() == fieldnum(Common, modifiedDateTime) ) { throw error(Error::wrongUseOfFunction(funcname())); } dictArrIdx = _dictField.arraySize() + _arrIdx; sqlName = _dictField.dateTimeTimeZoneRuleFieldName(_arrIdx - 1); infoName = _dictField.name() + #TZIDsuffix; } else { dictArrIdx = _arrIdx; sqlName = _dictField.name(DbBackend::Sql, _arrIdx); infoName = _dictField.name(); } select firstonly sqlDict where sqlDict.tabId == _dictField.tableid() && sqlDict.fieldId == _dictField.id() && sqlDict.array == dictArrIdx ; if (!sqlDict) { sqlDict.clear(); sqlDict.initValue(); sqlDict.tabId = _dictField.tableid(); sqlDict.fieldId = _dictField.id(); sqlDict.array = dictArrIdx; sqlDict.name = strupr(_dictField.name(DbBackend::Native, _arrIdx)); sqlDict.sqlName = sqlName; dictType = new SysDictType( _dictField.typeId() ); if (_isTzIdField) { sqlDict.fieldType = Types::Integer; } else if ( _dictField.id() == fieldnum(Common, RecId) || _dictField.id() == fieldnum(Common, createdTransactionId) || _dictField.id() == fieldnum(Common, modifiedTransactionId) || _dictField.id() == fieldnum(Common, Partition) || _dictField.typeId() == extendedtypenum(RecId) || _dictField.typeId() == extendedtypenum(RefRecId) || _dictField.typeId() == extendedtypenum(createdTransactionId) || _dictField.typeId() == extendedtypenum(modifiedTransactionId) || _dictField.typeId() == extendedtypenum(Partition) || ( dictType && ( dictType.isExtending(extendedtypenum(RecId)) || dictType.isExtending(extendedtypenum(createdTransactionId)) || dictType.isExtending(extendedtypenum(modifiedTransactionId)) || dictType.isExtending(extendedtypenum(Partition)) ) ) ) { // для RecId в поле fieldType прописывается не Types::Int64, а число 49 sqlDict.fieldType = #RecIdBaseType; } else if ( _dictField.BaseType() == Types::Guid || _dictField.BaseType() == 45 // Types_MRC::SysGuid ) { sqlDict.fieldType = 45; } else { sqlDict.fieldType = _dictField.baseType(); } sqlDict.strSize = _dictField.stringLen(); sqlDict.shadow = bitTest( _dictField.flags(), #DBF_SHADOW ); sqlDict.rightJustify = bitTest( _dictField.flags(), #DBF_RIGHT ); sqlDict.flags = sqlDict.shadow; // а вот ни фига не _dictField.flags(); sqlDict.nullable = _dictField.baseType() == Types::Container || _dictField.baseType() == Types::VarString ; if (sqlDict.validateWrite()) { sqlDict.insert(); ret = true; info(strfmt(@"Создана запись для поля %1.%2%3", dictTable.name(), infoName, _dictField.arraySize() > 1 ? strfmt(@"[%1]", _arrIdx) : '' )); // для всех несистемных полей UtcDateTime создаем также связанное поле TZID if ( !_isTzIdField && _dictField.baseType() == Types::UtcDateTime && _dictField.id() != fieldnum(Common, createdDateTime) && _dictField.id() != fieldnum(Common, modifiedDateTime) ) { processTableField(_dictField, _arrIdx, true); } } else { ret = checkFailed(strfmt(@"Запись в %1 для поля %2.%3 не была создана", tablestr(SqlDictionary), dictTable.name(), infoName)); } } return ret; } ; dictTable = new DictTable( _tableId ); if (!dictTable) { throw error(strfmt(@"Не удалось создать объект %1 для таблицы '%2' (%3)", classstr(DictTable), tableid2name( _tableId ), _tableId)); } if (dictTable.isSystemTable()) { throw error(strfmt(@"Таблица '%1' - системная, на это я пойтить не могу", dictTable.name())); } if (!dictTable.isSql()) { throw error(strfmt(@"Таблицы '%1' вообще не должно быть в базе", dictTable.name())); } for (fieldId = dictTable.fieldNext( 0 ); fieldId; fieldId = dictTable.fieldNext( fieldId )) { dictField = dictTable.fieldObject( fieldId ); if (dictField && dictField.isSql()) { fieldName = dictField.name(); for (arrIdx = 1; arrIdx <= dictField.arraySize(); arrIdx++) { numOfSqlFields++; processTableField( dictField, arrIdx ); } } } select firstonly sqlDict where sqlDict.tabId == _tableId && sqlDict.fieldId == 0 ; if (!sqlDict) { sqlDict.clear(); sqlDict.initValue(); sqlDict.tabId = _tableId; sqlDict.name = strupr(dictTable.name()); sqlDict.sqlName = dictTable.name(DbBackend::Sql); sqlDict.strSize = numOfSqlFields; // для "заголовка" таблицы тут указывается, сколько у нее полей в БД sqlDict.flags = dictTable.isView(); // "мой дедуктивный метод..." sqlDict.insert(); info(strfmt(@"Создана запись для таблицы %1", dictTable.name())); } } |
|
![]() |
#6 |
Участник
|
Спасибо за обкатку на AX 2012 R3
![]() |
|
![]() |
#7 |
Участник
|
Скрипт хороший, а нет что-нибудь подобного для индексов? или в какой табличке хранятся индексы таблиц?
Синхронизирую одну таблицу, ошибка drop index на индекс, которого в AX 2012 нет. Не пойму, где его почистить .. |
|
![]() |
#8 |
Участник
|
Так нет таблички аналога SQLDictionary для индексов. Так что причина ошибки скорее всего в чем-то другом. Может прав нет на удаление индекса.
|
|
![]() |
#9 |
Участник
|
Цитата:
Например, AX R3 не умеет дропать индекс если он PK и таблица синхронизируется с Retail Store. В данном случаем на тиблице включен Change Tracking и SQL не даст вам изменять PK (надо ручками отключать Change Tracking).
__________________
AxAssist 2012 - Productivity Tool for Dynamics AX 2012/2009/4.0/3.0 |
|
|
За это сообщение автора поблагодарили: gl00mie (3), Logger (1). |
![]() |
#10 |
Administrator
|
А почему бы просто не сделать Check/Synchronize и экспортировать DDL-скрипт? Там все команды для заполнения SqlDictionary будут.
С индексом проблему, кстати, так же можно решить: сделайте Check/Synchronize и запустите скрипт в SQL Management Studio.
__________________
Not registered yet? Register here! Have comments, questions, suggestions or anything else regarding our web site? Don't hesitate, send them to me |
|
![]() |
#11 |
Участник
|
Под 2012 удалось кому-нибудь переделать?
|
|
![]() |
#12 |
Administrator
|
На будущее оставлю здесь ссылку на SQL-скрипт для AX2012
https://github.com/dodiggitydag/AX-2...model%20db.sql
__________________
Возможно сделать все. Вопрос времени |
|
|
За это сообщение автора поблагодарили: mazzy (2), Logger (5). |
![]() |
#13 |
Участник
|
Цитата:
Сообщение от sukhanchik
![]() На будущее оставлю здесь ссылку на SQL-скрипт для AX2012
https://github.com/dodiggitydag/AX-2...model%20db.sql В скрипте не хватает обработки NumberSequenceDatatype. |
|
|
За это сообщение автора поблагодарили: sukhanchik (10). |
![]() |
#14 |
Administrator
|
Ну так-то все таблицы, где присутствует в данных TableId или ExtendedDataTypeId надо обрабатывать. Соглашусь, что NumberSequenceDatatype из этого списка достаточно важная таблица.
__________________
Возможно сделать все. Вопрос времени |
|
![]() |
#15 |
Участник
|
Цитата:
Сообщение от sukhanchik
![]() На будущее оставлю здесь ссылку на SQL-скрипт для AX2012
https://github.com/dodiggitydag/AX-2...model%20db.sql X++: ---------------------------------------------------------------------------------------------- -- Step 4 Fix the field ids in SQLDictionary which do not match ---------------------------------------------------------------------------------------------- PRINT 'Step 4'; WITH t AS ( SELECT ( SELECT m1.NAME FROM ModelElement m1 WHERE m1.ElementHandle = m.ParentHandle ) AS [Table Name], m.NAME AS [mName], m.AXid, s.RECID, M.ParentId, s.TableId, s.FieldID, S.NAME, s.SQLNAME FROM ModelElement m LEFT OUTER JOIN AX2012DB.dbo.SQLDICTIONARY s ON m.ParentId = s.TABLEID AND s.NAME = upper(m.NAME) collate Latin1_General_CI_AS WHERE m.ElementType = 42 -- UtilElementType::TableField AND (s.ARRAY = 1 OR s.ARRAY IS NULL) AND (s.FieldID > 0 OR s.FieldID IS NULL) AND s.FieldID != m.AxId ) UPDATE AX2012DB.dbo.SQLDICTIONARY SET FIELDID = (t.axid * -1) -- Set to a negative number but correct ID FROM t join AX2012DB.dbo.SQLDICTIONARY s ON upper(t.mName) collate Latin1_General_CI_AS = s.NAME AND s.FieldID <> 0 AND s.TableId = t.ParentId Последний раз редактировалось Logger; 04.05.2023 в 17:42. |
|
|
За это сообщение автора поблагодарили: sukhanchik (10). |
![]() |
#16 |
Участник
|
Оказывается джоб неверно заполнял поля flags и shadow для вьюх.
Подправил. X++: // [url=https://axforum.info/forums/showthread.php?t=39005&highlight=sqlDictionary]Программное воссоздание записей SqlDictionary для определенной таблицы[/url] public static server void sqlDictionaryFill4Table(tableId _tableId) { #macrolib.DictField #define.RecIdBaseType (49) // для полей с типом RecId/RefRecId/createdTransactionId/modifiedTransactionId используется не Types::Int64, а этот тип #define.TZIDsuffix ('_TZID') // для несистемных полей типа UtcDateTime в БД создается дополнительное поле с кодом временной зоны, в которой было записано значение SqlDictionary sqlDict; SysdictType dictType; DictTable dictTable; DictField dictField; ArrayIdx arrIdx; Counter numOfSqlFields; // сколько записей для полей таблицы должно быть в SqlDictionary fieldName fieldName; fieldId fieldId; boolean processTableField(DictField _dictField, ArrayIdx _arrIdx, boolean _isTzIdField = false) { ArrayIdx dictArrIdx; str infoName; // это имя поля сугубо для сообщений FieldName sqlName; boolean ret; ; if (_isTzIdField) { if ( _dictField.baseType() != Types::UtcDateTime || _dictField.id() == fieldnum(Common, createdDateTime) || _dictField.id() == fieldnum(Common, modifiedDateTime) ) { throw error(Error::wrongUseOfFunction(funcname())); } dictArrIdx = _dictField.arraySize() + _arrIdx; sqlName = _dictField.dateTimeTimeZoneRuleFieldName(_arrIdx - 1); infoName = _dictField.name() + #TZIDsuffix; } else { dictArrIdx = _arrIdx; sqlName = _dictField.name(DbBackend::Sql, _arrIdx); infoName = _dictField.name(); } select firstonly sqlDict where sqlDict.tabId == _dictField.tableid() && sqlDict.fieldId == _dictField.id() && sqlDict.array == dictArrIdx ; if (!sqlDict) { sqlDict.clear(); sqlDict.initValue(); sqlDict.tabId = _dictField.tableid(); sqlDict.fieldId = _dictField.id(); sqlDict.array = dictArrIdx; sqlDict.name = strupr(_dictField.name(DbBackend::Native, _arrIdx)); sqlDict.sqlName = sqlName; dictType = new SysDictType( _dictField.typeId() ); if (_isTzIdField) { sqlDict.fieldType = Types::Integer; } else if ( _dictField.id() == fieldnum(Common, RecId) || _dictField.id() == fieldnum(Common, createdTransactionId) || _dictField.id() == fieldnum(Common, modifiedTransactionId) || _dictField.id() == fieldnum(Common, Partition) || _dictField.typeId() == extendedtypenum(RecId) || _dictField.typeId() == extendedtypenum(RefRecId) || _dictField.typeId() == extendedtypenum(createdTransactionId) || _dictField.typeId() == extendedtypenum(modifiedTransactionId) || _dictField.typeId() == extendedtypenum(Partition) || ( dictType && ( dictType.isExtending(extendedtypenum(RecId)) || dictType.isExtending(extendedtypenum(createdTransactionId)) || dictType.isExtending(extendedtypenum(modifiedTransactionId)) || dictType.isExtending(extendedtypenum(Partition)) ) ) ) { // для RecId в поле fieldType прописывается не Types::Int64, а число 49 sqlDict.fieldType = #RecIdBaseType; } else if ( _dictField.BaseType() == Types::Guid || _dictField.BaseType() == 45 // Types_MRC::SysGuid ) { sqlDict.fieldType = 45; } else { sqlDict.fieldType = _dictField.baseType(); } sqlDict.strSize = _dictField.stringLen(); // sqlDict.shadow = bitTest( _dictField.flags(), #DBF_SHADOW ); sqlDict.shadow = 0; // shadow может быть отлично от 0 только для записей VIEW // для VIEW помимо полей также в SQLDictionary хранится перечень входящих во VIEW табличек. Вот для таких записей shadow = 1 // но мы такие записи в этом джобе не воссоздаем, проще грохнуть вьюху и синхронизировать sqlDict.rightJustify = bitTest( _dictField.flags(), #DBF_RIGHT ); // sqlDict.flags = sqlDict.shadow; // а вот ни фига не _dictField.flags(); sqlDict.flags = 0; // flags может быть отлично от 0 только для записей VIEW // для VIEW помимо полей также в SQLDictionary хранится перечень входящих во VIEW табличек. Вот для таких записей flags = 1 // а также для самой записи соответвующей шапке VIEW flags = 1 // но мы такие записи в этом джобе не воссоздаем, проще грохнуть вьюху и синхронизировать sqlDict.nullable = _dictField.baseType() == Types::Container || _dictField.baseType() == Types::VarString ; if (sqlDict.validateWrite()) { sqlDict.insert(); ret = true; info(strfmt(@"Создана запись для поля %1.%2%3", dictTable.name(), infoName, _dictField.arraySize() > 1 ? strfmt(@"[%1]", _arrIdx) : '' )); // для всех несистемных полей UtcDateTime создаем также связанное поле TZID if ( !_isTzIdField && _dictField.baseType() == Types::UtcDateTime && _dictField.id() != fieldnum(Common, createdDateTime) && _dictField.id() != fieldnum(Common, modifiedDateTime) ) { processTableField(_dictField, _arrIdx, true); } } else { ret = checkFailed(strfmt(@"Запись в %1 для поля %2.%3 не была создана", tablestr(SqlDictionary), dictTable.name(), infoName)); } } return ret; } ; dictTable = new DictTable( _tableId ); if (!dictTable) { throw error(strfmt(@"Не удалось создать объект %1 для таблицы '%2' (%3)", classstr(DictTable), tableid2name( _tableId ), _tableId)); } if (dictTable.isSystemTable()) { throw error(strfmt(@"Таблица '%1' - системная, на это я пойтить не могу", dictTable.name())); } if (!dictTable.isSql()) { throw error(strfmt(@"Таблицы '%1' вообще не должно быть в базе", dictTable.name())); } if (dictTable.isView()) { throw error(strfmt(@"Таблица '%1' это не табличка а VIEW. Для нее проще просто удалить объект в БД. Либо дописывать корректное заполнение полей shadow и flags", dictTable.name())); } for (fieldId = dictTable.fieldNext( 0 ); fieldId; fieldId = dictTable.fieldNext( fieldId )) { dictField = dictTable.fieldObject( fieldId ); if (dictField && dictField.isSql()) { fieldName = dictField.name(); for (arrIdx = 1; arrIdx <= dictField.arraySize(); arrIdx++) { numOfSqlFields++; processTableField( dictField, arrIdx ); } } } select firstonly sqlDict where sqlDict.tabId == _tableId && sqlDict.fieldId == 0 ; if (!sqlDict) { sqlDict.clear(); sqlDict.initValue(); sqlDict.tabId = _tableId; sqlDict.name = strupr(dictTable.name()); sqlDict.sqlName = dictTable.name(DbBackend::Sql); sqlDict.strSize = numOfSqlFields; // для "заголовка" таблицы тут указывается, сколько у нее полей в БД sqlDict.flags = dictTable.isView(); // "мой дедуктивный метод..." sqlDict.insert(); info(strfmt(@"Создана запись для таблицы %1", dictTable.name())); } } |
|
|
За это сообщение автора поблагодарили: propeller (1). |
![]() |
#17 |
Участник
|
Да уж натворили они делов.
Надо было тогда везде, где возможно отказываться от идентификаторов таблиц, EDT Или использовать Origin который guid |
|
![]() |
#18 |
Administrator
|
Спасибо! По опыту использования этого скрипта - могу подтвердить - что там точно есть где-то ошибка, но так детально я не исследовал
__________________
Возможно сделать все. Вопрос времени |
|
Теги |
ax2009, sqldictionary, законченный пример |
|
|