查找

質問
· 2024年7月22日

SQL Gateway getClob() errors in Redshift and Postgresql

I'm trying to use the EnsLib.SQL.Operation.GenericOperation component in a production to read a column from a Redshift table that is set up as VARCHAR(65535) and am getting the following error.  

An error was received : ERROR #5023: Remote Gateway Error: JDBC Gateway getClob(0,1) errorRemote JDBC error: Cannot convert the column of type VARCHAR to requested type long..

The query I'm using is a simple 'SELECT column_name FROM table_name'.  I've done a little research and it sounds like Redshift doesn't support getClob().  Is there anything I can do to force the gateway to not use getClob() on this Redshift column, or some other work around?  Casting the column in my select statement doesn't work... it seems the getClob() call is done under the hood by the gateway, and I would potentially need some way to override that?  

Thanks,

Don Martin, Sanford Health

5 Comments
ディスカッション (5)3
続けるにはログインするか新規登録を行ってください
記事
· 2024年7月16日 9m read

管理ポータルのウェブ・アプリケーションメニューの設定をプログラムで作成する方法

これは InterSystems FAQ サイトの記事です。

管理ポータル > [システム管理] > [セキュリティ] 以下の設定は、%SYSネームスペースにあるSecurityパッケージ以下クラスが提供するメソッドを利用することでプログラムから作成することができます。

以下シナリオに合わせたセキュリティ設定例をご紹介します。

シナリオ:RESTアプリケーション用設定を作成する

事前準備

シナリオの中で使用するソースを2種類インポートします。

アプリケーション用RESTディスパッチクラスをインポートします。

Class Test.REST Extends %CSP.REST
{

Parameter CHARSET = "utf-8";
Parameter CONTENTTYPE = "application/json";
Parameter CONVERTINPUTSTREAM = 1;
XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]
{
<Routes>
<Route Url="/hello" Method="POST" Call="TestInsert" Cors="true" />
<Route Url="/hello" Method="DELETE" Call="TestDelete" Cors="true" />
</Routes>
}

/// Description
ClassMethod TestInsert() As %Status
{
    #dim %request As %CSP.Request
    #dim rset As %SQL.StatementResult
    set status = $$$OK
    Try {
        set bodyjson={}.%FromJSON(%request.Content)
        set ^Test=bodyjson.Message
        set stmt=##class(%SQL.Statement).%New()
        $$$ThrowOnError(stmt.%Prepare("insert into Test.Human (Name,Message) VALUES(?,?)"))
        set rset=stmt.%Execute($username,bodyjson.Message)
        if rset.%SQLCODE<0 {
            throw ##class(%Exception.SQL).CreateFromSQLCODE(rset.%SQLCODE,rset.%Message)
        }
        set j={}
        set j.Result="こんにちは!メッセージをUSERネームスペースのグローバル^TestとTest.Humanテーブルに格納しました。"
        do j.%ToJSON()     }
    Catch ex {
        Set status=ex.AsStatus()
    }
    Return status
}

ClassMethod TestDelete() As %Status
{
    #dim %request As %CSP.Request
    #dim rset As %SQL.StatementResult
    set status = $$$OK
    Try{
        kill ^Test
        set stmt=##class(%SQL.Statement).%New()
        $$$ThrowOnError(stmt.%Prepare("Delete from Test.Human"))
        set rset=stmt.%Execute()
        if rset.%SQLCODE<0 {
            throw ##class(%Exception.SQL).CreateFromSQLCODE(rset.%SQLCODE,rset.%Message)
        }
        set j={}
        set j.Result="USERネームスペースの^TestとTest.Humnaのデータを削除しました"
        do j.%ToJSON()
    }
    catch ex {
        Set status=ex.AsStatus()
    }
    Return status
}

}

続いて、Test.Humanテーブル用定義もインポートします。

Class Test.Human Extends %Persistent
{

Property Name As %String;
Property Message As %String;
}

 

それでは設定してみましょう。

1) RESTアプリケーション(/testApp)があり、USERネームスペースで動作するRESTアプリケーションに対して不特定多数のユーザが利用できるようにします=(認証なしアクセスを許可します)。

2) RESTアプリケーション利用時は、USERネームスペースのTestスキーマ以下テーブルに対してINSERT/UPDATE/DELETE/SELECTが行えるように設定します。

 

