I am currently trying to use table.insert to insert print into a table my code is below:
local myTable = {}
table.insert(myTable, print("hi"))
assume:
local myTable = {}
Case 1
to insert a function into myTable:
table.insert(myTable, print)
to execute a function inserted into myTable:
myTable[1]("hi") --just as an ordinary print
*Case 2 *
to insert a function into myTable with an argument assigned:
table.insert(myTable, function() print("hi") end)
to execute a function inserted into myTable:
myTable[1]()
Related
I have a snowflake table being used to store records of sql being executed by a stored procedure and any error messages. The records in this table are being saved as a string with special chars escaped with javascripts escape('log') function. I then want to create a view to this log table that will return the records in an easily readable format.
My first attempt at this was to create an additional stored procedure to query the log table, pass the record to the unescape() function, then return it. Calling this procedure works as intended but we can't then create a view of this data say with something like
create view log_view as
select (call UNESCAPE_PROC());
The other idea was to use a UDF rather than a stored procedure. However this also fails as we can't execute sql code with a javascript UDF. This post touches on this idea.
My question is how can I record these executed statements in a table in such a way as they can be later viewed in a plain text readable format. I've outlined my attempts below but perhaps my approach is flawed, open to any suggestions.
Minimal working example below
Sample log table and procedure to write a statement to said table
create or replace table event_table(
event varchar,
event_stamp timestamp
);
create or replace procedure insert_to_event(stamp string)
RETURNS VARCHAR
LANGUAGE JAVASCRIPT
COMMENT = 'SP to log an event message with timestamp to event_table'
EXECUTE AS CALLER
AS
$$
// some variables to log in our event table
var str_stamp = (new Date()).toISOString();
to_log = `insert into dummy_table values(2, 'Bill', `+STAMP+`);`;
sql =
`INSERT INTO event_table (
event,
event_stamp
)
VALUES
('`+escape(to_log)+`', to_timestamp('`+str_stamp+`'));`;
var stmnt = snowflake.createStatement({ sqlText: sql });
stmnt.execute();
return "logged: "+ escape(to_log)
$$;
call insert_to_event(current_timestamp());
select * from event_table;
Stored procedure to return readable log records
CREATE OR REPLACE PROCEDURE UNESCAPE_PROC()
RETURNS VARCHAR
LANGUAGE JAVASCRIPT
COMMENT = 'SP will select a chosen column from event_table table, pass it to javascripts unescape() fn and return it'
EXECUTE AS CALLER
AS
$$
unescape_sql =
`select event from event_table`
var errs_res = [];
try {
all_logs = snowflake.execute(
{ sqlText: unescape_sql }
);
// iterate over all columns
while (all_logs.next()) {
errs_res.push(all_logs.getColumnValue(1));
}
return unescape(errs_res)
}
catch(err){
return "something went wrong: " + err
}
$$;
call UNESCAPE_PROC();
Which returns the records in a readable form as expected
However this of course wont work as part of a view eg.
-- define a view calling this procedure??
create view log_view as
select (call UNESCAPE_PROC());
Javascript user defined function can be used in a view like this, however it cannot be used to execute sql as in the stored procedures
-- use a UDF instead
CREATE OR REPLACE FUNCTION UNESCAPE_UDF()
RETURNS string
LANGUAGE JAVASCRIPT
AS
$$
unescape_sql =
`select event from event_table`
var errs_res = [];
try {
all_logs = snowflake.execute(
{ sqlText: unescape_sql }
);
// iterate over all columns
while (all_logs.next()) {
errs_res.push(all_logs.getColumnValue(1));
}
return unescape(errs_res)
}
catch(err){
return "something went wrong: " + err
}
$$
;
select UNESCAPE_UDF();
Stored procedures will solve one half of my problem for me, whilst UDF's will solve the other half. How can I combine the functionality of these two methods to solve this issue?
A much cleaner approach using parameters binding:
create or replace procedure insert_to_event(stamp string)
RETURNS VARCHAR
LANGUAGE JAVASCRIPT
COMMENT = 'SP to log an event message with timestamp to event_table'
EXECUTE AS CALLER
AS
$$
// some variables to log in our event table
var str_stamp = (new Date()).toISOString();
to_log = `insert into dummy_table values(2, 'Bill', '${str_stamp}');`;
sql = `INSERT INTO event_table (event,event_stamp)
VALUES(?, try_to_timestamp(?));`;
var stmnt = snowflake.createStatement({sqlText: sql, binds:[to_log, str_stamp]});
stmnt.execute();
return "logged: "+ to_log
$$;
Call:
call insert_to_event(current_timestamp());
-- logged: insert into dummy_table values(2, 'Bill', '2022-02-03T17:45:44.140Z');
select * from event_table;
Found a solution/workaround.
Rather than using javascripts escape/unescape functions to remove special chars from the logs, we use a regex replace eg.
create or replace procedure insert_to_event(stamp string)
RETURNS VARCHAR
LANGUAGE JAVASCRIPT
COMMENT = 'SP to log an event message with timestamp to event_table'
EXECUTE AS CALLER
AS
$$
// some variables to log in our event table
var str_stamp = (new Date()).toISOString();
to_log = `insert into dummy_table values(2, 'Bill', `+STAMP+`);`;
to_log = to_log.replace(/[`~!##$%^&*|+=?'"<>\{\}\[\]\\\/]/gi, '');
sql =
`INSERT INTO event_table (
event,
event_stamp
)
VALUES
('`+to_log+`', to_timestamp('`+str_stamp+`'));`;
var stmnt = snowflake.createStatement({ sqlText: sql });
stmnt.execute();
return "logged: "+ to_log
$$;
call insert_to_event(current_timestamp());
select * from event_table;
Which writes to the log table in an easily readable format with no need for additional stored procedures/UDF's.
I'm using a procedure to insert rows into a table. I want to return the count of rows inserted, but I can't figure out how to turn on the variable substitution from inside a procedure. I've tried all of the following, but they all return errors:
snowflake.execute({sqlText: '!set variable_substitution=True'});
snowflake.execute({sqlText: 'set variable_substitution=True'});
snowflake.execute({sqlText: '-o variable_substitution=True'});
How do I turn this option on so I can run "select &__rowcount;" and get my count back?
Here's code for a test procedure, if that helps:
CREATE OR REPLACE PROCEDURE TEST_OF_GETTING_COUNTS()
RETURNS VARCHAR
LANGUAGE javascript
CALLED ON NULL INPUT
AS
$$
// Turn on variable substitution
snowflake.execute(
{sqlText: '!set variable_substitution=True'}
);
// Prepare SQL to identify tables to be updated
snowflake.execute({sqlText: 'SELECT 1'});
// Now get the count of rows selected
var getCount = snowflake.createStatement({sqlText: "SELECT &__ROWCOUNT;"});
var rowCountResultSet = getCount.execute();
while (rowCount.next()) {
rowCount= rowCountResultSet.getColumnValue(1);
}
// Turn off variable substitution
snowflake.execute({sqlText: '!set variable_substitution=False'});
return rowCount;
$$;
CALL TEST_OF_GETTING_COUNTS();
NickW, in his comment above, gave me a link to a page in Snowflake's documentation that lists all the JavaScript methods available for the snowflake, Statement, ResultSet, and SfDate objects. It gave me exactly what I needed: the getRowCount() method.
Is it possible to have a function that can access arbitrarily nested entries of a table?
The following example is just for one table. But in my real application I need the function to check several different tables for the given (nested) index.
local table1 = {
value1 = "test1",
subtable1 = {
subvalue1 = "subvalue1",
},
}
local function myAccess(index)
return table1[index]
end
-- This is fine:
print (myAccess("value1"))
-- But how do I access subtable1.subvalue1?
print (myAccess("subtable1.subvalue1???"))
You won't be able to do this using a string unless you use load to treat it as Lua code or make a function to walk on a table.
You can make a function which will split your string by . to get each key and then go one by one.
You can do this using gmatch + one local above gmatch with current table.
#Spar: Is this what you were suggesting? It works anyway, so thanks!
local table1 = {
value1 = "test1",
subtable1 = {
subvalue1 = "subvalue1",
},
}
local function myAccess(index)
local returnValue = table1
for key in string.gmatch(index, "[^.]+") do
if returnValue[key] then
returnValue = returnValue[key]
else
return nil
end
end
return returnValue
end
-- This is fine:
print (myAccess("value1"))
-- So is this:
print (myAccess("subtable1.subvalue1"))
So I'm currently working on creating blocks of codes which can be called simultaneously by a name id. I've decided to do that with a main table, which contains table with id and with functions. To do that, I wrote 3 functions
function hook.add(name, hookname, func)
hooks[hookname[name]] = func
end
function hook.create(name)
hooks[name] = {}
end
function hook.run(name)
for _, func in pairs(hooks[name]) do
func()
end
end
hook.create("MainHook")
local function func()
print("working")
end
hook.add("todo", "MainHook", func)
However it doesnt work and crashes with
bin/hooks.lua:27: table index is nil
Error contains in
hooks[hookname[name]] = func
line but I have no idea why because even if i print hookname and name there is no nil at all.
I would really appreciate if you help me
Your function hook.create creates empty table for name, so function hook.add should look like this:
function hook.add(name, hookname, func)
-- create hooks[hookname] table if not exists
hooks[hookname] = hooks[hookname] or {}
-- add function to hooks[hookname] table
hooks[hookname][name] = func
end
I need help to know how to do for use table.insert, and insert elements in to one table that is in to another table. This what i have:
Table = {} --Main table
function InsertNewValues()
local TIME = --Any value, string or integer
local SIGNAL = --Any value, string or integer
table.insert(Table, {TIME, SIGNAL})
end
Ok, this allow me to insert the values of TIME and SIGNAL everytime i call that function, so the table will have:
Table[1][1] = TIME
Table[1][2] = SINGAL
...
Table[...][1] = TIME
Table[...][2] = SIGNAL
BUT ... I need to insert the values of TIME and SIGNAL in to another table that is inside of the table "Table", and that table work as a KEY to refer those values ... TIME and SIGNAL ...
therefore, the resulting table would be the following:
+Table
|
+-[1]othertable
|
+-+[1]TIME - [2]SIGNAL
+-+[1]TIME - [2]SIGNAL
+- ...
|
+-[2]othertable
|
+-+[1]TIME - [2]SIGNAL
+-+[1]TIME - [2]SIGNAL
+- ...
How can i do for do that?
----------------- EDIT -----------------
I have not explained myself well, what I need is:
Given a table called "Table", I need to be able to use "strings" as "keys" within that table. That would be:
-- Name of my "container" table
Table = {}
Values to be introduced
Time = '1 seconds' -- this value can change as desired
Value = 'logic' -- this value can change as desired
Add to my master table "Table", using the string key "RandomName"
-- "RandomName" can change as desired
function AddNewValues ()
table.insert (Table [RandomName], {Time, Value})
end
Every time I call the function "AddNewValues ()", it should add the values present in "Time" and "Value" as a "NEW ENTRY" for that "RandomName".
So the table result may should look like this:
+table -- that contains
+-RandomName-- string key to access
+--"Time,Value"
+--"Time,Value"
+--"Time,Value"
+...
And then, to be able to access the values that are inside that table, using "RandomName" as the key:
function Load()
for key, v in pairs(Table) do
a = Table[key][RandomName][1] -- reference to "Time"
b = Table[key][RandomName][2] -- reference to "Value"
print('Time: ' .. a ..'/' .. 'Value: ' .. b)
end
end
You're just not starting deep enough into the table. When you want the sequence values for the table under the key equaling the value of RandomName, just do:
function Load()
for _, v in ipairs(Table[RandomName]) do
a = v[1] -- reference to "Time"
b = v[RandomName][2] -- reference to "Value"
print('Time: ' .. a ..'/' .. 'Value: ' .. b)
end
end
Answer to original question:
It appears that you want something like this:
Table = { ["[1]othertable"] = {}, ["[2]othertable"] = {} }
table.insert(Table["[1]othertable"], {TIME, SIGNAL})
The keys are "[1]othertable" and "[2]othertable".
You might prefer to use keys like "othertable1" and "othertable2". If you use valid identifiers you can drop some of the syntax:
Table = { othertable1 = {}, othertable2 = {} }
table.insert(Table.othertable1, {TIME, SIGNAL})
In fact, you might prefer to do something similar with TIME and SIGNAL. Instead of positive integer indices, you could use string keys:
Table = { othertable1 = {}, othertable2 = {} }
table.insert(Table.othertable1, {TIME = 12.42, SIGNAL = 3.2})
print(Table.othertable1[1].TIME)