Skip to content

Commit db7502b

Browse files
committed
Improve Metadata and Verify documentation
- Add comprehensive field documentation to Metadata struct with references to MaxMind DB specification - Add BuildTime() convenience method to convert BuildEpoch to time.Time - Enhance Verify() documentation explaining validation scope and use cases - Add ExampleReader_Verify showing verification and metadata access - Add TestMetadataBuildTime to verify BuildTime() method correctness
1 parent 7fc4929 commit db7502b

File tree

5 files changed

+131
-17
lines changed

5 files changed

+131
-17
lines changed

example_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,39 @@ func ExampleReader_Networks() {
102102
// 2003::/24: Cable/DSL
103103
}
104104

105+
// This example demonstrates how to validate a MaxMind DB file and access metadata.
106+
func ExampleReader_Verify() {
107+
db, err := maxminddb.Open("test-data/test-data/GeoIP2-City-Test.mmdb")
108+
if err != nil {
109+
log.Fatal(err)
110+
}
111+
defer db.Close() //nolint:errcheck // error doesn't matter
112+
113+
// Verify database integrity
114+
if err := db.Verify(); err != nil {
115+
log.Printf("Database validation failed: %v", err)
116+
return
117+
}
118+
119+
// Access metadata information
120+
metadata := db.Metadata
121+
fmt.Printf("Database type: %s\n", metadata.DatabaseType)
122+
fmt.Printf("Build time: %s\n", metadata.BuildTime().Format("2006-01-02 15:04:05"))
123+
fmt.Printf("IP version: IPv%d\n", metadata.IPVersion)
124+
fmt.Printf("Languages: %v\n", metadata.Languages)
125+
126+
if desc, ok := metadata.Description["en"]; ok {
127+
fmt.Printf("Description: %s\n", desc)
128+
}
129+
130+
// Output:
131+
// Database type: GeoIP2-City
132+
// Build time: 2022-07-26 07:53:10
133+
// IP version: IPv6
134+
// Languages: [en zh]
135+
// Description: GeoIP2 City Test Database (fake GeoIP2 data, for example purposes only)
136+
}
137+
105138
// This example demonstrates how to iterate over all networks in the
106139
// database which are contained within an arbitrary network.
107140
func ExampleReader_NetworksWithin() {

mmdbdata/doc.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,4 @@
5050
// if err != nil {
5151
// return err
5252
// }
53-
package mmdbdata
53+
package mmdbdata

reader.go

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ import (
112112
"net/netip"
113113
"os"
114114
"runtime"
115+
"time"
115116

116117
"github.com/oschwald/maxminddb-golang/v2/internal/decoder"
117118
"github.com/oschwald/maxminddb-golang/v2/internal/mmdberrors"
@@ -137,20 +138,62 @@ type Reader struct {
137138
hasMappedFile bool
138139
}
139140

140-
// Metadata holds the metadata decoded from the MaxMind DB file. In particular
141-
// it has the format version, the build time as Unix epoch time, the database
142-
// type and description, the IP version supported, and a slice of the natural
143-
// languages included.
141+
// Metadata holds the metadata decoded from the MaxMind DB file.
142+
//
143+
// Key fields include:
144+
// - DatabaseType: indicates the structure of data records (e.g., "GeoIP2-City")
145+
// - Description: localized descriptions in various languages
146+
// - Languages: locale codes for which the database may contain localized data
147+
// - BuildEpoch: database build timestamp as Unix epoch seconds
148+
// - IPVersion: supported IP version (4 for IPv4-only, 6 for IPv4/IPv6)
149+
// - NodeCount: number of nodes in the search tree
150+
// - RecordSize: size in bits of each record in the search tree (24, 28, or 32)
151+
//
152+
// For detailed field descriptions, see the MaxMind DB specification:
153+
// https://maxmind.github.io/MaxMind-DB/
144154
type Metadata struct {
145-
Description map[string]string `maxminddb:"description"`
146-
DatabaseType string `maxminddb:"database_type"`
147-
Languages []string `maxminddb:"languages"`
148-
BinaryFormatMajorVersion uint `maxminddb:"binary_format_major_version"`
149-
BinaryFormatMinorVersion uint `maxminddb:"binary_format_minor_version"`
150-
BuildEpoch uint `maxminddb:"build_epoch"`
151-
IPVersion uint `maxminddb:"ip_version"`
152-
NodeCount uint `maxminddb:"node_count"`
153-
RecordSize uint `maxminddb:"record_size"`
155+
// Description contains localized database descriptions.
156+
// Keys are language codes (e.g., "en", "zh-CN"), values are UTF-8 descriptions.
157+
Description map[string]string `maxminddb:"description"`
158+
159+
// DatabaseType indicates the structure of data records associated with IP addresses.
160+
// Names starting with "GeoIP" are reserved for MaxMind databases.
161+
DatabaseType string `maxminddb:"database_type"`
162+
163+
// Languages lists locale codes for which this database may contain localized data.
164+
// Records should not contain localized data for locales not in this array.
165+
Languages []string `maxminddb:"languages"`
166+
167+
// BinaryFormatMajorVersion is the major version of the MaxMind DB binary format.
168+
// Current supported version is 2.
169+
BinaryFormatMajorVersion uint `maxminddb:"binary_format_major_version"`
170+
171+
// BinaryFormatMinorVersion is the minor version of the MaxMind DB binary format.
172+
// Current supported version is 0.
173+
BinaryFormatMinorVersion uint `maxminddb:"binary_format_minor_version"`
174+
175+
// BuildEpoch contains the database build timestamp as Unix epoch seconds.
176+
// Use BuildTime() method for a time.Time representation.
177+
BuildEpoch uint `maxminddb:"build_epoch"`
178+
179+
// IPVersion indicates the IP version support:
180+
// 4: IPv4 addresses only
181+
// 6: Both IPv4 and IPv6 addresses
182+
IPVersion uint `maxminddb:"ip_version"`
183+
184+
// NodeCount is the number of nodes in the search tree.
185+
NodeCount uint `maxminddb:"node_count"`
186+
187+
// RecordSize is the size in bits of each record in the search tree.
188+
// Valid values are 24, 28, or 32.
189+
RecordSize uint `maxminddb:"record_size"`
190+
}
191+
192+
// BuildTime returns the database build time as a time.Time.
193+
// This is a convenience method that converts the BuildEpoch field
194+
// from Unix epoch seconds to a time.Time value.
195+
func (m Metadata) BuildTime() time.Time {
196+
return time.Unix(int64(m.BuildEpoch), 0)
154197
}
155198

156199
type readerOptions struct{}

reader_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,3 +1156,27 @@ func TestFallbackToReflection(t *testing.T) {
11561156
// Log the result for verification
11571157
t.Logf("Reflection fallback result: %+v", regularStruct)
11581158
}
1159+
1160+
func TestMetadataBuildTime(t *testing.T) {
1161+
reader, err := Open(testFile("GeoIP2-City-Test.mmdb"))
1162+
require.NoError(t, err)
1163+
defer func() {
1164+
if err := reader.Close(); err != nil {
1165+
t.Errorf("Error closing reader: %v", err)
1166+
}
1167+
}()
1168+
1169+
metadata := reader.Metadata
1170+
1171+
// Test that BuildTime() returns a valid time
1172+
buildTime := metadata.BuildTime()
1173+
assert.False(t, buildTime.IsZero(), "BuildTime should not be zero")
1174+
1175+
// Test that BuildTime() matches BuildEpoch
1176+
expectedTime := time.Unix(int64(metadata.BuildEpoch), 0)
1177+
assert.Equal(t, expectedTime, buildTime, "BuildTime should match time.Unix(BuildEpoch, 0)")
1178+
1179+
// Verify the build time is reasonable (after 2010, before 2030)
1180+
assert.True(t, buildTime.After(time.Date(2010, 1, 1, 0, 0, 0, 0, time.UTC)))
1181+
assert.True(t, buildTime.Before(time.Date(2030, 1, 1, 0, 0, 0, 0, time.UTC)))
1182+
}

verifier.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,23 @@ type verifier struct {
1010
reader *Reader
1111
}
1212

13-
// Verify checks that the database is valid. It validates the search tree,
14-
// the data section, and the metadata section. This verifier is stricter than
15-
// the specification and may return errors on databases that are readable.
13+
// Verify performs comprehensive validation of the MaxMind DB file.
14+
//
15+
// This method validates:
16+
// - Metadata section: format versions, required fields, and value constraints
17+
// - Search tree: traverses all networks to verify tree structure integrity
18+
// - Data section separator: validates the 16-byte separator between tree and data
19+
// - Data section: verifies all data records referenced by the search tree
20+
//
21+
// The verifier is stricter than the MaxMind DB specification and may return
22+
// errors on some databases that are still readable by normal operations.
23+
// This method is useful for:
24+
// - Validating database files after download or generation
25+
// - Debugging database corruption issues
26+
// - Ensuring database integrity in critical applications
27+
//
28+
// Note: Verification traverses the entire database and may be slow on large files.
29+
// The method is thread-safe and can be called on an active Reader.
1630
func (r *Reader) Verify() error {
1731
v := verifier{r}
1832
if err := v.verifyMetadata(); err != nil {

0 commit comments

Comments
 (0)