2008年2月20日星期三

authenticate_parameters connection event in ASA10(SQL Anywhere10.0.1)

Receives values from the remote that can be used to authenticate beyond a user ID and password. The values can also be used to arbitrarily customize each synchronization.

Parameters

In the following table, the description provides the SQL data type. If you are writing your script in Java or .NET, you should use the appropriate corresponding data type. See SQL-Java data types and SQL-.NET data types.

In SQL scripts, you can specify event parameters by name or with a question mark, but you cannot mix names and question marks within a script. If you use question marks, the parameters must be in the order shown below and are optional only if no subsequent parameters are specified (for example, you must use parameter 1 if you want to use parameter 2). If you use named parameters, you can specify any subset of the parameters in any order.

Parameter name for SQL scripts

Description

Order

s.authentication_status

INTEGER. This is an INOUT parameter.

1

s.remote_id VARCHAR(128). The MobiLink remote ID. You can only reference the remote ID if you are using named parameters. Not applicable

s.username

VARCHAR(128). The MobiLink user name.

2

a.N (one or more)

VARCHAR(128). For example, named parameters could be a.1 a.2.

3...

Parameter Description
  • authentication_status  The authentication_status parameter is required. It indicates the overall success of the authentication, and can be set to one of the following values:

    Returned Value

    authentication_status

    Description

    V <= 1999

    1000

    Authentication succeeded.

    1999 < V <= 2999

    2000

    Authentication succeeded, but password expiring soon.

    2999 < V <= 3999

    3000

    Authentication failed as password has expired.

    3999 < V <= 4999

    4000

    Authentication failed.

    4999 < V <= 5999

    5000

    Authentication failed as user is already synchronizing.

    5999 < V

    4000

    If the returned value is greater than 5999, MobiLink interprets it as a returned value of 4000 (authentication failed).

  • username  This parameter is the MobiLink user name. VARCHAR(128).

  • remote_ID   The MobiLink remote ID. You can only reference the remote ID if you are using named parameters.

    See Using remote IDs and MobiLink user names in scripts.

  • remote_parameters  The number of remote parameters must match the number expected or an error results. An error also occurs if parameters are sent from the client and there is no script for this event.

Remarks

You can send strings (or parameters in the form of strings) from both SQL Anywhere and UltraLite clients. This allows you to have authentication beyond a user ID and password. It also means that you can customize your synchronization based on the value of parameters, and do this in a pre-synchronization phase, during authentication.

The MobiLink server executes this event upon starting each synchronization. It is executed in the same transaction as the authenticate_user event.

You can use this event to replace the built-in MobiLink authentication mechanism with a custom mechanism. You may want to call into the authentication mechanism of your DBMS, or you may want to implement features not present in the MobiLink built-in mechanism.

If the authenticate_user or authenticate_user_hashed scripts are invoked and return an error, this event is not called.

SQL scripts for the authenticate_parameters event must be implemented as stored procedures.

我的hexdb manager简单界面

使用SQLite-JDBC的时候,碰到了命令行方式下和java-jdbc方式下插入数据相互读取出现乱码的情况。

现在还在完善,想做一个通用的可以管理常见的几种数据库。
同时也能实现常用的ETL功能。

Handling MobiLink server errors in Java through Implementing LogListener Interface

When scanning the log is not sufficient, you can monitor your applications programmatically. For example, you can send messages of a certain type in an email.
You can write methods that are passed a class representing every error or warning message that is printed to the log. This may help you monitor and audit a MobiLink server.
The following code installs a LogListener for all warning messages, and writes the information to a file.



class TestLogListener implements LogListener {
  FileOutputStream _out_file;
  public TestLogListener( FileOutputStream out_file ) {
    _out_file       = out_file;
  }

  public void messageLogged(  ServerContext   sc,
    LogMessage msg ) {
    String  type;
    String  user;
    try {
      if(msg.getType() == LogMessage.ERROR) {
        type = "ERROR";
      } else if(msg.getType() == LogMessage.WARNING) {
        type = "WARNING";
      } else {
        type = "UNKNOWN!!!";
      }

      user = msg.getUser();
      if( user == null ) {
        user = "NULL";
      }
      _out_file.write(
        ("Caught msg type=" + type +
         " user=" + user +
         " text=" +msg.getText() +
         "\n").getBytes() );
      _out_file.flush();
    } catch( Exception e ) {
      // Print some error output to the MobiLink log.
      e.printStackTrace();
    }
  }
}

 




