Validation to detect text in numeric field - oracle-apex

I'm trying to prevent users crashing the create new product apex page. On the create page i have a text field:product_name and a numeric field: product_quantity.
Currently when they enter text in the product_quantity field and click 'Save' they get the following error:
Error processing validation.
ORA-01722: invalid number
I have investigated the error however i thought in Apex, if you selected a numeric field, it would detect whether the user entered text or numeric characters?
Is there a method to display a validation message if the user has entered text, it shouts, otherwise it enables the user to save the new entry?
UPDATE
I know why its happening but dont know how to solve it.
I recreated my page and it worked. I then added two pieces of validation in my page processing and when i then try it i get the error in my intial post. If i disable them it works again. The validation use NOT EXISTS to find whether the entered value already exists in the table before they add it.
If only the validation kicks in after looking whether a numerical value has been entered. I stopped the validation looking at an associated item, and turned off the 'when button pressed' but still no joy.
select 1 from MY_TABLE where column_name = :P6_TEXT_FIELD
Is there a way to run the text box validation (checking whether its text entered) before the validation i have created in the page processing?

That's the thing with validations: they all get executed and do not short-circuit. You can actually clearly see this happening when you debug the page.
In the case of a number field, you'd see it does not pass the number validation. But this does not stop validation. So your second validation will be run which uses the submitted value, but would obviously fail when you entered text for instance.
There are some work-arounds for that.
For example, you could change your NOT-EXISTS validation to a PLSQL function returning an error message and execute something like this (example):
DECLARE
v_test_nbr NUMBER;
v_check_exists NUMBER;
BEGIN
BEGIN
v_test_nbr := to_number(:P6_TEXT_FIELD);
EXCEPTION
WHEN OTHERS THEN
-- or catch 1722 (invalid number) and 6502 (char to number conversion error)
v_test_nbr := NULL;
END;
IF v_test_nbr IS NOT NULL
THEN
-- if v_test_nbr is not null then the field should be numerically valid
-- if it isn't then this code would be skipped and this validation
-- will not throw an error.
-- However, the previous validation will still fail when text is entered,
-- so this shouldn't matter.
BEGIN
SELECT 1
INTO v_check_exists
FROM my_table
WHERE column_name = :P6_TEXT_FIELD;
EXCEPTION
WHEN no_data_found THEN
v_check_exists := 0;
END;
IF v_check_exists = 1
THEN
RETURN 'A record with this key already exists';
END IF;
END IF;
RETURN NULL;
END;
HOWEVER - in this case, where you want to check for duplicate entries a better option may exist if you are at least on version 4.1. If your table has the correct constraints defined, then you would have a unique key defined on the field you are performing the not-exists on. This means that if you would not have this validation on the field, you would get an ora-00001 DUP_VAL_ON_INDEX error.
You could then use the error processing provided by apex to catch this error, and produce a user-friendly message.
You can find an example of how to use and implement this on the blog of Patrick Wolf of the apex development team:
apex-4-1-error-handling-improvements-part-1/
apex-4-1-error-handling-improvements-part-2/

Related

Using Django SQL Explorer how do I correctly set default dates for paramters with PostgreSQL backend?

Our Django application just migrated from MySQL to PostgreSQL, and we are in the process of updating our user queries to use the correct syntax. These queries are stored/executed via Django-SQL-Explorer.
I have a query that for simplicity's sake looks like this:
SELECT * FROM table t WHERE t.scheduled_start_date BETWEEN $$from_date$$ AND $$to_date$$
The query above works, but I would like to set defaults for today & today+30 respectively.
I've tried the following WHERE clauses to no avail:
Works with user entered date, default throws syntax error
WHERE t.scheduled_start_date BETWEEN date('$$from_date:CURRENT_DATE$$') AND date('$$to_date:CURRENT_DATE + INTERVAL \'30 DAY\'$$')
Error using defaults:
syntax error at or near "30" LINE 28: ...urrent_date') AND date('current_date + interval \'30 day\'') ^
Defaults work correctly, but user entered dates do not:
WHERE t.scheduled_start_date BETWEEN date($$from_date:CURRENT_DATE$$) AND date($$to_date:CURRENT_DATE + INTERVAL '30 DAY'$$)
Error with user dates:
function date(integer) does not exist LINE 28: WHERE t.scheduled_start_date BETWEEN date(2019-09-30) AND da... ^ HINT: No function matches the given name and argument types. You might need to add explicit type casts.
The message about casting makes sense to me since the user enters in string data, however I can't seem to get the syntax right that casts to DATE but only when the default is overridden.
Any suggestions would be helpful. Thanks!
Your input is a string value, current_date is a function call. The former needs quotes whereas the latter does not. Therefore your client side substitution will not work. You can move the default logic into the SQL statement using the COALESCE() function doc.
In case the client sends NULL values for an empty user input this would result in the following where clause:
BETWEEN date(COALESCE('$$from_date$$',current_date))
AND date(COALESCE('$$from_date$$',current_date + interval '30 DAYS'))
You added that your client sends an empty string, not a NULL value. So this should be changed to:
BETWEEN COALESCE(NULLIF('$$from_date$$','')::date, current_date)
AND COALESCE(NULLIF('$$from_date$$','')::date, current_date + interval '30 DAYS')

How to show alert if user tries to save values, that are already present my table for my Oracle Apex Form based application

I have a form in my Oracle APEX based application, I want to have validation on submit button, so that the combination of two specific entries, if they already are present in the SQL table/View, I want to show an alert, like "The entry for this combination of values of A and B already exists, please enter correct values."
If those two specific entries are represented by two form items (e.g. :P1_ONE and :P2_TWO), then the validation procedure might be a function that returns error text, such as
declare
l_cnt number;
retval varchar2(200);
begin
select count(*)
into l_cnt
from your_table t
where t.column_one = :P1_ONE
and t.column_two = :P1_TWO;
if l_cnt > 0 then
retval := 'The entry for this combination already exists';
end if;
end;
The query itself might need to be modified, depending on what exactly you meant by describing the problem; that's the way I understood it.
Then you should have a unique constraint on the table, and let that validate incoming data.
Any violation of this constraint will have exception raised, which can be transformed within the APEX error handling procedure.

APEX: Item is blank when trying to prepopulate with value

My Scenario:
I have a report on page 1 which has a link to page 2. This link passes an ID to page 2 (V2_ID gets set with V1_ID).
I then have two items called V2_ID and V2_NAME on page 2, as well as a PL/SQL process on page 2 which executes after the header loads which
select name into :V2_NAME from table where id = :V_ID;
The V2_ID shows the value, but the V2_NAME is always blank.
How can I pre-populate this variable. This is a very simple example as my use case is much more complicated, but the concept is the same. I can't use automatic row fetch because each item comes from a different table (it is a horrible database design, but I have to work with it).
Cheers
If on page 2 you have a PL/SQL process at the process point='On Load - After Header' and the procedure looks something like this:
Begin
select name into :V2_NAME from table where id =:V2_ID;
end;
and no condition on it then it should work.
Check that :V2_NAME does not have Source Type= Always NULL;
Correct me if Im wrong, from what ive understood, only one value is passed from page 1 to page 2 which is V1_ID to V2_ID,right?then on page 2, you have a process that will execute after the header loads and that is
SELECT name
INTO :V2_NAME
FROM TABLE
WHERE id = :V_ID
Is the :V_ID a typo?It should be :V2_ID,if its not maybe its the cause as to why :V2_NAME doesnt give you a value.
Instead of having a process after header, why dont you just put the query in the item SOURCE, choose type: QUERY, used: ALWAYS, REPLACE ANY EXISTING VALUES ON SESSION STATE.
then in the query box, put
SELECT name
FROM TABLE
WHERE id = :V2_ID

VBA Validation List Error

I am pretty new to programming in vba. I need some coding help which I am not able to find the solution after I tried Googling for the solution.
Currently I have a self-defined type called category mapping. The type will be used to contain the items which I want to put as the option for the vadliation list. It looked as below:
Public Type categoryMapping
messageKey As Long
description As String
End Type
An example of the categoryMapping is to store gender codes, 6000 stands for Male, 6001 stands for Female.
Display of the Validation List
As I have all stored them in an array, displaying them is not easy. What I have done is as below:
'Validation drop down list for the whole row
If Has_Elements(mapping) Then
Dim code As String
Dim options() As categoryMapping
code = ""
options = mapping
Dim j As Integer
For j = LBound(options) To UBound(options)
code = code & options(j).messageKey & ": " & options(j).description & ","
Next j
With Range(Rows(7).Address).Validation 'TODO: Need to refactor
.Add Type:=xlValidateList, _
AlertStyle:=xlValidAlertStop, _
Operator:=xlBetween, _
Formula1:=code
.InCellDropdown = True
.InputMessage = "Please choose from of the following"
.ShowInput = True
End With
End If
Is there a easier to display them since I have already put all the item to be displayed into a array? Is it possible that I can call the array directly?
Use Cells
From the code above, it can be seen that I have use the address of the whole row to contain validation list, because actually what I want is the whole row, except the header cells to contain the validation list.
With Cells(7,1).Validation 'TODO: Need to refactor
.Add Type:=xlValidateList, _
AlertStyle:=xlValidAlertStop, _
Operator:=xlBetween, _
Formula1:=code
.InCellDropdown = True
.InputMessage = "Please choose from of the following"
.ShowInput = True
End With
I tried using the code above to do, but it fails. Is there any way to go about doing it?
Combine multiple validation delete
Due to the problem mentioned above, I need to put in the code to delete the validation list for some column, as shown below:
'Delete the unneccessary validation
'TODO: refactor the code so that write in 1 line
Range(Columns(1).Address).Validation.Delete
Range(Columns(2).Address).Validation.Delete
Range(Columns(3).Address).Validation.Delete
Range(Columns(4).Address).Validation.Delete
Is there some ways that I can combine all the delete validation into a single statement?
Display part of the option
As you can see from above, when my users select a option from the validation list, the whole string appear.
For example, if I have "6000: Male" & "6001: Female" as the option, and I choose Male, I wished for "6000" to appear instead of "6000: Male". Is there a way to do it?
Validation data not exist after reopen
After I generate the validation list, I close the program and reopen it, there is a error that say "Excel found unreadable content in 'File name.xls'. Do you want to recover the contents of this workbook? If you trust the source of this workbook, click Yes.
When I click Yes, my excel open but all my validation list is gone! And I got the below as a error message.
-
error064240_01.xml
Errors were detected in file 'file_name'
-
Removed Feature: Data validation from /xl/worksheets/sheet2.xml.part
I was guessing that the error occur because the option in the validation list is not stored in the worksheet, but it is stored in the program memory, hence when I closed the program, the memory is loss.
If my guess is correct, is there any way to go around solving this problem? I was thinking of creating another worksheet that contain all the data in the validation list and let my cell validation list refer to them, but is there better ways to store them in the same worksheet?
The unreadable content is because you are storing the validation list in an array in memory. As soon as you close the worksheet, that array goes out of scope and ceases to exist. Rather than storing it in an array, write the list to a worksheet somewhere, I normally create a new sheet called lists.
The actual validation for your cell won't use VBA. Just create a dynamic named range (using offset() and counta() to populate your validation list from the relevant column in the lists sheet. Then use your VBA code to write the array above to that column. (http://chandoo.org/wp/2010/09/13/dynamic-data-validation-excel/)
As far as displaying different text in the validation and the dropdown, that sounds like more trouble than it is worth. Is it possible to just resize your cell so it only shows the first 4 digits?

How to catch Unique constraint error in POSTGRESQL

If I create a table with a unique contraint, for example:
CREATE TABLE distributors (
did integer,
name varchar(40) UNIQUE
);
What would happen if I try to enter an entry with the name that already exists. I tried to do so and it just quit without displaying any error message. Is there a way to check whether a new entry was actually inserted?
If the insert failed than there should be error code set somewhere, readable by some method of the interface you're using - more details are definitely in documentation to your access library/module.
Alternatively you can change your insert to:
INSERT INTO distributors (did, name) values ( ... ) RETURNING did;
And if it didn't return anything - there has been error.
If you try to insert record with name that already exists, you'll receive error message like this:
ERROR: duplicate key value violates unique constraint "distributors_name_key"
DETAIL: Key (name)=(aaa) already exists.
and record will not be inserted.
If you do it from allplcation level, the exception will be thrown with a message similar to this. It's up to the programmer how to handle this exception.
If your ID field is autogenerating (SERIAL or BIGSERIAL), and you insert just name, if you insert name which already exists, ID sequence will increase by 1, even if you didn't insert any record.
To avoid that you can make "SELECT" query before INSERT to check, it the record already exists. Possible to do all in one transaction, in pseudo-code:
BEGIN TRANSACTION;
int records = SELECT name FROM table WHERE name = 'aaa' FOR UPDATE; //FOR UPDATE to lock the row from being read by other user until transaction finishes.
if (records == 0)
INSERT INTO table VALUES (1, 'aaa');
else
MessageBox.Show("Record already exists");
COMMIT TRANSACCTION;