SQLite User Forum

Table-valued PRAGMA statements vs functions within transactions
Login

Table-valued PRAGMA statements vs functions within transactions

(1) By Rodolfo Carvalho (rhcarvalho) on 2022-11-10 12:44:15 [link] [source]

(using SQLite 3.39.3)

Recently, I had to write ordinary PRAGMA statements where the equivalent pragma functions would have been more convenient¹.

It seems to me that pragma functions don't observe the effect of CREATE TABLE when in the middle of a transaction.

The documentation says pragma functions are experimental, though I haven't found this limitation explicitly documented.

After some web searches and having not found anything to clarify the matter, I decided to write this message. Is the difference in behavior imposed by the implementation constraints?

Repro:

$ sqlite3 <<EOF
BEGIN;
CREATE TABLE t(x);
PRAGMA table_info(t);
ROLLBACK;
EOF
0|x||0||0
$ sqlite3 <<EOF
BEGIN;
CREATE TABLE t(x);
SELECT pragma_table_info(t);
ROLLBACK;
EOF
Parse error near line 3: no such column: t
  SELECT pragma_table_info(t);
             error here ---^

¹ For the sake of completeness, example Tcl code showing what I was trying to accomplish:

proc create-triggers {tbl} {
  #set collist [db one "SELECT name FROM pragma_table_info(${tbl})"]
  set collist {}
  db eval "PRAGMA table_info(${tbl})" {lappend collist $name}

  # ... code that uses $collist omitted ...

  set sql "CREATE TRIGGER ${tbl}_ut AFTER UPDATE ON $tbl BEGIN\n"
  #db eval "SELECT name FROM pragma_table_info(${tbl}) WHERE pk=1 UNION SELECT 'from' FROM pragma_foreign_key_list(${tbl})" col {
  #  append sql "  SELECT RAISE(ROLLBACK,'${tbl}.$col(name) is immutable') WHERE OLD.$col(name) != NEW.$col(name);\n"
  #}
  set immulist {}
  db eval "PRAGMA table_info(${tbl})" {
    expr {$pk ? [lappend immulist $name] : [continue]}
  }
  db eval "PRAGMA foreign_key_list(${tbl})" {
    lappend immulist $from
  }
  foreach name [lsort -unique $immulist] {
    append sql "  SELECT RAISE(ROLLBACK,'${tbl}.$name is immutable') WHERE OLD.$name != NEW.${name};\n"
  }

  # ...
}

# ...

db transaction {
    # ...
    create-triggers tbl1
    create-triggers tbl2
    # ...
}

(2) By David Raymond (dvdraymond) on 2022-11-10 13:19:26 in reply to 1 [link] [source]

SELECT pragma_table_info(t);

You need to give it the table name as a string, and since it's a table-valued function you need to select FROM it, not select it directly.

SELECT * FROM pramga_table_info('t');

(4) By Rodolfo Carvalho (rhcarvalho) on 2022-11-12 16:44:26 in reply to 2 [link] [source]

Thanks, David.

The missing '* FROM' was an editing mistake on my side when posting.

The quoting on the argument does make a difference, and the error message "no such column ..." misguided my reasoning!

I think I incorrectly relied on SQLite "getting it right" for PRAGMA table_info(t);, even though there t should also be quoted (?).

I fixed the quoting by fixing my TCL code as below, letting eval substitute variables (instead of the TCL interpreter) and automatically quote SQL strings:

-  #set collist [db one "SELECT name FROM pragma_table_info(${tbl})"]
-  set collist {}
-  db eval "PRAGMA table_info(${tbl})" {lappend collist $name}
+  set collist [db eval {SELECT name FROM pragma_table_info($tbl)}]

Thanks again!

Rodolfo

(3) By Keith Medcalf (kmedcalf) on 2022-11-10 16:05:53 in reply to 1 [link] [source]

Further to what David said, the following are equivalent:

pragma <schema>.<pragmaname>(<arg>);

and

select * from pragma_<pragmaname> where schema='<schema>' and arg='<arg>'

So, if
<schema> is main
<pragmaname> is table_info
<arg> is t

Then that translates to:

pragma main.table_info(t)

is equivalent to

select * from pragma_table_info where schema='main' and arg='t'

(5) By Rodolfo Carvalho (rhcarvalho) on 2022-11-12 16:53:19 in reply to 3 [link] [source]

Thanks for sharing, Keith.

It would be useful to have this example of equivalence documented in https://www.sqlite.org/pragma.html#pragfunc, because now that I read the example I think I understand better the prose in that section.

The PRAGMA argument and schema, if any, are passed as arguments to the table-valued function, with the schema as an optional, last argument.

So, if I understand correctly, your example is also equivalent to

SELECT * FROM pragma_table_info('t','main');

(6) By Keith Medcalf (kmedcalf) on 2022-11-12 17:05:30 in reply to 5 [source]

Correct.

You can also do

select * from pragma_table_xinfo('pragma_<pragmaname>')
to see the columns and parameters used by the table pragma_<pragmaname>
pragma table_xinfo(pragma_<pragmaname>)