Haskell parsing queries (Parsec) - parsing

I'm trying to make a queries parser in Haskell, but don't understand how I'm supposed to allow different optional paths of parser logic. My attempt:
query :: Parser Query
query = do
-- add more queries
reserved "SELECT"
select <- sequenceOfExpr
reserved "FROM"
table <- identifier
semi
return $ Select select table (BoolConst True)
<|> do
reserved "SELECT"
select <- sequenceOfExpr
reserved "FROM"
table <- identifier
reserved "WHERE"
whereQ <- bExpression
semi
return $ Select select table whereQ
<|> do
reserved "INSERT"
insert <- sequenceOfExpr
reserved "INTO"
table <- identifier
semi
return $ Insert insert table
<|> do
reserved "REMOVE"
reserved "FROM"
table <- identifier
reserved "WHERE"
whereQ <- bExpression
semi
return $ Remove table whereQ
<|> do
reserved "CREATE"
table <- identifier
fields <- sequenceOfExpr
semi
return $ Create table fields
<|> do
reserved "DROP"
table <- identifier
semi
return $ Drop table
Which works when parsing a string that corresponds to the first do stmt structure, e.g.:
"SELECT testField FROM testTable;"
but not for the others. E.g. when parsing:
"SELECT testField FROM testTable WHERE TRUE"
Instead of trying the other paths, it returns:
unexpected "W"
expecting ";"
In other words it seems like it only tries the first logic. What am I doing wrong?
Any help would be much appreciated!

This happens because the SELECT FROM alternative has succeeded and returned its result, the parsing never got to trying the SELECT FROM WHERE alternative.
In this specific case, I would just flip their order: try SELECT FROM WHERE first, and if that doesn't work, fall back to SELECT FROM. You would also need to wrap it in a try in order for the parser to roll back to the beginning of the query.
Alternatively, you could make the WHERE parsing a conditional part of the SELECT FROM parser, something like this:
do
reserved "SELECT"
select <- sequenceOfExpr
reserved "FROM"
table <- identifier
whereQ <- try (reserved "WHERE" *> bExpression) <|> (pure $ BoolConst True)
semi
return $ Select select table whereQ

Related

Is there a simpler way to get the argument type list for a snowflake procedure?

