View · Index

Weblog

Filtered by popular tag oracle, 1 - 4 of 4 Postings (all, summary)

Fresh install of OpenACS 5.10 on Oracle 19c

Created by Gustaf Neumann, last modified by Gustaf Neumann 27 Nov 2023, at 05:48 PM

Here is a short summary of my steps to get the version of OpenACS from the branch oacs-5-10 installed on Oracle 19c. It seems there were since a while no fresh installations of OpenACS on Oracle, so several compatibility fixes were necessary in the oacs-5-10 branch to get things running. The branch oacs-5-10 will be released in the future as OpenACS 5.10.1.

Below are the steps to install Oracle on a virtual machine and to configure it such it works with OpenACS. There are probably many more ways to achieve similar tasks, but since the installation is not as trivial was with PostgreSQL, someone might find these notes useful. My installation was on macOS 11.6.3, and should work very similar on, e.g., Linux systems.

The currently recommended version by Oracle is 19c which is the Long Term Release with a support end date of April 30, 2027.

Installing a virtual machine with Oracle Linux and the Oracle Database

Preliminaries:

  • install vagrant
  • install VirtualBox
  • git clone Oracle's vagrant-projects
  • for installing Oracle 19c, change to vagrant-projects/OracleDatabase/19.3.0 and read the instructions. These instructions tell you to install the Oracle installation zip file for Linux in this directory. Without the proper Oracle database installation for your architecture, the "vagrant up" command below will fail. The Oracle version for private use is free to use.

Now, run in the vagrant-projects/OracleDatabase/19.3.0 directory the command

vagrant up

This command will install "Oracle Linux Server release 7.9" which is a flavor of Red Hat Enterprise Linux (RHEL) branded by Oracle. The command will run for a couple of minutes. Once the step has finished, you can use ssh to log into this machine

vagrant ssh

On this machine, you will find the Oracle server already running. To ease access to the database and the oracle utilities, add the following lines to the .bash_profile (of the vagrant user)

# User specific environment and startup programs
export ORACLE_HOME=/opt/oracle/product/19c/dbhome_1
PATH=$PATH:$HOME/.local/bin:$ORACLE_HOME/bin:$HOME/bin

To make the shared libraries easy accessible, add these to the standard paths (this will be needed e.g. for the compilation of the Oracle driver for NaviServer).

sudo sh -c "echo /opt/oracle/product/19c/dbhome_1/instantclient > /etc/ld.so.conf.d/oracle-instantclient.conf"
sudo sh -c "echo /opt/oracle/product/19c/dbhome_1/lib > /etc/ld.so.conf.d/oracle-libs.conf"
sudo ldconfig

You might also wish to change the password of the oracle database administrator IDs. We use here "oracle" as password, which is also used in later examples.

su - oracle -c "bash /home/oracle/setPassword.sh oracle"

Once $ORACLE_HOME/bin is on your path, you should be able to list the "status" and "services" provided by the Oracle database:

lsnrctl status
lsnrctl service

You will see there a container database "ORCLCDB" and a pluggable database "ORCLPDB1" (this is Oracle vocabulary). You should also be able to connect to the Oracle Database server via

sqlplus system/oracle@ORCLPDB1

Configuring an Oracle service for OpenACS

If this is working, Oracle and the VM are already functioning. Now we will create an own "service" for OpenACS from sqlplus:

exec dbms_service.CREATE_SERVICE('pdb1','openacs');  -- service_name and network_name
exec dbms_service.start_service('pdb1');

after leaving sqlplus, you should see the new database via "lsnrctl status". Now, you should be able to connect to the new service and save its state:

sqlplus system/oracle@localhost:1521/openacs
SQL> alter pluggable database save state;

Now, we will create an "openacs" user, a tablespace and grant this user the necessary permissions

CREATE TABLESPACE openacs  DATAFILE 'openacs_data.dbf' SIZE 100m;
CREATE USER openacs IDENTIFIED BY "openacs" DEFAULT TABLESPACE openacs TEMPORARY TABLESPACE temp;
GRANT CREATE SESSION TO openacs;
GRANT ALL PRIVILEGES TO openacs;
GRANT CTXAPP TO OPENACS;
GRANT EXECUTE ON CTXSYS.CTX_DDL TO OPENACS;

Note that the tablespace is set here to 100 MB, which is fine for simple setups for testing etc. But certainly, you might want to alter this value according to your needs. Note that the tablespace "openacs" is the default tablespace of the user "openacs".

Installing NaviServer and OpenACS on the virtual machine

The installation of NaviServer and OpenACS is done here based on the scripts from naviserver-openacs. Now, install git on the vm, get the installer via git, create the user "nsadmin", deactivate PostgreSQL from the default configuration, and install NaviServer with the module "nsoracle".

sudo yum install git
git clone https://github.com/gustafn/install-ns.git
cd install-ns
sudo useradd -g nsadmin nsadmin
sudo with_postgres=0 with_postgres_driver=0 ns_modules=nsoracle ORACLE_HOME=$ORACLE_HOME bash install-ns.sh build

Now, we install OpenACS from the CVS repository. This will OpenACS with oacs-5-10-compat flag set (the latest published version of the oacs-5-10 branch)

sudo bash install-oacs.sh build

