S3 credentials in COPY INTO not working with session variables - snowflake-cloud-data-platform

Running this in a worksheet I'm expecting the data to be loaded into my_table:
SET AWS_ACCESS_KEY_ID='XXXX';
SET AWS_SECRET_ACCESS_KEY='XXXX';
COPY INTO my_table (name,dog_name) FROM s3://my-bucket/*.csv
credentials=(aws_key_id='$AWS_ACCESS_KEY_ID' aws_secret_key='$AWS_SECRET_ACCESS_KEY')
file_format=(type=csv field_delimiter=',' empty_field_as_null=true compression=gzip
field_optionally_enclosed_by='"');
But I'm getting Failure using stage area. Cause: [The AWS Access Key Id you provided is not valid.].
Replacing the variables directly with value is working, am I missing something?

The variables in the SET command here is a SQL variable which can't be passed as an option inside a COPY INTO command dynamically, you can create a Stored Proc to use for this scenario, try using the following:
create or replace procedure execute_copy_into(AWS_ACCESS_KEY_ID STRING,AWS_SECRET_ACCESS_KEY STRING)
returns string
language javascript
execute as CALLER
as
$$
var sql_command="COPY INTO my_table (name,dog_name) FROM s3://my-bucket/*.csv \
credentials=(aws_key_id='"+AWS_ACCESS_KEY_ID+"' aws_secret_key='"+AWS_SECRET_ACCESS_KEY+"') \
file_format=(type=csv field_delimiter=',' empty_field_as_null=true compression=gzip \
field_optionally_enclosed_by='"'); "
var stmt = snowflake.createStatement(
{
sqlText: sql_command
}
);
var rs = stmt.execute();
// Looping to return all files processed
while (rs.next()) {
result += rs.getColumnValue(1) + "\n";
}
return result;
$$
;
// Now call the stored proc
SET AWS_ACCESS_KEY_ID='XXXX';
SET AWS_SECRET_ACCESS_KEY='XXXX';
CALL execute_copy_into($AWS_ACCESS_KEY_ID,$AWS_SECRET_ACCESS_KEY);
// Or just eliminate the variables and call it without
CALL execute_copy_into('XXXX','XXXX');

Related

Assign result from stored procedure to a variable

I have created a stored procedure that returns a create table sql statement; I want to be able to now call that procedure and assign the result to a variable like:
set create_table_statement = call sp_create_stage_table(target_db, table_name);
snowflake will not let me do this, so is there a way I can.
Context
We have just been handed over our new MDP which is built on AWS-S3, DBT & Snowflake, next week we go into production but we have 200+ tables and snowlpipes to code out. I wanted to semi automate this by generating the create table statements based off the tables metadata and then calling the results from that to create the tables. At the moment we're having to run the SQL, copy+paste the results in and then run that, which is fine in dev/pre-production mode when it's a handful of tables. but with just 2 of us it will be a lot of work to get all those tables and pipes created.
so I've found a work around, by creating a second procedure and calling the first one as a se=ql string to get the results as a string - then calling that string as a sql statement. like:
create or replace procedure sp_create_stage_table("db_name" string, "table_name" string)
returns string
language javascript
as
$$
var sql_string = "call sp_get_create_table_statement('" + db_name + "','" + table_name + "');";
var get_sql_query = snowflake.createStatement({sqlText: sql_string});
var get_result_set = get_sql_query.execute();
get_result_set.next();
var get_query_value = get_result_set.getColumnValue(1);
sql_string = get_query_value.toString();
try {
var main_sql_query = snowflake.createStatement({sqlText: sql_string});
main_sql_query.execute();
return "Stage Table " + table_name + " Successfully created in " + db_name + " database."
}
catch (err){
return "an error occured! \n error_code: " + err.code + "\n error_state: " + err.state + "\n error_message: " + err.message;
}
$$;
It is possible to assign scalar result of stored procedure to session variable. Instead:
SET var = CALL sp();
The pattern is:
SET var = (SELECT * FROM TABLE(RESULT_SCAN(LAST_QUERY_ID())));
Sample:
CREATE OR REPLACE PROCEDURE TEST()
RETURNS VARCHAR
LANGUAGE SQL
AS
BEGIN
RETURN 'Result from stored procedrue';
END;
CALL TEST();
SET variable = (SELECT * FROM TABLE(RESULT_SCAN(LAST_QUERY_ID())));
SELECT $variable;
-- Result from stored procedrue

Snowflake Syntax when passing array into insert query

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();

Query `list stage` in Snowflake UDF