まずは 1)について、

認証をしない「認証なし」アクセスを許可した場合、UnknownUserとしてInterSystems製品にログインします。

UnknownUserはインストール時の初期セキュリティの指定により初期設定が異なります。(初期セキュリティについて詳細は、記事「インストール時の初期セキュリティについて」をご参照ください。)

  • 「最小」:InterSystems製品の全ての情報にアクセス可能な %Allロールが付与されます。
  • 「通常」以上を指定した場合:ロールは何も設定されません。

このシナリオでは、UnkownUserに対してロールが付与されていない環境(=インストール時の初期セキュリティを「通常」以上とした場合)に対する設定方法を解説します。

手っ取り早くUnkownUserに%Allロールを付与するのも1つの方法ですが、その場合RESTアプリケーション以外の「認証なし」が許可されたアクセスに対しても%Allロールが適用されてしまうため、セキュアな設定とは言えません。このシナリオでは、RESTアプリケーションパスを通過したときのみ適切なロールを付与させることのできる「アプリケーションロール」を利用してロールを付与していきます。

ここで、RESTアプリケーションを動作させるために最低限必要となるアクセス許可はアプリケーションが動作するデータベースに対するREAD許可です。アプリケーションがデータベースに対して書き込みを行う場合はWRITE許可も必要となります。

InterSystems製品では、データベースを作成した際、一緒にセキュリティ設定で使用するデータベースリソースを作成することができます。データベースリソースを作成するとそのリソースに対するREADとWRITEの許可を持ったデータベースロールが自動的に作成されます。

今回はインストールデフォルトで作成されるUSERデータベースにアプリケーションをインストールして利用することにしています。

USERデータベースに対しては、%DB_USERリソースが用意されていてこのリソースに対してREADとWRITE許可を持つ%DB_USERロールが事前に用意されています。この%DB_USERロールをRESTアプリケーションに付与することにします。

 

さらに、2)では、

2) RESTアプリケーション利用時は、USERネームスペースのTestスキーマ以下テーブルに対してINSERT/UPDATE/DELETE/SELECTが行えるように設定します。

とあるので、Testスキーマに対する適切なテーブル権限が必要となります。テーブル権限はユーザに直接付与することもロールに付与することもできます。

この設定では、RESTアプリケーションにロールを付与したいので、ロールにテーブルの権限も付与することにします。

それでは、ロール:MyAppRoleを作成します。

アプリケーションはUSERネームスペースにアクセスする前提のため、%DB_USERロールを持つ新ロールを作成します。

%SYSネームスペースで実行します。

set $namespace="%SYS"
set status=##class(Security.Roles).Create("MyAppRole","アプリケーション用ロール",,"%DB_USER")

Security.RolesクラスCreate()メソッドに指定する引数は以下の通りです。

第1引数:ロール名
第2引数:ロールの説明
第3引数:リソースの割り当て(未指定もOK)
第4引数:割り当てるロール(複数ある場合はカンマ区切りで指定)

このロールを付与された人やアプリケーションはTestスキーマに対してSELECT/DELETE/UPDATE/INSERTができるようにこれらの権限を含む全テーブル権限を追加します。

set status=$system.SQL.Security.GrantPrivilege("*","Test","SCHEMA","MyAppRole")

%SYSTEM.SQL.SecurityクラスGrantPrivilege()メソッドで指定する引数は以下の通りです。

第1引数:以下のアクションをカンマ区切りで指定します。全部対象とする場合は * を指定します。

  • Alter
  • Select
  • Insert
  • Update
  • Delete
  • References
  • Execute
  • Use

第2引数:対象となるテーブル名やスキーマ名を指定できます。例ではTestスキーマを指定しています。

第3引数:対象となるタイプを指定します。例ではSCHEMAを指定しています。

第4引数:付与するユーザ名またはロール名を指定します。

戻り値を確認します。(1が返れば成功です。)

失敗している場合は、以下の出力をご確認ください。

do $system.OBJ.DisplayError(status)

管理ポータルでは以下のように設定を確認できます。

管理ポータル > [システム管理] > [セキュリティ] > [ロール] > [MyAppRole]の[Assigned To]と[SQL Tables]のタブ

 

作成したMyAppRoleをアプリケーション利用時に追加するようにREST用のウェブアプリケーションを定義します。

