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