1#![forbid(unsafe_code)]
2
3use super::*;
4
5impl BindValue {
6 pub(crate) fn is_output_only(&self) -> bool {
7 matches!(self, BindValue::Output { .. })
8 || matches!(self, BindValue::ReturnOutput { .. })
9 || matches!(self, BindValue::ObjectOutput { .. })
10 || matches!(self, BindValue::Array { values, .. } if values.is_empty())
11 }
12
13 pub(crate) fn is_return_output(&self) -> bool {
14 matches!(self, BindValue::ReturnOutput { .. })
15 || matches!(
16 self,
17 BindValue::ObjectOutput {
18 is_return: true,
19 ..
20 }
21 )
22 }
23}
24
25pub(crate) fn write_bind_metadata_with_type(
26 writer: &mut TtcWriter,
27 value: &BindValue,
28 ora_type_num: u8,
29 csfrm: u8,
30 buffer_size: u32,
31) -> Result<()> {
32 let (flags, max_elements) = match value {
33 BindValue::Array { max_elements, .. } => {
34 (TNS_BIND_USE_INDICATORS | TNS_BIND_ARRAY, *max_elements)
35 }
36 _ => (TNS_BIND_USE_INDICATORS, 0),
37 };
38 let buffer_size = if ora_type_num == ORA_TYPE_NUM_JSON {
41 TNS_JSON_MAX_LENGTH
42 } else {
43 buffer_size
44 };
45 writer.write_u8(ora_type_num);
46 writer.write_u8(flags);
47 writer.write_u8(0);
48 writer.write_u8(0);
49 writer.write_ub4(buffer_size);
50 writer.write_ub4(max_elements);
51 let cont_flags = if matches!(
52 ora_type_num,
53 ORA_TYPE_NUM_CLOB | ORA_TYPE_NUM_BLOB | ORA_TYPE_NUM_VECTOR | ORA_TYPE_NUM_JSON
54 ) {
55 TNS_LOB_PREFETCH_FLAG
56 } else {
57 0
58 };
59 writer.write_ub8(cont_flags);
60 if let BindValue::ObjectOutput { oid, version, .. }
61 | BindValue::ObjectInput { oid, version, .. } = value
62 {
63 writer.write_bytes_with_two_lengths(Some(oid))?;
64 writer.write_ub4(*version);
65 } else {
66 writer.write_ub4(0);
67 writer.write_ub2(0);
68 }
69 if csfrm != 0 {
70 writer.write_ub2(TNS_CHARSET_UTF8);
71 } else {
72 writer.write_ub2(0);
73 }
74 writer.write_u8(csfrm);
75 let lob_prefetch_length = match ora_type_num {
78 ORA_TYPE_NUM_VECTOR => TNS_VECTOR_MAX_LENGTH,
79 ORA_TYPE_NUM_JSON => TNS_JSON_MAX_LENGTH,
80 _ => 0,
81 };
82 writer.write_ub4(lob_prefetch_length);
83 writer.write_ub4(0);
84 Ok(())
85}
86
87pub fn bind_value_type_info(value: &BindValue) -> Option<BindTypeInfo> {
88 let (ora_type_num, csfrm, buffer_size) = match value {
89 BindValue::Null => return None,
90 BindValue::TypedNull {
91 ora_type_num,
92 csfrm,
93 buffer_size,
94 }
95 | BindValue::Output {
96 ora_type_num,
97 csfrm,
98 buffer_size,
99 }
100 | BindValue::ReturnOutput {
101 ora_type_num,
102 csfrm,
103 buffer_size,
104 } => (*ora_type_num, *csfrm, (*buffer_size).max(1)),
105 BindValue::ObjectOutput { buffer_size, .. }
106 | BindValue::ObjectInput { buffer_size, .. } => {
107 (ORA_TYPE_NUM_OBJECT, 0, (*buffer_size).max(1))
108 }
109 BindValue::Text(value) => {
115 let buffer_size = u32::try_from(value.chars().count())
116 .unwrap_or(u32::MAX)
117 .saturating_mul(4)
118 .max(1);
119 (ORA_TYPE_NUM_VARCHAR, CS_FORM_IMPLICIT, buffer_size)
120 }
121 BindValue::Raw(value) => {
122 let buffer_size = u32::try_from(value.len()).unwrap_or(u32::MAX).max(1);
123 (ORA_TYPE_NUM_RAW, 0, buffer_size)
124 }
125 BindValue::Lob {
126 ora_type_num,
127 csfrm,
128 ..
129 } => (*ora_type_num, *csfrm, 1),
130 BindValue::Number(_) => (ORA_TYPE_NUM_NUMBER, 0, ORA_TYPE_SIZE_NUMBER),
131 BindValue::BinaryInteger(_) => (ORA_TYPE_NUM_BINARY_INTEGER, 0, ORA_TYPE_SIZE_NUMBER),
132 BindValue::Boolean(_) => (ORA_TYPE_NUM_BOOLEAN, 0, ORA_TYPE_SIZE_BOOLEAN),
133 BindValue::BinaryDouble(_) => (ORA_TYPE_NUM_BINARY_DOUBLE, 0, ORA_TYPE_SIZE_BINARY_DOUBLE),
134 BindValue::BinaryFloat(_) => (ORA_TYPE_NUM_BINARY_FLOAT, 0, ORA_TYPE_SIZE_BINARY_FLOAT),
135 BindValue::IntervalDS { .. } => (ORA_TYPE_NUM_INTERVAL_DS, 0, ORA_TYPE_SIZE_INTERVAL_DS),
136 BindValue::IntervalYM { .. } => (ORA_TYPE_NUM_INTERVAL_YM, 0, ORA_TYPE_SIZE_INTERVAL_YM),
137 BindValue::DateTime { .. } => (ORA_TYPE_NUM_DATE, 0, ORA_TYPE_SIZE_DATE),
138 BindValue::Timestamp { ora_type_num, .. } => (
139 *ora_type_num,
140 0,
141 if *ora_type_num == ORA_TYPE_NUM_TIMESTAMP_TZ {
142 ORA_TYPE_SIZE_TIMESTAMP_TZ
143 } else {
144 ORA_TYPE_SIZE_TIMESTAMP
145 },
146 ),
147 BindValue::Array {
148 ora_type_num,
149 csfrm,
150 buffer_size,
151 ..
152 } => (*ora_type_num, *csfrm, (*buffer_size).max(1)),
153 BindValue::Vector(_) => (ORA_TYPE_NUM_VECTOR, 0, TNS_VECTOR_MAX_LENGTH),
156 BindValue::Json(_) => (ORA_TYPE_NUM_JSON, 0, 1),
162 BindValue::Cursor { .. } => (ORA_TYPE_NUM_CURSOR, 0, 4),
163 };
164 Some(BindTypeInfo {
165 ora_type_num,
166 csfrm,
167 buffer_size,
168 })
169}
170
171pub fn define_metadata_from_bind(source: &ColumnMetadata, value: &BindValue) -> ColumnMetadata {
172 let Some(mut info) = bind_value_type_info(value) else {
173 return source.clone();
174 };
175 if source.ora_type_num == ORA_TYPE_NUM_CLOB
176 && matches!(
177 info.ora_type_num,
178 ORA_TYPE_NUM_CHAR | ORA_TYPE_NUM_LONG | ORA_TYPE_NUM_VARCHAR
179 )
180 {
181 info.ora_type_num = ORA_TYPE_NUM_LONG;
182 if source.csfrm != 0 {
183 info.csfrm = source.csfrm;
184 }
185 }
186 let mut metadata = source.clone();
187 metadata.ora_type_num = info.ora_type_num;
188 metadata.csfrm = info.csfrm;
189 if info.ora_type_num == ORA_TYPE_NUM_LONG {
190 metadata.buffer_size = TNS_MAX_LONG_LENGTH;
191 metadata.max_size = 0;
192 } else {
193 metadata.buffer_size = info.buffer_size.max(1);
194 metadata.max_size = info.buffer_size.max(1);
195 }
196 metadata
197}
198
199pub fn adjust_refetch_metadata(previous: &ColumnMetadata, current: &mut ColumnMetadata) -> bool {
206 if current.ora_type_num == ORA_TYPE_NUM_CLOB
207 && matches!(
208 previous.ora_type_num,
209 ORA_TYPE_NUM_CHAR | ORA_TYPE_NUM_LONG | ORA_TYPE_NUM_VARCHAR
210 )
211 {
212 current.ora_type_num = ORA_TYPE_NUM_LONG;
213 current.csfrm = previous.csfrm;
214 current.buffer_size = TNS_MAX_LONG_LENGTH;
215 current.max_size = 0;
216 return true;
217 }
218 if current.ora_type_num == ORA_TYPE_NUM_BLOB
219 && matches!(
220 previous.ora_type_num,
221 ORA_TYPE_NUM_RAW | ORA_TYPE_NUM_LONG_RAW
222 )
223 {
224 current.ora_type_num = ORA_TYPE_NUM_LONG_RAW;
225 current.csfrm = 0;
226 current.buffer_size = TNS_MAX_LONG_LENGTH;
227 current.max_size = 0;
228 return true;
229 }
230 false
231}
232
233pub fn output_bind(value: BindValue) -> BindValue {
234 match value {
235 BindValue::ObjectOutput {
236 schema,
237 type_name,
238 oid,
239 version,
240 buffer_size,
241 ..
242 } => BindValue::ObjectOutput {
243 schema,
244 type_name,
245 oid,
246 version,
247 buffer_size: buffer_size.max(1),
248 is_return: false,
249 },
250 value => {
251 let info = bind_value_type_info(&value).unwrap_or(BindTypeInfo {
252 ora_type_num: ORA_TYPE_NUM_VARCHAR,
253 csfrm: CS_FORM_IMPLICIT,
254 buffer_size: 1,
255 });
256 BindValue::Output {
257 ora_type_num: info.ora_type_num,
258 csfrm: info.csfrm,
259 buffer_size: info.buffer_size,
260 }
261 }
262 }
263}
264
265pub fn returning_output_bind(value: BindValue) -> BindValue {
266 match value {
267 BindValue::ObjectOutput {
268 schema,
269 type_name,
270 oid,
271 version,
272 buffer_size,
273 ..
274 } => BindValue::ObjectOutput {
275 schema,
276 type_name,
277 oid,
278 version,
279 buffer_size: buffer_size.max(1),
280 is_return: true,
281 },
282 value => {
283 let info = bind_value_type_info(&value).unwrap_or(BindTypeInfo {
284 ora_type_num: ORA_TYPE_NUM_VARCHAR,
285 csfrm: CS_FORM_IMPLICIT,
286 buffer_size: 1,
287 });
288 BindValue::ReturnOutput {
289 ora_type_num: info.ora_type_num,
290 csfrm: info.csfrm,
291 buffer_size: info.buffer_size,
292 }
293 }
294 }
295}
296
297pub fn cursor_bind_template() -> BindValue {
298 BindValue::TypedNull {
299 ora_type_num: ORA_TYPE_NUM_CURSOR,
300 csfrm: 0,
301 buffer_size: 4,
302 }
303}
304
305pub fn is_cursor_bind_template(value: &BindValue) -> bool {
306 matches!(
307 value,
308 BindValue::TypedNull {
309 ora_type_num: ORA_TYPE_NUM_CURSOR,
310 ..
311 }
312 )
313}
314
315pub fn public_dbtype_name_from_type_name(type_name: &str) -> &'static str {
316 match type_name {
317 "NUMBER" | "DB_TYPE_NUMBER" | "int" | "float" | "Decimal" => "DB_TYPE_NUMBER",
318 "NATIVE_INT" | "DB_TYPE_BINARY_INTEGER" => "DB_TYPE_BINARY_INTEGER",
319 "NATIVE_FLOAT" | "DB_TYPE_BINARY_DOUBLE" => "DB_TYPE_BINARY_DOUBLE",
320 "DB_TYPE_BINARY_FLOAT" | "BINARY_FLOAT" => "DB_TYPE_BINARY_FLOAT",
321 "DB_TYPE_BOOLEAN" | "BOOLEAN" | "bool" => "DB_TYPE_BOOLEAN",
322 "DB_TYPE_INTERVAL_DS" | "INTERVAL DAY TO SECOND" | "timedelta" => "DB_TYPE_INTERVAL_DS",
323 "DB_TYPE_INTERVAL_YM" | "INTERVAL YEAR TO MONTH" | "IntervalYM" => "DB_TYPE_INTERVAL_YM",
324 "DB_TYPE_BFILE" | "BFILE" => "DB_TYPE_BFILE",
325 "DB_TYPE_JSON" | "JSON" => "DB_TYPE_JSON",
326 "STRING" | "DB_TYPE_VARCHAR" | "str" => "DB_TYPE_VARCHAR",
327 "DB_TYPE_CHAR" => "DB_TYPE_CHAR",
328 "DB_TYPE_NCHAR" => "DB_TYPE_NCHAR",
329 "DB_TYPE_NVARCHAR" => "DB_TYPE_NVARCHAR",
330 "DB_TYPE_CLOB" | "CLOB" => "DB_TYPE_CLOB",
331 "DB_TYPE_NCLOB" | "NCLOB" => "DB_TYPE_NCLOB",
332 "DB_TYPE_BLOB" | "BLOB" => "DB_TYPE_BLOB",
333 "DB_TYPE_LONG" | "LONG" | "LONG_STRING" => "DB_TYPE_LONG",
334 "DB_TYPE_LONG_NVARCHAR" | "LONG NVARCHAR" => "DB_TYPE_LONG_NVARCHAR",
335 "DB_TYPE_LONG_RAW" | "LONG RAW" | "LONG_BINARY" => "DB_TYPE_LONG_RAW",
336 "DB_TYPE_RAW" | "BINARY" | "bytes" => "DB_TYPE_RAW",
337 "ROWID" | "DB_TYPE_ROWID" => "DB_TYPE_ROWID",
338 "DB_TYPE_UROWID" => "DB_TYPE_UROWID",
339 "DATETIME" | "DB_TYPE_DATE" | "date" | "datetime" => "DB_TYPE_DATE",
340 "DB_TYPE_TIMESTAMP" | "TIMESTAMP" => "DB_TYPE_TIMESTAMP",
341 "DB_TYPE_TIMESTAMP_LTZ" | "TIMESTAMP WITH LOCAL TIME ZONE" => "DB_TYPE_TIMESTAMP_LTZ",
342 "DB_TYPE_TIMESTAMP_TZ" | "TIMESTAMP WITH TIME ZONE" => "DB_TYPE_TIMESTAMP_TZ",
343 "DB_TYPE_CURSOR" | "CURSOR" => "DB_TYPE_CURSOR",
344 "DB_TYPE_VECTOR" | "VECTOR" => "DB_TYPE_VECTOR",
345 _ => "DB_TYPE_VARCHAR",
346 }
347}
348
349pub fn column_metadata_is_xmltype(metadata: &ColumnMetadata) -> bool {
350 metadata
351 .object_schema
352 .as_deref()
353 .is_some_and(|schema| schema.eq_ignore_ascii_case("SYS"))
354 && metadata
355 .object_type_name
356 .as_deref()
357 .is_some_and(|name| name.eq_ignore_ascii_case("XMLTYPE"))
358}
359
360pub fn public_dbtype_name_from_column_metadata(metadata: &ColumnMetadata) -> &'static str {
361 if column_metadata_is_xmltype(metadata) {
362 return "DB_TYPE_XMLTYPE";
363 }
364 match (metadata.ora_type_num, metadata.csfrm) {
365 (ORA_TYPE_NUM_LONG, CS_FORM_NCHAR) => "DB_TYPE_LONG_NVARCHAR",
366 (ORA_TYPE_NUM_LONG, _) => "DB_TYPE_LONG",
367 (ORA_TYPE_NUM_LONG_RAW, _) => "DB_TYPE_LONG_RAW",
368 (ORA_TYPE_NUM_VARCHAR, CS_FORM_NCHAR) => "DB_TYPE_NVARCHAR",
369 (ORA_TYPE_NUM_CHAR, CS_FORM_NCHAR) => "DB_TYPE_NCHAR",
370 (ORA_TYPE_NUM_CHAR, _) => "DB_TYPE_CHAR",
371 (ORA_TYPE_NUM_VARCHAR, _) => "DB_TYPE_VARCHAR",
372 (ORA_TYPE_NUM_RAW, _) => "DB_TYPE_RAW",
373 (ORA_TYPE_NUM_ROWID, _) => "DB_TYPE_ROWID",
374 (ORA_TYPE_NUM_UROWID, _) => "DB_TYPE_UROWID",
375 (ORA_TYPE_NUM_BINARY_DOUBLE, _) => "DB_TYPE_BINARY_DOUBLE",
376 (ORA_TYPE_NUM_BINARY_FLOAT, _) => "DB_TYPE_BINARY_FLOAT",
377 (ORA_TYPE_NUM_BINARY_INTEGER, _) => "DB_TYPE_BINARY_INTEGER",
378 (ORA_TYPE_NUM_NUMBER, _) => "DB_TYPE_NUMBER",
379 (ORA_TYPE_NUM_CURSOR, _) => "DB_TYPE_CURSOR",
380 (ORA_TYPE_NUM_OBJECT, _) => "DB_TYPE_OBJECT",
381 (ORA_TYPE_NUM_CLOB, CS_FORM_NCHAR) => "DB_TYPE_NCLOB",
382 (ORA_TYPE_NUM_CLOB, _) => "DB_TYPE_CLOB",
383 (ORA_TYPE_NUM_BLOB, _) => "DB_TYPE_BLOB",
384 (ORA_TYPE_NUM_BFILE, _) => "DB_TYPE_BFILE",
385 (ORA_TYPE_NUM_DATE, _) => "DB_TYPE_DATE",
386 (ORA_TYPE_NUM_TIMESTAMP, _) => "DB_TYPE_TIMESTAMP",
387 (ORA_TYPE_NUM_TIMESTAMP_LTZ, _) => "DB_TYPE_TIMESTAMP_LTZ",
388 (ORA_TYPE_NUM_TIMESTAMP_TZ, _) => "DB_TYPE_TIMESTAMP_TZ",
389 (ORA_TYPE_NUM_INTERVAL_DS, _) => "DB_TYPE_INTERVAL_DS",
390 (ORA_TYPE_NUM_INTERVAL_YM, _) => "DB_TYPE_INTERVAL_YM",
391 (ORA_TYPE_NUM_BOOLEAN, _) => "DB_TYPE_BOOLEAN",
392 (ORA_TYPE_NUM_VECTOR, _) => "DB_TYPE_VECTOR",
393 (ORA_TYPE_NUM_JSON, _) => "DB_TYPE_JSON",
394 _ => "DB_TYPE_VARCHAR",
395 }
396}
397
398pub fn public_dbtype_size_info(dbtype_name: &str) -> (u32, u32) {
402 match dbtype_name {
403 "DB_TYPE_BFILE" => (0, 4000),
404 "DB_TYPE_BINARY_DOUBLE" => (0, ORA_TYPE_SIZE_BINARY_DOUBLE),
405 "DB_TYPE_BINARY_FLOAT" => (0, ORA_TYPE_SIZE_BINARY_FLOAT),
406 "DB_TYPE_BINARY_INTEGER" | "DB_TYPE_NUMBER" => (0, ORA_TYPE_SIZE_NUMBER),
407 "DB_TYPE_BLOB" | "DB_TYPE_CLOB" | "DB_TYPE_NCLOB" => (0, 112),
408 "DB_TYPE_BOOLEAN" => (0, ORA_TYPE_SIZE_BOOLEAN),
409 "DB_TYPE_CHAR" | "DB_TYPE_NCHAR" => (2000, 4),
410 "DB_TYPE_CURSOR" => (0, 4),
411 "DB_TYPE_DATE" => (0, ORA_TYPE_SIZE_DATE),
412 "DB_TYPE_INTERVAL_DS" => (0, ORA_TYPE_SIZE_INTERVAL_DS),
413 "DB_TYPE_INTERVAL_YM" => (0, 5),
414 "DB_TYPE_LONG" | "DB_TYPE_LONG_NVARCHAR" | "DB_TYPE_LONG_RAW" => (0, TNS_MAX_LONG_LENGTH),
415 "DB_TYPE_NVARCHAR" | "DB_TYPE_VARCHAR" => (4000, 4),
416 "DB_TYPE_RAW" => (4000, 1),
417 "DB_TYPE_ROWID" => (0, ORA_TYPE_SIZE_ROWID),
418 "DB_TYPE_TIMESTAMP" | "DB_TYPE_TIMESTAMP_LTZ" => (0, ORA_TYPE_SIZE_TIMESTAMP),
419 "DB_TYPE_TIMESTAMP_TZ" => (0, ORA_TYPE_SIZE_TIMESTAMP_TZ),
420 "DB_TYPE_JSON" | "DB_TYPE_VECTOR" => (0, 1),
421 _ => (0, 0),
422 }
423}
424
425pub fn check_fetch_conversion(
434 source: &ColumnMetadata,
435 to_ora_type_num: u8,
436 to_csfrm: u8,
437) -> Option<ColumnMetadata> {
438 const CHAR_TYPES: [u8; 3] = [ORA_TYPE_NUM_CHAR, ORA_TYPE_NUM_LONG, ORA_TYPE_NUM_VARCHAR];
439 let from = source.ora_type_num;
440 let to = to_ora_type_num;
441 if from == to {
442 return Some(source.clone());
443 }
444 let supported = match from {
445 ORA_TYPE_NUM_BINARY_DOUBLE | ORA_TYPE_NUM_BINARY_FLOAT => {
446 matches!(
447 to,
448 ORA_TYPE_NUM_BINARY_INTEGER
449 | ORA_TYPE_NUM_BINARY_DOUBLE
450 | ORA_TYPE_NUM_BINARY_FLOAT
451 | ORA_TYPE_NUM_NUMBER
452 ) || CHAR_TYPES.contains(&to)
453 }
454 ORA_TYPE_NUM_BINARY_INTEGER => to == ORA_TYPE_NUM_NUMBER || CHAR_TYPES.contains(&to),
455 ORA_TYPE_NUM_BLOB => {
456 if matches!(to, ORA_TYPE_NUM_RAW | ORA_TYPE_NUM_LONG_RAW) {
457 let mut metadata = source.clone();
458 metadata.ora_type_num = ORA_TYPE_NUM_LONG_RAW;
459 metadata.csfrm = 0;
460 metadata.buffer_size = TNS_MAX_LONG_LENGTH;
461 metadata.max_size = 0;
462 return Some(metadata);
463 }
464 false
465 }
466 ORA_TYPE_NUM_CHAR | ORA_TYPE_NUM_LONG | ORA_TYPE_NUM_VARCHAR => {
467 matches!(
468 to,
469 ORA_TYPE_NUM_BINARY_DOUBLE
470 | ORA_TYPE_NUM_BINARY_FLOAT
471 | ORA_TYPE_NUM_NUMBER
472 | ORA_TYPE_NUM_BINARY_INTEGER
473 ) || CHAR_TYPES.contains(&to)
474 }
475 ORA_TYPE_NUM_CLOB => {
476 if CHAR_TYPES.contains(&to) {
477 let mut metadata = source.clone();
478 metadata.ora_type_num = ORA_TYPE_NUM_LONG;
479 metadata.buffer_size = TNS_MAX_LONG_LENGTH;
480 metadata.max_size = 0;
481 return Some(metadata);
482 }
483 false
484 }
485 ORA_TYPE_NUM_DATE
486 | ORA_TYPE_NUM_TIMESTAMP
487 | ORA_TYPE_NUM_TIMESTAMP_LTZ
488 | ORA_TYPE_NUM_TIMESTAMP_TZ => {
489 matches!(
490 to,
491 ORA_TYPE_NUM_DATE
492 | ORA_TYPE_NUM_TIMESTAMP
493 | ORA_TYPE_NUM_TIMESTAMP_LTZ
494 | ORA_TYPE_NUM_TIMESTAMP_TZ
495 ) || CHAR_TYPES.contains(&to)
496 }
497 ORA_TYPE_NUM_INTERVAL_DS | ORA_TYPE_NUM_INTERVAL_YM | ORA_TYPE_NUM_ROWID => {
498 CHAR_TYPES.contains(&to)
499 }
500 ORA_TYPE_NUM_NUMBER => {
501 matches!(
502 to,
503 ORA_TYPE_NUM_BINARY_INTEGER
504 | ORA_TYPE_NUM_BINARY_DOUBLE
505 | ORA_TYPE_NUM_BINARY_FLOAT
506 ) || CHAR_TYPES.contains(&to)
507 }
508 ORA_TYPE_NUM_JSON => {
509 if matches!(to, ORA_TYPE_NUM_CHAR | ORA_TYPE_NUM_VARCHAR) {
527 let mut metadata = source.clone();
528 metadata.ora_type_num = ORA_TYPE_NUM_VARCHAR;
529 metadata.csfrm = CS_FORM_IMPLICIT;
530 metadata.buffer_size = TNS_MAX_LONG_LENGTH;
531 metadata.max_size = 0;
532 return Some(metadata);
533 }
534 if to == ORA_TYPE_NUM_RAW {
538 let mut metadata = source.clone();
539 metadata.ora_type_num = ORA_TYPE_NUM_RAW;
540 metadata.csfrm = 0;
541 metadata.buffer_size = TNS_MAX_LONG_LENGTH;
542 metadata.max_size = 0;
543 return Some(metadata);
544 }
545 false
546 }
547 ORA_TYPE_NUM_VECTOR => {
548 if CHAR_TYPES.contains(&to) {
552 let mut metadata = source.clone();
553 metadata.ora_type_num = ORA_TYPE_NUM_LONG;
554 metadata.csfrm = CS_FORM_IMPLICIT;
555 metadata.buffer_size = TNS_MAX_LONG_LENGTH;
556 metadata.max_size = 0;
557 return Some(metadata);
558 }
559 if to == ORA_TYPE_NUM_CLOB {
560 let mut metadata = source.clone();
561 metadata.ora_type_num = ORA_TYPE_NUM_CLOB;
562 return Some(metadata);
563 }
564 false
565 }
566 _ => false,
567 };
568 let _ = to_csfrm;
569 if supported {
570 Some(source.clone())
571 } else {
572 None
573 }
574}
575
576pub fn public_dbtype_name_from_oracle_type_name(type_name: &str) -> &'static str {
577 let upper = type_name.to_ascii_uppercase();
578 if upper.starts_with("TIMESTAMP") {
579 if upper.contains("LOCAL TIME ZONE") || upper.contains("LOCAL TZ") {
580 return "DB_TYPE_TIMESTAMP_LTZ";
581 }
582 if upper.contains("TIME ZONE") || upper.contains("WITH TZ") {
583 return "DB_TYPE_TIMESTAMP_TZ";
584 }
585 return "DB_TYPE_TIMESTAMP";
586 }
587 match upper.as_str() {
588 "CHAR" => "DB_TYPE_CHAR",
589 "NCHAR" => "DB_TYPE_NCHAR",
590 "VARCHAR2" | "VARCHAR" => "DB_TYPE_VARCHAR",
591 "NVARCHAR2" | "NVARCHAR" => "DB_TYPE_NVARCHAR",
592 "RAW" => "DB_TYPE_RAW",
593 "DATE" => "DB_TYPE_DATE",
594 "TIMESTAMP" => "DB_TYPE_TIMESTAMP",
595 "TIMESTAMP WITH TIME ZONE" | "TIMESTAMP WITH TZ" => "DB_TYPE_TIMESTAMP_TZ",
596 "TIMESTAMP WITH LOCAL TIME ZONE" | "TIMESTAMP WITH LOCAL TZ" => "DB_TYPE_TIMESTAMP_LTZ",
597 "CLOB" => "DB_TYPE_CLOB",
598 "NCLOB" => "DB_TYPE_NCLOB",
599 "BLOB" => "DB_TYPE_BLOB",
600 "XMLTYPE" => "DB_TYPE_XMLTYPE",
601 "BINARY_FLOAT" => "DB_TYPE_BINARY_FLOAT",
602 "BINARY_DOUBLE" => "DB_TYPE_BINARY_DOUBLE",
603 "NUMBER" | "INTEGER" | "SMALLINT" | "REAL" | "DOUBLE PRECISION" | "FLOAT" => {
604 "DB_TYPE_NUMBER"
605 }
606 "BOOLEAN" | "PL/SQL BOOLEAN" => "DB_TYPE_BOOLEAN",
611 "BINARY_INTEGER" | "PLS_INTEGER" | "PL/SQL BINARY INTEGER" | "PL/SQL PLS INTEGER" => {
612 "DB_TYPE_BINARY_INTEGER"
613 }
614 "LONG" => "DB_TYPE_LONG",
615 "LONG RAW" => "DB_TYPE_LONG_RAW",
616 "ROWID" => "DB_TYPE_ROWID",
617 "UROWID" => "DB_TYPE_UROWID",
618 "BFILE" => "DB_TYPE_BFILE",
619 "JSON" => "DB_TYPE_JSON",
620 "VECTOR" => "DB_TYPE_VECTOR",
621 "INTERVAL DAY TO SECOND" => "DB_TYPE_INTERVAL_DS",
622 "INTERVAL YEAR TO MONTH" => "DB_TYPE_INTERVAL_YM",
623 _ => "DB_TYPE_OBJECT",
626 }
627}
628
629pub fn dbobject_attr_precision_scale(
630 type_name: &str,
631 precision: Option<i8>,
632 scale: Option<i8>,
633) -> (i8, i8) {
634 match type_name.to_ascii_uppercase().as_str() {
635 "NUMBER" => (
636 precision.unwrap_or(if scale == Some(0) { 38 } else { 0 }),
637 scale.unwrap_or(-127),
638 ),
639 "INTEGER" | "SMALLINT" => (precision.unwrap_or(38), scale.unwrap_or(0)),
640 "REAL" => (precision.unwrap_or(63), scale.unwrap_or(-127)),
641 "DOUBLE PRECISION" | "FLOAT" => (precision.unwrap_or(126), scale.unwrap_or(-127)),
642 _ => (0, 0),
643 }
644}
645
646pub fn dbobject_attr_max_size(type_name: &str, length: Option<u32>) -> u32 {
647 let length = length.unwrap_or(0);
648 match type_name.to_ascii_uppercase().as_str() {
649 "NCHAR" | "NVARCHAR2" | "NVARCHAR" => length.saturating_mul(2),
650 _ => length,
651 }
652}
653
654pub fn dbobject_rowtype_attr_max_size(
655 type_name: &str,
656 data_length: Option<u32>,
657 char_length: Option<u32>,
658) -> u32 {
659 match type_name.to_ascii_uppercase().as_str() {
660 "CHAR" | "VARCHAR" | "VARCHAR2" | "RAW" => data_length.unwrap_or(0),
661 "NCHAR" | "NVARCHAR" | "NVARCHAR2" => dbobject_attr_max_size(
662 type_name,
663 char_length.filter(|length| *length > 0).or(data_length),
664 ),
665 _ => 0,
666 }
667}
668
669pub fn public_dbtype_name_from_bind(value: &BindValue) -> &'static str {
670 match value {
671 BindValue::TypedNull {
672 ora_type_num,
673 csfrm,
674 ..
675 }
676 | BindValue::Output {
677 ora_type_num,
678 csfrm,
679 ..
680 }
681 | BindValue::ReturnOutput {
682 ora_type_num,
683 csfrm,
684 ..
685 }
686 | BindValue::Array {
687 ora_type_num,
688 csfrm,
689 ..
690 } => public_dbtype_name_from_type_info(*ora_type_num, *csfrm),
691 BindValue::ObjectOutput { .. } | BindValue::ObjectInput { .. } => "DB_TYPE_OBJECT",
692 BindValue::Text(_) => "DB_TYPE_VARCHAR",
693 BindValue::Raw(_) => "DB_TYPE_RAW",
694 BindValue::Lob {
695 ora_type_num,
696 csfrm,
697 ..
698 } => match (*ora_type_num, *csfrm) {
699 (ORA_TYPE_NUM_BLOB, _) => "DB_TYPE_BLOB",
700 (ORA_TYPE_NUM_CLOB, CS_FORM_NCHAR) => "DB_TYPE_NCLOB",
701 (ORA_TYPE_NUM_CLOB, _) => "DB_TYPE_CLOB",
702 _ => "DB_TYPE_CLOB",
703 },
704 BindValue::Number(_) => "DB_TYPE_NUMBER",
705 BindValue::BinaryInteger(_) => "DB_TYPE_BINARY_INTEGER",
706 BindValue::BinaryDouble(_) => "DB_TYPE_BINARY_DOUBLE",
707 BindValue::BinaryFloat(_) => "DB_TYPE_BINARY_FLOAT",
708 BindValue::Boolean(_) => "DB_TYPE_BOOLEAN",
709 BindValue::IntervalDS { .. } => "DB_TYPE_INTERVAL_DS",
710 BindValue::IntervalYM { .. } => "DB_TYPE_INTERVAL_YM",
711 BindValue::DateTime { .. } => "DB_TYPE_DATE",
712 BindValue::Timestamp { ora_type_num, .. } => match *ora_type_num {
713 ORA_TYPE_NUM_TIMESTAMP_LTZ => "DB_TYPE_TIMESTAMP_LTZ",
714 ORA_TYPE_NUM_TIMESTAMP_TZ => "DB_TYPE_TIMESTAMP_TZ",
715 _ => "DB_TYPE_TIMESTAMP",
716 },
717 BindValue::Vector(_) => "DB_TYPE_VECTOR",
718 BindValue::Json(_) => "DB_TYPE_JSON",
719 BindValue::Cursor { .. } => "DB_TYPE_CURSOR",
720 BindValue::Null => "DB_TYPE_VARCHAR",
721 }
722}
723
724pub fn bind_template_from_type_name(type_name: &str, size: u32) -> BindValue {
725 let text_buffer_size = if size == 0 { 4000 } else { size.max(1) };
726 let nchar_buffer_size = text_buffer_size.saturating_mul(4);
727 match type_name {
728 "NUMBER" | "DB_TYPE_NUMBER" | "int" | "float" | "Decimal" => BindValue::TypedNull {
729 ora_type_num: ORA_TYPE_NUM_NUMBER,
730 csfrm: 0,
731 buffer_size: ORA_TYPE_SIZE_NUMBER,
732 },
733 "NATIVE_INT" | "DB_TYPE_BINARY_INTEGER" => BindValue::TypedNull {
734 ora_type_num: ORA_TYPE_NUM_BINARY_INTEGER,
735 csfrm: 0,
736 buffer_size: ORA_TYPE_SIZE_NUMBER,
737 },
738 "NATIVE_FLOAT" | "DB_TYPE_BINARY_DOUBLE" => BindValue::TypedNull {
739 ora_type_num: ORA_TYPE_NUM_BINARY_DOUBLE,
740 csfrm: 0,
741 buffer_size: ORA_TYPE_SIZE_BINARY_DOUBLE,
742 },
743 "DB_TYPE_BINARY_FLOAT" | "BINARY_FLOAT" => BindValue::TypedNull {
744 ora_type_num: ORA_TYPE_NUM_BINARY_FLOAT,
745 csfrm: 0,
746 buffer_size: ORA_TYPE_SIZE_BINARY_FLOAT,
747 },
748 "DB_TYPE_BOOLEAN" | "BOOLEAN" | "bool" => BindValue::TypedNull {
749 ora_type_num: ORA_TYPE_NUM_BOOLEAN,
750 csfrm: 0,
751 buffer_size: ORA_TYPE_SIZE_BOOLEAN,
752 },
753 "DB_TYPE_INTERVAL_DS" | "INTERVAL DAY TO SECOND" | "timedelta" => BindValue::TypedNull {
754 ora_type_num: ORA_TYPE_NUM_INTERVAL_DS,
755 csfrm: 0,
756 buffer_size: ORA_TYPE_SIZE_INTERVAL_DS,
757 },
758 "DB_TYPE_INTERVAL_YM" | "INTERVAL YEAR TO MONTH" | "IntervalYM" => BindValue::TypedNull {
759 ora_type_num: ORA_TYPE_NUM_INTERVAL_YM,
760 csfrm: 0,
761 buffer_size: ORA_TYPE_SIZE_INTERVAL_YM,
762 },
763 "STRING" | "DB_TYPE_VARCHAR" | "DB_TYPE_CHAR" | "str" => BindValue::TypedNull {
764 ora_type_num: ORA_TYPE_NUM_VARCHAR,
765 csfrm: CS_FORM_IMPLICIT,
766 buffer_size: text_buffer_size,
767 },
768 "DB_TYPE_NCHAR" | "DB_TYPE_NVARCHAR" => BindValue::TypedNull {
769 ora_type_num: ORA_TYPE_NUM_VARCHAR,
770 csfrm: CS_FORM_NCHAR,
771 buffer_size: nchar_buffer_size,
772 },
773 "DB_TYPE_CLOB" | "CLOB" => BindValue::TypedNull {
774 ora_type_num: ORA_TYPE_NUM_LONG,
775 csfrm: CS_FORM_IMPLICIT,
776 buffer_size: TNS_MAX_LONG_LENGTH,
777 },
778 "DB_TYPE_NCLOB" | "NCLOB" => BindValue::TypedNull {
779 ora_type_num: ORA_TYPE_NUM_LONG,
780 csfrm: CS_FORM_NCHAR,
781 buffer_size: TNS_MAX_LONG_LENGTH,
782 },
783 "DB_TYPE_BLOB" | "BLOB" => BindValue::TypedNull {
784 ora_type_num: ORA_TYPE_NUM_LONG_RAW,
785 csfrm: 0,
786 buffer_size: TNS_MAX_LONG_LENGTH,
787 },
788 "DB_TYPE_LONG" | "LONG" | "LONG_STRING" => BindValue::TypedNull {
789 ora_type_num: ORA_TYPE_NUM_LONG,
790 csfrm: CS_FORM_IMPLICIT,
791 buffer_size: TNS_MAX_LONG_LENGTH,
792 },
793 "DB_TYPE_LONG_NVARCHAR" | "LONG NVARCHAR" => BindValue::TypedNull {
794 ora_type_num: ORA_TYPE_NUM_LONG,
795 csfrm: CS_FORM_NCHAR,
796 buffer_size: TNS_MAX_LONG_LENGTH,
797 },
798 "DB_TYPE_LONG_RAW" | "LONG RAW" | "LONG_BINARY" => BindValue::TypedNull {
799 ora_type_num: ORA_TYPE_NUM_LONG_RAW,
800 csfrm: 0,
801 buffer_size: TNS_MAX_LONG_LENGTH,
802 },
803 "DB_TYPE_RAW" | "BINARY" | "bytes" => BindValue::TypedNull {
804 ora_type_num: ORA_TYPE_NUM_RAW,
805 csfrm: 0,
806 buffer_size: size.max(1).max(4000),
807 },
808 "ROWID" | "DB_TYPE_ROWID" | "DB_TYPE_UROWID" => BindValue::TypedNull {
809 ora_type_num: ORA_TYPE_NUM_VARCHAR,
810 csfrm: CS_FORM_IMPLICIT,
811 buffer_size: 5267,
812 },
813 "DATETIME" | "DB_TYPE_DATE" | "date" | "datetime" => BindValue::TypedNull {
814 ora_type_num: ORA_TYPE_NUM_DATE,
815 csfrm: 0,
816 buffer_size: ORA_TYPE_SIZE_DATE,
817 },
818 "DB_TYPE_TIMESTAMP" | "TIMESTAMP" => BindValue::TypedNull {
819 ora_type_num: ORA_TYPE_NUM_TIMESTAMP,
820 csfrm: 0,
821 buffer_size: ORA_TYPE_SIZE_TIMESTAMP,
822 },
823 "DB_TYPE_TIMESTAMP_LTZ" | "TIMESTAMP WITH LOCAL TIME ZONE" => BindValue::TypedNull {
824 ora_type_num: ORA_TYPE_NUM_TIMESTAMP_LTZ,
825 csfrm: 0,
826 buffer_size: ORA_TYPE_SIZE_TIMESTAMP,
827 },
828 "DB_TYPE_TIMESTAMP_TZ" | "TIMESTAMP WITH TIME ZONE" => BindValue::TypedNull {
829 ora_type_num: ORA_TYPE_NUM_TIMESTAMP_TZ,
830 csfrm: 0,
831 buffer_size: ORA_TYPE_SIZE_TIMESTAMP_TZ,
832 },
833 "DB_TYPE_CURSOR" | "CURSOR" => cursor_bind_template(),
834 "DB_TYPE_VECTOR" | "VECTOR" => BindValue::TypedNull {
835 ora_type_num: ORA_TYPE_NUM_VECTOR,
836 csfrm: 0,
837 buffer_size: TNS_VECTOR_MAX_LENGTH,
838 },
839 "DB_TYPE_JSON" | "JSON" => BindValue::TypedNull {
840 ora_type_num: ORA_TYPE_NUM_JSON,
841 csfrm: 0,
842 buffer_size: TNS_VECTOR_MAX_LENGTH,
843 },
844 _ => BindValue::Null,
845 }
846}
847
848pub fn dbobject_element_bind_type_info(dbtype_name: &str, max_size: u32) -> BindTypeInfo {
849 let buffer_size = max_size.max(1);
850 let (ora_type_num, csfrm, buffer_size) = match dbtype_name {
851 "DB_TYPE_NUMBER" => (ORA_TYPE_NUM_NUMBER, 0, ORA_TYPE_SIZE_NUMBER),
852 "DB_TYPE_RAW" | "DB_TYPE_BLOB" => (ORA_TYPE_NUM_RAW, 0, buffer_size.max(4000)),
853 "DB_TYPE_NCHAR" | "DB_TYPE_NVARCHAR" | "DB_TYPE_NCLOB" => {
854 (ORA_TYPE_NUM_VARCHAR, CS_FORM_NCHAR, buffer_size.max(4000))
855 }
856 "DB_TYPE_DATE" => (ORA_TYPE_NUM_DATE, 0, ORA_TYPE_SIZE_DATE),
857 "DB_TYPE_TIMESTAMP" => (ORA_TYPE_NUM_TIMESTAMP, 0, ORA_TYPE_SIZE_TIMESTAMP),
858 "DB_TYPE_TIMESTAMP_LTZ" => (ORA_TYPE_NUM_TIMESTAMP_LTZ, 0, ORA_TYPE_SIZE_TIMESTAMP),
859 "DB_TYPE_TIMESTAMP_TZ" => (ORA_TYPE_NUM_TIMESTAMP_TZ, 0, ORA_TYPE_SIZE_TIMESTAMP_TZ),
860 _ => (
861 ORA_TYPE_NUM_VARCHAR,
862 CS_FORM_IMPLICIT,
863 buffer_size.max(4000),
864 ),
865 };
866 BindTypeInfo {
867 ora_type_num,
868 csfrm,
869 buffer_size,
870 }
871}
872
873pub(crate) fn public_dbtype_name_from_type_info(ora_type_num: u8, csfrm: u8) -> &'static str {
874 match (ora_type_num, csfrm) {
875 (ORA_TYPE_NUM_BINARY_DOUBLE, _) => "DB_TYPE_BINARY_DOUBLE",
876 (ORA_TYPE_NUM_BINARY_FLOAT, _) => "DB_TYPE_BINARY_FLOAT",
877 (ORA_TYPE_NUM_INTERVAL_DS, _) => "DB_TYPE_INTERVAL_DS",
878 (ORA_TYPE_NUM_INTERVAL_YM, _) => "DB_TYPE_INTERVAL_YM",
879 (ORA_TYPE_NUM_BOOLEAN, _) => "DB_TYPE_BOOLEAN",
880 (ORA_TYPE_NUM_BINARY_INTEGER, _) => "DB_TYPE_BINARY_INTEGER",
881 (ORA_TYPE_NUM_NUMBER, _) => "DB_TYPE_NUMBER",
882 (ORA_TYPE_NUM_CHAR, CS_FORM_NCHAR) | (ORA_TYPE_NUM_VARCHAR, CS_FORM_NCHAR) => {
883 "DB_TYPE_NVARCHAR"
884 }
885 (ORA_TYPE_NUM_CHAR, _) => "DB_TYPE_CHAR",
886 (ORA_TYPE_NUM_VARCHAR, _) => "DB_TYPE_VARCHAR",
887 (ORA_TYPE_NUM_LONG, CS_FORM_NCHAR) => "DB_TYPE_LONG_NVARCHAR",
888 (ORA_TYPE_NUM_LONG, _) => "DB_TYPE_LONG",
889 (ORA_TYPE_NUM_LONG_RAW, _) => "DB_TYPE_LONG_RAW",
890 (ORA_TYPE_NUM_RAW, _) => "DB_TYPE_RAW",
891 (ORA_TYPE_NUM_DATE, _) => "DB_TYPE_DATE",
892 (ORA_TYPE_NUM_TIMESTAMP, _) => "DB_TYPE_TIMESTAMP",
893 (ORA_TYPE_NUM_TIMESTAMP_LTZ, _) => "DB_TYPE_TIMESTAMP_LTZ",
894 (ORA_TYPE_NUM_TIMESTAMP_TZ, _) => "DB_TYPE_TIMESTAMP_TZ",
895 (ORA_TYPE_NUM_CURSOR, _) => "DB_TYPE_CURSOR",
896 (ORA_TYPE_NUM_OBJECT, _) => "DB_TYPE_OBJECT",
897 (ORA_TYPE_NUM_VECTOR, _) => "DB_TYPE_VECTOR",
898 (ORA_TYPE_NUM_JSON, _) => "DB_TYPE_JSON",
899 _ => "DB_TYPE_VARCHAR",
900 }
901}
902
903pub(crate) fn bind_metadata(value: &BindValue) -> (u8, u8, u32) {
904 bind_value_type_info(value)
905 .map(|info| (info.ora_type_num, info.csfrm, info.buffer_size))
906 .unwrap_or((ORA_TYPE_NUM_VARCHAR, CS_FORM_IMPLICIT, 1))
907}
908
909pub(crate) fn write_bind_value(writer: &mut TtcWriter, value: &BindValue, csfrm: u8) -> Result<()> {
910 match value {
911 BindValue::TypedNull {
912 ora_type_num: ORA_TYPE_NUM_CURSOR,
913 ..
914 } => {
915 writer.write_u8(1);
916 writer.write_u8(0);
917 Ok(())
918 }
919 BindValue::TypedNull {
924 ora_type_num: ORA_TYPE_NUM_BOOLEAN,
925 ..
926 } => {
927 writer.write_u8(TNS_ESCAPE_CHAR);
928 writer.write_u8(1);
929 Ok(())
930 }
931 BindValue::Null | BindValue::TypedNull { .. } => {
932 writer.write_u8(0);
933 Ok(())
934 }
935 BindValue::Output { .. } | BindValue::ReturnOutput { .. } => {
936 writer.write_u8(0);
937 Ok(())
938 }
939 BindValue::ObjectOutput { .. } => {
940 writer.write_ub4(0);
943 writer.write_ub4(0);
944 writer.write_ub4(0);
945 writer.write_ub2(0);
946 writer.write_ub4(0);
947 writer.write_ub4(TNS_OBJ_TOP_LEVEL);
948 Ok(())
949 }
950 BindValue::ObjectInput { oid, image, .. } => write_dbobject_bind(writer, oid, image),
951 BindValue::Text(value) => {
952 let bytes = encode_text_value(value, csfrm);
953 writer.write_bytes_with_length(&bytes)
954 }
955 BindValue::Raw(value) => writer.write_bytes_with_length(value),
956 BindValue::Lob { locator, .. } => writer.write_bytes_with_two_lengths(Some(locator)),
957 BindValue::Number(value) | BindValue::BinaryInteger(value) => {
958 let bytes = encode_number_text(value)?;
959 writer.write_bytes_with_length(&bytes)
960 }
961 BindValue::Boolean(value) => {
964 let bytes: &[u8] = if *value { &[1, 1] } else { &[0] };
965 writer.write_bytes_with_length(bytes)
966 }
967 BindValue::BinaryDouble(value) => {
968 let bytes = encode_binary_double(*value);
969 writer.write_bytes_with_length(&bytes)
970 }
971 BindValue::BinaryFloat(value) => {
972 let bytes = encode_binary_float(*value as f32);
973 writer.write_bytes_with_length(&bytes)
974 }
975 BindValue::IntervalDS {
976 days,
977 seconds,
978 microseconds,
979 } => {
980 let bytes = encode_interval_ds(*days, *seconds, *microseconds)?;
981 writer.write_bytes_with_length(&bytes)
982 }
983 BindValue::IntervalYM { years, months } => {
984 let bytes = encode_interval_ym(*years, *months)?;
985 writer.write_bytes_with_length(&bytes)
986 }
987 BindValue::DateTime {
988 year,
989 month,
990 day,
991 hour,
992 minute,
993 second,
994 } => {
995 let bytes = encode_oracle_date(*year, *month, *day, *hour, *minute, *second)?;
996 writer.write_bytes_with_length(&bytes)
997 }
998 BindValue::Timestamp {
999 year,
1000 month,
1001 day,
1002 hour,
1003 minute,
1004 second,
1005 nanosecond,
1006 ora_type_num,
1007 } => {
1008 let bytes = if matches!(*ora_type_num, ORA_TYPE_NUM_TIMESTAMP_TZ) {
1009 encode_oracle_timestamp_tz(
1010 *year,
1011 *month,
1012 *day,
1013 *hour,
1014 *minute,
1015 *second,
1016 *nanosecond,
1017 )?
1018 } else {
1019 encode_oracle_timestamp(*year, *month, *day, *hour, *minute, *second, *nanosecond)?
1020 };
1021 writer.write_bytes_with_length(&bytes)
1022 }
1023 BindValue::Array {
1024 values,
1025 csfrm: array_csfrm,
1026 ..
1027 } => {
1028 writer.write_ub4(u32::try_from(values.len()).map_err(|_| {
1029 ProtocolError::InvalidPacketLength {
1030 length: values.len(),
1031 minimum: 0,
1032 }
1033 })?);
1034 for value in values {
1035 match value {
1036 Some(value) => write_bind_value(writer, value, *array_csfrm)?,
1037 None => writer.write_u8(0),
1038 }
1039 }
1040 Ok(())
1041 }
1042 BindValue::Vector(vector) => {
1045 let image = crate::vector::encode_vector(vector);
1046 crate::vector::write_vector_image(writer, &image)
1047 }
1048 BindValue::Json(image) => crate::vector::write_vector_image(writer, image),
1051 BindValue::Cursor { cursor_id } => {
1052 if *cursor_id == 0 {
1053 writer.write_u8(1);
1054 writer.write_u8(0);
1055 } else {
1056 writer.write_ub4(1);
1057 writer.write_ub4(*cursor_id);
1058 }
1059 Ok(())
1060 }
1061 }
1062}
1063
1064pub(crate) fn encode_text_value(value: &str, csfrm: u8) -> Vec<u8> {
1065 if csfrm == CS_FORM_NCHAR {
1066 let mut bytes = Vec::with_capacity(value.len().saturating_mul(2));
1067 for unit in value.encode_utf16() {
1068 bytes.extend_from_slice(&unit.to_be_bytes());
1069 }
1070 bytes
1071 } else {
1072 value.as_bytes().to_vec()
1073 }
1074}