Skysnake

SQLite Alter Table Drop Column (with some AutoIt magic)

1 post in this topic

#1 ·  Posted (edited)

#cs ----------------------------------------------------------------------------

    AutoIt Version: 3.3.14.0
    Author:         Skysnake
    Date:           2016.05.17

    Script Function:
    Alter table drop column.

    By way of example the original table has these three columns:
    name,surname,nickname
    to be replaced with
    name,surname,number

    "We cannot drop a specific column in SQLite 3. See the FAQ. It is not possible to
    rename a column, remove a column, or add or remove constraints from a table.
    While you can always create a new table and then drop the older one.Dec 9, 2011"

    http://stackoverflow.com/questions/8442147/how-to-delete-or-add-column-in-sqlite


    The only ALTER TABLE functions that SQLite supports are those that can be
    accomplished without having to rewrite the entire table.  Or, to put it
    another way, the only ALTER TABLE functions supported are those that can be
    done in O(1) time.

    ALTER TABLE DROP COLUMN requires writing every row of the table.

    D. Richard Hipp

    http://sqlite.1065341.n5.nabble.com/Why-can-t-SQLite-drop-columns-td64687.html

    Notes:
    Required original array size of table to be altered (zero count +1)
    Required the name of the column to drop
    Required full "create table ..." syntax for new column eg: , number  text   not null default 'Contact Number')


#ce ----------------------------------------------------------------------------

; Script Start - Add your code below here
; SQLite.dll version must match

#include <Array.au3>
#include <MsgBoxConstants.au3>
#include <SQLite.au3>
#include <SQLite.dll.au3>

Local Const $iMax = 6 ; this is the current table column count
Local $query, $aResult, $iRows, $iColumns, $ColName, $sMsg, $mySQLdb, $aLessColmns[$iMax][1] ; will require only one column
Local $sNewColNames


Local $sLocalSQLiteDll = "sqlite3_win32-x86-3120100.dll" ;

Local $sSQliteDll = _SQLite_Startup($sLocalSQLiteDll, False, 1)
If @error Then
    MsgBox($MB_SYSTEMMODAL, "SQLite Error", "SQLite3.dll Can't be Loaded!" & @CRLF & @CRLF & _
            "Not FOUND in @SystemDir, @WindowsDir, @ScriptDir, @WorkingDir or from www.autoitscript.com")
    Exit -1
EndIf

ConsoleWrite("_SQLite_LibVersion=" & _SQLite_LibVersion() & @CRLF)
$mySQLdb = _SQLite_Open(@ScriptDir & '\mydb.sql3')


; Without $sCallback it's a resultless statement
; ..................................................... create table tORiginal for System Info
$query = "CREATE TABLE if not exists tORiginal ( " _
         & "id  integer primary     key         unique not null " _
         & ", name                  text        not null default 'First Name' " _
         & ", surname               text        not null default 'Surname' " _
         & ", nickname              text        not null default 'Nickname' " _
         & ", RegDate               text        NOT NULL DEFAULT current_timestamp " _
         & " ) ; "

If Not _SQLite_Exec($mySQLdb, $query) = $SQLITE_OK Then _
        MsgBox($MB_SYSTEMMODAL, "SQLite Error", _SQLite_ErrMsg() & " * " & @ScriptLineNumber)

ConsoleWrite("SQL created tORiginal " & @CRLF)



; Without $sCallback it's a resultless statement
; ..................................................... create table tORiginal for System Info
$query = "Insert into tORiginal (name,surname,nickname) values ('John','Jones','Johnny') " _
         & "  ; "

If Not _SQLite_Exec($mySQLdb, $query) = $SQLITE_OK Then _
        MsgBox($MB_SYSTEMMODAL, "SQLite Error", _SQLite_ErrMsg() & " * " & @ScriptLineNumber)

ConsoleWrite("SQL data inserted into tORiginal " & @CRLF)



$query = " SELECT * FROM tORiginal " _
         & " ; "
; Query
; Query
$iRval = _SQLite_GetTable2d($mySQLdb, $query, $aResult, $iRows, $iColumns)
If $iRval = $SQLITE_OK Then
    ConsoleWrite("$SQLITE_OK at Line " & $SQLITE_OK & " " &@ScriptLineNumber & @CRLF)
Else
    MsgBox($MB_SYSTEMMODAL, "SQLite Error: " & $iRval, _SQLite_ErrMsg())
EndIf

_ArrayDisplay($aResult, "CREATEd and INSERTed"  ) ; show the results of insert into SQL table

; https://www.sqlite.org/pragma.html#pragma_table_info
$query = " pragma table_info(tORiginal) " _
         & " ; "
; Query
; Query
$iRval = _SQLite_GetTable2d($mySQLdb, $query, $aResult, $iRows, $iColumns)
If $iRval = $SQLITE_OK Then
    ConsoleWrite("$SQLITE_OK at Line " & $SQLITE_OK & " " &@ScriptLineNumber & @CRLF)
Else
    MsgBox($MB_SYSTEMMODAL, "SQLite Error: " & $iRval, _SQLite_ErrMsg())
EndIf

_ArrayDisplay($aResult, "Pragma Result: Original Table") ; show the results of pragma

ConsoleWrite("SQL PRAGMA to find all columns " & @CRLF)

; this is the AutoIt magic
; SQLite does not allow manipulation of the pragma result, but
; AutoIt can process this result as normal query result
; delete column %id% from result
#cs
    Row|Col 0   |Col 1      |Col 2  |Col 3  |Col 4      |Col 5
    [0]|cid     |name       |type   |notnull|dflt_value |pk
    [1]|0       |id         |INTEGER|0      |           |1
    [2]|1       |Name       |       |0      |           |0
    [3]|2       |Surname    |       |0      |           |0
    [4]|3       |Nickname   |       |0      |           |0 ; >>>>>>>>> column to remove

#CE

;https://www.autoitscript.com/wiki/Arrays#Comparing_Arrays
Local $transfound = UBound($aResult, 1) - 1
For $i = 1 To $transfound Step +1
    ConsoleWrite($aResult[$i][1] & @CRLF)

    ; Check that the array is big enough
    If UBound($aLessColmns) = $i Then
        ; Resize the array when $i is equal to the element count in the array to prevent subscript error
        ReDim $aLessColmns[$aLessColmns[0] + $iMax]

    EndIf

    If StringRegExp($aResult[$i][1], "nick") Then ;>>>>>>>>> remove the column you dont want
        ; it may be dangerous to use a Regex here :)
        ; do nothing, do not add this column name
    Else
        $aLessColmns[$i][0] = $aResult[$i][1] ; safely add data to new index element

        $aLessColmns[0][0] = $i ; update the index count for future reference
    EndIf
Next

; for good measure
; Move backwards through the array deleting the blank lines
For $i = UBound($aLessColmns) - 1 To 0 Step -1
    If $aLessColmns[$i][0] = "" Then
        _ArrayDelete($aLessColmns, $i)
    EndIf
Next
$aLessColmns[0][0] = UBound($aLessColmns) - 1 ; update the index count for future reference

_ArrayDisplay($aLessColmns, "Column names from tORiginal less one") ; show the new array

$sNewColNames = _ArrayToString($aLessColmns, "|", 1, -1, ",") ; entire array
MsgBox(0, "$sNewColNames", $sNewColNames)
ConsoleWrite("New Column names " & $sNewColNames & @CRLF)
; now create a transient table, insert values, and rename

_SQLite_Exec($mySQLdb, "Create table tNewColumns (" & $sNewColNames & ", number             text        not null default 'Contact Number') ; " & _
        "Insert into tNewColumns (" & $sNewColNames & ") select " & $sNewColNames & " from  tORiginal " & _
        " ; ")


$query = " SELECT * FROM tNewColumns " _
         & " ; "
; Query
; Query
$iRval = _SQLite_GetTable2d($mySQLdb, $query, $aResult, $iRows, $iColumns)
If $iRval = $SQLITE_OK Then
    ConsoleWrite("$SQLITE_OK at Line " & $SQLITE_OK & @ScriptLineNumber & @CRLF)
Else
    MsgBox($MB_SYSTEMMODAL, "SQLite Error: " & $iRval, _SQLite_ErrMsg())
EndIf

_ArrayDisplay($aResult, "Results of new table") ; show the results of insert into SQL table

; https://www.sqlite.org/pragma.html#pragma_table_info
$query = " pragma table_info(tORiginal) " _
         & " ; "
; Query
; Query
$iRval = _SQLite_GetTable2d($mySQLdb, $query, $aResult, $iRows, $iColumns)
If $iRval = $SQLITE_OK Then
    ConsoleWrite("$SQLITE_OK at Line " & $SQLITE_OK & " " &@ScriptLineNumber & @CRLF)
