Oracle

Drupal reserved word conflict with Oracle

accesslog.pathaccesslog.pathansi
accesslog.timestampaccesslog.timestampansi
accesslog.uidaccesslog.user_idoci
actions.typeactions.typewell-know
aggregator_item.timestampaggregator_item.timestampansi
authmap.moduleauthmap.moduleansi
authmap.uidauthmap.user_idoci
batch.timestampbatch.timestampansi
block.moduleblock.moduleansi
block_role.moduleblock_role.moduleansi
blogapi_files.uidblogapi_files.user_idoci
cache.datacache.dataansi
cache_block.datacache_block.dataansi
cache_filter.datacache_filter.dataansi
cache_form.datacache_form.dataansi
cache_menu.datacache_menu.dataansi
cache_page.datacache_page.dataansi
cache_registry.datacache_registry.dataansi
cache_update.datacache_update.dataansi
comments.commentcomments.bodyoci, well-know
comments.timestampcomments.timestampansi
comments.uidcomments.user_idoci
files.timestampfiles.timestampansi
files.uidfiles.user_idoci
filters.modulefilters.moduleansi
flood.timestampflood.timestampansi
history.timestamphistory.timestampansi
history.uidhistory.user_idoci
languages.domainlanguages.domainansi
languages.languagelanguages.languageansi
locales_target.languagelocales_target.languageansi
locales_target.translationlocales_target.translationansi
menu_links.depthmenu_links.depthansi
menu_links.externalmenu_links.externalansi
menu_links.modulemenu_links.moduleansi
menu_router.pathmenu_router.pathansi
menu_router.positionmenu_router.positionansi
menu_router.typemenu_router.typewell-know
node.commentnode.comment_modeoci, well-know
node.languagenode.languageansi
node.translatenode.translateansi
node.typenode.typewell-know
node.uidnode.user_idoci
node_counter.timestampnode_counter.timestampansi
node_revisions.timestampnode_revisions.timestampansi
node_revisions.uidnode_revisions.user_idoci
node_type.typenode_type.typewell-know
poll_votes.uidpoll_votes.user_idoci
profile_fields.typeprofile_fields.typewell-know
profile_values.uidprofile_values.user_idoci
profile_values.valueprofile_values.valueansi, well-know
registry.moduleregistry.moduleansi
registry.typeregistry.typewell-know
roleroleansi
search_dataset.datasearch_dataset.dataansi
search_dataset.reindexsearch_dataset.reindexsqlite
search_dataset.typesearch_dataset.typewell-know
search_index.typesearch_index.typewell-know
search_node_links.typesearch_node_links.typewell-know
search_total.countsearch_total.countansi
sessions.sessionsessions.session_dataansi, oci
sessions.timestampsessions.timestampansi
sessions.uidsessions.user_idoci
simpletest.filesimpletest.filenameoci
simpletest.functionsimpletest.functionansi
systemsystemansi
system.typesystem.typewell-know
url_alias.languageurl_alias.languageansi
users.accessusers.last_accessoci
users.datausers.dataansi
users.languageusers.languageansi
users.uidusers.user_idoci
users_roles.uidusers_roles.user_idoci
variable.valuevariable.valueansi, well-know
vocabulary.modulevocabulary.moduleansi
vocabulary_node_types.typevocabulary_node_types.typewell-know
watchdog.timestampwatchdog.timestampansi
watchdog.typewatchdog.typewell-know
watchdog.uidwatchdog.user_idoci

Drupal 7.x Oracle driver development quick checklist

This is a quick checklist for develop Drupal 7.x Oracle driver. They need to be solved before Oracle slip into Drupal core.

Reserved word conflict (integrate with siren)