I need to transfer ownership of snowflake procedures post clone to a new Role.
To do this I'm using a procedure which works through all objects from the database.information_schema.xxxx views.
The procedures are problematic though, the SHOW PROCEDURE has a column which shows the procedure signature as just argument types, but the information_schema.procedures view shows the actual parameter name as well as its argument type, which if passed into a GRANT command does not work - the grant expects the Argument Type signature only, not the parameter names :/
SHOW PROCEDURE ARGUMENTS => PROCEDURE_NAME(VARCHAR) RETURN VARCHAR
INFORMATION_SCHEMA.PROCEDURES.ARGUMENT_SIGNATURE => PROCEDURE_NAME(P_PARAM1 VARCHAR)
I eventually came upwith this which was fun, but feels rather complicated, the question is - have I missed a simpler approach?
SELECT procedure_name
, concat('(',listagg(argtype, '') within group (order by argindex)) cleanArgTypes
FROM (SELECT procedure_name
, argument_signature
, lf.index argindex
, lf.value argtype
FROM rock_dev_test_1.information_schema.procedures
, lateral flatten(input=>split(decode(argument_signature
,'()','( )'
,argument_signature
),' ')
,outer=>true) lf
WHERE lf.index/2 != round(lf.index/2)
)
GROUP BY procedure_name
, argument_signature
ORDER by 1,2;
cleanArgTypes => (VARCHAR)
This takes the overspecific argument_signature splits it into an array using space as a delimiter, then laterally flatten the return set into rows, discard the parameter names (always at an even index) then groups by parameter name and signature and uses ListAgg to put the parameter argument types back into a string.
Small wrinkle in that () doesn't work, so has to be shifted to ( )
Whilst I enjoyed dabbling with some of Snowflakes Semi-structured capabilities, If there was a simpler approach I'd rather use it!
Mostly the same code, but it doesn't need to be nested, I swapped from the arg_sig (the input) to using the SEQ of the split, but mostly the same still:
SELECT p.procedure_name
,'( '|| listagg(split_part(trim(t.value),' ',2), ', ') within group (order by t.index) || ')' as out
FROM information_schema.procedures as p
,table(split_to_table(substring(p.argument_signature, 2,length(p.argument_signature)-2), ',')) t
group by 1, t.seq;
for the toy procedures in my stack overflow schema I get:
PROCEDURE_NAME
OUT
DATE_HANDLER
( DATE)
TODAYS_DELIVERY_AMOUNT
( VARCHAR)
ABC
( TIMESTAMP_NTZ, TIMESTAMP_NTZ, VARCHAR)
ABC_DAILY
( )
STRING_HANDLER
( VARCHAR)
I don't think there's a built-in way to do this. Here's an alternate way:
with A as
(
select PROCEDURE_NAME, split(replace(trim(ARGUMENT_SIGNATURE, '()'), ','), ' ') ARGS
,ARGUMENT_SIGNATURE
from test.information_schema.procedures P
)
select PROCEDURE_NAME
,listagg(VALUE::string, ',') as ARGS
from A, table(flatten(ARGS))
where index % 2 = 1
group by PROCEDURE_NAME
;
You could also use a result scan after the show command to get the name of the procedure and argument signature in a single string:
show procedures;
select split("arguments", ')')[0] || ')' as SIGNATURE from table(result_scan(last_query_id()));
I wrote this to pull out the list of procedures from the information schema with properly formatted argument signature, using a combination of splitting the string up with SPLIT, putting each value on a separate row with LATERAL FLATTEN, filtering to only get the data types using the INDEX, then re-grouping with LISTAGG. No subquery needed either.
SELECT PROCEDURE_SCHEMA || '.' || PROCEDURE_NAME || '(' ||
REPLACE(LISTAGG(C.Value,' ') WITHIN GROUP (ORDER BY C.INDEX),'(','') AS "Procedure"
FROM INFORMATION_SCHEMA.PROCEDURES,
LATERAL FLATTEN (INPUT => SPLIT(ARGUMENT_SIGNATURE,' ')) C
WHERE INDEX % 2 = 1 OR ARGUMENT_SIGNATURE = '()'
GROUP BY PROCEDURE_SCHEMA, PROCEDURE_NAME, ARGUMENT_SIGNATURE

Joining on column names with spaces

I'm trying to join to tables using PROQ SQL. One of the columns I'm using for the join has a space in the column name. The query I'm using:
PROC SQL;
CREATE TABLE TEST AS
SELECT a.*, b.*
FROM TABLE_1 a
INNER JOIN TABLE_2 b
ON a.CONTNO = b."Contract Number";
RUN;
This is the error I'm getting:
ERROR 22-322: Syntax error, expecting one of the following: a name, *.
How do I fix this?
You just need to add square brackets around the Column name. For example:
b.[Contract Number]
Tips: Using alias (a, b) can be costly. When you only have one table to join, consider typing out the table rather than doing an alias.

How to select specific column types in SQLite3?

PRAGMA table_info(myTable)
This query selects all the info in a table, for example: if there are 2 columns in a table then this query will select all the column names, column types e.t.c
I just want add one clause i.e I want to get info of specific columns that I define in the query.
Like this:
PRAGMA table_info(myTable) where columnNames = 'a' and columnNames = 'b' // this is wrong query but I just mentioned it to make my question more clear.
How can I do this?
You can pragma_table_info() in a query with a WHERE clause:
SELECT *
FROM pragma_table_info('myTable') -- note the single quotes
WHERE name IN ('a', 'b') -- equivalent: name = 'a' OR name = 'b'
See the demo.

Run query where column = *

