0

在 SQL lite 中创建了数据库,并改进了处理它的小程序(列出、添加、删除记录)。此时,我正在尝试使用准备好的语句 step() 函数列出数据库中的内容。但是,我无法遍历数据库中的行和列。

我怀疑其原因是我没有在这一行中适当地处理该语句:

stmt:Sqlite.Statement = null

如果是这种情况,如何将语句从主(init)函数传递给子函数?

这是到目前为止的全部代码:

// Trying to do a cookbook program
// raw_imput for Genie included, compile with valac --pkg sqlite3 cookbook.gs
[indent=4]
uses Sqlite

def raw_input (query:string = ""):string
    stdout.printf ("%s", query)
    return stdin.read_line ()


init
    db : Sqlite.Database? = null
    if (Sqlite.Database.open ("cookbook.db3", out db) != Sqlite.OK)
        stderr.printf ("Error: %d: %s \n", db.errcode (), db.errmsg ())
        Process.exit (-1)

    loop:bool = true
    while loop = true
        print "==================================================="
        print "                 RECIPE DATABASE  "
        print " 1 - Show All Recipes"
        print " 2 - Search for a recipe"
        print " 3 - Show a Recipe"
        print " 4 - Delete a recipe"
        print " 5 - Add a recipe"
        print " 6 - Print a recipe"
        print " 0 - Exit"
        print "==================================================="
        response:string = raw_input("Enter a selection -> ")
        if response == "1" // Show All Recipes
            PrintAllRecipes()
        else if response is "2" // Search for a recipe
            pass
        else if response is "3" //Show a Recipe
            pass
        else if response is "4"//Delete a recipe
            pass
        else if response is "5" //Add a recipe
            pass
        else if response is "6" //Print a recipe
            pass
        else if response is "0" //Exit
            print "Goodbye"
            Process.exit (-1)
        else
            print "Unrecognized command. Try again."


def PrintAllRecipes ()
    print "%-5s%-30s%-20s%-30s", "Item", "Name", "Serves", "Source"
    print "--------------------------------------------------------------------------------------"
    stmt:Sqlite.Statement = null
    param_position:int = stmt.bind_parameter_index ("$UID")
    //assert (param_position > 0)

    stmt.bind_int (param_position, 1)
    cols:int = stmt.column_count ()
    while stmt.step () == Sqlite.ROW
        for i:int = 0 to cols
            i++
            col_name:string = stmt.column_name (i)
            val:string = stmt.column_text (i) 
            type_id:int = stmt.column_type (i)
            stdout.printf ("column: %s\n", col_name)
            stdout.printf ("value: %s\n", val)
            stdout.printf ("type: %d\n", type_id)


/*    while stmt.step () == Sqlite.ROW
            col_item:string = stmt.column_name (1)
            col_name:string = stmt.column_name (2)
            col_serves:string = stmt.column_name (3)
            col_source:string = stmt.column_name (4)
            print "%-5s%-30s%-20s%-30s", col_item, col_name, col_serves, col_source */

额外的问题是:

  • 函数的定义应该在init之前还是之后?我注意到如果我在初始化后离开所有这些,它们就不会被调用。但是通过在开头留下 raw_input 错误消失了。

  • 出于教学原因,我试图在一个类中定义 PrintAllRecipes()。但我最终使它对主程序“不可见”。

非常感谢,

4

1 回答 1

1

Yes, you need to assign a prepared statement, not null, to stmt. For example:

// Trying to do a cookbook program
// raw_input for Genie included, compile with
// valac --pkg sqlite3 --pkg gee-0.8 cookbook.gs
[indent=4]
uses Sqlite

init
    db:Database
    if (Database.open ("cookbook.db3", out db) != OK)
        stderr.printf ("Error: %d: %s \n", db.errcode (), db.errmsg ())
        Process.exit (-1)

    while true
        response:string = UserInterface.get_input_from_menu()
        if response is "1" // Show All Recipes
            PrintAllRecipes( db )
        else if response is "2" // Search for a recipe
            pass
        else if response is "3" //Show a Recipe
            pass
        else if response is "4"//Delete a recipe
            pass
        else if response is "5" //Add a recipe
            pass
        else if response is "6" //Print a recipe
            pass
        else if response is "0" //Exit
            print "Goodbye"
            break
        else
            print "Unrecognized command. Try again."

namespace UserInterface
    def get_input_from_menu():string
        show_menu()
        return raw_input("Enter a selection -> ")

    def raw_input (query:string = ""):string
        stdout.printf ("%s", query)
        return stdin.read_line ()

    def show_menu()
        print """===================================================
                 RECIPE DATABASE  
 1 - Show All Recipes
 2 - Search for a recipe
 3 - Show a Recipe
 4 - Delete a recipe
 5 - Add a recipe
 6 - Print a recipe
 0 - Exit
==================================================="""

namespace PreparedStatements
    def select_all( db:Database ):Statement
        statement:Statement
        db.prepare_v2( """
select name, servings as serves, source from Recipes
""", -1, out statement )
        return statement

def PrintAllRecipes ( db:Database )
    print "%-5s%-30s%-20s%-30s", "Item", "Name", "Serves", "Source"
    print "--------------------------------------------------------------------------------------"
    stmt:Statement = PreparedStatements.select_all( db )
    cols:int = stmt.column_count ()
    var row = new dict of string, string
    item:int = 1
    while stmt.step() == ROW
        for i:int = 0 to (cols - 1)
            row[ stmt.column_name( i ) ] = stmt.column_text( i )
        stdout.printf( "%-5s", item.to_string( "%03i" ))
        stdout.printf( "%-30s", row[ "name" ])
        stdout.printf( "%-20s", row[ "serves" ])
        stdout.printf( "%-30s\n", row[ "source" ])
        item++

A few pointers

  • Generally you want to avoid assigning null. null is no value. For example a boolean can either be true or false and nothing else, but a variable that can have no value makes things more complicated.

    a:bool? = null
    if a == null
        print "I'm a boolean variable, but I am neither true nor false???"
    

    If you are looking to declare a variable in Genie before assigning a value, for example when calling a function with an out parameter, don't assign anything. I have changed db:Database to show this

  • Process.exit( -1 ) should probably be used sparingly and really only for error conditions that you want to signal to a calling command line script. I don't think a user selected exit from the program is such an error condition, so I have changed Process.exit( -1 ) to break for that
  • The definition of functions doesn't matter whether it is before or after init, I prefer to put them after so the first function that is called, i.e. init, is at the top and easy to read
  • A class is a data type and yes, it can have functions, but usually you need some data defined in the class and the function is written to act on that data. A function in a class is often called a 'method' and in the past with object oriented programming classes were defined to group methods together. These methods had no data to act on and are defined as 'static' methods. The modern practise is to mainly use static methods for creating more complex object constructors, look up 'factory' methods and creational design patterns. Instead to group functions, and other syntax, we use namespaces. I have used a couple of namespaces in the example. Usually a namespace is given its own file or files. If you are thinking of splitting your Genie project into more source files then take a look at https://wiki.gnome.org/Projects/Genie#A_Simple_Build_Script
  • A primary key should be internal to the database and would not be presented to a user, only a database administrator would be interested in such things. So I have changed 'item' in the output to be a count of the number of entries displayed
  • Genie and Vala bind the SQLite C interface. If you need more details on a particular function take a look at C-language Interface Specification for SQLite
于 2015-10-21T00:18:07.413 回答