create or replace function sppi()
returns VARCHAR
language javascript
as
$$
var A= regexp_replace('Customers - (NY)','\\(|\\)','');
return A;
$$
;
call sppi();
Well your REGEXP is valid from the console/WebUI perspective:
select 'Customers - (NY)' as str, regexp_replace(str,'\\(|\\)','');
STR
REGEXP_REPLACE(STR,'\(|\)','')
Customers - (NY)
Customers - NY
so in javascipt functions you cannot directly call SQL functions, so if we flip to a Snowflake Scripting we can though.
BEGIN
let A := regexp_replace('Customers - (NY)','\\(|\\)','');
RETURN :A;
END;
anonymous block
Customers - NY
where-as if you want to stay in Javasript, lets use a Javascript replace:
create or replace function sppi()
returns VARCHAR
language javascript
as
$$
var A= 'Customers - (NY)'.replace(/\(|\)/g,'');
return A;
$$
;
select sppi();
SPPI()
Customers - NY
Related
I need to snowflake how to create dummy stored procedure with out put array as push in snowflake.
Dummy stored procedure using Snowflake Scripting:
CREATE PROCEDURE dummy()
RETURNS ARRAY
LANGUAGE SQL
AS
$$
begin
return ARRAY_CONSTRUCT(1,2,3);
end;
$$;
CALL dummy();
-- [ 1, 2, 3 ]
DESC RESULT LAST_QUERY_ID();
-- name type kind
-- DUMMY ARRAY COLUMN
Since you mentioned pushing to the array, I'll add to Lukasz's answer to show a JavaScript example with pushing to the array in a loop:
create or replace procedure ARRAY_SAMPLE()
returns array
language javascript
as
$$
var arr = [];
var rs = snowflake.execute({sqlText:'select N_NAME from SNOWFLAKE_SAMPLE_DATA.TPCH_SF1.NATION'});
while (rs.next()) {
arr.push(rs.getColumnValue('N_NAME'));
}
return arr;
$$;
call ARRAY_SAMPLE();
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.');
(Submitting on behalf of a Snowflake User...)
QUESTION:
Is it possible to nest multiple functions inside of a function and pass all the parameters required?
for example...
CREATE OR REPLACE FUNCTION "udf_InteractionIndicator"("ROH_RENEWAL_SYSTEM_STATUS1" VARCHAR(100), "GOLS_OPPORTUNITY_LINE_STATUS" VARCHAR(100)
, "ROH_CLIENT_CURRENT_TEMPERATURE1" VARCHAR(100)
, "ROH_PO_ATTACHED" VARCHAR(100)
, "ROH_PO_NUMBER" VARCHAR(100)
, "RT_PAID_OVERRIDE" VARCHAR(100), "ROH_RENEWAL_OPPORTUNITY_STATUS1" VARCHAR(100)
, "ROH_RENEWAL_CONVERSATION_DATE" DATE, "ROH_APPROVAL_RECEIVED_DATE" DATETIME)
RETURNS NUMBER(1,0)
AS
$$
CASE WHEN ("udf_RenewalNoticeSentIndicator"("ROH_RENEWAL_SYSTEM_STATUS1", "ROH_CLIENT_CURRENT_TEMPERATURE1"
, "GOLS_OPPORTUNITY_LINE_STATUS"
, "ROH_PO_ATTACHED", "RT_PAID_OVERRIDE"
, "ROH_RENEWAL_OPPORTUNITY_STATUS1")) = 1
AND (ROH_RENEWAL_CONVERSATION_DATE IS NOT NULL
OR ("udf_AuthorizedIndicator"(ROH_APPROVAL_RECEIVED_DATE, "ROH_PO_ATTACHED", "ROH_PO_NUMBER")) = 1
OR ("udf_PaidIndicator"("GOLS_OPPORTUNITY_LINE_STATUS")) = 1
OR ("udf_ChurnIndicator"("GOLS_OPPORTUNITY_LINE_STATUS")) = 1
)
THEN 1 ELSE 0 END
$$
;
I've received the recommendation to:
...create a SQL UDF or JavaScript UDF. A JavaScript UDF can only
contain JavaScript code, and an SQL UDF can contain only one SQL
statement (no DML and DDL). In case of nesting, SQL UDF can call
another SQL UDF or a JavaScript UDF but the same is not true with the
JavaScript UDF(it only contains JavaScript code).
CREATE OR REPLACE FUNCTION udf_InteractionIndicator_nested(ID DOUBLE)
RETURNS DOUBLE
AS
$$
SELECT ID
$$;
create or replace function js_factorial(d double)
returns double
language javascript
strict
as '
if (D <= 0) {
return 1;
} else {
var result = 1;
for (var i = 2; i <= D; i++) {
result = result * i;
}
return result;
}
';
CREATE OR REPLACE FUNCTION udf_InteractionIndicator(ID DOUBLE)
RETURNS double
AS
$$
select udf_InteractionIndicator_nested(ID) + js_factorial(ID)
$$;
select udf_InteractionIndicator(4);
+-----------------------------+
| UDF_INTERACTIONINDICATOR(4) |
|-----------------------------|
| 28 |
+-----------------------------+
HOWEVER, I'm trying to accomplish this with a SQL UDF. It makes sense that a nested function can be created as long as they use the same parameter. I'd like to create a function that accepts say 8 parameters and the underlying functions may reference all, some or none of the parent function parameters. That is where I run into an issue... THOUGHTS?
(A consultant in our community offered the following answer...)
With a JavaScript UDF the design will be much more compact and maintainable, if your use case is that there is a "main" function that breaks down work into subfunctions which will only be invoked from main.
Then you simply define all underlying functions within the main function, which is possible with JavaScript but not with an SQL UDF, and then you are free to use the main parameters everywhere.
CREATE OR REPLACE FUNCTION MAIN_JS(P1 STRING, P2 FLOAT)
RETURNS FLOAT
LANGUAGE JAVASCRIPT
AS '
function helper_1(p) { return p * 2; }
function helper_2(p) { return p == "triple" ? P2 * 3 : P2; }
return helper_1(P2) + helper_2(P1);
';
SELECT MAIN_JS('triple', 4); -- => 20
I am using pgv10. The function that I need seems this wrong function:
CREATE FUNCTION array_coalesce(ANYARRAY) RETURNS ANYARRAY AS $f$
SELECT CASE WHEN $1 IS NULL THEN array[]::ANYARRAY ELSE $1 END;
$f$ language SQL IMMUTABLE;
Curiosity
... I started to simplify a complex problem, and arrives in the test select coalesce(null::text[], array[]::text[]) that not worked... So it was a good question, how to implement it? But sorry, I do something workng, COALESCE(array,array) is working fine (phew!).
So, "coalesce problem" is merely illustrative/didatic. What I really want to understand here is: How to use ANYARRAY?
PS: other curiosity, the string concat(), || and other concatenation operators in PostgreSQL do some "coalescing",
select concat(NULL::text, 'Hello', NULL::text); -- 'Hello'
select null::text[] || array[]::text[]; -- []
select array[]::text[] || null::text[]; -- []
How to use anyarray?
It's an interesting issue, in the context of the usage described in the question. The only way I know is to use an argument as a variable. It's possible in plpgsql (not in plain sql) function:
create or replace function array_coalesce(anyarray)
returns anyarray as $f$
begin
if $1 is null then
select '{}' into $1;
end if;
return $1;
end
$f$ language plpgsql immutable;
select array_coalesce(null::int[]);
array_coalesce
----------------
{}
(1 row)
By the way, you can simply use coalesce() for arrays:
select coalesce(null::text[], '{}'::text[]);
coalesce
----------
{}
(1 row)
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.