I've been going through the test files of https://github.com/DATA-DOG/go-sqlmock to figure out how to create a stored procedure for mocking purposes. I have:
_, err = db.Exec(`
CREATE OR REPLACE FUNCTION val() RETURNS INT AS
$$ SELECT 1; $$
LANGUAGE sql;
`)
if err != nil {
t.Fatal(err)
}
I get:
all expectations were already fulfilled, call to exec 'CREATE OR REPLACE FUNCTION val() RETURNS INT AS $$ SELECT 1; $$ LANGUAGE sql;' query with args [] was not expected
If, instead, I try it with
mock.ExpectExec(`
CREATE OR REPLACE FUNCTION val() RETURNS INT AS
$$ SELECT 1; $$
LANGUAGE sql;
`,
).WillReturnResult(sqlmock.NewResult(0, 0))
if err := mock.ExpectationsWereMet(); err != nil {
t.Fatal(err)
}
I get:
there is a remaining expectation which was not matched: ExpectedExec => expecting Exec which:
- matches sql: 'CREATE OR REPLACE FUNCTION val() RETURNS INT AS $$ SELECT 1; $$ LANGUAGE sql;'
- is without arguments
- should return Result having:
LastInsertId: 0
RowsAffected: 0
I am really confused on how to setup a basic stored procedure.
The sqlmock library works pretty well for this.
But please note that the ExpectExec receives a regular expression in order to match:
// ExpectExec expects Exec() to be called with sql query
// which match sqlRegexStr given regexp.
// the *ExpectedExec allows to mock database response
ExpectExec(sqlRegexStr string) *ExpectedExec
You are sending that function the exact string you expect to receive without any escaping.
To escape the string, add this:
import (
"regexp"
)
And then when adding the expectation, escape your string (note the regexp.QuoteMeta):
mock.ExpectExec(regexp.QuoteMeta(`
CREATE OR REPLACE FUNCTION val() RETURNS INT AS
$$
SELECT 1;
$$
LANGUAGE sql;
`),
).WillReturnResult(sqlmock.NewResult(0, 0))
That way, the escaped regexp will match your exec command.
Related
I proceed to specify my question and the solution I gave to the problem, for the benefit of the community.
I was trying to perform a multi-column insert using the identifier with a function.
For which, I was getting an error, my code was the following:
CREATE OR REPLACE FUNCTION acc.asignar_periodo(ids NUMERIC[], periodo INTEGER,codigo_subdiario VARCHAR)
RETURNS void
VOLATILE
AS
$$
DECLARE
cant_registros integer:= 0;
BEGIN
cant_registros := array_length(ids,1);
FOR i IN 1..cant_registros LOOP
EXECUTE'UPDATE '||$3||' SET periodo_tributario = $2 WHERE id = ids[i]';
END LOOP;
END;
$$ LANGUAGE plpgsql;
and my query is:
SELECT acc.asignar_periodo('{2291,2292,2293,2294,2295,2296,2297,2298,2299,2300,2301,2302}'::NUMERIC[],201612,'_08');
My solution was the following:
CREATE OR REPLACE FUNCTION acc.asignar_periodo(INTEGER[],INTEGER,INTEGER) RETURNS text VOLATILE AS
$$
DECLARE
qty integer:= array_length($1,1);
respuesta varchar := null;
BEGIN
FOR i IN 1..qty LOOP
EXECUTE'UPDATE _'||$3||' SET periodo_tributario = '||$2||' WHERE id = '||$1[i];
END LOOP;
respuesta := 'Periodo '||$2||' asignado a '||qty||' comprobantes del subdiario '||$3;
RETURN respuesta;
END;
$$ LANGUAGE plpgsql;
Note the correction, since when using EXECUTE it is necessary that the arguments escape the statements
There is no to loop needed to process the array. Postgres will process the entry array at once. After all set processing is what SQL is all about. Get into the mindset that whenever you write loop, likely incorrect and much slower. (Yes there occasions where it is necessary, but very few.) So: (see demo)
create or replace function asignar_periodo(ids numeric[], periodo integer,codigo_subdiario varchar)
returns void
language plpgsql
as $$
declare
stmt constant text = 'update %I set periodo_tributario = %s where id = any (''%s'')';
torun text;
begin
--torun = format(stmt, $3, $2, $1); -- this would work but
torun = format(stmt, codigo_subdiario, periodo, ids); -- I perfer parameter names to position reference
raise notice '%', torun;
execute torun;
end ;
$$;
I tried substring method but it is not working and giving me this error.
create or replace procedure
dsa ()
returns varchar
language javascript
$$
var b="{asdfasdf}"
var c=b.substring(1);
return c;
$$;
call dsa();
SQL compilation error: syntax error line 5 at position 0 unexpected '$$ var b="{asdfasdf}" var c=b.substring(1); return c; $$'.
You're missing the keyword AS in your stored procedure definition. You also need to define two parameters on the overloaded JavaScript function for substring. Finally, are you sure you want to use a stored procedure for this instead of a UDF or just SQL?
create or replace procedure
dsa ()
returns varchar
language javascript
as
$$
var b="{asdfasdf}"
var c=b.substring(0,1);
return c;
$$;
call dsa();
My code obtains the column list of a table I have created
the attributes of this table are the contextual values of a session in snowflake
such as USER,DATABASE,WAREHOUSE...ETC
Afterwards it places those attribute names into an array
I then try to call on these names in making an insert query and this is where I am struggling with the syntax
Because each value in my array is USER, DATABASE,WAREHOUSE
I am trying to call on the context functions
like CURRENT_WAREHOUSE()
Can someone please help me with the syntax
for(i=0;i<arr.length;i++){
v_sqlCode = `INSERT INTO SESSION_ATTRIBUTES( arr[i] )
"VALUES ("CALL CURRENT_"+arr[i]+"()")';
}
You can't directly use the output of a Snowflake stored procedure programmatically. If you need to use the output value, you have to collect it using the RESULT_SCAN table function. That can get a bit complex to handle, directly in code, so it's far better to place it into a helper function. See this example of one SP calling another one and using its output value. It does this by calling the SP using the callSP helper function. Use the entire SQL statement including the call command into the SQL parameter for that function. It will run the SP, grab the result from the result_scan table function and return it.
create or replace procedure HELLO_WORLD()
returns string
language javascript
as
$$
return "Hello, world.";
$$;
create or replace procedure CALL_HELLO_WORLD()
returns string
language javascript
execute as caller
as
$$
return callSP(`call HELLO_WORLD()`);
// ---- Main function above, helper functions below.
function callSP(sql){
let cmd = {sqlText: sql};
let stmt = snowflake.createStatement(cmd);
stmt.execute();
let result_scan = `select $1 from table(result_scan(last_query_id()));`;
let result_cmd = {sqlText: result_scan};
let result_stmt = snowflake.createStatement(result_cmd);
let rs = stmt.execute();
if(rs.next()) {
return rs.getColumnValue(1);
} else {
return null;
}
}
$$;
call call_hello_world();
I'm constructing a snowflake stored procedure and I'm facing difficulty in using the passed argument in the snowflake procedure.
create or replace procedure dumper(n float)
returns float
language javascript
execute as caller
as
$$
var text = "select file_name from table(information_schema.COPY_HISTORY(TABLE_NAME=> 'records', start_time=> dateadd(hours, ?, current_timestamp())));";
var statement = snowflake.createStatement({sqlText: text, binds: [n]});
var result = statement.execute();
return statement.getRowCount();
$$
;
attempting to call the above procedure
call dumper(-2);
result in the following error
JavaScript execution error: Uncaught ReferenceError: n is not defined in DUMPER at ' var statement = snowflake.createStatement({sqlText: text, binds: [n]});' position 70 stackstrace: DUMPER line: 4
I tried using the interpolation one discussed over here but that too had no success.
Any clue on how to work with passed argument.
You have to capitalize "N" in your JavaScript code:
var statement = snowflake.createStatement({sqlText: text, binds: [N]});
Variables passed into Snowflake stored procedures behave like other object names until they're inside the JavaScript. If they're not double quoted, then Snowflake implicitly capitalizes them. Remember to uppercase all parameters passed into SPs and UDFs in Snowflake. Variables defined inside the SP or UDF using JavaScript follow the normal rules for the language.
Since the regular rules apply to Snowflake identifiers as they do to variables passed into procedures and functions, you can double quote parameters if you want to use lower or mixed case variable names:
create or replace function echo_string("n" string)
returns string
language javascript
as
$$
return n; // This works because "n" is double quoted in the signature
$$;
select echo_string('Hello world.');
I'm using PostgreSQL 9.1.3 and the following functions:
CREATE OR REPLACE FUNCTION cad(INOUT args text[], OUT retval int4) AS $cad$
BEGIN
retval := 0;
RAISE NOTICE 'cad: %', args;
END;
$cad$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION dodo(in_args text[]) RETURNS text[] AS $dodo$
DECLARE
_res text[];
_rv int4;
BEGIN
_res := in_args;
EXECUTE 'SELECT cad($1)' USING _res INTO _res, _rv;
RETURN _res;
END;
$dodo$ LANGUAGE plpgsql;
When I call cad directly, I get expected output:
psql$ select cad(ARRAY['Quiz']);
NOTICE: cad: {Quiz}
-[ RECORD 1 ]---
cad | ({Quiz},0)
Time: 0,319 ms
My expected result for the dodo(ARRAY['Quiz']) call is the input array without changes. But instead I receive the following error:
psql$ select dodo(ARRAY['Quiz']);
NOTICE: cad: {Quiz}CONTEXT: SQL statement "SELECT cad($1)"
PL/pgSQL function "dodo" line 8 at EXECUTE statement
ERROR: array value must start with "{" or dimension information
CONTEXT: PL/pgSQL function "dodo" line 8 at EXECUTE statement
What is wrong here?
P.S.: I have to use EXECUTE as function to call will vary, code simplified for the purpose of question.
You want something like:
EXECUTE 'SELECT * FROM cad($1)' USING _res INTO _res, _rv;
The return type isn't two columns of text[],int it's a record of (text[],int) which needs unwrapping.