The following code registers TestLogListener to receive warning messages. Call this code from anywhere that has access to the ServerContext such as a class constructor or synchronization script.
// ServerContext serv_context; serv_context.addWarningListener(       new MyLogListener( ll_out_file ));




========================
http://iihero.8800.org/
========================
Regards,
Sean.
▁▁▁▁▁
▕ █ ██ ▏
▕▔▔ ▔▔\
▕═╭╮══╭╮══
▔╰╯▔▔╰╯▔▔o
▔▔▔▔▔▔▔▔▔▔

ASA10中的Global increment default扩展(Important)

当我使用这个类型的时候:
create table Admin (
  admin_id      bigint default global autoincrement(1000000) primary key,
  data          varchar(30),
  last_modified timestamp default timestamp
);
直接insert into Admin(data) values(1)
失败,原因是没有设置一个选项:
public.Global_database_id的值。
set option public.Global_database_id = 10;
insert into Admin(data) values('21425.34');
select * from Admin;
admin_id,data,last_modified
10000001,'21425.34','2008-02-20 17:09:31.111'
它的起始值从global_database_id * autoincrement区段值 开始,最大增长到autoincrement。
详细说明如下:
The GLOBAL AUTOINCREMENT default is intended for use when multiple databases are used in a SQL Remote replication or MobiLink synchronization environment. It ensures unique primary keys across multiple databases.
This option is similar to AUTOINCREMENT, except that the domain is partitioned. Each partition contains the same number of values. You assign each copy of the database a unique global database identification number. SQL Anywhere supplies default values in a database only from the partition uniquely identified by that database's number.
The partition size can be any positive integer, although the partition size is generally chosen so that the supply of numbers within any one partition will rarely, if ever, be exhausted.
If the column is of type BIGINT or UNSIGNED BIGINT, the default partition size is 232 = 4294967296; for columns of all other types, the default partition size is 216 = 65536. Since these defaults may be inappropriate, especially if your column is not of type INT or BIGINT, it is best to specify the partition size explicitly.
When using this option, the value of the public option global_database_id in each database must be set to a unique, non-negative integer. This value uniquely identifies the database and indicates from which partition default values are to be assigned. The range of allowed values is np + 1 to (n + 1) p, where n is the value of the public option global_database_id and p is the partition size. For example, if you define the partition size to be 1000 and set global_database_id to 3, then the range is from 3001 to 4000.
If the previous value is less than (n + 1) p, the next default value is one greater than the previous largest value in column. If the column contains no values, the first default value is np + 1. Default column values are not affected by values in the column outside of the current partition; that is, by numbers less than np + 1 or greater than p(n + 1). Such values may be present if they have been replicated from another database via MobiLink synchronization.
Because the public option global_database_id cannot be set to a negative value, the values chosen are always positive. The maximum identification number is restricted only by the column data type and the partition size.
If the public option global_database_id is set to the default value of 2147483647, a NULL value is inserted into the column. If NULL values are not permitted, attempting to insert the row causes an error. This situation arises, for example, if the column is contained in the table's primary key.
NULL default values are also generated when the supply of values within the partition has been exhausted. In this case, a new value of global_database_id should be assigned to the database to allow default values to be chosen from another partition. Attempting to insert the NULL value causes an error if the column does not permit NULLs. To detect that the supply of unused values is low and handle this condition, create an event of type GlobalAutoincrement. See Understanding events.
Global autoincrement columns are typically primary key columns or columns constrained to hold unique values (see Enforcing entity integrity).
While using the global autoincrement default in other cases is possible, doing so can adversely affect database performance. For example, in cases where the next value for each column is stored as a 64-bit signed integer, using values greater than 231 - 1 or large double or numeric values may cause wraparound to negative values.
You can retrieve the most recent value inserted into an autoincrement column using the @@identity global variable. For more information, see @@identity global variable.

MobiLink Event pseudo code

 
 
------------------------------------------------------
MobiLink complete event model.
------------------------------------------------------
Legend:
- // This is a comment.
- <name>
    The pseudo code for <name> is listed separately
    in a later section, under a banner:
        ------------------------
        name
        ------------------------
