I have a function with a loop inside a loop and I'm getting 'numeric or value error'.
sendXML CLOB;
FOR p IN (
SELECT ID, NAME, GUID FROM products
WHERE ID = IN_PROJECT_ID
)
LOOP
if p.ID is not null and p.NAME is not null then
sendXML := sendXML || '<product type="product" id="' || p.ID|| '" name="' || p.NAME || '">';
FOR t IN (
SELECT
identifier ATTR_IDENTIFIER,
label ATTR_LABEL,
CASE type
WHEN UPPER('STRING') THEN TRIM(string_value)
WHEN UPPER('NUMBER') THEN TRIM(TO_CHAR(number_value))
ELSE '' END ATTR_VALUE
FROM products_data
WHERE
product_id = p.ID AND
identifier is not null
ORDER BY identifier
)
LOOP
sendXML := sendXML || '<attribute identifier="' || t.ATTR_IDENTIFIER || '" label="'|| t.ATTR_LABEL || '">' || t.ATTR_VALUE || '</attribute>';
END LOOP;
END IF;
END LOOP;
The error
ORA-06502: PL/SQL:numeric or value error ORA-06512: at "ASM.XXXX", line 85 06502
06502. 00000 - "PL/SQL: numeric or value error%s"
throws for the line:
sendXML := sendXML || '<product type="product" id="' || p.ID|| '" name="' || p.NAME || '">';
But I found out that if I delete the last
sendXML := sendXML || '<attribute identifier="' || t.ATTR_IDENTIFIER || '" label="'|| t.ATTR_LABEL || '">' || t.ATTR_VALUE || '</attribute>';
I'm not getting any error.
Wheres the problem?
SOLUTION:
p.ID is integer and must be char... TO_CHAR(p.ID) solved my problem!
sendXML := sendXML || '<product type="product" id="' || TO_CHAR(p.ID) ||
My guess is that the string becomes larger than the maximum for varchar2 in PL/SQL.
Try the following to append text to a clob:
dbms_lob.append(sendXML, to_clob('<product type="product" id="' || p.ID|| '" name="' || p.NAME || '">'));
and the second one:
dbms_lob.append(sendXML, to_clob('<attribute identifier="' || t.ATTR_IDENTIFIER || '" label="'|| t.ATTR_LABEL || '">' || t.ATTR_VALUE || '</attribute>'));
Use quote operator like
sendXML := sendXML || q'[<attribute identifier=']' || t.ATTR_IDENTIFIER || q'[' label=']'|| t.ATTR_LABEL || q'['>]' || t.ATTR_VALUE || q'[</attribute>]';
To escape quotes. See if error persists
Related
I have a varchar2 with an INSERT and I want to execute it in a procedure I try to do it with an execute but this happens:
EXECUTE IMMEDIATE sql_str;
Error:
ERROR at line 1:
ORA-00911: invalid character
ORA-06512: at "SYS.INSERT_MOVIMIENTOS", line 47
ORA-06512: at line 1
Varchar2 carries an insert that is this and works if I paste it but when executing it in the procedure something of the procedure fails.
INSERT INTO MOVIMIENTOS (COD_BANCO, COD_SUCUR, NUM_CTA, FECHA_MOV, TIPO_MOV, IMPORTE) VALUES (2000, 2000, 0, '11/11/08', 'I', 500);
My procedure
CREATE OR REPLACE PROCEDURE INSERT_MOVIMIENTOS (
INSERTMOV_COD_BANCO IN NUMBER,
INSERTMOV_COD_SUCUR IN NUMBER,
INSERTMOV_NUM_CTA IN NUMBER,
INSERTMOV_FECHA_MOV IN DATE,
INSERTMOV_TIPO_MOV IN CHAR,
INSERTMOV_IMPORTE IN NUMBER
)
IS
sql_str VARCHAR2(500) := 'INSERT INTO MOVIMIENTOS (';
movimiento movimientos_typ;
BEGIN
movimiento := movimientos_typ(
INSERTMOV_COD_BANCO,
INSERTMOV_COD_SUCUR,
INSERTMOV_NUM_CTA,
INSERTMOV_FECHA_MOV,
INSERTMOV_TIPO_MOV,
INSERTMOV_IMPORTE
);
IF movimiento.getCOD_BANCO() != 0 THEN
sql_str := sql_str || 'COD_BANCO, COD_SUCUR, NUM_CTA, FECHA_MOV, TIPO_MOV, IMPORTE) VALUES (' ||
movimiento.getCOD_BANCO() || ', ' ||
movimiento.getCOD_SUCUR() || ', ' ||
movimiento.getNUM_CTA() || ', ''' ||
movimiento.getFECHA_MOV() || ''', ''' ||
movimiento.getTIPO_MOV() || ''', ' ||
movimiento.getIMPORTE() || ');';
ELSE
sql_str := sql_str || 'COD_SUCUR, NUM_CTA, FECHA_MOV, TIPO_MOV, IMPORTE) VALUES (' ||
movimiento.getCOD_SUCUR() || ', ' ||
movimiento.getNUM_CTA() || ', ''' ||
movimiento.getFECHA_MOV() || ''', ''' ||
movimiento.getTIPO_MOV() || ''', ' ||
movimiento.getIMPORTE() || ');';
END IF;
DBMS_OUTPUT.PUT_LINE('////////////////////////////////////////');
DBMS_OUTPUT.PUT_LINE('CONSULTA: ' || sql_str);
DBMS_OUTPUT.PUT_LINE('////////////////////////////////////////');
DBMS_OUTPUT.PUT_LINE('DATOS INTRODUCIDOS: ');
movimiento.display;
DBMS_OUTPUT.PUT_LINE('////////////////////////////////////////');
EXECUTE IMMEDIATE sql_str;
DBMS_OUTPUT.PUT_LINE('FUNCION REALIZADA CON EXITO');
END;
/
Donot end with the semicolon ; in your query string.
movimiento.getIMPORTE() || ')';
The error is just because of it.
by the way, you should be using bind variables. Dynamically constructing the values this way is vulnerable to SQL Injection.
This is how I create my search_term:
IF char_length(search_term) > 0 THEN
order_by := 'ts_rank_cd(textsearchable_index_col, to_tsquery(''' || search_term || ':*''))+GREATEST(0,(-1*EXTRACT(epoch FROM age(last_edited)/86400))+60)/60 DESC';
search_term := 'to_tsquery(''' || search_term || ':*'') ## textsearchable_index_col';
ELSE
search_term := 'true';
END IF;
I am having some trouble with a PLPGSQL function:
RETURN QUERY EXECUTE '
SELECT
*
FROM
articles
WHERE
$1 AND
' || publication_date_query || ' AND
primary_category LIKE ''' || category_filter || ''' AND
' || tags_query || ' AND
' || districts_query || ' AND
' || capability_query || ' AND
' || push_notification_query || ' AND
' || distance_query || ' AND
' || revision_by || ' AND
' || publication_priority_query || ' AND
' || status_query || ' AND
is_template = ' || only_templates || ' AND
status <> ''DELETED''
ORDER BY ' || order_by || ' LIMIT 500'
USING search_term;
END; $$;
returns ERROR:
argument of AND must be type boolean, not type text at character 64
As opposed to:
RETURN QUERY EXECUTE '
SELECT
*
FROM
articles
WHERE
' || search_term || ' AND
' || publication_date_query || ' AND
primary_category LIKE ''' || category_filter || ''' AND
' || tags_query || ' AND
' || districts_query || ' AND
' || capability_query || ' AND
' || push_notification_query || ' AND
' || distance_query || ' AND
' || revision_by || ' AND
' || publication_priority_query || ' AND
' || status_query || ' AND
is_template = ' || only_templates || ' AND
status <> ''DELETED''
ORDER BY ' || order_by || ' LIMIT 500';
END; $$;
... which works. Am I missing something?
My goal is to sanitize my user input.
If some of your input parameters can be NULL or empty and should be ignored in this case, you best build your whole statement dynamically depending on user input - and omit respective WHERE / ORDER BY clauses completely.
The key is to handle NULL and empty string correctly, safely (and elegantly) in the process. For starters, search_term <> '' is a smarter test than char_length(search_term) > 0. See:
Best way to check for "empty or null value"
And you need a firm understanding of PL/pgSQL, or you may be in over your head. Example code for your case:
CREATE OR REPLACE FUNCTION my_func(
_search_term text = NULL -- default value NULL to allow short call
, _publication_date_query date = NULL
-- , more parameters
)
RETURNS SETOF articles AS
$func$
DECLARE
sql text;
sql_order text; -- defaults to NULL
BEGIN
sql := concat_ws(' AND '
,'SELECT * FROM articles WHERE status <> ''DELETED''' -- first WHERE clause is immutable
, CASE WHEN _search_term <> '' THEN '$1 ## textsearchable_index_col' END -- ELSE NULL is implicit
, CASE WHEN _publication_date_query <> '' THEN 'publication_date > $2' END -- or similar ...
-- , more more parameters
);
IF search_term <> '' THEN -- note use of $1!
sql_order := 'ORDER BY ts_rank_cd(textsearchable_index_col, $1) + GREATEST(0,(-1*EXTRACT(epoch FROM age(last_edited)/86400))+60)/60 DESC';
END IF;
RETURN QUERY EXECUTE concat_ws(' ', sql, sql_order, 'LIMIT 500')
USING to_tsquery(_search_term || ':*') -- $1 -- prepare ts_query once here!
, _publication_date_query -- $2 -- order of params must match!
-- , more parameters
;
END
$func$ LANGUAGE plpgsql;
I added default values for function parameters, so you can omit params that don't apply in the call. Like:
SELECT * FROM my_func(_publication_date_query => '2016-01-01');
More:
Functions with variable number of input parameters
The forgotten assignment operator "=" and the commonplace ":="
Note the strategic use of concat_ws(). See:
How to concatenate columns in a Postgres SELECT?
Here is a related answer with lots of explanation:
Test for null in function with varying parameters
I need regex to capture full "create procedure" statement.
Here is one of examples, which I used for testing my regex:
CREATE OR REPLACE PROCEDURE sp_for_comp (P_VARNAME IN VARCHAR2, P_VALUE IN OUT NUMBER)
as
v_if_exists NUMBER(10,0);
BEGIN
SELECT COUNT(*) INTO v_if_exists FROM PKG_VAR WHERE VARIABLENAME = P_VARNAME;
IF v_if_exists > 0
THEN
begin
SELECT VALUE INTO P_VALUE FROM PKG_VAR WHERE VARIABLENAME = P_VARNAME;
EXCEPTION
WHEN OTHERS THEN
NULL;
end;
ELSE
begin
INSERT INTO PKG_VAR VALUES(P_VARNAME, P_VALUE);
EXCEPTION
WHEN OTHERS THEN
NULL;
end;
END IF;
END;
/
Current regex:
/CREATE\s+(OR\s+REPLACE\s+)?PROCEDURE\s+(\w+)\s*\(((?!.*\bEND\b\s*(\w+\s*)?\;\s*\/).*\s*)+.+/
As for my issue: I use QRegularExpression class and program failed when I run it on large files. Also, when I run it on small file - all works correctly.
After a lot of tests on online debuggers, like regexr.com, I could not find problem in regex.
How I should change it and where are may be problems?
Try something very simple like:
CREATE(\s+OR\s+REPLACE)\s+PROCEDURE.*?END;\s*/
It just looks for the CREATE OR REPLACE PROCEDURE at the start and then the end will be END; followed by / (indicating the end of the PL/SQL block in the SQL scope) on the next line with the minimal amount between.
(Note: You will probably want to use the ni regular expression match parameters to allow . to match the newline character and to do case-insensitive matches.)
Example:
CREATE TABLE script (value ) AS
SELECT 'CREATE OR REPLACE PROCEDURE sp_for_comp (P_VARNAME IN VARCHAR2, P_VALUE IN OUT NUMBER)' || CHR(10)
|| ' as' || CHR(10)
|| ' v_if_exists NUMBER(10,0);' || CHR(10)
|| 'BEGIN' || CHR(10)
|| ' SELECT COUNT(*) INTO v_if_exists FROM PKG_VAR WHERE VARIABLENAME = P_VARNAME;' || CHR(10)
|| ' IF v_if_exists > 0' || CHR(10)
|| ' THEN' || CHR(10)
|| ' begin' || CHR(10)
|| ' SELECT VALUE INTO P_VALUE FROM PKG_VAR WHERE VARIABLENAME = P_VARNAME;' || CHR(10)
|| ' EXCEPTION' || CHR(10)
|| ' WHEN OTHERS THEN' || CHR(10)
|| ' NULL;' || CHR(10)
|| ' end;' || CHR(10)
|| 'ELSE' || CHR(10)
|| ' begin' || CHR(10)
|| ' INSERT INTO PKG_VAR VALUES(P_VARNAME, P_VALUE);' || CHR(10)
|| ' EXCEPTION' || CHR(10)
|| ' WHEN OTHERS THEN' || CHR(10)
|| ' NULL;' || CHR(10)
|| ' end;' || CHR(10)
|| ' END IF;' || CHR(10)
|| 'END;' || CHR(10)
|| '/'
FROM DUAL;
SELECT COUNT(*)
FROM script
WHERE REGEXP_LIKE( value, '^CREATE(\s+OR\s+REPLACE)\s+PROCEDURE.*?END;\s*/$', 'n' );
Outputs:
COUNT(*)
--------
1
I have complex Select statement in Oracle, with Case When conditions and they select all Concatenate values.
So something similar to this:
END
END
FROM something
...
What I need is to put Other value from other table instead of My Value in concatenate.
So Instead of (My Value) I would have:
(Select textValue from textView A where A.textID = '395')
If I run this statement alone, it will take out one exact value I want. However if I put it instead of (My Value) into concatenate it gives me error: ora-00936 missing expression
(Yes '395' is string in that other table)
Any Ideas please?
You can use inline views inside the case clause in Oracle. Isn't that what you are trying to do? "Missing expression" error is probably a missing bracket or some similar code error.
select case object_type
when 'TABLE' then
'select * from ' || object_name
when 'SYNONYM' then
'describe ' || object_name
when 'PACKAGE' then
(select to_char (count (*))
from user_source s
where s.type = o.object_type and s.name = o.object_name)
else
(select object_type from dual)
end
as objects
from user_objects o
Or simply create a function that returns that does this for you and call it.
create function gettextval (p_textid in varchar2)
return varchar2 is
l_returnval varchar2 (32767);
begin
select text_val
into l_returnval
from v_texts s
where s.text_id = p_textid;
return l_returnval;
exception
when no_data_found then
return p_textid;
end;
In this example I make it return the input string of nothing is found.
Then you can reference the function in your previous select.
' || gettextval('395') || '
can you post you query with the "select text" bit inside?
When use case->when i do like that:
SELECT
CASE A.LIST_TYPE_CD
WHEN '1' THEN '<A HREF="censured?thresholdId=censured' || GET_SITE_SUFFIX() || chr(38) || 'task=runSQL' || chr(38) || 'parseParams=true' || chr(38) || 'list_id=' || A.LIST_ID || chr(38) || 'list_name=' || A.LIST_NAME ||
'">' || (Select textValue from textView A where A.textID = '395') || '</A>'
WHEN '3' THEN '<A HREF=censured' || GET_SITE_SUFFIX() || chr(38) || 'task=runSQL' || chr(38) || 'parseParams=true' || chr(38) || 'list_id=' || A.LIST_ID ||
'">' || (Select textValue from textView B where B.textID = '395') || '</A>'
END
FROM something A
Little help needed. i have a dynamic query that outputs 4 column names and two table names into 6 cursor variables. Now i need to use the cursor variables to select the first 4 columns and then from the two table names using the cursor variables since those contain the data think something with a fetch through query using a variable that contains the query but i don’t know how to go about that. here’s what i have now i just need to fetch the cursor variables and runt hem into a query
DECLARE
arow VARCHAR2 (1000);
column1 VARCHAR2 (50);
column2 VARCHAR2 (50);
column3 VARCHAR2 (50);
column4 VARCHAR2 (50);
table1 VARCHAR2 (50);
table2 VARCHAR2 (50);
match VARCHAR2 (50);
match1 VARCHAR2 (50);
sql_statement VARCHAR2 (500);
BEGIN
FOR arow IN (SELECT column_name_old,
column_name_new,
column_name_old_2,
column_name_new_2,
table_name_old,
table_name_new
FROM A550003.META_DATA_TABLE)
LOOP
sql_statement :=
'INSERT'
|| ' '
|| 'INTO'
|| ' '
|| 'a550003.MATCH_TABLE'
|| ' '
|| 'SELECT '
|| arow.column_name_old
|| ', '
|| arow.column_name_new
|| ', '
|| 'DECODE( '
|| arow.column_name_old
|| ', '
|| arow.column_name_new
|| ','
|| '1'
|| ','
|| '0)'
|| 'AS'
|| ' '
|| 'MATCH'
|| ','
|| arow.column_name_old_2
|| ', '
|| arow.column_name_new_2
|| ','
|| 'DECODE( '
|| arow.column_name_old_2
|| ', '
|| arow.column_name_new_2
|| ','
|| '1'
|| ','
|| '0)'
|| 'AS'
|| ' '
|| 'MATCH1'
|| ' FROM '
||' '
|| arow.table_name_old
|| ', '
|| arow.table_name_new
|| ' WHERE '
|| arow.column_name_old
|| '='
|| arow.column_name_new
|| '(+)';
DBMS_OUTPUT.PUT_LINE (sql_statement);
EXECUTE IMMEDIATE sql_statement;
COMMIT;
END LOOP;
END;
First of all you sql_statement is wrong. You should set the value of this variable to sth like this (when you will get all needed names):
sql_statement := 'SELECT '
|| column1
|| ', '
|| column2
|| ', '
|| column3
|| ', '
|| column4
|| ' FROM '
|| table1
|| ', '
|| table2
|| ' WHERE '
|| -- JOIN_CONDITION
And then you can use EXECUTE IMMEDIATE statement:
EXECUTE IMMEDIATE sql_statement
INTO col1_val
,col2_val
,col3_val
,col4_val
;
Of course variables col[1..4]_val have to be declared.
Also watch out for SQL Injection.
It must be this:
sql_statement := 'SELECT '
|| column1 || ', ' || column2 || ', ' || column3 || ', ' || column4
|| ' FROM ' || table1 || ', ' || table2
|| ' WHERE ' || column1 ||'=' ||column2||'(+)';
EXECUTE IMMEDIATE sql_statement INTO col1_val, col2_val, col3_val, col4_val;
or preferably
sql_statement := 'SELECT '
|| column1 || ', ' || column2 || ', ' || column3 || ', ' || column4
||' FROM '||table1||' LEFT OUTER JOIN '||table2||' ON '||column1||'='||column2;
EXECUTE IMMEDIATE sql_statement INTO col1_val, col2_val, col3_val, col4_val;
For debugging DBMS_OUTPUT.PUT_LINE (sql_statement); will be usefull!
Then you don't need a Ref-Cursor, simply do:
BEGIN
For aRow in (SELECT * FROM a550003.meta_data_table) LOOP
sql_statement := 'SELECT '||aRow.column1||','||aRow.column2 ...
END LOOP;
END;