exec StoredProcedure passing Array of Integers? - voltdb

I have a stored procedure which has .. WHERE something IN ? .....
I can't find any documentation on how to call this procedure using "exec"
I tried all combinations
exec bestThumbs ([[324622 ,321235]]);
Invalid parameter count for procedure: bestThumbs (expected: 1, received: 2)
exec bestThumbs [324622,321235,3454345];
Invalid parameter count for procedure: bestThumbs (expected: 1, received: 3)
exec bestThumbs [[324622 ,321235, 3454345]];
Invalid parameter count for procedure: bestThumbs (expected: 1, received: 3)
Furthermore , trying to do the same in PHP via JSON interface:
$a = array([163195,163199,163196]);
$params = json_encode($a);
$params = urlencode($params);
$querystring = "Procedure=$proc&Parameters=$params";
returns: VOLTDB ERROR: PROCEDURE bestThumbs TYPE ERROR FOR PARAMETER 0: org.voltdb.VoltTypeException: tryScalarMakeCompatible: Unable to match parameter array:int to provided long
What is the proper way of doing this ?
Thanks !

VoltDB's sqlcmd interface and PHP client library do not support array parameters. Some of the other client libraries do.
If you are using a java procedure, you can format an array of numbers as a string and split the string and parse the values within the procedure, then build an int[] or long[] to pass into voltQueueSQL() when calling the SQLStmt.
However, if your procedure's only input was intended to be an array of integers, keep in mind that a concatenated String parameter as I suggested would not allow the procedure to be partitioned. Even if you were using a client library such as python or java that supports array parameters, the procedure cannot be partitioned on a parameter which is an array. This would mean it must be a multi-partition procedure which runs in all of the partitions. It would be more scalable to have a procedure that takes a single parameter value, if you then partitioned the procedure so it runs in only one partition based on that input value. If the client has an array of values to evaluate, you could iterate through the array and make separate calls to the procedure, each one being executed on only one partition.
I work at VoltDB.

Related

variadic function and parametised safe queries

Using node-pg and Postgres 11.
I have a variadic function in postgres
CALL schema.function(('1'),('2'))
In order to prevent sql injection I need to do something like
await client.query('CALL schema.function($1::smallint);', XXX);
Where XXX is what the call uses for substitutions.
The problem is that my function expects a list of records, not an array ('1'),('2').
Has anyone else encountered this?
Stored procedure:
CREATE OR REPLACE PROCEDURE update_events_variadic(
VARIADIC _events_array event_array[]
)
LANGUAGE plpgsql AS
$$
DECLARE
EVENT_RECORD RECORD;
BEGIN
FOR EVENT_RECORD IN
SELECT
metadata,
payload
FROM unnest(_events_array) as t(
metadata,
payload
)
LOOP
INSERT INTO events
(
metadata,
payload
)
VALUES
(
EVENT_RECORD.metadata,
EVENT_RECORD.payload
);
END LOOP;
END;
$$;
If that's the only type of call you expect, variadic is just syntax noise. You can redefine your procedure to accept a regular array of type that fits into table events, that you're trying to insert into. Also, you don't have to loop over a select from unnest() - there's a FORACH loop that lets you iterate over the array directly.
CREATE OR REPLACE PROCEDURE update_events ( events_array events[] )
LANGUAGE plpgsql AS $$
DECLARE
event_record events;
BEGIN
FOREACH event_record IN ARRAY events_array LOOP
INSERT INTO events
( metadata,
payload
)
VALUES
( event_record.metadata,
event_record.payload
);
END LOOP;
END; $$;
You can also insert directly from a select without any loop, gaining some performance from that and from switching to plain language SQL that doesn't have the PLpgSQL overhead:
CREATE OR REPLACE PROCEDURE update_events ( events_array events[] )
LANGUAGE SQL AS $$
INSERT INTO events
( metadata,
payload
)
select
event_record.metadata,
event_record.payload
from unnest(events_array)
as event_record
( metadata,
payload
)
$$;
You can either cast each element in the array, or cast the whole array at once:
await client.query('CALL schema.function( ARRAY[$1]::events[] );', XXX);
Demo. If you happen to initially get a malformed record literal error after these changes, it means that some or all of what you're supplying as XXX isn't a valid set of values that can make up events record. Another demo, showing some examples.
I'm naively assuming your events table has only those two columns - if it has more, you'll have to define an intermediate type or table and use that as your array type in procedure definition and call argument cast. Example
In this case variadic would be useful if you wanted to be able to use both types of calls below:
call update_events( --not an array in a strict sense, just a bunch of arguments
('metadata1','payload1')::events,
('metadata2','payload2')::events,
('metadata3','payload3')::events);
The above isn't passing an array in a strict sense, just a bunch of arguments that the function will internally collect and make available as a single, actual array.
call update_events( VARIADIC
ARRAY[('metadata1','payload1'),
('metadata2','payload2'),
('metadata3','payload3')
]::events[] );
VARIADIC informs the function that nothing has to be collected into an array as the argument list is already provided as one.

Update multiple column with bind variable using cx_Oracle.executemany()

