1 | /* |
2 | |
3 | Derby - Class org.apache.derby.client.net.NetConnectionRequest |
4 | |
5 | Copyright (c) 2001, 2005 The Apache Software Foundation or its licensors, where applicable. |
6 | |
7 | Licensed under the Apache License, Version 2.0 (the "License"); |
8 | you may not use this file except in compliance with the License. |
9 | You may obtain a copy of the License at |
10 | |
11 | http://www.apache.org/licenses/LICENSE-2.0 |
12 | |
13 | Unless required by applicable law or agreed to in writing, software |
14 | distributed under the License is distributed on an "AS IS" BASIS, |
15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | See the License for the specific language governing permissions and |
17 | limitations under the License. |
18 | |
19 | */ |
20 | |
21 | package org.apache.derby.client.net; |
22 | |
23 | |
24 | import javax.transaction.xa.Xid; |
25 | |
26 | import org.apache.derby.client.am.SqlException; |
27 | import org.apache.derby.client.am.ClientMessageId; |
28 | import org.apache.derby.client.am.Utils; |
29 | import org.apache.derby.shared.common.reference.SQLState; |
30 | |
31 | public class NetConnectionRequest extends Request implements ConnectionRequestInterface { |
32 | NetConnectionRequest(NetAgent netAgent, CcsidManager ccsidManager, int bufferSize) { |
33 | super(netAgent, ccsidManager, bufferSize); |
34 | } |
35 | //----------------------------- entry points --------------------------------- |
36 | |
37 | void writeExchangeServerAttributes(String externalName, |
38 | int targetAgent, |
39 | int targetSqlam, |
40 | int targetRdb, |
41 | int targetSecmgr, |
42 | int targetCmntcpip, |
43 | int targetCmnappc, |
44 | int targetXamgr, |
45 | int targetSyncptmgr, |
46 | int targetRsyncmgr) throws SqlException { |
47 | // send the exchange server attributes command to the server. |
48 | // no other commands will be chained to the excsat because |
49 | // the manager levels are needed before anything else is attempted. |
50 | buildEXCSAT(externalName, |
51 | targetAgent, |
52 | targetSqlam, |
53 | targetRdb, |
54 | targetSecmgr, |
55 | targetCmntcpip, |
56 | targetCmnappc, |
57 | targetXamgr, |
58 | targetSyncptmgr, |
59 | targetRsyncmgr); |
60 | |
61 | } |
62 | |
63 | void writeDummyExchangeServerAttributes() throws SqlException { |
64 | // send the exchange server attributes command to the server, |
65 | // without any parameters |
66 | buildDummyEXCSAT(); |
67 | } |
68 | |
69 | void writeAccessSecurity(int securityMechanism, |
70 | String databaseName, |
71 | byte[] publicKey) throws SqlException { |
72 | buildACCSEC(securityMechanism, databaseName, publicKey); |
73 | } |
74 | |
75 | void writeSecurityCheck(int securityMechanism, |
76 | String databaseName, |
77 | String userid, |
78 | String password, |
79 | byte[] encryptedUserid, |
80 | byte[] encryptedPassword) throws SqlException { |
81 | buildSECCHK(securityMechanism, |
82 | databaseName, |
83 | userid, |
84 | password, |
85 | encryptedUserid, |
86 | encryptedPassword); |
87 | } |
88 | |
89 | void writeAccessDatabase(String rdbnam, |
90 | boolean readOnly, |
91 | byte[] correlationToken, |
92 | byte[] productData, |
93 | Typdef typdef) throws SqlException { |
94 | buildACCRDB(rdbnam, |
95 | readOnly, |
96 | correlationToken, |
97 | productData, |
98 | typdef); |
99 | } |
100 | |
101 | |
102 | public void writeCommitSubstitute(NetConnection connection) throws SqlException { |
103 | buildDummyEXCSAT(); |
104 | } |
105 | |
106 | public void writeLocalCommit(NetConnection connection) throws SqlException { |
107 | buildRDBCMM(); |
108 | } |
109 | |
110 | public void writeLocalRollback(NetConnection connection) throws SqlException { |
111 | buildRDBRLLBCK(); |
112 | } |
113 | |
114 | public void writeLocalXAStart(NetConnection connection) throws SqlException { |
115 | } |
116 | |
117 | |
118 | //Build the SYNNCTL commit command |
119 | public void writeLocalXACommit(NetConnection conn) throws SqlException { |
120 | } |
121 | |
122 | //Build the SYNNCTL rollback command |
123 | public void writeLocalXARollback(NetConnection conn) throws SqlException { |
124 | } |
125 | |
126 | public void writeXaStartUnitOfWork(NetConnection conn) throws SqlException { |
127 | } |
128 | |
129 | public void writeXaEndUnitOfWork(NetConnection conn) throws SqlException { |
130 | } |
131 | |
132 | protected void writeXaPrepare(NetConnection conn) throws SqlException { |
133 | } |
134 | |
135 | protected void writeXaCommit(NetConnection conn, Xid xid) throws SqlException { |
136 | } |
137 | |
138 | protected void writeXaRollback(NetConnection conn, Xid xid) throws SqlException { |
139 | } |
140 | |
141 | protected void writeXaRecover(NetConnection conn, int flag) throws SqlException { |
142 | } |
143 | |
144 | protected void writeXaForget(NetConnection conn, Xid xid) throws SqlException { |
145 | } |
146 | |
147 | public void writeSYNCType(int codepoint, int syncType) { |
148 | writeScalar1Byte(codepoint, syncType); |
149 | } |
150 | |
151 | public void writeForget(int codepoint, int value) { |
152 | } |
153 | |
154 | public void writeReleaseConversation(int codepoint, int value) { |
155 | } |
156 | |
157 | void writeNullXID(int codepoint) { |
158 | } |
159 | |
160 | void writeXID(int codepoint, Xid xid) throws SqlException { |
161 | } |
162 | |
163 | |
164 | void writeXAFlags(int codepoint, int xaFlags) { |
165 | } |
166 | |
167 | |
168 | //----------------------helper methods---------------------------------------- |
169 | // These methods are "private protected", which is not a recognized java privilege, |
170 | // but means that these methods are private to this class and to subclasses, |
171 | // and should not be used as package-wide friendly methods. |
172 | |
173 | // RDB Commit Unit of Work (RDBCMM) Command commits all work performed |
174 | // for the current unit of work. |
175 | // |
176 | // The Relational Database Name (RDBNAM) is an optional parameter |
177 | // which will not be sent by this command to reduce size, building, |
178 | // and parsing. |
179 | void buildRDBCMM() throws SqlException { |
180 | createCommand(); |
181 | writeLengthCodePoint(0x04, CodePoint.RDBCMM); |
182 | } |
183 | |
184 | // RDB Rollback Unit of Work(RDBRLLBCK) Command rolls back |
185 | // all work performed for the current unit of work. |
186 | // |
187 | // The Relational Database Name (RDBNAM) is an optional parameter |
188 | // which will not be sent by this command to reduce size, building, |
189 | // and parsing. |
190 | void buildRDBRLLBCK() throws SqlException { |
191 | createCommand(); |
192 | writeLengthCodePoint(0x04, CodePoint.RDBRLLBCK); |
193 | } |
194 | |
195 | // build the Exchange Server Attributes Command. |
196 | // This command sends the following information to the server. |
197 | // - this driver's server class name |
198 | // - this driver's level of each of the manager's it supports |
199 | // - this driver's product release level |
200 | // - this driver's external name |
201 | // - this driver's server name |
202 | void buildEXCSAT(String externalName, |
203 | int targetAgent, |
204 | int targetSqlam, |
205 | int targetRdb, |
206 | int targetSecmgr, |
207 | int targetCmntcpip, |
208 | int targetCmnappc, |
209 | int targetXamgr, |
210 | int targetSyncptmgr, |
211 | int targetRsyncmgr) throws SqlException { |
212 | createCommand(); |
213 | |
214 | // begin excsat collection by placing the 4 byte llcp in the buffer. |
215 | // the length of this command will be computed later and "filled in" |
216 | // with the call to request.updateLengthBytes(). |
217 | markLengthBytes(CodePoint.EXCSAT); |
218 | |
219 | // place the external name for the client into the buffer. |
220 | // the external name was previously calculated before the call to this method. |
221 | buildEXTNAM(externalName); |
222 | |
223 | // place the server name for the client into the buffer. |
224 | buildSRVNAM("Derby"); |
225 | |
226 | // place the server release level for the client into the buffer. |
227 | // this is a hard coded value for the driver. |
228 | buildSRVRLSLV(); |
229 | |
230 | // the managers supported by this driver and their levels will |
231 | // be sent to the server. the variables which store these values |
232 | // were initialized during object constrcution to the highest values |
233 | // supported by the driver. |
234 | |
235 | // for the case of the manager levels object, there is no |
236 | // need to have the length of the ddm object dynamically calculated |
237 | // because this method knows exactly how many will be sent and can set |
238 | // this now. |
239 | // each manager level class and level are 4 bytes long and |
240 | // right now 5 are being sent for a total of 20 bytes or 0x14 bytes. |
241 | // writeScalarHeader will be called to insert the llcp. |
242 | buildMGRLVLLS(targetAgent, |
243 | targetSqlam, |
244 | targetRdb, |
245 | targetSecmgr, |
246 | targetXamgr, |
247 | targetSyncptmgr, |
248 | targetRsyncmgr); |
249 | |
250 | |
251 | // place the server class name into the buffer. |
252 | // this value is hard coded for the driver. |
253 | buildSRVCLSNM(); |
254 | |
255 | // the excsat command is complete so the updateLengthBytes method |
256 | // is called to dynamically compute the length for this command and insert |
257 | // it into the buffer |
258 | updateLengthBytes(); |
259 | } |
260 | |
261 | void buildDummyEXCSAT() throws SqlException { |
262 | createCommand(); |
263 | |
264 | // begin excsat collection by placing the 4 byte llcp in the buffer. |
265 | // the length of this command will be computed later and "filled in" |
266 | // with the call to request.updateLengthBytes(). |
267 | markLengthBytes(CodePoint.EXCSAT); |
268 | |
269 | // the excsat command is complete so the updateLengthBytes method |
270 | // is called to dynamically compute the length for this command and insert |
271 | // it into the buffer |
272 | updateLengthBytes(); |
273 | } |
274 | |
275 | void buildACCSEC(int secmec, |
276 | String rdbnam, |
277 | byte[] sectkn) throws SqlException { |
278 | createCommand(); |
279 | |
280 | // place the llcp for the ACCSEC in the buffer. save the length bytes for |
281 | // later update |
282 | markLengthBytes(CodePoint.ACCSEC); |
283 | |
284 | // the security mechanism is a required instance variable. it will |
285 | // always be sent. |
286 | buildSECMEC(secmec); |
287 | |
288 | // the rdbnam will be built and sent. different sqlam levels support |
289 | // different lengths. at this point the length has been checked against |
290 | // the maximum allowable length. so write the bytes and padd up to the |
291 | // minimum length if needed. |
292 | buildRDBNAM(rdbnam); |
293 | |
294 | if (sectkn != null) { |
295 | buildSECTKN(sectkn); |
296 | } |
297 | |
298 | // the accsec command is complete so notify the the request object to |
299 | // update the ddm length and the dss header length. |
300 | updateLengthBytes(); |
301 | } |
302 | |
303 | void buildSECCHK(int secmec, |
304 | String rdbnam, |
305 | String user, |
306 | String password, |
307 | byte[] sectkn, |
308 | byte[] sectkn2) throws SqlException { |
309 | createCommand(); |
310 | markLengthBytes(CodePoint.SECCHK); |
311 | |
312 | // always send the negotiated security mechanism for the connection. |
313 | buildSECMEC(secmec); |
314 | |
315 | // the rdbnam will be built and sent. different sqlam levels support |
316 | // different lengths. at this point the length has been checked against |
317 | // the maximum allowable length. so write the bytes and padd up to the |
318 | // minimum length if needed. |
319 | buildRDBNAM(rdbnam); |
320 | if (user != null) { |
321 | buildUSRID(user); |
322 | } |
323 | if (password != null) { |
324 | buildPASSWORD(password); |
325 | } |
326 | if (sectkn != null) { |
327 | buildSECTKN(sectkn); |
328 | } |
329 | if (sectkn2 != null) { |
330 | buildSECTKN(sectkn2); |
331 | } |
332 | updateLengthBytes(); |
333 | |
334 | } |
335 | |
336 | // The Access RDB (ACCRDB) command makes a named relational database (RDB) |
337 | // available to a requester by creating an instance of an SQL application |
338 | // manager. The access RDB command then binds the created instance to the target |
339 | // agent and to the RDB. The RDB remains available (accessed) until |
340 | // the communications conversation is terminate. |
341 | void buildACCRDB(String rdbnam, |
342 | boolean readOnly, |
343 | byte[] crrtkn, |
344 | byte[] prddta, |
345 | Typdef typdef) throws SqlException { |
346 | createCommand(); |
347 | |
348 | markLengthBytes(CodePoint.ACCRDB); |
349 | |
350 | // the relational database name specifies the name of the rdb to |
351 | // be accessed. this can be different sizes depending on the level of |
352 | // support. the size will have ben previously checked so at this point just |
353 | // write the data and pad with the correct number of bytes as needed. |
354 | // this instance variable is always required. |
355 | buildRDBNAM(rdbnam); |
356 | |
357 | // the rdb access manager class specifies an instance of the SQLAM |
358 | // that accesses the RDB. the sqlam manager class codepoint |
359 | // is always used/required for this. this instance variable |
360 | // is always required. |
361 | buildRDBACCCL(); |
362 | |
363 | // product specific identifier specifies the product release level |
364 | // of this driver. see the hard coded value in the NetConfiguration class. |
365 | // this instance variable is always required. |
366 | buildPRDID(); |
367 | |
368 | // product specific data. this is an optional parameter which carries |
369 | // product specific information. although it is optional, it will be |
370 | // sent to the server. use the first byte to determine the number |
371 | // of the prddta bytes to write to the buffer. note: this length |
372 | // doesn't include itself so increment by it by 1 to get the actual |
373 | // length of this data. |
374 | buildPRDDTA(prddta); |
375 | |
376 | |
377 | // the typdefnam parameter specifies the name of the data type to data representation |
378 | // mappings used when this driver sends command data objects. |
379 | buildTYPDEFNAM(typdef.getTypdefnam()); |
380 | |
381 | if (crrtkn == null) { |
382 | netAgent_.netConnection_.constructCrrtkn(); |
383 | } |
384 | |
385 | buildCRRTKN(netAgent_.netConnection_.crrtkn_); |
386 | |
387 | // This specifies the single-byte, double-byte |
388 | // and mixed-byte CCSIDs of the Scalar Data Arrays (SDAs) in the identified |
389 | // data type to the data representation mapping definitions. This can |
390 | // contain 3 CCSIDs. The driver will only send the ones which were set. |
391 | buildTYPDEFOVR(typdef.isCcsidSbcSet(), |
392 | typdef.getCcsidSbc(), |
393 | typdef.isCcsidDbcSet(), |
394 | typdef.getCcsidDbc(), |
395 | typdef.isCcsidMbcSet(), |
396 | typdef.getCcsidMbc()); |
397 | |
398 | // RDB allow update is an optional parameter which indicates |
399 | // whether the RDB allows the requester to perform update operations |
400 | // in the RDB. If update operations are not allowed, this connection |
401 | // is limited to read-only access of the RDB resources. |
402 | buildRDBALWUPD(readOnly); |
403 | |
404 | |
405 | |
406 | // the Statement Decimal Delimiter (STTDECDEL), |
407 | // Statement String Delimiter (STTSTRDEL), |
408 | // and Target Default Value Return (TRGDFTRT) are all optional |
409 | // instance variables which will not be sent to the server. |
410 | |
411 | // the command and the dss are complete so make the call to notify |
412 | // the request object. |
413 | updateLengthBytes(); |
414 | } |
415 | |
416 | |
417 | void buildSYNCCTLMigrate() throws SqlException { |
418 | } |
419 | |
420 | void buildSYNCCTLCommit(int xaFlags, Xid xid) throws SqlException { |
421 | } |
422 | |
423 | void buildSYNCCTLRollback(int xaFlags) throws SqlException { |
424 | } |
425 | |
426 | |
427 | // The External Name is the name of the job, task, or process on a |
428 | // system for which a DDM server is active. |
429 | private void buildEXTNAM(String extnam) throws SqlException { |
430 | int extnamTruncateLength = Utils.min(extnam.length(), |
431 | NetConfiguration.EXTNAM_MAXSIZE); |
432 | |
433 | writeScalarString(CodePoint.EXTNAM, |
434 | extnam.substring(0, extnamTruncateLength)); |
435 | } |
436 | |
437 | // Server Name is the name of the DDM server. |
438 | private void buildSRVNAM(String srvnam) throws SqlException { |
439 | int srvnamTruncateLength = Utils.min(srvnam.length(), |
440 | NetConfiguration.SRVNAM_MAXSIZE); |
441 | writeScalarString(CodePoint.SRVNAM, |
442 | srvnam.substring(0, srvnamTruncateLength)); |
443 | } |
444 | |
445 | // Server Product Release Level String specifies the product |
446 | // release level of a DDM server. |
447 | private void buildSRVRLSLV() throws SqlException { |
448 | // Hard-coded to ClientDNC 1.0 for dnc 1.0. |
449 | writeScalarString(CodePoint.SRVRLSLV, NetConfiguration.SRVRLSLV); |
450 | } |
451 | |
452 | private void buildSRVCLSNM() throws SqlException { |
453 | // Server class name is hard-coded to QDERBY/JVM for dnc. |
454 | writeScalarString(CodePoint.SRVCLSNM, NetConfiguration.SRVCLSNM_JVM); |
455 | } |
456 | |
457 | // Precondition: valid secmec is assumed. |
458 | private void buildSECMEC(int secmec) throws SqlException { |
459 | writeScalar2Bytes(CodePoint.SECMEC, secmec); |
460 | } |
461 | |
462 | // Relational Database Name specifies the name of a relational database |
463 | // of the server. |
464 | // if length of RDB name <= 18 characters, there is not change to the format |
465 | // of the RDB name. The length of the RDBNAM remains fixed at 18 which includes |
466 | // any right bland padding if necessary. |
467 | // if length of the RDB name is > 18 characters, the length of the RDB name is |
468 | // identical to the length of the RDB name. No right blank padding is required. |
469 | private void buildRDBNAM(String rdbnam) throws SqlException { |
470 | // since this gets built more than once on the connect flow, |
471 | // see if we can optimize |
472 | |
473 | int rdbnamLength = rdbnam.length(); |
474 | if (rdbnamLength <= NetConfiguration.PKG_IDENTIFIER_FIXED_LEN) { |
475 | writeScalarPaddedString(CodePoint.RDBNAM, |
476 | rdbnam, |
477 | NetConfiguration.PKG_IDENTIFIER_FIXED_LEN); // minimum length of RDBNAM |
478 | } else { |
479 | if (rdbnamLength <= NetConfiguration.PKG_IDENTIFIER_MAX_LEN) { |
480 | writeScalarString(CodePoint.RDBNAM, rdbnam); |
481 | } else { |
482 | throw new SqlException(netAgent_.logWriter_, |
483 | new ClientMessageId(SQLState.NET_DBNAME_TOO_LONG), rdbnam); |
484 | } |
485 | //"at SQLAM level " + netAgent_.targetSqlam_); |
486 | } |
487 | } |
488 | |
489 | private void buildSECTKN(byte[] sectkn) throws SqlException { |
490 | if (sectkn.length > NetConfiguration.SECTKN_MAXSIZE) { |
491 | throw new SqlException(netAgent_.logWriter_, |
492 | new ClientMessageId(SQLState.NET_SECTKN_TOO_LONG)); |
493 | } |
494 | writeScalarBytes(CodePoint.SECTKN, sectkn); |
495 | } |
496 | |
497 | private void buildUSRID(String usrid) throws SqlException { |
498 | int usridLength = usrid.length(); |
499 | if ((usridLength == 0) || (usridLength > NetConfiguration.USRID_MAXSIZE)) { |
500 | throw new SqlException(netAgent_.logWriter_, |
501 | new ClientMessageId(SQLState.NET_USERID_TOO_LONG)); |
502 | } |
503 | |
504 | writeScalarString(CodePoint.USRID, usrid); |
505 | } |
506 | |
507 | private void buildPASSWORD(String password) throws SqlException { |
508 | int passwordLength = password.length(); |
509 | if ((passwordLength == 0) || (passwordLength > NetConfiguration.PASSWORD_MAXSIZE)) { |
510 | throw new SqlException(netAgent_.logWriter_, |
511 | new ClientMessageId(SQLState.NET_PASSWORD_TOO_LONG)); |
512 | } |
513 | if (netAgent_.logWriter_ != null) { |
514 | // remember the position of password in order to |
515 | // mask it out in trace (see Request.sendBytes()). |
516 | passwordIncluded_ = true; |
517 | passwordStart_ = offset_ + 4; |
518 | passwordLength_ = passwordLength; |
519 | } |
520 | writeScalarString(CodePoint.PASSWORD, password); |
521 | } |
522 | |
523 | private void buildRDBACCCL() throws SqlException { |
524 | writeScalar2Bytes(CodePoint.RDBACCCL, CodePoint.SQLAM); |
525 | } |
526 | |
527 | |
528 | private void buildPRDID() throws SqlException { |
529 | writeScalarString(CodePoint.PRDID, NetConfiguration.PRDID); // product id is hard-coded to DNC01000 for dnc 1.0. |
530 | } |
531 | |
532 | private void buildPRDDTA(byte[] prddta) throws SqlException { |
533 | int prddtaLength = (prddta[NetConfiguration.PRDDTA_LEN_BYTE] & 0xff) + 1; |
534 | writeScalarBytes(CodePoint.PRDDTA, prddta, 0, prddtaLength); |
535 | } |
536 | |
537 | private void buildTYPDEFNAM(String typdefnam) throws SqlException { |
538 | writeScalarString(CodePoint.TYPDEFNAM, typdefnam); |
539 | } |
540 | |
541 | void buildTYPDEFOVR(boolean sendCcsidSbc, |
542 | int ccsidSbc, |
543 | boolean sendCcsidDbc, |
544 | int ccsidDbc, |
545 | boolean sendCcsidMbc, |
546 | int ccsidMbc) throws SqlException { |
547 | markLengthBytes(CodePoint.TYPDEFOVR); |
548 | // write the single-byte ccsid used by this driver. |
549 | if (sendCcsidSbc) { |
550 | writeScalar2Bytes(CodePoint.CCSIDSBC, ccsidSbc); |
551 | } |
552 | |
553 | // write the double-byte ccsid used by this driver. |
554 | if (sendCcsidDbc) { |
555 | writeScalar2Bytes(CodePoint.CCSIDDBC, ccsidDbc); |
556 | } |
557 | |
558 | // write the mixed-byte ccsid used by this driver |
559 | if (sendCcsidMbc) { |
560 | writeScalar2Bytes(CodePoint.CCSIDMBC, ccsidMbc); |
561 | } |
562 | |
563 | updateLengthBytes(); |
564 | |
565 | } |
566 | |
567 | private void buildMGRLVLLS(int agent, |
568 | int sqlam, |
569 | int rdb, |
570 | int secmgr, |
571 | int xamgr, |
572 | int syncptmgr, |
573 | int rsyncmgr) throws SqlException { |
574 | markLengthBytes(CodePoint.MGRLVLLS); |
575 | |
576 | // place the managers and their levels in the buffer |
577 | writeCodePoint4Bytes(CodePoint.AGENT, agent); |
578 | writeCodePoint4Bytes(CodePoint.SQLAM, sqlam); |
579 | writeCodePoint4Bytes(CodePoint.RDB, rdb); |
580 | writeCodePoint4Bytes(CodePoint.SECMGR, secmgr); |
581 | |
582 | if (netAgent_.netConnection_.isXAConnection()) { |
583 | if (xamgr != NetConfiguration.MGRLVL_NA) { |
584 | writeCodePoint4Bytes(CodePoint.XAMGR, xamgr); |
585 | } |
586 | if (syncptmgr != NetConfiguration.MGRLVL_NA) { |
587 | writeCodePoint4Bytes(CodePoint.SYNCPTMGR, syncptmgr); |
588 | } |
589 | if (rsyncmgr != NetConfiguration.MGRLVL_NA) { |
590 | writeCodePoint4Bytes(CodePoint.RSYNCMGR, rsyncmgr); |
591 | } |
592 | } |
593 | updateLengthBytes(); |
594 | } |
595 | |
596 | private void buildCRRTKN(byte[] crrtkn) throws SqlException { |
597 | writeScalarBytes(CodePoint.CRRTKN, crrtkn); |
598 | } |
599 | |
600 | private void buildRDBALWUPD(boolean readOnly) throws SqlException { |
601 | if (readOnly) { |
602 | writeScalar1Byte(CodePoint.RDBALWUPD, CodePoint.FALSE); |
603 | } |
604 | } |
605 | |
606 | } |
607 | |
608 | |
609 | |