Drupal core use some reserved word of Oracle, e.g. uid. To solve this we have 2 choice: 1. escape all table/column/constrain name with escape characters (http://drupal.org/node/371#comment-636053), or 2. clone Moodle implementation and prevent the use of reserved words for all supported database (http://docs.moodle.org/en/XMLDB_reserved_words).

Here we choose to use solution 2. This come with some extra benefit that, if we need to connect Drupal's database from other projects, e.g. Moodle, they will able to fetch Drupal's column as there is no reserved word conflict. So this can improve both cross database and cross project compatibility.

Related issues:

VARCHAR2 is no large enough, use BLOB instead (integrate with siren)

This is another critical issue. Oracle only support VARCHAR2 with maximum 4000 characters. It is always not enough for CMS. As a replacement we can use BLOB because it is more universal.

We also need to clone PostgreSQL's BLOB handling for Oracle, with RETURNING.

Related issues:

Empty string is not allow in Oracle, and will translate as NULL (integrate with siren)

This is a critical bug of Oracle, all empty string will translate as NULL automatically. It is NO WAY for Oracle to emulate as other RDBMS because it is a 2-state machine (valid or NULL) but not 3-state (valid, empty or NULL). Logic Diagram:

|---------------------------------------------|
|         |     valid |     empty |      NULL |
|---------------------------------------------|
|  Oracle |   Support |        NA |   Support |
|---------------------------------------------|
| ANSI-92 |   Support |   Support |   Support |
|---------------------------------------------|

In order to overcome this limitation, we can first extend as 4 states: valid, "default", empty or NULL. Then restrict the use case as 3 states only, with replace the use of empty string as "default" value. Therefore both Oracle and other RDBMS can have identical support of SQL, and don't need to duel with different between = '' and IS NULL. Logic diagram:

|---------------------------------------------------------|
|         |     valid | "default" |     empty |      NULL |
|---------------------------------------------------------|
|  Oracle |   Support |   Support |        NA |   Support |
|---------------------------------------------------------|
| ANSI-92 |   Support |   Support |   Support |   Support |
|---------------------------------------------------------|

Procedure: 1. update all schema as nullable string if required (only allow null with valid default value but not empty string), 2. restrict all string I/O with no empty string (force translate as NULL), 3. debug and update programming logic for using empty string with "default" value if possible. Therefore:

  • Valid value: Keep untouch.
  • NULL: Act as default replacement of empty string if no programming logic break.
  • 'default': Use as exceptional replacement of empty string, work together with programming logic revamp.

P.S. This change will also benefit with the critical PostgrerSQL Blob bug with empty string (http://bugs.php.net/bug.php?id=41135), because we will no longer use empty string.

Related issues:

Auto upper case table and column name (integrate with siren)

This can be handle with PDO::ATTR_CASE (http://www.php.net/manual/en/pdo.setattribute.php). Becareful! Field names should be reserved word safe.

Related issues:

Max 30 characters for constraint name restriction (integrate with siren)

Oracle only allow 30 characters for constraint name. Moodle give a good example for solving this problem.

Related issues:

Some SQL functions need abstraction (integrate with siren)

Different database coming with different SQL syntax for same function, e.g. MySQL use SUBSTR, where PostgreSQL use SUBSTRING. Some simple abstraction is required. ADOdb give a good example for solving this problem.

Related issues:


dev note: update text fields as nullable and no default

Prepare schema:

  1. Check target fields (http://edin.no-ip.com/node/166).
    a. No default + NOT NULL => can't accept NULL so valid value only (1 status only)
    b. Default with '' => remove
  2. Update schema with "NOT NULL => FALSE, (remove default)".
  3. Running update.php. Ensure schema is valid.
  4. Run related simpletest.

Once schema ready for expert install:

  1. ORACLE_NULL => NULL
  2. Before insert: check '' => NULL (!?)
  3. Before normal variable binding: '' => NULL
  4. check SQL syntax, change from = '' to IS NULL

Testing procedure:

  1. expert mode -> patch -> update
  2. normal mode -> patch -> update
  3. normal mode + all modules -> patch -> update
  4. patch -> install -> all modules


Oracle basic test (Part 1)

Create testing table "test" as Drupal 7.x, with auto increment sequence + trigger (http://www.databaseanswers.org/sql_scripts/ora_sequence.htm):

CREATE TABLE "test" (
  "id" INT NOT NULL CHECK ("id" >= 0),
  "name" VARCHAR2(255) DEFAULT '' NOT NULL,
  "age" INT DEFAULT 0 NOT NULL CHECK ("age" >= 0),
  "job" VARCHAR2(255) DEFAULT 'Undefined' NOT NULL
);

CREATE SEQUENCE "test_id_seq" MINVALUE 1 INCREMENT BY 1 START WITH 1 NOCACHE NOORDER NOCYCLE;

CREATE OR REPLACE TRIGGER "test_id_tgr"
  BEFORE INSERT ON "test"
  FOR EACH ROW
  WHEN (NEW."id" IS NULL)
  BEGIN   
    SELECT "test_id_seq".NEXTVAL
    INTO :NEW."id"
    FROM DUAL;
  END;
/

Insert sample data:

INSERT INTO "test" ("name", "age", "job") VALUES ('John', 25, 'Singer');
INSERT INTO "test" ("name", "age", "job") VALUES ('George', 27, 'Singer');
INSERT INTO "test" ("name", "age", "job") VALUES ('Ringo', 28, 'Drummer');
INSERT INTO "test" ("name", "age", "job") VALUES ('Paul', 26, 'Songwriter');


Drupal 6.x + PDO_OCI: finally work together

After put down my multiple database backend research for Drupal among past 4 months, now finally get Drupal 6.x and PDO_OCI working together... It is all about my incorrect pdo_oci driver implementation... The progress is now available in here.

What is the bottleneck?

The main bottleneck of pdo_oci driver implementation is all about size of VARCHAR2 support under AL32UTF8. When working with AL32UTF8, as characters may store up to 2 or 3 bytes, the original maximum 4000 characters support of VARCHAR2 will cut off as 1333 (4000/3). If a single table coming with more than 1 column larger than 1333 characters, an annoying ORA-01461 error message MAY occur. This error message will not trigger when using oci8, but always happened with pdo_oci + Drupal 6.x (Siren 1.x)...

Any tries?

I have try some method to escape from this ORA-01461 error, including:

  1. Reduce most text field size mapping as VARCHAR2(1333), which only left text:big map with VARCHAR2(4000).
  2. Replace some fields as BLOB (e.g. fields storing serialized data) for unlimited storage size support, where restrict some other fields as varchar 256 or even smaller.

But above tries both face some critical error, and so need to rollback:

  1. Some fields (e.g. {menu_router}.access_arguments and {menu_router}.access_arguments) can't use VARCHAR2(1333) only, as they are storing serialized data; BTW, if mapping both to VARCHAR2(4000), ORA-01461 will occur.
  2. BLOB field need special handling (i.e drupal_write_record() and db_decode_blob()) which will complicate the I/O task, therefore not suitable for some special fields (e.g. again {menu_router}.access_arguments and {menu_router}.access_arguments). Since {menu_router} will finally store its rendered result in Drupal's cache system (already using BLOB field for storage), a DUPLICATED BLOB I/O handling result as a very fancy and buggy implementation.

Finally...

Well... Back to the main fantastic problem: as Drupal core hack are share among oci8 and pdo_oci version (backend independent), ~60% code and ~80% Oracle related programming logic are shared among oci8 and pdo_oci, where ~80% PDO related programming logic are also shared among pdo_pgsql and pdo_oci, I switch my focus to debug PHP's pdo_oci driver and my Drupal + pdo_oci driver implementation.

So, what is the problem? Christopher Jones on OPAL give me some hints: I have a wrong transaction handling in case of pdo_oci driver implementation. When working with Oracle + BLOB (or even not with BLOB) a transaction is always required, plus a try {} catch (Exception $e) {} in further more operation. Within my old implementation (trimmed version, share and clone between legacy and other PDO driver):

<?php
  $active_db
->beginTransaction();
 
// Execute the statement.
 
$result = $stmt->execute();
  if (
$result === FALSE) {
   
// Some error handling code.
   
$active_db->rollBack();
    return
FALSE;
  }
 
// Some PDO + BLOB stream I/O handling code.
  // On success, commit the transaction.
 
$active_db->commit();
?>

The correct implementation should be:

<?php
  $active_db
->beginTransaction();
  try {
   
// Execute the statement.
   
$result = $stmt->execute();
   
// Some PDO + BLOB stream I/O handling code.
    // On success, commit the transaction.
   
$active_db->commit();
  }
  catch (
Exception $e) {
   
// Some error handling code.
   
$active_db->rollBack();
    return
FALSE;
  }
?>

Conclusion

Even the research progress is functioning with Drupal 6.x, a lot of effort is still required. As Drupal 7.x will completely switch to PDO, where core database drivers (including MySQL and PostgreSQL) implementation are revamped with OOP, I still need to catch up my missing section among the past 4 months.

The patch for next generation is now RTBC, should I wait for its commit and propose my amendment afterward, or keep hard working and propose the amendment before everything shuttle down?


Still fighting with annoying ORA-01461 + pdo_oci...

As like as case before, Siren + pdo_oci is still buggy with ORA-01461 during installation. Well, since pdo_oci doesn't update since 3 years ago (http://pecl.php.net/package/PDO_OCI), for sure that the case will not have any change...

Anyway, this page give a very detail idea of the problem (http://littledan.itpub.net/post/6400/270539). When checking with script provided in document, I found that some table is now buggy with the problem:

If you make sure that there is only 1 LONG and no VARCHAR > 1333 bytes, OR just 1 VARCHAR > 1333 bytes in the table, you cannot hit this problem. The following query will give you all the tables with such a combination of columns:

SELECT * FROM
(SELECT TABLE_NAME, OWNER, count(*) NUM
FROM DBA_TAB_COLUMNS
WHERE DATA_TYPE='LONG'
OR (( DATA_TYPE='VARCHAR2'
or DATA_TYPE='CHAR'
or DATA_TYPE='NVARCHAR2'
or DATA_TYPE='NCHAR')
AND DATA_LENGTH > 1333)
AND OWNER NOT IN
('SYS','SYSTEM','SH','OLAPSYS','MDSYS','WKSYS','ODM','XDB','WMSYS')
GROUP BY TABLE_NAME, OWNER)
WHERE NUM > 1;

Both tables watchdog, menu_router and node_type need some modification. The solution maybe a bit simple (from other point of view, very complicated): change those text:big field into blob. This will require for both blob checking for INSERT/UPDATE and after SELECT before use.

Update: the above idea is not work, since sometime fields are using for GROUP BY where BLOB don't support for. As an alternative, I am now trying to restrict most text fields as max 1333, where only text:big as 4000. Therefore some data size limitation to normal storage, and also need to carefully specify which ONE field should be text:big. Seem more simple in solution, but need time to verify the impact...

Giving a try right now...


100% CPU Usage Overhead running EM DBConsole 11g on Debian sid

My Oracle 11gR1 gone crazy with 100% CPU loading when working with Debian sid. This page provide a functional solution:

Shutdown OEM, login as SYSMAN user and restart the provisioning daemon by executing the two packaged procedures

SYSMAN> execute MGMT_PAF_UTL.STOP_DAEMON

PL/SQL procedure successfully completed.

SYSMAN> execute MGMT_PAF_UTL.START_DAEMON

PL/SQL procedure successfully completed.

Start OEM again and the problem is gone.

WARNING: You must take this on your own responsibility!

So here is my detail procedures. For shutdown OEM (login as user oracle where Oracle 11gR1 installed in /u01/app/oracle/product/11.1.0/db_1/bin):

su - oracle
cd /u01/app/oracle/product/11.1.0/db_1/bin
./emctl stop dbconsole

Login as SYSMAN:

./sqlplus SYSMAN

Then execute the following PL/SQL commands:

SQL> execute MGMT_PAF_UTL.STOP_DAEMON
SQL> execute MGMT_PAF_UTL.START_DAEMON

Restart OEM:

./emctl start dbconsole

Check the CPU loading and it should be fine now :-)


Apache2.2 + PHP5.2 + OCI8/PDO_OCI from sketch on Debian sid HOWTO

This simple HOWTO will guide you about how to setup Apache2.2 + PHP5.2 + OCI8/PDO_OCI from sketch. Compile all package from tarball can give you the maximum flexibility of functionality, e.g. you can enable both mysql/mysqli/pgsql/oci8/pdo_mysql/pdo_pgsql/pdo_oci within single installation.

Before start, I will assume you have Debian sid and Oracle 11gR1 installed correctly, which will not detail within this document. If you really need some help for that, please refer to my other article for more indeed guideline.


Oracle Database 11g Release 1 on Debian sid HOWTO

This is a simple HOWTO, for installing Oracle Database 11g Release 1 on Debian sid.

But why we need this HOWTO? Since Oracle only officially support Red Hat Enterprise Linux, SUSE Enterprise Linux and other enterprise level Linux distribution; BTW, there shouldn't be any problem if you hope to use with other distribution, which just need some special configuration and install required dependent packages. On the other hand, some dependent packages are obsoleted in Debian etch, but only available in oldstable or sid, so install with sid should be better than etch + hybrid sid packages.

"Hey man! Please stop for a while! I have no idea about how to working with Debian sid!"... Ooops... If this is your case, my other article should be your cup of tea. Go back now and let's enjoy with that for a bit while :-)

Before start you will need to prepare your Debian with:

  • A complete Debian sid installation with X11.
  • Static public IP address (or you may bind to localhost during installation).
  • Functional hostname, e.g. hardcode it within /etc/hostname and /etc/hosts.


An annoying ORA-01461 error from pdo_oci...

When I am working with pdo_oci development of Siren 1.x (Drupal 6.x), I face a critical problem: pdo_oci is not able to complete the Drupal installation procedure. BTW, as oci8 now share around 90% similar programming logic as that of pdo_oci, it works fine...

Since oci8 and pdo_oci drivers are assumed as interchangeable (they share both common.oracle.inc and schema.oracle.inc, so ~60% codes are share), I try to install with oci8 in order to get the DB up, then further more use pdo_oci during running. It looks all fine (and even pass ab test), but face a critical problem when accessing admin/build/modules (here is one of the error message, the others are similar):

user warning: OCIStmtExecute: ORA-01461: can bind a LONG value only for insert into a LONG column (/usr/local/src/php-5.2.5/ext/pdo_oci/oci_statement.c:146) query: INSERT INTO "menu_router" ("path", "load_functions", "to_arg_functions", "access_callback", "access_arguments", "page_callback", "page_arguments", "fit", "number_parts", "tab_parent", "tab_root", "title", "title_callback", "title_arguments", "type", "block_callback", "description", "position", "weight", "file") VALUES (:l5d0bafc60, :l5d0bafc61, :l5d0bafc62, :l5d0bafc63, :l5d0bafc64, :l5d0bafc65, :l5d0bafc66, :l5d0bafc67, :l5d0bafc68, :l5d0bafc69, :l5d0bafc610, :l5d0bafc611, :l5d0bafc612, :l5d0bafc613, :l5d0bafc614, :l5d0bafc615, :l5d0bafc616, :l5d0bafc617, :l5d0bafc618, :l5d0bafc619) in /mnt/hswong3i/project/drupal/drupal-6.x-siren-1.x/includes/common.oracle.inc on line 194.

When pressing F5, the case become even worse:

Fatal error: Uncaught exception 'PDOException' with message 'There is already an active transaction' in /mnt/hswong3i/project/drupal/drupal-6.x-siren-1.x/includes/database.pdo_oci.inc:182 Stack trace: #0 /mnt/hswong3i/project/drupal/drupal-6.x-siren-1.x/includes/database.pdo_oci.inc(182): PDO->beginTransaction() #1 /mnt/hswong3i/project/drupal/drupal-6.x-siren-1.x/includes/database.pdo_oci.inc(113): _db_query(Array) #2 /mnt/hswong3i/project/drupal/drupal-6.x-siren-1.x/includes/common.oracle.inc(194): db_query('INSERT INTO [{w...', Array) #3 /mnt/hswong3i/project/drupal/drupal-6.x-siren-1.x/includes/common.inc(3292): db_query_insert('watchdog', Array) #4 /mnt/hswong3i/project/drupal/drupal-6.x-siren-1.x/modules/dblog/dblog.module(130): drupal_write_record('watchdog', Object(stdClass)) #5 [internal function]: dblog_watchdog(Array) #6 /mnt/hswong3i/project/drupal/drupal-6.x-siren-1.x/includes/module.inc(450): call_user_func_array('dblog_watchdog', Array) #7 /mnt/hswong3i/project/drupal/drupal-6.x-siren-1.x/includes/bootstrap in /mnt/hswong3i/project/drupal/drupal-6.x-siren-1.x/includes/database.pdo_oci.inc on line 182

Fatal error: Exception thrown without a stack frame in Unknown on line 0

I guess the first ORA-01461 error is all due to pdo_oci mishandling on VARCHAR2(4000). When you search in google, you may found that similar errors were happened once before in JDBC, on 10gR2 and etc... As DB can setup successfully by oci8, there shouldn't be the bug of DB configuration nor programming logic. I will try to report this later ;-(

P.S. the interchangeable of drivers can also apply to MySQL's (mysql, mysqli and pdo_mysql) and PostgreSQL's (pgsql and pdo_pgsql). They are both functioning correctly ;-)


Syndicate content