...

Source file src/github.com/doug-martin/goqu/v9/sqlgen/select_sql_generator.go

Documentation: github.com/doug-martin/goqu/v9/sqlgen

     1  package sqlgen
     2  
     3  import (
     4  	"github.com/doug-martin/goqu/v9/exp"
     5  	"github.com/doug-martin/goqu/v9/internal/errors"
     6  	"github.com/doug-martin/goqu/v9/internal/sb"
     7  )
     8  
     9  type (
    10  	// An adapter interface to be used by a Dataset to generate SQL for a specific dialect.
    11  	// See DefaultAdapter for a concrete implementation and examples.
    12  	SelectSQLGenerator interface {
    13  		Dialect() string
    14  		Generate(b sb.SQLBuilder, clauses exp.SelectClauses)
    15  	}
    16  	// The default adapter. This class should be used when building a new adapter. When creating a new adapter you can
    17  	// either override methods, or more typically update default values.
    18  	// See (github.com/doug-martin/goqu/dialect/postgres)
    19  	selectSQLGenerator struct {
    20  		CommonSQLGenerator
    21  	}
    22  )
    23  
    24  func ErrNotSupportedJoinType(j exp.JoinExpression) error {
    25  	return errors.New("dialect does not support %v", j.JoinType())
    26  }
    27  
    28  func ErrJoinConditionRequired(j exp.JoinExpression) error {
    29  	return errors.New("join condition required for conditioned join %v", j.JoinType())
    30  }
    31  
    32  func ErrDistinctOnNotSupported(dialect string) error {
    33  	return errors.New("dialect does not support DISTINCT ON clause [dialect=%s]", dialect)
    34  }
    35  
    36  func ErrWindowNotSupported(dialect string) error {
    37  	return errors.New("dialect does not support WINDOW clause [dialect=%s]", dialect)
    38  }
    39  
    40  var ErrNoWindowName = errors.New("window expresion has no valid name")
    41  
    42  func NewSelectSQLGenerator(dialect string, do *SQLDialectOptions) SelectSQLGenerator {
    43  	return &selectSQLGenerator{NewCommonSQLGenerator(dialect, do)}
    44  }
    45  
    46  func (ssg *selectSQLGenerator) Generate(b sb.SQLBuilder, clauses exp.SelectClauses) {
    47  	for _, f := range ssg.DialectOptions().SelectSQLOrder {
    48  		if b.Error() != nil {
    49  			return
    50  		}
    51  		switch f {
    52  		case CommonTableSQLFragment:
    53  			ssg.ExpressionSQLGenerator().Generate(b, clauses.CommonTables())
    54  		case SelectSQLFragment:
    55  			ssg.SelectSQL(b, clauses)
    56  		case SelectWithLimitSQLFragment:
    57  			ssg.SelectWithLimitSQL(b, clauses)
    58  		case FromSQLFragment:
    59  			ssg.FromSQL(b, clauses.From())
    60  		case JoinSQLFragment:
    61  			ssg.JoinSQL(b, clauses.Joins())
    62  		case WhereSQLFragment:
    63  			ssg.WhereSQL(b, clauses.Where())
    64  		case GroupBySQLFragment:
    65  			ssg.GroupBySQL(b, clauses.GroupBy())
    66  		case HavingSQLFragment:
    67  			ssg.HavingSQL(b, clauses.Having())
    68  		case WindowSQLFragment:
    69  			ssg.WindowSQL(b, clauses.Windows())
    70  		case CompoundsSQLFragment:
    71  			ssg.CompoundsSQL(b, clauses.Compounds())
    72  		case OrderSQLFragment:
    73  			ssg.OrderSQL(b, clauses.Order())
    74  		case OrderWithOffsetFetchSQLFragment:
    75  			ssg.OrderWithOffsetFetchSQL(b, clauses.Order(), clauses.Offset(), clauses.Limit())
    76  		case LimitSQLFragment:
    77  			ssg.LimitSQL(b, clauses.Limit())
    78  		case OffsetSQLFragment:
    79  			ssg.OffsetSQL(b, clauses.Offset())
    80  		case ForSQLFragment:
    81  			ssg.ForSQL(b, clauses.Lock())
    82  		default:
    83  			b.SetError(ErrNotSupportedFragment("SELECT", f))
    84  		}
    85  	}
    86  }
    87  
    88  func (ssg *selectSQLGenerator) selectSQLCommon(b sb.SQLBuilder, clauses exp.SelectClauses) {
    89  	dc := clauses.Distinct()
    90  	if dc != nil {
    91  		b.Write(ssg.DialectOptions().DistinctFragment)
    92  		if !dc.IsEmpty() {
    93  			if ssg.DialectOptions().SupportsDistinctOn {
    94  				b.Write(ssg.DialectOptions().OnFragment).WriteRunes(ssg.DialectOptions().LeftParenRune)
    95  				ssg.ExpressionSQLGenerator().Generate(b, dc)
    96  				b.WriteRunes(ssg.DialectOptions().RightParenRune, ssg.DialectOptions().SpaceRune)
    97  			} else {
    98  				b.SetError(ErrDistinctOnNotSupported(ssg.Dialect()))
    99  				return
   100  			}
   101  		} else {
   102  			b.WriteRunes(ssg.DialectOptions().SpaceRune)
   103  		}
   104  	}
   105  
   106  	if cols := clauses.Select(); clauses.IsDefaultSelect() || len(cols.Columns()) == 0 {
   107  		b.WriteRunes(ssg.DialectOptions().StarRune)
   108  	} else {
   109  		ssg.ExpressionSQLGenerator().Generate(b, cols)
   110  	}
   111  }
   112  
   113  // Adds the SELECT clause and columns to a sql statement
   114  func (ssg *selectSQLGenerator) SelectSQL(b sb.SQLBuilder, clauses exp.SelectClauses) {
   115  	b.Write(ssg.DialectOptions().SelectClause).WriteRunes(ssg.DialectOptions().SpaceRune)
   116  	ssg.selectSQLCommon(b, clauses)
   117  }
   118  
   119  // Adds the SELECT clause along with LIMIT to a SQL statement (e.g. MSSQL dialect: SELECT TOP 10 ...)
   120  func (ssg *selectSQLGenerator) SelectWithLimitSQL(b sb.SQLBuilder, clauses exp.SelectClauses) {
   121  	b.Write(ssg.DialectOptions().SelectClause).WriteRunes(ssg.DialectOptions().SpaceRune)
   122  	if clauses.Offset() == 0 && clauses.Limit() != nil {
   123  		ssg.LimitSQL(b, clauses.Limit())
   124  		b.WriteRunes(ssg.DialectOptions().SpaceRune)
   125  	}
   126  	ssg.selectSQLCommon(b, clauses)
   127  }
   128  
   129  // Generates the JOIN clauses for an SQL statement
   130  func (ssg *selectSQLGenerator) JoinSQL(b sb.SQLBuilder, joins exp.JoinExpressions) {
   131  	if len(joins) > 0 {
   132  		for _, j := range joins {
   133  			joinType, ok := ssg.DialectOptions().JoinTypeLookup[j.JoinType()]
   134  			if !ok {
   135  				b.SetError(ErrNotSupportedJoinType(j))
   136  				return
   137  			}
   138  			b.Write(joinType)
   139  			ssg.ExpressionSQLGenerator().Generate(b, j.Table())
   140  			if t, ok := j.(exp.ConditionedJoinExpression); ok {
   141  				if t.IsConditionEmpty() {
   142  					b.SetError(ErrJoinConditionRequired(j))
   143  					return
   144  				}
   145  				ssg.joinConditionSQL(b, t.Condition())
   146  			}
   147  		}
   148  	}
   149  }
   150  
   151  // Generates the GROUP BY clause for an SQL statement
   152  func (ssg *selectSQLGenerator) GroupBySQL(b sb.SQLBuilder, groupBy exp.ColumnListExpression) {
   153  	if groupBy != nil && len(groupBy.Columns()) > 0 {
   154  		b.Write(ssg.DialectOptions().GroupByFragment)
   155  		ssg.ExpressionSQLGenerator().Generate(b, groupBy)
   156  	}
   157  }
   158  
   159  // Generates the HAVING clause for an SQL statement
   160  func (ssg *selectSQLGenerator) HavingSQL(b sb.SQLBuilder, having exp.ExpressionList) {
   161  	if having != nil && len(having.Expressions()) > 0 {
   162  		b.Write(ssg.DialectOptions().HavingFragment)
   163  		ssg.ExpressionSQLGenerator().Generate(b, having)
   164  	}
   165  }
   166  
   167  // Generates the OFFSET clause for an SQL statement
   168  func (ssg *selectSQLGenerator) OffsetSQL(b sb.SQLBuilder, offset uint) {
   169  	if offset > 0 {
   170  		b.Write(ssg.DialectOptions().OffsetFragment)
   171  		ssg.ExpressionSQLGenerator().Generate(b, offset)
   172  	}
   173  }
   174  
   175  // Generates the compound sql clause for an SQL statement (e.g. UNION, INTERSECT)
   176  func (ssg *selectSQLGenerator) CompoundsSQL(b sb.SQLBuilder, compounds []exp.CompoundExpression) {
   177  	for _, compound := range compounds {
   178  		ssg.ExpressionSQLGenerator().Generate(b, compound)
   179  	}
   180  }
   181  
   182  // Generates the FOR (aka "locking") clause for an SQL statement
   183  func (ssg *selectSQLGenerator) ForSQL(b sb.SQLBuilder, lockingClause exp.Lock) {
   184  	if lockingClause == nil {
   185  		return
   186  	}
   187  	switch lockingClause.Strength() {
   188  	case exp.ForNolock:
   189  		return
   190  	case exp.ForUpdate:
   191  		b.Write(ssg.DialectOptions().ForUpdateFragment)
   192  	case exp.ForNoKeyUpdate:
   193  		b.Write(ssg.DialectOptions().ForNoKeyUpdateFragment)
   194  	case exp.ForShare:
   195  		b.Write(ssg.DialectOptions().ForShareFragment)
   196  	case exp.ForKeyShare:
   197  		b.Write(ssg.DialectOptions().ForKeyShareFragment)
   198  	}
   199  
   200  	of := lockingClause.Of()
   201  	if ofLen := len(of); ofLen > 0 {
   202  		if ofFragment := ssg.DialectOptions().OfFragment; len(ofFragment) > 0 {
   203  			b.Write(ofFragment)
   204  			for i, table := range of {
   205  				ssg.ExpressionSQLGenerator().Generate(b, table)
   206  				if i < ofLen-1 {
   207  					b.WriteRunes(ssg.DialectOptions().CommaRune, ssg.DialectOptions().SpaceRune)
   208  				}
   209  			}
   210  			b.WriteRunes(ssg.DialectOptions().SpaceRune)
   211  		}
   212  	}
   213  
   214  	// the WAIT case is the default in Postgres, and is what you get if you don't specify NOWAIT or
   215  	// SKIP LOCKED. There's no special syntax for it in PG, so we don't do anything for it here
   216  	switch lockingClause.WaitOption() {
   217  	case exp.Wait:
   218  		return
   219  	case exp.NoWait:
   220  		b.Write(ssg.DialectOptions().NowaitFragment)
   221  	case exp.SkipLocked:
   222  		b.Write(ssg.DialectOptions().SkipLockedFragment)
   223  	}
   224  }
   225  
   226  func (ssg *selectSQLGenerator) WindowSQL(b sb.SQLBuilder, windows []exp.WindowExpression) {
   227  	weLen := len(windows)
   228  	if weLen == 0 {
   229  		return
   230  	}
   231  	if !ssg.DialectOptions().SupportsWindowFunction {
   232  		b.SetError(ErrWindowNotSupported(ssg.Dialect()))
   233  		return
   234  	}
   235  	b.Write(ssg.DialectOptions().WindowFragment)
   236  	for i, we := range windows {
   237  		if !we.HasName() {
   238  			b.SetError(ErrNoWindowName)
   239  		}
   240  		ssg.ExpressionSQLGenerator().Generate(b, we)
   241  		if i < weLen-1 {
   242  			b.WriteRunes(ssg.DialectOptions().CommaRune, ssg.DialectOptions().SpaceRune)
   243  		}
   244  	}
   245  }
   246  
   247  func (ssg *selectSQLGenerator) joinConditionSQL(b sb.SQLBuilder, jc exp.JoinCondition) {
   248  	switch t := jc.(type) {
   249  	case exp.JoinOnCondition:
   250  		ssg.joinOnConditionSQL(b, t)
   251  	case exp.JoinUsingCondition:
   252  		ssg.joinUsingConditionSQL(b, t)
   253  	}
   254  }
   255  
   256  func (ssg *selectSQLGenerator) joinUsingConditionSQL(b sb.SQLBuilder, jc exp.JoinUsingCondition) {
   257  	b.Write(ssg.DialectOptions().UsingFragment).
   258  		WriteRunes(ssg.DialectOptions().LeftParenRune)
   259  	ssg.ExpressionSQLGenerator().Generate(b, jc.Using())
   260  	b.WriteRunes(ssg.DialectOptions().RightParenRune)
   261  }
   262  
   263  func (ssg *selectSQLGenerator) joinOnConditionSQL(b sb.SQLBuilder, jc exp.JoinOnCondition) {
   264  	b.Write(ssg.DialectOptions().OnFragment)
   265  	ssg.ExpressionSQLGenerator().Generate(b, jc.On())
   266  }
   267  

View as plain text