mirror of
https://github.com/SigNoz/signoz.git
synced 2026-02-03 08:33:26 +00:00
feat(sqlschema): add sqlschema (#8384)
## 📄 Summary
- add sqlschema package
- add unique index on email,org_id in users and user_invite
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/SigNoz/signoz/ee/licensing"
|
||||
"github.com/SigNoz/signoz/ee/licensing/httplicensing"
|
||||
"github.com/SigNoz/signoz/ee/query-service/app"
|
||||
"github.com/SigNoz/signoz/ee/sqlschema/postgressqlschema"
|
||||
"github.com/SigNoz/signoz/ee/sqlstore/postgressqlstore"
|
||||
"github.com/SigNoz/signoz/ee/zeus"
|
||||
"github.com/SigNoz/signoz/ee/zeus/httpzeus"
|
||||
@@ -21,6 +22,7 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/modules/organization"
|
||||
baseconst "github.com/SigNoz/signoz/pkg/query-service/constants"
|
||||
"github.com/SigNoz/signoz/pkg/signoz"
|
||||
"github.com/SigNoz/signoz/pkg/sqlschema"
|
||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||
"github.com/SigNoz/signoz/pkg/sqlstore/sqlstorehook"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
@@ -145,6 +147,14 @@ func main() {
|
||||
signoz.NewEmailingProviderFactories(),
|
||||
signoz.NewCacheProviderFactories(),
|
||||
signoz.NewWebProviderFactories(),
|
||||
func(sqlstore sqlstore.SQLStore) factory.NamedMap[factory.ProviderFactory[sqlschema.SQLSchema, sqlschema.Config]] {
|
||||
existingFactories := signoz.NewSQLSchemaProviderFactories(sqlstore)
|
||||
if err := existingFactories.Add(postgressqlschema.NewFactory(sqlstore)); err != nil {
|
||||
zap.L().Fatal("Failed to add postgressqlschema factory", zap.Error(err))
|
||||
}
|
||||
|
||||
return existingFactories
|
||||
},
|
||||
sqlStoreFactories,
|
||||
signoz.NewTelemetryStoreProviderFactories(),
|
||||
)
|
||||
|
||||
36
ee/sqlschema/postgressqlschema/formatter.go
Normal file
36
ee/sqlschema/postgressqlschema/formatter.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package postgressqlschema
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/sqlschema"
|
||||
)
|
||||
|
||||
type Formatter struct {
|
||||
sqlschema.Formatter
|
||||
}
|
||||
|
||||
func (formatter Formatter) SQLDataTypeOf(dataType sqlschema.DataType) string {
|
||||
if dataType == sqlschema.DataTypeTimestamp {
|
||||
return "TIMESTAMPTZ"
|
||||
}
|
||||
|
||||
return strings.ToUpper(dataType.String())
|
||||
}
|
||||
|
||||
func (formatter Formatter) DataTypeOf(dataType string) sqlschema.DataType {
|
||||
switch strings.ToUpper(dataType) {
|
||||
case "TIMESTAMPTZ", "TIMESTAMP", "TIMESTAMP WITHOUT TIME ZONE", "TIMESTAMP WITH TIME ZONE":
|
||||
return sqlschema.DataTypeTimestamp
|
||||
case "INT8":
|
||||
return sqlschema.DataTypeBigInt
|
||||
case "INT2", "INT4", "SMALLINT", "INTEGER":
|
||||
return sqlschema.DataTypeInteger
|
||||
case "BOOL", "BOOLEAN":
|
||||
return sqlschema.DataTypeBoolean
|
||||
case "VARCHAR", "CHARACTER VARYING", "CHARACTER":
|
||||
return sqlschema.DataTypeText
|
||||
}
|
||||
|
||||
return formatter.Formatter.DataTypeOf(dataType)
|
||||
}
|
||||
285
ee/sqlschema/postgressqlschema/provider.go
Normal file
285
ee/sqlschema/postgressqlschema/provider.go
Normal file
@@ -0,0 +1,285 @@
|
||||
package postgressqlschema
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/factory"
|
||||
"github.com/SigNoz/signoz/pkg/sqlschema"
|
||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
type provider struct {
|
||||
settings factory.ScopedProviderSettings
|
||||
fmter sqlschema.SQLFormatter
|
||||
sqlstore sqlstore.SQLStore
|
||||
operator sqlschema.SQLOperator
|
||||
}
|
||||
|
||||
func NewFactory(sqlstore sqlstore.SQLStore) factory.ProviderFactory[sqlschema.SQLSchema, sqlschema.Config] {
|
||||
return factory.NewProviderFactory(factory.MustNewName("postgres"), func(ctx context.Context, providerSettings factory.ProviderSettings, config sqlschema.Config) (sqlschema.SQLSchema, error) {
|
||||
return New(ctx, providerSettings, config, sqlstore)
|
||||
})
|
||||
}
|
||||
|
||||
func New(ctx context.Context, providerSettings factory.ProviderSettings, config sqlschema.Config, sqlstore sqlstore.SQLStore) (sqlschema.SQLSchema, error) {
|
||||
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/pkg/sqlschema/postgressqlschema")
|
||||
fmter := Formatter{Formatter: sqlschema.NewFormatter(sqlstore.BunDB().Dialect())}
|
||||
|
||||
return &provider{
|
||||
sqlstore: sqlstore,
|
||||
fmter: fmter,
|
||||
settings: settings,
|
||||
operator: sqlschema.NewOperator(fmter, sqlschema.OperatorSupport{
|
||||
DropConstraint: true,
|
||||
ColumnIfNotExistsExists: true,
|
||||
AlterColumnSetNotNull: true,
|
||||
}),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (provider *provider) Formatter() sqlschema.SQLFormatter {
|
||||
return provider.fmter
|
||||
}
|
||||
|
||||
func (provider *provider) Operator() sqlschema.SQLOperator {
|
||||
return provider.operator
|
||||
}
|
||||
|
||||
func (provider *provider) GetTable(ctx context.Context, tableName sqlschema.TableName) (*sqlschema.Table, []*sqlschema.UniqueConstraint, error) {
|
||||
rows, err := provider.
|
||||
sqlstore.
|
||||
BunDB().
|
||||
QueryContext(ctx, `
|
||||
SELECT
|
||||
c.column_name,
|
||||
c.is_nullable = 'YES',
|
||||
c.udt_name,
|
||||
c.column_default
|
||||
FROM
|
||||
information_schema.columns AS c
|
||||
WHERE
|
||||
c.table_name = ?`, string(tableName))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := rows.Close(); err != nil {
|
||||
provider.settings.Logger().ErrorContext(ctx, "error closing rows", "error", err)
|
||||
}
|
||||
}()
|
||||
|
||||
columns := make([]*sqlschema.Column, 0)
|
||||
for rows.Next() {
|
||||
var (
|
||||
name string
|
||||
sqlDataType string
|
||||
nullable bool
|
||||
defaultVal *string
|
||||
)
|
||||
if err := rows.Scan(&name, &nullable, &sqlDataType, &defaultVal); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
columnDefault := ""
|
||||
if defaultVal != nil {
|
||||
columnDefault = *defaultVal
|
||||
}
|
||||
|
||||
columns = append(columns, &sqlschema.Column{
|
||||
Name: sqlschema.ColumnName(name),
|
||||
Nullable: nullable,
|
||||
DataType: provider.fmter.DataTypeOf(sqlDataType),
|
||||
Default: columnDefault,
|
||||
})
|
||||
}
|
||||
|
||||
constraintsRows, err := provider.
|
||||
sqlstore.
|
||||
BunDB().
|
||||
QueryContext(ctx, `
|
||||
SELECT
|
||||
c.column_name,
|
||||
constraint_name,
|
||||
constraint_type
|
||||
FROM
|
||||
information_schema.table_constraints tc
|
||||
JOIN information_schema.constraint_column_usage AS ccu USING (constraint_schema, constraint_catalog, table_name, constraint_name)
|
||||
JOIN information_schema.columns AS c ON c.table_schema = tc.constraint_schema AND tc.table_name = c.table_name AND ccu.column_name = c.column_name
|
||||
WHERE
|
||||
c.table_name = ?`, string(tableName))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := constraintsRows.Close(); err != nil {
|
||||
provider.settings.Logger().ErrorContext(ctx, "error closing rows", "error", err)
|
||||
}
|
||||
}()
|
||||
|
||||
var primaryKeyConstraint *sqlschema.PrimaryKeyConstraint
|
||||
uniqueConstraintsMap := make(map[string]*sqlschema.UniqueConstraint)
|
||||
for constraintsRows.Next() {
|
||||
var (
|
||||
name string
|
||||
constraintName string
|
||||
constraintType string
|
||||
)
|
||||
|
||||
if err := constraintsRows.Scan(&name, &constraintName, &constraintType); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if constraintType == "PRIMARY KEY" {
|
||||
if primaryKeyConstraint == nil {
|
||||
primaryKeyConstraint = (&sqlschema.PrimaryKeyConstraint{
|
||||
ColumnNames: []sqlschema.ColumnName{sqlschema.ColumnName(name)},
|
||||
}).Named(constraintName).(*sqlschema.PrimaryKeyConstraint)
|
||||
} else {
|
||||
primaryKeyConstraint.ColumnNames = append(primaryKeyConstraint.ColumnNames, sqlschema.ColumnName(name))
|
||||
}
|
||||
}
|
||||
|
||||
if constraintType == "UNIQUE" {
|
||||
if _, ok := uniqueConstraintsMap[constraintName]; !ok {
|
||||
uniqueConstraintsMap[constraintName] = (&sqlschema.UniqueConstraint{
|
||||
ColumnNames: []sqlschema.ColumnName{sqlschema.ColumnName(name)},
|
||||
}).Named(constraintName).(*sqlschema.UniqueConstraint)
|
||||
} else {
|
||||
uniqueConstraintsMap[constraintName].ColumnNames = append(uniqueConstraintsMap[constraintName].ColumnNames, sqlschema.ColumnName(name))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreignKeyConstraintsRows, err := provider.
|
||||
sqlstore.
|
||||
BunDB().
|
||||
QueryContext(ctx, `
|
||||
SELECT
|
||||
tc.constraint_name,
|
||||
kcu.table_name AS referencing_table,
|
||||
kcu.column_name AS referencing_column,
|
||||
ccu.table_name AS referenced_table,
|
||||
ccu.column_name AS referenced_column
|
||||
FROM
|
||||
information_schema.key_column_usage kcu
|
||||
JOIN information_schema.table_constraints tc ON kcu.constraint_name = tc.constraint_name AND kcu.table_schema = tc.table_schema
|
||||
JOIN information_schema.constraint_column_usage ccu ON ccu.constraint_name = tc.constraint_name AND ccu.table_schema = tc.table_schema
|
||||
WHERE
|
||||
tc.constraint_type = ?
|
||||
AND kcu.table_name = ?`, "FOREIGN KEY", string(tableName))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := foreignKeyConstraintsRows.Close(); err != nil {
|
||||
provider.settings.Logger().ErrorContext(ctx, "error closing rows", "error", err)
|
||||
}
|
||||
}()
|
||||
|
||||
foreignKeyConstraints := make([]*sqlschema.ForeignKeyConstraint, 0)
|
||||
for foreignKeyConstraintsRows.Next() {
|
||||
var (
|
||||
constraintName string
|
||||
referencingTable string
|
||||
referencingColumn string
|
||||
referencedTable string
|
||||
referencedColumn string
|
||||
)
|
||||
|
||||
if err := foreignKeyConstraintsRows.Scan(&constraintName, &referencingTable, &referencingColumn, &referencedTable, &referencedColumn); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
foreignKeyConstraints = append(foreignKeyConstraints, (&sqlschema.ForeignKeyConstraint{
|
||||
ReferencingColumnName: sqlschema.ColumnName(referencingColumn),
|
||||
ReferencedTableName: sqlschema.TableName(referencedTable),
|
||||
ReferencedColumnName: sqlschema.ColumnName(referencedColumn),
|
||||
}).Named(constraintName).(*sqlschema.ForeignKeyConstraint))
|
||||
}
|
||||
|
||||
uniqueConstraints := make([]*sqlschema.UniqueConstraint, 0)
|
||||
for _, uniqueConstraint := range uniqueConstraintsMap {
|
||||
uniqueConstraints = append(uniqueConstraints, uniqueConstraint)
|
||||
}
|
||||
|
||||
return &sqlschema.Table{
|
||||
Name: tableName,
|
||||
Columns: columns,
|
||||
PrimaryKeyConstraint: primaryKeyConstraint,
|
||||
ForeignKeyConstraints: foreignKeyConstraints,
|
||||
}, uniqueConstraints, nil
|
||||
}
|
||||
|
||||
func (provider *provider) GetIndices(ctx context.Context, name sqlschema.TableName) ([]sqlschema.Index, error) {
|
||||
rows, err := provider.
|
||||
sqlstore.
|
||||
BunDB().
|
||||
QueryContext(ctx, `
|
||||
SELECT
|
||||
ct.relname AS table_name,
|
||||
ci.relname AS index_name,
|
||||
i.indisunique AS unique,
|
||||
i.indisprimary AS primary,
|
||||
a.attname AS column_name
|
||||
FROM
|
||||
pg_index i
|
||||
LEFT JOIN pg_class ct ON ct.oid = i.indrelid
|
||||
LEFT JOIN pg_class ci ON ci.oid = i.indexrelid
|
||||
LEFT JOIN pg_attribute a ON a.attrelid = ct.oid
|
||||
LEFT JOIN pg_constraint con ON con.conindid = i.indexrelid
|
||||
WHERE
|
||||
a.attnum = ANY(i.indkey)
|
||||
AND con.oid IS NULL
|
||||
AND ct.relkind = 'r'
|
||||
AND ct.relname = ?`, string(name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := rows.Close(); err != nil {
|
||||
provider.settings.Logger().ErrorContext(ctx, "error closing rows", "error", err)
|
||||
}
|
||||
}()
|
||||
|
||||
uniqueIndicesMap := make(map[string]*sqlschema.UniqueIndex)
|
||||
for rows.Next() {
|
||||
var (
|
||||
tableName string
|
||||
indexName string
|
||||
unique bool
|
||||
primary bool
|
||||
columnName string
|
||||
)
|
||||
|
||||
if err := rows.Scan(&tableName, &indexName, &unique, &primary, &columnName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if unique {
|
||||
if _, ok := uniqueIndicesMap[indexName]; !ok {
|
||||
uniqueIndicesMap[indexName] = &sqlschema.UniqueIndex{
|
||||
TableName: name,
|
||||
ColumnNames: []sqlschema.ColumnName{sqlschema.ColumnName(columnName)},
|
||||
}
|
||||
} else {
|
||||
uniqueIndicesMap[indexName].ColumnNames = append(uniqueIndicesMap[indexName].ColumnNames, sqlschema.ColumnName(columnName))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
indices := make([]sqlschema.Index, 0)
|
||||
for _, index := range uniqueIndicesMap {
|
||||
indices = append(indices, index)
|
||||
}
|
||||
|
||||
return indices, nil
|
||||
}
|
||||
|
||||
func (provider *provider) ToggleFKEnforcement(_ context.Context, _ bun.IDB, _ bool) error {
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user