Now get the updated configuration file, which has the right name of the driver, environment variables etc. preconfigured from GitHub

openacs-config.tcl

move it e.g. to /usr/local/ns/config-oacs-5-10-0-ora.tcl, and change "ipaddress" to "0.0.0.0", "server" to "oacs-5-10-0", and "database" to "oracle". Now you can start OpenACS in the foreground on the virtual machine using e.g.:

sudo /usr/local/ns/bin/nsd -u nsadmin -f -t /usr/local/ns/config-oacs-5-10-0-ora.tcl 2>&1

The server will be listening on [0.0.0.0]:8000. We are now essentially done. You should consider exporting port 8000 from the guest machine as e.g. port 9000 on the host by adding the line

  config.vm.network "forwarded_port", guest: 8000, host: 9000

in the Vagrantfile (in folder vagrant-projects/OracleDatabase/19.3.0) on the host, such that after a "vagrant halt" and "vagrant up" one can use a browser on the host to connect to OpenACS on the virtual machine.

Enjoy!

for beginning developers

Created by OpenACS community, last modified by Antonio Pisano 03 Nov 2023, at 11:55 AM

Tutorials for OpenACS development

Other related tutorials

See also: OpenACS Mentorship Program

Other useful resources

Web Sites

Books

New Interface for Calling Database functions

Created by Gustaf Neumann, last modified by Gustaf Neumann 10 Mar 2022, at 09:34 AM

Scheduled for OpenACS 5.10.1, there is a new interface available that provides several improvements over the current solutions:

The new interface ...

  • is significantly faster than the existing official OpenACS interface (package_exec_plsql) and easier to use by supporting a standard calling interface (not the special "var_list" which has to be assembled for package_exec_plsql)
  • is about the same performance as the "::xo::db::sql::*" interface without sharing its disadvantages (being a separate pattern, just one backend, ...)
  • is more secure (thorough argument checking on the Tcl and bind-vars level), and
  • more flexible (works with multiple databases and multiple driver types), and
  • much more feature-complete (e.g. support for functions returning tables, etc.)

For more details, check below. The plan is to replace the "::xo::db::sql::*" interface in a first step and replace calls to package_exec_plsql in the supported packages, and to mark the obsolete functions as deprecated. In the near future, more features of the xo::db interface will be added. The plain ugly old interface of OpenACS of calling db-functions where the highest priority.

Implemented features:

  • SQL function can be called for multiple database connection types (driver types nsdb and nsdbi, backend types PostgreSQL and Oracle) with less overhead (in essence, a value added, better performing replacement of "package_exec_plsql"). The  interface provided via xo::db supported just a single combination of the above.

  • Support for PostgreSQL and Oracle

  • Ability to talk to multiple databases from the same OpenACS instance. These databases can be

    • multiple databases of the same driver and backend  (e.g. multiple PostgreSQL databases)
    • multiple databases using different drivers (e.g. nsdb and nsdbi)
    • multiple databases with different backends vendors (e.g. PostgreSQL and Oracle).

    The selection of the backed happens of via the standard "-dbn" parameter. For the nsdb driver, one can use  e.g., "-dbn legacy", an example for nsdbi is "-dbn dbi1",  where the value provided via "-dbn" is passed for uniformity to the "-db" option of the nsdbi API.

  • Support for functions returning composite SQL types (SQL type "record" in PostgreSQL or "table" in Oracle). Results are returned as lists of lists.  This feature is implemented and tested for PostgreSQL connected via nsdb and nsdbi drivers and Oracle via nsdb.

  • SQL functions returning "void" can be called as well. In the Oracle cases, these are "procedures" which have to be called differently.

  • Additional (Tcl-level) argument checking is provided before calling into SQL for improved security and documentation. The API handles among other types the SQL types "integer" and "bigint".

  • Arguments of SQL functions are passed to the database via bind variables (implemented for PostgreSQL with nsdb and nsdbi). This is a security improvement over the previous  implementation in xotcl-core.

  • Standard default handling (argument default values like for all Tcl procs, although the way how defaults are handled is different in PostgreSQL and Oracle).

  • Optional arguments passed in as empty strings are treated as NULL values (standard behavior in OpenACS)

  • Dropped shortcomings of xo::db interface (naming and calling conflicts).

  • Independent of xotcl-core

 Possible further extensions:

  • Argument types could/should be displayed in the API browser (general feature request, not specific to the new DB interface)

Usage:

At startup, a single database interface is creates, which is named "::acs::dc", which takes the parameters from the default  setup of the OpenACS configuration.

 The following command creates a second database interface based on the "nsdbi" driver and define the interface stubs for the nsdbi driver.

::acs::db::require_dc -driver nsdbi -name ::acs::dc2
::acs::dc2 create_db_function_interface

For specifying a different backend, one could use e.g.:

::acs::db::require_dc -backend oracle -name ::acs::dc3

SQL query to a second DB (db pool called "legacy") via nsdb interface:

::acs::dc list_of_lists -dbn legacy get_list {select 1 from dual}

 Call database function from a second DB (db pool called "legacy") via nsdb driver:

::acs::dc call content_item get_latest_revision -dbn legacy -item_id 33357

 Call database function from a second DB via nsdbi driver named "dbi1"

