1+ // Copyright 2025 Google LLC
2+ //
3+ // Licensed under the Apache License, Version 2.0 (the "License");
4+ // you may not use this file except in compliance with the License.
5+ // You may obtain a copy of the License at
6+ //
7+ // https://www.apache.org/licenses/LICENSE-2.0
8+ //
9+ // Unless required by applicable law or agreed to in writing, software
10+ // distributed under the License is distributed on an "AS IS" BASIS,
11+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+ // See the License for the specific language governing permissions and
13+ // limitations under the License.
14+
15+ using System . Data ;
16+ using System . Data . Common ;
17+ using System . Text . RegularExpressions ;
18+ using TypeCode = Google . Cloud . Spanner . V1 . TypeCode ;
19+
20+ namespace Google . Cloud . Spanner . DataProvider . Tests ;
21+
22+ public class SchemaTests : AbstractMockServerTests
23+ {
24+ [ Test ]
25+ public async Task MetaDataCollections ( )
26+ {
27+ await using var conn = await OpenConnectionAsync ( ) ;
28+
29+ var metaDataCollections = await conn . GetSchemaAsync ( DbMetaDataCollectionNames . MetaDataCollections ) ;
30+ Assert . That ( metaDataCollections . Rows , Has . Count . GreaterThan ( 0 ) ) ;
31+
32+ foreach ( var row in metaDataCollections . Rows . OfType < DataRow > ( ) )
33+ {
34+ var collectionName = ( string ) row ! [ "CollectionName" ] ;
35+ Assert . That ( await conn . GetSchemaAsync ( collectionName ) , Is . Not . Null , $ "Collection { collectionName } advertise in MetaDataCollections but is null") ;
36+ }
37+ }
38+
39+ [ Test ]
40+ public async Task NoParameter ( )
41+ {
42+ await using var conn = await OpenConnectionAsync ( ) ;
43+
44+ var dataTable1 = conn . GetSchema ( ) ;
45+ var collections1 = dataTable1 . Rows
46+ . Cast < DataRow > ( )
47+ . Select ( r => ( string ) r [ "CollectionName" ] )
48+ . ToList ( ) ;
49+
50+ var dataTable2 = conn . GetSchema ( DbMetaDataCollectionNames . MetaDataCollections ) ;
51+ var collections2 = dataTable2 . Rows
52+ . Cast < DataRow > ( )
53+ . Select ( r => ( string ) r [ "CollectionName" ] )
54+ . ToList ( ) ;
55+
56+ Assert . That ( collections1 , Is . EquivalentTo ( collections2 ) ) ;
57+ }
58+
59+ [ Test ]
60+ public async Task CaseInsensitiveCollectionName ( )
61+ {
62+ await using var conn = await OpenConnectionAsync ( ) ;
63+
64+ var dataTable1 = conn . GetSchema ( DbMetaDataCollectionNames . MetaDataCollections ) ;
65+ var collections1 = dataTable1 . Rows
66+ . Cast < DataRow > ( )
67+ . Select ( r => ( string ) r [ "CollectionName" ] )
68+ . ToList ( ) ;
69+
70+ var dataTable2 = conn . GetSchema ( "METADATACOLLECTIONS" ) ;
71+ var collections2 = dataTable2 . Rows
72+ . Cast < DataRow > ( )
73+ . Select ( r => ( string ) r [ "CollectionName" ] )
74+ . ToList ( ) ;
75+
76+ var dataTable3 = conn . GetSchema ( "metadatacollections" ) ;
77+ var collections3 = dataTable3 . Rows
78+ . Cast < DataRow > ( )
79+ . Select ( r => ( string ) r [ "CollectionName" ] )
80+ . ToList ( ) ;
81+
82+ var dataTable4 = conn . GetSchema ( "MetaDataCollections" ) ;
83+ var collections4 = dataTable4 . Rows
84+ . Cast < DataRow > ( )
85+ . Select ( r => ( string ) r [ "CollectionName" ] )
86+ . ToList ( ) ;
87+
88+ var dataTable5 = conn . GetSchema ( "METADATACOLLECTIONS" , null ! ) ;
89+ var collections5 = dataTable5 . Rows
90+ . Cast < DataRow > ( )
91+ . Select ( r => ( string ) r [ "CollectionName" ] )
92+ . ToList ( ) ;
93+
94+ var dataTable6 = conn . GetSchema ( "metadatacollections" , null ! ) ;
95+ var collections6 = dataTable6 . Rows
96+ . Cast < DataRow > ( )
97+ . Select ( r => ( string ) r [ "CollectionName" ] )
98+ . ToList ( ) ;
99+
100+ var dataTable7 = conn . GetSchema ( "MetaDataCollections" , null ! ) ;
101+ var collections7 = dataTable7 . Rows
102+ . Cast < DataRow > ( )
103+ . Select ( r => ( string ) r [ "CollectionName" ] )
104+ . ToList ( ) ;
105+
106+ Assert . That ( collections1 , Is . EquivalentTo ( collections2 ) ) ;
107+ Assert . That ( collections1 , Is . EquivalentTo ( collections3 ) ) ;
108+ Assert . That ( collections1 , Is . EquivalentTo ( collections4 ) ) ;
109+ Assert . That ( collections1 , Is . EquivalentTo ( collections5 ) ) ;
110+ Assert . That ( collections1 , Is . EquivalentTo ( collections6 ) ) ;
111+ Assert . That ( collections1 , Is . EquivalentTo ( collections7 ) ) ;
112+ }
113+
114+ [ Test ]
115+ public async Task DataSourceInformation ( )
116+ {
117+ await using var conn = await OpenConnectionAsync ( ) ;
118+ var dataTable = conn . GetSchema ( DbMetaDataCollectionNames . MetaDataCollections ) ;
119+ var metadata = dataTable . Rows
120+ . Cast < DataRow > ( )
121+ . Single ( r => r [ "CollectionName" ] . Equals ( "DataSourceInformation" ) ) ;
122+ Assert . That ( metadata [ "NumberOfRestrictions" ] , Is . Zero ) ;
123+ Assert . That ( metadata [ "NumberOfIdentifierParts" ] , Is . Zero ) ;
124+
125+ var dataSourceInfo = conn . GetSchema ( DbMetaDataCollectionNames . DataSourceInformation ) ;
126+ var row = dataSourceInfo . Rows . Cast < DataRow > ( ) . Single ( ) ;
127+
128+ Assert . That ( row [ "DataSourceProductName" ] , Is . EqualTo ( "Spanner" ) ) ;
129+ Assert . That ( row [ "DataSourceProductVersion" ] , Is . EqualTo ( "1.0.0" ) ) ;
130+ Assert . That ( row [ "DataSourceProductVersionNormalized" ] , Is . EqualTo ( "001.000.0000" ) ) ;
131+
132+ Assert . That ( Regex . Match ( "`some_identifier`" , ( string ) row [ "QuotedIdentifierPattern" ] ) . Groups [ 1 ] . Value ,
133+ Is . EqualTo ( "some_identifier" ) ) ;
134+ }
135+
136+ [ Test ]
137+ public async Task DataTypes ( )
138+ {
139+ await using var connection = await OpenConnectionAsync ( ) ;
140+
141+ var dataTable = connection . GetSchema ( DbMetaDataCollectionNames . MetaDataCollections ) ;
142+ var metadata = dataTable . Rows
143+ . Cast < DataRow > ( )
144+ . Single ( r => r [ "CollectionName" ] . Equals ( "DataTypes" ) ) ;
145+ Assert . That ( metadata [ "NumberOfRestrictions" ] , Is . Zero ) ;
146+ Assert . That ( metadata [ "NumberOfIdentifierParts" ] , Is . Zero ) ;
147+
148+ var dataTypes = connection . GetSchema ( DbMetaDataCollectionNames . DataTypes ) ;
149+
150+ var boolRow = dataTypes . Rows . Cast < DataRow > ( ) . Single ( r => r [ "TypeName" ] . Equals ( "Bool" ) ) ;
151+ Assert . That ( boolRow [ "DataType" ] , Is . EqualTo ( "System.Boolean" ) ) ;
152+ Assert . That ( boolRow [ "ProviderDbType" ] , Is . EqualTo ( ( int ) TypeCode . Bool ) ) ;
153+ Assert . That ( boolRow [ "IsUnsigned" ] , Is . EqualTo ( DBNull . Value ) ) ;
154+
155+ var bytesRow = dataTypes . Rows . Cast < DataRow > ( ) . Single ( r => r [ "TypeName" ] . Equals ( "Bytes" ) ) ;
156+ Assert . That ( bytesRow [ "DataType" ] , Is . EqualTo ( "System.Byte[]" ) ) ;
157+ Assert . That ( bytesRow [ "ProviderDbType" ] , Is . EqualTo ( ( int ) TypeCode . Bytes ) ) ;
158+ Assert . That ( bytesRow [ "IsUnsigned" ] , Is . EqualTo ( DBNull . Value ) ) ;
159+ Assert . That ( bytesRow [ "IsBestMatch" ] , Is . True ) ;
160+
161+ var dateRow = dataTypes . Rows . Cast < DataRow > ( ) . Single ( r => r [ "TypeName" ] . Equals ( "Date" ) ) ;
162+ Assert . That ( dateRow [ "DataType" ] , Is . EqualTo ( "System.DateOnly" ) ) ;
163+ Assert . That ( dateRow [ "ProviderDbType" ] , Is . EqualTo ( ( int ) TypeCode . Date ) ) ;
164+ Assert . That ( dateRow [ "IsUnsigned" ] , Is . EqualTo ( DBNull . Value ) ) ;
165+ Assert . That ( dateRow [ "IsBestMatch" ] , Is . True ) ;
166+
167+ var enumRow = dataTypes . Rows . Cast < DataRow > ( ) . Single ( r => r [ "TypeName" ] . Equals ( "Enum" ) ) ;
168+ Assert . That ( enumRow [ "DataType" ] , Is . EqualTo ( "System.Int64" ) ) ;
169+ Assert . That ( enumRow [ "ProviderDbType" ] , Is . EqualTo ( ( int ) TypeCode . Enum ) ) ;
170+ Assert . That ( enumRow [ "IsUnsigned" ] , Is . EqualTo ( DBNull . Value ) ) ;
171+ Assert . That ( enumRow [ "IsBestMatch" ] , Is . False ) ;
172+
173+ var float32Row = dataTypes . Rows . Cast < DataRow > ( ) . Single ( r => r [ "TypeName" ] . Equals ( "Float32" ) ) ;
174+ Assert . That ( float32Row [ "DataType" ] , Is . EqualTo ( "System.Single" ) ) ;
175+ Assert . That ( float32Row [ "ProviderDbType" ] , Is . EqualTo ( ( int ) TypeCode . Float32 ) ) ;
176+ Assert . That ( float32Row [ "IsUnsigned" ] , Is . False ) ;
177+ Assert . That ( float32Row [ "IsBestMatch" ] , Is . True ) ;
178+
179+ var float64Row = dataTypes . Rows . Cast < DataRow > ( ) . Single ( r => r [ "TypeName" ] . Equals ( "Float64" ) ) ;
180+ Assert . That ( float64Row [ "DataType" ] , Is . EqualTo ( "System.Double" ) ) ;
181+ Assert . That ( float64Row [ "ProviderDbType" ] , Is . EqualTo ( ( int ) TypeCode . Float64 ) ) ;
182+ Assert . That ( float64Row [ "IsUnsigned" ] , Is . False ) ;
183+ Assert . That ( float64Row [ "IsBestMatch" ] , Is . True ) ;
184+
185+ var int64Row = dataTypes . Rows . Cast < DataRow > ( ) . Single ( r => r [ "TypeName" ] . Equals ( "Int64" ) ) ;
186+ Assert . That ( int64Row [ "DataType" ] , Is . EqualTo ( "System.Int64" ) ) ;
187+ Assert . That ( int64Row [ "ProviderDbType" ] , Is . EqualTo ( ( int ) TypeCode . Int64 ) ) ;
188+ Assert . That ( int64Row [ "IsUnsigned" ] , Is . False ) ;
189+ Assert . That ( int64Row [ "IsBestMatch" ] , Is . True ) ;
190+
191+ var intervalRow = dataTypes . Rows . Cast < DataRow > ( ) . Single ( r => r [ "TypeName" ] . Equals ( "Interval" ) ) ;
192+ Assert . That ( intervalRow [ "DataType" ] , Is . EqualTo ( "System.TimeSpan" ) ) ;
193+ Assert . That ( intervalRow [ "ProviderDbType" ] , Is . EqualTo ( ( int ) TypeCode . Interval ) ) ;
194+ Assert . That ( intervalRow [ "IsUnsigned" ] , Is . EqualTo ( DBNull . Value ) ) ;
195+ Assert . That ( intervalRow [ "IsBestMatch" ] , Is . True ) ;
196+
197+ var jsonRow = dataTypes . Rows . Cast < DataRow > ( ) . Single ( r => r [ "TypeName" ] . Equals ( "Json" ) ) ;
198+ Assert . That ( jsonRow [ "DataType" ] , Is . EqualTo ( "System.String" ) ) ;
199+ Assert . That ( jsonRow [ "ProviderDbType" ] , Is . EqualTo ( ( int ) TypeCode . Json ) ) ;
200+ Assert . That ( jsonRow [ "IsUnsigned" ] , Is . EqualTo ( DBNull . Value ) ) ;
201+ Assert . That ( jsonRow [ "IsBestMatch" ] , Is . False ) ;
202+
203+ var numericRow = dataTypes . Rows . Cast < DataRow > ( ) . Single ( r => r [ "TypeName" ] . Equals ( "Numeric" ) ) ;
204+ Assert . That ( numericRow [ "DataType" ] , Is . EqualTo ( "System.Decimal" ) ) ;
205+ Assert . That ( numericRow [ "ProviderDbType" ] , Is . EqualTo ( ( int ) TypeCode . Numeric ) ) ;
206+ Assert . That ( numericRow [ "IsUnsigned" ] , Is . False ) ;
207+ Assert . That ( numericRow [ "IsBestMatch" ] , Is . True ) ;
208+
209+ var protoRow = dataTypes . Rows . Cast < DataRow > ( ) . Single ( r => r [ "TypeName" ] . Equals ( "Proto" ) ) ;
210+ Assert . That ( protoRow [ "DataType" ] , Is . EqualTo ( "System.Byte[]" ) ) ;
211+ Assert . That ( protoRow [ "ProviderDbType" ] , Is . EqualTo ( ( int ) TypeCode . Proto ) ) ;
212+ Assert . That ( protoRow [ "IsUnsigned" ] , Is . EqualTo ( DBNull . Value ) ) ;
213+ Assert . That ( protoRow [ "IsBestMatch" ] , Is . False ) ;
214+
215+ var stringRow = dataTypes . Rows . Cast < DataRow > ( ) . Single ( r => r [ "TypeName" ] . Equals ( "String" ) ) ;
216+ Assert . That ( stringRow [ "DataType" ] , Is . EqualTo ( "System.String" ) ) ;
217+ Assert . That ( stringRow [ "ProviderDbType" ] , Is . EqualTo ( ( int ) TypeCode . String ) ) ;
218+ Assert . That ( stringRow [ "IsUnsigned" ] , Is . EqualTo ( DBNull . Value ) ) ;
219+ Assert . That ( stringRow [ "IsBestMatch" ] , Is . True ) ;
220+
221+ var timestampRow = dataTypes . Rows . Cast < DataRow > ( ) . Single ( r => r [ "TypeName" ] . Equals ( "Timestamp" ) ) ;
222+ Assert . That ( timestampRow [ "DataType" ] , Is . EqualTo ( "System.DateTime" ) ) ;
223+ Assert . That ( timestampRow [ "ProviderDbType" ] , Is . EqualTo ( ( int ) TypeCode . Timestamp ) ) ;
224+ Assert . That ( timestampRow [ "IsUnsigned" ] , Is . EqualTo ( DBNull . Value ) ) ;
225+ Assert . That ( timestampRow [ "IsBestMatch" ] , Is . True ) ;
226+
227+ var uuidRow = dataTypes . Rows . Cast < DataRow > ( ) . Single ( r => r [ "TypeName" ] . Equals ( "Uuid" ) ) ;
228+ Assert . That ( uuidRow [ "DataType" ] , Is . EqualTo ( "System.Guid" ) ) ;
229+ Assert . That ( uuidRow [ "ProviderDbType" ] , Is . EqualTo ( ( int ) TypeCode . Uuid ) ) ;
230+ Assert . That ( uuidRow [ "IsUnsigned" ] , Is . EqualTo ( DBNull . Value ) ) ;
231+ Assert . That ( uuidRow [ "IsBestMatch" ] , Is . True ) ;
232+ }
233+
234+ [ Test ]
235+ public async Task Restrictions ( )
236+ {
237+ await using var conn = await OpenConnectionAsync ( ) ;
238+ var restrictions = conn . GetSchema ( DbMetaDataCollectionNames . Restrictions ) ;
239+ Assert . That ( restrictions . Rows , Has . Count . GreaterThan ( 0 ) ) ;
240+ }
241+
242+ [ Test ]
243+ public async Task ReservedWords ( )
244+ {
245+ await using var conn = await OpenConnectionAsync ( ) ;
246+ var reservedWords = conn . GetSchema ( DbMetaDataCollectionNames . ReservedWords ) ;
247+ Assert . That ( reservedWords . Rows , Has . Count . GreaterThan ( 0 ) ) ;
248+ }
249+
250+ }
0 commit comments