-
Notifications
You must be signed in to change notification settings - Fork 0
/
ds2dd.go
141 lines (114 loc) · 3.24 KB
/
ds2dd.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package main
import (
"context"
"fmt"
"log"
"os"
"sort"
"strings"
"go.mercari.io/datastore"
"go.mercari.io/datastore/clouddatastore"
)
const (
sqlHeader = `-- DDL generated by ds2dd. DO NOT EDIT!
`
nestedKeyDelimiter = "__"
)
type prop struct {
Repr []string `datastore:"property_representation"`
}
// represents map[table name][column name] = [<type1>, <type2>, ...]
type propertyTypes map[string]map[string][]string
// getProperties returns table/column/types from actual Datastore properties.
func getPropertyTypes(ctx context.Context, client datastore.Client) propertyTypes {
query := client.NewQuery("__property__")
var props []prop
keys, err := client.GetAll(ctx, query, &props)
if err != nil {
log.Fatalf("client.GetAll: %v", err)
}
propNum := len(props)
keyNum := len(keys)
if propNum != keyNum {
log.Fatalf("Invalid result: props %d values, keys %d values.", propNum, keyNum)
}
properties := make(propertyTypes)
for i := 0; i < propNum; i++ {
k := keys[i]
repr := props[i].Repr
tableName := k.ParentKey().Name()
colName := strings.Replace(k.Name(), ".", nestedKeyDelimiter, -1)
if properties[tableName] == nil {
properties[tableName] = make(map[string][]string)
}
properties[tableName][colName] = repr
}
return properties
}
func format(t propertyTypes) string {
var sqlStr string
tableNames := make([]string, 0, len(t))
for n, _ := range t {
tableNames = append(tableNames, n)
}
sort.Strings(tableNames)
for _, tableName := range tableNames {
cols, tableOk := t[tableName]
if !tableOk {
fmt.Fprintf(os.Stderr, "Unknown table entry. key name : %s\n", tableName)
continue
}
colNames := make([]string, 0, len(cols))
for n, _ := range cols {
colNames = append(colNames, n)
}
sort.Strings(colNames)
var columns []string
for _, colName := range colNames {
colTypes, columnOk := cols[colName]
if !columnOk {
fmt.Fprintf(os.Stderr, "Unknown column entry. key name : %s\n", colName)
continue
}
if len(colTypes) > 1 {
fmt.Fprintf(os.Stderr, "Type of %s is ambiguous. A first value is selected. : acceptable types are %v\n", colName, colTypes)
continue
}
t := propRepr2mysqlType(colTypes[0])
columns = append(columns, fmt.Sprintf("%s %s", colName, t))
}
sqlStr += fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s ( \n %s \n);\n", tableName, strings.Join(columns, ",\n "))
}
return sqlStr
}
// Convert property_representation to MySQL type.
// See detail about the repr on https://cloud.google.com/datastore/docs/concepts/metadataqueries#property_queries_property_representations
func propRepr2mysqlType(propRepr string) string {
switch propRepr {
case "INT64":
return "BIGINT"
case "DOUBLE":
return "DOUBLE"
case "BOOLEAN":
return "BOOLEAN"
case "STRING":
return "VARCHAR(255)"
default:
fmt.Fprintf(os.Stderr, "Non convertable type : %s\n", propRepr)
os.Exit(-1)
}
return ""
}
func main() {
ctx := context.Background()
// Pass config via env.
// If you use Datastore emulator, get it by 'gcloud beta emulators datastore env-init'
client, err := clouddatastore.FromContext(ctx)
if err != nil {
log.Fatal(err)
}
defer client.Close()
properties := getPropertyTypes(ctx, client)
sqlStr := sqlHeader + format(properties)
fmt.Println(sqlStr)
}