@@ -120,7 +120,6 @@ describe('Spanner with mock server', () => {
120
120
}
121
121
) ;
122
122
} ) ;
123
- server . start ( ) ;
124
123
spannerMock . putStatementResult (
125
124
selectSql ,
126
125
mock . StatementResult . resultSet ( mock . createSimpleResultSet ( ) )
@@ -3695,10 +3694,10 @@ describe('Spanner with mock server', () => {
3695
3694
} ) ;
3696
3695
3697
3696
it ( 'should return all values from PartialResultSet with chunked string value' , async ( ) => {
3698
- for ( const includeResumeToken in [ true , false ] ) {
3697
+ for ( const includeResumeToken of [ true , false ] ) {
3699
3698
// eslint-disable-next-line @typescript-eslint/no-explicit-any
3700
3699
let errorOnIndexes : any ;
3701
- for ( errorOnIndexes in [ [ ] , [ 0 ] , [ 1 ] , [ 0 , 1 ] ] ) {
3700
+ for ( errorOnIndexes of [ [ ] , [ 0 ] , [ 1 ] , [ 0 , 1 ] ] ) {
3702
3701
const sql = 'SELECT * FROM TestTable' ;
3703
3702
const prs1 = PartialResultSet . create ( {
3704
3703
resumeToken : includeResumeToken
@@ -3747,10 +3746,10 @@ describe('Spanner with mock server', () => {
3747
3746
} ) ;
3748
3747
3749
3748
it ( 'should return all values from PartialResultSet with chunked string value in an array' , async ( ) => {
3750
- for ( const includeResumeToken in [ true , false ] ) {
3749
+ for ( const includeResumeToken of [ true , false ] ) {
3751
3750
// eslint-disable-next-line @typescript-eslint/no-explicit-any
3752
3751
let errorOnIndexes : any ;
3753
- for ( errorOnIndexes in [ [ ] , [ 0 ] , [ 1 ] , [ 0 , 1 ] ] ) {
3752
+ for ( errorOnIndexes of [ [ ] , [ 0 ] , [ 1 ] , [ 0 , 1 ] ] ) {
3754
3753
const sql = 'SELECT * FROM TestTable' ;
3755
3754
const prs1 = PartialResultSet . create ( {
3756
3755
resumeToken : includeResumeToken
@@ -3800,10 +3799,10 @@ describe('Spanner with mock server', () => {
3800
3799
} ) ;
3801
3800
3802
3801
it ( 'should return all values from PartialResultSet with chunked list value' , async ( ) => {
3803
- for ( const includeResumeToken in [ true , false ] ) {
3802
+ for ( const includeResumeToken of [ true , false ] ) {
3804
3803
// eslint-disable-next-line @typescript-eslint/no-explicit-any
3805
3804
let errorOnIndexes : any ;
3806
- for ( errorOnIndexes in [ [ ] , [ 0 ] , [ 1 ] , [ 0 , 1 ] ] ) {
3805
+ for ( errorOnIndexes of [ [ ] , [ 0 ] , [ 1 ] , [ 0 , 1 ] ] ) {
3807
3806
const sql = 'SELECT * FROM TestTable' ;
3808
3807
const prs1 = PartialResultSet . create ( {
3809
3808
resumeToken : includeResumeToken
@@ -4047,6 +4046,200 @@ describe('Spanner with mock server', () => {
4047
4046
}
4048
4047
} ) ;
4049
4048
4049
+ it ( 'should clear pending values if the last partial result did not have a resume token and was not a complete row' , async ( ) => {
4050
+ const sql = 'SELECT * FROM TestTable' ;
4051
+ const prs1 = PartialResultSet . create ( {
4052
+ resumeToken : undefined ,
4053
+ metadata : createMultiColumnMetadata ( ) ,
4054
+ values : [
4055
+ { stringValue : 'id1.1' } ,
4056
+ { stringValue : 'id1.2' } ,
4057
+ { stringValue : '100' } ,
4058
+ ] ,
4059
+ chunkedValue : false ,
4060
+ } ) ;
4061
+ const prs2 = PartialResultSet . create ( {
4062
+ resumeToken : undefined ,
4063
+ values : [
4064
+ { boolValue : true } ,
4065
+ { boolValue : true } ,
4066
+ { numberValue : 0.5 } ,
4067
+ { stringValue : 'id2.1' } ,
4068
+ { stringValue : 'id2.2' } ,
4069
+ ] ,
4070
+ chunkedValue : false ,
4071
+ } ) ;
4072
+ const prs3 = PartialResultSet . create ( {
4073
+ resumeToken : undefined ,
4074
+ values : [
4075
+ { stringValue : '200' } ,
4076
+ { boolValue : true } ,
4077
+ { boolValue : true } ,
4078
+ { numberValue : 0.5 } ,
4079
+ ] ,
4080
+ } ) ;
4081
+ // Let the stream return UNAVAILABLE on index 1 (so the second PartialResultSet).
4082
+ setupResultsAndErrors ( sql , [ prs1 , prs2 , prs3 ] , [ 1 ] ) ;
4083
+ const database = newTestDatabase ( ) ;
4084
+ try {
4085
+ const [ rows ] = ( await database . run ( {
4086
+ sql,
4087
+ json : true ,
4088
+ } ) ) as Json [ ] [ ] ;
4089
+ verifyQueryResult ( rows ) ;
4090
+ } finally {
4091
+ await database . close ( ) ;
4092
+ }
4093
+ } ) ;
4094
+
4095
+ it ( 'should not clear pending values if the last partial result had a resume token and was not a complete row' , async ( ) => {
4096
+ for ( const errorIndexes of [ [ 1 ] , [ 2 ] ] ) {
4097
+ const sql = 'SELECT * FROM TestTable' ;
4098
+ const prs1 = PartialResultSet . create ( {
4099
+ resumeToken : Buffer . from ( '00000000' ) ,
4100
+ metadata : createMultiColumnMetadata ( ) ,
4101
+ values : [
4102
+ { stringValue : 'id1.1' } ,
4103
+ { stringValue : 'id1.2' } ,
4104
+ { stringValue : '100' } ,
4105
+ ] ,
4106
+ chunkedValue : false ,
4107
+ } ) ;
4108
+ const prs2 = PartialResultSet . create ( {
4109
+ resumeToken : undefined ,
4110
+ values : [
4111
+ { boolValue : true } ,
4112
+ { boolValue : true } ,
4113
+ { numberValue : 0.5 } ,
4114
+ { stringValue : 'id2.1' } ,
4115
+ { stringValue : 'id2.2' } ,
4116
+ ] ,
4117
+ chunkedValue : false ,
4118
+ } ) ;
4119
+ const prs3 = PartialResultSet . create ( {
4120
+ resumeToken : undefined ,
4121
+ values : [
4122
+ { stringValue : '200' } ,
4123
+ { boolValue : true } ,
4124
+ { boolValue : true } ,
4125
+ { numberValue : 0.5 } ,
4126
+ ] ,
4127
+ } ) ;
4128
+ setupResultsAndErrors ( sql , [ prs1 , prs2 , prs3 ] , errorIndexes ) ;
4129
+ const database = newTestDatabase ( ) ;
4130
+ try {
4131
+ const [ rows ] = ( await database . run ( {
4132
+ sql,
4133
+ json : true ,
4134
+ } ) ) as Json [ ] [ ] ;
4135
+ verifyQueryResult ( rows ) ;
4136
+ } finally {
4137
+ await database . close ( ) ;
4138
+ }
4139
+ }
4140
+ } ) ;
4141
+
4142
+ it ( 'should not clear pending values if the last partial result was chunked and had a resume token' , async ( ) => {
4143
+ for ( const errorIndexes of [ [ 2 ] ] ) {
4144
+ const sql = 'SELECT * FROM TestTable' ;
4145
+ const prs1 = PartialResultSet . create ( {
4146
+ resumeToken : Buffer . from ( '00000000' ) ,
4147
+ metadata : createMultiColumnMetadata ( ) ,
4148
+ values : [
4149
+ { stringValue : 'id1.1' } ,
4150
+ { stringValue : 'id1.2' } ,
4151
+ { stringValue : '100' } ,
4152
+ ] ,
4153
+ chunkedValue : true ,
4154
+ } ) ;
4155
+ const prs2 = PartialResultSet . create ( {
4156
+ resumeToken : undefined ,
4157
+ values : [
4158
+ // The previous value was chunked, but it is still perfectly possible that it actually contained
4159
+ // the entire value. So in this case the actual value was '100'.
4160
+ { stringValue : '' } ,
4161
+ { boolValue : true } ,
4162
+ { boolValue : true } ,
4163
+ { numberValue : 0.5 } ,
4164
+ { stringValue : 'id2.1' } ,
4165
+ { stringValue : 'id2.2' } ,
4166
+ ] ,
4167
+ chunkedValue : false ,
4168
+ } ) ;
4169
+ const prs3 = PartialResultSet . create ( {
4170
+ resumeToken : undefined ,
4171
+ values : [
4172
+ { stringValue : '200' } ,
4173
+ { boolValue : true } ,
4174
+ { boolValue : true } ,
4175
+ { numberValue : 0.5 } ,
4176
+ ] ,
4177
+ } ) ;
4178
+ setupResultsAndErrors ( sql , [ prs1 , prs2 , prs3 ] , errorIndexes ) ;
4179
+ const database = newTestDatabase ( ) ;
4180
+ try {
4181
+ const [ rows ] = ( await database . run ( {
4182
+ sql,
4183
+ json : true ,
4184
+ } ) ) as Json [ ] [ ] ;
4185
+ verifyQueryResult ( rows ) ;
4186
+ } finally {
4187
+ await database . close ( ) ;
4188
+ }
4189
+ }
4190
+ } ) ;
4191
+
4192
+ function verifyQueryResult ( rows : Json [ ] ) {
4193
+ assert . strictEqual ( rows . length , 2 ) ;
4194
+ assert . strictEqual ( rows [ 0 ] . col1 , 'id1.1' ) ;
4195
+ assert . strictEqual ( rows [ 0 ] . col2 , 'id1.2' ) ;
4196
+ assert . strictEqual ( rows [ 0 ] . col3 , 100 ) ;
4197
+ assert . strictEqual ( rows [ 0 ] . col4 , true ) ;
4198
+ assert . strictEqual ( rows [ 0 ] . col5 , true ) ;
4199
+ assert . strictEqual ( rows [ 0 ] . col6 , 0.5 ) ;
4200
+
4201
+ assert . strictEqual ( rows [ 1 ] . col1 , 'id2.1' ) ;
4202
+ assert . strictEqual ( rows [ 1 ] . col2 , 'id2.2' ) ;
4203
+ assert . strictEqual ( rows [ 1 ] . col3 , 200 ) ;
4204
+ assert . strictEqual ( rows [ 1 ] . col4 , true ) ;
4205
+ assert . strictEqual ( rows [ 1 ] . col5 , true ) ;
4206
+ assert . strictEqual ( rows [ 1 ] . col6 , 0.5 ) ;
4207
+ }
4208
+
4209
+ function createMultiColumnMetadata ( ) {
4210
+ const fields = [
4211
+ protobuf . StructType . Field . create ( {
4212
+ name : 'col1' ,
4213
+ type : protobuf . Type . create ( { code : protobuf . TypeCode . STRING } ) ,
4214
+ } ) ,
4215
+ protobuf . StructType . Field . create ( {
4216
+ name : 'col2' ,
4217
+ type : protobuf . Type . create ( { code : protobuf . TypeCode . STRING } ) ,
4218
+ } ) ,
4219
+ protobuf . StructType . Field . create ( {
4220
+ name : 'col3' ,
4221
+ type : protobuf . Type . create ( { code : protobuf . TypeCode . INT64 } ) ,
4222
+ } ) ,
4223
+ protobuf . StructType . Field . create ( {
4224
+ name : 'col4' ,
4225
+ type : protobuf . Type . create ( { code : protobuf . TypeCode . BOOL } ) ,
4226
+ } ) ,
4227
+ protobuf . StructType . Field . create ( {
4228
+ name : 'col5' ,
4229
+ type : protobuf . Type . create ( { code : protobuf . TypeCode . BOOL } ) ,
4230
+ } ) ,
4231
+ protobuf . StructType . Field . create ( {
4232
+ name : 'col6' ,
4233
+ type : protobuf . Type . create ( { code : protobuf . TypeCode . FLOAT64 } ) ,
4234
+ } ) ,
4235
+ ] ;
4236
+ return new protobuf . ResultSetMetadata ( {
4237
+ rowType : new protobuf . StructType ( {
4238
+ fields,
4239
+ } ) ,
4240
+ } ) ;
4241
+ }
4242
+
4050
4243
function createMetadata ( ) {
4051
4244
const fields = [
4052
4245
protobuf . StructType . Field . create ( {
0 commit comments