blob: 5e820829b5f7950c50406b95b93a566c9ae5c39a [file] [log] [blame]
/*
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package dal
import (
"database/sql"
"github.com/apache/incubator-devlake/errors"
"reflect"
"gorm.io/gorm/schema"
)
type Clause struct {
Type string
Data interface{}
}
// ColumnMeta column type interface
type ColumnMeta interface {
Name() string
DatabaseTypeName() string // varchar
ColumnType() (columnType string, ok bool) // varchar(64)
PrimaryKey() (isPrimaryKey bool, ok bool)
AutoIncrement() (isAutoIncrement bool, ok bool)
Length() (length int64, ok bool)
DecimalSize() (precision int64, scale int64, ok bool)
Nullable() (nullable bool, ok bool)
Unique() (unique bool, ok bool)
ScanType() reflect.Type
Comment() (value string, ok bool)
DefaultValue() (value string, ok bool)
}
// Dal aims to facilitate an isolation between DBS and our System by defining a set of operations should a DBS provide
type Dal interface {
// AutoMigrate runs auto migration for given entity
AutoMigrate(entity interface{}, clauses ...Clause) errors.Error
// AddColumn add column for the table
AddColumn(table, columnName, columnType string) errors.Error
// DropColumn drop column from the table
DropColumn(table, columnName string) errors.Error
// Exec executes raw sql query
Exec(query string, params ...interface{}) errors.Error
// RawCursor executes raw sql query and returns a database cursor
RawCursor(query string, params ...interface{}) (*sql.Rows, errors.Error)
// Cursor returns a database cursor, cursor is especially useful when handling big amount of rows of data
Cursor(clauses ...Clause) (*sql.Rows, errors.Error)
// Fetch loads row data from `cursor` into `dst`
Fetch(cursor *sql.Rows, dst interface{}) errors.Error
// All loads matched rows from database to `dst`, USE IT WITH CAUTIOUS!!
All(dst interface{}, clauses ...Clause) errors.Error
// First loads first matched row from database to `dst`, error will be returned if no records were found
First(dst interface{}, clauses ...Clause) errors.Error
// Count matched rows from database
Count(clauses ...Clause) (int64, errors.Error)
// Pluck used to query single column
Pluck(column string, dest interface{}, clauses ...Clause) errors.Error
// Create insert record to database
Create(entity interface{}, clauses ...Clause) errors.Error
// Update updates record
Update(entity interface{}, clauses ...Clause) errors.Error
// UpdateColumns batch records in database
UpdateColumns(entity interface{}, clauses ...Clause) errors.Error
// CreateOrUpdate tries to create the record, or fallback to update all if failed
CreateOrUpdate(entity interface{}, clauses ...Clause) errors.Error
// CreateIfNotExist tries to create the record if not exist
CreateIfNotExist(entity interface{}, clauses ...Clause) errors.Error
// Delete records from database
Delete(entity interface{}, clauses ...Clause) errors.Error
// AllTables returns all tables in database
AllTables() ([]string, errors.Error)
// GetColumns returns table columns in database
GetColumns(dst schema.Tabler, filter func(columnMeta ColumnMeta) bool) (cms []ColumnMeta, err errors.Error)
// GetPrimaryKeyFields get the PrimaryKey from `gorm` tag
GetPrimaryKeyFields(t reflect.Type) []reflect.StructField
// Dialect returns the dialect of current database
Dialect() string
}
// GetColumnNames returns table Column Names in database
func GetColumnNames(d Dal, dst schema.Tabler, filter func(columnMeta ColumnMeta) bool) (names []string, err errors.Error) {
columns, err := d.GetColumns(dst, filter)
if err != nil {
return
}
for _, pkColumn := range columns {
names = append(names, pkColumn.Name())
}
return
}
// GetPrimarykeyColumns get returns PrimaryKey table Meta in database
func GetPrimarykeyColumns(d Dal, dst schema.Tabler) ([]ColumnMeta, errors.Error) {
return d.GetColumns(dst, func(columnMeta ColumnMeta) bool {
isPrimaryKey, ok := columnMeta.PrimaryKey()
return isPrimaryKey && ok
})
}
// GetPrimarykeyColumnNames get returns PrimaryKey Column Names in database
func GetPrimarykeyColumnNames(d Dal, dst schema.Tabler) (names []string, err errors.Error) {
pkColumns, err := GetPrimarykeyColumns(d, dst)
if err != nil {
return
}
for _, pkColumn := range pkColumns {
names = append(names, pkColumn.Name())
}
return
}
type DalClause struct {
Expr string
Params []interface{}
}
const JoinClause string = "Join"
// Join creates a new JoinClause
func Join(clause string, params ...interface{}) Clause {
return Clause{Type: JoinClause, Data: DalClause{clause, params}}
}
const WhereClause string = "Where"
// Where creates a new WhereClause
func Where(clause string, params ...interface{}) Clause {
return Clause{Type: WhereClause, Data: DalClause{clause, params}}
}
const LimitClause string = "Limit"
// Limit creates a new LimitClause
func Limit(limit int) Clause {
return Clause{Type: LimitClause, Data: limit}
}
const OffsetClause string = "Offset"
// Offset creates a new OffsetClause
func Offset(offset int) Clause {
return Clause{Type: OffsetClause, Data: offset}
}
const FromClause string = "From"
// From creates a new TableClause
func From(table interface{}) Clause {
return Clause{Type: FromClause, Data: table}
}
const SelectClause string = "Select"
// Select creates a new TableClause
func Select(fields string) Clause {
return Clause{Type: SelectClause, Data: fields}
}
const OrderbyClause string = "OrderBy"
// Orderby creates a new Orderby clause
func Orderby(expr string) Clause {
return Clause{Type: OrderbyClause, Data: expr}
}
const GroupbyClause string = "GroupBy"
// Groupby creates a new Groupby clause
func Groupby(expr string) Clause {
return Clause{Type: GroupbyClause, Data: expr}
}
const HavingClause string = "Having"
// Having creates a new Having clause
func Having(clause string, params ...interface{}) Clause {
return Clause{Type: HavingClause, Data: DalClause{clause, params}}
}