1 #+TITLE: Using Org-Mode Table Formatting Functions
5 #+TEXT: *Abstract:* Org-mode's ability to slice one table into many
6 #+TEXT: separately formatted destinations helps keep documentation
7 #+TEXT: and data in sync. We provide an example using both the
8 #+TEXT: multiple-target facilities and formatting with functions.
9 #+TEXT: Side-effects in the functions gather header data necessary
10 #+TEXT: for generating flexible SQL insertion statements.
11 #+OPTIONS: H:3 num:nil toc:t \n:nil @:t ::t |:t ^:t -:t f:t *:t TeX:t LaTeX:t skip:nil d:(HIDE) tags:not-in-toc
12 #+STARTUP: align fold nodlcheck hidestars oddeven lognotestate
13 #+SEQ_TODO: TODO(t) INPROGRESS(i) WAITING(w@) | DONE(d) CANCELED(c@)
14 #+TAGS: Write(w) Update(u) Fix(f) Check(c)
16 #+CATEGORY: worg-tutorial
18 [[file:../index.org][{Back to Worg's index}]]
22 In large-scale data analysis, one often associates integers with
23 parameter name rather than strings. The performance and storage
24 difference is significant for multi-gigabyte data sets. But integers
25 are not at all convenient or descriptive. Systems like [[http://www.r-project.org][R]] provide a
26 =factor= data type that translates the stored integers into user-level
27 strings. Emulating that construct in SQL is handy for data transfer
28 with [[http://www.sqlite.org][SQLite]] or out-of-core analysis in R using a [[http://cran.r-project.org/web/packages/SQLiteDF/index.html][SQL back-end]]. That
29 leaves the problem of maintaining a registry of integer level codes,
30 string names, and documentation.
32 [[http://orgmode.org][Org-mode]] provide a fast, light-weight table mechanism that can be sent
33 in email, bundled with code, or embedded in documentation. The tables
34 can be transformed and placed elsewhere. It sounds like a perfect
35 registry, generating documentation and code from one data table.
36 Similar techniques could be used in a multi-lingual document to store
37 many translations in one table and send them to sections in specific
40 We start with a simple table:
42 #+ORGTBL: SEND exdoc orgtbl-to-orgtbl :skipcols (2) :fmt (1 "=%s=") :hfmt (1 "%s")
43 #+ORGTBL: SEND exsql orgtbl-to-sqlinsert :sqlname "extbl" :fmt (2 "%s") :tstart "#+BEGIN_EXAMPLE\nBEGIN TRANSACTION;" :tend "COMMIT;\n#+END_EXAMPLE"
44 | Name | Level | Description |
45 |-------+-------+-----------------|
46 | normx | 1 | norm(x, \infty) |
47 | normb | 2 | norm(b, \infty) |
48 | normA | 3 | norm(A, \infty) |
50 That one source table contains the documentation in the first and third
53 #+BEGIN RECEIVE ORGTBL exdoc
54 | Name | Description |
55 |---------+-----------------|
56 | =normx= | norm(x, \infty) |
57 | =normb= | norm(b, \infty) |
58 | =normA= | norm(A, \infty) |
59 #+END RECEIVE ORGTBL exdoc
61 The first two columns of the source table provide the data we must
62 transfer the SQL. The third column can be used to embed some
63 documentation into the table itself after the string is sanitized for
64 SQL. The remaining necessary information, the SQL destination table
65 name, can be provided as a parameter to =#+ORGTBL: SEND=, producing the
68 #+BEGIN RECEIVE ORGTBL exsql
71 INSERT INTO extbl( Name, Level, Description ) VALUES ( 'normx' , 1 , 'norm(x, infty)' );
72 INSERT INTO extbl( Name, Level, Description ) VALUES ( 'normb' , 2 , 'norm(b, infty)' );
73 INSERT INTO extbl( Name, Level, Description ) VALUES ( 'normA' , 3 , 'norm(A, infty)' );
76 #+END RECEIVE ORGTBL exsql
78 We will explain the parameters used to produce both outputs. The SQL
79 insertion statements use functions as formatting parameters, some of
80 which are called purely for the side-effect of gathering the header
81 fields. The SQL-generating code is distributed with org-mode in
82 =contrib/lisp/orgtbl-sqlinsert.el=.
84 * Specifying multiple destinations for a single table
86 Sending one table to multiple destinations is straight-forward. Add one
87 =SEND= directive for each destination. For example, the first table has
88 the following two directives prepended, with parameters described later:
90 : #+ORGTBL: SEND exdoc orgtbl-to-orgtbl ...
91 : #+ORGTBL: SEND exsql orgtbl-to-sqlinsert ...
93 The documentation removes the second column and adds fiddly formatting
96 : :skipcols (2) :fmt (1 "=%s=") :hfmt (1 "%s")
98 The SQL-generating line gathers the destination table name and passes
99 integers through unchanged with the parameters
101 : :sqlname "extbl" :fmt (2 "%s")
103 The SQL table name defaults to the name of the target, =exsql= in this
104 case. And the default formatting used for other columns is
105 =orgtbl-sql-strip-and-quote=. That routine only removes potentially
106 non-portable constructs; it is not designed to prevent insertion
109 We could apply =orgtbl-sql-strip-and-quote= to the first column of the
110 documentation table to ensure the strings match exactly, but it easier
111 to use simple, non-mangled strings as names.
113 By default, a block of insertions is wrapped in =BEGIN TRANSACTION= and
114 =COMMIT= statements. These can be supressed by setting =:tstart= and
115 =:tend= to =nil=. The example used in this document uses a
116 "double-embedding" trick to wrap the statement in an org-mode code
119 : :tstart "#+BEGIN_EXAMPLE\nBEGIN TRANSACTION;"
120 : :tend "COMMIT;\n#+END_EXAMPLE"
122 Similar wrapping can embed the SQL statements into literate programs.
123 There is built-in support for [[http://www.eecs.harvard.edu/nr/noweb/][Noweb]] with the =:nowebname= parameter.
124 Setting =:nowebname= to a string wraps the insertions in a Noweb code
125 chunk named with the string.
127 * Formatting with functions for side effects and display
129 The =orgtbl-to-sqlinsert= routine calls =orgtbl-to-generic= for all the
130 generic table parsing. The parameters provide an example of using
131 functions for gathering data as well as formatting. Emacs Lisp's
132 dynamic binding allows manipulating any symbols in the current
133 environment, so the formatting functions do not need to pass parameters
134 through the outer functions.
136 The default =:tstart= parameter is one example used strictly for
137 formatting. After the =:nowebname= parameter is decoded and bound to
138 =nowebname=, it can be checked within a thunk to produce the starting
141 : :tstart (lambda () (concat (if nowebname
142 : (format "<<%s>>= \n" nowebname)
144 : "BEGIN TRANSACTION;"))
146 The functions need not be pure. The header formatting gathers the
147 first header line into the variable =hdrlist= with
148 : :hfmt (lambda (f) (progn (if firstheader (push f hdrlist)) ""))
149 Then each line is preceded with a function that uses =hdrlist= to ensure
150 data values are associated with named columns rather than just
153 : :lstart (lambda () (concat "INSERT INTO "
155 : (mapconcat 'identity (reverse hdrlist)
157 : " )" (if breakvals "\n" " ")
160 Note that =orgtbl-to-sqlinsert= takes advantage of org-mode's applying
161 the formatting to each cell /before/ checking for a line-formatting
162 function. The header line itself and sectioning line are suppressed
165 : :hlfmt (lambda (lst) (setq firstheader nil))
167 : :remove-nil-lines t
169 Similar techniques could be used to generate a table's SQL definition
170 from the second header line.
172 * Current limitations
174 One current limitation is that all the tables are in the same Emacs
175 buffer and hence the same text file. A literate programming mechanism
176 like [[http://www.eecs.harvard.edu/nr/noweb/][Noweb]] can separate the chunks.
178 Also, the tables must be sent manually. Writing a function that scans
179 an entire buffer for all =SEND= and =RECEIVE= pairs is feasible, as is
180 using [[http://www.gnu.org/software/emacs/elisp/html_node/Overlays.html][overlays]] to manage automatic updates.