Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CIWEMB-33: Fix creating custom fields with logging on #92

Conversation

ahed-compucorp
Copy link

Overview

This PR backports the patch civicrm#21019 which fixes a DB syntax error when creating custom fields via the CiviCRM API if CiviCRM logging feature was on.

Before

Creating custom fields is not working via CiviCRM API.

[error] $Fatal Error Details = Array
(
    [callback] => Array
        (
            [0] => CRM_Core_Error
            [1] => exceptionHandler
        )

    [code] => -2
    [message] => DB Error: syntax error
    [mode] => 16
    [debug_info] => ALTER TABLE `cw2_civicrm`.log_civicrm_value_cpd_activity1_40 ADD  [nativecode=1064 ** You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1]
    [type] => DB_Error
    [user_info] => ALTER TABLE `cw2_civicrm`.log_civicrm_value_cpd_activity1_40 ADD  [nativecode=1064 ** You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1]
    [to_string] => [db_error: message="DB Error: syntax error" code=-2 mode=callback callback=CRM_Core_Error::exceptionHandler prefix="" info="ALTER TABLE `cw2_civicrm`.log_civicrm_value_cpd_activity1_40 ADD  [nativecode=1064 ** You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1]"]
)

#7 /civicrm/vendor/pear/db/DB/mysqli.php(406): DB_mysqli->mysqliRaiseError()
#8 /civicrm/vendor/pear/db/DB/common.php(1234): DB_mysqli->simpleQuery("ALTER TABLE `cw2_civicrm`.log_civicrm_value_cpd_activity1_40 ADD ")
#9 /civicrm/packages/DB/DataObject.php(2696): DB_common->query("ALTER TABLE `cw2_civicrm`.log_civicrm_value_cpd_activity1_40 ADD ")
#10 /civicrm/packages/DB/DataObject.php(1829): DB_DataObject->_query("ALTER TABLE `cw2_civicrm`.log_civicrm_value_cpd_activity1_40 ADD ")
#11 /civicrm/CRM/Core/DAO.php(454): DB_DataObject->query("ALTER TABLE `cw2_civicrm`.log_civicrm_value_cpd_activity1_40 ADD ")
#12 /civicrm/CRM/Core/DAO.php(1599): CRM_Core_DAO->query("ALTER TABLE `cw2_civicrm`.log_civicrm_value_cpd_activity1_40 ADD ", FALSE)
#13 /civicrm/CRM/Logging/Schema.php(462): CRM_Core_DAO::executeQuery("ALTER TABLE `cw2_civicrm`.log_civicrm_value_cpd_activity1_40 ADD ", (Array:0), TRUE, NULL, FALSE, FALSE)
#14 /civicrm/CRM/Core/BAO/CustomField.php(172): CRM_Logging_Schema->fixSchemaDifferencesFor("civicrm_value_cpd_activity1_40", (Array:1))
#15 /civicrm/Civi/Api4/Generic/Traits/DAOActionTrait.php(158): CRM_Core_BAO_CustomField::writeRecords((Array:1))
#16 /civicrm/Civi/Api4/Generic/DAOCreateAction.php(40): Civi\Api4\Generic\DAOCreateAction->writeObjects((Array:1))
#17 /civicrm/Civi/Api4/Provider/ActionObjectProvider.php(68): Civi\Api4\Generic\DAOCreateAction->_run(Object(Civi\Api4\Generic\Result))
#18 /civicrm/Civi/API/Kernel.php(149): Civi\Api4\Provider\ActionObjectProvider->invoke(Object(Civi\Api4\Generic\DAOCreateAction))
#19 /civicrm/Civi/Api4/Generic/AbstractAction.php(239): Civi\API\Kernel->runRequest(Object(Civi\Api4\Generic\DAOCreateAction))
#20 /civicrm/api/api.php(85): Civi\Api4\Generic\AbstractAction->execute()
#21 /ssp_core/modules/ssp_core_cpd/ssp_core_cpd.install(131): civicrm_api4("CustomField", "create", (Array:2))
#22 /ssp_core/modules/ssp_core_cpd/ssp_core_cpd.install(53): _ssp_core_cpd_create_cpd_activity_custom_fields(40)
#23 /ssp_core/modules/ssp_core_cpd/ssp_core_cpd.install(25): _ssp_core_cpd_create_cpd_activity_group()
#24 /ssp_core/modules/ssp_core_cpd/ssp_core_cpd.install(29): ssp_core_cpd_install()

After

Creating custom fields is working via CiviCRM API.

Technical Details

When creating custom fields via CiviCRM API like

    $result = civicrm_api4('CustomField', 'create', [
      'values' => $params,
      'checkPermissions' => FALSE,
    ]);

CiviCRM will create a table to store the data e.g. for the custom group cpd_activity with the id 40, CiviCRM creates the civicrm_value_cpd_activity_40. If the logging feature is on, another table will be created log_civicrm_value_cpd_activity_40.

For each new custom field we create via the CiviCRM API, CiviCRM alters the civicrm_value_* and alters the logging table to create a column for each custom field e.g. for the custom field cpd_title the column cpd_title_145 will be created in two places where the 145 is its id in the civicrm_custom_fields table.

CiviCRM fails at altering the logging table because the code send the custom field name instead of its column_name at the line CustomField.php#L157

  $addedColumns[$tableName][] = $customField->name; // ['civicrm_value_cpd_activity_40' => 'cpd_title']
  
  ...
  $logging->fixSchemaDifferencesFor($tableName, ['ADD' => $addedColumns[$tableName]]); // throws an error

Which results in creating a wrong query to create the logging table ALTER TABLE log_civicrm_value_cpd_activity_40 ADD , the missing part of this query is because CiviCRM tries to search for the line in the "create query" of the table civicrm_value_cpd_activity_40 that match the regex

 /^  `cpd_title` /

The regex will not match any line in the "create query" of the table civicrm_value_cpd_activity_40

 CREATE TABLE `civicrm_value_cpd_activity1_46` (
 ...
 `cpd_title_145` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
 ...
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

This is the relevant part in the function Schema::_getColumnQuery

  private function _getColumnQuery($col, $createQuery) {
   $line = preg_grep("/^  `$col` /", $createQuery);
   $line = rtrim(array_pop($line), ',');
   // CRM-11179
   $line = self::fixTimeStampAndNotNullSQL($line);
   return $line;
 }

The patch uses the column name instead of the field so that CiviCRM can generate the query successfully.

  $addedColumns[$tableName][] = $customField->column_name; // ['civicrm_value_cpd_activity_40' => 'cpd_title_145']

@ahed-compucorp ahed-compucorp merged commit 4606a71 into 5.39.1-patches Dec 1, 2022
@ahed-compucorp ahed-compucorp deleted the CIWEMB-33-fix-creating-custom-fields-with-logging-on branch December 1, 2022 10:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants