I want to pass a table as parameter on an ajax callback procedure in Oracle APEX 5, because I need to make an SQL query on that table.
The SQL process is stored as shared component inside the Apex 5 application. Screenshot
My procedure is like this
(procedure name: THIS_PROCESS)
declare
v_tablename varchar(128);--max table_name lenght
v_ID number;
v_somevar
BEGIN
SELECT Columname,
INTO v_somevar
FROM v_tablename
WHERE ID = v_ID;
--Do stuff
END;
This code (FROM v_tablename) gives me a compilation error:
ORA-00942: table or view does not exist ORA-06550: line 9, column 5:
PL/SQL: SQL Statement ignored
I'm a total newbie. I had been reading that I should call that procedure with this javascript:
apex.server.process ( "THIS_PROCESS", {
x01: "TABLENAME",
x02: "Row_ID",
pageItems: "#P1_Item,#P2_Item"
},{
success: function( pData )
// do something here
}
} );
I do not understand why I should pass x01 and x02 instead of v_tablename and v_ID
Do x01 and x02 automatically are assigned to v_tablename and v_ID?
Here's an example page process THIS_PROCESS of type "Ajax Callback". Note that you need Dynamic SQL to select from a table name that isn't hardcoded.
declare
v_table varchar2(128) := apex_application.g_x01;
v_id number := apex_application.g_x02;
v_somevar varchar2(100);
v_sql varchar2(4000);
begin
-- validate v_table parameter to avoid sql injection. will throw exception if it fails
select table_name into v_table from all_tables where table_name = v_table;
v_sql := 'SELECT Columname
FROM ' || v_table || '
WHERE ID = :A1';
execute immediate v_sql into v_somevar using v_id;
-- do something with v_somevar
end;
Do be careful with this sort of thing - this design will allow a malicious user to write their own javascript function which can pass any table name that it likes to your procedure.
You need to use dynamic sql:
declare
v_tablename varchar(128);--max table_name lenght
v_sql varchar2(1000);
v_ID number;
v_somevar varchar2(100);
BEGIN
v_sql := 'SELECT Columname FROM ' || v_tablename || ' where ID = :1';
EXECUTE IMMEDIATE v_sql INTO v_somevar USING v_ID;
--Do stuff
END;
/
Related
I'm building a backend for my food application, and I need to create columns in food table and INSERT rows in nutrients table. I'm constructing a query, there are ~60 nutrients in every food, and there are hundreds of different nutrient types.
I used one of answers from MySQL: ALTER TABLE if column not exists as my template
for (let i = 0; i < food.nutrients.length; i++) {
createColumnsString += `
DROP PROCEDURE IF EXISTS \`create_column\`;
DELIMITER //
CREATE PROCEDURE \`create_column\`()
BEGIN
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN END;
ALTER TABLE \`food\` ADD COLUMN \`${food.nutrients[i].nutrientNumber}\` VARCHAR(45);
INSERT INTO \`nutrients\` (nutrientid, nutrientname, unit) VALUES ("${food.nutrients[i].nutrientNumber}", "${food.nutrients[i].nutrientName}", "${food.nutrients[i].unitName}");
END //
DELIMITER ;
CALL \`create_column\`();
DROP PROCEDURE \`create_column\`; `;
}
console.log(createColumnsString);
db.query(createColumnsString);
the console.log(createColumnsString) for each nutrient prints this in Node console:
DROP PROCEDURE IF EXISTS `create_column`;
DELIMITER //
CREATE PROCEDURE `create_column`()
BEGIN
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN END;
ALTER TABLE `food` ADD COLUMN `269.3` VARCHAR(45);
INSERT INTO `nutrients` (nutrientid, nutrientname, unit) VALUES ("269.3", "Sugars, Total NLEA", "G");
END //
DELIMITER ;
CALL `create_column`();
DROP PROCEDURE `create_column`;
And it works when i paste it to MySQL Workbench. I can put all ~60 queries one after another and it does what it's supposed to do.
On the other hand, db.query(createColumnsString) gives me this:
code: 'ER_PARSE_ERROR',
errno: 1064,
sqlMessage: "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DELIMITER //\n" +
'CREATE PROCEDURE `create_column`()\n' +
'BEGIN\n' +
"DECLARE CONTINUE HANDLER F' at line 1",
sqlState: '42000',
index: 1,
sql: '\n' +
'DROP PROCEDURE IF EXISTS `create_column`; \n' +
'DELIMITER //\n' +
'CREATE PROCEDURE `create_column`()\n' +
'BEGIN\n' +
'DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN END;\n' +
'ALTER TABLE `food` ADD COLUMN `303` VARCHAR(45); \n' +
'INSERT INTO `nutrients` (nutrientid, nutrientname, unit) VALUES ("303", "Iron, Fe", "MG"); \n' +
'END // \n' +
'DELIMITER ; \n' +
'CALL `create_column`(); \n' +
'DROP PROCEDURE `create_column`; \n' +
'DROP PROCEDURE IF EXISTS `create_column`; \n' +
I'm using mysql library for connection. Does it even permit the use of DELIMITER? What am I doing wrong?
Create a Food table to contain the food info.
id int
name varchar(30)
... etc etc
Create a Nutriets table to hold nutrient info.
id int
nutrientname varchar(30)
unit _not sure of type_
. . . etc etc
Then as many foods will have the same nutrients in them you need a xref table, or link table to connect them
That table is simply something like this
food_nutrients table
id int
food_id int
nutrient_id int
Nowyou can link any food to any nutrient, all you need is either the id of the food or the id of the nutrient to be able to list all a foods nutrients, or all the food that contain any nutrient.
I wonder if is there equivalent of try catch blocks from javascript in pure sql in Snowflake.
I want to have a procedure which will check all views and does something with them, but some of the views are invalid and these I want to skip.
The javascript version looks like this:
create or replace procedure test_views_js()
returns varchar
language javascript
as
$$
var sql = "select table_name from INFORMATION_SCHEMA.views where table_schema='TEST'";
var stmt = snowflake.createStatement ({sqlText:sql});
var result_set = stmt.execute();
var cnt = 0;
while (result_set.next()){
try{
var sql_text = "select count(*) from "+result_set.getColumnValue(1);
var stmt2 = snowflake.createStatement ({sqlText:sql_text});
var r = stmt2.execute();
r.next();
cnt+=r.getColumnValue(1);
}catch (err){
continue
}
}
return cnt;
$$
Can I achieve the same result with sql?
UPDATE
I am getting syntax error when I try to put exception in the loop. When it's in different place syntax is valid, but 'break is outside of loop' . Maybe there is some obvious typo that i cannot see?
create or replace procedure test_views()
returns integer not null
language sql
as
declare
sel varchar;
row_cnt integer default 0;
res resultset default
(select table_name
from INFORMATION_SCHEMA.views
where table_schema='TEST') ;
c1 cursor for res;
begin
for row_variable in c1 do
row_cnt:= (select count(*) from view_test);
exception when statement_error then continue;
end for;
return row_cnt;
end;
It is possible to use exception inside loop. Instead of:
for row_variable in c1 do
row_cnt:= (select count(*) from view_test);
exception when statement_error then continue;
end for;
All the code should be wrapped with its own BEGIN EXCEPTION END block.
create or replace procedure test_views()
returns integer not null
language sql
as
declare
sel varchar;
row_cnt integer default 0;
res resultset default
(select table_name
from INFORMATION_SCHEMA.views
where table_schema='TEST') ;
c1 cursor for res;
begin
for row_variable in c1 do
begin
row_cnt:= (select count(*) from view_test);
exception when other then continue;
end;
end for;
return row_cnt;
end;
Yes - there RAISE/EXCEPTION constructs documented here: https://docs.snowflake.com/en/developer-guide/snowflake-scripting/exceptions.html
I have a dynamic string that is generated like one of the following:
var q = "FROM Table SELECT avg(1), avg(2), avg(3) where x='y'
var q = "SELECT avg(1), avg(2), avg(3) FROM Table where z='x' since x days ago
The values after the select are also dynamic where there could be 1 select option, or 10. I'm trying to create some logic to always pluck whatever is selected into an array, but having trouble dealing with the dynamic nature (string being constructed dynamically AND the # of selects being dynamic).
Basically, end result something like this:
['avg(1)', 'avg(2)', 'avg(3)']
Currently I'm doing something like the following, but it always expects the string to be formatted in a certain order (always starting with SELECT and where after the fields to pluck):
let splitQ = q.match(".*SELECT(.*)where");
let selects = splitQ[1].trim().split(",");
Here is a working solution.
It makes these assumptions about the query (after lowercased).
the values come after the first instance of the word 'select '
if the query starts with 'from', values end before the first instance of ' where'
if the query starts with 'select', values end before the first instance of ' from'
const test1 = "FROM Table SELECT avg(1), avg(2), avg(3) where x='y'";
const test2 = "SELECT avg(1), avg(2), avg(3) FROM Table where z='x' since x days ago";
function extractValues(query) {
// in both scenarios, the values always come directly after 'select '
const valuesComeAfterMe = 'select ';
query = query.toLowerCase();
let valuesEndBeforeMe;
// conditionally handle both query syntaxes
if (query.startsWith('from')) {
valuesEndBeforeMe = ' where';
} else if (query.startsWith('select')) {
valuesEndBeforeMe = ' from';
} else {
throw Error('query not handled');
}
// remove start
query = query.slice(query.indexOf(valuesComeAfterMe) + valuesComeAfterMe.length);
// remove end
query = query.slice(0, query.indexOf(valuesEndBeforeMe));
// split values and trim whitespace
return query.split(',').map(item => item.trim());
}
console.log(extractValues(test1));
console.log(extractValues(test2));
In my Cordova app, I need to query a SQLite database and select rows where the value of the column EventName contains a substring. I want to be able to use ? to hold values to avoid SQL injection. I tried this query:
SELECT * FROM EventName WHERE 1 = 1 AND lower(EventName) LIKE lower('%?%');
This is my JavaScript code that I use to query the database:
function searchEvent(onSearch, eventName) {
// First create the query string
var params = [];
var query = "SELECT * FROM Event WHERE 1 = 1";
if (eventName != null && eventName != "") {
query += " AND lower(EventName) LIKE lower('%?%')";
params.push(eventName);
}
query += ";";
console.log(query); // Log the query
console.log(params); // Log the parameters
// Then execute query statement
db.transaction(function(tx) {
tx.executeSql(query, params, function(tx, rs) {
onSearch(rs);
});
}, function(err) {
console.log(err); // This statement was executed
});
}
This is the logged query:
SELECT * FROM Event WHERE 1 = 1 AND lower(EventName) LIKE
lower('%?%');
This is the logged paramaters:
[ 'myInput' ]
This is the error the was returned:
{
code: 5,
messsage: 'number of \'?\'s in statement string does not match argument count'
}
As you can see there is 1 ? placeholder and 1 input parameter so the numbers DO match. I think it is because the ? is between the single quotes ('') so it is thought to be a part of the searched string. How do I fix this?
EDIT:
The JavaScript statement "SELECT * FROM Event WHERE 1 = 1" + " AND lower(EventName) LIKE lower('%" + eventName + "%')" is ok, but I wanna use a method that can protect me against SQL injection
In order to prevent the eventName from SQL injection, check it with regEx validation to include only alphanumneric and whitelist specific special characters /^[ A-Za-z0-9_#./#&+-]*$/. Also try this regEx /^[a-zA-Z0-9!##\$%\^\&*)(+=._-]+$/g. I do not believe that ? would work with SQL SELECT statement, so you need to pass +eventname+
Hope it helps.
So I've been having trouble getting this Java code working properly. I'm trying to use a "find" function to locate a specific set of information from an access database file (.accdb) and either display or delete it.
I'm not having much luck. Which is puzzling, considering as far as I can tell, the "find" code is also needed for adding new entries to the database, and I can do that without problems. I suspect I've mislabeled something somewhere, frankly.
Below is a snippet of my code, which also shows an example of looking for a specific file identified by the "key" which a user writes into a textbox. Help in solving this annoyance would be appreciated.
// Implement the four instance methods *************
// addNew, delete, update - called from each specific PD class
// find - used locally by addNew(), delete(), and update().
/**
* An instance method to find a record in the database.<br /><br />
*
* This is a private method and can only be used locally within objects instantiated from this class.<br />
* Used by addNew(), delete(), and update().
*
* #param String objectType
* #param String key
* #return a Anime object
* #throws NotFoundException
* #throws SQLException
*/
private Anime find (String objectType, String key) throws NotFoundException, SQLException {
anAnime = null;
if (objectType.equalsIgnoreCase ("PlushToys")) {
// define the SQL query statement using the phone number key
String sqlQuery = "SELECT Anime, Character, Ability, CanItSpeak, Material, Size " +
"FROM PlushToys" +
"Where Character = '" + key +"'";
// execute the SQL query statement
ResultSet rs = aStatement.executeQuery (sqlQuery);
// next method sets cursor & returns true if there is data
boolean gotIt = rs.next();
if (gotIt) {
// extract the data
String Anime = rs.getString(1);
String Character = rs.getString (2);
String Ability = rs.getString (3);
String CanItSpeak = rs.getString (4);
String Material = rs.getString (5);
String Size = rs.getString (6);
// create PlushToy instance & add it to the ArrayList
anAnime = new PlushToys (Anime, Character, Ability, CanItSpeak, Material, Size);
rs.close();
} else {
// nothing was retrieved
rs.close();
throw (new NotFoundException ("not found "));
}
After the SQL-query you need to ask whether the result is empty or not. Therefor you have to count the number of rows in the result. You can do that with something like this:
ResultSet rs;
...
int count = 0;
while(rs.next()) {
count++;
}
And when count is zero, than you have no entries in the result and the thing the user is searching for is not in the database.