Starting with TCL

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
23 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Starting with TCL

Cecil Westerhof-5
I just wanted to start using SQLite with TCL. How can I give a formatted
output?

When I use:
puts [db eval {SELECT * FROM teaInStock}]

I get:
Brandnetel {} 2017-11-16 1 Oolong {} 2017-10-29 2 Goudsbloem {} 2017-10-22
3 Jasmijn …


When I use:
db eval {SELECT * FROM teaInStock} {
    puts $Tea
}

I get:
Brandnetel
Oolong
Goudsbloem
Jasmijn



When I use:
db eval {SELECT * FROM teaInStock} {
    puts $Tea, $Location
}

I get:
can not find channel named "Brandnetel,"
    while executing
"puts $Tea, $Location"
    invoked from within
"db eval {SELECT * FROM teaInStock} {
    puts $Tea, $Location
}"


I would like something like:
Brandnetel   2017-11-16   1
Oolong       2017-10-29   2
Goudsbloem   2017-10-22   3
Jasmijn      …


And probably another complication: one of the columns is called: "Last
Used".

--
Cecil Westerhof
_______________________________________________
sqlite-users mailing list
[hidden email]
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
Reply | Threaded
Open this post in threaded view
|

Re: Starting with TCL

Peter da Silva

On 11/16/17, 11:37 AM, "sqlite-users on behalf of Cecil Westerhof" <[hidden email] on behalf of [hidden email]> wrote:
> When I use:
>     db eval {SELECT * FROM teaInStock} {
>        puts $Tea, $Location
>     }

