gitee.com/go-genie/sqlx@v1.0.3/connectors/postgresql/schemas.go (about)

     1  package postgresql
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/pkg/errors"
     6  	"regexp"
     7  	"strings"
     8  
     9  	"gitee.com/go-genie/sqlx"
    10  	"gitee.com/go-genie/sqlx/builder"
    11  )
    12  
    13  func toInterfaces(list ...string) []interface{} {
    14  	s := make([]interface{}, len(list))
    15  	for i, v := range list {
    16  		s[i] = v
    17  	}
    18  	return s
    19  }
    20  
    21  var reUsing = regexp.MustCompile(`USING ([^ ]+)`)
    22  
    23  const COMMAND_MIGRATE = "migrate"
    24  const COMMAND_GENERATE = "generate"
    25  
    26  func dbFromInformationSchema(db sqlx.DBExecutor, usage string) (*sqlx.Database, error) {
    27  	d := db.D()
    28  
    29  	dbName := d.Name
    30  	dbSchema := d.Schema
    31  	tableNames := d.Tables.TableNames()
    32  
    33  	d = sqlx.NewDatabase(dbName).WithSchema(dbSchema)
    34  
    35  	tableColumnSchema := SchemaDatabase.T(&ColumnSchema{}).WithSchema("information_schema")
    36  	columnSchemaList := make([]ColumnSchema, 0)
    37  
    38  	tableSchema := "public"
    39  	if d.Schema != "" {
    40  		tableSchema = d.Schema
    41  	}
    42  
    43  	tableCondition := builder.EmptyCond().And(tableColumnSchema.F("TABLE_SCHEMA").Eq(tableSchema))
    44  	switch usage {
    45  	case COMMAND_GENERATE:
    46  		tableCondition = tableCondition.And(tableColumnSchema.F("TABLE_NAME").RightLike("t_")).And(tableColumnSchema.F("TABLE_NAME").Neq("t_sql_meta_enum"))
    47  	case COMMAND_MIGRATE:
    48  		tableCondition = tableCondition.And(tableColumnSchema.F("TABLE_NAME").In(toInterfaces(tableNames...)...))
    49  	default:
    50  		return nil, errors.New("unknown usage")
    51  	}
    52  
    53  	stmt := builder.Select(tableColumnSchema.Columns.Clone()).From(tableColumnSchema,
    54  		builder.Where(
    55  			tableCondition,
    56  		),
    57  	)
    58  
    59  	err := db.QueryExprAndScan(stmt, &columnSchemaList)
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  
    64  	for i := range columnSchemaList {
    65  		columnSchema := columnSchemaList[i]
    66  
    67  		table := d.Table(columnSchema.TABLE_NAME)
    68  		if table == nil {
    69  			table = builder.T(columnSchema.TABLE_NAME)
    70  			d.AddTable(table)
    71  		}
    72  
    73  		table.AddCol(colFromColumnSchema(&columnSchema))
    74  	}
    75  
    76  	if tableColumnSchema.Columns.Len() != 0 {
    77  		tableIndexSchema := SchemaDatabase.T(&IndexSchema{})
    78  
    79  		indexCondition := builder.EmptyCond().And(tableIndexSchema.F("TABLE_SCHEMA").Eq(tableSchema))
    80  		switch usage {
    81  		case COMMAND_GENERATE:
    82  			indexCondition = indexCondition.And(tableIndexSchema.F("TABLE_NAME").RightLike("t_")).And(tableIndexSchema.F("TABLE_NAME").Neq("t_sql_meta_enum"))
    83  		case COMMAND_MIGRATE:
    84  			indexCondition = indexCondition.And(tableIndexSchema.F("TABLE_NAME").In(toInterfaces(tableNames...)...))
    85  		default:
    86  			return nil, errors.New("unknown usage")
    87  		}
    88  
    89  		indexList := make([]IndexSchema, 0)
    90  
    91  		err = db.QueryExprAndScan(
    92  			builder.Select(tableIndexSchema.Columns.Clone()).
    93  				From(
    94  					tableIndexSchema,
    95  					builder.Where(
    96  						indexCondition,
    97  					),
    98  				),
    99  			&indexList,
   100  		)
   101  
   102  		if err != nil {
   103  			return nil, err
   104  		}
   105  
   106  		for _, indexSchema := range indexList {
   107  			table := d.Table(indexSchema.TABLE_NAME)
   108  
   109  			key := &builder.Key{}
   110  			switch usage {
   111  			case COMMAND_GENERATE:
   112  				key.Name = strings.ToLower(indexSchema.INDEX_NAME)
   113  			case COMMAND_MIGRATE:
   114  				key.Name = strings.ToLower(indexSchema.INDEX_NAME[len(table.Name)+1:])
   115  			}
   116  
   117  			key.Method = strings.ToUpper(reUsing.FindString(indexSchema.INDEX_DEF)[6:])
   118  			key.IsUnique = strings.Contains(indexSchema.INDEX_DEF, "UNIQUE")
   119  
   120  			key.Def.Expr = strings.Replace(strings.TrimSpace(reUsing.Split(indexSchema.INDEX_DEF, 2)[1]), ", ", ",", -1)
   121  
   122  			table.AddKey(key)
   123  		}
   124  	}
   125  
   126  	return d, nil
   127  }
   128  
   129  var SchemaDatabase = sqlx.NewDatabase("INFORMATION_SCHEMA")
   130  
   131  func init() {
   132  	SchemaDatabase.Register(&ColumnSchema{})
   133  	SchemaDatabase.Register(&IndexSchema{})
   134  }
   135  
   136  func colFromColumnSchema(columnSchema *ColumnSchema) *builder.Column {
   137  	col := builder.Col(columnSchema.COLUMN_NAME)
   138  
   139  	defaultValue := columnSchema.COLUMN_DEFAULT
   140  
   141  	if defaultValue != "" {
   142  		col.AutoIncrement = strings.HasSuffix(columnSchema.COLUMN_DEFAULT, "_seq'::regclass)")
   143  
   144  		if !col.AutoIncrement {
   145  			if !strings.Contains(defaultValue, "'::") && '0' <= defaultValue[0] && defaultValue[0] <= '9' {
   146  				defaultValue = fmt.Sprintf("'%s'::integer", defaultValue)
   147  			}
   148  			col.Default = &defaultValue
   149  		}
   150  	}
   151  
   152  	dataType := columnSchema.DATA_TYPE
   153  
   154  	if col.AutoIncrement {
   155  		if strings.HasPrefix(dataType, "big") {
   156  			dataType = "bigserial"
   157  		} else {
   158  			dataType = "serial"
   159  		}
   160  	}
   161  
   162  	col.DataType = dataType
   163  
   164  	// numeric type
   165  	if columnSchema.NUMERIC_PRECISION > 0 {
   166  		col.Length = columnSchema.NUMERIC_PRECISION
   167  		col.Decimal = columnSchema.NUMERIC_SCALE
   168  	} else {
   169  		col.Length = columnSchema.CHARACTER_MAXIMUM_LENGTH
   170  	}
   171  
   172  	if columnSchema.IS_NULLABLE == "YES" {
   173  		col.Null = true
   174  	}
   175  
   176  	return col
   177  }
   178  
   179  type ColumnSchema struct {
   180  	TABLE_SCHEMA             string `db:"table_schema"`
   181  	TABLE_NAME               string `db:"table_name"`
   182  	COLUMN_NAME              string `db:"column_name"`
   183  	DATA_TYPE                string `db:"data_type"`
   184  	IS_NULLABLE              string `db:"is_nullable"`
   185  	COLUMN_DEFAULT           string `db:"column_default"`
   186  	CHARACTER_MAXIMUM_LENGTH uint64 `db:"character_maximum_length"`
   187  	NUMERIC_PRECISION        uint64 `db:"numeric_precision"`
   188  	NUMERIC_SCALE            uint64 `db:"numeric_scale"`
   189  }
   190  
   191  func (ColumnSchema) TableName() string {
   192  	return "columns"
   193  }
   194  
   195  type IndexSchema struct {
   196  	TABLE_SCHEMA string `db:"schemaname"`
   197  	TABLE_NAME   string `db:"tablename"`
   198  	INDEX_NAME   string `db:"indexname"`
   199  	INDEX_DEF    string `db:"indexdef"`
   200  }
   201  
   202  func (IndexSchema) TableName() string {
   203  	return "pg_indexes"
   204  }