SQLite syntax error on insert default value during trigger after - sqlite

The problem: SQLite yields "near DEFAULT: syntax error" when running this through sqlite3_exec. The insertion works fine outside the trigger, and other statements works inside the trigger, but somehow the DEFAULT VALUES won´t work inside the trigger. Why is this happening?
SQLite code:
CREATE TABLE Symbol (
Label VARCHAR(127) PRIMARY KEY
);
CREATE TABLE Process (
Name INTEGER PRIMARY KEY
);
CREATE TABLE Named_Process_Definition (
Label VARCHAR(127),
Name INTEGER,
FOREIGN KEY (Label) REFERENCES Symbol (Label),
FOREIGN KEY (Name) REFERENCES Process_Definition (Name)
);
CREATE TRIGGER pre_new_named_process BEFORE INSERT ON Named_Process_Definition
BEGIN
INSERT INTO Symbol (Label) VALUES (NEW.Label);
END;
CREATE TRIGGER post_new_named_process AFTER INSERT ON Named_Process_Definition
BEGIN
INSERT INTO Process DEFAULT VALUES;
UPDATE Named_Process_Definition SET Name=last_insert_rowid() WHERE rowid=NEW.rowid;
END;
The triggers are meant to simplify inserting Named_Process_Definitions by automatically generating internal "unnamed" resources such as Process.

sqlite docs state:
The "INSERT INTO table DEFAULT VALUES" form of the INSERT statement is not supported.
You can work around this by inserting a null, e.g.:
CREATE TRIGGER post_new_named_process AFTER INSERT ON Named_Process_Definition
BEGIN
INSERT INTO Process(rowid) VALUES(NULL);
END;

Related

trigger with insert inside a select

I'm trying to create a trigger in which, only under certain circumstances, an insert is performed on another table. Consider the following code:
create table journal (
pk integer primary key autoincrement,
dsc varchar(10) not null
);
create table users (
name varchar(30) primary key not null
);
create trigger users_ai
after insert on users
begin
select
case
when 1 then
insert into journal(dsc) values('foo')
end;
end;
I get the following error when I run this code:
Error: near line 10: near "insert": syntax error
In production, the "1" in the when clause would be replaced by a more complex expression. I've also tried "true" and get the same results. I've also tried surrounding the insert statement in parens and get the same results. Any ideas how to accomplish what I want?
If you look at the syntax diagram for "CREATE TRIGGER", you'll see your attempt just doesn't match. You can, however, simply use the WHEN branch (without needing FOR EACH ROW):
create trigger users_ai
after insert on users
when 1 begin
insert into journal(dsc) values('foo');
end;
OK, figured it out. Instead of putting a conditional expression in the block of the trigger, I used a when clause. Here's the code that works:
create trigger users_ai
after insert on users when 1
begin
insert into journal(dsc) values('foo');
end;
If that when expression is changed to something that returns false (say 0) then the insert isn't done. In production, the expression will sometimes return true, sometimes false, which, of course, is the point of this code. Thanks everybody!
I think that you want a CASE statement, not a CASE expression.
create trigger users_ai after insert on users
begin
case
when ... then insert into journal(dsc) values('foo');
when ... then ...;
else ...;
end case;
end;
Note: if your trigger needs access to the data that was just inserted, its definition should the for each row option.
You can try to use an INSERT ... SELECT and your expression in the WHERE clause.
...
INSERT INTO journal
(dsc)
SELECT 'foo'
WHERE 1 = 1;
...
1 = 1 needs to be replaced by your Boolean expression.

tcl var substitution in sqlite3 eval

