...

Source file src/github.com/jackc/pgtype/hstore_array_test.go

Documentation: github.com/jackc/pgtype

     1  package pgtype_test
     2  
     3  import (
     4  	"context"
     5  	"reflect"
     6  	"testing"
     7  
     8  	"github.com/jackc/pgtype"
     9  	"github.com/jackc/pgtype/testutil"
    10  	"github.com/jackc/pgx/v4"
    11  )
    12  
    13  func TestHstoreArrayTranscode(t *testing.T) {
    14  	conn := testutil.MustConnectPgx(t)
    15  	defer testutil.MustCloseContext(t, conn)
    16  
    17  	var hstoreOID uint32
    18  	err := conn.QueryRow(context.Background(), "select t.oid from pg_type t where t.typname='hstore';").Scan(&hstoreOID)
    19  	if err != nil {
    20  		t.Fatalf("did not find hstore OID, %v", err)
    21  	}
    22  	conn.ConnInfo().RegisterDataType(pgtype.DataType{Value: &pgtype.Hstore{}, Name: "hstore", OID: hstoreOID})
    23  
    24  	var hstoreArrayOID uint32
    25  	err = conn.QueryRow(context.Background(), "select t.oid from pg_type t where t.typname='_hstore';").Scan(&hstoreArrayOID)
    26  	if err != nil {
    27  		t.Fatalf("did not find _hstore OID, %v", err)
    28  	}
    29  	conn.ConnInfo().RegisterDataType(pgtype.DataType{Value: &pgtype.HstoreArray{}, Name: "_hstore", OID: hstoreArrayOID})
    30  
    31  	text := func(s string) pgtype.Text {
    32  		return pgtype.Text{String: s, Status: pgtype.Present}
    33  	}
    34  
    35  	values := []pgtype.Hstore{
    36  		{Map: map[string]pgtype.Text{}, Status: pgtype.Present},
    37  		{Map: map[string]pgtype.Text{"foo": text("bar")}, Status: pgtype.Present},
    38  		{Map: map[string]pgtype.Text{"foo": text("bar"), "baz": text("quz")}, Status: pgtype.Present},
    39  		{Map: map[string]pgtype.Text{"NULL": text("bar")}, Status: pgtype.Present},
    40  		{Map: map[string]pgtype.Text{"foo": text("NULL")}, Status: pgtype.Present},
    41  		{Status: pgtype.Null},
    42  	}
    43  
    44  	specialStrings := []string{
    45  		`"`,
    46  		`'`,
    47  		`\`,
    48  		`\\`,
    49  		`=>`,
    50  		` `,
    51  		`\ / / \\ => " ' " '`,
    52  	}
    53  	for _, s := range specialStrings {
    54  		// Special key values
    55  		values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{s + "foo": text("bar")}, Status: pgtype.Present})         // at beginning
    56  		values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo" + s + "bar": text("bar")}, Status: pgtype.Present}) // in middle
    57  		values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo" + s: text("bar")}, Status: pgtype.Present})         // at end
    58  		values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{s: text("bar")}, Status: pgtype.Present})                 // is key
    59  
    60  		// Special value values
    61  		values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text(s + "bar")}, Status: pgtype.Present})         // at beginning
    62  		values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("foo" + s + "bar")}, Status: pgtype.Present}) // in middle
    63  		values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("foo" + s)}, Status: pgtype.Present})         // at end
    64  		values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text(s)}, Status: pgtype.Present})                 // is key
    65  	}
    66  
    67  	src := &pgtype.HstoreArray{
    68  		Elements:   values,
    69  		Dimensions: []pgtype.ArrayDimension{{Length: int32(len(values)), LowerBound: 1}},
    70  		Status:     pgtype.Present,
    71  	}
    72  
    73  	_, err = conn.Prepare(context.Background(), "test", "select $1::hstore[]")
    74  	if err != nil {
    75  		t.Fatal(err)
    76  	}
    77  
    78  	formats := []struct {
    79  		name       string
    80  		formatCode int16
    81  	}{
    82  		{name: "TextFormat", formatCode: pgx.TextFormatCode},
    83  		{name: "BinaryFormat", formatCode: pgx.BinaryFormatCode},
    84  	}
    85  
    86  	for _, fc := range formats {
    87  		queryResultFormats := pgx.QueryResultFormats{fc.formatCode}
    88  		vEncoder := testutil.ForceEncoder(src, fc.formatCode)
    89  		if vEncoder == nil {
    90  			t.Logf("%#v does not implement %v", src, fc.name)
    91  			continue
    92  		}
    93  
    94  		var result pgtype.HstoreArray
    95  		err := conn.QueryRow(context.Background(), "test", queryResultFormats, vEncoder).Scan(&result)
    96  		if err != nil {
    97  			t.Errorf("%v: %v", fc.name, err)
    98  			continue
    99  		}
   100  
   101  		if result.Status != src.Status {
   102  			t.Errorf("%v: expected Status %v, got %v", fc.formatCode, src.Status, result.Status)
   103  			continue
   104  		}
   105  
   106  		if len(result.Elements) != len(src.Elements) {
   107  			t.Errorf("%v: expected %v elements, got %v", fc.formatCode, len(src.Elements), len(result.Elements))
   108  			continue
   109  		}
   110  
   111  		for i := range result.Elements {
   112  			a := src.Elements[i]
   113  			b := result.Elements[i]
   114  
   115  			if a.Status != b.Status {
   116  				t.Errorf("%v element idx %d: expected status %v, got %v", fc.formatCode, i, a.Status, b.Status)
   117  			}
   118  
   119  			if len(a.Map) != len(b.Map) {
   120  				t.Errorf("%v element idx %d: expected %v pairs, got %v", fc.formatCode, i, len(a.Map), len(b.Map))
   121  			}
   122  
   123  			for k := range a.Map {
   124  				if a.Map[k] != b.Map[k] {
   125  					t.Errorf("%v element idx %d: expected key %v to be %v, got %v", fc.formatCode, i, k, a.Map[k], b.Map[k])
   126  				}
   127  			}
   128  		}
   129  	}
   130  }
   131  
   132  func TestHstoreArraySet(t *testing.T) {
   133  	successfulTests := []struct {
   134  		src    interface{}
   135  		result pgtype.HstoreArray
   136  	}{
   137  		{
   138  			src: []map[string]string{{"foo": "bar"}},
   139  			result: pgtype.HstoreArray{
   140  				Elements: []pgtype.Hstore{
   141  					{
   142  						Map:    map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}},
   143  						Status: pgtype.Present,
   144  					},
   145  				},
   146  				Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
   147  				Status:     pgtype.Present,
   148  			},
   149  		},
   150  		{
   151  			src: [][]map[string]string{{{"foo": "bar"}}, {{"baz": "quz"}}},
   152  			result: pgtype.HstoreArray{
   153  				Elements: []pgtype.Hstore{
   154  					{
   155  						Map:    map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}},
   156  						Status: pgtype.Present,
   157  					},
   158  					{
   159  						Map:    map[string]pgtype.Text{"baz": {String: "quz", Status: pgtype.Present}},
   160  						Status: pgtype.Present,
   161  					},
   162  				},
   163  				Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}},
   164  				Status:     pgtype.Present,
   165  			},
   166  		},
   167  		{
   168  			src: [][][][]map[string]string{
   169  				{{{{"foo": "bar"}, {"baz": "quz"}, {"bar": "baz"}}}},
   170  				{{{{"wibble": "wobble"}, {"wubble": "wabble"}, {"wabble": "wobble"}}}}},
   171  			result: pgtype.HstoreArray{
   172  				Elements: []pgtype.Hstore{
   173  					{
   174  						Map:    map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}},
   175  						Status: pgtype.Present,
   176  					},
   177  					{
   178  						Map:    map[string]pgtype.Text{"baz": {String: "quz", Status: pgtype.Present}},
   179  						Status: pgtype.Present,
   180  					},
   181  					{
   182  						Map:    map[string]pgtype.Text{"bar": {String: "baz", Status: pgtype.Present}},
   183  						Status: pgtype.Present,
   184  					},
   185  					{
   186  						Map:    map[string]pgtype.Text{"wibble": {String: "wobble", Status: pgtype.Present}},
   187  						Status: pgtype.Present,
   188  					},
   189  					{
   190  						Map:    map[string]pgtype.Text{"wubble": {String: "wabble", Status: pgtype.Present}},
   191  						Status: pgtype.Present,
   192  					},
   193  					{
   194  						Map:    map[string]pgtype.Text{"wabble": {String: "wobble", Status: pgtype.Present}},
   195  						Status: pgtype.Present,
   196  					},
   197  				},
   198  				Dimensions: []pgtype.ArrayDimension{
   199  					{LowerBound: 1, Length: 2},
   200  					{LowerBound: 1, Length: 1},
   201  					{LowerBound: 1, Length: 1},
   202  					{LowerBound: 1, Length: 3}},
   203  				Status: pgtype.Present,
   204  			},
   205  		},
   206  		{
   207  			src: [2][1]map[string]string{{{"foo": "bar"}}, {{"baz": "quz"}}},
   208  			result: pgtype.HstoreArray{
   209  				Elements: []pgtype.Hstore{
   210  					{
   211  						Map:    map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}},
   212  						Status: pgtype.Present,
   213  					},
   214  					{
   215  						Map:    map[string]pgtype.Text{"baz": {String: "quz", Status: pgtype.Present}},
   216  						Status: pgtype.Present,
   217  					},
   218  				},
   219  				Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}},
   220  				Status:     pgtype.Present,
   221  			},
   222  		},
   223  		{
   224  			src: [2][1][1][3]map[string]string{
   225  				{{{{"foo": "bar"}, {"baz": "quz"}, {"bar": "baz"}}}},
   226  				{{{{"wibble": "wobble"}, {"wubble": "wabble"}, {"wabble": "wobble"}}}}},
   227  			result: pgtype.HstoreArray{
   228  				Elements: []pgtype.Hstore{
   229  					{
   230  						Map:    map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}},
   231  						Status: pgtype.Present,
   232  					},
   233  					{
   234  						Map:    map[string]pgtype.Text{"baz": {String: "quz", Status: pgtype.Present}},
   235  						Status: pgtype.Present,
   236  					},
   237  					{
   238  						Map:    map[string]pgtype.Text{"bar": {String: "baz", Status: pgtype.Present}},
   239  						Status: pgtype.Present,
   240  					},
   241  					{
   242  						Map:    map[string]pgtype.Text{"wibble": {String: "wobble", Status: pgtype.Present}},
   243  						Status: pgtype.Present,
   244  					},
   245  					{
   246  						Map:    map[string]pgtype.Text{"wubble": {String: "wabble", Status: pgtype.Present}},
   247  						Status: pgtype.Present,
   248  					},
   249  					{
   250  						Map:    map[string]pgtype.Text{"wabble": {String: "wobble", Status: pgtype.Present}},
   251  						Status: pgtype.Present,
   252  					},
   253  				},
   254  				Dimensions: []pgtype.ArrayDimension{
   255  					{LowerBound: 1, Length: 2},
   256  					{LowerBound: 1, Length: 1},
   257  					{LowerBound: 1, Length: 1},
   258  					{LowerBound: 1, Length: 3}},
   259  				Status: pgtype.Present,
   260  			},
   261  		},
   262  	}
   263  
   264  	for i, tt := range successfulTests {
   265  		var dst pgtype.HstoreArray
   266  		err := dst.Set(tt.src)
   267  		if err != nil {
   268  			t.Errorf("%d: %v", i, err)
   269  		}
   270  
   271  		if !reflect.DeepEqual(dst, tt.result) {
   272  			t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.src, tt.result, dst)
   273  		}
   274  	}
   275  }
   276  
   277  func TestHstoreArrayAssignTo(t *testing.T) {
   278  	var hstoreSlice []map[string]string
   279  	var hstoreSliceDim2 [][]map[string]string
   280  	var hstoreSliceDim4 [][][][]map[string]string
   281  	var hstoreArrayDim2 [2][1]map[string]string
   282  	var hstoreArrayDim4 [2][1][1][3]map[string]string
   283  
   284  	simpleTests := []struct {
   285  		src      pgtype.HstoreArray
   286  		dst      interface{}
   287  		expected interface{}
   288  	}{
   289  		{
   290  			src: pgtype.HstoreArray{
   291  				Elements: []pgtype.Hstore{
   292  					{
   293  						Map:    map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}},
   294  						Status: pgtype.Present,
   295  					},
   296  				},
   297  				Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
   298  				Status:     pgtype.Present,
   299  			},
   300  			dst:      &hstoreSlice,
   301  			expected: []map[string]string{{"foo": "bar"}}},
   302  		{
   303  			src: pgtype.HstoreArray{Status: pgtype.Null}, dst: &hstoreSlice, expected: (([]map[string]string)(nil)),
   304  		},
   305  		{
   306  			src: pgtype.HstoreArray{Status: pgtype.Present}, dst: &hstoreSlice, expected: []map[string]string{},
   307  		},
   308  		{
   309  			src: pgtype.HstoreArray{
   310  				Elements: []pgtype.Hstore{
   311  					{
   312  						Map:    map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}},
   313  						Status: pgtype.Present,
   314  					},
   315  					{
   316  						Map:    map[string]pgtype.Text{"baz": {String: "quz", Status: pgtype.Present}},
   317  						Status: pgtype.Present,
   318  					},
   319  				},
   320  				Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}},
   321  				Status:     pgtype.Present,
   322  			},
   323  			dst:      &hstoreSliceDim2,
   324  			expected: [][]map[string]string{{{"foo": "bar"}}, {{"baz": "quz"}}},
   325  		},
   326  		{
   327  			src: pgtype.HstoreArray{
   328  				Elements: []pgtype.Hstore{
   329  					{
   330  						Map:    map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}},
   331  						Status: pgtype.Present,
   332  					},
   333  					{
   334  						Map:    map[string]pgtype.Text{"baz": {String: "quz", Status: pgtype.Present}},
   335  						Status: pgtype.Present,
   336  					},
   337  					{
   338  						Map:    map[string]pgtype.Text{"bar": {String: "baz", Status: pgtype.Present}},
   339  						Status: pgtype.Present,
   340  					},
   341  					{
   342  						Map:    map[string]pgtype.Text{"wibble": {String: "wobble", Status: pgtype.Present}},
   343  						Status: pgtype.Present,
   344  					},
   345  					{
   346  						Map:    map[string]pgtype.Text{"wubble": {String: "wabble", Status: pgtype.Present}},
   347  						Status: pgtype.Present,
   348  					},
   349  					{
   350  						Map:    map[string]pgtype.Text{"wabble": {String: "wobble", Status: pgtype.Present}},
   351  						Status: pgtype.Present,
   352  					},
   353  				},
   354  				Dimensions: []pgtype.ArrayDimension{
   355  					{LowerBound: 1, Length: 2},
   356  					{LowerBound: 1, Length: 1},
   357  					{LowerBound: 1, Length: 1},
   358  					{LowerBound: 1, Length: 3}},
   359  				Status: pgtype.Present,
   360  			},
   361  			dst: &hstoreSliceDim4,
   362  			expected: [][][][]map[string]string{
   363  				{{{{"foo": "bar"}, {"baz": "quz"}, {"bar": "baz"}}}},
   364  				{{{{"wibble": "wobble"}, {"wubble": "wabble"}, {"wabble": "wobble"}}}}},
   365  		},
   366  		{
   367  			src: pgtype.HstoreArray{
   368  				Elements: []pgtype.Hstore{
   369  					{
   370  						Map:    map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}},
   371  						Status: pgtype.Present,
   372  					},
   373  					{
   374  						Map:    map[string]pgtype.Text{"baz": {String: "quz", Status: pgtype.Present}},
   375  						Status: pgtype.Present,
   376  					},
   377  				},
   378  				Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}},
   379  				Status:     pgtype.Present,
   380  			},
   381  			dst:      &hstoreArrayDim2,
   382  			expected: [2][1]map[string]string{{{"foo": "bar"}}, {{"baz": "quz"}}},
   383  		},
   384  		{
   385  			src: pgtype.HstoreArray{
   386  				Elements: []pgtype.Hstore{
   387  					{
   388  						Map:    map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}},
   389  						Status: pgtype.Present,
   390  					},
   391  					{
   392  						Map:    map[string]pgtype.Text{"baz": {String: "quz", Status: pgtype.Present}},
   393  						Status: pgtype.Present,
   394  					},
   395  					{
   396  						Map:    map[string]pgtype.Text{"bar": {String: "baz", Status: pgtype.Present}},
   397  						Status: pgtype.Present,
   398  					},
   399  					{
   400  						Map:    map[string]pgtype.Text{"wibble": {String: "wobble", Status: pgtype.Present}},
   401  						Status: pgtype.Present,
   402  					},
   403  					{
   404  						Map:    map[string]pgtype.Text{"wubble": {String: "wabble", Status: pgtype.Present}},
   405  						Status: pgtype.Present,
   406  					},
   407  					{
   408  						Map:    map[string]pgtype.Text{"wabble": {String: "wobble", Status: pgtype.Present}},
   409  						Status: pgtype.Present,
   410  					},
   411  				},
   412  				Dimensions: []pgtype.ArrayDimension{
   413  					{LowerBound: 1, Length: 2},
   414  					{LowerBound: 1, Length: 1},
   415  					{LowerBound: 1, Length: 1},
   416  					{LowerBound: 1, Length: 3}},
   417  				Status: pgtype.Present,
   418  			},
   419  			dst: &hstoreArrayDim4,
   420  			expected: [2][1][1][3]map[string]string{
   421  				{{{{"foo": "bar"}, {"baz": "quz"}, {"bar": "baz"}}}},
   422  				{{{{"wibble": "wobble"}, {"wubble": "wabble"}, {"wabble": "wobble"}}}}},
   423  		},
   424  	}
   425  
   426  	for i, tt := range simpleTests {
   427  		err := tt.src.AssignTo(tt.dst)
   428  		if err != nil {
   429  			t.Errorf("%d: %v", i, err)
   430  		}
   431  
   432  		if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) {
   433  			t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst)
   434  		}
   435  	}
   436  }
   437  

View as plain text