ANTを使用して以下に示すトリガーコードを実行すると、エラーが発生します
org.postgresql.util.PSQLException: ERROR: unterminated quoted string at or near "' DECLARE timeout integer"
Position: 57
PGADmin(postgres提供)およびコマンドラインユーティリティ「psql」を使用して以下のコードを正常に実行することができ、トリガー機能が追加されますが、ANTを介して実行すると毎回失敗します
BEGIN TRANSACTION;
CREATE OR REPLACE FUNCTION sweeper() RETURNS trigger as '
DECLARE
timeout integer;
BEGIN
timeout = 30 * 24 * 60 * 60 ;
DELETE FROM diagnosticdata WHERE current_timestamp - teststarttime > (timeout * ''1 sec''::interval);
return NEW;
END;
' LANGUAGE 'plpgsql';
-- Trigger: sweep on diagnosticdata
CREATE TRIGGER sweep
AFTER INSERT
ON diagnosticdata
FOR EACH ROW
EXECUTE PROCEDURE sweeper();
END;
liquibaseでこのエラーが発生し、このページは最初の検索結果の1つであったため、このページでソリューションを共有すると思います。
Sql全体を別のファイルに入れて、これを変更セットに含めることができます。 splitStatements
オプションをfalse
に設定することが重要です。
チェンジセット全体は次のようになります
<changeSet author="fgrosse" id="530b61fec3ac9">
<sqlFile path="your_sql_file_here.sql" splitStatements="false"/>
</changeSet>
私はいつも、それらの大きなSQL部分(関数の更新など)を別々のファイルに入れたいです。この方法により、SQLファイルを開くときに適切な構文の強調表示が得られ、XMLとSQLを1つのファイルに混在させる必要がなくなります。
Edit:コメントで述べたように、 sql
change はsplitStatements
オプション(AndreyTにそれを指摘するためのthx)も。
Liquibaseで使用されているJDBCドライバーにも同じ問題がありました。
ドライバーはセミコロンで終了する各行を爆発させ、個別のSQLコマンドとして実行するようです。そのため、以下のコードが次の順序でJDBCドライバーによって実行されます。
CREATE OR REPLACE FUNCTION test(text) RETURNS VOID AS ' DECLARE tmp text
BEGIN tmp := "test"
END;
' LANGUAGE plpgsql
もちろん、これは無効なSQLであり、次のエラーが発生します。
unterminated dollar-quoted string at or near ' DECLARE tmp text
これを修正するには、各行がセミコロンで終わった後にバックスラッシュを使用する必要があります。
CREATE OR REPLACE FUNCTION test(text)
RETURNS void AS ' DECLARE tmp text; \
BEGIN
tmp := "test"; \
END;' LANGUAGE plpgsql;
または、定義全体を1行に配置できます。
私はHeidiSQLクライアントを使用していますが、これはCREATE OR REPLACEステートメントの前にDELIMITER //を配置することで解決しました。 。
このエラーは、サーバーへの接続に使用される特定のクライアントと関数の形式との間の相互作用として発生します。説明する:
次のコードは、Netbeans 7、Squirrel、DbSchema、PgAdmin3で犠牲なく実行されます。
CREATE OR REPLACE FUNCTION author.revision_number()
RETURNS trigger AS
$BODY$
begin
new.rev := new.rev + 1;
new.revised := current_timestamp;
return new;
end;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
「begin」ステートメントは、「$」引用符付き文字列の直後に来ることに注意してください。
次のコードは、PgAdmin3を除く上記のすべてのクライアントを停止します。
CREATE OR REPLACE FUNCTION author.Word_count()
RETURNS trigger AS
$BODY$
declare
wordcount integer := 0; -- counter for words
indexer integer := 1; -- position in the whole string
charac char(1); -- the first character of the Word
prevcharac char(1);
begin
while indexer <= length(new.blab) loop
charac := substring(new.blab,indexer,1); -- first character of string
if indexer = 1 then
prevcharac := ' '; -- absolute start of counting
else
prevcharac := substring(new.blab, indexer - 1, 1); -- indexer has increased
end if;
if prevcharac = ' ' and charac != ' ' then
wordcount := wordcount + 1;
end if;
indexer := indexer + 1;
end loop;
new.words := wordcount;
return new;
end;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
2番目の例の決定的な違いは、「宣言」セクションです。バックスラッシュを使用すると、PgAdmin3でエラーが発生します。
要約すると、さまざまなツールを試すことをお勧めします。一部のツールは、テキストファイルを記述することになっているにもかかわらず、目に見えないものをテキストに入れます。残念ながら、これはセッションまたは名前空間を実装しようとするphpファイルを停止するUnicode BOMで発生します。これは解決策ではありませんが、役立つことを願っています。
Zeosとc ++ builderでも同じ問題がありました。私の場合の解決策:
プロパティ区切り文字(通常は「;」)を、使用したコンポーネント(クラス)の別の区切り文字に変更します。
dm->ZSQLProcessor1->DelimiterType=sdGo;
おそらく、Antには似たようなものがあります。
この例は、PostgreSQL 14.1およびHeidiSQL 9.4.0.5125で機能しました
DROP TABLE IF EXISTS emp;
CREATE TABLE emp (
empname text NOT NULL,
salary integer
);
DROP TABLE IF EXISTS EMP_AUDIT;
CREATE TABLE emp_audit(
operation char(1) NOT NULL,
stamp timestamp NOT NULL,
userid text NOT NULL,
empname text NOT NULL,
salary integer
);
DELIMITER //
CREATE OR REPLACE FUNCTION process_emp_audit() RETURNS TRIGGER AS $$
BEGIN
--
-- Create a row in emp_audit to reflect the operation performed on emp,
-- make use of the special variable TG_OP to work out the operation.
--
IF (TG_OP = 'DELETE') THEN
INSERT INTO emp_audit SELECT 'D', now(), user, OLD.*;
RETURN OLD;
ELSIF (TG_OP = 'UPDATE') THEN
INSERT INTO emp_audit SELECT 'U', now(), user, NEW.*;
RETURN NEW;
ELSIF (TG_OP = 'INSERT') THEN
INSERT INTO emp_audit SELECT 'I', now(), user, NEW.*;
RETURN NEW;
END IF;
RETURN NULL; -- result is ignored since this is an AFTER trigger
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS emp_audit ON emp;
CREATE TRIGGER emp_audit
AFTER INSERT OR UPDATE OR DELETE ON emp
FOR EACH ROW EXECUTE PROCEDURE process_emp_audit();
私はこの質問がずっと前に尋ねられたことを知っていますが、AntのSQLタスクを使用したPostgresqlスクリプト(Jenkinsから実行)でも同じ問題がありました。
私はこのSQLを実行しようとしました(audit.sqlという名前のファイルに保存されています):
DROP SCHEMA IF EXISTS audit CASCADE
;
CREATE SCHEMA IF NOT EXISTS audit AUTHORIZATION faktum
;
CREATE FUNCTION audit.extract_interval_trigger ()
RETURNS trigger AS $extractintervaltrigger$
BEGIN
NEW."last_change_ts" := current_timestamp;
NEW."last_change_by" := current_user;
RETURN NEW;
END;
$extractintervaltrigger$ LANGUAGE plpgsql
;
しかし、「ドル記号で囲まれていない文字列が終了していない」というエラーが発生しました。 pgAdminから実行しても問題ありません。
スクリプトを「;」ごとに分割するのはドライバーではないことがわかりました。むしろアリ。
http://grokbase.com/t/postgresql/pgsql-jdbc/06cjx3s3y0/ant-sql-tag-for-dollar-quoting 答えが見つかりました:
Antは、変数処理の一部としてdouble-$$を食べます。ストアドプロシージャで$ BODY $(または同様の)を使用し、独自の行に区切り文字を配置する必要があります(delimitertype = "row"を使用)。 Antはそれから協力します。
私のAnt SQLスクリプトは次のようになり、動作します。
<sql
driver="org.postgresql.Driver" url="jdbc:postgresql://localhost:5432/jenkins"
userid="user" password="*****"
keepformat="true"
autocommit="true"
delimitertype="row"
encoding="utf-8"
src="audit.sql"
/>
次のような新しい行にセミコロンがあったため、同じエラーが表示されました。
WHERE colA is NULL
;
それらが1行になっていることを確認してください
WHERE colA is NULL;