I have a file filled with sqlite statements I'd like to parse. I've done so previously using the C-interface but now I have trouble with the Tcl interface.
sqlite3 cspdb ":memory:"
set s [read [set f [open csp_sql.txt]]]
set ms [string map {"\n" "\0"} $s]
puts $ms
cspdb eval {$ms}
The error I get is:
"near "$ms": syntax error while executing cspdb eval {$ms}"
It works fine when I paste the output from puts $ms directly into the eval brackets. I also tried "{$ms}" and just plain $ms but the result is the same; syntax error.
I might chose another solution for the problem all together but it really bugs my why it does not work...
Here is what the file looks like:
BEGIN TRANSACTION;
CREATE TABLE Symbol (
Label TEXT PRIMARY KEY,
Type TEXT DEFAULT('no_type')
);
CREATE TABLE Process (
Name INTEGER PRIMARY KEY,
Type TEXT DEFAULT('no_type')
);
CREATE TABLE Named_Process (
Label TEXT UNIQUE,
Definition INTEGER UNIQUE,
FOREIGN KEY(Label) REFERENCES Symbol (Label),
FOREIGN KEY(Definition) REFERENCES Definition(Name)
);
CREATE TABLE Definition (
Name INTEGER UNIQUE,
Definition INTEGER,
FOREIGN KEY(Name) REFERENCES Process(Name),
FOREIGN KEY(Definition) REFERENCES Process(Name)
);
CREATE TABLE Reference (
Name INTEGER UNIQUE,
Reference TEXT,
FOREIGN KEY(Name) REFERENCES Process(Name),
FOREIGN KEY(Reference) REFERENCES Definition(Name)
);
CREATE TABLE Event (
Label TEXT PRIMARY KEY,
Type TEXT DEFAULT('no_type')
);
CREATE TABLE Environment (
Label TEXT UNIQUE,
FOREIGN KEY (Label) REFERENCES Event (Label)
);
CREATE TABLE Prefix (
Name INTEGER UNIQUE,
P INTEGER,
Event TEXT,
FOREIGN KEY (Name) REFERENCES Process (Name),
FOREIGN KEY (P) REFERENCES Process (Name),
FOREIGN KEY (Event) REFERENCES Event (Label)
);
CREATE TABLE Choice (
Name INTEGER UNIQUE,
P INTEGER,
Q INTEGER,
FOREIGN KEY(Name) REFERENCES Process(Name),
FOREIGN KEY(P) REFERENCES Process(Name),
FOREIGN KEY(Q) REFERENCES Process(Name)
);
/* Language defined processes */
/* SKIP */
INSERT INTO Symbol(Label,Type) VALUES('SKIP','named_process');
INSERT INTO Named_Process(Label) VALUES('SKIP');
INSERT INTO Process(type) VALUES('definition');
UPDATE Named_Process SET Definition=last_insert_rowid() WHERE Label='SKIP';
INSERT INTO Definition(Name,Definition) VALUES(last_insert_rowid(),last_insert_rowid());
END TRANSACTION;
(Not so sure removing newlines is necessary...)
If the string that you've read from the file is SQL, you should be able to do this:
sqlite3 cspdb ":memory:"
set f [open "csp_sql.txt"]
set sql [read $f]
close $f
cspdb eval $sql
By comparison, the literal string $ms is not a valid SQL statement or query, nor is it syntactically legal to surround a SQL statement with braces (which is what "{$ms}" ended up doing; the outer "…" makes the inside just a bunch of characters).
I don't know why you are translating newlines into NULs, but that's really unlikely to be a good idea. Bulk import of data (possibly with NULs in it) should be done in a different way.
The eval subcommand created by Sqlite does limited variable substitution, but only in places where a string value is expected.
Try
cspdb eval $ms
to let the Tcl interpreter substitute the SQL statements before sending them to eval.
turns out the newline-to-null was the culprit making substitution fail. final working code:
sqlite3 cspdb ":memory:"
cspdb eval [read [set f [open csp_sql.txt]]]
the reason newline-to-null turned up at all was when trying to debug making the string match my original working C-code which used multiline string literals in which newlines diappeared. Except, "\0" is not a "disappearing" character...

How to use a bind variable in trigger body?

