From 0371b15c8c6a8bbfe17bbfdcb975a3d83fe640d2 Mon Sep 17 00:00:00 2001 From: vato007 Date: Tue, 29 Apr 2025 18:38:14 +0930 Subject: [PATCH] Initial Commit --- .vscode/launch.json | 26 ++++++++++++++++++++ README.md | 17 +++++++++++++ database.go | 50 ++++++++++++++++++++++++++++++++++++++ generator.go | 59 +++++++++++++++++++++++++++++++++++++++++++++ go.mod | 18 ++++++++++++++ go.sum | 51 +++++++++++++++++++++++++++++++++++++++ main.go | 47 ++++++++++++++++++++++++++++++++++++ 7 files changed, 268 insertions(+) create mode 100644 .vscode/launch.json create mode 100644 README.md create mode 100644 database.go create mode 100644 generator.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..c674194 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,26 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Package", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceFolder}", + "args": [ + "test.csv", + "-d", + "TestIngeyDatabase", + "-t", + "My Test Table", + "-u", + "sa", + "-w", + "TestOnlyContainer123" + ] + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..8a8d4db --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# sqlcsvgenerator + +A simple cli application that takes in a database table and outputs a csv with random data. Currently only supports varchar, text, integer and float type columns + +## Usage + +Run `sqlcsvgen -h` for full usage instructions. + +Basic use: + +`sqlcsvgen output.csv -d MyDatabase -t MyTable` + +## Development + +Run the ingey mssql database container, then you can run this package through vs code with the preconfigured launch configuration. + +`docker pull gitea.michaelpivato.dev/vato007/ingey-test-db-mssql:latest` diff --git a/database.go b/database.go new file mode 100644 index 0000000..eef31ed --- /dev/null +++ b/database.go @@ -0,0 +1,50 @@ +package main + +import ( + "database/sql" + "fmt" + + _ "github.com/microsoft/go-mssqldb" +) + +type Column struct { + Name string + TypeName string + MaxCharacterLength *int +} + +func fetchTableMetadata() ([]Column, error) { + connStr := fmt.Sprintf("server=%s;user id=%s;password=%s;port=%d;database=%s;encrypt=disable", + host, username, password, port, dbname) + + db, err := sql.Open("sqlserver", connStr) + if err != nil { + return nil, err + } + defer db.Close() + + query := ` + SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_NAME = @tableName; + ` + + var columns []Column + + rows, err := db.Query(query, sql.Named("tableName", table)) + if err != nil { + return nil, err + } + defer rows.Close() + + for rows.Next() { + var col Column + err = rows.Scan(&col.Name, &col.TypeName, &col.MaxCharacterLength) + if err != nil { + return nil, err + } + columns = append(columns, col) + } + + return columns, nil +} diff --git a/generator.go b/generator.go new file mode 100644 index 0000000..cf0263f --- /dev/null +++ b/generator.go @@ -0,0 +1,59 @@ +package main + +import ( + "encoding/csv" + "fmt" + "math/rand" + "os" +) + +func generateCSV(outputFile string) error { + columns, err := fetchTableMetadata() + if err != nil { + return fmt.Errorf("error fetching table metadata: %w", err) + } + + file, err := os.Create(outputFile) + if err != nil { + return fmt.Errorf("could not create file: %w", err) + } + defer file.Close() + + writer := csv.NewWriter(file) + defer writer.Flush() + + header := make([]string, len(columns)) + for i, col := range columns { + header[i] = col.Name + } + + err = writer.Write(header) + if err != nil { + return fmt.Errorf("error writing header: %w", err) + } + + for i := 0; i < numRows; i++ { + rowData := make([]string, len(columns)) + for j, col := range columns { + switch col.TypeName { + case "integer", "float": + rowData[j] = fmt.Sprint(rand.Intn(1000)) + case "varchar", "text": + rowData[j] = fmt.Sprintf("sample text %d", rand.Intn(100)) + default: + rowData[j] = "" + } + if col.MaxCharacterLength != nil && *col.MaxCharacterLength < len(rowData[j]) { + rowData[j] = rowData[j][:*col.MaxCharacterLength] + } + } + + err := writer.Write(rowData) + if err != nil { + return fmt.Errorf("error writing row data: %w", err) + } + } + + fmt.Printf("CSV file '%s' generated successfully\n", outputFile) + return nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..62e2a99 --- /dev/null +++ b/go.mod @@ -0,0 +1,18 @@ +module sql_csv_generator + +go 1.24.2 + +require ( + github.com/microsoft/go-mssqldb v1.8.0 + github.com/spf13/cobra v1.9.1 +) + +require ( + github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect + github.com/golang-sql/sqlexp v0.1.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/spf13/pflag v1.0.6 // indirect + golang.org/x/crypto v0.24.0 // indirect + golang.org/x/text v0.16.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..8dd9dd4 --- /dev/null +++ b/go.sum @@ -0,0 +1,51 @@ +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 h1:E+OJmp2tPvt1W+amx48v1eqbjDYsgN+RzP4q16yV5eM= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1/go.mod h1:a6xsAQUZg+VsS3TJ05SRp524Hs4pZ/AeFSr5ENf0Yjo= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 h1:U2rTu3Ef+7w9FHKIAXM6ZyqF3UOWJZ12zIm8zECAFfg= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 h1:jBQA3cKT4L2rWMpgE7Yt3Hwh2aUj8KXjIGLxjHeYNNo= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0/go.mod h1:4OG6tQ9EOP/MT0NMjDlRzWoVFxfu9rN9B2X+tlSVktg= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1 h1:MyVTgWR8qd/Jw1Le0NZebGBUCLbtak3bJ3z1OlqZBpw= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1/go.mod h1:GpPjLhVR9dnUoJMyHWSPy71xY9/lcmpzIPZXmF0FCVY= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 h1:D3occbWoio4EBLkbkevetNMAVX197GkzbUMtqjGWn80= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0/go.mod h1:bTSOgj05NGRuHHhQwAdPnYr9TOdNmKlZTgGLL6nyAdI= +github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU= +github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= +github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/microsoft/go-mssqldb v1.8.0 h1:7cyZ/AT7ycDsEoWPIXibd+aVKFtteUNhDGf3aobP+tw= +github.com/microsoft/go-mssqldb v1.8.0/go.mod h1:6znkekS3T2vp0waiMhen4GPU1BiAsrP+iXHcE7a7rFo= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go new file mode 100644 index 0000000..652a643 --- /dev/null +++ b/main.go @@ -0,0 +1,47 @@ +package main + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" +) + +var ( + dbname string + host string + port int + username string + password string + table string + numRows int +) + +func main() { + var rootCmd = &cobra.Command{ + Use: "sqlcsvgen [output.csv]", + Short: "A CLI tool to generate random CSV data for SQL tables.", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + if err := generateCSV(args[0]); err != nil { + fmt.Println(err) + os.Exit(1) + } + }, + } + + rootCmd.Flags().StringVarP(&dbname, "dbname", "d", "", "Database name") + rootCmd.Flags().StringVarP(&host, "host", "s", "localhost", "Host address") + rootCmd.Flags().IntVarP(&port, "port", "p", 1433, "Port number") + rootCmd.Flags().StringVarP(&username, "user", "u", "", "Database username") + rootCmd.Flags().StringVarP(&password, "password", "w", "", "Database password") + rootCmd.Flags().IntVarP(&numRows, "num-rows", "n", 1000, "Number of rows to generate") + rootCmd.Flags().StringVarP(&table, "table", "t", "", "Table to generate data") + rootCmd.MarkFlagRequired("dbname") + rootCmd.MarkFlagRequired("table") + + if err := rootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) + } +}