puts takes a single string, so you can do {puts “$Tea\t$Location”. Arguments are separated by space, comma has no intrinsic meaning, and puts takes two arguments: the file handle to write on and the string to print. So it’s interpreting “$Tea,” as the name of a file handle.

You probably want something like:

db eval {SELECT * FROM teaInStock} {
        puts [format “%12s %12s %s” $Tea ${Last Used} $Location]
}
 

_______________________________________________
sqlite-users mailing list
[hidden email]
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
Reply | Threaded
Open this post in threaded view
|

Re: Starting with TCL

Richard Hipp-3
In reply to this post by Cecil Westerhof-5
On 11/16/17, Cecil Westerhof <[hidden email]> wrote:
>     puts $Tea, $Location

Everything in TCL is a function.  The syntax is "FUNCTIONNAME ARG1
ARG2 ARG3 ..." where the arguments are separated by white space.  The
"puts" function takes either one or two arguments.  The one-argument
form of "puts" outputs ARG1 to standard-output.  The two-argument form
of "puts" sends ARG2 to output stream specified by ARG1.

Your code above tries to invoke the two-argument form of "puts".
Equivalent javascript code would be:

     puts(Tea + ",", Location)

The error arises because there is no output channel named by the
result of Tea+",".  What you want is the one-argument form, equivalent
to this JS:

    puts(Tea + ", " + Location)

To get that using TCL syntax, you can use quoting to make the two
separate arguments into one:

    puts "$Tea, $Location"

The key point is that everything in TCL is of the format FUNCTION ARG1
ARG2 ....  The processing steps are like this:

(1) Identify arguments separated by whitespace.  Note that all text
within "..." and within nested {...} is a single argument.

(2) Resolve quotes.  This means remove the outermost {...} from
arguments quoted using {...}.  Remove the "..." around double-quoted
argments, and also resolve any $variable name within the double
quotes.  The $variable name resolution does not happen with {...}

(3) Invoke the function with its arguments.

Note that *everything* is a function.  Even "control" statements.  In
Tcl when you see:

     if {$i<10} {
        puts "yes"
     } else {
        puts "no"
     }

That really is invoking the "if" function with 4 arguments.   Since
everything is a function, everything follows exactly the same quoting
rules.  This is an important feature of Tcl that programmers whose
prior experience has been exclusively using Algol-derived languages
such as C, Java, Javascript, and Python may have difficulty getting
their heads around.  But once you do "get it", it starts to seem very
natural.


--
D. Richard Hipp
[hidden email]
_______________________________________________
sqlite-users mailing list
[hidden email]
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
Reply | Threaded
Open this post in threaded view
|

Re: Starting with TCL

Cecil Westerhof-5
In reply to this post by Peter da Silva
2017-11-16 18:44 GMT+01:00 Peter Da Silva <[hidden email]>:

>
> On 11/16/17, 11:37 AM, "sqlite-users on behalf of Cecil Westerhof" <
> [hidden email] on behalf of
> [hidden email]> wrote:
> > When I use:
> >     db eval {SELECT * FROM teaInStock} {
> >        puts $Tea, $Location
> >     }
>
> puts takes a single string, so you can do {puts “$Tea\t$Location”.
> Arguments are separated by space, comma has no intrinsic meaning, and puts
> takes two arguments: the file handle to write on and the string to print.
> So it’s interpreting “$Tea,” as the name of a file handle.
>
> You probably want something like:
>
> db eval {SELECT * FROM teaInStock} {
>         puts [format “%12s %12s %s” $Tea ${Last Used} $Location]
> }
>

​This is what I use:
    puts [format "%-30s %-10s %2s" $Tea ${Last Used} $Location]

​Thanks.​

--
Cecil Westerhof
_______________________________________________
sqlite-users mailing list
[hidden email]
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
Reply | Threaded
Open this post in threaded view
|

Re: Starting with TCL

Cecil Westerhof-5
In reply to this post by Cecil Westerhof-5
​Is it possible to get the library version before connecting to a database?
Now I do the following:
#!/usr/bin/env tclsh

package require sqlite3


sqlite3 db ~/Databases/general.sqlite

puts [db version]


But I would prefer to check the version before connecting to a database. Is
this possible?

--
Cecil Westerhof
_______________________________________________
sqlite-users mailing list
[hidden email]
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
Reply | Threaded
Open this post in threaded view
|

Re: Starting with TCL

Richard Hipp-3
On 11/16/17, Cecil Westerhof <[hidden email]> wrote:
> ​Is it possible to get the library version before connecting to a database?

puts [sqlite -version]

--
D. Richard Hipp
[hidden email]
_______________________________________________
sqlite-users mailing list
[hidden email]
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
Reply | Threaded
Open this post in threaded view
|

Re: Starting with TCL

Eric-2
In reply to this post by Cecil Westerhof-5
On Thu, 16 Nov 2017 21:28:10 +0100, Cecil Westerhof <[hidden email]> wrote:

> Is it possible to get the library version before connecting to a database?
> Now I do the following:
> #!/usr/bin/env tclsh
>
> package require sqlite3
>
>
> sqlite3 db ~/Databases/general.sqlite
>
> puts [db version]
>
>
> But I would prefer to check the version before connecting to a database. Is
> this possible?

Yes:

set ver [package require sqlite3]

puts $ver

Eric
--
ms fnd in a lbry
_______________________________________________
sqlite-users mailing list
[hidden email]
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
Reply | Threaded
Open this post in threaded view
|

Re: Starting with TCL

Cecil Westerhof-5
In reply to this post by Richard Hipp-3
2017-11-16 22:20 GMT+01:00 Richard Hipp <[hidden email]>:

> On 11/16/17, Cecil Westerhof <[hidden email]> wrote:
> > ​Is it possible to get the library version before connecting to a
> database?
>
> puts [sqlite -version]
>

​Combining yours and Eric's version, I made:
    #!/usr/bin/env tclsh

    package require sqlite3

    set    sqliteVersion [sqlite3 -version]
    puts   ${sqliteVersion}
    sqlite db ~/Databases/general.sqlite

In real life I will use it to checkthe SQLite verion if necessary.

--
Cecil Westerhof
_______________________________________________
sqlite-users mailing list
[hidden email]
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
Reply | Threaded
Open this post in threaded view
|

Re: Starting with TCL

Cecil Westerhof-5
2017-11-17 5:38 GMT+01:00 Cecil Westerhof <[hidden email]>:

>     set    sqliteVersion [sqlite3 -version]
>

​By the way, I think it is a good idea to amend:
    https://sqlite.org/tclsqlite.html
to show this possibility.

--
Cecil Westerhof
_______________________________________________
sqlite-users mailing list
[hidden email]
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
Reply | Threaded
Open this post in threaded view
|

Re: Starting with TCL

Cecil Westerhof-5
In reply to this post by Cecil Westerhof-5
The folowing works:
    db eval ${getTeasToDrinkStr} {
        puts [format "%-30s %-10s %2s %d" ${Tea} ${Last Used} ${Location}
${Randomiser}]
    }

But I want to reuse what I get, so I tried the following:
​    set teaChoices [db eval ${getTeasToDrinkStr}]
    foreach tea [array names teaChoices] {
        puts ${teaChoices}(${tea})
    }

But that does not work. teaChoices is filled, but not as an array. When
using:
    puts ${teaChoices}
    puts [array size teaChoices]

I see in the first line what I expect in teaChoices, but the size is zero.
So teaChoices is a string instead of an array. How do I get it filled as an
array?

--
Cecil Westerhof
_______________________________________________
sqlite-users mailing list
[hidden email]
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
Reply | Threaded
Open this post in threaded view
|

Re: Starting with TCL

Dan Kennedy-4
On 11/17/2017 03:20 PM, Cecil Westerhof wrote:

> The folowing works:
>      db eval ${getTeasToDrinkStr} {
>          puts [format "%-30s %-10s %2s %d" ${Tea} ${Last Used} ${Location}
> ${Randomiser}]
>      }
> ​
> But I want to reuse what I get, so I tried the following:
> ​    set teaChoices [db eval ${getTeasToDrinkStr}]
>      foreach tea [array names teaChoices] {
>          puts ${teaChoices}(${tea})
>      }
>
> But that does not work. teaChoices is filled, but not as an array. When
> using:
>      puts ${teaChoices}
>      puts [array size teaChoices]
>
> I see in the first line what I expect in teaChoices, but the size is zero.
> So teaChoices is a string instead of an array. How do I get it filled as an
> array?

$teaChoices is a Tcl list. Assuming your query is still:

   SELECT tea, "last used", location FROM teaInStock;

then $teaChoices contains three elements for each row returned by the
query. The first of each set of three is the "tea", the second the "last
used" value and the third the "location". So:

   set teaChoices [db eval $getTeasToDrinkStr]
   for {t last_used loc} $teaChoices {
     puts $t
   }

will print the list of teas.

Not sure whether or not you really want an "array". In Tcl, array means
associative array - a key-value structure like an STL map. A list is a
flat vector of values, like an STL vector or an array in plain old C.

Dan.


_______________________________________________
sqlite-users mailing list
[hidden email]
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
Reply | Threaded
Open this post in threaded view
|

Re: Starting with TCL

Cecil Westerhof-5
2017-11-17 9:38 GMT+01:00 Dan Kennedy <[hidden email]>:

> On 11/17/2017 03:20 PM, Cecil Westerhof wrote:
>
>> The folowing works:
>>      db eval ${getTeasToDrinkStr} {
>>          puts [format "%-30s %-10s %2s %d" ${Tea} ${Last Used} ${Location}
>> ${Randomiser}]
>>      }
>> ​
>> But I want to reuse what I get, so I tried the following:
>> ​    set teaChoices [db eval ${getTeasToDrinkStr}]
>>      foreach tea [array names teaChoices] {
>>          puts ${teaChoices}(${tea})
>>      }
>>
>> But that does not work. teaChoices is filled, but not as an array. When
>> using:
>>      puts ${teaChoices}
>>      puts [array size teaChoices]
>>
>> I see in the first line what I expect in teaChoices, but the size is zero.
>> So teaChoices is a string instead of an array. How do I get it filled as
>> an
>> array?
>>
>
> $teaChoices is a Tcl list. Assuming your query is still:
>
>   SELECT tea, "last used", location FROM teaInStock;
>

​Nope, this one is:
    SELECT   Tea
    FROM     teaInStock
    ORDER BY "Last Used" DESC
    LIMIT    5
    ;



> then $teaChoices contains three elements for each row returned by the
> query. The first of each set of three is the "tea", the second the "last
> used" value and the third the "location". So:
>
>   set teaChoices [db eval $getTeasToDrinkStr]
>   for {t last_used loc} $teaChoices {
>     puts $t
>   }
>
> will print the list of teas.
>

​This gives:
wrong # args: should be "for start test next command"
    while executing
"for {t last_used loc} $teaChoices {
    puts $t
}

There is something going wrong, because:
    puts [llength teaChoices]
gives:
    1
while it should give:
    5​




> Not sure whether or not you really want an "array". In Tcl, array means
> associative array - a key-value structure like an STL map. A list is a flat
> vector of values, like an STL vector or an array in plain old C.


​Nope, I want a list then. (Or maybe a list of arrays.)​


--
Cecil Westerhof
_______________________________________________
sqlite-users mailing list
[hidden email]
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
Reply | Threaded
Open this post in threaded view
|

Re: Starting with TCL

Simon Slavin-3
In reply to this post by Dan Kennedy-4


On 17 Nov 2017, at 8:38am, Dan Kennedy <[hidden email]> wrote:

> Not sure whether or not you really want an "array". In Tcl, array means associative array - a key-value structure like an STL map. A list is a flat vector of values, like an STL vector or an array in plain old C.

I thought in Tcl everything was a function ?

(don’t know Tcl, not really interested, just funnin’)

Simon.
_______________________________________________
sqlite-users mailing list
[hidden email]
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
Reply | Threaded
Open this post in threaded view
|

Re: Starting with TCL

Dan Kennedy-4
In reply to this post by Cecil Westerhof-5

> ​Nope, this one is:
>      SELECT   Tea
>      FROM     teaInStock
>      ORDER BY "Last Used" DESC
>      LIMIT    5
>      ;
> ​
>
>
>> then $teaChoices contains three elements for each row returned by the
>> query. The first of each set of three is the "tea", the second the "last
>> used" value and the third the "location". So:
>>
>>    set teaChoices [db eval $getTeasToDrinkStr]
>>    for {t last_used loc} $teaChoices {
>>      puts $t
>>    }
>>
>> will print the list of teas.
>>
> ​This gives:
> wrong # args: should be "for start test next command"
>      while executing
> "for {t last_used loc} $teaChoices {
>      puts $t
> }

