diff --git a/case.go b/case.go index 15ed01f..88a07ff 100644 --- a/case.go +++ b/case.go @@ -2,12 +2,14 @@ package sqlingo import "strings" +// CaseExpression indicates the status in a CASE statement type CaseExpression interface { WhenThen(when BooleanExpression, then interface{}) CaseExpression Else(value interface{}) CaseExpressionWithElse End() Expression } +// CaseExpressionWithElse indicates the status in CASE ... ELSE ... statement type CaseExpressionWithElse interface { End() Expression } @@ -23,6 +25,7 @@ type whenThen struct { then interface{} } +// Case initiates a CASE statement func Case() CaseExpression { return caseStatus{} } diff --git a/common.go b/common.go index 4e010ab..698058b 100644 --- a/common.go +++ b/common.go @@ -6,11 +6,13 @@ import ( "strings" ) +// Model is the interface of generated model struct type Model interface { GetTable() Table GetValues() []interface{} } +// Assignment is an assignment statement type Assignment interface { GetSQL(scope scope) (string, error) } diff --git a/cursor.go b/cursor.go index d98b005..9068374 100644 --- a/cursor.go +++ b/cursor.go @@ -7,6 +7,7 @@ import ( "strconv" ) +// Cursor is the interface of a row cursor. type Cursor interface { Next() bool Scan(dest ...interface{}) error diff --git a/database.go b/database.go index 564ff54..e3a8d99 100644 --- a/database.go +++ b/database.go @@ -6,24 +6,42 @@ import ( "time" ) +// Database is the interface of a database with underlying sql.DB object. type Database interface { + // Get the underlying sql.DB object of the database GetDB() *sql.DB BeginTx(ctx context.Context, opts *sql.TxOptions, f func(tx Transaction) error) error + // Executes a query and return the cursor Query(sql string) (Cursor, error) + // Executes a query with context and return the cursor QueryContext(ctx context.Context, sqlString string) (Cursor, error) + // Executes a statement Execute(sql string) (sql.Result, error) + // Executes a statement with context ExecuteContext(ctx context.Context, sql string) (sql.Result, error) + // Set the logger function SetLogger(logger func(sql string, durationNano int64)) + // Set the retry policy function. + // The retry policy function returns true if needs retry. SetRetryPolicy(retryPolicy func(err error) bool) + // enable or disable caller info EnableCallerInfo(enableCallerInfo bool) + // Set a interceptor function SetInterceptor(interceptor InterceptorFunc) + // Initiate a SELECT statement Select(fields ...interface{}) selectWithFields + // Initiate a SELECT DISTINCT statement SelectDistinct(fields ...interface{}) selectWithFields + // Initiate a SELECT * FROM statement SelectFrom(tables ...Table) selectWithTables + // Initiate a INSERT INTO statement InsertInto(table Table) insertWithTable + // Initiate a REPLACE INTO statement ReplaceInto(table Table) insertWithTable + // Initiate a UPDATE statement Update(table Table) updateWithSet + // Initiate a DELETE FROM statement DeleteFrom(table Table) deleteWithTable } @@ -58,6 +76,7 @@ func (d *database) SetInterceptor(interceptor InterceptorFunc) { d.interceptor = interceptor } +// Open a database, similar to sql.Open func Open(driverName string, dataSourceName string) (db Database, err error) { var sqlDB *sql.DB if dataSourceName != "" { @@ -80,9 +99,8 @@ func (d database) GetDB() *sql.DB { func (d database) getTxOrDB() txOrDB { if d.tx != nil { return d.tx - } else { - return d.db } + return d.db } func (d database) Query(sqlString string) (Cursor, error) { @@ -118,6 +136,9 @@ func (d database) queryContextOnce(ctx context.Context, sqlStringWithCallerInfo interceptor := d.interceptor var rows *sql.Rows invoker := func(ctx context.Context, sql string) (err error) { + if ctx == nil { + ctx = context.Background() + } rows, err = d.getTxOrDB().QueryContext(ctx, sql) return } diff --git a/expression.go b/expression.go index 7003e9c..a08bffc 100644 --- a/expression.go +++ b/expression.go @@ -6,15 +6,23 @@ import ( "strconv" ) +// Expression is the interface of a SQL expression. type Expression interface { + // get the SQL string GetSQL(scope scope) (string, error) getOperatorPriority() int + // <> operator NotEquals(other interface{}) BooleanExpression + // == operator Equals(other interface{}) BooleanExpression + // < operator LessThan(other interface{}) BooleanExpression + // <= operator LessThanOrEquals(other interface{}) BooleanExpression + // > operator GreaterThan(other interface{}) BooleanExpression + // >= operator GreaterThanOrEquals(other interface{}) BooleanExpression IsNull() BooleanExpression @@ -29,10 +37,12 @@ type Expression interface { IfNull(altValue interface{}) Expression } +// Alias is the interface of an table/column alias. type Alias interface { GetSQL(scope scope) (string, error) } +// BooleanExpression is the interface of a SQL expression with boolean value. type BooleanExpression interface { Expression And(other interface{}) BooleanExpression @@ -40,6 +50,7 @@ type BooleanExpression interface { Not() BooleanExpression } +// NumberExpression is the interface of a SQL expression with number value. type NumberExpression interface { Expression Add(other interface{}) NumberExpression @@ -55,6 +66,7 @@ type NumberExpression interface { Max() UnknownExpression } +// StringExpression is the interface of a SQL expression with string value. type StringExpression interface { Expression Min() UnknownExpression @@ -63,6 +75,7 @@ type StringExpression interface { Contains(substring string) BooleanExpression } +// UnknownExpression is the interface of a SQL expression with unknown value. type UnknownExpression interface { Expression And(other interface{}) BooleanExpression @@ -116,6 +129,7 @@ func falseExpression() expression { } } +// Raw create a raw SQL statement func Raw(sql string) UnknownExpression { return expression{ sql: sql, @@ -123,6 +137,7 @@ func Raw(sql string) UnknownExpression { } } +// And creates an expression with AND operator. func And(expressions ...BooleanExpression) (result BooleanExpression) { if len(expressions) == 0 { result = trueExpression() @@ -138,6 +153,7 @@ func And(expressions ...BooleanExpression) (result BooleanExpression) { return } +// Or creates an expression with OR operator. func Or(expressions ...BooleanExpression) (result BooleanExpression) { if len(expressions) == 0 { result = falseExpression() @@ -490,20 +506,20 @@ func (e expression) NotIn(values ...interface{}) BooleanExpression { return expression{builder: builder, priority: 11} } -type JoinerFunc = func(exprSql, valuesSql string) string -type BooleanFunc = func(other interface{}) BooleanExpression -type BuilderFunc = func(scope scope) (string, error) +type joinerFunc = func(exprSql, valuesSql string) string +type booleanFunc = func(other interface{}) BooleanExpression +type builderFunc = func(scope scope) (string, error) -func (e expression) getBuilder(single BooleanFunc, joiner JoinerFunc, values ...interface{}) BuilderFunc { +func (e expression) getBuilder(single booleanFunc, joiner joinerFunc, values ...interface{}) builderFunc { return func(scope scope) (string, error) { var valuesSql string var err error if len(values) == 1 { value := values[0] - if select_, ok := value.(toSelectFinal); ok { + if selectStatus, ok := value.(toSelectFinal); ok { // IN subquery - valuesSql, err = select_.GetSQL() + valuesSql, err = selectStatus.GetSQL() if err != nil { return "", err } diff --git a/field.go b/field.go index 7e75132..979baa2 100644 --- a/field.go +++ b/field.go @@ -2,18 +2,22 @@ package sqlingo import "strings" +// Field is the interface of a generated field. type Field interface { Expression } +// NumberField is the interface of a generated field of number type. type NumberField interface { NumberExpression } +// BooleanField is the interface of a generated field of boolean type. type BooleanField interface { BooleanExpression } +// StringField is the interface of a generated field of string type. type StringField interface { StringExpression } @@ -41,21 +45,24 @@ func newFieldExpression(tableName string, fieldName string) expression { } } +// NewNumberField creates a reference to a number field. It should only be called from generated code. func NewNumberField(tableName string, fieldName string) NumberField { return newFieldExpression(tableName, fieldName) } +// NewBooleanField creates a reference to a boolean field. It should only be called from generated code. func NewBooleanField(tableName string, fieldName string) BooleanField { return newFieldExpression(tableName, fieldName) } +// NewStringField creates a reference to a string field. It should only be called from generated code. func NewStringField(tableName string, fieldName string) StringField { return newFieldExpression(tableName, fieldName) } -type FieldList []Field +type fieldList []Field -func (fields FieldList) GetSQL(scope scope) (string, error) { +func (fields fieldList) GetSQL(scope scope) (string, error) { isSingleTable := len(scope.Tables) == 1 && scope.lastJoin == nil var sb strings.Builder if len(fields) == 0 { diff --git a/field_test.go b/field_test.go index 0eb30bf..4bf332f 100644 --- a/field_test.go +++ b/field_test.go @@ -37,7 +37,7 @@ func TestField(t *testing.T) { assertValue(t, NewBooleanField("t1", "f1").Equals(true), "`t1`.`f1` = 1") assertValue(t, NewStringField("t1", "f1").Equals("x"), "`t1`.`f1` = 'x'") - sql, _ := FieldList{}.GetSQL(scope{ + sql, _ := fieldList{}.GetSQL(scope{ Tables: []Table{ &dummyTable{}, }, @@ -46,7 +46,7 @@ func TestField(t *testing.T) { t.Error(sql) } - sql, _ = FieldList{}.GetSQL(scope{ + sql, _ = fieldList{}.GetSQL(scope{ Tables: []Table{ &dummyTable{}, &dummyTable{}, @@ -56,7 +56,7 @@ func TestField(t *testing.T) { t.Error(sql) } - if _, err := (FieldList{ + if _, err := (fieldList{ expression{builder: func(scope scope) (string, error) { return "", errors.New("error") }}, diff --git a/function.go b/function.go index fb699c9..4139423 100644 --- a/function.go +++ b/function.go @@ -10,26 +10,32 @@ func function(name string, args ...interface{}) expression { }} } +// Function creates an expression of the call to specified function. func Function(name string, args ...interface{}) Expression { return function(name, args...) } +// Concat creates an expression of CONCAT function. func Concat(args ...interface{}) StringExpression { return function("CONCAT", args...) } +// Count creates an expression of COUNT aggregator. func Count(arg interface{}) NumberExpression { return function("COUNT", arg) } +// If creates an expression of IF function. func If(predicate Expression, trueValue interface{}, falseValue interface{}) (result Expression) { return function("IF", predicate, trueValue, falseValue) } +// Length creates an expression of LENGTH function. func Length(arg interface{}) NumberExpression { return function("LENGTH", arg) } +// Sum creates an expression of SUM aggregator. func Sum(arg interface{}) NumberExpression { return function("SUM", arg) } diff --git a/interceptor.go b/interceptor.go index 3e26bcf..da815f7 100644 --- a/interceptor.go +++ b/interceptor.go @@ -4,7 +4,10 @@ import ( "context" ) +// InvokerFunc is the function type of the actual invoker. It should be called in an interceptor. type InvokerFunc = func(ctx context.Context, sql string) error + +// InterceptorFunc is the function type of an interceptor. An interceptor should implement this function to fulfill it's purpose. type InterceptorFunc = func(ctx context.Context, sql string, invoker InvokerFunc) error // TODO: add some common interceptors diff --git a/order.go b/order.go index 3f0104f..0ecc986 100644 --- a/order.go +++ b/order.go @@ -1,5 +1,6 @@ package sqlingo +// OrderBy indicates the ORDER BY column and the status of descending order. type OrderBy interface { GetSQL(scope scope) (string, error) } diff --git a/select.go b/select.go index 30f96e3..a47fda1 100644 --- a/select.go +++ b/select.go @@ -1,7 +1,7 @@ package sqlingo import ( - . "context" + "context" "errors" "reflect" "strconv" @@ -101,7 +101,7 @@ type selectWithLock interface { } type toSelectWithContext interface { - WithContext(ctx Context) toSelectFinal + WithContext(ctx context.Context) toSelectFinal } type toSelectFinal interface { @@ -123,14 +123,14 @@ type join struct { type selectStatus struct { scope scope distinct bool - fields FieldList + fields fieldList where BooleanExpression orderBys []OrderBy groupBys []Expression having BooleanExpression limit *int offset int - ctx Context + ctx context.Context lock string } @@ -263,8 +263,8 @@ func (s selectStatus) ForUpdate() selectWithLock { func (s selectStatus) asDerivedTable(name string) Table { return derivedTable{ - name: name, - select_: s, + name: name, + selectStatus: s, } } @@ -364,26 +364,18 @@ func (s selectStatus) GetSQL() (string, error) { return sb.String(), nil } -func (s selectStatus) WithContext(ctx Context) toSelectFinal { +func (s selectStatus) WithContext(ctx context.Context) toSelectFinal { s.ctx = ctx return s } -func (s selectStatus) getContext() Context { - if s.ctx != nil { - return s.ctx - } else { - return Background() - } -} - func (s selectStatus) FetchCursor() (Cursor, error) { sqlString, err := s.GetSQL() if err != nil { return nil, err } - cursor, err := s.scope.Database.QueryContext(s.getContext(), sqlString) + cursor, err := s.scope.Database.QueryContext(s.ctx, sqlString) if err != nil { return nil, err } diff --git a/table.go b/table.go index 1c11cc3..880ac3a 100644 --- a/table.go +++ b/table.go @@ -1,5 +1,6 @@ package sqlingo +// Table is the interface of a generated table. type Table interface { GetName() string GetSQL(scope scope) string @@ -27,13 +28,14 @@ func (t table) getOperatorPriority() int { return 0 } +// NewTable creates a reference to a table. It should only be called from generated code. func NewTable(name string) Table { return table{name: name, sqlDialects: quoteIdentifier(name)} } type derivedTable struct { - name string - select_ selectStatus + name string + selectStatus selectStatus } func (t derivedTable) GetFieldByName(name string) Field { @@ -53,10 +55,10 @@ func (t derivedTable) GetName() string { } func (t derivedTable) GetSQL(scope scope) string { - sql, _ := t.select_.GetSQL() + sql, _ := t.selectStatus.GetSQL() return "(" + sql + ") AS " + t.name } func (t derivedTable) GetFields() []Field { - return t.select_.fields + return t.selectStatus.fields } diff --git a/table_test.go b/table_test.go index 8d1ecc9..c5cd8c0 100644 --- a/table_test.go +++ b/table_test.go @@ -12,7 +12,7 @@ func TestTable(t *testing.T) { func TestDerivedTable(t *testing.T) { dummyFields := []Field{NewNumberField("table", "field")} dt := derivedTable{ - select_: selectStatus{ + selectStatus: selectStatus{ fields: dummyFields, }, } diff --git a/transaction.go b/transaction.go index d51cc9d..00a6778 100644 --- a/transaction.go +++ b/transaction.go @@ -5,6 +5,7 @@ import ( "database/sql" ) +// Transaction is the interface of a transaction with underlying sql.Tx object. type Transaction interface { GetDB() *sql.DB GetTx() *sql.Tx