github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/cmd/generate-binary/main.go (about) 1 // Copyright 2016 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 // This connects to a postgres server and crafts postgres-protocol message 12 // to encode its arguments into postgres' text and binary encodings. The 13 // result is printed as JSON "test cases" on standard out. If no arguments 14 // are provided, a set of default values for the specified data type will 15 // be sent. If arguments are provided, they will be sent as the values. 16 // 17 // The target postgres server must accept plaintext (non-ssl) connections from 18 // the postgres:postgres account. A suitable server can be started with: 19 // 20 // `docker run -p 127.0.0.1:5432:5432 postgres` 21 // 22 // The output of this file generates pkg/sql/pgwire/testdata/encodings.json. 23 package main 24 25 import ( 26 "bytes" 27 "context" 28 "encoding/json" 29 "flag" 30 "fmt" 31 "log" 32 "math" 33 "os" 34 "sort" 35 "text/template" 36 37 "github.com/cockroachdb/cockroach/pkg/cmd/cmp-protocol/pgconnect" 38 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgwirebase" 39 ) 40 41 var ( 42 postgresAddr = flag.String("addr", "localhost:5432", "Postgres server address") 43 postgresUser = flag.String("user", "postgres", "Postgres user") 44 45 funcMap = template.FuncMap{ 46 "json": func(v interface{}) (string, error) { 47 b, err := json.Marshal(v) 48 return string(b), err 49 }, 50 "binary": toString, 51 } 52 tmpl = template.Must(template.New("json").Funcs(funcMap).Parse(outputJSON)) 53 ) 54 55 func main() { 56 flag.Parse() 57 58 var data []entry 59 ctx := context.Background() 60 61 stmts := os.Args[1:] 62 63 if len(stmts) == 0 { 64 // Sort hard coded inputs by key name. 65 var formats []string 66 for format := range inputs { 67 formats = append(formats, format) 68 } 69 sort.Strings(formats) 70 71 for _, format := range formats { 72 list := inputs[format] 73 for _, input := range list { 74 sql := fmt.Sprintf(format, input) 75 stmts = append(stmts, sql) 76 } 77 } 78 } 79 80 for _, expr := range stmts { 81 sql := fmt.Sprintf("SELECT %s", expr) 82 text, err := pgconnect.Connect(ctx, sql, *postgresAddr, *postgresUser, pgwirebase.FormatText) 83 if err != nil { 84 log.Fatalf("text: %s: %v", sql, err) 85 } 86 binary, err := pgconnect.Connect(ctx, sql, *postgresAddr, *postgresUser, pgwirebase.FormatBinary) 87 if err != nil { 88 log.Fatalf("binary: %s: %v", sql, err) 89 } 90 sql = fmt.Sprintf("SELECT pg_typeof(%s)::int", expr) 91 id, err := pgconnect.Connect(ctx, sql, *postgresAddr, *postgresUser, pgwirebase.FormatText) 92 if err != nil { 93 log.Fatalf("oid: %s: %v", sql, err) 94 } 95 data = append(data, entry{ 96 SQL: expr, 97 Oid: string(id), 98 Text: text, 99 Binary: binary, 100 }) 101 } 102 103 // This code "manually" produces JSON to avoid the inconvenience where the 104 // json package insists on serializing byte arrays as base64-encoded 105 // strings, and integer arrays with each member on a separate line. We want 106 // integer array-looking output with all members on the same line. 107 if err := tmpl.Execute(os.Stdout, data); err != nil { 108 log.Fatal(err) 109 } 110 } 111 112 type entry struct { 113 SQL string 114 Oid string 115 Text []byte 116 Binary []byte 117 } 118 119 func toString(b []byte) string { 120 var buf bytes.Buffer 121 buf.WriteString("[") 122 for i, e := range b { 123 if i > 0 { 124 buf.WriteString(", ") 125 } 126 fmt.Fprint(&buf, e) 127 } 128 buf.WriteString("]") 129 return buf.String() 130 } 131 132 const outputJSON = `[ 133 {{- range $idx, $ele := .}} 134 {{- if gt $idx 0 }},{{end}} 135 { 136 "SQL": {{.SQL | json}}, 137 "Oid": {{.Oid}}, 138 "Text": {{printf "%q" .Text}}, 139 "TextAsBinary": {{.Text | binary}}, 140 "Binary": {{.Binary | binary}} 141 } 142 {{- end}} 143 ] 144 ` 145 146 var inputs = map[string][]string{ 147 "'%s'::decimal": { 148 "NaN", 149 "-000.000", 150 "-0000021234.23246346000000", 151 "-1.2", 152 ".0", 153 ".1", 154 ".1234", 155 ".12345", 156 "0", 157 "0.", 158 "0.0", 159 "0.000006", 160 "0.0000124000", 161 "0.00005", 162 "0.0004", 163 "0.003", 164 "0.00300", 165 "0.02", 166 "0.038665987681445668", 167 "0.1", 168 "00.00", 169 "1", 170 "1.000000000000006", 171 "1.00000000000005", 172 "1.0000000000004", 173 "1.000000000003", 174 "1.00000000002", 175 "1.0000000001", 176 "1.000000009", 177 "1.00000008", 178 "1.0000007", 179 "1.000006", 180 "1.00005", 181 "1.0004", 182 "1.003", 183 "1.02", 184 "1.1", 185 "10000.000006", 186 "10000.00005", 187 "10000.0004", 188 "10000.003", 189 "10000.02", 190 "10000.1", 191 "1000000", 192 "123", 193 "12345", 194 "12345.1", 195 "12345.1234", 196 "12345.12345", 197 "2.2289971159100284", 198 "3409589268520956934250.234098732045120934701239846", 199 "42", 200 }, 201 202 "'%s'::float8": { 203 // The Go binary encoding of NaN differs from Postgres by a 1 at the 204 // end. Go also uses Inf instead of Infinity (used by Postgres) for text 205 // float encodings. These deviations are still correct, and it's not worth 206 // special casing them into the code, so they are commented out here. 207 //"NaN", 208 //"Inf", 209 //"-Inf", 210 "-000.000", 211 "-0000021234.23246346000000", 212 "-1.2", 213 ".0", 214 ".1", 215 ".1234", 216 ".12345", 217 fmt.Sprint(math.MaxFloat32), 218 fmt.Sprint(math.SmallestNonzeroFloat32), 219 fmt.Sprint(math.MaxFloat64), 220 fmt.Sprint(math.SmallestNonzeroFloat64), 221 }, 222 223 "'%s'::float4": { 224 // The Go binary encoding of NaN differs from Postgres by a 1 at the 225 // end. Go also uses Inf instead of Infinity (used by Postgres) for text 226 // float encodings. These deviations are still correct, and it's not worth 227 // special casing them into the code, so they are commented out here. 228 //"NaN", 229 //"Inf", 230 //"-Inf", 231 "-000.000", 232 "-0000021234.2", 233 "-1.2", 234 ".0", 235 ".1", 236 ".1234", 237 ".12345", 238 "3.40282e+38", 239 "1.4013e-45", 240 }, 241 242 "'%s'::int2": { 243 "0", 244 "1", 245 "-1", 246 "-32768", 247 "32767", 248 }, 249 250 "'%s'::int4": { 251 "0", 252 "1", 253 "-1", 254 "-32768", 255 "32767", 256 "-2147483648", 257 "2147483647", 258 }, 259 260 "'%s'::int8": { 261 "0", 262 "1", 263 "-1", 264 "-32768", 265 "32767", 266 "-2147483648", 267 "2147483647", 268 "-9223372036854775808", 269 "9223372036854775807", 270 }, 271 272 "'%s'::timestamp": { 273 "1999-01-08 04:05:06+00", 274 "1999-01-08 04:05:06+00:00", 275 "1999-01-08 04:05:06+10", 276 "1999-01-08 04:05:06+10:00", 277 "1999-01-08 04:05:06+10:30", 278 "1999-01-08 04:05:06", 279 "2004-10-19 10:23:54", 280 "0001-01-01 00:00:00", 281 "0004-10-19 10:23:54", 282 "0004-10-19 10:23:54 BC", 283 "4004-10-19 10:23:54", 284 "9004-10-19 10:23:54", 285 }, 286 287 /* TODO(mjibson): fix these; there's a slight timezone display difference 288 "'%s'::timestamptz": { 289 "1999-01-08 04:05:06+00", 290 "1999-01-08 04:05:06+00:00", 291 "1999-01-08 04:05:06+10", 292 "1999-01-08 04:05:06+10:00", 293 "1999-01-08 04:05:06+10:30", 294 "1999-01-08 04:05:06", 295 "2004-10-19 10:23:54", 296 "0001-01-01 00:00:00", 297 "0004-10-19 10:23:54", 298 "0004-10-19 10:23:54 BC", 299 "4004-10-19 10:23:54", 300 "9004-10-19 10:23:54", 301 }, 302 */ 303 304 "'%s'::date": { 305 "1999-01-08", 306 "0009-01-08", 307 "9999-01-08", 308 "1999-12-30", 309 "1996-02-29", 310 "0001-01-01", 311 "0001-12-31 BC", 312 "0001-01-01 BC", 313 "3592-12-31 BC", 314 "4713-01-01 BC", 315 "4714-11-24 BC", 316 "5874897-12-31", 317 "2000-01-01", 318 "2000-01-02", 319 "1999-12-31", 320 "infinity", 321 "-infinity", 322 "epoch", 323 }, 324 325 "'%s'::time": { 326 "00:00:00", 327 "12:00:00.000001", 328 "23:59:59.999999", 329 }, 330 331 "'%s'::interval": { 332 "10y10mon", 333 "10mon10d", 334 "1y1mon", 335 "1y1m", 336 "1y", 337 "1mon", 338 "21d2h", 339 "1w", 340 "1d", 341 "23:12:34", 342 "21 days", 343 "3h", 344 "2h", 345 "1h", 346 "1m", 347 "1s", 348 "-23:00:00", 349 "-10d", 350 "-1mon", 351 "-1mon10s", 352 "-1y", 353 "-1y1mon", 354 "-1y1mon10s", 355 "1ms", 356 ".2ms", 357 ".003ms", 358 "-6s2ms", 359 "-1d6s2ms", 360 "-1d -6s2ms", 361 "-1mon1m", 362 "-1mon -1m", 363 "-1d1m", 364 "-1d -1m", 365 "-1y1m", 366 "-1y -1m", 367 "3y4mon5d6ms", 368 "296537y20d15h30m7s", 369 "-2965y -20d -15h -30m -7s", 370 "00:00:00", 371 "-00:00:00", 372 }, 373 374 "'%s'::inet": { 375 "0.0.0.0", 376 "0.0.0.0/20", 377 "0.0.0.0/0", 378 "255.255.255.255", 379 "255.255.255.255/10", 380 "::0/0", 381 "::0/64", 382 "::0", 383 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 384 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/0", 385 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/10", 386 "0.0.0.1", 387 "111::fff/120", 388 "127.0.0.1/10", 389 "192.168.1.2", 390 "192.168.1.2/16", 391 "192.168.1.2/10", 392 "2001:4f8:3:ba::/64", 393 "2001:4f8:3:ba:2e0:81ff:fe22:d1f1/128", 394 "::ffff:1.2.3.1/120", 395 "::ffff:1.2.3.1/128", 396 "::ffff:1.2.3.1/120", 397 "::ffff:1.2.3.1/20", 398 "::1", 399 "192/10", 400 "192.168/23", 401 "192.168./10", 402 }, 403 404 "'%s'::jsonb": { 405 `123`, 406 `"hello"`, 407 `{}`, 408 `[]`, 409 `0`, 410 `0.0000`, 411 `""`, 412 `"\uD83D\uDE80"`, 413 `{"\uD83D\uDE80": "hello"}`, 414 `[1, 2, 3]`, 415 `{"foo": 123}`, 416 `{"foo": {"bar": true}}`, 417 `true`, 418 `false`, 419 `null`, 420 `[[[[true, false, null]]]]`, 421 `["\u0001", "\u0041", "\u26a3", "\ud83e\udd37"]`, 422 }, 423 424 "'%s'::uuid[]": { 425 "{00000000-0000-0000-0000-000000000000}", 426 "{9753b405-88c0-4e93-b6c3-4e49fff11b57}", 427 "{be18196d-b20a-4df2-8a2b-259c22842ee8,e0794335-6d39-47d9-b836-1f2ff349bf5d}", 428 }, 429 430 "'%s'::decimal[]": { 431 "{-000.000,-0000021234.23246346000000,-1.2,.0,.1,.1234}", 432 "{.12345,0,0.,0.0,0.000006}", 433 "{0.0000124000,0.00005,0.0004,0.003,0.00300,0.02,0.038665987681445668}", 434 "{0.1,00.00,1}", 435 "{1.000000000000006,1.00000000000005,1.0000000000004,1.000000000003,1.00000000002,1.0000000001,1.000000009,1.00000008,1.0000007,1.000006,1.00005,1.0004,1.003,1.02,1.1}", 436 "{10000.000006}", 437 "{10000.00005}", 438 "{10000.0004}", 439 "{10000.003,10000.02,10000.1,1000000,123}", 440 "{12345,12345.1,12345.1234,12345.12345}", 441 "{2.2289971159100284,3409589268520956934250.234098732045120934701239846,42}", 442 }, 443 444 "B'%s'": { 445 "", 446 "0", 447 "1", 448 "010", 449 "00000000", 450 "000000001", 451 "0010101000011010101111100100011001110101100001010101", 452 "00101010000110101011111001000110011101011000000101010000110101011111001000110011101011000010101011010101", 453 "0010101000011010101111100100011001110101001010100001101010111110010001100111010110000100101010000110101011111001000110011101011000010101010101010000101010000110101011111001000110011101010010101000011010101111100100011001110101100001001010100001101010111110010001100111010110000101010101010100101010000110101011111001000110011101011000010010101000011010101111100100011011111111111111111111111111111111111111111111111111111111111111110111010111111111111111111111111111111111111111111111111111111111111111111000010101010000000000000000000000000000000000000000000000000000000000000000101011000010010101000011010101111100100011001110101100001010101010101101010000110101011111001000110011101011000010010101000011010101111100100011011111111111111111111111111111111111111111111111111111111111111110111010111111111111111111111111111111111111111111111111111111111111111111000010101010000000000000000000000000000000000000000000000000000000000000000101011000010010101000011010101111100100011001110101100001010101010101", 454 "000000000000000000000000000000000000000000000000000000000000000", 455 "0000000000000000000000000000000000000000000000000000000000000000", 456 "00000000000000000000000000000000000000000000000000000000000000000", 457 "000000000000000000000000000000000000000000000000000000000000001", 458 "0000000000000000000000000000000000000000000000000000000000000001", 459 "00000000000000000000000000000000000000000000000000000000000000001", 460 "100000000000000000000000000000000000000000000000000000000000000", 461 "1000000000000000000000000000000000000000000000000000000000000000", 462 "10000000000000000000000000000000000000000000000000000000000000000", 463 "111111111111111111111111111111111111111111111111111111111111111", 464 "1111111111111111111111111111111111111111111111111111111111111111", 465 "11111111111111111111111111111111111111111111111111111111111111111", 466 }, 467 468 "array[%s]::text[]": { 469 `NULL`, 470 `NULL,NULL`, 471 `1,NULL,2`, 472 `''`, 473 `'test'`, 474 `'test with spaces'`, 475 `e'\f'`, 476 // byte order mark 477 `e'\uFEFF'`, 478 // snowman 479 `e'\u2603'`, 480 }, 481 482 "array[%s]": { 483 `''`, 484 `'\x0d53e338548082'::BYTEA`, 485 `'test with spaces'::BYTEA`, 486 `'name'::NAME`, 487 }, 488 489 "%s": { 490 `array[1,NULL]::int8[]`, 491 `array[0.1,NULL]::float8[]`, 492 `array[1,NULL]::numeric[]`, 493 `array['test',NULL]::text[]`, 494 `array['test',NULL]::name[]`, 495 `array[]::int4[]`, 496 }, 497 498 "(%s,null)": { 499 `1::int8,2::int8,3::int8,4::int8`, 500 `'test with spaces'::BYTEA`, 501 `'test with spaces'::TEXT`, 502 `'name'::NAME`, 503 `'false'::JSONB`, 504 `'{"a": []}'::JSONB`, 505 }, 506 }