::acs::dc2 call content_item get_latest_revision -dbn dbi1 -item_id 33357

 Return multiple records via nsdb

::acs::dc call content_item get_children -item_id 169303

 Return multiple records via nsdbi

::acs::dc2 call content_item get_children -item_id 169303

 Some calls with performance data (for ds/shell):

lappend _ [package_exec_plsql -var_list {{item_id 33357}} content_item get_latest_revision]
lappend _ [::xo::db::sql::content_item get_latest_revision -item_id 33357]
lappend _ [::acs::dc call content_item get_latest_revision -item_id 33357]
lappend _ [::acs::dc2 call content_item get_latest_revision -item_id 33357]

lappend _ [time {package_exec_plsql -var_list {{item_id 33357}} content_item get_latest_revision} 1000]
lappend _ [time {::xo::db::sql::content_item get_latest_revision -item_id 33357} 1000]
lappend _ [time {::acs::dc call content_item get_latest_revision -item_id 33357} 1000]
lappend _ [time {::acs::dc2 call content_item get_latest_revision -item_id 33357} 1000]
join $_ \n

The results as displayed by ds/shell

33358
33358
33358
33358
402.096544 microseconds per iteration
190.805835 microseconds per iteration
187.885927 microseconds per iteration
171.99336300000002 microseconds per iteration

 

::xo::db Object Relational Database Interface

Created by Gustaf Neumann, last modified by Antonio Pisano 26 Jun 2018, at 06:01 PM

The database abstraction layer of xotcl-core interfaces between the OpenACS object model used in the relational Database and XOTcl objects. The database abstraction layer works for PostgreSQL and Oracle in exact the same way.

The functionality of the database abstraction layer provides separate classes for operation on generic ACS objects and on objects stored in the OpenACS content repository. The generic lookup queries and instantiate methods are provided through the following classes

Generic operations on ACS Object IDs:

  • ::xo::db::Class get_object_type -id
  • ::xo::db::Class get_instance_from_db -id
  • ::xo::db::Class exists_in_db -id
  • ::xo::db::Class delete -id

Generic operations on ACS Object Types:

  • ::xo::db::Class get_table_name -object_type
  • ::xo::db::Class object_type_exists_in_db -object_type
  • ::xo::db::Class get_class_from_db -object_type
  • ::xo::db::Class drop_type -object_type -drop_table -cascade_p
  • ::xo::db::Class delete_all_acs_objects -object_type

Generic operations on items of the content repository:

  • ::xo::db::CrClass get_object_type -item_id -revision_id
  • ::xo::db::CrClass get_instance_from_db -item_id -revision_id
  • ::xo::db::CrClass lookup -item_id -revision_id
  • ::xo::db::CrClass delete -item_id

Methods on classes for ACS Object Types

  • <classname> new_persistent_object -package_id -creation_user -creation_ip ...
  • <classname> object_types -subtypes_first
  • <classname> create_object_type
  • <classname> drop_object_type -cascade
  • <classname> instance_select_query ...
  • <classname> get_instances_from_db ...

Methods on content repository classes

  • <classname> new_persistent_object -package_id -creation_user -creation_ip ...
  • <classname> create_object_type
  • <classname> drop_object_type
  • <classname> get_instance_from_db -item_id -revision_id
  • <classname> instance_select_query ...
  • <classname> get_instances_from_db ...
  • <classname> folder_type_unregister_all -include_subtypes -folderid register|unregister
  • <classname> folder_type_unregister_all -include_subtypes

Methods on instances of ordinary classes

  • <object> save -package_id -modifying_user
  • <object> save_new -package_id -creation_user -creation_ip
  • <object> delete

Methods on instances of content repository classes

  • <object> save -modifying_user -live_p
  • <object> save_new -package_id -creation_user -creation_ip -live_p
  • <object> set_live_revision -revision_id -publish_status
  • <object> delete

The following links provide a detailed description of these classes in the version as they are installed on openacs.org: ::xo::db::Class and ::xo::db::CrClass

The xotcl-core Database Abstraction Layer by Examples 

The remainder of this page provides a short tutorial into the basic functionality of the interface. A more detailed description can be found in the xotcl-core tutorial

############################################################
#
# 1) Create new ACS Objects, destroy it in memory, 
#    load it from the database, delete it in the database.
#
.. Create a plain new ACS object just for demo purposes.
.. The ACS object is created with a new object id.

>> set o [::xo::db::Object new_persistent_object]
=  ::7845

.. Show the contents of object ::7845 by serializing it:

>> ::7845 serialize
=  ::xo::db::Object create ::7845 -noinit \
    -set object_title {Object 7845} \
    -set object_id 7845 


