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 }