Archive for the ‘sql’ Category
Reset your MySQL Password
A couple students stumbled on the MySQL on-line documentation today. Specifically, they didn’t find instructions on how a non-root
user could change their password. I’ve also added this as an explanation on the MySQL documentation for 12.5.1.6. SET PASSWORD
Syntax page.
You reset your own password when you’re not the root
user with this syntax:
mysql> SET PASSWORD = PASSWORD('new_password'); |
This is simple syntax but carries a risk if you walk away from your terminal because somebody can run it without needing to know the original user password. Oracle requires that you know the original password if you use this syntax:
SQL> PASSWORD Changing password FOR PLSQL OLD password: NEW password: Retype NEW password: Password changed |
If you think that you’re safe walking away from an Oracle SQL*Plus console, check this because it doesn’t require knowing the current password either.
SQL> SHOW USER USER IS "PLSQL" SQL> ALTER USER plsql IDENTIFIED BY plsql; USER altered. |
Don’t walk away, always lock your terminal or use the QUIT
command to exit MySQL. This generally prevents somebody hijacking your password. You can check this post if you’re interested in changing the root
password when you don’t have it to begin with. Hope this helps a few folks.
Collection Aggregation
The longest outstanding question I’ve received is on how you can aggregate the number of rows in a nested table (one inside another). If you need a refresher on joining non-collection columns to nested columns, you should check this earlier blog post.
The basic premise is boils down to two points. One are you trying to get a count of the elements in the nested collection, or a count of the elements in the nested collection based on one or more columns in the nested table. There are two ways to perform a basic count of the nested line numbers, and one way to perform the other. You find out how to perform both in the post.
1. You can run the following script to create a sample data environment. It is re-runnable, and designed to run on either Oracle Database 10g or 11g.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | -- Conditionally delete objects in reverse dependency order. BEGIN FOR i IN (SELECT object_name , object_type FROM user_objects WHERE object_type IN ('TABLE','SEQUENCE','TYPE') AND object_name IN ('TELEPHONE_TYPE','TELEPHONE_TABLE' ,'CONTACT_BOOK','CONTACT_BOOK_S1') ORDER BY object_type, object_name DESC) LOOP EXECUTE IMMEDIATE 'DROP '||i.object_type||' '||i.object_name; END LOOP; END; / -- Create a composite data type (structure) and collection. CREATE TYPE telephone_type AS OBJECT ( country_code VARCHAR2(3) , prefix_code VARCHAR2(5) , phone_number VARCHAR2(9)); / CREATE TYPE telephone_table AS TABLE OF telephone_type; / -- Create a table with a nested composite table and sequence. CREATE TABLE contact_book ( contact_book_id NUMBER , first_name VARCHAR2(30) , last_name VARCHAR2(30) , telephone_numbers TELEPHONE_TABLE ) NESTED TABLE telephone_numbers STORE AS telephone_struct; CREATE SEQUENCE contact_book_s1; -- Insert two rows of data. INSERT INTO contact_book VALUES ( contact_book_s1.nextval ,'Harry','Potter' ,telephone_table(telephone_type('44','20','7299 2127') ,telephone_type('44','17','8926 8115') ,telephone_type('44','18','3344 5566') ,telephone_type('44','72','9878 5436'))); INSERT INTO contact_book VALUES ( contact_book_s1.nextval ,'Weasley','Ronald' ,telephone_table(telephone_type('40','021','407 47 46') ,telephone_type('44','19','4088 1062') ,telephone_type('44','21','4075 5066'))); |
2. You can query the line numbers of the nested table by the scalar columns two ways. The first uses the CARDINALITY
function introduced in Oracle 10g. The second leverages a CROSS JOIN
and traditional aggregation tools.
2(a). Using the CARDINALITY
function is generally the simplest and best approach to this problem:
1 2 3 4 | SELECT first_name , last_name , CARDINALITY(telephone_numbers) list FROM contact_book; |
This yield the following data set:
FIRST_NAME LAST_NAME LIST ------------ ------------ ------ Harry Potter 4 Weasley Ronald 3 |
2(b). Using the traditional CROSS JOIN
and GROUP BY
clause:
1 2 3 4 5 6 7 | SELECT cj.first_name , cj.last_name , COUNT(*) FROM (SELECT * FROM contact_book cb CROSS JOIN TABLE(cb.telephone_numbers)) cj GROUP BY cj.first_name , cj.last_name; |
This yield the following data set:
FIRST_NAME LAST_NAME LIST ------------ ------------ ------ Harry Potter 4 Ronald Weasley 3 |
3. If you wanted to know how many numbers where in the nested table by country code, you need to solve the problem through a CROSS JOIN
and GROUP BY
clause, like this:
1 2 3 4 5 6 7 8 9 | SELECT cj.first_name , cj.last_name , cj.country_code , COUNT(*) FROM (SELECT * FROM contact_book cb CROSS JOIN TABLE(cb.telephone_numbers)) cj GROUP BY cj.first_name , cj.last_name , cj.country_code; |
This yield the following data set:
FIRST_NAME LAST_NAME COUNTRY_CODE LIST ------------ ------------ ------------ ------ Harry Potter 44 4 Ronald Weasley 44 2 Ronald Weasley 40 1 |
If I captured the basics of the question, great. If there are more questions, please let me know.
Oracle LOB Storage Syntax
Somebody asked me to post some examples of Large Object syntax. I checked Chapter 9 of the Oracle Database SecureFiles and Large Object Developer’s Guide, and I agree an example or two would be helpful. I’ve provided CREATE
and ALTER
statements.
Here’s a sample CREATE TABLE
statement for a CLOB
, BLOB
, and BFILE
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | CREATE TABLE item ( item_id NUMBER CONSTRAINT pk_item PRIMARY KEY , item_barcode VARCHAR2(20) CONSTRAINT nn_item_1 NOT NULL , item_type NUMBER CONSTRAINT nn_item_2 NOT NULL , item_title VARCHAR2(60) CONSTRAINT nn_item_3 NOT NULL , item_subtitle VARCHAR2(60) , item_desc CLOB CONSTRAINT nn_item_4 NOT NULL , item_blob BLOB , item_photo BFILE , item_rating VARCHAR2(8) CONSTRAINT nn_item_5 NOT NULL , item_rating_agency VARCHAR2(4) CONSTRAINT nn_item_6 NOT NULL , item_release_date DATE CONSTRAINT nn_item_7 NOT NULL , created_by NUMBER CONSTRAINT nn_item_8 NOT NULL , creation_date DATE CONSTRAINT nn_item_9 NOT NULL , last_updated_by NUMBER CONSTRAINT nn_item_10 NOT NULL , last_update_date DATE CONSTRAINT nn_item_11 NOT NULL , CONSTRAINT fk_item_1 FOREIGN KEY(item_type) REFERENCES common_lookup(common_lookup_id) , CONSTRAINT fk_item_2 FOREIGN KEY(created_by) REFERENCES system_user(system_user_id) , CONSTRAINT fk_item_3 FOREIGN KEY(last_updated_by) REFERENCES system_user(system_user_id)) LOB (item_desc) STORE AS BASICFILE item_desc (TABLESPACE users ENABLE STORAGE IN ROW CHUNK 32768 PCTVERSION 10 NOCACHE LOGGING STORAGE (INITIAL 1048576 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645)) , LOB (item_blob) STORE AS item_blob (TABLESPACE users ENABLE STORAGE IN ROW CHUNK 32768 PCTVERSION 10 NOCACHE LOGGING STORAGE (INITIAL 1048576 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645)); |
If you attempt to make the CHUNK
size greater than 32,768, it will raise an ORA-22851: invalid CHUNK LOB storage option value
error.
Here’s a sample ALTER TABLE
statement that adds a new large object column to a table. You can’t modify an existing large object column. You raise an ORA-22859: invalid modification of columns
error when you try it. If you need to make modifications, check this post form last year on re-organizing LOB indexes.
1 2 3 4 5 6 7 8 | ALTER TABLE item ADD (another CLOB) LOB (another) STORE AS BASICFILE item_clob (TABLESPACE users ENABLE STORAGE IN ROW CHUNK 32768 PCTVERSION 10 NOCACHE LOGGING STORAGE (INITIAL 1048576 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645)); |
This all makes sense, but hold on there’s a potential problem when you add a large object column to a table. I’m not sure it’s a bug but I’ll probably open a TAR on it later in the week (arghhhh! I once worked in Oracle Support. :-)). Anyway, here’s what I found:
If you drop and recreate the table, you can encounter an ORA-01430
error. It appears that the large object is really hanging out in the catalog. As soon as you try to re-add it, you get the error.
If you describe the table after recreating it, you’ll see the following:
SQL> DESCRIBE item Name Null? Type ----------------------------------------- -------- --------------- ITEM_ID NOT NULL NUMBER ITEM_BARCODE NOT NULL VARCHAR2(20) ITEM_TYPE NOT NULL NUMBER ITEM_TITLE NOT NULL VARCHAR2(60) ITEM_SUBTITLE VARCHAR2(60) ITEM_DESC NOT NULL CLOB ITEM_BLOB BLOB ITEM_PHOTO BINARY FILE LOB ITEM_RATING NOT NULL VARCHAR2(8) ITEM_RATING_AGENCY NOT NULL VARCHAR2(4) ITEM_RELEASE_DATE NOT NULL DATE CREATED_BY NOT NULL NUMBER CREATION_DATE NOT NULL DATE LAST_UPDATED_BY NOT NULL NUMBER LAST_UPDATE_DATE NOT NULL DATE |
Also, if you run the following query with the DBMS_METADATA
package, the column doesn’t exist in the table defintion:
SELECT dbms_metadata.get_ddl('TABLE','ITEM') FROM dual; |
If try to run the ALTER
statement to add the column that doesn’t appear to exist, you’ll get the following message in Oracle 11.1.0.7.0:
ALTER TABLE item ADD (another CLOB) * ERROR at line 1: ORA-01430: COLUMN being added already EXISTS IN TABLE |
If you rerun the DBMS_METADATA.GET_DDL
query, it’ll be there in the table definition. Also, in its non-existence but existent state (ghost-state), there’s nothing in the DBA_LOB
, or DBA_SEGMENT
views. You can find a query to check large object segments and indexes here. My questions is where has it gone, why is it there, and did I do something wrong because this looks like basic functionality.
If you know the mystery or the existence of a bug, please post a comment.
Watch the Event Logs
It’s the end of our Spring term, and yes occasionally somebody can’t sign on to their Oracle instance because their event log is full. They get the following message on Winodws:
C:\>sqlplus / AS sysdba SQL*Plus: Release 11.1.0.7.0 - Production ON Wed Jul 15 10:19:37 2009 Copyright (c) 1982, 2008, Oracle. ALL rights reserved. Enter password: ERROR: ORA-28056: Writing audit records TO Windows Event Log failed |
The fix is simple, just delete your items from your Windows’ event log. 😉
Naughty Function Context
I was playing around with some external table filtering examples, and I stumbled on a restriction that I’d previously missed. You can’t filter external data with SQL functions, like REGEXP_LIKE
. Unfortunately, the limitation applies to equality and non-equality validation in combination with the AND
and OR
operators.
CREATE TABLE item_load ( item_title VARCHAR2(60) , item_subtitle VARCHAR2(60) , release_date DATE) ORGANIZATION EXTERNAL ( TYPE oracle_loader DEFAULT DIRECTORY upload_source ACCESS PARAMETERS ( RECORDS DELIMITED BY NEWLINE CHARACTERSET US7ASCII BADFILE 'UPLOAD_LOG':'item_load.bad' DISCARDFILE 'UPLOAD_LOG':'item_load.dis' LOGFILE 'UPLOAD_LOG':'item_load.log' LOAD WHEN (REGEXP_LIKE(item_title,'^Harry.')) FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY "'" MISSING FIELD VALUES ARE NULL ) LOCATION ('item_load.csv')) REJECT LIMIT UNLIMITED; |
It threw the following exception, which includes a new error message (at least for me), the KUP-01005
. I suppose that basically means you can’t use function calls inside external table access parameters but I couldn’t find it in the documentation. Chapter 13 (the lucky number) in the Oracle Database Utilities 11g manual only provides examples of equality and non-equality.
Here’s the raise exception for those using external tables:
SQL> SELECT * FROM item_load; SELECT * FROM item_load * ERROR at line 1: ORA-29913: error IN executing ODCIEXTTABLEOPEN callout ORA-29400: DATA cartridge error KUP-00554: error encountered while parsing access parameters KUP-01005: syntax error: found "(": expecting one OF: "equal, notequal" KUP-01007: at line 5 COLUMN 29 |
I couldn’t leave it alone, I tried the LIKE
operator and a jackpot – another new error message:
KUP-01008: the bad identifier was: LIKE |
It makes sense, it’s the 13th of July and two days before the new Harry Potter flick, therefore it must be Chapter 13 was calling to me. Hope this helps a few folks.
External Table Query Fix
The fact that you could raise an ugly error when you query an external table always bothered me. I looked at Java stored procedures as the best solution initially. That was overkill. This afternoon, while writing about them for the new PL/SQL Workboook, it became clear. The fix is really easy.
If you know little to nothing about external tables, you can go read this earlier post. Likewise, if you don’t know about objects and object collection, you can refer to this post. Having provided you with the context, here’s an example that eliminates errors when querying an external table without an external file.
- Create an external file, like this
character
table.
CREATE TABLE CHARACTER ( character_id NUMBER , first_name VARCHAR2(20) , last_name VARCHAR2(20)) ORGANIZATION EXTERNAL ( TYPE oracle_loader DEFAULT DIRECTORY download ACCESS PARAMETERS ( RECORDS DELIMITED BY NEWLINE CHARACTERSET US7ASCII BADFILE 'DOWNLOAD':'character.bad' DISCARDFILE 'DOWNLOAD':'character.dis' LOGFILE 'DOWNLOAD':'character.log' FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY "'" MISSING FIELD VALUES ARE NULL ) LOCATION ('character.csv')) REJECT LIMIT UNLIMITED; |
- Create a user-defined object type that mirrors your external table defintion, like this:
CREATE OR REPLACE TYPE character_obj IS OBJECT ( character_id NUMBER , first_name VARCHAR2(20) , last_name VARCHAR2(20)); / |
- Create a user-defined collection of your object type, like
CREATE OR REPLACE TYPE character_obj_table IS TABLE OF character_obj; / |
- Create a function that returns the user-defined collection of your object type, like
CREATE OR REPLACE FUNCTION character_source RETURN character_obj_table IS c NUMBER; collection CHARACTER_OBJ_TABLE := character_obj_table(); BEGIN FOR i IN (SELECT * FROM CHARACTER) LOOP collection.EXTEND; collection(c) := character_obj( i.character_id , i.first_name , i.last_name); c := c + 1; END LOOP; RETURN collection; EXCEPTION WHEN OTHERS THEN RETURN collection; END; / |
- Query the function not the table, which returns no rows found when the file doesn’t physically exist, or the file contains no data. Lastly, the function returns the data when it is there.
SELECT * FROM TABLE(character_source); |
Hope this helps those using external tables to avoid the typical error stack:
SELECT * FROM CHARACTER * ERROR at line 1: ORA-29913: error IN executing ODCIEXTTABLEOPEN callout ORA-29400: DATA cartridge error KUP-04040: file CHARACTER.csv IN CHARACTER NOT found |
I also wrote this older post about confirming it in the database catalog. If you any follow-up suggestions, please let me know.
Toad for MySQL Freeware
While SQL Developer is a nice tool and free, Toad is still an awesome development platform, albeit for Windows. In fact, it was annoying to have to install the Microsoft .NET Framework before installing it. It is free for MySQL!
Since my students have to do all their work in Oracle and then port it to MySQL, I demonstrate Quest’s Toad for MySQL’s at the end of the term. I don’t want them to leverage the automatic ERD diagramming while they’re learning how to do it.
There’s only one real trick to making automatic ERD diagramming work. That trick requires that you write your loading scripts for the Inno DB and use referential integrity constraints. My sample Video Store scripts for my database class are updated for MySQL referential integrity.
Unlike the friendly CASCADE CONSTRAINTS
clause you can use in Oracle, MySQL won’t let you create a re-runnable script with only DDL statements. Actually, the constraint comes from the InnoDB engine. You must issue a specialized InnoDB command before running your script:
11 12 13 | -- This enables dropping tables with foreign key dependencies. -- It is specific to the InnoDB Engine. SET FOREIGN_KEY_CHECKS = 0; |
Primary keys are a bit different from Oracle and it appears you can’t name them, at least I couldn’t see how to do it. Here’s an example of primary and foreign key constraints in MySQL. The primary key is inline and the foreign key constraints are out of line. This example from the downloadable scripts uses self referencing foreign key constraints.
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | CREATE TABLE system_user ( system_user_id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT , system_user_name CHAR(20) NOT NULL , system_user_group_id INT UNSIGNED NOT NULL , system_user_type INT UNSIGNED NOT NULL , first_name CHAR(20) , middle_name CHAR(20) , last_name CHAR(20) , created_by INT UNSIGNED NOT NULL , creation_date DATE NOT NULL , last_updated_by INT UNSIGNED NOT NULL , last_update_date DATE NOT NULL , KEY system_user_fk1 (created_by) , CONSTRAINT system_user_fk1 FOREIGN KEY (created_by) REFERENCES system_user (system_user_id) , KEY system_user_fk2 (last_updated_by) , CONSTRAINT system_user_fk2 FOREIGN KEY (last_updated_by) REFERENCES system_user (system_user_id) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; |
Once installed (instructions are here) and connected to the MySQL database, you simply click the ERD icon in the top panel and drag the tables onto the canvas. You’ll see something like this (by the way click on the image to see its full size):
Have fun with it. It’ll be interesting to see how Oracle positions MySQL when they own it. My hunch is that they’ll continue to sell it and provide it as an open source product.
PHP, LOBs, and Oracle
I finally got around to summarizing how to use PHP to store, retrieve, and display CLOBs and BLOBs from an Oracle database. I think too often we default to BFILEs. I put all the code in zip files with instructions and suggestions for locations. This is really the second entry that goes with configuring Zend Server Community Edition or the DEPRECATED Zend Core for Oracle.
If you’re new to PHP, check out the Underground PHP and Oracle book from Christopher Jones and Alison Holloway. It’s FREE!
The Oracle LOB Processing entry is in this blog page. I know it means another click, but I’ll probably add and modify it over time. If you’ve got time and interest, take a look and let me know what you think and what improvements you’d like to see. Thanks.
Oracle to MySQL dates
My students have to do their group labs in Oracle and then port them individually to MySQL. Most of the conversion resolves around dates because Oracle spoils us with their simplicity in their SQL dialect.
For example, if we wanted to add 9 days to today’s system date (June 27, 2009) we can do this in Oracle:
SQL> SELECT SYSDATE + 9 FROM dual; |
It prints
06-JUL-09 |
If we tried the equivalent in MySQL, we get a null because it treats any day value over 31 as a null. The maximum date in any month is 31, regardless of month. If you add more days than the maximum number minus your current date, you return a null. This is because adding a day never changes the month, and that yields invalid dates. In MySQL, you need to make this calculation with the adddate()
or date_add() functions because they’ll increment months and years.
By way of example, if we only added four to today’s date (June 27, 2009) and formatted the output as a date, it works
mysql> SELECT str_to_date(utc_date() + 4,'%Y%m%d'); |
We’d get this:
+--------------------------------------+ | str_to_date(utc_date() + 4,'%Y%m%d') | +--------------------------------------+ | 2009-06-31 | +--------------------------------------+ |
I always wanted an extra day in June. 😉
In short, there are three possible non-dates in February that format as dates, and one day in every thirty day month. I’ve a hunch this is a bug (I’ve logged a bug and they’ve verified it and moved it to triage).
The correct way to perform this calculation in MySQL is to use either the adddate()
or date_add()
functions. They take the same arguments. Here’s a quick example:
mysql> SELECT adddate(utc_date(),INTERVAL 9 DAY); |
It yields the correct date:
+------------------------------------+ | adddate(utc_date(),INTERVAL 9 DAY) | +------------------------------------+ | 2009-07-06 | +------------------------------------+ |
I know this answers a student email but I hope it helps a few others too.
Object constructor quirk
Never change something that works! Not really, but sometimes you feel that way. Especially, when you toast 5 to 10 minutes working through an undocumented behavior in PL/SQL. You’d think after writing it for 19 years, I’d have seen it all but not so.
I was working through a tried and true example from Chapter 14 of the Oracle Database 11g PL/SQL Programming book to prepare for teaching my class tomorrow, when I found this nice quirk. It took a few minutes to figure out what was happening, but here it is so you don’t have to look for it too. You can only use variable names that are attributes of the object type as formal parameters in object type constructors. If you try to vary it, you’ll trigger the following exception:
LINE/COL ERROR -------- ----------------------------------------------------------------- 4/11 PL/SQL: Item ignored 4/26 PLS-00307: too many declarations OF 'HELLO_THERE' MATCH this CALL 6/5 PL/SQL: Statement ignored 6/13 PLS-00320: the declaration OF the TYPE OF this expression IS incomplete OR malformed |
All I did to trigger this exception was change the who
variable to make it scope specific, like iv_who
for instance variable, pv_who
for parameter variable, and lv_who
for local variable.
Broken Code ↓
This shows you the broken code and explains why it’s broken.
The broken code has an object type like the following. Interestingly enough, the object type will compile fine but the object body fails when the attribute variable name differs from a constructor function parameter value.
1 2 3 4 5 6 7 8 9 10 11 12 | CREATE OR REPLACE TYPE hello_there IS OBJECT ( iv_who VARCHAR2(20) , CONSTRUCTOR FUNCTION hello_there RETURN SELF AS RESULT , CONSTRUCTOR FUNCTION hello_there ( pv_who VARCHAR2 ) RETURN SELF AS RESULT , MEMBER FUNCTION get_who RETURN VARCHAR2 , MEMBER PROCEDURE set_who (pv_who VARCHAR2) , MEMBER PROCEDURE to_string ) INSTANTIABLE NOT FINAL; / |
The broken code has an object body like the following. The difference between the parameter name in the constructor and the object type attribute name causes the PLS-00307
exception. It took some playing around to figure out what it was really complaining about.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | CREATE OR REPLACE TYPE BODY hello_there IS CONSTRUCTOR FUNCTION hello_there RETURN SELF AS RESULT IS hello HELLO_THERE := hello_there('Generic Object.'); BEGIN SELF := hello; RETURN; END hello_there; CONSTRUCTOR FUNCTION hello_there (pv_who VARCHAR2) RETURN SELF AS RESULT IS BEGIN SELF.iv_who := pv_who; RETURN; END hello_there; MEMBER FUNCTION get_who RETURN VARCHAR2 IS BEGIN RETURN SELF.iv_who; END get_who; MEMBER PROCEDURE set_who (pv_who VARCHAR2) IS BEGIN SELF.iv_who := pv_who; END set_who; MEMBER PROCEDURE to_string IS BEGIN dbms_output.put_line('Hello '||SELF.iv_who); END to_string; END; / |
Working Code ↓
This shows you the working code and explains why it works.
The working code has an object type like the following. You should notice that the only difference renames the pv_who
in the overriding constructor’s parameter list to iv_who
. While this doesn’t throw an exception creating the user-defined object type, it does throw an exception when you try to create the object body or implementation of the object type.
1 2 3 4 5 6 7 8 9 10 11 12 | CREATE OR REPLACE TYPE hello_there IS OBJECT ( iv_who VARCHAR2(20) , CONSTRUCTOR FUNCTION hello_there RETURN SELF AS RESULT , CONSTRUCTOR FUNCTION hello_there ( iv_who VARCHAR2 ) RETURN SELF AS RESULT , MEMBER FUNCTION get_who RETURN VARCHAR2 , MEMBER PROCEDURE set_who (pv_who VARCHAR2) , MEMBER PROCEDURE to_string ) INSTANTIABLE NOT FINAL; / |
The working code has an object body like the following. Like the object type before, the pv_who
as a formal parameter of the constructor now uses the same variable name as the attribute for the object type.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | CREATE OR REPLACE TYPE BODY hello_there IS CONSTRUCTOR FUNCTION hello_there RETURN SELF AS RESULT IS hello HELLO_THERE := hello_there('Generic Object.'); BEGIN SELF := hello; RETURN; END hello_there; CONSTRUCTOR FUNCTION hello_there (iv_who VARCHAR2) RETURN SELF AS RESULT IS BEGIN SELF.iv_who := iv_who; RETURN; END hello_there; MEMBER FUNCTION get_who RETURN VARCHAR2 IS BEGIN RETURN SELF.iv_who; END get_who; MEMBER PROCEDURE set_who (pv_who VARCHAR2) IS BEGIN SELF.iv_who := pv_who; END set_who; MEMBER PROCEDURE to_string IS BEGIN dbms_output.put_line('Hello '||SELF.iv_who); END to_string; END; / |
This is certainly interesting. I’ve no clue why such a limitation exists. The name of a parameter list member in the constructor should be independent from the attribute of a user object.