RTTI: Get length of a character column - abap

My function module receives a table name and a column name at runtime.
I would like to get the length of the column: How many characters are allowed in the transparent table?
I used my favorite search engine and found RTTS.
But the examples in the documentation pass a variable to the RTTS method DESCRIBE_BY_DATA; in my case, I don't have a variable, I just have the type names in table_name and column_name.
How to get the length?

For retrieving the type of a given DDIC type only known at runtime, use the method DESCRIBE_BY_NAME. The RTTI length is always returned as a number of bytes.
Example to get the type of the column CARRID of table SFLIGHT (I know it's a column of 3 characters) :
cl_abap_typedescr=>describe_by_name(
EXPORTING
p_name = 'SFLIGHT-CARRID'
RECEIVING
p_descr_ref = DATA(lo_typedescr)
EXCEPTIONS
type_not_found = 1 ).
" you should handle the error if SY-SUBRC <> 0
" Because it's SFLIGHT-CARRID, I expect 6 BYTES
ASSERT lo_typedescr->length = 6. " 3 characters * 2 bytes (Unicode)
" Length in CHARACTERS
CASE lo_typedescr->type_kind.
WHEN lo_typedescr->typekind_char
OR lo_typedescr->typekind_num
OR lo_typedescr->typekind_date
OR lo_typedescr->typekind_time
OR lo_typedescr->typekind_string.
DATA(no_of_characters) = lo_typedescr->length / cl_abap_char_utilities=>charsize.
ASSERT no_of_characters = 3.
ENDCASE.

You don't need RTTS for this. You can Do one of this
Select table DD03L with TABNAME and FIELDNAME
Use Function DDIF_FIELDINFO_GET

Related

Difference between ORA-12899 and ORA-01480

I would like to understand the difference between ORA-12899 and ORA-01480
ORA-12899: value too large for column
ORA-01480: trailing null missing from STR bind value
Based on my understanding, I know about ORA-12899 and how it can come. Lets say if datatype for some column is VARCHAR2(100 BYTE) and I am trying to insert more than 100 BYTE in the column then I am getting ORA-12899.
What about ORA-01480 ? I searchthe ed on internet and and the similar explanation like ORA-12899
From google :
ORA-01480: trailing null missing from STR bind value
Cause: A bind variable of type 5 (null-terminated string) does not contain the terminating null in its buffer.
Maybe you're trying to insert a string in a column that is bigger than the column length. So, the terminating character is not being
inserted at the end of the string.
Both ORA-12899 and ORA-01480 look similar. Can someone please explain the exact difference with an example?
The error:
ORA-01480: trailing null missing from STR bind value
Is for languages such as C or C++ (among many others) which allow low level manipulation of string-like objects.
If you have a VARCHAR2(100) column and you try to pass, to a bind variable, the byte array:
['h','e','l','l','o']
Then you may get the ORA-01480 error because a NUL terminated string is expected and it should have been:
['h','e','l','l','o','\0']
And the NUL character is expected as it allows the SQL engine to determine the length of the VARCHAR2 variable length string.
An example is using Pro*C:
char s[2] = "ab";
EXEC SQL SELECT :s into :s2 FROM dual;
Outputs: ORA-01480: trailing null missing from STR bind value
char s[3] = "ab\0";
EXEC SQL SELECT :s into :s2 FROM dual;
Works.

Casting of structure to string fails when structure contains a String field

I have a dynamic internal table <ft_dyn_tab>. I want to cast each row of the internal table to the type string via the field symbol <lf_string>:
LOOP AT <ft_dyn_tab> ASSIGNING <fs_dyn_wa>.
ASSIGN <fs_dyn_wa> to <lf_string> CASTING.
...
"other logic
...
ENDLOOP.
Normally, CASTING works fine when all fields of the structure are of type character. But when one field is of type string, it gives a runtime error. Can anyone explain why? And how to resolve this issue?
Why a structure with only character-like and String components can't be "casted" as a text variable
The reason is given by the ABAP documentation of Strings:
"A structure that contains a string is a deep structure and cannot be used as a character-like field in the same way as a flat structure.".
and of Deep:
"Deep: [...] the content [...] is addressed internally using references ([...], strings..."
and of Memory Requirement for Deep Data Objects:
"The memory requirement for the reference is 8 byte. [...] In strings, [...] an implicit reference is created internally."
and of ASSIGN - casting_spec:
"If the data type determined by CASTING is deep or if deep data objects are stored in the assigned memory area, the deep components must appear with exactly the same type and position in the assigned memory area. In particular, this means that individual reference variables can be assigned to only one field symbol that is typed as a reference variable by the same static type."
Now, the reason why the compiler and the run time don't let you do that, is that if you cast a whole deep structure, you could change the 8-bytes reference to access any place in the memory, that could be dangerous (How dangerous is it to access an array out of bounds?) and very difficult to analyze the subsequent bugs. In all programming languages, as far as possible, the compiler prevents out-of-bounds accesses or the checks are done at run time (Bounds checking).
Workaround
Your issue happens at run time because you use dynamically-created data objects, but you'd have exactly the same issue at compile time with statically-defined data objects. Below is a simple solution with a statically-defined structure.
You can access each field of the structure and concatenate it to a string:
DATA: BEGIN OF dyn_wa,
country TYPE c LENGTH 3,
city TYPE string,
END OF dyn_wa,
lf_string TYPE string.
FIELD-SYMBOLS: <lf_field> TYPE clike.
dyn_wa = VALUE #( country = 'FR' city = 'Paris' ).
DO.
ASSIGN COMPONENT sy-index OF STRUCTURE dyn_wa TO <lf_field>.
IF sy-subrc <> 0.
EXIT.
ENDIF.
CONCATENATE lf_string <lf_field> INTO lf_string RESPECTING BLANKS.
ENDDO.
ASSERT lf_string = 'FR Paris'. " one space because country is 3 characters
RESPECTING BLANKS keeps trailing spaces, to mimic ASSIGN ... CASTING.
Sounds like you want to assign the complete structured row to a plain string field symbol. This doesn't work. You can only assign the individual type-compatible components of the structured row to the string field symbol.
Otherwise, this kind of assignment works fine. For a table with a single column with type string:
TYPES table_type TYPE STANDARD TABLE OF string WITH EMPTY KEY.
DATA(filled_table) = VALUE table_type( ( `Test` ) ).
ASSIGN filled_table TO FIELD-SYMBOL(<dynamic_table>).
FIELD-SYMBOLS <string> TYPE string.
LOOP AT <dynamic_table> ASSIGNING FIELD-SYMBOL(<row>).
ASSIGN <row> TO FIELD-SYMBOL(<string>).
ENDLOOP.
For a table with a structured row type:
TYPES:
BEGIN OF row_type,
some_character_field TYPE char80,
the_string_field TYPE string,
END OF row_type.
TYPES table_type TYPE STANDARD TABLE OF row_type WITH EMPTY KEY.
DATA(filled_table) = VALUE table_type( ( some_character_field = 'ABC'
the_string_field = `Test` ) ).
ASSIGN filled_table TO FIELD-SYMBOL(<dynamic_table>).
FIELD-SYMBOLS <string> TYPE string.
LOOP AT <dynamic_table> ASSIGNING FIELD-SYMBOL(<row>).
ASSIGN <row>-the_string_field TO <string>.
ENDLOOP.
I have just tested this and it gives runtime error also when the structure does not have any string typed field.
I change the ASSIGN to a simple MOVE to a string variable g_string and it fails with runtime. If this fail it means that such an assignment is not possible, so the casting will not be either.
REPORT ZZZ.
TYPES BEGIN OF t_test.
TYPES: f1 TYPE c LENGTH 2,
f2 TYPE n LENGTH 4,
f3 TYPE string.
TYPEs END OF t_test.
TYPES BEGIN OF t_test2.
TYPES: f1 TYPE c LENGTH 2,
f2 TYPE n LENGTH 4,
f3 TYPE c LENGTH 80.
TYPES END OF t_test2.
TYPES: tt_test TYPE STANDARD TABLE OF t_test WITH EMPTY KEY,
tt_test2 TYPE STANDARD TABLE OF t_test2 WITH EMPTY KEY.
DATA(gt_test) = VALUE tt_test( ( f1 = '01' f2 = '1234' f3 = `Test`) ).
DATA(gt_test2) = VALUE tt_test2( ( f1 = '01' f2 = '1234' f3 = 'Test') ).
DATA: g_string TYPE string.
FIELD-SYMBOLS: <g_any_table> TYPE ANY TABLE,
<g_string> TYPE string.
ASSIGN gt_test2 TO <g_any_table>.
ASSERT <g_any_table> IS ASSIGNED.
LOOP AT <g_any_table> ASSIGNING FIELD-SYMBOL(<g_any_wa2>).
* ASSIGN <g_any_wa2> TO <g_string> CASTING.
g_string = <g_any_wa2>.
ENDLOOP.
UNASSIGN <g_any_table>.
ASSIGN gt_test TO <g_any_table>.
ASSERT <g_any_table> IS ASSIGNED.
LOOP AT <g_any_table> ASSIGNING FIELD-SYMBOL(<g_any_wa>).
* ASSIGN <g_any_wa> TO <g_string> CASTING.
g_string = <g_any_wa>.
ENDLOOP.

How do I split multiple times in abap?

I want to split my code into a workarea and append this to a an internal table for a later perform.
But sometimes the text contains more than 3 numbers for example 3;5;3;6;2;5 but its always 3,6,9,12... number. How can I solve the problem that I want to loop 3 times, then the next 3 numbers and so on?
DATA: text(100) type c,
it_1 TYPE STANDART TABLE LIKE text,
it_2 TYPE STANDART TABLE LIKE text,
it_3 TYPE STANDART TABLE LIKE text,
string(100) TYPE c.
text = '123;2;2'.
SPLIT text AT ';' INTO wa_1-c1 wa_1-c2 wa_1-c3.
APPEND wa_1-c1 to it_1.
APPEND wa_1-c2 to it_2.
APPEND wa_1-c3 to it_3.
LOOP at it_1 INTO string.
PERFORM task using string.
ENDLOOP.
You should use the INTO TABLE addition to the split keyword rather than hard coding the fields.
DATA: text_s TYPE string.
text_s = '123;2;2'.
DATA: text_tab TYPE TABLE OF string.
SPLIT text_s AT ';' INTO TABLE text_tab.
LOOP AT text_tab ASSIGNING FIELD-SYMBOL(<line>).
"do whatever on each token here
ENDLOOP.
This will split the string in 3-er blocks, while overwriting it with the rest:
WHILE text IS NOT INITIAL.
SPLIT AT ';'
INTO wa_1-c1
wa_1-c2
wa_1-c3
text.
APPEND: wa_1-c1 to it_1,
wa_1-c2 to it_2,
wa_1-c3 to it_3.
ENDWHILE.
Please note, the string variable text will be initial at the end, if its original value is still needed, than you can define another string, copy the value and use that one for the split.
You can try using Sy-tabix if you want to control the iterations three times and since you are saving the text values in 3 different internal tables.
DATA: text(100) type c,
it_1 TYPE STANDARD TABLE OF text,
it_2 TYPE STANDARD TABLE OF text,
it_3 TYPE STANDARD TABLE OF text,
string(100) TYPE c.
text = '123;2;2'.
SPLIT text AT ';' INTO TABLE it_1.
LOOP at it_1 INTO string WHERE sy-tabix = 3.
WRITE : string.
ENDLOOP.
if sy-tabix = 3.
LOOP AT it_2 INTO string WHERE sy-tabix = sy-tabix+3.
"do the next loop
ENDLOOP.
ENDIF.

QTP, How to convert a decimal value (VarType = 14) to any other VarType like int, etc

I'm running the following query in UFT to retrieve a value from the database. It returns the correct number for me. The number is 21572939 but the vartype for the number is vbDecimal(14).
sqlMsgQuery = ("SELECT MAX(CONTNO)FROM SERIES A, CONTRACT B WHERE A.SERIES = B.SERIES AND B.SERIES = "&"'AAA-W'"&_
"AND A.PARTY_ROLE IS NULL AND B.CONTNO BETWEEN 21573931 AND 21574930")
On the other hand, i have another variable that holds the same value but this value is being retrieve from an oracle application and the vartype is vbString(8)
my job is to verify if the the two numbers are equal and then write it to datatable. I've tried a lot but failed to convert a decimal value with a vartype(14) to int and compare it with the other string that i've converted to int already.
What i'm looking for:
1. Either a query that will convert the number into int in the database before it is sent to the variable so that i can use it as int
2. Or a vbscript function/code that will do the conversion.
3. Or any other solution.
Thanks in advance.
If the length of the SQL column CONTNO is 8, you need to create a variable with VBString(8) as data type for storing the result from the SQL query .
And modify the existing query like below.
sqlMsgQuery = ("SELECT CAST(MAX(CONTNO) as VARCHAR (8))
FROM SERIES A, CONTRACT B
WHERE A.SERIES = B.SERIES AND B.SERIES = "&"'AAA-W'"&_
"AND A.PARTY_ROLE IS NULL AND B.CONTNO BETWEEN 21573931 AND 21574930")
You can change the required data to the data type you want using CAST in SQL.
CAST ( expression AS data_type [ ( length ) ] )
When you get the column CAST it as (data_type you want) then compare. Make sure both are same DATA_TYPE and same LENGTH.

IBM DB2 for i SQL (iSeries) - Removing a character from end of a field using update

I have a product table called PDPRODP - for certain styles within this table I used a concat statement to add a full-stop to their description (PRDESC), I now wish to remove this full stop.
The descriptions are varying length, the field max size is 30 characters and I need to physically remove the full-stop rather than using a select statement to trim the full-stop.
I tried;
UPDATE PDPRODP SET PRDESC = PRDESC-1 where PRSTYLE = 1234
But I got this error:
Character in CAST argument not valid.
I also tried this following some googling;
UPDATE PDPRODP SET PRDESC=LEFT(PRDESC, LEN(PRDESC)-1)
WHERE PRCOMP = 1 AND PRSTYL = 31285
But got this error:
LEN in *LIBL type *N not found.
Use LENGTH
UPDATE PDPRODP SET PRDESC=LEFT(PRDESC, LENGTH(PRDESC)-1)
WHERE PRCOMP = 1 AND PRSTYL = 31285
The REPLACE() function can search for all occurrences of some string, and substitute another in its place. You might search for your full-stop, and replace it with a zero-length string ''. This would be handy in cases where your search string may not always be at the end.