File Coverage

blib/lib/MongoDB/Role/_SingleBatchDocWrite.pm
Criterion Covered Total %
statement 33 105 31.4
branch 0 58 0.0
condition 0 25 0.0
subroutine 11 14 78.5
pod n/a
total 44 202 21.7


line stmt bran cond sub pod time code
1             # Copyright 2016 - present MongoDB, Inc.
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             # http://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 59     59   157796 use strict;
  59         136  
  59         1705  
16 59     59   281 use warnings;
  59         112  
  59         2349  
17             package MongoDB::Role::_SingleBatchDocWrite;
18              
19             # MongoDB interface for database insert/update/delete operations
20              
21 59     59   300 use version;
  59         134  
  59         432  
22             our $VERSION = 'v2.2.2';
23              
24 59     59   4201 use Moo::Role;
  59         145  
  59         383  
25              
26 59     59   19570 use MongoDB::CommandResult;
  59         127  
  59         1556  
27 59     59   363 use MongoDB::Error;
  59         141  
  59         6599  
28 59     59   21653 use MongoDB::UnacknowledgedResult;
  59         172  
  59         1875  
29 59     59   657 use MongoDB::_Constants;
  59         120  
  59         5638  
30 59     59   358 use MongoDB::_Protocol;
  59         119  
  59         1361  
31 59         436 use MongoDB::_Types qw(
32             WriteConcern
33             to_IxHash
34 59     59   287 );
  59         123  
35              
36 59     59   65128 use namespace::clean;
  59         131  
  59         278  