Sorry - [for] should be [foreach].

So with your query as above, you want:

   set teaChoices [db eval $getTeasToDrinkStr]
   foreach t $teaChoices {
     puts $t
   }

> There is something going wrong, because:
>      puts [llength teaChoices]
> gives:
>      1
> while it should give:
>      5​

Missing $ sign. Should be:

   puts [llength $teaChoices]

Without the $, it's returning the length of the literal "teaChoices" -
one element. Not the length of the list contained in the variable named
"teaChoices".

Dan.


_______________________________________________
sqlite-users mailing list
[hidden email]
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
Reply | Threaded
Open this post in threaded view
|

Re: Starting with TCL

Clemens Ladisch
In reply to this post by Simon Slavin-3
Simon Slavin wrote:
> I thought in Tcl everything was a function ?

In Tcl, all values are strings.  <https://wiki.tcl.tk/3018>

A list is a string with entries as words according to the Tcl syntax rules.
A dictionary is a list with an even number of elements (key/value pairs).
(An array is not a value; it's a special kind of variable.)


Regards,
Clemens
_______________________________________________
sqlite-users mailing list
[hidden email]
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
Reply | Threaded
Open this post in threaded view
|

Re: Starting with TCL

Simon Slavin-3
On 16 Nov 2017, at 6:58pm, Richard Hipp <[hidden email]> wrote:

> Everything in TCL is a function.

On 17 Nov 2017, at 8:38am, Dan Kennedy <[hidden email]> wrote:

> `In Tcl, array means associative array - a key-value structure like an STL map. A list is a flat vector of values, like an STL vector or an array in plain old C.

On 17 Nov 2017, at 9:52am, Clemens Ladisch <[hidden email]> wrote:

> In Tcl, all values are strings.  <https://wiki.tcl.tk/3018>

Fight !  Fight !  Fight !  Sliderules at dawn !

Simon.
_______________________________________________
sqlite-users mailing list
[hidden email]
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
Reply | Threaded
Open this post in threaded view
|

Re: Starting with TCL

Cecil Westerhof-5
In reply to this post by Dan Kennedy-4
2017-11-17 9:57 GMT+01:00 Dan Kennedy <[hidden email]>:

>
> This gives:
>> wrong # args: should be "for start test next command"
>>      while executing
>> "for {t last_used loc} $teaChoices {
>>      puts $t
>> }
>>
>
> Sorry - [for] should be [foreach].
>
> So with your query as above, you want:
>
>   set teaChoices [db eval $getTeasToDrinkStr]
>   foreach t $teaChoices {
>     puts $t
>   }
>

​That solved it, yes.




>
> There is something going wrong, because:
>>      puts [llength teaChoices]
>> gives:
>>      1
>> while it should give:
>>      5​
>>
>
> Missing $ sign. Should be:
>
>   puts [llength $teaChoices]
>
> Without the $, it's returning the length of the literal "teaChoices" - one
> element. Not the length of the list contained in the variable named
> "teaChoices".


​That was the problem.


I am trying a bit different route:
    set teaList []
    db eval ${getTeasToDrinkStr} {
        lappend teaList [dict create                \
                             Tea        ${Tea}      \
                             LastUsed   ${LastUsed} \
                             Location   ${Location} \
                             Randomiser ${Randomiser}]
    }
    set i 0
    while {${i} < [llength ${teaList}]} {
        puts [format "%d: %-30s %-10s %2s %d" [expr ${i} + 1] \
                  [dict get [lindex $teaList ${i}] Tea]       \
                  [dict get [lindex $teaList ${i}] LastUsed]  \
                  [dict get [lindex $teaList ${i}] Location]  \
                  [dict get [lindex $teaList ${i}] Randomiser]]
        incr i
    }

This does what I want (I need the value of Tea to update the database), but
is there a better way?

--
Cecil Westerhof
_______________________________________________
sqlite-users mailing list
[hidden email]
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
Reply | Threaded
Open this post in threaded view
|

Re: Starting with TCL

Cecil Westerhof-5
In reply to this post by Cecil Westerhof-5
​In Bash I can use:
    continue 2
​to continue not the current loop, but the loop surrounding it.

This does not work in TCL. Is there another way to do this?

--
Cecil Westerhof
_______________________________________________
sqlite-users mailing list
[hidden email]
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
Reply | Threaded
Open this post in threaded view
|

Re: Starting with TCL

Cecil Westerhof-5
In reply to this post by Cecil Westerhof-5
I have the following:
    set getLatestTeasStr {
        SELECT   Tea
        FROM     teaInStock
        ORDER BY LastUsed DESC
        LIMIT    5
        ;
    }

But because I want to define the limit at runtime I want to change it to:
    set getLatestTeasStr {
        SELECT   Tea
        FROM     teaInStock
        ORDER BY LastUsed DESC
        LIMIT    ?
        ;
    }

​In Java I would do something like:
     psSel.setInt(1, nrToFetch);​

​How do I do this in TCL?​

--
Cecil Westerhof
_______________________________________________
sqlite-users mailing list
[hidden email]
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
Reply | Threaded
Open this post in threaded view
|

Re: Starting with TCL

Peter da Silva
Sqlite will perform the substitution of Tcl variables in a query. You can flag the variable with a ‘$’ or with a ‘:’ (which makes it more like other SQL APIs).

So you can write:

    $db eval {
            SELECT   Tea
            FROM     teaInStock
            ORDER BY LastUsed DESC
            LIMIT   :nrToFetch;
    } {
            ...
    }

or even

    $db eval {
            SELECT   Tea
            FROM     teaInStock
            ORDER BY LastUsed DESC
            LIMIT   $nrToFetch;
    } {
            ... do something with $Tea ...
    }

This latter case works because the query is surrounded by {} so Tcl won’t substitute the variable, it will be seen and securely inserted into the query by SQLite.

This is rather nifty, which is why I recently added pretty much exactly this functionality to Pgtcl (though due to differences between PostgreSQL and SQLite syntax I had to restrict it to using “:”). The equivalent code would be:

pg_select $db -variables {
            SELECT   Tea
            FROM     teaInStock
            ORDER BY LastUsed DESC
            LIMIT   :nrToFetch;
    } row {
            ... do something with $row(Tea) ...
    }


_______________________________________________
sqlite-users mailing list
[hidden email]
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
12