PostgreSQL implementation of SQL is Turing Complete, as show by Andrew Gierth on PostgreSQL wiki, where a Cyclic Tag System (CTS), which is proven Turing-complete, is implemented using WITH RECURSIVE
, a WINDOW
function, CASE
conditional expressions and an OUTER JOIN
. Although the proof is sound, there is a long path from a CTS to an actual Turing Machine (TM).
The first question I want to answer is: how to build a Turing Machine with SQL only?
The second question I would like to investigate is: what SQL features are actually required to build a relational Turing Machine. For instance, can it be done withoutWITH RECURSIVE
, WINDOW
functions and OUTER JOIN
? (Teaser for later posts: yes!)
Turing Machine with Arrays
In this post I will show how to build a TM in SQL with the following features: WITH RECURSIVE
to iterate till the machine stops, INNER JOIN
to get transition and state informations, ARRAY
operations to build and store the tape, one COALESCE
function to deal with the end of the tape, and a sub-SELECT
with an ORDER
clause to initiate the tape.
Obviously the use of ARRAY
s make this solution not really relational, so this is cheating, but it is in SQL, it works, and allows to introduce the general setup that I will use for other more relational solutions.
Turing Machine description
First, the TM will be reprensented by a set of relations for the states, symbols, transitions, and the initial tape contents.
CREATETABLEState(-- TM statessidINTEGERPRIMARYKEY,-- 0 is always the initial stateisFinalBOOLEANNOTNULL,snameTEXTUNIQUENOTNULL-- just for show);CREATETABLESymbol(-- TM symbolscidINTEGERPRIMARYKEY,-- 0 is always the blank symbolcnameTEXTUNIQUENOTNULL);CREATETABLETransition(-- TM transition functionsidINTEGERNOTNULLREFERENCESState,-- initial statesymbolINTEGERNOTNULLREFERENCESSymbol,-- & symbolUNIQUE(sid,symbol),new_stateINTEGERNOTNULLREFERENCESState,new_symbolINTEGERNOTNULLREFERENCESSymbol,moveINTEGERNOTNULLCHECK(move=-1ORmove=1));CREATETABLETape(-- TM initial tape contentstidINTEGERPRIMARYKEY,symbolINTEGERREFERENCESSymbol);
The TM run will be recorded in the following table:
CREATETABLERun(ridINTEGERPRIMARYKEY,-- machine iterationsidINTEGERNOTNULLREFERENCESState,-- current statetapeINTEGER[]NOTNULL,-- full tape stored as an arrayposINTEGERNOTNULL-- current position on tape);
Turing Machine execution
Let us now record a run with a recursive query:
WITHRECURSIVErunning(rid,sid,tape,pos)AS(-- first store initial tape contentsSELECT0,0,ARRAY(SELECTsymbolFROMTapeORDERBYtid),1UNION-- then proceed to compute iterationsSELECTp.rid+1,t.new_state,-- build updated tape as an arrayp.tape[1:p.pos-1]||-- prefixt.new_symbol||-- updated cellp.tape[p.pos+1:array_length(p.tape,1)],-- suffix-- move cursor positionp.pos+t.moveFROMrunningASp-- get state details, to know whether to stopJOINStateASsON(p.sid=s.sid)-- get corresponding state transitionJOINTransitionAStON(t.sid=p.sidAND-- coalesce defaults to blankt.symbol=COALESCE(p.tape[p.pos],0))WHERE-- stop on a final stateNOTs.isFinal)-- just store the computed tableINSERTINTORunSELECT*FROMrunning;
Note that on each iteration, the next iterations of all rows are recomputed. This can be avoided with the help of a SEQUENCE
. The COALESCE
call creates implicit blanks at the end of the tape.
You can try this self-contained SQL script which implements a Turing Machine for accepting the AnBnCn language using the above method.
In the next post, I’ll show how to put the tape in a separate TABLE
so that the TM is really relational, although it will be at the price of using SQL functions.