.. In the next steps, we (a) get the object_id of the newly
.. created ACS object, (b) destroy the XOTcl object (the ACS
.. object is still in the database, (c) we recreate the
.. XOTcl object from the database, and (d) delete it in the 
.. database.

.. Step (a)
>> set o_id [::7845 object_id]
=  7845

#
# Delete object from memory: <object> destroy 
# Check, if an XOTcl object exists: ::xotcl::Object isobject <obj>
#
>> ::xotcl::Object isobject ::7845
=  1

.. Step (b)
>> ::7845 destroy

>> ::xotcl::Object isobject ::7845
=  0

#
# Load an object from the database: ::xo::db::Class get_instance_from_db -id <id>
#
.. Step (c)
>> set o [::xo::db::Class get_instance_from_db -id 7845]
=  ::7845

>> ::xotcl::Object isobject ::7845
=  1

.. Now, we have recreated the same object as before:

>> ::7845 serialize
=  ::xo::db::Object create ::7845 -noinit \
    -set object_title {Object 7845} \
    -set object_id 7845 


#
# Check, if an ACS object exists in the database: ::xo::db::Class exists_in_db -id <id>
# Delete object from memory and database: <object> delete 
#
>> ::xo::db::Class exists_in_db -id 7845
=  1

.. Step (d)
>> ::7845 delete

.. Now, we have deleted the ACS Object and the XOTcl object:

>> ::xo::db::Class exists_in_db -id 7845
=  0

>> ::xotcl::Object isobject ::7845
=  0


############################################################
#
# 2) Create new ACS Object Types, ACS Attributes and 
#    SQL Tables from XOTcl Classes with slot definitions.
#
.. We want to create a new ACS Object type and 
.. an XOTcl class named ::demo::Person.

.. Does the ACS Object type ::demo::Person exist in the database?
>> ::xo::db::Class object_type_exists_in_db -object_type ::demo::Person
=  0

.. The ACS Object Type should not exist (statement should return 0)

.. We create a new XOTcl Class '::demo::Person'.
.. By defining this class, the database layer takes care
.. of creating the ACS Object Type and the necessary table via SQL.

.. The persistent attributes (stored in the database) are defined
.. as slots of type ::xo::db::Attribute.

>> 
::xo::db::Class create ::demo::Person  \
   -superclass ::xo::db::Object  \
   -slots {
      ::xo::db::Attribute create name -column_name pname
      ::xo::db::Attribute create age -default 0 -datatype integer
      ::xo::db::Attribute create projects -default {} -multivalued true
    }

=  ::demo::Person

.. If the ACS Object Type and the ACS Attributes would be
.. already defined in the database, the class definition above 
.. would be a no-op operation.

.. Now, the ACS Object Type exists in the database

>> ::xo::db::Class object_type_exists_in_db -object_type ::demo::Person
=  1

.. The XOTcl class definition created automatically the 
.. following table for storing instances:

CREATE TABLE demo_person (
    age  integer DEFAULT '0' ,
    pname  text ,
    projects  text DEFAULT '' ,
    person_id  integer REFERENCES acs_objects(object_id) ON DELETE CASCADE
    CONSTRAINT demo_person_person_id_pk PRIMARY KEY 
)

.. SQL attribute slot names:
>> ::demo::Person array names db_slot
=  name age projects person_id

.. The XOTcl class definition created as well a 'save' and
.. an 'insert' method:

.. Created 'save' method:

::demo::Person instproc save {}  {
      db_transaction {
    next
    my instvar object_id name age projects
    db_dml dbqd..update_demo_person {update demo_person
      set pname = :name,age = :age,projects = :projects
          where person_id = :object_id
    }
      }
    }

.. Created 'insert' method:

::demo::Person instproc insert {}  {
      set __table_name [[self class] table_name]
      set __id [[self class] id_column]
      my set $__id [my set object_id]
      my log "ID insert in $__table_name, id = $__id = [my set $__id]"
      next
      foreach {__slot_name __slot} [[self class] array get db_slot] {
    my instvar $__slot_name
    if {[info exists $__slot_name]} { 
      lappend __vars $__slot_name
      lappend __atts [$__slot column_name]
    }
      }
      db_dml dbqd..insert_$__table_name "insert into $__table_name
        ([join $__atts ,]) values (:[join $__vars ,:])"
    }

.. The 'insert' method is typically called via
.. '<classname> new', which takes care about db_transaction (see below)

#
# Create a new instance of ::demo::Person with name 'Gustaf'
#
# The method 'new_persistent_object' of a database class (instance of ::xo::db::Class)
# creates an ACS Object with a fresh id in the database and 
# creates as well an XOTcl object in memory

>> set p [::demo::Person new_persistent_object -name Gustaf -age 105]
=  ::7846

::7846 created

.. check, if object ::7846 exists in memory
>> ::xotcl::Object isobject ::7846
=  1

.. check, if object ::7846 exists in the database
>> ::xo::db::Class exists_in_db -id 7846
=  1

.. Every persistent XOTcl object has an object_id.
>> ::7846 exists object_id
=  1

.. Show the contents of object ::7846 (using serialize)

>> ::7846 serialize
=  ::demo::Person create ::7846 -noinit \
    -set object_title {Person 7846} \
    -set name Gustaf \
    -set age 105 \
    -set projects {} \
    -set person_id 7846 \
    -set object_id 7846 


.. modify some attributes of the XOTcl object
>> ::7846 incr age
=  106

.. show modified contents of ::7846

::demo::Person create ::7846 -noinit \
    -set object_title {Person 7846} \
    -set name Gustaf \
    -set age 106 \
    -set projects {} \
    -set person_id 7846 \
    -set object_id 7846 

.. save the modified object data in the database
>> ::7846 save

>> set person_id [$p person_id]
=  7846

.. deleting xotcl object ::7846 in memory
>> $p destroy

.. check, if object ::7846 exists in memory
>> ::xotcl::Object isobject ::7846
=  0

.. check, if object ::7846 exists in the database
>> ::xo::db::Class exists_in_db -id 7846
=  1

.. fetch person again from database:

>> set p [::xo::db::Class get_instance_from_db -id 7846]
=  ::7846

.. check, if object ::7846 exists in memory
>> ::xotcl::Object isobject ::7846
=  1

::demo::Person create ::7846 -noinit \
    -set object_title {Person 7846} \
    -set name Gustaf \
    -set age 106 \
    -set projects {} \
    -set object_id 7846 \
    -set person_id 7846 


.. The object ::7846 was fetched from the database using the
.. automatically created query:

SELECT demo_person.pname AS name,demo_person.age,
   demo_person.projects,demo_person.person_id,
   acs_objects.title AS object_title,acs_objects.object_id
FROM demo_person,acs_objects
WHERE person_id = 7846 and object_id = person_id

.. In order to delete an object from the database, 
.. the method 'delete' can be used. 'delete' deletes the 
.. ACS object in the database as well the XOTcl object in memory.

>> set p_id [::7846 object_id]
=  7846

>> ::xo::db::Class exists_in_db -id 7846
=  1

>> ::7846 delete

>> ::xo::db::Class exists_in_db -id 7846
=  0

.. Instead of using 'new_persistent_object' to create
.. new acs_objects, one can use the method 'new' in 
.. combination with 'save_new'. While the method 'new' creates
.. an XOTcl object in memory, the method 'save_new' can be used
.. to create a new ACS object (with a new object_id) an to 
.. save this object persistently in the database.

>> set p [::demo::Person new -name Gustaf -age 105]
=  ::xotcl::__#A

>> ::xotcl::__#A exists object_id
=  0

>> ::xotcl::__#A save_new
=  7847

>> ::xotcl::__#A exists object_id
=  1


.. Now, we create a subclass of ::demo::Person called ::demo::Employee
.. which has a few more attributes. Again, we define an XOTcl class
.. ::demo::Employee which creates the ACS Object Type, the ACS
.. attributes and the table, if necessary.

>> 
::xo::db::Class create ::demo::Employee  \
   -superclass ::demo::Person  \
   -table_name demo_employee \
   -id_column employee_id  \
   -slots {
      ::xo::db::Attribute create salary -datatype integer
      ::xo::db::Attribute create dept_nr -datatype integer -default "0"
    }

=  ::demo::Employee


.. The XOTcl class definition created automatically the 
.. following table for storing instances:

CREATE TABLE demo_employee (
    dept_nr  integer DEFAULT '0' ,
    salary  integer ,
    employee_id  integer REFERENCES demo_person(person_id) ON DELETE CASCADE
    CONSTRAINT demo_employee_employee_id_pk PRIMARY KEY 
)

.. Create a first new employee:
>> set p1 [::demo::Employee new_persistent_object -name Neophytos -age 25]
=  ::7848

.. show contents of ::7848

::demo::Employee create ::7848 -noinit \
    -set employee_id 7848 \
    -set object_title {Employee 7848} \
    -set dept_nr 0 \
    -set name Neophytos \
    -set age 25 \
    -set projects {} \
    -set person_id 7848 \
    -set object_id 7848 

.. Create a second new employee:
>> set p2 [::demo::Employee new_persistent_object -name Lore -age 35 -salary 100000]
=  ::7849

.. show contents of ::7849

::demo::Employee create ::7849 -noinit \
    -set employee_id 7849 \
    -set object_title {Employee 7849} \
    -set salary 100000 \
    -set dept_nr 0 \
    -set name Lore \
    -set age 35 \
    -set projects {} \
    -set person_id 7849 \
    -set object_id 7849 

.. Create a third new employee:
>> set p3 [::demo::Employee new_persistent_object -name Nora -age 7 -salary 100]
=  ::7850

.. show contents of ::7850

::demo::Employee create ::7850 -noinit \
    -set employee_id 7850 \
    -set object_title {Employee 7850} \
    -set salary 100 \
    -set dept_nr 0 \
    -set name Nora \
    -set age 7 \
    -set projects {} \
    -set person_id 7850 \
    -set object_id 7850 

#
# Delete object from memory: <object> destroy 
#
>> ::xotcl::Object isobject ::7848
=  1

>> set p1_id [$p1 object_id]
=  7848

>> ::xo::db::Class exists_in_db -id 7848
=  1

>> $p1 destroy

>> ::xotcl::Object isobject ::7848
=  0

>> ::xo::db::Class exists_in_db -id 7848
=  1

#
# Delete object from memory and database: <object> delete 
#
>> ::xotcl::Object isobject ::7849
=  1

>> set p2_id [$p2 object_id]
=  7849

>> ::xo::db::Class exists_in_db -id 7849
=  1

>> $p2 delete

>> ::xotcl::Object isobject ::7849
=  0

>> ::xo::db::Class exists_in_db -id 7849
=  0

.. Fetch employee with id 7848 again from the database:

>> set p [::xo::db::Class get_instance_from_db -id 7848]
=  ::7848

.. show contents of ::7848

::demo::Employee create ::7848 -noinit \
    -set object_title {Employee 7848} \
    -set salary {} \
    -set employee_id 7848 \
    -set dept_nr 0 \
    -set name Neophytos \
    -set age 25 \
    -set projects {} \
    -set object_id 7848 \
    -set person_id 7848 

.. The object ::7848 was fetched from the database using the
.. automatically created query:

SELECT demo_employee.employee_id,demo_employee.salary,
   demo_employee.dept_nr, 
   demo_person.pname AS name,demo_person.age,
   demo_person.projects,demo_person.person_id,
   acs_objects.title AS object_title,acs_objects.object_id
FROM demo_employee,demo_person,acs_objects
WHERE employee_id = 7848 and person_id = employee_id 
   and object_id = employee_id

>> ::7848 destroy



.. Query the instances from the database

.. Instances are queried by default based on the following,
.. automatically generated SQL Query:

>> ::demo::Employee instance_select_query
=  SELECT demo_employee.employee_id,demo_employee.salary,
     demo_employee.dept_nr,
     demo_person.pname AS name,demo_person.age,
     demo_person.projects, demo_person.person_id,
     acs_objects.title AS object_title,acs_objects.object_id 
   FROM demo_employee,demo_person,acs_objects  
   WHERE person_id = employee_id and object_id = employee_id   

.. Query the object using this query into a set of objects.
>> set s [::demo::Employee get_instances_from_db]
=  ::xotcl::__#H

.. The result is an OrderedComposite. Children can be accessed
.. via the 'children' method:

>> llength [$s children]
=  2

.. serialize all children
::xotcl::Object create ::xotcl::__#I -noinit \
    -set __parent ::xotcl::__#H \
    -set object_title {Employee 7848} \
    -set dept_nr 0 \
    -set salary {} \
    -set employee_id 7848 \
    -set age 25 \
    -set name Neophytos \
    -set projects {} \
    -set object_id 7848 \
    -set person_id 7848 

::xotcl::Object create ::xotcl::__#J -noinit \
    -set __parent ::xotcl::__#H \
    -set object_title {Employee 7850} \
    -set dept_nr 0 \
    -set salary 100 \
    -set employee_id 7850 \
    -set age 7 \
    -set name Nora \
    -set projects {} \
    -set object_id 7850 \
    -set person_id 7850 

.. The instance_select_query can be configured in many ways.
.. Here we use the '-count true' option to return just the number
.. of solutions:

>> ::demo::Employee instance_select_query -count true
=  SELECT count(*) 
   FROM demo_employee,demo_person,acs_objects  
   WHERE person_id = employee_id and object_id = employee_id   

>> db_string cnt [::demo::Employee instance_select_query -count true]
=  2

.. The same queries for ::demo::Person instead of ::demo::Employee.
.. Note, that we get the person data of employees as well, since
.. employees are as well persons (i.e. a specialization of person):

>> ::demo::Person instance_select_query
=  SELECT demo_person.pname AS name,demo_person.age,
     demo_person.projects,demo_person.person_id,
     acs_objects.title AS object_title,acs_objects.object_id 
   FROM demo_person,acs_objects  
   WHERE object_id = person_id   

>> set s [::demo::Person get_instances_from_db]
=  ::xotcl::__#M

>> llength [$s children]
=  3

::xotcl::Object create ::xotcl::__#N -noinit \
    -set __parent ::xotcl::__#M \
    -set object_title {Person 7847} \
    -set age 105 \
    -set name Gustaf \
    -set projects {} \
    -set object_id 7847 \
    -set person_id 7847 

::xotcl::Object create ::xotcl::__#O -noinit \
    -set __parent ::xotcl::__#M \
    -set object_title {Employee 7848} \
    -set age 25 \
    -set name Neophytos \
    -set projects {} \
    -set object_id 7848 \
    -set person_id 7848 

::xotcl::Object create ::xotcl::__#P -noinit \
    -set __parent ::xotcl::__#M \
    -set object_title {Employee 7850} \
    -set age 7 \
    -set name Nora \
    -set projects {} \
    -set object_id 7850 \
    -set person_id 7850 

TODO: explain more options for instance_select_query and get_instances_from_db

############################################################
#
# 3) Create XOTcl classes from existing ACS Object Types
#    and ACS Attributes based on the definitions in the
#    database

>> set c [::xo::db::Class get_class_from_db -object_type party]
=  ::xo::db::party

.. XOTcl class ::xo::db::party created (superclass ::xo::db::Object)
.. SQL attributes:
>> ::xo::db::party array names db_slot
=  email party_id url

>> set c [::xo::db::Class get_class_from_db -object_type person]
=  ::xo::db::person

.. XOTcl class ::xo::db::person created (superclass ::xo::db::party)
.. SQL attributes:
>> ::xo::db::person array names db_slot
=  last_name first_names person_id

>> set c [::xo::db::Class get_class_from_db -object_type user]
=  ::xo::db::user

.. XOTcl class ::xo::db::user created (superclass ::xo::db::person)
.. SQL attributes:
>> ::xo::db::user array names db_slot
=  user_id

>> set c [::xo::db::Class get_class_from_db -object_type group]
=  ::xo::db::group

.. XOTcl class ::xo::db::group created (superclass ::xo::db::party)
.. SQL attributes:
>> ::xo::db::group array names db_slot
=  group_name group_id

.. Create XOTcl object for user_id = 0

.. please log in
>> set u [::xo::db::Class get_instance_from_db -id $myuser]
=  ::0

.. ::0 created, class ::xo::db::user
.. class hierarchy for ::0 ::xo::db::user ::xo::db::person ::xo::db::party ::xo::db::Object ::xotcl::Object
.. show contents of ::0

::xo::db::user create ::0 -noinit \
    -set object_title Unregistered Visitor \
    -set party_id 0 \
    -set email {} \
    -set last_name Visitor \
    -set object_id 0 \
    -set url {} \
    -set person_id 0 \
    -set first_names Unregistered \
    -set user_id 0 

.. The object ::0 was fetched from the database using the
.. automatically created query:

SELECT users.user_id,persons.last_name,persons.first_names,
  persons.person_id,parties.email,
  parties.party_id,parties.url,
  acs_objects.title AS object_title,acs_objects.object_id
FROM users,persons,parties,acs_objects
WHERE user_id = 0 and person_id = user_id and 
  party_id = user_id and object_id = user_id

.. Now we modify the user. To be on the safe side, we
.. a) save firstly the original URL of the current user, then
.. b) we set it to a new value and save the user information
..    to the database
.. c) delete the user object in memory
.. d) load it again from the database to see the modified data
.. e) reset it to the original value and save it again to restore
..    the original state

