Why can't I make this insert statement a procedure? The insert statement works, but when I try to make it a procedure it doesn't compile.
CREATE OR REPLACE PROCEDURE INSERT_INTO_CPP AS
BEGIN
    INSERT INTO cpp /*+ ignore_row_on_dupkey_index ( cpp ( pk_cpp ) ) */
    SELECT * FROM EXTERNAL (
        (
      cpp VARCHAR2 (50),
      rfu1 NUMBER (6, 2),
      rfu2 NUMBER (6, 2),
      mean_rfu NUMBER (6, 2),
      charge_ph7_4 NUMBER (2),
      hydropathy NUMBER (3, 1))
    
        TYPE ORACLE_LOADER
        DEFAULT DIRECTORY cpp_data 
        ACCESS PARAMETERS (
            RECORDS DELIMITED BY NEWLINE
            SKIP 1
            BADFILE cpp_data:'cpp_badfile.bad'
            FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '&quot;'
            MISSING FIELD VALUES ARE NULL 
            ) 
        LOCATION ('LYS_cpp.CSV')
        REJECT LIMIT UNLIMITED) ext
        WHERE NOT EXISTS (
            SELECT * FROM cpp c
            WHERE c.pk_cpp = cpp );
END INSERT_INTO_CPP;
/

I get this error:
PL/SQL: ORA-06553: PLS-103: Encountered the symbol &quot;)&quot; when expecting one of the following:     . ( of
I've raised a bug for this. In the meantime, you can work around the issue by using dynamic SQL:
create or replace directory tmp as '/tmp';
declare
  f utl_file.file_type;
begin
  f := utl_file.fopen ('TMP', 'test.csv', 'w');
  utl_file.put_line(f, '1,1');
  utl_file.put_line(f, '2,2');
  utl_file.fclose(f);
end;
/

create table t (
  c1 int, c2 int
);

create or replace procedure p as
begin
  execute immediate q'!insert into t 
    select * from external (
    ( c1 int, c2 int )
    default directory tmp
    location ( 'test.csv' )
  )!';
end p;
/

exec p();

select * from t;

C1     C2   
    1     1 
    2     2