squirrel icon indicating copy to clipboard operation
squirrel copied to clipboard

Does squirrel support, or plan to support, common table expressions

Open mojochao opened this issue 4 years ago • 8 comments

Is there a way to generate a CTE? I didn't see anything in docs mentioning this. I'm using PostgreSQL.

mojochao avatar Dec 31 '20 21:12 mojochao

Only via the generic Prefix method, e.g. .Select("*").Prefix("WITH ...") -> WITH ... SELECT *

lann avatar Jan 01 '21 00:01 lann

May be it solves your use case. Support CTE and UNION https://github.com/tnissen375/squirrel

Drop me a line if it does not work as expected. (Only tested with MySql)

tnissen375 avatar Feb 26 '22 12:02 tnissen375

@lann

There's currently a few forks of this that support CTEs. Any reason they are not getting merged back in? IMO, supporting CTEs/With Clause is a pretty common sql operation. I attempted to set them up in this repo yesterday and was never really pleased with the solution (using prefixes), so now I am using https://github.com/jack-t/squirrel but it looks like https://github.com/tnissen375/squirrel is running the same implementation and keeps the repo a little more up to date with the upstream changes here.

There's also an open PR here from this repo which seems to be a bit more fleshed out than some of the others.

I am fine with running a fork if need be; definitely appreciate all the hard work on this package. I am just curious if this feature will ever get merged back in?

tjsampson avatar Sep 19 '23 14:09 tjsampson

Probably not. From the readme:

Squirrel is "complete".

Bug fixes will still be merged (slowly). Bug reports are welcome, but I will not necessarily > respond to them. If another fork (or substantially similar project) actively improves on what > Squirrel does, let me know and I may link to it here.

lann avatar Sep 19 '23 16:09 lann

I also need this feature. I think CTE is a very common SQL requirement.

ilxqx avatar Nov 18 '23 16:11 ilxqx

I also need this feature. I think CTE is a very common SQL requirement.

@ilxqx I am just leveraging a fork that supports CTEs (there are a few out there) and then using a go mod replace to override this pkg:

// we are using the fork of squirrel because the original one does not support CTEs // https://pkg.go.dev/github.com/jack-t/squirrel // https://github.com/Masterminds/squirrel/issues/271#issuecomment-1725792877 replace github.com/Masterminds/squirrel v1.5.4 => github.com/jack-t/squirrel v1.6.0

Then you can use it like so:

package blah

import (
    sq "github.com/Masterminds/squirrel"
)

queryExpression := sq.Select(`col1,col2`).From('some_table')

queryCTE := sq.CTE{
    Alias:      "some_alias",
    Recursive:  false,
    Expression: queryExpression
}

mainQuery := sq.Select(`col_foo,col_bar`.From('other_table').WithCTE(queryCTE).InnerJoin('some_alias.col1 ON other_table.col_foo`) 

Hope this helps.

tjsampson avatar Nov 18 '23 20:11 tjsampson

@tjsampson Very nice, thank you.

ilxqx avatar Nov 20 '23 03:11 ilxqx

I was looking for cte support aswell, came up with this idea.

func With(b ...sq.SelectBuilder) (string, []any, error) {
	var args_all []any
	var sql_all string
	for i := 0; i < len(b); i++ {
		sql, args, err := b[i].ToSql()
		if err != nil {
			return "", nil, err
		}
		args_all = append(args_all, args...)

		if i == 0 {
			sql_all += fmt.Sprintf("with cte%v as (%s)", i+1, sql)
		} else if i > 0 && i < len(b)-1 {
			sql_all += fmt.Sprintf(", cte%v as (%s) ", i+1, sql)
		} else if i == len(b)-1 {
			sql_all += sql
		}
	}
	return sql_all, args_all, nil
}

use it like this, the cte names get auto generated eg. "cte1", "cte2" ...

sql, args, err := With(
  sq.Select("id").
	  From("tags").
	  Where(sq.Eq{"name": []string{ "tag1", "tag2" }).
	  GroupBy("id"),
  sq.Select("files.*").
	  From("files").
	  Join("file_tags on files.id = file_tags.id_file").
	  Where("file_tags.id_tag in cte1"),
  sq.Select("*").
	  From("cte2"),
)

sql == with cte1 as (SELECT id FROM tags WHERE name IN (?,?) GROUP BY id), cte2 as (SELECT files.* FROM files JOIN file_tags on files.id = file_tags.id_file WHERE file_tags.id_tag in cte1) SELECT * FROM cte2

frbrno avatar Jun 09 '24 15:06 frbrno