37              
38             with $_ for qw(
39             MongoDB::Role::_WriteOp
40             MongoDB::Role::_SessionSupport
41             MongoDB::Role::_CommandMonitoring
42             );
43              
44             requires qw/db_name write_concern _parse_cmd _parse_gle/;
45              
46             sub _send_legacy_op_with_gle {
47 0     0     my ( $self, $link, $op_bson, $request_id, $op_doc, $result_class, $cmd_name ) = @_;
48              
49 0           my $wc_args = $self->write_concern->as_args();
50 0 0         my @write_concern = scalar @$wc_args ? %{ $wc_args->[1] } : ();
  0            
51              
52 0           my $gle = $self->bson_codec->encode_one( [ getlasterror => 1, @write_concern ] );
53 0           my ( $gle_bson, $gle_request_id ) =
54             MongoDB::_Protocol::write_query( $self->db_name . '.$cmd', $gle, undef, 0, -1 );
55              
56             # write op sent as a unit with GLE command to ensure GLE applies to the
57             # operation without other operations in between
58 0           $op_bson .= $gle_bson;
59              
60 0 0         if ( length($op_bson) > MAX_BSON_WIRE_SIZE ) {
61             # XXX should this become public?
62 0           MongoDB::_CommandSizeError->throw(
63             message => "database command too large",
64             size => length $op_bson,
65             );
66             }
67              
68 0 0         $self->publish_legacy_write_started( $link, $cmd_name, $op_doc, $request_id )
69             if $self->monitoring_callback;
70              
71 0           my $result;
72 0           eval {
73 0           $link->write( $op_bson ),
74             ( $result = MongoDB::_Protocol::parse_reply( $link->read, $gle_request_id ) );
75             };
76 0 0         if ( my $err = $@ ) {
77 0 0         $self->publish_command_exception($err) if $self->monitoring_callback;
78 0           die $err;
79             }
80              
81             $self->publish_command_reply( $result->{docs} )
82 0 0         if $self->monitoring_callback;
83              
84 0           my $res = $self->bson_codec->decode_one( $result->{docs} );
85              
86             # errors in the command itself get handled as normal CommandResult
87 0 0 0       if ( !$res->{ok} && ( $res->{errmsg} || $res->{'$err'} ) ) {
      0        
88 0           return MongoDB::CommandResult->_new(
89             output => $res,
90             address => $link->address,
91             );
92             }
93              
94             # 'ok' false means GLE itself failed
95             # usually we shouldn't check wnote or jnote, but the Bulk API QA test says we should
96             # detect no journal or replication not enabled, so we check for special strings.
97             # These strings were checked back to MongoDB 1.8.5.
98             my $got_error =
99             ( exists( $res->{jnote} ) && $res->{jnote} =~ NO_JOURNAL_RE ) ? $res->{jnote}
100             : ( exists( $res->{wnote} ) && $res->{wnote} =~ NO_REPLICATION_RE ) ? $res->{wnote}
101 0 0 0       : undef;
    0 0        
102              
103 0 0         if ($got_error) {
104 0           MongoDB::DatabaseError->throw(
105             message => $got_error,
106             result => MongoDB::CommandResult->_new(
107             output => $res,
108             address => $link->address,
109             ),
110             );
111             }
112              
113             # otherwise, construct the desired result object, calling back
114             # on class-specific parser to generate additional attributes
115 0           my ( $write_concern_error, $write_error );
116 0           my $errmsg = $res->{err};
117 0           my $wtimeout = $res->{wtimeout};
118              
119 0 0         if ($wtimeout) {
    0          
120             $write_concern_error = {
121             errmsg => $errmsg,
122             errInfo => { wtimeout => $wtimeout },
123 0   0       code => $res->{code} || WRITE_CONCERN_ERROR,
124             };
125             }
126             elsif ($errmsg) {
127             $write_error = {
128             errmsg => $errmsg,
129 0   0       code => $res->{code} || UNKNOWN_ERROR,
130             index => 0,
131             op => $op_doc,
132             };
133             }
134              
135 0 0         return $result_class->_new(
    0          
136             acknowledged => 1,
137             write_errors => ( $write_error ? [$write_error] : [] ),
138             write_concern_errors => ( $write_concern_error ? [$write_concern_error] : [] ),
139             $self->_parse_gle( $res, $op_doc ),
140             );
141             }
142              
143             sub _send_legacy_op_noreply {
144 0     0     my ( $self, $link, $op_bson, $request_id, $op_doc, $result_class, $cmd_name) = @_;
145              
146 0 0         $self->publish_legacy_write_started( $link, $cmd_name, $op_doc, $request_id )
147             if $self->monitoring_callback;
148              
149 0           eval { $link->write($op_bson) };
  0            
150 0 0         if ( my $err = $@ ) {
151 0 0         $self->publish_command_exception($err) if $self->monitoring_callback;
152 0           die $err;
153             }
154              
155 0 0         $self->publish_command_reply( { ok => 1 } )
156             if $self->monitoring_callback;
157              
158 0           return MongoDB::UnacknowledgedResult->_new(
159             $self->_parse_gle( {}, $op_doc ),
160             acknowledged => 0,
161             write_errors => [],
162             write_concern_errors => [],
163             );
164             }
165              
166             sub _send_write_command {
167 0     0     my ( $self, $link, $cmd, $op_doc, $result_class ) = @_;
168              
169 0           $self->_apply_session_and_cluster_time( $link, \$cmd );
170              
171 0           my ( $op_bson, $request_id );
172 0 0         if ( $link->supports_op_msg ) {
173 0           $cmd = to_IxHash( $cmd );
174 0           $cmd->Push( '$db', $self->db_name );
175 0           ( $op_bson, $request_id ) =
176             MongoDB::_Protocol::write_msg( $self->bson_codec, undef, $cmd );
177             } else {
178             # send command and get response document
179 0           my $command = $self->bson_codec->encode_one( $cmd );
180 0           ( $op_bson, $request_id ) =
181             MongoDB::_Protocol::write_query( $self->db_name . '.$cmd',
182             $command, undef, 0, -1, undef );
183             }
184              
185 0 0         if ( length($op_bson) > MAX_BSON_WIRE_SIZE ) {
186             # XXX should this become public?
187 0           MongoDB::_CommandSizeError->throw(
188             message => "database command too large",
189             size => length $op_bson,
190             );
191             }
192              
193 0 0         $self->publish_command_started( $link, $cmd, $request_id )
194             if $self->monitoring_callback;
195              
196 0           my $result;
197 0           eval {
198 0           $link->write( $op_bson ),
199             ( $result = MongoDB::_Protocol::parse_reply( $link->read, $request_id ) );
200             };
201 0 0         if ( my $err = $@ ) {
202 0           $self->_update_session_connection_error( $err );
203 0 0         $self->publish_command_exception($err) if $self->monitoring_callback;
204 0           die $err;
205             }
206              
207             $self->publish_command_reply( $result->{docs} )
208 0 0         if $self->monitoring_callback;
209              
210 0           my $res = $self->bson_codec->decode_one( $result->{docs} );
211              
212 0           $self->_update_session_pre_assert( $res );
213              
214 0           $self->_update_session_and_cluster_time($res);
215              
216             # Error checking depends on write concern
217 0 0         if ( $self->_should_use_acknowledged_write ) {
218             # errors in the command itself get handled as normal CommandResult
219 0 0 0       if ( !$res->{ok} && ( $res->{errmsg} || $res->{'$err'} ) ) {
      0        
220 0           return MongoDB::CommandResult->_new(
221             output => $res,
222             address => $link->address,
223             session => $self->session,
224             );
225             }
226              
227             # if an error occurred, add the op document involved
228 0 0 0       if ( exists($res->{writeErrors}) && @{$res->{writeErrors}} ) {
  0            
229 0           $res->{writeErrors}[0]{op} = $op_doc;
230             }
231              
232             # otherwise, construct the desired result object, calling back
233             # on class-specific parser to generate additional attributes
234             my $built_result = $result_class->_new(
235             write_errors => ( $res->{writeErrors} ? $res->{writeErrors} : [] ),
236             write_concern_errors =>
237 0 0         ( $res->{writeConcernError} ? [ $res->{writeConcernError} ] : [] ),
    0          
238             $self->_parse_cmd($res),
239             );
240 0           $self->_assert_session_errors( $built_result );
241 0           return $built_result;
242             }
243             else {
244 0           return MongoDB::UnacknowledgedResult->_new(
245             write_errors => [],
246             write_concern_errors => [],
247             );
248             }
249             }
250              
251             1;