Need to run a query on google sheets where the column match any(*) value. I'm doing this because the criteria is filled by refering to another cell (like a filter).
Probably I'm missing something in the syntax.
My last try:
=QUERY(PROD!A:U;"SELECT L, SUM(M), SUM(O), SUM(Q), (1-(SUM(Q)/SUM(O)))
WHERE T = '*' AND D = '*' AND U = '*'
GROUP BY L
ORDER BY SUM(M) DESC
LABEL L 'PRODUTO', SUM(M) 'QUANTIDADE', SUM(Q) 'CUSTO', SUM(O) 'VENDA', (1-(SUM(Q)/SUM(O))) 'MARK-UP'")
Thank you in advance.
In Google Visualization API Query Language (not to be confused with T-SQL, MySQL, etc) the clause WHERE T = '*' means that the content of column T is literally the string *.
To test for a cell being nonempty, use T <> '' (for text columns) or T is not null (for numeric columns).
There are also like and matches for more complex text filtering.

MS Access 07 - wildcard in JOIN query

I've got the following query
SELECT tblUsers.userfullname,
tblReports.reportdate,
tblReports.reportnumber,
tblRawData.reportcategory,
tblRawData.reportissue
FROM tblRawData
RIGHT JOIN (tblUsers RIGHT JOIN tblReports ON tblUsers.userID = tblReports.userID) ON tblReports.reportnumber LIKE "*" & tblRawData.reportnum
WHERE (
((tblUsers.username) Like "*" & [Forms]![frmSelect]![txtUser] & "*")
AND
((tblUsers.userShift) Like "*" & [Forms]![frmSelect]![txtShift] & "*")
);
Which works - except the part of
ON tblReports.reportnumber LIKE "*" & tblRawData.reportnum
what i'm trying to match is instances where
tblReports.reportnumber = 410145
and
tblRawData.reportnum = 12345.410145
or just
tblRawDatw.reportnum = 410145
but for some reason it just will not find that first match (ex: 12345.410145) unless the number is identical like the second match (ex: 410145). I've tried formatting it as a number as well as text - and no luck.
any idea what I may be missing?
Update: I tried making another query with just the two tables and it doesn't like to match. i tried removing the "." (example: 12345.410145 into 12345410145) and no luck. here's my second query.
SELECT tblReports.userID,
tblRawData.reportnum,
tblRawData.reportcategory,
tblRawData.reportissue,
tblReports.reportdate,
tblReports.reportnumber
FROM tblReports
LEFT JOIN tblRawData ON tblReports.reportnumber LIKE "*" & tblRawData.reportnum;
where if the data is like such.
tblReports Report numbers:
410145
410144
410143
410142
410141
and tblRawData report numbers are such:
12345.410145
410143
12344.410141
the resulting query should show me all 5 records from tblReports - but three of those records have the notes and such from tblRawData.
Rewritten to allow for no match.
SELECT
u.userfullname,
r.reportdate,
r.reportnumber,
q.reportcategory,
q.reportissue
FROM (tblusers u
LEFT JOIN tblreports r
ON u.userid = r.userid)
LEFT JOIN (
SELECT
Val(Mid([reportnum],InStr([reportnum],".")+1)) AS RepNo,
r.reportcategory,
r.reportissue
FROM Rawdata AS r) AS q
ON r.reportnumber = q.RepNo
AND q.username Like "*" & [Forms]![frmSelect]![txtUser] & "*"
AND q.userShift Like "*" & [Forms]![frmSelect]![txtShift] & "*"
It is a common enough convention that LEFT JOINs are used rather than RIGHT JOINs.
I didn't check everything, but I have some ideas. Maybe you have to swap the LIKE condition?
tblRawData.reportnum LIKE "*" & tblReports.reportnumber
if that's not the problem, could you try to use the trim function?
Trim(tblReports.reportnumber) LIKE "*" & Trim(tblRawData.reportnum)
The wildcard character for SQL queries is the percent sign '%' not the asterisk. Try using that instead. But if you are including every match then why have that in your query at all?
Access wildcard character reference

Resources