- VariableName <- value
    Assign the given value to the given variable name.
    Variable names are in mixed case.
- event_name
    If you have defined a script for the given event name,
    it will be invoked.
------------------------------------------------------
 

CONNECT to consolidated database
begin_connection_autocommit
begin_connection
COMMIT
for each synchronization request with
     the same script version {
  <synchronize>
}
end_connection
COMMIT
DISCONNECT from consolidated database
 

------------------------------------------------------
synchronize
------------------------------------------------------
 
<authenticate>
<begin_synchronization>
<upload>
<prepare_for_download>
<download>
<end_synchronization>
 

------------------------------------------------------
authenticate
------------------------------------------------------
 
Status <- 1000
UseDefaultAuthentication <- TRUE
if( authenticate_user script is defined ) {
  UseDefaultAuthentication <- FALSE
  TempStatus <- authenticate_user
  if( TempStatus > Status ) {
    Status <- TempStatus
  }
}
 
if( authenticate_user_hashed script is defined ) {
  UseDefaultAuthentication <- FALSE
  TempStatus <- authenticate_user_hashed
  if( TempStatus > Status ) {
    Status <- TempStatus
  }
}
  if( authenticate_parameters script is defined )
 {
    TempStatus <- authenticate_parameters
    if( TempStatus > Status ) {
      Status <- TempStatus
  }
 
if( UseDefaultAuthentication ) {
  if( the user exists in the ml_user table ) {
    if( ml_user.hashed_password column is not NULL ) {
      if( password matches ml_user.hashed_password ) {
        Status <- 1000
      } else {
        Status <- 4000
      }
    } else {
      Status <- 1000
    }
  } else if( -zu+ was on the command line ) {
    Status <- 1000
  } else {
    Status <- 4000
  }
}
if( Status >= 3000 ) {
  // Abort the synchronization.
} else {
  // UserName defaults to MobiLink user name
  // sent from the remote.
  if( modify_user script is defined ) {
    UserName <- modify_user
    // The new value of UserName is later passed to
    // all scripts that expect the MobiLink user name.
  }
}
COMMIT
 
------------------------------------------------------
begin_synchronization
------------------------------------------------------
 
begin_synchronization   // Connection event.
for each table being synchronized {
    begin_synchronization    // Call the table level script.
}
for each publication being synchronized {
  begin_publication
}
COMMIT
 

------------------------------------------------------
end_synchronization
------------------------------------------------------
 
for each publication being synchronized {
  if( begin_publication script was called ) {
    end_publication
  }
}
for each table being synchronized {
  if( begin_synchronization table script was called ) {
    end_synchronization // Table event.
  }
}
if( begin_synchronization table script was called ) {
  end_synchronization     // Connection event.
}
for each table being synchronized {
synchronization_statistics // Table event.
}
synchronization_statistics // Connection event.
for each table being synchronized {
  time_statistics // Table event.
}
time_statistics // Connection event.
 
COMMIT
======================================================
------------------------------------------------------
Events during uploadThe following pseudocode illustrates
how upload events and upload scripts are invoked.
These events take place at the upload location in the
complete event model. See Overview of MobiLink events.
Overview of the upload
------------------------------------------------------
upload
------------------------------------------------------
begin_upload // Connection event
for each table being synchronized {
  begin_upload // Table event
}
  handle_UploadData
  for each table being synchronized {
    begin_upload_rows
    for each uploaded INSERT or UPDATE for this table {
      if( INSERT ) {
        <upload_inserted_row>
      }
      if( UPDATE ) {
        <upload_updated_row>
      }
    }
    end_upload_rows
  }
  for each table being synchronized IN REVERSE ORDER {
    begin_upload_deletes
    for each uploaded DELETE for this table {
      <upload_deleted_row>
    }
    end_upload_deletes
  }
 
For each table being synchronized {
  if( begin_upload table script is called ) {
    end_upload // Table event
  }
}
if( begin_upload connection script was called ) {
  end_upload // Connection event
 
  for each table being synchronized {
    upload_statistics  // Table event.
  }
    upload_statistics  // Connection event.
 
  COMMIT
Upload inserts
------------------------------------------------------
<upload_inserted_row>
------------------------------------------------------
// NOTES:
// - Only table scripts for the current table are involved.
 
  ConflictsAreExpected <- (
       upload_new_row_insert script is defined
    or upload_old_row_insert script is defined
    or resolve_conflict script is defined )
  if( upload_insert script is defined ) {
    upload_insert
  } else if( ConflictsAreExpected
      and upload_update script is not defined
      and upload_insert script is not defined
      and upload_delete script is not defined ) {
      // Forced conflict.
      upload_new_row_insert
      resolve_conflict
  } else {
      // Ignore the insert.
  }
 
Upload updates
------------------------------------------------------
upload_updated_row
------------------------------------------------------
// NOTES:
// - Only table scripts for the current table are involved.
// - Both the old (original) and new rows are uploaded for
//   each update.
 
  ConflictsAreExpected <- (
       upload_new_row_insert script is defined
    or upload_old_row_insert script is defined
    or resolve_conflict script is defined )
  Conflicted <- FALSE
  if( upload_update script is defined ) {
    if( ConflictsAreExpected
      and upload_fetch script is defined ) {
      FETCH using upload_fetch INTO current_row
      if( current_row <> old row ) {
        Conflicted <- TRUE
      }
    }
    if( not Conflicted ) {
      upload_update
    }
  } else if( upload_update script is not defined
      and upload_insert script is not defined
      and upload_delete script is not defined ) {
      // Forced conflict.
      Conflicted <- TRUE
  }
  if( ConflictsAreExpected and Conflicted ) {
    upload_old_row_insert
    upload_new_row_insert
    resolve_conflict
  }
 

Upload deletes
------------------------------------------------------
upload_deleted_row
------------------------------------------------------
// NOTES:
// - Only table scripts for the current table are involved.
 
  ConflictsAreExpected <- (
       upload_new_row_insert script is defined
    or upload_old_row_insert script is defined
    or resolve_conflict script is defined )
  if( upload_delete is defined ) {
    upload_delete
  } else if( ConflictsAreExpected
    and upload_update script is not defined
    and upload_insert script is not defined
    and upload_delete script is not defined ) {
    // Forced conflict.
    upload_old_row_insert
    resolve_conflict
  } else {
    // Ignore this delete.
  }
 
=========================================================
------------------------------------------------------
prepare_for_download
------------------------------------------------------
 
modify_last_download_timestamp
prepare_for_download
if( modify_last_download_timestamp script is defined
    or prepare_for_download script is defined ) {
    COMMIT
}
------------------------------------------------------
download
------------------------------------------------------
 
begin_download
begin_download // Connection event.
for each table being synchronized {
   begin_download // Table event.
}
   handle_DownloadData
   for each table being synchronized {
     begin_download_deletes
     for each row in download_delete_cursor {
       if( all primary key columns are NULL ) {
         send TRUNCATE to remote
       } else {
         send DELETE to remote
       }
     }
     end_download_deletes
     begin_download_rows
     for each row in download_cursor {
       send INSERT ON EXISTING UPDATE to remote
     }
     end_download_rows
   }
   modify_next_last_download_timestamp
   for each table being synchronized {
     if( begin_download table script is called ) {
        end_download // Table event
     }
}
if( begin_download connect script is called ) {
   end_download // Connection event
}
   for each table being synchronized {
     download_statistics   // Table event.
   }
     download_statistics   // Connection event.
 
COMMIT
 

 

ASA10(SQLAnywhere10)中的TIMESTAMP类型(蛮奇怪的)

TIMESTAMP indicates when each row in the table was last modified. When a column is declared with DEFAULT TIMESTAMP, a default value is provided for inserts, and the value is updated with the current date and time whenever the row is updated.

Data type

TIMESTAMP

Remarks

Columns declared with DEFAULT TIMESTAMP contain unique values so that applications can detect near-simultaneous updates to the same row. If the current timestamp value is the same as the last value, it is incremented by the value of the default_timestamp_increment option.
You can automatically truncate timestamp values in SQL Anywhere based on the default_timestamp_increment option. This is useful for maintaining compatibility with other database software that records less precise timestamp values.
The global variable @@dbts returns a TIMESTAMP value representing the last value generated for a column using DEFAULT TIMESTAMP
The main difference between DEFAULT TIMESTAMP and DEFAULT CURRENT TIMESTAMP is that DEFAULT CURRENT TIMESTAMP is set only at INSERT, while DEFAULT TIMESTAMP is set at both INSERT and UPDATE.