diff --git a/cache/database/types.go b/cache/database/types.go index 88d0ab8..d59fb34 100644 --- a/cache/database/types.go +++ b/cache/database/types.go @@ -1,10 +1,45 @@ package database +// Errors const ( ErrorClosingDatabase = "closing database" ErrorDatabaseNotInitialized = "database not initialized" ) +// Data types +/* +SQLite 3 data type: + Declared Type Type Affinity Storage Class + INTEGER INTEGER INTEGER + TINYINT, SMALLINT INTEGER INTEGER + MEDIUMINT, BIGINT INTEGER INTEGER + INT INTEGER INTEGER + REAL, DOUBLE, FLOAT REAL REAL + NUMERIC, DECIMAL NUMERIC REAL or INTEGER (if possible) + TEXT TEXT TEXT + CHARACTER, VARCHAR TEXT TEXT + CLOB TEXT TEXT + BLOB BLOB BLOB + BOOLEAN NUMERIC INTEGER (1 for true, 0 for false) + DATE, DATETIME NUMERIC TEXT, REAL, or INTEGER depending on the format +*/ +const ( + DataTypeInteger = "INTEGER" + DataTypeTinyint = "TINYINT" + DataTypeBigInteger = "BIGINT" + DataTypeReal = "REAL" + DataTypeNumeric = "NUMERIC" + DataTypeDecimal = "DECIMAL" + DataTypeBoolean = "BOOLEAN" + DataTypeText = "TEXT" + DataTypeCharacter = "CHARACTER" + DataTypeVarchar = "VARCHAR" + DataTypeClob = "CLOB" + DataTypeBlob = "BLOB" + DataTypeDate = "DATE" + DataTypeDateTime = "DATETIME" +) + // Table is a basic representation in a struct of a table in a SQL DB type Table struct { Name string diff --git a/cache/parser/column/column.go b/cache/parser/column/column.go new file mode 100644 index 0000000..cf55d02 --- /dev/null +++ b/cache/parser/column/column.go @@ -0,0 +1,31 @@ +package column + +import "github.com/AmadlaOrg/hery/cache/database" + +var ( + // Id returns simple database column structure for `Id` + Id = database.Column{ + ColumnName: "Id", + DataType: "string", + Default: `( +lower(hex(randomblob(4)) +|| '-' +|| hex(randomblob(2)) +|| '-4' +|| substr(hex(randomblob(2)), 2) +|| '-' +|| substr('89ab', abs(random() % 4) + 1, 1) +|| substr(hex(randomblob(2)), 2) +|| '-' +|| hex(randomblob(6))) +)`, + } + InsertDateTime = database.Column{ + ColumnName: "InsertDateTime", + DataType: "DATETIME", + } + UpdateDateTime = database.Column{ + ColumnName: "UpdateDateTime", + DataType: "DATETIME", + } +) diff --git a/cache/parser/parser.go b/cache/parser/parser.go index ee98d32..3a587ca 100644 --- a/cache/parser/parser.go +++ b/cache/parser/parser.go @@ -21,7 +21,7 @@ var ( jsonMarshal = json.Marshal ) -// Parse +// Entity parses the entity to SQLite 3 struct that can be used in the query builder in the database package func (s *SParser) Entity(entity entity.Entity) ([]database.Table, error) { // TODO: Use schema to determine the data type for the SQL // string == TEXT @@ -63,34 +63,11 @@ func (s *SParser) Entity(entity entity.Entity) ([]database.Table, error) { ); */ - /* - Convert JSON-Schema Types to SQLite 3 data types: - string. - number. - integer. - object. - array. - boolean. - null. - - SQLite 3 data type: - Declared Type Type Affinity Storage Class - INTEGER INTEGER INTEGER - TINYINT, SMALLINT INTEGER INTEGER - MEDIUMINT, BIGINT INTEGER INTEGER - INT INTEGER INTEGER - REAL, DOUBLE, FLOAT REAL REAL - NUMERIC, DECIMAL NUMERIC REAL or INTEGER (if possible) - TEXT TEXT TEXT - CHARACTER, VARCHAR TEXT TEXT - CLOB TEXT TEXT - BLOB BLOB BLOB - BOOLEAN NUMERIC INTEGER (1 for true, 0 for false) - DATE, DATETIME NUMERIC TEXT, REAL, or INTEGER depending on the format - */ - schema := entity.Schema.Schema + // TODO: + //entity.Schema.CompiledSchema.Types + var ( dynamicColumns []database.Column dynamicRelationships []database.Relationships @@ -110,41 +87,13 @@ func (s *SParser) Entity(entity entity.Entity) ([]database.Table, error) { continue // Skip if schemaPropertyValue is not a map } - // Basic data type var dataType string - if typeValue, ok := propertyDetails["type"].(string); ok { - switch typeValue { - case "string": - dataType = "TEXT" - case "number": - dataType = "BIGINT" - case "integer": - dataType = "BIGINT" - case "object": - dataType = "TEXT" - case "array": - dataType = "TEXT" - case "boolean": - dataType = "BOOLEAN" - case "null": - dataType = "TEXT" // SQLite has no direct equivalent; treat as NULL - default: - dataType = "TEXT" - } - } // Change the data type if the "format" property is present if formatValue, ok := propertyDetails["format"].(string); ok { - switch formatValue { - case "date-time": - dataType = "DATETIME" - case "time": - dataType = "TEXT" - case "date": - dataType = "DATE" - case "duration": - dataType = "TEXT" - } + dataType = parseJsonSchemaFormatToSQLiteType(formatValue) + } else if typeValue, ok := propertyDetails["type"].(string); ok { + dataType = parseJsonSchemaToSQLiteType(typeValue) } // Append the column definition @@ -173,6 +122,41 @@ func (s *SParser) Entity(entity entity.Entity) ([]database.Table, error) { dynamicRelationships = append(dynamicRelationships, database.Relationships{}) }*/ + return []database.Table{ + {}, + {}, + { + Name: s.EntityToTableName(entity.Uri), + Columns: []database.Column{ + {}, + }, + }, + }, nil +} + +// EntityToTableName +func (s *SParser) EntityToTableName(entity string) string { + re := regexp.MustCompile(`[^a-zA-Z0-9]+`) + tableName := re.ReplaceAllString(entity, "_") + return strings.Trim(tableName, "_") +} + +// ParseTable +func (s *SParser) DatabaseTable(data []byte) (entity.Entity, error) { + return entity.Entity{}, nil +} + +// ParseRow +func (s *SParser) DatabaseRow(data []byte) (entity.Entity, error) { + return entity.Entity{}, nil +} + +// +// Private methods +// + +func (s *SParser) databaseInsertTableEntities(entity entity.Entity) (*[]database.Table, error) { + schema := entity.Schema.Schema // Convert schema map[string]any into a JSON string for cache storage schemaJsonBytes, err := jsonMarshal(schema) if err != nil { @@ -180,68 +164,9 @@ func (s *SParser) Entity(entity entity.Entity) ([]database.Table, error) { } schemaJsonString := string(schemaJsonBytes) - return []database.Table{ + return &[]database.Table{ { Name: "Entities", - Columns: []database.Column{ - { - ColumnName: "Id", - DataType: "string", - Default: "(lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)), 2) || '-' || substr('89ab', abs(random() % 4) + 1, 1) || substr(hex(randomblob(2)), 2) || '-' || hex(randomblob(6))))", - }, - { - ColumnName: "Entity", - DataType: "string", - }, - { - ColumnName: "Name", - DataType: "string", - }, - { - ColumnName: "RepoUrl", - DataType: "string", - }, - { - ColumnName: "Origin", - DataType: "string", - }, - { - ColumnName: "Version", - DataType: "string", - }, - { - ColumnName: "IsLatestVersion", - DataType: "bool", - }, - { - ColumnName: "IsPseudoVersion", - DataType: "bool", - }, - { - ColumnName: "AbsPath", - DataType: "string", - }, - { - ColumnName: "Have", - DataType: "bool", - }, - { - ColumnName: "Hash", - DataType: "string", - }, - { - ColumnName: "Exist", - DataType: "bool", - }, - { - ColumnName: "Schema", - DataType: "string", - }, - { - ColumnName: "Content", - DataType: "string", - }, - }, Rows: []map[string]any{ { "Id": entity.Id.String(), @@ -262,27 +187,14 @@ func (s *SParser) Entity(entity entity.Entity) ([]database.Table, error) { }, }, { - Name: s.EntityToTableName(entity.Uri), - Columns: []database.Column{ - {}, + Name: "Ids", + Rows: []map[string]any{ + { + "Id": entity.Id.String(), + "Uri": entity.Uri, + "Name": entity.Name, + }, }, }, }, nil } - -// EntityToTableName -func (s *SParser) EntityToTableName(entity string) string { - re := regexp.MustCompile(`[^a-zA-Z0-9]+`) - tableName := re.ReplaceAllString(entity, "_") - return strings.Trim(tableName, "_") -} - -// ParseTable -func (s *SParser) DatabaseTable(data []byte) (entity.Entity, error) { - return entity.Entity{}, nil -} - -// ParseRow -func (s *SParser) DatabaseRow(data []byte) (entity.Entity, error) { - return entity.Entity{}, nil -} diff --git a/cache/parser/table/table.go b/cache/parser/table/table.go new file mode 100644 index 0000000..5cdfdc9 --- /dev/null +++ b/cache/parser/table/table.go @@ -0,0 +1,85 @@ +package table + +import ( + "github.com/AmadlaOrg/hery/cache/database" + "github.com/AmadlaOrg/hery/cache/parser/column" +) + +var ( + Ids = database.Table{ + Name: "Ids", + Columns: []database.Column{ + column.Id, + { + ColumnName: "CustomId", + DataType: "TEXT", + }, + { + ColumnName: "RefId", + DataType: "TEXT", + }, + column.InsertDateTime, + column.UpdateDateTime, + }, + } + Entities = database.Table{ + Name: "Entities", + Columns: []database.Column{ + column.Id, + { + ColumnName: "Entity", + DataType: "TEXT", + }, + { + ColumnName: "Name", + DataType: "TEXT", + }, + { + ColumnName: "RepoUrl", + DataType: "TEXT", + }, + { + ColumnName: "Origin", + DataType: "TEXT", + }, + { + ColumnName: "Version", + DataType: "TEXT", + }, + { + ColumnName: "IsLatestVersion", + DataType: "BOOLEAN", + }, + { + ColumnName: "IsPseudoVersion", + DataType: "BOOLEAN", + }, + { + ColumnName: "AbsPath", + DataType: "TEXT", + }, + { + ColumnName: "Have", + DataType: "BOOLEAN", + }, + { + ColumnName: "Hash", + DataType: "TEXT", + }, + { + ColumnName: "Exist", + DataType: "BOOLEAN", + }, + { + ColumnName: "Schema", + DataType: "TEXT", + }, + { + ColumnName: "Content", + DataType: "TEXT", + }, + column.InsertDateTime, + column.UpdateDateTime, + }, + } +) diff --git a/cache/parser/util.go b/cache/parser/util.go new file mode 100644 index 0000000..fddae04 --- /dev/null +++ b/cache/parser/util.go @@ -0,0 +1,43 @@ +package parser + +// parseJsonSchemaToSQLiteType +func parseJsonSchemaToSQLiteType(jsonSchemaType string) string { + var sqliteType string + switch jsonSchemaType { + case "string": + sqliteType = "TEXT" + case "number": + sqliteType = "BIGINT" + case "integer": + sqliteType = "BIGINT" + case "object": + sqliteType = "TEXT" + case "array": + sqliteType = "TEXT" + case "boolean": + sqliteType = "BOOLEAN" + case "null": + sqliteType = "TEXT" // SQLite has no direct equivalent; treat as NULL + default: + sqliteType = "TEXT" + } + + return sqliteType +} + +// parseJsonSchemaFormatToSQLiteType +func parseJsonSchemaFormatToSQLiteType(jsonSchemaType string) string { + var sqliteType string + switch jsonSchemaType { + case "date-time": + sqliteType = "DATETIME" + case "time": + sqliteType = "TEXT" + case "date": + sqliteType = "DATE" + case "duration": + sqliteType = "TEXT" + } + + return sqliteType +} diff --git a/cache/parser/util_test.go b/cache/parser/util_test.go new file mode 100644 index 0000000..dd16395 --- /dev/null +++ b/cache/parser/util_test.go @@ -0,0 +1,50 @@ +package parser + +import "testing" + +func TestParseJsonSchemaToSQLiteType(t *testing.T) { + tests := []struct { + input string + expected string + }{ + {"string", "TEXT"}, + {"number", "BIGINT"}, + {"integer", "BIGINT"}, + {"object", "TEXT"}, + {"array", "TEXT"}, + {"boolean", "BOOLEAN"}, + {"null", "TEXT"}, + {"unknown", "TEXT"}, // Default case + } + + for _, tt := range tests { + t.Run(tt.input, func(t *testing.T) { + result := parseJsonSchemaToSQLiteType(tt.input) + if result != tt.expected { + t.Errorf("parseJsonSchemaToSQLiteType(%q) = %q; want %q", tt.input, result, tt.expected) + } + }) + } +} + +func TestParseJsonSchemaFormatToSQLiteType(t *testing.T) { + tests := []struct { + input string + expected string + }{ + {"date-time", "DATETIME"}, + {"time", "TEXT"}, + {"date", "DATE"}, + {"duration", "TEXT"}, + {"unknown", ""}, // Default case: returns an empty string + } + + for _, tt := range tests { + t.Run(tt.input, func(t *testing.T) { + result := parseJsonSchemaFormatToSQLiteType(tt.input) + if result != tt.expected { + t.Errorf("parseJsonSchemaFormatToSQLiteType(%q) = %q; want %q", tt.input, result, tt.expected) + } + }) + } +} diff --git a/entity/schema/types.go b/entity/schema/types.go index 8706da6..d967955 100644 --- a/entity/schema/types.go +++ b/entity/schema/types.go @@ -7,6 +7,28 @@ const ( EntityJsonSchemaIdURN = `^urn:([a-z0-9][a-z0-9-]{0,31}):([a-z0-9][a-z0-9-]+):([a-zA-Z0-9\-.:]+):([a-zA-Z0-9\-.]+)$` ) +// JSON-Schema data type +/* +Convert JSON-Schema Types to SQLite 3 data types: + string. + number. + integer. + object. + array. + boolean. + null. +*/ +const ( + DataTypeString = "string" + DataTypeNumber = "number" + DataTypeInteger = "integer" + DataTypeObject = "object" + DataTypeArray = "array" + DataTypeBoolean = "boolean" + DateTypeNull = "null" +) + +// Schema different data type Schema struct { CompiledSchema *jsonschema.Schema SchemaPath string