I'm new to PL/SQL. I'm using oracle 11g XE along with sql developer. I'm trying to create to create an after insert trigger as follows
create or replace trigger tr1
after
insert ON
employee
for each row
begin
print :new.emp_id;
end;
The employee table is as follows
create table employee
( emp_id varchar2(5) primary key,
emp_name varchar2(10),
salary number,
company varchar2(10) foreign key references companies(comp_name)
);
When I run the statement I got a 'enter binds' window for the bind variable :new. But I was confused that why do I need to enter the value for :new since it is pseudorecord. Then I entered 'employee' as the values for :new. Now I'm getting the following error.
Error(2,8): PLS-00103: Encountered the symbol "" when expecting one of the following: := . ( # % ; The symbol ":=" was substituted for "" to continue.
Your problem is not in the :new pseudorecord. The error is coming from the usage of print, which is used to print the bind variable used in successful PL/SQL block or used in an EXECUTE command. For example, you can use it this way:
VARIABLE n NUMBER
BEGIN
:n := 1;
END;
/
Then
PRINT n;
But if you want to test the value being inserted, you can use DBMS_OUTPUT.PUT_LINE like this:
create or replace trigger tr1
after
insert ON
employee
for each row
BEGIN
dbms_output.put_line(:new.emp_id);
END;
/
Enable DBMS_OUTPUT window in your SQL Developer, then run
insert into employee values(1, 'empName', 1000, 'ABC');
You'll see 1 printed out.
However, you can always test the value from the table. Because the value should be already inserted into table. You can just query.

Creating SQL triggers for full text search index in SQLite

I'm trying to create triggers for a regular table to then update a full text index in SQLite, but I'm getting some errors and I'm not sure where I've gone wrong.
The app I'm making is a bookmarking app and the database I save the bookmark data to is created using the following SQL statement:
create table "pages" (
"pageUrl" text not null unique on conflict replace,
"dateCreated" integer not null,
"pageDomain" text not null,
"pageTitle" text null,
"pageText" text null,
"pageDescription" text null,
"archiveLink" text null,
"safeBrowsing" text null,
primary key ("pageUrl")
);
Then the full text search index is created with:
create virtual table fts using fts5(
content='pages',
content_rowid='pageUrl',
pageDomain,
pageTitle,
pageText,
pageDescription
);
So then I'd like to update the fts index when the "pages" table is updated via an insert or a delete.
The trigger I have for insert:
create trigger afterPagesInsert after insert on pages begin
insert into fts(
rowid,
pageDomain,
pageTitle,
pageText,
pageDescription
)
values(
new.pageUrl,
new.pageDomain,
new.pageTitle,
new.pageText,
new.pageDescription
);
end;
The trigger I have for the delete:
create trigger afterPagesDelete after delete on pages begin
insert into fts(
fts,
rowid,
pageDomain,
pageTitle,
pageText,
pageDescription
)
values(
'delete',
old.pageUrl,
old.pageDomain,
old.pageTitle,
old.pageText,
old.pageDescription
);
end;
Here's an example of an sql insert statement I'm using:
insert into "pages" (
"pageUrl",
"dateCreated",
"pageDomain",
"pageTitle",
"pageText",
"pageDescription",
"archiveLink",
"safeBrowsing"
)
values(
'https://www.reddit.com/',
1456465040177,
'reddit.com',
'reddit: the front page of the internet',
'reddit: the front page of the internet',
'reddit: the front page of the internet',
NULL,
NULL
)
And the delete statement:
delete from "pages" where "pageUrl" = 'https://www.reddit.com/'
But, I'm getting an error of SQLITE_MISMATCH: datatype mismatch] errno: 20, code: 'SQLITE_MISMATCH' for both the insert and the delete trigger, which I guess seems to indicate that the wrong data is going in to the wrong column, but I'm not sure why. I've gone through the triggers section in the External Content Tables section in the docs here and I've followed what was listed, so I'm not sure where I'm going wrong.
Any help would be appreciated.
note: I'm using the fts5 version of the SQLite full text search: https://www.sqlite.org/fts5.html
The content_rowid must refer to the rowid of the actual table, i.e., the INTEGER PRIMARY KEY column.

Insert default values in SQLite2

How to crate a now row without knowing the any of the columns of the table and using default values therefore?
In sqlite3 I simply do:
sqlite> CREATE TABLE t ("id" INTEGER PRIMARY KEY, "text" TEXT DEFAULT "hello world");
sqlite> INSERT INTO t DEFAULT VALUES;
sqlite> SELECT * FROM t;
1|hello world
But in sqlite2.8.17 I get:
sqlite> INSERT INTO t DEFAULT VALUES;
SQL error near 'DEFAULT': Syntax error.
Is there a way to do this right in sqlite2 or do I need to give the values manually in the insert statement?
You have to specify at least one value; all the others will then get their default values.
The rowid automatically gets a value when you specify NULL, so you can use that one:
INSERT INTO t(id) VALUES(NULL);

Resources