Last updated November 29, 2022
Custom triggers aren’t officially supported and Heroku Support can’t provide help with them. Proceed at your own risk.
Heroku Connect uses Postgres triggers to observe changes in your Connect tables and write your changes to Salesforce. Implementing your own custom triggers on Connect tables can create additional load and Connect can miss your updates.
Understanding xmlbinary
Heroku Connect uses a Postgres variable called xmlbinary in its own triggers. The value of xmlbinary determines whether to send updates to Salesforce. This variable is how Connect prevents sending its own updates back to Salesforce and creating an infinite loop.
Connect triggers detect row updates to send to Salesforce when the xmlbinary value is base64. Connect triggers ignore row updates when xmlbinary is hex. You can set this value in your own custom trigger to temporarily enable or disable sending updates to Salesforce.
Make Changes in Response to Data Coming from Salesforce or To Foreign Tables
When reacting to changes coming from Salesforce, use an AFTER trigger to temporarily change the value of xmlbinary to base64. Restore the previous value after you have made your changes. This method also works well if you’re altering data in another table that requires capturing.
Here’s an example for a Contact mapping:
- A
Contactrecord created in Salesforce and written to your Heroku Postgres database with Connect as part of normal sync operations. - A trigger updates the row in Heroku Postgres with an
externalid__c. - Heroku Connect detects this update and syncs it back to Salesforce.
The custom trigger for the Contact example looks like:
CREATE OR REPLACE FUNCTION salesforce.contact_external_id_proc() RETURNS TRIGGER AS $$
DECLARE
oldxmlbinary varchar;
BEGIN
-- Save old value
oldxmlbinary := get_xmlbinary();
-- Change value base64 to ensure writing to _trigger_log is enabled
SET LOCAL xmlbinary TO 'base64';
-- Add your custom trigger code here.
-- Update the external ID
UPDATE salesforce.contact SET externalid__c = gen_random_uuid()
WHERE id = NEW.id;
-- Your custom trigger code ends here.
-- Reset the value
EXECUTE 'SET LOCAL xmlbinary TO ' || oldxmlbinary;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS contact_after_trigger ON salesforce.contact;
CREATE TRIGGER contact_after_trigger
AFTER INSERT OR UPDATE ON salesforce.contact
FOR EACH ROW
WHEN (get_xmlbinary()::text = 'hex'::text AND NEW.externalid__c IS NULL)
EXECUTE PROCEDURE salesforce.contact_external_id_proc();
Connect often INSERTs, UPDATEs, or DELETEs thousands of records at a time. Ensure that custom triggers perform well with large volume of records by testing in staging. These triggers also work best when they don’t write many changes per record change. Additionally, use simple calculations over complex calculations. Complex calculations can have an adverse impact on the speed of writing data from Salesforce.
Alter the Result of Your Own Data Change Statements
You can also use custom triggers to alter the data captured by Connect in response to your own SQL statements. For example, you can ensure that a UUID External ID is inserted every time you write a new record to your database. In this case, use a BEFORE trigger so that Connect picks up with the original INSERT statement.
Continuing with the Contact example from the previous section, the BEFORE trigger looks like this:
CREATE OR REPLACE FUNCTION salesforce.contact_external_id__before_proc() RETURNS TRIGGER AS $$
BEGIN
-- Add your custom trigger code here.
-- Update the external ID
NEW.external_id__c := gen_random_uuid();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS contact_before_trigger ON salesforce.contact;
CREATE TRIGGER contact_before_trigger
BEFORE INSERT OR UPDATE ON salesforce.contact
FOR EACH ROW
WHEN (get_xmlbinary()::text = 'base64'::text AND NEW.externalid__c IS NULL)
EXECUTE PROCEDURE salesforce.contact_external_id_before_proc();
In this case, the WHEN condition guarantees that xmlbinary is base64. There’s no need to set or unset its value to ensure that Connect captures the change with the rest of the record.