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  }