Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support pretty printing in SQL() #226

Open
apstndb opened this issue Dec 17, 2024 · 0 comments
Open

Support pretty printing in SQL() #226

apstndb opened this issue Dec 17, 2024 · 0 comments

Comments

@apstndb
Copy link
Contributor

apstndb commented Dec 17, 2024

Pretty printing is wanted features, but it is hard to implement by memefish users.
If SQL() will be enhanced to support pretty printing, it will be very useful.

  • It is better to be default behavior.
  • It should support also printing without newlines and indentation(current behavior)
  • It may better to support indentation configuration.
  • If it is used for formatter, parse-unparse should retain comments.
    • I think it is not mandatory for SQL builder usecases.

Proposal

  • Introduce structs to have format context and options.
// FormatContext is container of format option and current indentation.
// Note: All methods of FormatContext must support nil receiver.
type FormatContext struct {
  // unexported
}

// FormatContextCompact is format context without newline and indentation.
func FormatContextCompact() *FormatContext

// FormatContextPretty is format context with newline and configured indentation.
func FormatContextPretty(indent int) *FormatContext

// newlineOr returns newline with indentation if formatOptionPretty is used.
// Otherwise, it returns argument string.
func (fc *FormatContext) newlineOr(s string) string

// indentScope executes inner function using FormatContext with deeper indentation.
func (fc *FormatContext) indentScope(f func(fc *FormatContext) string) string
  • Conceptually enhance Node.SQL(), sqlJoin, sqlOpt to support FormatContext.

Migration

There is two method to migrate.

a) Change Node.SQL(), sqlJoin, sqlOpt to receive FormatContext

  • If breaking change is permitted, it is possible to change Node.SQL().
  • It may be possible to migrate using text manipulation like sed.

b) Keep Node.SQL() signature and use NodeContext.sqlContext(*FormatContext) and (*FormatContext).SQL(Node)

  • SQL() can be implemented using sqlContext(*FormatContext).
  • It dosn't introduce breaking changes
  • Migration can be incremental
  • It also require FormatContext version of sqlJoin, sqlOpt.
  • Prototyping in PoC of pretty printing #227
// SQL is entry point of pretty printing.
// If node implements NodeFormat, it calls NodeFormat.sqlContext() instead of Node.SQL().
// If it is called with nil receiver, FormatContextCompact() is used instead.
func (fc *FormatContext) SQL(node Node) string

// NodeFormat is Node with FormatContext support.
// If it is implemented, (*FormatContext).SQL() calls sqlContext() instead of SQL()
type NodeFormat interface {
	Node

	// sqlContext is Node.SQL() with FormatContext conceptually.
	// If it is called with nil FormatContext, FormatContextCompact() is used instead.
	// Note: It would become to Node.SQL() finally.
	sqlContext(fmtCtx *FormatContext) string
}
Typical implementation of sqlContext()
before
func (s *Select) SQL() string {
	return "SELECT" +
		strOpt(s.AllOrDistinct != "", string(s.AllOrDistinct)+" ") +
		sqlOpt("", s.As, " ") +
		sqlJoin(s.Results, ", ") +
		sqlOpt(" ", s.From, "") +
		sqlOpt(" ", s.Where, "") +
		sqlOpt(" ", s.GroupBy, "") +
		sqlOpt(" ", s.Having, "")
after
func (s *Select) sqlContext(fc *FormatContext) string {
	return "SELECT" +
		strOpt(s.AllOrDistinct != "", " "+string(s.AllOrDistinct)) +
		sqlOptCtx(fc, " ", s.As, "") +
		fc.indentScope(func(fc *FormatContext) string {
			return strIfElse(len(s.Results) > 1, fc.newlineOr(" "), " ") +
				sqlJoinCtx(fc, s.Results, ","+fc.newlineOr(" "))
		}) +
		sqlOptCtx(fc, fc.newlineOr(" "), s.From, "") +
		sqlOptCtx(fc, fc.newlineOr(" "), s.Where, "") +
		sqlOptCtx(fc, fc.newlineOr(" "), s.GroupBy, "") +
		sqlOptCtx(fc, fc.newlineOr(" "), s.Having, "")
}

// SQL() can be implemented using sqlcontext()
func (s *Select) SQL() string {
	return s.sqlContext(nil)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant