gocraft / dbr

Additions to Go's database/sql for super fast performance and convenience.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

UNION and SELECT...AS Syntax Errors

AdamSLevy opened this issue · comments

I am constructing a complex query that uses a WHERE col IN ? clause that uses a UNION of SELECTs. The builder is inserting an additional set of parentheses which is causing a syntax error.

Here is the simplified code I'm using to construct the query. The Dialect is SQLite3.

stmt := sess.Select("*").From("entries").Limit(uint64(limit)).Offset(uint64(page*limit) + 1)
addressIDStmt := dbr.Select("id").From("addresses").Where("rcd_hash = ?", rcdHash)

entriesStmt := dbr.UnionAll(
        dbr.Select("entry_id").From("address_transactions_to").Where("address_id == ?", addressIDStmt),
        dbr.Select("entry_id").From("address_transactions_from").Where("address_id == ?", addressIDStmt))

stmt = stmt.Where("id IN ?", entriesStmt)

The error I get is near "UNION": syntax error.
The constructed query is:

SELECT * FROM entries WHERE 
        (id IN 
                (   --- EXTRA PARENTHESIS, SYNTAX ERROR
                        (SELECT entry_id FROM address_transactions_to WHERE (address_id == (SELECT id FROM addresses WHERE (rcd_hash = X'6a0c800d303c0260dd998e6609f89909974d9576ec72d070f9ec7aa493e84884')))) 
                        UNION ALL 
                        (SELECT entry_id FROM address_transactions_from WHERE (address_id == (SELECT id FROM addresses WHERE (rcd_hash = X'6a0c800d303c0260dd998e6609f89909974d9576ec72d070f9ec7aa493e84884'))))
                )   --- EXTRA PARENTHESIS, SYNTAX ERROR
        )
LIMIT 25 OFFSET 1

I have indented the query to clearly show the extra set of parentheses.

I believe the solution is to remove the *union type from the switch statement on line 106 of interpolate.go.

I do not believe it is ever valid syntax to wrap a UNION or UNION ALL in parenthesis or to follow that with an AS.

I reviewed the test cases in interpolate_test.go and found these "correct" test results to be troubling:

want: "((SELECT a FROM table1) UNION ALL (SELECT b FROM table2)) AS `t`",
Also
want: "(SELECT a FROM table) AS `a1`",

Thus I believe for a complete fix, the Union() and UnionAll() functions should be modified to simply return the Builder interface, without the As(string) Builder method. It also appears that the As function provided by SelectStmt results in additional invalid parenthesis.

UPDATE: I was incorrect in my initial assessment. The problematic parentheses are not the outer most ones around the UNION but the inner parentheses surrounding the SELECT statements within the UNION.

Thus the correct SQL output should be:

SELECT * FROM entries WHERE 
        (id IN 
                (
                        SELECT entry_id FROM address_transactions_to WHERE (address_id == (SELECT id FROM addresses WHERE (rcd_hash = X'6a0c800d303c0260dd998e6609f89909974d9576ec72d070f9ec7aa493e84884')))
                        UNION ALL 
                        SELECT entry_id FROM address_transactions_from WHERE (address_id == (SELECT id FROM addresses WHERE (rcd_hash = X'6a0c800d303c0260dd998e6609f89909974d9576ec72d070f9ec7aa493e84884')))
                )
        )
LIMIT 25 OFFSET 1

The fundamental issue is that only PostgreSQL and MySQL accept parentheses around individual SELECTs within a UNION, which is not standard SQL syntax.

See https://www.w3schools.com/sql/sql_union.asp and https://dev.mysql.com/doc/refman/8.0/en/union.html for reference. When the SELECT needs to be wrapped in parentheses depends on whether a LIMIT or ORDER BY clause is

I believe the best solution is to simply not support this MySQL/PostgreSQL syntax directly. If a user wants to form (SELECT...LIMIT) UNION (SELECT...LIMIT) then they could use the SelectBySql() function and use (?) around their query.

I'm making a comment to avoid the issue being marked as stale.

I have to say that the bot that is automatically closing issues is incredibly bad practice and IMO completely negligent. This package has numerous bugs that appear to have been closed simply because the maintainers are not willing to respond and use a bot to automatically close issues.

sound good to me. could you reopen the old pr?

commented

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@taylorchu I did that...

Any further interest in resolving this?

commented

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.