2023-09-28 16:45:44 +02:00
|
|
|
package postgres
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2023-10-27 10:43:15 +02:00
|
|
|
"database/sql"
|
2023-09-28 16:45:44 +02:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"github.com/ManyakRus/crud_generator/internal/config"
|
2023-10-27 10:43:15 +02:00
|
|
|
"github.com/ManyakRus/crud_generator/internal/mini_func"
|
2023-09-28 16:45:44 +02:00
|
|
|
"github.com/ManyakRus/crud_generator/internal/types"
|
2023-10-27 10:43:15 +02:00
|
|
|
"github.com/ManyakRus/crud_generator/pkg/dbmeta"
|
2023-09-28 16:45:44 +02:00
|
|
|
"github.com/ManyakRus/starter/contextmain"
|
|
|
|
"github.com/ManyakRus/starter/log"
|
|
|
|
"github.com/ManyakRus/starter/postgres_gorm"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type TableColumn struct {
|
|
|
|
TableName string `json:"table_name" gorm:"column:table_name;default:''"`
|
|
|
|
ColumnName string `json:"column_name" gorm:"column:column_name;default:''"`
|
|
|
|
ColumnType string `json:"type_name" gorm:"column:type_name;default:''"`
|
|
|
|
ColumnIsIdentity string `json:"is_identity" gorm:"column:is_identity;default:''"`
|
2023-10-26 09:33:18 +02:00
|
|
|
ColumnIsNullable string `json:"is_nullable" gorm:"column:is_nullable;default:''"`
|
2023-09-28 16:45:44 +02:00
|
|
|
ColumnDescription string `json:"description" gorm:"column:description;default:''"`
|
|
|
|
ColumnTableKey string `json:"table_key" gorm:"column:table_key;default:''"`
|
|
|
|
ColumnColumnKey string `json:"column_key" gorm:"column:column_key;default:''"`
|
2023-11-03 13:21:35 +02:00
|
|
|
TableComment string `json:"table_comment" gorm:"column:table_comment;default:''"`
|
2023-09-28 16:45:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// FillMapTable - возвращает массив MassTable данными из БД
|
|
|
|
func FillMapTable() (map[string]*types.Table, error) {
|
|
|
|
var err error
|
|
|
|
//MassTable := make([]types.Table, 0)
|
|
|
|
MapTable := make(map[string]*types.Table, 0)
|
|
|
|
|
|
|
|
TextSQL := `
|
|
|
|
drop table if exists temp_keys;
|
|
|
|
CREATE TEMPORARY TABLE temp_keys (table_from text, column_from text, table_to text, column_to text);
|
|
|
|
|
|
|
|
------------------------------------------- Все внешние ключи ------------------------------
|
|
|
|
insert into temp_keys
|
|
|
|
SELECT
|
|
|
|
(select r.relname from pg_class r where r.oid = c.conrelid) as table_from,
|
|
|
|
UNNEST((select array_agg(attname) from pg_attribute where attrelid = c.conrelid and array[attnum] <@ c.conkey)) as column_from,
|
|
|
|
(select r.relname from pg_class r where r.oid = c.confrelid) as table_to,
|
|
|
|
a.attname as column_to
|
2023-11-03 13:21:35 +02:00
|
|
|
|
2023-09-28 16:45:44 +02:00
|
|
|
FROM
|
|
|
|
pg_constraint c
|
|
|
|
|
|
|
|
join
|
|
|
|
pg_attribute a
|
|
|
|
on
|
|
|
|
c.confrelid=a.attrelid and a.attnum = ANY(confkey)
|
|
|
|
|
|
|
|
WHERE 1=1
|
2023-11-03 13:21:35 +02:00
|
|
|
--and c.confrelid = (select oid from pg_class where relname = 'lawsuit_invoices')
|
2023-11-02 14:57:13 +02:00
|
|
|
--AND c.confrelid!=c.conrelid
|
2023-09-28 16:45:44 +02:00
|
|
|
;
|
|
|
|
|
|
|
|
------------------------------------------- Все таблицы и колонки ------------------------------
|
|
|
|
|
|
|
|
SELECT
|
|
|
|
c.table_name,
|
|
|
|
c.column_name,
|
|
|
|
c.udt_name as type_name,
|
|
|
|
c.is_identity as is_identity,
|
2023-10-26 09:33:18 +02:00
|
|
|
c.is_nullable as is_nullable,
|
2023-09-28 16:45:44 +02:00
|
|
|
COALESCE(pgd.description, '') as description,
|
|
|
|
COALESCE(keys.table_to, '') as table_key,
|
2023-11-03 13:21:35 +02:00
|
|
|
COALESCE(keys.column_to, '') as column_key,
|
|
|
|
(SELECT obj_description(oid) FROM pg_class as r WHERE relkind = 'r' and r.oid = st.relid) as table_comment
|
|
|
|
|
2023-09-28 16:45:44 +02:00
|
|
|
FROM
|
|
|
|
pg_catalog.pg_statio_all_tables as st
|
|
|
|
|
|
|
|
right join
|
|
|
|
information_schema.columns c
|
|
|
|
on
|
2023-11-03 13:21:35 +02:00
|
|
|
|
|
|
|
c.table_schema = st.schemaname
|
2023-09-28 16:45:44 +02:00
|
|
|
and c.table_name = st.relname
|
|
|
|
|
2023-11-03 13:21:35 +02:00
|
|
|
left join
|
|
|
|
pg_catalog.pg_description pgd
|
|
|
|
on
|
|
|
|
pgd.objoid = st.relid
|
|
|
|
and pgd.objsubid = c.ordinal_position
|
|
|
|
|
2023-09-28 16:45:44 +02:00
|
|
|
|
|
|
|
LEFT JOIN --внешние ключи
|
|
|
|
temp_keys as keys
|
|
|
|
ON
|
|
|
|
keys.table_from = c.table_name
|
|
|
|
and keys.column_from = c.column_name
|
|
|
|
|
|
|
|
|
|
|
|
LEFT JOIN --вьюхи
|
|
|
|
INFORMATION_SCHEMA.views as v
|
|
|
|
ON
|
|
|
|
v.table_schema = 'public'
|
|
|
|
and v.table_name = c.table_name
|
|
|
|
|
|
|
|
|
|
|
|
where 1=1
|
|
|
|
and c.table_schema='public'
|
|
|
|
and v.table_name is null
|
|
|
|
--INCLUDE_TABLES
|
|
|
|
--EXCLUDE_TABLES
|
2023-11-03 13:21:35 +02:00
|
|
|
--and c.table_name = 'lawsuit_invoices'
|
2023-09-28 16:45:44 +02:00
|
|
|
|
|
|
|
order by
|
|
|
|
table_name,
|
|
|
|
is_identity desc,
|
|
|
|
column_name
|
2023-11-03 13:21:35 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
2023-09-28 16:45:44 +02:00
|
|
|
`
|
|
|
|
|
|
|
|
SCHEMA := strings.Trim(postgres_gorm.Settings.DB_SCHEMA, " ")
|
|
|
|
if SCHEMA != "" {
|
|
|
|
TextSQL = strings.ReplaceAll(TextSQL, "public", SCHEMA)
|
|
|
|
}
|
|
|
|
|
|
|
|
if config.Settings.INCLUDE_TABLES != "" {
|
|
|
|
TextSQL = strings.ReplaceAll(TextSQL, "--INCLUDE_TABLES", "and c.table_name ~* '"+config.Settings.INCLUDE_TABLES+"'")
|
|
|
|
}
|
|
|
|
|
|
|
|
if config.Settings.EXCLUDE_TABLES != "" {
|
|
|
|
TextSQL = strings.ReplaceAll(TextSQL, "--EXCLUDE_TABLES", "and c.table_name !~* '"+config.Settings.EXCLUDE_TABLES+"'")
|
|
|
|
}
|
|
|
|
|
|
|
|
//соединение
|
|
|
|
ctxMain := contextmain.GetContext()
|
|
|
|
ctx, ctxCancelFunc := context.WithTimeout(ctxMain, time.Second*60)
|
|
|
|
defer ctxCancelFunc()
|
|
|
|
|
|
|
|
db := postgres_gorm.GetConnection()
|
|
|
|
db.WithContext(ctx)
|
|
|
|
|
|
|
|
//запрос
|
|
|
|
//запустим все запросы отдельно
|
|
|
|
var tx *gorm.DB
|
|
|
|
sqlSlice := strings.Split(TextSQL, ";")
|
|
|
|
len1 := len(sqlSlice)
|
|
|
|
for i, TextSQL1 := range sqlSlice {
|
|
|
|
//batch.Queue(TextSQL1)
|
|
|
|
if i == len1-1 {
|
|
|
|
tx = db.Raw(TextSQL1)
|
|
|
|
} else {
|
|
|
|
tx = db.Exec(TextSQL1)
|
|
|
|
//rows.Close()
|
|
|
|
}
|
|
|
|
err = tx.Error
|
|
|
|
if err != nil {
|
|
|
|
log.Panic("DB.Raw() error:", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//tx := db.Raw(TextSQL)
|
|
|
|
//err = tx.Error
|
|
|
|
//if err != nil {
|
|
|
|
// sError := fmt.Sprint("db.Raw() error: ", err)
|
|
|
|
// log.Panicln(sError)
|
|
|
|
// return MassTable, err
|
|
|
|
//}
|
|
|
|
|
|
|
|
//ответ в структуру
|
|
|
|
MassTableColumn := make([]TableColumn, 0)
|
|
|
|
tx = tx.Scan(&MassTableColumn)
|
|
|
|
err = tx.Error
|
|
|
|
if err != nil {
|
|
|
|
sError := fmt.Sprint("Get_error() error: ", err)
|
|
|
|
log.Panicln(sError)
|
|
|
|
return MapTable, err
|
|
|
|
}
|
|
|
|
|
|
|
|
//проверка 0 строк
|
|
|
|
if tx.RowsAffected == 0 {
|
|
|
|
sError := fmt.Sprint("db.Raw() RowsAffected =0 ")
|
|
|
|
log.Warn(sError)
|
|
|
|
err = errors.New(sError)
|
|
|
|
//log.Panicln(sError)
|
|
|
|
return MapTable, err
|
|
|
|
}
|
|
|
|
|
|
|
|
//заполним MapTable
|
|
|
|
MapColumns := make(map[string]types.Column, 0)
|
|
|
|
OrderNumberColumn := 0
|
|
|
|
OrderNumberTable := 0
|
|
|
|
TableName0 := ""
|
|
|
|
Table1 := CreateTable()
|
|
|
|
for _, v := range MassTableColumn {
|
|
|
|
if v.TableName != TableName0 {
|
|
|
|
OrderNumberColumn = 0
|
|
|
|
Table1.MapColumns = MapColumns
|
|
|
|
MapColumns = make(map[string]types.Column, 0)
|
|
|
|
if TableName0 != "" {
|
|
|
|
//MassTable = append(MassTable, Table1)
|
|
|
|
MapTable[TableName0] = Table1
|
|
|
|
OrderNumberTable++
|
|
|
|
}
|
|
|
|
Table1 = CreateTable()
|
|
|
|
Table1.Name = v.TableName
|
|
|
|
Table1.OrderNumber = OrderNumberTable
|
2023-11-03 13:21:35 +02:00
|
|
|
Table1.Comment = v.TableComment
|
2023-09-28 16:45:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Column1 := types.Column{}
|
|
|
|
Column1.Name = v.ColumnName
|
|
|
|
Column1.Type = v.ColumnType
|
2023-10-27 10:43:15 +02:00
|
|
|
|
|
|
|
//Type_go
|
|
|
|
SQLMapping1, ok := dbmeta.GetMappings()[Column1.Type]
|
|
|
|
if ok == false {
|
|
|
|
log.Panic("GetMappings() ", Column1.Type, " error: not found")
|
|
|
|
}
|
|
|
|
Type_go := SQLMapping1.GoType
|
|
|
|
Column1.TypeGo = Type_go
|
|
|
|
|
|
|
|
//
|
2023-09-28 16:45:44 +02:00
|
|
|
if v.ColumnIsIdentity == "YES" {
|
2023-10-26 09:33:18 +02:00
|
|
|
Column1.IsIdentity = true
|
|
|
|
}
|
|
|
|
if v.ColumnIsNullable == "YES" {
|
|
|
|
Column1.IsNullable = true
|
2023-09-28 16:45:44 +02:00
|
|
|
}
|
|
|
|
Column1.Description = v.ColumnDescription
|
|
|
|
Column1.OrderNumber = OrderNumberColumn
|
|
|
|
Column1.TableKey = v.ColumnTableKey
|
|
|
|
Column1.ColumnKey = v.ColumnColumnKey
|
|
|
|
|
|
|
|
MapColumns[v.ColumnName] = Column1
|
|
|
|
//Table1.Columns = append(Table1.Columns, Column1)
|
|
|
|
|
|
|
|
OrderNumberColumn++
|
|
|
|
TableName0 = v.TableName
|
|
|
|
}
|
2023-10-27 10:43:15 +02:00
|
|
|
|
|
|
|
//
|
2023-09-28 16:45:44 +02:00
|
|
|
if Table1.Name != "" {
|
|
|
|
Table1.MapColumns = MapColumns
|
|
|
|
MapTable[TableName0] = Table1
|
|
|
|
}
|
|
|
|
|
2023-10-27 10:43:15 +02:00
|
|
|
//FillTypeGo(MapTable)
|
|
|
|
|
|
|
|
FillIDMinimum(MapTable)
|
|
|
|
|
2023-09-28 16:45:44 +02:00
|
|
|
return MapTable, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func CreateTable() *types.Table {
|
|
|
|
Otvet := &types.Table{}
|
|
|
|
Otvet.MapColumns = make(map[string]types.Column, 0)
|
|
|
|
|
|
|
|
return Otvet
|
|
|
|
}
|
2023-10-27 10:43:15 +02:00
|
|
|
|
|
|
|
func FillIDMinimum(MapTable map[string]*types.Table) {
|
|
|
|
var err error
|
|
|
|
|
|
|
|
//соединение
|
|
|
|
db := postgres_gorm.GetConnection()
|
|
|
|
ctxMain := contextmain.GetContext()
|
|
|
|
|
|
|
|
for TableName, Table1 := range MapTable {
|
|
|
|
//текст запроса
|
|
|
|
NameID, TypeGo := FindNameTypeID(Table1)
|
|
|
|
if NameID == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
TextSQL := "SELECT Min(" + NameID + ") from \"" + postgres_gorm.Settings.DB_SCHEMA + "\".\"" + TableName + "\""
|
|
|
|
ctx, ctxCancelFunc := context.WithTimeout(ctxMain, time.Second*60)
|
|
|
|
defer ctxCancelFunc()
|
|
|
|
db.WithContext(ctx)
|
|
|
|
|
|
|
|
//запрос
|
|
|
|
tx := db.Raw(TextSQL)
|
|
|
|
err = tx.Error
|
|
|
|
if err != nil {
|
|
|
|
log.Panic("Wrong SQL query: ", TextSQL, " error: ", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var IDMinimum sql.NullString
|
|
|
|
tx = tx.Scan(&IDMinimum)
|
|
|
|
err = tx.Error
|
|
|
|
if err != nil {
|
|
|
|
log.Panic("Wrong SQL Scan(): ", TextSQL, " error: ", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
if TypeGo == "string" {
|
|
|
|
Table1.IDMinimum = `"` + IDMinimum.String + `"`
|
|
|
|
} else if mini_func.IsNumberType(TypeGo) == true {
|
|
|
|
Table1.IDMinimum = IDMinimum.String
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func FindNameTypeID(Table1 *types.Table) (string, string) {
|
|
|
|
Otvet := ""
|
|
|
|
Type := ""
|
|
|
|
|
|
|
|
for ColumnName, Column1 := range Table1.MapColumns {
|
|
|
|
if Column1.IsIdentity == true {
|
|
|
|
return ColumnName, Column1.TypeGo
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Otvet, Type
|
|
|
|
}
|
|
|
|
|
|
|
|
//// FillTypeGo - заполняет тип golang из типа postgres
|
|
|
|
//func FillTypeGo(MapTable map[string]*types.Table) {
|
|
|
|
//
|
|
|
|
// for _, Table1 := range MapTable {
|
|
|
|
// for _, Column1 := range Table1.MapColumns {
|
|
|
|
//
|
|
|
|
// SQLMapping1, ok := dbmeta.GetMappings()[Column1.Type]
|
|
|
|
// if ok == false {
|
|
|
|
// log.Panic("GetMappings() ", Column1.Type, " error: not found")
|
|
|
|
// }
|
|
|
|
// Type_go := SQLMapping1.GoType
|
|
|
|
// Column1.TypeGo = Type_go
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
//}
|