.. Step a:
>> set url [::0 url]

.. Step b:
>> ::0 url https://openacs.org/
=  https://openacs.org/

>> ::0 save

.. Step c:
>> ::0 destroy

.. Step d:
>> set u [::xo::db::Class get_instance_from_db -id $myuser]
=  ::0

.. show contents of ::0

::xo::db::user create ::0 -noinit \
    -set object_title Unregistered Visitor \
    -set party_id 0 \
    -set email {} \
    -set last_name Visitor \
    -set object_id 0 \
    -set url https://openacs.org" \
    -set person_id 0 \
    -set first_names Unregistered \
    -set user_id 0 

.. Step e:
>> ::0 url $url

>> ::0 save

>> ::0 destroy

>> set u [::xo::db::Class get_instance_from_db -id $myuser]
=  ::0

.. show contents of ::0

::xo::db::user create ::0 -noinit \
    -set object_title Unregistered Visitor \
    -set party_id 0 \
    -set email {} \
    -set last_name Visitor \
    -set object_id 0 \
    -set url {} \
    -set person_id 0 \
    -set first_names Unregistered \
    -set user_id 0 



############################################################
#
# 4) Use the generic Content Repository interface (using 
#    just cr_items and cr_revisions)
#

.. check, if cr_item with name 'testing' exists
>> set item_id [::xo::db::CrClass lookup -name testing]
=  7841

