You seem to be misinterpreting the documentation:
If the dynamic SQL statement represents an anonymous PL/SQL block or a CALL statement, repetition of placeholder names is significant.
This states that if the statement that is being dynamically executed is a PL/SQL block, then the bind variable don't have to be repeated. Your dynamic statement is a SQL statement, which does not qualify. You can fix this by adding begin
and end;
to the dynamic statement:
BEGIN
EXECUTE IMMEDIATE q'[
BEGIN
MERGE INTO Person p
USING (SELECT :x id, :y first_name, :z last_name FROM Dual) data
ON (p.id = :x) -- note the repeated placeholder :x here
WHEN MATCHED THEN
UPDATE SET p.first_name = data.first_name, p.last_name = data.last_name
WHEN NOT MATCHED THEN
INSERT (id, first_name, last_name) VALUES (data.id, data.first_name, data.last_name)
END;
]' USING 123, 'Foo', 'Bar';
END;
/
In an case, it's simple to fix this statement without repeating the bind variable:
BEGIN
EXECUTE IMMEDIATE q'[
MERGE INTO Person p
USING (SELECT :x id, :y first_name, :z last_name FROM Dual) data
ON (p.id = data.id)
WHEN MATCHED THEN
UPDATE SET p.first_name = data.first_name, p.last_name = data.last_name
WHEN NOT MATCHED THEN
INSERT (id, first_name, last_name) VALUES (data.id, data.first_name, data.last_name)
]' USING 123, 'Foo', 'Bar';
END;
/
Finally, I feel I must point out that there's little reason to use dynamic SQL in this situation. Unless there's more going on that is affecting the SQL being run, this should just be a static SQL statement.