Как исправить ошибку при запуске процедуры: "PLS-00306: wrong number or types of arguments in call to"?
Как исправить эту ошибку:
ORA-06550: line 1, column 7: PLS-00306: wrong number or types of arguments in call to 'GET_DATA_INFO'
ORA-06550: line 1, column 7: PL/SQL: Statement ignored
Получаю ее когда запускаю процедуру:
PROCEDURE get_data_info (
p_ticker VARCHAR2,
p_acronym VARCHAR2,
p_sort VARCHAR2,
p_call_source VARCHAR2,
p_data OUT REF_CRS);
Node.js код:
sql = 'begin get_data_info(:p_ticker, :p_acronym, :p_sort, :p_call_source, :p_data); end;';
const ticker_data = {
p_ticker: '',
p_acronym: '',
p_sort: '',
p_call_source: '',
p_data: ''
};
data = { p_ticker: 'AAPL', p_data: ':output'};
binds = Object.assign({}, ticker_data, data);
options = {
outFormat: oracledb.OUT_FORMAT_OBJECT,
};
result = await connection.execute(sql, binds, options);
Свободный перевод вопроса PLS-00306 when calling Oracle stored procedure in Node.js от участника @siddhartha
Ответы (3 шт):
Предполагаю, что REF_CRS является REF CURSOR, и проблема заключается в определении p_data в объекте параметра связывания. Нужно дать ему тип и направление:
p_data: {dir: oracledb.BIND_OUT, type: oracledb.CURSOR}
Пдробней в документции к node-oracledb в главе REF CURSOR Bind Parameters.
Вот как выглядит рабочий пример:
SQL:
create or replace PROCEDURE get_data_info (
p_ticker VARCHAR2,
p_acronym VARCHAR2,
p_sort VARCHAR2,
p_call_source VARCHAR2,
p_data OUT SYS_REFCURSOR) as
begin
open p_data for select * from dual;
end;
/
Node.js:
const oracledb = require('oracledb');
const dbConfig = { user: 'me', password: 'me', connectString: 'localhost/pdb1' };
if (process.platform === 'darwin') {
oracledb.initOracleClient({libDir: '/Users/cjones/instantclient_19_3'});
}
let sql, binds, options, result;
async function run() {
let connection;
try {
connection = await oracledb.getConnection(dbConfig);
sql = 'begin get_data_info(:p_ticker,:p_acronym,:p_sort,:p_call_source,:p_data); end;';
const ticker_data = {
p_ticker: '',
p_acronym: '',
p_sort: '',
p_call_source: '',
p_data: ''
};
const data = { p_ticker: 'AAPL', p_data: {dir: oracledb.BIND_OUT, type: oracledb.CURSOR}};
const binds = Object.assign({}, ticker_data, data);
result = await connection.execute(sql, binds, options);
const resultSet = result.outBinds.p_data;
let row;
while ((row = await resultSet.getRow())) {
console.log(row);
}
await resultSet.close();
} catch (err) {
console.error(err);
} finally {
if (connection) {
try {
await connection.close();
} catch (err) {
console.error(err);
}
}
}
}
run();
PS (Прим.ред. на основе комментариев): такая запись подстановочных переменных будет проще и понятней:
const binds = {
p_ticker: 'AAPL',
p_acronym: undefined,
p_sort: undefined,
p_call_source: undefined,
p_data: {dir: oracledb.BIND_OUT, type: oracledb.CURSOR}
};
Свободный перевод ответа от участника @Christopher Jones
Вместе с @0xdb провели рефакторинг кода, предложенного в ответе @Christopher Jones.
Код полностью рабочий, проверен на macos, nodejs v14.10.0, драйвер node-oracledb v5.0.0.
create or replace procedure get_data_info (
p_ticker varchar2,
p_acronym varchar2,
p_sort varchar2,
p_call_source varchar2,
p_data out sys_refcursor) as
begin
open p_data for select 1 id, 'memo' memo, sysdate created from dual;
end;
const oracledb = require('oracledb');
const dbConfig = { user: 'me', password: 'me', connectString: 'localhost/pdb1' };
(async () => await run())();
async function run() {
let connection;
try {
connection = await oracledb.getConnection(dbConfig);
const sql = `
begin
get_data_info(:p_ticker,:p_acronym,:p_sort,:p_call_source,:p_data);
end;`;
const binds = {
p_ticker: 'AAPL',
p_data: { dir: oracledb.BIND_OUT, type: oracledb.CURSOR },
p_acronym: undefined,
p_sort: undefined,
p_call_source: undefined
};
const options = {
outFormat: oracledb.OUT_FORMAT_OBJECT,
};
const result = await connection.execute(sql, binds, options);
const resultSet = result.outBinds.p_data;
let row;
while (row = await resultSet.getRow()) {
console.log(row);
}
await resultSet.close();
} catch (err) {
console.error(err);
} finally {
if (connection) {
try {
await connection.close();
} catch (err) {
console.error(err);
}
}
}
}
Результат выполнения
{ ID: 1, MEMO: 'memo', CREATED: 2020-10-02T10:55:07.000Z }
Разделы документации, которые стоит изучить в связи с этим вопросом:
REF CURSOR Bind Parameters For an OUT bind, the resulting bind variable becomes a ResultSet, allowing rows to be fetched using getRow() or getRows()
IN Bind Parameters Bind by Name
Для пользователей process.platform === 'darwin'
В целом, установка драйвера описана в соответствующем разделе документации, но её можно упростить, установив instantclient с помощью менеджера пакетов homebrew
brew tap InstantClientTap/instantclient
brew install instantclient-basic
Вариант для typescript:
create or replace procedure get_data_info (
p_ticker varchar2,
p_acronym varchar2,
p_sort varchar2,
p_call_source varchar2,
p_data out sys_refcursor) as
begin
open p_data for select 1 id, 'memo' memo, sysdate created from dual;
end;
import * as OracleDB from 'OracleDB';
const dbConfig: OracleDB.ConnectionAttributes = {
user: 'me',
password: 'me',
connectString: 'localhost/pdb1'
};
class ConnectDAO {
public async ConnectionDB(): Promise<OracleDB.Connection> {
return OracleDB.getConnection(dbConfig);
}
public async fetchData<T>(
connection: OracleDB.Connection,
sql: string,
bindParams: OracleDB.BindParameters = {},
options: OracleDB.ExecuteOptions = {}
): Promise<OracleDB.Result<T>> {
return connection.execute<T>(sql, bindParams, options);
}
}
async function connectAndExecute() {
let connectDao = new ConnectDAO();
try {
const connection = await connectDao.ConnectionDB();
const sql = `
begin
get_data_info(:p_ticker,:p_acronym,:p_sort,:p_call_source,:p_data);
end;`;
const binds: OracleDB.BindParameters = {
p_ticker: 'AAPL',
p_acronym: undefined,
p_sort: undefined,
p_call_source: undefined,
p_data: {
dir: OracleDB.BIND_OUT,
type: OracleDB.CURSOR
}
};
const options: OracleDB.ExecuteOptions = {
outFormat: OracleDB.OUT_FORMAT_OBJECT
};
const results = await connectDao.fetchData<{p_data: OracleDB.ResultSet<Record<string, any>>}>(connection, sql, binds, options);
const resultSet = results.outBinds.p_data;
let row: Record<string, any>;
while (row = await resultSet.getRow()) {
console.log(row);
}
await resultSet.close();
await connection.close();
} catch (err) {
console.log(`error caught ${err}`);
}
}
connectAndExecute();