.. yes, it exists; delete it without instantiating
>> ::xo::db::CrItem delete -item_id 7841

.. create item
>> set i [::xo::db::CrItem new  -name "testing"  -title "Title of Hello World"  -text "Hello World"  ]
=  ::xotcl::__#a

>> $i save_new
=  7852

.. show contents of item

::xo::db::CrItem create ::xotcl::__#a -noinit \
    -set creation_date {2007-09-03 23:28:26.727511+02} \
    -set creation_user 0 \
    -set object_id 7852 \
    -set description {} \
    -set text {Hello World} \
    -set nls_language en_US \
    -set mime_type text/plain \
    -set name testing \
    -set publish_status ready \
    -set title {Title of Hello World} \
    -set revision_id 7851 \
    -set item_id 7852 \
    -set last_modified {2007-09-03 23:28:26.727511+02} \
    -set parent_id -100 

.. update content

>> ::xotcl::__#a append text {... more text.. more text.. more text.}
=  Hello World... more text.. more text.. more text.

>> ::xotcl::__#a append title { - v2}
=  Title of Hello World - v2

.. show modified contents of item

::xo::db::CrItem create ::xotcl::__#a -noinit \
    -set creation_date {2007-09-03 23:28:26.727511+02} \
    -set creation_user 0 \
    -set object_id 7852 \
    -set description {} \
    -set text {Hello World... more text.. more text.. more text.} \
    -set nls_language en_US \
    -set mime_type text/plain \
    -set name testing \
    -set publish_status ready \
    -set title {Title of Hello World - v2} \
    -set revision_id 7851 \
    -set item_id 7852 \
    -set last_modified {2007-09-03 23:28:26.727511+02} \
    -set parent_id -100 