作成には、Security.ApplicationsクラスCreate()メソッドを利用します。

Create()メソッドの第2引数には設定に必要な情報を配列変数で指定します。サブスクリプトの指定については以下の通りです。

  • DispatchClass:RESTディスパッチクラス名を指定します。
  • NameSpace:RESTディスパッチクラスがあるネームスペースを指定します。
  • Enable:アプリケーションを有効とする場合は1を指定します。
  • AutheEnabled:認証なしは64を設定します。詳細はAutheEnabledプロパティの説明をご参照ください。(認証なしは Bit 6のAutheUnauthenticated の値を設定します。)
  • MatchRoles:アプリケーション通過時に付与するアプリケーションロールの場合は、:ロール名 を設定します。

%SYSネームスペースで以下実行します。 

set webName="/testApp"
set webProperties("DispatchClass")="Test.REST"
set webProperties("NameSpace")="USER"
set webProperties("Enable")=1
set webProperties("AutheEnabled")=64
set webProperties("MatchRoles")=":MyAppRole"
set status=##class(Security.Applications).Create(webName, .webProperties)

戻り値を確認します。(1が返れば成功です。)

失敗している場合は、以下の出力をご確認ください。

do $system.OBJ.DisplayError(status)

管理ポータルでは以下のように表示されます。

管理ポータル > [システム管理] > [セキュリティ] > [アプリケーション] > [ウェブ・アプリケーション] > /testApp選択

 

設定が完了したので最初にPOST要求を試します。 URLには、webサーバ/testApp/hello を指定しBodyに以下のプロパティを持つJSONを指定しPOST要求をテストします。

{
    "Message":"新しいデータをいれます"
}

 POST要求が成功すると、以下応答として返送します。

{
    "Result": "こんにちは!メッセージをUSERネームスペースのグローバル^TestとTest.Humanテーブルに格納しました。"
}

グローバル^Test、またはTest.Humanの中身をご確認ください。

 

続いて、DELETE要求を実行します。(POSTと同じURLを使用します)

 DELETE要求が成功すると、以下応答として返送します。

{
    "Result": "USERネームスペースの^TestとTest.Humnaのデータを削除しました"
}

グローバル^TestとTest.Humanテーブルのデータが削除されたことを確認してください。

 

ご参考:RESTアプリケーション(/testApp)にテーブル権限を設定し忘れると以下のエラーが出力されます。

{
    "errors": [
        {
            "code": 5540,
            "domain": "%ObjectErrors",
            "error": "エラー #5540: SQLCODE: -99 メッセージ: User UnknownUser is not privileged for the operation",
            "id": "SQLCode",
            "params": [
                -99,
                "User UnknownUser is not privileged for the operation"
            ]
        }
    ],
    "summary": "エラー #5540: SQLCODE: -99 メッセージ: User UnknownUser is not privileged for the operation"
}
ディスカッション (0)1
続けるにはログインするか新規登録を行ってください
質問
· 2024年7月15日

Class methods vs MAC routines

Hello everyone,

I just want to know if there is a difference in performance between using Class methods versus MAC routines?

For example:

do Method^MyFunction()

versus

do ##class(MyFunction).Method()

2 Comments
ディスカッション (2)1
続けるにはログインするか新規登録を行ってください
ディスカッション (5)3
続けるにはログインするか新規登録を行ってください
質問
· 2024年7月15日

General Logging In ObjectScript Classes (forDebugging)

I am trying to log certain program data in my ObjectScript REST class, to track down a bug I have. I am comparing two values at runtime, and one result does one thing, and another a different thing. Since this is a REST API class, I have no way of seeing in real time what the value is to debug. I cannot simply run the method in debug in Studio as it will not run properly being a REST class method, nor have the correct incoming header data to correctly replicate what is happening in the API at runtime when being hit by client apps. In other languages like C# or Java, when I would debug an API method like this, I would put in a line like
logger.Log("debug", {string containing any useful info for debugging the problem}...)
and then hit my services with the client app, run the scenario in question, and then check the logs and see what happened. I cannot figure out how to do this in ObjectScript. I've looked in the documentation for Logging, and all I've found so far are exception logging references, which is not what I'm after. Is there any way to do what I'm trying to do in ObjectScript classes?

6 Comments
ディスカッション (6)6
続けるにはログインするか新規登録を行ってください