MINI MINI MANI MO

Path : /opt/oracle/product/18c/dbhomeXE/md/admin/
File Upload :
Current File : //opt/oracle/product/18c/dbhomeXE/md/admin/sdortprt.sql

Rem
Rem $Header: sdo/admin/sdortprt.sql /main/63 2017/10/20 11:01:24 rjanders Exp $
Rem
Rem sdortprt.sql
Rem
Rem Copyright (c) 2004, 2017, Oracle and/or its affiliates. 
Rem All rights reserved.
Rem
Rem    NAME
Rem      sdortprt.sql - SDO Router Partition
Rem
Rem    DESCRIPTION
Rem      Partitions the road network data for the Oracle Router
Rem
Rem    NOTES
Rem
Rem    BEGIN SQL_FILE_METADATA 
Rem    SQL_SOURCE_FILE: sdo/admin/sdortprt.sql 
Rem    SQL_SHIPPED_FILE: md/admin/sdortprt.sql 
Rem    SQL_PHASE: SDORTPRT
Rem    SQL_STARTUP_MODE: NORMAL 
Rem    SQL_IGNORABLE_ERRORS: NONE 
Rem    SQL_CALLING_FILE: sdo/admin/catmdsdop3.sql 
Rem    END SQL_FILE_METADATA
Rem
Rem    MODIFIED   (MM/DD/YY)
Rem    rjanders    10/08/17 - Add MDSYS to spatial type
Rem    rjanders    06/30/17 - Add SYS/MDSYS prefixes
Rem    begeorge 06/26/17 - Add link level info in get_geometry_info (geom simplification)
Rem    rjanders    05/10/17 - #26037683: Raise 'when other' exceptions
Rem    rjanders    04/13/17 - #25814260: Allow quoted bumpy-case usernames
Rem    rjanders 03/23/17 - #25437999: Remove 'when others then NULL' handlers
Rem    mhorhamm 03/09/17 - Move is_simple_file_name to sdo_utl
Rem    begeorge 02/16/17 - bug 25513512
Rem    sravada  01/11/17 - bug 25370681
Rem    cfreiwal 04/08/16 - timezone raw data table name change
Rem    cfreiwal 01/08/16 - fix typo in elocation_partition_router
Rem    sravada  12/18/15 - factor out JVM dependency
Rem    cfreiwal 10/02/15 - rename router_edge_data to router_timezone_data
Rem    jcwang   09/09/15 - add SQL file metadata
Rem    cfreiwal 01/20/15 - add border user data
Rem    rjanders 03/18/15 - #20725259: Long identifier project
Rem    cfreiwal 01/21/15 - Fix bug 20394061: control parallelism on ctas
Rem    rjanders 01/02/15 - long identifier project phase 1
Rem    cfreiwal 06/26/14 - Fix bug 19072255: create trucking user data wrapper
Rem    cfreiwal 06/11/14 - add edge user data partitioning
Rem    cfreiwal 02/24/14 - Bug 18299483: path error in create dir
Rem    bgouslin 02/14/14 - Fix typo at line one per Siva.
Rem    cfreiwal 09/24/13 - change network default name
Rem    rjanders 01/29/14 - #18146131: Use NUMBER_TO_CHAR() for TO_CHAR()
Rem    rjanders 09/16/13 - OCCS: Remove hardtabs
Rem    jcwang   05/01/13 - fix min_eigenvector degenerate cases
Rem    cfreiwal 04/09/13 - Fix bug 13391393: cleanup node_tmp on failure
Rem    rjanders 03/15/13 - #16473696: Start/End _ORACLE_SCRIPT initialization
Rem    cfreiwal 11/30/12 - Fix bug 15942114: remove index check
Rem    cfreiwal 08/23/12 - Add memory sizing parameter to partitioning
Rem    cfreiwal 06/14/12 - Fix bug 14164507: secure directory name
Rem    cfreiwal 04/03/12 - add create_sdo_router_log_dir
Rem    sravada  03/16/12 - error handle drop table sql
Rem    rjanders 03/14/12 - Change upper() to nls_upper() [security]
Rem    cfreiwal 02/22/12 - Fix bug 13770406, make securefile use optional
Rem    cfreiwal 10/12/10 - Add restricted driving maneuvers
Rem    cfreiwal 11/14/11 - XbranchMerge cfreiwal_bug-13364731
Rem    cfreiwal 11/10/11 - XbranchMerge cfreiwal_bug-12957042_11.2.0.4.0
Rem    cfreiwal 09/08/11 - Fix bug 12957042, increase heap size to 1.5G
Rem    cfreiwal 06/17/11 - Backport cfreiwal_bug-10113265 from main
Rem    cfreiwal 05/26/11 - XbranchMerge cfreiwal_bug-12572284 from
Rem                        st_sdo_11.2.0: functional index on edge
Rem    cfreiwal 05/24/11 - Fix bug 12572284, functional index on edge
Rem    cfreiwal 09/27/10 - Fix primary/foreign key problem on node_tmp
Rem    cfreiwal 05/17/10 - XbranchMerge cfreiwal_bug-9720595 from st_sdo_11.2.0
Rem    cfreiwal 05/17/10 - Fix bug 9720595, view name protection
Rem    cfreiwal 12/01/09 - fix bug 9167221 (partitioning performance)
Rem    cfreiwal 11/02/09 - Fix bug 9077960, Router view naming
Rem    sravada  02/11/09 - change to SYS.DBMS_ASSERT
Rem    cfreiwal 01/05/09 - Adjust VM for partition validation
Rem    cfreiwal 12/05/08 - Add trucking data
Rem    cfreiwal 10/29/08 - Add views and metadata for NDM
Rem    cfreiwal 02/12/08 - Fix bug 6762742, Router versioning
Rem    cfreiwal 08/16/07 - add partition validation
Rem    cfreiwal 07/19/07 - Bug 6159004
Rem    cfreiwal 05/11/07 - Fix bug 5856048
Rem    cfreiwal 05/09/07 - fix bug 5940583
Rem    cfreiwal 04/25/07 - Bug 5989050: fix driving directions
Rem    cfreiwal 02/05/07 - Add turn restrictions
Rem    jcwang   09/05/06 - add table_type to hand tables and views
Rem    jcwang   09/04/06 - allow initial partition table to be a view 
Rem    jcwang   08/30/06 - add set_log_info and log_message without date information
Rem    cfreiwal 10/08/05 - reconcile with router_partition_pkg.sql 
Rem    cfreiwal 04/18/05 - Improve get geometry performance 
Rem    sravada  07/30/04 - add create/replace 
Rem    sravada  06/17/04 - sravada_fix_route_partitioning
Rem    sravada  06/15/04 - fix route partitioning (rename, create syn, 
Rem                        and grant public privs and install under mdsys. 
Rem    cfreiwal    06/10/04 - cfreiwal_router_partition_package
Rem    cfreiwal    06/02/04 - create partition table 
Rem    cfreiwal    05/25/04 - Created
Rem

Rem ********************************************************************
Rem #16473696: Indicate Oracle-Supplied object
@@?/rdbms/admin/sqlsessstart.sql
Rem ********************************************************************

-- Type to represent a Vector 2D
--

CREATE or REPLACE TYPE vector_2d as OBJECT ( x NUMBER, y NUMBER);
/
grant execute on vector_2d to public;


CREATE or REPLACE TYPE NUM_ARRAY as VARRAY(100) of NUMBER;
/
grant execute on NUM_ARRAY to public;


CREATE or REPLACE TYPE string_array AS VARRAY(1048576) OF VARCHAR2(256);
/
grant execute on string_array to public;


SHOW ERRORS;

--
-- Function used to build a link level functional index
--
CREATE or REPLACE FUNCTION elocation_edge_link_level(func_class in number)
  RETURN NUMBER  DETERMINISTIC   
IS
BEGIN
   RETURN floor((8-FUNC_CLASS)/3);
END;
/

SHOW ERRORS;

-- Wrapper to expand the memory available in the Java user heap
--
CREATE OR REPLACE PROCEDURE ElocationSetJVMHeapSize(sz NUMBER)  IS
BEGIN
    SDO_JAVA_STP.ElocationSetJVMHeapSize(sz);
END;
/

SHOW ERRORS;

-- Drop and recreate the temporary tables needed for partitioning
--
declare
  table_not_found exception;
  pragma exception_init(table_not_found, -00942);
begin
  begin
    execute immediate 'DROP TABLE final_partition';
    exception
      when table_not_found then NULL;
      when others then
        SYS.DBMS_SYSTEM.KSDWRT(SYS.DBMS_SYSTEM.TRACE_FILE,
                               'EXCEPTION[sdortprt.sql(' || $$PLSQL_LINE || ')1]: ' || SQLERRM); RAISE;
  end;

  begin
    execute immediate 'DROP TABLE partition_tmp_2';
    exception
      when table_not_found then NULL;
      when others then
        SYS.DBMS_SYSTEM.KSDWRT(SYS.DBMS_SYSTEM.TRACE_FILE,
                               'EXCEPTION[sdortprt.sql(' || $$PLSQL_LINE || ')2]: ' || SQLERRM); RAISE;
  end;

  begin
    execute immediate 'DROP TABLE partition_tmp_3';
    exception
      when table_not_found then NULL;
      when others then
        SYS.DBMS_SYSTEM.KSDWRT(SYS.DBMS_SYSTEM.TRACE_FILE,
                             'EXCEPTION[sdortprt.sql(' || $$PLSQL_LINE || ')3]: ' || SQLERRM); RAISE;
  end;
end;
/



CREATE TABLE final_partition(vertex_id NUMBER, p_id NUMBER, 
                              x NUMBER, y NUMBER);

CREATE TABLE partition_tmp_2(vertex_id NUMBER, p_id NUMBER, 
                              x NUMBER, y NUMBER, m NUMBER);

CREATE TABLE partition_tmp_3(vertex_id NUMBER, p_id NUMBER, 
                              x NUMBER, y NUMBER, m NUMBER);


-- Create the partitioning package
--
CREATE OR REPLACE PACKAGE MDSYS.SDO_ROUTER_PARTITION AUTHID current_user AS
  FUNCTION adjust_m(start_m IN NUMBER, end_m IN NUMBER, m IN NUMBER)
    RETURN NUMBER;
  
  FUNCTION get_pid(m IN NUMBER, pid IN NUMBER)
    RETURN NUMBER;

  FUNCTION min_eigenvector(sum_x2 IN NUMBER, sum_y2 IN NUMBER, sum_xy IN NUMBER)
    RETURN mdsys.vector_2d;

  PROCEDURE cleanup_router(all_tables IN BOOLEAN DEFAULT TRUE);

  PROCEDURE create_sdo_router_log_dir(
             router_schema    IN VARCHAR2,
             new_dir_path     IN VARCHAR2 DEFAULT NULL);

  PROCEDURE partition_router(log_file_name IN VARCHAR2 := 'sdo_router_partition.log', 
                        max_v_no IN NUMBER DEFAULT 10000,
                        driving_side IN VARCHAR2 := 'R',
                        network_name IN VARCHAR := 'ROUTER_NETWORK', 
                        max_memory IN NUMBER := 1.75, 
                        cleanup IN BOOLEAN DEFAULT TRUE, 
                        use_securefiles IN BOOLEAN DEFAULT TRUE,
                        generate_11g_restrictions IN BOOLEAN DEFAULT TRUE);

  PROCEDURE validate_sdo_router_log_dir(log_file_name IN VARCHAR2 := 'sdo_router_partition.log');

  PROCEDURE create_border_data(
                      log_file_name IN VARCHAR2 := 'sdo_router_partition.log',
                      cleanup IN BOOLEAN DEFAULT TRUE);

  PROCEDURE create_timezone_data(
                      log_file_name IN VARCHAR2 := 'sdo_router_partition.log',
                      cleanup IN BOOLEAN DEFAULT TRUE);

  PROCEDURE create_trucking_data(
                      log_file_name IN VARCHAR2 := 'sdo_router_partition.log',
                      cleanup IN BOOLEAN DEFAULT TRUE);

  PROCEDURE create_turn_restriction_data(
                      log_file_name IN VARCHAR2 := 'sdo_router_partition.log',
                      cleanup IN BOOLEAN DEFAULT TRUE);
 
  PROCEDURE dump_partitions(log_file_name IN VARCHAR2 := 'sdo_router_partition.log', 
                      start_pid IN NUMBER DEFAULT 0, 
                      end_pid IN NUMBER DEFAULT -1,
                      verbose IN BOOLEAN DEFAULT FALSE);

  PROCEDURE dump_border_data(log_file_name IN VARCHAR2 := 'sdo_router_partition.log',
                      start_pid IN NUMBER DEFAULT 0,
                      end_pid IN NUMBER DEFAULT -1);

  PROCEDURE dump_timezone_data(log_file_name IN VARCHAR2 := 'sdo_router_partition.log', 
                      start_pid IN NUMBER DEFAULT 0, 
                      end_pid IN NUMBER DEFAULT -1);

  PROCEDURE dump_trucking_data(log_file_name IN VARCHAR2 := 'sdo_router_partition.log', 
                      start_pid IN NUMBER DEFAULT 0, 
                      end_pid IN NUMBER DEFAULT -1,
                      skip_unsupported IN BOOLEAN DEFAULT TRUE);

  PROCEDURE dump_turn_restriction_data(log_file_name IN VARCHAR2 := 'sdo_router_partition.log', 
                      start_pid IN NUMBER DEFAULT 0,
                      end_pid IN NUMBER DEFAULT -1,
                      dump_soft_restrictions IN BOOLEAN DEFAULT FALSE);

  PROCEDURE validate_partitions(log_file_name IN VARCHAR2 := 'sdo_router_partition.log', 
                      start_pid IN NUMBER DEFAULT 0, 
                      end_pid IN NUMBER DEFAULT -1,
                      verbose IN BOOLEAN DEFAULT FALSE);

   PROCEDURE gather_table_stats(table_name IN VARCHAR2);

   PROCEDURE get_version(log_file_name IN VARCHAR2 := 'sdo_router_partition.log');

  PROCEDURE create_router_network(log_file_name IN VARCHAR2 := 'sdo_router_partition.log',
                                  network_name  IN VARCHAR2 := 'ROUTER_NETWORK');

  PROCEDURE delete_router_network(log_file_name IN VARCHAR2 := 'sdo_router_partition.log',
                                  network_name  IN VARCHAR2 := 'ROUTER_NETWORK',
                                  log_delete IN BOOLEAN := TRUE);

  PROCEDURE elocation_partition_router (logfile_name  IN VARCHAR2,
                                        blob_format IN VARCHAR2) ;
    
  PROCEDURE elocation_border_data (logfile_name IN VARCHAR2);

  PROCEDURE elocation_timezone_data (logfile_name IN VARCHAR2) ;

  PROCEDURE elocation_trucking_data (logfile_name IN VARCHAR2) ;

  PROCEDURE elocation_turn_restrict_data (logfile_name IN VARCHAR2) ;

  PROCEDURE elocation_dump_partition (logfile_name in VARCHAR2, 
                                      start_pid in NUMBER, 
                                      end_pid in NUMBER, 
                                      verbose in BOOLEAN,
                                      is10g in BOOLEAN) ;

  PROCEDURE elocation_dump_border_data (logfile_name IN VARCHAR2, 
                                  start_pid IN NUMBER, 
                                  end_pid IN NUMBER) ;

  PROCEDURE elocation_dump_timezone_data (logfile_name IN VARCHAR2,
                                         start_pid IN NUMBER,
                                         end_pid IN NUMBER) ;

  PROCEDURE elocation_dump_trucking_data (logfile_name IN VARCHAR2, 
                                         start_pid IN NUMBER, 
                                         end_pid IN NUMBER,
                                         skip_unsupported IN BOOLEAN) ;

  -- PLSQL limit, full name elocation_dump_turn_restrict_data
  PROCEDURE elocation_dump_turn_restrict (logfile_name IN VARCHAR2, 
                                         start_pid IN NUMBER, 
                                         end_pid IN NUMBER, 
                                         dump_soft_restrictions IN BOOLEAN) ;

  PROCEDURE elocation_validate_partition (logfile_name in VARCHAR2, 
                                          start_pid in NUMBER, 
                                          end_pid in NUMBER, 
                                          verbose in BOOLEAN,
                                          is10g in BOOLEAN) ;

  PROCEDURE build_turn_restrictions (logdir in VARCHAR2, 
                                     drivingside in VARCHAR2) ;

  PROCEDURE elocation_validate_logfile(logfile_name  IN VARCHAR2,
                                     schema_name IN VARCHAR2) ;

    FUNCTION get_edge_info(edge_ids       IN  MDSYS.sdo_list_type,
                         to_edge_ids    OUT MDSYS.sdo_list_type,
                         rets           OUT mdsys.string_array,
                         angle_segments OUT MDSYS.sdo_list_type)
  RETURN mdsys.string_array ;


  FUNCTION get_geometry_info(edge_ids       IN  MDSYS.sdo_list_type,
                             merged_coords  OUT MDSYS.sdo_list_type)
  RETURN NUMBER;

  FUNCTION get_geometry_info_link_lvl(edge_ids       IN  MDSYS.sdo_list_type,
                                      merged_coords  OUT MDSYS.sdo_list_type)
  RETURN NUMBER;

END SDO_ROUTER_PARTITION;
/

--#
CREATE OR REPLACE PACKAGE BODY MDSYS.SDO_ROUTER_PARTITION AS

-- Partitioning log file
  part_log_file   utl_file.file_type := NULL;
  schema_name     VARCHAR2(260) := NULL;
  JAVA_ERROR      EXCEPTION;
  PARAMETER_ERROR EXCEPTION;

--
-- Logging
--

--- Log a message
PROCEDURE log_message(message IN VARCHAR2, show_time IN BOOLEAN DEFAULT TRUE)
IS
BEGIN

  if  ( utl_file.is_open(part_log_file) = FALSE )  then
     return;
  end if;
  IF ( show_time ) THEN
    utl_file.put_line (part_log_file, sdo_util.number_to_char(sysdate,'Dy fmMon DD HH24:MI:SS YYYY'));
  END IF;

  utl_file.put_line (part_log_file, message);
  utl_file.fflush(part_log_file);

EXCEPTION
  WHEN OTHERS THEN
    raise_application_error(-20002, 'PLSQL Error Writing Log File');
END log_message;

--- Open the log file
FUNCTION open_log_file(log_file_name IN VARCHAR2)
RETURN VARCHAR2 AS
  full_file_name  VARCHAR2(256);
  stmt         VARCHAR2(256);
BEGIN
  BEGIN
    if (sdo_util.is_simple_file_name(log_file_name) = 'FALSE') then
          raise_application_error(-20020, '[ERROR] bad file name');
    end if;
    -- Open the routers partition log file

    IF (NOT utl_file.is_open(part_log_file)) THEN
      part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
    END IF;
     
    stmt := 'SELECT directory_path FROM SYS.all_directories 
             WHERE directory_name=''SDO_ROUTER_LOG_DIR''';
    EXECUTE IMMEDIATE stmt into full_file_name;

    -- Make sure the path is terminated with a /
    if (instr(full_file_name, '/', length(full_file_name)) = 0)
    then full_file_name := full_file_name || '/';
    end if;

    full_file_name := full_file_name || log_file_name;

  EXCEPTION
    WHEN utl_file.invalid_path THEN
      raise_application_error(-20006, '[ERROR] SDO_ROUTER_LOG_DIR directory not found');
    WHEN utl_file.invalid_operation THEN
      raise_application_error(-20006, '[ERROR] bad directory path specified for SDO_ROUTER_LOG_DIR directory');
    WHEN OTHERS THEN
      raise_application_error(-20001, '[ERROR] could not open log file');
  END;
  
  RETURN full_file_name;
END open_log_file;

--- Create the SDO Router log directory
PROCEDURE create_sdo_router_log_dir(
             router_schema    IN VARCHAR2,
             new_dir_path     IN VARCHAR2 DEFAULT NULL)
IS
  current_schema VARCHAR2(130) := sys_context('userenv', 'CURRENT_SCHEMA');
  directory_path VARCHAR2(256) := NULL;
  row_count      NUMBER := 0;
  stmt           VARCHAR2(256); 
BEGIN
  -- Make sure current user isn't one of the two being granted privs
  if (current_schema = sdo_util.get_quoted_name(router_schema) OR current_schema = 'MDSYS')
  then raise_application_error(-20010, '[ERROR] you may not GRANT privileges to yourself');
  end if;

  -- Make sure 2 schemas get access to SDO_ROUTER_LOG_DIR
  if (sdo_util.get_quoted_name(router_schema) = 'MDSYS')
  then raise_application_error(-20014, '[ERROR] MDSYS schema specified, please use a Routeserver schema');
  end if;
  
  -- Make sure the Routeserver schema specified actually exists
  SELECT COUNT(*) INTO row_count
  FROM sys.all_users
  WHERE username = sdo_util.get_quoted_name(SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(router_schema));

  if (row_count = 0)
  then raise_application_error(-20011, '[ERROR] schema does not exist');
  end if;

  if (new_dir_path is NULL)
  then
    SELECT COUNT(*) INTO row_count
    FROM SYS.all_directories
    WHERE directory_name = 'SDO_ROUTER_LOG_DIR';

    -- If SDO_ROUTER_LOG_DIR does not already exist the new_dir_path parameter
    -- must be specified. Raise an error if the directory does not exist and
    -- the parameter was not specified.
    if (row_count = 0)
    then raise_application_error(-20012, '[ERROR] SDO_ROUTER_LOG_DIR directory does not exist');
    end if;

    SELECT directory_path INTO directory_path
    FROM SYS.all_directories
    WHERE directory_name = 'SDO_ROUTER_LOG_DIR';

    directory_path := replace(directory_path, '\', '/');
  else 
    directory_path := replace(new_dir_path, '\', '/');

    -- Be sure the path is not relative. The first character or 
    -- the first character following the : (Windows) must be /
    -- if / is not the first character, the : must exist and 
    -- not be the very first character
    if (SUBSTR(directory_path, 1, 1) != '/') AND
       (INSTR(directory_path, ':') < 2 OR 
        SUBSTR(directory_path, INSTR(directory_path, ':')+1, 1) != '/')
    then raise_application_error(-20013, '[ERROR] malformed directory path specified');
    end if;
  end if; 

  -- Make sure the path is terminated with a /
  if (instr(directory_path, '/', length(directory_path)) = 0)
  then directory_path := directory_path || '/';
  end if;

  -- Create the directory
  stmt := 'CREATE OR REPLACE DIRECTORY sdo_router_log_dir as '|| 
                SYS.DBMS_ASSERT.ENQUOTE_LITERAL(directory_path);
  EXECUTE IMMEDIATE stmt;

  directory_path := directory_path || '*';

  -- Grant privileges to the directory to the Router user and MDSYS
  stmt := 'GRANT read, write ON DIRECTORY sdo_router_log_dir TO ' || sdo_util.get_quoted_name(SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(router_schema));
  EXECUTE IMMEDIATE stmt;
  EXECUTE IMMEDIATE ' begin 
  dbms_java.grant_permission(
    sdo_util.get_quoted_name(SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(:router_schema)), ''java.io.FilePermission'',
    :directory_path, ''read,write''); end; '
     using router_schema, directory_path;
     
  EXECUTE IMMEDIATE 'GRANT read, write ON DIRECTORY sdo_router_log_dir TO MDSYS';

  EXECUTE IMMEDIATE ' begin 
  dbms_java.grant_permission(
    ''MDSYS'', ''java.io.FilePermission'',
    :directory_path, ''read,write''); end; ' 
      using  directory_path;

END create_sdo_router_log_dir;

PROCEDURE validate_sdo_router_log_dir(log_file_name IN VARCHAR2 := 'sdo_router_partition.log')
IS
  full_file_name VARCHAR2(256);
  schema_name    VARCHAR2(130):= sys_context('userenv', 'CURRENT_SCHEMA');
BEGIN
  -- From PLSQL
  full_file_name := open_log_file(log_file_name);
  log_message('[INFO] PLSQL logging OK (' || schema_name || ')');
  utl_file.fclose(part_log_file);

  -- From Java
  elocation_validate_logfile(full_file_name, schema_name);
  
END validate_sdo_router_log_dir;

--
-- check if the given constraint exists
--
FUNCTION constraint_exists(constraint_name IN VARCHAR2)
  RETURN VARCHAR2
IS
  stmt  VARCHAR2(256);
  no    NUMBER := 0;
BEGIN
  stmt := 'SELECT COUNT(*) FROM user_constraints WHERE CONSTRAINT_NAME = :name';
  EXECUTE IMMEDIATE stmt into no using nls_upper(constraint_name);
  IF (no = 1) THEN
    RETURN 'TRUE';
  ELSE
    RETURN 'FALSE';
  END IF;    
END constraint_exists;

--
-- check if the given index exists
--
FUNCTION index_exists(index_name IN VARCHAR2)
  RETURN VARCHAR2
IS
  stmt  VARCHAR2(256);
  no    NUMBER := 0;
BEGIN
  stmt := 'SELECT COUNT(*) FROM IND WHERE INDEX_NAME = :name';
  EXECUTE IMMEDIATE stmt into no using nls_upper(index_name);
  IF (no = 1) THEN
    RETURN 'TRUE';
  ELSE
    RETURN 'FALSE';
  END IF;    
END index_exists;

--
-- check if the given network exists
--
FUNCTION network_exists(network_name IN VARCHAR2)
  RETURN VARCHAR2
IS
  md_stmt  VARCHAR2(256);
  ud_stmt  VARCHAR2(256);
  no    NUMBER := 0;
BEGIN
  md_stmt := 'SELECT COUNT(*) FROM MDSYS.USER_SDO_NETWORK_METADATA WHERE NETWORK = :name';
  EXECUTE IMMEDIATE md_stmt into no using nls_upper(network_name);
  IF (no = 1) THEN
    ud_stmt := 'SELECT COUNT(*) FROM MDSYS.USER_SDO_NETWORK_USER_DATA WHERE NETWORK = :name';
    EXECUTE IMMEDIATE ud_stmt into no using nls_upper(network_name);
        IF (no > 0) THEN
      RETURN 'TRUE';
    END IF;
  END IF;    

  RETURN 'FALSE';
END network_exists;

--
-- check if the given table exists
--
FUNCTION table_exists(tab_name IN VARCHAR2)
  RETURN VARCHAR2
IS
  stmt  VARCHAR2(256);
  no    NUMBER := 0;
BEGIN
  stmt := 'SELECT COUNT(*) FROM TAB WHERE TNAME = :name';
  EXECUTE IMMEDIATE stmt into no using nls_upper(tab_name);
  IF (no = 1) THEN
    RETURN 'TRUE';
  ELSE
    RETURN 'FALSE';
  END IF;    
END table_exists;

--
-- disk based graph partition functions/procedures
-- based on moment of inertia appraoch
--
FUNCTION min_eigenvector(sum_x2 IN NUMBER, sum_y2 IN NUMBER, sum_xy IN NUMBER)
  RETURN mdsys.vector_2d                                            
IS
  lamda     NUMBER := 0;
  tmp_sum   NUMBER := 0;
  k         NUMBER := 0;
  eigenvector_1 NUMBER := 0;
  eigenvector_2 NUMBER := 0;
BEGIN
  tmp_sum := sum_x2 + sum_y2;
  lamda := (tmp_sum - 
    sqrt(tmp_sum*tmp_sum -4.0*(sum_x2*sum_y2-sum_xy*sum_xy)))/2;

  IF (sum_xy = 0 or (sum_x2-lamda) = 0 ) THEN
    IF (sum_x2 > sum_y2) THEN
      eigenvector_1 := 0;
      eigenvector_2 := 1.0;
    ELSE
      eigenvector_1 := 1.0;
      eigenvector_2 := 0;
    END IF;
  ELSE
    k := -sum_xy/(sum_x2-lamda);
    eigenvector_2 := 1.0/sqrt(k*k+1.0);
    eigenvector_1 := k*eigenvector_2;
  END IF;

  RETURN mdsys.vector_2d(eigenvector_1,eigenvector_2);
  
END min_eigenvector;

--
-- Return a negative value if the value falls in the given range (start_m,end_m)
--
FUNCTION adjust_m(start_m IN NUMBER, end_m IN NUMBER, m IN NUMBER)
  RETURN NUMBER
IS
BEGIN
  IF (m >= start_m AND m <= end_m) THEN
    RETURN -m;
  ELSE
    RETURN m;
  END IF;      
END adjust_m;

--
-- return the p_id based on the given m value (m > 0) ? pid : (pid+1)
--
FUNCTION get_pid(m IN NUMBER, pid IN NUMBER)
  RETURN NUMBER
IS
BEGIN
  IF ( m < 0 ) THEN
    RETURN pid;
  ELSE
    RETURN pid+1;
  END IF;  
      
END get_pid;

--
--
--
PROCEDURE adjust_final_pid(p_tab_name IN VARCHAR2)
IS
  min_pid NUMBER;
  stmt VARCHAR2(256);
BEGIN

  EXECUTE IMMEDIATE 'truncate table ' || 
      SYS.DBMS_ASSERT.ENQUOTE_NAME(p_tab_name);

  stmt := 'SELECT MIN(P_ID) FROM final_partition';
  EXECUTE IMMEDIATE stmt into min_pid ;

  stmt := 'INSERT /*+ APPEND */ into ' || 
    SYS.DBMS_ASSERT.ENQUOTE_NAME(p_tab_name) || ' (vertex_id,p_id,x,y) ' || 
    ' select vertex_id, (p_id-:min_pid+1),x,y from final_partition';
  EXECUTE IMMEDIATE stmt USING min_pid;

  COMMIT;  
END adjust_final_pid;

--
-- move vertices along the principal axes to make two partition with equal size 
--
PROCEDURE make_partition_equal(tab_name IN VARCHAR2, 
                                pid IN NUMBER, 
                                v_no IN NUMBER, 
                                part_counter IN NUMBER)
IS
  no          INTEGER;
  vno1        NUMBER;
  vno2        NUMBER;
  stmt        VARCHAR2(256);
  part_m      NUMBER;
BEGIN
  part_m := 0;

  -- two partitions for partition pid is based on the m sign (<0 or >= 0 )
  stmt := 'SELECT COUNT(*) FROM ' || 
    SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(tab_name) || ' WHERE m < 0 ';
  EXECUTE IMMEDIATE stmt into vno1;

  vno2 := v_no - vno1;

  IF (vno1 > vno2) THEN 
    -- move n vertices from set (m < 0) to set (m>= 0) 
    -- by flipping its sign of m
    no := (vno1-vno2)/2;
    stmt := 'SELECT min(m) FROM (SELECT m FROM ' || 
      SYS.DBMS_ASSERT.ENQUOTE_NAME(tab_name) || 
        ' WHERE m < 0 ORDER BY m DESC)  ' || ' WHERE rownum <= :no ';
    EXECUTE IMMEDIATE stmt into part_m USING no;

    INSERT /*+ APPEND */ INTO partition_tmp_3 (vertex_id,p_id,x,y,m)
      SELECT vertex_id,
          mdsys.SDO_ROUTER_PARTITION.get_pid(
            mdsys.SDO_ROUTER_PARTITION.adjust_m(part_m,0,m),part_counter),
          x,y,mdsys.SDO_ROUTER_PARTITION.adjust_m(part_m,0,m)
        FROM partition_tmp_2;
  ELSE
    -- move n vertices from set (m >= 0) to set ( m < 0) by 
    -- updating pid in vertex table
    no := (vno2-vno1)/2;
    stmt := 'SELECT max(m) FROM (SELECT m FROM ' || 
      SYS.DBMS_ASSERT.ENQUOTE_NAME(tab_name) || 
        ' WHERE m >= 0 ORDER BY m)  ' || ' WHERE rownum <= :no ' ;
    EXECUTE IMMEDIATE stmt into part_m USING no;

    INSERT /*+ APPEND */ INTO partition_tmp_3 (vertex_id,p_id,x,y,m)
      SELECT vertex_id,
              mdsys.SDO_ROUTER_PARTITION.get_pid(
                mdsys.SDO_ROUTER_PARTITION.adjust_m(0,part_m,m),part_counter),
              x,y,mdsys.SDO_ROUTER_PARTITION.adjust_m(0,part_m,m)
        FROM partition_tmp_2;
  END IF;

  COMMIT;

END make_partition_equal;

--
-- partition procedure
--  
PROCEDURE new_partition_proc(p_tab_name IN VARCHAR2, 
                              max_v_no IN NUMBER,
                              partition_id IN NUMBER, 
                              make_equal IN BOOLEAN, 
                              part_counter IN OUT NUMBER)
IS
  m_mean  NUMBER;
  v_no    NUMBER;
  x_mean  NUMBER;
  y_mean  NUMBER; 
  eigenvec vector_2d;
  partition_tmp_1 VARCHAR2(256);
  stmt            VARCHAR2(256);
  table_name      VARCHAR2(256);
BEGIN
  --
  -- terminal condition for bisecting
  -- if vertex no. smaller than max_v_no ,stops

  IF (partition_id = 0) THEN 
    partition_tmp_1 := SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(p_tab_name);
  ELSE
    partition_tmp_1 := SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(p_tab_name) || '_' || partition_id;
  END IF;  

  stmt := 'SELECT COUNT(*) FROM ' || partition_tmp_1;
  EXECUTE IMMEDIATE stmt into  v_no;

  IF (v_no = 0) THEN
    RETURN;
  END IF;  

  --
  -- prepare data for eigenvalue/eigenvector calculation
  --

  stmt := 'SELECT AVG(x), AVG(y) from ' || SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(partition_tmp_1);
  EXECUTE IMMEDIATE stmt into x_mean, y_mean;

  stmt := 'SELECT mdsys.SDO_ROUTER_PARTITION.min_eigenvector(
                          sum(power(x-:1,2)), sum(power(y-:2,2)),
                          sum((x-:3)*(y-:4))) FROM ' || partition_tmp_1;
  EXECUTE IMMEDIATE stmt INTO eigenvec USING x_mean, y_mean, x_mean, y_mean;

  stmt := 'SELECT AVG(:1*(x - :2) + :3*(y-:4)) FROM ' || partition_tmp_1;
  EXECUTE IMMEDIATE stmt INTO m_mean USING -eigenvec.y, x_mean, eigenvec.x, y_mean;

  stmt := 'INSERT /*+ APPEND */ into partition_tmp_2 (vertex_id,p_id,x,y,m)
    SELECT vertex_id,p_id,x,y, (:1*(x - :2) + :3*(y-:4) - :5) 
      FROM ' || partition_tmp_1;
  EXECUTE IMMEDIATE stmt USING -eigenvec.y, x_mean, eigenvec.x, y_mean, m_mean;

  COMMIT;

  --
  -- make equal size if required
  --  

  IF (make_equal) THEN
    make_partition_equal('partition_tmp_2',partition_id,v_no,part_counter);
  ELSE
    INSERT /*+ APPEND */ INTO partition_tmp_3 (vertex_id,p_id,x,y,m)
      SELECT vertex_id,
              mdsys.SDO_ROUTER_PARTITION.get_pid(m, part_counter),
              x,y,m 
      FROM partition_tmp_2;      

    COMMIT;    
  END IF;

  IF (partition_id = 0) THEN
    EXECUTE IMMEDIATE 'TRUNCATE TABLE ' || SYS.DBMS_ASSERT.ENQUOTE_NAME(p_tab_name);
  ELSE
    table_name := SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(p_tab_name) || '_' || partition_id;
    EXECUTE IMMEDIATE 'DROP TABLE ' || table_name;
  END IF;


  IF (v_no/2 > max_v_no) THEN

    table_name := SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(p_tab_name) || '_' || part_counter;
    IF ( table_exists(SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(table_name)) = 'TRUE') THEN
            execute immediate 'DROP TABLE ' || SYS.DBMS_ASSERT.ENQUOTE_NAME(table_name);
    END IF;

    stmt := 'CREATE  TABLE ' || table_name || 
      ' STORAGE (maxextents unlimited), NOLOGGING as 
        SELECT * FROM partition_tmp_3 WHERE  p_id=' || part_counter;
    EXECUTE IMMEDIATE stmt;

    table_name := SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(p_tab_name) || '_' || sdo_util.number_to_char(part_counter+1);

    IF ( table_exists(SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(table_name)) = 'TRUE') THEN
            execute immediate 'DROP TABLE ' || SYS.DBMS_ASSERT.ENQUOTE_NAME(table_name);
    END IF;
    stmt := 'CREATE TABLE ' || table_name || 
      ' STORAGE (maxextents unlimited), NOLOGGING as 
      SELECT * FROM partition_tmp_3 WHERE p_id=' || sdo_util.number_to_char(part_counter+1);
    EXECUTE IMMEDIATE stmt;

    COMMIT;
  ELSE
    INSERT /*+ APPEND */ INTO final_partition (vertex_id,p_id,x,y)
     SELECT vertex_id,p_id,x,y FROM partition_tmp_3;

    COMMIT;
  END IF;

  part_counter := part_counter+2;  

  EXECUTE IMMEDIATE 'TRUNCATE TABLE partition_tmp_2'; 
  EXECUTE IMMEDIATE 'TRUNCATE TABLE partition_tmp_3'; 

END new_partition_proc;

--
-- main pl/sql procedure to partition a graph with coordinate information
--
PROCEDURE graph_partition(p_tab_name IN VARCHAR2, 
                          max_v_no IN NUMBER, 
                          make_equal IN BOOLEAN)
IS
  p_level   INTEGER;
  stmt      VARCHAR2(256);
  v_no      NUMBER;
  min_pid   NUMBER;
  max_pid   NUMBER;
  pid       NUMBER;
  p_counter NUMBER;
  p_date    date;
  show_time BOOLEAN := FALSE;   
BEGIN  
  IF (table_exists(p_tab_name) = 'FALSE' ) THEN
    log_message('ERROR: ' || p_tab_name || ' table not found');
    RETURN ;
  END IF;
  
  stmt := 'SELECT MIN(p_id), MAX(p_id) FROM ' ||
    SYS.DBMS_ASSERT.ENQUOTE_NAME(p_tab_name);
  EXECUTE IMMEDIATE stmt INTO min_pid, max_pid;

  stmt := 'SELECT COUNT(*) FROM ' || 
    SYS.DBMS_ASSERT.ENQUOTE_NAME(p_tab_name) || ' WHERE p_id = :min_pid';
  EXECUTE IMMEDIATE stmt INTO v_no USING  min_pid;

  p_level := floor(LN(v_no/max_v_no)/LN(2.0));


  -- issue warning if the no of nodes in the table is smaller than max_v_no
  if ( p_level < 0 ) THEN
   log_message('WARNING: no. of nodes: ' || v_no || ' in table: ' || p_tab_name || 
        ' is smaller than the given max_v_no: ' || max_v_no);
   RETURN; 
  end if;       


  p_counter := max_pid+1; -- starting partition counter
  pid := min_pid;

  log_message('INFO: begin partitioning of '|| p_tab_name || 
    ' partition level: ' || p_level || ' min(partition id): ' || 
      min_pid || ' max(partition id): ' || max_pid);

  log_message('INFO: generating ' || power(2,p_level+1) || 
              ' partitions from level:0 to level: ' || p_level ||' ...', show_time);
        
  FOR k IN min_pid..max_pid LOOP
    FOR i IN 0..p_level LOOP
      p_date := sysdate;        
      FOR j IN 1..power(2,i) LOOP
        new_partition_proc(SYS.DBMS_ASSERT.NOOP(p_tab_name), max_v_no, pid, make_equal, p_counter);
        pid := pid +1; 
      END LOOP;
      log_message('INFO:    partitioning '|| p_tab_name || 
        ' level: ' || i || ' partition id: ' || pid);
      -- add computation time form each level
      log_message('INFO:    partitioning level: ' || i || ' with ' || power(2,i+1) || ' partitions '|| ' took ' || 
                  sdo_util.number_to_char((sysdate-p_date)*24*60,'99999.999') || ' min.',show_time) ;
    END LOOP;
  END LOOP;

  -- Copy the result back to original table and ajust the pids
  adjust_final_pid(SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(p_tab_name));

  log_message('INFO: completed partitioning of '|| p_tab_name);

EXCEPTION
  WHEN OTHERS THEN
   log_message('Exception processing partition '|| pid ||
    ' of the ' || p_tab_name || ' table');
   log_message(SQLERRM);
   raise_application_error(-20009, 'Error Graphing Partitions');
  
END graph_partition;

--
-- drop all temporary tables for partitioning
--
PROCEDURE clean_tables (cleanup IN BOOLEAN DEFAULT TRUE)
 
IS
BEGIN
  -- Tables that should always be cleaned up
  --
  IF (table_exists('node_tmp') = 'TRUE') THEN
    EXECUTE IMMEDIATE 'rename node_tmp to node';

    IF (constraint_exists('FK_EDGE_START_NODE_ID') = 'TRUE') THEN
       EXECUTE IMMEDIATE 'ALTER TABLE edge DROP CONSTRAINT FK_EDGE_START_NODE_ID';
    END IF;

    IF (constraint_exists('FK_EDGE_END_NODE_ID') = 'TRUE') THEN
       EXECUTE IMMEDIATE 'ALTER TABLE edge DROP CONSTRAINT FK_EDGE_END_NODE_ID';
    END IF;

    IF (constraint_exists('PK_NODE') = 'TRUE') THEN
       EXECUTE IMMEDIATE 'ALTER TABLE node DROP CONSTRAINT PK_NODE';
    END IF;

    IF (index_exists('node_id_index') = 'TRUE') THEN
        EXECUTE IMMEDIATE 'DROP INDEX node_id_index';
    END IF;
    IF (index_exists('node_id_idx') = 'TRUE') THEN
        EXECUTE IMMEDIATE 'DROP INDEX node_id_idx';
    END IF;

    IF (index_exists('node_partition_index') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP INDEX node_partition_index';
    END IF;
    IF (index_exists('node_partition_idx') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP INDEX node_partition_idx';
    END IF;

    -- rebuild node index 
    log_message('INFO: rebuilding node_id_idx on node');
    EXECUTE IMMEDIATE 'CREATE INDEX node_id_idx on node(node_id)';

    -- rebuild primary key
    EXECUTE IMMEDIATE 'ALTER TABLE node ADD CONSTRAINT pk_node PRIMARY KEY(node_id)';

    -- rebuild foreign keys
    EXECUTE IMMEDIATE 'ALTER TABLE edge ADD CONSTRAINT fk_edge_start_node_id FOREIGN KEY (start_node_id) REFERENCES node(node_id)';
    EXECUTE IMMEDIATE 'ALTER TABLE edge ADD CONSTRAINT fk_edge_end_node_id FOREIGN KEY (end_node_id) REFERENCES node(node_id)';

    -- rebuild node/partition index
    log_message('INFO: rebuild node_partition_idx on node');
    EXECUTE IMMEDIATE 'CREATE INDEX node_partition_idx on node(partition_id)';
  END IF;

  IF (table_exists('edge_array_tmp') = 'TRUE') THEN
    EXECUTE IMMEDIATE 'DROP TABLE edge_array_tmp';
  END IF;

  IF (table_exists('final_partition') = 'TRUE') THEN
    EXECUTE IMMEDIATE 'DROP TABLE final_partition';
  END IF;

  IF (table_exists('partition_tmp_2') = 'TRUE') THEN
    EXECUTE IMMEDIATE 'DROP TABLE partition_tmp_2';
  END IF;

  IF (table_exists('partition_tmp_3') = 'TRUE') THEN
    EXECUTE IMMEDIATE 'DROP TABLE partition_tmp_3';
  END IF;

  -- Tables we may want to keep for debugging purposes.
  -- 
  IF (cleanup) THEN
    IF (table_exists('edge_part') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP TABLE edge_part';
    END IF;

    IF (table_exists('node_part') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP TABLE node_part';
    END IF;

    IF (table_exists('restricted_nodes')= 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP TABLE restricted_nodes';
    END IF;

    IF (table_exists('restricted_edges')= 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP TABLE restricted_edges';
    END IF;

    IF (table_exists('super_edge_ids') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP TABLE super_edge_ids';
    END IF;

    IF (table_exists('super_node_ids') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP TABLE super_node_ids';
    END IF;
    -- Trucking intermediate data
    --
    IF (table_exists('router_partitioned_truck_data') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP TABLE router_partitioned_truck_data';
    END IF;
    IF (table_exists('partitioned_router_transport') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP TABLE partitioned_router_transport';
    END IF;
    IF (table_exists('new_trucking_data') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP TABLE new_trucking_data';
    END IF;
    -- Turn restriction intermediate data
    IF (table_exists('partitioned_router_nav_strand') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP TABLE partitioned_router_nav_strand';
    END IF;
    IF (table_exists('router_max_seq') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP TABLE router_max_seq';
    END IF;
    IF (table_exists('new_turn_restriction_data') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP TABLE new_turn_restriction_data';
    END IF;
    -- Edge intermediate user data
    --
    IF (table_exists('new_timezone_data') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP TABLE new_timezone_data';
    END IF;
    IF (table_exists('partitioned_timezones_edges') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP TABLE partitioned_timezones_edges';
    END IF;
    IF (table_exists('router_highway_edges') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP TABLE router_highway_edges';
    END IF;
 END IF;
END;

--
-- setup all temporary tables for partitioning
--
PROCEDURE setup_tables 
IS
BEGIN
  log_message('INFO: setting up partitioning temporary tables');

  IF (table_exists('partition_tmp_2') = 'TRUE') THEN
    EXECUTE IMMEDIATE 'TRUNCATE TABLE partition_tmp_2';
  ELSE
   EXECUTE IMMEDIATE 'CREATE TABLE partition_tmp_2  (vertex_id NUMBER, p_id NUMBER, x NUMBER, y NUMBER,m NUMBER)
                        STORAGE (maxextents unlimited), NOLOGGING'; 
  END IF;

  IF (table_exists('partition_tmp_3') = 'TRUE') THEN
    EXECUTE IMMEDIATE 'TRUNCATE TABLE partition_tmp_3';
  ELSE
   EXECUTE IMMEDIATE 'CREATE TABLE partition_tmp_3  (vertex_id NUMBER, p_id NUMBER, x NUMBER, y NUMBER,m NUMBER)
                        STORAGE (maxextents unlimited), NOLOGGING';  
  END IF;
                                        
  IF (table_exists('final_partition')= 'TRUE') THEN
    EXECUTE IMMEDIATE 'TRUNCATE TABLE final_partition';
  ELSE
    EXECUTE IMMEDIATE 'CREATE TABLE final_partition  (vertex_id NUMBER, p_id NUMBER, x NUMBER, y NUMBER)
                        STORAGE (maxextents unlimited),  NOLOGGING';  
  END IF;

  IF (table_exists('edge_part') = 'TRUE') THEN
    EXECUTE IMMEDIATE 'DROP TABLE edge_part';
  END IF;

  IF (table_exists('node_part') = 'TRUE') THEN
    EXECUTE IMMEDIATE 'DROP TABLE node_part';
  END IF;

  IF (table_exists('restricted_nodes')= 'TRUE') THEN
    EXECUTE IMMEDIATE 'DROP TABLE restricted_nodes';
  END IF;

  IF (table_exists('restricted_edges')= 'TRUE') THEN
    EXECUTE IMMEDIATE 'DROP TABLE restricted_edges';
  END IF;

  IF (table_exists('super_edge_ids') = 'TRUE') THEN
    EXECUTE IMMEDIATE 'DROP TABLE super_edge_ids';
  END IF;

  IF (table_exists('super_node_ids') = 'TRUE') THEN
    EXECUTE IMMEDIATE 'DROP TABLE super_node_ids';
  END IF;
END setup_tables;


--
-- Create the node_part table needed for partitioning
--
PROCEDURE create_node_part
IS
  stmt  VARCHAR2(256);
BEGIN
  IF (table_exists('node_part') = 'TRUE') THEN
    EXECUTE IMMEDIATE 'DROP TABLE node_part';
  END IF;

  log_message('INFO: create and load node_part table');

  EXECUTE IMMEDIATE 'CREATE TABLE node_part(vertex_id NUMBER, x NUMBER, y NUMBER, p_id NUMBER, outedges mdsys.num_array, inedges mdsys.num_array)
    STORAGE (maxextents unlimited), NOLOGGING';

  EXECUTE IMMEDIATE 'INSERT /*+ APPEND */ into node_part 
    SELECT n.node_id, n.geometry.sdo_point.x, n.geometry.sdo_point.y, 0, null, null
      FROM node n';

  COMMIT;

  -- Create an index on the node_part table
  --
  log_message('INFO: create index np_vp_idx on node_part');
  EXECUTE IMMEDIATE 'CREATE INDEX np_vp_idx on node_part(vertex_id, p_id)';

  -- gather table and index stats
  log_message('INFO: gather node_part table statistics');
  gather_table_stats('NODE_PART');

END create_node_part;

--
-- Create and load the restricted_edges table.
--
PROCEDURE create_restricted_edges
IS
  TYPE CURSOR_TYPE IS REF CURSOR;
  coords    MDSYS.SDO_ORDINATE_ARRAY;
  divider   VARCHAR2(1);  
  edge_id   NUMBER;
  ins_stmt  VARCHAR(256);
  p_cursor  CURSOR_TYPE;
BEGIN
  IF (table_exists('restricted_edges') = 'TRUE') THEN
    EXECUTE IMMEDIATE 'DROP TABLE restricted_edges';
  END IF;

  log_message('INFO: Create and load the restricted_edges table');

  EXECUTE IMMEDIATE 'CREATE TABLE restricted_edges(edge_id NUMBER,
                                                   divider VARCHAR2(1),
                                                   startx1 NUMBER, starty1 NUMBER,
                                                   startx2 NUMBER, starty2 NUMBER,
                                                   endx1 NUMBER, endy1 NUMBER,
                                                   endx2 NUMBER, endy2 NUMBER)
                       STORAGE (maxextents unlimited), NOLOGGING';

  ins_stmt := 'INSERT INTO restricted_edges VALUES ' ||
    '(:eid, :div, :sx1, :sy1, :sx2, :sy2,:ex1, :ey1, :ex2, :ey2)';

  -- Find all edges attached to nodes that have restricted edges either inbound
  -- or outbound from the node.
  OPEN p_cursor FOR 'SELECT t.edge_id, t.divider, t.geometry.sdo_ordinates' ||
    ' FROM edge t WHERE t.edge_id in ' ||
      '(SELECT edge_id FROM EDGE WHERE start_node_id IN (SELECT node_id FROM restricted_nodes) UNION' ||
      ' SELECT edge_id FROM EDGE WHERE end_node_id IN (SELECT node_id FROM restricted_nodes))';

  LOOP
    FETCH p_cursor INTO edge_id, divider, coords;
    EXIT WHEN p_cursor%NOTFOUND;

    -- Find and store the edges first and last segments
    EXECUTE IMMEDIATE ins_stmt USING 
      edge_id, divider, 
      coords(1), coords(2), 
      coords(3), coords(4), 
      coords(coords.count-3), coords(coords.count-2), 
      coords(coords.count-1), coords(coords.count);
  END LOOP;
  CLOSE p_cursor;

  COMMIT;

  log_message('INFO: create index restricted_edges_idx');
  EXECUTE IMMEDIATE 'CREATE INDEX restricted_edges_idx on restricted_edges(edge_id)';

  log_message('INFO: gather restricted_edges table statistics');
  gather_table_stats('RESTRICTED_EDGES');
END create_restricted_edges;

--
-- Create the restricted_nodes table needed for turn restriction generation
--
PROCEDURE create_restricted_nodes
IS
BEGIN
  IF (table_exists('restricted_nodes') = 'TRUE') THEN
    EXECUTE IMMEDIATE 'DROP TABLE restricted_nodes';
  END IF;

  log_message('INFO: Create and load the restricted_nodes table');

  EXECUTE IMMEDIATE 'CREATE TABLE restricted_nodes(node_id NUMBER, inedges mdsys.num_array, outedges mdsys.num_array)
     STORAGE (maxextents unlimited), NOLOGGING';

  -- Find all nodes that are the start or end point of a restricted edge.
  EXECUTE IMMEDIATE 'INSERT /*+ APPEND */ INTO restricted_nodes 
    SELECT vertex_id, inedges, outedges FROM NODE_PART where vertex_id IN
      (SELECT source_id FROM edge_part WHERE divider IN (''1'', ''A'') union 
       SELECT target_id FROM edge_part WHERE divider IN (''2'', ''A''))';

  COMMIT;

END create_restricted_nodes;

PROCEDURE update_node_part_edge_arrays
IS
  TYPE CURSOR_TYPE IS REF CURSOR;
  e_cursor CURSOR_TYPE;
  e_id NUMBER; 
  e_array1 mdsys.num_array;
  e_array2 mdsys.num_array;
  n_cursor CURSOR_TYPE;
  n_id NUMBER; 
  stmt  VARCHAR2(4000);
BEGIN
  -- Create a temporary table to store the results of the array builds
  IF (table_exists('edge_array_tmp') = 'TRUE') THEN
          EXECUTE IMMEDIATE 'DROP TABLE edge_array_tmp';
  END IF;

  stmt := 'CREATE TABLE edge_array_tmp(vertex_id NUMBER, outedges mdsys.num_array, inedges mdsys.num_array) 
             STORAGE (maxextents unlimited), NOLOGGING';

  EXECUTE IMMEDIATE stmt;

  -- For every vertex in the node_part table build an in and out edge array.
  -- The where clause forces a fast index only scan
  OPEN n_cursor FOR 'SELECT vertex_id FROM node_part where vertex_id>0';
  LOOP
    FETCH n_cursor into n_id;
      EXIT WHEN n_cursor%NOTFOUND;

    e_array1 := mdsys.num_array();
    
    -- Build an array of outedges for a particular node
    OPEN e_cursor for 'select edge_id from edge_part where source_id=:id' using n_id;
    LOOP
      FETCH e_cursor INTO e_id;
        EXIT WHEN e_cursor%NOTFOUND;
          
        e_array1.extend(1);
        e_array1(e_array1.count) := e_id;    
    END LOOP;
    CLOSE e_cursor;

    e_array2 := mdsys.num_array();

    -- Build an array of inedges for a particular node
    OPEN e_cursor for 'select edge_id from edge_part where target_id=:id' using n_id;
    LOOP
      FETCH e_cursor INTO e_id;
        EXIT WHEN e_cursor%NOTFOUND;
          
        e_array2.extend(1);
        e_array2(e_array2.count) := e_id;    
    END LOOP;
    CLOSE e_cursor;

    -- Store the results  
    stmt := 'INSERT INTO edge_array_tmp VALUES (:node_id, :outedges, :inedges)'; 
    EXECUTE IMMEDIATE stmt USING n_id, e_array1, e_array2;
  END LOOP;
  CLOSE n_cursor;

  COMMIT;

  EXECUTE IMMEDIATE 'CREATE INDEX eat_idx on edge_array_tmp(vertex_id)';
  gather_table_stats('EDGE_ARRAY_TMP');
  
  -- Rebuild the node_part table from scratch using the edge array information
  stmt := 'CREATE TABLE new_node_part 
            STORAGE (maxextents unlimited), NOLOGGING AS 
              SELECT n.vertex_id, n.x, n.y, n.p_id, e.outedges, e.inedges 
              FROM node_part n, edge_array_tmp e
              WHERE n.vertex_id=e.vertex_id';
  EXECUTE IMMEDIATE stmt;

  -- Replace the old node_part table with the new one containing 
  -- the in and out edge arrays
  --
  EXECUTE IMMEDIATE 'DROP TABLE node_part';
  EXECUTE IMMEDIATE 'RENAME new_node_part to node_part';  

  -- Add needed indexes to the node_part table.
  --
  log_message('INFO: rebuild index np_vp_idx on node_part');
  EXECUTE IMMEDIATE 'CREATE INDEX np_vp_idx on node_part(vertex_id, p_id)';

  log_message('INFO: create index node_part_p_idx on node_part');
  EXECUTE IMMEDIATE 'CREATE INDEX node_part_p_idx on node_part(p_id)';

  -- rebuild the table and index stats
  log_message('INFO: rebuild node_part table statistics');
  gather_table_stats('NODE_PART');

  EXECUTE IMMEDIATE 'DROP TABLE edge_array_tmp';
END update_node_part_edge_arrays;

--
-- Create the node_part table needed for partitioning
--
PROCEDURE create_super_tables
IS
  stmt  VARCHAR2(256);
BEGIN
  --
  -- Drop any constraints on the EDGE and  NODE tables
  IF (constraint_exists('FK_EDGE_START_NODE_ID') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'ALTER TABLE edge DROP CONSTRAINT FK_EDGE_START_NODE_ID';
  END IF;
  IF (constraint_exists('FK_EDGE_END_NODE_ID') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'ALTER TABLE edge DROP CONSTRAINT FK_EDGE_END_NODE_ID';
  END IF;

  IF (constraint_exists('PK_NODE') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'ALTER TABLE node DROP CONSTRAINT PK_NODE';
  ELSE 
      EXECUTE IMMEDIATE 'ALTER TABLE node DROP PRIMARY KEY'; 
  END IF;

  -- Drop and indexes on the NODE table
  IF (index_exists('node_id_index') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP INDEX node_id_index';
  END IF;
  IF (index_exists('node_id_idx') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP INDEX node_id_idx';
  END IF;
  
  IF (index_exists('node_partition_index') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP INDEX node_partition_index';
  END IF;
  IF (index_exists('node_partition_idx') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP INDEX node_partition_idx';
  END IF;

  -- Rename the node table so we can use CTAS to rebuild it
  EXECUTE IMMEDIATE 'RENAME node to node_tmp';

  -- Create and populate the edge_part table. 
  -- 
  IF (table_exists('edge_part') = 'TRUE') THEN
    EXECUTE IMMEDIATE 'DROP TABLE edge_part';
  END IF;

  log_message('INFO: create and load edge_part');
  EXECUTE IMMEDIATE 'CREATE TABLE edge_part(edge_id NUMBER, source_id NUMBER,
      target_id NUMBER, source_p_id NUMBER, target_p_id NUMBER,
      func_class NUMBER, length NUMBER, speed_limit NUMBER, divider VARCHAR2(1),
      turn_restrictions mdsys.num_array)
    STORAGE (maxextents unlimited), NOLOGGING';

  EXECUTE IMMEDIATE 'INSERT /*+ APPEND */ INTO edge_part
    SELECT edge_id, start_node_id, end_node_id, 
        (SELECT p_id FROM node_part WHERE vertex_id = start_node_id),
        (SELECT p_id FROM node_part WHERE vertex_id = end_node_id),
        func_class, length, speed_limit, divider, null
      FROM edge';

  COMMIT;

  -- Create useful indices on edge_part.
  --
  log_message('INFO: create index edge_part_e_idx on edge_part');
  EXECUTE IMMEDIATE 'CREATE INDEX edge_part_e_idx on edge_part(edge_id)';

  log_message('INFO: create index edge_part_s_e_idx on edge_part');
  EXECUTE IMMEDIATE 'CREATE INDEX edge_part_s_e_idx on edge_part(source_id, edge_id)';

  log_message('INFO: create index edge_part_t_e_idx on edge_part');
  EXECUTE IMMEDIATE 'CREATE INDEX edge_part_t_e_idx on edge_part(target_id, edge_id)';

  log_message('INFO: create index edge_part_st_p_idx on edge_part');
  EXECUTE IMMEDIATE 'CREATE INDEX edge_part_st_p_idx on edge_part(source_p_id, target_p_id)';

  log_message('INFO: create index edge_part_ts_p_idx on edge_part');
  EXECUTE IMMEDIATE 'CREATE INDEX edge_part_ts_p_idx on edge_part(target_p_id, source_p_id)';

  -- build the table and index stats
  log_message('INFO: gather edge_part table statistics');
  gather_table_stats('EDGE_PART');
  
  -- Populate the inedges and outedges fields to the node_part table.
  --  
  log_message('INFO: create and load outedge and inedge columns in node_part table');
  update_node_part_edge_arrays;

  create_restricted_nodes;

  -- Recreate the node table and load it from the node_tmp table.
  --
  log_message('INFO: recreating node table with partitioning information');
  EXECUTE IMMEDIATE 'CREATE TABLE node STORAGE (maxextents unlimited), NOLOGGING AS 
                      SELECT nt.node_id, nt.geometry, np.p_id partition_id 
                      FROM node_tmp nt, node_part np  
                      WHERE nt.node_id = np.vertex_id';
  COMMIT;

  EXECUTE IMMEDIATE 'DROP TABLE node_tmp CASCADE CONSTRAINTS';

  -- Create an index on the node and partition id fields in the NODE table.
  --
  log_message('INFO: create index node_id_idx on node');
  EXECUTE IMMEDIATE 'CREATE INDEX node_id_idx on node(node_id)';

  -- Make node_id a Primary key
  EXECUTE IMMEDIATE 'ALTER TABLE node ADD CONSTRAINT pk_node PRIMARY KEY(node_id)';

  log_message('INFO: create foreign keys');
  EXECUTE IMMEDIATE 'ALTER TABLE edge ADD CONSTRAINT fk_edge_start_node_id FOREIGN KEY (start_node_id) REFERENCES node(node_id)';
  EXECUTE IMMEDIATE 'ALTER TABLE edge ADD CONSTRAINT fk_edge_end_node_id FOREIGN KEY (end_node_id) REFERENCES node(node_id)';

  log_message('INFO: create index node_partition_idx on node');
  EXECUTE IMMEDIATE 'CREATE INDEX node_partition_idx on node(partition_id)';

  log_message('INFO: rebuild node table statistics');
  gather_table_stats('NODE');
  
  -- Set the partition ids in the edge table.
  --
  EXECUTE IMMEDIATE 'ALTER TABLE edge nologging';
  IF (index_exists('edge_partition_index') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP INDEX edge_partition_index';
  END IF;

  log_message('INFO: updating edge table with partitioning information');
  EXECUTE IMMEDIATE 'UPDATE edge 
    SET partition_id = (SELECT p_id from node_part 
                          WHERE  vertex_id = start_node_id)';
  COMMIT;

  -- Create an index on the partition id field in the EDGE table.
  --
  log_message('INFO: create index edge_partition_index on edge');
  EXECUTE IMMEDIATE 'CREATE INDEX edge_partition_index on edge(partition_id)';

  -- build the table and index stats
  log_message('INFO: gather edge table statistics');
  gather_table_stats('EDGE');

  -- Create and populate the super_node and super_edge tables.
  --
  log_message('INFO: creating and loading super_node_ids table');
  IF (table_exists('super_node_ids') = 'TRUE') THEN
    EXECUTE IMMEDIATE 'TRUNCATE TABLE super_node_ids';
  ELSE 
    EXECUTE IMMEDIATE 'CREATE TABLE super_node_ids (node_id number)';
  END IF;

  EXECUTE IMMEDIATE 'INSERT /*+APPEND */ into super_node_ids
    (SELECT source_id FROM edge_part WHERE func_class = 1 or func_class=2
      UNION
    SELECT target_id FROM edge_part WHERE func_class = 1 or func_class=2)';

  log_message('INFO: creating and loading super_edge_ids table');
  IF (table_exists('super_edge_ids') = 'TRUE') THEN
    EXECUTE IMMEDIATE 'TRUNCATE TABLE super_edge_ids';
  ELSE
    EXECUTE IMMEDIATE 'CREATE TABLE super_edge_ids (edge_id number)';
  END IF;

  EXECUTE IMMEDIATE 'INSERT /*+APPEND */ into super_edge_ids
    SELECT edge_id FROM edge_part WHERE func_class =1 or func_class = 2';

  COMMIT;
END create_super_tables;

---
---
---
PROCEDURE gather_table_stats(table_name IN VARCHAR2)
IS
  stmt  VARCHAR2(256):= 'EXECUTE DBMS_STATS.GATHER_TABLE_STATS(:schema_name, :table_name, NULL, DBMS_STATS.AUTO_SAMPLE_SIZE)';
BEGIN
  IF (table_exists(SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(table_name)) = 'FALSE') THEN
    log_message('ERROR: ' || table_name || ' not found for gather table statistics');
    RETURN ;
  END IF;

  IF (schema_name IS NULL) THEN
     stmt := 'SELECT username FROM sys.user_users';
     EXECUTE IMMEDIATE stmt into schema_name;
  END IF;

  -- Gather table and index stats for the specified table
  DBMS_STATS.GATHER_TABLE_STATS(
    ownname => schema_name,
    tabname => table_name,
    estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE,
    degree => DBMS_STATS.AUTO_DEGREE);
                                
END gather_table_stats;

---
---
---
FUNCTION get_edge_info(edge_ids       IN  MDSYS.sdo_list_type,
                       to_edge_ids    OUT MDSYS.sdo_list_type,
                       rets           OUT mdsys.string_array,
                       angle_segments OUT MDSYS.sdo_list_type)
RETURN mdsys.string_array AS
    n           INTEGER;
    i           INTEGER;
    k           INTEGER;
    base        INTEGER := 1;
    to_edge_id  INTEGER;
    coords      mdsys.sdo_ordinate_array;
    names       mdsys.string_array;
    name_query  VARCHAR2(2000);
    sign_query  VARCHAR2(2000);
    ret         VARCHAR2(200);
    TYPE cursor_type IS REF CURSOR;
    sign_cursor cursor_type;
BEGIN
    IF (edge_ids IS NULL) THEN
        RETURN NULL;
    END IF;
  
    -- Initialize varrays
    n := edge_ids.count;
    names := mdsys.string_array();
    to_edge_ids := MDSYS.sdo_list_type();
    rets := mdsys.string_array();
    angle_segments := MDSYS.sdo_list_type();
    names.extend(n);
    to_edge_ids.extend(n);
    rets.extend(n);
    -- Need 4 points to describe a start and end segment for each edge id
    angle_segments.extend(n*8);

    -- Initialize name query
    name_query := 'SELECT t.name,t.geometry.sdo_ordinates FROM edge t WHERE t.edge_id = :1';
    -- Initialize sign query
    sign_query := 'SELECT to_edge_id, ' ||
                  'ramp || '':'' || exit || '':'' || toward '||
                  'FROM sign_post ' ||
                  'WHERE from_edge_id = :1';
          
  
    -- Iterate through route edge_ids and find info for each.
    FOR i IN 1..n LOOP
        EXECUTE IMMEDIATE name_query
        INTO names(i), coords
        USING edge_ids(i);
        IF (names(i) IS NULL) THEN
            names(i) := 'RAMP';
        END IF;
        -- Get sign information, if any.
        to_edge_ids(i) := 0;
        rets(i) := NULL;
        -- Get the coordinates for the start and end segments of the edge
        angle_segments(base)   := coords(1);
        angle_segments(base+1) := coords(2);
        angle_segments(base+2) := coords(3);
        angle_segments(base+3) := coords(4);
        angle_segments(base+4) := coords(coords.count-3);
        angle_segments(base+5) := coords(coords.count-2);
        angle_segments(base+6) := coords(coords.count-1);
        angle_segments(base+7) := coords(coords.count);
        base := base + 8;
        -- We have to use ABS(edge_ids(i)) since sign_post
        -- table contains only NAVSTREETS edge ids
        -- (positive only) not routeserver edge ids (which
        -- can be negative).
        OPEN sign_cursor FOR sign_query USING ABS(edge_ids(i));
        LOOP
            FETCH sign_cursor INTO to_edge_id, ret;
            EXIT WHEN sign_cursor%NOTFOUND;
            FOR k IN i+1..n LOOP
                IF (to_edge_id = edge_ids(k) OR
                    (-1*to_edge_id) = edge_ids(k)) THEN
                    -- Make sure we assign router edge id:
                    -- (negative or positive)!!!
                    to_edge_ids(i) := edge_ids(k);
                    rets(i) := ret;
                    EXIT;
                END IF;
            END LOOP;
            IF (to_edge_ids(i) <> 0) THEN
                EXIT;
            END IF;
        END LOOP;
        CLOSE sign_cursor;
    END LOOP;
    RETURN names;
END get_edge_info;

---
---
---
FUNCTION get_geometry_info (edge_ids      IN  MDSYS.sdo_list_type,
                            merged_coords OUT MDSYS.sdo_list_type)
RETURN NUMBER AS
  coords            MDSYS.SDO_ORDINATE_ARRAY;
  j                 NUMBER;
  k                 NUMBER;
BEGIN
  IF (edge_ids IS NULL) THEN
    RETURN 0;
  END IF;

  k := 1;
  merged_coords := MDSYS.sdo_list_type();

  -- For each input edge id, get the list of coordinates for the edge and 
  -- build a list of all coordinates for the edges.
  FOR i in 1 .. edge_ids.count
  LOOP
    EXECUTE IMMEDIATE
     'select t.geometry.sdo_ordinates from edge t ' ||
     'where edge_id=:i'
    INTO coords USING edge_ids(i);

    j := 1;

    merged_coords.extend(coords.count + 1);
    merged_coords(k) := coords.count;
    k := k + 1;

    WHILE j <= coords.count
    LOOP
      merged_coords(k) := coords(j);
      merged_coords(k+1) := coords(j+1);

      j := j + 2;
      k := k + 2;
    END LOOP;
  END LOOP;

  RETURN merged_coords.count;

END get_geometry_info;

---
---
---
FUNCTION get_geometry_info_link_lvl (edge_ids      IN  MDSYS.sdo_list_type, 
                                        merged_coords OUT MDSYS.sdo_list_type)
RETURN NUMBER AS
  coords            MDSYS.SDO_ORDINATE_ARRAY;
  j                 NUMBER;
  k                 NUMBER;
  link_level	    NUMBER;
BEGIN
  IF (edge_ids IS NULL) THEN
    RETURN 0;
  END IF;

  k := 1;
  merged_coords := MDSYS.sdo_list_type();

  -- For each input edge id, get the list of coordinates for the edge and 
  -- build a list of all coordinates for the edges.
  FOR i in 1 .. edge_ids.count
  LOOP
    EXECUTE IMMEDIATE
     'select t.geometry.sdo_ordinates, elocation_edge_link_level(t.func_class) '||
     ' from edge t ' || 
     'where edge_id=:i'
    INTO coords, link_level USING edge_ids(i); 

    j := 1;

    merged_coords.extend(coords.count + 2);
    merged_coords(k)   := coords.count;
    merged_coords(k+1) := link_level;
    k := k + 2;

    WHILE j <= coords.count
    LOOP
      merged_coords(k) := coords(j);
      merged_coords(k+1) := coords(j+1);

      j := j + 2;
      k := k + 2;
    END LOOP;  
  END LOOP;
      
  RETURN merged_coords.count;

END get_geometry_info_link_lvl;

---
--- Find and validate the Routeservers data version
---
FUNCTION get_version_info
  RETURN VARCHAR2 
IS
  data_version  VARCHAR2(32) := '10.2.0.4.0';
  major_version VARCHAR2(16);
  stmt          VARCHAR2(256);
  v_count       NUMBER;
BEGIN
  IF (table_exists('SDO_ROUTER_DATA_VERSION')= 'TRUE') THEN
    stmt := 'SELECT COUNT(*) FROM SDO_ROUTER_DATA_VERSION';
    EXECUTE IMMEDIATE stmt INTO v_count;

    IF (v_count != 1) THEN
      log_message('ERROR: Routeserver data version table corrupted, ' || 
        'multiple versions found');
      raise_application_error(-20005, 'Error getting data version, multiple versions found');
    END IF;

    stmt := 'SELECT data_version FROM MDSYS.SDO_ROUTER_DATA_VERSION';
    EXECUTE IMMEDIATE stmt INTO data_version;

    major_version := SUBSTR(data_version, 0, INSTR(data_version, '.')-1);

    IF (INSTR(data_version, '.', 1, 4) = 0 OR 
        (major_version!='12' AND major_version!='11' AND major_version!='10')) THEN
      log_message('ERROR: Routeserver data version table corrupted, ' || 
        'unsupported data version ' || data_version);
      raise_application_error(-20005, 
        'Error getting data version, unsupported data version ' || data_version);
    END IF;
  END IF;

  RETURN data_version;
END get_version_info;

---
---
---
FUNCTION is_10g(version IN VARCHAR2)
  RETURN BOOLEAN 
IS
BEGIN
  RETURN (SUBSTR(version, 0, INSTR(version, '.')-1) = '10');
END;

--
-- Entry point to used to create a network on the Router data
--
PROCEDURE create_router_network(log_file_name IN VARCHAR2 := 'sdo_router_partition.log',
                                network_name  IN VARCHAR2 := 'ROUTER_NETWORK')
IS
  expression      VARCHAR2(1024);
  full_file_name  VARCHAR2(256);
  l_network_name  VARCHAR2(256);
  link_view_name  VARCHAR2(256);
  node_view_name  VARCHAR2(256);
  part_view_name  VARCHAR2(256);
  pblob_view_name VARCHAR2(256);
  stmt            VARCHAR2(1024);
BEGIN
  if (sdo_util.is_simple_file_name(log_file_name) = 'FALSE') then
       raise_application_error(-20020, '[ERROR] bad file name');
  end if;
  full_file_name := open_log_file(log_file_name);

  -- Sanity check the passed in network name
  l_network_name := nls_upper(SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(network_name));
  
  -- Build the view names based on the network names
  link_view_name := l_network_name || '_LINK$';
  node_view_name := l_network_name || '_NODE$';
  part_view_name := l_network_name || '_PART$';
  pblob_view_name := l_network_name || '_PBLOB$';

  log_message('INFO: creating the Routeserver network: ' || l_network_name);
  
  -- if the network already exists, delete it first
  IF (network_exists(l_network_name) = 'TRUE') THEN
     delete_router_network(log_file_name, l_network_name, FALSE);
  END IF;

  -- create index for edge func_class
  IF (index_exists('EDGE_FUNC_CLASS_IDX') = 'FALSE') THEN
    log_message('      creating function class index', FALSE);
    EXECUTE IMMEDIATE 'create index EDGE_FUNC_CLASS_IDX on EDGE (FUNC_CLASS)';
  END IF;

  -- create a function based index for link level
  IF (index_exists('EDGE_LEVEL_IDX') = 'FALSE') THEN
    log_message('      creating link level index', FALSE);
    EXECUTE IMMEDIATE 'create index EDGE_LEVEL_IDX on EDGE(elocation_edge_link_level(FUNC_CLASS))';
  ELSE
    stmt := 'SELECT COLUMN_EXPRESSION FROM USER_IND_EXPRESSIONS WHERE INDEX_NAME = :name';
    EXECUTE IMMEDIATE stmt into expression using 'EDGE_LEVEL_IDX';      

    IF(substr(expression, 10, 25) <> 'ELOCATION_EDGE_LINK_LEVEL') THEN
      log_message('      dropping current link level index', FALSE);
      EXECUTE IMMEDIATE 'DROP INDEX EDGE_LEVEL_IDX';

      log_message('      creating link level index', FALSE);
      EXECUTE IMMEDIATE 'create index EDGE_LEVEL_IDX on EDGE(elocation_edge_link_level(FUNC_CLASS))';
    END IF;
  END IF;

  -- rebuild the table and index stats
  log_message('INFO: rebuild edge table statistics');
  gather_table_stats('EDGE');

  log_message('      creating views', FALSE);

  -- create a view on the NODE table
  stmt:=
    'create or replace view ' || node_view_name || 
        ' as select n.node_id node_id, 
               n.geometry geometry, 
               n.geometry.sdo_point.x x, 
               n.geometry.sdo_point.y y 
           from NODE n';

  EXECUTE IMMEDIATE stmt;

  -- create a view on the EDGE table
  stmt := 
    'create or replace view ' || link_view_name || 
        ' as select edge_id link_id, 
              start_node_id start_node_id,
              end_node_id end_node_id,
              elocation_edge_link_level(FUNC_CLASS) link_level,
              length length,
              speed_limit s,
              func_class f,
              geometry geometry,
              name name,
              divider divider
            from EDGE';

  EXECUTE IMMEDIATE stmt;

  --create a view on the NODE table node_id and partition_id information
  stmt := 
    'create or replace view ' || part_view_name || 
        ' as select node_id node_id, 
              partition_id partition_id,
              1 link_level
            from NODE';

  EXECUTE IMMEDIATE stmt;

  -- create a view on the PARTITION table adding link level and changing
  -- the format of the edge counts
  stmt := 'create or replace view ' || pblob_view_name || 
      ' as select link_level link_level,
            a.partition_id partition_id, 
            subnetwork blob,
            num_nodes num_inodes,
            num_outgoing_boundary_edges+num_incoming_boundary_edges num_enodes,
            num_non_boundary_edges num_ilinks,
            num_outgoing_boundary_edges+num_incoming_boundary_edges num_elinks,
            num_incoming_boundary_edges num_inlinks,
            num_outgoing_boundary_edges num_outlinks, ' ||
            SYS.DBMS_ASSERT.ENQUOTE_LITERAL('Y') || ' user_data_included
           from 
            (select 1 link_level, partition_id partition_id
             from PARTITION
             where partition_id > 0 
              union all
             select 2 link_level, partition_id partition_id
             from PARTITION
             where partition_id = 0) a,
            PARTITION b
           where a.partition_id = b.partition_id';

  EXECUTE IMMEDIATE stmt;

  log_message('      generating metadata', FALSE);

  --insert network metadata
  stmt := 'insert into MDSYS.USER_SDO_NETWORK_METADATA
            (network, 
             network_category,
             geometry_type,
             node_table_name,
             node_geom_column,
             link_table_name,
             link_geom_column,
             link_cost_column,
             link_direction,
             partition_table_name,
             partition_blob_table_name,
             user_defined_data)
           values (:1, :2, :3, :4, :5, :6, :7, :8, :9, :10, :11, :12)';
  
  EXECUTE IMMEDIATE stmt USING l_network_name, 'SPATIAL', 'SDO_GEOMETRY',
    node_view_name, 'GEOMETRY', link_view_name, 'GEOMETRY', 'LENGTH', 
    'DIRECTED', part_view_name, pblob_view_name, 'Y';

  -- insert user data metadata
  -- node x coordinate
  stmt := 'insert into mdsys.user_sdo_network_user_data
            (network, table_type, data_name,data_type)
           values (:1, :2, :3, :4)';
  EXECUTE IMMEDIATE stmt USING l_network_name, 'NODE', 'X', 'NUMBER';

  -- node y coordinate
  stmt := 'insert into mdsys.user_sdo_network_user_data
            (network, table_type, data_name, data_type)
           values (:1, :2, :3, :4)';
  EXECUTE IMMEDIATE stmt USING l_network_name, 'NODE', 'Y', 'NUMBER';

  -- link speed limit
  stmt := 'insert into mdsys.user_sdo_network_user_data
            (network, table_type, data_name, data_type)
           values (:1, :2, :3, :4)';
  EXECUTE IMMEDIATE stmt USING l_network_name, 'LINK', 'S', 'NUMBER';

  -- link function class
  stmt := 'insert into mdsys.user_sdo_network_user_data 
            (network, table_type, data_name, data_type)
           values (:1, :2, :3, :4)';
  EXECUTE IMMEDIATE stmt USING l_network_name, 'LINK', 'F', 'NUMBER';

  COMMIT;

  utl_file.fclose(part_log_file);
END;

--
-- Entry point to used to create a network on the Router data
--
PROCEDURE delete_router_network(log_file_name IN VARCHAR2 := 'sdo_router_partition.log',
                                network_name  IN VARCHAR2 := 'ROUTER_NETWORK',
                                log_delete IN BOOLEAN := TRUE)
IS
  full_file_name  VARCHAR2(256);
  l_network_name  VARCHAR2(256);
  link_view_name  VARCHAR2(256);
  node_view_name  VARCHAR2(256);
  part_view_name  VARCHAR2(256);
  pblob_view_name VARCHAR2(256);
  stmt            VARCHAR2(4000);
BEGIN
  if (sdo_util.is_simple_file_name(log_file_name) = 'FALSE') then
      raise_application_error(-20020, '[ERROR] bad file name');
  end if;
  full_file_name := open_log_file(log_file_name);
  l_network_name := nls_upper(SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(network_name));

  -- raise an error if the network doesn't exist
  IF (network_exists(l_network_name) = 'FALSE') THEN  
    log_message('ERROR: network delete failed, ' || l_network_name || ' network not found');
    raise_application_error(-20020, 'Network delete failed, ' || l_network_name || ' network not found');
  END IF;

  IF (log_delete) THEN
    log_message('INFO: deleting the Routeserver network: ' || l_network_name);
  ELSE
    log_message('Ping');
  END IF;
  
  stmt := 'select node_table_name, link_table_name, 
                  partition_table_name, partition_blob_table_name
           from MDSYS.USER_SDO_NETWORK_METADATA 
           where NETWORK = :name';

  EXECUTE IMMEDIATE stmt 
    INTO node_view_name, link_view_name, part_view_name, pblob_view_name
    USING l_network_name;

  -- cleanup metadata
  stmt := 'delete from MDSYS.USER_SDO_NETWORK_METADATA where NETWORK = :name'; 
  EXECUTE IMMEDIATE stmt using l_network_name;

  stmt := 'delete from MDSYS.USER_SDO_NETWORK_USER_DATA where NETWORK = :name'; 
  EXECUTE IMMEDIATE stmt using l_network_name;

  -- cleanup views 
  IF (table_exists(link_view_name) = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP VIEW ' || 
        SYS.DBMS_ASSERT.ENQUOTE_NAME(link_view_name);
  END IF;

  IF (table_exists(node_view_name) = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP VIEW ' || 
        SYS.DBMS_ASSERT.ENQUOTE_NAME(node_view_name);
  END IF;

  IF (table_exists(part_view_name) = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP VIEW ' || 
        SYS.DBMS_ASSERT.ENQUOTE_NAME(part_view_name);
  END IF;

  IF (table_exists(pblob_view_name) = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP VIEW ' || 
        SYS.DBMS_ASSERT.ENQUOTE_NAME(pblob_view_name);
  END IF;

  COMMIT;

  utl_file.fclose(part_log_file);
END;

--
-- Entry point and driver of the entire partitioning process.
-- high level procedure for partitioning graph based on coordinate 
-- information (inertia bisecting). The parameters are defaulted 
-- so the customers don't have to worry about them.
--
PROCEDURE partition_router(log_file_name IN VARCHAR2 := 'sdo_router_partition.log', 
                        max_v_no IN NUMBER DEFAULT 10000,
                        driving_side IN VARCHAR2 := 'R',
                        network_name IN VARCHAR := 'ROUTER_NETWORK', 
                        max_memory IN NUMBER := 1.75, 
                        cleanup IN BOOLEAN DEFAULT TRUE,
                        use_securefiles IN BOOLEAN DEFAULT TRUE,
                        generate_11g_restrictions IN BOOLEAN DEFAULT TRUE)
IS
  full_file_name VARCHAR2(256);
  max_memory_in_bytes NUMBER := (max_memory*1073741824);
  stmt         VARCHAR2(256);
  msg_cleanup  VARCHAR2(10) := 'TRUE';
  msg_blob_format VARCHAR2(16) := 'SECUREFILE';
  msg_restrictions VARCHAR2(10) := 'TRUE';  
BEGIN
  if (sdo_util.is_simple_file_name(log_file_name) = 'FALSE') then
      raise_application_error(-20020, '[ERROR] bad file name');
  end if;
  IF (NOT cleanup) THEN 
    msg_cleanup := 'FALSE';
  END IF;

  IF (NOT use_securefiles) THEN 
    msg_blob_format := 'BASICFILE';
  END IF;

  IF (NOT generate_11g_restrictions) THEN 
    msg_restrictions := 'FALSE';
  END IF;

  full_file_name := open_log_file(log_file_name);

  -- Query the schema name for gathering statistics
  stmt := 'SELECT username FROM sys.user_users';
  EXECUTE IMMEDIATE stmt into schema_name;

  log_message('******** Beginning SDO Router partitioning');
  log_message('** Schema: ' || schema_name, FALSE);
  log_message('** Logfile location: ' || full_file_name, FALSE);
  log_message('** Nodes per partition: ' || max_v_no, FALSE);
  log_message('** Driving side: ' || driving_side, FALSE);
  log_message('** Router network name: ' || network_name, FALSE);
  log_message('** Max JVM Memory Size: ' || max_memory || 'GB (' || max_memory_in_bytes || ' bytes)', FALSE);
  log_message('** Cleanup temporary files: ' || msg_cleanup, FALSE);
  log_message('** BLOBs stored in ' || msg_blob_format || ' format' , FALSE);
  log_message('** Generating 11g turn restrictions: ' || msg_restrictions, FALSE);

  setup_tables; 

  create_node_part;

  graph_partition('NODE_PART', max_v_no, TRUE);

  create_super_tables;

  IF (generate_11g_restrictions) THEN 
    -- Table of nodes that are either the start or end node of a restricted edge.
    create_restricted_nodes;

    -- Table of edges that that have either a start or end node in the 
    -- restricted_nodes table. We store the first and last segment of each edge.
    -- These segments are far more accurate than the edge as a whole for 
    -- computing turn angles.
    create_restricted_edges;
  END IF;

  -- Close the log file so the Java code can use it
  utl_file.fclose(part_log_file);

  -- Adjust the Oracle JVM maximum memory size to 1.5g. 
  -- This is the equivelent to specifying -Xmx1536m to 
  -- java outside the database. Memory size is specified in bytes.
  ElocationSetJVMHeapSize(max_memory_in_bytes);

  IF (generate_11g_restrictions) THEN 
    -- Generate the 11g turn restrictions for compatability.
    -- This MUST be done after the NODE_PART and EDGE_PART tables have 
    -- been populated and before the creation of the partition table.
    build_turn_restrictions(full_file_name, driving_side);
  END IF;

BEGIN
  -- Java code to create the partiton table
  elocation_partition_router(full_file_name, msg_blob_format);
EXCEPTION
  WHEN OTHERS THEN
    if (sdo_util.is_simple_file_name(log_file_name) = 'FALSE') then
      raise_application_error(-20020, '[ERROR] bad file name');
    end if;
    part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
    log_message(SQLERRM);
    utl_file.fclose(part_log_file);
    RAISE JAVA_ERROR;
END;

  -- Reopen the logfile
  part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');

  log_message('INFO: creating the final partition table');

  -- Rename the new partition table and build an index on it.
  IF (table_exists('partition')= 'TRUE') THEN
    EXECUTE IMMEDIATE 'DROP TABLE partition';
  END IF;

  EXECUTE IMMEDIATE 'RENAME new_partition to partition';

  log_message('INFO: create index partition_p_idx on partition table');
  EXECUTE IMMEDIATE 'CREATE INDEX partition_p_idx on partition(partition_id)';

  log_message('INFO: gather partition table statistics');
  gather_table_stats('PARTITION');
  
  -- Create the indexes, views and metadata needed by the NDM on top of
  -- the Router data
  create_router_network(log_file_name, network_name);

  -- Generate user data based turn restrictions. 
  -- This must be done after the NODE_PART and EDGE_PART tables 
  create_turn_restriction_data(log_file_name, cleanup);

  -- If trucking user data exists, partition it.
  IF (table_exists('router_transport') = 'TRUE') THEN
    create_trucking_data(log_file_name, TRUE);
  END IF;

  -- Reopen the logfile
  part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
  clean_tables(cleanup);
  log_message('******** Completed SDO Router partitioning');

  -- Close the log file
  utl_file.fclose(part_log_file);
EXCEPTION
    WHEN JAVA_ERROR THEN
        raise_application_error(-20000, 'Oracle Router partitioning failed');        
    WHEN OTHERS THEN
        IF (utl_file.is_open(part_log_file) = FALSE) THEN
          if (sdo_util.is_simple_file_name(log_file_name) = 'FALSE') then
              raise_application_error(-20020, '[ERROR] bad file name');
          end if;
          part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
        END IF;
        log_message(SQLERRM);
        utl_file.fclose(part_log_file);
        raise_application_error(-20000, 'Oracle Router partitioning failed');
END partition_router;

--
-- Entry point to used to produce a dump of the partition BLOBs
--
PROCEDURE dump_partitions(log_file_name IN VARCHAR2 := 'sdo_router_partition.log', 
                      start_pid IN NUMBER DEFAULT 0, 
                      end_pid IN NUMBER DEFAULT -1,
                      verbose IN BOOLEAN DEFAULT FALSE)
IS
  full_file_name  VARCHAR2(256);
  l_end_pid       NUMBER := end_pid;
  max_pid         NUMBER;
  stmt            VARCHAR2(256);
  version         VARCHAR2(32);
BEGIN
  if (sdo_util.is_simple_file_name(log_file_name) = 'FALSE') then
      raise_application_error(-20020, '[ERROR] bad file name');
  end if;
  full_file_name := open_log_file(log_file_name);
  
  -- Make sure the table is actually there
  IF (table_exists('PARTITION') = 'FALSE' ) THEN
    log_message('ERROR: Partition dump failed, PARTITION table not found');
    utl_file.fclose(part_log_file);

    RAISE PARAMETER_ERROR;
  END IF;

  stmt := 'SELECT MAX(PARTITION_ID) FROM PARTITION';
  EXECUTE IMMEDIATE stmt INTO max_pid;
  
  -- The default value for the end partition id is max(partition_id)
  IF (l_end_pid = -1) THEN
    l_end_pid := max_pid;
  END IF;

  -- Validate the starting partition id.
  IF ((start_pid < 0) OR (start_pid > max_pid)) THEN
    log_message('ERROR: Invald Start Partition ID '||start_pid||', Valid Range (0,'||max_pid||')');
    utl_file.fclose(part_log_file);

    RAISE PARAMETER_ERROR;
  END IF;

  -- Validate the ending partition id.
  IF ((l_end_pid < start_pid) OR (l_end_pid > max_pid) OR (l_end_pid < 0)) THEN
    log_message('ERROR: Invald End Partition ID '||sdo_util.number_to_char(end_pid)||', Valid Range ('||start_pid||','||max_pid||')');
    utl_file.fclose(part_log_file);

    RAISE PARAMETER_ERROR;
  END IF;

  version := get_version_info();
  log_message('******** Beginning partition dump');
  log_message('** Logfile location: ' || full_file_name, FALSE);
  log_message('** Routeserver data version: ' || version, FALSE);
  log_message('** Start partition id: ' || start_pid, FALSE);
  log_message('** End partition id: ' || l_end_pid, FALSE);
  IF (verbose) THEN
    log_message('** Verbose mode: TRUE', FALSE);
  ELSE
    log_message('** Verbose mode: FALSE', FALSE); 
  END IF;

  log_message('', FALSE); 

  -- Close the log file so the Java code can use it
  utl_file.fclose(part_log_file);

  elocation_dump_partition(full_file_name, start_pid, l_end_pid, 
    verbose, is_10g(version)); 

  EXCEPTION
    WHEN PARAMETER_ERROR THEN
      raise_application_error(-20004, 'Error, partition dump failed, see log file.');
    WHEN OTHERS THEN
        IF (utl_file.is_open(part_log_file) = FALSE) THEN
           if (sdo_util.is_simple_file_name(log_file_name) = 'FALSE') then
                raise_application_error(-20020, '[ERROR] bad file name');
           end if;
          part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
        END IF;
        log_message(SQLERRM);
        utl_file.fclose(part_log_file);
        raise_application_error(-20004, 'Error, partition dump failed, see log file.');
END dump_partitions;

--
-- Entry point to used to validate the partition BLOBs
--
PROCEDURE validate_partitions(log_file_name IN VARCHAR2 := 'sdo_router_partition.log', 
                              start_pid IN NUMBER DEFAULT 0, 
                              end_pid IN NUMBER DEFAULT -1,
                              verbose IN BOOLEAN DEFAULT FALSE)
IS
  full_file_name  VARCHAR2(256);
  l_end_pid       NUMBER := end_pid;
  max_pid         NUMBER;
  stmt            VARCHAR2(256);
  version         VARCHAR2(32);
BEGIN
  if (sdo_util.is_simple_file_name(log_file_name) = 'FALSE') then
      raise_application_error(-20020, '[ERROR] bad file name');
  end if;
  full_file_name := open_log_file(log_file_name);
  
  -- Make sure the table is actually there
  IF (table_exists('PARTITION') = 'FALSE' ) THEN
    log_message('ERROR: Partition validate failed, PARTITION table not found');
    utl_file.fclose(part_log_file);

    RAISE PARAMETER_ERROR;
  END IF;

  stmt := 'SELECT MAX(PARTITION_ID) FROM PARTITION';
  EXECUTE IMMEDIATE stmt INTO max_pid;
  
  -- The default value for the end partition id is max(partition_id)
  IF (l_end_pid = -1) THEN
    l_end_pid := max_pid;
  END IF;

  -- Validate the starting partition id.
  IF ((start_pid < 0) OR (start_pid > max_pid)) THEN
    log_message('ERROR: Invald Start Partition ID '||start_pid||
      ', Valid Range (0,'||max_pid||')');
    utl_file.fclose(part_log_file);

    RAISE PARAMETER_ERROR;
  END IF;

  -- Validate the ending partition id.
  IF ((l_end_pid < start_pid) OR (l_end_pid > max_pid) OR (l_end_pid < 0)) THEN
    log_message('ERROR: Invald End Partition ID '||sdo_util.number_to_char(end_pid)||
      ', Valid Range ('||start_pid||','||max_pid||')');
    utl_file.fclose(part_log_file);

    RAISE PARAMETER_ERROR;
  END IF;

  version := get_version_info();
  log_message('******** Beginning partition validation');
  log_message('** Logfile location: ' || full_file_name, FALSE);
  log_message('** Routeserver data version: ' || version, FALSE);

  log_message('** Start partition id: ' || start_pid, FALSE);
  log_message('** End partition id: ' || l_end_pid, FALSE);
  IF (verbose) THEN
    log_message('** Verbose mode: TRUE', FALSE);
  ELSE
    log_message('** Verbose mode: FALSE', FALSE); 
  END IF;
  log_message('', FALSE); 

  -- Close the log file so the Java code can use it
  utl_file.fclose(part_log_file);

  -- Adjust the Oracle JVM maximum memory size to 800M. 
  -- This is the equivelent to specifying -Xmx800m to 
  -- java outside the database. Memory size is specified in bytes.
  ElocationSetJVMHeapSize(838860800);

  elocation_validate_partition(full_file_name, start_pid, l_end_pid, 
    verbose, is_10g(version));

  EXCEPTION
    WHEN PARAMETER_ERROR THEN
      raise_application_error(-20003, 'Error, partition validation failed, see log file.');
    WHEN OTHERS THEN
        IF (utl_file.is_open(part_log_file) = FALSE) THEN
          part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
        END IF;
        log_message(SQLERRM);
        utl_file.fclose(part_log_file);
        raise_application_error(-20003, 'Error, partition validation failed, see log file.');
END;

---
--- Entry point to partition the country border user data
---
PROCEDURE create_border_data(log_file_name IN VARCHAR2 := 'sdo_router_partition.log',
                             cleanup IN BOOLEAN DEFAULT TRUE)
IS
  TYPE CURSOR_TYPE IS REF CURSOR;
  full_file_name  VARCHAR2(256);
  stmt            VARCHAR2(4000);

BEGIN
  if (sdo_util.is_simple_file_name(log_file_name) = 'FALSE') then
      raise_application_error(-20020, '[ERROR] bad file name');
  end if;
  full_file_name := open_log_file(log_file_name);

  log_message('******** Begin generation of country border user data');
  log_message('** Logfile location: ' || full_file_name, FALSE);

  -- Make sure the raw border data exists
  IF (table_exists('ROUTER_BORDER_EDGES') = 'FALSE') THEN
    log_message('ERROR: ROUTER_BORDER_EDGES table not found');
    RETURN ;
  END IF;

  -- Make sure the EDGE table exists.
  -- Provides the partitioning information
  IF (table_exists('EDGE') = 'FALSE') THEN
    log_message('ERROR: EDGE table not found');
    RETURN ;
  END IF;

  -- Make sure the data version table exists
  IF (table_exists('SDO_ROUTER_DATA_VERSION') = 'FALSE') THEN
    log_message('ERROR: SDO_ROUTER_DATA_VERSION table not found');
    RETURN ;
  END IF;

  -- Drop the intermediate partitioned border data table
  IF (table_exists('PARTITIONED_ROUTER_BORDER_EDGE') = 'TRUE') THEN
    execute immediate 'DROP TABLE partitioned_router_border_edge';
  END IF;

  -- Create the intermediate border data partitioning table
  stmt := 'CREATE TABLE partitioned_router_border_edge PARALLEL 4 UNRECOVERABLE AS
    SELECT from_edge_id, from_country, to_edge_id, to_country, partition_id
    FROM  edge, router_border_edges
    WHERE to_edge_id=edge_id';
 
 EXECUTE IMMEDIATE stmt;

  -- Add highway edges to partition 0, the highway partition
  stmt := 'INSERT /*+ APPEND */ INTO partitioned_router_border_edge
    SELECT from_edge_id, from_country, to_edge_id, to_country, 0
    FROM router_border_edges
    WHERE to_edge_id IN
      (SELECT edge_id FROM edge WHERE func_class=1 OR func_class=2)';

  EXECUTE IMMEDIATE stmt;

  COMMIT;

  -- create an index on the partitioned data partition_id field
  EXECUTE IMMEDIATE 'CREATE INDEX prbe_pi_idx ON
    partitioned_router_border_edge(partition_id) PARALLEL (DEGREE 4)';
  EXECUTE IMMEDIATE 'CREATE INDEX prbe_teid_idx ON
    partitioned_router_border_edge(to_edge_id) PARALLEL (DEGREE 4)';

  -- Close the log file so the Java code can use it
  utl_file.fclose(part_log_file);

  -- Adjust the Oracle JVM maximum memory size to 800M.
  -- This is the equivelent to specifying -Xmx800m to
  -- java outside the database. Memory size is specified in bytes.
  ElocationSetJVMHeapSize(838860800);

BEGIN
  -- Java code to partition the border user data
  elocation_border_data(full_file_name);
EXCEPTION
  WHEN OTHERS THEN
    if (sdo_util.is_simple_file_name(log_file_name) = 'FALSE') then
        raise_application_error(-20020, '[ERROR] bad file name');
    end if;
    part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
    log_message(SQLERRM);
    utl_file.fclose(part_log_file);
    RAISE JAVA_ERROR;
END;

  -- Reopen the logfile
  part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');

  log_message('INFO: creating the country border user data table');

  -- Rename the new user data table and build an index on it.
  IF (table_exists('router_border_data')= 'TRUE') THEN
    EXECUTE IMMEDIATE 'DROP TABLE router_border_data';
  END IF;

  EXECUTE IMMEDIATE
    'RENAME new_border_data to router_border_data';

  log_message('INFO: create index on partition_id in the router_border_data table');
  EXECUTE IMMEDIATE 'CREATE INDEX rbd_p_idx on router_border_data(partition_id)';

  -- Border data specific intermediate table cleanup
  IF (cleanup) THEN
    IF (table_exists('new_border_data') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP TABLE new_border_data';
    END IF;
    IF (table_exists('partitioned_router_border_edge') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP TABLE partitioned_router_border_edge';
    END IF;
  END IF;
  log_message('******** Completed generation of country border user data ');

  -- Close the log file
  utl_file.fclose(part_log_file);
EXCEPTION
    WHEN JAVA_ERROR THEN
        raise_application_error(-20015, 'Oracle Router country border user data generation failed');
    WHEN OTHERS THEN
        IF (utl_file.is_open(part_log_file) = FALSE) THEN
        if (sdo_util.is_simple_file_name(log_file_name) = 'FALSE') then
            raise_application_error(-20020, '[ERROR] bad file name');
          end if;
          part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
        END IF;
        log_message(SQLERRM);
        utl_file.fclose(part_log_file);
        raise_application_error(-20015, 'Oracle Router country border user data generation failed');
END create_border_data;

--
-- Entry point to used to produce a dump of the country border BLOBs
--
PROCEDURE dump_border_data(
    log_file_name IN VARCHAR2 := 'sdo_router_partition.log',
    start_pid IN NUMBER DEFAULT 0,
    end_pid IN NUMBER DEFAULT -1)
IS
  full_file_name  VARCHAR2(256);
  l_end_pid       NUMBER := end_pid;
  max_pid         NUMBER;
  stmt            VARCHAR2(256);
  version         VARCHAR2(32);
BEGIN
  if (sdo_util.is_simple_file_name(log_file_name) = 'FALSE') then
      raise_application_error(-20020, '[ERROR] bad file name');
  end if;
  full_file_name := open_log_file(log_file_name);

  -- Make sure the table is actually there
  IF (table_exists('ROUTER_BORDER_DATA') = 'FALSE' ) THEN
    log_message('ERROR: Country border data dump failed, ROUTER_BORDER_DATA table not found');
    utl_file.fclose(part_log_file);

    RAISE PARAMETER_ERROR;
  END IF;

  stmt := 'SELECT MAX(PARTITION_ID) FROM ROUTER_BORDER_DATA';
  EXECUTE IMMEDIATE stmt INTO max_pid;

  -- The default value for the end partition id is max(partition_id)
  IF (l_end_pid = -1) THEN
    l_end_pid := max_pid;
  END IF;

  -- Validate the starting partition id.
  IF ((start_pid < 0) OR (start_pid > max_pid)) THEN
    log_message('ERROR: Invald Start Partition ID '||start_pid||', Valid Range (0,'||max_pid||')');
    utl_file.fclose(part_log_file);

    RAISE PARAMETER_ERROR;
  END IF;

  -- Validate the ending partition id.
  IF ((l_end_pid < start_pid) OR (l_end_pid > max_pid) OR (l_end_pid < 0)) THEN
    log_message('ERROR: Invald End Partition ID '||sdo_util.number_to_char(end_pid)||', Valid Range ('||start_pid||','||max_pid||')');
    utl_file.fclose(part_log_file);

    RAISE PARAMETER_ERROR;
  END IF;

  version := get_version_info();
  log_message('******** Beginning country border dump');
  log_message('** Logfile location: ' || full_file_name, FALSE);
  log_message('** Routeserver data version: ' || version, FALSE);
  log_message('** Start partition id: ' || start_pid, FALSE);
  log_message('** End partition id: ' || l_end_pid, FALSE);

  log_message('', FALSE);

  -- Close the log file so the Java code can use it
  utl_file.fclose(part_log_file);

  elocation_dump_border_data(full_file_name, start_pid, l_end_pid);

  EXCEPTION
    WHEN PARAMETER_ERROR THEN
      raise_application_error(-20004, 'Error, border data dump failed, see log file.');
    WHEN OTHERS THEN
        IF (utl_file.is_open(part_log_file) = FALSE) THEN
          if (sdo_util.is_simple_file_name(log_file_name) = 'FALSE') then
            raise_application_error(-20020, '[ERROR] bad file name');
          end if;
          part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
        END IF;
        log_message(SQLERRM);
        utl_file.fclose(part_log_file);
        raise_application_error(-20004, 'Error, border data dump failed, see log file.');
END dump_border_data;

---
--- Entry point to partition the edge user data
---
PROCEDURE create_timezone_data(log_file_name IN VARCHAR2 := 'sdo_router_partition.log',
                           cleanup IN BOOLEAN DEFAULT TRUE)
IS
  TYPE CURSOR_TYPE IS REF CURSOR;
  cursor1 CURSOR_TYPE;

  full_file_name  VARCHAR2(256);
  stmt            VARCHAR2(4000);
  tz_name         VARCHAR(32);
  tz_id NUMBER := 0;

BEGIN
  if (sdo_util.is_simple_file_name(log_file_name) = 'FALSE') then
      raise_application_error(-20020, '[ERROR] bad file name');
  end if;
  full_file_name := open_log_file(log_file_name);

  log_message('******** Begin generation of timezone user data');
  log_message('** Logfile location: ' || full_file_name, FALSE);

  -- Make sure the raw edge user data exists
  IF (table_exists('ROUTER_TIMEZONES_EDGES') = 'FALSE') THEN
    log_message('ERROR: ROUTER_TIMEZONES_EDGES table not found');
    RETURN ;
  END IF;

  -- Make sure the EDGE table exists.
  IF (table_exists('EDGE') = 'FALSE') THEN
    log_message('ERROR: EDGE table not found');
    RETURN ;
  END IF;

  -- Make sure the data version table exists
  IF (table_exists('SDO_ROUTER_DATA_VERSION') = 'FALSE') THEN
    log_message('ERROR: SDO_ROUTER_DATA_VERSION table not found');
    RETURN ;
  END IF;

  -- Drop the intermediate partitioned timezones table if it exists
  IF (table_exists('PARTITIONED_TIMEZONES_EDGES') = 'TRUE') THEN
    execute immediate 'DROP TABLE partitioned_timezones_edges';
  END IF;

  -- Drop the intermediate highway edges table if it exists
  IF (table_exists('ROUTER_HIGHWAY_EDGES') = 'TRUE') THEN
    execute immediate 'DROP TABLE router_highway_edges';
  END IF;

  -- Drop the Router timezones mapping table if it exists
  IF (table_exists('ROUTER_TIMEZONES') = 'TRUE') THEN
    execute immediate 'DROP TABLE router_timezones';
  END IF;

  -- Create the Routers Timezones mapping table
  EXECUTE IMMEDIATE 'CREATE TABLE router_timezones(timezone_id NUMBER, timezone_name VARCHAR(30))';

  stmt := 'INSERT INTO router_timezones VALUES (:id, :name)';

  OPEN cursor1 FOR 'SELECT distinct(tzid) FROM router_timezones_edges ORDER BY tzid';

  LOOP
    FETCH cursor1 into tz_name;
    EXIT WHEN cursor1%NOTFOUND;

    EXECUTE IMMEDIATE stmt USING tz_id, tz_name;
    tz_id := tz_id + 1;
  END LOOP;
  CLOSE cursor1;

  COMMIT;

  -- Create the intermediate timezones partitioning table. This table may
  -- contain duplicate edge ids. These are edge ids where the positive and
  -- negative versions are in different partitions. Both are needed.
  stmt := 'CREATE TABLE partitioned_timezones_edges PARALLEL 4 UNRECOVERABLE AS
    SELECT e.edge_id, e.partition_id, rt.timezone_id
    FROM  edge e, router_timezones_edges rte, router_timezones rt
    WHERE e.edge_id=rte.edge_id AND rt.timezone_name=rte.tzid';

  EXECUTE IMMEDIATE stmt;

  -- Find the highway edges
  stmt := 'CREATE TABLE router_highway_edges PARALLEL 4 UNRECOVERABLE AS
    SELECT edge_id
    FROM  edge
    WHERE func_class=1 OR func_class=2';

  EXECUTE IMMEDIATE stmt;

  -- Add the highway edges as partition 0, the highway partition
  stmt := 'INSERT /*+ APPEND */ INTO partitioned_timezones_edges
    SELECT rh.edge_id, 0, rt.timezone_id
    FROM router_highway_edges rh, router_timezones_edges rte, router_timezones rt
    WHERE rte.edge_id=rh.edge_id AND rt.timezone_name=rte.tzid';

  EXECUTE IMMEDIATE stmt;

  COMMIT;

  -- useful indexes on the partitioned timezone data
  EXECUTE IMMEDIATE 'CREATE INDEX pte_eid_idx ON
    partitioned_timezones_edges(edge_id) PARALLEL (DEGREE 4)';
  EXECUTE IMMEDIATE 'CREATE INDEX pte_pid_idx ON
    partitioned_timezones_edges(partition_id) PARALLEL (DEGREE 4)';
  EXECUTE IMMEDIATE 'CREATE INDEX pte_tzid_idx ON
    partitioned_timezones_edges(timezone_id) PARALLEL (DEGREE 4)';

  -- Close the log file so the Java code can use it
  utl_file.fclose(part_log_file);

  -- Adjust the Oracle JVM maximum memory size to 800M.
  -- This is the equivelent to specifying -Xmx800m to
  -- java outside the database. Memory size is specified in bytes.
  ElocationSetJVMHeapSize(838860800);

BEGIN
  -- Java code to partition the timezone user data
  elocation_timezone_data(full_file_name);
EXCEPTION
  WHEN OTHERS THEN
    if (sdo_util.is_simple_file_name(log_file_name) = 'FALSE') then
      raise_application_error(-20020, '[ERROR] bad file name');
    end if;
    part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
    log_message(SQLERRM);
    utl_file.fclose(part_log_file);
    RAISE JAVA_ERROR;
END;

  -- Reopen the logfile
  part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');

  log_message('INFO: creating the final timezone user data table');

  -- Rename the new timezone user data table and build an index on it.
  IF (table_exists('router_timezone_data')= 'TRUE') THEN
    EXECUTE IMMEDIATE 'DROP TABLE router_timezone_data';
  END IF;

  EXECUTE IMMEDIATE
    'RENAME new_timezone_data to router_timezone_data';

  log_message('INFO: create index rtd_p_idx on router_timezone_data table');
  EXECUTE IMMEDIATE 'CREATE INDEX rtd_p_idx on router_timezone_data(partition_id)';

  -- Edge user data specific intermediate table cleanup
  IF (cleanup) THEN
    IF (table_exists('new_timezone_data') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP TABLE new_timezone_data';
    END IF;
    IF (table_exists('partitioned_timezones_edges') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP TABLE partitioned_timezones_edges';
    END IF;
    IF (table_exists('router_highway_edges') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP TABLE router_highway_edges';
    END IF;
  END IF;
END create_timezone_data;


--
-- Entry point to used to produce a dump of the edge user data BLOBs
--
PROCEDURE dump_timezone_data(
    log_file_name IN VARCHAR2 := 'sdo_router_partition.log',
    start_pid IN NUMBER DEFAULT 0,
    end_pid IN NUMBER DEFAULT -1)
IS
  full_file_name  VARCHAR2(256);
  l_end_pid       NUMBER := end_pid;
  max_pid         NUMBER;
  stmt            VARCHAR2(256);
  version         VARCHAR2(32);
BEGIN
  if (sdo_util.is_simple_file_name(log_file_name) = 'FALSE') then
      raise_application_error(-20020, '[ERROR] bad file name');
  end if;
  full_file_name := open_log_file(log_file_name);

  -- Make sure the table is actually there
  IF (table_exists('ROUTER_TIMEZONE_DATA') = 'FALSE' ) THEN
    log_message('ERROR: Timezone user data dump failed, ROUTER_TIMEZONE_DATA table not found');
    utl_file.fclose(part_log_file);

    RAISE PARAMETER_ERROR;
  END IF;

  stmt := 'SELECT MAX(PARTITION_ID) FROM ROUTER_TIMEZONE_DATA';
  EXECUTE IMMEDIATE stmt INTO max_pid;

  -- The default value for the end partition id is max(partition_id)
  IF (l_end_pid = -1) THEN
    l_end_pid := max_pid;
  END IF;

  -- Validate the starting partition id.
  IF ((start_pid < 0) OR (start_pid > max_pid)) THEN
    log_message('ERROR: Invald Start Partition ID '||start_pid||', Valid Range (0,'||max_pid||')');
    utl_file.fclose(part_log_file);

    RAISE PARAMETER_ERROR;
  END IF;

  -- Validate the ending partition id.
  IF ((l_end_pid < start_pid) OR (l_end_pid > max_pid) OR (l_end_pid < 0)) THEN
    log_message('ERROR: Invald End Partition ID '||sdo_util.number_to_char(end_pid)||', Valid Range ('||start_pid||','||max_pid||')');
    utl_file.fclose(part_log_file);

    RAISE PARAMETER_ERROR;
  END IF;

  version := get_version_info();
  log_message('******** Beginning timezone user data dump');
  log_message('** Logfile location: ' || full_file_name, FALSE);
  log_message('** Routeserver data version: ' || version, FALSE);
  log_message('** Start partition id: ' || start_pid, FALSE);
  log_message('** End partition id: ' || l_end_pid, FALSE);

  log_message('', FALSE);

  -- Close the log file so the Java code can use it
  utl_file.fclose(part_log_file);

  elocation_dump_timezone_data(full_file_name, start_pid, l_end_pid);

  EXCEPTION
    WHEN PARAMETER_ERROR THEN
      raise_application_error(-20004, 'Error, edge data dump failed, see log file.');
    WHEN OTHERS THEN
        IF (utl_file.is_open(part_log_file) = FALSE) THEN
        if (sdo_util.is_simple_file_name(log_file_name) = 'FALSE') then
           raise_application_error(-20020, '[ERROR] bad file name');
         end if;
          part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
        END IF;
        log_message(SQLERRM);
        utl_file.fclose(part_log_file);
        raise_application_error(-20004, 'Error, edge data dump failed, see log file.');
END dump_timezone_data;

---
--- Entry point to partition the Routers trucking data
---
PROCEDURE create_trucking_data(log_file_name IN VARCHAR2 := 'sdo_router_partition.log',
                               cleanup IN BOOLEAN DEFAULT TRUE)
IS
  TYPE CURSOR_TYPE IS REF CURSOR;
  full_file_name  VARCHAR2(256);
  stmt            VARCHAR2(4000);
BEGIN
  full_file_name := open_log_file(log_file_name);
  
  log_message('******** Begin generation of trucking user data');
  log_message('** Logfile location: ' || full_file_name, FALSE);

  -- Make sure the raw truck data exists and is in Router format
  IF (table_exists('router_transport') = 'FALSE') THEN
    log_message('ERROR: ROUTER_TRANSPORT table not found');
    RETURN ;
  END IF;

  -- Make sure the Edge table exists
  IF (table_exists('EDGE') = 'FALSE') THEN
    log_message('ERROR: EDGE table not found');
    RETURN ;
  END IF;

  -- Make sure the data version table exists
  IF (table_exists('sdo_router_data_version') = 'FALSE') THEN
    log_message('ERROR: SDO_ROUTER_DATA_VERSION table not found');
    RETURN ;
  END IF;
  
  -- Cleanup the old intermediate truck data partitioning table
  IF (table_exists('router_partitioned_truck_data') = 'TRUE') THEN
    execute immediate 'DROP TABLE router_partitioned_truck_data';
  END IF;

  -- Cleanup the intermediate truck data partitioning table
  IF (table_exists('partitioned_router_transport') = 'TRUE') THEN
    execute immediate 'DROP TABLE partitioned_router_transport';
  END IF;

  -- Create truck data intermediate partitioning table
  stmt := 'CREATE TABLE partitioned_router_transport PARALLEL 4 UNRECOVERABLE AS
    SELECT r.edge_id, partition_id, maintype, subtype, value
    FROM edge e, router_transport r
    WHERE e.edge_id=r.edge_id';

  EXECUTE IMMEDIATE stmt;

  -- Add highway edges to partition 0, the highway partition
  stmt := 'INSERT /*+ APPEND */ INTO partitioned_router_transport 
    SELECT edge_id, 0, maintype, subtype, value
    FROM router_transport
    WHERE edge_id IN 
      (SELECT edge_id FROM edge WHERE func_class=1 OR func_class=2)';

  EXECUTE IMMEDIATE stmt;

  COMMIT;

  -- create an index on the partitioned datas partition_id field
  EXECUTE IMMEDIATE 'CREATE INDEX prt_p_idx ON partitioned_router_transport(partition_id)';

  -- Close the log file so the Java code can use it
  utl_file.fclose(part_log_file);

  -- Adjust the Oracle JVM maximum memory size to 800M. 
  -- This is the equivelent to specifying -Xmx800m to 
  -- java outside the database. Memory size is specified in bytes.
  ElocationSetJVMHeapSize(838860800);

BEGIN
  -- Java code to partition the trucking data
  elocation_trucking_data(full_file_name);
EXCEPTION
  WHEN OTHERS THEN
    if (sdo_util.is_simple_file_name(log_file_name) = 'FALSE') then
      raise_application_error(-20020, '[ERROR] bad file name');
    end if;
    part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
    log_message(SQLERRM);
    utl_file.fclose(part_log_file);
    RAISE JAVA_ERROR;
END;

  -- Reopen the logfile
  part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');

  log_message('INFO: creating the final trucking user data table');

  -- Rename the new partition table and build an index on it.
  IF (table_exists('trucking_user_data')= 'TRUE') THEN
    EXECUTE IMMEDIATE 'DROP TABLE trucking_user_data';
  END IF;

  -- Rename the new partition table and build an index on it.
  IF (table_exists('router_trucking_data')= 'TRUE') THEN
    EXECUTE IMMEDIATE 'DROP TABLE router_trucking_data';
  END IF;

  EXECUTE IMMEDIATE 'RENAME new_trucking_data to router_trucking_data';

  log_message('INFO: create index rtud_p_idx on router_trucking_data table');
  EXECUTE IMMEDIATE 'CREATE INDEX rtud_p_idx on router_trucking_data(partition_id)';

  -- Trucking specific intermediate table cleanup
  IF (cleanup) THEN
    IF (table_exists('partitioned_router_transport') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP TABLE partitioned_router_transport';
    END IF;
    IF (table_exists('new_trucking_data') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP TABLE new_trucking_data';
    END IF;
  END IF;

  log_message('******** Completed generation of trucking user data ');

  -- Close the log file
  utl_file.fclose(part_log_file);
EXCEPTION
    WHEN JAVA_ERROR THEN
        raise_application_error(-20015, 'Oracle Router trucking user data generation failed');
    WHEN OTHERS THEN
        IF (utl_file.is_open(part_log_file) = FALSE) THEN
         if (sdo_util.is_simple_file_name(log_file_name) = 'FALSE') then
            raise_application_error(-20020, '[ERROR] bad file name');
          end if;
          part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
        END IF;
        log_message(SQLERRM);
        utl_file.fclose(part_log_file);
        raise_application_error(-20015, 'Oracle Router trucking user data generation failed');
END create_trucking_data;

-- Wrapper around the new trucking user data generation.
PROCEDURE create_trucking_user_data(log_file_name IN VARCHAR2 := 'sdo_router_partition.log',
                                  cleanup IN BOOLEAN DEFAULT TRUE)
IS
BEGIN
  create_trucking_data(log_file_name, cleanup);
END create_trucking_user_data;

--
-- Entry point to used to produce a dump of the trucking data BLOBs
--
PROCEDURE dump_trucking_data(
    log_file_name IN VARCHAR2 := 'sdo_router_partition.log', 
    start_pid IN NUMBER DEFAULT 0, 
    end_pid IN NUMBER DEFAULT -1,
    skip_unsupported IN BOOLEAN DEFAULT TRUE)
IS
  full_file_name  VARCHAR2(256);
  l_end_pid       NUMBER := end_pid;
  max_pid         NUMBER;
  stmt            VARCHAR2(256);
  version         VARCHAR2(32);
BEGIN
  full_file_name := open_log_file(log_file_name);
  
  -- Make sure the table is actually there
  IF (table_exists('ROUTER_TRUCKING_DATA') = 'FALSE' ) THEN
    log_message('ERROR: Trucking data dump failed, ROUTER_TRUCKING_DATA table not found');
    utl_file.fclose(part_log_file);

    RAISE PARAMETER_ERROR;
  END IF;

  stmt := 'SELECT MAX(PARTITION_ID) FROM ROUTER_TRUCKING_DATA';
  EXECUTE IMMEDIATE stmt INTO max_pid;
  
  -- The default value for the end partition id is max(partition_id)
  IF (l_end_pid = -1) THEN
    l_end_pid := max_pid;
  END IF;

  -- Validate the starting partition id.
  IF ((start_pid < 0) OR (start_pid > max_pid)) THEN
    log_message('ERROR: Invald Start Partition ID '||start_pid||', Valid Range (0,'||max_pid||')');
    utl_file.fclose(part_log_file);

    RAISE PARAMETER_ERROR;
  END IF;

  -- Validate the ending partition id.
  IF ((l_end_pid < start_pid) OR (l_end_pid > max_pid) OR (l_end_pid < 0)) THEN
    log_message('ERROR: Invald End Partition ID '||sdo_util.number_to_char(end_pid)||', Valid Range ('||start_pid||','||max_pid||')');
    utl_file.fclose(part_log_file);

    RAISE PARAMETER_ERROR;
  END IF;

  version := get_version_info();
  log_message('******** Beginning trucking data dump');
  log_message('** Logfile location: ' || full_file_name, FALSE);
  log_message('** Routeserver data version: ' || version, FALSE);
  log_message('** Start partition id: ' || start_pid, FALSE);
  log_message('** End partition id: ' || l_end_pid, FALSE);

  log_message('', FALSE); 

  -- Close the log file so the Java code can use it
  utl_file.fclose(part_log_file);

  elocation_dump_trucking_data(full_file_name, start_pid, l_end_pid, skip_unsupported);

  EXCEPTION
    WHEN PARAMETER_ERROR THEN
      raise_application_error(-20004, 'Error, trucking data dump failed, see log file.');
    WHEN OTHERS THEN
        IF (utl_file.is_open(part_log_file) = FALSE) THEN
          if (sdo_util.is_simple_file_name(log_file_name) = 'FALSE') then
            raise_application_error(-20020, '[ERROR] bad file name');
          end if;
          part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
        END IF;
        log_message(SQLERRM);
        utl_file.fclose(part_log_file);
        raise_application_error(-20004, 'Error, trucking data dump failed, see log file.');
END dump_trucking_data;

---
--- Entry point to partition the Routers turn restriction data
---
PROCEDURE create_turn_restriction_data(log_file_name IN VARCHAR2 := 'sdo_router_partition.log',
                                       cleanup IN BOOLEAN DEFAULT TRUE)
IS
  TYPE CURSOR_TYPE IS REF CURSOR;
  full_file_name  VARCHAR2(256);
  stmt            VARCHAR2(4000);
BEGIN
  full_file_name := open_log_file(log_file_name);
  
  log_message('******** Begin generation of turn restriction user data');
  log_message('** Logfile location: ' || full_file_name, FALSE);

  -- Make sure the raw turn restriction data exists
  IF (table_exists('ROUTER_NAV_STRAND') = 'FALSE') THEN
    log_message('ERROR: ROUTER_NAV_STRAND table not found');
    RETURN ;
  END IF;

  -- Make sure the Edge table exists.
  IF (table_exists('EDGE') = 'FALSE') THEN
    log_message('ERROR: EDGE table not found');
    RETURN ;
  END IF;

  -- Make sure the data version table exists
  IF (table_exists('SDO_ROUTER_DATA_VERSION') = 'FALSE') THEN
    log_message('ERROR: SDO_ROUTER_DATA_VERSION table not found');
    RETURN ;
  END IF;

  -- Make sure the router_condition table exists
  IF (table_exists('ROUTER_CONDITION') = 'FALSE') THEN
    log_message('ERROR: ROUTER_CONDITION table not found');
    RETURN ;
  END IF;
  
  -- Drop the intermediate partitioned turn restriction table
  IF (table_exists('PARTITIONED_ROUTER_NAV_STRAND') = 'TRUE') THEN
    execute immediate 'DROP TABLE partitioned_router_nav_strand';
  END IF;

  -- Create the intermediate restricted driving maneuvers partitioning table
  stmt := 'CREATE TABLE partitioned_router_nav_strand PARALLEL 4 UNRECOVERABLE AS
    SELECT nav_strand_id, seq_num, link_id, node_id, partition_id
    FROM  edge, router_nav_strand
    WHERE edge_id=link_id';

  EXECUTE IMMEDIATE stmt;

  -- Add highway edges to partition 0, the highway partition
  stmt := 'INSERT /*+ APPEND */ INTO partitioned_router_nav_strand
    SELECT nav_strand_id, seq_num, link_id, node_id, 0
    FROM router_nav_strand
    WHERE link_id IN 
      (SELECT edge_id FROM edge WHERE func_class=1 OR func_class=2)';

  EXECUTE IMMEDIATE stmt;

  COMMIT;

  -- create an index on the partitioned datas partition_id field
  EXECUTE IMMEDIATE 'CREATE INDEX prns_pi_idx ON 
    partitioned_router_nav_strand(partition_id) PARALLEL (DEGREE 4)';
  EXECUTE IMMEDIATE 'CREATE INDEX prns_li_idx ON 
    partitioned_router_nav_strand(link_id) PARALLEL (DEGREE 4)';
  EXECUTE IMMEDIATE 'CREATE INDEX prns_nsi_sn_idx ON 
    partitioned_router_nav_strand(nav_strand_id, seq_num) PARALLEL (DEGREE 4)';

  -- Drop the current look up table if it exists
  IF (table_exists('router_max_seq') = 'TRUE') THEN
    execute immediate 'DROP TABLE router_max_seq';
  END IF;

  -- create a lookup table to associate the max sequence number to a strand id
  stmt := 'CREATE TABLE router_max_seq
    (nav_strand_id, max_seq_num,  
      CONSTRAINT pk_max_seq PRIMARY KEY(nav_strand_id)) 
    ORGANIZATION INDEX
    PARALLEL 4 UNRECOVERABLE AS 
      SELECT nav_strand_id, max(seq_num) 
      FROM router_nav_strand GROUP BY nav_strand_id';

  EXECUTE IMMEDIATE stmt;

  -- Close the log file so the Java code can use it
  utl_file.fclose(part_log_file);

  -- Adjust the Oracle JVM maximum memory size to 800M. 
  -- This is the equivelent to specifying -Xmx800m to 
  -- java outside the database. Memory size is specified in bytes.
  ElocationSetJVMHeapSize(838860800);

BEGIN
  -- Java code to partition the restricted driving maneuver data
  elocation_turn_restrict_data(full_file_name);
EXCEPTION
  WHEN OTHERS THEN
    if (sdo_util.is_simple_file_name(log_file_name) = 'FALSE') then
      raise_application_error(-20020, '[ERROR] bad file name');
    end if;
    part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
    log_message(SQLERRM);
    utl_file.fclose(part_log_file);
    RAISE JAVA_ERROR;
END;

  -- Reopen the logfile
  part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');

  log_message('INFO: creating the final turn restriction user data table');

  -- Rename the new partition table and build an index on it.
  IF (table_exists('router_turn_restriction_data')= 'TRUE') THEN
    EXECUTE IMMEDIATE 'DROP TABLE router_turn_restriction_data';
  END IF;

  EXECUTE IMMEDIATE 
    'RENAME new_turn_restriction_data to router_turn_restriction_data';

  log_message('INFO: create index rtrud_p_idx on router_turn_restriction_data table');
  EXECUTE IMMEDIATE 'CREATE INDEX rtrud_p_idx on router_turn_restriction_data(partition_id)';

  -- Turn restriction specific intermediate table cleanup
  IF (cleanup) THEN
    IF (table_exists('new_turn_restriction_data') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP TABLE new_turn_restriction_data';
    END IF;
    IF (table_exists('partitioned_router_nav_strand') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP TABLE partitioned_router_nav_strand';
    END IF;
    IF (table_exists('router_max_seq') = 'TRUE') THEN
      EXECUTE IMMEDIATE 'DROP TABLE router_max_seq';
    END IF;
  END IF;

  log_message('******** Completed generation of turn restriction user data ');

  -- Close the log file
  utl_file.fclose(part_log_file);
EXCEPTION
    WHEN JAVA_ERROR THEN
        raise_application_error(-20015, 'Oracle Router turn restriction user data generation failed');
    WHEN OTHERS THEN
        IF (utl_file.is_open(part_log_file) = FALSE) THEN
          if (sdo_util.is_simple_file_name(log_file_name) = 'FALSE') then
             raise_application_error(-20020, '[ERROR] bad file name');
          end if;
          part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
        END IF;
        log_message(SQLERRM);
        utl_file.fclose(part_log_file);
        raise_application_error(-20015, 'Oracle Router turn restriction user data generation failed');
END create_turn_restriction_data;

--
-- Entry point to used to produce a dump of the turn restriction BLOBs
--
PROCEDURE dump_turn_restriction_data(
    log_file_name IN VARCHAR2 := 'sdo_router_partition.log', 
    start_pid IN NUMBER DEFAULT 0, 
    end_pid IN NUMBER DEFAULT -1,
    dump_soft_restrictions IN BOOLEAN DEFAULT FALSE)
IS
  full_file_name  VARCHAR2(256);
  l_end_pid       NUMBER := end_pid;
  max_pid         NUMBER;
  stmt            VARCHAR2(256);
  version         VARCHAR2(32);
BEGIN
  full_file_name := open_log_file(log_file_name);
  
  -- Make sure the table is actually there
  IF (table_exists('ROUTER_TURN_RESTRICTION_DATA') = 'FALSE' ) THEN
    log_message('ERROR: Turn restriction dump failed, ROUTER_TURN_RESTRICTION_DATA table not found');
    utl_file.fclose(part_log_file);

    RAISE PARAMETER_ERROR;
  END IF;

  stmt := 'SELECT MAX(PARTITION_ID) FROM ROUTER_TURN_RESTRICTION_DATA';
  EXECUTE IMMEDIATE stmt INTO max_pid;
  
  -- The default value for the end partition id is max(partition_id)
  IF (l_end_pid = -1) THEN
    l_end_pid := max_pid;
  END IF;

  -- Validate the starting partition id.
  IF ((start_pid < 0) OR (start_pid > max_pid)) THEN
    log_message('ERROR: Invald Start Partition ID '||start_pid||', Valid Range (0,'||max_pid||')');
    utl_file.fclose(part_log_file);

    RAISE PARAMETER_ERROR;
  END IF;

  -- Validate the ending partition id.
  IF ((l_end_pid < start_pid) OR (l_end_pid > max_pid) OR (l_end_pid < 0)) THEN
    log_message('ERROR: Invald End Partition ID '||sdo_util.number_to_char(end_pid)||', Valid Range ('||start_pid||','||max_pid||')');
    utl_file.fclose(part_log_file);

    RAISE PARAMETER_ERROR;
  END IF;

  version := get_version_info();
  log_message('******** Beginning turn restriction dump');
  log_message('** Logfile location: ' || full_file_name, FALSE);
  log_message('** Routeserver data version: ' || version, FALSE);
  log_message('** Start partition id: ' || start_pid, FALSE);
  log_message('** End partition id: ' || l_end_pid, FALSE);
  IF (dump_soft_restrictions) THEN
    log_message('** Dumping soft restrictions: TRUE', FALSE);
  ELSE
    log_message('** Dumping soft restrictions: FALSE', FALSE); 
  END IF;

  log_message('', FALSE); 

  -- Close the log file so the Java code can use it
  utl_file.fclose(part_log_file);

  elocation_dump_turn_restrict(full_file_name, start_pid, l_end_pid, dump_soft_restrictions);

  EXCEPTION
    WHEN PARAMETER_ERROR THEN
      raise_application_error(-20004, 'Error, turn restrictions dump failed, see log file.');
    WHEN OTHERS THEN
        IF (utl_file.is_open(part_log_file) = FALSE) THEN
          if (sdo_util.is_simple_file_name(log_file_name) = 'FALSE') then
             raise_application_error(-20020, '[ERROR] bad file name');
          end if;
          part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
        END IF;
        log_message(SQLERRM);
        utl_file.fclose(part_log_file);
        raise_application_error(-20004, 'Error, turn restriction dump failed, see log file.');
END dump_turn_restriction_data;

--
-- Entry point to cleanup tables used for debugging
--
PROCEDURE cleanup_router(all_tables IN BOOLEAN)
IS
BEGIN
  -- Cleanup all temporary tables.
  clean_tables(all_tables);
END;

--
-- Entry point to used to get the Router data version
--
PROCEDURE get_version(log_file_name IN VARCHAR2 := 'sdo_router_partition.log')
IS
  data_version    VARCHAR2(32);
  full_file_name  VARCHAR2(256);
BEGIN
  full_file_name := open_log_file(log_file_name);

  data_version := get_version_info();

  dbms_output.put_line('Routeserver: data version '|| data_version);
  log_message('INFO: Routeserver data version: ' || data_version);

  utl_file.fclose(part_log_file);
END;

--
-- main pl/sql procedure to partition a graph with coordinate information
-- for recovering with 
--
PROCEDURE recover_graph_partition(p_tab_name IN VARCHAR2, 
                                  min_pid IN NUMBER,
                                  max_pid IN NUMBER,
                                  p_level IN NUMBER,
                                  max_v_no IN NUMBER, 
                                  make_equal IN BOOLEAN)
IS
  stmt      VARCHAR2(256);
  v_no      NUMBER;
  pid       NUMBER;
  p_counter NUMBER;
  tmp_p_tab_name VARCHAR2(130);
BEGIN
  p_counter := max_pid+1; -- starting partition counter
  pid := min_pid;

  -- start logging
  log_message('INFO: starting recovery of '|| p_tab_name || ' partitioning' || 
    ' partition level:' || p_level || ' min(partition id):' || min_pid || 
      ' max(partition id)' || max_pid);

  FOR k IN min_pid..max_pid LOOP
    FOR i IN 0..p_level LOOP
      FOR j IN 1..power(2,i) LOOP
        tmp_p_tab_name := p_tab_name || '_' || pid;

        IF (table_exists(tmp_p_tab_name) = 'TRUE' ) THEN 
          new_partition_proc(p_tab_name, max_v_no,pid,make_equal,p_counter);

          log_message('INFO:    partitioning '|| p_tab_name || 
            ' level: ' || j || ' partition id: ' || pid);
        ELSE
          p_counter := p_counter + 2;
        END IF; 

        pid := pid +1;
      end loop;
    end loop;
  end loop;

  -- copy the result back to original table and ajust the pids
  adjust_final_pid(SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(p_tab_name));

  log_message('INFO: completed recovery of '|| p_tab_name || ' partitioning');

EXCEPTION
  WHEN OTHERS THEN
    log_message(SQLERRM);
    log_message('ERROR: exception recovering partition '|| pid ||
      ' of the ' || p_tab_name || ' table');
    raise_application_error(-20010, 'Error Recovering Graph Partitioning');
END recover_graph_partition;

 PROCEDURE elocation_partition_router (logfile_name  IN VARCHAR2,
                                        blob_format IN VARCHAR2)
  IS
  BEGIN
    SDO_JAVA_STP.elocation_partition_router(logfile_name, blob_format);
  END;


  PROCEDURE elocation_border_data (logfile_name IN VARCHAR2)
  IS
  BEGIN
    SDO_JAVA_STP.elocation_border_data(logfile_name);
  END;


 PROCEDURE elocation_timezone_data (logfile_name IN VARCHAR2)
  IS
  BEGIN
    SDO_JAVA_STP.elocation_timezone_data(logfile_name);
  END;

  PROCEDURE elocation_trucking_data (logfile_name IN VARCHAR2)
  IS
  BEGIN
    SDO_JAVA_STP.elocation_trucking_data(logfile_name);
  END;

  PROCEDURE elocation_turn_restrict_data (logfile_name IN VARCHAR2)
  IS
  BEGIN
    SDO_JAVA_STP.elocation_turn_restrict_data(logfile_name);
  END;

  PROCEDURE elocation_dump_partition (logfile_name in VARCHAR2,
                                      start_pid in NUMBER,
                                      end_pid in NUMBER,
                                      verbose in BOOLEAN,
                                      is10g in BOOLEAN)
  IS
  BEGIN
    SDO_JAVA_STP.elocation_dump_partition(logfile_name, start_pid, end_pid, 
                                          verbose, is10g);
  END;

  PROCEDURE elocation_dump_border_data (logfile_name IN VARCHAR2,
                                  start_pid IN NUMBER,
                                  end_pid IN NUMBER)
  IS
  BEGIN
    SDO_JAVA_STP.elocation_dump_border_data(logfile_name, start_pid, end_pid);
  END;

  PROCEDURE elocation_dump_timezone_data (logfile_name IN VARCHAR2,
                                         start_pid IN NUMBER,
                                         end_pid IN NUMBER)
  IS
  BEGIN
    SDO_JAVA_STP.elocation_dump_timezone_data(logfile_name,start_pid,end_pid);
  END;

  PROCEDURE elocation_dump_trucking_data (logfile_name IN VARCHAR2,
                                         start_pid IN NUMBER,
                                         end_pid IN NUMBER,
                                         skip_unsupported IN BOOLEAN)
  IS
  BEGIN
    SDO_JAVA_STP.elocation_dump_trucking_data(logfile_name, start_pid, 
                                     end_pid, skip_unsupported);
  END;

  PROCEDURE elocation_dump_turn_restrict (logfile_name IN VARCHAR2,
                                         start_pid IN NUMBER,
                                         end_pid IN NUMBER,
                                         dump_soft_restrictions IN BOOLEAN)
  IS
  BEGIN
    SDO_JAVA_STP.elocation_dump_turn_restrict(logfile_name, start_pid, end_pid,
                                                 dump_soft_restrictions);
  END;

  PROCEDURE elocation_validate_partition (logfile_name in VARCHAR2,
                                          start_pid in NUMBER,
                                          end_pid in NUMBER,
                                          verbose in BOOLEAN,
                                          is10g in BOOLEAN)
  IS
  BEGIN
    SDO_JAVA_STP.elocation_validate_partition(logfile_name, start_pid, end_pid,
                                               verbose, is10g);
  END;

  PROCEDURE build_turn_restrictions (logdir in VARCHAR2,
                                     drivingside in VARCHAR2)
  IS
  BEGIN
    SDO_JAVA_STP.build_turn_restrictions(logdir, drivingside);
  END;

  PROCEDURE elocation_validate_logfile(logfile_name  IN VARCHAR2,
                                     schema_name IN VARCHAR2)
  IS
  BEGIN
    SDO_JAVA_STP.elocation_validate_logfile(logfile_name, schema_name);
  END;



--
-- Unused procedures and functions to delete
--
--

--
-- End unused procedures and functions
-- 

END SDO_ROUTER_PARTITION;
/

show errors;

grant execute on SDO_ROUTER_PARTITION to public;
create or replace  public synonym SDO_ROUTER_PARTITION for 
  MDSYS.SDO_ROUTER_PARTITION;

grant execute on ELOCATION_EDGE_LINK_LEVEL to public;
create or replace  public synonym ELOCATION_EDGE_LINK_LEVEL for
  MDSYS.ELOCATION_EDGE_LINK_LEVEL;

commit;

Rem ********************************************************************
Rem #16473696: Indicate Oracle-Supplied object
@?/rdbms/admin/sqlsessend.sql
Rem ********************************************************************


OHA YOOOO