I have been trying to update some columns of a database table using cx_Oracle in Python. I created a list named check_to_process which is a result of another sql query. I am creating log_msg in the program based on success or failure and want to update same in the table only for records in check_to_process list. When I update the table without using bind variable <MESSAGE = %s>, it works fine. But when I try to use bind variable to update the columns it gives me error :
cursor.executemany("UPDATE apps.SLCAP_CITI_PAYMENT_BATCH SET MESSAGE = %s, "
TypeError: an integer is required (got type str)
Below is the code, I am using:
import cx_Oracle
connection = cx_Oracle.connect(user=os.environ['ORA_DB_USR'], password=os.environ['ORA_DB_PWD'], dsn=os.environ['ORA_DSN'])
cursor = connection.cursor()
check_to_process = ['ACHRMUS-20-OCT-2021 00:12:57', 'ACHRMUS-12-OCT-2021 16:12:01']
placeholders = ','.join(":x%d" % i for i,_ in enumerate(check_to_process))
log_msg = 'Success'
cursor.executemany("UPDATE apps.SLCAP_CITI_PAYMENT_BATCH SET MESSAGE = %s, "
"PAYMENT_FILE_CREATED_FLAG='N' "
"WHERE PAYMENT_BATCH_NAME = :1",
[(i,) for i in check_to_process], log_msg, arraydmlrowcounts=True)
Many thanks for suggestions and insights!
Your code has an odd mix of string substitution (the %s) and bind variable placeholders (the :1). And odd code that creates bind variable placeholders that aren't used. Passing log_msg the way you do isn't going to work, since executemany() syntax doesn't support string substitution.
You probably want to use some kind of IN list, as shown in the cx_Oracle documentation Binding Multiple Values to a SQL WHERE IN Clause. Various solutions are shown there, depending on the number of values and frequency that the statement will be re-executed.
Use only bind variables. You should be able to use execute() instead of executemany(). Effectively you would do:
cursor.execute("""UPDATE apps.SLCAP_CITI_PAYMENT_BATCH
SET MESSAGE = :1
WHERE PAYMENT_BATCH_NAME IN (something goes here - see the doc)""",
bind_values)
The bottom line is: read the documentation and review examples like batch_errors.py. If you still have problems, refine your question, correct it, and add more detail.

If i store index number fetched from db in variable & using in select from list by index, m getting err as expected string, int found-Robot Framework

enter image description here
select from list by index ${locator_var} ${inp_msge_type}
--getting error as expected string, int found
select from list by index ${locator_var} 7
-----not getting any error
${inp_msge_type}----contains 7 from DB query the result is stored in this variable, to avoid hard coding we need to do this
Is there any way to write
Do not add links to screenshots of code, or error messages, and format the code pieces accordingly - use the ` (tick) symbol to surround them.
The rant now behind us, your issue is that the keyword Select From List By Index expects the type of the index argument to be a string.
When you called it
Select From List By Index ${locator_var} 7
, that "7" is actually a string (though it looks like a number), because this is what the framework defaults to on any typed text. And so it works.
When you get the value from the DB, it is of the type that the DB stores it with; and probably the table schema says it is int. So now you pass an int to the keyword - and it fails.
The fix is simple - just cast (convert) the variable to a string type:
${inp_msge_type}= Convert To String ${inp_msge_type}
, and now you can call the keyword as you did before.

How to remove milliseconds from utcnow() result in Azure data factory

I want to pass a value for parameter usertime, the value should be like 2020-07-23T13:19:31Z , which will be used in my source connection url.
For this i supplied utcnow() function in the value tab. But i realized utcnow() will return the value as "2018-04-15T13:00:00.0000000Z"
To remove the millisecond part i have used the expression substring(utcnow(),1,20).
and also used expression formatDateTime('utcnow()', 'yyyy-MM-ddTHH:mm:ss').
Both my trails are useless where my expression returning error ass invalid parameter.
Could you please help me how can i supply the value 2020-07-23T13:19:31Z in Azure data factory datasource parameters.
You don't want utcNow inside quotes, here is an example from one of my pipelines using your format:
#formatDateTime(utcnow(), 'yyyy-MM-ddTHH:mm:ss')
which gives this result, setting a variable named x:
{
"name": "x",
"value": "2020-07-24T13:44:42Z"
}
Build it in 'Add dynamic content' as you can pick the functions and it will format properly if you aren't familiar.
Your substring won't work because it requires a string for the first parameter and utcnow is a timestamp.

Hapi/Joi Validation For Number Fails

I am trying to validate number value which will include integer as well as float values. Following is my implementation for the same.
Joi Schema.
const numcheckschema = Joi.object().keys({
v1:Joi.number().empty("").allow(null).default(99999),
v2:Joi.number().empty("").allow(null).default(99999),
v3:Joi.number().empty("").allow(null).default(99999)
})
Object
objnum={
v1:"15",
v2:"13.",
v3:"15"
}
objValidated = Joi.validate(objnum, numcheckschema);
console.log(objValidated);
When i execute the above mentioned code I get an error
ValidationError: child "v2" fails because ["v2" must be a number]
as per the documentation when we tries to pass any numeric value as a string it converts the values to number but here in this case my value is 13. which is not able to convert into number and throwing an error.
Is there any way by which we can convert this value to 13.0
You can use a regex in order to match numbers with a dot, for instance:
Joi.string().regex(/\d{1,2}[\,\.]{1}/)
And then combine both validations using Joi.alternatives:
Joi.alternatives().try([
Joi.number().empty("").allow(null),
Joi.string().regex(/\d{1,2}[\,\.]{1}/)
])
However, I think you may need to convert the payload to number using Number(string value). You need to check the payload type, if it isn't a Number, you need to convert it.
If you want to know more about the regex used in the example, you can test it in here: https://regexr.com/

Resources