fernandobatels / rsfbclient

Rust Firebird Client
MIT License
76 stars 11 forks source link

Why are all Firebird type constants in Rust SqlType + 1? #134

Closed datiscum closed 2 years ago

datiscum commented 2 years ago

I've only been looking at Rust for two days, but I've been dealing with Firebird/Interbase for 20 years. When trying with the sample sources, I wanted to test the SQLType of the field:

let abd: (u32, u32) = row.cols[0].value.sql_type_and_subtype(); if abd.0 == SQL_INT64 { println!("Int64 " ); }

This did not work because " abd.0 == SQL_INT64 + 1 " is

Extract from params.rs impl SqlType { /// Convert the sql value to interbase format pub fn sql_type_and_subtype(&self) -> (u32, u32) { match self { Text(s) => { if s.len() > MAX_TEXT_LENGTH { (ibase::SQL_BLOB + 1, 1) } else { (ibase::SQLTEXT + 1, 0) } } Integer() => (ibase::SQLINT64 + 1, 0), Floating() => (ibase::SQLDOUBLE + 1, 0), Timestamp() => (ibase::SQL_TIMESTAMP + 1, 0), Null => (ibase::SQLTEXT + 1, 0), Binary() => (ibase::SQLBLOB + 1, 0), Boolean() => (ibase::SQL_BOOLEAN + 1, 0), } } } What reason is there to take any original type of Firebird +1 ?

I think the blob subtypes are also swapped. Excerpt from : https://firebirdsql.org/file/documentation/chunk/en/refdocs/fblangref30/fblangref30-datatypes-bnrytypes.html

3.7.1 BLOB subtypes

The optional SUB_TYPE parameter specifies the nature of data written to the column. Firebird provides two pre-defined subtypes for storing user data:

Subtype 0: BINARY (ibase::SQL_BLOB + 1, ->1<-) If a subtype is not specified, the specification is assumed to be for untyped data and the default SUB_TYPE 0 is applied. The alias for subtype zero is BINARY. This is the subtype to specify when the data are any form of binary file or stream: images, audio, word-processor files, PDFs and so on. Subtype 1: TEXT (ibase::SQL_TEXT + 1, ->0<- ) Subtype 1 has an alias, TEXT, which can be used in declarations and definitions. For instance, BLOB SUB_TYPE TEXT. It is a specialised subtype used to store plain text data that is too large to fit into a string type.

fernandobatels commented 2 years ago

What reason is there to take any original type of Firebird +1 ?

Because we perform some "Bitwise AND" operations with the sqltype. So this sql_type_and_subtype doesn't reflects the exactly firebird type.

I think the blob subtypes are also swapped.

Not exactly. We use the sub_type 0 for raw binary here.

But when we have a text until 32.769 chars, we can send it as a normal varchar(ibase::SQL_TEXT + 1), after that size we send it as a blob sub_type 0(ibase::SQL_BLOB + 1).

fernandobatels commented 2 years ago

For matching the Firebird types, you can use the SqlType enum values:

let abd = row.cols[0];
if abd == SqlType::Integer {
     println!("Int64 " );
}
datiscum commented 2 years ago

For matching the Firebird types, you can use the SqlType enum values:

let abd = row.cols[0];
if abd == SqlType::Integer {
     println!("Int64 " );
}

Unfortunately, it doesn't work like that and my attempts to change it myself haven't worked yet either. But when I am a bit more familiar with RUST, it should be feasible. That was the reason why I came up with the idea of "if abd.0 == SQL_INT64" in the first place. Thank you

fernandobatels commented 2 years ago

I haven't tested the code in my answer, maybe a .value is missing:

let abd = row.cols[0].value;

If you need more help, you can share a gist with your full code.

datiscum commented 2 years ago

Didn't work either! I have tested this and other variants, but only got from one error to the next.

jairinhohw commented 2 years ago

The last bit of the sqltype number is 0 if the column is not nullable, and 1 if the column is nullable. If you just want to compare with the type, you can use sqltype & (!1) == ibase::SQL_TEXT to set the last bit to 0.