>> ::xotcl::__#a save
=  7852

.. method 'save' creates a new revision; see changed revision_id below

::xo::db::CrItem create ::xotcl::__#a -noinit \
    -set creation_date {2007-09-03 23:28:26.727511+02} \
    -set creation_user 0 \
    -set object_id 7852 \
    -set description {} \
    -set text {Hello World... more text.. more text.. more text.} \
    -set nls_language en_US \
    -set mime_type text/plain \
    -set name testing \
    -set publish_status ready \
    -set title {Title of Hello World - v2} \
    -set revision_id 7853 \
    -set item_id 7852 \
    -set last_modified {2007-09-03 23:28:26.727511+02} \
    -set parent_id -100 

>> set item_id 7852
=  7852

>> ::xotcl::__#a destroy

>> set i [::xo::db::CrClass get_instance_from_db -item_id 7852]
=  ::7852

::xo::db::CrItem create ::7852 -noinit \
    -set object_type content_revision \
    -set creation_date {2007-09-03 23:28:26.727511+02} \
    -set object_id 7853 \
    -set creation_user 0 \
    -set description {} \
    -set text {Hello World... more text.. more text.. more text.} \
    -set nls_language en_US \
    -set package_id 492 \
    -set name testing \
    -set mime_type text/plain \
    -set publish_status ready \
    -set title {Title of Hello World - v2} \
    -set item_id 7852 \
    -set revision_id 7853 \
    -set last_modified {2007-09-03 23:28:26.750059+02} \
    -set parent_id -100 

