I tried to automate json views in snowflake. But it displays error in the regex - snowflake-cloud-data-platform

I tried this approach and it is displaying an error that the regex has a missing statement. The approach that I am trying is from the Automating Snowflake’s Semi-Structured JSON Data Handling
CREATE OR REPLACE PROCEDURE create_view_over_json (TABLE_NAME varchar, COL_NAME varchar, VIEW_NAME varchar, COLUMN_CASE varchar, COLUMN_TYPE varchar)
RETURNS VARCHAR
LANGUAGE javascript
AS
$$
var alias_dbl_quote = "";
var path_name = "regexp_replace(regexp_replace(f.path,'\\[(.+)\\]'),'(\\w+)','"\\1"')" // This generates paths with levels enclosed by double quotes (ex: "path"."to"."element"). It also strips any bracket-enclosed array element references (like "[0]")
var attribute_type = "DECODE (substr(typeof(f.value),1,1),'A','ARRAY','B','BOOLEAN','I','FLOAT','D','FLOAT','STRING')"; // This generates column datatypes of ARRAY, BOOLEAN, FLOAT, and STRING only
var alias_name = "REGEXP_REPLACE(REGEXP_REPLACE(f.path, '\\[(.+)\\]'),'[^a-zA-Z0-9]','_')" ; // This generates column aliases based on the path
var table_list = TABLE_NAME;
var col_list = "";
var array_num = 0;
if (COLUMN_CASE.toUpperCase().charAt(0) == 'M') {
alias_dbl_quote = """; } // COLUMN_CASE parameter is set to 'match col case' so add double quotes around view column alias name
if (COLUMN_TYPE.toUpperCase().charAt(0) == 'S') {
attribute_type = "DECODE (typeof(f.value),'ARRAY','ARRAY','STRING')"; } // COLUMN_TYPE parameter is set to 'string datatypes' so typecast to STRING instead of value returned by TYPEPOF function
// Build a query that returns a list of elements which will be used to build the column list for the CREATE VIEW statement
var element_query = "SELECT DISTINCT n" +
path_name + " AS path_name, n" +
attribute_type + " AS attribute_type, n" +
alias_name + " AS alias_name n" +
"FROM n" +
TABLE_NAME + ", n" +
"LATERAL FLATTEN(" + COL_NAME + ", RECURSIVE=>true) f n" +
"WHERE TYPEOF(f.value) != 'OBJECT' n" +
"AND NOT contains(f.path,'[') "; // This prevents traversal down into arrays
// Run the query...
var element_stmt = snowflake.createStatement({sqlText:element_query});
var element_res = element_stmt.execute();
// ...And loop through the list that was returned
while (element_res.next()) {
if (element_res.getColumnValue(2) != 'ARRAY') {
if (col_list != "") {
col_list += ", n";}
col_list += COL_NAME + ":" + element_res.getColumnValue(1); // Start with the element path name
col_list += "::" + element_res.getColumnValue(2); // Add the datatype
col_list += " as " + alias_dbl_quote + element_res.getColumnValue(3) + alias_dbl_quote; // And finally the element alias
}
// Array elements get handled in the following section:
else {
array_num++;
var simple_array_col_list = "";
var object_array_col_list = "";
// Build a query that returns the elements in the current array
var array_query = "SELECT DISTINCT n"+
path_name + " AS path_name, n" +
attribute_type + " AS attribute_type, n" +
alias_name + " AS attribute_name, n" +
"f.index n" +
"FROM n" +
TABLE_NAME + ", n" +
"LATERAL FLATTEN(" + COL_NAME + ":" + element_res.getColumnValue(1) + ", RECURSIVE=>true) f n" +
"WHERE REGEXP_REPLACE(f.path, '.+(\\w+\\[.+\\]).+', 'SubArrayEle') != 'SubArrayEle' "; // This prevents return of elements of nested arrays (the entire array will be returned in this case)
while (array_res.next()) {
if (array_res.getColumnValue(1).substring(1) == "") { // The element path name is empty, so this is a simple array element
if (simple_array_col_list != "") {
simple_array_col_list += ", n";}
simple_array_col_list += COL_NAME + ":" + element_res.getColumnValue(1); // Start with the element path name
simple_array_col_list += "[" + array_res.getColumnValue(4) + "]"; // Add the array index
simple_array_col_list += "::" + array_res.getColumnValue(2); // Add the datatype
simple_array_col_list += " as " + alias_dbl_quote + element_res.getColumnValue(3) + "_" + array_res.getColumnValue(4) + alias_dbl_quote; // And finally the element alias - Note that the array alias is added as a prefix to ensure uniqueness
}
else { // This is an object array element
if (object_array_col_list != "") {
object_array_col_list += ", n";}
object_array_col_list += "a" + array_num + ".value:" + array_res.getColumnValue(1).substring(1); // Start with the element name (minus the leading '.' character)
object_array_col_list += "::" + array_res.getColumnValue(2); // Add the datatype
object_array_col_list += " as " + alias_dbl_quote + element_res.getColumnValue(3) + array_res.getColumnValue(3) + alias_dbl_quote; // And finally the element alias - Note that the array alias is added as a prefix to ensure uniqueness
}
}
// If no object array elements were found then add the simple array elements to the
// column list...
if (object_array_col_list == "") {
if (col_list != "") {
col_list += ", n";}
col_list += simple_array_col_list;
}
// ...otherwise, add the object array elements to the column list along with a
// LATERAL FLATTEN clause that references the current array to the table list
else {
if (col_list != "") {
col_list += ", n";}
col_list += object_array_col_list;
table_list += ",n LATERAL FLATTEN(" + COL_NAME + ":" + element_res.getColumnValue(1) + ") a" + array_num;
}
}
}
// Now build the CREATE VIEW statement
var view_ddl = "CREATE OR REPLACE VIEW " + VIEW_NAME + " AS n" +
"SELECT n" + col_list + "n" +
"FROM " + table_list;
// Now run the CREATE VIEW statement
var view_stmt = snowflake.createStatement({sqlText:view_ddl});
var view_res = view_stmt.execute();
return view_res.next();
$$;
I tried this approach but it is displaying an error that regex has the [link I followed ][1]error.
REGEX ERROR*
[1]: https://www.snowflake.com/blog/automating-snowflakes-semi-structured-json-data-handling-part-2/#
I am getting this error:
JavaScript compilation error: Uncaught SyntaxError: Invalid or unexpected token
in CREATE_VIEW_OVER_JSON at ' var path_name = "regexp_replace(regexp_replace(f.path,'\[(.+)\]'),'(\\w+)','"\\1"')" ;'
position 82

Your error can be produced with just the first two line of the SP because you have you quotes incorrectly escaped/paired for the task:
CREATE OR REPLACE PROCEDURE create_view_over_json (TABLE_NAME varchar, COL_NAME varchar, VIEW_NAME varchar, COLUMN_CASE varchar, COLUMN_TYPE varchar)
RETURNS VARCHAR
LANGUAGE javascript
AS
$$
var alias_dbl_quote = "";
var path_name = "regexp_replace(regexp_replace(f.path,'\\[(.+)\\]'),'(\\w+)','"\\1"')" // This generates paths with levels enclosed by double quotes
$$;
call create_view_over_json(null,null,null,null,null);
gives:
100131 (P0000): JavaScript compilation error: Uncaught SyntaxError: Invalid or unexpected token in CREATE_VIEW_OVER_JSON at 'var path_name = "regexp_replace(regexp_replace(f.path,'\[(.+)\]'),'(\w+)','"\1"')" // This generates paths with levels enclosed by double quotes ' position 79
In SQL and in Javascript you cannot put the Quote you are using to wrap are "string" in the string without escaping. In SQL you can use $$ or ' so you have used $$ for the stored procedure body. Where inside the JavaScript you can use ', ", `
Thus in this line you have used double quoutes, which means you can embed the single quotes, but those doubles you have in the string terminate the string..
"regexp_replace(regexp_replace(f.path,'\\[(.+)\\]'),'(\\w+)','"\\1"')"
^ ^
Thus given you need to use both single and double you should wrap the string in back qoutes `
var path_name = `regexp_replace(regexp_replace(f.path,'\\[(.+)\\]'),'(\\w+)','"\\1"')`;

Related

How to pass a variant value to an insert statement in a Snowflake stored procedure

My goal: allow the user to pass a JSON string to a Snowflake stored procedure. I then want the stored procedure to cast the string as a variant data type and insert the value into a field in a table that is of the variant data type.
What I have tried:
wrapping the user's value in parse_json(),to_variant() and cast ( val as VARIANT)
I have tried all the above functions using the binding method and by constructing a sql statement
using a combination of strings and variables with no success
My code is below. Any help you can provide me with would be greatly appreciated, thanks!
CREATE OR REPLACE PROCEDURE LOGGING_TEST_PR (ETL_NAME VARCHAR(16777216),ETL_RUN_GUID VARCHAR(16777216),TASK_NAME VARCHAR(16777216),RECORDS FLOAT8,RUN_DATA_JSON VARCHAR(16777216) )
RETURNS VARCHAR
LANGUAGE JAVASCRIPT
AS
$$
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//USER INPUT VARIABLES
var USER_JOB_NAME = ETL_NAME.toLowerCase(),USER_JOB_RUN_GUID = ETL_RUN_GUID, USER_JOB_TASK_NAME = TASK_NAME.toLowerCase(),USER_JOB_RECORDS, USER_JOB_RUN_DATA_JSON;
//SQL_STATEMENT_COUNT VARIABLES
var SQL_STATEMENT_COUNT,SQL_COUNT,RECORD_COUNT;
//SQL_STATEMENT_INSERT VARIABLES
var SQL_INSERT_RECORD, SQL_INSERT, ROW_NUM_INSERT, RESULT_INSERT_RETVALUE;
//SQL_STATEMENT_SELECT VARIABLES
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//REVERT UNDEFNED VARIABLE VALUES BACK TO NULL
function NULL_PARAM(PARAM)
{
if (typeof PARAM === 'undefined')
{
return null;
}
else
{
return PARAM;
}
}
USER_JOB_RECORDS = NULL_PARAM(RECORDS);
USER_JOB_RUN_DATA_JSON = NULL_PARAM(RUN_DATA_JSON);
//SQL STATEMENT VARIABLES
var SQL_STATEMENT_COUNT = " SELECT COUNT(*) AS COUNT FROM EDW_DEV.LOGGING.JOB_LOG_SPROC WHERE LOWER(ETL_NAME) ='"
+ USER_JOB_NAME +"'"
+ " AND LOWER(ETL_RUN_GUID) ='" + USER_JOB_RUN_GUID +"'"
+ " AND LOWER(TASK_NAME) ='" + USER_JOB_TASK_NAME + "'";
var SQL_STATEMENT_SELECT = "SELECT LOWER(ETL_NAME) AS ETL_NAME,LOWER(ETL_RUN_GUID) AS ETL_RUN_GUID,LOWER(TASK_NAME) AS TASK_NAME,START_DTS,END_DTS FROM EDW_DEV.LOGGING.JOB_LOG_SPROC WHERE LOWER(ETL_NAME) ='"
+ USER_JOB_NAME +"'"
+ " AND LOWER(ETL_RUN_GUID) ='" + USER_JOB_RUN_GUID +"'"
+ " AND LOWER(TASK_NAME) ='" + USER_JOB_TASK_NAME + "'";
var SQL_INSERT_RECORD = "INSERT INTO EDW_DEV.LOGGING.JOB_LOG_SPROC (ETL_NAME,ETL_RUN_GUID,TASK_NAME,START_DTS,RECORDS_START,RUN_DATA_JSON)"
+ "VALUES("+ "'" + USER_JOB_NAME + "'," + "'" + USER_JOB_RUN_GUID + "'," + "'" + USER_JOB_TASK_NAME + "'," + "CONVERT_TIMEZONE('UTC', CAST(CURRENT_TIMESTAMP() AS TIMESTAMP_TZ(9)))" + ","
+ USER_JOB_RECORDS + "," + USER_JOB_RUN_DATA_JSON + ")";
try {
//CHECK IF THE RECORD FOR THE TASK_NAME EXISTS IN THE TABLE
SQL_COUNT = snowflake.createStatement({sqlText:SQL_STATEMENT_COUNT});
RESULT_COUNT = SQL_COUNT.execute(); //EXECUTE MAH SQL STATEMENT
RESULT_COUNT.next(); //GO TO FIRST ROW IN RESULT SET
RECORD_COUNT = RESULT_COUNT.getColumnValue(1);
//THE TASK_NAME IS NOT IN THE TABLE
if (RECORD_COUNT == 0)
{SQL_INSERT = snowflake.createStatement({sqlText:"INSERT INTO EDW_DEV.LOGGING.JOB_LOG_SPROC (ETL_NAME,ETL_RUN_GUID,TASK_NAME,RECORDS_START,RUN_DATA_JSON) VALUES(?,?,?,?,?);",
binds:[USER_JOB_NAME,USER_JOB_RUN_GUID,USER_JOB_TASK_NAME,USER_JOB_RECORDS,CAST(USER_JOB_RUN_DATA_JSON AS VARIANT)]});
INSERT_RESULT = SQL_INSERT.execute();
ROW_NUM_INSERT = INSERT_RESULT.next();
return RESULT_INSERT_RETVALUE = INSERT_RESULT.getColumnValue(1);
}
//THE TASK NAME IS IN THE TABLE
else if (RECORD_COUNT == 1)
{ return "UPDATE RECORD WITH END TIMESTAMP AND OPTIONAL COUNTS AND RUN DATA JSON"; }
//RUH ROOOOOOO!
else
{ return "THERE WAS AN UNFORSEEN ERROR.";}
}
catch (ERR) {
return ERR
}
$$
CALL LOGGING_TEST_PR('WAYNE','15','BAGELS',1,'{"dude":"whoa"}') }
The PARSE_JSON function is strict about the JSON. If it encounters anything in the string that doesn't match the specs perfectly it will generate an error. You can test the JSON string at https://jsonlint.com.
from a Sales Engineer at Snowflake:
//create the table for inserting data
create table tester (json variant);
//adding a select statement with the parse_json I am able to insert values into the table in json format
insert into tester
select parse_json(column1) as v
from values ('{ "x" : "abc", "y" : false, "z": 10} ')
as vals;
//see the result
Select * from tester;

How to get row data where column = ? also applying limit to rows?

I am getting an error on applying a raw query in sq-lite.The error is
Caused by: android.database.sqlite.SQLiteException: near "Limit": syntax error (code 1): ,
while compiling: SELECT * FROM quiz_questions WHERE Levels = "Level 1"+ Limit 2+
The query SELECT * FROM quiz_questions WHERE Levels = "Level 1" works and gives me multiple rows but when i am applying limit to query, it gives error.
This is code for it.
public ArrayList<Question> getLockedLevels(ArrayList<String> Level) {
ArrayList<Question> questionList = new ArrayList<>();
db = getReadableDatabase();
for (int i = 0; i < Level.size(); i++)
{
String levelID = Level.get(i);
String SELECT_TABLE_QUERY = "SELECT * FROM " + TABLE_NAME + " WHERE " + COLUMN_LEVEL + " = \"" + levelID + "\"+ Limit 1";
Cursor cursor = db.rawQuery(SELECT_TABLE_QUERY, null);
if (cursor.moveToFirst()) {
do {
Question question = new Question();
question.setmLevels(cursor.getString(cursor.getColumnIndex(COLUMN_LEVEL)));
question.setmLevels_lockmanager(cursor.getInt(cursor.getColumnIndex(COLUMN_LEVEL_LOCKMANAGER)));
questionList.add(question);
} while (cursor.moveToNext());
}
cursor.close();
}
return questionList;
}
Try
String SELECT_TABLE_QUERY = "SELECT * FROM " + TABLE_NAME + " WHERE " + COLUMN_LEVEL + " = '" + levelID + "' Limit 1";
That should resovle to :-
SELECT * FROM quiz_questions WHERE Levels = 'Level 1' Limit 1
However, the above is subject to potential SQL Injection.
Using :-
String levelID = Level.get(i);
String SELECT_TABLE_QUERY = "SELECT * FROM " + TABLE_NAME + " WHERE " + COLUMN_LEVEL + "=? Limit 1";
Cursor cursor = db.rawQuery(SELECT_TABLE_QUERY, new String[]{levelID });
would remove the potential for SQL Injection and is therefore considered the more correct way.
Alternatively you could utilise the convenience query method that not only protects against SQL Injection but also generates much of the SQL.
So you could use :-
for (int i = 0; i < Level.size(); i++) {
Cursor cursor = db.query(TABLE_NAME,null,COLUMN_LEVEL + "=?",new String[]{Level.get(i)},null,null,null,"1");
while (cursor.moveToNext()) {
Question question = new Question();
question.setmLevels(cursor.getString(cursor.getColumnIndex(COLUMN_LEVEL)));
question.setmLevels_lockmanager(cursor.getInt(cursor.getColumnIndex(COLUMN_LEVEL_LOCKMANAGER)));
questionList.add(question);
}
cursor.close();
}
See SQliteDatabase - query
The above also uses the shorter while(cursor.moveToNext) { ..... } for looping through the Cursor (if there are no rows then the loop is not entered as moveToNext will return false if the move cannot be made)

Eiffel: multilines string formatting

Didn't find a better way to format a multiline string than this way... seems complicated. What would be the best way to format this type of code?
l_qry := "SELECT%
% * %
%FROM %
% enumerate %
%WHERE %
% " + {like item_prototype}.Primary_key_db_column_name + " = " + l_category_id + " %
%UNION %
% SELECT %
% e.* %
% FROM %
% enumerate e %
%INNER JOIN %
% enumerates_leaves s ON s." + {like item_prototype}.Primary_key_db_column_name + " = e." + {like item_prototype}.Category_db_column_name + " %
%) SELECT * FROM enumerates_leaves WHERE enumerates_leaves." + {like item_prototype}.Category_db_column_name + " IS NOT NULL;"
You can use Verbatin Strings for that purpose.
sql_select_country : STRING = "[
SELECT *
from COUNTRY
]"
-- Select all country
For building SQL queries dynamically, you can define a template
with placeholders and then replace them with the expected values.
template_query : STRING = "[
SELECT
*
FROM
enumerate
WHERE
$Primary_key_db_column_name = :id
UNION
SELECT
e.*
FROM
enumerate e
INNER JOIN
enumerates_leaves s ON s.$Primary_key_db_column_name = e.$Category_db_column_name
) SELECT * FROM enumerates_leaves WHERE enumerates_leaves.$Category_db_column_name IS NOT NULL;"
]"
-- Template query `query_name` ....
Using the template
l_query: STRING
create l_query.make_from_string (template_query)
l_query.replace_substring_all ("$Primary_key_db_column_name", {like item_prototype}.Primary_key_db_column_name)
...
In fact, one can generalize this idea an build something like
sql_query_builder (query_template: READABLE_STRING_GENERAL; arguments: ITERABLE [READABLE_STRING_GENERAL]) :STRING

What's the most efficient way to search text of any data type in EF6?

I try to dynamically create some LINQ query for searching data in all columns.
Currently, I use the following code to build dynamic LINQ query. However, it's quite buggy when deals with complex column.
var type = property[col.ToLower()].PropertyType;
var isNullableType = type.IsGenericType && type.GetGenericTypeDefinition() == typeof (Nullable<>);
if (type.IsValueType && !isNullableType)
{
filter += col + ".ToString().ToLower().Contains(#" + i + ".ToLower())";
}
else if (isNullableType)
{
filter += col + ".HasValue ? " + col + ".Value.ToString().ToLower().Contains(#" + i + ".ToLower())" + " : false";
}
else
{
filter += "(" + col + " != null ? " + col + " : \"\").ToString().ToLower().Contains(#" + i + ".ToLower())";
}
Do you have any idea to simplify my above code? I'm OK if your suggestion is work only for SQL Server 2008 or later.
Note:
Column data can be any type like integer, string, object, enum and it can be null.

Searching in database from a form returning empty input

I want to do search in my database using some fields filled by a form
but if some fields are left empty i don't want to include them
which kind of query can help me in achieving this??
Currently i am using a query like:
Select * from DATABASE where COLUMN='form_input';
but as my form will return empty it will try and select rows which have null entries but rather i want to this time see a result of all rows in database i.e i want to invalidate the filter by COLUMN='form_input'
As we do not know your server side scripting language -
The psheuducode should be -
if(request['form_input']!=null)
Select * from DATABASE where COLUMN='form_input';
else
Select * from DATABASE;
Also If there are many fields for form_input then we can design
our code something like -
String wherequery = "";
if(request['form_input1']!=null)
{
wherequery = wherequery + " COLUMN='form_input1' ";
}
if(request['form_input2']!=null)
{
wherequery = wherequery + " And "
wherequery = wherequery + " COLUMN='form_input2' ";
}
if(request['form_input3']!=null)
{
wherequery = wherequery + " And "
wherequery = wherequery + " COLUMN='form_input3' ";
}
....
And so on
....
String selectQuery = "";
if(wherequery == "")
{
selectQuery = "Select * from TABLE";
}
else
{
selectQuery = "Select * from TABLE where" + wherequery ;
}
execute (selectQuery);
Please note we are using pseudo code here. We can take the form inputs and concatenate query for each input which is not null.
If we find the concatenated string as blank string, we will select the full table.
Otherwise
we will select with the where clause query.
Hope, this help you out.

Resources