Else
    MsgBox($MB_SYSTEMMODAL, "SQLite Error: " & $iRval, _SQLite_ErrMsg())
EndIf

_ArrayDisplay($aResult, "Pragma Result: altered Original Table") ; show the results of pragma

ConsoleWrite("SQL PRAGMA to find all columns " & @CRLF)


; now drop tORiginal
; https://www.sqlite.org/lang_altertable.html
; ALTER TABLE tNewColumns RENAME TO tORiginal


ConsoleWrite("_SQLite_Shutdown " & @CRLF)
_SQLite_Shutdown()
FileDelete($mySQLdb)

I believe it is suitable to post on this Forum, as it serves no purpose anywhere else.  I have searched, and except for some ancient threads referring to this, could find nothing similar.  

I used SQLite 3.12.1 which can be downloaded here Precompiled Binaries for Windows. I see they are up to 3.12.2 now.

Painfully obvious is of course that one must know the name of the offending column. :)

 

Edited by Skysnake
2016.06.04 Improved

Skysnake

Why is the snake in the sky?

Share this post


Link to post
Share on other sites



Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now

  • Similar Content

    • WoodGrain
      By WoodGrain
      Hi guys,
      This post was originally going to be a question on how to fix this issue but as I ended up figuring it out I thought I'd post it here for others that have the same issue.
      So you've downloaded and extracted the latest version of the SQLite dll files etc into the same directory as your SQLite script but it's failing at _SQLite_Startup()?
      What you need to do, that I couldn't see anywhere in the documentation, to fix the issue is rename the dll files from (for example) "sqlite3_301500000.dll" to "sqlite3.dll" and "sqlite3_x64_301500000.dll" to "sqlite3_x64.dll".
      Fixed my issues instantly!
      Hope it can help others too.
      Cheers.
    • corgano
      By corgano
      I have a script working on a windows 7, 64 bit OS that uses SQLite. I went to transfer to to my laptop so i could work on it while on the go, but it's not running on my laptop (Windows 10, 64 bit). The script / autoit is configured to compile and run as x86 for compatibility's sake, via the option during autoit setup.

      Here is the minimal code to reproduce my problem:
       
      #include <SQLite.au3> #include <SQLite.dll.au3> ;Without this, downloading fails HttpSetUserAgent("Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.0") If @CPUArch = "x86" and Not FileExists("sqlite3.dll") then InetGet("https://www.autoitscript.com/autoit3/pkgmgr/sqlite/sqlite3.dll", @ScriptDir&"\sqlite3.dll") ConsoleWrite("Error = "&@error&@CRLF) EndIf If @CPUArch = "x64" and Not FileExists("sqlite3_x64.dll") then InetGet("https://www.autoitscript.com/autoit3/pkgmgr/sqlite/sqlite3_x64.dll", @ScriptDir&"\sqlite3_x64.dll") ConsoleWrite("Error = "&@error&@CRLF) EndIf _SQLite_Startup() If @error Then MsgBox($MB_SYSTEMMODAL, "SQLite Error "&@error, "SQLite3.dll Can't be Loaded!") ConsoleWrite("https://www.autoitscript.com/autoit3/pkgmgr/sqlite/sqlite3_x64.dll"&@CRLF) ConsoleWrite(@ScriptDir&"\sqlite3_x64.dll"&@CRLF) Exit -1 EndIf Downloading works, files are downlaoded to @scriptdir properly. However no matter what I do with _SQLite_Startup() it fails with @error = 1. I've tried explicitly telling it to use both versions of the dll as well as leaving it to choose for itself and nothing works. Help?
    • kashamalasha
      By kashamalasha
      Hello.
      Could you help me find the answer for my issue. I'm trying to set fomatted data wich is selected from SQL to GUICtrlCreateEdit field.
      And the GUICtrlSetData function is inserting it in one line. But when I'm trying to do the same using MsgBox the data looks fine.
      I'm trying to not use _GUICtrlRichEdit UDF.
      Thanks in advance. Here is my test code.
      #include <GUIConstants.au3> #include <GUIListBox.au3> #include <SQLite.au3> #include <SQLite.dll.au3> Opt("GUIOnEventMode", 1) $hMainGUI = GUICreate("Test", 520, 240) $hListBox = _GUICtrlListBox_Create($hMainGUI, "", 10, 10, 180, 80) $hEdit = GUICtrlCreateEdit("", 10, 80, 500, 150) GUISetState(@SW_SHOW, $hMainGUI) GUIRegisterMsg($WM_COMMAND, "_WM_COMMAND") Local $hQuery, $aRow _SQLite_Startup() $hDB = _SQLite_Open('MyDB.sqlite') _SQLite_Query(-1, "SELECT ID ||"". "" || Name FROM Templates ORDER BY ID;", $hQuery) While _SQLite_FetchData($hQuery, $aRow) = $SQLITE_OK _GUICtrlListBox_AddString($hListBox, $aRow[0]) WEnd GUISetOnEvent($GUI_EVENT_CLOSE, "CLOSE_Button") While 1 Sleep(100) WEnd Func _WM_COMMAND($hWnd, $iMsg, $wParam, $lParam) Local $hWndFrom, $iIDFrom, $iCode, $hWndListBox If Not IsHWnd($hListBox) Then $hWndListBox = GUICtrlGetHandle($hListBox) $hWndFrom = $lParam $iIDFrom = BitAND($wParam, 0xFFFF) ; Low Word $iCode = BitShift($wParam, 16) ; Hi Word Switch $hWndFrom Case $hListBox, $hWndListBox Switch $iCode Case $LBN_DBLCLK Select_Template(_GUICtrlListBox_GetCurSel($hListBox) + 1) Return 0 EndSwitch EndSwitch EndFunc ;==>_WM_COMMAND Func CLOSE_Button() _SQLite_Close() _SQLite_Shutdown() Exit EndFunc ;==>CLOSE_Button Func Select_Template($sListItem) _SQLite_QuerySingleRow($hDB, _ "SELECT Content " & _ "FROM Templates " & _ "WHERE ID = " & $sListItem & ";", $aRow) ;~ MsgBox(64, "Test: " & $sListItem, $aRow[0]) GUICtrlSetData($hEdit, $aRow[0]) WinSetTitle($hMainGUI, "", "Test: " & $sListItem) EndFunc ;==>Select_Template  


    • Blank517
      By Blank517
      Hi, when a client sends /logout to the server i want that the server find the id of the client and set his online status to 0
      Using 'default' I have no problems, but when I do this with 'blank517' gives me id 10 and then for the database remains online
      Database while Blank517 logout:
      id  |  username  |  password |   permissions  |  online  |
      0   |  default        |    pass1      |           0             |      0       |
      1   |  Blank517    |     pass       |           0              |     1       |
      Server recv:
      Func _Recv_From_Sockets_() For $0 = 1 To $max_connections $Recv = TCPRecv ($Socket_Data[$0][0],1024) If StringLeft($Recv, 1) = "/" Then If StringInStr($Recv, "logout") Then _SQLite_Query(-1, "SELECT id FROM Users WHERE username = '" & $Socket_Data[$0][1] & "' AND online = '1';", $hQuery) While _SQLite_FetchData($hQuery, $aRow) = $SQLITE_OK $sMsg &= $aRow[0]; <-- $sMsg = 10 after FetchData WEnd _SQLite_Exec(-1, "UPDATE Users SET online = '0' WHERE id = '" & $sMsg & "';") For $000 = 1 To $max_connections TCPSend($Socket_Data[$000][0], $Socket_Data[$0][1] & " ha effettuato il logout") Next TCPCloseSocket($Socket_Data[$0][0]) $sMsg = Null EndIf Else _Broadcast_To_Sockets_ ($Recv) EndIf Next EndFunc *excuse me for my bad english *
    • Blank517
      By Blank517
      Hi, I would like to make a login script with SQLite. The database looks like this:
      _SQLite_Exec(-1, "CREATE TABLE Users (id INT(8) NOT NULL, username VARCHAR(30) NOT NULL, password VARCHAR(255) NOT NULL, permission INT(8) NOT NULL, PRIMARY KEY (id)); CREATE UNIQUE INDEX 'user_name_unique' ON 'Users' ('username' );") _SQLite_Exec(-1, "INSERT INTO Users(id, username, password, permission) VALUES ('0', 'default', 'password', '0');")
      theoretically I should make a query in this way, right?
      _SQLite_Query (-1, "SELECT id FROM Users WHERE username = '" & $ Recv [1] & "' AND password = '" & $ Recv [2] & "';", $ hQuery)
      but then I do not know how to know if you have found the 'id'