Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/cmd_admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func cmdInit(cmd *cobra.Command, args []string) {
setup(cpath)
initDB(true)

if err := serv.InitAdmin(db, conf.DBType); err != nil {
if err := serv.InitAdmin(db, conf.DB.Type); err != nil {
log.Fatal(err)
}

Expand Down
7 changes: 6 additions & 1 deletion cmd/tmpl/dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,12 @@ database:
{{- end }}
user: {{ .DBUser }}
password: {{ .DBPass }}
#schema: "public"

schemas:
allowed:
- public
- private
default: public

# alternatively you can use a connection string
#connection_string: postgres://jack:secret@pg.example.com:5432/mydb?sslmode=verify-ca&pool_max_conns=10
Expand Down
9 changes: 7 additions & 2 deletions cmd/tmpl/prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,12 @@ database:
{{- end }}
user: {{ .DBUser }}
password: {{ .DBPass }}
#schema: "public"

schemas:
allowed:
- public
- private
default: public

# Size of database connection pool
# pool_size: 15
Expand Down Expand Up @@ -145,4 +150,4 @@ database:
# client_cert: ./client-cert.pem

# Required for tls. Can be a file path or the contents of the pem file
# client_key: ./client-key.pem
# client_key: ./client-key.pem
49 changes: 46 additions & 3 deletions core/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,50 @@ import (
)

// Configuration for the GraphJin compiler core
// DatabaseConfig holds database connection and schema configuration
type DatabaseConfig struct {
// Database type name (postgres, mysql, etc.)
Type string `mapstructure:"type" json:"type" yaml:"type"`

// Database connection parameters
Host string `mapstructure:"host" json:"host" yaml:"host"`
Port int `mapstructure:"port" json:"port" yaml:"port"`
DBName string `mapstructure:"dbname" json:"dbname" yaml:"dbname"`
User string `mapstructure:"user" json:"user" yaml:"user"`
Password string `mapstructure:"password" json:"password" yaml:"password"`

// Connection string (alternative to individual parameters)
ConnectionString string `mapstructure:"connection_string" json:"connection_string" yaml:"connection_string"`

// Connection pool settings
PoolSize int `mapstructure:"pool_size" json:"pool_size" yaml:"pool_size"`
MaxConnections int `mapstructure:"max_connections" json:"max_connections" yaml:"max_connections"`
MaxConnectionIdleTime time.Duration `mapstructure:"max_connection_idle_time" json:"max_connection_idle_time" yaml:"max_connection_idle_time"`
MaxConnectionLifetime time.Duration `mapstructure:"max_connection_life_time" json:"max_connection_life_time" yaml:"max_connection_life_time"`

// Schema configuration
Schemas struct {
// AllowedSchemas is a list of allowed schemas
Allowed []string `mapstructure:"allowed" json:"allowed" yaml:"allowed"`

// DefaultSchema is the default schema to use
Default string `mapstructure:"default" json:"default" yaml:"default"`

// Separator for cross-schema table names
Separator string `mapstructure:"separator" json:"separator" yaml:"separator"`
} `mapstructure:"schemas" json:"schemas" yaml:"schemas"`

// TLS configuration
EnableTLS bool `mapstructure:"enable_tls" json:"enable_tls" yaml:"enable_tls"`
ServerName string `mapstructure:"server_name" json:"server_name" yaml:"server_name"`
ServerCert string `mapstructure:"server_cert" json:"server_cert" yaml:"server_cert"`
ClientCert string `mapstructure:"client_cert" json:"client_cert" yaml:"client_cert"`
ClientKey string `mapstructure:"client_key" json:"client_key" yaml:"client_key"`

// Ping timeout for health checks
PingTimeout time.Duration `mapstructure:"ping_timeout" json:"ping_timeout" yaml:"ping_timeout"`
}

type Config struct {
// Is used to encrypt opaque values such as the cursor. Auto-generated when not set
SecretKey string `mapstructure:"secret_key" json:"secret_key" yaml:"secret_key" jsonschema:"title=Secret Key"`
Expand Down Expand Up @@ -63,8 +107,8 @@ type Config struct {
// and 'anon' when it's not. Use the 'Roles Query' config to add more custom roles
Roles []Role

// Database type name Defaults to 'postgres' (options: mysql, postgres)
DBType string `mapstructure:"db_type" json:"db_type" yaml:"db_type" jsonschema:"title=Database Type,enum=postgres,enum=mysql"`
// Database configuration
Database DatabaseConfig `mapstructure:"database" json:"database" yaml:"database"`

// Log warnings and other debug information
Debug bool `jsonschema:"title=Debug,default=false"`
Expand Down Expand Up @@ -103,7 +147,6 @@ type Config struct {
FS interface{} `mapstructure:"-" jsonschema:"-" json:"-"`
}

// Configuration for a database table
type Table struct {
Name string
Schema string
Expand Down
116 changes: 87 additions & 29 deletions core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,17 @@ func (gj *graphjinEngine) getIntroResult() (data json.RawMessage, err error) {

// Initializes the database discovery process on graphjin
func (gj *graphjinEngine) initDiscover() (err error) {
switch gj.conf.DBType {
case "":
// Set the database type based on the configuration
gj.dbtype = gj.conf.Database.Type
if gj.dbtype == "" {
gj.dbtype = "postgres"
case "mssql":
gj.dbtype = "mysql"
}

switch gj.dbtype {
case "mysql", "postgres", "mssql":
// Valid database types, use as is
default:
gj.dbtype = gj.conf.DBType
gj.dbtype = "postgres"
}

if err = gj._initDiscover(); err != nil {
Expand Down Expand Up @@ -117,6 +121,14 @@ func (gj *graphjinEngine) _initDiscover() (err error) {
}
}

// Set the database type in the database info
if gj.dbinfo.Type == "" {
gj.dbinfo.Type = gj.conf.Database.Type
if gj.dbinfo.Type == "" {
gj.dbinfo.Type = "postgres"
}
}

if !gj.prod && gj.conf.EnableSchema {
var buf bytes.Buffer
if err := writeSchema(gj.dbinfo, &buf); err != nil {
Expand All @@ -133,48 +145,87 @@ func (gj *graphjinEngine) _initDiscover() (err error) {

// Initializes the database schema on graphjin
func (gj *graphjinEngine) initSchema() error {
gj.log.Printf("DEBUG: Initializing schema for database type: %s", gj.dbtype)
if gj.dbinfo == nil {
gj.log.Printf("WARNING: dbinfo is nil in initSchema")
} else {
gj.log.Printf("DEBUG: dbinfo has %d tables", len(gj.dbinfo.Tables))
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Debug logging statements accidentally left in production code

Multiple DEBUG: and WARNING: log statements have been left in the production code. The PR discussion confirms this was unintentional, with the author stating "that code isn't supposed to be in this branch.. i was playing with the library, learning a few new things". These debug statements will clutter production logs with internal implementation details.

Additional Locations (2)

Fix in Cursor Fix in Web


if err := gj._initSchema(); err != nil {
return fmt.Errorf("%s: %w", gj.dbtype, err)
}

if gj.schema == nil {
gj.log.Printf("WARNING: schema is still nil after _initSchema")
} else {
gj.log.Printf("DEBUG: Schema initialized with default schema: %s", gj.schema.DefaultSchema())
}

return nil
}

// _initSchema initializes the database schema with proper error handling and validation
func (gj *graphjinEngine) _initSchema() (err error) {
// Validate database tables exist
if len(gj.dbinfo.Tables) == 0 {
return fmt.Errorf("no tables found in database")
}

schema := gj.dbinfo.Schema
for i, t := range gj.conf.Tables {
if t.Schema == "" {
gj.conf.Tables[i].Schema = schema
t.Schema = schema
}
// skip aliases
if t.Table != "" && t.Type == "" {
continue
}
if err = gj.addTableInfo(t); err != nil {
return
}
// Create a new DBSchema instance
schemaConfig := sdata.Config{
DefaultSchema: gj.conf.Database.Schemas.Default,
AllowedSchemas: gj.conf.Database.Schemas.Allowed,
CrossSchemaSeparator: gj.conf.Database.Schemas.Separator,
}

if err = addTables(gj.conf, gj.dbinfo); err != nil {
return
// Set default values if not provided
if schemaConfig.CrossSchemaSeparator == "" {
schemaConfig.CrossSchemaSeparator = "Of"
}

if err = addForeignKeys(gj.conf, gj.dbinfo); err != nil {
return
gj.schema, err = sdata.NewDBSchema(gj.dbinfo, nil, schemaConfig)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Table aliases not passed to schema initialization

The NewDBSchema call passes nil for the aliases parameter instead of getDBTableAliases(gj.conf). The old code passed the configured table aliases to allow users to define alternative names for tables. With nil, any table aliases defined in the configuration will be silently ignored, breaking alias functionality for users who rely on it.

Fix in Cursor Fix in Web

if err != nil {
return fmt.Errorf("failed to create database schema: %w", err)
}

gj.schema, err = sdata.NewDBSchema(
gj.dbinfo,
getDBTableAliases(gj.conf))
if err != nil {
return
// Process table configurations
processTableConfigs := func() error {
schema := gj.dbinfo.Schema
for i, t := range gj.conf.Tables {
// Set default schema if not specified
if t.Schema == "" {
gj.conf.Tables[i].Schema = schema
t.Schema = schema
}

// Skip alias configurations
if t.Table != "" && t.Type == "" {
continue
}

// Add table info to the schema
if err := gj.addTableInfo(t); err != nil {
return fmt.Errorf("failed to add table info for %s: %w", t.Name, err)
}
}
return nil
}

return
// Execute schema operations in sequence
operations := []func() error{
processTableConfigs,
func() error { return addTables(gj.conf, gj.dbinfo) },
func() error { return addForeignKeys(gj.conf, gj.dbinfo) },
}

for _, op := range operations {
if err := op(); err != nil {
return err
}
}

return nil
}

func (gj *graphjinEngine) initIntro() (err error) {
Expand All @@ -194,14 +245,21 @@ func (gj *graphjinEngine) initIntro() (err error) {

// Initializes the qcode compilers
func (gj *graphjinEngine) initCompilers() (err error) {
// Debug logging for schema initialization
if gj.schema == nil {
gj.log.Printf("WARNING: Schema is nil in initCompilers")
} else {
gj.log.Printf("DEBUG: Schema initialized with default schema: %s", gj.schema.DefaultSchema())
}

qcc := qcode.Config{
TConfig: gj.tmap,
DefaultBlock: gj.conf.DefaultBlock,
DefaultLimit: gj.conf.DefaultLimit,
DisableAgg: gj.conf.DisableAgg,
DisableFuncs: gj.conf.DisableFuncs,
EnableCamelcase: gj.conf.EnableCamelcase,
DBSchema: gj.schema.DBSchema(),
DBSchema: gj.schema.DefaultSchema(),
Validators: valid.Validators,
}

Expand Down
Loading
Loading