Initial import
37
sqlitebrowser/sqlitebrowser/LICENSING
Executable file
@@ -0,0 +1,37 @@
|
||||
LICENSING
|
||||
|
||||
This code is in Public Domain. You may do whatever you want with it.
|
||||
|
||||
However, the same is not true for the QT libraries needed to build
|
||||
this software, and this places restrictions on how you can develop
|
||||
and license derivative works.
|
||||
|
||||
The original code was developed and built with licensed, commercial
|
||||
versions of QT on Windows, MacOSX and Linux. Developers that wish to
|
||||
produce derivative work and use it in commercial projects need to hold
|
||||
a commercial QT license as well.
|
||||
|
||||
If you are however working on a project licensed with the GPL you may
|
||||
use the GPL version of QT. You may release derivative works of this
|
||||
software with any license you want as long as we are concerned, including
|
||||
GPL, Public Domain or Commercial, provided you abide to the licensing
|
||||
terms of the version of QT you have installed on your machine.
|
||||
|
||||
There are GPL versions of QT available at www.trolltech.com for X11
|
||||
and MacOSX, and QT/GPL is included in almost all Linux distributions
|
||||
since it is required by KDE. Windows users however need to purchase
|
||||
a commercial version of QT (there is no GPL version for QT 3.x for
|
||||
Windows at this time.) Please consult www.trolltech.com/licensing
|
||||
if you have any questions about QT licensing.
|
||||
|
||||
|
||||
REDISTRIBUTION OF BINARIES
|
||||
|
||||
The binaries available at sqlitebrowser.sourceforge.net were built with
|
||||
fully licensed commercial versions of QT, and can be redistributed freely.
|
||||
The Windows and MacOSX versions are standalone applications (QT has been
|
||||
statically linked), while the Linux packages used a shared QT 3 library
|
||||
for better compatibility.
|
||||
|
||||
|
||||
|
||||
83
sqlitebrowser/sqlitebrowser/aboutform.ui
Executable file
@@ -0,0 +1,83 @@
|
||||
<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
|
||||
<class>aboutForm</class>
|
||||
<widget class="QDialog">
|
||||
<property name="name">
|
||||
<cstring>aboutForm</cstring>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>396</width>
|
||||
<height>218</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="caption">
|
||||
<string>About</string>
|
||||
</property>
|
||||
<grid>
|
||||
<property name="name">
|
||||
<cstring>unnamed</cstring>
|
||||
</property>
|
||||
<widget class="QTextBrowser" row="0" column="0" rowspan="1" colspan="2">
|
||||
<property name="name">
|
||||
<cstring>aboutBrowser</cstring>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>Plain</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Text</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" row="1" column="1">
|
||||
<property name="name">
|
||||
<cstring>okButton</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<spacer row="1" column="0">
|
||||
<property name="name">
|
||||
<cstring>spacer5</cstring>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint">
|
||||
<size>
|
||||
<width>141</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</grid>
|
||||
</widget>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>okButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>aboutForm</receiver>
|
||||
<slot>close()</slot>
|
||||
</connection>
|
||||
</connections>
|
||||
<includes>
|
||||
<include location="local" impldecl="in implementation">sqlitedb.h</include>
|
||||
<include location="local" impldecl="in implementation">aboutform.ui.h</include>
|
||||
</includes>
|
||||
<functions>
|
||||
<function access="private" specifier="non virtual">init()</function>
|
||||
</functions>
|
||||
<pixmapinproject/>
|
||||
<layoutdefaults spacing="6" margin="11"/>
|
||||
</UI>
|
||||
14
sqlitebrowser/sqlitebrowser/aboutform.ui.h
Executable file
@@ -0,0 +1,14 @@
|
||||
/****************************************************************************
|
||||
** ui.h extension file, included from the uic-generated form implementation.
|
||||
**
|
||||
** If you wish to add, delete or rename functions or slots use
|
||||
** Qt Designer which will update this file, preserving your code. Create an
|
||||
** init() function in place of a constructor, and a destroy() function in
|
||||
** place of a destructor.
|
||||
*****************************************************************************/
|
||||
|
||||
void aboutForm::init()
|
||||
{
|
||||
this->setCaption(applicationName);
|
||||
aboutBrowser->setText(aboutText);
|
||||
}
|
||||
22
sqlitebrowser/sqlitebrowser/browsermain.cpp
Executable file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
** This file is part of SQLite Database Browser
|
||||
** http://sqlitebrowser.sourceforge.net
|
||||
**
|
||||
** Originally developed by Mauricio Piacentini, Tabuleiro
|
||||
**
|
||||
** The author disclaims copyright to this source code.
|
||||
** Consult the LICENSING file for known restrictions
|
||||
**
|
||||
*/
|
||||
|
||||
#include <qapplication.h>
|
||||
#include "form1.h"
|
||||
|
||||
int main( int argc, char ** argv )
|
||||
{
|
||||
QApplication a( argc, argv );
|
||||
mainForm w;
|
||||
w.show();
|
||||
a.connect( &a, SIGNAL( lastWindowClosed() ), &a, SLOT( quit() ) );
|
||||
return a.exec();
|
||||
}
|
||||
42
sqlitebrowser/sqlitebrowser/building
Executable file
@@ -0,0 +1,42 @@
|
||||
BUILD INSTRUCTIONS AND REQUIREMENTS
|
||||
|
||||
SQLite Database Browser requires Trolltech's QT. It has been tested
|
||||
with versions 3.12 and 3.2 of QT. QT can be included as
|
||||
a static or shared library, depending on the current QT configuration
|
||||
in the building machine. For more information on QT please consult
|
||||
http://www.trolltech.com. The GPL version of QT is available in almost
|
||||
all Linux distributions as a default package.
|
||||
|
||||
SQLite is not used as a shared library: it is built from source code
|
||||
included in this package (sqlite_source directory), and linked as a
|
||||
static library into the executable.
|
||||
|
||||
SQLite is ideal for embedding, so the only requirement
|
||||
for building this code is the presence of QT. QT can be included as
|
||||
a static or shared library, depending on the current QT configuration
|
||||
in the building machine.
|
||||
|
||||
Provided you have QT installed and configured, simply run
|
||||
|
||||
qmake
|
||||
|
||||
followed by
|
||||
|
||||
make
|
||||
|
||||
in the main directory. This will generate the
|
||||
|
||||
browser (or browser.exe, or browser.app)
|
||||
|
||||
application in the browser subdirectory.
|
||||
|
||||
The same process works for building the code
|
||||
in any platform supported by QT (including other Unix systems with
|
||||
X11.)
|
||||
|
||||
You may want to rename the final executable or .app bundle from
|
||||
"browser" to "SQLite Database Browser", to match the official binaries
|
||||
released at http://sqlitebrowser.sourceforge.net.
|
||||
|
||||
Please check the LICENSING file for additional information.
|
||||
|
||||
294
sqlitebrowser/sqlitebrowser/createindexform.ui
Executable file
@@ -0,0 +1,294 @@
|
||||
<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
|
||||
<class>createIndexForm</class>
|
||||
<widget class="QDialog">
|
||||
<property name="name">
|
||||
<cstring>createIndexForm</cstring>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>300</width>
|
||||
<height>258</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="caption">
|
||||
<string>Create Index</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<pixmap>icone16.png</pixmap>
|
||||
</property>
|
||||
<vbox>
|
||||
<property name="name">
|
||||
<cstring>unnamed</cstring>
|
||||
</property>
|
||||
<widget class="QLayoutWidget">
|
||||
<property name="name">
|
||||
<cstring>layout15</cstring>
|
||||
</property>
|
||||
<hbox>
|
||||
<property name="name">
|
||||
<cstring>unnamed</cstring>
|
||||
</property>
|
||||
<widget class="QLabel">
|
||||
<property name="name">
|
||||
<cstring>textLabel2</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Index name:</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit">
|
||||
<property name="name">
|
||||
<cstring>indexLineEdit</cstring>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>LineEditPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>Sunken</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string></string>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Enter the name for the new index</string>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>This area contains the name of the index to be created</string>
|
||||
</property>
|
||||
</widget>
|
||||
</hbox>
|
||||
</widget>
|
||||
<widget class="QGroupBox">
|
||||
<property name="name">
|
||||
<cstring>groupBox2</cstring>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Define properties:</string>
|
||||
</property>
|
||||
<grid>
|
||||
<property name="name">
|
||||
<cstring>unnamed</cstring>
|
||||
</property>
|
||||
<widget class="QLayoutWidget" row="0" column="1">
|
||||
<property name="name">
|
||||
<cstring>layout12</cstring>
|
||||
</property>
|
||||
<vbox>
|
||||
<property name="name">
|
||||
<cstring>unnamed</cstring>
|
||||
</property>
|
||||
<widget class="QComboBox">
|
||||
<property name="name">
|
||||
<cstring>comboTables</cstring>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Choose the table to index</string>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>This control is used to select the table to be indexed. Changing the selected table will automatically update the fields available in the control below</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QComboBox">
|
||||
<property name="name">
|
||||
<cstring>comboFields</cstring>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Choose the field to be indexed</string>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>This control specifies the field to be used as an index</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QComboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Ascending</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Descending</string>
|
||||
</property>
|
||||
</item>
|
||||
<property name="name">
|
||||
<cstring>comboOrder</cstring>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Choose the index order</string>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>This option controls the ordering of the index. Ascending is the recommended ordering</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QComboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Allowed</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Not allowed</string>
|
||||
</property>
|
||||
</item>
|
||||
<property name="name">
|
||||
<cstring>comboUnique</cstring>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Allow duplicate values in the index field</string>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>This control determines if the indexed field allows duplicate values to be inserted into the database. Attempting to insert a duplicate value in an indexed fiield that does not allow this option will generate an error</string>
|
||||
</property>
|
||||
</widget>
|
||||
</vbox>
|
||||
</widget>
|
||||
<widget class="QLayoutWidget" row="0" column="0">
|
||||
<property name="name">
|
||||
<cstring>layout13</cstring>
|
||||
</property>
|
||||
<vbox>
|
||||
<property name="name">
|
||||
<cstring>unnamed</cstring>
|
||||
</property>
|
||||
<widget class="QLabel">
|
||||
<property name="name">
|
||||
<cstring>textLabel3</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Table to index:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>AlignVCenter|AlignRight</set>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel">
|
||||
<property name="name">
|
||||
<cstring>textLabel4</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Field to index:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>AlignVCenter|AlignRight</set>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel">
|
||||
<property name="name">
|
||||
<cstring>textLabel5</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Indexing order:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>AlignVCenter|AlignRight</set>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel">
|
||||
<property name="name">
|
||||
<cstring>textLabel6</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Duplicate values:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>AlignVCenter|AlignRight</set>
|
||||
</property>
|
||||
</widget>
|
||||
</vbox>
|
||||
</widget>
|
||||
</grid>
|
||||
</widget>
|
||||
<widget class="QLayoutWidget">
|
||||
<property name="name">
|
||||
<cstring>layout16</cstring>
|
||||
</property>
|
||||
<hbox>
|
||||
<property name="name">
|
||||
<cstring>unnamed</cstring>
|
||||
</property>
|
||||
<spacer>
|
||||
<property name="name">
|
||||
<cstring>spacer8</cstring>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint">
|
||||
<size>
|
||||
<width>51</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
<widget class="QPushButton">
|
||||
<property name="name">
|
||||
<cstring>buttonCreate</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Create</string>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Create Index</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton">
|
||||
<property name="name">
|
||||
<cstring>buttonCancel</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Cancel</string>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Cancel and close dialog box</string>
|
||||
</property>
|
||||
</widget>
|
||||
</hbox>
|
||||
</widget>
|
||||
</vbox>
|
||||
</widget>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonCreate</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>createIndexForm</receiver>
|
||||
<slot>confirmCreate()</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonCancel</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>createIndexForm</receiver>
|
||||
<slot>reject()</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>comboTables</sender>
|
||||
<signal>activated(const QString&)</signal>
|
||||
<receiver>createIndexForm</receiver>
|
||||
<slot>tableSelected(const QString&)</slot>
|
||||
</connection>
|
||||
</connections>
|
||||
<includes>
|
||||
<include location="local" impldecl="in implementation">qmessagebox.h</include>
|
||||
<include location="local" impldecl="in declaration">sqlitedb.h</include>
|
||||
<include location="local" impldecl="in implementation">createindexform.ui.h</include>
|
||||
</includes>
|
||||
<variables>
|
||||
<variable access="public">tableMap mtablemap;</variable>
|
||||
<variable access="public">QString createStatement;</variable>
|
||||
</variables>
|
||||
<slots>
|
||||
<slot>tableSelected( const QString & entry )</slot>
|
||||
<slot>confirmCreate()</slot>
|
||||
<slot>populateTable( tableMap rmap )</slot>
|
||||
</slots>
|
||||
<pixmapinproject/>
|
||||
<layoutdefaults spacing="6" margin="11"/>
|
||||
</UI>
|
||||
81
sqlitebrowser/sqlitebrowser/createindexform.ui.h
Executable file
@@ -0,0 +1,81 @@
|
||||
/****************************************************************************
|
||||
** ui.h extension file, included from the uic-generated form implementation.
|
||||
**
|
||||
** If you wish to add, delete or rename functions or slots use
|
||||
** Qt Designer which will update this file, preserving your code. Create an
|
||||
** init() function in place of a constructor, and a destroy() function in
|
||||
** place of a destructor.
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
void createIndexForm::tableSelected( const QString & entry )
|
||||
{
|
||||
tableMap::Iterator it;
|
||||
for ( it = mtablemap.begin(); it != mtablemap.end(); ++it ) {
|
||||
QString tname = it.data().getname() ;
|
||||
|
||||
//populate the fields with first table name
|
||||
if ((tname.compare(entry)==0)){
|
||||
comboFields->clear();
|
||||
fieldMap::Iterator fit;
|
||||
fieldMap fmap = it.data().fldmap;
|
||||
for ( fit = fmap.begin(); fit != fmap.end(); ++fit ) {
|
||||
comboFields->insertItem( fit.data().getname(), -1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void createIndexForm::confirmCreate()
|
||||
{
|
||||
bool ok = true;
|
||||
QString idxname = indexLineEdit->text();
|
||||
if (idxname.isEmpty()) {
|
||||
ok = false;
|
||||
QMessageBox::information( this, applicationName, "Please select a name for the index" );
|
||||
return;
|
||||
}
|
||||
if (idxname.contains(" ")>0) {
|
||||
ok = false;
|
||||
QMessageBox::warning( this, applicationName, "Spaces are not allowed in the index name" );
|
||||
return;
|
||||
}
|
||||
if (ok){
|
||||
createStatement = "CREATE ";
|
||||
if (comboUnique->currentItem()==1){
|
||||
createStatement.append("UNIQUE ");
|
||||
}
|
||||
createStatement.append("INDEX ");
|
||||
createStatement.append(indexLineEdit->text());
|
||||
createStatement.append(" ON ");
|
||||
createStatement.append(comboTables->currentText());
|
||||
createStatement.append("(");
|
||||
createStatement.append(comboFields->currentText());
|
||||
createStatement.append(" ");
|
||||
if (comboOrder->currentItem()==0){
|
||||
createStatement.append("ASC");
|
||||
} else {
|
||||
createStatement.append("DESC");
|
||||
}
|
||||
createStatement.append(");");
|
||||
accept();
|
||||
}
|
||||
}
|
||||
|
||||
void createIndexForm::populateTable(tableMap rmap)
|
||||
{
|
||||
mtablemap = rmap;
|
||||
tableMap::Iterator it;
|
||||
for ( it = mtablemap.begin(); it != mtablemap.end(); ++it ) {
|
||||
comboTables->insertItem( it.data().getname() , -1);
|
||||
|
||||
//populate the fields with first table name
|
||||
if (it==mtablemap.begin()){
|
||||
fieldMap::Iterator fit;
|
||||
fieldMap fmap = it.data().fldmap;
|
||||
for ( fit = fmap.begin(); fit != fmap.end(); ++fit ) {
|
||||
comboFields->insertItem( fit.data().getname(), -1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
268
sqlitebrowser/sqlitebrowser/createtableform.ui
Executable file
@@ -0,0 +1,268 @@
|
||||
<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
|
||||
<class>createTableForm</class>
|
||||
<widget class="QDialog">
|
||||
<property name="name">
|
||||
<cstring>createTableForm</cstring>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>309</width>
|
||||
<height>312</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="caption">
|
||||
<string>Create Table</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<pixmap>icone16.png</pixmap>
|
||||
</property>
|
||||
<vbox>
|
||||
<property name="name">
|
||||
<cstring>unnamed</cstring>
|
||||
</property>
|
||||
<widget class="QLayoutWidget">
|
||||
<property name="name">
|
||||
<cstring>layout7</cstring>
|
||||
</property>
|
||||
<hbox>
|
||||
<property name="name">
|
||||
<cstring>unnamed</cstring>
|
||||
</property>
|
||||
<widget class="QLabel">
|
||||
<property name="name">
|
||||
<cstring>textLabel1</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Table name:</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit">
|
||||
<property name="name">
|
||||
<cstring>tablenameLineEdit</cstring>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>LineEditPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>Sunken</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string></string>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Enter the name for the new table</string>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>Use this control to enter the name of the table to be created.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</hbox>
|
||||
</widget>
|
||||
<widget class="QGroupBox">
|
||||
<property name="name">
|
||||
<cstring>groupBox1</cstring>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Define fields:</string>
|
||||
</property>
|
||||
<vbox>
|
||||
<property name="name">
|
||||
<cstring>unnamed</cstring>
|
||||
</property>
|
||||
<widget class="QTable">
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Field Name</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Field Type</string>
|
||||
</property>
|
||||
</column>
|
||||
<property name="name">
|
||||
<cstring>fieldsTable</cstring>
|
||||
</property>
|
||||
<property name="acceptDrops">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="resizePolicy">
|
||||
<enum>Default</enum>
|
||||
</property>
|
||||
<property name="numRows">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="numCols">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="rowMovingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>SingleRow</enum>
|
||||
</property>
|
||||
<property name="focusStyle">
|
||||
<enum>FollowStyle</enum>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>This area contains the definitions for the fields in your new table. Add a new field using the ADD button. You can type the name of the field and the field type directly. Suggested field types are text, numeric or blob (for images and binary data)</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLayoutWidget">
|
||||
<property name="name">
|
||||
<cstring>layout6</cstring>
|
||||
</property>
|
||||
<hbox>
|
||||
<property name="name">
|
||||
<cstring>unnamed</cstring>
|
||||
</property>
|
||||
<spacer>
|
||||
<property name="name">
|
||||
<cstring>spacer4</cstring>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint">
|
||||
<size>
|
||||
<width>111</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
<widget class="QPushButton">
|
||||
<property name="name">
|
||||
<cstring>buttonAddField</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add</string>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Add a new field definition</string>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>This button is used to add a new field definition to your table</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton">
|
||||
<property name="name">
|
||||
<cstring>buttonDeleteField</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Delete current field definition</string>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>This button is used to delete the currently selected field definition from your table</string>
|
||||
</property>
|
||||
</widget>
|
||||
</hbox>
|
||||
</widget>
|
||||
</vbox>
|
||||
</widget>
|
||||
<widget class="QLayoutWidget">
|
||||
<property name="name">
|
||||
<cstring>layout8</cstring>
|
||||
</property>
|
||||
<hbox>
|
||||
<property name="name">
|
||||
<cstring>unnamed</cstring>
|
||||
</property>
|
||||
<spacer>
|
||||
<property name="name">
|
||||
<cstring>spacer6</cstring>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint">
|
||||
<size>
|
||||
<width>91</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
<widget class="QPushButton">
|
||||
<property name="name">
|
||||
<cstring>buttonCreate</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Create</string>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Create the table</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton">
|
||||
<property name="name">
|
||||
<cstring>buttonCancel</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Cancel</string>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Cancel and close dialog box</string>
|
||||
</property>
|
||||
</widget>
|
||||
</hbox>
|
||||
</widget>
|
||||
</vbox>
|
||||
</widget>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonCancel</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>createTableForm</receiver>
|
||||
<slot>reject()</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonCreate</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>createTableForm</receiver>
|
||||
<slot>confirmCreate()</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonAddField</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>createTableForm</receiver>
|
||||
<slot>addField()</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonDeleteField</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>createTableForm</receiver>
|
||||
<slot>deleteField()</slot>
|
||||
</connection>
|
||||
</connections>
|
||||
<includes>
|
||||
<include location="local" impldecl="in implementation">qmessagebox.h</include>
|
||||
<include location="local" impldecl="in declaration">sqlitedb.h</include>
|
||||
<include location="local" impldecl="in implementation">createtableform.ui.h</include>
|
||||
</includes>
|
||||
<variables>
|
||||
<variable access="public">QString createStatement;</variable>
|
||||
</variables>
|
||||
<slots>
|
||||
<slot>confirmCreate()</slot>
|
||||
<slot>addField()</slot>
|
||||
<slot>deleteField()</slot>
|
||||
</slots>
|
||||
<functions>
|
||||
<function access="private" specifier="non virtual">init()</function>
|
||||
</functions>
|
||||
<pixmapinproject/>
|
||||
<layoutdefaults spacing="6" margin="11"/>
|
||||
</UI>
|
||||
84
sqlitebrowser/sqlitebrowser/createtableform.ui.h
Executable file
@@ -0,0 +1,84 @@
|
||||
/****************************************************************************
|
||||
** ui.h extension file, included from the uic-generated form implementation.
|
||||
**
|
||||
** If you wish to add, delete or rename functions or slots use
|
||||
** Qt Designer which will update this file, preserving your code. Create an
|
||||
** init() function in place of a constructor, and a destroy() function in
|
||||
** place of a destructor.
|
||||
*****************************************************************************/
|
||||
void createTableForm::init()
|
||||
{
|
||||
fieldsTable->setNumRows(0);
|
||||
// fieldsTable->setNumCols(0);
|
||||
}
|
||||
|
||||
void createTableForm::confirmCreate()
|
||||
{
|
||||
bool ok = true;
|
||||
QString tabname = tablenameLineEdit->text();
|
||||
if (tabname.isEmpty()) {
|
||||
ok = false;
|
||||
QMessageBox::information( this, applicationName, "Please select a name for the table" );
|
||||
return;
|
||||
}
|
||||
if (tabname.contains(" ")>0) {
|
||||
ok = false;
|
||||
QMessageBox::warning( this, applicationName, "Spaces are not allowed in the table name" );
|
||||
return;
|
||||
}
|
||||
|
||||
if (fieldsTable->numRows()==0) {
|
||||
ok = false;
|
||||
QMessageBox::information( this, applicationName, "No fields defined" );
|
||||
return;
|
||||
}
|
||||
|
||||
/*check field names for empty or illegal names*/
|
||||
|
||||
for (int r=0; r<fieldsTable->numRows();r++){
|
||||
QString rowname = fieldsTable->text(r, 0);
|
||||
if (rowname.isEmpty()) {
|
||||
ok = false;
|
||||
QMessageBox::warning( this, applicationName, "Empty field names are not allowed" );
|
||||
break;
|
||||
}
|
||||
if (rowname.contains(" ")>0) {
|
||||
ok = false;
|
||||
QMessageBox::warning( this, applicationName, "Spaces are not allowed in the field names" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ok){
|
||||
return;
|
||||
}
|
||||
|
||||
if (ok){
|
||||
createStatement = "CREATE TABLE ";
|
||||
createStatement.append(tabname);
|
||||
createStatement.append(" (");
|
||||
for (int r=0; r<fieldsTable->numRows();r++){
|
||||
createStatement.append(fieldsTable->text(r, 0));
|
||||
createStatement.append(" ");
|
||||
createStatement.append(fieldsTable->text(r, 1));
|
||||
if (r<(fieldsTable->numRows() - 1))
|
||||
createStatement.append(", ");
|
||||
}
|
||||
createStatement.append(");");
|
||||
accept();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void createTableForm::addField()
|
||||
{
|
||||
fieldsTable->insertRows(fieldsTable->numRows());
|
||||
}
|
||||
|
||||
|
||||
void createTableForm::deleteField()
|
||||
{
|
||||
if (fieldsTable->currentRow()!=-1){
|
||||
fieldsTable->removeRow(fieldsTable->currentRow());
|
||||
}
|
||||
}
|
||||
155
sqlitebrowser/sqlitebrowser/deleteindexform.ui
Executable file
@@ -0,0 +1,155 @@
|
||||
<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
|
||||
<class>deleteIndexForm</class>
|
||||
<widget class="QDialog">
|
||||
<property name="name">
|
||||
<cstring>deleteIndexForm</cstring>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>236</width>
|
||||
<height>137</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="caption">
|
||||
<string>Delete Index</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<pixmap>icone16.png</pixmap>
|
||||
</property>
|
||||
<vbox>
|
||||
<property name="name">
|
||||
<cstring>unnamed</cstring>
|
||||
</property>
|
||||
<widget class="QLayoutWidget">
|
||||
<property name="name">
|
||||
<cstring>layout21</cstring>
|
||||
</property>
|
||||
<hbox>
|
||||
<property name="name">
|
||||
<cstring>unnamed</cstring>
|
||||
</property>
|
||||
<widget class="QLabel">
|
||||
<property name="name">
|
||||
<cstring>textLabel2</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Index name:</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QComboBox">
|
||||
<property name="name">
|
||||
<cstring>comboOptions</cstring>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Choose the index to delete</string>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>Use this control to select the name of the index to be deleted</string>
|
||||
</property>
|
||||
</widget>
|
||||
</hbox>
|
||||
</widget>
|
||||
<spacer>
|
||||
<property name="name">
|
||||
<cstring>spacer13</cstring>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>41</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
<widget class="QLayoutWidget">
|
||||
<property name="name">
|
||||
<cstring>layout19</cstring>
|
||||
</property>
|
||||
<hbox>
|
||||
<property name="name">
|
||||
<cstring>unnamed</cstring>
|
||||
</property>
|
||||
<spacer>
|
||||
<property name="name">
|
||||
<cstring>spacer11</cstring>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint">
|
||||
<size>
|
||||
<width>31</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
<widget class="QPushButton">
|
||||
<property name="name">
|
||||
<cstring>buttonDelete</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Delete the selected index</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton">
|
||||
<property name="name">
|
||||
<cstring>buttonCancel</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Cancel</string>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Cancel and close dialog box</string>
|
||||
</property>
|
||||
</widget>
|
||||
</hbox>
|
||||
</widget>
|
||||
</vbox>
|
||||
</widget>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonDelete</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>deleteIndexForm</receiver>
|
||||
<slot>confirmDelete()</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonCancel</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>deleteIndexForm</receiver>
|
||||
<slot>reject()</slot>
|
||||
</connection>
|
||||
</connections>
|
||||
<includes>
|
||||
<include location="local" impldecl="in implementation">qmessagebox.h</include>
|
||||
<include location="local" impldecl="in declaration">qstring.h</include>
|
||||
<include location="local" impldecl="in declaration">qstringlist.h</include>
|
||||
<include location="local" impldecl="in declaration">sqlitedb.h</include>
|
||||
<include location="local" impldecl="in implementation">deleteindexform.ui.h</include>
|
||||
</includes>
|
||||
<variables>
|
||||
<variable access="public">QString option;</variable>
|
||||
</variables>
|
||||
<slots>
|
||||
<slot>confirmDelete()</slot>
|
||||
<slot>populateOptions( QStringList entries )</slot>
|
||||
</slots>
|
||||
<pixmapinproject/>
|
||||
<layoutdefaults spacing="6" margin="11"/>
|
||||
</UI>
|
||||
32
sqlitebrowser/sqlitebrowser/deleteindexform.ui.h
Executable file
@@ -0,0 +1,32 @@
|
||||
/****************************************************************************
|
||||
** ui.h extension file, included from the uic-generated form implementation.
|
||||
**
|
||||
** If you wish to add, delete or rename functions or slots use
|
||||
** Qt Designer which will update this file, preserving your code. Create an
|
||||
** init() function in place of a constructor, and a destroy() function in
|
||||
** place of a destructor.
|
||||
*****************************************************************************/
|
||||
|
||||
void deleteIndexForm::confirmDelete()
|
||||
{
|
||||
QString msg = "Are you sure you want to delete index ";
|
||||
msg.append(comboOptions->currentText());
|
||||
msg.append("?");
|
||||
|
||||
if (QMessageBox::warning( this, applicationName,
|
||||
msg,
|
||||
QMessageBox::Yes | QMessageBox::Default,
|
||||
QMessageBox::No | QMessageBox::Escape )
|
||||
== QMessageBox::Yes ){
|
||||
option = comboOptions->currentText();
|
||||
accept();
|
||||
}
|
||||
}
|
||||
|
||||
void deleteIndexForm::populateOptions(QStringList entries)
|
||||
{
|
||||
comboOptions->clear();
|
||||
for ( QStringList::Iterator ct = entries.begin(); ct != entries.end(); ++ct ) {
|
||||
comboOptions->insertItem(*ct,-1);
|
||||
}
|
||||
}
|
||||
155
sqlitebrowser/sqlitebrowser/deletetableform.ui
Executable file
@@ -0,0 +1,155 @@
|
||||
<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
|
||||
<class>deleteTableForm</class>
|
||||
<widget class="QDialog">
|
||||
<property name="name">
|
||||
<cstring>deleteTableForm</cstring>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>236</width>
|
||||
<height>137</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="caption">
|
||||
<string>Delete Table</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<pixmap>icone16.png</pixmap>
|
||||
</property>
|
||||
<vbox>
|
||||
<property name="name">
|
||||
<cstring>unnamed</cstring>
|
||||
</property>
|
||||
<widget class="QLayoutWidget">
|
||||
<property name="name">
|
||||
<cstring>layout21</cstring>
|
||||
</property>
|
||||
<hbox>
|
||||
<property name="name">
|
||||
<cstring>unnamed</cstring>
|
||||
</property>
|
||||
<widget class="QLabel">
|
||||
<property name="name">
|
||||
<cstring>textLabel2</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Table name:</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QComboBox">
|
||||
<property name="name">
|
||||
<cstring>comboOptions</cstring>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Choose the table to delete</string>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>Use this control to select the name of the table to be deleted</string>
|
||||
</property>
|
||||
</widget>
|
||||
</hbox>
|
||||
</widget>
|
||||
<spacer>
|
||||
<property name="name">
|
||||
<cstring>spacer13</cstring>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>41</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
<widget class="QLayoutWidget">
|
||||
<property name="name">
|
||||
<cstring>layout19</cstring>
|
||||
</property>
|
||||
<hbox>
|
||||
<property name="name">
|
||||
<cstring>unnamed</cstring>
|
||||
</property>
|
||||
<spacer>
|
||||
<property name="name">
|
||||
<cstring>spacer11</cstring>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint">
|
||||
<size>
|
||||
<width>31</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
<widget class="QPushButton">
|
||||
<property name="name">
|
||||
<cstring>buttonDelete</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Delete the selected table</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton">
|
||||
<property name="name">
|
||||
<cstring>buttonCancel</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Cancel</string>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Cancel and close dialog box</string>
|
||||
</property>
|
||||
</widget>
|
||||
</hbox>
|
||||
</widget>
|
||||
</vbox>
|
||||
</widget>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonCancel</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>deleteTableForm</receiver>
|
||||
<slot>reject()</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonDelete</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>deleteTableForm</receiver>
|
||||
<slot>confirmDelete()</slot>
|
||||
</connection>
|
||||
</connections>
|
||||
<includes>
|
||||
<include location="local" impldecl="in implementation">qmessagebox.h</include>
|
||||
<include location="local" impldecl="in declaration">qstringlist.h</include>
|
||||
<include location="local" impldecl="in declaration">qstring.h</include>
|
||||
<include location="local" impldecl="in declaration">sqlitedb.h</include>
|
||||
<include location="local" impldecl="in implementation">deletetableform.ui.h</include>
|
||||
</includes>
|
||||
<variables>
|
||||
<variable access="public">QString option;</variable>
|
||||
</variables>
|
||||
<slots>
|
||||
<slot>confirmDelete()</slot>
|
||||
<slot>populateOptions( QStringList entries )</slot>
|
||||
</slots>
|
||||
<pixmapinproject/>
|
||||
<layoutdefaults spacing="6" margin="11"/>
|
||||
</UI>
|
||||
33
sqlitebrowser/sqlitebrowser/deletetableform.ui.h
Executable file
@@ -0,0 +1,33 @@
|
||||
/****************************************************************************
|
||||
** ui.h extension file, included from the uic-generated form implementation.
|
||||
**
|
||||
** If you wish to add, delete or rename functions or slots use
|
||||
** Qt Designer which will update this file, preserving your code. Create an
|
||||
** init() function in place of a constructor, and a destroy() function in
|
||||
** place of a destructor.
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
void deleteTableForm::confirmDelete()
|
||||
{
|
||||
QString msg = "Are you sure you want to delete table ";
|
||||
msg.append(comboOptions->currentText());
|
||||
msg.append("? \n All data in the table will be lost");
|
||||
|
||||
if (QMessageBox::warning( this, applicationName,
|
||||
msg,
|
||||
QMessageBox::Yes | QMessageBox::Default,
|
||||
QMessageBox::No | QMessageBox::Escape )
|
||||
== QMessageBox::Yes ){
|
||||
option = comboOptions->currentText();
|
||||
accept();
|
||||
}
|
||||
}
|
||||
|
||||
void deleteTableForm::populateOptions(QStringList entries)
|
||||
{
|
||||
comboOptions->clear();
|
||||
for ( QStringList::Iterator ct = entries.begin(); ct != entries.end(); ++ct ) {
|
||||
comboOptions->insertItem(*ct,-1);
|
||||
}
|
||||
}
|
||||
240
sqlitebrowser/sqlitebrowser/findform.ui
Executable file
@@ -0,0 +1,240 @@
|
||||
<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
|
||||
<class>findForm</class>
|
||||
<widget class="QDialog">
|
||||
<property name="name">
|
||||
<cstring>findForm</cstring>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>239</width>
|
||||
<height>319</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="caption">
|
||||
<string>Find</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<pixmap>icone16.png</pixmap>
|
||||
</property>
|
||||
<vbox>
|
||||
<property name="name">
|
||||
<cstring>unnamed</cstring>
|
||||
</property>
|
||||
<widget class="QLayoutWidget">
|
||||
<property name="name">
|
||||
<cstring>layout5</cstring>
|
||||
</property>
|
||||
<grid>
|
||||
<property name="name">
|
||||
<cstring>unnamed</cstring>
|
||||
</property>
|
||||
<widget class="QPushButton" row="1" column="2">
|
||||
<property name="name">
|
||||
<cstring>searchButton</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Search</string>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Perform the search</string>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>This button starts the search process</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QComboBox" row="0" column="0">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>user</string>
|
||||
</property>
|
||||
</item>
|
||||
<property name="name">
|
||||
<cstring>findFieldCombobox</cstring>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Field to be searched</string>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>Use this control to select the field to be searched in the current table</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" row="1" column="0" rowspan="1" colspan="2">
|
||||
<property name="name">
|
||||
<cstring>searchLine</cstring>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>LineEditPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>Sunken</enum>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Enter values or words to search</string>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>This is a place to enter the word or number to be searched in the database</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QComboBox" row="0" column="1" rowspan="1" colspan="2">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>=</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>contains</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>></string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>>=</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string><=</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string><</string>
|
||||
</property>
|
||||
</item>
|
||||
<property name="name">
|
||||
<cstring>findOperatorComboBox</cstring>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Search criteria: use 'contains' for partial matches</string>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>This control is used to select the search criteria used to look for the search term in the database. Use '=' or 'contains' to find words, and the comparison symbols to filter numeric data.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</grid>
|
||||
</widget>
|
||||
<widget class="QListView">
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Record</string>
|
||||
</property>
|
||||
<property name="clickable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="resizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Data</string>
|
||||
</property>
|
||||
<property name="clickable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="resizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</column>
|
||||
<property name="name">
|
||||
<cstring>findListView</cstring>
|
||||
</property>
|
||||
<property name="midLineWidth">
|
||||
<number>30</number>
|
||||
</property>
|
||||
<property name="resizePolicy">
|
||||
<enum>Manual</enum>
|
||||
</property>
|
||||
<property name="resizeMode">
|
||||
<enum>LastColumn</enum>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>Results of the search will appear in this area. Click on a result to select the corresponding record in the database</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLayoutWidget">
|
||||
<property name="name">
|
||||
<cstring>layout18</cstring>
|
||||
</property>
|
||||
<hbox>
|
||||
<property name="name">
|
||||
<cstring>unnamed</cstring>
|
||||
</property>
|
||||
<widget class="QLabel">
|
||||
<property name="name">
|
||||
<cstring>resultsLabel</cstring>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy>
|
||||
<hsizetype>7</hsizetype>
|
||||
<vsizetype>5</vsizetype>
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Found:</string>
|
||||
</property>
|
||||
</widget>
|
||||
<spacer>
|
||||
<property name="name">
|
||||
<cstring>spacer10</cstring>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</hbox>
|
||||
</widget>
|
||||
</vbox>
|
||||
</widget>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>searchButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>findForm</receiver>
|
||||
<slot>find()</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>findListView</sender>
|
||||
<signal>clicked(QListViewItem*)</signal>
|
||||
<receiver>findForm</receiver>
|
||||
<slot>recordSelected(QListViewItem*)</slot>
|
||||
</connection>
|
||||
</connections>
|
||||
<includes>
|
||||
<include location="local" impldecl="in declaration">sqlitedb.h</include>
|
||||
<include location="local" impldecl="in implementation">findform.ui.h</include>
|
||||
</includes>
|
||||
<signals>
|
||||
<signal>lookfor(const QString&, const QString&, const QString&);</signal>
|
||||
<signal>showrecord(int);</signal>
|
||||
<signal>goingAway();</signal>
|
||||
</signals>
|
||||
<slots>
|
||||
<slot>showResults( resultMap rmap )</slot>
|
||||
<slot>find()</slot>
|
||||
<slot>resetFields( QStringList fieldlist )</slot>
|
||||
<slot>resetResults()</slot>
|
||||
<slot>recordSelected( QListViewItem * witem )</slot>
|
||||
<slot>closeEvent( QCloseEvent * )</slot>
|
||||
</slots>
|
||||
<pixmapinproject/>
|
||||
<layoutdefaults spacing="6" margin="11"/>
|
||||
</UI>
|
||||
60
sqlitebrowser/sqlitebrowser/findform.ui.h
Executable file
@@ -0,0 +1,60 @@
|
||||
/****************************************************************************
|
||||
** ui.h extension file, included from the uic-generated form implementation.
|
||||
**
|
||||
** If you wish to add, delete or rename functions or slots use
|
||||
** Qt Designer which will update this file, preserving your code. Create an
|
||||
** init() function in place of a constructor, and a destroy() function in
|
||||
** place of a destructor.
|
||||
*****************************************************************************/
|
||||
|
||||
void findForm::showResults(resultMap rmap)
|
||||
{
|
||||
findListView->clear();
|
||||
findListView->setSorting(-1);
|
||||
resultMap::Iterator it;
|
||||
QListViewItem * lasttbitem = 0;
|
||||
for ( it = rmap.begin(); it != rmap.end(); ++it ) {
|
||||
QListViewItem * tbitem = new QListViewItem( findListView, lasttbitem );
|
||||
//tbitem->setOpen( TRUE );
|
||||
tbitem->setText( 0, QString::number(it.key() + 1,10) ); //increase from index 0
|
||||
tbitem->setText( 1, it.data() );
|
||||
lasttbitem = tbitem;
|
||||
}
|
||||
QString results = "Found: ";
|
||||
results.append(QString::number(findListView->childCount()));
|
||||
resultsLabel->setText(results);
|
||||
}
|
||||
|
||||
|
||||
void findForm::find()
|
||||
{
|
||||
emit lookfor( findFieldCombobox->currentText(), findOperatorComboBox->currentText(),searchLine->text() );
|
||||
}
|
||||
|
||||
void findForm::resetFields(QStringList fieldlist)
|
||||
{
|
||||
findFieldCombobox->clear();
|
||||
for ( QStringList::Iterator ct = fieldlist.begin(); ct != fieldlist.end(); ++ct ) {
|
||||
findFieldCombobox->insertItem(*ct,-1);
|
||||
}
|
||||
}
|
||||
|
||||
void findForm::resetResults()
|
||||
{
|
||||
findListView->clear();
|
||||
resultsLabel->setText("Found: 0");
|
||||
}
|
||||
|
||||
|
||||
void findForm::recordSelected( QListViewItem * witem)
|
||||
{
|
||||
if (witem) {
|
||||
int recNum = (witem->text(0)).toInt() ;
|
||||
emit showrecord(recNum);
|
||||
}
|
||||
}
|
||||
|
||||
void findForm::closeEvent( QCloseEvent * )
|
||||
{
|
||||
emit goingAway();
|
||||
}
|
||||
913
sqlitebrowser/sqlitebrowser/form1.ui
Executable file
@@ -0,0 +1,913 @@
|
||||
<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
|
||||
<class>mainForm</class>
|
||||
<widget class="QMainWindow">
|
||||
<property name="name">
|
||||
<cstring>mainForm</cstring>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>668</width>
|
||||
<height>476</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="caption">
|
||||
<string>Browser</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<pixmap>icone16.png</pixmap>
|
||||
</property>
|
||||
<vbox>
|
||||
<property name="name">
|
||||
<cstring>unnamed</cstring>
|
||||
</property>
|
||||
<widget class="QTabWidget">
|
||||
<property name="name">
|
||||
<cstring>mainTab</cstring>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string></string>
|
||||
</property>
|
||||
<widget class="QWidget">
|
||||
<property name="name">
|
||||
<cstring>tab</cstring>
|
||||
</property>
|
||||
<attribute name="title">
|
||||
<string>Database Structure</string>
|
||||
</attribute>
|
||||
<vbox>
|
||||
<property name="name">
|
||||
<cstring>unnamed</cstring>
|
||||
</property>
|
||||
<widget class="QLayoutWidget">
|
||||
<property name="name">
|
||||
<cstring>layout14</cstring>
|
||||
</property>
|
||||
<vbox>
|
||||
<property name="name">
|
||||
<cstring>unnamed</cstring>
|
||||
</property>
|
||||
<widget class="QListView">
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
<property name="clickable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="resizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Object</string>
|
||||
</property>
|
||||
<property name="clickable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="resizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Type</string>
|
||||
</property>
|
||||
<property name="clickable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="resizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Schema</string>
|
||||
</property>
|
||||
<property name="clickable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="resizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</column>
|
||||
<property name="name">
|
||||
<cstring>dblistView</cstring>
|
||||
</property>
|
||||
<property name="resizePolicy">
|
||||
<enum>Manual</enum>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>NoSelection</enum>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>This area shows the structure of your database, including all tables and indexes. It is not possible to modify an existing table or index directly.</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLayoutWidget">
|
||||
<property name="name">
|
||||
<cstring>layout13</cstring>
|
||||
</property>
|
||||
<hbox>
|
||||
<property name="name">
|
||||
<cstring>unnamed</cstring>
|
||||
</property>
|
||||
<widget class="QPushButton">
|
||||
<property name="name">
|
||||
<cstring>buttonCreateTable</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Create Table</string>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Open the Create Table wizard</string>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>This button is used to open the Create Table wizard, where it is possible to define the name and fields for a new table in the database</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton">
|
||||
<property name="name">
|
||||
<cstring>buttonDeleteTable</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete Table</string>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Open the Delete Table wizard</string>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>This button opens the Delete Table wizard, where you can choose a table to be dropped from the database</string>
|
||||
</property>
|
||||
</widget>
|
||||
<spacer>
|
||||
<property name="name">
|
||||
<cstring>spacer7</cstring>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint">
|
||||
<size>
|
||||
<width>21</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
<widget class="QPushButton">
|
||||
<property name="name">
|
||||
<cstring>buttonCreateIndex</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Create Index</string>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Open the Create Index wizard</string>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>This button opens the Create Index wizard, where it is possible to define the characteristics and create a new index in the database</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton">
|
||||
<property name="name">
|
||||
<cstring>buttonDeleteIndex</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete Index</string>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Open the Delete Index wizard</string>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>This button opens the Delete Index wizard, where it is possible to select an index to be removed from the database. Removing an index does not affect the data stored in the tables.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</hbox>
|
||||
</widget>
|
||||
</vbox>
|
||||
</widget>
|
||||
</vbox>
|
||||
</widget>
|
||||
<widget class="QWidget">
|
||||
<property name="name">
|
||||
<cstring>tab</cstring>
|
||||
</property>
|
||||
<attribute name="title">
|
||||
<string>Browse Data</string>
|
||||
</attribute>
|
||||
<vbox>
|
||||
<property name="name">
|
||||
<cstring>unnamed</cstring>
|
||||
</property>
|
||||
<widget class="QLayoutWidget">
|
||||
<property name="name">
|
||||
<cstring>layout2</cstring>
|
||||
</property>
|
||||
<hbox>
|
||||
<property name="name">
|
||||
<cstring>unnamed</cstring>
|
||||
</property>
|
||||
<widget class="QLabel">
|
||||
<property name="name">
|
||||
<cstring>textLabel1</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Table:</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QComboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>select</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>users</string>
|
||||
</property>
|
||||
</item>
|
||||
<property name="name">
|
||||
<cstring>comboBrowseTable</cstring>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>115</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Select a table to browse data</string>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>Use this list to select a table to be displayed in the database view</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton">
|
||||
<property name="name">
|
||||
<cstring>buttonFind</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string></string>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap>searchfind.png</pixmap>
|
||||
</property>
|
||||
<property name="toggleButton">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Open or close the floating find window</string>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>This button toggles the appearance of the Find window, used to search records in the database view</string>
|
||||
</property>
|
||||
</widget>
|
||||
<spacer>
|
||||
<property name="name">
|
||||
<cstring>spacer1</cstring>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint">
|
||||
<size>
|
||||
<width>51</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
<widget class="QPushButton">
|
||||
<property name="name">
|
||||
<cstring>buttonNewRecord</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>New Record</string>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Insert a new record in the current table</string>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>This button creates a new, empty record in the database</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton">
|
||||
<property name="name">
|
||||
<cstring>buttonDeleteRecord</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete Record</string>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Delete the current record</string>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>This button deletes the record currently selected in the database</string>
|
||||
</property>
|
||||
</widget>
|
||||
</hbox>
|
||||
</widget>
|
||||
<widget class="QTable">
|
||||
<property name="name">
|
||||
<cstring>dataTable</cstring>
|
||||
</property>
|
||||
<property name="resizePolicy">
|
||||
<enum>AutoOneFit</enum>
|
||||
</property>
|
||||
<property name="numRows">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="numCols">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>SingleRow</enum>
|
||||
</property>
|
||||
<property name="focusStyle">
|
||||
<enum>SpreadSheet</enum>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string></string>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>This is the database view. You can double-click any record to edit its contents. All changes are commited to the database when you press ENTER or deselect the record. To undo changes press ESC while editing the record</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLayoutWidget">
|
||||
<property name="name">
|
||||
<cstring>layout9</cstring>
|
||||
</property>
|
||||
<hbox>
|
||||
<property name="name">
|
||||
<cstring>unnamed</cstring>
|
||||
</property>
|
||||
<widget class="QPushButton">
|
||||
<property name="name">
|
||||
<cstring>buttonPrevious</cstring>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy>
|
||||
<hsizetype>0</hsizetype>
|
||||
<vsizetype>0</vsizetype>
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><</string>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Go to previous record set page</string>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>This button is used to navigate to the previous set of 1000 records in the database view</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel">
|
||||
<property name="name">
|
||||
<cstring>labelRecordset</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>1000 - 2000 of 100000</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton">
|
||||
<property name="name">
|
||||
<cstring>buttonNext</cstring>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy>
|
||||
<hsizetype>0</hsizetype>
|
||||
<vsizetype>0</vsizetype>
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>></string>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Go to next record set page</string>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>This button is used to navigate to the next 1000 records set in the database view</string>
|
||||
</property>
|
||||
</widget>
|
||||
<spacer>
|
||||
<property name="name">
|
||||
<cstring>spacer4</cstring>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
<widget class="QPushButton">
|
||||
<property name="name">
|
||||
<cstring>buttonGoto</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Go to:</string>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Click here to browse the specified record </string>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>This button is used to navigate to the record number specied in the Go to: area.</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit">
|
||||
<property name="name">
|
||||
<cstring>editGoto</cstring>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy>
|
||||
<hsizetype>0</hsizetype>
|
||||
<vsizetype>0</vsizetype>
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>LineEditPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>Sunken</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
<property name="toolTip" stdset="0">
|
||||
<string>Enter record number to browse</string>
|
||||
</property>
|
||||
<property name="whatsThis" stdset="0">
|
||||
<string>Type a record number in this area and click the Go to: button to display the record in the database view</string>
|
||||
</property>
|
||||
</widget>
|
||||
</hbox>
|
||||
</widget>
|
||||
</vbox>
|
||||
</widget>
|
||||
</widget>
|
||||
</vbox>
|
||||
</widget>
|
||||
<menubar>
|
||||
<property name="name">
|
||||
<cstring>menubar</cstring>
|
||||
</property>
|
||||
<item text="&File" name="fileMenu">
|
||||
<action name="fileNewAction"/>
|
||||
<action name="fileOpenAction"/>
|
||||
<action name="compactAction"/>
|
||||
<action name="fileCloseAction"/>
|
||||
<separator/>
|
||||
<action name="fileExitAction"/>
|
||||
</item>
|
||||
<item text="&Edit" name="editMenu">
|
||||
<action name="editCopyAction"/>
|
||||
<action name="editPasteAction"/>
|
||||
</item>
|
||||
<item text="Help" name="PopupMenu">
|
||||
<action name="helpWhatsThisAction"/>
|
||||
<action name="helpAboutAction"/>
|
||||
</item>
|
||||
</menubar>
|
||||
<toolbars>
|
||||
<toolbar dock="2">
|
||||
<property name="name">
|
||||
<cstring>Toolbar</cstring>
|
||||
</property>
|
||||
<property name="label">
|
||||
<string>Toolbar</string>
|
||||
</property>
|
||||
<action name="fileNewAction"/>
|
||||
<action name="fileOpenAction"/>
|
||||
<separator/>
|
||||
<action name="editCopyAction"/>
|
||||
<action name="editPasteAction"/>
|
||||
<separator/>
|
||||
<action name="helpWhatsThisAction"/>
|
||||
</toolbar>
|
||||
</toolbars>
|
||||
<actions>
|
||||
<action>
|
||||
<property name="name">
|
||||
<cstring>fileNewAction</cstring>
|
||||
</property>
|
||||
<property name="iconSet">
|
||||
<iconset>filenew.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>New Database</string>
|
||||
</property>
|
||||
<property name="menuText">
|
||||
<string>&New Database</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Create a new database file</string>
|
||||
</property>
|
||||
<property name="accel">
|
||||
<string>Ctrl+N</string>
|
||||
</property>
|
||||
</action>
|
||||
<action>
|
||||
<property name="name">
|
||||
<cstring>fileOpenAction</cstring>
|
||||
</property>
|
||||
<property name="iconSet">
|
||||
<iconset>fileopen.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Open Database</string>
|
||||
</property>
|
||||
<property name="menuText">
|
||||
<string>&Open Database</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Open an existing database file</string>
|
||||
</property>
|
||||
<property name="accel">
|
||||
<string>Ctrl+O</string>
|
||||
</property>
|
||||
</action>
|
||||
<action>
|
||||
<property name="name">
|
||||
<cstring>fileExitAction</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Exit</string>
|
||||
</property>
|
||||
<property name="menuText">
|
||||
<string>E&xit</string>
|
||||
</property>
|
||||
<property name="accel">
|
||||
<string></string>
|
||||
</property>
|
||||
</action>
|
||||
<action>
|
||||
<property name="name">
|
||||
<cstring>editCopyAction</cstring>
|
||||
</property>
|
||||
<property name="iconSet">
|
||||
<iconset>editcopy.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Copy</string>
|
||||
</property>
|
||||
<property name="menuText">
|
||||
<string>&Copy</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>Copies the selected text to the clipboard</string>
|
||||
</property>
|
||||
<property name="accel">
|
||||
<string>Ctrl+C</string>
|
||||
</property>
|
||||
</action>
|
||||
<action>
|
||||
<property name="name">
|
||||
<cstring>editPasteAction</cstring>
|
||||
</property>
|
||||
<property name="iconSet">
|
||||
<iconset>editpaste.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Paste</string>
|
||||
</property>
|
||||
<property name="menuText">
|
||||
<string>&Paste</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>Pastes text from the clipboard into the current text insertion point</string>
|
||||
</property>
|
||||
<property name="accel">
|
||||
<string>Ctrl+V</string>
|
||||
</property>
|
||||
</action>
|
||||
<action>
|
||||
<property name="name">
|
||||
<cstring>editFindAction</cstring>
|
||||
</property>
|
||||
<property name="iconSet">
|
||||
<iconset></iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Find</string>
|
||||
</property>
|
||||
<property name="menuText">
|
||||
<string>&Find...</string>
|
||||
</property>
|
||||
<property name="accel">
|
||||
<string>F</string>
|
||||
</property>
|
||||
</action>
|
||||
<action>
|
||||
<property name="name">
|
||||
<cstring>helpContentsAction</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Contents</string>
|
||||
</property>
|
||||
<property name="menuText">
|
||||
<string>&Contents...</string>
|
||||
</property>
|
||||
<property name="accel">
|
||||
<string></string>
|
||||
</property>
|
||||
</action>
|
||||
<action>
|
||||
<property name="name">
|
||||
<cstring>helpIndexAction</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Index</string>
|
||||
</property>
|
||||
<property name="menuText">
|
||||
<string>&Index...</string>
|
||||
</property>
|
||||
<property name="accel">
|
||||
<string></string>
|
||||
</property>
|
||||
</action>
|
||||
<action>
|
||||
<property name="name">
|
||||
<cstring>helpAboutAction</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>About</string>
|
||||
</property>
|
||||
<property name="menuText">
|
||||
<string>&About</string>
|
||||
</property>
|
||||
<property name="accel">
|
||||
<string></string>
|
||||
</property>
|
||||
</action>
|
||||
<action>
|
||||
<property name="name">
|
||||
<cstring>fileCloseAction</cstring>
|
||||
</property>
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Close Database</string>
|
||||
</property>
|
||||
</action>
|
||||
<action>
|
||||
<property name="name">
|
||||
<cstring>newRecordAction</cstring>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>New Record</string>
|
||||
</property>
|
||||
</action>
|
||||
<action>
|
||||
<property name="name">
|
||||
<cstring>compactAction</cstring>
|
||||
</property>
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Compact Database</string>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Compact the database file, removing space wasted by deleted records.</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>Compact the database file, removing space wasted by deleted records.</string>
|
||||
</property>
|
||||
</action>
|
||||
<action>
|
||||
<property name="name">
|
||||
<cstring>helpWhatsThisAction</cstring>
|
||||
</property>
|
||||
<property name="iconSet">
|
||||
<iconset>whatsthis.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>What's This?</string>
|
||||
</property>
|
||||
<property name="accel">
|
||||
<string>Shift+F1</string>
|
||||
</property>
|
||||
</action>
|
||||
</actions>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>fileExitAction</sender>
|
||||
<signal>activated()</signal>
|
||||
<receiver>mainForm</receiver>
|
||||
<slot>fileExit()</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>fileOpenAction</sender>
|
||||
<signal>activated()</signal>
|
||||
<receiver>mainForm</receiver>
|
||||
<slot>fileOpen()</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>fileNewAction</sender>
|
||||
<signal>activated()</signal>
|
||||
<receiver>mainForm</receiver>
|
||||
<slot>fileNew()</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>fileCloseAction</sender>
|
||||
<signal>activated()</signal>
|
||||
<receiver>mainForm</receiver>
|
||||
<slot>fileClose()</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>comboBrowseTable</sender>
|
||||
<signal>activated(const QString&)</signal>
|
||||
<receiver>mainForm</receiver>
|
||||
<slot>populateTable(const QString&)</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonNewRecord</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>mainForm</receiver>
|
||||
<slot>addRecord()</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonDeleteRecord</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>mainForm</receiver>
|
||||
<slot>deleteRecord()</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonPrevious</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>mainForm</receiver>
|
||||
<slot>navigatePrevious()</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonNext</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>mainForm</receiver>
|
||||
<slot>navigateNext()</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>editGoto</sender>
|
||||
<signal>returnPressed()</signal>
|
||||
<receiver>mainForm</receiver>
|
||||
<slot>navigateGoto()</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonGoto</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>mainForm</receiver>
|
||||
<slot>navigateGoto()</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonFind</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>mainForm</receiver>
|
||||
<slot>browseFind(bool)</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonCreateTable</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>mainForm</receiver>
|
||||
<slot>createTable()</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonCreateIndex</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>mainForm</receiver>
|
||||
<slot>createIndex()</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>compactAction</sender>
|
||||
<signal>activated()</signal>
|
||||
<receiver>mainForm</receiver>
|
||||
<slot>compact()</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonDeleteTable</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>mainForm</receiver>
|
||||
<slot>deleteTable()</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonDeleteIndex</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>mainForm</receiver>
|
||||
<slot>deleteIndex()</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>editCopyAction</sender>
|
||||
<signal>activated()</signal>
|
||||
<receiver>mainForm</receiver>
|
||||
<slot>copy()</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>editPasteAction</sender>
|
||||
<signal>activated()</signal>
|
||||
<receiver>mainForm</receiver>
|
||||
<slot>paste()</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>dataTable</sender>
|
||||
<signal>valueChanged(int,int)</signal>
|
||||
<receiver>mainForm</receiver>
|
||||
<slot>recordEdited(int,int)</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>helpWhatsThisAction</sender>
|
||||
<signal>activated()</signal>
|
||||
<receiver>mainForm</receiver>
|
||||
<slot>helpWhatsThis()</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>helpAboutAction</sender>
|
||||
<signal>activated()</signal>
|
||||
<receiver>mainForm</receiver>
|
||||
<slot>helpAbout()</slot>
|
||||
</connection>
|
||||
</connections>
|
||||
<includes>
|
||||
<include location="local" impldecl="in declaration">qvalidator.h</include>
|
||||
<include location="local" impldecl="in declaration">findform.h</include>
|
||||
<include location="local" impldecl="in declaration">qclipboard.h</include>
|
||||
<include location="local" impldecl="in declaration">sqlitedb.h</include>
|
||||
<include location="local" impldecl="in implementation">qmessagebox.h</include>
|
||||
<include location="local" impldecl="in implementation">qfiledialog.h</include>
|
||||
<include location="local" impldecl="in implementation">qfile.h</include>
|
||||
<include location="local" impldecl="in implementation">qapplication.h</include>
|
||||
<include location="local" impldecl="in implementation">createtableform.h</include>
|
||||
<include location="local" impldecl="in implementation">createindexform.h</include>
|
||||
<include location="local" impldecl="in implementation">deletetableform.h</include>
|
||||
<include location="local" impldecl="in implementation">deleteindexform.h</include>
|
||||
<include location="local" impldecl="in implementation">aboutform.h</include>
|
||||
<include location="local" impldecl="in implementation">form1.ui.h</include>
|
||||
</includes>
|
||||
<variables>
|
||||
<variable access="public">QIntValidator * gotoValidator;</variable>
|
||||
<variable>DBBrowserDB db;</variable>
|
||||
<variable access="public">int recsPerView;</variable>
|
||||
<variable access="public">int recAtTop;</variable>
|
||||
<variable access="public">findForm * findWin;</variable>
|
||||
<variable access="public">QClipboard * clipboard;</variable>
|
||||
</variables>
|
||||
<slots>
|
||||
<slot>fileOpen()</slot>
|
||||
<slot>fileNew()</slot>
|
||||
<slot>populateStructure()</slot>
|
||||
<slot>populateTable( const QString & tablename )</slot>
|
||||
<slot>resetBrowser()</slot>
|
||||
<slot>fileClose()</slot>
|
||||
<slot>fileExit()</slot>
|
||||
<slot>closeEvent( QCloseEvent * )</slot>
|
||||
<slot>addRecord()</slot>
|
||||
<slot>recordEdited( int wrow, int wcol )</slot>
|
||||
<slot>deleteRecord()</slot>
|
||||
<slot>updateTableView( int lineToSelect )</slot>
|
||||
<slot>navigatePrevious()</slot>
|
||||
<slot>navigateNext()</slot>
|
||||
<slot>navigateGoto()</slot>
|
||||
<slot>setRecordsetLabel()</slot>
|
||||
<slot>browseFind( bool open )</slot>
|
||||
<slot>browseFindAway()</slot>
|
||||
<slot>lookfor( const QString & wfield, const QString & woperator, const QString & wsearchterm )</slot>
|
||||
<slot>showrecord( int dec )</slot>
|
||||
<slot>createTable()</slot>
|
||||
<slot>createIndex()</slot>
|
||||
<slot>compact()</slot>
|
||||
<slot>deleteTable()</slot>
|
||||
<slot>deleteIndex()</slot>
|
||||
<slot>copy()</slot>
|
||||
<slot>paste()</slot>
|
||||
<slot>helpWhatsThis()</slot>
|
||||
<slot>helpAbout()</slot>
|
||||
</slots>
|
||||
<functions>
|
||||
<function access="private" specifier="non virtual">init()</function>
|
||||
<function access="private" specifier="non virtual">destroy()</function>
|
||||
</functions>
|
||||
<pixmapinproject/>
|
||||
<layoutdefaults spacing="6" margin="11"/>
|
||||
</UI>
|
||||
568
sqlitebrowser/sqlitebrowser/form1.ui.h
Executable file
@@ -0,0 +1,568 @@
|
||||
/****************************************************************************
|
||||
** ui.h extension file, included from the uic-generated form implementation.
|
||||
**
|
||||
** If you wish to add, delete or rename functions or slots use
|
||||
** Qt Designer which will update this file, preserving your code. Create an
|
||||
** init() function in place of a constructor, and a destroy() function in
|
||||
** place of a destructor.
|
||||
*****************************************************************************/
|
||||
|
||||
void mainForm::init()
|
||||
{
|
||||
clipboard = QApplication::clipboard();
|
||||
if ( clipboard->supportsSelection() )
|
||||
clipboard->setSelectionMode( TRUE );
|
||||
|
||||
findWin = 0;
|
||||
recsPerView = 1000;
|
||||
recAtTop = 0;
|
||||
gotoValidator = new QIntValidator(0,0,this);
|
||||
editGoto->setValidator(gotoValidator);
|
||||
gotoValidator->setRange ( 0, 0);
|
||||
resetBrowser();
|
||||
this->setCaption(applicationName);
|
||||
|
||||
}
|
||||
|
||||
void mainForm::destroy()
|
||||
{
|
||||
if (gotoValidator)
|
||||
delete gotoValidator;
|
||||
}
|
||||
|
||||
void mainForm::fileOpen()
|
||||
{
|
||||
QString fileName = QFileDialog::getOpenFileName(
|
||||
"",
|
||||
"",
|
||||
this,
|
||||
"open file dialog"
|
||||
"Choose a database file" );
|
||||
if (QFile::exists(fileName) )
|
||||
{
|
||||
db.open(fileName);
|
||||
populateStructure();
|
||||
resetBrowser();
|
||||
fileCloseAction->setEnabled(true);
|
||||
compactAction->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void mainForm::fileNew()
|
||||
{
|
||||
QString fileName = QFileDialog::getSaveFileName(
|
||||
"",
|
||||
"",
|
||||
this,
|
||||
"create file dialog"
|
||||
"Choose a filename to save under" );
|
||||
if (QFile::exists(fileName) )
|
||||
{
|
||||
QString err = "File ";
|
||||
err.append(fileName);
|
||||
err.append(" already exists. Please choose a different name");
|
||||
QMessageBox::information( this, applicationName ,err);
|
||||
return;
|
||||
}
|
||||
if (!fileName.isNull())
|
||||
{
|
||||
db.create(fileName);
|
||||
populateStructure();
|
||||
resetBrowser();
|
||||
createTable();
|
||||
fileCloseAction->setEnabled(true);
|
||||
compactAction->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void mainForm::populateStructure()
|
||||
{
|
||||
dblistView->clear();
|
||||
dblistView->setSorting(-1);
|
||||
if (!db.isOpen()){
|
||||
return;
|
||||
}
|
||||
db.updateSchema();
|
||||
tableMap::Iterator it;
|
||||
tableMap tmap = db.tbmap;
|
||||
QListViewItem * lasttbitem = 0;
|
||||
for ( it = tmap.begin(); it != tmap.end(); ++it ) {
|
||||
QListViewItem * tbitem = new QListViewItem( dblistView, lasttbitem );
|
||||
//tbitem->setOpen( TRUE );
|
||||
tbitem->setText( 0, it.data().getname() );
|
||||
tbitem->setText( 1, "table" );
|
||||
tbitem->setText( 3, it.data().getsql() );
|
||||
fieldMap::Iterator fit;
|
||||
fieldMap fmap = it.data().fldmap;
|
||||
QListViewItem * lastflditem = 0;
|
||||
for ( fit = fmap.begin(); fit != fmap.end(); ++fit ) {
|
||||
QListViewItem * fielditem = new QListViewItem(tbitem, lastflditem);
|
||||
fielditem->setText( 0, fit.data().getname() );
|
||||
fielditem->setText( 1, "field" );
|
||||
fielditem->setText( 2, fit.data().gettype() );
|
||||
lastflditem = fielditem;
|
||||
}
|
||||
lasttbitem = tbitem;
|
||||
}
|
||||
indexMap::Iterator it2;
|
||||
indexMap imap = db.idxmap;
|
||||
for ( it2 = imap.begin(); it2 != imap.end(); ++it2 ) {
|
||||
QListViewItem * item = new QListViewItem( dblistView, lasttbitem );
|
||||
item->setText( 0, it2.data().getname());
|
||||
item->setText( 1, "index" );
|
||||
item->setText( 3, it2.data().getsql() );
|
||||
lasttbitem = item ;
|
||||
}
|
||||
}
|
||||
|
||||
void mainForm::populateTable( const QString & tablename)
|
||||
{
|
||||
bool mustreset = false;
|
||||
QApplication::setOverrideCursor( waitCursor, TRUE );
|
||||
if (tablename.compare(db.curBrowseTableName)!=0)
|
||||
mustreset = true;
|
||||
|
||||
if (!db.browseTable(tablename)){
|
||||
dataTable->setNumRows( 0 );
|
||||
dataTable->setNumCols( 0 );
|
||||
QApplication::restoreOverrideCursor();
|
||||
if (findWin){
|
||||
findWin->resetFields(db.getTableFields(""));
|
||||
findWin->resetResults();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (mustreset){
|
||||
recAtTop = 0;
|
||||
updateTableView(0);
|
||||
if (findWin) findWin->resetFields(db.getTableFields(db.curBrowseTableName));
|
||||
} else {
|
||||
updateTableView(-1);
|
||||
}
|
||||
//got to keep findWin in synch
|
||||
if (findWin){
|
||||
findWin->resetResults();
|
||||
}
|
||||
}
|
||||
|
||||
void mainForm::resetBrowser()
|
||||
{
|
||||
recAtTop = 0;
|
||||
comboBrowseTable->clear();
|
||||
QStringList tab = db.getTableNames();
|
||||
if (tab.isEmpty()){
|
||||
comboBrowseTable->insertItem("",-1);
|
||||
} else {
|
||||
for ( QStringList::Iterator ct = tab.begin(); ct != tab.end(); ++ct ) {
|
||||
comboBrowseTable->insertItem(*ct,-1);
|
||||
}
|
||||
}
|
||||
setRecordsetLabel();
|
||||
comboBrowseTable->setCurrentItem(0);
|
||||
populateTable(comboBrowseTable->currentText());
|
||||
}
|
||||
|
||||
void mainForm::fileClose()
|
||||
{
|
||||
db.close();
|
||||
resetBrowser();
|
||||
populateStructure();
|
||||
fileCloseAction->setEnabled(false);
|
||||
compactAction->setEnabled(false);
|
||||
}
|
||||
|
||||
|
||||
void mainForm::fileExit()
|
||||
{
|
||||
db.close();
|
||||
QApplication::exit( 0 );
|
||||
}
|
||||
|
||||
void mainForm::closeEvent( QCloseEvent * )
|
||||
{
|
||||
fileExit();
|
||||
}
|
||||
|
||||
void mainForm::addRecord()
|
||||
{
|
||||
if (db.addRecord()){
|
||||
//added record will be the last one in view
|
||||
recAtTop = ((db.getRecordCount()-1)/recsPerView)*recsPerView;
|
||||
updateTableView(db.getRecordCount()-recAtTop-1);
|
||||
}else{
|
||||
QMessageBox::information( this, applicationName,
|
||||
"Please select a table first" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void mainForm::recordEdited( int wrow, int wcol )
|
||||
{
|
||||
if (!db.updateRecord(wrow+recAtTop, wcol, dataTable->text(wrow, wcol))){
|
||||
//could not update
|
||||
rowList tab = db.browseRecs;
|
||||
rowList::iterator rt = tab.at(wrow+recAtTop);
|
||||
QString rowid = (*rt).first();
|
||||
QStringList::Iterator cv = (*rt).at(wcol+1);//must account for rowid
|
||||
dataTable->setText( wrow, wcol, *cv );
|
||||
QMessageBox::information( this, applicationName, "Data can not be edited" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void mainForm::deleteRecord()
|
||||
{
|
||||
if (dataTable->currentRow()!=-1){
|
||||
db.deleteRecord(dataTable->currentRow()+recAtTop);
|
||||
populateTable(db.curBrowseTableName);
|
||||
} else {
|
||||
QMessageBox::information( this, applicationName, "Please select a record first" ); }
|
||||
}
|
||||
|
||||
void mainForm::updateTableView(int lineToSelect)
|
||||
{
|
||||
// qDebug("line to select value is %d, rowAttop = %d",lineToSelect, recAtTop);
|
||||
QApplication::setOverrideCursor( waitCursor, TRUE );
|
||||
QStringList fields = db.browseFields;
|
||||
|
||||
dataTable->setNumRows(0);
|
||||
dataTable->setNumCols( fields.count() );
|
||||
int cheadnum = 0;
|
||||
for ( QStringList::Iterator ct = fields.begin(); ct != fields.end(); ++ct ) {
|
||||
dataTable->horizontalHeader()->setLabel( cheadnum, *ct );
|
||||
cheadnum++;
|
||||
}
|
||||
|
||||
rowList tab = db.browseRecs;
|
||||
int maxRecs = db.getRecordCount();
|
||||
int recsThisView = maxRecs - recAtTop;
|
||||
|
||||
if (recsThisView>recsPerView)
|
||||
recsThisView = recsPerView;
|
||||
|
||||
// qDebug("recsthisview= %d\n",recsThisView);
|
||||
|
||||
dataTable->setNumRows( recsThisView);
|
||||
rowList::iterator rt;
|
||||
int rowNum = 0;
|
||||
int colNum = 0;
|
||||
//int dcols =0;
|
||||
QString rowLabel;
|
||||
for ( rt = tab.at(recAtTop); rt !=tab.end(); ++rt )
|
||||
{
|
||||
rowLabel.setNum(recAtTop+rowNum+1);
|
||||
dataTable->verticalHeader()->setLabel( rowNum, rowLabel );
|
||||
colNum = 0;
|
||||
for ( QStringList::Iterator it = (*rt).begin(); it != (*rt).end(); ++it ) {
|
||||
//skip first one (the rowid)
|
||||
if (it!=(*rt).begin()){
|
||||
dataTable->setText( rowNum, colNum, *it );
|
||||
colNum++;
|
||||
}
|
||||
}
|
||||
rowNum++;
|
||||
if (rowNum==recsThisView) break;
|
||||
}
|
||||
|
||||
//dataTable->clearSelection(true);
|
||||
if (lineToSelect!=-1){
|
||||
//qDebug("inside selection");
|
||||
dataTable->clearSelection(true);
|
||||
dataTable->selectRow(lineToSelect);
|
||||
dataTable->setCurrentCell(lineToSelect, 0);
|
||||
dataTable->ensureCellVisible (lineToSelect, 0 ) ;
|
||||
}
|
||||
setRecordsetLabel();
|
||||
QApplication::restoreOverrideCursor();
|
||||
}
|
||||
|
||||
void mainForm::navigatePrevious()
|
||||
{
|
||||
int nextFirstRec = recAtTop - recsPerView;
|
||||
if (nextFirstRec >= 0 ) {
|
||||
recAtTop = nextFirstRec;
|
||||
updateTableView(-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void mainForm::navigateNext()
|
||||
{
|
||||
int nextFirstRec = recAtTop + recsPerView;
|
||||
//qDebug("called navigatenext, nextFirstRec=%d\n",nextFirstRec);
|
||||
if (nextFirstRec < db.getRecordCount() ) {
|
||||
recAtTop = nextFirstRec;
|
||||
updateTableView(-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void mainForm::navigateGoto()
|
||||
{
|
||||
QString typed = editGoto->text();
|
||||
bool ok;
|
||||
int dec = typed.toInt( &ok);
|
||||
if (dec==0) dec=1;
|
||||
if (dec>db.getRecordCount()) dec = db.getRecordCount();
|
||||
|
||||
recAtTop = ((dec-1)/recsPerView)*recsPerView;
|
||||
updateTableView(dec-recAtTop-1);
|
||||
editGoto->setText(QString::number(dec,10));
|
||||
}
|
||||
|
||||
void mainForm::setRecordsetLabel()
|
||||
{
|
||||
if (db.getRecordCount()==0){
|
||||
labelRecordset->setText("0 - 0 of 0");
|
||||
} else {
|
||||
QString label = QString::number(recAtTop+1,10);
|
||||
label.append(" - ");
|
||||
int lastrec = db.getRecordCount();
|
||||
int lastthisview = recAtTop+recsPerView;
|
||||
if (lastthisview > lastrec) lastthisview = lastrec;
|
||||
label.append(QString::number(lastthisview,10));
|
||||
label.append(" of ");
|
||||
label.append(QString::number(db.getRecordCount(),10));
|
||||
labelRecordset->setText(label);
|
||||
}
|
||||
}
|
||||
|
||||
void mainForm::browseFind(bool open)
|
||||
{
|
||||
if (open){
|
||||
if ( ! findWin ) {
|
||||
findWin= new findForm( this );
|
||||
connect( findWin, SIGNAL( lookfor(const QString&, const QString&, const QString&) ),
|
||||
this, SLOT( lookfor(const QString&, const QString&, const QString&) ) );
|
||||
connect( findWin, SIGNAL( showrecord(int) ),this, SLOT( showrecord(int) ) );
|
||||
connect( findWin, SIGNAL( goingAway() ),this, SLOT( browseFindAway() ) );
|
||||
}
|
||||
findWin->resetFields(db.getTableFields(db.curBrowseTableName));
|
||||
findWin->show();
|
||||
} else {
|
||||
if (findWin){
|
||||
findWin->hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mainForm::browseFindAway()
|
||||
{
|
||||
buttonFind->toggle();
|
||||
}
|
||||
|
||||
void mainForm::lookfor( const QString & wfield, const QString & woperator, const QString & wsearchterm )
|
||||
{
|
||||
if (!db.isOpen()){
|
||||
QMessageBox::information( this, applicationName, "There is no database opened. Please open or create a new database file." );
|
||||
return;
|
||||
}
|
||||
|
||||
//we may need to modify woperator and wsearchterm, so use copies
|
||||
QString * finaloperator = new QString(woperator);
|
||||
QString * finalsearchterm = new QString(wsearchterm);
|
||||
|
||||
//special case for CONTAINS operator: use LIKE and surround the search word with % characters
|
||||
if (woperator.compare("contains")==0){
|
||||
finaloperator = new QString("LIKE");
|
||||
QString newsearchterm = "%";
|
||||
newsearchterm.append(wsearchterm);
|
||||
newsearchterm.append("%");
|
||||
finalsearchterm = new QString( newsearchterm);
|
||||
}
|
||||
QApplication::setOverrideCursor( waitCursor, TRUE );
|
||||
QString statement = "SELECT rowid, ";
|
||||
statement.append(wfield);
|
||||
statement.append(" FROM ");
|
||||
statement.append( db.curBrowseTableName);
|
||||
statement.append(" WHERE ");
|
||||
statement.append(wfield);
|
||||
statement.append(" ");
|
||||
statement.append(*finaloperator);
|
||||
statement.append(" ");
|
||||
//searchterm needs to be quoted if it is not a number
|
||||
bool ok = false;
|
||||
wsearchterm.toDouble(&ok);
|
||||
if (!ok) wsearchterm.toInt(&ok, 10);
|
||||
if (!ok) {//not a number, quote it
|
||||
char * formSQL = sqlite_mprintf("%Q",(*finalsearchterm).latin1());
|
||||
statement.append(formSQL);
|
||||
if (formSQL) sqlite_freemem(formSQL);
|
||||
} else {//append the number, unquoted
|
||||
statement.append(*finalsearchterm);
|
||||
}
|
||||
statement.append(" ORDER BY rowid; ");
|
||||
resultMap res = db.getFindResults(statement);
|
||||
findWin->showResults(res);
|
||||
QApplication::restoreOverrideCursor();
|
||||
}
|
||||
|
||||
|
||||
void mainForm::showrecord( int dec )
|
||||
{
|
||||
recAtTop = ((dec-1)/recsPerView)*recsPerView;
|
||||
updateTableView(dec-recAtTop-1);
|
||||
}
|
||||
|
||||
void mainForm::createTable()
|
||||
{
|
||||
if (!db.isOpen()){
|
||||
QMessageBox::information( this, applicationName, "There is no database opened. Please open or create a new database file." );
|
||||
return;
|
||||
}
|
||||
createTableForm * tableForm = new createTableForm( this, "createtable", TRUE );
|
||||
if ( tableForm->exec() ) {
|
||||
if (!db.executeSQL(tableForm->createStatement)){
|
||||
QString error = "Error: could not create the table. Message from database engine: ";
|
||||
error.append(db.lastErrorMessage);
|
||||
QMessageBox::warning( this, applicationName, error );
|
||||
} else {
|
||||
populateStructure();
|
||||
resetBrowser();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void mainForm::createIndex()
|
||||
{
|
||||
if (!db.isOpen()){
|
||||
QMessageBox::information( this, applicationName, "There is no database opened. Please open or create a new database file." );
|
||||
return;
|
||||
}
|
||||
createIndexForm * indexForm = new createIndexForm( this, "createindex", TRUE );
|
||||
tableMap tmap = db.tbmap;
|
||||
indexForm->populateTable( tmap );
|
||||
if ( indexForm->exec() ) {
|
||||
if (!db.executeSQL(indexForm->createStatement)){
|
||||
QString error = "Error: could not create the index. Message from database engine: ";
|
||||
error.append(db.lastErrorMessage);
|
||||
QMessageBox::warning( this, applicationName, error );
|
||||
} else {
|
||||
populateStructure();
|
||||
resetBrowser();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void mainForm::compact()
|
||||
{
|
||||
QApplication::setOverrideCursor( waitCursor, TRUE );
|
||||
if (db.isOpen()){
|
||||
if (!db.compact()){
|
||||
QString error = "Error: could not compact the database file. Message from database engine: ";
|
||||
error.append(db.lastErrorMessage);
|
||||
QMessageBox::warning( this, applicationName, error );
|
||||
} else {
|
||||
QMessageBox::warning( this, applicationName, "Database compacted" );
|
||||
}
|
||||
}
|
||||
db.open(db.curDBFilename);
|
||||
populateStructure();
|
||||
resetBrowser();
|
||||
fileCloseAction->setEnabled(true);
|
||||
compactAction->setEnabled(true);
|
||||
QApplication::restoreOverrideCursor( );
|
||||
}
|
||||
|
||||
|
||||
void mainForm::deleteTable()
|
||||
{
|
||||
if (!db.isOpen()){
|
||||
QMessageBox::information( this, applicationName, "There is no database opened." );
|
||||
return;
|
||||
}
|
||||
deleteTableForm * tableForm = new deleteTableForm( this, "deletetable", TRUE );
|
||||
tableForm->populateOptions( db.getTableNames());
|
||||
if ( tableForm->exec() ) {
|
||||
QString statement = "DROP TABLE ";
|
||||
statement.append(tableForm->option);
|
||||
statement.append(";");
|
||||
if (!db.executeSQL( statement)){
|
||||
QString error = "Error: could not delete the table. Message from database engine: ";
|
||||
error.append(db.lastErrorMessage);
|
||||
QMessageBox::warning( this, applicationName, error );
|
||||
} else {
|
||||
populateStructure();
|
||||
resetBrowser();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void mainForm::deleteIndex()
|
||||
{
|
||||
if (!db.isOpen()){
|
||||
QMessageBox::information( this, applicationName, "There is no database opened." );
|
||||
return;
|
||||
}
|
||||
deleteIndexForm * indexForm = new deleteIndexForm( this, "deleteindex", TRUE );
|
||||
indexForm->populateOptions( db.getIndexNames());
|
||||
if ( indexForm->exec() ) {
|
||||
QString statement = "DROP INDEX ";
|
||||
statement.append(indexForm->option);
|
||||
statement.append(";");
|
||||
if (!db.executeSQL( statement)){
|
||||
QString error = "Error: could not delete the index. Message from database engine: ";
|
||||
error.append(db.lastErrorMessage);
|
||||
QMessageBox::warning( this, applicationName, error );
|
||||
} else {
|
||||
populateStructure();
|
||||
resetBrowser();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void mainForm::copy()
|
||||
{
|
||||
QWidget * t =dataTable->cellWidget(dataTable->currentRow(), dataTable->currentColumn());
|
||||
if (t!=0){
|
||||
if (t->isA( "QLineEdit" )){
|
||||
/*we are in edit mode*/
|
||||
if (t->hasFocus()){
|
||||
QLineEdit * le = (QLineEdit *) t;
|
||||
le->copy();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (editGoto->hasFocus())
|
||||
editGoto->copy();
|
||||
}
|
||||
|
||||
|
||||
void mainForm::paste()
|
||||
{
|
||||
QWidget * t =dataTable->cellWidget(dataTable->currentRow(), dataTable->currentColumn());
|
||||
if (t!=0){
|
||||
if (t->isA( "QLineEdit" )){
|
||||
/*we are in edit mode*/
|
||||
if (t->hasFocus()){
|
||||
QLineEdit * le = (QLineEdit *) t;
|
||||
le->paste();}
|
||||
}
|
||||
}
|
||||
if (editGoto->hasFocus())
|
||||
editGoto->paste();
|
||||
}
|
||||
|
||||
|
||||
void mainForm::helpWhatsThis()
|
||||
{
|
||||
QWhatsThis::enterWhatsThisMode ();
|
||||
}
|
||||
|
||||
|
||||
void mainForm::helpAbout()
|
||||
{
|
||||
/*QString wcaption = "About ";
|
||||
wcaption.append(applicationName);
|
||||
QString wtext = "This text, etc...";
|
||||
QMessageBox::about ( this, wcaption, wtext);*/
|
||||
aboutForm * aForm = new aboutForm( this, "about", TRUE );
|
||||
aForm ->exec() ;
|
||||
}
|
||||
BIN
sqlitebrowser/sqlitebrowser/iconwin.ico
Executable file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
sqlitebrowser/sqlitebrowser/images/editcopy.png
Executable file
|
After Width: | Height: | Size: 306 B |
BIN
sqlitebrowser/sqlitebrowser/images/editpaste.png
Executable file
|
After Width: | Height: | Size: 328 B |
BIN
sqlitebrowser/sqlitebrowser/images/filenew.png
Executable file
|
After Width: | Height: | Size: 231 B |
BIN
sqlitebrowser/sqlitebrowser/images/fileopen.png
Executable file
|
After Width: | Height: | Size: 268 B |
BIN
sqlitebrowser/sqlitebrowser/images/icone16.png
Executable file
|
After Width: | Height: | Size: 486 B |
BIN
sqlitebrowser/sqlitebrowser/images/searchfind.png
Executable file
|
After Width: | Height: | Size: 662 B |
BIN
sqlitebrowser/sqlitebrowser/images/whatsthis.png
Executable file
|
After Width: | Height: | Size: 26 KiB |
BIN
sqlitebrowser/sqlitebrowser/macapp.icns
Executable file
278
sqlitebrowser/sqlitebrowser/sqlite_source/attach.c
Executable file
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
** 2003 April 6
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains code used to implement the ATTACH and DETACH commands.
|
||||
**
|
||||
** $Id: attach.c,v 1.1.1.1 2003-08-21 02:24:02 tabuleiro Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** This routine is called by the parser to process an ATTACH statement:
|
||||
**
|
||||
** ATTACH DATABASE filename AS dbname
|
||||
**
|
||||
** The pFilename and pDbname arguments are the tokens that define the
|
||||
** filename and dbname in the ATTACH statement.
|
||||
*/
|
||||
void sqliteAttach(Parse *pParse, Token *pFilename, Token *pDbname){
|
||||
Db *aNew;
|
||||
int rc, i;
|
||||
char *zFile, *zName;
|
||||
sqlite *db;
|
||||
|
||||
if( pParse->explain ) return;
|
||||
db = pParse->db;
|
||||
if( db->file_format<4 ){
|
||||
sqliteErrorMsg(pParse, "cannot attach auxiliary databases to an "
|
||||
"older format master database", 0);
|
||||
pParse->rc = SQLITE_ERROR;
|
||||
return;
|
||||
}
|
||||
if( db->nDb>=MAX_ATTACHED+2 ){
|
||||
sqliteErrorMsg(pParse, "too many attached databases - max %d",
|
||||
MAX_ATTACHED);
|
||||
pParse->rc = SQLITE_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
zFile = 0;
|
||||
sqliteSetNString(&zFile, pFilename->z, pFilename->n, 0);
|
||||
if( zFile==0 ) return;
|
||||
sqliteDequote(zFile);
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
if( sqliteAuthCheck(pParse, SQLITE_ATTACH, zFile, 0, 0)!=SQLITE_OK ){
|
||||
sqliteFree(zFile);
|
||||
return;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_AUTHORIZATION */
|
||||
|
||||
zName = 0;
|
||||
sqliteSetNString(&zName, pDbname->z, pDbname->n, 0);
|
||||
if( zName==0 ) return;
|
||||
sqliteDequote(zName);
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( db->aDb[i].zName && sqliteStrICmp(db->aDb[i].zName, zName)==0 ){
|
||||
sqliteErrorMsg(pParse, "database %z is already in use", zName);
|
||||
pParse->rc = SQLITE_ERROR;
|
||||
sqliteFree(zFile);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if( db->aDb==db->aDbStatic ){
|
||||
aNew = sqliteMalloc( sizeof(db->aDb[0])*3 );
|
||||
if( aNew==0 ) return;
|
||||
memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2);
|
||||
}else{
|
||||
aNew = sqliteRealloc(db->aDb, sizeof(db->aDb[0])*(db->nDb+1) );
|
||||
if( aNew==0 ) return;
|
||||
}
|
||||
db->aDb = aNew;
|
||||
aNew = &db->aDb[db->nDb++];
|
||||
memset(aNew, 0, sizeof(*aNew));
|
||||
sqliteHashInit(&aNew->tblHash, SQLITE_HASH_STRING, 0);
|
||||
sqliteHashInit(&aNew->idxHash, SQLITE_HASH_STRING, 0);
|
||||
sqliteHashInit(&aNew->trigHash, SQLITE_HASH_STRING, 0);
|
||||
sqliteHashInit(&aNew->aFKey, SQLITE_HASH_STRING, 1);
|
||||
aNew->zName = zName;
|
||||
rc = sqliteBtreeFactory(db, zFile, 0, MAX_PAGES, &aNew->pBt);
|
||||
if( rc ){
|
||||
sqliteErrorMsg(pParse, "unable to open database: %s", zFile);
|
||||
}
|
||||
sqliteFree(zFile);
|
||||
db->flags &= ~SQLITE_Initialized;
|
||||
if( pParse->nErr ) return;
|
||||
rc = sqliteInit(pParse->db, &pParse->zErrMsg);
|
||||
if( rc ){
|
||||
sqliteResetInternalSchema(db, 0);
|
||||
pParse->nErr++;
|
||||
pParse->rc = SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is called by the parser to process a DETACH statement:
|
||||
**
|
||||
** DETACH DATABASE dbname
|
||||
**
|
||||
** The pDbname argument is the name of the database in the DETACH statement.
|
||||
*/
|
||||
void sqliteDetach(Parse *pParse, Token *pDbname){
|
||||
int i;
|
||||
sqlite *db;
|
||||
|
||||
if( pParse->explain ) return;
|
||||
db = pParse->db;
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( db->aDb[i].pBt==0 || db->aDb[i].zName==0 ) continue;
|
||||
if( strlen(db->aDb[i].zName)!=pDbname->n ) continue;
|
||||
if( sqliteStrNICmp(db->aDb[i].zName, pDbname->z, pDbname->n)==0 ) break;
|
||||
}
|
||||
if( i>=db->nDb ){
|
||||
sqliteErrorMsg(pParse, "no such database: %T", pDbname);
|
||||
return;
|
||||
}
|
||||
if( i<2 ){
|
||||
sqliteErrorMsg(pParse, "cannot detach database %T", pDbname);
|
||||
return;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
if( sqliteAuthCheck(pParse,SQLITE_DETACH,db->aDb[i].zName,0,0)!=SQLITE_OK ){
|
||||
return;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_AUTHORIZATION */
|
||||
sqliteBtreeClose(db->aDb[i].pBt);
|
||||
db->aDb[i].pBt = 0;
|
||||
sqliteFree(db->aDb[i].zName);
|
||||
sqliteResetInternalSchema(db, i);
|
||||
db->nDb--;
|
||||
if( i<db->nDb ){
|
||||
db->aDb[i] = db->aDb[db->nDb];
|
||||
memset(&db->aDb[db->nDb], 0, sizeof(db->aDb[0]));
|
||||
sqliteResetInternalSchema(db, i);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Initialize a DbFixer structure. This routine must be called prior
|
||||
** to passing the structure to one of the sqliteFixAAAA() routines below.
|
||||
**
|
||||
** The return value indicates whether or not fixation is required. TRUE
|
||||
** means we do need to fix the database references, FALSE means we do not.
|
||||
*/
|
||||
int sqliteFixInit(
|
||||
DbFixer *pFix, /* The fixer to be initialized */
|
||||
Parse *pParse, /* Error messages will be written here */
|
||||
int iDb, /* This is the database that must must be used */
|
||||
const char *zType, /* "view", "trigger", or "index" */
|
||||
const Token *pName /* Name of the view, trigger, or index */
|
||||
){
|
||||
sqlite *db;
|
||||
|
||||
if( iDb<0 || iDb==1 ) return 0;
|
||||
db = pParse->db;
|
||||
assert( db->nDb>iDb );
|
||||
pFix->pParse = pParse;
|
||||
pFix->zDb = db->aDb[iDb].zName;
|
||||
pFix->zType = zType;
|
||||
pFix->pName = pName;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** The following set of routines walk through the parse tree and assign
|
||||
** a specific database to all table references where the database name
|
||||
** was left unspecified in the original SQL statement. The pFix structure
|
||||
** must have been initialized by a prior call to sqliteFixInit().
|
||||
**
|
||||
** These routines are used to make sure that an index, trigger, or
|
||||
** view in one database does not refer to objects in a different database.
|
||||
** (Exception: indices, triggers, and views in the TEMP database are
|
||||
** allowed to refer to anything.) If a reference is explicitly made
|
||||
** to an object in a different database, an error message is added to
|
||||
** pParse->zErrMsg and these routines return non-zero. If everything
|
||||
** checks out, these routines return 0.
|
||||
*/
|
||||
int sqliteFixSrcList(
|
||||
DbFixer *pFix, /* Context of the fixation */
|
||||
SrcList *pList /* The Source list to check and modify */
|
||||
){
|
||||
int i;
|
||||
const char *zDb;
|
||||
|
||||
if( pList==0 ) return 0;
|
||||
zDb = pFix->zDb;
|
||||
for(i=0; i<pList->nSrc; i++){
|
||||
if( pList->a[i].zDatabase==0 ){
|
||||
pList->a[i].zDatabase = sqliteStrDup(zDb);
|
||||
}else if( sqliteStrICmp(pList->a[i].zDatabase,zDb)!=0 ){
|
||||
sqliteErrorMsg(pFix->pParse,
|
||||
"%s %z cannot reference objects in database %s",
|
||||
pFix->zType, sqliteStrNDup(pFix->pName->z, pFix->pName->n),
|
||||
pList->a[i].zDatabase);
|
||||
return 1;
|
||||
}
|
||||
if( sqliteFixSelect(pFix, pList->a[i].pSelect) ) return 1;
|
||||
if( sqliteFixExpr(pFix, pList->a[i].pOn) ) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int sqliteFixSelect(
|
||||
DbFixer *pFix, /* Context of the fixation */
|
||||
Select *pSelect /* The SELECT statement to be fixed to one database */
|
||||
){
|
||||
while( pSelect ){
|
||||
if( sqliteFixExprList(pFix, pSelect->pEList) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqliteFixSrcList(pFix, pSelect->pSrc) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqliteFixExpr(pFix, pSelect->pWhere) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqliteFixExpr(pFix, pSelect->pHaving) ){
|
||||
return 1;
|
||||
}
|
||||
pSelect = pSelect->pPrior;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int sqliteFixExpr(
|
||||
DbFixer *pFix, /* Context of the fixation */
|
||||
Expr *pExpr /* The expression to be fixed to one database */
|
||||
){
|
||||
while( pExpr ){
|
||||
if( sqliteFixSelect(pFix, pExpr->pSelect) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqliteFixExprList(pFix, pExpr->pList) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqliteFixExpr(pFix, pExpr->pRight) ){
|
||||
return 1;
|
||||
}
|
||||
pExpr = pExpr->pLeft;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int sqliteFixExprList(
|
||||
DbFixer *pFix, /* Context of the fixation */
|
||||
ExprList *pList /* The expression to be fixed to one database */
|
||||
){
|
||||
int i;
|
||||
if( pList==0 ) return 0;
|
||||
for(i=0; i<pList->nExpr; i++){
|
||||
if( sqliteFixExpr(pFix, pList->a[i].pExpr) ){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int sqliteFixTriggerStep(
|
||||
DbFixer *pFix, /* Context of the fixation */
|
||||
TriggerStep *pStep /* The trigger step be fixed to one database */
|
||||
){
|
||||
while( pStep ){
|
||||
if( sqliteFixSelect(pFix, pStep->pSelect) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqliteFixExpr(pFix, pStep->pWhere) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqliteFixExprList(pFix, pStep->pExprList) ){
|
||||
return 1;
|
||||
}
|
||||
pStep = pStep->pNext;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
225
sqlitebrowser/sqlitebrowser/sqlite_source/auth.c
Executable file
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
** 2003 January 11
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains code used to implement the sqlite_set_authorizer()
|
||||
** API. This facility is an optional feature of the library. Embedded
|
||||
** systems that do not need this facility may omit it by recompiling
|
||||
** the library with -DSQLITE_OMIT_AUTHORIZATION=1
|
||||
**
|
||||
** $Id: auth.c,v 1.1.1.1 2003-08-21 02:24:03 tabuleiro Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** All of the code in this file may be omitted by defining a single
|
||||
** macro.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
|
||||
/*
|
||||
** Set or clear the access authorization function.
|
||||
**
|
||||
** The access authorization function is be called during the compilation
|
||||
** phase to verify that the user has read and/or write access permission on
|
||||
** various fields of the database. The first argument to the auth function
|
||||
** is a copy of the 3rd argument to this routine. The second argument
|
||||
** to the auth function is one of these constants:
|
||||
**
|
||||
** SQLITE_COPY
|
||||
** SQLITE_CREATE_INDEX
|
||||
** SQLITE_CREATE_TABLE
|
||||
** SQLITE_CREATE_TEMP_INDEX
|
||||
** SQLITE_CREATE_TEMP_TABLE
|
||||
** SQLITE_CREATE_TEMP_TRIGGER
|
||||
** SQLITE_CREATE_TEMP_VIEW
|
||||
** SQLITE_CREATE_TRIGGER
|
||||
** SQLITE_CREATE_VIEW
|
||||
** SQLITE_DELETE
|
||||
** SQLITE_DROP_INDEX
|
||||
** SQLITE_DROP_TABLE
|
||||
** SQLITE_DROP_TEMP_INDEX
|
||||
** SQLITE_DROP_TEMP_TABLE
|
||||
** SQLITE_DROP_TEMP_TRIGGER
|
||||
** SQLITE_DROP_TEMP_VIEW
|
||||
** SQLITE_DROP_TRIGGER
|
||||
** SQLITE_DROP_VIEW
|
||||
** SQLITE_INSERT
|
||||
** SQLITE_PRAGMA
|
||||
** SQLITE_READ
|
||||
** SQLITE_SELECT
|
||||
** SQLITE_TRANSACTION
|
||||
** SQLITE_UPDATE
|
||||
**
|
||||
** The third and fourth arguments to the auth function are the name of
|
||||
** the table and the column that are being accessed. The auth function
|
||||
** should return either SQLITE_OK, SQLITE_DENY, or SQLITE_IGNORE. If
|
||||
** SQLITE_OK is returned, it means that access is allowed. SQLITE_DENY
|
||||
** means that the SQL statement will never-run - the sqlite_exec() call
|
||||
** will return with an error. SQLITE_IGNORE means that the SQL statement
|
||||
** should run but attempts to read the specified column will return NULL
|
||||
** and attempts to write the column will be ignored.
|
||||
**
|
||||
** Setting the auth function to NULL disables this hook. The default
|
||||
** setting of the auth function is NULL.
|
||||
*/
|
||||
int sqlite_set_authorizer(
|
||||
sqlite *db,
|
||||
int (*xAuth)(void*,int,const char*,const char*,const char*,const char*),
|
||||
void *pArg
|
||||
){
|
||||
db->xAuth = xAuth;
|
||||
db->pAuthArg = pArg;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write an error message into pParse->zErrMsg that explains that the
|
||||
** user-supplied authorization function returned an illegal value.
|
||||
*/
|
||||
static void sqliteAuthBadReturnCode(Parse *pParse, int rc){
|
||||
char zBuf[20];
|
||||
sprintf(zBuf, "(%d)", rc);
|
||||
sqliteSetString(&pParse->zErrMsg, "illegal return value ", zBuf,
|
||||
" from the authorization function - should be SQLITE_OK, "
|
||||
"SQLITE_IGNORE, or SQLITE_DENY", 0);
|
||||
pParse->nErr++;
|
||||
pParse->rc = SQLITE_MISUSE;
|
||||
}
|
||||
|
||||
/*
|
||||
** The pExpr should be a TK_COLUMN expression. The table referred to
|
||||
** is in pTabList or else it is the NEW or OLD table of a trigger.
|
||||
** Check to see if it is OK to read this particular column.
|
||||
**
|
||||
** If the auth function returns SQLITE_IGNORE, change the TK_COLUMN
|
||||
** instruction into a TK_NULL. If the auth function returns SQLITE_DENY,
|
||||
** then generate an error.
|
||||
*/
|
||||
void sqliteAuthRead(
|
||||
Parse *pParse, /* The parser context */
|
||||
Expr *pExpr, /* The expression to check authorization on */
|
||||
SrcList *pTabList /* All table that pExpr might refer to */
|
||||
){
|
||||
sqlite *db = pParse->db;
|
||||
int rc;
|
||||
Table *pTab; /* The table being read */
|
||||
const char *zCol; /* Name of the column of the table */
|
||||
int iSrc; /* Index in pTabList->a[] of table being read */
|
||||
const char *zDBase; /* Name of database being accessed */
|
||||
|
||||
if( db->xAuth==0 ) return;
|
||||
assert( pExpr->op==TK_COLUMN );
|
||||
for(iSrc=0; iSrc<pTabList->nSrc; iSrc++){
|
||||
if( pExpr->iTable==pTabList->a[iSrc].iCursor ) break;
|
||||
}
|
||||
if( iSrc>=0 && iSrc<pTabList->nSrc ){
|
||||
pTab = pTabList->a[iSrc].pTab;
|
||||
}else{
|
||||
/* This must be an attempt to read the NEW or OLD pseudo-tables
|
||||
** of a trigger.
|
||||
*/
|
||||
TriggerStack *pStack; /* The stack of current triggers */
|
||||
pStack = pParse->trigStack;
|
||||
assert( pStack!=0 );
|
||||
assert( pExpr->iTable==pStack->newIdx || pExpr->iTable==pStack->oldIdx );
|
||||
pTab = pStack->pTab;
|
||||
}
|
||||
if( pTab==0 ) return;
|
||||
if( pExpr->iColumn>=0 ){
|
||||
assert( pExpr->iColumn<pTab->nCol );
|
||||
zCol = pTab->aCol[pExpr->iColumn].zName;
|
||||
}else if( pTab->iPKey>=0 ){
|
||||
assert( pTab->iPKey<pTab->nCol );
|
||||
zCol = pTab->aCol[pTab->iPKey].zName;
|
||||
}else{
|
||||
zCol = "ROWID";
|
||||
}
|
||||
assert( pExpr->iDb<db->nDb );
|
||||
zDBase = db->aDb[pExpr->iDb].zName;
|
||||
rc = db->xAuth(db->pAuthArg, SQLITE_READ, pTab->zName, zCol, zDBase,
|
||||
pParse->zAuthContext);
|
||||
if( rc==SQLITE_IGNORE ){
|
||||
pExpr->op = TK_NULL;
|
||||
}else if( rc==SQLITE_DENY ){
|
||||
if( db->nDb>2 || pExpr->iDb!=0 ){
|
||||
sqliteSetString(&pParse->zErrMsg,"access to ", zDBase, ".",
|
||||
pTab->zName, ".", zCol, " is prohibited", 0);
|
||||
}else{
|
||||
sqliteSetString(&pParse->zErrMsg,"access to ", pTab->zName, ".",
|
||||
zCol, " is prohibited", 0);
|
||||
}
|
||||
pParse->nErr++;
|
||||
pParse->rc = SQLITE_AUTH;
|
||||
}else if( rc!=SQLITE_OK ){
|
||||
sqliteAuthBadReturnCode(pParse, rc);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Do an authorization check using the code and arguments given. Return
|
||||
** either SQLITE_OK (zero) or SQLITE_IGNORE or SQLITE_DENY. If SQLITE_DENY
|
||||
** is returned, then the error count and error message in pParse are
|
||||
** modified appropriately.
|
||||
*/
|
||||
int sqliteAuthCheck(
|
||||
Parse *pParse,
|
||||
int code,
|
||||
const char *zArg1,
|
||||
const char *zArg2,
|
||||
const char *zArg3
|
||||
){
|
||||
sqlite *db = pParse->db;
|
||||
int rc;
|
||||
|
||||
if( db->xAuth==0 ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
rc = db->xAuth(db->pAuthArg, code, zArg1, zArg2, zArg3, pParse->zAuthContext);
|
||||
if( rc==SQLITE_DENY ){
|
||||
sqliteSetString(&pParse->zErrMsg, "not authorized", 0);
|
||||
pParse->rc = SQLITE_AUTH;
|
||||
pParse->nErr++;
|
||||
}else if( rc!=SQLITE_OK && rc!=SQLITE_IGNORE ){
|
||||
rc = SQLITE_DENY;
|
||||
sqliteAuthBadReturnCode(pParse, rc);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Push an authorization context. After this routine is called, the
|
||||
** zArg3 argument to authorization callbacks will be zContext until
|
||||
** popped. Or if pParse==0, this routine is a no-op.
|
||||
*/
|
||||
void sqliteAuthContextPush(
|
||||
Parse *pParse,
|
||||
AuthContext *pContext,
|
||||
const char *zContext
|
||||
){
|
||||
pContext->pParse = pParse;
|
||||
if( pParse ){
|
||||
pContext->zAuthContext = pParse->zAuthContext;
|
||||
pParse->zAuthContext = zContext;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Pop an authorization context that was previously pushed
|
||||
** by sqliteAuthContextPush
|
||||
*/
|
||||
void sqliteAuthContextPop(AuthContext *pContext){
|
||||
if( pContext->pParse ){
|
||||
pContext->pParse->zAuthContext = pContext->zAuthContext;
|
||||
pContext->pParse = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SQLITE_OMIT_AUTHORIZATION */
|
||||
3585
sqlitebrowser/sqlitebrowser/sqlite_source/btree.c
Executable file
156
sqlitebrowser/sqlitebrowser/sqlite_source/btree.h
Executable file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
** 2001 September 15
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This header file defines the interface that the sqlite B-Tree file
|
||||
** subsystem. See comments in the source code for a detailed description
|
||||
** of what each interface routine does.
|
||||
**
|
||||
** @(#) $Id: btree.h,v 1.1.1.1 2003-08-21 02:24:05 tabuleiro Exp $
|
||||
*/
|
||||
#ifndef _BTREE_H_
|
||||
#define _BTREE_H_
|
||||
|
||||
/*
|
||||
** Forward declarations of structure
|
||||
*/
|
||||
typedef struct Btree Btree;
|
||||
typedef struct BtCursor BtCursor;
|
||||
typedef struct BtOps BtOps;
|
||||
typedef struct BtCursorOps BtCursorOps;
|
||||
|
||||
|
||||
/*
|
||||
** An instance of the following structure contains pointers to all
|
||||
** methods against an open BTree. Alternative BTree implementations
|
||||
** (examples: file based versus in-memory) can be created by substituting
|
||||
** different methods. Users of the BTree cannot tell the difference.
|
||||
**
|
||||
** In C++ we could do this by defining a virtual base class and then
|
||||
** creating subclasses for each different implementation. But this is
|
||||
** C not C++ so we have to be a little more explicit.
|
||||
*/
|
||||
struct BtOps {
|
||||
int (*Close)(Btree*);
|
||||
int (*SetCacheSize)(Btree*, int);
|
||||
int (*SetSafetyLevel)(Btree*, int);
|
||||
int (*BeginTrans)(Btree*);
|
||||
int (*Commit)(Btree*);
|
||||
int (*Rollback)(Btree*);
|
||||
int (*BeginCkpt)(Btree*);
|
||||
int (*CommitCkpt)(Btree*);
|
||||
int (*RollbackCkpt)(Btree*);
|
||||
int (*CreateTable)(Btree*, int*);
|
||||
int (*CreateIndex)(Btree*, int*);
|
||||
int (*DropTable)(Btree*, int);
|
||||
int (*ClearTable)(Btree*, int);
|
||||
int (*Cursor)(Btree*, int iTable, int wrFlag, BtCursor **ppCur);
|
||||
int (*GetMeta)(Btree*, int*);
|
||||
int (*UpdateMeta)(Btree*, int*);
|
||||
char *(*IntegrityCheck)(Btree*, int*, int);
|
||||
const char *(*GetFilename)(Btree*);
|
||||
int (*Copyfile)(Btree*,Btree*);
|
||||
#ifdef SQLITE_TEST
|
||||
int (*PageDump)(Btree*, int, int);
|
||||
struct Pager *(*Pager)(Btree*);
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of this structure defines all of the methods that can
|
||||
** be executed against a cursor.
|
||||
*/
|
||||
struct BtCursorOps {
|
||||
int (*Moveto)(BtCursor*, const void *pKey, int nKey, int *pRes);
|
||||
int (*Delete)(BtCursor*);
|
||||
int (*Insert)(BtCursor*, const void *pKey, int nKey,
|
||||
const void *pData, int nData);
|
||||
int (*First)(BtCursor*, int *pRes);
|
||||
int (*Last)(BtCursor*, int *pRes);
|
||||
int (*Next)(BtCursor*, int *pRes);
|
||||
int (*Previous)(BtCursor*, int *pRes);
|
||||
int (*KeySize)(BtCursor*, int *pSize);
|
||||
int (*Key)(BtCursor*, int offset, int amt, char *zBuf);
|
||||
int (*KeyCompare)(BtCursor*, const void *pKey, int nKey,
|
||||
int nIgnore, int *pRes);
|
||||
int (*DataSize)(BtCursor*, int *pSize);
|
||||
int (*Data)(BtCursor*, int offset, int amt, char *zBuf);
|
||||
int (*CloseCursor)(BtCursor*);
|
||||
#ifdef SQLITE_TEST
|
||||
int (*CursorDump)(BtCursor*, int*);
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
** The number of 4-byte "meta" values contained on the first page of each
|
||||
** database file.
|
||||
*/
|
||||
#define SQLITE_N_BTREE_META 10
|
||||
|
||||
int sqliteBtreeOpen(const char *zFilename, int mode, int nPg, Btree **ppBtree);
|
||||
int sqliteRbtreeOpen(const char *zFilename, int mode, int nPg, Btree **ppBtree);
|
||||
|
||||
#define btOps(pBt) (*((BtOps **)(pBt)))
|
||||
#define btCOps(pCur) (*((BtCursorOps **)(pCur)))
|
||||
|
||||
#define sqliteBtreeClose(pBt) (btOps(pBt)->Close(pBt))
|
||||
#define sqliteBtreeSetCacheSize(pBt, sz) (btOps(pBt)->SetCacheSize(pBt, sz))
|
||||
#define sqliteBtreeSetSafetyLevel(pBt, sl) (btOps(pBt)->SetSafetyLevel(pBt, sl))
|
||||
#define sqliteBtreeBeginTrans(pBt) (btOps(pBt)->BeginTrans(pBt))
|
||||
#define sqliteBtreeCommit(pBt) (btOps(pBt)->Commit(pBt))
|
||||
#define sqliteBtreeRollback(pBt) (btOps(pBt)->Rollback(pBt))
|
||||
#define sqliteBtreeBeginCkpt(pBt) (btOps(pBt)->BeginCkpt(pBt))
|
||||
#define sqliteBtreeCommitCkpt(pBt) (btOps(pBt)->CommitCkpt(pBt))
|
||||
#define sqliteBtreeRollbackCkpt(pBt) (btOps(pBt)->RollbackCkpt(pBt))
|
||||
#define sqliteBtreeCreateTable(pBt,piTable)\
|
||||
(btOps(pBt)->CreateTable(pBt,piTable))
|
||||
#define sqliteBtreeCreateIndex(pBt, piIndex)\
|
||||
(btOps(pBt)->CreateIndex(pBt, piIndex))
|
||||
#define sqliteBtreeDropTable(pBt, iTable) (btOps(pBt)->DropTable(pBt, iTable))
|
||||
#define sqliteBtreeClearTable(pBt, iTable)\
|
||||
(btOps(pBt)->ClearTable(pBt, iTable))
|
||||
#define sqliteBtreeCursor(pBt, iTable, wrFlag, ppCur)\
|
||||
(btOps(pBt)->Cursor(pBt, iTable, wrFlag, ppCur))
|
||||
#define sqliteBtreeMoveto(pCur, pKey, nKey, pRes)\
|
||||
(btCOps(pCur)->Moveto(pCur, pKey, nKey, pRes))
|
||||
#define sqliteBtreeDelete(pCur) (btCOps(pCur)->Delete(pCur))
|
||||
#define sqliteBtreeInsert(pCur, pKey, nKey, pData, nData) \
|
||||
(btCOps(pCur)->Insert(pCur, pKey, nKey, pData, nData))
|
||||
#define sqliteBtreeFirst(pCur, pRes) (btCOps(pCur)->First(pCur, pRes))
|
||||
#define sqliteBtreeLast(pCur, pRes) (btCOps(pCur)->Last(pCur, pRes))
|
||||
#define sqliteBtreeNext(pCur, pRes) (btCOps(pCur)->Next(pCur, pRes))
|
||||
#define sqliteBtreePrevious(pCur, pRes) (btCOps(pCur)->Previous(pCur, pRes))
|
||||
#define sqliteBtreeKeySize(pCur, pSize) (btCOps(pCur)->KeySize(pCur, pSize) )
|
||||
#define sqliteBtreeKey(pCur, offset, amt, zBuf)\
|
||||
(btCOps(pCur)->Key(pCur, offset, amt, zBuf))
|
||||
#define sqliteBtreeKeyCompare(pCur, pKey, nKey, nIgnore, pRes)\
|
||||
(btCOps(pCur)->KeyCompare(pCur, pKey, nKey, nIgnore, pRes))
|
||||
#define sqliteBtreeDataSize(pCur, pSize) (btCOps(pCur)->DataSize(pCur, pSize))
|
||||
#define sqliteBtreeData(pCur, offset, amt, zBuf)\
|
||||
(btCOps(pCur)->Data(pCur, offset, amt, zBuf))
|
||||
#define sqliteBtreeCloseCursor(pCur) (btCOps(pCur)->CloseCursor(pCur))
|
||||
#define sqliteBtreeGetMeta(pBt, aMeta) (btOps(pBt)->GetMeta(pBt, aMeta))
|
||||
#define sqliteBtreeUpdateMeta(pBt, aMeta) (btOps(pBt)->UpdateMeta(pBt, aMeta))
|
||||
#define sqliteBtreeIntegrityCheck(pBt, aRoot, nRoot)\
|
||||
(btOps(pBt)->IntegrityCheck(pBt, aRoot, nRoot))
|
||||
#define sqliteBtreeGetFilename(pBt) (btOps(pBt)->GetFilename(pBt))
|
||||
#define sqliteBtreeCopyFile(pBt1, pBt2) (btOps(pBt1)->Copyfile(pBt1, pBt2))
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
#define sqliteBtreePageDump(pBt, pgno, recursive)\
|
||||
(btOps(pBt)->PageDump(pBt, pgno, recursive))
|
||||
#define sqliteBtreeCursorDump(pCur, aResult)\
|
||||
(btCOps(pCur)->CursorDump(pCur, aResult))
|
||||
#define sqliteBtreePager(pBt) (btOps(pBt)->Pager(pBt))
|
||||
int btree_native_byte_order;
|
||||
#endif /* SQLITE_TEST */
|
||||
|
||||
|
||||
#endif /* _BTREE_H_ */
|
||||
1417
sqlitebrowser/sqlitebrowser/sqlite_source/btree_rb.c
Executable file
2214
sqlitebrowser/sqlitebrowser/sqlite_source/build.c
Executable file
1
sqlitebrowser/sqlitebrowser/sqlite_source/config.h
Executable file
@@ -0,0 +1 @@
|
||||
#define SQLITE_PTR_SZ 4
|
||||
120
sqlitebrowser/sqlitebrowser/sqlite_source/copy.c
Executable file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
** 2003 April 6
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains code used to implement the COPY command.
|
||||
**
|
||||
** $Id: copy.c,v 1.1.1.1 2003-08-21 02:24:07 tabuleiro Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** The COPY command is for compatibility with PostgreSQL and specificially
|
||||
** for the ability to read the output of pg_dump. The format is as
|
||||
** follows:
|
||||
**
|
||||
** COPY table FROM file [USING DELIMITERS string]
|
||||
**
|
||||
** "table" is an existing table name. We will read lines of code from
|
||||
** file to fill this table with data. File might be "stdin". The optional
|
||||
** delimiter string identifies the field separators. The default is a tab.
|
||||
*/
|
||||
void sqliteCopy(
|
||||
Parse *pParse, /* The parser context */
|
||||
SrcList *pTableName, /* The name of the table into which we will insert */
|
||||
Token *pFilename, /* The file from which to obtain information */
|
||||
Token *pDelimiter, /* Use this as the field delimiter */
|
||||
int onError /* What to do if a constraint fails */
|
||||
){
|
||||
Table *pTab;
|
||||
int i;
|
||||
Vdbe *v;
|
||||
int addr, end;
|
||||
Index *pIdx;
|
||||
char *zFile = 0;
|
||||
const char *zDb;
|
||||
sqlite *db = pParse->db;
|
||||
|
||||
|
||||
if( sqlite_malloc_failed ) goto copy_cleanup;
|
||||
assert( pTableName->nSrc==1 );
|
||||
pTab = sqliteSrcListLookup(pParse, pTableName);
|
||||
if( pTab==0 || sqliteIsReadOnly(pParse, pTab, 0) ) goto copy_cleanup;
|
||||
zFile = sqliteStrNDup(pFilename->z, pFilename->n);
|
||||
sqliteDequote(zFile);
|
||||
assert( pTab->iDb<db->nDb );
|
||||
zDb = db->aDb[pTab->iDb].zName;
|
||||
if( sqliteAuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb)
|
||||
|| sqliteAuthCheck(pParse, SQLITE_COPY, pTab->zName, zFile, zDb) ){
|
||||
goto copy_cleanup;
|
||||
}
|
||||
v = sqliteGetVdbe(pParse);
|
||||
if( v ){
|
||||
sqliteBeginWriteOperation(pParse, 1, pTab->iDb);
|
||||
addr = sqliteVdbeAddOp(v, OP_FileOpen, 0, 0);
|
||||
sqliteVdbeChangeP3(v, addr, pFilename->z, pFilename->n);
|
||||
sqliteVdbeDequoteP3(v, addr);
|
||||
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenWrite, 0, pTab->tnum);
|
||||
sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
|
||||
for(i=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
|
||||
assert( pIdx->iDb==1 || pIdx->iDb==pTab->iDb );
|
||||
sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenWrite, i, pIdx->tnum);
|
||||
sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC);
|
||||
}
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
sqliteVdbeAddOp(v, OP_Integer, 0, 0); /* Initialize the row count */
|
||||
}
|
||||
end = sqliteVdbeMakeLabel(v);
|
||||
addr = sqliteVdbeAddOp(v, OP_FileRead, pTab->nCol, end);
|
||||
if( pDelimiter ){
|
||||
sqliteVdbeChangeP3(v, addr, pDelimiter->z, pDelimiter->n);
|
||||
sqliteVdbeDequoteP3(v, addr);
|
||||
}else{
|
||||
sqliteVdbeChangeP3(v, addr, "\t", 1);
|
||||
}
|
||||
if( pTab->iPKey>=0 ){
|
||||
sqliteVdbeAddOp(v, OP_FileColumn, pTab->iPKey, 0);
|
||||
sqliteVdbeAddOp(v, OP_MustBeInt, 0, 0);
|
||||
}else{
|
||||
sqliteVdbeAddOp(v, OP_NewRecno, 0, 0);
|
||||
}
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
if( i==pTab->iPKey ){
|
||||
/* The integer primary key column is filled with NULL since its
|
||||
** value is always pulled from the record number */
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
}else{
|
||||
sqliteVdbeAddOp(v, OP_FileColumn, i, 0);
|
||||
}
|
||||
}
|
||||
sqliteGenerateConstraintChecks(pParse, pTab, 0, 0, pTab->iPKey>=0,
|
||||
0, onError, addr);
|
||||
sqliteCompleteInsertion(pParse, pTab, 0, 0, 0, 0, -1);
|
||||
if( (db->flags & SQLITE_CountRows)!=0 ){
|
||||
sqliteVdbeAddOp(v, OP_AddImm, 1, 0); /* Increment row count */
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, addr);
|
||||
sqliteVdbeResolveLabel(v, end);
|
||||
sqliteVdbeAddOp(v, OP_Noop, 0, 0);
|
||||
sqliteEndWriteOperation(pParse);
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
sqliteVdbeAddOp(v, OP_ColumnName, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, "rows inserted", P3_STATIC);
|
||||
sqliteVdbeAddOp(v, OP_Callback, 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
copy_cleanup:
|
||||
sqliteSrcListDelete(pTableName);
|
||||
sqliteFree(zFile);
|
||||
return;
|
||||
}
|
||||
396
sqlitebrowser/sqlitebrowser/sqlite_source/delete.c
Executable file
@@ -0,0 +1,396 @@
|
||||
/*
|
||||
** 2001 September 15
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle DELETE FROM statements.
|
||||
**
|
||||
** $Id: delete.c,v 1.1.1.1 2003-08-21 02:24:07 tabuleiro Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** Look up every table that is named in pSrc. If any table is not found,
|
||||
** add an error message to pParse->zErrMsg and return NULL. If all tables
|
||||
** are found, return a pointer to the last table.
|
||||
*/
|
||||
Table *sqliteSrcListLookup(Parse *pParse, SrcList *pSrc){
|
||||
Table *pTab = 0;
|
||||
int i;
|
||||
for(i=0; i<pSrc->nSrc; i++){
|
||||
const char *zTab = pSrc->a[i].zName;
|
||||
const char *zDb = pSrc->a[i].zDatabase;
|
||||
pTab = sqliteLocateTable(pParse, zTab, zDb);
|
||||
pSrc->a[i].pTab = pTab;
|
||||
}
|
||||
return pTab;
|
||||
}
|
||||
|
||||
/*
|
||||
** Check to make sure the given table is writable. If it is not
|
||||
** writable, generate an error message and return 1. If it is
|
||||
** writable return 0;
|
||||
*/
|
||||
int sqliteIsReadOnly(Parse *pParse, Table *pTab, int viewOk){
|
||||
if( pTab->readOnly ){
|
||||
sqliteErrorMsg(pParse, "table %s may not be modified", pTab->zName);
|
||||
return 1;
|
||||
}
|
||||
if( !viewOk && pTab->pSelect ){
|
||||
sqliteErrorMsg(pParse, "cannot modify %s because it is a view",pTab->zName);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Process a DELETE FROM statement.
|
||||
*/
|
||||
void sqliteDeleteFrom(
|
||||
Parse *pParse, /* The parser context */
|
||||
SrcList *pTabList, /* The table from which we should delete things */
|
||||
Expr *pWhere /* The WHERE clause. May be null */
|
||||
){
|
||||
Vdbe *v; /* The virtual database engine */
|
||||
Table *pTab; /* The table from which records will be deleted */
|
||||
const char *zDb; /* Name of database holding pTab */
|
||||
int end, addr; /* A couple addresses of generated code */
|
||||
int i; /* Loop counter */
|
||||
WhereInfo *pWInfo; /* Information about the WHERE clause */
|
||||
Index *pIdx; /* For looping over indices of the table */
|
||||
int iCur; /* VDBE Cursor number for pTab */
|
||||
sqlite *db; /* Main database structure */
|
||||
int isView; /* True if attempting to delete from a view */
|
||||
AuthContext sContext; /* Authorization context */
|
||||
|
||||
int row_triggers_exist = 0; /* True if any triggers exist */
|
||||
int before_triggers; /* True if there are BEFORE triggers */
|
||||
int after_triggers; /* True if there are AFTER triggers */
|
||||
int oldIdx = -1; /* Cursor for the OLD table of AFTER triggers */
|
||||
|
||||
sContext.pParse = 0;
|
||||
if( pParse->nErr || sqlite_malloc_failed ){
|
||||
pTabList = 0;
|
||||
goto delete_from_cleanup;
|
||||
}
|
||||
db = pParse->db;
|
||||
assert( pTabList->nSrc==1 );
|
||||
|
||||
/* Locate the table which we want to delete. This table has to be
|
||||
** put in an SrcList structure because some of the subroutines we
|
||||
** will be calling are designed to work with multiple tables and expect
|
||||
** an SrcList* parameter instead of just a Table* parameter.
|
||||
*/
|
||||
pTab = sqliteSrcListLookup(pParse, pTabList);
|
||||
if( pTab==0 ) goto delete_from_cleanup;
|
||||
before_triggers = sqliteTriggersExist(pParse, pTab->pTrigger,
|
||||
TK_DELETE, TK_BEFORE, TK_ROW, 0);
|
||||
after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger,
|
||||
TK_DELETE, TK_AFTER, TK_ROW, 0);
|
||||
row_triggers_exist = before_triggers || after_triggers;
|
||||
isView = pTab->pSelect!=0;
|
||||
if( sqliteIsReadOnly(pParse, pTab, before_triggers) ){
|
||||
goto delete_from_cleanup;
|
||||
}
|
||||
assert( pTab->iDb<db->nDb );
|
||||
zDb = db->aDb[pTab->iDb].zName;
|
||||
if( sqliteAuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){
|
||||
goto delete_from_cleanup;
|
||||
}
|
||||
|
||||
/* If pTab is really a view, make sure it has been initialized.
|
||||
*/
|
||||
if( isView && sqliteViewGetColumnNames(pParse, pTab) ){
|
||||
goto delete_from_cleanup;
|
||||
}
|
||||
|
||||
/* Allocate a cursor used to store the old.* data for a trigger.
|
||||
*/
|
||||
if( row_triggers_exist ){
|
||||
oldIdx = pParse->nTab++;
|
||||
}
|
||||
|
||||
/* Resolve the column names in all the expressions.
|
||||
*/
|
||||
assert( pTabList->nSrc==1 );
|
||||
iCur = pTabList->a[0].iCursor = pParse->nTab++;
|
||||
if( pWhere ){
|
||||
if( sqliteExprResolveIds(pParse, pTabList, 0, pWhere) ){
|
||||
goto delete_from_cleanup;
|
||||
}
|
||||
if( sqliteExprCheck(pParse, pWhere, 0, 0) ){
|
||||
goto delete_from_cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/* Start the view context
|
||||
*/
|
||||
if( isView ){
|
||||
sqliteAuthContextPush(pParse, &sContext, pTab->zName);
|
||||
}
|
||||
|
||||
/* Begin generating code.
|
||||
*/
|
||||
v = sqliteGetVdbe(pParse);
|
||||
if( v==0 ){
|
||||
goto delete_from_cleanup;
|
||||
}
|
||||
sqliteBeginWriteOperation(pParse, row_triggers_exist, pTab->iDb);
|
||||
|
||||
/* If we are trying to delete from a view, construct that view into
|
||||
** a temporary table.
|
||||
*/
|
||||
if( isView ){
|
||||
Select *pView = sqliteSelectDup(pTab->pSelect);
|
||||
sqliteSelect(pParse, pView, SRT_TempTable, iCur, 0, 0, 0);
|
||||
sqliteSelectDelete(pView);
|
||||
}
|
||||
|
||||
/* Initialize the counter of the number of rows deleted, if
|
||||
** we are counting rows.
|
||||
*/
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
sqliteVdbeAddOp(v, OP_Integer, 0, 0);
|
||||
}
|
||||
|
||||
/* Special case: A DELETE without a WHERE clause deletes everything.
|
||||
** It is easier just to erase the whole table. Note, however, that
|
||||
** this means that the row change count will be incorrect.
|
||||
*/
|
||||
if( pWhere==0 && !row_triggers_exist ){
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
/* If counting rows deleted, just count the total number of
|
||||
** entries in the table. */
|
||||
int endOfLoop = sqliteVdbeMakeLabel(v);
|
||||
int addr;
|
||||
if( !isView ){
|
||||
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenRead, iCur, pTab->tnum);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_Rewind, iCur, sqliteVdbeCurrentAddr(v)+2);
|
||||
addr = sqliteVdbeAddOp(v, OP_AddImm, 1, 0);
|
||||
sqliteVdbeAddOp(v, OP_Next, iCur, addr);
|
||||
sqliteVdbeResolveLabel(v, endOfLoop);
|
||||
sqliteVdbeAddOp(v, OP_Close, iCur, 0);
|
||||
}
|
||||
if( !isView ){
|
||||
sqliteVdbeAddOp(v, OP_Clear, pTab->tnum, pTab->iDb);
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
sqliteVdbeAddOp(v, OP_Clear, pIdx->tnum, pIdx->iDb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* The usual case: There is a WHERE clause so we have to scan through
|
||||
** the table an pick which records to delete.
|
||||
*/
|
||||
else{
|
||||
/* Begin the database scan
|
||||
*/
|
||||
pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 1, 0);
|
||||
if( pWInfo==0 ) goto delete_from_cleanup;
|
||||
|
||||
/* Remember the key of every item to be deleted.
|
||||
*/
|
||||
sqliteVdbeAddOp(v, OP_ListWrite, 0, 0);
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
sqliteVdbeAddOp(v, OP_AddImm, 1, 0);
|
||||
}
|
||||
|
||||
/* End the database scan loop.
|
||||
*/
|
||||
sqliteWhereEnd(pWInfo);
|
||||
|
||||
/* Open the pseudo-table used to store OLD if there are triggers.
|
||||
*/
|
||||
if( row_triggers_exist ){
|
||||
sqliteVdbeAddOp(v, OP_OpenPseudo, oldIdx, 0);
|
||||
}
|
||||
|
||||
/* Delete every item whose key was written to the list during the
|
||||
** database scan. We have to delete items after the scan is complete
|
||||
** because deleting an item can change the scan order.
|
||||
*/
|
||||
sqliteVdbeAddOp(v, OP_ListRewind, 0, 0);
|
||||
end = sqliteVdbeMakeLabel(v);
|
||||
|
||||
/* This is the beginning of the delete loop when there are
|
||||
** row triggers.
|
||||
*/
|
||||
if( row_triggers_exist ){
|
||||
addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end);
|
||||
sqliteVdbeAddOp(v, OP_Dup, 0, 0);
|
||||
if( !isView ){
|
||||
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenRead, iCur, pTab->tnum);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_MoveTo, iCur, 0);
|
||||
|
||||
sqliteVdbeAddOp(v, OP_Recno, iCur, 0);
|
||||
sqliteVdbeAddOp(v, OP_RowData, iCur, 0);
|
||||
sqliteVdbeAddOp(v, OP_PutIntKey, oldIdx, 0);
|
||||
if( !isView ){
|
||||
sqliteVdbeAddOp(v, OP_Close, iCur, 0);
|
||||
}
|
||||
|
||||
sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1,
|
||||
oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
|
||||
addr);
|
||||
}
|
||||
|
||||
if( !isView ){
|
||||
/* Open cursors for the table we are deleting from and all its
|
||||
** indices. If there are row triggers, this happens inside the
|
||||
** OP_ListRead loop because the cursor have to all be closed
|
||||
** before the trigger fires. If there are no row triggers, the
|
||||
** cursors are opened only once on the outside the loop.
|
||||
*/
|
||||
pParse->nTab = iCur + 1;
|
||||
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenWrite, iCur, pTab->tnum);
|
||||
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||
sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenWrite, pParse->nTab++, pIdx->tnum);
|
||||
}
|
||||
|
||||
/* This is the beginning of the delete loop when there are no
|
||||
** row triggers */
|
||||
if( !row_triggers_exist ){
|
||||
addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end);
|
||||
}
|
||||
|
||||
/* Delete the row */
|
||||
sqliteGenerateRowDelete(db, v, pTab, iCur, pParse->trigStack==0);
|
||||
}
|
||||
|
||||
/* If there are row triggers, close all cursors then invoke
|
||||
** the AFTER triggers
|
||||
*/
|
||||
if( row_triggers_exist ){
|
||||
if( !isView ){
|
||||
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||
sqliteVdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_Close, iCur, 0);
|
||||
}
|
||||
sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1,
|
||||
oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
|
||||
addr);
|
||||
}
|
||||
|
||||
/* End of the delete loop */
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, addr);
|
||||
sqliteVdbeResolveLabel(v, end);
|
||||
sqliteVdbeAddOp(v, OP_ListReset, 0, 0);
|
||||
|
||||
/* Close the cursors after the loop if there are no row triggers */
|
||||
if( !row_triggers_exist ){
|
||||
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||
sqliteVdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_Close, iCur, 0);
|
||||
pParse->nTab = iCur;
|
||||
}
|
||||
}
|
||||
sqliteEndWriteOperation(pParse);
|
||||
|
||||
/*
|
||||
** Return the number of rows that were deleted.
|
||||
*/
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
sqliteVdbeAddOp(v, OP_ColumnName, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, "rows deleted", P3_STATIC);
|
||||
sqliteVdbeAddOp(v, OP_Callback, 1, 0);
|
||||
}
|
||||
|
||||
delete_from_cleanup:
|
||||
sqliteAuthContextPop(&sContext);
|
||||
sqliteSrcListDelete(pTabList);
|
||||
sqliteExprDelete(pWhere);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine generates VDBE code that causes a single row of a
|
||||
** single table to be deleted.
|
||||
**
|
||||
** The VDBE must be in a particular state when this routine is called.
|
||||
** These are the requirements:
|
||||
**
|
||||
** 1. A read/write cursor pointing to pTab, the table containing the row
|
||||
** to be deleted, must be opened as cursor number "base".
|
||||
**
|
||||
** 2. Read/write cursors for all indices of pTab must be open as
|
||||
** cursor number base+i for the i-th index.
|
||||
**
|
||||
** 3. The record number of the row to be deleted must be on the top
|
||||
** of the stack.
|
||||
**
|
||||
** This routine pops the top of the stack to remove the record number
|
||||
** and then generates code to remove both the table record and all index
|
||||
** entries that point to that record.
|
||||
*/
|
||||
void sqliteGenerateRowDelete(
|
||||
sqlite *db, /* The database containing the index */
|
||||
Vdbe *v, /* Generate code into this VDBE */
|
||||
Table *pTab, /* Table containing the row to be deleted */
|
||||
int iCur, /* Cursor number for the table */
|
||||
int count /* Increment the row change counter */
|
||||
){
|
||||
int addr;
|
||||
addr = sqliteVdbeAddOp(v, OP_NotExists, iCur, 0);
|
||||
sqliteGenerateRowIndexDelete(db, v, pTab, iCur, 0);
|
||||
sqliteVdbeAddOp(v, OP_Delete, iCur, count);
|
||||
sqliteVdbeChangeP2(v, addr, sqliteVdbeCurrentAddr(v));
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine generates VDBE code that causes the deletion of all
|
||||
** index entries associated with a single row of a single table.
|
||||
**
|
||||
** The VDBE must be in a particular state when this routine is called.
|
||||
** These are the requirements:
|
||||
**
|
||||
** 1. A read/write cursor pointing to pTab, the table containing the row
|
||||
** to be deleted, must be opened as cursor number "iCur".
|
||||
**
|
||||
** 2. Read/write cursors for all indices of pTab must be open as
|
||||
** cursor number iCur+i for the i-th index.
|
||||
**
|
||||
** 3. The "iCur" cursor must be pointing to the row that is to be
|
||||
** deleted.
|
||||
*/
|
||||
void sqliteGenerateRowIndexDelete(
|
||||
sqlite *db, /* The database containing the index */
|
||||
Vdbe *v, /* Generate code into this VDBE */
|
||||
Table *pTab, /* Table containing the row to be deleted */
|
||||
int iCur, /* Cursor number for the table */
|
||||
char *aIdxUsed /* Only delete if aIdxUsed!=0 && aIdxUsed[i]!=0 */
|
||||
){
|
||||
int i;
|
||||
Index *pIdx;
|
||||
|
||||
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||
int j;
|
||||
if( aIdxUsed!=0 && aIdxUsed[i-1]==0 ) continue;
|
||||
sqliteVdbeAddOp(v, OP_Recno, iCur, 0);
|
||||
for(j=0; j<pIdx->nColumn; j++){
|
||||
int idx = pIdx->aiColumn[j];
|
||||
if( idx==pTab->iPKey ){
|
||||
sqliteVdbeAddOp(v, OP_Dup, j, 0);
|
||||
}else{
|
||||
sqliteVdbeAddOp(v, OP_Column, iCur, idx);
|
||||
}
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0);
|
||||
if( db->file_format>=4 ) sqliteAddIdxKeyType(v, pIdx);
|
||||
sqliteVdbeAddOp(v, OP_IdxDelete, iCur+i, 0);
|
||||
}
|
||||
}
|
||||
246
sqlitebrowser/sqlitebrowser/sqlite_source/encode.c
Executable file
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
** 2002 April 25
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains helper routines used to translate binary data into
|
||||
** a null-terminated string (suitable for use in SQLite) and back again.
|
||||
** These are convenience routines for use by people who want to store binary
|
||||
** data in an SQLite database. The code in this file is not used by any other
|
||||
** part of the SQLite library.
|
||||
**
|
||||
** $Id: encode.c,v 1.1.1.1 2003-08-21 02:24:07 tabuleiro Exp $
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "encode.h"
|
||||
|
||||
/*
|
||||
** How This Encoder Works
|
||||
**
|
||||
** The output is allowed to contain any character except 0x27 (') and
|
||||
** 0x00. This is accomplished by using an escape character to encode
|
||||
** 0x27 and 0x00 as a two-byte sequence. The escape character is always
|
||||
** 0x01. An 0x00 is encoded as the two byte sequence 0x01 0x01. The
|
||||
** 0x27 character is encoded as the two byte sequence 0x01 0x03. Finally,
|
||||
** the escape character itself is encoded as the two-character sequence
|
||||
** 0x01 0x02.
|
||||
**
|
||||
** To summarize, the encoder works by using an escape sequences as follows:
|
||||
**
|
||||
** 0x00 -> 0x01 0x01
|
||||
** 0x01 -> 0x01 0x02
|
||||
** 0x27 -> 0x01 0x03
|
||||
**
|
||||
** If that were all the encoder did, it would work, but in certain cases
|
||||
** it could double the size of the encoded string. For example, to
|
||||
** encode a string of 100 0x27 character would require 100 instances of
|
||||
** the 0x01 0x03 escape sequence resulting in a 200-character output.
|
||||
** We would prefer to keep the size of the encoded string smaller than
|
||||
** this.
|
||||
**
|
||||
** To minimize the encoding size, we first add a fixed offset value to each
|
||||
** byte in the sequence. The addition is module 256. (That is to say, if
|
||||
** the sum of the original character value and the offset exceeds 256, then
|
||||
** the higher order bits are truncated.) The offset is chosen to minimize
|
||||
** the number of characters in the string that need to be escaped. For
|
||||
** example, in the case above where the string was composed of 100 0x27
|
||||
** characters, the offset might be 0x01. Each of the 0x27 characters would
|
||||
** then be converted into an 0x28 character which would not need to be
|
||||
** escaped at all and so the 100 character input string would be converted
|
||||
** into just 100 characters of output. Actually 101 characters of output -
|
||||
** we have to record the offset used as the first byte in the sequence so
|
||||
** that the string can be decoded. Since the offset value is stored as
|
||||
** part of the output string and the output string is not allowed to contain
|
||||
** characters 0x00 or 0x27, the offset cannot be 0x00 or 0x27.
|
||||
**
|
||||
** Here, then, are the encoding steps:
|
||||
**
|
||||
** (1) Choose an offset value and make it the first character of
|
||||
** output.
|
||||
**
|
||||
** (2) Copy each input character into the output buffer, one by
|
||||
** one, adding the offset value as you copy.
|
||||
**
|
||||
** (3) If the value of an input character plus offset is 0x00, replace
|
||||
** that one character by the two-character sequence 0x01 0x01.
|
||||
** If the sum is 0x01, replace it with 0x01 0x02. If the sum
|
||||
** is 0x27, replace it with 0x01 0x03.
|
||||
**
|
||||
** (4) Put a 0x00 terminator at the end of the output.
|
||||
**
|
||||
** Decoding is obvious:
|
||||
**
|
||||
** (5) Copy encoded characters except the first into the decode
|
||||
** buffer. Set the first encoded character aside for use as
|
||||
** the offset in step 7 below.
|
||||
**
|
||||
** (6) Convert each 0x01 0x01 sequence into a single character 0x00.
|
||||
** Convert 0x01 0x02 into 0x01. Convert 0x01 0x03 into 0x27.
|
||||
**
|
||||
** (7) Subtract the offset value that was the first character of
|
||||
** the encoded buffer from all characters in the output buffer.
|
||||
**
|
||||
** The only tricky part is step (1) - how to compute an offset value to
|
||||
** minimize the size of the output buffer. This is accomplished to testing
|
||||
** all offset values and picking the one that results in the fewest number
|
||||
** of escapes. To do that, we first scan the entire input and count the
|
||||
** number of occurances of each character value in the input. Suppose
|
||||
** the number of 0x00 characters is N(0), the number of occurances of 0x01
|
||||
** is N(1), and so forth up to the number of occurances of 0xff is N(256).
|
||||
** An offset of 0 is not allowed so we don't have to test it. The number
|
||||
** of escapes required for an offset of 1 is N(1)+N(2)+N(40). The number
|
||||
** of escapes required for an offset of 2 is N(2)+N(3)+N(41). And so forth.
|
||||
** In this way we find the offset that gives the minimum number of escapes,
|
||||
** and thus minimizes the length of the output string.
|
||||
*/
|
||||
|
||||
/*
|
||||
** Encode a binary buffer "in" of size n bytes so that it contains
|
||||
** no instances of characters '\'' or '\000'. The output is
|
||||
** null-terminated and can be used as a string value in an INSERT
|
||||
** or UPDATE statement. Use sqlite_decode_binary() to convert the
|
||||
** string back into its original binary.
|
||||
**
|
||||
** The result is written into a preallocated output buffer "out".
|
||||
** "out" must be able to hold at least (256*n + 1262)/253 bytes.
|
||||
** In other words, the output will be expanded by as much as 3
|
||||
** bytes for every 253 bytes of input plus 2 bytes of fixed overhead.
|
||||
** (This is approximately 2 + 1.019*n or about a 2% size increase.)
|
||||
**
|
||||
** The return value is the number of characters in the encoded
|
||||
** string, excluding the "\000" terminator.
|
||||
*/
|
||||
int sqlite_encode_binary(const unsigned char *in, int n, unsigned char *out){
|
||||
int i, j, e, m;
|
||||
int cnt[256];
|
||||
if( n<=0 ){
|
||||
out[0] = 'x';
|
||||
out[1] = 0;
|
||||
return 1;
|
||||
}
|
||||
memset(cnt, 0, sizeof(cnt));
|
||||
for(i=n-1; i>=0; i--){ cnt[in[i]]++; }
|
||||
m = n;
|
||||
for(i=1; i<256; i++){
|
||||
int sum;
|
||||
if( i=='\'' ) continue;
|
||||
sum = cnt[i] + cnt[(i+1)&0xff] + cnt[(i+'\'')&0xff];
|
||||
if( sum<m ){
|
||||
m = sum;
|
||||
e = i;
|
||||
if( m==0 ) break;
|
||||
}
|
||||
}
|
||||
out[0] = e;
|
||||
j = 1;
|
||||
for(i=0; i<n; i++){
|
||||
int c = (in[i] - e)&0xff;
|
||||
if( c==0 ){
|
||||
out[j++] = 1;
|
||||
out[j++] = 1;
|
||||
}else if( c==1 ){
|
||||
out[j++] = 1;
|
||||
out[j++] = 2;
|
||||
}else if( c=='\'' ){
|
||||
out[j++] = 1;
|
||||
out[j++] = 3;
|
||||
}else{
|
||||
out[j++] = c;
|
||||
}
|
||||
}
|
||||
out[j] = 0;
|
||||
return j;
|
||||
}
|
||||
|
||||
/*
|
||||
** Decode the string "in" into binary data and write it into "out".
|
||||
** This routine reverses the encoded created by sqlite_encode_binary().
|
||||
** The output will always be a few bytes less than the input. The number
|
||||
** of bytes of output is returned. If the input is not a well-formed
|
||||
** encoding, -1 is returned.
|
||||
**
|
||||
** The "in" and "out" parameters may point to the same buffer in order
|
||||
** to decode a string in place.
|
||||
*/
|
||||
int sqlite_decode_binary(const unsigned char *in, unsigned char *out){
|
||||
int i, c, e;
|
||||
e = *(in++);
|
||||
i = 0;
|
||||
while( (c = *(in++))!=0 ){
|
||||
if( c==1 ){
|
||||
c = *(in++);
|
||||
if( c==1 ){
|
||||
c = 0;
|
||||
}else if( c==2 ){
|
||||
c = 1;
|
||||
}else if( c==3 ){
|
||||
c = '\'';
|
||||
}else{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
out[i++] = (c + e)&0xff;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
#ifdef ENCODER_TEST
|
||||
/*
|
||||
** The subroutines above are not tested by the usual test suite. To test
|
||||
** these routines, compile just this one file with a -DENCODER_TEST=1 option
|
||||
** and run the result.
|
||||
*/
|
||||
int main(int argc, char **argv){
|
||||
int i, j, n, m, nOut;
|
||||
unsigned char in[30000];
|
||||
unsigned char out[33000];
|
||||
|
||||
for(i=0; i<sizeof(in); i++){
|
||||
printf("Test %d: ", i+1);
|
||||
n = rand() % (i+1);
|
||||
if( i%100==0 ){
|
||||
int k;
|
||||
for(j=k=0; j<n; j++){
|
||||
/* if( k==0 || k=='\'' ) k++; */
|
||||
in[j] = k;
|
||||
k = (k+1)&0xff;
|
||||
}
|
||||
}else{
|
||||
for(j=0; j<n; j++) in[j] = rand() & 0xff;
|
||||
}
|
||||
nOut = sqlite_encode_binary(in, n, out);
|
||||
if( nOut!=strlen(out) ){
|
||||
printf(" ERROR return value is %d instead of %d\n", nOut, strlen(out));
|
||||
exit(1);
|
||||
}
|
||||
m = (256*n + 1262)/253;
|
||||
printf("size %d->%d (max %d)", n, strlen(out)+1, m);
|
||||
if( strlen(out)+1>m ){
|
||||
printf(" ERROR output too big\n");
|
||||
exit(1);
|
||||
}
|
||||
for(j=0; out[j]; j++){
|
||||
if( out[j]=='\'' ){
|
||||
printf(" ERROR contains (')\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
j = sqlite_decode_binary(out, out);
|
||||
if( j!=n ){
|
||||
printf(" ERROR decode size %d\n", j);
|
||||
exit(1);
|
||||
}
|
||||
if( memcmp(in, out, n)!=0 ){
|
||||
printf(" ERROR decode mismatch\n");
|
||||
exit(1);
|
||||
}
|
||||
printf(" OK\n");
|
||||
}
|
||||
}
|
||||
#endif /* ENCODER_TEST */
|
||||
18
sqlitebrowser/sqlitebrowser/sqlite_source/encode.h
Executable file
@@ -0,0 +1,18 @@
|
||||
#ifndef _ENCODE_H_
|
||||
#define _ENCODE_H_
|
||||
|
||||
/*
|
||||
** Make sure we can call this stuff from C++.
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int sqlite_encode_binary(const unsigned char *in, int n, unsigned char *out);
|
||||
int sqlite_decode_binary(const unsigned char *in, unsigned char *out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* End of the 'extern "C"' block */
|
||||
#endif
|
||||
|
||||
#endif /* _ENCODE_H_ */
|
||||
1623
sqlitebrowser/sqlitebrowser/sqlite_source/expr.c
Executable file
573
sqlitebrowser/sqlitebrowser/sqlite_source/func.c
Executable file
@@ -0,0 +1,573 @@
|
||||
/*
|
||||
** 2002 February 23
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains the C functions that implement various SQL
|
||||
** functions of SQLite.
|
||||
**
|
||||
** There is only one exported symbol in this file - the function
|
||||
** sqliteRegisterBuildinFunctions() found at the bottom of the file.
|
||||
** All other code has file scope.
|
||||
**
|
||||
** $Id: func.c,v 1.1.1.1 2003-08-21 02:24:08 tabuleiro Exp $
|
||||
*/
|
||||
#include <ctype.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** Implementation of the non-aggregate min() and max() functions
|
||||
*/
|
||||
static void minFunc(sqlite_func *context, int argc, const char **argv){
|
||||
const char *zBest;
|
||||
int i;
|
||||
|
||||
if( argc==0 ) return;
|
||||
zBest = argv[0];
|
||||
if( zBest==0 ) return;
|
||||
for(i=1; i<argc; i++){
|
||||
if( argv[i]==0 ) return;
|
||||
if( sqliteCompare(argv[i], zBest)<0 ){
|
||||
zBest = argv[i];
|
||||
}
|
||||
}
|
||||
sqlite_set_result_string(context, zBest, -1);
|
||||
}
|
||||
static void maxFunc(sqlite_func *context, int argc, const char **argv){
|
||||
const char *zBest;
|
||||
int i;
|
||||
|
||||
if( argc==0 ) return;
|
||||
zBest = argv[0];
|
||||
if( zBest==0 ) return;
|
||||
for(i=1; i<argc; i++){
|
||||
if( argv[i]==0 ) return;
|
||||
if( sqliteCompare(argv[i], zBest)>0 ){
|
||||
zBest = argv[i];
|
||||
}
|
||||
}
|
||||
sqlite_set_result_string(context, zBest, -1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the length() function
|
||||
*/
|
||||
static void lengthFunc(sqlite_func *context, int argc, const char **argv){
|
||||
const char *z;
|
||||
int len;
|
||||
|
||||
assert( argc==1 );
|
||||
z = argv[0];
|
||||
if( z==0 ) return;
|
||||
#ifdef SQLITE_UTF8
|
||||
for(len=0; *z; z++){ if( (0xc0&*z)!=0x80 ) len++; }
|
||||
#else
|
||||
len = strlen(z);
|
||||
#endif
|
||||
sqlite_set_result_int(context, len);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the abs() function
|
||||
*/
|
||||
static void absFunc(sqlite_func *context, int argc, const char **argv){
|
||||
const char *z;
|
||||
assert( argc==1 );
|
||||
z = argv[0];
|
||||
if( z==0 ) return;
|
||||
if( z[0]=='-' && isdigit(z[1]) ) z++;
|
||||
sqlite_set_result_string(context, z, -1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the substr() function
|
||||
*/
|
||||
static void substrFunc(sqlite_func *context, int argc, const char **argv){
|
||||
const char *z;
|
||||
#ifdef SQLITE_UTF8
|
||||
const char *z2;
|
||||
int i;
|
||||
#endif
|
||||
int p1, p2, len;
|
||||
assert( argc==3 );
|
||||
z = argv[0];
|
||||
if( z==0 ) return;
|
||||
p1 = atoi(argv[1]?argv[1]:0);
|
||||
p2 = atoi(argv[2]?argv[2]:0);
|
||||
#ifdef SQLITE_UTF8
|
||||
for(len=0, z2=z; *z2; z2++){ if( (0xc0&*z2)!=0x80 ) len++; }
|
||||
#else
|
||||
len = strlen(z);
|
||||
#endif
|
||||
if( p1<0 ){
|
||||
p1 += len;
|
||||
if( p1<0 ){
|
||||
p2 += p1;
|
||||
p1 = 0;
|
||||
}
|
||||
}else if( p1>0 ){
|
||||
p1--;
|
||||
}
|
||||
if( p1+p2>len ){
|
||||
p2 = len-p1;
|
||||
}
|
||||
#ifdef SQLITE_UTF8
|
||||
for(i=0; i<p1; i++){
|
||||
assert( z[i] );
|
||||
if( (z[i]&0xc0)==0x80 ) p1++;
|
||||
}
|
||||
while( z[i] && (z[i]&0xc0)==0x80 ){ i++; p1++; }
|
||||
for(; i<p1+p2; i++){
|
||||
assert( z[i] );
|
||||
if( (z[i]&0xc0)==0x80 ) p2++;
|
||||
}
|
||||
while( z[i] && (z[i]&0xc0)==0x80 ){ i++; p2++; }
|
||||
#endif
|
||||
if( p2<0 ) p2 = 0;
|
||||
sqlite_set_result_string(context, &z[p1], p2);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the round() function
|
||||
*/
|
||||
static void roundFunc(sqlite_func *context, int argc, const char **argv){
|
||||
int n;
|
||||
double r;
|
||||
char zBuf[100];
|
||||
assert( argc==1 || argc==2 );
|
||||
if( argv[0]==0 || (argc==2 && argv[1]==0) ) return;
|
||||
n = argc==2 ? atoi(argv[1]) : 0;
|
||||
if( n>30 ) n = 30;
|
||||
if( n<0 ) n = 0;
|
||||
r = atof(argv[0]);
|
||||
sprintf(zBuf,"%.*f",n,r);
|
||||
sqlite_set_result_string(context, zBuf, -1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the upper() and lower() SQL functions.
|
||||
*/
|
||||
static void upperFunc(sqlite_func *context, int argc, const char **argv){
|
||||
char *z;
|
||||
int i;
|
||||
if( argc<1 || argv[0]==0 ) return;
|
||||
z = sqlite_set_result_string(context, argv[0], -1);
|
||||
if( z==0 ) return;
|
||||
for(i=0; z[i]; i++){
|
||||
if( islower(z[i]) ) z[i] = toupper(z[i]);
|
||||
}
|
||||
}
|
||||
static void lowerFunc(sqlite_func *context, int argc, const char **argv){
|
||||
char *z;
|
||||
int i;
|
||||
if( argc<1 || argv[0]==0 ) return;
|
||||
z = sqlite_set_result_string(context, argv[0], -1);
|
||||
if( z==0 ) return;
|
||||
for(i=0; z[i]; i++){
|
||||
if( isupper(z[i]) ) z[i] = tolower(z[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the IFNULL(), NVL(), and COALESCE() functions.
|
||||
** All three do the same thing. They return the first argument
|
||||
** non-NULL argument.
|
||||
*/
|
||||
static void ifnullFunc(sqlite_func *context, int argc, const char **argv){
|
||||
int i;
|
||||
for(i=0; i<argc; i++){
|
||||
if( argv[i] ){
|
||||
sqlite_set_result_string(context, argv[i], -1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of random(). Return a random integer.
|
||||
*/
|
||||
static void randomFunc(sqlite_func *context, int argc, const char **argv){
|
||||
sqlite_set_result_int(context, sqliteRandomInteger());
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the last_insert_rowid() SQL function. The return
|
||||
** value is the same as the sqlite_last_insert_rowid() API function.
|
||||
*/
|
||||
static void last_insert_rowid(sqlite_func *context, int arg, const char **argv){
|
||||
sqlite *db = sqlite_user_data(context);
|
||||
sqlite_set_result_int(context, sqlite_last_insert_rowid(db));
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the like() SQL function. This function implements
|
||||
** the build-in LIKE operator. The first argument to the function is the
|
||||
** string and the second argument is the pattern. So, the SQL statements:
|
||||
**
|
||||
** A LIKE B
|
||||
**
|
||||
** is implemented as like(A,B).
|
||||
*/
|
||||
static void likeFunc(sqlite_func *context, int arg, const char **argv){
|
||||
if( argv[0]==0 || argv[1]==0 ) return;
|
||||
sqlite_set_result_int(context, sqliteLikeCompare((const unsigned char *)argv[0], (const unsigned char *)argv[1]));
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the glob() SQL function. This function implements
|
||||
** the build-in GLOB operator. The first argument to the function is the
|
||||
** string and the second argument is the pattern. So, the SQL statements:
|
||||
**
|
||||
** A GLOB B
|
||||
**
|
||||
** is implemented as glob(A,B).
|
||||
*/
|
||||
static void globFunc(sqlite_func *context, int arg, const char **argv){
|
||||
if( argv[0]==0 || argv[1]==0 ) return;
|
||||
sqlite_set_result_int(context, sqliteGlobCompare((const unsigned char *)argv[0], (const unsigned char *)argv[1]));
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the NULLIF(x,y) function. The result is the first
|
||||
** argument if the arguments are different. The result is NULL if the
|
||||
** arguments are equal to each other.
|
||||
*/
|
||||
static void nullifFunc(sqlite_func *context, int argc, const char **argv){
|
||||
if( argv[0]!=0 && sqliteCompare(argv[0],argv[1])!=0 ){
|
||||
sqlite_set_result_string(context, argv[0], -1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the VERSION(*) function. The result is the version
|
||||
** of the SQLite library that is running.
|
||||
*/
|
||||
static void versionFunc(sqlite_func *context, int argc, const char **argv){
|
||||
sqlite_set_result_string(context, sqlite_version, -1);
|
||||
}
|
||||
|
||||
#ifdef SQLITE_SOUNDEX
|
||||
/*
|
||||
** Compute the soundex encoding of a word.
|
||||
*/
|
||||
static void soundexFunc(sqlite_func *context, int argc, const char **argv){
|
||||
char zResult[8];
|
||||
const char *zIn;
|
||||
int i, j;
|
||||
static const unsigned char iCode[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0,
|
||||
1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0,
|
||||
1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0,
|
||||
};
|
||||
assert( argc==1 );
|
||||
zIn = argv[0];
|
||||
for(i=0; zIn[i] && !isalpha(zIn[i]); i++){}
|
||||
if( zIn[i] ){
|
||||
zResult[0] = toupper(zIn[i]);
|
||||
for(j=1; j<4 && zIn[i]; i++){
|
||||
int code = iCode[zIn[i]&0x7f];
|
||||
if( code>0 ){
|
||||
zResult[j++] = code + '0';
|
||||
}
|
||||
}
|
||||
while( j<4 ){
|
||||
zResult[j++] = '0';
|
||||
}
|
||||
zResult[j] = 0;
|
||||
sqlite_set_result_string(context, zResult, 4);
|
||||
}else{
|
||||
sqlite_set_result_string(context, "?000", 4);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
/*
|
||||
** This function generates a string of random characters. Used for
|
||||
** generating test data.
|
||||
*/
|
||||
static void randStr(sqlite_func *context, int argc, const char **argv){
|
||||
static const char zSrc[] =
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"0123456789"
|
||||
".-!,:*^+=_|?/<> ";
|
||||
int iMin, iMax, n, r, i;
|
||||
char zBuf[1000];
|
||||
if( argc>=1 ){
|
||||
iMin = atoi(argv[0]);
|
||||
if( iMin<0 ) iMin = 0;
|
||||
if( iMin>=sizeof(zBuf) ) iMin = sizeof(zBuf)-1;
|
||||
}else{
|
||||
iMin = 1;
|
||||
}
|
||||
if( argc>=2 ){
|
||||
iMax = atoi(argv[1]);
|
||||
if( iMax<iMin ) iMax = iMin;
|
||||
if( iMax>=sizeof(zBuf) ) iMax = sizeof(zBuf);
|
||||
}else{
|
||||
iMax = 50;
|
||||
}
|
||||
n = iMin;
|
||||
if( iMax>iMin ){
|
||||
r = sqliteRandomInteger() & 0x7fffffff;
|
||||
n += r%(iMax + 1 - iMin);
|
||||
}
|
||||
r = 0;
|
||||
for(i=0; i<n; i++){
|
||||
r = (r + sqliteRandomByte())% (sizeof(zSrc)-1);
|
||||
zBuf[i] = zSrc[r];
|
||||
}
|
||||
zBuf[n] = 0;
|
||||
sqlite_set_result_string(context, zBuf, n);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** An instance of the following structure holds the context of a
|
||||
** sum() or avg() aggregate computation.
|
||||
*/
|
||||
typedef struct SumCtx SumCtx;
|
||||
struct SumCtx {
|
||||
double sum; /* Sum of terms */
|
||||
int cnt; /* Number of elements summed */
|
||||
};
|
||||
|
||||
/*
|
||||
** Routines used to compute the sum or average.
|
||||
*/
|
||||
static void sumStep(sqlite_func *context, int argc, const char **argv){
|
||||
SumCtx *p;
|
||||
if( argc<1 ) return;
|
||||
p = sqlite_aggregate_context(context, sizeof(*p));
|
||||
if( p && argv[0] ){
|
||||
p->sum += atof(argv[0]);
|
||||
p->cnt++;
|
||||
}
|
||||
}
|
||||
static void sumFinalize(sqlite_func *context){
|
||||
SumCtx *p;
|
||||
p = sqlite_aggregate_context(context, sizeof(*p));
|
||||
sqlite_set_result_double(context, p ? p->sum : 0.0);
|
||||
}
|
||||
static void avgFinalize(sqlite_func *context){
|
||||
SumCtx *p;
|
||||
p = sqlite_aggregate_context(context, sizeof(*p));
|
||||
if( p && p->cnt>0 ){
|
||||
sqlite_set_result_double(context, p->sum/(double)p->cnt);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** An instance of the following structure holds the context of a
|
||||
** variance or standard deviation computation.
|
||||
*/
|
||||
typedef struct StdDevCtx StdDevCtx;
|
||||
struct StdDevCtx {
|
||||
double sum; /* Sum of terms */
|
||||
double sum2; /* Sum of the squares of terms */
|
||||
int cnt; /* Number of terms counted */
|
||||
};
|
||||
|
||||
#if 0 /* Omit because math library is required */
|
||||
/*
|
||||
** Routines used to compute the standard deviation as an aggregate.
|
||||
*/
|
||||
static void stdDevStep(sqlite_func *context, int argc, const char **argv){
|
||||
StdDevCtx *p;
|
||||
double x;
|
||||
if( argc<1 ) return;
|
||||
p = sqlite_aggregate_context(context, sizeof(*p));
|
||||
if( p && argv[0] ){
|
||||
x = atof(argv[0]);
|
||||
p->sum += x;
|
||||
p->sum2 += x*x;
|
||||
p->cnt++;
|
||||
}
|
||||
}
|
||||
static void stdDevFinalize(sqlite_func *context){
|
||||
double rN = sqlite_aggregate_count(context);
|
||||
StdDevCtx *p = sqlite_aggregate_context(context, sizeof(*p));
|
||||
if( p && p->cnt>1 ){
|
||||
double rCnt = cnt;
|
||||
sqlite_set_result_double(context,
|
||||
sqrt((p->sum2 - p->sum*p->sum/rCnt)/(rCnt-1.0)));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The following structure keeps track of state information for the
|
||||
** count() aggregate function.
|
||||
*/
|
||||
typedef struct CountCtx CountCtx;
|
||||
struct CountCtx {
|
||||
int n;
|
||||
};
|
||||
|
||||
/*
|
||||
** Routines to implement the count() aggregate function.
|
||||
*/
|
||||
static void countStep(sqlite_func *context, int argc, const char **argv){
|
||||
CountCtx *p;
|
||||
p = sqlite_aggregate_context(context, sizeof(*p));
|
||||
if( (argc==0 || argv[0]) && p ){
|
||||
p->n++;
|
||||
}
|
||||
}
|
||||
static void countFinalize(sqlite_func *context){
|
||||
CountCtx *p;
|
||||
p = sqlite_aggregate_context(context, sizeof(*p));
|
||||
sqlite_set_result_int(context, p ? p->n : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function tracks state information for the min() and max()
|
||||
** aggregate functions.
|
||||
*/
|
||||
typedef struct MinMaxCtx MinMaxCtx;
|
||||
struct MinMaxCtx {
|
||||
char *z; /* The best so far */
|
||||
char zBuf[28]; /* Space that can be used for storage */
|
||||
};
|
||||
|
||||
/*
|
||||
** Routines to implement min() and max() aggregate functions.
|
||||
*/
|
||||
static void minStep(sqlite_func *context, int argc, const char **argv){
|
||||
MinMaxCtx *p;
|
||||
p = sqlite_aggregate_context(context, sizeof(*p));
|
||||
if( p==0 || argc<1 || argv[0]==0 ) return;
|
||||
if( p->z==0 || sqliteCompare(argv[0],p->z)<0 ){
|
||||
int len;
|
||||
if( p->z && p->z!=p->zBuf ){
|
||||
sqliteFree(p->z);
|
||||
}
|
||||
len = strlen(argv[0]);
|
||||
if( len < sizeof(p->zBuf) ){
|
||||
p->z = p->zBuf;
|
||||
}else{
|
||||
p->z = sqliteMalloc( len+1 );
|
||||
if( p->z==0 ) return;
|
||||
}
|
||||
strcpy(p->z, argv[0]);
|
||||
}
|
||||
}
|
||||
static void maxStep(sqlite_func *context, int argc, const char **argv){
|
||||
MinMaxCtx *p;
|
||||
p = sqlite_aggregate_context(context, sizeof(*p));
|
||||
if( p==0 || argc<1 || argv[0]==0 ) return;
|
||||
if( p->z==0 || sqliteCompare(argv[0],p->z)>0 ){
|
||||
int len;
|
||||
if( p->z && p->z!=p->zBuf ){
|
||||
sqliteFree(p->z);
|
||||
}
|
||||
len = strlen(argv[0]);
|
||||
if( len < sizeof(p->zBuf) ){
|
||||
p->z = p->zBuf;
|
||||
}else{
|
||||
p->z = sqliteMalloc( len+1 );
|
||||
if( p->z==0 ) return;
|
||||
}
|
||||
strcpy(p->z, argv[0]);
|
||||
}
|
||||
}
|
||||
static void minMaxFinalize(sqlite_func *context){
|
||||
MinMaxCtx *p;
|
||||
p = sqlite_aggregate_context(context, sizeof(*p));
|
||||
if( p && p->z ){
|
||||
sqlite_set_result_string(context, p->z, strlen(p->z));
|
||||
}
|
||||
if( p && p->z && p->z!=p->zBuf ){
|
||||
sqliteFree(p->z);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This function registered all of the above C functions as SQL
|
||||
** functions. This should be the only routine in this file with
|
||||
** external linkage.
|
||||
*/
|
||||
void sqliteRegisterBuiltinFunctions(sqlite *db){
|
||||
static struct {
|
||||
char *zName;
|
||||
int nArg;
|
||||
int dataType;
|
||||
void (*xFunc)(sqlite_func*,int,const char**);
|
||||
} aFuncs[] = {
|
||||
{ "min", -1, SQLITE_ARGS, minFunc },
|
||||
{ "min", 0, 0, 0 },
|
||||
{ "max", -1, SQLITE_ARGS, maxFunc },
|
||||
{ "max", 0, 0, 0 },
|
||||
{ "length", 1, SQLITE_NUMERIC, lengthFunc },
|
||||
{ "substr", 3, SQLITE_TEXT, substrFunc },
|
||||
{ "abs", 1, SQLITE_NUMERIC, absFunc },
|
||||
{ "round", 1, SQLITE_NUMERIC, roundFunc },
|
||||
{ "round", 2, SQLITE_NUMERIC, roundFunc },
|
||||
{ "upper", 1, SQLITE_TEXT, upperFunc },
|
||||
{ "lower", 1, SQLITE_TEXT, lowerFunc },
|
||||
{ "coalesce", -1, SQLITE_ARGS, ifnullFunc },
|
||||
{ "coalesce", 0, 0, 0 },
|
||||
{ "coalesce", 1, 0, 0 },
|
||||
{ "ifnull", 2, SQLITE_ARGS, ifnullFunc },
|
||||
{ "random", -1, SQLITE_NUMERIC, randomFunc },
|
||||
{ "like", 2, SQLITE_NUMERIC, likeFunc },
|
||||
{ "glob", 2, SQLITE_NUMERIC, globFunc },
|
||||
{ "nullif", 2, SQLITE_ARGS, nullifFunc },
|
||||
{ "sqlite_version",0,SQLITE_TEXT, versionFunc},
|
||||
#ifdef SQLITE_SOUNDEX
|
||||
{ "soundex", 1, SQLITE_TEXT, soundexFunc},
|
||||
#endif
|
||||
#ifdef SQLITE_TEST
|
||||
{ "randstr", 2, SQLITE_TEXT, randStr },
|
||||
#endif
|
||||
};
|
||||
static struct {
|
||||
char *zName;
|
||||
int nArg;
|
||||
int dataType;
|
||||
void (*xStep)(sqlite_func*,int,const char**);
|
||||
void (*xFinalize)(sqlite_func*);
|
||||
} aAggs[] = {
|
||||
{ "min", 1, 0, minStep, minMaxFinalize },
|
||||
{ "max", 1, 0, maxStep, minMaxFinalize },
|
||||
{ "sum", 1, SQLITE_NUMERIC, sumStep, sumFinalize },
|
||||
{ "avg", 1, SQLITE_NUMERIC, sumStep, avgFinalize },
|
||||
{ "count", 0, SQLITE_NUMERIC, countStep, countFinalize },
|
||||
{ "count", 1, SQLITE_NUMERIC, countStep, countFinalize },
|
||||
#if 0
|
||||
{ "stddev", 1, SQLITE_NUMERIC, stdDevStep, stdDevFinalize },
|
||||
#endif
|
||||
};
|
||||
int i;
|
||||
|
||||
for(i=0; i<sizeof(aFuncs)/sizeof(aFuncs[0]); i++){
|
||||
sqlite_create_function(db, aFuncs[i].zName,
|
||||
aFuncs[i].nArg, aFuncs[i].xFunc, 0);
|
||||
if( aFuncs[i].xFunc ){
|
||||
sqlite_function_type(db, aFuncs[i].zName, aFuncs[i].dataType);
|
||||
}
|
||||
}
|
||||
sqlite_create_function(db, "last_insert_rowid", 0,
|
||||
last_insert_rowid, db);
|
||||
sqlite_function_type(db, "last_insert_rowid", SQLITE_NUMERIC);
|
||||
for(i=0; i<sizeof(aAggs)/sizeof(aAggs[0]); i++){
|
||||
sqlite_create_aggregate(db, aAggs[i].zName,
|
||||
aAggs[i].nArg, aAggs[i].xStep, aAggs[i].xFinalize, 0);
|
||||
sqlite_function_type(db, aAggs[i].zName, aAggs[i].dataType);
|
||||
}
|
||||
}
|
||||
354
sqlitebrowser/sqlitebrowser/sqlite_source/hash.c
Executable file
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
** 2001 September 22
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This is the implementation of generic hash-tables
|
||||
** used in SQLite.
|
||||
**
|
||||
** $Id: hash.c,v 1.1.1.1 2003-08-21 02:24:08 tabuleiro Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <assert.h>
|
||||
|
||||
/* Turn bulk memory into a hash table object by initializing the
|
||||
** fields of the Hash structure.
|
||||
**
|
||||
** "new" is a pointer to the hash table that is to be initialized.
|
||||
** keyClass is one of the constants SQLITE_HASH_INT, SQLITE_HASH_POINTER,
|
||||
** SQLITE_HASH_BINARY, or SQLITE_HASH_STRING. The value of keyClass
|
||||
** determines what kind of key the hash table will use. "copyKey" is
|
||||
** true if the hash table should make its own private copy of keys and
|
||||
** false if it should just use the supplied pointer. CopyKey only makes
|
||||
** sense for SQLITE_HASH_STRING and SQLITE_HASH_BINARY and is ignored
|
||||
** for other key classes.
|
||||
*/
|
||||
void sqliteHashInit(Hash *new, int keyClass, int copyKey){
|
||||
assert( new!=0 );
|
||||
assert( keyClass>=SQLITE_HASH_INT && keyClass<=SQLITE_HASH_BINARY );
|
||||
new->keyClass = keyClass;
|
||||
new->copyKey = copyKey &&
|
||||
(keyClass==SQLITE_HASH_STRING || keyClass==SQLITE_HASH_BINARY);
|
||||
new->first = 0;
|
||||
new->count = 0;
|
||||
new->htsize = 0;
|
||||
new->ht = 0;
|
||||
}
|
||||
|
||||
/* Remove all entries from a hash table. Reclaim all memory.
|
||||
** Call this routine to delete a hash table or to reset a hash table
|
||||
** to the empty state.
|
||||
*/
|
||||
void sqliteHashClear(Hash *pH){
|
||||
HashElem *elem; /* For looping over all elements of the table */
|
||||
|
||||
assert( pH!=0 );
|
||||
elem = pH->first;
|
||||
pH->first = 0;
|
||||
if( pH->ht ) sqliteFree(pH->ht);
|
||||
pH->ht = 0;
|
||||
pH->htsize = 0;
|
||||
while( elem ){
|
||||
HashElem *next_elem = elem->next;
|
||||
if( pH->copyKey && elem->pKey ){
|
||||
sqliteFree(elem->pKey);
|
||||
}
|
||||
sqliteFree(elem);
|
||||
elem = next_elem;
|
||||
}
|
||||
pH->count = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Hash and comparison functions when the mode is SQLITE_HASH_INT
|
||||
*/
|
||||
static int intHash(const void *pKey, int nKey){
|
||||
return nKey ^ (nKey<<8) ^ (nKey>>8);
|
||||
}
|
||||
static int intCompare(const void *pKey1, int n1, const void *pKey2, int n2){
|
||||
return n2 - n1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Hash and comparison functions when the mode is SQLITE_HASH_POINTER
|
||||
*/
|
||||
static int ptrHash(const void *pKey, int nKey){
|
||||
uptr x = Addr(pKey);
|
||||
return x ^ (x<<8) ^ (x>>8);
|
||||
}
|
||||
static int ptrCompare(const void *pKey1, int n1, const void *pKey2, int n2){
|
||||
if( pKey1==pKey2 ) return 0;
|
||||
if( pKey1<pKey2 ) return -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Hash and comparison functions when the mode is SQLITE_HASH_STRING
|
||||
*/
|
||||
static int strHash(const void *pKey, int nKey){
|
||||
return sqliteHashNoCase((const char*)pKey, nKey);
|
||||
}
|
||||
static int strCompare(const void *pKey1, int n1, const void *pKey2, int n2){
|
||||
if( n1!=n2 ) return n2-n1;
|
||||
return sqliteStrNICmp((const char*)pKey1,(const char*)pKey2,n1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Hash and comparison functions when the mode is SQLITE_HASH_BINARY
|
||||
*/
|
||||
static int binHash(const void *pKey, int nKey){
|
||||
int h = 0;
|
||||
const char *z = (const char *)pKey;
|
||||
while( nKey-- > 0 ){
|
||||
h = (h<<3) ^ h ^ *(z++);
|
||||
}
|
||||
return h & 0x7fffffff;
|
||||
}
|
||||
static int binCompare(const void *pKey1, int n1, const void *pKey2, int n2){
|
||||
if( n1!=n2 ) return n2-n1;
|
||||
return memcmp(pKey1,pKey2,n1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a pointer to the appropriate hash function given the key class.
|
||||
**
|
||||
** The C syntax in this function definition may be unfamilar to some
|
||||
** programmers, so we provide the following additional explanation:
|
||||
**
|
||||
** The name of the function is "hashFunction". The function takes a
|
||||
** single parameter "keyClass". The return value of hashFunction()
|
||||
** is a pointer to another function. Specifically, the return value
|
||||
** of hashFunction() is a pointer to a function that takes two parameters
|
||||
** with types "const void*" and "int" and returns an "int".
|
||||
*/
|
||||
static int (*hashFunction(int keyClass))(const void*,int){
|
||||
switch( keyClass ){
|
||||
case SQLITE_HASH_INT: return &intHash;
|
||||
case SQLITE_HASH_POINTER: return &ptrHash;
|
||||
case SQLITE_HASH_STRING: return &strHash;
|
||||
case SQLITE_HASH_BINARY: return &binHash;;
|
||||
default: break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a pointer to the appropriate hash function given the key class.
|
||||
**
|
||||
** For help in interpreted the obscure C code in the function definition,
|
||||
** see the header comment on the previous function.
|
||||
*/
|
||||
static int (*compareFunction(int keyClass))(const void*,int,const void*,int){
|
||||
switch( keyClass ){
|
||||
case SQLITE_HASH_INT: return &intCompare;
|
||||
case SQLITE_HASH_POINTER: return &ptrCompare;
|
||||
case SQLITE_HASH_STRING: return &strCompare;
|
||||
case SQLITE_HASH_BINARY: return &binCompare;
|
||||
default: break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Resize the hash table so that it cantains "new_size" buckets.
|
||||
** "new_size" must be a power of 2. The hash table might fail
|
||||
** to resize if sqliteMalloc() fails.
|
||||
*/
|
||||
static void rehash(Hash *pH, int new_size){
|
||||
struct _ht *new_ht; /* The new hash table */
|
||||
HashElem *elem, *next_elem; /* For looping over existing elements */
|
||||
HashElem *x; /* Element being copied to new hash table */
|
||||
int (*xHash)(const void*,int); /* The hash function */
|
||||
|
||||
assert( (new_size & (new_size-1))==0 );
|
||||
new_ht = (struct _ht *)sqliteMalloc( new_size*sizeof(struct _ht) );
|
||||
if( new_ht==0 ) return;
|
||||
if( pH->ht ) sqliteFree(pH->ht);
|
||||
pH->ht = new_ht;
|
||||
pH->htsize = new_size;
|
||||
xHash = hashFunction(pH->keyClass);
|
||||
for(elem=pH->first, pH->first=0; elem; elem = next_elem){
|
||||
int h = (*xHash)(elem->pKey, elem->nKey) & (new_size-1);
|
||||
next_elem = elem->next;
|
||||
x = new_ht[h].chain;
|
||||
if( x ){
|
||||
elem->next = x;
|
||||
elem->prev = x->prev;
|
||||
if( x->prev ) x->prev->next = elem;
|
||||
else pH->first = elem;
|
||||
x->prev = elem;
|
||||
}else{
|
||||
elem->next = pH->first;
|
||||
if( pH->first ) pH->first->prev = elem;
|
||||
elem->prev = 0;
|
||||
pH->first = elem;
|
||||
}
|
||||
new_ht[h].chain = elem;
|
||||
new_ht[h].count++;
|
||||
}
|
||||
}
|
||||
|
||||
/* This function (for internal use only) locates an element in an
|
||||
** hash table that matches the given key. The hash for this key has
|
||||
** already been computed and is passed as the 4th parameter.
|
||||
*/
|
||||
static HashElem *findElementGivenHash(
|
||||
const Hash *pH, /* The pH to be searched */
|
||||
const void *pKey, /* The key we are searching for */
|
||||
int nKey,
|
||||
int h /* The hash for this key. */
|
||||
){
|
||||
HashElem *elem; /* Used to loop thru the element list */
|
||||
int count; /* Number of elements left to test */
|
||||
int (*xCompare)(const void*,int,const void*,int); /* comparison function */
|
||||
|
||||
if( pH->ht ){
|
||||
elem = pH->ht[h].chain;
|
||||
count = pH->ht[h].count;
|
||||
xCompare = compareFunction(pH->keyClass);
|
||||
while( count-- && elem ){
|
||||
if( (*xCompare)(elem->pKey,elem->nKey,pKey,nKey)==0 ){
|
||||
return elem;
|
||||
}
|
||||
elem = elem->next;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Remove a single entry from the hash table given a pointer to that
|
||||
** element and a hash on the element's key.
|
||||
*/
|
||||
static void removeElementGivenHash(
|
||||
Hash *pH, /* The pH containing "elem" */
|
||||
HashElem* elem, /* The element to be removed from the pH */
|
||||
int h /* Hash value for the element */
|
||||
){
|
||||
if( elem->prev ){
|
||||
elem->prev->next = elem->next;
|
||||
}else{
|
||||
pH->first = elem->next;
|
||||
}
|
||||
if( elem->next ){
|
||||
elem->next->prev = elem->prev;
|
||||
}
|
||||
if( pH->ht[h].chain==elem ){
|
||||
pH->ht[h].chain = elem->next;
|
||||
}
|
||||
pH->ht[h].count--;
|
||||
if( pH->ht[h].count<=0 ){
|
||||
pH->ht[h].chain = 0;
|
||||
}
|
||||
if( pH->copyKey && elem->pKey ){
|
||||
sqliteFree(elem->pKey);
|
||||
}
|
||||
sqliteFree( elem );
|
||||
pH->count--;
|
||||
}
|
||||
|
||||
/* Attempt to locate an element of the hash table pH with a key
|
||||
** that matches pKey,nKey. Return the data for this element if it is
|
||||
** found, or NULL if there is no match.
|
||||
*/
|
||||
void *sqliteHashFind(const Hash *pH, const void *pKey, int nKey){
|
||||
int h; /* A hash on key */
|
||||
HashElem *elem; /* The element that matches key */
|
||||
int (*xHash)(const void*,int); /* The hash function */
|
||||
|
||||
if( pH==0 || pH->ht==0 ) return 0;
|
||||
xHash = hashFunction(pH->keyClass);
|
||||
assert( xHash!=0 );
|
||||
h = (*xHash)(pKey,nKey);
|
||||
assert( (pH->htsize & (pH->htsize-1))==0 );
|
||||
elem = findElementGivenHash(pH,pKey,nKey, h & (pH->htsize-1));
|
||||
return elem ? elem->data : 0;
|
||||
}
|
||||
|
||||
/* Insert an element into the hash table pH. The key is pKey,nKey
|
||||
** and the data is "data".
|
||||
**
|
||||
** If no element exists with a matching key, then a new
|
||||
** element is created. A copy of the key is made if the copyKey
|
||||
** flag is set. NULL is returned.
|
||||
**
|
||||
** If another element already exists with the same key, then the
|
||||
** new data replaces the old data and the old data is returned.
|
||||
** The key is not copied in this instance. If a malloc fails, then
|
||||
** the new data is returned and the hash table is unchanged.
|
||||
**
|
||||
** If the "data" parameter to this function is NULL, then the
|
||||
** element corresponding to "key" is removed from the hash table.
|
||||
*/
|
||||
void *sqliteHashInsert(Hash *pH, const void *pKey, int nKey, void *data){
|
||||
int hraw; /* Raw hash value of the key */
|
||||
int h; /* the hash of the key modulo hash table size */
|
||||
HashElem *elem; /* Used to loop thru the element list */
|
||||
HashElem *new_elem; /* New element added to the pH */
|
||||
int (*xHash)(const void*,int); /* The hash function */
|
||||
|
||||
assert( pH!=0 );
|
||||
xHash = hashFunction(pH->keyClass);
|
||||
assert( xHash!=0 );
|
||||
hraw = (*xHash)(pKey, nKey);
|
||||
assert( (pH->htsize & (pH->htsize-1))==0 );
|
||||
h = hraw & (pH->htsize-1);
|
||||
elem = findElementGivenHash(pH,pKey,nKey,h);
|
||||
if( elem ){
|
||||
void *old_data = elem->data;
|
||||
if( data==0 ){
|
||||
removeElementGivenHash(pH,elem,h);
|
||||
}else{
|
||||
elem->data = data;
|
||||
}
|
||||
return old_data;
|
||||
}
|
||||
if( data==0 ) return 0;
|
||||
new_elem = (HashElem*)sqliteMalloc( sizeof(HashElem) );
|
||||
if( new_elem==0 ) return data;
|
||||
if( pH->copyKey && pKey!=0 ){
|
||||
new_elem->pKey = sqliteMallocRaw( nKey );
|
||||
if( new_elem->pKey==0 ){
|
||||
sqliteFree(new_elem);
|
||||
return data;
|
||||
}
|
||||
memcpy((void*)new_elem->pKey, pKey, nKey);
|
||||
}else{
|
||||
new_elem->pKey = (void*)pKey;
|
||||
}
|
||||
new_elem->nKey = nKey;
|
||||
pH->count++;
|
||||
if( pH->htsize==0 ) rehash(pH,8);
|
||||
if( pH->htsize==0 ){
|
||||
pH->count = 0;
|
||||
sqliteFree(new_elem);
|
||||
return data;
|
||||
}
|
||||
if( pH->count > pH->htsize ){
|
||||
rehash(pH,pH->htsize*2);
|
||||
}
|
||||
assert( (pH->htsize & (pH->htsize-1))==0 );
|
||||
h = hraw & (pH->htsize-1);
|
||||
elem = pH->ht[h].chain;
|
||||
if( elem ){
|
||||
new_elem->next = elem;
|
||||
new_elem->prev = elem->prev;
|
||||
if( elem->prev ){ elem->prev->next = new_elem; }
|
||||
else { pH->first = new_elem; }
|
||||
elem->prev = new_elem;
|
||||
}else{
|
||||
new_elem->next = pH->first;
|
||||
new_elem->prev = 0;
|
||||
if( pH->first ){ pH->first->prev = new_elem; }
|
||||
pH->first = new_elem;
|
||||
}
|
||||
pH->ht[h].count++;
|
||||
pH->ht[h].chain = new_elem;
|
||||
new_elem->data = data;
|
||||
return 0;
|
||||
}
|
||||
109
sqlitebrowser/sqlitebrowser/sqlite_source/hash.h
Executable file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
** 2001 September 22
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This is the header file for the generic hash-table implemenation
|
||||
** used in SQLite.
|
||||
**
|
||||
** $Id: hash.h,v 1.1.1.1 2003-08-21 02:24:08 tabuleiro Exp $
|
||||
*/
|
||||
#ifndef _SQLITE_HASH_H_
|
||||
#define _SQLITE_HASH_H_
|
||||
|
||||
/* Forward declarations of structures. */
|
||||
typedef struct Hash Hash;
|
||||
typedef struct HashElem HashElem;
|
||||
|
||||
/* A complete hash table is an instance of the following structure.
|
||||
** The internals of this structure are intended to be opaque -- client
|
||||
** code should not attempt to access or modify the fields of this structure
|
||||
** directly. Change this structure only by using the routines below.
|
||||
** However, many of the "procedures" and "functions" for modifying and
|
||||
** accessing this structure are really macros, so we can't really make
|
||||
** this structure opaque.
|
||||
*/
|
||||
struct Hash {
|
||||
char keyClass; /* SQLITE_HASH_INT, _POINTER, _STRING, _BINARY */
|
||||
char copyKey; /* True if copy of key made on insert */
|
||||
int count; /* Number of entries in this table */
|
||||
HashElem *first; /* The first element of the array */
|
||||
int htsize; /* Number of buckets in the hash table */
|
||||
struct _ht { /* the hash table */
|
||||
int count; /* Number of entries with this hash */
|
||||
HashElem *chain; /* Pointer to first entry with this hash */
|
||||
} *ht;
|
||||
};
|
||||
|
||||
/* Each element in the hash table is an instance of the following
|
||||
** structure. All elements are stored on a single doubly-linked list.
|
||||
**
|
||||
** Again, this structure is intended to be opaque, but it can't really
|
||||
** be opaque because it is used by macros.
|
||||
*/
|
||||
struct HashElem {
|
||||
HashElem *next, *prev; /* Next and previous elements in the table */
|
||||
void *data; /* Data associated with this element */
|
||||
void *pKey; int nKey; /* Key associated with this element */
|
||||
};
|
||||
|
||||
/*
|
||||
** There are 4 different modes of operation for a hash table:
|
||||
**
|
||||
** SQLITE_HASH_INT nKey is used as the key and pKey is ignored.
|
||||
**
|
||||
** SQLITE_HASH_POINTER pKey is used as the key and nKey is ignored.
|
||||
**
|
||||
** SQLITE_HASH_STRING pKey points to a string that is nKey bytes long
|
||||
** (including the null-terminator, if any). Case
|
||||
** is ignored in comparisons.
|
||||
**
|
||||
** SQLITE_HASH_BINARY pKey points to binary data nKey bytes long.
|
||||
** memcmp() is used to compare keys.
|
||||
**
|
||||
** A copy of the key is made for SQLITE_HASH_STRING and SQLITE_HASH_BINARY
|
||||
** if the copyKey parameter to HashInit is 1.
|
||||
*/
|
||||
#define SQLITE_HASH_INT 1
|
||||
#define SQLITE_HASH_POINTER 2
|
||||
#define SQLITE_HASH_STRING 3
|
||||
#define SQLITE_HASH_BINARY 4
|
||||
|
||||
/*
|
||||
** Access routines. To delete, insert a NULL pointer.
|
||||
*/
|
||||
void sqliteHashInit(Hash*, int keytype, int copyKey);
|
||||
void *sqliteHashInsert(Hash*, const void *pKey, int nKey, void *pData);
|
||||
void *sqliteHashFind(const Hash*, const void *pKey, int nKey);
|
||||
void sqliteHashClear(Hash*);
|
||||
|
||||
/*
|
||||
** Macros for looping over all elements of a hash table. The idiom is
|
||||
** like this:
|
||||
**
|
||||
** Hash h;
|
||||
** HashElem *p;
|
||||
** ...
|
||||
** for(p=sqliteHashFirst(&h); p; p=sqliteHashNext(p)){
|
||||
** SomeStructure *pData = sqliteHashData(p);
|
||||
** // do something with pData
|
||||
** }
|
||||
*/
|
||||
#define sqliteHashFirst(H) ((H)->first)
|
||||
#define sqliteHashNext(E) ((E)->next)
|
||||
#define sqliteHashData(E) ((E)->data)
|
||||
#define sqliteHashKey(E) ((E)->pKey)
|
||||
#define sqliteHashKeysize(E) ((E)->nKey)
|
||||
|
||||
/*
|
||||
** Number of entries in a hash table
|
||||
*/
|
||||
#define sqliteHashCount(H) ((H)->count)
|
||||
|
||||
#endif /* _SQLITE_HASH_H_ */
|
||||
891
sqlitebrowser/sqlitebrowser/sqlite_source/insert.c
Executable file
@@ -0,0 +1,891 @@
|
||||
/*
|
||||
** 2001 September 15
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle INSERT statements in SQLite.
|
||||
**
|
||||
** $Id: insert.c,v 1.1.1.1 2003-08-21 02:24:09 tabuleiro Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** This routine is call to handle SQL of the following forms:
|
||||
**
|
||||
** insert into TABLE (IDLIST) values(EXPRLIST)
|
||||
** insert into TABLE (IDLIST) select
|
||||
**
|
||||
** The IDLIST following the table name is always optional. If omitted,
|
||||
** then a list of all columns for the table is substituted. The IDLIST
|
||||
** appears in the pColumn parameter. pColumn is NULL if IDLIST is omitted.
|
||||
**
|
||||
** The pList parameter holds EXPRLIST in the first form of the INSERT
|
||||
** statement above, and pSelect is NULL. For the second form, pList is
|
||||
** NULL and pSelect is a pointer to the select statement used to generate
|
||||
** data for the insert.
|
||||
**
|
||||
** The code generated follows one of three templates. For a simple
|
||||
** select with data coming from a VALUES clause, the code executes
|
||||
** once straight down through. The template looks like this:
|
||||
**
|
||||
** open write cursor to <table> and its indices
|
||||
** puts VALUES clause expressions onto the stack
|
||||
** write the resulting record into <table>
|
||||
** cleanup
|
||||
**
|
||||
** If the statement is of the form
|
||||
**
|
||||
** INSERT INTO <table> SELECT ...
|
||||
**
|
||||
** And the SELECT clause does not read from <table> at any time, then
|
||||
** the generated code follows this template:
|
||||
**
|
||||
** goto B
|
||||
** A: setup for the SELECT
|
||||
** loop over the tables in the SELECT
|
||||
** gosub C
|
||||
** end loop
|
||||
** cleanup after the SELECT
|
||||
** goto D
|
||||
** B: open write cursor to <table> and its indices
|
||||
** goto A
|
||||
** C: insert the select result into <table>
|
||||
** return
|
||||
** D: cleanup
|
||||
**
|
||||
** The third template is used if the insert statement takes its
|
||||
** values from a SELECT but the data is being inserted into a table
|
||||
** that is also read as part of the SELECT. In the third form,
|
||||
** we have to use a intermediate table to store the results of
|
||||
** the select. The template is like this:
|
||||
**
|
||||
** goto B
|
||||
** A: setup for the SELECT
|
||||
** loop over the tables in the SELECT
|
||||
** gosub C
|
||||
** end loop
|
||||
** cleanup after the SELECT
|
||||
** goto D
|
||||
** C: insert the select result into the intermediate table
|
||||
** return
|
||||
** B: open a cursor to an intermediate table
|
||||
** goto A
|
||||
** D: open write cursor to <table> and its indices
|
||||
** loop over the intermediate table
|
||||
** transfer values form intermediate table into <table>
|
||||
** end the loop
|
||||
** cleanup
|
||||
*/
|
||||
void sqliteInsert(
|
||||
Parse *pParse, /* Parser context */
|
||||
SrcList *pTabList, /* Name of table into which we are inserting */
|
||||
ExprList *pList, /* List of values to be inserted */
|
||||
Select *pSelect, /* A SELECT statement to use as the data source */
|
||||
IdList *pColumn, /* Column names corresponding to IDLIST. */
|
||||
int onError /* How to handle constraint errors */
|
||||
){
|
||||
Table *pTab; /* The table to insert into */
|
||||
char *zTab; /* Name of the table into which we are inserting */
|
||||
const char *zDb; /* Name of the database holding this table */
|
||||
int i, j, idx; /* Loop counters */
|
||||
Vdbe *v; /* Generate code into this virtual machine */
|
||||
Index *pIdx; /* For looping over indices of the table */
|
||||
int nColumn; /* Number of columns in the data */
|
||||
int base; /* VDBE Cursor number for pTab */
|
||||
int iCont, iBreak; /* Beginning and end of the loop over srcTab */
|
||||
sqlite *db; /* The main database structure */
|
||||
int keyColumn = -1; /* Column that is the INTEGER PRIMARY KEY */
|
||||
int endOfLoop; /* Label for the end of the insertion loop */
|
||||
int useTempTable; /* Store SELECT results in intermediate table */
|
||||
int srcTab; /* Data comes from this temporary cursor if >=0 */
|
||||
int iSelectLoop; /* Address of code that implements the SELECT */
|
||||
int iCleanup; /* Address of the cleanup code */
|
||||
int iInsertBlock; /* Address of the subroutine used to insert data */
|
||||
int iCntMem; /* Memory cell used for the row counter */
|
||||
int isView; /* True if attempting to insert into a view */
|
||||
|
||||
int row_triggers_exist = 0; /* True if there are FOR EACH ROW triggers */
|
||||
int before_triggers; /* True if there are BEFORE triggers */
|
||||
int after_triggers; /* True if there are AFTER triggers */
|
||||
int newIdx = -1; /* Cursor for the NEW table */
|
||||
|
||||
if( pParse->nErr || sqlite_malloc_failed ) goto insert_cleanup;
|
||||
db = pParse->db;
|
||||
|
||||
/* Locate the table into which we will be inserting new information.
|
||||
*/
|
||||
assert( pTabList->nSrc==1 );
|
||||
zTab = pTabList->a[0].zName;
|
||||
if( zTab==0 ) goto insert_cleanup;
|
||||
pTab = sqliteSrcListLookup(pParse, pTabList);
|
||||
if( pTab==0 ){
|
||||
goto insert_cleanup;
|
||||
}
|
||||
assert( pTab->iDb<db->nDb );
|
||||
zDb = db->aDb[pTab->iDb].zName;
|
||||
if( sqliteAuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb) ){
|
||||
goto insert_cleanup;
|
||||
}
|
||||
|
||||
/* Ensure that:
|
||||
* (a) the table is not read-only,
|
||||
* (b) that if it is a view then ON INSERT triggers exist
|
||||
*/
|
||||
before_triggers = sqliteTriggersExist(pParse, pTab->pTrigger, TK_INSERT,
|
||||
TK_BEFORE, TK_ROW, 0);
|
||||
after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger, TK_INSERT,
|
||||
TK_AFTER, TK_ROW, 0);
|
||||
row_triggers_exist = before_triggers || after_triggers;
|
||||
isView = pTab->pSelect!=0;
|
||||
if( sqliteIsReadOnly(pParse, pTab, before_triggers) ){
|
||||
goto insert_cleanup;
|
||||
}
|
||||
if( pTab==0 ) goto insert_cleanup;
|
||||
|
||||
/* If pTab is really a view, make sure it has been initialized.
|
||||
*/
|
||||
if( isView && sqliteViewGetColumnNames(pParse, pTab) ){
|
||||
goto insert_cleanup;
|
||||
}
|
||||
|
||||
/* Allocate a VDBE
|
||||
*/
|
||||
v = sqliteGetVdbe(pParse);
|
||||
if( v==0 ) goto insert_cleanup;
|
||||
sqliteBeginWriteOperation(pParse, pSelect || row_triggers_exist, pTab->iDb);
|
||||
|
||||
/* if there are row triggers, allocate a temp table for new.* references. */
|
||||
if( row_triggers_exist ){
|
||||
newIdx = pParse->nTab++;
|
||||
}
|
||||
|
||||
/* Figure out how many columns of data are supplied. If the data
|
||||
** is coming from a SELECT statement, then this step also generates
|
||||
** all the code to implement the SELECT statement and invoke a subroutine
|
||||
** to process each row of the result. (Template 2.) If the SELECT
|
||||
** statement uses the the table that is being inserted into, then the
|
||||
** subroutine is also coded here. That subroutine stores the SELECT
|
||||
** results in a temporary table. (Template 3.)
|
||||
*/
|
||||
if( pSelect ){
|
||||
/* Data is coming from a SELECT. Generate code to implement that SELECT
|
||||
*/
|
||||
int rc, iInitCode;
|
||||
iInitCode = sqliteVdbeAddOp(v, OP_Goto, 0, 0);
|
||||
iSelectLoop = sqliteVdbeCurrentAddr(v);
|
||||
iInsertBlock = sqliteVdbeMakeLabel(v);
|
||||
rc = sqliteSelect(pParse, pSelect, SRT_Subroutine, iInsertBlock, 0,0,0);
|
||||
if( rc || pParse->nErr || sqlite_malloc_failed ) goto insert_cleanup;
|
||||
iCleanup = sqliteVdbeMakeLabel(v);
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, iCleanup);
|
||||
assert( pSelect->pEList );
|
||||
nColumn = pSelect->pEList->nExpr;
|
||||
|
||||
/* Set useTempTable to TRUE if the result of the SELECT statement
|
||||
** should be written into a temporary table. Set to FALSE if each
|
||||
** row of the SELECT can be written directly into the result table.
|
||||
**
|
||||
** A temp table must be used if the table being updated is also one
|
||||
** of the tables being read by the SELECT statement. Also use a
|
||||
** temp table in the case of row triggers.
|
||||
*/
|
||||
if( row_triggers_exist ){
|
||||
useTempTable = 1;
|
||||
}else{
|
||||
int addr = sqliteVdbeFindOp(v, OP_OpenRead, pTab->tnum);
|
||||
useTempTable = 0;
|
||||
if( addr>0 ){
|
||||
VdbeOp *pOp = sqliteVdbeGetOp(v, addr-2);
|
||||
if( pOp->opcode==OP_Integer && pOp->p1==pTab->iDb ){
|
||||
useTempTable = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( useTempTable ){
|
||||
/* Generate the subroutine that SELECT calls to process each row of
|
||||
** the result. Store the result in a temporary table
|
||||
*/
|
||||
srcTab = pParse->nTab++;
|
||||
sqliteVdbeResolveLabel(v, iInsertBlock);
|
||||
sqliteVdbeAddOp(v, OP_MakeRecord, nColumn, 0);
|
||||
sqliteVdbeAddOp(v, OP_NewRecno, srcTab, 0);
|
||||
sqliteVdbeAddOp(v, OP_Pull, 1, 0);
|
||||
sqliteVdbeAddOp(v, OP_PutIntKey, srcTab, 0);
|
||||
sqliteVdbeAddOp(v, OP_Return, 0, 0);
|
||||
|
||||
/* The following code runs first because the GOTO at the very top
|
||||
** of the program jumps to it. Create the temporary table, then jump
|
||||
** back up and execute the SELECT code above.
|
||||
*/
|
||||
sqliteVdbeChangeP2(v, iInitCode, sqliteVdbeCurrentAddr(v));
|
||||
sqliteVdbeAddOp(v, OP_OpenTemp, srcTab, 0);
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, iSelectLoop);
|
||||
sqliteVdbeResolveLabel(v, iCleanup);
|
||||
}else{
|
||||
sqliteVdbeChangeP2(v, iInitCode, sqliteVdbeCurrentAddr(v));
|
||||
}
|
||||
}else{
|
||||
/* This is the case if the data for the INSERT is coming from a VALUES
|
||||
** clause
|
||||
*/
|
||||
SrcList dummy;
|
||||
assert( pList!=0 );
|
||||
srcTab = -1;
|
||||
useTempTable = 0;
|
||||
assert( pList );
|
||||
nColumn = pList->nExpr;
|
||||
dummy.nSrc = 0;
|
||||
for(i=0; i<nColumn; i++){
|
||||
if( sqliteExprResolveIds(pParse, &dummy, 0, pList->a[i].pExpr) ){
|
||||
goto insert_cleanup;
|
||||
}
|
||||
if( sqliteExprCheck(pParse, pList->a[i].pExpr, 0, 0) ){
|
||||
goto insert_cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure the number of columns in the source data matches the number
|
||||
** of columns to be inserted into the table.
|
||||
*/
|
||||
if( pColumn==0 && nColumn!=pTab->nCol ){
|
||||
sqliteErrorMsg(pParse,
|
||||
"table %S has %d columns but %d values were supplied",
|
||||
pTabList, 0, pTab->nCol, nColumn);
|
||||
goto insert_cleanup;
|
||||
}
|
||||
if( pColumn!=0 && nColumn!=pColumn->nId ){
|
||||
sqliteErrorMsg(pParse, "%d values for %d columns", nColumn, pColumn->nId);
|
||||
goto insert_cleanup;
|
||||
}
|
||||
|
||||
/* If the INSERT statement included an IDLIST term, then make sure
|
||||
** all elements of the IDLIST really are columns of the table and
|
||||
** remember the column indices.
|
||||
**
|
||||
** If the table has an INTEGER PRIMARY KEY column and that column
|
||||
** is named in the IDLIST, then record in the keyColumn variable
|
||||
** the index into IDLIST of the primary key column. keyColumn is
|
||||
** the index of the primary key as it appears in IDLIST, not as
|
||||
** is appears in the original table. (The index of the primary
|
||||
** key in the original table is pTab->iPKey.)
|
||||
*/
|
||||
if( pColumn ){
|
||||
for(i=0; i<pColumn->nId; i++){
|
||||
pColumn->a[i].idx = -1;
|
||||
}
|
||||
for(i=0; i<pColumn->nId; i++){
|
||||
for(j=0; j<pTab->nCol; j++){
|
||||
if( sqliteStrICmp(pColumn->a[i].zName, pTab->aCol[j].zName)==0 ){
|
||||
pColumn->a[i].idx = j;
|
||||
if( j==pTab->iPKey ){
|
||||
keyColumn = i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( j>=pTab->nCol ){
|
||||
if( sqliteIsRowid(pColumn->a[i].zName) ){
|
||||
keyColumn = i;
|
||||
}else{
|
||||
sqliteErrorMsg(pParse, "table %S has no column named %s",
|
||||
pTabList, 0, pColumn->a[i].zName);
|
||||
pParse->nErr++;
|
||||
goto insert_cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If there is no IDLIST term but the table has an integer primary
|
||||
** key, the set the keyColumn variable to the primary key column index
|
||||
** in the original table definition.
|
||||
*/
|
||||
if( pColumn==0 ){
|
||||
keyColumn = pTab->iPKey;
|
||||
}
|
||||
|
||||
/* Open the temp table for FOR EACH ROW triggers
|
||||
*/
|
||||
if( row_triggers_exist ){
|
||||
sqliteVdbeAddOp(v, OP_OpenPseudo, newIdx, 0);
|
||||
}
|
||||
|
||||
/* Initialize the count of rows to be inserted
|
||||
*/
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
iCntMem = pParse->nMem++;
|
||||
sqliteVdbeAddOp(v, OP_Integer, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_MemStore, iCntMem, 1);
|
||||
}
|
||||
|
||||
/* Open tables and indices if there are no row triggers */
|
||||
if( !row_triggers_exist ){
|
||||
base = pParse->nTab;
|
||||
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum);
|
||||
sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
|
||||
for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
|
||||
sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenWrite, idx+base, pIdx->tnum);
|
||||
sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC);
|
||||
}
|
||||
pParse->nTab += idx;
|
||||
}
|
||||
|
||||
/* If the data source is a temporary table, then we have to create
|
||||
** a loop because there might be multiple rows of data. If the data
|
||||
** source is a subroutine call from the SELECT statement, then we need
|
||||
** to launch the SELECT statement processing.
|
||||
*/
|
||||
if( useTempTable ){
|
||||
iBreak = sqliteVdbeMakeLabel(v);
|
||||
sqliteVdbeAddOp(v, OP_Rewind, srcTab, iBreak);
|
||||
iCont = sqliteVdbeCurrentAddr(v);
|
||||
}else if( pSelect ){
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, iSelectLoop);
|
||||
sqliteVdbeResolveLabel(v, iInsertBlock);
|
||||
}
|
||||
|
||||
/* Run the BEFORE and INSTEAD OF triggers, if there are any
|
||||
*/
|
||||
endOfLoop = sqliteVdbeMakeLabel(v);
|
||||
if( before_triggers ){
|
||||
|
||||
/* build the NEW.* reference row. Note that if there is an INTEGER
|
||||
** PRIMARY KEY into which a NULL is being inserted, that NULL will be
|
||||
** translated into a unique ID for the row. But on a BEFORE trigger,
|
||||
** we do not know what the unique ID will be (because the insert has
|
||||
** not happened yet) so we substitute a rowid of -1
|
||||
*/
|
||||
if( keyColumn<0 ){
|
||||
sqliteVdbeAddOp(v, OP_Integer, -1, 0);
|
||||
}else if( useTempTable ){
|
||||
sqliteVdbeAddOp(v, OP_Column, srcTab, keyColumn);
|
||||
}else if( pSelect ){
|
||||
sqliteVdbeAddOp(v, OP_Dup, nColumn - keyColumn - 1, 1);
|
||||
}else{
|
||||
sqliteExprCode(pParse, pList->a[keyColumn].pExpr);
|
||||
sqliteVdbeAddOp(v, OP_NotNull, -1, sqliteVdbeCurrentAddr(v)+3);
|
||||
sqliteVdbeAddOp(v, OP_Pop, 1, 0);
|
||||
sqliteVdbeAddOp(v, OP_Integer, -1, 0);
|
||||
sqliteVdbeAddOp(v, OP_MustBeInt, 0, 0);
|
||||
}
|
||||
|
||||
/* Create the new column data
|
||||
*/
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
if( pColumn==0 ){
|
||||
j = i;
|
||||
}else{
|
||||
for(j=0; j<pColumn->nId; j++){
|
||||
if( pColumn->a[j].idx==i ) break;
|
||||
}
|
||||
}
|
||||
if( pColumn && j>=pColumn->nId ){
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, pTab->aCol[i].zDflt, P3_STATIC);
|
||||
}else if( useTempTable ){
|
||||
sqliteVdbeAddOp(v, OP_Column, srcTab, j);
|
||||
}else if( pSelect ){
|
||||
sqliteVdbeAddOp(v, OP_Dup, nColumn-j-1, 1);
|
||||
}else{
|
||||
sqliteExprCode(pParse, pList->a[j].pExpr);
|
||||
}
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
|
||||
sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0);
|
||||
|
||||
/* Fire BEFORE or INSTEAD OF triggers */
|
||||
if( sqliteCodeRowTrigger(pParse, TK_INSERT, 0, TK_BEFORE, pTab,
|
||||
newIdx, -1, onError, endOfLoop) ){
|
||||
goto insert_cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/* If any triggers exists, the opening of tables and indices is deferred
|
||||
** until now.
|
||||
*/
|
||||
if( row_triggers_exist && !isView ){
|
||||
base = pParse->nTab;
|
||||
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum);
|
||||
sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
|
||||
for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
|
||||
sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenWrite, idx+base, pIdx->tnum);
|
||||
sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC);
|
||||
}
|
||||
pParse->nTab += idx;
|
||||
}
|
||||
|
||||
/* Push the record number for the new entry onto the stack. The
|
||||
** record number is a randomly generate integer created by NewRecno
|
||||
** except when the table has an INTEGER PRIMARY KEY column, in which
|
||||
** case the record number is the same as that column.
|
||||
*/
|
||||
if( !isView ){
|
||||
if( keyColumn>=0 ){
|
||||
if( useTempTable ){
|
||||
sqliteVdbeAddOp(v, OP_Column, srcTab, keyColumn);
|
||||
}else if( pSelect ){
|
||||
sqliteVdbeAddOp(v, OP_Dup, nColumn - keyColumn - 1, 1);
|
||||
}else{
|
||||
sqliteExprCode(pParse, pList->a[keyColumn].pExpr);
|
||||
}
|
||||
/* If the PRIMARY KEY expression is NULL, then use OP_NewRecno
|
||||
** to generate a unique primary key value.
|
||||
*/
|
||||
sqliteVdbeAddOp(v, OP_NotNull, -1, sqliteVdbeCurrentAddr(v)+3);
|
||||
sqliteVdbeAddOp(v, OP_Pop, 1, 0);
|
||||
sqliteVdbeAddOp(v, OP_NewRecno, base, 0);
|
||||
sqliteVdbeAddOp(v, OP_MustBeInt, 0, 0);
|
||||
}else{
|
||||
sqliteVdbeAddOp(v, OP_NewRecno, base, 0);
|
||||
}
|
||||
|
||||
/* Push onto the stack, data for all columns of the new entry, beginning
|
||||
** with the first column.
|
||||
*/
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
if( i==pTab->iPKey ){
|
||||
/* The value of the INTEGER PRIMARY KEY column is always a NULL.
|
||||
** Whenever this column is read, the record number will be substituted
|
||||
** in its place. So will fill this column with a NULL to avoid
|
||||
** taking up data space with information that will never be used. */
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
continue;
|
||||
}
|
||||
if( pColumn==0 ){
|
||||
j = i;
|
||||
}else{
|
||||
for(j=0; j<pColumn->nId; j++){
|
||||
if( pColumn->a[j].idx==i ) break;
|
||||
}
|
||||
}
|
||||
if( pColumn && j>=pColumn->nId ){
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, pTab->aCol[i].zDflt, P3_STATIC);
|
||||
}else if( useTempTable ){
|
||||
sqliteVdbeAddOp(v, OP_Column, srcTab, j);
|
||||
}else if( pSelect ){
|
||||
sqliteVdbeAddOp(v, OP_Dup, i+nColumn-j, 1);
|
||||
}else{
|
||||
sqliteExprCode(pParse, pList->a[j].pExpr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Generate code to check constraints and generate index keys and
|
||||
** do the insertion.
|
||||
*/
|
||||
sqliteGenerateConstraintChecks(pParse, pTab, base, 0, keyColumn>=0,
|
||||
0, onError, endOfLoop);
|
||||
sqliteCompleteInsertion(pParse, pTab, base, 0,0,0,
|
||||
after_triggers ? newIdx : -1);
|
||||
}
|
||||
|
||||
/* Update the count of rows that are inserted
|
||||
*/
|
||||
if( (db->flags & SQLITE_CountRows)!=0 ){
|
||||
sqliteVdbeAddOp(v, OP_MemIncr, iCntMem, 0);
|
||||
}
|
||||
|
||||
if( row_triggers_exist ){
|
||||
/* Close all tables opened */
|
||||
if( !isView ){
|
||||
sqliteVdbeAddOp(v, OP_Close, base, 0);
|
||||
for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
|
||||
sqliteVdbeAddOp(v, OP_Close, idx+base, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Code AFTER triggers */
|
||||
if( sqliteCodeRowTrigger(pParse, TK_INSERT, 0, TK_AFTER, pTab, newIdx, -1,
|
||||
onError, endOfLoop) ){
|
||||
goto insert_cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/* The bottom of the loop, if the data source is a SELECT statement
|
||||
*/
|
||||
sqliteVdbeResolveLabel(v, endOfLoop);
|
||||
if( useTempTable ){
|
||||
sqliteVdbeAddOp(v, OP_Next, srcTab, iCont);
|
||||
sqliteVdbeResolveLabel(v, iBreak);
|
||||
sqliteVdbeAddOp(v, OP_Close, srcTab, 0);
|
||||
}else if( pSelect ){
|
||||
sqliteVdbeAddOp(v, OP_Pop, nColumn, 0);
|
||||
sqliteVdbeAddOp(v, OP_Return, 0, 0);
|
||||
sqliteVdbeResolveLabel(v, iCleanup);
|
||||
}
|
||||
|
||||
if( !row_triggers_exist ){
|
||||
/* Close all tables opened */
|
||||
sqliteVdbeAddOp(v, OP_Close, base, 0);
|
||||
for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
|
||||
sqliteVdbeAddOp(v, OP_Close, idx+base, 0);
|
||||
}
|
||||
}
|
||||
|
||||
sqliteEndWriteOperation(pParse);
|
||||
|
||||
/*
|
||||
** Return the number of rows inserted.
|
||||
*/
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
sqliteVdbeAddOp(v, OP_ColumnName, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, "rows inserted", P3_STATIC);
|
||||
sqliteVdbeAddOp(v, OP_MemLoad, iCntMem, 0);
|
||||
sqliteVdbeAddOp(v, OP_Callback, 1, 0);
|
||||
}
|
||||
|
||||
insert_cleanup:
|
||||
sqliteSrcListDelete(pTabList);
|
||||
if( pList ) sqliteExprListDelete(pList);
|
||||
if( pSelect ) sqliteSelectDelete(pSelect);
|
||||
sqliteIdListDelete(pColumn);
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code to do a constraint check prior to an INSERT or an UPDATE.
|
||||
**
|
||||
** When this routine is called, the stack contains (from bottom to top)
|
||||
** the following values:
|
||||
**
|
||||
** 1. The recno of the row to be updated before the update. This
|
||||
** value is omitted unless we are doing an UPDATE that involves a
|
||||
** change to the record number.
|
||||
**
|
||||
** 2. The recno of the row after the update.
|
||||
**
|
||||
** 3. The data in the first column of the entry after the update.
|
||||
**
|
||||
** i. Data from middle columns...
|
||||
**
|
||||
** N. The data in the last column of the entry after the update.
|
||||
**
|
||||
** The old recno shown as entry (1) above is omitted unless both isUpdate
|
||||
** and recnoChng are 1. isUpdate is true for UPDATEs and false for
|
||||
** INSERTs and recnoChng is true if the record number is being changed.
|
||||
**
|
||||
** The code generated by this routine pushes additional entries onto
|
||||
** the stack which are the keys for new index entries for the new record.
|
||||
** The order of index keys is the same as the order of the indices on
|
||||
** the pTable->pIndex list. A key is only created for index i if
|
||||
** aIdxUsed!=0 and aIdxUsed[i]!=0.
|
||||
**
|
||||
** This routine also generates code to check constraints. NOT NULL,
|
||||
** CHECK, and UNIQUE constraints are all checked. If a constraint fails,
|
||||
** then the appropriate action is performed. There are five possible
|
||||
** actions: ROLLBACK, ABORT, FAIL, REPLACE, and IGNORE.
|
||||
**
|
||||
** Constraint type Action What Happens
|
||||
** --------------- ---------- ----------------------------------------
|
||||
** any ROLLBACK The current transaction is rolled back and
|
||||
** sqlite_exec() returns immediately with a
|
||||
** return code of SQLITE_CONSTRAINT.
|
||||
**
|
||||
** any ABORT Back out changes from the current command
|
||||
** only (do not do a complete rollback) then
|
||||
** cause sqlite_exec() to return immediately
|
||||
** with SQLITE_CONSTRAINT.
|
||||
**
|
||||
** any FAIL Sqlite_exec() returns immediately with a
|
||||
** return code of SQLITE_CONSTRAINT. The
|
||||
** transaction is not rolled back and any
|
||||
** prior changes are retained.
|
||||
**
|
||||
** any IGNORE The record number and data is popped from
|
||||
** the stack and there is an immediate jump
|
||||
** to label ignoreDest.
|
||||
**
|
||||
** NOT NULL REPLACE The NULL value is replace by the default
|
||||
** value for that column. If the default value
|
||||
** is NULL, the action is the same as ABORT.
|
||||
**
|
||||
** UNIQUE REPLACE The other row that conflicts with the row
|
||||
** being inserted is removed.
|
||||
**
|
||||
** CHECK REPLACE Illegal. The results in an exception.
|
||||
**
|
||||
** Which action to take is determined by the overrideError parameter.
|
||||
** Or if overrideError==OE_Default, then the pParse->onError parameter
|
||||
** is used. Or if pParse->onError==OE_Default then the onError value
|
||||
** for the constraint is used.
|
||||
**
|
||||
** The calling routine must open a read/write cursor for pTab with
|
||||
** cursor number "base". All indices of pTab must also have open
|
||||
** read/write cursors with cursor number base+i for the i-th cursor.
|
||||
** Except, if there is no possibility of a REPLACE action then
|
||||
** cursors do not need to be open for indices where aIdxUsed[i]==0.
|
||||
**
|
||||
** If the isUpdate flag is true, it means that the "base" cursor is
|
||||
** initially pointing to an entry that is being updated. The isUpdate
|
||||
** flag causes extra code to be generated so that the "base" cursor
|
||||
** is still pointing at the same entry after the routine returns.
|
||||
** Without the isUpdate flag, the "base" cursor might be moved.
|
||||
*/
|
||||
void sqliteGenerateConstraintChecks(
|
||||
Parse *pParse, /* The parser context */
|
||||
Table *pTab, /* the table into which we are inserting */
|
||||
int base, /* Index of a read/write cursor pointing at pTab */
|
||||
char *aIdxUsed, /* Which indices are used. NULL means all are used */
|
||||
int recnoChng, /* True if the record number will change */
|
||||
int isUpdate, /* True for UPDATE, False for INSERT */
|
||||
int overrideError, /* Override onError to this if not OE_Default */
|
||||
int ignoreDest /* Jump to this label on an OE_Ignore resolution */
|
||||
){
|
||||
int i;
|
||||
Vdbe *v;
|
||||
int nCol;
|
||||
int onError;
|
||||
int addr;
|
||||
int extra;
|
||||
int iCur;
|
||||
Index *pIdx;
|
||||
int seenReplace = 0;
|
||||
int jumpInst1, jumpInst2;
|
||||
int contAddr;
|
||||
int hasTwoRecnos = (isUpdate && recnoChng);
|
||||
|
||||
v = sqliteGetVdbe(pParse);
|
||||
assert( v!=0 );
|
||||
assert( pTab->pSelect==0 ); /* This table is not a VIEW */
|
||||
nCol = pTab->nCol;
|
||||
|
||||
/* Test all NOT NULL constraints.
|
||||
*/
|
||||
for(i=0; i<nCol; i++){
|
||||
if( i==pTab->iPKey ){
|
||||
continue;
|
||||
}
|
||||
onError = pTab->aCol[i].notNull;
|
||||
if( onError==OE_None ) continue;
|
||||
if( overrideError!=OE_Default ){
|
||||
onError = overrideError;
|
||||
}else if( pParse->db->onError!=OE_Default ){
|
||||
onError = pParse->db->onError;
|
||||
}else if( onError==OE_Default ){
|
||||
onError = OE_Abort;
|
||||
}
|
||||
if( onError==OE_Replace && pTab->aCol[i].zDflt==0 ){
|
||||
onError = OE_Abort;
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_Dup, nCol-1-i, 1);
|
||||
addr = sqliteVdbeAddOp(v, OP_NotNull, 1, 0);
|
||||
switch( onError ){
|
||||
case OE_Rollback:
|
||||
case OE_Abort:
|
||||
case OE_Fail: {
|
||||
char *zMsg = 0;
|
||||
sqliteVdbeAddOp(v, OP_Halt, SQLITE_CONSTRAINT, onError);
|
||||
sqliteSetString(&zMsg, pTab->zName, ".", pTab->aCol[i].zName,
|
||||
" may not be NULL", 0);
|
||||
sqliteVdbeChangeP3(v, -1, zMsg, P3_DYNAMIC);
|
||||
break;
|
||||
}
|
||||
case OE_Ignore: {
|
||||
sqliteVdbeAddOp(v, OP_Pop, nCol+1+hasTwoRecnos, 0);
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, ignoreDest);
|
||||
break;
|
||||
}
|
||||
case OE_Replace: {
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, pTab->aCol[i].zDflt, P3_STATIC);
|
||||
sqliteVdbeAddOp(v, OP_Push, nCol-i, 0);
|
||||
break;
|
||||
}
|
||||
default: assert(0);
|
||||
}
|
||||
sqliteVdbeChangeP2(v, addr, sqliteVdbeCurrentAddr(v));
|
||||
}
|
||||
|
||||
/* Test all CHECK constraints
|
||||
*/
|
||||
/**** TBD ****/
|
||||
|
||||
/* If we have an INTEGER PRIMARY KEY, make sure the primary key
|
||||
** of the new record does not previously exist. Except, if this
|
||||
** is an UPDATE and the primary key is not changing, that is OK.
|
||||
*/
|
||||
if( recnoChng ){
|
||||
onError = pTab->keyConf;
|
||||
if( overrideError!=OE_Default ){
|
||||
onError = overrideError;
|
||||
}else if( pParse->db->onError!=OE_Default ){
|
||||
onError = pParse->db->onError;
|
||||
}else if( onError==OE_Default ){
|
||||
onError = OE_Abort;
|
||||
}
|
||||
|
||||
if( isUpdate ){
|
||||
sqliteVdbeAddOp(v, OP_Dup, nCol+1, 1);
|
||||
sqliteVdbeAddOp(v, OP_Dup, nCol+1, 1);
|
||||
jumpInst1 = sqliteVdbeAddOp(v, OP_Eq, 0, 0);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_Dup, nCol, 1);
|
||||
jumpInst2 = sqliteVdbeAddOp(v, OP_NotExists, base, 0);
|
||||
switch( onError ){
|
||||
default: {
|
||||
onError = OE_Abort;
|
||||
/* Fall thru into the next case */
|
||||
}
|
||||
case OE_Rollback:
|
||||
case OE_Abort:
|
||||
case OE_Fail: {
|
||||
sqliteVdbeAddOp(v, OP_Halt, SQLITE_CONSTRAINT, onError);
|
||||
sqliteVdbeChangeP3(v, -1, "PRIMARY KEY must be unique", P3_STATIC);
|
||||
break;
|
||||
}
|
||||
case OE_Replace: {
|
||||
sqliteGenerateRowIndexDelete(pParse->db, v, pTab, base, 0);
|
||||
if( isUpdate ){
|
||||
sqliteVdbeAddOp(v, OP_Dup, nCol+hasTwoRecnos, 1);
|
||||
sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
|
||||
}
|
||||
seenReplace = 1;
|
||||
break;
|
||||
}
|
||||
case OE_Ignore: {
|
||||
assert( seenReplace==0 );
|
||||
sqliteVdbeAddOp(v, OP_Pop, nCol+1+hasTwoRecnos, 0);
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, ignoreDest);
|
||||
break;
|
||||
}
|
||||
}
|
||||
contAddr = sqliteVdbeCurrentAddr(v);
|
||||
sqliteVdbeChangeP2(v, jumpInst2, contAddr);
|
||||
if( isUpdate ){
|
||||
sqliteVdbeChangeP2(v, jumpInst1, contAddr);
|
||||
sqliteVdbeAddOp(v, OP_Dup, nCol+1, 1);
|
||||
sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Test all UNIQUE constraints by creating entries for each UNIQUE
|
||||
** index and making sure that duplicate entries do not already exist.
|
||||
** Add the new records to the indices as we go.
|
||||
*/
|
||||
extra = -1;
|
||||
for(iCur=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCur++){
|
||||
if( aIdxUsed && aIdxUsed[iCur]==0 ) continue; /* Skip unused indices */
|
||||
extra++;
|
||||
|
||||
/* Create a key for accessing the index entry */
|
||||
sqliteVdbeAddOp(v, OP_Dup, nCol+extra, 1);
|
||||
for(i=0; i<pIdx->nColumn; i++){
|
||||
int idx = pIdx->aiColumn[i];
|
||||
if( idx==pTab->iPKey ){
|
||||
sqliteVdbeAddOp(v, OP_Dup, i+extra+nCol+1, 1);
|
||||
}else{
|
||||
sqliteVdbeAddOp(v, OP_Dup, i+extra+nCol-idx, 1);
|
||||
}
|
||||
}
|
||||
jumpInst1 = sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0);
|
||||
if( pParse->db->file_format>=4 ) sqliteAddIdxKeyType(v, pIdx);
|
||||
|
||||
/* Find out what action to take in case there is an indexing conflict */
|
||||
onError = pIdx->onError;
|
||||
if( onError==OE_None ) continue; /* pIdx is not a UNIQUE index */
|
||||
if( overrideError!=OE_Default ){
|
||||
onError = overrideError;
|
||||
}else if( pParse->db->onError!=OE_Default ){
|
||||
onError = pParse->db->onError;
|
||||
}else if( onError==OE_Default ){
|
||||
onError = OE_Abort;
|
||||
}
|
||||
if( seenReplace ){
|
||||
if( onError==OE_Ignore ) onError = OE_Replace;
|
||||
else if( onError==OE_Fail ) onError = OE_Abort;
|
||||
}
|
||||
|
||||
|
||||
/* Check to see if the new index entry will be unique */
|
||||
sqliteVdbeAddOp(v, OP_Dup, extra+nCol+1+hasTwoRecnos, 1);
|
||||
jumpInst2 = sqliteVdbeAddOp(v, OP_IsUnique, base+iCur+1, 0);
|
||||
|
||||
/* Generate code that executes if the new index entry is not unique */
|
||||
switch( onError ){
|
||||
case OE_Rollback:
|
||||
case OE_Abort:
|
||||
case OE_Fail: {
|
||||
sqliteVdbeAddOp(v, OP_Halt, SQLITE_CONSTRAINT, onError);
|
||||
sqliteVdbeChangeP3(v, -1, "uniqueness constraint failed", P3_STATIC);
|
||||
break;
|
||||
}
|
||||
case OE_Ignore: {
|
||||
assert( seenReplace==0 );
|
||||
sqliteVdbeAddOp(v, OP_Pop, nCol+extra+3+hasTwoRecnos, 0);
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, ignoreDest);
|
||||
break;
|
||||
}
|
||||
case OE_Replace: {
|
||||
sqliteGenerateRowDelete(pParse->db, v, pTab, base, 0);
|
||||
if( isUpdate ){
|
||||
sqliteVdbeAddOp(v, OP_Dup, nCol+extra+1+hasTwoRecnos, 1);
|
||||
sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
|
||||
}
|
||||
seenReplace = 1;
|
||||
break;
|
||||
}
|
||||
default: assert(0);
|
||||
}
|
||||
contAddr = sqliteVdbeCurrentAddr(v);
|
||||
#if NULL_DISTINCT_FOR_UNIQUE
|
||||
sqliteVdbeChangeP2(v, jumpInst1, contAddr);
|
||||
#endif
|
||||
sqliteVdbeChangeP2(v, jumpInst2, contAddr);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine generates code to finish the INSERT or UPDATE operation
|
||||
** that was started by a prior call to sqliteGenerateConstraintChecks.
|
||||
** The stack must contain keys for all active indices followed by data
|
||||
** and the recno for the new entry. This routine creates the new
|
||||
** entries in all indices and in the main table.
|
||||
**
|
||||
** The arguments to this routine should be the same as the first six
|
||||
** arguments to sqliteGenerateConstraintChecks.
|
||||
*/
|
||||
void sqliteCompleteInsertion(
|
||||
Parse *pParse, /* The parser context */
|
||||
Table *pTab, /* the table into which we are inserting */
|
||||
int base, /* Index of a read/write cursor pointing at pTab */
|
||||
char *aIdxUsed, /* Which indices are used. NULL means all are used */
|
||||
int recnoChng, /* True if the record number will change */
|
||||
int isUpdate, /* True for UPDATE, False for INSERT */
|
||||
int newIdx /* Index of NEW table for triggers. -1 if none */
|
||||
){
|
||||
int i;
|
||||
Vdbe *v;
|
||||
int nIdx;
|
||||
Index *pIdx;
|
||||
|
||||
v = sqliteGetVdbe(pParse);
|
||||
assert( v!=0 );
|
||||
assert( pTab->pSelect==0 ); /* This table is not a VIEW */
|
||||
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){}
|
||||
for(i=nIdx-1; i>=0; i--){
|
||||
if( aIdxUsed && aIdxUsed[i]==0 ) continue;
|
||||
sqliteVdbeAddOp(v, OP_IdxPut, base+i+1, 0);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
|
||||
if( newIdx>=0 ){
|
||||
sqliteVdbeAddOp(v, OP_Dup, 1, 0);
|
||||
sqliteVdbeAddOp(v, OP_Dup, 1, 0);
|
||||
sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_PutIntKey, base, pParse->trigStack?0:1);
|
||||
if( isUpdate && recnoChng ){
|
||||
sqliteVdbeAddOp(v, OP_Pop, 1, 0);
|
||||
}
|
||||
}
|
||||
1011
sqlitebrowser/sqlitebrowser/sqlite_source/main.c
Executable file
132
sqlitebrowser/sqlitebrowser/sqlite_source/opcodes.c
Executable file
@@ -0,0 +1,132 @@
|
||||
/* Automatically generated file. Do not edit */
|
||||
char *sqliteOpcodeNames[] = { "???",
|
||||
"Goto",
|
||||
"Gosub",
|
||||
"Return",
|
||||
"Halt",
|
||||
"Integer",
|
||||
"String",
|
||||
"Pop",
|
||||
"Dup",
|
||||
"Pull",
|
||||
"Push",
|
||||
"ColumnName",
|
||||
"Callback",
|
||||
"NullCallback",
|
||||
"Concat",
|
||||
"Add",
|
||||
"Subtract",
|
||||
"Multiply",
|
||||
"Divide",
|
||||
"Remainder",
|
||||
"Function",
|
||||
"BitAnd",
|
||||
"BitOr",
|
||||
"ShiftLeft",
|
||||
"ShiftRight",
|
||||
"AddImm",
|
||||
"IsNumeric",
|
||||
"MustBeInt",
|
||||
"Eq",
|
||||
"Ne",
|
||||
"Lt",
|
||||
"Le",
|
||||
"Gt",
|
||||
"Ge",
|
||||
"StrEq",
|
||||
"StrNe",
|
||||
"StrLt",
|
||||
"StrLe",
|
||||
"StrGt",
|
||||
"StrGe",
|
||||
"And",
|
||||
"Or",
|
||||
"Negative",
|
||||
"AbsValue",
|
||||
"Not",
|
||||
"BitNot",
|
||||
"Noop",
|
||||
"If",
|
||||
"IfNot",
|
||||
"IsNull",
|
||||
"NotNull",
|
||||
"MakeRecord",
|
||||
"MakeIdxKey",
|
||||
"MakeKey",
|
||||
"IncrKey",
|
||||
"Checkpoint",
|
||||
"Transaction",
|
||||
"Commit",
|
||||
"Rollback",
|
||||
"ReadCookie",
|
||||
"SetCookie",
|
||||
"VerifyCookie",
|
||||
"OpenRead",
|
||||
"OpenWrite",
|
||||
"OpenTemp",
|
||||
"OpenPseudo",
|
||||
"Close",
|
||||
"MoveLt",
|
||||
"MoveTo",
|
||||
"Distinct",
|
||||
"NotFound",
|
||||
"Found",
|
||||
"IsUnique",
|
||||
"NotExists",
|
||||
"NewRecno",
|
||||
"PutIntKey",
|
||||
"PutStrKey",
|
||||
"Delete",
|
||||
"KeyAsData",
|
||||
"RowData",
|
||||
"Column",
|
||||
"Recno",
|
||||
"FullKey",
|
||||
"NullRow",
|
||||
"Last",
|
||||
"Rewind",
|
||||
"Prev",
|
||||
"Next",
|
||||
"IdxPut",
|
||||
"IdxDelete",
|
||||
"IdxRecno",
|
||||
"IdxLT",
|
||||
"IdxGT",
|
||||
"IdxGE",
|
||||
"Destroy",
|
||||
"Clear",
|
||||
"CreateIndex",
|
||||
"CreateTable",
|
||||
"IntegrityCk",
|
||||
"ListWrite",
|
||||
"ListRewind",
|
||||
"ListRead",
|
||||
"ListReset",
|
||||
"ListPush",
|
||||
"ListPop",
|
||||
"SortPut",
|
||||
"SortMakeRec",
|
||||
"SortMakeKey",
|
||||
"Sort",
|
||||
"SortNext",
|
||||
"SortCallback",
|
||||
"SortReset",
|
||||
"FileOpen",
|
||||
"FileRead",
|
||||
"FileColumn",
|
||||
"MemStore",
|
||||
"MemLoad",
|
||||
"MemIncr",
|
||||
"AggReset",
|
||||
"AggInit",
|
||||
"AggFunc",
|
||||
"AggFocus",
|
||||
"AggSet",
|
||||
"AggGet",
|
||||
"AggNext",
|
||||
"SetInsert",
|
||||
"SetFound",
|
||||
"SetNotFound",
|
||||
"SetFirst",
|
||||
"SetNext",
|
||||
};
|
||||
130
sqlitebrowser/sqlitebrowser/sqlite_source/opcodes.h
Executable file
@@ -0,0 +1,130 @@
|
||||
/* Automatically generated file. Do not edit */
|
||||
#define OP_Goto 1
|
||||
#define OP_Gosub 2
|
||||
#define OP_Return 3
|
||||
#define OP_Halt 4
|
||||
#define OP_Integer 5
|
||||
#define OP_String 6
|
||||
#define OP_Pop 7
|
||||
#define OP_Dup 8
|
||||
#define OP_Pull 9
|
||||
#define OP_Push 10
|
||||
#define OP_ColumnName 11
|
||||
#define OP_Callback 12
|
||||
#define OP_NullCallback 13
|
||||
#define OP_Concat 14
|
||||
#define OP_Add 15
|
||||
#define OP_Subtract 16
|
||||
#define OP_Multiply 17
|
||||
#define OP_Divide 18
|
||||
#define OP_Remainder 19
|
||||
#define OP_Function 20
|
||||
#define OP_BitAnd 21
|
||||
#define OP_BitOr 22
|
||||
#define OP_ShiftLeft 23
|
||||
#define OP_ShiftRight 24
|
||||
#define OP_AddImm 25
|
||||
#define OP_IsNumeric 26
|
||||
#define OP_MustBeInt 27
|
||||
#define OP_Eq 28
|
||||
#define OP_Ne 29
|
||||
#define OP_Lt 30
|
||||
#define OP_Le 31
|
||||
#define OP_Gt 32
|
||||
#define OP_Ge 33
|
||||
#define OP_StrEq 34
|
||||
#define OP_StrNe 35
|
||||
#define OP_StrLt 36
|
||||
#define OP_StrLe 37
|
||||
#define OP_StrGt 38
|
||||
#define OP_StrGe 39
|
||||
#define OP_And 40
|
||||
#define OP_Or 41
|
||||
#define OP_Negative 42
|
||||
#define OP_AbsValue 43
|
||||
#define OP_Not 44
|
||||
#define OP_BitNot 45
|
||||
#define OP_Noop 46
|
||||
#define OP_If 47
|
||||
#define OP_IfNot 48
|
||||
#define OP_IsNull 49
|
||||
#define OP_NotNull 50
|
||||
#define OP_MakeRecord 51
|
||||
#define OP_MakeIdxKey 52
|
||||
#define OP_MakeKey 53
|
||||
#define OP_IncrKey 54
|
||||
#define OP_Checkpoint 55
|
||||
#define OP_Transaction 56
|
||||
#define OP_Commit 57
|
||||
#define OP_Rollback 58
|
||||
#define OP_ReadCookie 59
|
||||
#define OP_SetCookie 60
|
||||
#define OP_VerifyCookie 61
|
||||
#define OP_OpenRead 62
|
||||
#define OP_OpenWrite 63
|
||||
#define OP_OpenTemp 64
|
||||
#define OP_OpenPseudo 65
|
||||
#define OP_Close 66
|
||||
#define OP_MoveLt 67
|
||||
#define OP_MoveTo 68
|
||||
#define OP_Distinct 69
|
||||
#define OP_NotFound 70
|
||||
#define OP_Found 71
|
||||
#define OP_IsUnique 72
|
||||
#define OP_NotExists 73
|
||||
#define OP_NewRecno 74
|
||||
#define OP_PutIntKey 75
|
||||
#define OP_PutStrKey 76
|
||||
#define OP_Delete 77
|
||||
#define OP_KeyAsData 78
|
||||
#define OP_RowData 79
|
||||
#define OP_Column 80
|
||||
#define OP_Recno 81
|
||||
#define OP_FullKey 82
|
||||
#define OP_NullRow 83
|
||||
#define OP_Last 84
|
||||
#define OP_Rewind 85
|
||||
#define OP_Prev 86
|
||||
#define OP_Next 87
|
||||
#define OP_IdxPut 88
|
||||
#define OP_IdxDelete 89
|
||||
#define OP_IdxRecno 90
|
||||
#define OP_IdxLT 91
|
||||
#define OP_IdxGT 92
|
||||
#define OP_IdxGE 93
|
||||
#define OP_Destroy 94
|
||||
#define OP_Clear 95
|
||||
#define OP_CreateIndex 96
|
||||
#define OP_CreateTable 97
|
||||
#define OP_IntegrityCk 98
|
||||
#define OP_ListWrite 99
|
||||
#define OP_ListRewind 100
|
||||
#define OP_ListRead 101
|
||||
#define OP_ListReset 102
|
||||
#define OP_ListPush 103
|
||||
#define OP_ListPop 104
|
||||
#define OP_SortPut 105
|
||||
#define OP_SortMakeRec 106
|
||||
#define OP_SortMakeKey 107
|
||||
#define OP_Sort 108
|
||||
#define OP_SortNext 109
|
||||
#define OP_SortCallback 110
|
||||
#define OP_SortReset 111
|
||||
#define OP_FileOpen 112
|
||||
#define OP_FileRead 113
|
||||
#define OP_FileColumn 114
|
||||
#define OP_MemStore 115
|
||||
#define OP_MemLoad 116
|
||||
#define OP_MemIncr 117
|
||||
#define OP_AggReset 118
|
||||
#define OP_AggInit 119
|
||||
#define OP_AggFunc 120
|
||||
#define OP_AggFocus 121
|
||||
#define OP_AggSet 122
|
||||
#define OP_AggGet 123
|
||||
#define OP_AggNext 124
|
||||
#define OP_SetInsert 125
|
||||
#define OP_SetFound 126
|
||||
#define OP_SetNotFound 127
|
||||
#define OP_SetFirst 128
|
||||
#define OP_SetNext 129
|
||||
1544
sqlitebrowser/sqlitebrowser/sqlite_source/os.c
Executable file
190
sqlitebrowser/sqlitebrowser/sqlite_source/os.h
Executable file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
** 2001 September 16
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** This header file (together with is companion C source-code file
|
||||
** "os.c") attempt to abstract the underlying operating system so that
|
||||
** the SQLite library will work on both POSIX and windows systems.
|
||||
*/
|
||||
#ifndef _SQLITE_OS_H_
|
||||
#define _SQLITE_OS_H_
|
||||
|
||||
/*
|
||||
** Make sure we can call this stuff from C++.
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Helpful hint: To get this to compile on HP/UX, add -D_INCLUDE_POSIX_SOURCE
|
||||
** to the compiler command line.
|
||||
*/
|
||||
|
||||
/*
|
||||
** These #defines should enable >2GB file support on Posix if the
|
||||
** underlying operating system supports it. If the OS lacks
|
||||
** large file support, or if the OS is windows, these should be no-ops.
|
||||
**
|
||||
** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch
|
||||
** on the compiler command line. This is necessary if you are compiling
|
||||
** on a recent machine (ex: RedHat 7.2) but you want your code to work
|
||||
** on an older machine (ex: RedHat 6.0). If you compile on RedHat 7.2
|
||||
** without this option, LFS is enable. But LFS does not exist in the kernel
|
||||
** in RedHat 6.0, so the code won't work. Hence, for maximum binary
|
||||
** portability you should omit LFS.
|
||||
**
|
||||
** Similar is true for MacOS. LFS is only supported on MacOS 9 and later.
|
||||
*/
|
||||
#ifndef SQLITE_DISABLE_LFS
|
||||
# define _LARGE_FILE 1
|
||||
# define _FILE_OFFSET_BITS 64
|
||||
# define _LARGEFILE_SOURCE 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Temporary files are named starting with this prefix followed by 16 random
|
||||
** alphanumeric characters, and no file extension. They are stored in the
|
||||
** OS's standard temporary file directory, and are deleted prior to exit.
|
||||
** If sqlite is being embedded in another program, you may wish to change the
|
||||
** prefix to reflect your program's name, so that if your program exits
|
||||
** prematurely, old temporary files can be easily identified. This can be done
|
||||
** using -DTEMP_FILE_PREFIX=myprefix_ on the compiler command line.
|
||||
*/
|
||||
#ifndef TEMP_FILE_PREFIX
|
||||
# define TEMP_FILE_PREFIX "sqlite_"
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Figure out if we are dealing with Unix, Windows or MacOS.
|
||||
**
|
||||
** N.B. MacOS means Mac Classic (or Carbon). Treat Darwin (OS X) as Unix.
|
||||
** The MacOS build is designed to use CodeWarrior (tested with v8)
|
||||
*/
|
||||
#ifndef OS_UNIX
|
||||
# ifndef OS_WIN
|
||||
# ifndef OS_MAC
|
||||
# if defined(__MACOS__)
|
||||
# define OS_MAC 1
|
||||
# define OS_WIN 0
|
||||
# define OS_UNIX 0
|
||||
# elif defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__)
|
||||
# define OS_MAC 0
|
||||
# define OS_WIN 1
|
||||
# define OS_UNIX 0
|
||||
# else
|
||||
# define OS_MAC 0
|
||||
# define OS_WIN 0
|
||||
# define OS_UNIX 1
|
||||
# endif
|
||||
# else
|
||||
# define OS_WIN 0
|
||||
# define OS_UNIX 0
|
||||
# endif
|
||||
# else
|
||||
# define OS_MAC 0
|
||||
# define OS_UNIX 0
|
||||
# endif
|
||||
#else
|
||||
# define OS_MAC 0
|
||||
# define OS_WIN 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
** A handle for an open file is stored in an OsFile object.
|
||||
*/
|
||||
#if OS_UNIX
|
||||
# include <sys/types.h>
|
||||
# include <sys/stat.h>
|
||||
# include <fcntl.h>
|
||||
# include <unistd.h>
|
||||
typedef struct OsFile OsFile;
|
||||
struct OsFile {
|
||||
struct lockInfo *pLock; /* Information about locks on this inode */
|
||||
int fd; /* The file descriptor */
|
||||
int locked; /* True if this user holds the lock */
|
||||
};
|
||||
# define SQLITE_TEMPNAME_SIZE 200
|
||||
# if defined(HAVE_USLEEP) && HAVE_USLEEP
|
||||
# define SQLITE_MIN_SLEEP_MS 1
|
||||
# else
|
||||
# define SQLITE_MIN_SLEEP_MS 1000
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if OS_WIN
|
||||
#include <windows.h>
|
||||
#include <winbase.h>
|
||||
typedef struct OsFile OsFile;
|
||||
struct OsFile {
|
||||
HANDLE h; /* Handle for accessing the file */
|
||||
int locked; /* 0: unlocked, <0: write lock, >0: read lock */
|
||||
};
|
||||
|
||||
#ifndef _OFF_T_DEFINED
|
||||
# if defined(_MSC_VER) || defined(__BORLANDC__)
|
||||
typedef __int64 off_t;
|
||||
# else
|
||||
typedef long long off_t;
|
||||
# endif
|
||||
#endif
|
||||
# define SQLITE_TEMPNAME_SIZE (MAX_PATH+50)
|
||||
# define SQLITE_MIN_SLEEP_MS 1
|
||||
#endif
|
||||
|
||||
#if OS_MAC
|
||||
# include <unistd.h>
|
||||
# include <Files.h>
|
||||
typedef struct OsFile OsFile;
|
||||
struct OsFile {
|
||||
SInt16 refNum; /* Data fork/file reference number */
|
||||
SInt16 refNumRF; /* Resource fork reference number (for locking) */
|
||||
int locked; /* 0: unlocked, <0: write lock, >0: read lock */
|
||||
int delOnClose; /* True if file is to be deleted on close */
|
||||
char *pathToDel; /* Name of file to delete on close */
|
||||
};
|
||||
# ifdef _LARGE_FILE
|
||||
typedef SInt64 off_t;
|
||||
# else
|
||||
typedef SInt32 off_t;
|
||||
# endif
|
||||
# define SQLITE_TEMPNAME_SIZE _MAX_PATH
|
||||
# define SQLITE_MIN_SLEEP_MS 17
|
||||
#endif
|
||||
|
||||
int sqliteOsDelete(const char*);
|
||||
int sqliteOsFileExists(const char*);
|
||||
int sqliteOsFileRename(const char*, const char*);
|
||||
int sqliteOsOpenReadWrite(const char*, OsFile*, int*);
|
||||
int sqliteOsOpenExclusive(const char*, OsFile*, int);
|
||||
int sqliteOsOpenReadOnly(const char*, OsFile*);
|
||||
int sqliteOsTempFileName(char*);
|
||||
int sqliteOsClose(OsFile*);
|
||||
int sqliteOsRead(OsFile*, void*, int amt);
|
||||
int sqliteOsWrite(OsFile*, const void*, int amt);
|
||||
int sqliteOsSeek(OsFile*, off_t offset);
|
||||
int sqliteOsSync(OsFile*);
|
||||
int sqliteOsTruncate(OsFile*, off_t size);
|
||||
int sqliteOsFileSize(OsFile*, off_t *pSize);
|
||||
int sqliteOsReadLock(OsFile*);
|
||||
int sqliteOsWriteLock(OsFile*);
|
||||
int sqliteOsUnlock(OsFile*);
|
||||
int sqliteOsRandomSeed(char*);
|
||||
int sqliteOsSleep(int ms);
|
||||
void sqliteOsEnterMutex(void);
|
||||
void sqliteOsLeaveMutex(void);
|
||||
char *sqliteOsFullPathname(const char*);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* End of the 'extern "C"' block */
|
||||
#endif
|
||||
|
||||
#endif /* _SQLITE_OS_H_ */
|
||||
2086
sqlitebrowser/sqlitebrowser/sqlite_source/pager.c
Executable file
83
sqlitebrowser/sqlitebrowser/sqlite_source/pager.h
Executable file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
** 2001 September 15
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This header file defines the interface that the sqlite page cache
|
||||
** subsystem. The page cache subsystem reads and writes a file a page
|
||||
** at a time and provides a journal for rollback.
|
||||
**
|
||||
** @(#) $Id: pager.h,v 1.1.1.1 2003-08-21 02:24:12 tabuleiro Exp $
|
||||
*/
|
||||
|
||||
/*
|
||||
** The size of one page
|
||||
**
|
||||
** You can change this value to another (reasonable) power of two
|
||||
** such as 512, 2048, 4096, or 8192 and things will still work. But
|
||||
** experiments show that a page size of 1024 gives the best speed.
|
||||
** (The speed differences are minimal.)
|
||||
*/
|
||||
#define SQLITE_PAGE_SIZE 1024
|
||||
|
||||
/*
|
||||
** Maximum number of pages in one database. (This is a limitation of
|
||||
** imposed by 4GB files size limits.)
|
||||
*/
|
||||
#define SQLITE_MAX_PAGE 1073741823
|
||||
|
||||
/*
|
||||
** The type used to represent a page number. The first page in a file
|
||||
** is called page 1. 0 is used to represent "not a page".
|
||||
*/
|
||||
typedef unsigned int Pgno;
|
||||
|
||||
/*
|
||||
** Each open file is managed by a separate instance of the "Pager" structure.
|
||||
*/
|
||||
typedef struct Pager Pager;
|
||||
|
||||
/*
|
||||
** See source code comments for a detailed description of the following
|
||||
** routines:
|
||||
*/
|
||||
int sqlitepager_open(Pager **ppPager, const char *zFilename,
|
||||
int nPage, int nExtra, int useJournal);
|
||||
void sqlitepager_set_destructor(Pager*, void(*)(void*));
|
||||
void sqlitepager_set_cachesize(Pager*, int);
|
||||
int sqlitepager_close(Pager *pPager);
|
||||
int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage);
|
||||
void *sqlitepager_lookup(Pager *pPager, Pgno pgno);
|
||||
int sqlitepager_ref(void*);
|
||||
int sqlitepager_unref(void*);
|
||||
Pgno sqlitepager_pagenumber(void*);
|
||||
int sqlitepager_write(void*);
|
||||
int sqlitepager_iswriteable(void*);
|
||||
int sqlitepager_overwrite(Pager *pPager, Pgno pgno, void*);
|
||||
int sqlitepager_pagecount(Pager*);
|
||||
int sqlitepager_truncate(Pager*,Pgno);
|
||||
int sqlitepager_begin(void*);
|
||||
int sqlitepager_commit(Pager*);
|
||||
int sqlitepager_rollback(Pager*);
|
||||
int sqlitepager_isreadonly(Pager*);
|
||||
int sqlitepager_ckpt_begin(Pager*);
|
||||
int sqlitepager_ckpt_commit(Pager*);
|
||||
int sqlitepager_ckpt_rollback(Pager*);
|
||||
void sqlitepager_dont_rollback(void*);
|
||||
void sqlitepager_dont_write(Pager*, Pgno);
|
||||
int *sqlitepager_stats(Pager*);
|
||||
void sqlitepager_set_safety_level(Pager*,int);
|
||||
const char *sqlitepager_filename(Pager*);
|
||||
int sqlitepager_rename(Pager*, const char *zNewName);
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
void sqlitepager_refdump(Pager*);
|
||||
int pager_refinfo_enable;
|
||||
int journal_format;
|
||||
#endif
|
||||
7103
sqlitebrowser/sqlitebrowser/sqlite_source/parse.c
Executable file
130
sqlitebrowser/sqlitebrowser/sqlite_source/parse.h
Executable file
@@ -0,0 +1,130 @@
|
||||
#define TK_ABORT 1
|
||||
#define TK_AFTER 2
|
||||
#define TK_AGG_FUNCTION 3
|
||||
#define TK_ALL 4
|
||||
#define TK_AND 5
|
||||
#define TK_AS 6
|
||||
#define TK_ASC 7
|
||||
#define TK_ATTACH 8
|
||||
#define TK_BEFORE 9
|
||||
#define TK_BEGIN 10
|
||||
#define TK_BETWEEN 11
|
||||
#define TK_BITAND 12
|
||||
#define TK_BITNOT 13
|
||||
#define TK_BITOR 14
|
||||
#define TK_BY 15
|
||||
#define TK_CASCADE 16
|
||||
#define TK_CASE 17
|
||||
#define TK_CHECK 18
|
||||
#define TK_CLUSTER 19
|
||||
#define TK_COLLATE 20
|
||||
#define TK_COLUMN 21
|
||||
#define TK_COMMA 22
|
||||
#define TK_COMMENT 23
|
||||
#define TK_COMMIT 24
|
||||
#define TK_CONCAT 25
|
||||
#define TK_CONFLICT 26
|
||||
#define TK_CONSTRAINT 27
|
||||
#define TK_COPY 28
|
||||
#define TK_CREATE 29
|
||||
#define TK_DATABASE 30
|
||||
#define TK_DEFAULT 31
|
||||
#define TK_DEFERRABLE 32
|
||||
#define TK_DEFERRED 33
|
||||
#define TK_DELETE 34
|
||||
#define TK_DELIMITERS 35
|
||||
#define TK_DESC 36
|
||||
#define TK_DETACH 37
|
||||
#define TK_DISTINCT 38
|
||||
#define TK_DOT 39
|
||||
#define TK_DROP 40
|
||||
#define TK_EACH 41
|
||||
#define TK_ELSE 42
|
||||
#define TK_END 43
|
||||
#define TK_END_OF_FILE 44
|
||||
#define TK_EQ 45
|
||||
#define TK_EXCEPT 46
|
||||
#define TK_EXPLAIN 47
|
||||
#define TK_FAIL 48
|
||||
#define TK_FLOAT 49
|
||||
#define TK_FOR 50
|
||||
#define TK_FOREIGN 51
|
||||
#define TK_FROM 52
|
||||
#define TK_FUNCTION 53
|
||||
#define TK_GE 54
|
||||
#define TK_GLOB 55
|
||||
#define TK_GROUP 56
|
||||
#define TK_GT 57
|
||||
#define TK_HAVING 58
|
||||
#define TK_ID 59
|
||||
#define TK_IGNORE 60
|
||||
#define TK_ILLEGAL 61
|
||||
#define TK_IMMEDIATE 62
|
||||
#define TK_IN 63
|
||||
#define TK_INDEX 64
|
||||
#define TK_INITIALLY 65
|
||||
#define TK_INSERT 66
|
||||
#define TK_INSTEAD 67
|
||||
#define TK_INTEGER 68
|
||||
#define TK_INTERSECT 69
|
||||
#define TK_INTO 70
|
||||
#define TK_IS 71
|
||||
#define TK_ISNULL 72
|
||||
#define TK_JOIN 73
|
||||
#define TK_JOIN_KW 74
|
||||
#define TK_KEY 75
|
||||
#define TK_LE 76
|
||||
#define TK_LIKE 77
|
||||
#define TK_LIMIT 78
|
||||
#define TK_LP 79
|
||||
#define TK_LSHIFT 80
|
||||
#define TK_LT 81
|
||||
#define TK_MATCH 82
|
||||
#define TK_MINUS 83
|
||||
#define TK_NE 84
|
||||
#define TK_NOT 85
|
||||
#define TK_NOTNULL 86
|
||||
#define TK_NULL 87
|
||||
#define TK_OF 88
|
||||
#define TK_OFFSET 89
|
||||
#define TK_ON 90
|
||||
#define TK_OR 91
|
||||
#define TK_ORACLE_OUTER_JOIN 92
|
||||
#define TK_ORDER 93
|
||||
#define TK_PLUS 94
|
||||
#define TK_PRAGMA 95
|
||||
#define TK_PRIMARY 96
|
||||
#define TK_RAISE 97
|
||||
#define TK_REFERENCES 98
|
||||
#define TK_REM 99
|
||||
#define TK_REPLACE 100
|
||||
#define TK_RESTRICT 101
|
||||
#define TK_ROLLBACK 102
|
||||
#define TK_ROW 103
|
||||
#define TK_RP 104
|
||||
#define TK_RSHIFT 105
|
||||
#define TK_SELECT 106
|
||||
#define TK_SEMI 107
|
||||
#define TK_SET 108
|
||||
#define TK_SLASH 109
|
||||
#define TK_SPACE 110
|
||||
#define TK_STAR 111
|
||||
#define TK_STATEMENT 112
|
||||
#define TK_STRING 113
|
||||
#define TK_TABLE 114
|
||||
#define TK_TEMP 115
|
||||
#define TK_THEN 116
|
||||
#define TK_TRANSACTION 117
|
||||
#define TK_TRIGGER 118
|
||||
#define TK_UMINUS 119
|
||||
#define TK_UNCLOSED_STRING 120
|
||||
#define TK_UNION 121
|
||||
#define TK_UNIQUE 122
|
||||
#define TK_UPDATE 123
|
||||
#define TK_UPLUS 124
|
||||
#define TK_USING 125
|
||||
#define TK_VACUUM 126
|
||||
#define TK_VALUES 127
|
||||
#define TK_VIEW 128
|
||||
#define TK_WHEN 129
|
||||
#define TK_WHERE 130
|
||||
667
sqlitebrowser/sqlitebrowser/sqlite_source/pragma.c
Executable file
@@ -0,0 +1,667 @@
|
||||
/*
|
||||
** 2003 April 6
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains code used to implement the PRAGMA command.
|
||||
**
|
||||
** $Id: pragma.c,v 1.1.1.1 2003-08-21 02:24:17 tabuleiro Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
|
||||
/*
|
||||
** Interpret the given string as a boolean value.
|
||||
*/
|
||||
static int getBoolean(char *z){
|
||||
static char *azTrue[] = { "yes", "on", "true" };
|
||||
int i;
|
||||
if( z[0]==0 ) return 0;
|
||||
if( isdigit(z[0]) || (z[0]=='-' && isdigit(z[1])) ){
|
||||
return atoi(z);
|
||||
}
|
||||
for(i=0; i<sizeof(azTrue)/sizeof(azTrue[0]); i++){
|
||||
if( sqliteStrICmp(z,azTrue[i])==0 ) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Interpret the given string as a safety level. Return 0 for OFF,
|
||||
** 1 for ON or NORMAL and 2 for FULL.
|
||||
**
|
||||
** Note that the values returned are one less that the values that
|
||||
** should be passed into sqliteBtreeSetSafetyLevel(). The is done
|
||||
** to support legacy SQL code. The safety level used to be boolean
|
||||
** and older scripts may have used numbers 0 for OFF and 1 for ON.
|
||||
*/
|
||||
static int getSafetyLevel(char *z){
|
||||
static const struct {
|
||||
const char *zWord;
|
||||
int val;
|
||||
} aKey[] = {
|
||||
{ "no", 0 },
|
||||
{ "off", 0 },
|
||||
{ "false", 0 },
|
||||
{ "yes", 1 },
|
||||
{ "on", 1 },
|
||||
{ "true", 1 },
|
||||
{ "full", 2 },
|
||||
};
|
||||
int i;
|
||||
if( z[0]==0 ) return 1;
|
||||
if( isdigit(z[0]) || (z[0]=='-' && isdigit(z[1])) ){
|
||||
return atoi(z);
|
||||
}
|
||||
for(i=0; i<sizeof(aKey)/sizeof(aKey[0]); i++){
|
||||
if( sqliteStrICmp(z,aKey[i].zWord)==0 ) return aKey[i].val;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Interpret the given string as a temp db location. Return 1 for file
|
||||
** backed temporary databases, 2 for the Red-Black tree in memory database
|
||||
** and 0 to use the compile-time default.
|
||||
*/
|
||||
static int getTempStore(char *z){
|
||||
if( z[0]>='0' || z[0]<='2' ){
|
||||
return z[0] - '0';
|
||||
}else if( sqliteStrICmp(z, "file")==0 ){
|
||||
return 1;
|
||||
}else if( sqliteStrICmp(z, "memory")==0 ){
|
||||
return 2;
|
||||
}else{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Process a pragma statement.
|
||||
**
|
||||
** Pragmas are of this form:
|
||||
**
|
||||
** PRAGMA id = value
|
||||
**
|
||||
** The identifier might also be a string. The value is a string, and
|
||||
** identifier, or a number. If minusFlag is true, then the value is
|
||||
** a number that was preceded by a minus sign.
|
||||
*/
|
||||
void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
|
||||
char *zLeft = 0;
|
||||
char *zRight = 0;
|
||||
sqlite *db = pParse->db;
|
||||
Vdbe *v = sqliteGetVdbe(pParse);
|
||||
if( v==0 ) return;
|
||||
|
||||
zLeft = sqliteStrNDup(pLeft->z, pLeft->n);
|
||||
sqliteDequote(zLeft);
|
||||
if( minusFlag ){
|
||||
zRight = 0;
|
||||
sqliteSetNString(&zRight, "-", 1, pRight->z, pRight->n, 0);
|
||||
}else{
|
||||
zRight = sqliteStrNDup(pRight->z, pRight->n);
|
||||
sqliteDequote(zRight);
|
||||
}
|
||||
if( sqliteAuthCheck(pParse, SQLITE_PRAGMA, zLeft, zRight, 0) ){
|
||||
sqliteFree(zLeft);
|
||||
sqliteFree(zRight);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
** PRAGMA default_cache_size
|
||||
** PRAGMA default_cache_size=N
|
||||
**
|
||||
** The first form reports the current persistent setting for the
|
||||
** page cache size. The value returned is the maximum number of
|
||||
** pages in the page cache. The second form sets both the current
|
||||
** page cache size value and the persistent page cache size value
|
||||
** stored in the database file.
|
||||
**
|
||||
** The default cache size is stored in meta-value 2 of page 1 of the
|
||||
** database file. The cache size is actually the absolute value of
|
||||
** this memory location. The sign of meta-value 2 determines the
|
||||
** synchronous setting. A negative value means synchronous is off
|
||||
** and a positive value means synchronous is on.
|
||||
*/
|
||||
if( sqliteStrICmp(zLeft,"default_cache_size")==0 ){
|
||||
static VdbeOp getCacheSize[] = {
|
||||
{ OP_ReadCookie, 0, 2, 0},
|
||||
{ OP_AbsValue, 0, 0, 0},
|
||||
{ OP_Dup, 0, 0, 0},
|
||||
{ OP_Integer, 0, 0, 0},
|
||||
{ OP_Ne, 0, 6, 0},
|
||||
{ OP_Integer, MAX_PAGES,0, 0},
|
||||
{ OP_ColumnName, 0, 0, "cache_size"},
|
||||
{ OP_Callback, 1, 0, 0},
|
||||
};
|
||||
if( pRight->z==pLeft->z ){
|
||||
sqliteVdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize);
|
||||
}else{
|
||||
int addr;
|
||||
int size = atoi(zRight);
|
||||
if( size<0 ) size = -size;
|
||||
sqliteBeginWriteOperation(pParse, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Integer, size, 0);
|
||||
sqliteVdbeAddOp(v, OP_ReadCookie, 0, 2);
|
||||
addr = sqliteVdbeAddOp(v, OP_Integer, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Ge, 0, addr+3);
|
||||
sqliteVdbeAddOp(v, OP_Negative, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_SetCookie, 0, 2);
|
||||
sqliteEndWriteOperation(pParse);
|
||||
db->cache_size = db->cache_size<0 ? -size : size;
|
||||
sqliteBtreeSetCacheSize(db->aDb[0].pBt, db->cache_size);
|
||||
}
|
||||
}else
|
||||
|
||||
/*
|
||||
** PRAGMA cache_size
|
||||
** PRAGMA cache_size=N
|
||||
**
|
||||
** The first form reports the current local setting for the
|
||||
** page cache size. The local setting can be different from
|
||||
** the persistent cache size value that is stored in the database
|
||||
** file itself. The value returned is the maximum number of
|
||||
** pages in the page cache. The second form sets the local
|
||||
** page cache size value. It does not change the persistent
|
||||
** cache size stored on the disk so the cache size will revert
|
||||
** to its default value when the database is closed and reopened.
|
||||
** N should be a positive integer.
|
||||
*/
|
||||
if( sqliteStrICmp(zLeft,"cache_size")==0 ){
|
||||
static VdbeOp getCacheSize[] = {
|
||||
{ OP_ColumnName, 0, 0, "cache_size"},
|
||||
{ OP_Callback, 1, 0, 0},
|
||||
};
|
||||
if( pRight->z==pLeft->z ){
|
||||
int size = db->cache_size;;
|
||||
if( size<0 ) size = -size;
|
||||
sqliteVdbeAddOp(v, OP_Integer, size, 0);
|
||||
sqliteVdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize);
|
||||
}else{
|
||||
int size = atoi(zRight);
|
||||
if( size<0 ) size = -size;
|
||||
if( db->cache_size<0 ) size = -size;
|
||||
db->cache_size = size;
|
||||
sqliteBtreeSetCacheSize(db->aDb[0].pBt, db->cache_size);
|
||||
}
|
||||
}else
|
||||
|
||||
/*
|
||||
** PRAGMA default_synchronous
|
||||
** PRAGMA default_synchronous=ON|OFF|NORMAL|FULL
|
||||
**
|
||||
** The first form returns the persistent value of the "synchronous" setting
|
||||
** that is stored in the database. This is the synchronous setting that
|
||||
** is used whenever the database is opened unless overridden by a separate
|
||||
** "synchronous" pragma. The second form changes the persistent and the
|
||||
** local synchronous setting to the value given.
|
||||
**
|
||||
** If synchronous is OFF, SQLite does not attempt any fsync() systems calls
|
||||
** to make sure data is committed to disk. Write operations are very fast,
|
||||
** but a power failure can leave the database in an inconsistent state.
|
||||
** If synchronous is ON or NORMAL, SQLite will do an fsync() system call to
|
||||
** make sure data is being written to disk. The risk of corruption due to
|
||||
** a power loss in this mode is negligible but non-zero. If synchronous
|
||||
** is FULL, extra fsync()s occur to reduce the risk of corruption to near
|
||||
** zero, but with a write performance penalty. The default mode is NORMAL.
|
||||
*/
|
||||
if( sqliteStrICmp(zLeft,"default_synchronous")==0 ){
|
||||
static VdbeOp getSync[] = {
|
||||
{ OP_ColumnName, 0, 0, "synchronous"},
|
||||
{ OP_ReadCookie, 0, 3, 0},
|
||||
{ OP_Dup, 0, 0, 0},
|
||||
{ OP_If, 0, 0, 0}, /* 3 */
|
||||
{ OP_ReadCookie, 0, 2, 0},
|
||||
{ OP_Integer, 0, 0, 0},
|
||||
{ OP_Lt, 0, 5, 0},
|
||||
{ OP_AddImm, 1, 0, 0},
|
||||
{ OP_Callback, 1, 0, 0},
|
||||
{ OP_Halt, 0, 0, 0},
|
||||
{ OP_AddImm, -1, 0, 0}, /* 10 */
|
||||
{ OP_Callback, 1, 0, 0}
|
||||
};
|
||||
if( pRight->z==pLeft->z ){
|
||||
int addr = sqliteVdbeAddOpList(v, ArraySize(getSync), getSync);
|
||||
sqliteVdbeChangeP2(v, addr+3, addr+10);
|
||||
}else{
|
||||
int addr;
|
||||
int size = db->cache_size;
|
||||
if( size<0 ) size = -size;
|
||||
sqliteBeginWriteOperation(pParse, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_ReadCookie, 0, 2);
|
||||
sqliteVdbeAddOp(v, OP_Dup, 0, 0);
|
||||
addr = sqliteVdbeAddOp(v, OP_Integer, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Ne, 0, addr+3);
|
||||
sqliteVdbeAddOp(v, OP_AddImm, MAX_PAGES, 0);
|
||||
sqliteVdbeAddOp(v, OP_AbsValue, 0, 0);
|
||||
db->safety_level = getSafetyLevel(zRight)+1;
|
||||
if( db->safety_level==1 ){
|
||||
sqliteVdbeAddOp(v, OP_Negative, 0, 0);
|
||||
size = -size;
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_SetCookie, 0, 2);
|
||||
sqliteVdbeAddOp(v, OP_Integer, db->safety_level, 0);
|
||||
sqliteVdbeAddOp(v, OP_SetCookie, 0, 3);
|
||||
sqliteEndWriteOperation(pParse);
|
||||
db->cache_size = size;
|
||||
sqliteBtreeSetCacheSize(db->aDb[0].pBt, db->cache_size);
|
||||
sqliteBtreeSetSafetyLevel(db->aDb[0].pBt, db->safety_level);
|
||||
}
|
||||
}else
|
||||
|
||||
/*
|
||||
** PRAGMA synchronous
|
||||
** PRAGMA synchronous=OFF|ON|NORMAL|FULL
|
||||
**
|
||||
** Return or set the local value of the synchronous flag. Changing
|
||||
** the local value does not make changes to the disk file and the
|
||||
** default value will be restored the next time the database is
|
||||
** opened.
|
||||
*/
|
||||
if( sqliteStrICmp(zLeft,"synchronous")==0 ){
|
||||
static VdbeOp getSync[] = {
|
||||
{ OP_ColumnName, 0, 0, "synchronous"},
|
||||
{ OP_Callback, 1, 0, 0},
|
||||
};
|
||||
if( pRight->z==pLeft->z ){
|
||||
sqliteVdbeAddOp(v, OP_Integer, db->safety_level-1, 0);
|
||||
sqliteVdbeAddOpList(v, ArraySize(getSync), getSync);
|
||||
}else{
|
||||
int size = db->cache_size;
|
||||
if( size<0 ) size = -size;
|
||||
db->safety_level = getSafetyLevel(zRight)+1;
|
||||
if( db->safety_level==1 ) size = -size;
|
||||
db->cache_size = size;
|
||||
sqliteBtreeSetCacheSize(db->aDb[0].pBt, db->cache_size);
|
||||
sqliteBtreeSetSafetyLevel(db->aDb[0].pBt, db->safety_level);
|
||||
}
|
||||
}else
|
||||
|
||||
if( sqliteStrICmp(zLeft, "trigger_overhead_test")==0 ){
|
||||
if( getBoolean(zRight) ){
|
||||
always_code_trigger_setup = 1;
|
||||
}else{
|
||||
always_code_trigger_setup = 0;
|
||||
}
|
||||
}else
|
||||
|
||||
if( sqliteStrICmp(zLeft, "vdbe_trace")==0 ){
|
||||
if( getBoolean(zRight) ){
|
||||
db->flags |= SQLITE_VdbeTrace;
|
||||
}else{
|
||||
db->flags &= ~SQLITE_VdbeTrace;
|
||||
}
|
||||
}else
|
||||
|
||||
if( sqliteStrICmp(zLeft, "full_column_names")==0 ){
|
||||
if( getBoolean(zRight) ){
|
||||
db->flags |= SQLITE_FullColNames;
|
||||
}else{
|
||||
db->flags &= ~SQLITE_FullColNames;
|
||||
}
|
||||
}else
|
||||
|
||||
if( sqliteStrICmp(zLeft, "show_datatypes")==0 ){
|
||||
if( getBoolean(zRight) ){
|
||||
db->flags |= SQLITE_ReportTypes;
|
||||
}else{
|
||||
db->flags &= ~SQLITE_ReportTypes;
|
||||
}
|
||||
}else
|
||||
|
||||
if( sqliteStrICmp(zLeft, "count_changes")==0 ){
|
||||
if( getBoolean(zRight) ){
|
||||
db->flags |= SQLITE_CountRows;
|
||||
}else{
|
||||
db->flags &= ~SQLITE_CountRows;
|
||||
}
|
||||
}else
|
||||
|
||||
if( sqliteStrICmp(zLeft, "empty_result_callbacks")==0 ){
|
||||
if( getBoolean(zRight) ){
|
||||
db->flags |= SQLITE_NullCallback;
|
||||
}else{
|
||||
db->flags &= ~SQLITE_NullCallback;
|
||||
}
|
||||
}else
|
||||
|
||||
if( sqliteStrICmp(zLeft, "table_info")==0 ){
|
||||
Table *pTab;
|
||||
pTab = sqliteFindTable(db, zRight, 0);
|
||||
if( pTab ){
|
||||
static VdbeOp tableInfoPreface[] = {
|
||||
{ OP_ColumnName, 0, 0, "cid"},
|
||||
{ OP_ColumnName, 1, 0, "name"},
|
||||
{ OP_ColumnName, 2, 0, "type"},
|
||||
{ OP_ColumnName, 3, 0, "notnull"},
|
||||
{ OP_ColumnName, 4, 0, "dflt_value"},
|
||||
};
|
||||
int i;
|
||||
sqliteVdbeAddOpList(v, ArraySize(tableInfoPreface), tableInfoPreface);
|
||||
sqliteViewGetColumnNames(pParse, pTab);
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
sqliteVdbeAddOp(v, OP_Integer, i, 0);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, pTab->aCol[i].zName, P3_STATIC);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1,
|
||||
pTab->aCol[i].zType ? pTab->aCol[i].zType : "numeric", P3_STATIC);
|
||||
sqliteVdbeAddOp(v, OP_Integer, pTab->aCol[i].notNull, 0);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, pTab->aCol[i].zDflt, P3_STATIC);
|
||||
sqliteVdbeAddOp(v, OP_Callback, 5, 0);
|
||||
}
|
||||
}
|
||||
}else
|
||||
|
||||
if( sqliteStrICmp(zLeft, "index_info")==0 ){
|
||||
Index *pIdx;
|
||||
Table *pTab;
|
||||
pIdx = sqliteFindIndex(db, zRight, 0);
|
||||
if( pIdx ){
|
||||
static VdbeOp tableInfoPreface[] = {
|
||||
{ OP_ColumnName, 0, 0, "seqno"},
|
||||
{ OP_ColumnName, 1, 0, "cid"},
|
||||
{ OP_ColumnName, 2, 0, "name"},
|
||||
};
|
||||
int i;
|
||||
pTab = pIdx->pTable;
|
||||
sqliteVdbeAddOpList(v, ArraySize(tableInfoPreface), tableInfoPreface);
|
||||
for(i=0; i<pIdx->nColumn; i++){
|
||||
int cnum = pIdx->aiColumn[i];
|
||||
sqliteVdbeAddOp(v, OP_Integer, i, 0);
|
||||
sqliteVdbeAddOp(v, OP_Integer, cnum, 0);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
assert( pTab->nCol>cnum );
|
||||
sqliteVdbeChangeP3(v, -1, pTab->aCol[cnum].zName, P3_STATIC);
|
||||
sqliteVdbeAddOp(v, OP_Callback, 3, 0);
|
||||
}
|
||||
}
|
||||
}else
|
||||
|
||||
if( sqliteStrICmp(zLeft, "index_list")==0 ){
|
||||
Index *pIdx;
|
||||
Table *pTab;
|
||||
pTab = sqliteFindTable(db, zRight, 0);
|
||||
if( pTab ){
|
||||
v = sqliteGetVdbe(pParse);
|
||||
pIdx = pTab->pIndex;
|
||||
}
|
||||
if( pTab && pIdx ){
|
||||
int i = 0;
|
||||
static VdbeOp indexListPreface[] = {
|
||||
{ OP_ColumnName, 0, 0, "seq"},
|
||||
{ OP_ColumnName, 1, 0, "name"},
|
||||
{ OP_ColumnName, 2, 0, "unique"},
|
||||
};
|
||||
|
||||
sqliteVdbeAddOpList(v, ArraySize(indexListPreface), indexListPreface);
|
||||
while(pIdx){
|
||||
sqliteVdbeAddOp(v, OP_Integer, i, 0);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC);
|
||||
sqliteVdbeAddOp(v, OP_Integer, pIdx->onError!=OE_None, 0);
|
||||
sqliteVdbeAddOp(v, OP_Callback, 3, 0);
|
||||
++i;
|
||||
pIdx = pIdx->pNext;
|
||||
}
|
||||
}
|
||||
}else
|
||||
|
||||
if( sqliteStrICmp(zLeft, "database_list")==0 ){
|
||||
int i;
|
||||
static VdbeOp indexListPreface[] = {
|
||||
{ OP_ColumnName, 0, 0, "seq"},
|
||||
{ OP_ColumnName, 1, 0, "name"},
|
||||
{ OP_ColumnName, 2, 0, "file"},
|
||||
};
|
||||
|
||||
sqliteVdbeAddOpList(v, ArraySize(indexListPreface), indexListPreface);
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( db->aDb[i].pBt==0 ) continue;
|
||||
assert( db->aDb[i].zName!=0 );
|
||||
sqliteVdbeAddOp(v, OP_Integer, i, 0);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, db->aDb[i].zName, P3_STATIC);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, sqliteBtreeGetFilename(db->aDb[i].pBt),
|
||||
P3_STATIC);
|
||||
sqliteVdbeAddOp(v, OP_Callback, 3, 0);
|
||||
}
|
||||
}else
|
||||
/*
|
||||
** PRAGMA temp_store
|
||||
** PRAGMA temp_store = "default"|"memory"|"file"
|
||||
**
|
||||
** Return or set the local value of the temp_store flag. Changing
|
||||
** the local value does not make changes to the disk file and the default
|
||||
** value will be restored the next time the database is opened.
|
||||
**
|
||||
** Note that it is possible for the library compile-time options to
|
||||
** override this setting
|
||||
*/
|
||||
if( sqliteStrICmp(zLeft, "temp_store")==0 ){
|
||||
static VdbeOp getTmpDbLoc[] = {
|
||||
{ OP_ColumnName, 0, 0, "temp_store"},
|
||||
{ OP_Callback, 1, 0, 0},
|
||||
};
|
||||
if( pRight->z==pLeft->z ){
|
||||
sqliteVdbeAddOp(v, OP_Integer, db->temp_store, 0);
|
||||
sqliteVdbeAddOpList(v, ArraySize(getTmpDbLoc), getTmpDbLoc);
|
||||
}else{
|
||||
if (&db->aDb[1].pBt != 0) {
|
||||
sqliteErrorMsg(pParse, "The temporary database already exists - "
|
||||
"its location cannot now be changed");
|
||||
} else {
|
||||
db->temp_store = getTempStore(zRight);
|
||||
}
|
||||
}
|
||||
}else
|
||||
|
||||
/*
|
||||
** PRAGMA default_temp_store
|
||||
** PRAGMA default_temp_store = "default"|"memory"|"file"
|
||||
**
|
||||
** Return or set the value of the persistent temp_store flag (as
|
||||
** well as the value currently in force).
|
||||
**
|
||||
** Note that it is possible for the library compile-time options to
|
||||
** override this setting
|
||||
*/
|
||||
if( sqliteStrICmp(zLeft, "default_temp_store")==0 ){
|
||||
static VdbeOp getTmpDbLoc[] = {
|
||||
{ OP_ColumnName, 0, 0, "temp_store"},
|
||||
{ OP_ReadCookie, 0, 5, 0},
|
||||
{ OP_Callback, 1, 0, 0}};
|
||||
if( pRight->z==pLeft->z ){
|
||||
sqliteVdbeAddOpList(v, ArraySize(getTmpDbLoc), getTmpDbLoc);
|
||||
}else{
|
||||
if (&db->aDb[1].pBt != 0) {
|
||||
sqliteErrorMsg(pParse, "The temporary database already exists - "
|
||||
"its location cannot now be changed");
|
||||
} else {
|
||||
sqliteBeginWriteOperation(pParse, 0, 0);
|
||||
db->temp_store = getTempStore(zRight);
|
||||
sqliteVdbeAddOp(v, OP_Integer, db->temp_store, 0);
|
||||
sqliteVdbeAddOp(v, OP_SetCookie, 0, 5);
|
||||
sqliteEndWriteOperation(pParse);
|
||||
}
|
||||
}
|
||||
}else
|
||||
|
||||
#ifndef NDEBUG
|
||||
if( sqliteStrICmp(zLeft, "parser_trace")==0 ){
|
||||
extern void sqliteParserTrace(FILE*, char *);
|
||||
if( getBoolean(zRight) ){
|
||||
sqliteParserTrace(stdout, "parser: ");
|
||||
}else{
|
||||
sqliteParserTrace(0, 0);
|
||||
}
|
||||
}else
|
||||
#endif
|
||||
|
||||
if( sqliteStrICmp(zLeft, "integrity_check")==0 ){
|
||||
int i, j, addr;
|
||||
|
||||
/* Code that initializes the integrity check program. Set the
|
||||
** error message to an empty string and register the callback
|
||||
** column name.
|
||||
*/
|
||||
static VdbeOp initCode[] = {
|
||||
{ OP_String, 0, 0, ""},
|
||||
{ OP_MemStore, 0, 1, 0},
|
||||
{ OP_ColumnName, 0, 0, "integrity_check"},
|
||||
};
|
||||
|
||||
/* Code to do an BTree integrity check on a single database file.
|
||||
*/
|
||||
static VdbeOp checkDb[] = {
|
||||
{ OP_SetInsert, 0, 0, "2"},
|
||||
{ OP_Integer, 0, 0, 0}, /* 1 */
|
||||
{ OP_OpenRead, 0, 2, 0},
|
||||
{ OP_Rewind, 0, 7, 0}, /* 3 */
|
||||
{ OP_Column, 0, 3, 0}, /* 4 */
|
||||
{ OP_SetInsert, 0, 0, 0},
|
||||
{ OP_Next, 0, 4, 0}, /* 6 */
|
||||
{ OP_IntegrityCk, 0, 0, 0}, /* 7 */
|
||||
{ OP_Dup, 0, 1, 0},
|
||||
{ OP_String, 0, 0, "ok"},
|
||||
{ OP_StrEq, 0, 12, 0}, /* 10 */
|
||||
{ OP_MemLoad, 0, 0, 0},
|
||||
{ OP_String, 0, 0, "*** in database "},
|
||||
{ OP_String, 0, 0, 0}, /* 13 */
|
||||
{ OP_String, 0, 0, " ***\n"},
|
||||
{ OP_Pull, 4, 0, 0},
|
||||
{ OP_Concat, 5, 1, 0},
|
||||
{ OP_MemStore, 0, 1, 0},
|
||||
{ OP_Integer, 0, 0, 0},
|
||||
{ OP_Pop, 1, 0, 0},
|
||||
};
|
||||
|
||||
/* Code that appears at the end of the integrity check. If no error
|
||||
** messages have been generated, output OK. Otherwise output the
|
||||
** error message
|
||||
*/
|
||||
static VdbeOp endCode[] = {
|
||||
{ OP_MemLoad, 0, 0, 0},
|
||||
{ OP_Dup, 0, 1, 0},
|
||||
{ OP_String, 0, 0, ""},
|
||||
{ OP_StrNe, 0, 0, 0}, /* 3 */
|
||||
{ OP_Pop, 1, 0, 0},
|
||||
{ OP_String, 0, 0, "ok"},
|
||||
{ OP_Callback, 1, 0, 0},
|
||||
};
|
||||
|
||||
/* Initialize the VDBE program */
|
||||
sqliteVdbeAddOpList(v, ArraySize(initCode), initCode);
|
||||
|
||||
/* Do an integrity check on each database file */
|
||||
for(i=0; i<db->nDb; i++){
|
||||
HashElem *x;
|
||||
|
||||
/* Do an integrity check of the B-Tree
|
||||
*/
|
||||
addr = sqliteVdbeAddOpList(v, ArraySize(checkDb), checkDb);
|
||||
sqliteVdbeChangeP1(v, addr+1, i);
|
||||
sqliteVdbeChangeP2(v, addr+3, addr+7);
|
||||
sqliteVdbeChangeP2(v, addr+6, addr+4);
|
||||
sqliteVdbeChangeP2(v, addr+7, i);
|
||||
sqliteVdbeChangeP2(v, addr+10, addr+ArraySize(checkDb)-1);
|
||||
sqliteVdbeChangeP3(v, addr+13, db->aDb[i].zName, P3_STATIC);
|
||||
|
||||
/* Make sure all the indices are constructed correctly.
|
||||
*/
|
||||
sqliteCodeVerifySchema(pParse, i);
|
||||
for(x=sqliteHashFirst(&db->aDb[i].tblHash); x; x=sqliteHashNext(x)){
|
||||
Table *pTab = sqliteHashData(x);
|
||||
Index *pIdx;
|
||||
int loopTop;
|
||||
|
||||
if( pTab->pIndex==0 ) continue;
|
||||
sqliteVdbeAddOp(v, OP_Integer, i, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenRead, 1, pTab->tnum);
|
||||
sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
|
||||
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
|
||||
if( pIdx->tnum==0 ) continue;
|
||||
sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenRead, j+2, pIdx->tnum);
|
||||
sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_Integer, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_MemStore, 1, 1);
|
||||
loopTop = sqliteVdbeAddOp(v, OP_Rewind, 1, 0);
|
||||
sqliteVdbeAddOp(v, OP_MemIncr, 1, 0);
|
||||
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
|
||||
int k, jmp2;
|
||||
static VdbeOp idxErr[] = {
|
||||
{ OP_MemLoad, 0, 0, 0},
|
||||
{ OP_String, 0, 0, "rowid "},
|
||||
{ OP_Recno, 1, 0, 0},
|
||||
{ OP_String, 0, 0, " missing from index "},
|
||||
{ OP_String, 0, 0, 0}, /* 4 */
|
||||
{ OP_String, 0, 0, "\n"},
|
||||
{ OP_Concat, 6, 0, 0},
|
||||
{ OP_MemStore, 0, 1, 0},
|
||||
};
|
||||
sqliteVdbeAddOp(v, OP_Recno, 1, 0);
|
||||
for(k=0; k<pIdx->nColumn; k++){
|
||||
int idx = pIdx->aiColumn[k];
|
||||
if( idx==pTab->iPKey ){
|
||||
sqliteVdbeAddOp(v, OP_Recno, 1, 0);
|
||||
}else{
|
||||
sqliteVdbeAddOp(v, OP_Column, 1, idx);
|
||||
}
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0);
|
||||
if( db->file_format>=4 ) sqliteAddIdxKeyType(v, pIdx);
|
||||
jmp2 = sqliteVdbeAddOp(v, OP_Found, j+2, 0);
|
||||
addr = sqliteVdbeAddOpList(v, ArraySize(idxErr), idxErr);
|
||||
sqliteVdbeChangeP3(v, addr+4, pIdx->zName, P3_STATIC);
|
||||
sqliteVdbeChangeP2(v, jmp2, sqliteVdbeCurrentAddr(v));
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_Next, 1, loopTop+1);
|
||||
sqliteVdbeChangeP2(v, loopTop, sqliteVdbeCurrentAddr(v));
|
||||
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
|
||||
static VdbeOp cntIdx[] = {
|
||||
{ OP_Integer, 0, 0, 0},
|
||||
{ OP_MemStore, 2, 1, 0},
|
||||
{ OP_Rewind, 0, 0, 0}, /* 2 */
|
||||
{ OP_MemIncr, 2, 0, 0},
|
||||
{ OP_Next, 0, 0, 0}, /* 4 */
|
||||
{ OP_MemLoad, 1, 0, 0},
|
||||
{ OP_MemLoad, 2, 0, 0},
|
||||
{ OP_Eq, 0, 0, 0}, /* 7 */
|
||||
{ OP_MemLoad, 0, 0, 0},
|
||||
{ OP_String, 0, 0, "wrong # of entries in index "},
|
||||
{ OP_String, 0, 0, 0}, /* 10 */
|
||||
{ OP_String, 0, 0, "\n"},
|
||||
{ OP_Concat, 4, 0, 0},
|
||||
{ OP_MemStore, 0, 1, 0},
|
||||
};
|
||||
if( pIdx->tnum==0 ) continue;
|
||||
addr = sqliteVdbeAddOpList(v, ArraySize(cntIdx), cntIdx);
|
||||
sqliteVdbeChangeP1(v, addr+2, j+2);
|
||||
sqliteVdbeChangeP2(v, addr+2, addr+5);
|
||||
sqliteVdbeChangeP1(v, addr+4, j+2);
|
||||
sqliteVdbeChangeP2(v, addr+4, addr+3);
|
||||
sqliteVdbeChangeP2(v, addr+7, addr+ArraySize(cntIdx));
|
||||
sqliteVdbeChangeP3(v, addr+10, pIdx->zName, P3_STATIC);
|
||||
}
|
||||
}
|
||||
}
|
||||
addr = sqliteVdbeAddOpList(v, ArraySize(endCode), endCode);
|
||||
sqliteVdbeChangeP2(v, addr+3, addr+ArraySize(endCode)-1);
|
||||
}else
|
||||
|
||||
{}
|
||||
sqliteFree(zLeft);
|
||||
sqliteFree(zRight);
|
||||
}
|
||||
834
sqlitebrowser/sqlitebrowser/sqlite_source/printf.c
Executable file
@@ -0,0 +1,834 @@
|
||||
/*
|
||||
** The "printf" code that follows dates from the 1980's. It is in
|
||||
** the public domain. The original comments are included here for
|
||||
** completeness. They are slightly out-of-date.
|
||||
**
|
||||
** The following modules is an enhanced replacement for the "printf" subroutines
|
||||
** found in the standard C library. The following enhancements are
|
||||
** supported:
|
||||
**
|
||||
** + Additional functions. The standard set of "printf" functions
|
||||
** includes printf, fprintf, sprintf, vprintf, vfprintf, and
|
||||
** vsprintf. This module adds the following:
|
||||
**
|
||||
** * snprintf -- Works like sprintf, but has an extra argument
|
||||
** which is the size of the buffer written to.
|
||||
**
|
||||
** * mprintf -- Similar to sprintf. Writes output to memory
|
||||
** obtained from malloc.
|
||||
**
|
||||
** * xprintf -- Calls a function to dispose of output.
|
||||
**
|
||||
** * nprintf -- No output, but returns the number of characters
|
||||
** that would have been output by printf.
|
||||
**
|
||||
** * A v- version (ex: vsnprintf) of every function is also
|
||||
** supplied.
|
||||
**
|
||||
** + A few extensions to the formatting notation are supported:
|
||||
**
|
||||
** * The "=" flag (similar to "-") causes the output to be
|
||||
** be centered in the appropriately sized field.
|
||||
**
|
||||
** * The %b field outputs an integer in binary notation.
|
||||
**
|
||||
** * The %c field now accepts a precision. The character output
|
||||
** is repeated by the number of times the precision specifies.
|
||||
**
|
||||
** * The %' field works like %c, but takes as its character the
|
||||
** next character of the format string, instead of the next
|
||||
** argument. For example, printf("%.78'-") prints 78 minus
|
||||
** signs, the same as printf("%.78c",'-').
|
||||
**
|
||||
** + When compiled using GCC on a SPARC, this version of printf is
|
||||
** faster than the library printf for SUN OS 4.1.
|
||||
**
|
||||
** + All functions are fully reentrant.
|
||||
**
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** Undefine COMPATIBILITY to make some slight changes in the way things
|
||||
** work. I think the changes are an improvement, but they are not
|
||||
** backwards compatible.
|
||||
*/
|
||||
/* #define COMPATIBILITY / * Compatible with SUN OS 4.1 */
|
||||
|
||||
/*
|
||||
** Conversion types fall into various categories as defined by the
|
||||
** following enumeration.
|
||||
*/
|
||||
enum et_type { /* The type of the format field */
|
||||
etRADIX, /* Integer types. %d, %x, %o, and so forth */
|
||||
etFLOAT, /* Floating point. %f */
|
||||
etEXP, /* Exponentional notation. %e and %E */
|
||||
etGENERIC, /* Floating or exponential, depending on exponent. %g */
|
||||
etSIZE, /* Return number of characters processed so far. %n */
|
||||
etSTRING, /* Strings. %s */
|
||||
etDYNSTRING, /* Dynamically allocated strings. %z */
|
||||
etPERCENT, /* Percent symbol. %% */
|
||||
etCHARX, /* Characters. %c */
|
||||
etERROR, /* Used to indicate no such conversion type */
|
||||
/* The rest are extensions, not normally found in printf() */
|
||||
etCHARLIT, /* Literal characters. %' */
|
||||
etSQLESCAPE, /* Strings with '\'' doubled. %q */
|
||||
etSQLESCAPE2, /* Strings with '\'' doubled and enclosed in '',
|
||||
NULL pointers replaced by SQL NULL. %Q */
|
||||
etORDINAL /* 1st, 2nd, 3rd and so forth */
|
||||
};
|
||||
|
||||
/*
|
||||
** Each builtin conversion character (ex: the 'd' in "%d") is described
|
||||
** by an instance of the following structure
|
||||
*/
|
||||
typedef struct et_info { /* Information about each format field */
|
||||
int fmttype; /* The format field code letter */
|
||||
int base; /* The base for radix conversion */
|
||||
char *charset; /* The character set for conversion */
|
||||
int flag_signed; /* Is the quantity signed? */
|
||||
char *prefix; /* Prefix on non-zero values in alt format */
|
||||
enum et_type type; /* Conversion paradigm */
|
||||
} et_info;
|
||||
|
||||
/*
|
||||
** The following table is searched linearly, so it is good to put the
|
||||
** most frequently used conversion types first.
|
||||
*/
|
||||
static et_info fmtinfo[] = {
|
||||
{ 'd', 10, "0123456789", 1, 0, etRADIX, },
|
||||
{ 's', 0, 0, 0, 0, etSTRING, },
|
||||
{ 'z', 0, 0, 0, 0, etDYNSTRING, },
|
||||
{ 'q', 0, 0, 0, 0, etSQLESCAPE, },
|
||||
{ 'Q', 0, 0, 0, 0, etSQLESCAPE2, },
|
||||
{ 'c', 0, 0, 0, 0, etCHARX, },
|
||||
{ 'o', 8, "01234567", 0, "0", etRADIX, },
|
||||
{ 'u', 10, "0123456789", 0, 0, etRADIX, },
|
||||
{ 'x', 16, "0123456789abcdef", 0, "x0", etRADIX, },
|
||||
{ 'X', 16, "0123456789ABCDEF", 0, "X0", etRADIX, },
|
||||
{ 'r', 10, "0123456789", 0, 0, etORDINAL, },
|
||||
{ 'f', 0, 0, 1, 0, etFLOAT, },
|
||||
{ 'e', 0, "e", 1, 0, etEXP, },
|
||||
{ 'E', 0, "E", 1, 0, etEXP, },
|
||||
{ 'g', 0, "e", 1, 0, etGENERIC, },
|
||||
{ 'G', 0, "E", 1, 0, etGENERIC, },
|
||||
{ 'i', 10, "0123456789", 1, 0, etRADIX, },
|
||||
{ 'n', 0, 0, 0, 0, etSIZE, },
|
||||
{ '%', 0, 0, 0, 0, etPERCENT, },
|
||||
{ 'b', 2, "01", 0, "b0", etRADIX, }, /* Binary */
|
||||
{ 'p', 10, "0123456789", 0, 0, etRADIX, }, /* Pointers */
|
||||
{ '\'', 0, 0, 0, 0, etCHARLIT, }, /* Literal char */
|
||||
};
|
||||
#define etNINFO (sizeof(fmtinfo)/sizeof(fmtinfo[0]))
|
||||
|
||||
/*
|
||||
** If NOFLOATINGPOINT is defined, then none of the floating point
|
||||
** conversions will work.
|
||||
*/
|
||||
#ifndef etNOFLOATINGPOINT
|
||||
/*
|
||||
** "*val" is a double such that 0.1 <= *val < 10.0
|
||||
** Return the ascii code for the leading digit of *val, then
|
||||
** multiply "*val" by 10.0 to renormalize.
|
||||
**
|
||||
** Example:
|
||||
** input: *val = 3.14159
|
||||
** output: *val = 1.4159 function return = '3'
|
||||
**
|
||||
** The counter *cnt is incremented each time. After counter exceeds
|
||||
** 16 (the number of significant digits in a 64-bit float) '0' is
|
||||
** always returned.
|
||||
*/
|
||||
static int et_getdigit(double *val, int *cnt){
|
||||
int digit;
|
||||
double d;
|
||||
if( (*cnt)++ >= 16 ) return '0';
|
||||
digit = (int)*val;
|
||||
d = digit;
|
||||
digit += '0';
|
||||
*val = (*val - d)*10.0;
|
||||
return digit;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define etBUFSIZE 1000 /* Size of the output buffer */
|
||||
|
||||
/*
|
||||
** The root program. All variations call this core.
|
||||
**
|
||||
** INPUTS:
|
||||
** func This is a pointer to a function taking three arguments
|
||||
** 1. A pointer to anything. Same as the "arg" parameter.
|
||||
** 2. A pointer to the list of characters to be output
|
||||
** (Note, this list is NOT null terminated.)
|
||||
** 3. An integer number of characters to be output.
|
||||
** (Note: This number might be zero.)
|
||||
**
|
||||
** arg This is the pointer to anything which will be passed as the
|
||||
** first argument to "func". Use it for whatever you like.
|
||||
**
|
||||
** fmt This is the format string, as in the usual print.
|
||||
**
|
||||
** ap This is a pointer to a list of arguments. Same as in
|
||||
** vfprint.
|
||||
**
|
||||
** OUTPUTS:
|
||||
** The return value is the total number of characters sent to
|
||||
** the function "func". Returns -1 on a error.
|
||||
**
|
||||
** Note that the order in which automatic variables are declared below
|
||||
** seems to make a big difference in determining how fast this beast
|
||||
** will run.
|
||||
*/
|
||||
static int vxprintf(
|
||||
void (*func)(void*,char*,int),
|
||||
void *arg,
|
||||
const char *format,
|
||||
va_list ap
|
||||
){
|
||||
register const char *fmt; /* The format string. */
|
||||
register int c; /* Next character in the format string */
|
||||
register char *bufpt; /* Pointer to the conversion buffer */
|
||||
register int precision; /* Precision of the current field */
|
||||
register int length; /* Length of the field */
|
||||
register int idx; /* A general purpose loop counter */
|
||||
int count; /* Total number of characters output */
|
||||
int width; /* Width of the current field */
|
||||
int flag_leftjustify; /* True if "-" flag is present */
|
||||
int flag_plussign; /* True if "+" flag is present */
|
||||
int flag_blanksign; /* True if " " flag is present */
|
||||
int flag_alternateform; /* True if "#" flag is present */
|
||||
int flag_zeropad; /* True if field width constant starts with zero */
|
||||
int flag_long; /* True if "l" flag is present */
|
||||
int flag_center; /* True if "=" flag is present */
|
||||
unsigned long longvalue; /* Value for integer types */
|
||||
double realvalue; /* Value for real types */
|
||||
et_info *infop; /* Pointer to the appropriate info structure */
|
||||
char buf[etBUFSIZE]; /* Conversion buffer */
|
||||
char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */
|
||||
int errorflag = 0; /* True if an error is encountered */
|
||||
enum et_type xtype; /* Conversion paradigm */
|
||||
char *zExtra; /* Extra memory used for etTCLESCAPE conversions */
|
||||
static char spaces[] = " "
|
||||
" ";
|
||||
#define etSPACESIZE (sizeof(spaces)-1)
|
||||
#ifndef etNOFLOATINGPOINT
|
||||
int exp; /* exponent of real numbers */
|
||||
double rounder; /* Used for rounding floating point values */
|
||||
int flag_dp; /* True if decimal point should be shown */
|
||||
int flag_rtz; /* True if trailing zeros should be removed */
|
||||
int flag_exp; /* True to force display of the exponent */
|
||||
int nsd; /* Number of significant digits returned */
|
||||
#endif
|
||||
|
||||
fmt = format; /* Put in a register for speed */
|
||||
count = length = 0;
|
||||
bufpt = 0;
|
||||
for(; (c=(*fmt))!=0; ++fmt){
|
||||
if( c!='%' ){
|
||||
register int amt;
|
||||
bufpt = (char *)fmt;
|
||||
amt = 1;
|
||||
while( (c=(*++fmt))!='%' && c!=0 ) amt++;
|
||||
(*func)(arg,bufpt,amt);
|
||||
count += amt;
|
||||
if( c==0 ) break;
|
||||
}
|
||||
if( (c=(*++fmt))==0 ){
|
||||
errorflag = 1;
|
||||
(*func)(arg,"%",1);
|
||||
count++;
|
||||
break;
|
||||
}
|
||||
/* Find out what flags are present */
|
||||
flag_leftjustify = flag_plussign = flag_blanksign =
|
||||
flag_alternateform = flag_zeropad = flag_center = 0;
|
||||
do{
|
||||
switch( c ){
|
||||
case '-': flag_leftjustify = 1; c = 0; break;
|
||||
case '+': flag_plussign = 1; c = 0; break;
|
||||
case ' ': flag_blanksign = 1; c = 0; break;
|
||||
case '#': flag_alternateform = 1; c = 0; break;
|
||||
case '0': flag_zeropad = 1; c = 0; break;
|
||||
case '=': flag_center = 1; c = 0; break;
|
||||
default: break;
|
||||
}
|
||||
}while( c==0 && (c=(*++fmt))!=0 );
|
||||
if( flag_center ) flag_leftjustify = 0;
|
||||
/* Get the field width */
|
||||
width = 0;
|
||||
if( c=='*' ){
|
||||
width = va_arg(ap,int);
|
||||
if( width<0 ){
|
||||
flag_leftjustify = 1;
|
||||
width = -width;
|
||||
}
|
||||
c = *++fmt;
|
||||
}else{
|
||||
while( c>='0' && c<='9' ){
|
||||
width = width*10 + c - '0';
|
||||
c = *++fmt;
|
||||
}
|
||||
}
|
||||
if( width > etBUFSIZE-10 ){
|
||||
width = etBUFSIZE-10;
|
||||
}
|
||||
/* Get the precision */
|
||||
if( c=='.' ){
|
||||
precision = 0;
|
||||
c = *++fmt;
|
||||
if( c=='*' ){
|
||||
precision = va_arg(ap,int);
|
||||
#ifndef etCOMPATIBILITY
|
||||
/* This is sensible, but SUN OS 4.1 doesn't do it. */
|
||||
if( precision<0 ) precision = -precision;
|
||||
#endif
|
||||
c = *++fmt;
|
||||
}else{
|
||||
while( c>='0' && c<='9' ){
|
||||
precision = precision*10 + c - '0';
|
||||
c = *++fmt;
|
||||
}
|
||||
}
|
||||
/* Limit the precision to prevent overflowing buf[] during conversion */
|
||||
if( precision>etBUFSIZE-40 ) precision = etBUFSIZE-40;
|
||||
}else{
|
||||
precision = -1;
|
||||
}
|
||||
/* Get the conversion type modifier */
|
||||
if( c=='l' ){
|
||||
flag_long = 1;
|
||||
c = *++fmt;
|
||||
}else{
|
||||
flag_long = 0;
|
||||
}
|
||||
/* Fetch the info entry for the field */
|
||||
infop = 0;
|
||||
for(idx=0; idx<etNINFO; idx++){
|
||||
if( c==fmtinfo[idx].fmttype ){
|
||||
infop = &fmtinfo[idx];
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* No info entry found. It must be an error. */
|
||||
if( infop==0 ){
|
||||
xtype = etERROR;
|
||||
}else{
|
||||
xtype = infop->type;
|
||||
}
|
||||
zExtra = 0;
|
||||
|
||||
/*
|
||||
** At this point, variables are initialized as follows:
|
||||
**
|
||||
** flag_alternateform TRUE if a '#' is present.
|
||||
** flag_plussign TRUE if a '+' is present.
|
||||
** flag_leftjustify TRUE if a '-' is present or if the
|
||||
** field width was negative.
|
||||
** flag_zeropad TRUE if the width began with 0.
|
||||
** flag_long TRUE if the letter 'l' (ell) prefixed
|
||||
** the conversion character.
|
||||
** flag_blanksign TRUE if a ' ' is present.
|
||||
** width The specified field width. This is
|
||||
** always non-negative. Zero is the default.
|
||||
** precision The specified precision. The default
|
||||
** is -1.
|
||||
** xtype The class of the conversion.
|
||||
** infop Pointer to the appropriate info struct.
|
||||
*/
|
||||
switch( xtype ){
|
||||
case etORDINAL:
|
||||
case etRADIX:
|
||||
if( flag_long ) longvalue = va_arg(ap,long);
|
||||
else longvalue = va_arg(ap,int);
|
||||
#ifdef etCOMPATIBILITY
|
||||
/* For the format %#x, the value zero is printed "0" not "0x0".
|
||||
** I think this is stupid. */
|
||||
if( longvalue==0 ) flag_alternateform = 0;
|
||||
#else
|
||||
/* More sensible: turn off the prefix for octal (to prevent "00"),
|
||||
** but leave the prefix for hex. */
|
||||
if( longvalue==0 && infop->base==8 ) flag_alternateform = 0;
|
||||
#endif
|
||||
if( infop->flag_signed ){
|
||||
if( *(long*)&longvalue<0 ){
|
||||
longvalue = -*(long*)&longvalue;
|
||||
prefix = '-';
|
||||
}else if( flag_plussign ) prefix = '+';
|
||||
else if( flag_blanksign ) prefix = ' ';
|
||||
else prefix = 0;
|
||||
}else prefix = 0;
|
||||
if( flag_zeropad && precision<width-(prefix!=0) ){
|
||||
precision = width-(prefix!=0);
|
||||
}
|
||||
bufpt = &buf[etBUFSIZE];
|
||||
if( xtype==etORDINAL ){
|
||||
long a,b;
|
||||
a = longvalue%10;
|
||||
b = longvalue%100;
|
||||
bufpt -= 2;
|
||||
if( a==0 || a>3 || (b>10 && b<14) ){
|
||||
bufpt[0] = 't';
|
||||
bufpt[1] = 'h';
|
||||
}else if( a==1 ){
|
||||
bufpt[0] = 's';
|
||||
bufpt[1] = 't';
|
||||
}else if( a==2 ){
|
||||
bufpt[0] = 'n';
|
||||
bufpt[1] = 'd';
|
||||
}else if( a==3 ){
|
||||
bufpt[0] = 'r';
|
||||
bufpt[1] = 'd';
|
||||
}
|
||||
}
|
||||
{
|
||||
register char *cset; /* Use registers for speed */
|
||||
register int base;
|
||||
cset = infop->charset;
|
||||
base = infop->base;
|
||||
do{ /* Convert to ascii */
|
||||
*(--bufpt) = cset[longvalue%base];
|
||||
longvalue = longvalue/base;
|
||||
}while( longvalue>0 );
|
||||
}
|
||||
length = &buf[etBUFSIZE]-bufpt;
|
||||
for(idx=precision-length; idx>0; idx--){
|
||||
*(--bufpt) = '0'; /* Zero pad */
|
||||
}
|
||||
if( prefix ) *(--bufpt) = prefix; /* Add sign */
|
||||
if( flag_alternateform && infop->prefix ){ /* Add "0" or "0x" */
|
||||
char *pre, x;
|
||||
pre = infop->prefix;
|
||||
if( *bufpt!=pre[0] ){
|
||||
for(pre=infop->prefix; (x=(*pre))!=0; pre++) *(--bufpt) = x;
|
||||
}
|
||||
}
|
||||
length = &buf[etBUFSIZE]-bufpt;
|
||||
break;
|
||||
case etFLOAT:
|
||||
case etEXP:
|
||||
case etGENERIC:
|
||||
realvalue = va_arg(ap,double);
|
||||
#ifndef etNOFLOATINGPOINT
|
||||
if( precision<0 ) precision = 6; /* Set default precision */
|
||||
if( precision>etBUFSIZE-10 ) precision = etBUFSIZE-10;
|
||||
if( realvalue<0.0 ){
|
||||
realvalue = -realvalue;
|
||||
prefix = '-';
|
||||
}else{
|
||||
if( flag_plussign ) prefix = '+';
|
||||
else if( flag_blanksign ) prefix = ' ';
|
||||
else prefix = 0;
|
||||
}
|
||||
if( infop->type==etGENERIC && precision>0 ) precision--;
|
||||
rounder = 0.0;
|
||||
#ifdef COMPATIBILITY
|
||||
/* Rounding works like BSD when the constant 0.4999 is used. Wierd! */
|
||||
for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1);
|
||||
#else
|
||||
/* It makes more sense to use 0.5 */
|
||||
for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1);
|
||||
#endif
|
||||
if( infop->type==etFLOAT ) realvalue += rounder;
|
||||
/* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
|
||||
exp = 0;
|
||||
if( realvalue>0.0 ){
|
||||
int k = 0;
|
||||
while( realvalue>=1e8 && k++<100 ){ realvalue *= 1e-8; exp+=8; }
|
||||
while( realvalue>=10.0 && k++<100 ){ realvalue *= 0.1; exp++; }
|
||||
while( realvalue<1e-8 && k++<100 ){ realvalue *= 1e8; exp-=8; }
|
||||
while( realvalue<1.0 && k++<100 ){ realvalue *= 10.0; exp--; }
|
||||
if( k>=100 ){
|
||||
bufpt = "NaN";
|
||||
length = 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bufpt = buf;
|
||||
/*
|
||||
** If the field type is etGENERIC, then convert to either etEXP
|
||||
** or etFLOAT, as appropriate.
|
||||
*/
|
||||
flag_exp = xtype==etEXP;
|
||||
if( xtype!=etFLOAT ){
|
||||
realvalue += rounder;
|
||||
if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
|
||||
}
|
||||
if( xtype==etGENERIC ){
|
||||
flag_rtz = !flag_alternateform;
|
||||
if( exp<-4 || exp>precision ){
|
||||
xtype = etEXP;
|
||||
}else{
|
||||
precision = precision - exp;
|
||||
xtype = etFLOAT;
|
||||
}
|
||||
}else{
|
||||
flag_rtz = 0;
|
||||
}
|
||||
/*
|
||||
** The "exp+precision" test causes output to be of type etEXP if
|
||||
** the precision is too large to fit in buf[].
|
||||
*/
|
||||
nsd = 0;
|
||||
if( xtype==etFLOAT && exp+precision<etBUFSIZE-30 ){
|
||||
flag_dp = (precision>0 || flag_alternateform);
|
||||
if( prefix ) *(bufpt++) = prefix; /* Sign */
|
||||
if( exp<0 ) *(bufpt++) = '0'; /* Digits before "." */
|
||||
else for(; exp>=0; exp--) *(bufpt++) = et_getdigit(&realvalue,&nsd);
|
||||
if( flag_dp ) *(bufpt++) = '.'; /* The decimal point */
|
||||
for(exp++; exp<0 && precision>0; precision--, exp++){
|
||||
*(bufpt++) = '0';
|
||||
}
|
||||
while( (precision--)>0 ) *(bufpt++) = et_getdigit(&realvalue,&nsd);
|
||||
*(bufpt--) = 0; /* Null terminate */
|
||||
if( flag_rtz && flag_dp ){ /* Remove trailing zeros and "." */
|
||||
while( bufpt>=buf && *bufpt=='0' ) *(bufpt--) = 0;
|
||||
if( bufpt>=buf && *bufpt=='.' ) *(bufpt--) = 0;
|
||||
}
|
||||
bufpt++; /* point to next free slot */
|
||||
}else{ /* etEXP or etGENERIC */
|
||||
flag_dp = (precision>0 || flag_alternateform);
|
||||
if( prefix ) *(bufpt++) = prefix; /* Sign */
|
||||
*(bufpt++) = et_getdigit(&realvalue,&nsd); /* First digit */
|
||||
if( flag_dp ) *(bufpt++) = '.'; /* Decimal point */
|
||||
while( (precision--)>0 ) *(bufpt++) = et_getdigit(&realvalue,&nsd);
|
||||
bufpt--; /* point to last digit */
|
||||
if( flag_rtz && flag_dp ){ /* Remove tail zeros */
|
||||
while( bufpt>=buf && *bufpt=='0' ) *(bufpt--) = 0;
|
||||
if( bufpt>=buf && *bufpt=='.' ) *(bufpt--) = 0;
|
||||
}
|
||||
bufpt++; /* point to next free slot */
|
||||
if( exp || flag_exp ){
|
||||
*(bufpt++) = infop->charset[0];
|
||||
if( exp<0 ){ *(bufpt++) = '-'; exp = -exp; } /* sign of exp */
|
||||
else { *(bufpt++) = '+'; }
|
||||
if( exp>=100 ){
|
||||
*(bufpt++) = (exp/100)+'0'; /* 100's digit */
|
||||
exp %= 100;
|
||||
}
|
||||
*(bufpt++) = exp/10+'0'; /* 10's digit */
|
||||
*(bufpt++) = exp%10+'0'; /* 1's digit */
|
||||
}
|
||||
}
|
||||
/* The converted number is in buf[] and zero terminated. Output it.
|
||||
** Note that the number is in the usual order, not reversed as with
|
||||
** integer conversions. */
|
||||
length = bufpt-buf;
|
||||
bufpt = buf;
|
||||
|
||||
/* Special case: Add leading zeros if the flag_zeropad flag is
|
||||
** set and we are not left justified */
|
||||
if( flag_zeropad && !flag_leftjustify && length < width){
|
||||
int i;
|
||||
int nPad = width - length;
|
||||
for(i=width; i>=nPad; i--){
|
||||
bufpt[i] = bufpt[i-nPad];
|
||||
}
|
||||
i = prefix!=0;
|
||||
while( nPad-- ) bufpt[i++] = '0';
|
||||
length = width;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case etSIZE:
|
||||
*(va_arg(ap,int*)) = count;
|
||||
length = width = 0;
|
||||
break;
|
||||
case etPERCENT:
|
||||
buf[0] = '%';
|
||||
bufpt = buf;
|
||||
length = 1;
|
||||
break;
|
||||
case etCHARLIT:
|
||||
case etCHARX:
|
||||
c = buf[0] = (xtype==etCHARX ? va_arg(ap,int) : *++fmt);
|
||||
if( precision>=0 ){
|
||||
for(idx=1; idx<precision; idx++) buf[idx] = c;
|
||||
length = precision;
|
||||
}else{
|
||||
length =1;
|
||||
}
|
||||
bufpt = buf;
|
||||
break;
|
||||
case etSTRING:
|
||||
case etDYNSTRING:
|
||||
bufpt = va_arg(ap,char*);
|
||||
if( bufpt==0 ){
|
||||
bufpt = "";
|
||||
}else if( xtype==etDYNSTRING ){
|
||||
zExtra = bufpt;
|
||||
}
|
||||
length = strlen(bufpt);
|
||||
if( precision>=0 && precision<length ) length = precision;
|
||||
break;
|
||||
case etSQLESCAPE:
|
||||
case etSQLESCAPE2:
|
||||
{
|
||||
int i, j, n, c, isnull;
|
||||
char *arg = va_arg(ap,char*);
|
||||
isnull = arg==0;
|
||||
if( isnull ) arg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)");
|
||||
for(i=n=0; (c=arg[i])!=0; i++){
|
||||
if( c=='\'' ) n++;
|
||||
}
|
||||
n += i + 1 + ((!isnull && xtype==etSQLESCAPE2) ? 2 : 0);
|
||||
if( n>etBUFSIZE ){
|
||||
bufpt = zExtra = sqliteMalloc( n );
|
||||
if( bufpt==0 ) return -1;
|
||||
}else{
|
||||
bufpt = buf;
|
||||
}
|
||||
j = 0;
|
||||
if( !isnull && xtype==etSQLESCAPE2 ) bufpt[j++] = '\'';
|
||||
for(i=0; (c=arg[i])!=0; i++){
|
||||
bufpt[j++] = c;
|
||||
if( c=='\'' ) bufpt[j++] = c;
|
||||
}
|
||||
if( !isnull && xtype==etSQLESCAPE2 ) bufpt[j++] = '\'';
|
||||
bufpt[j] = 0;
|
||||
length = j;
|
||||
if( precision>=0 && precision<length ) length = precision;
|
||||
}
|
||||
break;
|
||||
case etERROR:
|
||||
buf[0] = '%';
|
||||
buf[1] = c;
|
||||
errorflag = 0;
|
||||
idx = 1+(c!=0);
|
||||
(*func)(arg,"%",idx);
|
||||
count += idx;
|
||||
if( c==0 ) fmt--;
|
||||
break;
|
||||
}/* End switch over the format type */
|
||||
/*
|
||||
** The text of the conversion is pointed to by "bufpt" and is
|
||||
** "length" characters long. The field width is "width". Do
|
||||
** the output.
|
||||
*/
|
||||
if( !flag_leftjustify ){
|
||||
register int nspace;
|
||||
nspace = width-length;
|
||||
if( nspace>0 ){
|
||||
if( flag_center ){
|
||||
nspace = nspace/2;
|
||||
width -= nspace;
|
||||
flag_leftjustify = 1;
|
||||
}
|
||||
count += nspace;
|
||||
while( nspace>=etSPACESIZE ){
|
||||
(*func)(arg,spaces,etSPACESIZE);
|
||||
nspace -= etSPACESIZE;
|
||||
}
|
||||
if( nspace>0 ) (*func)(arg,spaces,nspace);
|
||||
}
|
||||
}
|
||||
if( length>0 ){
|
||||
(*func)(arg,bufpt,length);
|
||||
count += length;
|
||||
}
|
||||
if( flag_leftjustify ){
|
||||
register int nspace;
|
||||
nspace = width-length;
|
||||
if( nspace>0 ){
|
||||
count += nspace;
|
||||
while( nspace>=etSPACESIZE ){
|
||||
(*func)(arg,spaces,etSPACESIZE);
|
||||
nspace -= etSPACESIZE;
|
||||
}
|
||||
if( nspace>0 ) (*func)(arg,spaces,nspace);
|
||||
}
|
||||
}
|
||||
if( zExtra ){
|
||||
if( xtype==etDYNSTRING ){
|
||||
free(zExtra);
|
||||
}else{
|
||||
sqliteFree(zExtra);
|
||||
}
|
||||
}
|
||||
}/* End for loop over the format string */
|
||||
return errorflag ? -1 : count;
|
||||
} /* End of function */
|
||||
|
||||
|
||||
/* This structure is used to store state information about the
|
||||
** write to memory that is currently in progress.
|
||||
*/
|
||||
struct sgMprintf {
|
||||
char *zBase; /* A base allocation */
|
||||
char *zText; /* The string collected so far */
|
||||
int nChar; /* Length of the string so far */
|
||||
int nAlloc; /* Amount of space allocated in zText */
|
||||
};
|
||||
|
||||
/*
|
||||
** This function implements the callback from vxprintf.
|
||||
**
|
||||
** This routine add nNewChar characters of text in zNewText to
|
||||
** the sgMprintf structure pointed to by "arg".
|
||||
*/
|
||||
static void mout(void *arg, char *zNewText, int nNewChar){
|
||||
struct sgMprintf *pM = (struct sgMprintf*)arg;
|
||||
if( pM->nChar + nNewChar + 1 > pM->nAlloc ){
|
||||
pM->nAlloc = pM->nChar + nNewChar*2 + 1;
|
||||
if( pM->zText==pM->zBase ){
|
||||
pM->zText = sqliteMalloc(pM->nAlloc);
|
||||
if( pM->zText && pM->nChar ) memcpy(pM->zText,pM->zBase,pM->nChar);
|
||||
}else{
|
||||
char *z = sqliteRealloc(pM->zText, pM->nAlloc);
|
||||
if( z==0 ){
|
||||
sqliteFree(pM->zText);
|
||||
pM->nChar = 0;
|
||||
pM->nAlloc = 0;
|
||||
}
|
||||
pM->zText = z;
|
||||
}
|
||||
}
|
||||
if( pM->zText ){
|
||||
memcpy(&pM->zText[pM->nChar], zNewText, nNewChar);
|
||||
pM->nChar += nNewChar;
|
||||
pM->zText[pM->nChar] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite_mprintf() works like printf(), but allocations memory to hold the
|
||||
** resulting string and returns a pointer to the allocated memory. Use
|
||||
** sqliteFree() to release the memory allocated.
|
||||
*/
|
||||
char *sqliteMPrintf(const char *zFormat, ...){
|
||||
va_list ap;
|
||||
struct sgMprintf sMprintf;
|
||||
char zBuf[200];
|
||||
|
||||
sMprintf.nChar = 0;
|
||||
sMprintf.nAlloc = sizeof(zBuf);
|
||||
sMprintf.zText = zBuf;
|
||||
sMprintf.zBase = zBuf;
|
||||
va_start(ap,zFormat);
|
||||
vxprintf(mout,&sMprintf,zFormat,ap);
|
||||
va_end(ap);
|
||||
sMprintf.zText[sMprintf.nChar] = 0;
|
||||
return sqliteRealloc(sMprintf.zText, sMprintf.nChar+1);
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite_mprintf() works like printf(), but allocations memory to hold the
|
||||
** resulting string and returns a pointer to the allocated memory. Use
|
||||
** sqliteFree() to release the memory allocated.
|
||||
*/
|
||||
char *sqlite_mprintf(const char *zFormat, ...){
|
||||
va_list ap;
|
||||
struct sgMprintf sMprintf;
|
||||
char *zNew;
|
||||
char zBuf[200];
|
||||
|
||||
sMprintf.nChar = 0;
|
||||
sMprintf.nAlloc = sizeof(zBuf);
|
||||
sMprintf.zText = zBuf;
|
||||
sMprintf.zBase = zBuf;
|
||||
va_start(ap,zFormat);
|
||||
vxprintf(mout,&sMprintf,zFormat,ap);
|
||||
va_end(ap);
|
||||
sMprintf.zText[sMprintf.nChar] = 0;
|
||||
zNew = malloc( sMprintf.nChar+1 );
|
||||
if( zNew ) strcpy(zNew,sMprintf.zText);
|
||||
if( sMprintf.zText!=sMprintf.zBase ){
|
||||
sqliteFree(sMprintf.zText);
|
||||
}
|
||||
return zNew;
|
||||
}
|
||||
|
||||
/* This is the varargs version of sqlite_mprintf.
|
||||
*/
|
||||
char *sqlite_vmprintf(const char *zFormat, va_list ap){
|
||||
struct sgMprintf sMprintf;
|
||||
char *zNew;
|
||||
char zBuf[200];
|
||||
sMprintf.nChar = 0;
|
||||
sMprintf.zText = zBuf;
|
||||
sMprintf.nAlloc = sizeof(zBuf);
|
||||
sMprintf.zBase = zBuf;
|
||||
vxprintf(mout,&sMprintf,zFormat,ap);
|
||||
sMprintf.zText[sMprintf.nChar] = 0;
|
||||
zNew = malloc( sMprintf.nChar+1 );
|
||||
if( zNew ) strcpy(zNew,sMprintf.zText);
|
||||
if( sMprintf.zText!=sMprintf.zBase ){
|
||||
sqliteFree(sMprintf.zText);
|
||||
}
|
||||
return zNew;
|
||||
}
|
||||
|
||||
/*
|
||||
** The following four routines implement the varargs versions of the
|
||||
** sqlite_exec() and sqlite_get_table() interfaces. See the sqlite.h
|
||||
** header files for a more detailed description of how these interfaces
|
||||
** work.
|
||||
**
|
||||
** These routines are all just simple wrappers.
|
||||
*/
|
||||
int sqlite_exec_printf(
|
||||
sqlite *db, /* An open database */
|
||||
const char *sqlFormat, /* printf-style format string for the SQL */
|
||||
sqlite_callback xCallback, /* Callback function */
|
||||
void *pArg, /* 1st argument to callback function */
|
||||
char **errmsg, /* Error msg written here */
|
||||
... /* Arguments to the format string. */
|
||||
){
|
||||
va_list ap;
|
||||
int rc;
|
||||
|
||||
va_start(ap, errmsg);
|
||||
rc = sqlite_exec_vprintf(db, sqlFormat, xCallback, pArg, errmsg, ap);
|
||||
va_end(ap);
|
||||
return rc;
|
||||
}
|
||||
int sqlite_exec_vprintf(
|
||||
sqlite *db, /* An open database */
|
||||
const char *sqlFormat, /* printf-style format string for the SQL */
|
||||
sqlite_callback xCallback, /* Callback function */
|
||||
void *pArg, /* 1st argument to callback function */
|
||||
char **errmsg, /* Error msg written here */
|
||||
va_list ap /* Arguments to the format string. */
|
||||
){
|
||||
char *zSql;
|
||||
int rc;
|
||||
|
||||
zSql = sqlite_vmprintf(sqlFormat, ap);
|
||||
rc = sqlite_exec(db, zSql, xCallback, pArg, errmsg);
|
||||
free(zSql);
|
||||
return rc;
|
||||
}
|
||||
int sqlite_get_table_printf(
|
||||
sqlite *db, /* An open database */
|
||||
const char *sqlFormat, /* printf-style format string for the SQL */
|
||||
char ***resultp, /* Result written to a char *[] that this points to */
|
||||
int *nrow, /* Number of result rows written here */
|
||||
int *ncol, /* Number of result columns written here */
|
||||
char **errmsg, /* Error msg written here */
|
||||
... /* Arguments to the format string */
|
||||
){
|
||||
va_list ap;
|
||||
int rc;
|
||||
|
||||
va_start(ap, errmsg);
|
||||
rc = sqlite_get_table_vprintf(db, sqlFormat, resultp, nrow, ncol, errmsg, ap);
|
||||
va_end(ap);
|
||||
return rc;
|
||||
}
|
||||
int sqlite_get_table_vprintf(
|
||||
sqlite *db, /* An open database */
|
||||
const char *sqlFormat, /* printf-style format string for the SQL */
|
||||
char ***resultp, /* Result written to a char *[] that this points to */
|
||||
int *nrow, /* Number of result rows written here */
|
||||
int *ncolumn, /* Number of result columns written here */
|
||||
char **errmsg, /* Error msg written here */
|
||||
va_list ap /* Arguments to the format string */
|
||||
){
|
||||
char *zSql;
|
||||
int rc;
|
||||
|
||||
zSql = sqlite_vmprintf(sqlFormat, ap);
|
||||
rc = sqlite_get_table(db, zSql, resultp, nrow, ncolumn, errmsg);
|
||||
free(zSql);
|
||||
return rc;
|
||||
}
|
||||
113
sqlitebrowser/sqlitebrowser/sqlite_source/random.c
Executable file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
** 2001 September 15
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains code to implement a pseudo-random number
|
||||
** generator (PRNG) for SQLite.
|
||||
**
|
||||
** Random numbers are used by some of the database backends in order
|
||||
** to generate random integer keys for tables or random filenames.
|
||||
**
|
||||
** $Id: random.c,v 1.1.1.1 2003-08-21 02:24:18 tabuleiro Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "os.h"
|
||||
|
||||
|
||||
/*
|
||||
** Get a single 8-bit random value from the RC4 PRNG. The Mutex
|
||||
** must be held while executing this routine.
|
||||
**
|
||||
** Why not just use a library random generator like lrand48() for this?
|
||||
** Because the OP_NewRecno opcode in the VDBE depends on having a very
|
||||
** good source of random numbers. The lrand48() library function may
|
||||
** well be good enough. But maybe not. Or maybe lrand48() has some
|
||||
** subtle problems on some systems that could cause problems. It is hard
|
||||
** to know. To minimize the risk of problems due to bad lrand48()
|
||||
** implementations, SQLite uses this random number generator based
|
||||
** on RC4, which we know works very well.
|
||||
*/
|
||||
static int randomByte(){
|
||||
int t;
|
||||
|
||||
/* All threads share a single random number generator.
|
||||
** This structure is the current state of the generator.
|
||||
*/
|
||||
static struct {
|
||||
int isInit; /* True if initialized */
|
||||
int i, j; /* State variables */
|
||||
int s[256]; /* State variables */
|
||||
} prng;
|
||||
|
||||
/* Initialize the state of the random number generator once,
|
||||
** the first time this routine is called. The seed value does
|
||||
** not need to contain a lot of randomness since we are not
|
||||
** trying to do secure encryption or anything like that...
|
||||
**
|
||||
** Nothing in this file or anywhere else in SQLite does any kind of
|
||||
** encryption. The RC4 algorithm is being used as a PRNG (pseudo-random
|
||||
** number generator) not as an encryption device.
|
||||
*/
|
||||
if( !prng.isInit ){
|
||||
int i;
|
||||
char k[256];
|
||||
prng.j = 0;
|
||||
prng.i = 0;
|
||||
sqliteOsRandomSeed(k);
|
||||
for(i=0; i<256; i++){
|
||||
prng.s[i] = i;
|
||||
}
|
||||
for(i=0; i<256; i++){
|
||||
int t;
|
||||
prng.j = (prng.j + prng.s[i] + k[i]) & 0xff;
|
||||
t = prng.s[prng.j];
|
||||
prng.s[prng.j] = prng.s[i];
|
||||
prng.s[i] = t;
|
||||
}
|
||||
prng.isInit = 1;
|
||||
}
|
||||
|
||||
/* Generate and return single random byte
|
||||
*/
|
||||
prng.i = (prng.i + 1) & 0xff;
|
||||
prng.j = (prng.j + prng.s[prng.i]) & 0xff;
|
||||
t = prng.s[prng.i];
|
||||
prng.s[prng.i] = prng.s[prng.j];
|
||||
prng.s[prng.j] = t;
|
||||
t = prng.s[prng.i] + prng.s[prng.j];
|
||||
return prng.s[t & 0xff];
|
||||
}
|
||||
|
||||
/*
|
||||
** Return an random 8-bit integer.
|
||||
*/
|
||||
int sqliteRandomByte(){
|
||||
int r;
|
||||
sqliteOsEnterMutex();
|
||||
r = randomByte();
|
||||
sqliteOsLeaveMutex();
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a random 32-bit integer. The integer is generated by making
|
||||
** 4 calls to sqliteRandomByte().
|
||||
*/
|
||||
int sqliteRandomInteger(){
|
||||
int r;
|
||||
int i;
|
||||
sqliteOsEnterMutex();
|
||||
r = randomByte();
|
||||
for(i=1; i<4; i++){
|
||||
r = (r<<8) + randomByte();
|
||||
}
|
||||
sqliteOsLeaveMutex();
|
||||
return r;
|
||||
}
|
||||
2450
sqlitebrowser/sqlitebrowser/sqlite_source/select.c
Executable file
706
sqlitebrowser/sqlitebrowser/sqlite_source/sqlite.h
Executable file
@@ -0,0 +1,706 @@
|
||||
/*
|
||||
** 2001 September 15
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This header file defines the interface that the SQLite library
|
||||
** presents to client programs.
|
||||
**
|
||||
** @(#) $Id: sqlite.h,v 1.1.1.1 2003-08-21 02:24:20 tabuleiro Exp $
|
||||
*/
|
||||
#ifndef _SQLITE_H_
|
||||
#define _SQLITE_H_
|
||||
#include <stdarg.h> /* Needed for the definition of va_list */
|
||||
|
||||
/*
|
||||
** Make sure we can call this stuff from C++.
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The version of the SQLite library.
|
||||
*/
|
||||
#define SQLITE_VERSION "2.8.5"
|
||||
|
||||
/*
|
||||
** The version string is also compiled into the library so that a program
|
||||
** can check to make sure that the lib*.a file and the *.h file are from
|
||||
** the same version.
|
||||
*/
|
||||
extern const char sqlite_version[];
|
||||
|
||||
/*
|
||||
** The SQLITE_UTF8 macro is defined if the library expects to see
|
||||
** UTF-8 encoded data. The SQLITE_ISO8859 macro is defined if the
|
||||
** iso8859 encoded should be used.
|
||||
*/
|
||||
#define SQLITE_ISO8859 1
|
||||
|
||||
/*
|
||||
** The following constant holds one of two strings, "UTF-8" or "iso8859",
|
||||
** depending on which character encoding the SQLite library expects to
|
||||
** see. The character encoding makes a difference for the LIKE and GLOB
|
||||
** operators and for the LENGTH() and SUBSTR() functions.
|
||||
*/
|
||||
extern const char sqlite_encoding[];
|
||||
|
||||
/*
|
||||
** Each open sqlite database is represented by an instance of the
|
||||
** following opaque structure.
|
||||
*/
|
||||
typedef struct sqlite sqlite;
|
||||
|
||||
/*
|
||||
** A function to open a new sqlite database.
|
||||
**
|
||||
** If the database does not exist and mode indicates write
|
||||
** permission, then a new database is created. If the database
|
||||
** does not exist and mode does not indicate write permission,
|
||||
** then the open fails, an error message generated (if errmsg!=0)
|
||||
** and the function returns 0.
|
||||
**
|
||||
** If mode does not indicates user write permission, then the
|
||||
** database is opened read-only.
|
||||
**
|
||||
** The Truth: As currently implemented, all databases are opened
|
||||
** for writing all the time. Maybe someday we will provide the
|
||||
** ability to open a database readonly. The mode parameters is
|
||||
** provided in anticipation of that enhancement.
|
||||
*/
|
||||
sqlite *sqlite_open(const char *filename, int mode, char **errmsg);
|
||||
|
||||
/*
|
||||
** A function to close the database.
|
||||
**
|
||||
** Call this function with a pointer to a structure that was previously
|
||||
** returned from sqlite_open() and the corresponding database will by closed.
|
||||
*/
|
||||
void sqlite_close(sqlite *);
|
||||
|
||||
/*
|
||||
** The type for a callback function.
|
||||
*/
|
||||
typedef int (*sqlite_callback)(void*,int,char**, char**);
|
||||
|
||||
/*
|
||||
** A function to executes one or more statements of SQL.
|
||||
**
|
||||
** If one or more of the SQL statements are queries, then
|
||||
** the callback function specified by the 3rd parameter is
|
||||
** invoked once for each row of the query result. This callback
|
||||
** should normally return 0. If the callback returns a non-zero
|
||||
** value then the query is aborted, all subsequent SQL statements
|
||||
** are skipped and the sqlite_exec() function returns the SQLITE_ABORT.
|
||||
**
|
||||
** The 4th parameter is an arbitrary pointer that is passed
|
||||
** to the callback function as its first parameter.
|
||||
**
|
||||
** The 2nd parameter to the callback function is the number of
|
||||
** columns in the query result. The 3rd parameter to the callback
|
||||
** is an array of strings holding the values for each column.
|
||||
** The 4th parameter to the callback is an array of strings holding
|
||||
** the names of each column.
|
||||
**
|
||||
** The callback function may be NULL, even for queries. A NULL
|
||||
** callback is not an error. It just means that no callback
|
||||
** will be invoked.
|
||||
**
|
||||
** If an error occurs while parsing or evaluating the SQL (but
|
||||
** not while executing the callback) then an appropriate error
|
||||
** message is written into memory obtained from malloc() and
|
||||
** *errmsg is made to point to that message. The calling function
|
||||
** is responsible for freeing the memory that holds the error
|
||||
** message. Use sqlite_freemem() for this. If errmsg==NULL,
|
||||
** then no error message is ever written.
|
||||
**
|
||||
** The return value is is SQLITE_OK if there are no errors and
|
||||
** some other return code if there is an error. The particular
|
||||
** return value depends on the type of error.
|
||||
**
|
||||
** If the query could not be executed because a database file is
|
||||
** locked or busy, then this function returns SQLITE_BUSY. (This
|
||||
** behavior can be modified somewhat using the sqlite_busy_handler()
|
||||
** and sqlite_busy_timeout() functions below.)
|
||||
*/
|
||||
int sqlite_exec(
|
||||
sqlite*, /* An open database */
|
||||
const char *sql, /* SQL to be executed */
|
||||
sqlite_callback, /* Callback function */
|
||||
void *, /* 1st argument to callback function */
|
||||
char **errmsg /* Error msg written here */
|
||||
);
|
||||
|
||||
/*
|
||||
** Return values for sqlite_exec() and sqlite_step()
|
||||
*/
|
||||
#define SQLITE_OK 0 /* Successful result */
|
||||
#define SQLITE_ERROR 1 /* SQL error or missing database */
|
||||
#define SQLITE_INTERNAL 2 /* An internal logic error in SQLite */
|
||||
#define SQLITE_PERM 3 /* Access permission denied */
|
||||
#define SQLITE_ABORT 4 /* Callback routine requested an abort */
|
||||
#define SQLITE_BUSY 5 /* The database file is locked */
|
||||
#define SQLITE_LOCKED 6 /* A table in the database is locked */
|
||||
#define SQLITE_NOMEM 7 /* A malloc() failed */
|
||||
#define SQLITE_READONLY 8 /* Attempt to write a readonly database */
|
||||
#define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite_interrupt() */
|
||||
#define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */
|
||||
#define SQLITE_CORRUPT 11 /* The database disk image is malformed */
|
||||
#define SQLITE_NOTFOUND 12 /* (Internal Only) Table or record not found */
|
||||
#define SQLITE_FULL 13 /* Insertion failed because database is full */
|
||||
#define SQLITE_CANTOPEN 14 /* Unable to open the database file */
|
||||
#define SQLITE_PROTOCOL 15 /* Database lock protocol error */
|
||||
#define SQLITE_EMPTY 16 /* (Internal Only) Database table is empty */
|
||||
#define SQLITE_SCHEMA 17 /* The database schema changed */
|
||||
#define SQLITE_TOOBIG 18 /* Too much data for one row of a table */
|
||||
#define SQLITE_CONSTRAINT 19 /* Abort due to contraint violation */
|
||||
#define SQLITE_MISMATCH 20 /* Data type mismatch */
|
||||
#define SQLITE_MISUSE 21 /* Library used incorrectly */
|
||||
#define SQLITE_NOLFS 22 /* Uses OS features not supported on host */
|
||||
#define SQLITE_AUTH 23 /* Authorization denied */
|
||||
#define SQLITE_FORMAT 24 /* Auxiliary database format error */
|
||||
#define SQLITE_ROW 100 /* sqlite_step() has another row ready */
|
||||
#define SQLITE_DONE 101 /* sqlite_step() has finished executing */
|
||||
|
||||
/*
|
||||
** Each entry in an SQLite table has a unique integer key. (The key is
|
||||
** the value of the INTEGER PRIMARY KEY column if there is such a column,
|
||||
** otherwise the key is generated at random. The unique key is always
|
||||
** available as the ROWID, OID, or _ROWID_ column.) The following routine
|
||||
** returns the integer key of the most recent insert in the database.
|
||||
**
|
||||
** This function is similar to the mysql_insert_id() function from MySQL.
|
||||
*/
|
||||
int sqlite_last_insert_rowid(sqlite*);
|
||||
|
||||
/*
|
||||
** This function returns the number of database rows that were changed
|
||||
** (or inserted or deleted) by the most recent called sqlite_exec().
|
||||
**
|
||||
** All changes are counted, even if they were later undone by a
|
||||
** ROLLBACK or ABORT. Except, changes associated with creating and
|
||||
** dropping tables are not counted.
|
||||
**
|
||||
** If a callback invokes sqlite_exec() recursively, then the changes
|
||||
** in the inner, recursive call are counted together with the changes
|
||||
** in the outer call.
|
||||
**
|
||||
** SQLite implements the command "DELETE FROM table" without a WHERE clause
|
||||
** by dropping and recreating the table. (This is much faster than going
|
||||
** through and deleting individual elements form the table.) Because of
|
||||
** this optimization, the change count for "DELETE FROM table" will be
|
||||
** zero regardless of the number of elements that were originally in the
|
||||
** table. To get an accurate count of the number of rows deleted, use
|
||||
** "DELETE FROM table WHERE 1" instead.
|
||||
*/
|
||||
int sqlite_changes(sqlite*);
|
||||
|
||||
/* If the parameter to this routine is one of the return value constants
|
||||
** defined above, then this routine returns a constant text string which
|
||||
** descripts (in English) the meaning of the return value.
|
||||
*/
|
||||
const char *sqlite_error_string(int);
|
||||
#define sqliteErrStr sqlite_error_string /* Legacy. Do not use in new code. */
|
||||
|
||||
/* This function causes any pending database operation to abort and
|
||||
** return at its earliest opportunity. This routine is typically
|
||||
** called in response to a user action such as pressing "Cancel"
|
||||
** or Ctrl-C where the user wants a long query operation to halt
|
||||
** immediately.
|
||||
*/
|
||||
void sqlite_interrupt(sqlite*);
|
||||
|
||||
|
||||
/* This function returns true if the given input string comprises
|
||||
** one or more complete SQL statements.
|
||||
**
|
||||
** The algorithm is simple. If the last token other than spaces
|
||||
** and comments is a semicolon, then return true. otherwise return
|
||||
** false.
|
||||
*/
|
||||
int sqlite_complete(const char *sql);
|
||||
|
||||
/*
|
||||
** This routine identifies a callback function that is invoked
|
||||
** whenever an attempt is made to open a database table that is
|
||||
** currently locked by another process or thread. If the busy callback
|
||||
** is NULL, then sqlite_exec() returns SQLITE_BUSY immediately if
|
||||
** it finds a locked table. If the busy callback is not NULL, then
|
||||
** sqlite_exec() invokes the callback with three arguments. The
|
||||
** second argument is the name of the locked table and the third
|
||||
** argument is the number of times the table has been busy. If the
|
||||
** busy callback returns 0, then sqlite_exec() immediately returns
|
||||
** SQLITE_BUSY. If the callback returns non-zero, then sqlite_exec()
|
||||
** tries to open the table again and the cycle repeats.
|
||||
**
|
||||
** The default busy callback is NULL.
|
||||
**
|
||||
** Sqlite is re-entrant, so the busy handler may start a new query.
|
||||
** (It is not clear why anyone would every want to do this, but it
|
||||
** is allowed, in theory.) But the busy handler may not close the
|
||||
** database. Closing the database from a busy handler will delete
|
||||
** data structures out from under the executing query and will
|
||||
** probably result in a coredump.
|
||||
*/
|
||||
void sqlite_busy_handler(sqlite*, int(*)(void*,const char*,int), void*);
|
||||
|
||||
/*
|
||||
** This routine sets a busy handler that sleeps for a while when a
|
||||
** table is locked. The handler will sleep multiple times until
|
||||
** at least "ms" milleseconds of sleeping have been done. After
|
||||
** "ms" milleseconds of sleeping, the handler returns 0 which
|
||||
** causes sqlite_exec() to return SQLITE_BUSY.
|
||||
**
|
||||
** Calling this routine with an argument less than or equal to zero
|
||||
** turns off all busy handlers.
|
||||
*/
|
||||
void sqlite_busy_timeout(sqlite*, int ms);
|
||||
|
||||
/*
|
||||
** This next routine is really just a wrapper around sqlite_exec().
|
||||
** Instead of invoking a user-supplied callback for each row of the
|
||||
** result, this routine remembers each row of the result in memory
|
||||
** obtained from malloc(), then returns all of the result after the
|
||||
** query has finished.
|
||||
**
|
||||
** As an example, suppose the query result where this table:
|
||||
**
|
||||
** Name | Age
|
||||
** -----------------------
|
||||
** Alice | 43
|
||||
** Bob | 28
|
||||
** Cindy | 21
|
||||
**
|
||||
** If the 3rd argument were &azResult then after the function returns
|
||||
** azResult will contain the following data:
|
||||
**
|
||||
** azResult[0] = "Name";
|
||||
** azResult[1] = "Age";
|
||||
** azResult[2] = "Alice";
|
||||
** azResult[3] = "43";
|
||||
** azResult[4] = "Bob";
|
||||
** azResult[5] = "28";
|
||||
** azResult[6] = "Cindy";
|
||||
** azResult[7] = "21";
|
||||
**
|
||||
** Notice that there is an extra row of data containing the column
|
||||
** headers. But the *nrow return value is still 3. *ncolumn is
|
||||
** set to 2. In general, the number of values inserted into azResult
|
||||
** will be ((*nrow) + 1)*(*ncolumn).
|
||||
**
|
||||
** After the calling function has finished using the result, it should
|
||||
** pass the result data pointer to sqlite_free_table() in order to
|
||||
** release the memory that was malloc-ed. Because of the way the
|
||||
** malloc() happens, the calling function must not try to call
|
||||
** malloc() directly. Only sqlite_free_table() is able to release
|
||||
** the memory properly and safely.
|
||||
**
|
||||
** The return value of this routine is the same as from sqlite_exec().
|
||||
*/
|
||||
int sqlite_get_table(
|
||||
sqlite*, /* An open database */
|
||||
const char *sql, /* SQL to be executed */
|
||||
char ***resultp, /* Result written to a char *[] that this points to */
|
||||
int *nrow, /* Number of result rows written here */
|
||||
int *ncolumn, /* Number of result columns written here */
|
||||
char **errmsg /* Error msg written here */
|
||||
);
|
||||
|
||||
/*
|
||||
** Call this routine to free the memory that sqlite_get_table() allocated.
|
||||
*/
|
||||
void sqlite_free_table(char **result);
|
||||
|
||||
/*
|
||||
** The following routines are wrappers around sqlite_exec() and
|
||||
** sqlite_get_table(). The only difference between the routines that
|
||||
** follow and the originals is that the second argument to the
|
||||
** routines that follow is really a printf()-style format
|
||||
** string describing the SQL to be executed. Arguments to the format
|
||||
** string appear at the end of the argument list.
|
||||
**
|
||||
** All of the usual printf formatting options apply. In addition, there
|
||||
** is a "%q" option. %q works like %s in that it substitutes a null-terminated
|
||||
** string from the argument list. But %q also doubles every '\'' character.
|
||||
** %q is designed for use inside a string literal. By doubling each '\''
|
||||
** character it escapes that character and allows it to be inserted into
|
||||
** the string.
|
||||
**
|
||||
** For example, so some string variable contains text as follows:
|
||||
**
|
||||
** char *zText = "It's a happy day!";
|
||||
**
|
||||
** We can use this text in an SQL statement as follows:
|
||||
**
|
||||
** sqlite_exec_printf(db, "INSERT INTO table VALUES('%q')",
|
||||
** callback1, 0, 0, zText);
|
||||
**
|
||||
** Because the %q format string is used, the '\'' character in zText
|
||||
** is escaped and the SQL generated is as follows:
|
||||
**
|
||||
** INSERT INTO table1 VALUES('It''s a happy day!')
|
||||
**
|
||||
** This is correct. Had we used %s instead of %q, the generated SQL
|
||||
** would have looked like this:
|
||||
**
|
||||
** INSERT INTO table1 VALUES('It's a happy day!');
|
||||
**
|
||||
** This second example is an SQL syntax error. As a general rule you
|
||||
** should always use %q instead of %s when inserting text into a string
|
||||
** literal.
|
||||
*/
|
||||
int sqlite_exec_printf(
|
||||
sqlite*, /* An open database */
|
||||
const char *sqlFormat, /* printf-style format string for the SQL */
|
||||
sqlite_callback, /* Callback function */
|
||||
void *, /* 1st argument to callback function */
|
||||
char **errmsg, /* Error msg written here */
|
||||
... /* Arguments to the format string. */
|
||||
);
|
||||
int sqlite_exec_vprintf(
|
||||
sqlite*, /* An open database */
|
||||
const char *sqlFormat, /* printf-style format string for the SQL */
|
||||
sqlite_callback, /* Callback function */
|
||||
void *, /* 1st argument to callback function */
|
||||
char **errmsg, /* Error msg written here */
|
||||
va_list ap /* Arguments to the format string. */
|
||||
);
|
||||
int sqlite_get_table_printf(
|
||||
sqlite*, /* An open database */
|
||||
const char *sqlFormat, /* printf-style format string for the SQL */
|
||||
char ***resultp, /* Result written to a char *[] that this points to */
|
||||
int *nrow, /* Number of result rows written here */
|
||||
int *ncolumn, /* Number of result columns written here */
|
||||
char **errmsg, /* Error msg written here */
|
||||
... /* Arguments to the format string */
|
||||
);
|
||||
int sqlite_get_table_vprintf(
|
||||
sqlite*, /* An open database */
|
||||
const char *sqlFormat, /* printf-style format string for the SQL */
|
||||
char ***resultp, /* Result written to a char *[] that this points to */
|
||||
int *nrow, /* Number of result rows written here */
|
||||
int *ncolumn, /* Number of result columns written here */
|
||||
char **errmsg, /* Error msg written here */
|
||||
va_list ap /* Arguments to the format string */
|
||||
);
|
||||
char *sqlite_mprintf(const char*,...);
|
||||
char *sqlite_vmprintf(const char*, va_list);
|
||||
|
||||
/*
|
||||
** Windows systems should call this routine to free memory that
|
||||
** is returned in the in the errmsg parameter of sqlite_open() when
|
||||
** SQLite is a DLL. For some reason, it does not work to call free()
|
||||
** directly.
|
||||
*/
|
||||
void sqlite_freemem(void *p);
|
||||
|
||||
/*
|
||||
** Windows systems need functions to call to return the sqlite_version
|
||||
** and sqlite_encoding strings.
|
||||
*/
|
||||
const char *sqlite_libversion(void);
|
||||
const char *sqlite_libencoding(void);
|
||||
|
||||
/*
|
||||
** A pointer to the following structure is used to communicate with
|
||||
** the implementations of user-defined functions.
|
||||
*/
|
||||
typedef struct sqlite_func sqlite_func;
|
||||
|
||||
/*
|
||||
** Use the following routines to create new user-defined functions. See
|
||||
** the documentation for details.
|
||||
*/
|
||||
int sqlite_create_function(
|
||||
sqlite*, /* Database where the new function is registered */
|
||||
const char *zName, /* Name of the new function */
|
||||
int nArg, /* Number of arguments. -1 means any number */
|
||||
void (*xFunc)(sqlite_func*,int,const char**), /* C code to implement */
|
||||
void *pUserData /* Available via the sqlite_user_data() call */
|
||||
);
|
||||
int sqlite_create_aggregate(
|
||||
sqlite*, /* Database where the new function is registered */
|
||||
const char *zName, /* Name of the function */
|
||||
int nArg, /* Number of arguments */
|
||||
void (*xStep)(sqlite_func*,int,const char**), /* Called for each row */
|
||||
void (*xFinalize)(sqlite_func*), /* Called once to get final result */
|
||||
void *pUserData /* Available via the sqlite_user_data() call */
|
||||
);
|
||||
|
||||
/*
|
||||
** Use the following routine to define the datatype returned by a
|
||||
** user-defined function. The second argument can be one of the
|
||||
** constants SQLITE_NUMERIC, SQLITE_TEXT, or SQLITE_ARGS or it
|
||||
** can be an integer greater than or equal to zero. The datatype
|
||||
** will be numeric or text (the only two types supported) if the
|
||||
** argument is SQLITE_NUMERIC or SQLITE_TEXT. If the argument is
|
||||
** SQLITE_ARGS, then the datatype is numeric if any argument to the
|
||||
** function is numeric and is text otherwise. If the second argument
|
||||
** is an integer, then the datatype of the result is the same as the
|
||||
** parameter to the function that corresponds to that integer.
|
||||
*/
|
||||
int sqlite_function_type(
|
||||
sqlite *db, /* The database there the function is registered */
|
||||
const char *zName, /* Name of the function */
|
||||
int datatype /* The datatype for this function */
|
||||
);
|
||||
#define SQLITE_NUMERIC (-1)
|
||||
#define SQLITE_TEXT (-2)
|
||||
#define SQLITE_ARGS (-3)
|
||||
|
||||
/*
|
||||
** The user function implementations call one of the following four routines
|
||||
** in order to return their results. The first parameter to each of these
|
||||
** routines is a copy of the first argument to xFunc() or xFinialize().
|
||||
** The second parameter to these routines is the result to be returned.
|
||||
** A NULL can be passed as the second parameter to sqlite_set_result_string()
|
||||
** in order to return a NULL result.
|
||||
**
|
||||
** The 3rd argument to _string and _error is the number of characters to
|
||||
** take from the string. If this argument is negative, then all characters
|
||||
** up to and including the first '\000' are used.
|
||||
**
|
||||
** The sqlite_set_result_string() function allocates a buffer to hold the
|
||||
** result and returns a pointer to this buffer. The calling routine
|
||||
** (that is, the implmentation of a user function) can alter the content
|
||||
** of this buffer if desired.
|
||||
*/
|
||||
char *sqlite_set_result_string(sqlite_func*,const char*,int);
|
||||
void sqlite_set_result_int(sqlite_func*,int);
|
||||
void sqlite_set_result_double(sqlite_func*,double);
|
||||
void sqlite_set_result_error(sqlite_func*,const char*,int);
|
||||
|
||||
/*
|
||||
** The pUserData parameter to the sqlite_create_function() and
|
||||
** sqlite_create_aggregate() routines used to register user functions
|
||||
** is available to the implementation of the function using this
|
||||
** call.
|
||||
*/
|
||||
void *sqlite_user_data(sqlite_func*);
|
||||
|
||||
/*
|
||||
** Aggregate functions use the following routine to allocate
|
||||
** a structure for storing their state. The first time this routine
|
||||
** is called for a particular aggregate, a new structure of size nBytes
|
||||
** is allocated, zeroed, and returned. On subsequent calls (for the
|
||||
** same aggregate instance) the same buffer is returned. The implementation
|
||||
** of the aggregate can use the returned buffer to accumulate data.
|
||||
**
|
||||
** The buffer allocated is freed automatically be SQLite.
|
||||
*/
|
||||
void *sqlite_aggregate_context(sqlite_func*, int nBytes);
|
||||
|
||||
/*
|
||||
** The next routine returns the number of calls to xStep for a particular
|
||||
** aggregate function instance. The current call to xStep counts so this
|
||||
** routine always returns at least 1.
|
||||
*/
|
||||
int sqlite_aggregate_count(sqlite_func*);
|
||||
|
||||
/*
|
||||
** This routine registers a callback with the SQLite library. The
|
||||
** callback is invoked (at compile-time, not at run-time) for each
|
||||
** attempt to access a column of a table in the database. The callback
|
||||
** returns SQLITE_OK if access is allowed, SQLITE_DENY if the entire
|
||||
** SQL statement should be aborted with an error and SQLITE_IGNORE
|
||||
** if the column should be treated as a NULL value.
|
||||
*/
|
||||
int sqlite_set_authorizer(
|
||||
sqlite*,
|
||||
int (*xAuth)(void*,int,const char*,const char*,const char*,const char*),
|
||||
void *pUserData
|
||||
);
|
||||
|
||||
/*
|
||||
** The second parameter to the access authorization function above will
|
||||
** be one of the values below. These values signify what kind of operation
|
||||
** is to be authorized. The 3rd and 4th parameters to the authorization
|
||||
** function will be parameters or NULL depending on which of the following
|
||||
** codes is used as the second parameter. The 5th parameter is the name
|
||||
** of the database ("main", "temp", etc.) if applicable. The 6th parameter
|
||||
** is the name of the inner-most trigger or view that is responsible for
|
||||
** the access attempt or NULL if this access attempt is directly from
|
||||
** input SQL code.
|
||||
**
|
||||
** Arg-3 Arg-4
|
||||
*/
|
||||
#define SQLITE_COPY 0 /* Table Name File Name */
|
||||
#define SQLITE_CREATE_INDEX 1 /* Index Name Table Name */
|
||||
#define SQLITE_CREATE_TABLE 2 /* Table Name NULL */
|
||||
#define SQLITE_CREATE_TEMP_INDEX 3 /* Index Name Table Name */
|
||||
#define SQLITE_CREATE_TEMP_TABLE 4 /* Table Name NULL */
|
||||
#define SQLITE_CREATE_TEMP_TRIGGER 5 /* Trigger Name Table Name */
|
||||
#define SQLITE_CREATE_TEMP_VIEW 6 /* View Name NULL */
|
||||
#define SQLITE_CREATE_TRIGGER 7 /* Trigger Name Table Name */
|
||||
#define SQLITE_CREATE_VIEW 8 /* View Name NULL */
|
||||
#define SQLITE_DELETE 9 /* Table Name NULL */
|
||||
#define SQLITE_DROP_INDEX 10 /* Index Name Table Name */
|
||||
#define SQLITE_DROP_TABLE 11 /* Table Name NULL */
|
||||
#define SQLITE_DROP_TEMP_INDEX 12 /* Index Name Table Name */
|
||||
#define SQLITE_DROP_TEMP_TABLE 13 /* Table Name NULL */
|
||||
#define SQLITE_DROP_TEMP_TRIGGER 14 /* Trigger Name Table Name */
|
||||
#define SQLITE_DROP_TEMP_VIEW 15 /* View Name NULL */
|
||||
#define SQLITE_DROP_TRIGGER 16 /* Trigger Name Table Name */
|
||||
#define SQLITE_DROP_VIEW 17 /* View Name NULL */
|
||||
#define SQLITE_INSERT 18 /* Table Name NULL */
|
||||
#define SQLITE_PRAGMA 19 /* Pragma Name 1st arg or NULL */
|
||||
#define SQLITE_READ 20 /* Table Name Column Name */
|
||||
#define SQLITE_SELECT 21 /* NULL NULL */
|
||||
#define SQLITE_TRANSACTION 22 /* NULL NULL */
|
||||
#define SQLITE_UPDATE 23 /* Table Name Column Name */
|
||||
#define SQLITE_ATTACH 24 /* Filename NULL */
|
||||
#define SQLITE_DETACH 25 /* Database Name NULL */
|
||||
|
||||
|
||||
/*
|
||||
** The return value of the authorization function should be one of the
|
||||
** following constants:
|
||||
*/
|
||||
/* #define SQLITE_OK 0 // Allow access (This is actually defined above) */
|
||||
#define SQLITE_DENY 1 /* Abort the SQL statement with an error */
|
||||
#define SQLITE_IGNORE 2 /* Don't allow access, but don't generate an error */
|
||||
|
||||
/*
|
||||
** Register a function that is called at every invocation of sqlite_exec()
|
||||
** or sqlite_compile(). This function can be used (for example) to generate
|
||||
** a log file of all SQL executed against a database.
|
||||
*/
|
||||
void *sqlite_trace(sqlite*, void(*xTrace)(void*,const char*), void*);
|
||||
|
||||
/*** The Callback-Free API
|
||||
**
|
||||
** The following routines implement a new way to access SQLite that does not
|
||||
** involve the use of callbacks.
|
||||
**
|
||||
** An sqlite_vm is an opaque object that represents a single SQL statement
|
||||
** that is ready to be executed.
|
||||
*/
|
||||
typedef struct sqlite_vm sqlite_vm;
|
||||
|
||||
/*
|
||||
** To execute an SQLite query without the use of callbacks, you first have
|
||||
** to compile the SQL using this routine. The 1st parameter "db" is a pointer
|
||||
** to an sqlite object obtained from sqlite_open(). The 2nd parameter
|
||||
** "zSql" is the text of the SQL to be compiled. The remaining parameters
|
||||
** are all outputs.
|
||||
**
|
||||
** *pzTail is made to point to the first character past the end of the first
|
||||
** SQL statement in zSql. This routine only compiles the first statement
|
||||
** in zSql, so *pzTail is left pointing to what remains uncompiled.
|
||||
**
|
||||
** *ppVm is left pointing to a "virtual machine" that can be used to execute
|
||||
** the compiled statement. Or if there is an error, *ppVm may be set to NULL.
|
||||
** If the input text contained no SQL (if the input is and empty string or
|
||||
** a comment) then *ppVm is set to NULL.
|
||||
**
|
||||
** If any errors are detected during compilation, an error message is written
|
||||
** into space obtained from malloc() and *pzErrMsg is made to point to that
|
||||
** error message. The calling routine is responsible for freeing the text
|
||||
** of this message when it has finished with it. Use sqlite_freemem() to
|
||||
** free the message. pzErrMsg may be NULL in which case no error message
|
||||
** will be generated.
|
||||
**
|
||||
** On success, SQLITE_OK is returned. Otherwise and error code is returned.
|
||||
*/
|
||||
int sqlite_compile(
|
||||
sqlite *db, /* The open database */
|
||||
const char *zSql, /* SQL statement to be compiled */
|
||||
const char **pzTail, /* OUT: uncompiled tail of zSql */
|
||||
sqlite_vm **ppVm, /* OUT: the virtual machine to execute zSql */
|
||||
char **pzErrmsg /* OUT: Error message. */
|
||||
);
|
||||
|
||||
/*
|
||||
** After an SQL statement has been compiled, it is handed to this routine
|
||||
** to be executed. This routine executes the statement as far as it can
|
||||
** go then returns. The return value will be one of SQLITE_DONE,
|
||||
** SQLITE_ERROR, SQLITE_BUSY, SQLITE_ROW, or SQLITE_MISUSE.
|
||||
**
|
||||
** SQLITE_DONE means that the execute of the SQL statement is complete
|
||||
** an no errors have occurred. sqlite_step() should not be called again
|
||||
** for the same virtual machine. *pN is set to the number of columns in
|
||||
** the result set and *pazColName is set to an array of strings that
|
||||
** describe the column names and datatypes. The name of the i-th column
|
||||
** is (*pazColName)[i] and the datatype of the i-th column is
|
||||
** (*pazColName)[i+*pN]. *pazValue is set to NULL.
|
||||
**
|
||||
** SQLITE_ERROR means that the virtual machine encountered a run-time
|
||||
** error. sqlite_step() should not be called again for the same
|
||||
** virtual machine. *pN is set to 0 and *pazColName and *pazValue are set
|
||||
** to NULL. Use sqlite_finalize() to obtain the specific error code
|
||||
** and the error message text for the error.
|
||||
**
|
||||
** SQLITE_BUSY means that an attempt to open the database failed because
|
||||
** another thread or process is holding a lock. The calling routine
|
||||
** can try again to open the database by calling sqlite_step() again.
|
||||
** The return code will only be SQLITE_BUSY if no busy handler is registered
|
||||
** using the sqlite_busy_handler() or sqlite_busy_timeout() routines. If
|
||||
** a busy handler callback has been registered but returns 0, then this
|
||||
** routine will return SQLITE_ERROR and sqltie_finalize() will return
|
||||
** SQLITE_BUSY when it is called.
|
||||
**
|
||||
** SQLITE_ROW means that a single row of the result is now available.
|
||||
** The data is contained in *pazValue. The value of the i-th column is
|
||||
** (*azValue)[i]. *pN and *pazColName are set as described in SQLITE_DONE.
|
||||
** Invoke sqlite_step() again to advance to the next row.
|
||||
**
|
||||
** SQLITE_MISUSE is returned if sqlite_step() is called incorrectly.
|
||||
** For example, if you call sqlite_step() after the virtual machine
|
||||
** has halted (after a prior call to sqlite_step() has returned SQLITE_DONE)
|
||||
** or if you call sqlite_step() with an incorrectly initialized virtual
|
||||
** machine or a virtual machine that has been deleted or that is associated
|
||||
** with an sqlite structure that has been closed.
|
||||
*/
|
||||
int sqlite_step(
|
||||
sqlite_vm *pVm, /* The virtual machine to execute */
|
||||
int *pN, /* OUT: Number of columns in result */
|
||||
const char ***pazValue, /* OUT: Column data */
|
||||
const char ***pazColName /* OUT: Column names and datatypes */
|
||||
);
|
||||
|
||||
/*
|
||||
** This routine is called to delete a virtual machine after it has finished
|
||||
** executing. The return value is the result code. SQLITE_OK is returned
|
||||
** if the statement executed successfully and some other value is returned if
|
||||
** there was any kind of error. If an error occurred and pzErrMsg is not
|
||||
** NULL, then an error message is written into memory obtained from malloc()
|
||||
** and *pzErrMsg is made to point to that error message. The calling routine
|
||||
** should use sqlite_freemem() to delete this message when it has finished
|
||||
** with it.
|
||||
**
|
||||
** This routine can be called at any point during the execution of the
|
||||
** virtual machine. If the virtual machine has not completed execution
|
||||
** when this routine is called, that is like encountering an error or
|
||||
** an interrupt. (See sqlite_interrupt().) Incomplete updates may be
|
||||
** rolled back and transactions cancelled, depending on the circumstances,
|
||||
** and the result code returned will be SQLITE_ABORT.
|
||||
*/
|
||||
int sqlite_finalize(sqlite_vm*, char **pzErrMsg);
|
||||
|
||||
/*
|
||||
** This routine deletes the virtual machine, writes any error message to
|
||||
** *pzErrMsg and returns an SQLite return code in the same way as the
|
||||
** sqlite_finalize() function.
|
||||
**
|
||||
** Additionally, if ppVm is not NULL, *ppVm is left pointing to a new virtual
|
||||
** machine loaded with the compiled version of the original query ready for
|
||||
** execution.
|
||||
**
|
||||
** If sqlite_reset() returns SQLITE_SCHEMA, then *ppVm is set to NULL.
|
||||
**
|
||||
******* THIS IS AN EXPERIMENTAL API AND IS SUBJECT TO CHANGE ******
|
||||
*/
|
||||
int sqlite_reset(sqlite_vm *, char **pzErrMsg, sqlite_vm **ppVm);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* End of the 'extern "C"' block */
|
||||
#endif
|
||||
|
||||
#endif /* _SQLITE_H_ */
|
||||
1195
sqlitebrowser/sqlitebrowser/sqlite_source/sqliteInt.h
Executable file
50
sqlitebrowser/sqlitebrowser/sqlite_source/sqlite_source.pro
Executable file
@@ -0,0 +1,50 @@
|
||||
SOURCES += where.c \
|
||||
attach.c \
|
||||
auth.c \
|
||||
btree.c \
|
||||
btree_rb.c \
|
||||
build.c \
|
||||
copy.c \
|
||||
delete.c \
|
||||
encode.c \
|
||||
expr.c \
|
||||
func.c \
|
||||
hash.c \
|
||||
insert.c \
|
||||
main.c \
|
||||
opcodes.c \
|
||||
os.c \
|
||||
pager.c \
|
||||
parse.c \
|
||||
pragma.c \
|
||||
printf.c \
|
||||
random.c \
|
||||
select.c \
|
||||
table.c \
|
||||
tokenize.c \
|
||||
trigger.c \
|
||||
update.c \
|
||||
util.c \
|
||||
vacuum.c \
|
||||
vdbe.c
|
||||
HEADERS += btree.h \
|
||||
config.h \
|
||||
encode.h \
|
||||
hash.h \
|
||||
opcodes.h \
|
||||
os.h \
|
||||
pager.h \
|
||||
parse.h \
|
||||
sqlite.h \
|
||||
sqliteInt.h \
|
||||
vdbe.h
|
||||
unix {
|
||||
UI_DIR = .ui
|
||||
MOC_DIR = .moc
|
||||
OBJECTS_DIR = .obj
|
||||
}
|
||||
TEMPLATE =lib
|
||||
CONFIG += qt warn_on release staticlib
|
||||
LANGUAGE = C
|
||||
DEFINES -= UNICODE
|
||||
DEFINES += NDEBUG
|
||||
202
sqlitebrowser/sqlitebrowser/sqlite_source/table.c
Executable file
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
** 2001 September 15
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains the sqlite_get_table() and sqlite_free_table()
|
||||
** interface routines. These are just wrappers around the main
|
||||
** interface routine of sqlite_exec().
|
||||
**
|
||||
** These routines are in a separate files so that they will not be linked
|
||||
** if they are not used.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** This structure is used to pass data from sqlite_get_table() through
|
||||
** to the callback function is uses to build the result.
|
||||
*/
|
||||
typedef struct TabResult {
|
||||
char **azResult;
|
||||
char *zErrMsg;
|
||||
int nResult;
|
||||
int nAlloc;
|
||||
int nRow;
|
||||
int nColumn;
|
||||
int nData;
|
||||
int rc;
|
||||
} TabResult;
|
||||
|
||||
/*
|
||||
** This routine is called once for each row in the result table. Its job
|
||||
** is to fill in the TabResult structure appropriately, allocating new
|
||||
** memory as necessary.
|
||||
*/
|
||||
static int sqlite_get_table_cb(void *pArg, int nCol, char **argv, char **colv){
|
||||
TabResult *p = (TabResult*)pArg;
|
||||
int need;
|
||||
int i;
|
||||
char *z;
|
||||
|
||||
/* Make sure there is enough space in p->azResult to hold everything
|
||||
** we need to remember from this invocation of the callback.
|
||||
*/
|
||||
if( p->nRow==0 && argv!=0 ){
|
||||
need = nCol*2;
|
||||
}else{
|
||||
need = nCol;
|
||||
}
|
||||
if( p->nData + need >= p->nAlloc ){
|
||||
char **azNew;
|
||||
p->nAlloc = p->nAlloc*2 + need + 1;
|
||||
azNew = realloc( p->azResult, sizeof(char*)*p->nAlloc );
|
||||
if( azNew==0 ){
|
||||
p->rc = SQLITE_NOMEM;
|
||||
return 1;
|
||||
}
|
||||
p->azResult = azNew;
|
||||
}
|
||||
|
||||
/* If this is the first row, then generate an extra row containing
|
||||
** the names of all columns.
|
||||
*/
|
||||
if( p->nRow==0 ){
|
||||
p->nColumn = nCol;
|
||||
for(i=0; i<nCol; i++){
|
||||
if( colv[i]==0 ){
|
||||
z = 0;
|
||||
}else{
|
||||
z = malloc( strlen(colv[i])+1 );
|
||||
if( z==0 ){
|
||||
p->rc = SQLITE_NOMEM;
|
||||
return 1;
|
||||
}
|
||||
strcpy(z, colv[i]);
|
||||
}
|
||||
p->azResult[p->nData++] = z;
|
||||
}
|
||||
}else if( p->nColumn!=nCol ){
|
||||
sqliteSetString(&p->zErrMsg,
|
||||
"sqlite_get_table() called with two or more incompatible queries", 0);
|
||||
p->rc = SQLITE_ERROR;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Copy over the row data
|
||||
*/
|
||||
if( argv!=0 ){
|
||||
for(i=0; i<nCol; i++){
|
||||
if( argv[i]==0 ){
|
||||
z = 0;
|
||||
}else{
|
||||
z = malloc( strlen(argv[i])+1 );
|
||||
if( z==0 ){
|
||||
p->rc = SQLITE_NOMEM;
|
||||
return 1;
|
||||
}
|
||||
strcpy(z, argv[i]);
|
||||
}
|
||||
p->azResult[p->nData++] = z;
|
||||
}
|
||||
p->nRow++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Query the database. But instead of invoking a callback for each row,
|
||||
** malloc() for space to hold the result and return the entire results
|
||||
** at the conclusion of the call.
|
||||
**
|
||||
** The result that is written to ***pazResult is held in memory obtained
|
||||
** from malloc(). But the caller cannot free this memory directly.
|
||||
** Instead, the entire table should be passed to sqlite_free_table() when
|
||||
** the calling procedure is finished using it.
|
||||
*/
|
||||
int sqlite_get_table(
|
||||
sqlite *db, /* The database on which the SQL executes */
|
||||
const char *zSql, /* The SQL to be executed */
|
||||
char ***pazResult, /* Write the result table here */
|
||||
int *pnRow, /* Write the number of rows in the result here */
|
||||
int *pnColumn, /* Write the number of columns of result here */
|
||||
char **pzErrMsg /* Write error messages here */
|
||||
){
|
||||
int rc;
|
||||
TabResult res;
|
||||
if( pazResult==0 ){ return SQLITE_ERROR; }
|
||||
*pazResult = 0;
|
||||
if( pnColumn ) *pnColumn = 0;
|
||||
if( pnRow ) *pnRow = 0;
|
||||
res.zErrMsg = 0;
|
||||
res.nResult = 0;
|
||||
res.nRow = 0;
|
||||
res.nColumn = 0;
|
||||
res.nData = 1;
|
||||
res.nAlloc = 20;
|
||||
res.rc = SQLITE_OK;
|
||||
res.azResult = malloc( sizeof(char*)*res.nAlloc );
|
||||
if( res.azResult==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
res.azResult[0] = 0;
|
||||
rc = sqlite_exec(db, zSql, sqlite_get_table_cb, &res, pzErrMsg);
|
||||
if( res.azResult ){
|
||||
res.azResult[0] = (char*)res.nData;
|
||||
}
|
||||
if( rc==SQLITE_ABORT ){
|
||||
sqlite_free_table(&res.azResult[1]);
|
||||
if( res.zErrMsg ){
|
||||
if( pzErrMsg ){
|
||||
free(*pzErrMsg);
|
||||
*pzErrMsg = res.zErrMsg;
|
||||
sqliteStrRealloc(pzErrMsg);
|
||||
}else{
|
||||
sqliteFree(res.zErrMsg);
|
||||
}
|
||||
}
|
||||
return res.rc;
|
||||
}
|
||||
sqliteFree(res.zErrMsg);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite_free_table(&res.azResult[1]);
|
||||
return rc;
|
||||
}
|
||||
if( res.nAlloc>res.nData ){
|
||||
char **azNew;
|
||||
azNew = realloc( res.azResult, sizeof(char*)*(res.nData+1) );
|
||||
if( azNew==0 ){
|
||||
sqlite_free_table(&res.azResult[1]);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
res.nAlloc = res.nData+1;
|
||||
res.azResult = azNew;
|
||||
}
|
||||
*pazResult = &res.azResult[1];
|
||||
if( pnColumn ) *pnColumn = res.nColumn;
|
||||
if( pnRow ) *pnRow = res.nRow;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine frees the space the sqlite_get_table() malloced.
|
||||
*/
|
||||
void sqlite_free_table(
|
||||
char **azResult /* Result returned from from sqlite_get_table() */
|
||||
){
|
||||
if( azResult ){
|
||||
int i, n;
|
||||
azResult--;
|
||||
if( azResult==0 ) return;
|
||||
n = (int)azResult[0];
|
||||
for(i=1; i<n; i++){ if( azResult[i] ) free(azResult[i]); }
|
||||
free(azResult);
|
||||
}
|
||||
}
|
||||
684
sqlitebrowser/sqlitebrowser/sqlite_source/tokenize.c
Executable file
@@ -0,0 +1,684 @@
|
||||
/*
|
||||
** 2001 September 15
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** An tokenizer for SQL
|
||||
**
|
||||
** This file contains C code that splits an SQL input string up into
|
||||
** individual tokens and sends those tokens one-by-one over to the
|
||||
** parser for analysis.
|
||||
**
|
||||
** $Id: tokenize.c,v 1.1.1.1 2003-08-21 02:24:21 tabuleiro Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "os.h"
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
** All the keywords of the SQL language are stored as in a hash
|
||||
** table composed of instances of the following structure.
|
||||
*/
|
||||
typedef struct Keyword Keyword;
|
||||
struct Keyword {
|
||||
char *zName; /* The keyword name */
|
||||
int len; /* Number of characters in the keyword */
|
||||
int tokenType; /* The token value for this keyword */
|
||||
Keyword *pNext; /* Next keyword with the same hash */
|
||||
};
|
||||
|
||||
/*
|
||||
** These are the keywords
|
||||
*/
|
||||
static Keyword aKeywordTable[] = {
|
||||
{ "ABORT", 0, TK_ABORT, 0 },
|
||||
{ "AFTER", 0, TK_AFTER, 0 },
|
||||
{ "ALL", 0, TK_ALL, 0 },
|
||||
{ "AND", 0, TK_AND, 0 },
|
||||
{ "AS", 0, TK_AS, 0 },
|
||||
{ "ASC", 0, TK_ASC, 0 },
|
||||
{ "ATTACH", 0, TK_ATTACH, 0 },
|
||||
{ "BEFORE", 0, TK_BEFORE, 0 },
|
||||
{ "BEGIN", 0, TK_BEGIN, 0 },
|
||||
{ "BETWEEN", 0, TK_BETWEEN, 0 },
|
||||
{ "BY", 0, TK_BY, 0 },
|
||||
{ "CASCADE", 0, TK_CASCADE, 0 },
|
||||
{ "CASE", 0, TK_CASE, 0 },
|
||||
{ "CHECK", 0, TK_CHECK, 0 },
|
||||
{ "CLUSTER", 0, TK_CLUSTER, 0 },
|
||||
{ "COLLATE", 0, TK_COLLATE, 0 },
|
||||
{ "COMMIT", 0, TK_COMMIT, 0 },
|
||||
{ "CONFLICT", 0, TK_CONFLICT, 0 },
|
||||
{ "CONSTRAINT", 0, TK_CONSTRAINT, 0 },
|
||||
{ "COPY", 0, TK_COPY, 0 },
|
||||
{ "CREATE", 0, TK_CREATE, 0 },
|
||||
{ "CROSS", 0, TK_JOIN_KW, 0 },
|
||||
{ "DATABASE", 0, TK_DATABASE, 0 },
|
||||
{ "DEFAULT", 0, TK_DEFAULT, 0 },
|
||||
{ "DEFERRED", 0, TK_DEFERRED, 0 },
|
||||
{ "DEFERRABLE", 0, TK_DEFERRABLE, 0 },
|
||||
{ "DELETE", 0, TK_DELETE, 0 },
|
||||
{ "DELIMITERS", 0, TK_DELIMITERS, 0 },
|
||||
{ "DESC", 0, TK_DESC, 0 },
|
||||
{ "DETACH", 0, TK_DETACH, 0 },
|
||||
{ "DISTINCT", 0, TK_DISTINCT, 0 },
|
||||
{ "DROP", 0, TK_DROP, 0 },
|
||||
{ "END", 0, TK_END, 0 },
|
||||
{ "EACH", 0, TK_EACH, 0 },
|
||||
{ "ELSE", 0, TK_ELSE, 0 },
|
||||
{ "EXCEPT", 0, TK_EXCEPT, 0 },
|
||||
{ "EXPLAIN", 0, TK_EXPLAIN, 0 },
|
||||
{ "FAIL", 0, TK_FAIL, 0 },
|
||||
{ "FOR", 0, TK_FOR, 0 },
|
||||
{ "FOREIGN", 0, TK_FOREIGN, 0 },
|
||||
{ "FROM", 0, TK_FROM, 0 },
|
||||
{ "FULL", 0, TK_JOIN_KW, 0 },
|
||||
{ "GLOB", 0, TK_GLOB, 0 },
|
||||
{ "GROUP", 0, TK_GROUP, 0 },
|
||||
{ "HAVING", 0, TK_HAVING, 0 },
|
||||
{ "IGNORE", 0, TK_IGNORE, 0 },
|
||||
{ "IMMEDIATE", 0, TK_IMMEDIATE, 0 },
|
||||
{ "IN", 0, TK_IN, 0 },
|
||||
{ "INDEX", 0, TK_INDEX, 0 },
|
||||
{ "INITIALLY", 0, TK_INITIALLY, 0 },
|
||||
{ "INNER", 0, TK_JOIN_KW, 0 },
|
||||
{ "INSERT", 0, TK_INSERT, 0 },
|
||||
{ "INSTEAD", 0, TK_INSTEAD, 0 },
|
||||
{ "INTERSECT", 0, TK_INTERSECT, 0 },
|
||||
{ "INTO", 0, TK_INTO, 0 },
|
||||
{ "IS", 0, TK_IS, 0 },
|
||||
{ "ISNULL", 0, TK_ISNULL, 0 },
|
||||
{ "JOIN", 0, TK_JOIN, 0 },
|
||||
{ "KEY", 0, TK_KEY, 0 },
|
||||
{ "LEFT", 0, TK_JOIN_KW, 0 },
|
||||
{ "LIKE", 0, TK_LIKE, 0 },
|
||||
{ "LIMIT", 0, TK_LIMIT, 0 },
|
||||
{ "MATCH", 0, TK_MATCH, 0 },
|
||||
{ "NATURAL", 0, TK_JOIN_KW, 0 },
|
||||
{ "NOT", 0, TK_NOT, 0 },
|
||||
{ "NOTNULL", 0, TK_NOTNULL, 0 },
|
||||
{ "NULL", 0, TK_NULL, 0 },
|
||||
{ "OF", 0, TK_OF, 0 },
|
||||
{ "OFFSET", 0, TK_OFFSET, 0 },
|
||||
{ "ON", 0, TK_ON, 0 },
|
||||
{ "OR", 0, TK_OR, 0 },
|
||||
{ "ORDER", 0, TK_ORDER, 0 },
|
||||
{ "OUTER", 0, TK_JOIN_KW, 0 },
|
||||
{ "PRAGMA", 0, TK_PRAGMA, 0 },
|
||||
{ "PRIMARY", 0, TK_PRIMARY, 0 },
|
||||
{ "RAISE", 0, TK_RAISE, 0 },
|
||||
{ "REFERENCES", 0, TK_REFERENCES, 0 },
|
||||
{ "REPLACE", 0, TK_REPLACE, 0 },
|
||||
{ "RESTRICT", 0, TK_RESTRICT, 0 },
|
||||
{ "RIGHT", 0, TK_JOIN_KW, 0 },
|
||||
{ "ROLLBACK", 0, TK_ROLLBACK, 0 },
|
||||
{ "ROW", 0, TK_ROW, 0 },
|
||||
{ "SELECT", 0, TK_SELECT, 0 },
|
||||
{ "SET", 0, TK_SET, 0 },
|
||||
{ "STATEMENT", 0, TK_STATEMENT, 0 },
|
||||
{ "TABLE", 0, TK_TABLE, 0 },
|
||||
{ "TEMP", 0, TK_TEMP, 0 },
|
||||
{ "TEMPORARY", 0, TK_TEMP, 0 },
|
||||
{ "THEN", 0, TK_THEN, 0 },
|
||||
{ "TRANSACTION", 0, TK_TRANSACTION, 0 },
|
||||
{ "TRIGGER", 0, TK_TRIGGER, 0 },
|
||||
{ "UNION", 0, TK_UNION, 0 },
|
||||
{ "UNIQUE", 0, TK_UNIQUE, 0 },
|
||||
{ "UPDATE", 0, TK_UPDATE, 0 },
|
||||
{ "USING", 0, TK_USING, 0 },
|
||||
{ "VACUUM", 0, TK_VACUUM, 0 },
|
||||
{ "VALUES", 0, TK_VALUES, 0 },
|
||||
{ "VIEW", 0, TK_VIEW, 0 },
|
||||
{ "WHEN", 0, TK_WHEN, 0 },
|
||||
{ "WHERE", 0, TK_WHERE, 0 },
|
||||
};
|
||||
|
||||
/*
|
||||
** This is the hash table
|
||||
*/
|
||||
#define KEY_HASH_SIZE 71
|
||||
static Keyword *apHashTable[KEY_HASH_SIZE];
|
||||
|
||||
|
||||
/*
|
||||
** This function looks up an identifier to determine if it is a
|
||||
** keyword. If it is a keyword, the token code of that keyword is
|
||||
** returned. If the input is not a keyword, TK_ID is returned.
|
||||
*/
|
||||
int sqliteKeywordCode(const char *z, int n){
|
||||
int h;
|
||||
Keyword *p;
|
||||
if( aKeywordTable[0].len==0 ){
|
||||
/* Initialize the keyword hash table */
|
||||
sqliteOsEnterMutex();
|
||||
if( aKeywordTable[0].len==0 ){
|
||||
int i;
|
||||
int n;
|
||||
n = sizeof(aKeywordTable)/sizeof(aKeywordTable[0]);
|
||||
for(i=0; i<n; i++){
|
||||
aKeywordTable[i].len = strlen(aKeywordTable[i].zName);
|
||||
h = sqliteHashNoCase(aKeywordTable[i].zName, aKeywordTable[i].len);
|
||||
h %= KEY_HASH_SIZE;
|
||||
aKeywordTable[i].pNext = apHashTable[h];
|
||||
apHashTable[h] = &aKeywordTable[i];
|
||||
}
|
||||
}
|
||||
sqliteOsLeaveMutex();
|
||||
}
|
||||
h = sqliteHashNoCase(z, n) % KEY_HASH_SIZE;
|
||||
for(p=apHashTable[h]; p; p=p->pNext){
|
||||
if( p->len==n && sqliteStrNICmp(p->zName, z, n)==0 ){
|
||||
return p->tokenType;
|
||||
}
|
||||
}
|
||||
return TK_ID;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** If X is a character that can be used in an identifier then
|
||||
** isIdChar[X] will be 1. Otherwise isIdChar[X] will be 0.
|
||||
**
|
||||
** In this implementation, an identifier can be a string of
|
||||
** alphabetic characters, digits, and "_" plus any character
|
||||
** with the high-order bit set. The latter rule means that
|
||||
** any sequence of UTF-8 characters or characters taken from
|
||||
** an extended ISO8859 character set can form an identifier.
|
||||
*/
|
||||
static const char isIdChar[] = {
|
||||
/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1x */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2x */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 8x */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 9x */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Ax */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Bx */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Cx */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Dx */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Ex */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Fx */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** Return the length of the token that begins at z[0]. Return
|
||||
** -1 if the token is (or might be) incomplete. Store the token
|
||||
** type in *tokenType before returning.
|
||||
*/
|
||||
static int sqliteGetToken(const unsigned char *z, int *tokenType){
|
||||
int i;
|
||||
switch( *z ){
|
||||
case ' ': case '\t': case '\n': case '\f': case '\r': {
|
||||
for(i=1; isspace(z[i]); i++){}
|
||||
*tokenType = TK_SPACE;
|
||||
return i;
|
||||
}
|
||||
case '-': {
|
||||
if( z[1]==0 ) return -1;
|
||||
if( z[1]=='-' ){
|
||||
for(i=2; z[i] && z[i]!='\n'; i++){}
|
||||
*tokenType = TK_COMMENT;
|
||||
return i;
|
||||
}
|
||||
*tokenType = TK_MINUS;
|
||||
return 1;
|
||||
}
|
||||
case '(': {
|
||||
if( z[1]=='+' && z[2]==')' ){
|
||||
*tokenType = TK_ORACLE_OUTER_JOIN;
|
||||
return 3;
|
||||
}else{
|
||||
*tokenType = TK_LP;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
case ')': {
|
||||
*tokenType = TK_RP;
|
||||
return 1;
|
||||
}
|
||||
case ';': {
|
||||
*tokenType = TK_SEMI;
|
||||
return 1;
|
||||
}
|
||||
case '+': {
|
||||
*tokenType = TK_PLUS;
|
||||
return 1;
|
||||
}
|
||||
case '*': {
|
||||
*tokenType = TK_STAR;
|
||||
return 1;
|
||||
}
|
||||
case '/': {
|
||||
if( z[1]!='*' || z[2]==0 ){
|
||||
*tokenType = TK_SLASH;
|
||||
return 1;
|
||||
}
|
||||
for(i=3; z[i] && (z[i]!='/' || z[i-1]!='*'); i++){}
|
||||
if( z[i] ) i++;
|
||||
*tokenType = TK_COMMENT;
|
||||
return i;
|
||||
}
|
||||
case '%': {
|
||||
*tokenType = TK_REM;
|
||||
return 1;
|
||||
}
|
||||
case '=': {
|
||||
*tokenType = TK_EQ;
|
||||
return 1 + (z[1]=='=');
|
||||
}
|
||||
case '<': {
|
||||
if( z[1]=='=' ){
|
||||
*tokenType = TK_LE;
|
||||
return 2;
|
||||
}else if( z[1]=='>' ){
|
||||
*tokenType = TK_NE;
|
||||
return 2;
|
||||
}else if( z[1]=='<' ){
|
||||
*tokenType = TK_LSHIFT;
|
||||
return 2;
|
||||
}else{
|
||||
*tokenType = TK_LT;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
case '>': {
|
||||
if( z[1]=='=' ){
|
||||
*tokenType = TK_GE;
|
||||
return 2;
|
||||
}else if( z[1]=='>' ){
|
||||
*tokenType = TK_RSHIFT;
|
||||
return 2;
|
||||
}else{
|
||||
*tokenType = TK_GT;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
case '!': {
|
||||
if( z[1]!='=' ){
|
||||
*tokenType = TK_ILLEGAL;
|
||||
return 2;
|
||||
}else{
|
||||
*tokenType = TK_NE;
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
case '|': {
|
||||
if( z[1]!='|' ){
|
||||
*tokenType = TK_BITOR;
|
||||
return 1;
|
||||
}else{
|
||||
*tokenType = TK_CONCAT;
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
case ',': {
|
||||
*tokenType = TK_COMMA;
|
||||
return 1;
|
||||
}
|
||||
case '&': {
|
||||
*tokenType = TK_BITAND;
|
||||
return 1;
|
||||
}
|
||||
case '~': {
|
||||
*tokenType = TK_BITNOT;
|
||||
return 1;
|
||||
}
|
||||
case '\'': case '"': {
|
||||
int delim = z[0];
|
||||
for(i=1; z[i]; i++){
|
||||
if( z[i]==delim ){
|
||||
if( z[i+1]==delim ){
|
||||
i++;
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( z[i] ) i++;
|
||||
*tokenType = TK_STRING;
|
||||
return i;
|
||||
}
|
||||
case '.': {
|
||||
*tokenType = TK_DOT;
|
||||
return 1;
|
||||
}
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9': {
|
||||
*tokenType = TK_INTEGER;
|
||||
for(i=1; isdigit(z[i]); i++){}
|
||||
if( z[i]=='.' && isdigit(z[i+1]) ){
|
||||
i += 2;
|
||||
while( isdigit(z[i]) ){ i++; }
|
||||
*tokenType = TK_FLOAT;
|
||||
}
|
||||
if( (z[i]=='e' || z[i]=='E') &&
|
||||
( isdigit(z[i+1])
|
||||
|| ((z[i+1]=='+' || z[i+1]=='-') && isdigit(z[i+2]))
|
||||
)
|
||||
){
|
||||
i += 2;
|
||||
while( isdigit(z[i]) ){ i++; }
|
||||
*tokenType = TK_FLOAT;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
case '[': {
|
||||
for(i=1; z[i] && z[i-1]!=']'; i++){}
|
||||
*tokenType = TK_ID;
|
||||
return i;
|
||||
}
|
||||
default: {
|
||||
if( !isIdChar[*z] ){
|
||||
break;
|
||||
}
|
||||
for(i=1; isIdChar[z[i]]; i++){}
|
||||
*tokenType = sqliteKeywordCode((char*)z, i);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
*tokenType = TK_ILLEGAL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Run the parser on the given SQL string. The parser structure is
|
||||
** passed in. An SQLITE_ status code is returned. If an error occurs
|
||||
** and pzErrMsg!=NULL then an error message might be written into
|
||||
** memory obtained from malloc() and *pzErrMsg made to point to that
|
||||
** error message. Or maybe not.
|
||||
*/
|
||||
int sqliteRunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
|
||||
int nErr = 0;
|
||||
int i;
|
||||
void *pEngine;
|
||||
int tokenType;
|
||||
int lastTokenParsed = -1;
|
||||
sqlite *db = pParse->db;
|
||||
extern void *sqliteParserAlloc(void*(*)(int));
|
||||
extern void sqliteParserFree(void*, void(*)(void*));
|
||||
extern int sqliteParser(void*, int, Token, Parse*);
|
||||
|
||||
db->flags &= ~SQLITE_Interrupt;
|
||||
pParse->rc = SQLITE_OK;
|
||||
i = 0;
|
||||
pEngine = sqliteParserAlloc((void*(*)(int))malloc);
|
||||
if( pEngine==0 ){
|
||||
sqliteSetString(pzErrMsg, "out of memory", 0);
|
||||
return 1;
|
||||
}
|
||||
pParse->sLastToken.dyn = 0;
|
||||
pParse->zTail = zSql;
|
||||
while( sqlite_malloc_failed==0 && zSql[i]!=0 ){
|
||||
|
||||
assert( i>=0 );
|
||||
pParse->sLastToken.z = &zSql[i];
|
||||
assert( pParse->sLastToken.dyn==0 );
|
||||
pParse->sLastToken.n = sqliteGetToken((unsigned char*)&zSql[i], &tokenType);
|
||||
i += pParse->sLastToken.n;
|
||||
switch( tokenType ){
|
||||
case TK_SPACE:
|
||||
case TK_COMMENT: {
|
||||
if( (db->flags & SQLITE_Interrupt)!=0 ){
|
||||
pParse->rc = SQLITE_INTERRUPT;
|
||||
sqliteSetString(pzErrMsg, "interrupt", 0);
|
||||
goto abort_parse;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TK_ILLEGAL: {
|
||||
sqliteSetNString(pzErrMsg, "unrecognized token: \"", -1,
|
||||
pParse->sLastToken.z, pParse->sLastToken.n, "\"", 1, 0);
|
||||
nErr++;
|
||||
goto abort_parse;
|
||||
}
|
||||
case TK_SEMI: {
|
||||
pParse->zTail = &zSql[i];
|
||||
/* Fall thru into the default case */
|
||||
}
|
||||
default: {
|
||||
sqliteParser(pEngine, tokenType, pParse->sLastToken, pParse);
|
||||
lastTokenParsed = tokenType;
|
||||
if( pParse->rc!=SQLITE_OK ){
|
||||
goto abort_parse;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
abort_parse:
|
||||
if( zSql[i]==0 && nErr==0 && pParse->rc==SQLITE_OK ){
|
||||
if( lastTokenParsed!=TK_SEMI ){
|
||||
sqliteParser(pEngine, TK_SEMI, pParse->sLastToken, pParse);
|
||||
pParse->zTail = &zSql[i];
|
||||
}
|
||||
sqliteParser(pEngine, 0, pParse->sLastToken, pParse);
|
||||
}
|
||||
sqliteParserFree(pEngine, (void *) free);
|
||||
if( pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE && pParse->zErrMsg==0 ){
|
||||
sqliteSetString(&pParse->zErrMsg, sqlite_error_string(pParse->rc), 0);
|
||||
}
|
||||
if( pParse->zErrMsg ){
|
||||
if( pzErrMsg && *pzErrMsg==0 ){
|
||||
*pzErrMsg = pParse->zErrMsg;
|
||||
}else{
|
||||
sqliteFree(pParse->zErrMsg);
|
||||
}
|
||||
pParse->zErrMsg = 0;
|
||||
if( !nErr ) nErr++;
|
||||
}
|
||||
if( pParse->pVdbe && (pParse->useCallback || pParse->nErr>0) ){
|
||||
sqliteVdbeDelete(pParse->pVdbe);
|
||||
pParse->pVdbe = 0;
|
||||
}
|
||||
if( pParse->pNewTable ){
|
||||
sqliteDeleteTable(pParse->db, pParse->pNewTable);
|
||||
pParse->pNewTable = 0;
|
||||
}
|
||||
if( pParse->pNewTrigger ){
|
||||
sqliteDeleteTrigger(pParse->pNewTrigger);
|
||||
pParse->pNewTrigger = 0;
|
||||
}
|
||||
if( nErr>0 && (pParse->rc==SQLITE_OK || pParse->rc==SQLITE_DONE) ){
|
||||
pParse->rc = SQLITE_ERROR;
|
||||
}
|
||||
return nErr;
|
||||
}
|
||||
|
||||
/*
|
||||
** Token types used by the sqlite_complete() routine. See the header
|
||||
** comments on that procedure for additional information.
|
||||
*/
|
||||
#define tkEXPLAIN 0
|
||||
#define tkCREATE 1
|
||||
#define tkTEMP 2
|
||||
#define tkTRIGGER 3
|
||||
#define tkEND 4
|
||||
#define tkSEMI 5
|
||||
#define tkWS 6
|
||||
#define tkOTHER 7
|
||||
|
||||
/*
|
||||
** Return TRUE if the given SQL string ends in a semicolon.
|
||||
**
|
||||
** Special handling is require for CREATE TRIGGER statements.
|
||||
** Whenever the CREATE TRIGGER keywords are seen, the statement
|
||||
** must end with ";END;".
|
||||
**
|
||||
** This implementation uses a state machine with 7 states:
|
||||
**
|
||||
** (0) START At the beginning or end of an SQL statement. This routine
|
||||
** returns 1 if it ends in the START state and 0 if it ends
|
||||
** in any other state.
|
||||
**
|
||||
** (1) EXPLAIN The keyword EXPLAIN has been seen at the beginning of
|
||||
** a statement.
|
||||
**
|
||||
** (2) CREATE The keyword CREATE has been seen at the beginning of a
|
||||
** statement, possibly preceeded by EXPLAIN and/or followed by
|
||||
** TEMP or TEMPORARY
|
||||
**
|
||||
** (3) NORMAL We are in the middle of statement which ends with a single
|
||||
** semicolon.
|
||||
**
|
||||
** (4) TRIGGER We are in the middle of a trigger definition that must be
|
||||
** ended by a semicolon, the keyword END, and another semicolon.
|
||||
**
|
||||
** (5) SEMI We've seen the first semicolon in the ";END;" that occurs at
|
||||
** the end of a trigger definition.
|
||||
**
|
||||
** (6) END We've seen the ";END" of the ";END;" that occurs at the end
|
||||
** of a trigger difinition.
|
||||
**
|
||||
** Transitions between states above are determined by tokens extracted
|
||||
** from the input. The following tokens are significant:
|
||||
**
|
||||
** (0) tkEXPLAIN The "explain" keyword.
|
||||
** (1) tkCREATE The "create" keyword.
|
||||
** (2) tkTEMP The "temp" or "temporary" keyword.
|
||||
** (3) tkTRIGGER The "trigger" keyword.
|
||||
** (4) tkEND The "end" keyword.
|
||||
** (5) tkSEMI A semicolon.
|
||||
** (6) tkWS Whitespace
|
||||
** (7) tkOTHER Any other SQL token.
|
||||
**
|
||||
** Whitespace never causes a state transition and is always ignored.
|
||||
*/
|
||||
int sqlite_complete(const char *zSql){
|
||||
u8 state = 0; /* Current state, using numbers defined in header comment */
|
||||
u8 token; /* Value of the next token */
|
||||
|
||||
/* The following matrix defines the transition from one state to another
|
||||
** according to what token is seen. trans[state][token] returns the
|
||||
** next state.
|
||||
*/
|
||||
static const u8 trans[7][8] = {
|
||||
/* Token: */
|
||||
/* State: ** EXPLAIN CREATE TEMP TRIGGER END SEMI WS OTHER */
|
||||
/* 0 START: */ { 1, 2, 3, 3, 3, 0, 0, 3, },
|
||||
/* 1 EXPLAIN: */ { 3, 2, 3, 3, 3, 0, 1, 3, },
|
||||
/* 2 CREATE: */ { 3, 3, 2, 4, 3, 0, 2, 3, },
|
||||
/* 3 NORMAL: */ { 3, 3, 3, 3, 3, 0, 3, 3, },
|
||||
/* 4 TRIGGER: */ { 4, 4, 4, 4, 4, 5, 4, 4, },
|
||||
/* 5 SEMI: */ { 4, 4, 4, 4, 6, 5, 5, 4, },
|
||||
/* 6 END: */ { 4, 4, 4, 4, 4, 0, 6, 4, },
|
||||
};
|
||||
|
||||
while( *zSql ){
|
||||
switch( *zSql ){
|
||||
case ';': { /* A semicolon */
|
||||
token = tkSEMI;
|
||||
break;
|
||||
}
|
||||
case ' ':
|
||||
case '\r':
|
||||
case '\t':
|
||||
case '\n':
|
||||
case '\f': { /* White space is ignored */
|
||||
token = tkWS;
|
||||
break;
|
||||
}
|
||||
case '/': { /* C-style comments */
|
||||
if( zSql[1]!='*' ){
|
||||
token = tkOTHER;
|
||||
break;
|
||||
}
|
||||
zSql += 2;
|
||||
while( zSql[0] && (zSql[0]!='*' || zSql[1]!='/') ){ zSql++; }
|
||||
if( zSql[0]==0 ) return 0;
|
||||
zSql++;
|
||||
token = tkWS;
|
||||
break;
|
||||
}
|
||||
case '-': { /* SQL-style comments from "--" to end of line */
|
||||
if( zSql[1]!='-' ){
|
||||
token = tkOTHER;
|
||||
break;
|
||||
}
|
||||
while( *zSql && *zSql!='\n' ){ zSql++; }
|
||||
if( *zSql==0 ) return state==0;
|
||||
token = tkWS;
|
||||
break;
|
||||
}
|
||||
case '[': { /* Microsoft-style identifiers in [...] */
|
||||
zSql++;
|
||||
while( *zSql && *zSql!=']' ){ zSql++; }
|
||||
if( *zSql==0 ) return 0;
|
||||
token = tkOTHER;
|
||||
break;
|
||||
}
|
||||
case '"': /* single- and double-quoted strings */
|
||||
case '\'': {
|
||||
int c = *zSql;
|
||||
zSql++;
|
||||
while( *zSql && *zSql!=c ){ zSql++; }
|
||||
if( *zSql==0 ) return 0;
|
||||
token = tkOTHER;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if( isIdChar[(u8)*zSql] ){
|
||||
/* Keywords and unquoted identifiers */
|
||||
int nId;
|
||||
for(nId=1; isIdChar[(u8)zSql[nId]]; nId++){}
|
||||
switch( *zSql ){
|
||||
case 'c': case 'C': {
|
||||
if( nId==6 && sqliteStrNICmp(zSql, "create", 6)==0 ){
|
||||
token = tkCREATE;
|
||||
}else{
|
||||
token = tkOTHER;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 't': case 'T': {
|
||||
if( nId==7 && sqliteStrNICmp(zSql, "trigger", 7)==0 ){
|
||||
token = tkTRIGGER;
|
||||
}else if( nId==4 && sqliteStrNICmp(zSql, "temp", 4)==0 ){
|
||||
token = tkTEMP;
|
||||
}else if( nId==9 && sqliteStrNICmp(zSql, "temporary", 9)==0 ){
|
||||
token = tkTEMP;
|
||||
}else{
|
||||
token = tkOTHER;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'e': case 'E': {
|
||||
if( nId==3 && sqliteStrNICmp(zSql, "end", 3)==0 ){
|
||||
token = tkEND;
|
||||
}else if( nId==7 && sqliteStrNICmp(zSql, "explain", 7)==0 ){
|
||||
token = tkEXPLAIN;
|
||||
}else{
|
||||
token = tkOTHER;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
token = tkOTHER;
|
||||
break;
|
||||
}
|
||||
}
|
||||
zSql += nId-1;
|
||||
}else{
|
||||
/* Operators and special symbols */
|
||||
token = tkOTHER;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
state = trans[state][token];
|
||||
zSql++;
|
||||
}
|
||||
return state==0;
|
||||
}
|
||||
760
sqlitebrowser/sqlitebrowser/sqlite_source/trigger.c
Executable file
@@ -0,0 +1,760 @@
|
||||
/*
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
*
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** Delete a linked list of TriggerStep structures.
|
||||
*/
|
||||
void sqliteDeleteTriggerStep(TriggerStep *pTriggerStep){
|
||||
while( pTriggerStep ){
|
||||
TriggerStep * pTmp = pTriggerStep;
|
||||
pTriggerStep = pTriggerStep->pNext;
|
||||
|
||||
if( pTmp->target.dyn ) sqliteFree((char*)pTmp->target.z);
|
||||
sqliteExprDelete(pTmp->pWhere);
|
||||
sqliteExprListDelete(pTmp->pExprList);
|
||||
sqliteSelectDelete(pTmp->pSelect);
|
||||
sqliteIdListDelete(pTmp->pIdList);
|
||||
|
||||
sqliteFree(pTmp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This is called by the parser when it sees a CREATE TRIGGER statement
|
||||
** up to the point of the BEGIN before the trigger actions. A Trigger
|
||||
** structure is generated based on the information available and stored
|
||||
** in pParse->pNewTrigger. After the trigger actions have been parsed, the
|
||||
** sqliteFinishTrigger() function is called to complete the trigger
|
||||
** construction process.
|
||||
*/
|
||||
void sqliteBeginTrigger(
|
||||
Parse *pParse, /* The parse context of the CREATE TRIGGER statement */
|
||||
Token *pName, /* The name of the trigger */
|
||||
int tr_tm, /* One of TK_BEFORE, TK_AFTER, TK_INSTEAD */
|
||||
int op, /* One of TK_INSERT, TK_UPDATE, TK_DELETE */
|
||||
IdList *pColumns, /* column list if this is an UPDATE OF trigger */
|
||||
SrcList *pTableName,/* The name of the table/view the trigger applies to */
|
||||
int foreach, /* One of TK_ROW or TK_STATEMENT */
|
||||
Expr *pWhen, /* WHEN clause */
|
||||
int isTemp /* True if the TEMPORARY keyword is present */
|
||||
){
|
||||
Trigger *nt;
|
||||
Table *tab;
|
||||
char *zName = 0; /* Name of the trigger */
|
||||
sqlite *db = pParse->db;
|
||||
int iDb; /* When database to store the trigger in */
|
||||
DbFixer sFix;
|
||||
|
||||
/* Check that:
|
||||
** 1. the trigger name does not already exist.
|
||||
** 2. the table (or view) does exist in the same database as the trigger.
|
||||
** 3. that we are not trying to create a trigger on the sqlite_master table
|
||||
** 4. That we are not trying to create an INSTEAD OF trigger on a table.
|
||||
** 5. That we are not trying to create a BEFORE or AFTER trigger on a view.
|
||||
*/
|
||||
if( sqlite_malloc_failed ) goto trigger_cleanup;
|
||||
assert( pTableName->nSrc==1 );
|
||||
if( pParse->initFlag
|
||||
&& sqliteFixInit(&sFix, pParse, pParse->iDb, "trigger", pName)
|
||||
&& sqliteFixSrcList(&sFix, pTableName)
|
||||
){
|
||||
goto trigger_cleanup;
|
||||
}
|
||||
tab = sqliteSrcListLookup(pParse, pTableName);
|
||||
if( !tab ){
|
||||
goto trigger_cleanup;
|
||||
}
|
||||
iDb = isTemp ? 1 : tab->iDb;
|
||||
if( iDb>=2 && !pParse->initFlag ){
|
||||
sqliteErrorMsg(pParse, "triggers may not be added to auxiliary "
|
||||
"database %s", db->aDb[tab->iDb].zName);
|
||||
goto trigger_cleanup;
|
||||
}
|
||||
|
||||
zName = sqliteStrNDup(pName->z, pName->n);
|
||||
if( sqliteHashFind(&(db->aDb[iDb].trigHash), zName,pName->n+1) ){
|
||||
sqliteErrorMsg(pParse, "trigger %T already exists", pName);
|
||||
goto trigger_cleanup;
|
||||
}
|
||||
if( sqliteStrNICmp(tab->zName, "sqlite_", 7)==0 ){
|
||||
sqliteErrorMsg(pParse, "cannot create trigger on system table");
|
||||
pParse->nErr++;
|
||||
goto trigger_cleanup;
|
||||
}
|
||||
if( tab->pSelect && tr_tm != TK_INSTEAD ){
|
||||
sqliteErrorMsg(pParse, "cannot create %s trigger on view: %S",
|
||||
(tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName, 0);
|
||||
goto trigger_cleanup;
|
||||
}
|
||||
if( !tab->pSelect && tr_tm == TK_INSTEAD ){
|
||||
sqliteErrorMsg(pParse, "cannot create INSTEAD OF"
|
||||
" trigger on table: %S", pTableName, 0);
|
||||
goto trigger_cleanup;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
{
|
||||
int code = SQLITE_CREATE_TRIGGER;
|
||||
const char *zDb = db->aDb[tab->iDb].zName;
|
||||
const char *zDbTrig = isTemp ? db->aDb[1].zName : zDb;
|
||||
if( tab->iDb==1 || isTemp ) code = SQLITE_CREATE_TEMP_TRIGGER;
|
||||
if( sqliteAuthCheck(pParse, code, zName, tab->zName, zDbTrig) ){
|
||||
goto trigger_cleanup;
|
||||
}
|
||||
if( sqliteAuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(tab->iDb), 0, zDb)){
|
||||
goto trigger_cleanup;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* INSTEAD OF triggers can only appear on views and BEGIN triggers
|
||||
** cannot appear on views. So we might as well translate every
|
||||
** INSTEAD OF trigger into a BEFORE trigger. It simplifies code
|
||||
** elsewhere.
|
||||
*/
|
||||
if (tr_tm == TK_INSTEAD){
|
||||
tr_tm = TK_BEFORE;
|
||||
}
|
||||
|
||||
/* Build the Trigger object */
|
||||
nt = (Trigger*)sqliteMalloc(sizeof(Trigger));
|
||||
if( nt==0 ) goto trigger_cleanup;
|
||||
nt->name = zName;
|
||||
zName = 0;
|
||||
nt->table = sqliteStrDup(pTableName->a[0].zName);
|
||||
if( sqlite_malloc_failed ) goto trigger_cleanup;
|
||||
nt->iDb = iDb;
|
||||
nt->iTabDb = tab->iDb;
|
||||
nt->op = op;
|
||||
nt->tr_tm = tr_tm;
|
||||
nt->pWhen = sqliteExprDup(pWhen);
|
||||
nt->pColumns = sqliteIdListDup(pColumns);
|
||||
nt->foreach = foreach;
|
||||
sqliteTokenCopy(&nt->nameToken,pName);
|
||||
assert( pParse->pNewTrigger==0 );
|
||||
pParse->pNewTrigger = nt;
|
||||
|
||||
trigger_cleanup:
|
||||
sqliteFree(zName);
|
||||
sqliteSrcListDelete(pTableName);
|
||||
sqliteIdListDelete(pColumns);
|
||||
sqliteExprDelete(pWhen);
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is called after all of the trigger actions have been parsed
|
||||
** in order to complete the process of building the trigger.
|
||||
*/
|
||||
void sqliteFinishTrigger(
|
||||
Parse *pParse, /* Parser context */
|
||||
TriggerStep *pStepList, /* The triggered program */
|
||||
Token *pAll /* Token that describes the complete CREATE TRIGGER */
|
||||
){
|
||||
Trigger *nt = 0; /* The trigger whose construction is finishing up */
|
||||
sqlite *db = pParse->db; /* The database */
|
||||
DbFixer sFix;
|
||||
|
||||
if( pParse->nErr || pParse->pNewTrigger==0 ) goto triggerfinish_cleanup;
|
||||
nt = pParse->pNewTrigger;
|
||||
pParse->pNewTrigger = 0;
|
||||
nt->step_list = pStepList;
|
||||
while( pStepList ){
|
||||
pStepList->pTrig = nt;
|
||||
pStepList = pStepList->pNext;
|
||||
}
|
||||
if( sqliteFixInit(&sFix, pParse, nt->iDb, "trigger", &nt->nameToken)
|
||||
&& sqliteFixTriggerStep(&sFix, nt->step_list) ){
|
||||
goto triggerfinish_cleanup;
|
||||
}
|
||||
|
||||
/* if we are not initializing, and this trigger is not on a TEMP table,
|
||||
** build the sqlite_master entry
|
||||
*/
|
||||
if( !pParse->initFlag ){
|
||||
static VdbeOp insertTrig[] = {
|
||||
{ OP_NewRecno, 0, 0, 0 },
|
||||
{ OP_String, 0, 0, "trigger" },
|
||||
{ OP_String, 0, 0, 0 }, /* 2: trigger name */
|
||||
{ OP_String, 0, 0, 0 }, /* 3: table name */
|
||||
{ OP_Integer, 0, 0, 0 },
|
||||
{ OP_String, 0, 0, 0 }, /* 5: SQL */
|
||||
{ OP_MakeRecord, 5, 0, 0 },
|
||||
{ OP_PutIntKey, 0, 0, 0 },
|
||||
};
|
||||
int addr;
|
||||
Vdbe *v;
|
||||
|
||||
/* Make an entry in the sqlite_master table */
|
||||
v = sqliteGetVdbe(pParse);
|
||||
if( v==0 ) goto triggerfinish_cleanup;
|
||||
sqliteBeginWriteOperation(pParse, 0, 0);
|
||||
sqliteOpenMasterTable(v, nt->iDb);
|
||||
addr = sqliteVdbeAddOpList(v, ArraySize(insertTrig), insertTrig);
|
||||
sqliteVdbeChangeP3(v, addr+2, nt->name, 0);
|
||||
sqliteVdbeChangeP3(v, addr+3, nt->table, 0);
|
||||
sqliteVdbeChangeP3(v, addr+5, pAll->z, pAll->n);
|
||||
if( nt->iDb==0 ){
|
||||
sqliteChangeCookie(db, v);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_Close, 0, 0);
|
||||
sqliteEndWriteOperation(pParse);
|
||||
}
|
||||
|
||||
if( !pParse->explain ){
|
||||
Table *pTab;
|
||||
sqliteHashInsert(&db->aDb[nt->iDb].trigHash,
|
||||
nt->name, strlen(nt->name)+1, nt);
|
||||
pTab = sqliteLocateTable(pParse, nt->table, db->aDb[nt->iTabDb].zName);
|
||||
assert( pTab!=0 );
|
||||
nt->pNext = pTab->pTrigger;
|
||||
pTab->pTrigger = nt;
|
||||
nt = 0;
|
||||
}
|
||||
|
||||
triggerfinish_cleanup:
|
||||
sqliteDeleteTrigger(nt);
|
||||
sqliteDeleteTrigger(pParse->pNewTrigger);
|
||||
pParse->pNewTrigger = 0;
|
||||
sqliteDeleteTriggerStep(pStepList);
|
||||
}
|
||||
|
||||
/*
|
||||
** Make a copy of all components of the given trigger step. This has
|
||||
** the effect of copying all Expr.token.z values into memory obtained
|
||||
** from sqliteMalloc(). As initially created, the Expr.token.z values
|
||||
** all point to the input string that was fed to the parser. But that
|
||||
** string is ephemeral - it will go away as soon as the sqlite_exec()
|
||||
** call that started the parser exits. This routine makes a persistent
|
||||
** copy of all the Expr.token.z strings so that the TriggerStep structure
|
||||
** will be valid even after the sqlite_exec() call returns.
|
||||
*/
|
||||
static void sqlitePersistTriggerStep(TriggerStep *p){
|
||||
if( p->target.z ){
|
||||
p->target.z = sqliteStrNDup(p->target.z, p->target.n);
|
||||
p->target.dyn = 1;
|
||||
}
|
||||
if( p->pSelect ){
|
||||
Select *pNew = sqliteSelectDup(p->pSelect);
|
||||
sqliteSelectDelete(p->pSelect);
|
||||
p->pSelect = pNew;
|
||||
}
|
||||
if( p->pWhere ){
|
||||
Expr *pNew = sqliteExprDup(p->pWhere);
|
||||
sqliteExprDelete(p->pWhere);
|
||||
p->pWhere = pNew;
|
||||
}
|
||||
if( p->pExprList ){
|
||||
ExprList *pNew = sqliteExprListDup(p->pExprList);
|
||||
sqliteExprListDelete(p->pExprList);
|
||||
p->pExprList = pNew;
|
||||
}
|
||||
if( p->pIdList ){
|
||||
IdList *pNew = sqliteIdListDup(p->pIdList);
|
||||
sqliteIdListDelete(p->pIdList);
|
||||
p->pIdList = pNew;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Turn a SELECT statement (that the pSelect parameter points to) into
|
||||
** a trigger step. Return a pointer to a TriggerStep structure.
|
||||
**
|
||||
** The parser calls this routine when it finds a SELECT statement in
|
||||
** body of a TRIGGER.
|
||||
*/
|
||||
TriggerStep *sqliteTriggerSelectStep(Select *pSelect){
|
||||
TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
|
||||
if( pTriggerStep==0 ) return 0;
|
||||
|
||||
pTriggerStep->op = TK_SELECT;
|
||||
pTriggerStep->pSelect = pSelect;
|
||||
pTriggerStep->orconf = OE_Default;
|
||||
sqlitePersistTriggerStep(pTriggerStep);
|
||||
|
||||
return pTriggerStep;
|
||||
}
|
||||
|
||||
/*
|
||||
** Build a trigger step out of an INSERT statement. Return a pointer
|
||||
** to the new trigger step.
|
||||
**
|
||||
** The parser calls this routine when it sees an INSERT inside the
|
||||
** body of a trigger.
|
||||
*/
|
||||
TriggerStep *sqliteTriggerInsertStep(
|
||||
Token *pTableName, /* Name of the table into which we insert */
|
||||
IdList *pColumn, /* List of columns in pTableName to insert into */
|
||||
ExprList *pEList, /* The VALUE clause: a list of values to be inserted */
|
||||
Select *pSelect, /* A SELECT statement that supplies values */
|
||||
int orconf /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */
|
||||
){
|
||||
TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
|
||||
if( pTriggerStep==0 ) return 0;
|
||||
|
||||
assert(pEList == 0 || pSelect == 0);
|
||||
assert(pEList != 0 || pSelect != 0);
|
||||
|
||||
pTriggerStep->op = TK_INSERT;
|
||||
pTriggerStep->pSelect = pSelect;
|
||||
pTriggerStep->target = *pTableName;
|
||||
pTriggerStep->pIdList = pColumn;
|
||||
pTriggerStep->pExprList = pEList;
|
||||
pTriggerStep->orconf = orconf;
|
||||
sqlitePersistTriggerStep(pTriggerStep);
|
||||
|
||||
return pTriggerStep;
|
||||
}
|
||||
|
||||
/*
|
||||
** Construct a trigger step that implements an UPDATE statement and return
|
||||
** a pointer to that trigger step. The parser calls this routine when it
|
||||
** sees an UPDATE statement inside the body of a CREATE TRIGGER.
|
||||
*/
|
||||
TriggerStep *sqliteTriggerUpdateStep(
|
||||
Token *pTableName, /* Name of the table to be updated */
|
||||
ExprList *pEList, /* The SET clause: list of column and new values */
|
||||
Expr *pWhere, /* The WHERE clause */
|
||||
int orconf /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */
|
||||
){
|
||||
TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
|
||||
if( pTriggerStep==0 ) return 0;
|
||||
|
||||
pTriggerStep->op = TK_UPDATE;
|
||||
pTriggerStep->target = *pTableName;
|
||||
pTriggerStep->pExprList = pEList;
|
||||
pTriggerStep->pWhere = pWhere;
|
||||
pTriggerStep->orconf = orconf;
|
||||
sqlitePersistTriggerStep(pTriggerStep);
|
||||
|
||||
return pTriggerStep;
|
||||
}
|
||||
|
||||
/*
|
||||
** Construct a trigger step that implements a DELETE statement and return
|
||||
** a pointer to that trigger step. The parser calls this routine when it
|
||||
** sees a DELETE statement inside the body of a CREATE TRIGGER.
|
||||
*/
|
||||
TriggerStep *sqliteTriggerDeleteStep(Token *pTableName, Expr *pWhere){
|
||||
TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
|
||||
if( pTriggerStep==0 ) return 0;
|
||||
|
||||
pTriggerStep->op = TK_DELETE;
|
||||
pTriggerStep->target = *pTableName;
|
||||
pTriggerStep->pWhere = pWhere;
|
||||
pTriggerStep->orconf = OE_Default;
|
||||
sqlitePersistTriggerStep(pTriggerStep);
|
||||
|
||||
return pTriggerStep;
|
||||
}
|
||||
|
||||
/*
|
||||
** Recursively delete a Trigger structure
|
||||
*/
|
||||
void sqliteDeleteTrigger(Trigger *pTrigger){
|
||||
if( pTrigger==0 ) return;
|
||||
sqliteDeleteTriggerStep(pTrigger->step_list);
|
||||
sqliteFree(pTrigger->name);
|
||||
sqliteFree(pTrigger->table);
|
||||
sqliteExprDelete(pTrigger->pWhen);
|
||||
sqliteIdListDelete(pTrigger->pColumns);
|
||||
if( pTrigger->nameToken.dyn ) sqliteFree((char*)pTrigger->nameToken.z);
|
||||
sqliteFree(pTrigger);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called to drop a trigger from the database schema.
|
||||
*
|
||||
* This may be called directly from the parser and therefore identifies
|
||||
* the trigger by name. The sqliteDropTriggerPtr() routine does the
|
||||
* same job as this routine except it take a spointer to the trigger
|
||||
* instead of the trigger name.
|
||||
*
|
||||
* Note that this function does not delete the trigger entirely. Instead it
|
||||
* removes it from the internal schema and places it in the trigDrop hash
|
||||
* table. This is so that the trigger can be restored into the database schema
|
||||
* if the transaction is rolled back.
|
||||
*/
|
||||
void sqliteDropTrigger(Parse *pParse, SrcList *pName){
|
||||
Trigger *pTrigger;
|
||||
int i;
|
||||
const char *zDb;
|
||||
const char *zName;
|
||||
int nName;
|
||||
sqlite *db = pParse->db;
|
||||
|
||||
if( sqlite_malloc_failed ) goto drop_trigger_cleanup;
|
||||
assert( pName->nSrc==1 );
|
||||
zDb = pName->a[0].zDatabase;
|
||||
zName = pName->a[0].zName;
|
||||
nName = strlen(zName);
|
||||
for(i=0; i<db->nDb; i++){
|
||||
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
|
||||
if( zDb && sqliteStrICmp(db->aDb[j].zName, zDb) ) continue;
|
||||
pTrigger = sqliteHashFind(&(db->aDb[j].trigHash), zName, nName+1);
|
||||
if( pTrigger ) break;
|
||||
}
|
||||
if( !pTrigger ){
|
||||
sqliteErrorMsg(pParse, "no such trigger: %S", pName, 0);
|
||||
goto drop_trigger_cleanup;
|
||||
}
|
||||
sqliteDropTriggerPtr(pParse, pTrigger, 0);
|
||||
|
||||
drop_trigger_cleanup:
|
||||
sqliteSrcListDelete(pName);
|
||||
}
|
||||
|
||||
/*
|
||||
** Drop a trigger given a pointer to that trigger. If nested is false,
|
||||
** then also generate code to remove the trigger from the SQLITE_MASTER
|
||||
** table.
|
||||
*/
|
||||
void sqliteDropTriggerPtr(Parse *pParse, Trigger *pTrigger, int nested){
|
||||
Table *pTable;
|
||||
Vdbe *v;
|
||||
sqlite *db = pParse->db;
|
||||
|
||||
assert( pTrigger->iDb<db->nDb );
|
||||
if( pTrigger->iDb>=2 ){
|
||||
sqliteErrorMsg(pParse, "triggers may not be removed from "
|
||||
"auxiliary database %s", db->aDb[pTrigger->iDb].zName);
|
||||
return;
|
||||
}
|
||||
pTable = sqliteFindTable(db, pTrigger->table,db->aDb[pTrigger->iTabDb].zName);
|
||||
assert(pTable);
|
||||
assert( pTable->iDb==pTrigger->iDb || pTrigger->iDb==1 );
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
{
|
||||
int code = SQLITE_DROP_TRIGGER;
|
||||
const char *zDb = db->aDb[pTrigger->iDb].zName;
|
||||
const char *zTab = SCHEMA_TABLE(pTrigger->iDb);
|
||||
if( pTrigger->iDb ) code = SQLITE_DROP_TEMP_TRIGGER;
|
||||
if( sqliteAuthCheck(pParse, code, pTrigger->name, pTable->zName, zDb) ||
|
||||
sqliteAuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Generate code to destroy the database record of the trigger.
|
||||
*/
|
||||
if( pTable!=0 && !nested && (v = sqliteGetVdbe(pParse))!=0 ){
|
||||
int base;
|
||||
static VdbeOp dropTrigger[] = {
|
||||
{ OP_Rewind, 0, ADDR(8), 0},
|
||||
{ OP_String, 0, 0, 0}, /* 1 */
|
||||
{ OP_MemStore, 1, 1, 0},
|
||||
{ OP_MemLoad, 1, 0, 0}, /* 3 */
|
||||
{ OP_Column, 0, 1, 0},
|
||||
{ OP_Ne, 0, ADDR(7), 0},
|
||||
{ OP_Delete, 0, 0, 0},
|
||||
{ OP_Next, 0, ADDR(3), 0}, /* 7 */
|
||||
};
|
||||
|
||||
sqliteBeginWriteOperation(pParse, 0, 0);
|
||||
sqliteOpenMasterTable(v, pTrigger->iDb);
|
||||
base = sqliteVdbeAddOpList(v, ArraySize(dropTrigger), dropTrigger);
|
||||
sqliteVdbeChangeP3(v, base+1, pTrigger->name, 0);
|
||||
if( pTrigger->iDb==0 ){
|
||||
sqliteChangeCookie(db, v);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_Close, 0, 0);
|
||||
sqliteEndWriteOperation(pParse);
|
||||
}
|
||||
|
||||
/*
|
||||
* If this is not an "explain", then delete the trigger structure.
|
||||
*/
|
||||
if( !pParse->explain ){
|
||||
const char *zName = pTrigger->name;
|
||||
int nName = strlen(zName);
|
||||
if( pTable->pTrigger == pTrigger ){
|
||||
pTable->pTrigger = pTrigger->pNext;
|
||||
}else{
|
||||
Trigger *cc = pTable->pTrigger;
|
||||
while( cc ){
|
||||
if( cc->pNext == pTrigger ){
|
||||
cc->pNext = cc->pNext->pNext;
|
||||
break;
|
||||
}
|
||||
cc = cc->pNext;
|
||||
}
|
||||
assert(cc);
|
||||
}
|
||||
sqliteHashInsert(&(db->aDb[pTrigger->iDb].trigHash), zName, nName+1, 0);
|
||||
sqliteDeleteTrigger(pTrigger);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** pEList is the SET clause of an UPDATE statement. Each entry
|
||||
** in pEList is of the format <id>=<expr>. If any of the entries
|
||||
** in pEList have an <id> which matches an identifier in pIdList,
|
||||
** then return TRUE. If pIdList==NULL, then it is considered a
|
||||
** wildcard that matches anything. Likewise if pEList==NULL then
|
||||
** it matches anything so always return true. Return false only
|
||||
** if there is no match.
|
||||
*/
|
||||
static int checkColumnOverLap(IdList *pIdList, ExprList *pEList){
|
||||
int e;
|
||||
if( !pIdList || !pEList ) return 1;
|
||||
for(e=0; e<pEList->nExpr; e++){
|
||||
if( sqliteIdListIndex(pIdList, pEList->a[e].zName)>=0 ) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* A global variable that is TRUE if we should always set up temp tables for
|
||||
* for triggers, even if there are no triggers to code. This is used to test
|
||||
* how much overhead the triggers algorithm is causing.
|
||||
*
|
||||
* This flag can be set or cleared using the "trigger_overhead_test" pragma.
|
||||
* The pragma is not documented since it is not really part of the interface
|
||||
* to SQLite, just the test procedure.
|
||||
*/
|
||||
int always_code_trigger_setup = 0;
|
||||
|
||||
/*
|
||||
* Returns true if a trigger matching op, tr_tm and foreach that is NOT already
|
||||
* on the Parse objects trigger-stack (to prevent recursive trigger firing) is
|
||||
* found in the list specified as pTrigger.
|
||||
*/
|
||||
int sqliteTriggersExist(
|
||||
Parse *pParse, /* Used to check for recursive triggers */
|
||||
Trigger *pTrigger, /* A list of triggers associated with a table */
|
||||
int op, /* one of TK_DELETE, TK_INSERT, TK_UPDATE */
|
||||
int tr_tm, /* one of TK_BEFORE, TK_AFTER */
|
||||
int foreach, /* one of TK_ROW or TK_STATEMENT */
|
||||
ExprList *pChanges /* Columns that change in an UPDATE statement */
|
||||
){
|
||||
Trigger * pTriggerCursor;
|
||||
|
||||
if( always_code_trigger_setup ){
|
||||
return 1;
|
||||
}
|
||||
|
||||
pTriggerCursor = pTrigger;
|
||||
while( pTriggerCursor ){
|
||||
if( pTriggerCursor->op == op &&
|
||||
pTriggerCursor->tr_tm == tr_tm &&
|
||||
pTriggerCursor->foreach == foreach &&
|
||||
checkColumnOverLap(pTriggerCursor->pColumns, pChanges) ){
|
||||
TriggerStack * ss;
|
||||
ss = pParse->trigStack;
|
||||
while( ss && ss->pTrigger != pTrigger ){
|
||||
ss = ss->pNext;
|
||||
}
|
||||
if( !ss )return 1;
|
||||
}
|
||||
pTriggerCursor = pTriggerCursor->pNext;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert the pStep->target token into a SrcList and return a pointer
|
||||
** to that SrcList.
|
||||
**
|
||||
** This routine adds a specific database name, if needed, to the target when
|
||||
** forming the SrcList. This prevents a trigger in one database from
|
||||
** referring to a target in another database. An exception is when the
|
||||
** trigger is in TEMP in which case it can refer to any other database it
|
||||
** wants.
|
||||
*/
|
||||
static SrcList *targetSrcList(
|
||||
Parse *pParse, /* The parsing context */
|
||||
TriggerStep *pStep /* The trigger containing the target token */
|
||||
){
|
||||
Token sDb; /* Dummy database name token */
|
||||
int iDb; /* Index of the database to use */
|
||||
SrcList *pSrc; /* SrcList to be returned */
|
||||
|
||||
iDb = pStep->pTrig->iDb;
|
||||
if( iDb==0 || iDb>=2 ){
|
||||
assert( iDb<pParse->db->nDb );
|
||||
sDb.z = pParse->db->aDb[iDb].zName;
|
||||
sDb.n = strlen(sDb.z);
|
||||
pSrc = sqliteSrcListAppend(0, &sDb, &pStep->target);
|
||||
} else {
|
||||
pSrc = sqliteSrcListAppend(0, &pStep->target, 0);
|
||||
}
|
||||
return pSrc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate VDBE code for zero or more statements inside the body of a
|
||||
** trigger.
|
||||
*/
|
||||
static int codeTriggerProgram(
|
||||
Parse *pParse, /* The parser context */
|
||||
TriggerStep *pStepList, /* List of statements inside the trigger body */
|
||||
int orconfin /* Conflict algorithm. (OE_Abort, etc) */
|
||||
){
|
||||
TriggerStep * pTriggerStep = pStepList;
|
||||
int orconf;
|
||||
|
||||
while( pTriggerStep ){
|
||||
int saveNTab = pParse->nTab;
|
||||
|
||||
orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin;
|
||||
pParse->trigStack->orconf = orconf;
|
||||
switch( pTriggerStep->op ){
|
||||
case TK_SELECT: {
|
||||
Select * ss = sqliteSelectDup(pTriggerStep->pSelect);
|
||||
assert(ss);
|
||||
assert(ss->pSrc);
|
||||
sqliteSelect(pParse, ss, SRT_Discard, 0, 0, 0, 0);
|
||||
sqliteSelectDelete(ss);
|
||||
break;
|
||||
}
|
||||
case TK_UPDATE: {
|
||||
SrcList *pSrc;
|
||||
pSrc = targetSrcList(pParse, pTriggerStep);
|
||||
sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0);
|
||||
sqliteUpdate(pParse, pSrc,
|
||||
sqliteExprListDup(pTriggerStep->pExprList),
|
||||
sqliteExprDup(pTriggerStep->pWhere), orconf);
|
||||
sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0);
|
||||
break;
|
||||
}
|
||||
case TK_INSERT: {
|
||||
SrcList *pSrc;
|
||||
pSrc = targetSrcList(pParse, pTriggerStep);
|
||||
sqliteInsert(pParse, pSrc,
|
||||
sqliteExprListDup(pTriggerStep->pExprList),
|
||||
sqliteSelectDup(pTriggerStep->pSelect),
|
||||
sqliteIdListDup(pTriggerStep->pIdList), orconf);
|
||||
break;
|
||||
}
|
||||
case TK_DELETE: {
|
||||
SrcList *pSrc;
|
||||
sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0);
|
||||
pSrc = targetSrcList(pParse, pTriggerStep);
|
||||
sqliteDeleteFrom(pParse, pSrc, sqliteExprDup(pTriggerStep->pWhere));
|
||||
sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
pParse->nTab = saveNTab;
|
||||
pTriggerStep = pTriggerStep->pNext;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** This is called to code FOR EACH ROW triggers.
|
||||
**
|
||||
** When the code that this function generates is executed, the following
|
||||
** must be true:
|
||||
**
|
||||
** 1. No cursors may be open in the main database. (But newIdx and oldIdx
|
||||
** can be indices of cursors in temporary tables. See below.)
|
||||
**
|
||||
** 2. If the triggers being coded are ON INSERT or ON UPDATE triggers, then
|
||||
** a temporary vdbe cursor (index newIdx) must be open and pointing at
|
||||
** a row containing values to be substituted for new.* expressions in the
|
||||
** trigger program(s).
|
||||
**
|
||||
** 3. If the triggers being coded are ON DELETE or ON UPDATE triggers, then
|
||||
** a temporary vdbe cursor (index oldIdx) must be open and pointing at
|
||||
** a row containing values to be substituted for old.* expressions in the
|
||||
** trigger program(s).
|
||||
**
|
||||
*/
|
||||
int sqliteCodeRowTrigger(
|
||||
Parse *pParse, /* Parse context */
|
||||
int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */
|
||||
ExprList *pChanges, /* Changes list for any UPDATE OF triggers */
|
||||
int tr_tm, /* One of TK_BEFORE, TK_AFTER */
|
||||
Table *pTab, /* The table to code triggers from */
|
||||
int newIdx, /* The indice of the "new" row to access */
|
||||
int oldIdx, /* The indice of the "old" row to access */
|
||||
int orconf, /* ON CONFLICT policy */
|
||||
int ignoreJump /* Instruction to jump to for RAISE(IGNORE) */
|
||||
){
|
||||
Trigger * pTrigger;
|
||||
TriggerStack * pTriggerStack;
|
||||
|
||||
assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE);
|
||||
assert(tr_tm == TK_BEFORE || tr_tm == TK_AFTER );
|
||||
|
||||
assert(newIdx != -1 || oldIdx != -1);
|
||||
|
||||
pTrigger = pTab->pTrigger;
|
||||
while( pTrigger ){
|
||||
int fire_this = 0;
|
||||
|
||||
/* determine whether we should code this trigger */
|
||||
if( pTrigger->op == op && pTrigger->tr_tm == tr_tm &&
|
||||
pTrigger->foreach == TK_ROW ){
|
||||
fire_this = 1;
|
||||
pTriggerStack = pParse->trigStack;
|
||||
while( pTriggerStack ){
|
||||
if( pTriggerStack->pTrigger == pTrigger ){
|
||||
fire_this = 0;
|
||||
}
|
||||
pTriggerStack = pTriggerStack->pNext;
|
||||
}
|
||||
if( op == TK_UPDATE && pTrigger->pColumns &&
|
||||
!checkColumnOverLap(pTrigger->pColumns, pChanges) ){
|
||||
fire_this = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if( fire_this && (pTriggerStack = sqliteMalloc(sizeof(TriggerStack)))!=0 ){
|
||||
int endTrigger;
|
||||
SrcList dummyTablist;
|
||||
Expr * whenExpr;
|
||||
AuthContext sContext;
|
||||
|
||||
dummyTablist.nSrc = 0;
|
||||
|
||||
/* Push an entry on to the trigger stack */
|
||||
pTriggerStack->pTrigger = pTrigger;
|
||||
pTriggerStack->newIdx = newIdx;
|
||||
pTriggerStack->oldIdx = oldIdx;
|
||||
pTriggerStack->pTab = pTab;
|
||||
pTriggerStack->pNext = pParse->trigStack;
|
||||
pTriggerStack->ignoreJump = ignoreJump;
|
||||
pParse->trigStack = pTriggerStack;
|
||||
sqliteAuthContextPush(pParse, &sContext, pTrigger->name);
|
||||
|
||||
/* code the WHEN clause */
|
||||
endTrigger = sqliteVdbeMakeLabel(pParse->pVdbe);
|
||||
whenExpr = sqliteExprDup(pTrigger->pWhen);
|
||||
if( sqliteExprResolveIds(pParse, &dummyTablist, 0, whenExpr) ){
|
||||
pParse->trigStack = pParse->trigStack->pNext;
|
||||
sqliteFree(pTriggerStack);
|
||||
sqliteExprDelete(whenExpr);
|
||||
return 1;
|
||||
}
|
||||
sqliteExprIfFalse(pParse, whenExpr, endTrigger, 1);
|
||||
sqliteExprDelete(whenExpr);
|
||||
|
||||
codeTriggerProgram(pParse, pTrigger->step_list, orconf);
|
||||
|
||||
/* Pop the entry off the trigger stack */
|
||||
pParse->trigStack = pParse->trigStack->pNext;
|
||||
sqliteAuthContextPop(&sContext);
|
||||
sqliteFree(pTriggerStack);
|
||||
|
||||
sqliteVdbeResolveLabel(pParse->pVdbe, endTrigger);
|
||||
}
|
||||
pTrigger = pTrigger->pNext;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
452
sqlitebrowser/sqlitebrowser/sqlite_source/update.c
Executable file
@@ -0,0 +1,452 @@
|
||||
/*
|
||||
** 2001 September 15
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle UPDATE statements.
|
||||
**
|
||||
** $Id: update.c,v 1.1.1.1 2003-08-21 02:24:22 tabuleiro Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** Process an UPDATE statement.
|
||||
**
|
||||
** UPDATE OR IGNORE table_wxyz SET a=b, c=d WHERE e<5 AND f NOT NULL;
|
||||
** \_______/ \________/ \______/ \________________/
|
||||
* onError pTabList pChanges pWhere
|
||||
*/
|
||||
void sqliteUpdate(
|
||||
Parse *pParse, /* The parser context */
|
||||
SrcList *pTabList, /* The table in which we should change things */
|
||||
ExprList *pChanges, /* Things to be changed */
|
||||
Expr *pWhere, /* The WHERE clause. May be null */
|
||||
int onError /* How to handle constraint errors */
|
||||
){
|
||||
int i, j; /* Loop counters */
|
||||
Table *pTab; /* The table to be updated */
|
||||
int addr; /* VDBE instruction address of the start of the loop */
|
||||
WhereInfo *pWInfo; /* Information about the WHERE clause */
|
||||
Vdbe *v; /* The virtual database engine */
|
||||
Index *pIdx; /* For looping over indices */
|
||||
int nIdx; /* Number of indices that need updating */
|
||||
int nIdxTotal; /* Total number of indices */
|
||||
int iCur; /* VDBE Cursor number of pTab */
|
||||
sqlite *db; /* The database structure */
|
||||
Index **apIdx = 0; /* An array of indices that need updating too */
|
||||
char *aIdxUsed = 0; /* aIdxUsed[i]==1 if the i-th index is used */
|
||||
int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the
|
||||
** an expression for the i-th column of the table.
|
||||
** aXRef[i]==-1 if the i-th column is not changed. */
|
||||
int chngRecno; /* True if the record number is being changed */
|
||||
Expr *pRecnoExpr; /* Expression defining the new record number */
|
||||
int openAll; /* True if all indices need to be opened */
|
||||
int isView; /* Trying to update a view */
|
||||
AuthContext sContext; /* The authorization context */
|
||||
|
||||
int before_triggers; /* True if there are any BEFORE triggers */
|
||||
int after_triggers; /* True if there are any AFTER triggers */
|
||||
int row_triggers_exist = 0; /* True if any row triggers exist */
|
||||
|
||||
int newIdx = -1; /* index of trigger "new" temp table */
|
||||
int oldIdx = -1; /* index of trigger "old" temp table */
|
||||
|
||||
sContext.pParse = 0;
|
||||
if( pParse->nErr || sqlite_malloc_failed ) goto update_cleanup;
|
||||
db = pParse->db;
|
||||
assert( pTabList->nSrc==1 );
|
||||
|
||||
/* Locate the table which we want to update.
|
||||
*/
|
||||
pTab = sqliteSrcListLookup(pParse, pTabList);
|
||||
if( pTab==0 ) goto update_cleanup;
|
||||
before_triggers = sqliteTriggersExist(pParse, pTab->pTrigger,
|
||||
TK_UPDATE, TK_BEFORE, TK_ROW, pChanges);
|
||||
after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger,
|
||||
TK_UPDATE, TK_AFTER, TK_ROW, pChanges);
|
||||
row_triggers_exist = before_triggers || after_triggers;
|
||||
isView = pTab->pSelect!=0;
|
||||
if( sqliteIsReadOnly(pParse, pTab, before_triggers) ){
|
||||
goto update_cleanup;
|
||||
}
|
||||
if( isView ){
|
||||
if( sqliteViewGetColumnNames(pParse, pTab) ){
|
||||
goto update_cleanup;
|
||||
}
|
||||
}
|
||||
aXRef = sqliteMalloc( sizeof(int) * pTab->nCol );
|
||||
if( aXRef==0 ) goto update_cleanup;
|
||||
for(i=0; i<pTab->nCol; i++) aXRef[i] = -1;
|
||||
|
||||
/* If there are FOR EACH ROW triggers, allocate cursors for the
|
||||
** special OLD and NEW tables
|
||||
*/
|
||||
if( row_triggers_exist ){
|
||||
newIdx = pParse->nTab++;
|
||||
oldIdx = pParse->nTab++;
|
||||
}
|
||||
|
||||
/* Allocate a cursors for the main database table and for all indices.
|
||||
** The index cursors might not be used, but if they are used they
|
||||
** need to occur right after the database cursor. So go ahead and
|
||||
** allocate enough space, just in case.
|
||||
*/
|
||||
pTabList->a[0].iCursor = iCur = pParse->nTab++;
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
pParse->nTab++;
|
||||
}
|
||||
|
||||
/* Resolve the column names in all the expressions of the
|
||||
** of the UPDATE statement. Also find the column index
|
||||
** for each column to be updated in the pChanges array. For each
|
||||
** column to be updated, make sure we have authorization to change
|
||||
** that column.
|
||||
*/
|
||||
chngRecno = 0;
|
||||
for(i=0; i<pChanges->nExpr; i++){
|
||||
if( sqliteExprResolveIds(pParse, pTabList, 0, pChanges->a[i].pExpr) ){
|
||||
goto update_cleanup;
|
||||
}
|
||||
if( sqliteExprCheck(pParse, pChanges->a[i].pExpr, 0, 0) ){
|
||||
goto update_cleanup;
|
||||
}
|
||||
for(j=0; j<pTab->nCol; j++){
|
||||
if( sqliteStrICmp(pTab->aCol[j].zName, pChanges->a[i].zName)==0 ){
|
||||
if( j==pTab->iPKey ){
|
||||
chngRecno = 1;
|
||||
pRecnoExpr = pChanges->a[i].pExpr;
|
||||
}
|
||||
aXRef[j] = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( j>=pTab->nCol ){
|
||||
if( sqliteIsRowid(pChanges->a[i].zName) ){
|
||||
chngRecno = 1;
|
||||
pRecnoExpr = pChanges->a[i].pExpr;
|
||||
}else{
|
||||
sqliteErrorMsg(pParse, "no such column: %s", pChanges->a[i].zName);
|
||||
goto update_cleanup;
|
||||
}
|
||||
}
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
{
|
||||
int rc;
|
||||
rc = sqliteAuthCheck(pParse, SQLITE_UPDATE, pTab->zName,
|
||||
pTab->aCol[j].zName, db->aDb[pTab->iDb].zName);
|
||||
if( rc==SQLITE_DENY ){
|
||||
goto update_cleanup;
|
||||
}else if( rc==SQLITE_IGNORE ){
|
||||
aXRef[j] = -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Allocate memory for the array apIdx[] and fill it with pointers to every
|
||||
** index that needs to be updated. Indices only need updating if their
|
||||
** key includes one of the columns named in pChanges or if the record
|
||||
** number of the original table entry is changing.
|
||||
*/
|
||||
for(nIdx=nIdxTotal=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdxTotal++){
|
||||
if( chngRecno ){
|
||||
i = 0;
|
||||
}else {
|
||||
for(i=0; i<pIdx->nColumn; i++){
|
||||
if( aXRef[pIdx->aiColumn[i]]>=0 ) break;
|
||||
}
|
||||
}
|
||||
if( i<pIdx->nColumn ) nIdx++;
|
||||
}
|
||||
if( nIdxTotal>0 ){
|
||||
apIdx = sqliteMalloc( sizeof(Index*) * nIdx + nIdxTotal );
|
||||
if( apIdx==0 ) goto update_cleanup;
|
||||
aIdxUsed = (char*)&apIdx[nIdx];
|
||||
}
|
||||
for(nIdx=j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
|
||||
if( chngRecno ){
|
||||
i = 0;
|
||||
}else{
|
||||
for(i=0; i<pIdx->nColumn; i++){
|
||||
if( aXRef[pIdx->aiColumn[i]]>=0 ) break;
|
||||
}
|
||||
}
|
||||
if( i<pIdx->nColumn ){
|
||||
apIdx[nIdx++] = pIdx;
|
||||
aIdxUsed[j] = 1;
|
||||
}else{
|
||||
aIdxUsed[j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Resolve the column names in all the expressions in the
|
||||
** WHERE clause.
|
||||
*/
|
||||
if( pWhere ){
|
||||
if( sqliteExprResolveIds(pParse, pTabList, 0, pWhere) ){
|
||||
goto update_cleanup;
|
||||
}
|
||||
if( sqliteExprCheck(pParse, pWhere, 0, 0) ){
|
||||
goto update_cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/* Start the view context
|
||||
*/
|
||||
if( isView ){
|
||||
sqliteAuthContextPush(pParse, &sContext, pTab->zName);
|
||||
}
|
||||
|
||||
/* Begin generating code.
|
||||
*/
|
||||
v = sqliteGetVdbe(pParse);
|
||||
if( v==0 ) goto update_cleanup;
|
||||
sqliteBeginWriteOperation(pParse, 1, pTab->iDb);
|
||||
|
||||
/* If we are trying to update a view, construct that view into
|
||||
** a temporary table.
|
||||
*/
|
||||
if( isView ){
|
||||
Select *pView;
|
||||
pView = sqliteSelectDup(pTab->pSelect);
|
||||
sqliteSelect(pParse, pView, SRT_TempTable, iCur, 0, 0, 0);
|
||||
sqliteSelectDelete(pView);
|
||||
}
|
||||
|
||||
/* Begin the database scan
|
||||
*/
|
||||
pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 1, 0);
|
||||
if( pWInfo==0 ) goto update_cleanup;
|
||||
|
||||
/* Remember the index of every item to be updated.
|
||||
*/
|
||||
sqliteVdbeAddOp(v, OP_ListWrite, 0, 0);
|
||||
|
||||
/* End the database scan loop.
|
||||
*/
|
||||
sqliteWhereEnd(pWInfo);
|
||||
|
||||
/* Initialize the count of updated rows
|
||||
*/
|
||||
if( db->flags & SQLITE_CountRows && !pParse->trigStack ){
|
||||
sqliteVdbeAddOp(v, OP_Integer, 0, 0);
|
||||
}
|
||||
|
||||
if( row_triggers_exist ){
|
||||
/* Create pseudo-tables for NEW and OLD
|
||||
*/
|
||||
sqliteVdbeAddOp(v, OP_OpenPseudo, oldIdx, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenPseudo, newIdx, 0);
|
||||
|
||||
/* The top of the update loop for when there are triggers.
|
||||
*/
|
||||
sqliteVdbeAddOp(v, OP_ListRewind, 0, 0);
|
||||
addr = sqliteVdbeAddOp(v, OP_ListRead, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Dup, 0, 0);
|
||||
|
||||
/* Open a cursor and make it point to the record that is
|
||||
** being updated.
|
||||
*/
|
||||
sqliteVdbeAddOp(v, OP_Dup, 0, 0);
|
||||
if( !isView ){
|
||||
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenRead, iCur, pTab->tnum);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_MoveTo, iCur, 0);
|
||||
|
||||
/* Generate the OLD table
|
||||
*/
|
||||
sqliteVdbeAddOp(v, OP_Recno, iCur, 0);
|
||||
sqliteVdbeAddOp(v, OP_RowData, iCur, 0);
|
||||
sqliteVdbeAddOp(v, OP_PutIntKey, oldIdx, 0);
|
||||
|
||||
/* Generate the NEW table
|
||||
*/
|
||||
if( chngRecno ){
|
||||
sqliteExprCode(pParse, pRecnoExpr);
|
||||
}else{
|
||||
sqliteVdbeAddOp(v, OP_Recno, iCur, 0);
|
||||
}
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
if( i==pTab->iPKey ){
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
continue;
|
||||
}
|
||||
j = aXRef[i];
|
||||
if( j<0 ){
|
||||
sqliteVdbeAddOp(v, OP_Column, iCur, i);
|
||||
}else{
|
||||
sqliteExprCode(pParse, pChanges->a[j].pExpr);
|
||||
}
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
|
||||
sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0);
|
||||
if( !isView ){
|
||||
sqliteVdbeAddOp(v, OP_Close, iCur, 0);
|
||||
}
|
||||
|
||||
/* Fire the BEFORE and INSTEAD OF triggers
|
||||
*/
|
||||
if( sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE, pTab,
|
||||
newIdx, oldIdx, onError, addr) ){
|
||||
goto update_cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if( !isView ){
|
||||
/*
|
||||
** Open every index that needs updating. Note that if any
|
||||
** index could potentially invoke a REPLACE conflict resolution
|
||||
** action, then we need to open all indices because we might need
|
||||
** to be deleting some records.
|
||||
*/
|
||||
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenWrite, iCur, pTab->tnum);
|
||||
if( onError==OE_Replace ){
|
||||
openAll = 1;
|
||||
}else{
|
||||
openAll = 0;
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
if( pIdx->onError==OE_Replace ){
|
||||
openAll = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
|
||||
if( openAll || aIdxUsed[i] ){
|
||||
sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenWrite, iCur+i+1, pIdx->tnum);
|
||||
assert( pParse->nTab>iCur+i+1 );
|
||||
}
|
||||
}
|
||||
|
||||
/* Loop over every record that needs updating. We have to load
|
||||
** the old data for each record to be updated because some columns
|
||||
** might not change and we will need to copy the old value.
|
||||
** Also, the old data is needed to delete the old index entires.
|
||||
** So make the cursor point at the old record.
|
||||
*/
|
||||
if( !row_triggers_exist ){
|
||||
sqliteVdbeAddOp(v, OP_ListRewind, 0, 0);
|
||||
addr = sqliteVdbeAddOp(v, OP_ListRead, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Dup, 0, 0);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_NotExists, iCur, addr);
|
||||
|
||||
/* If the record number will change, push the record number as it
|
||||
** will be after the update. (The old record number is currently
|
||||
** on top of the stack.)
|
||||
*/
|
||||
if( chngRecno ){
|
||||
sqliteExprCode(pParse, pRecnoExpr);
|
||||
sqliteVdbeAddOp(v, OP_MustBeInt, 0, 0);
|
||||
}
|
||||
|
||||
/* Compute new data for this record.
|
||||
*/
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
if( i==pTab->iPKey ){
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
continue;
|
||||
}
|
||||
j = aXRef[i];
|
||||
if( j<0 ){
|
||||
sqliteVdbeAddOp(v, OP_Column, iCur, i);
|
||||
}else{
|
||||
sqliteExprCode(pParse, pChanges->a[j].pExpr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Do constraint checks
|
||||
*/
|
||||
sqliteGenerateConstraintChecks(pParse, pTab, iCur, aIdxUsed, chngRecno, 1,
|
||||
onError, addr);
|
||||
|
||||
/* Delete the old indices for the current record.
|
||||
*/
|
||||
sqliteGenerateRowIndexDelete(db, v, pTab, iCur, aIdxUsed);
|
||||
|
||||
/* If changing the record number, delete the old record.
|
||||
*/
|
||||
if( chngRecno ){
|
||||
sqliteVdbeAddOp(v, OP_Delete, iCur, 0);
|
||||
}
|
||||
|
||||
/* Create the new index entries and the new record.
|
||||
*/
|
||||
sqliteCompleteInsertion(pParse, pTab, iCur, aIdxUsed, chngRecno, 1, -1);
|
||||
}
|
||||
|
||||
/* Increment the row counter
|
||||
*/
|
||||
if( db->flags & SQLITE_CountRows && !pParse->trigStack){
|
||||
sqliteVdbeAddOp(v, OP_AddImm, 1, 0);
|
||||
}
|
||||
|
||||
/* If there are triggers, close all the cursors after each iteration
|
||||
** through the loop. The fire the after triggers.
|
||||
*/
|
||||
if( row_triggers_exist ){
|
||||
if( !isView ){
|
||||
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
|
||||
if( openAll || aIdxUsed[i] )
|
||||
sqliteVdbeAddOp(v, OP_Close, iCur+i+1, 0);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_Close, iCur, 0);
|
||||
pParse->nTab = iCur;
|
||||
}
|
||||
if( sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_AFTER, pTab,
|
||||
newIdx, oldIdx, onError, addr) ){
|
||||
goto update_cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/* Repeat the above with the next record to be updated, until
|
||||
** all record selected by the WHERE clause have been updated.
|
||||
*/
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, addr);
|
||||
sqliteVdbeChangeP2(v, addr, sqliteVdbeCurrentAddr(v));
|
||||
sqliteVdbeAddOp(v, OP_ListReset, 0, 0);
|
||||
|
||||
/* Close all tables if there were no FOR EACH ROW triggers */
|
||||
if( !row_triggers_exist ){
|
||||
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
|
||||
if( openAll || aIdxUsed[i] ){
|
||||
sqliteVdbeAddOp(v, OP_Close, iCur+i+1, 0);
|
||||
}
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_Close, iCur, 0);
|
||||
pParse->nTab = iCur;
|
||||
}else{
|
||||
sqliteVdbeAddOp(v, OP_Close, newIdx, 0);
|
||||
sqliteVdbeAddOp(v, OP_Close, oldIdx, 0);
|
||||
}
|
||||
|
||||
sqliteEndWriteOperation(pParse);
|
||||
|
||||
/*
|
||||
** Return the number of rows that were changed.
|
||||
*/
|
||||
if( db->flags & SQLITE_CountRows && !pParse->trigStack ){
|
||||
sqliteVdbeAddOp(v, OP_ColumnName, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, "rows updated", P3_STATIC);
|
||||
sqliteVdbeAddOp(v, OP_Callback, 1, 0);
|
||||
}
|
||||
|
||||
update_cleanup:
|
||||
sqliteAuthContextPop(&sContext);
|
||||
sqliteFree(apIdx);
|
||||
sqliteFree(aXRef);
|
||||
sqliteSrcListDelete(pTabList);
|
||||
sqliteExprListDelete(pChanges);
|
||||
sqliteExprDelete(pWhere);
|
||||
return;
|
||||
}
|
||||
1152
sqlitebrowser/sqlitebrowser/sqlite_source/util.c
Executable file
315
sqlitebrowser/sqlitebrowser/sqlite_source/vacuum.c
Executable file
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
** 2003 April 6
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains code used to implement the VACUUM command.
|
||||
**
|
||||
** Most of the code in this file may be omitted by defining the
|
||||
** SQLITE_OMIT_VACUUM macro.
|
||||
**
|
||||
** $Id: vacuum.c,v 1.1.1.1 2003-08-21 02:24:23 tabuleiro Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "os.h"
|
||||
|
||||
/*
|
||||
** A structure for holding a dynamic string - a string that can grow
|
||||
** without bound.
|
||||
*/
|
||||
typedef struct dynStr dynStr;
|
||||
struct dynStr {
|
||||
char *z; /* Text of the string in space obtained from sqliteMalloc() */
|
||||
int nAlloc; /* Amount of space allocated to z[] */
|
||||
int nUsed; /* Next unused slot in z[] */
|
||||
};
|
||||
|
||||
/*
|
||||
** A structure that holds the vacuum context
|
||||
*/
|
||||
typedef struct vacuumStruct vacuumStruct;
|
||||
struct vacuumStruct {
|
||||
sqlite *dbOld; /* Original database */
|
||||
sqlite *dbNew; /* New database */
|
||||
Parse *pParse; /* The parser context */
|
||||
const char *zTable; /* Name of a table being copied */
|
||||
const char *zPragma; /* Pragma to execute with results */
|
||||
dynStr s1, s2; /* Two dynamic strings */
|
||||
};
|
||||
|
||||
#if !defined(SQLITE_OMIT_VACUUM) || SQLITE_OMIT_VACUUM
|
||||
/*
|
||||
** Append text to a dynamic string
|
||||
*/
|
||||
static void appendText(dynStr *p, const char *zText, int nText){
|
||||
if( nText<0 ) nText = strlen(zText);
|
||||
if( p->z==0 || p->nUsed + nText + 1 >= p->nAlloc ){
|
||||
char *zNew;
|
||||
p->nAlloc = p->nUsed + nText + 1000;
|
||||
zNew = sqliteRealloc(p->z, p->nAlloc);
|
||||
if( zNew==0 ){
|
||||
sqliteFree(p->z);
|
||||
memset(p, 0, sizeof(*p));
|
||||
return;
|
||||
}
|
||||
p->z = zNew;
|
||||
}
|
||||
memcpy(&p->z[p->nUsed], zText, nText+1);
|
||||
p->nUsed += nText;
|
||||
}
|
||||
|
||||
/*
|
||||
** Append text to a dynamic string, having first put the text in quotes.
|
||||
*/
|
||||
static void appendQuoted(dynStr *p, const char *zText){
|
||||
int i, j;
|
||||
appendText(p, "'", 1);
|
||||
for(i=j=0; zText[i]; i++){
|
||||
if( zText[i]=='\'' ){
|
||||
appendText(p, &zText[j], i-j+1);
|
||||
j = i + 1;
|
||||
appendText(p, "'", 1);
|
||||
}
|
||||
}
|
||||
if( j<i ){
|
||||
appendText(p, &zText[j], i-j);
|
||||
}
|
||||
appendText(p, "'", 1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Execute statements of SQL. If an error occurs, write the error
|
||||
** message into pParse->zErrMsg and return non-zero.
|
||||
*/
|
||||
static int execsql(Parse *pParse, sqlite *db, const char *zSql){
|
||||
int rc;
|
||||
char *zErrMsg = 0;
|
||||
|
||||
/* printf("***** executing *****\n%s\n", zSql); */
|
||||
rc = sqlite_exec(db, zSql, 0, 0, &zErrMsg);
|
||||
if( rc ){
|
||||
sqliteErrorMsg(pParse, "%s", zErrMsg);
|
||||
sqlite_freemem(zErrMsg);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This is the second stage callback. Each invocation contains all the
|
||||
** data for a single row of a single table in the original database. This
|
||||
** routine must write that information into the new database.
|
||||
*/
|
||||
static int vacuumCallback2(void *pArg, int argc, char **argv, char **NotUsed){
|
||||
vacuumStruct *p = (vacuumStruct*)pArg;
|
||||
int rc = 0;
|
||||
const char *zSep = "(";
|
||||
int i;
|
||||
|
||||
p->s2.nUsed = 0;
|
||||
appendText(&p->s2, "INSERT INTO ", -1);
|
||||
appendQuoted(&p->s2, p->zTable);
|
||||
appendText(&p->s2, " VALUES", -1);
|
||||
for(i=0; i<argc; i++){
|
||||
appendText(&p->s2, zSep, 1);
|
||||
zSep = ",";
|
||||
if( argv[i]==0 ){
|
||||
appendText(&p->s2, "NULL", 4);
|
||||
}else{
|
||||
appendQuoted(&p->s2, argv[i]);
|
||||
}
|
||||
}
|
||||
appendText(&p->s2,")", 1);
|
||||
rc = execsql(p->pParse, p->dbNew, p->s2.z);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This is the first stage callback. Each invocation contains three
|
||||
** arguments where are taken from the SQLITE_MASTER table of the original
|
||||
** database: (1) the entry type, (2) the entry name, and (3) the SQL for
|
||||
** the entry. In all cases, execute the SQL of the third argument.
|
||||
** For tables, run a query to select all entries in that table and
|
||||
** transfer them to the second-stage callback.
|
||||
*/
|
||||
static int vacuumCallback1(void *pArg, int argc, char **argv, char **NotUsed){
|
||||
vacuumStruct *p = (vacuumStruct*)pArg;
|
||||
int rc = 0;
|
||||
assert( argc==3 );
|
||||
assert( argv[0]!=0 );
|
||||
assert( argv[1]!=0 );
|
||||
assert( argv[2]!=0 );
|
||||
rc = execsql(p->pParse, p->dbNew, argv[2]);
|
||||
if( rc==SQLITE_OK && strcmp(argv[0],"table")==0 ){
|
||||
char *zErrMsg = 0;
|
||||
p->s1.nUsed = 0;
|
||||
appendText(&p->s1, "SELECT * FROM ", -1);
|
||||
appendQuoted(&p->s1, argv[1]);
|
||||
p->zTable = argv[1];
|
||||
rc = sqlite_exec(p->dbOld, p->s1.z, vacuumCallback2, p, &zErrMsg);
|
||||
if( rc && p->pParse->zErrMsg==0 ){
|
||||
sqliteErrorMsg(p->pParse, "%s", zErrMsg);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This callback is used to transfer PRAGMA settings from one database
|
||||
** to the other. The value in argv[0] should be passed to a pragma
|
||||
** identified by ((vacuumStruct*)pArg)->zPragma.
|
||||
*/
|
||||
static int vacuumCallback3(void *pArg, int argc, char **argv, char **NotUsed){
|
||||
vacuumStruct *p = (vacuumStruct*)pArg;
|
||||
int rc = 0;
|
||||
char zBuf[200];
|
||||
assert( argc==1 );
|
||||
assert( argv[0]!=0 );
|
||||
assert( strlen(p->zPragma)<100 );
|
||||
assert( strlen(argv[0])<30 );
|
||||
sprintf(zBuf,"PRAGMA %s=%s;", p->zPragma, argv[0]);
|
||||
rc = execsql(p->pParse, p->dbNew, zBuf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate a random name of 20 character in length.
|
||||
*/
|
||||
static void randomName(char *zBuf){
|
||||
static const char zChars[] =
|
||||
"abcdefghijklmnopqrstuvwxyz";
|
||||
int i;
|
||||
for(i=0; i<5; i++){
|
||||
int n = sqliteRandomByte() % (sizeof(zChars)-1);
|
||||
zBuf[i] = zChars[n];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The non-standard VACUUM command is used to clean up the database,
|
||||
** collapse free space, etc. It is modelled after the VACUUM command
|
||||
** in PostgreSQL.
|
||||
**
|
||||
** In version 1.0.x of SQLite, the VACUUM command would call
|
||||
** gdbm_reorganize() on all the database tables. But beginning
|
||||
** with 2.0.0, SQLite no longer uses GDBM so this command has
|
||||
** become a no-op.
|
||||
*/
|
||||
void sqliteVacuum(Parse *pParse, Token *pTableName){
|
||||
#if !defined(SQLITE_OMIT_VACUUM) || SQLITE_OMIT_VACUUM
|
||||
const char *zFilename; /* full pathname of the database file */
|
||||
int nFilename; /* number of characters in zFilename[] */
|
||||
char *zTemp = 0; /* a temporary file in same directory as zFilename */
|
||||
sqlite *dbNew = 0; /* The new vacuumed database */
|
||||
sqlite *db; /* The original database */
|
||||
int rc = SQLITE_OK; /* Return code from service routines */
|
||||
int i; /* Loop counter */
|
||||
char *zErrMsg = 0; /* Error messages stored here */
|
||||
int safety = 0; /* TRUE if safety is off */
|
||||
vacuumStruct sVac; /* Information passed to callbacks */
|
||||
|
||||
/* These are all of the pragmas that need to be transferred over
|
||||
** to the new database */
|
||||
static const char *zPragma[] = {
|
||||
"default_synchronous",
|
||||
"default_cache_size",
|
||||
/* "default_temp_store", */
|
||||
};
|
||||
|
||||
/* Initial error checks
|
||||
*/
|
||||
if( pParse->explain ){
|
||||
return;
|
||||
}
|
||||
db = pParse->db;
|
||||
if( db->flags & SQLITE_InTrans ){
|
||||
sqliteErrorMsg(pParse, "cannot VACUUM from within a transaction");
|
||||
return;
|
||||
}
|
||||
memset(&sVac, 0, sizeof(sVac));
|
||||
|
||||
/* Get the full pathname of the database file and create two
|
||||
** temporary filenames in the same directory as the original file.
|
||||
*/
|
||||
zFilename = sqliteBtreeGetFilename(db->aDb[0].pBt);
|
||||
if( zFilename==0 ){
|
||||
/* This only happens with the in-memory database. VACUUM is a no-op
|
||||
** there, so just return */
|
||||
return;
|
||||
}
|
||||
nFilename = strlen(zFilename);
|
||||
zTemp = sqliteMalloc( nFilename+100 );
|
||||
if( zTemp==0 ) return;
|
||||
strcpy(zTemp, zFilename);
|
||||
for(i=0; i<10; i++){
|
||||
zTemp[nFilename] = '-';
|
||||
randomName(&zTemp[nFilename+1]);
|
||||
if( !sqliteOsFileExists(zTemp) ) break;
|
||||
}
|
||||
if( i>=10 ){
|
||||
sqliteErrorMsg(pParse, "unable to create a temporary database file "
|
||||
"in the same directory as the original database");
|
||||
goto end_of_vacuum;
|
||||
}
|
||||
|
||||
|
||||
dbNew = sqlite_open(zTemp, 0, &zErrMsg);
|
||||
if( dbNew==0 ){
|
||||
sqliteErrorMsg(pParse, "unable to open a temporary database at %s - %s",
|
||||
zTemp, zErrMsg);
|
||||
goto end_of_vacuum;
|
||||
}
|
||||
if( sqliteSafetyOff(db) ){
|
||||
sqliteErrorMsg(pParse, "library routines called out of sequence");
|
||||
goto end_of_vacuum;
|
||||
}
|
||||
safety = 1;
|
||||
if( execsql(pParse, db, "BEGIN") ) goto end_of_vacuum;
|
||||
if( execsql(pParse, dbNew, "PRAGMA synchronous=off; BEGIN") ){
|
||||
goto end_of_vacuum;
|
||||
}
|
||||
sVac.dbOld = db;
|
||||
sVac.dbNew = dbNew;
|
||||
sVac.pParse = pParse;
|
||||
for(i=0; rc==SQLITE_OK && i<sizeof(zPragma)/sizeof(zPragma[0]); i++){
|
||||
char zBuf[200];
|
||||
assert( strlen(zPragma[i])<100 );
|
||||
sprintf(zBuf, "PRAGMA %s;", zPragma[i]);
|
||||
sVac.zPragma = zPragma[i];
|
||||
rc = sqlite_exec(db, zBuf, vacuumCallback3, &sVac, &zErrMsg);
|
||||
}
|
||||
if( !rc ){
|
||||
rc = sqlite_exec(db, "SELECT type, name, sql FROM sqlite_master "
|
||||
"WHERE sql NOT NULL", vacuumCallback1, &sVac, &zErrMsg);
|
||||
}
|
||||
if( !rc ){
|
||||
rc = sqliteBtreeCopyFile(db->aDb[0].pBt, dbNew->aDb[0].pBt);
|
||||
sqlite_exec(db, "COMMIT", 0, 0, 0);
|
||||
sqlite_exec(db, "ROLLBACK", 0, 0, 0); /* In case the COMMIT failed */
|
||||
sqliteResetInternalSchema(db, 0);
|
||||
}
|
||||
|
||||
end_of_vacuum:
|
||||
if( rc && pParse->zErrMsg==0 && zErrMsg!=0 ){
|
||||
sqliteErrorMsg(pParse, "unable to vacuum database - %s", zErrMsg);
|
||||
}
|
||||
if( safety ) {
|
||||
sqlite_exec(db, "COMMIT", 0, 0, 0);
|
||||
sqlite_exec(db, "ROLLBACK", 0, 0, 0); /* In case the COMMIT failed */
|
||||
sqliteSafetyOn(db);
|
||||
}
|
||||
if( dbNew ) sqlite_close(dbNew);
|
||||
sqliteOsDelete(zTemp);
|
||||
sqliteFree(zTemp);
|
||||
sqliteFree(sVac.s1.z);
|
||||
sqliteFree(sVac.s2.z);
|
||||
if( zErrMsg ) sqlite_freemem(zErrMsg);
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
5929
sqlitebrowser/sqlitebrowser/sqlite_source/vdbe.c
Executable file
97
sqlitebrowser/sqlitebrowser/sqlite_source/vdbe.h
Executable file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
** 2001 September 15
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** Header file for the Virtual DataBase Engine (VDBE)
|
||||
**
|
||||
** This header defines the interface to the virtual database engine
|
||||
** or VDBE. The VDBE implements an abstract machine that runs a
|
||||
** simple program to access and modify the underlying database.
|
||||
**
|
||||
** $Id: vdbe.h,v 1.1.1.1 2003-08-21 02:24:26 tabuleiro Exp $
|
||||
*/
|
||||
#ifndef _SQLITE_VDBE_H_
|
||||
#define _SQLITE_VDBE_H_
|
||||
#include <stdio.h>
|
||||
|
||||
/*
|
||||
** A single VDBE is an opaque structure named "Vdbe". Only routines
|
||||
** in the source file sqliteVdbe.c are allowed to see the insides
|
||||
** of this structure.
|
||||
*/
|
||||
typedef struct Vdbe Vdbe;
|
||||
|
||||
/*
|
||||
** A single instruction of the virtual machine has an opcode
|
||||
** and as many as three operands. The instruction is recorded
|
||||
** as an instance of the following structure:
|
||||
*/
|
||||
struct VdbeOp {
|
||||
int opcode; /* What operation to perform */
|
||||
int p1; /* First operand */
|
||||
int p2; /* Second parameter (often the jump destination) */
|
||||
char *p3; /* Third parameter */
|
||||
int p3type; /* P3_STATIC, P3_DYNAMIC or P3_POINTER */
|
||||
#ifdef VDBE_PROFILE
|
||||
int cnt; /* Number of times this instruction was executed */
|
||||
long long cycles; /* Total time spend executing this instruction */
|
||||
#endif
|
||||
};
|
||||
typedef struct VdbeOp VdbeOp;
|
||||
|
||||
/*
|
||||
** Allowed values of VdbeOp.p3type
|
||||
*/
|
||||
#define P3_NOTUSED 0 /* The P3 parameter is not used */
|
||||
#define P3_DYNAMIC (-1) /* Pointer to a string obtained from sqliteMalloc() */
|
||||
#define P3_STATIC (-2) /* Pointer to a static string */
|
||||
#define P3_POINTER (-3) /* P3 is a pointer to some structure or object */
|
||||
|
||||
/*
|
||||
** The following macro converts a relative address in the p2 field
|
||||
** of a VdbeOp structure into a negative number so that
|
||||
** sqliteVdbeAddOpList() knows that the address is relative. Calling
|
||||
** the macro again restores the address.
|
||||
*/
|
||||
#define ADDR(X) (-1-(X))
|
||||
|
||||
/*
|
||||
** The makefile scans the vdbe.c source file and creates the "opcodes.h"
|
||||
** header file that defines a number for each opcode used by the VDBE.
|
||||
*/
|
||||
#include "opcodes.h"
|
||||
|
||||
/*
|
||||
** Prototypes for the VDBE interface. See comments on the implementation
|
||||
** for a description of what each of these routines does.
|
||||
*/
|
||||
Vdbe *sqliteVdbeCreate(sqlite*);
|
||||
void sqliteVdbeCreateCallback(Vdbe*, int*);
|
||||
int sqliteVdbeAddOp(Vdbe*,int,int,int);
|
||||
int sqliteVdbeAddOpList(Vdbe*, int nOp, VdbeOp const *aOp);
|
||||
void sqliteVdbeChangeP1(Vdbe*, int addr, int P1);
|
||||
void sqliteVdbeChangeP2(Vdbe*, int addr, int P2);
|
||||
void sqliteVdbeChangeP3(Vdbe*, int addr, const char *zP1, int N);
|
||||
void sqliteVdbeDequoteP3(Vdbe*, int addr);
|
||||
int sqliteVdbeFindOp(Vdbe*, int, int);
|
||||
VdbeOp *sqliteVdbeGetOp(Vdbe*, int);
|
||||
int sqliteVdbeMakeLabel(Vdbe*);
|
||||
void sqliteVdbeDelete(Vdbe*);
|
||||
void sqliteVdbeMakeReady(Vdbe*,sqlite_callback,void*,int);
|
||||
int sqliteVdbeExec(Vdbe*);
|
||||
int sqliteVdbeList(Vdbe*);
|
||||
int sqliteVdbeFinalize(Vdbe*,char**);
|
||||
void sqliteVdbeResolveLabel(Vdbe*, int);
|
||||
int sqliteVdbeCurrentAddr(Vdbe*);
|
||||
void sqliteVdbeTrace(Vdbe*,FILE*);
|
||||
void sqliteVdbeCompressSpace(Vdbe*,int);
|
||||
int sqliteVdbeReset(Vdbe*,char **,Vdbe**);
|
||||
|
||||
#endif
|
||||
1198
sqlitebrowser/sqlitebrowser/sqlite_source/where.c
Executable file
37
sqlitebrowser/sqlitebrowser/sqlitebrowser.pro
Executable file
@@ -0,0 +1,37 @@
|
||||
SOURCES += browsermain.cpp \
|
||||
sqlitedb.cpp
|
||||
HEADERS += sqlitedb.h
|
||||
unix {
|
||||
UI_DIR = .ui
|
||||
MOC_DIR = .moc
|
||||
OBJECTS_DIR = .obj
|
||||
LIBS += ./sqlite_source/libsqlite_source.a
|
||||
}
|
||||
win32 {
|
||||
RC_FILE = winapp.rc
|
||||
LIBS += sqlite_source/sqlite_source.lib
|
||||
}
|
||||
mac {
|
||||
RC_FILE = macapp.icns
|
||||
LIBS += -framework Carbon -framework Quicktime -lz ./sqlite_source/libsqlite_source.a
|
||||
}
|
||||
|
||||
|
||||
FORMS = form1.ui \
|
||||
findform.ui \
|
||||
createtableform.ui \
|
||||
createindexform.ui \
|
||||
deletetableform.ui \
|
||||
deleteindexform.ui \
|
||||
aboutform.ui
|
||||
IMAGES = images/whatsthis.png \
|
||||
images/filenew.png \
|
||||
images/editpaste.png \
|
||||
images/fileopen.png \
|
||||
images/icone16.png \
|
||||
images/searchfind.png \
|
||||
images/editcopy.png
|
||||
|
||||
TEMPLATE =app
|
||||
CONFIG += qt warn_on release
|
||||
LANGUAGE = C++
|
||||
504
sqlitebrowser/sqlitebrowser/sqlitedb.cpp
Executable file
@@ -0,0 +1,504 @@
|
||||
/*
|
||||
** This file is part of SQLite Database Browser
|
||||
** http://sqlitebrowser.sourceforge.net
|
||||
**
|
||||
** Originally developed by Mauricio Piacentini, Tabuleiro
|
||||
**
|
||||
** The author disclaims copyright to this source code.
|
||||
** Consult the LICENSING file for known restrictions
|
||||
**
|
||||
*/
|
||||
|
||||
#include "sqlitedb.h"
|
||||
#include <stdlib.h>
|
||||
#include <qregexp.h>
|
||||
|
||||
|
||||
void DBBrowserTable::addField(int order, const QString& wfield,const QString& wtype)
|
||||
{
|
||||
QString num;
|
||||
num.setNum(order);
|
||||
fldmap[num] = DBBrowserField(wfield,wtype);
|
||||
}
|
||||
|
||||
bool DBBrowserDB::open ( const QString & db)
|
||||
{
|
||||
char *errmsg;
|
||||
bool ok=false;
|
||||
|
||||
if (isOpen()) close();
|
||||
|
||||
lastErrorMessage = QString("no error");
|
||||
|
||||
_db = sqlite_open(db.latin1(), 0666, &errmsg);
|
||||
if( _db==0 ){
|
||||
_db = sqlite_open(db.latin1(), 0444, &errmsg);
|
||||
if( _db==0 ){
|
||||
if( errmsg ){
|
||||
lastErrorMessage = QString(errmsg);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (_db){
|
||||
if (SQLITE_OK==sqlite_exec(_db,"PRAGMA empty_result_callbacks = ON;",
|
||||
NULL,NULL,NULL)){
|
||||
if (SQLITE_OK==sqlite_exec(_db,"PRAGMA show_datatypes = ON;",
|
||||
NULL,NULL,NULL)){
|
||||
ok=true;
|
||||
}
|
||||
curDBFilename = db;
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool DBBrowserDB::create ( const QString & db)
|
||||
{
|
||||
char *errmsg;
|
||||
bool ok=false;
|
||||
|
||||
if (isOpen()) close();
|
||||
|
||||
lastErrorMessage = QString("no error");
|
||||
|
||||
_db = sqlite_open(db.latin1(), 0666, &errmsg);
|
||||
if( _db==0 ){
|
||||
if( errmsg ){
|
||||
lastErrorMessage = QString(errmsg);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_db){
|
||||
if (SQLITE_OK==sqlite_exec(_db,"PRAGMA empty_result_callbacks = ON;",
|
||||
NULL,NULL,NULL)){
|
||||
if (SQLITE_OK==sqlite_exec(_db,"PRAGMA show_datatypes = ON;",
|
||||
NULL,NULL,NULL)){
|
||||
ok=true;
|
||||
}
|
||||
curDBFilename = db;
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
void DBBrowserDB::close (){
|
||||
if (_db)
|
||||
sqlite_close(_db);
|
||||
_db = 0;
|
||||
idxmap.clear();
|
||||
tbmap.clear();
|
||||
idmap.clear();
|
||||
browseRecs.clear();
|
||||
browseFields.clear();
|
||||
hasValidBrowseSet = false;
|
||||
}
|
||||
|
||||
bool DBBrowserDB::compact ( )
|
||||
{
|
||||
char *errmsg;
|
||||
bool ok=false;
|
||||
|
||||
if (!isOpen()) return false;
|
||||
|
||||
if (_db){
|
||||
if (SQLITE_OK==sqlite_exec(_db,"VACUUM;",
|
||||
NULL,NULL,&errmsg)){
|
||||
ok=true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ok){
|
||||
lastErrorMessage = QString(errmsg);
|
||||
return false;
|
||||
}else{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool DBBrowserDB::executeSQL ( const QString & statement)
|
||||
{
|
||||
char *errmsg;
|
||||
bool ok=false;
|
||||
|
||||
if (!isOpen()) return false;
|
||||
|
||||
if (_db){
|
||||
if (SQLITE_OK==sqlite_exec(_db,statement.latin1(),
|
||||
NULL,NULL,&errmsg)){
|
||||
ok=true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ok){
|
||||
lastErrorMessage = QString(errmsg);
|
||||
return false;
|
||||
}else{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool DBBrowserDB::addRecord ( )
|
||||
{
|
||||
char *errmsg;
|
||||
if (!hasValidBrowseSet) return false;
|
||||
if (!isOpen()) return false;
|
||||
bool ok = false;
|
||||
QString statement = "INSERT INTO ";
|
||||
statement.append(curBrowseTableName);
|
||||
statement.append("(");
|
||||
statement.append(browseFields.first());
|
||||
statement.append(") VALUES(NULL);");
|
||||
lastErrorMessage = QString("no error");
|
||||
if (_db){
|
||||
if (SQLITE_OK==sqlite_exec(_db,statement.latin1(),NULL,NULL, &errmsg)){
|
||||
ok=true;
|
||||
int newrowid = sqlite_last_insert_rowid(_db);
|
||||
//append to recordlist
|
||||
QStringList newRec;
|
||||
QString strowid;
|
||||
strowid.setNum(newrowid);
|
||||
newRec << strowid;
|
||||
for (uint a=0; a< browseFields.count();a++){
|
||||
newRec << "";
|
||||
}
|
||||
browseRecs.append(newRec);
|
||||
//insert rowid into idmap, mapping to record in browseFields (0 based)
|
||||
idmap.insert(newrowid,browseRecs.count()-1);
|
||||
} else {
|
||||
lastErrorMessage = QString(errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool DBBrowserDB::deleteRecord( int wrow)
|
||||
{
|
||||
char * errmsg;
|
||||
if (!hasValidBrowseSet) return false;
|
||||
if (!isOpen()) return false;
|
||||
bool ok = false;
|
||||
rowList tab = browseRecs;
|
||||
rowList::iterator rt = tab.at(wrow);
|
||||
QString rowid = (*rt).first();
|
||||
lastErrorMessage = QString("no error");
|
||||
|
||||
QString statement = "DELETE FROM ";
|
||||
statement.append(curBrowseTableName.latin1());
|
||||
statement.append(" WHERE rowid=");
|
||||
statement.append(rowid.latin1());
|
||||
statement.append(";");
|
||||
|
||||
if (_db){
|
||||
if (SQLITE_OK==sqlite_exec(_db,statement.latin1(),
|
||||
NULL,NULL,&errmsg)){
|
||||
ok=true;
|
||||
} else {
|
||||
lastErrorMessage = QString(errmsg);
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool DBBrowserDB::updateRecord(int wrow, int wcol, const QString & wtext)
|
||||
{
|
||||
char * errmsg;
|
||||
if (!hasValidBrowseSet) return false;
|
||||
if (!isOpen()) return false;
|
||||
bool ok = false;
|
||||
|
||||
lastErrorMessage = QString("no error");
|
||||
|
||||
rowList::iterator rt = browseRecs.at(wrow);
|
||||
QString rowid = (*rt).first();
|
||||
QStringList::Iterator cv = (*rt).at(wcol+1);//must account for rowid
|
||||
//qDebug(*cv);
|
||||
#ifdef ARCABUILD
|
||||
if((*cv).compare(binarySignature)==0)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
QStringList::Iterator ct = browseFields.at(wcol);
|
||||
|
||||
QString statement = "UPDATE ";
|
||||
statement.append(curBrowseTableName.latin1());
|
||||
statement.append(" SET ");
|
||||
statement.append(*ct);
|
||||
statement.append("=");
|
||||
|
||||
char * formSQL = sqlite_mprintf("%Q",wtext.latin1());
|
||||
statement.append(formSQL);
|
||||
if (formSQL) sqlite_freemem(formSQL);
|
||||
|
||||
statement.append(" WHERE rowid=");
|
||||
statement.append(rowid.latin1());
|
||||
statement.append(";");
|
||||
|
||||
if (_db){
|
||||
if (SQLITE_OK==sqlite_exec(_db,statement.latin1(),
|
||||
NULL,NULL,&errmsg)){
|
||||
ok=true;
|
||||
/*update local copy*/
|
||||
(*cv) = wtext.latin1();
|
||||
} else {
|
||||
lastErrorMessage = QString(errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*rowIdMap::iterator mit = idmap.find(rowid.toInt());
|
||||
qDebug("column with rowid %s is %d",rowid.latin1(),*mit);*/
|
||||
return ok;
|
||||
|
||||
}
|
||||
|
||||
bool DBBrowserDB::browseTable( const QString & tablename )
|
||||
{
|
||||
QStringList testFields = getTableFields( tablename );
|
||||
|
||||
if (testFields.count()>0) {//table exists
|
||||
getTableRecords( tablename );
|
||||
browseFields = testFields;
|
||||
hasValidBrowseSet = true;
|
||||
curBrowseTableName = tablename;
|
||||
} else {
|
||||
hasValidBrowseSet = false;
|
||||
curBrowseTableName = QString(" ");
|
||||
browseFields.clear();
|
||||
browseRecs.clear();
|
||||
idmap.clear();
|
||||
}
|
||||
return hasValidBrowseSet;
|
||||
}
|
||||
|
||||
void DBBrowserDB::getTableRecords( const QString & tablename )
|
||||
{
|
||||
sqlite_vm *vm;
|
||||
const char *tail;
|
||||
const char **vals;
|
||||
const char **names;
|
||||
|
||||
int ncol;
|
||||
QStringList r;
|
||||
// char *errmsg;
|
||||
int err=0;
|
||||
// int tabnum = 0;
|
||||
browseRecs.clear();
|
||||
idmap.clear();
|
||||
lastErrorMessage = QString("no error");
|
||||
|
||||
QString statement = "SELECT rowid, * FROM ";
|
||||
statement.append( tablename.latin1());
|
||||
statement.append(" ORDER BY rowid; ");
|
||||
//qDebug(statement);
|
||||
err=sqlite_compile(_db,statement.latin1(),
|
||||
&tail, &vm, NULL);
|
||||
if (err == SQLITE_OK){
|
||||
int rownum = 0;
|
||||
while ( sqlite_step(vm,&ncol,&vals, &names) == SQLITE_ROW ){
|
||||
r.clear();
|
||||
for (int e=0; e<ncol; e++){
|
||||
#ifdef ARCABUILD
|
||||
if (QString(vals[e]).startsWith(binaryPattern)){
|
||||
r<< binarySignature;
|
||||
} else {
|
||||
r << vals[e];
|
||||
}
|
||||
#else
|
||||
r << vals[e];
|
||||
#endif
|
||||
if (e==0){
|
||||
idmap.insert(QString(vals[e]).toInt(),rownum);
|
||||
rownum++;
|
||||
}
|
||||
}
|
||||
browseRecs.append(r);
|
||||
}
|
||||
|
||||
sqlite_finalize(vm, NULL);
|
||||
}else{
|
||||
lastErrorMessage = QString ("could not get fields");
|
||||
}
|
||||
}
|
||||
|
||||
resultMap DBBrowserDB::getFindResults( const QString & wstatement)
|
||||
{
|
||||
sqlite_vm *vm;
|
||||
const char *tail;
|
||||
const char **vals;
|
||||
const char **names;
|
||||
|
||||
int ncol;
|
||||
|
||||
char *errmsg;
|
||||
int err=0;
|
||||
resultMap res;
|
||||
lastErrorMessage = QString("no error");
|
||||
|
||||
err=sqlite_compile(_db,wstatement.latin1(),
|
||||
&tail, &vm, &errmsg);
|
||||
if (err == SQLITE_OK){
|
||||
int rownum = 0;
|
||||
int recnum = 0;
|
||||
QString r;
|
||||
while ( sqlite_step(vm,&ncol,&vals, &names) == SQLITE_ROW ){
|
||||
for (int e=0; e<ncol; e++){
|
||||
#ifdef ARCABUILD
|
||||
if (QString(vals[e]).startsWith(binaryPattern)){
|
||||
r = binarySignature;
|
||||
} else {
|
||||
r = vals[e];
|
||||
}
|
||||
#else
|
||||
r = vals[e];
|
||||
#endif
|
||||
if (e==0){
|
||||
rownum = QString(vals[e]).toInt();
|
||||
rowIdMap::iterator mit = idmap.find(rownum);
|
||||
recnum = *mit;
|
||||
}
|
||||
}
|
||||
res.insert(recnum, r);
|
||||
}
|
||||
|
||||
sqlite_finalize(vm, NULL);
|
||||
}else{
|
||||
lastErrorMessage = QString(errmsg);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
QStringList DBBrowserDB::getTableNames()
|
||||
{
|
||||
tableMap::Iterator it;
|
||||
tableMap tmap = tbmap;
|
||||
QStringList res;
|
||||
|
||||
for ( it = tmap.begin(); it != tmap.end(); ++it ) {
|
||||
res.append( it.data().getname() );
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
QStringList DBBrowserDB::getIndexNames()
|
||||
{
|
||||
indexMap::Iterator it;
|
||||
indexMap tmap = idxmap;
|
||||
QStringList res;
|
||||
|
||||
for ( it = tmap.begin(); it != tmap.end(); ++it ) {
|
||||
res.append( it.data().getname() );
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
QStringList DBBrowserDB::getTableFields(const QString & tablename)
|
||||
{
|
||||
tableMap::Iterator it;
|
||||
tableMap tmap = tbmap;
|
||||
QStringList res;
|
||||
|
||||
for ( it = tmap.begin(); it != tmap.end(); ++it ) {
|
||||
if (tablename.compare(it.data().getname())==0 ){
|
||||
fieldMap::Iterator fit;
|
||||
fieldMap fmap = it.data().fldmap;
|
||||
|
||||
for ( fit = fmap.begin(); fit != fmap.end(); ++fit ) {
|
||||
res.append( fit.data().getname() );
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int DBBrowserDB::getRecordCount()
|
||||
{
|
||||
return browseRecs.count();
|
||||
}
|
||||
void DBBrowserDB::updateSchema( )
|
||||
{
|
||||
// qDebug ("Getting list of tables");
|
||||
sqlite_vm *vm;
|
||||
const char *tail;
|
||||
const char **vals;
|
||||
const char **names;
|
||||
|
||||
int ncol;
|
||||
QStringList r;
|
||||
char *errmsg;
|
||||
int err=0;
|
||||
QString num;
|
||||
int idxnum =0;
|
||||
int tabnum = 0;
|
||||
|
||||
idxmap.clear();
|
||||
tbmap.clear();
|
||||
|
||||
lastErrorMessage = QString("no error");
|
||||
|
||||
err=sqlite_compile(_db,"SELECT name, sql "
|
||||
"FROM sqlite_master "
|
||||
"WHERE type='table' ;"
|
||||
/*"ORDER BY name;"*/ ,
|
||||
&tail, &vm, &errmsg);
|
||||
if (err == SQLITE_OK){
|
||||
|
||||
while ( sqlite_step(vm,&ncol,&vals, &names) == SQLITE_ROW ){
|
||||
num.setNum(tabnum);
|
||||
tbmap[num] = DBBrowserTable(vals[0],vals[1]);
|
||||
tabnum++;
|
||||
}
|
||||
sqlite_finalize(vm, NULL);
|
||||
}else{
|
||||
qDebug ("could not get list of tables: %d, %s",err,errmsg);
|
||||
}
|
||||
|
||||
//now get the field list for each table in tbmap
|
||||
tableMap::Iterator it;
|
||||
for ( it = tbmap.begin(); it != tbmap.end(); ++it ) {
|
||||
QString statement = "SELECT * FROM ";
|
||||
statement.append( (const char *) it.data().getname().latin1());
|
||||
statement.append(" LIMIT 1;");
|
||||
err=sqlite_compile(_db,statement.latin1(),
|
||||
&tail, &vm, NULL);
|
||||
if (err == SQLITE_OK){
|
||||
err = sqlite_step(vm,&ncol,&vals, &names);
|
||||
if ((err==SQLITE_ROW)||(err==SQLITE_DONE)){
|
||||
it.data(). fldmap.clear();
|
||||
for (int e=0; e<ncol; e++)
|
||||
it.data().addField(e,names[e],names[ncol+e]);
|
||||
}
|
||||
|
||||
sqlite_finalize(vm, NULL);
|
||||
}else{
|
||||
lastErrorMessage = QString ("could not get types");
|
||||
}
|
||||
}
|
||||
|
||||
//finally get indices
|
||||
err=sqlite_compile(_db,"SELECT name, sql "
|
||||
"FROM sqlite_master "
|
||||
"WHERE type='index' "
|
||||
/*"ORDER BY name;"*/,
|
||||
&tail, &vm, &errmsg);
|
||||
if (err == SQLITE_OK){
|
||||
while ( sqlite_step(vm,&ncol,&vals, &names) == SQLITE_ROW ){
|
||||
num.setNum(idxnum);
|
||||
idxmap[num] = DBBrowserIndex(vals[0],vals[1]);
|
||||
idxnum ++;
|
||||
}
|
||||
sqlite_finalize(vm, NULL);
|
||||
}else{
|
||||
lastErrorMessage = QString ("could not get list of indices");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
128
sqlitebrowser/sqlitebrowser/sqlitedb.h
Executable file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
** This file is part of SQLite Database Browser
|
||||
** http://sqlitebrowser.sourceforge.net
|
||||
**
|
||||
** Originally developed by Mauricio Piacentini, Tabuleiro
|
||||
**
|
||||
** The author disclaims copyright to this source code.
|
||||
** Consult the LICENSING file for known restrictions
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SQLITEDB_H
|
||||
#define SQLITEDB_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <qstringlist.h>
|
||||
#include <qmap.h>
|
||||
#include <qvaluelist.h>
|
||||
#include "sqlite_source/sqlite.h"
|
||||
|
||||
//#define ARCABUILD
|
||||
|
||||
#ifdef ARCABUILD
|
||||
static QString binarySignature = QString("<BINARY DATA>");
|
||||
static QString binaryPattern = QString("BINARYENCODED");
|
||||
static QString applicationName = QString("Arca Database Browser");
|
||||
static QString aboutText = QString("Version 1.0\n\nArca Database Browser is a visual tool used to create, design and edit Arca database files.\n\nFor more information on Arca Database Xtra please visit our site at: http://xtras.tabuleiro.com\n\nArca Database Browser Copyright Tabuleiro Producoes Ltda 2003\nXtra is a trademark of Macromedia, Inc.");
|
||||
#else
|
||||
static QString applicationName = QString("SQLite Database Browser");
|
||||
static QString aboutText = QString("Version 1.0\n\nSQLite Database Browser is a freeware, public domain, open source visual tool used to create, design and edit database files compatible with SQLite 2.x.\n\nIt has been developed originally by Mauricio Piacentini from Tabuleiro Producoes, as the Arca Database Browser. The original code is used with the Arca Database Xtra, a commercial product that uses SQLite databases with some additional extensions for handling of compressed and binary data. \n\n A generic version of the code was also produced to be compatible with standard SQLite 2.1 databases, and was released into Public Domain by the original author. \n\n You can find the latest versions of the source code of SQLite Database browser at http://sqlitebrowser.sourceforge.net. \n\nIn the spirit of the original SQLite source code, the author disclaims copyright to this source code. Enjoy!");
|
||||
#endif
|
||||
|
||||
|
||||
typedef QMap<QString, class DBBrowserField> fieldMap;
|
||||
typedef QMap<QString, class DBBrowserTable> tableMap;
|
||||
typedef QMap<QString, class DBBrowserIndex> indexMap;
|
||||
typedef QMap<int, int> rowIdMap;
|
||||
|
||||
typedef QValueList<QStringList> rowList;
|
||||
typedef QMap<int, QString> resultMap;
|
||||
|
||||
class DBBrowserField
|
||||
{
|
||||
public:
|
||||
DBBrowserField() : name( 0 ) { }
|
||||
DBBrowserField( const QString& wname,const QString& wtype )
|
||||
: name( wname), type( wtype )
|
||||
{ }
|
||||
QString getname() const { return name; }
|
||||
QString gettype() const { return type; }
|
||||
private:
|
||||
QString name;
|
||||
QString type;
|
||||
};
|
||||
|
||||
class DBBrowserIndex
|
||||
{
|
||||
public:
|
||||
DBBrowserIndex() : name( 0 ) { }
|
||||
DBBrowserIndex( const QString& wname,const QString& wsql )
|
||||
: name( wname), sql( wsql )
|
||||
{ }
|
||||
QString getname() const { return name; }
|
||||
QString getsql() const { return sql; }
|
||||
private:
|
||||
QString name;
|
||||
QString sql;
|
||||
};
|
||||
|
||||
|
||||
class DBBrowserTable
|
||||
{
|
||||
public:
|
||||
DBBrowserTable() : name( 0 ) { }
|
||||
DBBrowserTable( const QString& wname,const QString& wsql )
|
||||
: name( wname), sql( wsql )
|
||||
{ }
|
||||
|
||||
void addField(int order, const QString& wfield,const QString& wtype);
|
||||
|
||||
QString getname() const { return name; }
|
||||
QString getsql() const { return sql; }
|
||||
fieldMap fldmap;
|
||||
private:
|
||||
QString name;
|
||||
QString sql;
|
||||
};
|
||||
|
||||
|
||||
class DBBrowserDB
|
||||
{
|
||||
public:
|
||||
DBBrowserDB (): _db( 0 ) , hasValidBrowseSet(false) {}
|
||||
~DBBrowserDB (){}
|
||||
bool open ( const QString & db);
|
||||
bool create ( const QString & db);
|
||||
void close ();
|
||||
bool compact ();
|
||||
bool executeSQL ( const QString & statement);
|
||||
void updateSchema() ;
|
||||
bool addRecord();
|
||||
bool deleteRecord(int wrow);
|
||||
bool updateRecord(int wrow, int wcol, const QString & wtext);
|
||||
bool browseTable( const QString & tablename );
|
||||
QStringList getTableFields(const QString & tablename);
|
||||
QStringList getTableNames();
|
||||
QStringList getIndexNames();
|
||||
resultMap getFindResults( const QString & wstatement);
|
||||
int getRecordCount();
|
||||
bool isOpen() const { return _db!=0; }
|
||||
sqlite * _db;
|
||||
|
||||
tableMap tbmap;
|
||||
indexMap idxmap;
|
||||
rowIdMap idmap;
|
||||
|
||||
rowList browseRecs;
|
||||
QStringList browseFields;
|
||||
bool hasValidBrowseSet;
|
||||
QString curBrowseTableName;
|
||||
QString lastErrorMessage;
|
||||
QString curDBFilename;
|
||||
private:
|
||||
void getTableRecords( const QString & tablename );
|
||||
};
|
||||
|
||||
#endif
|
||||
1
sqlitebrowser/sqlitebrowser/winapp.rc
Executable file
@@ -0,0 +1 @@
|
||||
IDI_ICON1 ICON DISCARDABLE "iconwin.ico"
|
||||
5
sqlitebrowser/sqlitedbbrowser.pro
Executable file
@@ -0,0 +1,5 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS = sqlitebrowser/sqlite_source \
|
||||
sqlitebrowser
|
||||
|
||||