I am trying to write a Snowflake UDF that accepts a stage name and specific folder name as input parameters and returns the latest file id ( striping from full file name) as the output. Could anyone help me with a simple code to achieve this?
I'm not sure if you want a UDF or stored procedure. The syntax to create would be similar so I think this can help. Here is a stored procedure which will fetch latest staged file from a given stage and path. Just be aware of the limit 1 in query, multiple staged files may share the same last modified date while this procedure returns a scalar (single) value.
Stored Procedure Definition
create or replace procedure "MYDB"."MYSCHEMA"."LATEST_STAGED_FILE"(stage_name text, folder text)
returns string not null
language javascript
execute as caller
as
$$
var sql_text = "list #" + STAGE_NAME + "/" + FOLDER ;
var sql_command0 = snowflake.createStatement({ sqlText: sql_text});
var sql_command1 = snowflake.createStatement({ sqlText:`SELECT "name" FROM table(result_scan(last_query_id())) WHERE "last_modified" = (select MAX("last_modified") from table(result_scan(last_query_id()))) LIMIT 1;`});
try {
sql_command0.execute();
var resultSet = sql_command1.execute();
while(resultSet.next())
{
var resultFile = resultSet.getColumnValue('name').split("/")
return resultFile[resultFile.length - 1]
}
}
catch (err) {
return "Failed: " + err;
}
$$;
You can then call the stored procedure like
call "MYDB"."MYSCHEMA"."LATEST_STAGED_FILE"('MYDB.MYSCHEMA.MYSTAGE', 'mypath/myotherpath');
References
select from list #
list stage via SP

Dynamic file paths for Snowflake stages

I am copying data from a Snowflake table into an S3 external stage:
COPY INTO '#my_stage/my_folder/my_file.csv.gz' FROM (
SELECT *
FROM my_table
)
However this code runs daily and I don't want to overwrite my_file.csv.gz but rather keep all the historical versions. However I haven't found a way to create dynamic paths:
SET stage_name=CONCAT('#my_stage/my_folder/my_file', '_date.csv.gz');
COPY INTO $stage_name FROM (
SELECT *
FROM my_table
);
COPY INTO IDENTIFIER($stage_name) FROM (
SELECT *
FROM my_table
);
None of the later 2 queries work!
My question: How can I create dynamic Stage paths in Snowflake? Thanks
Here's a stored procedure you can use and modify. Note that the line with the comment to modify your copy into statement uses backticks instead of single or double quotes. In JavaScript, that allows use of single or double quotes in the string, multi-line constants, and replacement tokens in the form ${variable_name}
create or replace procedure COPY_TO_STAGE(PATH string)
returns variant
language javascript
as
$$
class Query{
constructor(statement){
this.statement = statement;
}
}
// Start of main function
var out = {};
// Change your copy into statement here.
var q = getQuery(`copy into '${PATH}' from (select * from my_table);`);
if (q.resultSet.next()) {
out["rows_unloaded"] = q.resultSet.getColumnValue("rows_unloaded");
out["input_bytes"] = q.resultSet.getColumnValue("input_bytes");
out["output_bytes"] = q.resultSet.getColumnValue("output_bytes");
} else {
out["Error"] = "Unknown error";
}
return out;
// End of main function
function getQuery(sql){
cmd1 = {sqlText: sql};
var query = new Query(snowflake.createStatement(cmd1));
query.resultSet = query.statement.execute();
return query;
}
$$;
Once you define it, you can use SQL variables as the input if you want:
SET stage_name=CONCAT('#my_stage/my_folder/my_file', '_date.csv.gz');
call copy_to_stage($stage_name);
This won't work. Unfortunately using variables for identifiers does not work for stages. You might need to create a Stored procedure with Dynamic SQL:
https://docs.snowflake.com/en/sql-reference/stored-procedures-usage.html#label-example-of-dynamic-sql-in-stored-procedure
So you can just call this procedure every day or generating a SP with several parameters for the path (Stage), the query which will be executed and the target filename.

How to assign a sql query result to a variable in stored procedure

I am trying to assign a simple sql query result in stored procedure to a variable which I will use it later in another sql statement. If I execute below statement with out putting in procedure it works but not with stored procedure, I get an error while calling sp , Can someone please help me here ?
Procedure code:
$$
VAR NAME = 'ABC'
SET (COUNT_VALUE) = (SELECT COUNT(*) FROM COUNT_TABLE)
With out Procedure below code works
SET (COUNT_VALUE) = (SELECT COUNT(*) FROM COUNT_TABLE)
select $COUNT_VALUE
Thanks
Here's an example that uses a simple helper function to return a result set from a query. You can read the Snowflake docs to see what the result set API has in it. The getResultSet function returns a variable that has the results of the query. Since the SQL is a count, you need to use rs.next() once to get to the first row and then read the value in the aliased count(*). You could also use rs.getColumnValue(1) to get a column by ordinal position, but I recommend using column names with SQL aliases is necessary.
create or replace procedure COUNT_EXAMPLE()
returns string
language javascript
as
$$
var rowCount = 0;
var sql = "select count(*) as ROW_COUNT from SNOWFLAKE_SAMPLE_DATA.TPCH_SF10000.ORDERS;";
try {
var rs = getResultSet(sql);
if (rs.next()) {
var rowCount = rs.getColumnValue("ROW_COUNT");
} else {
return "Error: Count query failed.";
}
}
catch(err) {
return "Error: " + err.message;
}
return "The table has " + rowCount + " rows.";
//--------------------------- End of main function ---------------------------
function getResultSet(sql){
cmd1 = {sqlText: sql};
stmt = snowflake.createStatement(cmd1);
var rs;
rs = stmt.execute();
return rs;
}
$$;
call COUNT_EXAMPLE();

Resources