############################################################
#
# 5) Create new application classes by sub-typing the 
#    Content Repository, adding additional attributes
#

.. We create a subclass of ::xo::db::CrItem called ::demo::Page
.. which has a few more attributes. Actually, this class is very
.. similar to ::xowiki::Page. Again, we define an XOTcl class
.. ::demo::Page which creates the ACS Object Type, the ACS
.. attributes and the table, if necessary.

>> 
  # content class very similar to ::xowiki::Page
  ::xo::db::CrClass create Page \
    -superclass ::xo::db::CrItem  \
    -pretty_name "demo Page"  \
    -mime_type text/html  \
    -slots {
        if {[::xo::db::has_ltree]} {
          ::xo::db::CrAttribute create page_order \
            -sqltype ltree -validator page_order \
            -default ""
        }
        ::xo::db::CrAttribute create creator \
            -column_name creator_string
      }

=  ::demo::Page

.. The sql_attribute_names for content items contain
.. the attributes of all supertypes:

>> ::demo::Page array names db_slot
=  creator page_order page_id object_id description text object_title nls_language mime_type name title item_id revision_id

>> ::demo::Page info slots
=  ::demo::Page::slot::creator ::demo::Page::slot::page_order ::demo::Page::slot::mime_type ::demo::Page::slot::page_id

>> ::xo::slotobjects ::demo::Page
=  ::demo::Page::slot::creator ::demo::Page::slot::page_order ::demo::Page::slot::mime_type ::demo::Page::slot::page_id ::xo::db::CrItem::slot::package_id ::xo::db::CrItem::slot::nls_language ::xo::db::CrItem::slot::description ::xo::db::CrItem::slot::name ::xo::db::CrItem::slot::text ::xo::db::CrItem::slot::publish_status ::xo::db::CrItem::slot::parent_id ::xo::db::CrItem::slot::title ::xo::db::CrItem::slot::revision_id ::xo::db::CrItem::slot::item_id ::xo::db::Object::slot::object_title ::xo::db::Object::slot::object_id

.. create page
>> set i [Page new  \
     -name "page0"  \
     -title "Joke of the Month" \
     -creator "GN" \
     -text "Three cannibals meet in a NYC subway station..."  ]
=  ::xotcl::__#j

>> $i save_new
=  7855

.. show contents of page object ::xotcl::__#j (including creator and page_order,
.. when ltree is available)

::demo::Page create ::xotcl::__#j -noinit \
    -set page_order {} \
    -set creator GN \
    -set page_id {} \
    -set creation_date {2007-09-03 23:28:29.149591+02} \
    -set object_id 7855 \
    -set creation_user 0 \
    -set description {} \
    -set text {Three cannibals meet in a NYC subway station...} \
    -set nls_language en_US \
    -set mime_type text/html \
    -set name page0 \
    -set publish_status ready \
    -set title {Joke of the Month} \
    -set revision_id 7854 \
    -set item_id 7855 \
    -set last_modified {2007-09-03 23:28:29.149591+02} \
    -set parent_id -100 

>> set item_id [::xotcl::__#j item_id]
=  7855

>> ::xotcl::__#j destroy

>> ::demo::Page get_instance_from_db -item_id 7855
=  ::7855

::demo::Page create ::7855 -noinit \
    -set page_order {} \
    -set object_type ::demo::Page \
    -set page_id 7854 \
    -set creation_date {2007-09-03 23:28:29.149591+02} \
    -set object_id 7854 \
    -set creation_user 0 \
    -set description {} \
    -set text {Three cannibals meet in a NYC subway station...} \
    -set nls_language en_US \
    -set package_id 492 \
    -set name page0 \
    -set mime_type text/html \
    -set title {Joke of the Month} \
    -set publish_status ready \
    -set revision_id 7854 \
    -set item_id 7855 \
    -set last_modified {2007-09-03 23:28:29.149591+02} \
    -set parent_id -100 \
    -set creator_string GN

previous March 2024
Sun Mon Tue Wed Thu Fri Sat
25 26 27 28 29 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 1 2 3 4 5 6

Popular tags

17 , 5.10 , 5.10.0 , 5.9.0 , 5.9.1 , ad_form , ADP , ajax , aolserver , asynchronous , bgdelivery , bootstrap , bugtracker , CentOS , COMET , compatibility , CSP , CSRF , cvs , debian , docker , docker-compose , emacs , engineering-standards , exec , fedora , FreeBSD , guidelines , host-node-map , hstore
No registered users in community xowiki
in last 30 minutes
Contributors

OpenACS.org