libgda r3205 - in trunk: . doc/C doc/C/tmpl libgda libgda/sqlite/sqlite-src libgda/sqlite/virtual tests/value-holders
- From: vivien svn gnome org
- To: svn-commits-list gnome org
- Subject: libgda r3205 - in trunk: . doc/C doc/C/tmpl libgda libgda/sqlite/sqlite-src libgda/sqlite/virtual tests/value-holders
- Date: Mon, 15 Sep 2008 18:30:06 +0000 (UTC)
Author: vivien
Date: Mon Sep 15 18:30:05 2008
New Revision: 3205
URL: http://svn.gnome.org/viewvc/libgda?rev=3205&view=rev
Log:
2008-09-15 Vivien Malerba <malerba gnome-db org>
* doc/C: documentation improvements regarding data validation and programmer's control
* libgda/gda-holder.[ch]: renamed the "before-change" signal to "validate-change" (and changed
its return value)
* libgda/gda-set.[ch]:
- changed the "before-holder-change" to "validate_holder_change"(and changed
its return value)
- added a "validate-set" signal
- the gda_set_is_valid() method has been changed to emit the "validate-set" signal
before it returns a value.
* libgda/gda-data-model-iter.[ch]: removed the "row-to-change" signal (it now uses the
GdaSet::validate-set signal), and the gda_data_model_iter_can_be_moved() method
* libgda/gda-server-operation.c:
* libgda/gda-data-model.c: adaptations to the API Changes
* libgda/gda-data-proxy.[ch]:
- adaptations to the API Changes,
- renamed the "pre-changes-applied" signal to "validate-row-changes" (and changed
its return value)
- renamed the "post-changes-applied" signal to "row-changes-applied"
- bug correction in gda_data_proxy_get_n_rows()
* libgda/gda-marshal.list: new signals' signatures
* libgda/gda-data-comparator.c: added assertions
* libgda/gda-connection.c: don't remove items from a GHashTable while in g_hash_table_foreach(),
this is probably the reason for bug #545979
* libgda/gda-value.c: corrected the gda_value_differ() function
* libgda/sqlite/virtual/gda-vprovider-data-model.c: corrected the virtualNext() method which
moves SQLite's internal cursor forward
* libgda/gda-data-model-iter.[ch]: renamed gda_data_model_iter_set_at_row() to
gda_data_model_iter_move_at_row()
* libgda/Makefile.am:
* libgda/gda-data-model-iter-extra.h: new file
* libgda/gda-data-model-extra.h:
* libgda/gda-data-model-private.h:
* libgda/gda-data-model-import.c:
* libgda/gda-data-model.c:
* libgda/gda-data-proxy.c:
* libgda/gda-data-select.c:
* libgda/sqlite/virtual/virtual-test.c:
- moved the code which implements the GdaDataModelIter
movements (forward, backward and to a row) to the gda-data-model-iter.c file
- renamed gda_data_model_move_iter_at_row_default() to gda_data_model_iter_move_at_row_default()
- renamed gda_data_model_move_iter_next_default() to gda_data_model_iter_move_next_default()
- renamed gda_data_model_move_iter_prev_default() to gda_data_model_iter_move_prev_default()
* libgda/sqlite/sqlite-src/: up to version 3.6.2
* tests/value-holders/: adaptations to the API Changes
Added:
trunk/doc/C/data_validation.xml
trunk/doc/C/data_validation_holder.dia (contents, props changed)
trunk/doc/C/data_validation_holder.png (contents, props changed)
trunk/doc/C/data_validation_proxy.dia (contents, props changed)
trunk/doc/C/data_validation_proxy.png (contents, props changed)
trunk/doc/C/data_validation_set.dia (contents, props changed)
trunk/doc/C/data_validation_set.png (contents, props changed)
trunk/libgda/gda-data-model-iter-extra.h
Modified:
trunk/ChangeLog
trunk/NEWS
trunk/doc/C/Makefile.am
trunk/doc/C/howto.xml
trunk/doc/C/libgda-4.0-docs.sgml
trunk/doc/C/libgda-4.0-sections.txt
trunk/doc/C/tmpl/gda-data-model-iter.sgml
trunk/doc/C/tmpl/gda-data-proxy.sgml
trunk/doc/C/tmpl/gda-holder.sgml
trunk/doc/C/tmpl/gda-set.sgml
trunk/doc/C/tmpl/gda-value.sgml
trunk/libgda/Makefile.am
trunk/libgda/gda-connection.c
trunk/libgda/gda-data-comparator.c
trunk/libgda/gda-data-model-extra.h
trunk/libgda/gda-data-model-import.c
trunk/libgda/gda-data-model-iter.c
trunk/libgda/gda-data-model-iter.h
trunk/libgda/gda-data-model-private.h
trunk/libgda/gda-data-model.c
trunk/libgda/gda-data-proxy.c
trunk/libgda/gda-data-proxy.h
trunk/libgda/gda-data-select.c
trunk/libgda/gda-holder.c
trunk/libgda/gda-holder.h
trunk/libgda/gda-marshal.list
trunk/libgda/gda-server-operation.c
trunk/libgda/gda-set.c
trunk/libgda/gda-set.h
trunk/libgda/gda-value.c
trunk/libgda/sqlite/sqlite-src/PragmasPatch
trunk/libgda/sqlite/sqlite-src/sqlite3.c
trunk/libgda/sqlite/sqlite-src/sqlite3.h
trunk/libgda/sqlite/virtual/gda-vprovider-data-model.c
trunk/libgda/sqlite/virtual/virtual-test.c
trunk/tests/value-holders/check_holder.c
trunk/tests/value-holders/check_set.c
Modified: trunk/NEWS
==============================================================================
--- trunk/NEWS (original)
+++ trunk/NEWS Mon Sep 15 18:30:05 2008
@@ -1,5 +1,5 @@
- - bug fixes: #549953
+ - bug fixes: #549953, #535633, #551058
libgda 3.99.3, 2008-07-06
Modified: trunk/doc/C/Makefile.am
==============================================================================
--- trunk/doc/C/Makefile.am (original)
+++ trunk/doc/C/Makefile.am Mon Sep 15 18:30:05 2008
@@ -50,7 +50,8 @@
architecture.png parts.png stmt-unknown.png stmt-select.png stmt-insert1.png stmt-insert2.png \
stmt-update.png stmt-compound.png information_schema.png \
MetaStore1.png MetaStore2.png i_s_data_types.png \
- writable_data_model.png GdaDataModelIter.png
+ writable_data_model.png GdaDataModelIter.png \
+ data_validation_holder.png data_validation_proxy.png data_validation_set.png
# Extra options to supply to gtkdoc-fixref
FIXXREF_OPTIONS=
@@ -60,7 +61,7 @@
# Other files to distribute
EXTRA_DIST += examples/full_example.c installation.xml limitations.xml migration.xml migration2.xml \
server-operation.xml gettingstarted.xml virtual.xml virtual-notice.xml store-meta-type.xml \
- prov-writing.xml i_s_doc.xml howto.xml gda-sql-manual.xml \
+ prov-writing.xml i_s_doc.xml howto.xml gda-sql-manual.xml data_validation.xml \
DataModels.svg \
architecture.svg parts.svg stmt-unknown.svg stmt-select.svg stmt-insert1.svg stmt-insert2.svg \
stmt-update.svg stmt-compound.svg information_schema.svg
Added: trunk/doc/C/data_validation.xml
==============================================================================
--- (empty file)
+++ trunk/doc/C/data_validation.xml Mon Sep 15 18:30:05 2008
@@ -0,0 +1,128 @@
+<sect1 id="data-validation">
+ <title>Custom data validation</title>
+ <para>
+ &LIBGDA; allows the programmer to specify some rules of his own to control data changes (for example
+ business rules). This section details the various control points, how to implement them, and how or when
+ they are invoked.
+ </para>
+
+ <sect2 id="data-validation-GdaHolder">
+ <title>GdaHolder controls</title>
+ <para>
+ The <link linkend="GdaHolder">GdaHolder</link> object holds a single value
+ (as a <link linkend="GValue">GValue</link>). When that value is to be modified, using for example
+ <link linkend="gda-holder-set-value">gda_holder_set_value()</link>,
+ then the proposed new value's validation process is executed:
+ <orderedlist>
+ <listitem><para>it is determined if the proposed new value is of the correct type and
+ if it respects the holder's policy (for example a NULL value won't be accepted if
+ the holder can't be NULL). If the proposed value does not respect the policy, then
+ the value change will be rejected</para></listitem>
+ <listitem><para>the <link linkend="GdaHolder-validate-change">"validate-change"</link> signal is
+ emitted. If any handler for this signal returns a pointer to a filled <link linkend="GError">GError</link>
+ structure, then the signal's propagation is stopped and the value change will be rejected.</para></listitem>
+ </orderedlist>
+ </para>
+ <para>
+ An example illustrating how to use the <link linkend="GdaHolder-validate-change">"validate-change"</link> signal
+ is:
+ <programlisting>
+static GError *
+my_validate_change_cb (GdaHolder *h, const GValue *value, gpointer data)
+{
+ GError *error = NULL;
+
+ /* for example check that value is inferior to 5 and not NULL */
+ if (gda_value_is_null (value))
+ g_set_error (&error, 0, 0, "NULL values are not allowed!");
+ else if (g_value_get_int (value) >= 5)
+ g_set_error (&error, 0, 0, "Value sould be inferior to 5");
+
+ return error;
+}
+
+{
+ GdaHolder *holder;
+ GError *error = NULL;
+ [...]
+ g_signal_connect (G_OBJECT (holder), "validate-change",
+ G_CALLBACK (my_validate_change_cb), NULL);
+ if (! gda_holder_set_value (holder, value, &error)) {
+ g_print ("Error: %s\n", error->message);
+ g_error_free (error);
+ [...]
+ }
+}
+ </programlisting>
+ </para>
+ <para>
+ <mediaobject>
+ <imageobject role="html">
+ <imagedata fileref="data_validation_holder.png" format="PNG" contentwidth="100mm"/>
+ </imageobject>
+ <textobject>
+ <phrase>GdaHolder's value change control</phrase>
+ </textobject>
+ </mediaobject>
+ </para>
+ </sect2>
+
+ <sect2 id="data-validation-GdaSet">
+ <title>GdaSet controls</title>
+ <para>
+ The <link linkend="GdaSet">GdaSet</link> object is an ordered list (or vector) of values,
+ each represented by a <link linkend="GdaHolder">GdaHolder</link> object. One can place controls
+ at two key events:
+ <itemizedlist>
+ <listitem><para>
+ When any value of a <link linkend="GdaHolder">GdaHolder</link> changes, where
+ the <link linkend="GdaSet-validate-holder-change">"validate-holder-change"</link> signal is
+ emitted. If any handler for this signal returns a pointer to a filled <link linkend="GError">GError</link>
+ structure, then the signal's propagation is stopped and the value change will be rejected. This
+ key event allows one to control each holder's value change as they occur.
+ </para></listitem>
+ <listitem><para>
+ When the <link linkend="gda-set-is-valid">gda_set_is_valid()</link> method is called, where
+ the <link linkend="GdaSet-validate-set">"validate-set"</link> signal is
+ emitted. If any handler for this signal returns a pointer to a filled <link linkend="GError">GError</link>
+ structure, then the signal's propagation is stopped and the calling method will return the generated
+ error. This key event allows one to control the validity of a set of values altogether (for example
+ before writing to a table).
+ </para></listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ <mediaobject>
+ <imageobject role="html">
+ <imagedata fileref="data_validation_set.png" format="PNG" contentwidth="180mm"/>
+ </imageobject>
+ <textobject>
+ <phrase>GdaSet's changes controls</phrase>
+ </textobject>
+ </mediaobject>
+ </para>
+ </sect2>
+
+ <sect2 id="data-validation-GdaDataProxy">
+ <title>GdaDataProxy controls</title>
+ <para>
+ The <link linkend="GdaDataProxy">GdaDataProxy</link> data model allows one to store temporary modifications
+ to a data model, and then apply (write to the proxied data model) those modifications row by row. Before
+ applying any row modification, the <link linkend="GdaDataProxy">GdaDataProxy</link> data model emits
+ the <link linkend="GdaDataProxy::validate-row-changes">"validate-row-changes"</link> signal, and
+ if handler for this signal returns a pointer to a filled <link linkend="GError">GError</link>
+ structure, then the signal's propagation is stopped and the row's modifications are not applied.
+ </para>
+ <para>
+ <mediaobject>
+ <imageobject role="html">
+ <imagedata fileref="data_validation_proxy.png" format="PNG" contentwidth="100mm"/>
+ </imageobject>
+ <textobject>
+ <phrase>GdaDataProxy's changes controls</phrase>
+ </textobject>
+ </mediaobject>
+ </para>
+ </sect2>
+
+</sect1>
Added: trunk/doc/C/data_validation_holder.dia
==============================================================================
Binary file. No diff available.
Added: trunk/doc/C/data_validation_holder.png
==============================================================================
Binary file. No diff available.
Added: trunk/doc/C/data_validation_proxy.dia
==============================================================================
Binary file. No diff available.
Added: trunk/doc/C/data_validation_proxy.png
==============================================================================
Binary file. No diff available.
Added: trunk/doc/C/data_validation_set.dia
==============================================================================
Binary file. No diff available.
Added: trunk/doc/C/data_validation_set.png
==============================================================================
Binary file. No diff available.
Modified: trunk/doc/C/howto.xml
==============================================================================
--- trunk/doc/C/howto.xml (original)
+++ trunk/doc/C/howto.xml Mon Sep 15 18:30:05 2008
@@ -431,5 +431,16 @@
</para>
</sect1>
+ <sect1>
+ <title>Control value's assignment to various objects</title>
+ <para>
+ &LIBGDA; has builtin support to allow the programmer to control what values are acceptable
+ for some objects which hold values (namely the <link linkend="GdaHolder">GdaHolder</link>,
+ <link linkend="GdaSet">GdaSet</link> and <link linkend="GdaDataProxy">GdaDataProxy</link> objects).
+ For more information about this topic, see the <link linkend="data-validation">custom data validation</link>
+ section.
+ </para>
+ </sect1>
+
</chapter>
Modified: trunk/doc/C/libgda-4.0-docs.sgml
==============================================================================
--- trunk/doc/C/libgda-4.0-docs.sgml (original)
+++ trunk/doc/C/libgda-4.0-docs.sgml Mon Sep 15 18:30:05 2008
@@ -38,6 +38,7 @@
<!ENTITY examples SYSTEM "gettingstarted.xml">
<!ENTITY migration SYSTEM "migration.xml">
<!ENTITY migration2 SYSTEM "migration2.xml">
+<!ENTITY data-validation SYSTEM "data_validation.xml">
<!ENTITY limitations SYSTEM "limitations.xml">
<!ENTITY libgda-GdaCommand SYSTEM "xml/gda-command.xml">
<!ENTITY libgda-config SYSTEM "xml/gda-config.xml">
@@ -569,6 +570,7 @@
</itemizedlist>
</para>
&libgda-GValue;
+ &data-validation;
&libgda-GdaBlobOp;
&libgda-GdaDataModel;
&libgda-GdaColumn;
Modified: trunk/doc/C/libgda-4.0-sections.txt
==============================================================================
--- trunk/doc/C/libgda-4.0-sections.txt (original)
+++ trunk/doc/C/libgda-4.0-sections.txt Mon Sep 15 18:30:05 2008
@@ -377,7 +377,7 @@
gda_data_model_iter_get_value_at
gda_data_model_iter_get_value_for_field
gda_data_model_iter_is_valid
-gda_data_model_iter_set_at_row
+gda_data_model_iter_move_at_row
gda_data_model_iter_move_next
gda_data_model_iter_move_prev
gda_data_model_iter_get_row
Modified: trunk/doc/C/tmpl/gda-data-model-iter.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-data-model-iter.sgml (original)
+++ trunk/doc/C/tmpl/gda-data-model-iter.sgml Mon Sep 15 18:30:05 2008
@@ -33,7 +33,7 @@
gda_data_model_iter_move_next() which moves to the first row).
</para>
<para>
- The gda_data_model_iter_set_at_row() method, if the iterator can be moved both forward and backwards, can move the
+ The gda_data_model_iter_move_at_row() method, if the iterator can be moved both forward and backwards, can move the
iterator to a specific row (sometimes faster than moving it forward or backwards a number of times).
</para>
<para>
@@ -77,15 +77,6 @@
@gdadatamodeliter: the object which received the signal.
@arg1:
-<!-- ##### SIGNAL GdaDataModelIter::row-to-change ##### -->
-<para>
-
-</para>
-
- gdadatamodeliter: the object which received the signal.
- arg1:
- Returns:
-
<!-- ##### ARG GdaDataModelIter:current-row ##### -->
<para>
@@ -135,7 +126,7 @@
@Returns:
-<!-- ##### FUNCTION gda_data_model_iter_set_at_row ##### -->
+<!-- ##### FUNCTION gda_data_model_iter_move_at_row ##### -->
<para>
</para>
Modified: trunk/doc/C/tmpl/gda-data-proxy.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-data-proxy.sgml (original)
+++ trunk/doc/C/tmpl/gda-data-proxy.sgml Mon Sep 15 18:30:05 2008
@@ -57,7 +57,7 @@
@gdadataproxy: the object which received the signal.
-<!-- ##### SIGNAL GdaDataProxy::post-changes-applied ##### -->
+<!-- ##### SIGNAL GdaDataProxy::row-changes-applied ##### -->
<para>
</para>
@@ -66,7 +66,7 @@
@arg1:
@arg2:
-<!-- ##### SIGNAL GdaDataProxy::pre-changes-applied ##### -->
+<!-- ##### SIGNAL GdaDataProxy::row-delete-changed ##### -->
<para>
</para>
@@ -74,9 +74,8 @@
@gdadataproxy: the object which received the signal.
@arg1:
@arg2:
- Returns:
-<!-- ##### SIGNAL GdaDataProxy::row-delete-changed ##### -->
+<!-- ##### SIGNAL GdaDataProxy::sample-changed ##### -->
<para>
</para>
@@ -85,22 +84,23 @@
@arg1:
@arg2:
-<!-- ##### SIGNAL GdaDataProxy::sample-changed ##### -->
+<!-- ##### SIGNAL GdaDataProxy::sample-size-changed ##### -->
<para>
</para>
@gdadataproxy: the object which received the signal.
@arg1:
- arg2:
-<!-- ##### SIGNAL GdaDataProxy::sample-size-changed ##### -->
+<!-- ##### SIGNAL GdaDataProxy::validate-row-changes ##### -->
<para>
</para>
@gdadataproxy: the object which received the signal.
@arg1:
+ arg2:
+ Returns:
<!-- ##### ARG GdaDataProxy:defer-sync ##### -->
<para>
@@ -131,8 +131,8 @@
@row_delete_changed:
@sample_size_changed:
@sample_changed:
- pre_changes_applied:
- post_changes_applied:
+ validate_row_changes:
+ row_changes_applied:
<!-- ##### STRUCT GdaDataProxyPrivate ##### -->
<para>
Modified: trunk/doc/C/tmpl/gda-holder.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-holder.sgml (original)
+++ trunk/doc/C/tmpl/gda-holder.sgml Mon Sep 15 18:30:05 2008
@@ -24,28 +24,28 @@
</para>
-<!-- ##### SIGNAL GdaHolder::before-change ##### -->
+<!-- ##### SIGNAL GdaHolder::changed ##### -->
<para>
</para>
@gdaholder: the object which received the signal.
- arg1:
- Returns:
-<!-- ##### SIGNAL GdaHolder::changed ##### -->
+<!-- ##### SIGNAL GdaHolder::source-changed ##### -->
<para>
</para>
@gdaholder: the object which received the signal.
-<!-- ##### SIGNAL GdaHolder::source-changed ##### -->
+<!-- ##### SIGNAL GdaHolder::validate-change ##### -->
<para>
</para>
@gdaholder: the object which received the signal.
+ arg1:
+ Returns:
<!-- ##### ARG GdaHolder:description ##### -->
<para>
Modified: trunk/doc/C/tmpl/gda-set.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-set.sgml (original)
+++ trunk/doc/C/tmpl/gda-set.sgml Mon Sep 15 18:30:05 2008
@@ -29,17 +29,15 @@
</para>
-<!-- ##### SIGNAL GdaSet::before-holder-change ##### -->
+<!-- ##### SIGNAL GdaSet::holder-attr-changed ##### -->
<para>
-
+
</para>
@gdaset: the object which received the signal.
@arg1:
- arg2:
- Returns:
-<!-- ##### SIGNAL GdaSet::holder-attr-changed ##### -->
+<!-- ##### SIGNAL GdaSet::holder-changed ##### -->
<para>
</para>
@@ -47,7 +45,7 @@
@gdaset: the object which received the signal.
@arg1:
-<!-- ##### SIGNAL GdaSet::holder-changed ##### -->
+<!-- ##### SIGNAL GdaSet::holder-plugin-changed ##### -->
<para>
</para>
@@ -55,20 +53,30 @@
@gdaset: the object which received the signal.
@arg1:
-<!-- ##### SIGNAL GdaSet::holder-plugin-changed ##### -->
+<!-- ##### SIGNAL GdaSet::public-data-changed ##### -->
+<para>
+
+</para>
+
+ gdaset: the object which received the signal.
+
+<!-- ##### SIGNAL GdaSet::validate-holder-change ##### -->
<para>
</para>
@gdaset: the object which received the signal.
@arg1:
+ arg2:
+ Returns:
-<!-- ##### SIGNAL GdaSet::public-data-changed ##### -->
+<!-- ##### SIGNAL GdaSet::validate-set ##### -->
<para>
</para>
@gdaset: the object which received the signal.
+ Returns:
<!-- ##### ARG GdaSet:description ##### -->
<para>
@@ -144,6 +152,7 @@
</para>
@set:
+ error:
@Returns:
Modified: trunk/doc/C/tmpl/gda-value.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-value.sgml (original)
+++ trunk/doc/C/tmpl/gda-value.sgml Mon Sep 15 18:30:05 2008
@@ -1,8 +1,8 @@
<!-- ##### SECTION Title ##### -->
-Gda Value
+A single Value
<!-- ##### SECTION Short_Description ##### -->
-Single value stored in a #GdaDataModel
+Assorted functions for dealing with #GValue values.
<!-- ##### SECTION Long_Description ##### -->
<para>
Modified: trunk/libgda/Makefile.am
==============================================================================
--- trunk/libgda/Makefile.am (original)
+++ trunk/libgda/Makefile.am Mon Sep 15 18:30:05 2008
@@ -41,6 +41,7 @@
gda-data-model-extra.h \
gda-data-model-import.h \
gda-data-model-iter.h \
+ gda-data-model-iter-extra.h \
gda-data-model-private.h \
gda-data-model-query.h \
gda-data-access-wrapper.h \
Modified: trunk/libgda/gda-connection.c
==============================================================================
--- trunk/libgda/gda-connection.c (original)
+++ trunk/libgda/gda-connection.c Mon Sep 15 18:30:05 2008
@@ -246,9 +246,10 @@
cnc->priv->auto_clear_events_list = TRUE;
cnc->priv->events_list = NULL;
cnc->priv->trans_status = NULL; /* no transaction yet */
+ cnc->priv->prepared_stmts = NULL;
}
-static void prepared_stms_foreach_func (GdaStatement *gda_stmt, GdaStatement *prepared_stmt, GdaConnection *cnc);
+static void prepared_stms_foreach_func (GdaStatement *gda_stmt, GdaPStmt *prepared_stmt, GdaConnection *cnc);
static void
gda_connection_dispose (GObject *object)
{
@@ -894,10 +895,11 @@
g_return_if_fail (GDA_IS_CONNECTION (cnc));
g_return_if_fail (cnc->priv);
- if (! cnc->priv->is_open)
- return;
-
gda_connection_lock ((GdaLockable*) cnc);
+ if (! cnc->priv->is_open) {
+ gda_connection_unlock ((GdaLockable*) cnc);
+ return;
+ }
/* get rid of prepared statements to avoid problems */
if (cnc->priv->prepared_stmts) {
@@ -3673,15 +3675,15 @@
* Prepared statements handling
*/
-static void prepared_stms_stmt_destroyed_cb (GdaStatement *gda_stmt, GdaConnection *cnc);
+static void prepared_stmts_stmt_reset_cb (GdaStatement *gda_stmt, GdaConnection *cnc);
static void statement_weak_notify_cb (GdaConnection *cnc, GdaStatement *stmt);
static void
-prepared_stms_stmt_destroyed_cb (GdaStatement *gda_stmt, GdaConnection *cnc)
+prepared_stmts_stmt_reset_cb (GdaStatement *gda_stmt, GdaConnection *cnc)
{
gda_connection_lock ((GdaLockable*) cnc);
- g_signal_handlers_disconnect_by_func (gda_stmt, G_CALLBACK (prepared_stms_stmt_destroyed_cb), cnc);
+ g_signal_handlers_disconnect_by_func (gda_stmt, G_CALLBACK (prepared_stmts_stmt_reset_cb), cnc);
g_object_weak_unref (G_OBJECT (gda_stmt), (GWeakNotify) statement_weak_notify_cb, cnc);
g_assert (cnc->priv->prepared_stmts);
g_hash_table_remove (cnc->priv->prepared_stmts, gda_stmt);
@@ -3690,6 +3692,13 @@
}
static void
+prepared_stms_foreach_func (GdaStatement *gda_stmt, GdaPStmt *prepared_stmt, GdaConnection *cnc)
+{
+ g_signal_handlers_disconnect_by_func (gda_stmt, G_CALLBACK (prepared_stmts_stmt_reset_cb), cnc);
+ g_object_weak_unref (G_OBJECT (gda_stmt), (GWeakNotify) statement_weak_notify_cb, cnc);
+}
+
+static void
statement_weak_notify_cb (GdaConnection *cnc, GdaStatement *stmt)
{
gda_connection_lock ((GdaLockable*) cnc);
@@ -3718,6 +3727,7 @@
{
g_return_if_fail (GDA_IS_CONNECTION (cnc));
g_return_if_fail (cnc->priv);
+ g_return_if_fail (GDA_IS_STATEMENT (gda_stmt));
g_return_if_fail (GDA_IS_PSTMT (prepared_stmt));
gda_connection_lock ((GdaLockable*) cnc);
@@ -3726,10 +3736,11 @@
cnc->priv->prepared_stmts = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
g_hash_table_remove (cnc->priv->prepared_stmts, gda_stmt);
g_hash_table_insert (cnc->priv->prepared_stmts, gda_stmt, prepared_stmt);
+ g_object_ref (prepared_stmt);
/* destroy the prepared statement if gda_stmt is destroyed, or changes */
g_object_weak_ref (G_OBJECT (gda_stmt), (GWeakNotify) statement_weak_notify_cb, cnc);
- g_signal_connect (G_OBJECT (gda_stmt), "reset", G_CALLBACK (prepared_stms_stmt_destroyed_cb), cnc);
+ g_signal_connect (G_OBJECT (gda_stmt), "reset", G_CALLBACK (prepared_stmts_stmt_reset_cb), cnc);
gda_connection_unlock ((GdaLockable*) cnc);
}
@@ -3777,17 +3788,10 @@
gda_connection_lock ((GdaLockable*) cnc);
g_return_if_fail (GDA_IS_CONNECTION (cnc));
if (gda_connection_get_prepared_statement (cnc, gda_stmt))
- prepared_stms_stmt_destroyed_cb (gda_stmt, cnc);
+ prepared_stmts_stmt_reset_cb (gda_stmt, cnc);
gda_connection_unlock ((GdaLockable*) cnc);
}
-static void
-prepared_stms_foreach_func (GdaStatement *gda_stmt, GdaStatement *prepared_stmt, GdaConnection *cnc)
-{
- prepared_stms_stmt_destroyed_cb (gda_stmt, cnc);
-}
-
-
/*
* Provider's specific connection data management
*/
Modified: trunk/libgda/gda-data-comparator.c
==============================================================================
--- trunk/libgda/gda-data-comparator.c (original)
+++ trunk/libgda/gda-data-comparator.c Mon Sep 15 18:30:05 2008
@@ -478,7 +478,9 @@
/* actual differences computations : rows to insert / update */
onrows = gda_data_model_get_n_rows (comp->priv->old_model);
+ g_assert (onrows >= 0);
nnrows = gda_data_model_get_n_rows (comp->priv->new_model);
+ g_assert (nnrows >= 0);
rows_to_del = g_new (gboolean, onrows);
memset (rows_to_del, TRUE, sizeof (gboolean) * onrows);
for (i = 0; i < nnrows; i++) {
Modified: trunk/libgda/gda-data-model-extra.h
==============================================================================
--- trunk/libgda/gda-data-model-extra.h (original)
+++ trunk/libgda/gda-data-model-extra.h Mon Sep 15 18:30:05 2008
@@ -35,9 +35,6 @@
void gda_data_model_row_removed (GdaDataModel *model, gint row);
void gda_data_model_reset (GdaDataModel *model);
-gboolean gda_data_model_move_iter_at_row_default (GdaDataModel *model, GdaDataModelIter *iter, gint row);
-gboolean gda_data_model_move_iter_next_default (GdaDataModel *model, GdaDataModelIter *iter);
-gboolean gda_data_model_move_iter_prev_default (GdaDataModel *model, GdaDataModelIter *iter);
G_END_DECLS
#endif
Modified: trunk/libgda/gda-data-model-import.c
==============================================================================
--- trunk/libgda/gda-data-model-import.c (original)
+++ trunk/libgda/gda-data-model-import.c Mon Sep 15 18:30:05 2008
@@ -46,6 +46,7 @@
#include <libgda/gda-data-model-extra.h>
#include <libgda/gda-data-access-wrapper.h>
#include <libgda/gda-data-model-iter.h>
+#include <libgda/gda-data-model-iter-extra.h>
#include <libgda/gda-holder.h>
#include <libgda/gda-set.h>
#include <libgda/gda-data-model-private.h> /* For gda_data_model_add_data_from_xml_node() */
@@ -1811,7 +1812,7 @@
/* if there is a random access model, then use it */
if (imodel->priv->format == FORMAT_XML_NODE)
- return gda_data_model_move_iter_next_default (model, iter);
+ return gda_data_model_iter_move_next_default (model, iter);
/* fetch the next row if necessary */
switch (imodel->priv->format) {
@@ -1907,7 +1908,7 @@
/* if there is a random access model, then use it */
if (imodel->priv->format == FORMAT_XML_NODE)
- return gda_data_model_move_iter_prev_default (model, iter);
+ return gda_data_model_iter_move_prev_default (model, iter);
/* fetch the previous row if necessary */
switch (imodel->priv->format) {
Added: trunk/libgda/gda-data-model-iter-extra.h
==============================================================================
--- (empty file)
+++ trunk/libgda/gda-data-model-iter-extra.h Mon Sep 15 18:30:05 2008
@@ -0,0 +1,35 @@
+/* gda-data-model-iter-extra.h
+ *
+ * Copyright (C) 2008 Vivien Malerba
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __GDA_DATA_MODEL_ITER_EXTRA_H_
+#define __GDA_DATA_MODEL_ITER_EXTRA_H_
+
+#include "gda-data-model-iter.h"
+
+G_BEGIN_DECLS
+
+gboolean gda_data_model_iter_move_at_row_default (GdaDataModel *model, GdaDataModelIter *iter, gint row);
+gboolean gda_data_model_iter_move_next_default (GdaDataModel *model, GdaDataModelIter *iter);
+gboolean gda_data_model_iter_move_prev_default (GdaDataModel *model, GdaDataModelIter *iter);
+
+G_END_DECLS
+
+#endif
Modified: trunk/libgda/gda-data-model-iter.c
==============================================================================
--- trunk/libgda/gda-data-model-iter.c (original)
+++ trunk/libgda/gda-data-model-iter.c Mon Sep 15 18:30:05 2008
@@ -21,6 +21,7 @@
#include <glib/gi18n-lib.h>
#include <string.h>
#include "gda-data-model-iter.h"
+#include "gda-data-model-iter-extra.h"
#include "gda-data-model.h"
#include "gda-data-model-private.h"
#include "gda-holder.h"
@@ -50,7 +51,7 @@
static void model_row_updated_cb (GdaDataModel *model, gint row, GdaDataModelIter *iter);
static void model_row_removed_cb (GdaDataModel *model, gint row, GdaDataModelIter *iter);
-static GError *before_holder_change_cb (GdaSet *paramlist, GdaHolder *param, const GValue *new_value);
+static GError *validate_holder_change_cb (GdaSet *paramlist, GdaHolder *param, const GValue *new_value);
static void holder_attr_changed_cb (GdaSet *paramlist, GdaHolder *param);
/* get a pointer to the parents to be able to cvalue their destructor */
@@ -59,13 +60,12 @@
/* signals */
enum
{
- ROW_TO_CHANGE,
ROW_CHANGED,
END_OF_DATA,
LAST_SIGNAL
};
-static gint gda_data_model_iter_signals[LAST_SIGNAL] = { 0, 0, 0 };
+static gint gda_data_model_iter_signals[LAST_SIGNAL] = { 0, 0 };
/* properties */
enum
@@ -124,26 +124,6 @@
return type;
}
-static gboolean
-row_to_change_accumulator (GSignalInvocationHint *ihint,
- GValue *return_accu,
- const GValue *handler_return,
- gpointer data)
-{
- gboolean thisvalue;
-
- thisvalue = g_value_get_boolean (handler_return);
- g_value_set_boolean (return_accu, thisvalue);
-
- return thisvalue; /* stop signal if 'thisvalue' is FALSE */
-}
-
-static gboolean
-m_row_to_change (GdaDataModelIter *iter, gint row)
-{
- return TRUE; /* defaults allows row to change */
-}
-
static void
gda_data_model_iter_class_init (GdaDataModelIterClass *class)
{
@@ -152,13 +132,6 @@
parent_class = g_type_class_peek_parent (class);
- gda_data_model_iter_signals [ROW_TO_CHANGE] =
- g_signal_new ("row-to-change",
- G_TYPE_FROM_CLASS (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GdaDataModelIterClass, row_to_change),
- row_to_change_accumulator, NULL,
- gda_marshal_BOOLEAN__INT, G_TYPE_BOOLEAN, 1, G_TYPE_INT);
gda_data_model_iter_signals [ROW_CHANGED] =
g_signal_new ("row-changed",
G_TYPE_FROM_CLASS (object_class),
@@ -174,13 +147,12 @@
NULL, NULL,
g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
- class->row_to_change = m_row_to_change;
class->row_changed = NULL;
class->end_of_data = NULL;
object_class->dispose = gda_data_model_iter_dispose;
object_class->finalize = gda_data_model_iter_finalize;
- paramlist_class->before_holder_change = before_holder_change_cb;
+ paramlist_class->validate_holder_change = validate_holder_change_cb;
paramlist_class->holder_attr_changed = holder_attr_changed_cb;
/* Properties */
@@ -224,7 +196,7 @@
/* sync parameters with the new values in row */
if (iter->priv->row == row) {
iter->priv->keep_param_changes = TRUE;
- gda_data_model_move_iter_at_row (iter->priv->data_model, iter, row);
+ gda_data_model_iter_move_at_row (iter, row);
iter->priv->keep_param_changes = FALSE;
}
}
@@ -240,7 +212,7 @@
* then make all the parameters invalid */
if (iter->priv->row == row) {
gda_data_model_iter_invalidate_contents (iter);
- gda_data_model_iter_set_at_row (iter, -1);
+ gda_data_model_iter_move_at_row (iter, -1);
}
else {
/* shift iter's row by one to keep good numbers */
@@ -255,7 +227,7 @@
* paramlist is an iter for, return an error if the data model could not be modified
*/
static GError *
-before_holder_change_cb (GdaSet *paramlist, GdaHolder *param, const GValue *new_value)
+validate_holder_change_cb (GdaSet *paramlist, GdaHolder *param, const GValue *new_value)
{
GdaDataModelIter *iter;
gint col;
@@ -290,9 +262,9 @@
g_signal_handler_unblock (iter->priv->data_model, iter->priv->model_changes_signals [1]);
}
- if (!error && ((GdaSetClass *) parent_class)->before_holder_change)
+ if (!error && ((GdaSetClass *) parent_class)->validate_holder_change)
/* for the parent class */
- return ((GdaSetClass *) parent_class)->before_holder_change (paramlist, param, new_value);
+ return ((GdaSetClass *) parent_class)->validate_holder_change (paramlist, param, new_value);
return error;
}
@@ -590,36 +562,7 @@
}
/**
- * gda_data_model_iter_can_be_moved
- * @iter: a #GdaDataModelIter object
- *
- * Tells if @iter can point to another row. Note the @iter by itself will not refuse
- * a row change, but that the row change may be refused by another object using
- * @iter.
- *
- * Returns: TRUE if the row represented by @iter can be changed
- */
-gboolean
-gda_data_model_iter_can_be_moved (GdaDataModelIter *iter)
-{
- gboolean move_ok = TRUE;
-
- g_return_val_if_fail (GDA_IS_DATA_MODEL_ITER (iter), FALSE);
- g_return_val_if_fail (iter->priv, FALSE);
-
- if (!gda_data_model_iter_is_valid (iter))
- return TRUE;
-
- /* optionally validate the row change */
- g_signal_emit (G_OBJECT (iter),
- gda_data_model_iter_signals[ROW_TO_CHANGE],
- 0, iter->priv->row, &move_ok);
-
- return move_ok;
-}
-
-/**
- * gda_data_model_iter_set_at_row
+ * gda_data_model_iter_move_at_row
* @iter: a #GdaDataModelIter object
* @row: the row to set @iter to
*
@@ -634,7 +577,7 @@
* Returns: TRUE if no error occurred
*/
gboolean
-gda_data_model_iter_set_at_row (GdaDataModelIter *iter, gint row)
+gda_data_model_iter_move_at_row (GdaDataModelIter *iter, gint row)
{
g_return_val_if_fail (GDA_IS_DATA_MODEL_ITER (iter), FALSE);
g_return_val_if_fail (iter->priv, FALSE);
@@ -648,11 +591,87 @@
}
return TRUE;
}
- else
- return gda_data_model_move_iter_at_row (iter->priv->data_model, iter, row);
+ else {
+ GdaDataModel *model;
+ if ((gda_data_model_iter_get_row (iter) >= 0) &&
+ (gda_data_model_iter_get_row (iter) != row) &&
+ ! gda_set_is_valid ((GdaSet*) iter, NULL))
+ return FALSE;
+
+ model = iter->priv->data_model;
+ if (GDA_DATA_MODEL_GET_CLASS (model)->i_iter_at_row)
+ return (GDA_DATA_MODEL_GET_CLASS (model)->i_iter_at_row) (model, iter, row);
+ else {
+ /* default method */
+ return gda_data_model_iter_move_at_row_default (model, iter, row);
+ }
+ }
+}
+
+static void
+set_param_attributes (GdaHolder *holder, GdaValueAttribute flags)
+{
+ if (flags & GDA_VALUE_ATTR_IS_DEFAULT)
+ gda_holder_set_value_to_default (holder);
+
+ if (flags & GDA_VALUE_ATTR_IS_NULL)
+ gda_holder_set_value (holder, NULL, NULL);
+ if (flags & GDA_VALUE_ATTR_DATA_NON_VALID)
+ gda_holder_force_invalid (holder);
}
/**
+ * gda_data_model_iter_move_at_row_default
+ */
+gboolean
+gda_data_model_iter_move_at_row_default (GdaDataModel *model, GdaDataModelIter *iter, gint row)
+{
+ /* default method */
+ GSList *list;
+ gint col;
+ GdaDataModel *test;
+ gboolean update_model;
+
+ g_return_val_if_fail (GDA_IS_DATA_MODEL (model), FALSE);
+
+ if (! (gda_data_model_get_access_flags (model) & GDA_DATA_MODEL_ACCESS_RANDOM))
+ return FALSE;
+
+ /* validity tests */
+ if ((row < 0) || (row >= gda_data_model_get_n_rows (model))) {
+ gda_data_model_iter_invalidate_contents (iter);
+ g_object_set (G_OBJECT (iter), "current-row", -1, NULL);
+ return FALSE;
+ }
+
+ g_return_val_if_fail (GDA_IS_DATA_MODEL_ITER (iter), FALSE);
+
+ g_object_get (G_OBJECT (iter), "data-model", &test, NULL);
+ g_return_val_if_fail (test == model, FALSE);
+ g_object_unref (test);
+
+ /* actual sync. */
+ g_object_get (G_OBJECT (iter), "update-model", &update_model, NULL);
+ g_object_set (G_OBJECT (iter), "update-model", FALSE, NULL);
+ for (col = 0, list = ((GdaSet *) iter)->holders; list; col++, list = list->next) {
+ const GValue *cvalue;
+ cvalue = gda_data_model_get_value_at (model, col, row, NULL);
+ if (!cvalue ||
+ !gda_holder_set_value ((GdaHolder*) list->data, cvalue, NULL)) {
+ g_object_set (G_OBJECT (iter), "current-row", row,
+ "update-model", update_model, NULL);
+ gda_data_model_iter_invalidate_contents (iter);
+ return FALSE;
+ }
+ set_param_attributes ((GdaHolder*) list->data,
+ gda_data_model_get_attributes_at (model, col, row));
+ }
+ g_object_set (G_OBJECT (iter), "current-row", row, "update-model", update_model, NULL);
+ return TRUE;
+}
+
+
+/**
* gda_data_model_iter_move_next
* @iter: a #GdaDataModelIter object
*
@@ -671,10 +690,71 @@
gboolean
gda_data_model_iter_move_next (GdaDataModelIter *iter)
{
+ GdaDataModel *model;
g_return_val_if_fail (GDA_IS_DATA_MODEL_ITER (iter), FALSE);
g_return_val_if_fail (iter->priv, FALSE);
- return gda_data_model_move_iter_next (iter->priv->data_model, iter);
+ if ((gda_data_model_iter_get_row (iter) >= 0) &&
+ ! gda_set_is_valid ((GdaSet*) iter, NULL))
+ return FALSE;
+
+ model = iter->priv->data_model;
+ if (GDA_DATA_MODEL_GET_CLASS (model)->i_iter_next)
+ return (GDA_DATA_MODEL_GET_CLASS (model)->i_iter_next) (model, iter);
+ else
+ /* default method */
+ return gda_data_model_iter_move_next_default (model, iter);
+}
+
+/**
+ * gda_data_model_iter_move_next_default
+ */
+gboolean
+gda_data_model_iter_move_next_default (GdaDataModel *model, GdaDataModelIter *iter)
+{
+ GSList *list;
+ gint col;
+ gint row;
+ GdaDataModel *test;
+ gboolean update_model;
+
+ /* validity tests */
+ if (! (gda_data_model_get_access_flags (model) & GDA_DATA_MODEL_ACCESS_RANDOM))
+ return FALSE;
+
+ g_return_val_if_fail (GDA_IS_DATA_MODEL_ITER (iter), FALSE);
+
+ g_object_get (G_OBJECT (iter), "data-model", &test, NULL);
+ g_return_val_if_fail (test == model, FALSE);
+ g_object_unref (test);
+
+ g_object_get (G_OBJECT (iter), "current-row", &row, NULL);
+ row++;
+ if (row >= gda_data_model_get_n_rows (model)) {
+ gda_data_model_iter_invalidate_contents (iter);
+ g_object_set (G_OBJECT (iter), "current-row", -1, NULL);
+ return FALSE;
+ }
+
+ /* actual sync. */
+ g_object_get (G_OBJECT (iter), "update-model", &update_model, NULL);
+ g_object_set (G_OBJECT (iter), "update-model", FALSE, NULL);
+ for (col = 0, list = ((GdaSet *) iter)->holders; list; col++, list = list->next) {
+ const GValue *cvalue;
+ cvalue = gda_data_model_get_value_at (model, col, row, NULL);
+ if (!cvalue ||
+ !gda_holder_set_value ((GdaHolder *) list->data, cvalue, NULL)) {
+ g_object_set (G_OBJECT (iter), "current-row", row,
+ "update-model", update_model, NULL);
+ gda_data_model_iter_invalidate_contents (iter);
+ return FALSE;
+ }
+ set_param_attributes ((GdaHolder *) list->data,
+ gda_data_model_get_attributes_at (model, col, row));
+ }
+ g_object_set (G_OBJECT (iter), "current-row", row,
+ "update-model", update_model, NULL);
+ return TRUE;
}
/**
@@ -696,13 +776,76 @@
gboolean
gda_data_model_iter_move_prev (GdaDataModelIter *iter)
{
+ GdaDataModel *model;
+
g_return_val_if_fail (GDA_IS_DATA_MODEL_ITER (iter), FALSE);
g_return_val_if_fail (iter->priv, FALSE);
- return gda_data_model_move_iter_prev (iter->priv->data_model, iter);
+ if ((gda_data_model_iter_get_row (iter) >= 0) &&
+ ! gda_set_is_valid ((GdaSet*) iter, NULL))
+ return FALSE;
+
+ model = iter->priv->data_model;
+ if (GDA_DATA_MODEL_GET_CLASS (model)->i_iter_prev)
+ return (GDA_DATA_MODEL_GET_CLASS (model)->i_iter_prev) (model, iter);
+ else
+ /* default method */
+ return gda_data_model_iter_move_prev_default (model, iter);
}
/**
+ * gda_data_model_iter_move_prev_default
+ */
+gboolean
+gda_data_model_iter_move_prev_default (GdaDataModel *model, GdaDataModelIter *iter)
+{
+ GSList *list;
+ gint col;
+ gint row;
+ GdaDataModel *test;
+ gboolean update_model;
+
+ /* validity tests */
+ if (! (gda_data_model_get_access_flags (model) & GDA_DATA_MODEL_ACCESS_RANDOM))
+ return FALSE;
+
+ g_return_val_if_fail (GDA_IS_DATA_MODEL_ITER (iter), FALSE);
+
+ g_object_get (G_OBJECT (iter), "data-model", &test, NULL);
+ g_return_val_if_fail (test == model, FALSE);
+ g_object_unref (test);
+
+ g_object_get (G_OBJECT (iter), "current-row", &row, NULL);
+ row--;
+ if (row < 0) {
+ gda_data_model_iter_invalidate_contents (iter);
+ g_object_set (G_OBJECT (iter), "current-row", -1, NULL);
+ return FALSE;
+ }
+
+ /* actual sync. */
+ g_object_get (G_OBJECT (iter), "update-model", &update_model, NULL);
+ g_object_set (G_OBJECT (iter), "update-model", FALSE, NULL);
+ for (col = 0, list = ((GdaSet *) iter)->holders; list; col++, list = list->next) {
+ const GValue *cvalue;
+ cvalue = gda_data_model_get_value_at (model, col, row, NULL);
+ if (!cvalue ||
+ !gda_holder_set_value ((GdaHolder*) list->data, cvalue, NULL)) {
+ g_object_set (G_OBJECT (iter), "current-row", row,
+ "update-model", update_model, NULL);
+ gda_data_model_iter_invalidate_contents (iter);
+ return FALSE;
+ }
+ set_param_attributes ((GdaHolder*) list->data,
+ gda_data_model_get_attributes_at (model, col, row));
+ }
+ g_object_set (G_OBJECT (iter), "current-row", row,
+ "update-model", update_model, NULL);
+ return TRUE;
+}
+
+
+/**
* gda_data_model_iter_get_row
* @iter: a #GdaDataModelIter object
*
Modified: trunk/libgda/gda-data-model-iter.h
==============================================================================
--- trunk/libgda/gda-data-model-iter.h (original)
+++ trunk/libgda/gda-data-model-iter.h Mon Sep 15 18:30:05 2008
@@ -55,7 +55,6 @@
{
GdaSetClass parent_class;
- gboolean (* row_to_change) (GdaDataModelIter *iter, gint row);
void (* row_changed) (GdaDataModelIter *iter, gint row);
void (* end_of_data) (GdaDataModelIter *iter);
};
@@ -65,12 +64,11 @@
const GValue *gda_data_model_iter_get_value_at (GdaDataModelIter *iter, gint col);
const GValue *gda_data_model_iter_get_value_for_field (GdaDataModelIter *iter, const gchar *field_name);
-gboolean gda_data_model_iter_set_at_row (GdaDataModelIter *iter, gint row);
+gboolean gda_data_model_iter_move_at_row (GdaDataModelIter *iter, gint row);
gboolean gda_data_model_iter_move_next (GdaDataModelIter *iter);
gboolean gda_data_model_iter_move_prev (GdaDataModelIter *iter);
gint gda_data_model_iter_get_row (GdaDataModelIter *iter);
-gboolean gda_data_model_iter_can_be_moved (GdaDataModelIter *iter);
void gda_data_model_iter_invalidate_contents (GdaDataModelIter *iter);
gboolean gda_data_model_iter_is_valid (GdaDataModelIter *iter);
Modified: trunk/libgda/gda-data-model-private.h
==============================================================================
--- trunk/libgda/gda-data-model-private.h (original)
+++ trunk/libgda/gda-data-model-private.h Mon Sep 15 18:30:05 2008
@@ -38,10 +38,6 @@
const gint *rows, gint nb_rows, const gchar *name);
gboolean gda_data_model_add_data_from_xml_node (GdaDataModel *model, xmlNodePtr node, GError **error);
-gboolean gda_data_model_move_iter_at_row (GdaDataModel *model, GdaDataModelIter *iter, gint row);
-gboolean gda_data_model_move_iter_next (GdaDataModel *model, GdaDataModelIter *iter);
-gboolean gda_data_model_move_iter_prev (GdaDataModel *model, GdaDataModelIter *iter);
-
G_END_DECLS
#endif
Modified: trunk/libgda/gda-data-model.c
==============================================================================
--- trunk/libgda/gda-data-model.c (original)
+++ trunk/libgda/gda-data-model.c Mon Sep 15 18:30:05 2008
@@ -751,9 +751,9 @@
* rows in @model.
*
* The row the returned #GdaDataModelIter represents is undefined. For models which can be accessed
- * randomly the correspoding row can be set using gda_data_model_move_iter_at_row(),
+ * randomly the correspoding row can be set using gda_data_model_iter_move_at_row(),
* and for models which are accessible sequentially only then the first row will be
- * fetched using gda_data_model_move_iter_next().
+ * fetched using gda_data_model_iter_move_next().
*
* Returns: a new #GdaDataModelIter object, or %NULL if an error occurred
*/
@@ -770,240 +770,6 @@
}
/**
- * gda_data_model_move_iter_at_row
- * @model: a #GdaDataModel object.
- * @iter: a #GdaDataModelIter object
- * @row: row number.
- *
- * Sets @iter to represent the @row row. @iter must be a valid iterator object obtained
- * using the gda_data_model_create_iter() method.
- *
- * Returns: TRUE if no error occurred
- */
-gboolean
-gda_data_model_move_iter_at_row (GdaDataModel *model, GdaDataModelIter *iter, gint row)
-{
- g_return_val_if_fail (GDA_IS_DATA_MODEL (model), FALSE);
-
- if ((gda_data_model_iter_get_row (iter) != row) && ! gda_data_model_iter_can_be_moved (iter))
- return FALSE;
-
- if (GDA_DATA_MODEL_GET_CLASS (model)->i_iter_at_row)
- return (GDA_DATA_MODEL_GET_CLASS (model)->i_iter_at_row) (model, iter, row);
- else
- /* default method */
- return gda_data_model_move_iter_at_row_default (model, iter, row);
-}
-
-static void set_param_attributes (GdaHolder *holder, GdaValueAttribute flags);
-
-gboolean
-gda_data_model_move_iter_at_row_default (GdaDataModel *model, GdaDataModelIter *iter, gint row)
-{
- /* default method */
- GSList *list;
- gint col;
- GdaDataModel *test;
- gboolean update_model;
-
- g_return_val_if_fail (GDA_IS_DATA_MODEL (model), FALSE);
-
- if (! (gda_data_model_get_access_flags (model) & GDA_DATA_MODEL_ACCESS_RANDOM))
- return FALSE;
-
- /* validity tests */
- if ((row < 0) || (row >= gda_data_model_get_n_rows (model))) {
- gda_data_model_iter_invalidate_contents (iter);
- g_object_set (G_OBJECT (iter), "current-row", -1, NULL);
- return FALSE;
- }
-
- g_return_val_if_fail (GDA_IS_DATA_MODEL_ITER (iter), FALSE);
-
- g_object_get (G_OBJECT (iter), "data-model", &test, NULL);
- g_return_val_if_fail (test == model, FALSE);
- g_object_unref (test);
-
- /* actual sync. */
- g_object_get (G_OBJECT (iter), "update-model", &update_model, NULL);
- g_object_set (G_OBJECT (iter), "update-model", FALSE, NULL);
- for (col = 0, list = ((GdaSet *) iter)->holders; list; col++, list = list->next) {
- const GValue *cvalue;
- cvalue = gda_data_model_get_value_at (model, col, row, NULL);
- if (!cvalue ||
- !gda_holder_set_value ((GdaHolder*) list->data, cvalue, NULL)) {
- g_object_set (G_OBJECT (iter), "current-row", row,
- "update-model", update_model, NULL);
- gda_data_model_iter_invalidate_contents (iter);
- return FALSE;
- }
- set_param_attributes ((GdaHolder*) list->data,
- gda_data_model_get_attributes_at (model, col, row));
- }
- g_object_set (G_OBJECT (iter), "current-row", row, "update-model", update_model, NULL);
- return TRUE;
-}
-
-static void
-set_param_attributes (GdaHolder *holder, GdaValueAttribute flags)
-{
- if (flags & GDA_VALUE_ATTR_IS_DEFAULT)
- gda_holder_set_value_to_default (holder);
-
- if (flags & GDA_VALUE_ATTR_IS_NULL)
- gda_holder_set_value (holder, NULL, NULL);
- if (flags & GDA_VALUE_ATTR_DATA_NON_VALID)
- gda_holder_force_invalid (holder);
-}
-
-/**
- * gda_data_model_move_iter_next
- * @model: a #GdaDataModel object.
- * @iter: a #GdaDataModelIter object
- *
- * Sets @iter to the next available row in @model. @iter must be a valid iterator object obtained
- * using the gda_data_model_create_iter() method.
- *
- * Returns: TRUE if no error occurred
- */
-gboolean
-gda_data_model_move_iter_next (GdaDataModel *model, GdaDataModelIter *iter)
-{
- g_return_val_if_fail (GDA_IS_DATA_MODEL (model), FALSE);
-
- if (! gda_data_model_iter_can_be_moved (iter))
- return FALSE;
-
- if (GDA_DATA_MODEL_GET_CLASS (model)->i_iter_next)
- return (GDA_DATA_MODEL_GET_CLASS (model)->i_iter_next) (model, iter);
- else
- /* default method */
- return gda_data_model_move_iter_next_default (model, iter);
-}
-
-gboolean
-gda_data_model_move_iter_next_default (GdaDataModel *model, GdaDataModelIter *iter)
-{
- GSList *list;
- gint col;
- gint row;
- GdaDataModel *test;
- gboolean update_model;
-
- /* validity tests */
- if (! (gda_data_model_get_access_flags (model) & GDA_DATA_MODEL_ACCESS_RANDOM))
- return FALSE;
-
- g_return_val_if_fail (GDA_IS_DATA_MODEL_ITER (iter), FALSE);
-
- g_object_get (G_OBJECT (iter), "data-model", &test, NULL);
- g_return_val_if_fail (test == model, FALSE);
- g_object_unref (test);
-
- g_object_get (G_OBJECT (iter), "current-row", &row, NULL);
- row++;
- if (row >= gda_data_model_get_n_rows (model)) {
- gda_data_model_iter_invalidate_contents (iter);
- g_object_set (G_OBJECT (iter), "current-row", -1, NULL);
- return FALSE;
- }
-
- /* actual sync. */
- g_object_get (G_OBJECT (iter), "update-model", &update_model, NULL);
- g_object_set (G_OBJECT (iter), "update-model", FALSE, NULL);
- for (col = 0, list = ((GdaSet *) iter)->holders; list; col++, list = list->next) {
- const GValue *cvalue;
- cvalue = gda_data_model_get_value_at (model, col, row, NULL);
- if (!cvalue ||
- !gda_holder_set_value ((GdaHolder *) list->data, cvalue, NULL)) {
- g_object_set (G_OBJECT (iter), "current-row", row,
- "update-model", update_model, NULL);
- gda_data_model_iter_invalidate_contents (iter);
- return FALSE;
- }
- set_param_attributes ((GdaHolder *) list->data,
- gda_data_model_get_attributes_at (model, col, row));
- }
- g_object_set (G_OBJECT (iter), "current-row", row,
- "update-model", update_model, NULL);
- return TRUE;
-}
-
-/**
- * gda_data_model_move_iter_prev
- * @model: a #GdaDataModel object.
- * @iter: a #GdaDataModelIter object
- *
- * Sets @iter to the previous available row in @model. @iter must be a valid iterator object obtained
- * using the gda_data_model_create_iter() method.
- *
- * Returns: TRUE if no error occurred
- */
-gboolean
-gda_data_model_move_iter_prev (GdaDataModel *model, GdaDataModelIter *iter)
-{
- g_return_val_if_fail (GDA_IS_DATA_MODEL (model), FALSE);
-
- if (! gda_data_model_iter_can_be_moved (iter))
- return FALSE;
-
- if (GDA_DATA_MODEL_GET_CLASS (model)->i_iter_prev)
- return (GDA_DATA_MODEL_GET_CLASS (model)->i_iter_prev) (model, iter);
- else
- /* default method */
- return gda_data_model_move_iter_prev_default (model, iter);
-}
-
-gboolean
-gda_data_model_move_iter_prev_default (GdaDataModel *model, GdaDataModelIter *iter)
-{
- GSList *list;
- gint col;
- gint row;
- GdaDataModel *test;
- gboolean update_model;
-
- /* validity tests */
- if (! (gda_data_model_get_access_flags (model) & GDA_DATA_MODEL_ACCESS_RANDOM))
- return FALSE;
-
- g_return_val_if_fail (GDA_IS_DATA_MODEL_ITER (iter), FALSE);
-
- g_object_get (G_OBJECT (iter), "data-model", &test, NULL);
- g_return_val_if_fail (test == model, FALSE);
- g_object_unref (test);
-
- g_object_get (G_OBJECT (iter), "current-row", &row, NULL);
- row--;
- if (row < 0) {
- gda_data_model_iter_invalidate_contents (iter);
- g_object_set (G_OBJECT (iter), "current-row", -1, NULL);
- return FALSE;
- }
-
- /* actual sync. */
- g_object_get (G_OBJECT (iter), "update-model", &update_model, NULL);
- g_object_set (G_OBJECT (iter), "update-model", FALSE, NULL);
- for (col = 0, list = ((GdaSet *) iter)->holders; list; col++, list = list->next) {
- const GValue *cvalue;
- cvalue = gda_data_model_get_value_at (model, col, row, NULL);
- if (!cvalue ||
- !gda_holder_set_value ((GdaHolder*) list->data, cvalue, NULL)) {
- g_object_set (G_OBJECT (iter), "current-row", row,
- "update-model", update_model, NULL);
- gda_data_model_iter_invalidate_contents (iter);
- return FALSE;
- }
- set_param_attributes ((GdaHolder*) list->data,
- gda_data_model_get_attributes_at (model, col, row));
- }
- g_object_set (G_OBJECT (iter), "current-row", row,
- "update-model", update_model, NULL);
- return TRUE;
-}
-
-
-/**
* gda_data_model_append_values
* @model: a #GdaDataModel object.
* @values: #GList of #GValue* representing the row to add. The
Modified: trunk/libgda/gda-data-proxy.c
==============================================================================
--- trunk/libgda/gda-data-proxy.c (original)
+++ trunk/libgda/gda-data-proxy.c Mon Sep 15 18:30:05 2008
@@ -97,8 +97,8 @@
ROW_DELETE_CHANGED,
SAMPLE_SIZE_CHANGED,
SAMPLE_CHANGED,
- PRE_CHANGES_APPLIED,
- POST_CHANGES_APPLIED,
+ VALIDATE_ROW_CHANGES,
+ ROW_CHANGES_APPLIED,
FILTER_CHANGED,
LAST_SIGNAL
};
@@ -513,23 +513,23 @@
}
static gboolean
-pre_changes_accumulator (GSignalInvocationHint *ihint,
- GValue *return_accu,
- const GValue *handler_return,
- gpointer data)
+validate_row_changes_accumulator (GSignalInvocationHint *ihint,
+ GValue *return_accu,
+ const GValue *handler_return,
+ gpointer data)
{
- gboolean thisvalue;
+ GError *error;
- thisvalue = g_value_get_boolean (handler_return);
- g_value_set_boolean (return_accu, thisvalue);
+ error = g_value_get_pointer (handler_return);
+ g_value_set_pointer (return_accu, error);
- return thisvalue; /* stop signal if 'thisvalue' is FALSE */
+ return error ? FALSE : TRUE; /* stop signal if 'thisvalue' is FALSE */
}
-static gboolean
-m_pre_changes_applied (GdaDataProxy *proxy, gint row, gint proxied_row)
+static GError *
+m_validate_row_changes (GdaDataProxy *proxy, gint row, gint proxied_row)
{
- return TRUE; /* defaults allows changes */
+ return NULL; /* defaults allows changes */
}
static void
@@ -561,18 +561,18 @@
G_STRUCT_OFFSET (GdaDataProxyClass, sample_changed),
NULL, NULL,
gda_marshal_VOID__INT_INT, G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
- gda_data_proxy_signals [PRE_CHANGES_APPLIED] =
- g_signal_new ("pre-changes-applied",
+ gda_data_proxy_signals [VALIDATE_ROW_CHANGES] =
+ g_signal_new ("validate-row-changes",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GdaDataProxyClass, pre_changes_applied),
- pre_changes_accumulator, NULL,
- gda_marshal_BOOLEAN__INT_INT, G_TYPE_BOOLEAN, 2, G_TYPE_INT, G_TYPE_INT);
- gda_data_proxy_signals [POST_CHANGES_APPLIED] =
- g_signal_new ("post-changes-applied",
+ G_STRUCT_OFFSET (GdaDataProxyClass, validate_row_changes),
+ validate_row_changes_accumulator, NULL,
+ gda_marshal_POINTER__INT_INT, G_TYPE_POINTER, 2, G_TYPE_INT, G_TYPE_INT);
+ gda_data_proxy_signals [ROW_CHANGES_APPLIED] =
+ g_signal_new ("row-changes-applied",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET (GdaDataProxyClass, post_changes_applied),
+ G_STRUCT_OFFSET (GdaDataProxyClass, row_changes_applied),
NULL, NULL,
gda_marshal_VOID__INT_INT, G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
gda_data_proxy_signals [FILTER_CHANGED] =
@@ -586,8 +586,8 @@
class->row_delete_changed = NULL;
class->sample_size_changed = NULL;
class->sample_changed = NULL;
- class->pre_changes_applied = m_pre_changes_applied;
- class->post_changes_applied = NULL;
+ class->validate_row_changes = m_validate_row_changes;
+ class->row_changes_applied = NULL;
/* virtual functions */
object_class->dispose = gda_data_proxy_dispose;
@@ -706,6 +706,10 @@
{
if (proxy->priv->all_modifs) {
gda_data_proxy_cancel_all_changes (proxy);
+ g_assert (! proxy->priv->all_modifs);
+ }
+
+ if (proxy->priv->modify_rows) {
g_hash_table_destroy (proxy->priv->modify_rows);
proxy->priv->modify_rows = NULL;
}
@@ -1841,8 +1845,8 @@
commit_row_modif (GdaDataProxy *proxy, RowModif *rm, gboolean adjust_display, GError **error)
{
gboolean err = FALSE;
- gboolean mod_ok;
gint proxy_row;
+ GError *lerror = NULL;
if (!rm)
return TRUE;
@@ -1852,22 +1856,21 @@
/*
* Steps in this procedure:
- * -1- send the "pre-changes-applied" signal, and abort if return value is FALSE
+ * -1- send the "validate-row-changes" signal, and abort if return value is FALSE
* -2- apply desired modification (which _should_ trigger "row_{inserted,removed,updated}" signals from
* the proxied model)
* -3- if no error then destroy the RowModif which has just been applied
* and refresh displayed chunks if @adjust_display is set to TRUE
- * -4- send the "post-changes-applied" signal
+ * -4- send the "row-changes-applied" signal
*/
proxy_row = row_modif_to_proxy_row (proxy, rm);
/* validate the changes to this row */
g_signal_emit (G_OBJECT (proxy),
- gda_data_proxy_signals[PRE_CHANGES_APPLIED],
- 0, proxy_row, rm->model_row, &mod_ok);
- if (!mod_ok) {
- g_set_error (error, GDA_DATA_PROXY_ERROR, GDA_DATA_PROXY_COMMIT_CANCELLED,
- _("Modifications cancelled"));
+ gda_data_proxy_signals[VALIDATE_ROW_CHANGES],
+ 0, proxy_row, rm->model_row, &lerror);
+ if (lerror) {
+ g_propagate_error (error, lerror);
return FALSE;
}
@@ -1983,7 +1986,7 @@
/* signal row actually changed */
g_signal_emit (G_OBJECT (proxy),
- gda_data_proxy_signals[POST_CHANGES_APPLIED],
+ gda_data_proxy_signals[ROW_CHANGES_APPLIED],
0, proxy_row, -1);
}
else if ((proxy->priv->catched_inserted_row < 0) &&
@@ -2006,7 +2009,7 @@
if (!err && rm) {
/* signal row actually changed */
g_signal_emit (G_OBJECT (proxy),
- gda_data_proxy_signals[POST_CHANGES_APPLIED],
+ gda_data_proxy_signals[ROW_CHANGES_APPLIED],
0, proxy_row, rm->model_row);
/* get rid of the commited change; if the changes have been applied correctly, @rm should
@@ -2369,7 +2372,7 @@
repl_row = index;
if (!iter)
iter = gda_data_model_create_iter (proxy->priv->model);
- if (!gda_data_model_iter_set_at_row (iter, repl_row))
+ if (!gda_data_model_iter_move_at_row (iter, repl_row))
repl_row = -1;
}
@@ -2828,7 +2831,6 @@
if (! vcnc) {
g_set_error (error, GDA_DATA_PROXY_ERROR, GDA_DATA_PROXY_FILTER_ERROR,
_("Could not create virtual connection"));
- g_object_unref (vcnc);
proxy->priv->force_direct_mapping = FALSE;
goto clean_previous_filter;
}
@@ -3163,7 +3165,7 @@
else
return -1; /* unknown number of rows */
}
- if (proxy->priv->add_null_entry)
+ if (!proxy->priv->force_direct_mapping && proxy->priv->add_null_entry)
nbrows += 1;
return nbrows;
@@ -3297,7 +3299,8 @@
else {
/* non existing row, return NULL */
g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ROW_OUT_OF_RANGE_ERROR,
- _("Row %d out of range (0-%d)"), proxy_row, gda_data_model_get_n_rows (model) - 1);
+ _("Row %d out of range (0-%d)"), proxy_row,
+ gda_data_model_get_n_rows (model) - 1);
retval = NULL;
}
}
@@ -3328,7 +3331,8 @@
else {
/* non existing row, return NULL */
g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ROW_OUT_OF_RANGE_ERROR,
- _("Row %d out of range (0-%d)"), proxy_row, gda_data_model_get_n_rows (model) - 1);
+ _("Row %d out of range (0-%d)"), proxy_row,
+ gda_data_model_get_n_rows (model) - 1);
retval = NULL;
}
}
Modified: trunk/libgda/gda-data-proxy.h
==============================================================================
--- trunk/libgda/gda-data-proxy.h (original)
+++ trunk/libgda/gda-data-proxy.h Mon Sep 15 18:30:05 2008
@@ -65,8 +65,8 @@
void (* sample_size_changed) (GdaDataProxy *proxy, gint sample_size);
void (* sample_changed) (GdaDataProxy *proxy, gint sample_start, gint sample_end);
- gboolean (* pre_changes_applied) (GdaDataProxy *proxy, gint row, gint proxied_row);
- void (* post_changes_applied) (GdaDataProxy *proxy, gint row, gint proxied_row);
+ GError *(* validate_row_changes) (GdaDataProxy *proxy, gint row, gint proxied_row);
+ void (* row_changes_applied) (GdaDataProxy *proxy, gint row, gint proxied_row);
};
GType gda_data_proxy_get_type (void) G_GNUC_CONST;
Modified: trunk/libgda/gda-data-select.c
==============================================================================
--- trunk/libgda/gda-data-select.c (original)
+++ trunk/libgda/gda-data-select.c Mon Sep 15 18:30:05 2008
@@ -23,6 +23,7 @@
#include <glib/gi18n-lib.h>
#include <libgda/gda-data-model.h>
#include <libgda/gda-data-model-iter.h>
+#include <libgda/gda-data-model-iter-extra.h>
#include <libgda/gda-data-model-private.h>
#include <string.h>
#include "gda-data-select.h"
@@ -1644,7 +1645,7 @@
g_return_val_if_fail (CLASS (model)->fetch_next, FALSE);
if (imodel->priv->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)
- return gda_data_model_move_iter_next_default (model, iter);
+ return gda_data_model_iter_move_next_default (model, iter);
g_return_val_if_fail (iter, FALSE);
g_return_val_if_fail (imodel->priv->iter == iter, FALSE);
@@ -1693,7 +1694,7 @@
g_return_val_if_fail (CLASS (model)->fetch_prev, FALSE);
if (imodel->priv->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)
- return gda_data_model_move_iter_prev_default (model, iter);
+ return gda_data_model_iter_move_prev_default (model, iter);
g_return_val_if_fail (iter, FALSE);
g_return_val_if_fail (imodel->priv->iter == iter, FALSE);
@@ -1743,7 +1744,7 @@
int_row = external_to_internal_row (imodel, row, NULL);
if (imodel->priv->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)
- return gda_data_model_move_iter_at_row_default (model, iter, row);
+ return gda_data_model_iter_move_at_row_default (model, iter, row);
g_return_val_if_fail (iter, FALSE);
g_return_val_if_fail (imodel->priv->iter == iter, FALSE);
Modified: trunk/libgda/gda-holder.c
==============================================================================
--- trunk/libgda/gda-holder.c (original)
+++ trunk/libgda/gda-holder.c Mon Sep 15 18:30:05 2008
@@ -57,7 +57,7 @@
{
CHANGED,
SOURCE_CHANGED,
- BEFORE_CHANGE,
+ VALIDATE_CHANGE,
LAST_SIGNAL
};
@@ -143,7 +143,7 @@
}
static gboolean
-before_change_accumulator (GSignalInvocationHint *ihint,
+validate_change_accumulator (GSignalInvocationHint *ihint,
GValue *return_accu,
const GValue *handler_return,
gpointer data)
@@ -157,7 +157,7 @@
}
static GError *
-m_before_change (GdaHolder *holder, const GValue *new_value)
+m_validate_change (GdaHolder *holder, const GValue *new_value)
{
return NULL;
}
@@ -194,17 +194,17 @@
* Return value: NULL if @holder is allowed to change its value to @new_value, or a #GError
* otherwise.
*/
- gda_holder_signals[BEFORE_CHANGE] =
- g_signal_new ("before-change",
+ gda_holder_signals[VALIDATE_CHANGE] =
+ g_signal_new ("validate-change",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GdaHolderClass, before_change),
- before_change_accumulator, NULL,
+ G_STRUCT_OFFSET (GdaHolderClass, validate_change),
+ validate_change_accumulator, NULL,
gda_marshal_POINTER__POINTER, G_TYPE_POINTER, 1, G_TYPE_POINTER);
class->changed = NULL;
class->source_changed = NULL;
- class->before_change = m_before_change;
+ class->validate_change = m_validate_change;
/* virtual functions */
object_class->dispose = gda_holder_dispose;
@@ -751,7 +751,7 @@
* Note2: if @holder can't accept the @value value, then this method returns FALSE, and @holder will be left
* in an invalid state.
*
- * Note3: before the change is accepted by @holder, the "before-change" signal will be emitted (the value
+ * Note3: before the change is accepted by @holder, the "validate-change" signal will be emitted (the value
* of which can prevent the change from happening) which can be connected to to have a greater control
* of which values @holder can have, or implement some business rules.
*
@@ -829,7 +829,7 @@
* Note1: if @holder can't accept the @value value, then this method returns FALSE, and @holder will be left
* in an invalid state.
*
- * Note2: before the change is accepted by @holder, the "before-change" signal will be emitted (the value
+ * Note2: before the change is accepted by @holder, the "validate-change" signal will be emitted (the value
* of which can prevent the change from happening) which can be connected to to have a greater control
* of which values @holder can have, or implement some business rules.
*
@@ -906,7 +906,7 @@
/* check if we are allowed to change value */
GError *lerror = NULL;
- g_signal_emit (holder, gda_holder_signals[BEFORE_CHANGE], 0, value, &lerror);
+ g_signal_emit (holder, gda_holder_signals[VALIDATE_CHANGE], 0, value, &lerror);
if (lerror) {
/* change refused by signal callback */
g_propagate_error (error, lerror);
Modified: trunk/libgda/gda-holder.h
==============================================================================
--- trunk/libgda/gda-holder.h (original)
+++ trunk/libgda/gda-holder.h Mon Sep 15 18:30:05 2008
@@ -56,7 +56,7 @@
GObjectClass parent_class;
void (*changed) (GdaHolder *holder);
void (*source_changed) (GdaHolder *holder);
- GError *(*before_change) (GdaHolder *holder, const GValue *new_value);
+ GError *(*validate_change) (GdaHolder *holder, const GValue *new_value);
};
GType gda_holder_get_type (void) G_GNUC_CONST;
Modified: trunk/libgda/gda-marshal.list
==============================================================================
--- trunk/libgda/gda-marshal.list (original)
+++ trunk/libgda/gda-marshal.list Mon Sep 15 18:30:05 2008
@@ -38,8 +38,8 @@
VOID:INT,BOOLEAN
VOID:STRING,INT
VOID:ENUM,POINTER
-BOOLEAN:INT
-BOOLEAN:INT,INT
+POINTER:INT,INT
+POINTER:VOID
POINTER:POINTER
BOOLEAN:POINTER
POINTER:OBJECT,POINTER
Modified: trunk/libgda/gda-server-operation.c
==============================================================================
--- trunk/libgda/gda-server-operation.c (original)
+++ trunk/libgda/gda-server-operation.c Mon Sep 15 18:30:05 2008
@@ -2106,8 +2106,7 @@
if (!xml_file) {
/* basic validity test */
- list = op->priv->allnodes;
- while (list && valid) {
+ for (list = op->priv->allnodes; list; list = list->next) {
Node *node;
node = NODE (list->data);
@@ -2122,22 +2121,16 @@
valid = FALSE;
g_set_error (error, 0, 0,
_("Missing required value for '%s'"), path);
+ break;
}
g_free (path);
}
else if (node->type == GDA_SERVER_OPERATION_NODE_PARAMLIST) {
- valid = gda_set_is_valid (node->d.plist);
- if (!valid) {
- gchar *path;
-
- path = node_get_complete_path (op, node);
- g_set_error (error, 0, 0,
- _("Missing required value for list of parameters '%s'"), path);
- g_free (path);
- }
+ valid = gda_set_is_valid (node->d.plist, error);
+ if (!valid)
+ break;
}
}
- list = list->next;
}
}
else {
Modified: trunk/libgda/gda-set.c
==============================================================================
--- trunk/libgda/gda-set.c (original)
+++ trunk/libgda/gda-set.c Mon Sep 15 18:30:05 2008
@@ -51,7 +51,7 @@
static void set_remove_source (GdaSet *set, GdaSetSource *source);
static void changed_holder_cb (GdaHolder *holder, GdaSet *dataset);
-static GError *before_change_holder_cb (GdaHolder *holder, const GValue *value, GdaSet *dataset);
+static GError *validate_change_holder_cb (GdaHolder *holder, const GValue *value, GdaSet *dataset);
static void source_changed_holder_cb (GdaHolder *holder, GdaSet *dataset);
static void notify_holder_cb (GdaHolder *holder, GParamSpec *pspec, GdaSet *dataset);
@@ -78,11 +78,12 @@
PUBLIC_DATA_CHANGED,
HOLDER_PLUGIN_CHANGED,
HOLDER_ATTR_CHANGED,
- BEFORE_HOLDER_CHANGE,
+ VALIDATE_HOLDER_CHANGE,
+ VALIDATE_SET,
LAST_SIGNAL
};
-static gint gda_set_signals[LAST_SIGNAL] = { 0, 0, 0, 0, 0 };
+static gint gda_set_signals[LAST_SIGNAL] = { 0, 0, 0, 0, 0, 0};
/* private structure */
@@ -197,7 +198,7 @@
}
static gboolean
-before_holder_change_accumulator (GSignalInvocationHint *ihint,
+validate_accumulator (GSignalInvocationHint *ihint,
GValue *return_accu,
const GValue *handler_return,
gpointer data)
@@ -211,7 +212,13 @@
}
static GError *
-m_before_holder_change (GdaSet *set, GdaHolder *holder, const GValue *new_value)
+m_validate_holder_change (GdaSet *set, GdaHolder *holder, const GValue *new_value)
+{
+ return NULL;
+}
+
+static GError *
+m_validate_set (GdaSet *set)
{
return NULL;
}
@@ -233,7 +240,7 @@
GDA_TYPE_HOLDER);
/**
- * GdaSet::before-holder-change:
+ * GdaSet::validate-holder-change:
* @set: the object which received the signal
* @holder: the #GdaHolder which is going to change
* @new_value: the proposed new value for @holder
@@ -244,14 +251,31 @@
* Return value: NULL if @holder is allowed to change its value to @new_value, or a #GError
* otherwise.
*/
- gda_set_signals[BEFORE_HOLDER_CHANGE] =
- g_signal_new ("before-holder-change",
+ gda_set_signals[VALIDATE_HOLDER_CHANGE] =
+ g_signal_new ("validate-holder-change",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GdaSetClass, before_holder_change),
- before_holder_change_accumulator, NULL,
+ G_STRUCT_OFFSET (GdaSetClass, validate_holder_change),
+ validate_accumulator, NULL,
gda_marshal_POINTER__OBJECT_POINTER, G_TYPE_POINTER, 2,
GDA_TYPE_HOLDER, G_TYPE_POINTER);
+ /**
+ * GdaSet::validate-set:
+ * @set: the object which received the signal
+ *
+ * Gets emitted when gda_set_is_valid() is called, use
+ * this signal to control which combination of values @set's holder can have (for example to implement some business rules)
+ *
+ * Return value: NULL if @set's contents has been validated, or a #GError
+ * otherwise.
+ */
+ gda_set_signals[VALIDATE_SET] =
+ g_signal_new ("validate-set",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GdaSetClass, validate_set),
+ validate_accumulator, NULL,
+ gda_marshal_POINTER__VOID, G_TYPE_POINTER, 0);
gda_set_signals[HOLDER_PLUGIN_CHANGED] =
g_signal_new ("holder-plugin-changed",
G_TYPE_FROM_CLASS (object_class),
@@ -277,7 +301,8 @@
gda_marshal_VOID__VOID, G_TYPE_NONE, 0);
class->holder_changed = NULL;
- class->before_holder_change = m_before_holder_change;
+ class->validate_holder_change = m_validate_holder_change;
+ class->validate_set = m_validate_set;
class->holder_plugin_changed = NULL;
class->holder_attr_changed = NULL;
class->public_data_changed = NULL;
@@ -925,7 +950,7 @@
g_signal_handlers_disconnect_by_func (G_OBJECT (holder),
G_CALLBACK (changed_holder_cb), set);
g_signal_handlers_disconnect_by_func (G_OBJECT (holder),
- G_CALLBACK (before_change_holder_cb), set);
+ G_CALLBACK (validate_change_holder_cb), set);
g_signal_handlers_disconnect_by_func (G_OBJECT (holder),
G_CALLBACK (source_changed_holder_cb), set);
g_signal_handlers_disconnect_by_func (G_OBJECT (holder),
@@ -980,16 +1005,16 @@
}
static GError *
-before_change_holder_cb (GdaHolder *holder, const GValue *value, GdaSet *set)
+validate_change_holder_cb (GdaHolder *holder, const GValue *value, GdaSet *set)
{
- /* signal the holder before-change */
+ /* signal the holder validate-change */
GError *error = NULL;
#ifdef GDA_DEBUG_signal
- g_print (">> 'BEFORE_HOLDER_CHANGE' from %s\n", __FUNCTION__);
+ g_print (">> 'VALIDATE_HOLDER_CHANGE' from %s\n", __FUNCTION__);
#endif
- g_signal_emit (G_OBJECT (set), gda_set_signals[BEFORE_HOLDER_CHANGE], 0, holder, value, &error);
+ g_signal_emit (G_OBJECT (set), gda_set_signals[VALIDATE_HOLDER_CHANGE], 0, holder, value, &error);
#ifdef GDA_DEBUG_signal
- g_print ("<< 'BEFORE_HOLDER_CHANGED' from %s\n", __FUNCTION__);
+ g_print ("<< 'VALIDATE_HOLDER_CHANGED' from %s\n", __FUNCTION__);
#endif
return error;
}
@@ -1030,7 +1055,7 @@
g_signal_handlers_disconnect_by_func (G_OBJECT (list->data),
G_CALLBACK (changed_holder_cb), set);
g_signal_handlers_disconnect_by_func (G_OBJECT (list->data),
- G_CALLBACK (before_change_holder_cb), set);
+ G_CALLBACK (validate_change_holder_cb), set);
g_signal_handlers_disconnect_by_func (G_OBJECT (list->data),
G_CALLBACK (source_changed_holder_cb), set);
g_signal_handlers_disconnect_by_func (G_OBJECT (list->data),
@@ -1332,8 +1357,8 @@
g_object_ref (holder);
g_signal_connect (G_OBJECT (holder), "changed",
G_CALLBACK (changed_holder_cb), set);
- g_signal_connect (G_OBJECT (holder), "before-change",
- G_CALLBACK (before_change_holder_cb), set);
+ g_signal_connect (G_OBJECT (holder), "validate-change",
+ G_CALLBACK (validate_change_holder_cb), set);
g_signal_connect (G_OBJECT (holder), "source-changed",
G_CALLBACK (source_changed_holder_cb), set);
g_signal_connect (G_OBJECT (holder), "notify",
@@ -1399,13 +1424,18 @@
/**
* gda_set_is_valid
* @set: a #GdaSet object
+ * @error: a place to store validation errors, or %NULL
*
- * Tells if all the set's holders have valid data
+ * This method tells if all @set's #GdaHolder objects are valid, and if
+ * they represent a valid combination of values, as defined by rules
+ * external to Libgda: the "validate-set" signal is emitted and if none of the signal handlers return an
+ * error, then the returned value is TRUE, otherwise the return value is FALSE as soon as a signal handler
+ * returns an error.
*
* Returns: TRUE if the set is valid
*/
gboolean
-gda_set_is_valid (GdaSet *set)
+gda_set_is_valid (GdaSet *set, GError **error)
{
GSList *holders;
gboolean retval = TRUE;
@@ -1414,9 +1444,28 @@
g_return_val_if_fail (set->priv, FALSE);
for (holders = set->holders; holders && retval; holders = g_slist_next (holders)) {
- if (!gda_holder_is_valid ((GdaHolder*) holders->data))
+ if (!gda_holder_is_valid ((GdaHolder*) holders->data)) {
+ g_set_error (error, GDA_SET_ERROR, GDA_SET_INVALID_ERROR,
+ _("One or more values are invalid"));
retval = FALSE;
+ }
+ if (retval) {
+ /* signal the holder validate-set */
+ GError *lerror = NULL;
+#ifdef GDA_DEBUG_signal
+ g_print (">> 'VALIDATE_SET' from %s\n", __FUNCTION__);
+#endif
+ g_signal_emit (G_OBJECT (set), gda_set_signals[VALIDATE_SET], 0, &lerror);
+#ifdef GDA_DEBUG_signal
+ g_print ("<< 'VALIDATE_SET' from %s\n", __FUNCTION__);
+#endif
+ if (lerror) {
+ g_propagate_error (error, lerror);
+ retval = FALSE;
+ }
+ }
+
#ifdef GDA_DEBUG_NO
g_print ("== HOLDER %p: valid= %d, value=%s\n", holders->data, gda_holder_is_valid (GDA_HOLDER (holders->data)),
gda_holder_get_value (GDA_HOLDER (holders->data)) ?
Modified: trunk/libgda/gda-set.h
==============================================================================
--- trunk/libgda/gda-set.h (original)
+++ trunk/libgda/gda-set.h Mon Sep 15 18:30:05 2008
@@ -40,7 +40,8 @@
typedef enum
{
GDA_SET_XML_SPEC_ERROR,
- GDA_SET_HOLDER_NOT_FOUND_ERROR
+ GDA_SET_HOLDER_NOT_FOUND_ERROR,
+ GDA_SET_INVALID_ERROR
} GdaSetError;
typedef enum {
@@ -117,7 +118,8 @@
{
GObjectClass parent_class;
- GError *(*before_holder_change) (GdaSet *set, GdaHolder *holder, const GValue *new_value);
+ GError *(*validate_holder_change)(GdaSet *set, GdaHolder *holder, const GValue *new_value);
+ GError *(*validate_set) (GdaSet *set);
void (*holder_changed) (GdaSet *set, GdaHolder *holder);
void (*holder_plugin_changed) (GdaSet *set, GdaHolder *holder);
void (*holder_attr_changed) (GdaSet *set, GdaHolder *holder);
@@ -139,7 +141,7 @@
gboolean gda_set_add_holder (GdaSet *set, GdaHolder *holder);
void gda_set_remove_holder (GdaSet *set, GdaHolder *holder);
void gda_set_merge_with_set (GdaSet *set, GdaSet *set_to_merge);
-gboolean gda_set_is_valid (GdaSet *set);
+gboolean gda_set_is_valid (GdaSet *set, GError **error);
/* public data lookup functions */
GdaSetNode *gda_set_get_node (GdaSet *set, GdaHolder *param);
Modified: trunk/libgda/gda-value.c
==============================================================================
--- trunk/libgda/gda-value.c (original)
+++ trunk/libgda/gda-value.c Mon Sep 15 18:30:05 2008
@@ -1882,7 +1882,9 @@
const GdaBinary *binary1 = gda_value_get_binary (value1);
const GdaBinary *binary2 = gda_value_get_binary (value2);
if (binary1 && binary2 && (binary1->binary_length == binary2->binary_length))
- return bcmp (binary1->data, binary2->data, binary1->binary_length) ;
+ return bcmp (binary1->data, binary2->data, binary1->binary_length);
+ else
+ return 1;
}
else if (type == GDA_TYPE_BLOB) {
@@ -1893,6 +1895,7 @@
return bcmp (((GdaBinary *)blob1)->data, ((GdaBinary *)blob2)->data,
((GdaBinary *)blob1)->binary_length);
}
+ return 1;
}
else if (type == G_TYPE_DATE) {
@@ -1902,6 +1905,7 @@
d2 = (GDate *) g_value_get_boxed (value2);
if (d1 && d2)
return g_date_compare (d1, d2);
+ return 1;
}
else if (type == GDA_TYPE_GEOMETRIC_POINT) {
@@ -1910,6 +1914,7 @@
p2 = gda_value_get_geometric_point (value2);
if (p1 && p2)
return bcmp (p1, p2, sizeof (GdaGeometricPoint));
+ return 1;
}
else if (type == G_TYPE_OBJECT) {
@@ -1935,6 +1940,7 @@
num2 = gda_value_get_numeric (value2);
if (num1 && num2)
return strcmp (num1->number, num2->number);
+ return 1;
}
else if (type == G_TYPE_STRING) {
@@ -1943,6 +1949,7 @@
str2 = g_value_get_string (value2);
if (str1 && str2)
return strcmp (str1, str2);
+ return 1;
}
else if (type == GDA_TYPE_TIME) {
@@ -1951,6 +1958,7 @@
t2 = gda_value_get_time (value2);
if (t1 && t2)
return bcmp (t1, t2, sizeof (GdaTime));
+ return 1;
}
else if (type == GDA_TYPE_TIMESTAMP) {
@@ -1959,6 +1967,7 @@
ts2 = gda_value_get_timestamp (value2);
if (ts1 && ts2)
return bcmp (ts1, ts2, sizeof (GdaTimestamp));
+ return 1;
}
else if ((type == G_TYPE_INT) ||
Modified: trunk/libgda/sqlite/sqlite-src/PragmasPatch
==============================================================================
--- trunk/libgda/sqlite/sqlite-src/PragmasPatch (original)
+++ trunk/libgda/sqlite/sqlite-src/PragmasPatch Mon Sep 15 18:30:05 2008
@@ -1,6 +1,6 @@
---- sqlite3.c.orig 2008-08-28 21:24:44.000000000 +0200
-+++ sqlite3.c 2008-08-28 21:24:51.000000000 +0200
-@@ -65313,6 +65313,48 @@
+--- sqlite3.c.orig 2008-08-30 18:23:34.000000000 +0200
++++ sqlite3.c 2008-09-15 12:03:08.000000000 +0200
+@@ -66756,6 +66756,48 @@
#ifndef SQLITE_OMIT_SCHEMA_PRAGMAS
/*
Modified: trunk/libgda/sqlite/sqlite-src/sqlite3.c
==============================================================================
--- trunk/libgda/sqlite/sqlite-src/sqlite3.c (original)
+++ trunk/libgda/sqlite/sqlite-src/sqlite3.c Mon Sep 15 18:30:05 2008
@@ -1,6 +1,6 @@
/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
-** version 3.6.1. By combining all the individual C code files into this
+** version 3.6.2. By combining all the individual C code files into this
** single large file, the entire code can be compiled as a one translation
** unit. This allows many compilers to do optimizations that would not be
** possible if the files were compiled separately. Performance improvements
@@ -11,13 +11,13 @@
** programs, you need this file and the "sqlite3.h" header file that defines
** the programming interface to the SQLite library. (If you do not have
** the "sqlite3.h" header file at hand, you will find a copy in the first
-** 6279 lines past this header comment.) Additional code files may be
+** 6312 lines past this header comment.) Additional code files may be
** needed if you want a wrapper to interface SQLite with your choice of
** programming language. The code for the "sqlite3" command-line shell
** is also in a separate file. This file contains only code for the core
** SQLite library.
**
-** This amalgamation was generated on 2008-08-05 21:36:42 UTC.
+** This amalgamation was generated on 2008-08-30 16:23:32 UTC.
*/
#define SQLITE_CORE 1
#define SQLITE_AMALGAMATION 1
@@ -41,7 +41,7 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
-** @(#) $Id: sqliteInt.h,v 1.752 2008/08/04 20:13:27 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.764 2008/08/29 18:42:30 rse Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_
@@ -425,6 +425,9 @@
# define _XOPEN_SOURCE 500 /* Needed to enable pthread recursive mutexes */
#endif
+/*
+** The TCL headers are only needed when compiling the TCL bindings.
+*/
#if defined(SQLITE_TCL) || defined(TCLSH)
# include <tcl.h>
#endif
@@ -474,7 +477,7 @@
** the version number) and changes its name to "sqlite3.h" as
** part of the build process.
**
-** @(#) $Id: sqlite.h.in,v 1.387 2008/08/05 17:53:23 drh Exp $
+** @(#) $Id: sqlite.h.in,v 1.394 2008/08/25 21:23:02 drh Exp $
*/
#ifndef _SQLITE3_H_
#define _SQLITE3_H_
@@ -496,6 +499,31 @@
#endif
/*
+** Add the ability to mark interfaces as deprecated.
+*/
+#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
+ /* GCC added the deprecated attribute in version 3.1 */
+ #define SQLITE_DEPRECATED __attribute__ ((deprecated))
+#elif defined(_MSC_VER)
+ #define SQLITE_DEPRECATED __declspec(deprecated)
+#else
+ #define SQLITE_DEPRECATED
+#endif
+
+/*
+** Add the ability to mark interfaces as experimental.
+*/
+#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
+ /* I can confirm that it does not work on version 4.1.0... */
+ /* First appears in GCC docs for version 4.3.0 */
+ #define SQLITE_EXPERIMENTAL __attribute__ ((warning ("is experimental")))
+#elif defined(_MSC_VER)
+ #define SQLITE_EXPERIMENTAL __declspec(deprecated("was declared experimental"))
+#else
+ #define SQLITE_EXPERIMENTAL
+#endif
+
+/*
** Ensure these symbols were not defined by some previous header file.
*/
#ifdef SQLITE_VERSION
@@ -535,8 +563,8 @@
** with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z
** are the major version, minor version, and release number.
*/
-#define SQLITE_VERSION "3.6.1"
-#define SQLITE_VERSION_NUMBER 3006001
+#define SQLITE_VERSION "3.6.2"
+#define SQLITE_VERSION_NUMBER 3006002
/*
** CAPI3REF: Run-Time Library Version Numbers {H10020} <S60100>
@@ -935,6 +963,7 @@
#define SQLITE_IOERR_NOMEM (SQLITE_IOERR | (12<<8))
#define SQLITE_IOERR_ACCESS (SQLITE_IOERR | (13<<8))
#define SQLITE_IOERR_CHECKRESERVEDLOCK (SQLITE_IOERR | (14<<8))
+#define SQLITE_IOERR_LOCK (SQLITE_IOERR | (15<<8))
/*
** CAPI3REF: Flags For File Open Operations {H10230} <H11120> <H12700>
@@ -957,6 +986,7 @@
#define SQLITE_OPEN_SUBJOURNAL 0x00002000
#define SQLITE_OPEN_MASTER_JOURNAL 0x00004000
#define SQLITE_OPEN_NOMUTEX 0x00008000
+#define SQLITE_OPEN_FULLMUTEX 0x00010000
/*
** CAPI3REF: Device Characteristics {H10240} <H11120>
@@ -1410,7 +1440,7 @@
** If the option is unknown or SQLite is unable to set the option
** then this routine returns a non-zero [error code].
*/
-SQLITE_API int sqlite3_config(int, ...);
+SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_config(int, ...);
/*
** CAPI3REF: Configure database connections {H10180} <S20000>
@@ -1431,7 +1461,7 @@
** New verbs are likely to be added in future releases of SQLite.
** Additional arguments depend on the verb.
*/
-SQLITE_API int sqlite3_db_config(sqlite3*, int op, ...);
+SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_db_config(sqlite3*, int op, ...);
/*
** CAPI3REF: Memory Allocation Routines {H10155} <S20120>
@@ -2520,6 +2550,11 @@
** previous call. Disable the authorizer by installing a NULL callback.
** The authorizer is disabled by default.
**
+** When [sqlite3_prepare_v2()] is used to prepare a statement, the
+** statement might be reprepared during [sqlite3_step()] due to a
+** schema change. Hence, the application should ensure that the
+** correct authorizer callback remains in place during the [sqlite3_step()].
+**
** Note that the authorizer callback is invoked only during
** [sqlite3_prepare()] or its variants. Authorization is not
** performed during statement evaluation in [sqlite3_step()].
@@ -2530,11 +2565,11 @@
** authorizer callback with database connection D.
**
** {H12502} The authorizer callback is invoked as SQL statements are
-** being compiled.
+** being parseed and compiled.
**
** {H12503} If the authorizer callback returns any value other than
** [SQLITE_IGNORE], [SQLITE_OK], or [SQLITE_DENY], then
-** the [sqlite3_prepare_v2()] or equivalent call that caused
+** the application interface call that caused
** the authorizer callback to run shall fail with an
** [SQLITE_ERROR] error code and an appropriate error message.
**
@@ -2542,7 +2577,7 @@
** described is processed normally.
**
** {H12505} When the authorizer callback returns [SQLITE_DENY], the
-** [sqlite3_prepare_v2()] or equivalent call that caused the
+** application interface call that caused the
** authorizer callback to run shall fail
** with an [SQLITE_ERROR] error code and an error message
** explaining that access is denied.
@@ -2616,21 +2651,21 @@
** INVARIANTS:
**
** {H12551} The second parameter to an
-** [sqlite3_set_authorizer | authorizer callback] is always an integer
+** [sqlite3_set_authorizer | authorizer callback] shall be an integer
** [SQLITE_COPY | authorizer code] that specifies what action
** is being authorized.
**
** {H12552} The 3rd and 4th parameters to the
** [sqlite3_set_authorizer | authorization callback]
-** will be parameters or NULL depending on which
+** shall be parameters or NULL depending on which
** [SQLITE_COPY | authorizer code] is used as the second parameter.
**
** {H12553} The 5th parameter to the
-** [sqlite3_set_authorizer | authorizer callback] is the name
+** [sqlite3_set_authorizer | authorizer callback] shall be the name
** of the database (example: "main", "temp", etc.) if applicable.
**
** {H12554} The 6th parameter to the
-** [sqlite3_set_authorizer | authorizer callback] is the name
+** [sqlite3_set_authorizer | authorizer callback] shall be 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
** top-level SQL code.
@@ -2690,16 +2725,17 @@
**
** INVARIANTS:
**
-** {H12281} The callback function registered by [sqlite3_trace()] is
+** {H12281} The callback function registered by [sqlite3_trace()]
+** shall be invoked
** whenever an SQL statement first begins to execute and
** whenever a trigger subprogram first begins to run.
**
-** {H12282} Each call to [sqlite3_trace()] overrides the previously
+** {H12282} Each call to [sqlite3_trace()] shall override the previously
** registered trace callback.
**
-** {H12283} A NULL trace callback disables tracing.
+** {H12283} A NULL trace callback shall disable tracing.
**
-** {H12284} The first argument to the trace callback is a copy of
+** {H12284} The first argument to the trace callback shall be a copy of
** the pointer which was the 3rd argument to [sqlite3_trace()].
**
** {H12285} The second argument to the trace callback is a
@@ -2723,8 +2759,8 @@
** of the number of nanoseconds of wall-clock time required to
** run the SQL statement from start to finish.
*/
-SQLITE_API void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*);
-SQLITE_API void *sqlite3_profile(sqlite3*,
+SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*);
+SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*,
void(*xProfile)(void*,const char*,sqlite3_uint64), void*);
/*
@@ -2893,11 +2929,11 @@
** reading and writing if possible, or for reading only if the
** file is write protected by the operating system.
**
-** {H12713} If the G parameter to [sqlite3_open(v2(F,D,G,V)] omits the
+** {H12713} If the G parameter to [sqlite3_open_v2(F,D,G,V)] omits the
** bit value [SQLITE_OPEN_CREATE] and the database does not
** previously exist, an error is returned.
**
-** {H12714} If the G parameter to [sqlite3_open(v2(F,D,G,V)] contains the
+** {H12714} If the G parameter to [sqlite3_open_v2(F,D,G,V)] contains the
** bit value [SQLITE_OPEN_CREATE] and the database does not
** previously exist, then an attempt is made to create and
** initialize the database.
@@ -4391,12 +4427,12 @@
** the use of these functions. To help encourage people to avoid
** using these functions, we are not going to tell you want they do.
*/
-SQLITE_API int sqlite3_aggregate_count(sqlite3_context*);
-SQLITE_API int sqlite3_expired(sqlite3_stmt*);
-SQLITE_API int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*);
-SQLITE_API int sqlite3_global_recover(void);
-SQLITE_API void sqlite3_thread_cleanup(void);
-SQLITE_API int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int),void*,sqlite3_int64);
+SQLITE_API SQLITE_DEPRECATED int sqlite3_aggregate_count(sqlite3_context*);
+SQLITE_API SQLITE_DEPRECATED int sqlite3_expired(sqlite3_stmt*);
+SQLITE_API SQLITE_DEPRECATED int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*);
+SQLITE_API SQLITE_DEPRECATED int sqlite3_global_recover(void);
+SQLITE_API SQLITE_DEPRECATED void sqlite3_thread_cleanup(void);
+SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int),void*,sqlite3_int64);
/*
** CAPI3REF: Obtaining SQL Function Parameter Values {H15100} <S20200>
@@ -5821,7 +5857,7 @@
** This interface is experimental and is subject to change or
** removal in future releases of SQLite.
*/
-SQLITE_API int sqlite3_create_module(
+SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_create_module(
sqlite3 *db, /* SQLite connection to register module with */
const char *zName, /* Name of the module */
const sqlite3_module *, /* Methods for the module */
@@ -5836,7 +5872,7 @@
** except that it allows a destructor function to be specified. It is
** even more experimental than the rest of the virtual tables API.
*/
-SQLITE_API int sqlite3_create_module_v2(
+SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_create_module_v2(
sqlite3 *db, /* SQLite connection to register module with */
const char *zName, /* Name of the module */
const sqlite3_module *, /* Methods for the module */
@@ -5908,7 +5944,7 @@
** This interface is experimental and is subject to change or
** removal in future releases of SQLite.
*/
-SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zCreateTable);
+SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_declare_vtab(sqlite3*, const char *zCreateTable);
/*
** CAPI3REF: Overload A Function For A Virtual Table {H18300} <S20400>
@@ -5929,7 +5965,7 @@
** This API should be considered part of the virtual table interface,
** which is experimental and subject to change.
*/
-SQLITE_API int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg);
+SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg);
/*
** The interface to the virtual-table mechanism defined above (back up
@@ -6586,7 +6622,7 @@
**
** See also: [sqlite3_db_status()]
*/
-SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag);
+SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag);
/*
** CAPI3REF: Database Connection Status {H17201} <S60200>
@@ -6606,7 +6642,7 @@
**
** See also: [sqlite3_status()].
*/
-SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
+SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
/*
** CAPI3REF: Status Parameters {H17250} <H17200>
@@ -6987,12 +7023,11 @@
#define TK_ILLEGAL 144
#define TK_SPACE 145
#define TK_UNCLOSED_STRING 146
-#define TK_COMMENT 147
-#define TK_FUNCTION 148
-#define TK_COLUMN 149
-#define TK_AGG_FUNCTION 150
-#define TK_AGG_COLUMN 151
-#define TK_CONST_FUNC 152
+#define TK_FUNCTION 147
+#define TK_COLUMN 148
+#define TK_AGG_FUNCTION 149
+#define TK_AGG_COLUMN 150
+#define TK_CONST_FUNC 151
/************** End of parse.h ***********************************************/
/************** Continuing where we left off in sqliteInt.h ******************/
@@ -7133,7 +7168,7 @@
typedef UINT16_TYPE u16; /* 2-byte unsigned integer */
typedef INT16_TYPE i16; /* 2-byte signed integer */
typedef UINT8_TYPE u8; /* 1-byte unsigned integer */
-typedef UINT8_TYPE i8; /* 1-byte signed integer */
+typedef INT8_TYPE i8; /* 1-byte signed integer */
/*
** Macros to determine whether the machine is big or little endian,
@@ -7222,6 +7257,7 @@
typedef struct ExprList ExprList;
typedef struct FKey FKey;
typedef struct FuncDef FuncDef;
+typedef struct FuncDefHash FuncDefHash;
typedef struct IdList IdList;
typedef struct Index Index;
typedef struct KeyClass KeyClass;
@@ -7240,6 +7276,8 @@
typedef struct TriggerStack TriggerStack;
typedef struct TriggerStep TriggerStep;
typedef struct Trigger Trigger;
+typedef struct UnpackedRecord UnpackedRecord;
+typedef struct Walker Walker;
typedef struct WhereInfo WhereInfo;
typedef struct WhereLevel WhereLevel;
@@ -7265,7 +7303,7 @@
** subsystem. See comments in the source code for a detailed description
** of what each interface routine does.
**
-** @(#) $Id: btree.h,v 1.102 2008/07/11 21:02:54 drh Exp $
+** @(#) $Id: btree.h,v 1.103 2008/08/13 19:11:48 drh Exp $
*/
#ifndef _BTREE_H_
#define _BTREE_H_
@@ -7374,8 +7412,6 @@
SQLITE_PRIVATE int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value);
SQLITE_PRIVATE void sqlite3BtreeTripAllCursors(Btree*, int);
-struct UnpackedRecord; /* Forward declaration. Definition in vdbeaux.c. */
-
SQLITE_PRIVATE int sqlite3BtreeCursor(
Btree*, /* BTree containing table to open */
int iTable, /* Index of root page */
@@ -7389,11 +7425,17 @@
SQLITE_PRIVATE int sqlite3BtreeMoveto(
BtCursor*,
const void *pKey,
- struct UnpackedRecord *pUnKey,
i64 nKey,
int bias,
int *pRes
);
+SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
+ BtCursor*,
+ UnpackedRecord *pUnKey,
+ i64 intKey,
+ int bias,
+ int *pRes
+);
SQLITE_PRIVATE int sqlite3BtreeCursorHasMoved(BtCursor*, int*);
SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor*);
SQLITE_PRIVATE int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey,
@@ -7491,7 +7533,7 @@
** 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.135 2008/08/01 20:10:08 drh Exp $
+** $Id: vdbe.h,v 1.138 2008/08/20 22:06:48 drh Exp $
*/
#ifndef _SQLITE_VDBE_H_
#define _SQLITE_VDBE_H_
@@ -7509,7 +7551,6 @@
*/
typedef struct VdbeFunc VdbeFunc;
typedef struct Mem Mem;
-typedef struct UnpackedRecord UnpackedRecord;
/*
** A single instruction of the virtual machine has an opcode
@@ -7844,7 +7885,8 @@
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
SQLITE_PRIVATE int sqlite3VdbeReleaseMemory(int);
#endif
-SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,void*,int);
+SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,
+ UnpackedRecord*,int);
SQLITE_PRIVATE void sqlite3VdbeDeleteUnpackedRecord(UnpackedRecord*);
SQLITE_PRIVATE int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*);
@@ -7880,7 +7922,7 @@
** 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.77 2008/07/16 18:17:56 danielk1977 Exp $
+** @(#) $Id: pager.h,v 1.81 2008/08/27 15:16:34 danielk1977 Exp $
*/
#ifndef _PAGER_H_
@@ -7937,9 +7979,8 @@
** See source code comments for a detailed description of the following
** routines:
*/
-SQLITE_PRIVATE int sqlite3PagerOpen(sqlite3_vfs *, Pager **ppPager, const char*, int,int,int);
+SQLITE_PRIVATE int sqlite3PagerOpen(sqlite3_vfs *, Pager **ppPager, const char*, void(*)(DbPage*), int,int,int);
SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(Pager*, BusyHandler *pBusyHandler);
-SQLITE_PRIVATE void sqlite3PagerSetDestructor(Pager*, void(*)(DbPage*,int));
SQLITE_PRIVATE void sqlite3PagerSetReiniter(Pager*, void(*)(DbPage*,int));
SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u16*);
SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager*, int);
@@ -7963,7 +8004,7 @@
SQLITE_PRIVATE int sqlite3PagerStmtCommit(Pager*);
SQLITE_PRIVATE int sqlite3PagerStmtRollback(Pager*);
SQLITE_PRIVATE void sqlite3PagerDontRollback(DbPage*);
-SQLITE_PRIVATE void sqlite3PagerDontWrite(DbPage*);
+SQLITE_PRIVATE int sqlite3PagerDontWrite(DbPage*);
SQLITE_PRIVATE int sqlite3PagerRefcount(Pager*);
SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(Pager*,int,int);
SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager*);
@@ -7980,10 +8021,7 @@
SQLITE_PRIVATE i64 sqlite3PagerJournalSizeLimit(Pager *, i64);
SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*);
SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager);
-
-#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) && !defined(SQLITE_OMIT_DISKIO)
-SQLITE_PRIVATE int sqlite3PagerReleaseMemory(int);
-#endif
+SQLITE_PRIVATE void sqlite3PagerAlwaysRollback(Pager *pPager);
#ifdef SQLITE_HAS_CODEC
SQLITE_PRIVATE void sqlite3PagerSetCodec(Pager*,void*(*)(void*,void*,Pgno,int),void*);
@@ -8012,6 +8050,177 @@
/************** End of pager.h ***********************************************/
/************** Continuing where we left off in sqliteInt.h ******************/
+/************** Include pcache.h in the middle of sqliteInt.h ****************/
+/************** Begin file pcache.h ******************************************/
+/*
+** 2008 August 05
+**
+** 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.
+**
+** @(#) $Id: pcache.h,v 1.9 2008/08/29 09:10:03 danielk1977 Exp $
+*/
+
+#ifndef _PCACHE_H_
+
+typedef struct PgHdr PgHdr;
+typedef struct PCache PCache;
+
+/*
+** Every page in the cache is controlled by an instance of the following
+** structure.
+*/
+struct PgHdr {
+ void *pData; /* Content of this page */
+ void *pExtra; /* Extra content */
+ PgHdr *pDirty; /* Transient list of dirty pages */
+ Pgno pgno; /* Page number for this page */
+ Pager *pPager; /* The pager this page is part of */
+#ifdef SQLITE_CHECK_PAGES
+ u32 pageHash; /* Hash of page content */
+#endif
+ u16 flags; /* PGHDR flags defined below */
+ /**********************************************************************
+ ** Elements above are public. All that follows is private to pcache.c
+ ** and should not be accessed by other modules.
+ */
+ i16 nRef; /* Number of users of this page */
+ PCache *pCache; /* Cache that owns this page */
+ void *apSave[2]; /* Journal entries for in-memory databases */
+ /**********************************************************************
+ ** Elements above are accessible at any time by the owner of the cache
+ ** without the need for a mutex. The elements that follow can only be
+ ** accessed while holding the SQLITE_MUTEX_STATIC_LRU mutex.
+ */
+ PgHdr *pNextHash, *pPrevHash; /* Hash collision chain for PgHdr.pgno */
+ PgHdr *pNext, *pPrev; /* List of clean or dirty pages */
+ PgHdr *pNextLru, *pPrevLru; /* Part of global LRU list */
+};
+
+/* Bit values for PgHdr.flags */
+#define PGHDR_IN_JOURNAL 0x001 /* Page is in rollback journal */
+#define PGHDR_IN_STMTJRNL 0x002 /* Page is in the statement journal */
+#define PGHDR_DIRTY 0x004 /* Page has changed */
+#define PGHDR_NEED_SYNC 0x008 /* Peed to fsync this page */
+#define PGHDR_NEED_READ 0x020 /* Content is unread */
+#define PGHDR_IS_INIT 0x040 /* pData is initialized */
+#define PGHDR_REUSE_UNLIKELY 0x080 /* Hint: Reuse is unlikely */
+#define PGHDR_DONT_WRITE 0x100 /* Do not write content to disk */
+
+/* Initialize and shutdown the page cache subsystem */
+SQLITE_PRIVATE int sqlite3PcacheInitialize(void);
+SQLITE_PRIVATE void sqlite3PcacheShutdown(void);
+
+/* Page cache buffer management:
+** These routines implement SQLITE_CONFIG_PAGECACHE.
+*/
+SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *, int sz, int n);
+SQLITE_PRIVATE void *sqlite3PCacheMalloc(int sz);
+SQLITE_PRIVATE void sqlite3PCacheFree(void*);
+
+/* Create a new pager cache.
+** Under memory stress, invoke xStress to try to make pages clean.
+** Only clean and unpinned pages can be reclaimed.
+*/
+SQLITE_PRIVATE void sqlite3PcacheOpen(
+ int szPage, /* Size of every page */
+ int szExtra, /* Extra space associated with each page */
+ int bPurgeable, /* True if pages are on backing store */
+ void (*xDestroy)(PgHdr *), /* Called to destroy a page */
+ int (*xStress)(void*, PgHdr*), /* Call to try to make pages clean */
+ void *pStress, /* Argument to xStress */
+ PCache *pToInit /* Preallocated space for the PCache */
+);
+
+/* Modify the page-size after the cache has been created. */
+SQLITE_PRIVATE void sqlite3PcacheSetPageSize(PCache *, int);
+
+/* Return the size in bytes of a PCache object. Used to preallocate
+** storage space.
+*/
+SQLITE_PRIVATE int sqlite3PcacheSize(void);
+
+/* One release per successful fetch. Page is pinned until released.
+** Reference counted.
+*/
+SQLITE_PRIVATE int sqlite3PcacheFetch(PCache*, Pgno, int createFlag, PgHdr**);
+SQLITE_PRIVATE void sqlite3PcacheRelease(PgHdr*);
+
+SQLITE_PRIVATE void sqlite3PcacheDrop(PgHdr*); /* Remove page from cache */
+SQLITE_PRIVATE void sqlite3PcacheMakeDirty(PgHdr*); /* Make sure page is marked dirty */
+SQLITE_PRIVATE void sqlite3PcacheMakeClean(PgHdr*); /* Mark a single page as clean */
+SQLITE_PRIVATE void sqlite3PcacheCleanAll(PCache*); /* Mark all dirty list pages as clean */
+
+/* Change a page number. Used by incr-vacuum. */
+SQLITE_PRIVATE void sqlite3PcacheMove(PgHdr*, Pgno);
+
+/* Remove all pages with pgno>x. Reset the cache if x==0 */
+SQLITE_PRIVATE void sqlite3PcacheTruncate(PCache*, Pgno x);
+
+/* Routines used to implement transactions on memory-only databases. */
+SQLITE_PRIVATE int sqlite3PcachePreserve(PgHdr*, int); /* Preserve current page content */
+SQLITE_PRIVATE void sqlite3PcacheCommit(PCache*, int); /* Drop preserved copy */
+SQLITE_PRIVATE void sqlite3PcacheRollback(PCache*, int); /* Rollback to preserved copy */
+
+/* Get a list of all dirty pages in the cache, sorted by page number */
+SQLITE_PRIVATE PgHdr *sqlite3PcacheDirtyList(PCache*);
+
+/* Reset and close the cache object */
+SQLITE_PRIVATE void sqlite3PcacheClose(PCache*);
+
+/* Set flags on all pages in the page cache */
+SQLITE_PRIVATE void sqlite3PcacheSetFlags(PCache*, int andMask, int orMask);
+
+/* Assert flags settings on all pages. Debugging only */
+SQLITE_PRIVATE void sqlite3PcacheAssertFlags(PCache*, int trueMask, int falseMask);
+
+/* Return true if the number of dirty pages is 0 or 1 */
+SQLITE_PRIVATE int sqlite3PcacheZeroOrOneDirtyPages(PCache*);
+
+/* Discard the contents of the cache */
+SQLITE_PRIVATE int sqlite3PcacheClear(PCache*);
+
+/* Return the total number of outstanding page references */
+SQLITE_PRIVATE int sqlite3PcacheRefCount(PCache*);
+
+/* Increment the reference count of an existing page */
+SQLITE_PRIVATE void sqlite3PcacheRef(PgHdr*);
+
+/* Return the total number of pages stored in the cache */
+SQLITE_PRIVATE int sqlite3PcachePagecount(PCache*);
+
+/* Iterate through all pages currently stored in the cache. This interface
+** is only available if SQLITE_CHECK_PAGES is defined when the library is
+** built.
+*/
+SQLITE_PRIVATE void sqlite3PcacheIterate(PCache *pCache, void (*xIter)(PgHdr *));
+
+/* Set and get the suggested cache-size for the specified pager-cache.
+**
+** If no global maximum is configured, then the system attempts to limit
+** the total number of pages cached by purgeable pager-caches to the sum
+** of the suggested cache-sizes.
+*/
+SQLITE_PRIVATE int sqlite3PcacheGetCachesize(PCache *);
+SQLITE_PRIVATE void sqlite3PcacheSetCachesize(PCache *, int);
+
+/* Try to return memory used by the pcache module to the main memory heap */
+SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int);
+
+SQLITE_PRIVATE void sqlite3PcacheStats(int*,int*,int*,int*);
+
+#endif /* _PCACHE_H_ */
+
+/************** End of pcache.h **********************************************/
+/************** Continuing where we left off in sqliteInt.h ******************/
/************** Include os.h in the middle of sqliteInt.h ********************/
/************** Begin file os.h **********************************************/
@@ -8477,7 +8686,7 @@
u8 bMalloced; /* True if pStart obtained from sqlite3_malloc() */
int nOut; /* Number of buffers currently checked out */
int mxOut; /* Highwater mark for nOut */
- LookasideSlot *pFree; /* List if available buffers */
+ LookasideSlot *pFree; /* List of available buffers */
void *pStart; /* First byte of available memory space */
void *pEnd; /* First byte past end of available space */
};
@@ -8486,6 +8695,16 @@
};
/*
+** A hash table for function definitions.
+**
+** Hash each FuncDef structure into one of the FuncDefHash.a[] slots.
+** Collisions are on the FuncDef.pHash chain.
+*/
+struct FuncDefHash {
+ FuncDef *a[23]; /* Hash table for functions */
+};
+
+/*
** Each database is an instance of the following structure.
**
** The sqlite.lastRowid records the last insert rowid generated by an
@@ -8581,7 +8800,7 @@
sqlite3_vtab **aVTrans; /* Virtual tables with open transactions */
int nVTrans; /* Allocated size of aVTrans */
#endif
- Hash aFunc; /* All functions that can be in SQL exprs */
+ FuncDefHash aFunc; /* Hash table of connection functions */
Hash aCollSeq; /* All collating sequences */
BusyHandler busyHandler; /* Busy callback */
int busyTimeout; /* Busy handler timeout, in msec */
@@ -8646,7 +8865,7 @@
** points to a linked list of these structures.
*/
struct FuncDef {
- i16 nArg; /* Number of arguments. -1 means unlimited */
+ i8 nArg; /* Number of arguments. -1 means unlimited */
u8 iPrefEnc; /* Preferred text encoding (SQLITE_UTF8, 16LE, 16BE) */
u8 needCollSeq; /* True if sqlite3GetFuncCollSeq() might be called */
u8 flags; /* Some combination of SQLITE_FUNC_* */
@@ -8655,10 +8874,51 @@
void (*xFunc)(sqlite3_context*,int,sqlite3_value**); /* Regular function */
void (*xStep)(sqlite3_context*,int,sqlite3_value**); /* Aggregate step */
void (*xFinalize)(sqlite3_context*); /* Aggregate finializer */
- char zName[1]; /* SQL name of the function. MUST BE LAST */
+ char *zName; /* SQL name of the function. */
+ FuncDef *pHash; /* Next with a different name but the same hash */
};
/*
+** Possible values for FuncDef.flags
+*/
+#define SQLITE_FUNC_LIKE 0x01 /* Candidate for the LIKE optimization */
+#define SQLITE_FUNC_CASE 0x02 /* Case-sensitive LIKE-type function */
+#define SQLITE_FUNC_EPHEM 0x04 /* Ephermeral. Delete with VDBE */
+
+/*
+** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are
+** used to create the initializers for the FuncDef structures.
+**
+** FUNCTION(zName, nArg, iArg, bNC, xFunc)
+** Used to create a scalar function definition of a function zName
+** implemented by C function xFunc that accepts nArg arguments. The
+** value passed as iArg is cast to a (void*) and made available
+** as the user-data (sqlite3_user_data()) for the function. If
+** argument bNC is true, then the FuncDef.needCollate flag is set.
+**
+** AGGREGATE(zName, nArg, iArg, bNC, xStep, xFinal)
+** Used to create an aggregate function definition implemented by
+** the C functions xStep and xFinal. The first four parameters
+** are interpreted in the same way as the first 4 parameters to
+** FUNCTION().
+**
+** LIKEFUNC(zName, nArg, pArg, flags)
+** Used to create a scalar function definition of a function zName
+** that accepts nArg arguments and is implemented by a call to C
+** function likeFunc. Argument pArg is cast to a (void *) and made
+** available as the function user-data (sqlite3_user_data()). The
+** FuncDef.flags variable is set to the value passed as the flags
+** parameter.
+*/
+#define FUNCTION(zName, nArg, iArg, bNC, xFunc) \
+ {nArg, SQLITE_UTF8, bNC, 0, SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName}
+#define LIKEFUNC(zName, nArg, arg, flags) \
+ {nArg, SQLITE_UTF8, 0, flags, (void *)arg, 0, likeFunc, 0, 0, #zName}
+#define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal) \
+ {nArg, SQLITE_UTF8, nc, 0, SQLITE_INT_TO_PTR(arg), 0, 0, xStep,xFinal, #zName}
+
+
+/*
** Each SQLite module (virtual table definition) is defined by an
** instance of the following structure, stored in the sqlite3.aModule
** hash table.
@@ -8671,13 +8931,6 @@
};
/*
-** Possible values for FuncDef.flags
-*/
-#define SQLITE_FUNC_LIKE 0x01 /* Candidate for the LIKE optimization */
-#define SQLITE_FUNC_CASE 0x02 /* Case-sensitive LIKE-type function */
-#define SQLITE_FUNC_EPHEM 0x04 /* Ephermeral. Delete with VDBE */
-
-/*
** information about each column of an SQL table is held in an instance
** of this structure.
*/
@@ -8725,7 +8978,7 @@
};
/*
-** Allowed values of CollSeq flags:
+** Allowed values of CollSeq.type:
*/
#define SQLITE_COLL_BINARY 1 /* The default memcmp() collating sequence */
#define SQLITE_COLL_NOCASE 2 /* The built-in NOCASE collating sequence */
@@ -8743,7 +8996,7 @@
**
** These used to have mnemonic name like 'i' for SQLITE_AFF_INTEGER and
** 't' for SQLITE_AFF_TEXT. But we can save a little space and improve
-** the speed a little by number the values consecutively.
+** the speed a little by numbering the values consecutively.
**
** But rather than start with 0 or 1, we begin with 'a'. That way,
** when multiple affinity types are concatenated into a string and
@@ -8789,14 +9042,14 @@
** that the datatype of the PRIMARY KEY must be INTEGER for this field to
** be set. An INTEGER PRIMARY KEY is used as the rowid for each row of
** the table. If a table has no INTEGER PRIMARY KEY, then a random rowid
-** is generated for each row of the table. Table.hasPrimKey is true if
+** is generated for each row of the table. TF_HasPrimaryKey is set if
** the table has any PRIMARY KEY, INTEGER or otherwise.
**
** Table.tnum is the page number for the root BTree page of the table in the
** database file. If Table.iDb is the index of the database table backend
** in sqlite.aDb[]. 0 is for the main database and 1 is for the file that
-** holds temporary tables and indices. If Table.isEphem
-** is true, then the table is stored in a file that is automatically deleted
+** holds temporary tables and indices. If TF_Ephemeral is set
+** then the table is stored in a file that is automatically deleted
** when the VDBE cursor to the table is closed. In this case Table.tnum
** refers VDBE cursor number that holds the table open, not to the root
** page number. Transient tables are used to hold the results of a
@@ -8804,47 +9057,54 @@
** of a SELECT statement.
*/
struct Table {
- sqlite3 *db; /* Associated database connection. Might be NULL. */
- char *zName; /* Name of the table */
- int nCol; /* Number of columns in this table */
- Column *aCol; /* Information about each column */
- int iPKey; /* If not less then 0, use aCol[iPKey] as the primary key */
- Index *pIndex; /* List of SQL indexes on this table. */
- int tnum; /* Root BTree node for this table (see note above) */
- Select *pSelect; /* NULL for tables. Points to definition if a view. */
- int nRef; /* Number of pointers to this Table */
- Trigger *pTrigger; /* List of SQL triggers on this table */
- FKey *pFKey; /* Linked list of all foreign keys in this table */
- char *zColAff; /* String defining the affinity of each column */
+ sqlite3 *db; /* Associated database connection. Might be NULL. */
+ char *zName; /* Name of the table or view */
+ int iPKey; /* If not negative, use aCol[iPKey] as the primary key */
+ int nCol; /* Number of columns in this table */
+ Column *aCol; /* Information about each column */
+ Index *pIndex; /* List of SQL indexes on this table. */
+ int tnum; /* Root BTree node for this table (see note above) */
+ Select *pSelect; /* NULL for tables. Points to definition if a view. */
+ u16 nRef; /* Number of pointers to this Table */
+ u8 tabFlags; /* Mask of TF_* values */
+ u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */
+ Trigger *pTrigger; /* List of SQL triggers on this table */
+ FKey *pFKey; /* Linked list of all foreign keys in this table */
+ char *zColAff; /* String defining the affinity of each column */
#ifndef SQLITE_OMIT_CHECK
- Expr *pCheck; /* The AND of all CHECK constraints */
+ Expr *pCheck; /* The AND of all CHECK constraints */
#endif
#ifndef SQLITE_OMIT_ALTERTABLE
- int addColOffset; /* Offset in CREATE TABLE statement to add a new column */
+ int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */
#endif
- u8 readOnly; /* True if this table should not be written by the user */
- u8 isEphem; /* True if created using OP_OpenEphermeral */
- u8 hasPrimKey; /* True if there exists a primary key */
- u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */
- u8 autoInc; /* True if the integer primary key is autoincrement */
#ifndef SQLITE_OMIT_VIRTUALTABLE
- u8 isVirtual; /* True if this is a virtual table */
- u8 isCommit; /* True once the CREATE TABLE has been committed */
- Module *pMod; /* Pointer to the implementation of the module */
- sqlite3_vtab *pVtab; /* Pointer to the module instance */
- int nModuleArg; /* Number of arguments to the module */
- char **azModuleArg; /* Text of all module args. [0] is module name */
+ Module *pMod; /* Pointer to the implementation of the module */
+ sqlite3_vtab *pVtab; /* Pointer to the module instance */
+ int nModuleArg; /* Number of arguments to the module */
+ char **azModuleArg; /* Text of all module args. [0] is module name */
#endif
- Schema *pSchema; /* Schema that contains this table */
+ Schema *pSchema; /* Schema that contains this table */
};
/*
+** Allowed values for Tabe.tabFlags.
+*/
+#define TF_Readonly 0x01 /* Read-only system table */
+#define TF_Ephemeral 0x02 /* An emphermal table */
+#define TF_HasPrimaryKey 0x04 /* Table has a primary key */
+#define TF_Autoincrement 0x08 /* Integer primary key is autoincrement */
+#define TF_Virtual 0x10 /* Is a virtual table */
+#define TF_NeedMetadata 0x20 /* aCol[].zType and aCol[].pColl missing */
+
+
+
+/*
** Test to see whether or not a table is a virtual table. This is
** done as a macro so that it will be optimized out when virtual
** table support is omitted from the build.
*/
#ifndef SQLITE_OMIT_VIRTUALTABLE
-# define IsVirtual(X) ((X)->isVirtual)
+# define IsVirtual(X) (((X)->tabFlags & TF_Virtual)!=0)
# define IsHiddenColumn(X) ((X)->isHidden)
#else
# define IsVirtual(X) 0
@@ -8936,22 +9196,46 @@
** An instance of the following structure is passed as the first
** argument to sqlite3VdbeKeyCompare and is used to control the
** comparison of the two index keys.
-**
-** If the KeyInfo.incrKey value is true and the comparison would
-** otherwise be equal, then return a result as if the second key
-** were larger.
*/
struct KeyInfo {
sqlite3 *db; /* The database connection */
u8 enc; /* Text encoding - one of the TEXT_Utf* values */
- u8 incrKey; /* Increase 2nd key by epsilon before comparison */
- u8 prefixIsEqual; /* Treat a prefix as equal */
- int nField; /* Number of entries in aColl[] */
+ u16 nField; /* Number of entries in aColl[] */
u8 *aSortOrder; /* If defined an aSortOrder[i] is true, sort DESC */
CollSeq *aColl[1]; /* Collating sequence for each term of the key */
};
/*
+** An instance of the following structure holds information about a
+** single index record that has already been parsed out into individual
+** values.
+**
+** A record is an object that contains one or more fields of data.
+** Records are used to store the content of a table row and to store
+** the key of an index. A blob encoding of a record is created by
+** the OP_MakeRecord opcode of the VDBE and is disassemblied by the
+** OP_Column opcode.
+**
+** This structure holds a record that has already been disassembled
+** into its constitutent fields.
+*/
+struct UnpackedRecord {
+ KeyInfo *pKeyInfo; /* Collation and sort-order information */
+ u16 nField; /* Number of entries in apMem[] */
+ u16 flags; /* Boolean settings. UNPACKED_... below */
+ Mem *aMem; /* Values */
+};
+
+/*
+** Allowed values of UnpackedRecord.flags
+*/
+#define UNPACKED_NEED_FREE 0x0001 /* Memory is from sqlite3Malloc() */
+#define UNPACKED_NEED_DESTROY 0x0002 /* apMem[]s should all be destroyed */
+#define UNPACKED_IGNORE_ROWID 0x0004 /* Ignore trailing rowid on key1 */
+#define UNPACKED_INCRKEY 0x0008 /* Make this key an epsilon larger */
+#define UNPACKED_PREFIX_MATCH 0x0010 /* A prefix match is considered OK */
+
+/*
** Each SQL index is represented in memory by an
** instance of the following structure.
**
@@ -9114,7 +9398,7 @@
int iRightJoinTable; /* If EP_FromJoin, the right table of the join */
Select *pSelect; /* When the expression is a sub-select. Also the
** right side of "<expr> IN (<select>)" */
- Table *pTab; /* Table for OP_Column expressions. */
+ Table *pTab; /* Table for TK_COLUMN expressions. */
#if SQLITE_MAX_EXPR_DEPTH>0
int nHeight; /* Height of the tree headed by this node */
#endif
@@ -9160,8 +9444,9 @@
Expr *pExpr; /* The list of expressions */
char *zName; /* Token associated with this expression */
u8 sortOrder; /* 1 for DESC or 0 for ASC */
- u8 isAgg; /* True if this is an aggregate like count(*) */
u8 done; /* A flag to indicate when processing is finished */
+ u16 iCol; /* For ORDER BY, column number in result set */
+ u16 iAlias; /* Index into Parse.aAlias[] for zName */
} *a; /* One entry for each expression */
};
@@ -9375,12 +9660,8 @@
struct Select {
ExprList *pEList; /* The fields of the result */
u8 op; /* One of: TK_UNION TK_ALL TK_INTERSECT TK_EXCEPT */
- u8 isDistinct; /* True if the DISTINCT keyword is present */
- u8 isResolved; /* True once sqlite3SelectResolve() has run. */
- u8 isAgg; /* True if this is an aggregate query */
- u8 usesEphm; /* True if uses an OpenEphemeral opcode */
- u8 disallowOrderBy; /* Do not allow an ORDER BY to be attached if TRUE */
char affinity; /* MakeRecord with this affinity for SRT_Set */
+ u16 selFlags; /* Various SF_* values */
SrcList *pSrc; /* The FROM clause */
Expr *pWhere; /* The WHERE clause */
ExprList *pGroupBy; /* The GROUP BY clause */
@@ -9396,7 +9677,20 @@
};
/*
-** The results of a select can be distributed in several ways.
+** Allowed values for Select.selFlags. The "SF" prefix stands for
+** "Select Flag".
+*/
+#define SF_Distinct 0x0001 /* Output should be DISTINCT */
+#define SF_Resolved 0x0002 /* Identifiers have been resolved */
+#define SF_Aggregate 0x0004 /* Contains aggregate functions */
+#define SF_UsesEphemeral 0x0008 /* Uses the OpenEphemeral opcode */
+#define SF_Expanded 0x0010 /* sqlite3SelectExpand() called on this */
+#define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */
+
+
+/*
+** The results of a select can be distributed in several ways. The
+** "SRT" prefix means "SELECT Result Type".
*/
#define SRT_Union 1 /* Store result as keys in an index */
#define SRT_Except 2 /* Remove result from a UNION index */
@@ -9406,7 +9700,7 @@
/* The ORDER BY clause is ignored for all of the above */
#define IgnorableOrderby(X) ((X->eDest)<=SRT_Discard)
-#define SRT_Callback 5 /* Invoke a callback with each row of result */
+#define SRT_Output 5 /* Output each row of result */
#define SRT_Mem 6 /* Store result in a memory cell */
#define SRT_Set 7 /* Store results as keys in an index */
#define SRT_Table 8 /* Store result as data with an automatic rowid */
@@ -9489,6 +9783,8 @@
int nVarExpr; /* Number of used slots in apVarExpr[] */
int nVarExprAlloc; /* Number of allocated slots in apVarExpr[] */
Expr **apVarExpr; /* Pointers to :aaa and $aaaa wildcard expressions */
+ int nAlias; /* Number of aliased result set columns */
+ int *aAlias; /* Register used to hold aliased result */
u8 explain; /* True if the EXPLAIN flag is found on the query */
Token sErrToken; /* The token at which the error occurred */
Token sNameToken; /* Token with unqualified schema object name */
@@ -9730,11 +10026,40 @@
int isInit; /* True after initialization has finished */
int isMallocInit; /* True after malloc is initialized */
sqlite3_mutex *pInitMutex; /* Mutex used by sqlite3_initialize() */
+ int nRefInitMutex; /* Number of users of pInitMutex */
int nSmall; /* alloc size threshold used by mem6.c */
int mxParserStack; /* maximum depth of the parser stack */
};
/*
+** Context pointer passed down through the tree-walk.
+*/
+struct Walker {
+ int (*xExprCallback)(Walker*, Expr*); /* Callback for expressions */
+ int (*xSelectCallback)(Walker*,Select*); /* Callback for SELECTs */
+ Parse *pParse; /* Parser context. */
+ union { /* Extra data for callback */
+ NameContext *pNC; /* Naming context */
+ int i; /* Integer value */
+ } u;
+};
+
+/* Forward declarations */
+SQLITE_PRIVATE int sqlite3WalkExpr(Walker*, Expr*);
+SQLITE_PRIVATE int sqlite3WalkExprList(Walker*, ExprList*);
+SQLITE_PRIVATE int sqlite3WalkSelect(Walker*, Select*);
+SQLITE_PRIVATE int sqlite3WalkSelectExpr(Walker*, Select*);
+SQLITE_PRIVATE int sqlite3WalkSelectFrom(Walker*, Select*);
+
+/*
+** Return code from the parse-tree walking primitives and their
+** callbacks.
+*/
+#define WRC_Continue 0
+#define WRC_Prune 1
+#define WRC_Abort 2
+
+/*
** Assuming zIn points to the first byte of a UTF-8 character,
** advance zIn to point to the first byte of the next UTF-8 character.
*/
@@ -9797,7 +10122,6 @@
SQLITE_PRIVATE int sqlite3MutexEnd(void);
#endif
-SQLITE_PRIVATE void sqlite3StatusReset(void);
SQLITE_PRIVATE int sqlite3StatusValue(int);
SQLITE_PRIVATE void sqlite3StatusAdd(int, int);
SQLITE_PRIVATE void sqlite3StatusSet(int, int);
@@ -9842,7 +10166,7 @@
SQLITE_PRIVATE void sqlite3ResetInternalSchema(sqlite3*, int);
SQLITE_PRIVATE void sqlite3BeginParse(Parse*,int);
SQLITE_PRIVATE void sqlite3CommitInternalChanges(sqlite3*);
-SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse*,char*,Select*);
+SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse*,Select*);
SQLITE_PRIVATE void sqlite3OpenMasterTable(Parse *, int);
SQLITE_PRIVATE void sqlite3StartTable(Parse*,Token*,Token*,int,int,int,int);
SQLITE_PRIVATE void sqlite3AddColumn(Parse*,Token*);
@@ -9885,7 +10209,7 @@
SQLITE_PRIVATE void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*,
Token*, int, int);
SQLITE_PRIVATE void sqlite3DropIndex(Parse*, SrcList*, int);
-SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*, Select*, int, int*);
+SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*);
SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*,
Expr*,ExprList*,int,Expr*,Expr*);
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3*, Select*);
@@ -9920,7 +10244,6 @@
SQLITE_PRIVATE int sqlite3RunVacuum(char**, sqlite3*);
SQLITE_PRIVATE char *sqlite3NameFromToken(sqlite3*, Token*);
SQLITE_PRIVATE int sqlite3ExprCompare(Expr*, Expr*);
-SQLITE_PRIVATE int sqlite3ExprResolveNames(NameContext *, Expr *);
SQLITE_PRIVATE void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*);
SQLITE_PRIVATE void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*);
SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse*);
@@ -9952,9 +10275,12 @@
SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3*,SrcList*);
SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3*,IdList*);
SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3*,Select*);
+SQLITE_PRIVATE void sqlite3FuncDefInsert(FuncDefHash*, FuncDef*);
SQLITE_PRIVATE FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,int,u8,int);
SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(sqlite3*);
-SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(sqlite3*);
+SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void);
+SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void);
+SQLITE_PRIVATE int sqlite3GetBuiltinFunction(const char *, int, FuncDef **);
#ifdef SQLITE_DEBUG
SQLITE_PRIVATE int sqlite3SafetyOn(sqlite3*);
SQLITE_PRIVATE int sqlite3SafetyOff(sqlite3*);
@@ -9965,7 +10291,7 @@
SQLITE_PRIVATE int sqlite3SafetyCheckOk(sqlite3*);
SQLITE_PRIVATE int sqlite3SafetyCheckSickOrOk(sqlite3*);
SQLITE_PRIVATE void sqlite3ChangeCookie(Parse*, int);
-SQLITE_PRIVATE void sqlite3MaterializeView(Parse*, Select*, Expr*, int);
+SQLITE_PRIVATE void sqlite3MaterializeView(Parse*, Table*, Expr*, int);
#ifndef SQLITE_OMIT_TRIGGER
SQLITE_PRIVATE void sqlite3BeginTrigger(Parse*, Token*,Token*,int,int,IdList*,SrcList*,
@@ -10018,7 +10344,6 @@
SQLITE_PRIVATE int sqlite3FixExprList(DbFixer*, ExprList*);
SQLITE_PRIVATE int sqlite3FixTriggerStep(DbFixer*, TriggerStep*);
SQLITE_PRIVATE int sqlite3AtoF(const char *z, double*);
-SQLITE_API char *sqlite3_snprintf(int,char*,const char*,...);
SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*);
SQLITE_PRIVATE int sqlite3FitsIn64Bits(const char *, int);
SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar);
@@ -10092,6 +10417,7 @@
#ifndef SQLITE_AMALGAMATION
SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[];
SQLITE_PRIVATE struct Sqlite3Config sqlite3Config;
+SQLITE_PRIVATE FuncDefHash sqlite3GlobalFunctions;
#endif
SQLITE_PRIVATE void sqlite3RootPageMoved(Db*, int, int);
SQLITE_PRIVATE void sqlite3Reindex(Parse*, Token*, Token*);
@@ -10101,7 +10427,10 @@
SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...);
SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*);
SQLITE_PRIVATE void sqlite3CodeSubselect(Parse *, Expr *, int);
-SQLITE_PRIVATE int sqlite3SelectResolve(Parse *, Select *, NameContext *);
+SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*);
+SQLITE_PRIVATE int sqlite3ResolveExprNames(NameContext*, Expr*);
+SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*);
+SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*);
SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *, Table *, int);
SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *, Token *);
SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *, SrcList *);
@@ -10223,9 +10552,11 @@
#if SQLITE_MAX_EXPR_DEPTH>0
SQLITE_PRIVATE void sqlite3ExprSetHeight(Parse *pParse, Expr *p);
SQLITE_PRIVATE int sqlite3SelectExprHeight(Select *);
+SQLITE_PRIVATE int sqlite3ExprCheckHeight(Parse*, int);
#else
#define sqlite3ExprSetHeight(x,y)
#define sqlite3SelectExprHeight(x) 0
+ #define sqlite3ExprCheckHeight(x,y)
#endif
SQLITE_PRIVATE u32 sqlite3Get4byte(const u8*);
@@ -10271,7 +10602,7 @@
**
** This file contains definitions of global variables and contants.
**
-** $Id: global.c,v 1.4 2008/07/28 19:34:53 drh Exp $
+** $Id: global.c,v 1.6 2008/08/21 20:21:35 drh Exp $
*/
@@ -10334,6 +10665,14 @@
/* Other fields all default to zero */
};
+
+/*
+** Hash table for global functions - functions common to all
+** database connections. After initialization, this table is
+** read-only.
+*/
+SQLITE_PRIVATE FuncDefHash sqlite3GlobalFunctions;
+
/************** End of global.c **********************************************/
/************** Begin file status.c ******************************************/
/*
@@ -10351,7 +10690,7 @@
** This module implements the sqlite3_status() interface and related
** functionality.
**
-** $Id: status.c,v 1.7 2008/08/05 17:53:23 drh Exp $
+** $Id: status.c,v 1.8 2008/08/12 15:21:12 drh Exp $
*/
/*
@@ -10364,14 +10703,6 @@
/*
-** Reset the status records. This routine is called by
-** sqlite3_initialize().
-*/
-SQLITE_PRIVATE void sqlite3StatusReset(void){
- memset(&sqlite3Stat, 0, sizeof(sqlite3Stat));
-}
-
-/*
** Return the current value of a status parameter.
*/
SQLITE_PRIVATE int sqlite3StatusValue(int op){
@@ -10467,7 +10798,7 @@
** sqlite3RegisterDateTimeFunctions() found at the bottom of the file.
** All other code has file scope.
**
-** $Id: date.c,v 1.87 2008/07/28 19:34:53 drh Exp $
+** $Id: date.c,v 1.88 2008/08/21 20:21:35 drh Exp $
**
** SQLite processes all times and dates as Julian Day numbers. The
** dates and times are stored as the number of days since noon
@@ -11500,44 +11831,28 @@
** functions. This should be the only routine in this file with
** external linkage.
*/
-SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(sqlite3 *db){
+SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void){
+ static FuncDef aDateTimeFuncs[] = {
#ifndef SQLITE_OMIT_DATETIME_FUNCS
- static const struct {
- char *zName;
- int nArg;
- void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
- } aFuncs[] = {
- { "julianday", -1, juliandayFunc },
- { "date", -1, dateFunc },
- { "time", -1, timeFunc },
- { "datetime", -1, datetimeFunc },
- { "strftime", -1, strftimeFunc },
- { "current_time", 0, ctimeFunc },
- { "current_timestamp", 0, ctimestampFunc },
- { "current_date", 0, cdateFunc },
- };
- int i;
-
- for(i=0; i<sizeof(aFuncs)/sizeof(aFuncs[0]); i++){
- sqlite3CreateFunc(db, aFuncs[i].zName, aFuncs[i].nArg,
- SQLITE_UTF8, 0, aFuncs[i].xFunc, 0, 0);
- }
+ FUNCTION(julianday, -1, 0, 0, juliandayFunc ),
+ FUNCTION(date, -1, 0, 0, dateFunc ),
+ FUNCTION(time, -1, 0, 0, timeFunc ),
+ FUNCTION(datetime, -1, 0, 0, datetimeFunc ),
+ FUNCTION(strftime, -1, 0, 0, strftimeFunc ),
+ FUNCTION(current_time, 0, 0, 0, ctimeFunc ),
+ FUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc),
+ FUNCTION(current_date, 0, 0, 0, cdateFunc ),
#else
- static const struct {
- char *zName;
- char *zFormat;
- } aFuncs[] = {
- { "current_time", "%H:%M:%S" },
- { "current_date", "%Y-%m-%d" },
- { "current_timestamp", "%Y-%m-%d %H:%M:%S" }
+ FUNCTION(current_time, 0, "%H:%M:%S", 0, currentTimeFunc),
+ FUNCTION(current_timestamp, 0, "%Y-%m-%d", 0, currentTimeFunc),
+ FUNCTION(current_date, 0, "%Y-%m-%d %H:%M:%S", 0, currentTimeFunc),
+#endif
};
int i;
- for(i=0; i<sizeof(aFuncs)/sizeof(aFuncs[0]); i++){
- sqlite3CreateFunc(db, aFuncs[i].zName, 0, SQLITE_UTF8,
- aFuncs[i].zFormat, currentTimeFunc, 0, 0);
+ for(i=0; i<ArraySize(aDateTimeFuncs); i++){
+ sqlite3FuncDefInsert(&sqlite3GlobalFunctions, &aDateTimeFuncs[i]);
}
-#endif
}
/************** End of date.c ************************************************/
@@ -12064,7 +12379,7 @@
** This file contains implementations of the low-level memory allocation
** routines specified in the sqlite3_mem_methods object.
**
-** $Id: mem2.c,v 1.37 2008/07/25 08:49:00 danielk1977 Exp $
+** $Id: mem2.c,v 1.38 2008/08/12 15:04:59 danielk1977 Exp $
*/
/*
@@ -12080,7 +12395,7 @@
extern int backtrace(void**,int);
extern void backtrace_symbols_fd(void*const*,int,int);
#else
-# define backtrace(A,B) 0
+# define backtrace(A,B) 1
# define backtrace_symbols_fd(A,B,C)
#endif
@@ -15323,7 +15638,7 @@
**
** Memory allocation functions used throughout sqlite.
**
-** $Id: malloc.c,v 1.34 2008/08/05 17:53:23 drh Exp $
+** $Id: malloc.c,v 1.37 2008/08/29 17:56:13 danielk1977 Exp $
*/
/*
@@ -15370,8 +15685,11 @@
*/
SQLITE_API int sqlite3_release_memory(int n){
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
- int nRet = sqlite3VdbeReleaseMemory(n);
- nRet += sqlite3PagerReleaseMemory(n-nRet);
+ int nRet = 0;
+#if 0
+ nRet += sqlite3VdbeReleaseMemory(n);
+#endif
+ nRet += sqlite3PcacheReleaseMemory(n-nRet);
return nRet;
#else
return SQLITE_OK;
@@ -15617,10 +15935,10 @@
}else{
int i;
i = mem0.aScratchFree[--mem0.nScratchFree];
- sqlite3_mutex_leave(mem0.mutex);
i *= sqlite3Config.szScratch;
sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, 1);
sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n);
+ sqlite3_mutex_leave(mem0.mutex);
p = (void*)&((char*)sqlite3Config.pScratch)[i];
}
}
@@ -15690,6 +16008,7 @@
** and that memory is of the right size and is not completely
** consumed. Otherwise, failover to sqlite3Malloc().
*/
+#if 0
SQLITE_PRIVATE void *sqlite3PageMalloc(int n){
void *p;
assert( n>0 );
@@ -15770,6 +16089,7 @@
}
}
}
+#endif
/*
** TRUE if p is a lookaside memory allocation from db
@@ -16058,7 +16378,7 @@
** an historical reference. Most of the "enhancements" have been backed
** out so that the functionality is now the same as standard printf().
**
-** $Id: printf.c,v 1.93 2008/07/28 19:34:53 drh Exp $
+** $Id: printf.c,v 1.94 2008/08/22 14:08:36 drh Exp $
**
**************************************************************************
**
@@ -16298,7 +16618,6 @@
const et_info *infop; /* Pointer to the appropriate info structure */
char buf[etBUFSIZE]; /* Conversion buffer */
char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */
- etByte errorflag = 0; /* True if an error is encountered */
etByte xtype; /* Conversion paradigm */
char *zExtra; /* Extra memory used for etTCLESCAPE conversions */
#ifndef SQLITE_OMIT_FLOATING_POINT
@@ -16322,7 +16641,6 @@
if( c==0 ) break;
}
if( (c=(*++fmt))==0 ){
- errorflag = 1;
sqlite3StrAccumAppend(pAccum, "%", 1);
break;
}
@@ -17125,7 +17443,7 @@
** This file contains routines used to translate between UTF-8,
** UTF-16, UTF-16BE, and UTF-16LE.
**
-** $Id: utf.c,v 1.63 2008/07/29 11:25:14 danielk1977 Exp $
+** $Id: utf.c,v 1.65 2008/08/12 15:04:59 danielk1977 Exp $
**
** Notes on UTF-8:
**
@@ -17167,7 +17485,7 @@
** 6000 lines long) it was split up into several smaller files and
** this header information was factored out.
**
-** $Id: vdbeInt.h,v 1.153 2008/08/02 03:50:39 drh Exp $
+** $Id: vdbeInt.h,v 1.154 2008/08/13 19:11:48 drh Exp $
*/
#ifndef _VDBEINT_H_
#define _VDBEINT_H_
@@ -17223,13 +17541,11 @@
Bool deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */
Bool isTable; /* True if a table requiring integer keys */
Bool isIndex; /* True if an index containing keys only - no data */
- u8 bogusIncrKey; /* Something for pIncrKey to point to if pKeyInfo==0 */
i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */
Btree *pBt; /* Separate file holding temporary table */
int nData; /* Number of bytes in pData */
char *pData; /* Data for a NEW or OLD pseudo-table */
i64 iKey; /* Key for the NEW or OLD pseudo-table row */
- u8 *pIncrKey; /* Pointer to pKeyInfo->incrKey */
KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */
int nField; /* Number of fields in the header */
i64 seqCount; /* Sequence counter */
@@ -17494,28 +17810,6 @@
};
/*
-** An instance of the following structure holds information about a
-** single index record that has already been parsed out into individual
-** values.
-**
-** A record is an object that contains one or more fields of data.
-** Records are used to store the content of a table row and to store
-** the key of an index. A blob encoding of a record is created by
-** the OP_MakeRecord opcode of the VDBE and is disassemblied by the
-** OP_Column opcode.
-**
-** This structure holds a record that has already been disassembled
-** into its constitutent fields.
-*/
-struct UnpackedRecord {
- KeyInfo *pKeyInfo; /* Collation and sort-order information */
- u16 nField; /* Number of entries in apMem[] */
- u8 needFree; /* True if memory obtained from sqlite3_malloc() */
- u8 needDestroy; /* True if apMem[]s should be destroyed on close */
- Mem *aMem; /* Values */
-};
-
-/*
** The following are allowed values for Vdbe.magic
*/
#define VDBE_MAGIC_INIT 0x26bceaa5 /* Building a VDBE program */
@@ -17539,10 +17833,9 @@
SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(VdbeFunc*, int);
int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *);
-SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(Cursor*,UnpackedRecord *,int,const unsigned char*,int*);
+SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(Cursor*,UnpackedRecord*,int*);
SQLITE_PRIVATE int sqlite3VdbeIdxRowid(BtCursor *, i64 *);
SQLITE_PRIVATE int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*);
-SQLITE_PRIVATE int sqlite3VdbeIdxRowidLen(const u8*, int, int*);
SQLITE_PRIVATE int sqlite3VdbeExec(Vdbe*);
SQLITE_PRIVATE int sqlite3VdbeList(Vdbe*);
SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe*);
@@ -17966,7 +18259,7 @@
SQLITE_PRIVATE int sqlite3Utf8To8(unsigned char *zIn){
unsigned char *zOut = zIn;
unsigned char *zStart = zIn;
- unsigned char *zTerm;
+ unsigned char *zTerm = &zIn[strlen((char *)zIn)];
u32 c;
while( zIn[0] ){
@@ -18043,7 +18336,7 @@
** It checks that the primitives for serializing and deserializing
** characters in each encoding are inverses of each other.
*/
-SQLITE_PRIVATE void sqlite3UtfSelfTest(){
+SQLITE_PRIVATE void sqlite3UtfSelfTest(void){
unsigned int i, t;
unsigned char zBuf[20];
unsigned char *z;
@@ -19637,7 +19930,7 @@
**
** This file contains code that is specific to OS/2.
**
-** $Id: os_os2.c,v 1.55 2008/07/29 18:49:29 pweilbacher Exp $
+** $Id: os_os2.c,v 1.56 2008/08/22 13:47:57 pweilbacher Exp $
*/
@@ -20602,7 +20895,7 @@
int *pOutFlags /* Status return flags */
){
HFILE h;
- ULONG ulFileAttribute = 0;
+ ULONG ulFileAttribute = FILE_NORMAL;
ULONG ulOpenFlags = 0;
ULONG ulOpenMode = 0;
os2File *pFile = (os2File*)id;
@@ -20627,7 +20920,6 @@
OSTRACE2( "OPEN want %d\n", flags );
- /*ulOpenMode = flags & SQLITE_OPEN_READWRITE ? OPEN_ACCESS_READWRITE : OPEN_ACCESS_READONLY;*/
if( flags & SQLITE_OPEN_READWRITE ){
ulOpenMode |= OPEN_ACCESS_READWRITE;
OSTRACE1( "OPEN read/write\n" );
@@ -20636,7 +20928,6 @@
OSTRACE1( "OPEN read only\n" );
}
- /*ulOpenFlags = flags & SQLITE_OPEN_CREATE ? OPEN_ACTION_CREATE_IF_NEW : OPEN_ACTION_FAIL_IF_NEW;*/
if( flags & SQLITE_OPEN_CREATE ){
ulOpenFlags |= OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW;
OSTRACE1( "OPEN open new/create\n" );
@@ -20645,7 +20936,6 @@
OSTRACE1( "OPEN open existing\n" );
}
- /*ulOpenMode |= flags & SQLITE_OPEN_MAIN_DB ? OPEN_SHARE_DENYNONE : OPEN_SHARE_DENYWRITE;*/
if( flags & SQLITE_OPEN_MAIN_DB ){
ulOpenMode |= OPEN_SHARE_DENYNONE;
OSTRACE1( "OPEN share read/write\n" );
@@ -20654,18 +20944,15 @@
OSTRACE1( "OPEN share read only\n" );
}
- if( flags & (SQLITE_OPEN_TEMP_DB | SQLITE_OPEN_TEMP_JOURNAL
- | SQLITE_OPEN_SUBJOURNAL) ){
+ if( flags & SQLITE_OPEN_DELETEONCLOSE ){
char pathUtf8[CCHMAXPATH];
#ifdef NDEBUG /* when debugging we want to make sure it is deleted */
ulFileAttribute = FILE_HIDDEN;
#endif
- ulFileAttribute = FILE_NORMAL;
os2FullPathname( pVfs, zName, CCHMAXPATH, pathUtf8 );
pFile->pathToDel = convertUtf8PathToCp( pathUtf8 );
OSTRACE1( "OPEN hidden/delete on close file attributes\n" );
}else{
- ulFileAttribute = FILE_ARCHIVED | FILE_NORMAL;
pFile->pathToDel = NULL;
OSTRACE1( "OPEN normal file attribute\n" );
}
@@ -20990,7 +21277,7 @@
**
** This file contains code that is specific to Unix systems.
**
-** $Id: os_unix.c,v 1.195 2008/07/30 17:28:04 drh Exp $
+** $Id: os_unix.c,v 1.198 2008/08/22 18:41:37 aswift Exp $
*/
#if SQLITE_OS_UNIX /* This file is used on unix only */
@@ -21088,6 +21375,7 @@
#if SQLITE_THREADSAFE
pthread_t tid; /* The thread that "owns" this unixFile */
#endif
+ int lastErrno; /* The unix errno from the last I/O error */
};
/*
@@ -21578,6 +21866,12 @@
#define LOCKING_STYLE_AFP 5
/*
+** Only set the lastErrno if the error code is a real error and not
+** a normal expected return code of SQLITE_BUSY or SQLITE_OK
+*/
+#define IS_LOCK_ERROR(x) ((x != SQLITE_OK) && (x != SQLITE_BUSY))
+
+/*
** Helper functions to obtain and relinquish the global mutex.
*/
static void enterMutex(){
@@ -21823,7 +22117,11 @@
{ "hfs", LOCKING_STYLE_POSIX },
{ "ufs", LOCKING_STYLE_POSIX },
{ "afpfs", LOCKING_STYLE_AFP },
+#ifdef SQLITE_ENABLE_AFP_LOCKING_SMB
+ { "smbfs", LOCKING_STYLE_AFP },
+#else
{ "smbfs", LOCKING_STYLE_FLOCK },
+#endif
{ "msdos", LOCKING_STYLE_DOTFILE },
{ "webdav", LOCKING_STYLE_NONE },
{ 0, 0 }
@@ -21835,7 +22133,7 @@
return LOCKING_STYLE_NONE;
}
if( pVfs->pAppData ){
- return (int)pVfs->pAppData;
+ return SQLITE_PTR_TO_INT(pVfs->pAppData);
}
if( statfs(filePath, &fsInfo) != -1 ){
@@ -22331,13 +22629,77 @@
}
/*
+** This routine translates a standard POSIX errno code into something
+** useful to the clients of the sqlite3 functions. Specifically, it is
+** intended to translate a variety of "try again" errors into SQLITE_BUSY
+** and a variety of "please close the file descriptor NOW" errors into
+** SQLITE_IOERR
+**
+** Errors during initialization of locks, or file system support for locks,
+** should handle ENOLCK, ENOTSUP, EOPNOTSUPP separately.
+*/
+static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) {
+ switch (posixError) {
+ case 0:
+ return SQLITE_OK;
+
+ case EAGAIN:
+ case ETIMEDOUT:
+ case EBUSY:
+ case EINTR:
+ case ENOLCK:
+ /* random NFS retry error, unless during file system support
+ * introspection, in which it actually means what it says */
+ return SQLITE_BUSY;
+
+ case EACCES:
+ /* EACCES is like EAGAIN during locking operations, but not any other time*/
+ if( (sqliteIOErr == SQLITE_IOERR_LOCK) ||
+ (sqliteIOErr == SQLITE_IOERR_UNLOCK) ||
+ (sqliteIOErr == SQLITE_IOERR_RDLOCK) ||
+ (sqliteIOErr == SQLITE_IOERR_CHECKRESERVEDLOCK) ){
+ return SQLITE_BUSY;
+ }
+ /* else fall through */
+ case EPERM:
+ return SQLITE_PERM;
+
+ case EDEADLK:
+ return SQLITE_IOERR_BLOCKED;
+
+#if EOPNOTSUPP!=ENOTSUP
+ case EOPNOTSUPP:
+ /* something went terribly awry, unless during file system support
+ * introspection, in which it actually means what it says */
+#endif
+ case ENOTSUP:
+ /* invalid fd, unless during file system support introspection, in which
+ * it actually means what it says */
+ case EIO:
+ case EBADF:
+ case EINVAL:
+ case ENOTCONN:
+ case ENODEV:
+ case ENXIO:
+ case ENOENT:
+ case ESTALE:
+ case ENOSYS:
+ /* these should force the client to close the file and reconnect */
+
+ default:
+ return sqliteIOErr;
+ }
+}
+
+/*
** This routine checks if there is a RESERVED lock held on the specified
-** file by this or any other process. If such a lock is held, return
-** non-zero. If the file is unlocked or holds only SHARED locks, then
-** return zero.
+** file by this or any other process. If such a lock is held, set *pResOut
+** to a non-zero value otherwise *pResOut is set to zero. The return value
+** is set to SQLITE_OK unless an I/O error occurs during lock checking.
*/
static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){
- int r = 0;
+ int rc = SQLITE_OK;
+ int reserved = 0;
unixFile *pFile = (unixFile*)id;
SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
@@ -22347,28 +22709,31 @@
/* Check if a thread in this process holds such a lock */
if( pFile->pLock->locktype>SHARED_LOCK ){
- r = 1;
+ reserved = 1;
}
/* Otherwise see if some other process holds it.
*/
- if( !r ){
+ if( !reserved ){
struct flock lock;
lock.l_whence = SEEK_SET;
lock.l_start = RESERVED_BYTE;
lock.l_len = 1;
lock.l_type = F_WRLCK;
- fcntl(pFile->h, F_GETLK, &lock);
- if( lock.l_type!=F_UNLCK ){
- r = 1;
+ if (-1 == fcntl(pFile->h, F_GETLK, &lock)) {
+ int tErrno = errno;
+ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_CHECKRESERVEDLOCK);
+ pFile->lastErrno = tErrno;
+ } else if( lock.l_type!=F_UNLCK ){
+ reserved = 1;
}
}
leaveMutex();
- OSTRACE3("TEST WR-LOCK %d %d\n", pFile->h, r);
+ OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved);
- *pResOut = r;
- return SQLITE_OK;
+ *pResOut = reserved;
+ return rc;
}
/*
@@ -22514,7 +22879,11 @@
lock.l_start = PENDING_BYTE;
s = fcntl(pFile->h, F_SETLK, &lock);
if( s==(-1) ){
- rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY;
+ int tErrno = errno;
+ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
+ if( IS_LOCK_ERROR(rc) ){
+ pFile->lastErrno = tErrno;
+ }
goto end_lock;
}
}
@@ -22524,24 +22893,36 @@
** operating system calls for the specified lock.
*/
if( locktype==SHARED_LOCK ){
+ int tErrno = 0;
assert( pLock->cnt==0 );
assert( pLock->locktype==0 );
/* Now get the read-lock */
lock.l_start = SHARED_FIRST;
lock.l_len = SHARED_SIZE;
- s = fcntl(pFile->h, F_SETLK, &lock);
-
+ if( (s = fcntl(pFile->h, F_SETLK, &lock))==(-1) ){
+ tErrno = errno;
+ }
/* Drop the temporary PENDING lock */
lock.l_start = PENDING_BYTE;
lock.l_len = 1L;
lock.l_type = F_UNLCK;
if( fcntl(pFile->h, F_SETLK, &lock)!=0 ){
- rc = SQLITE_IOERR_UNLOCK; /* This should never happen */
- goto end_lock;
+ if( s != -1 ){
+ /* This could happen with a network mount */
+ tErrno = errno;
+ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
+ if( IS_LOCK_ERROR(rc) ){
+ pFile->lastErrno = tErrno;
+ }
+ goto end_lock;
+ }
}
if( s==(-1) ){
- rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY;
+ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
+ if( IS_LOCK_ERROR(rc) ){
+ pFile->lastErrno = tErrno;
+ }
}else{
pFile->locktype = SHARED_LOCK;
pFile->pOpen->nLock++;
@@ -22571,7 +22952,11 @@
}
s = fcntl(pFile->h, F_SETLK, &lock);
if( s==(-1) ){
- rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY;
+ int tErrno = errno;
+ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
+ if( IS_LOCK_ERROR(rc) ){
+ pFile->lastErrno = tErrno;
+ }
}
}
@@ -22630,7 +23015,12 @@
lock.l_start = SHARED_FIRST;
lock.l_len = SHARED_SIZE;
if( fcntl(h, F_SETLK, &lock)==(-1) ){
- rc = SQLITE_IOERR_RDLOCK;
+ int tErrno = errno;
+ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK);
+ if( IS_LOCK_ERROR(rc) ){
+ pFile->lastErrno = tErrno;
+ }
+ goto end_unlock;
}
}
lock.l_type = F_UNLCK;
@@ -22640,7 +23030,12 @@
if( fcntl(h, F_SETLK, &lock)!=(-1) ){
pLock->locktype = SHARED_LOCK;
}else{
- rc = SQLITE_IOERR_UNLOCK;
+ int tErrno = errno;
+ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
+ if( IS_LOCK_ERROR(rc) ){
+ pFile->lastErrno = tErrno;
+ }
+ goto end_unlock;
}
}
if( locktype==NO_LOCK ){
@@ -22661,8 +23056,13 @@
if( fcntl(h, F_SETLK, &lock)!=(-1) ){
pLock->locktype = NO_LOCK;
}else{
- rc = SQLITE_IOERR_UNLOCK;
+ int tErrno = errno;
+ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
+ if( IS_LOCK_ERROR(rc) ){
+ pFile->lastErrno = tErrno;
+ }
pLock->cnt = 1;
+ goto end_unlock;
}
}
@@ -22685,6 +23085,8 @@
}
}
}
+
+end_unlock:
leaveMutex();
if( rc==SQLITE_OK ) pFile->locktype = locktype;
return rc;
@@ -22772,14 +23174,11 @@
#define afpfsByteRangeLock2FSCTL _IOWR('z', 23, struct ByteRangeLockPB2)
/*
-** Return 0 on success, 1 on failure. To match the behavior of the
-** normal posix file locking (used in unixLock for example), we should
-** provide 'richer' return codes - specifically to differentiate between
-** 'file busy' and 'file system error' results.
-*/
+ ** Return SQLITE_OK on success, SQLITE_BUSY on failure.
+ */
static int _AFPFSSetLock(
const char *path,
- int fd,
+ unixFile *pFile,
unsigned long long offset,
unsigned long long length,
int setLockFlag
@@ -22791,55 +23190,63 @@
pb.startEndFlag = 0;
pb.offset = offset;
pb.length = length;
- pb.fd = fd;
+ pb.fd = pFile->h;
OSTRACE5("AFPLOCK setting lock %s for %d in range %llx:%llx\n",
- (setLockFlag?"ON":"OFF"), fd, offset, length);
+ (setLockFlag?"ON":"OFF"), pFile->h, offset, length);
err = fsctl(path, afpfsByteRangeLock2FSCTL, &pb, 0);
if ( err==-1 ) {
- OSTRACE4("AFPLOCK failed to fsctl() '%s' %d %s\n", path, errno,
- strerror(errno));
- return 1; /* error */
+ int rc;
+ int tErrno = errno;
+ OSTRACE4("AFPLOCK failed to fsctl() '%s' %d %s\n", path, tErrno, strerror(tErrno));
+ rc = sqliteErrorFromPosixError(tErrno, setLockFlag ? SQLITE_IOERR_LOCK : SQLITE_IOERR_UNLOCK); /* error */
+ if( IS_LOCK_ERROR(rc) ){
+ pFile->lastErrno = tErrno;
+ }
+ return rc;
} else {
- return 0;
+ return SQLITE_OK;
}
}
-/*
- ** This routine checks if there is a RESERVED lock held on the specified
- ** file by this or any other process. If such a lock is held, return
- ** non-zero. If the file is unlocked or holds only SHARED locks, then
- ** return zero.
- */
+/* AFP-style reserved lock checking following the behavior of
+** unixCheckReservedLock, see the unixCheckReservedLock function comments */
static int afpCheckReservedLock(sqlite3_file *id, int *pResOut){
- int r = 0;
+ int rc = SQLITE_OK;
+ int reserved = 0;
unixFile *pFile = (unixFile*)id;
- assert( pFile );
+ SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
+
+ assert( pFile );
afpLockingContext *context = (afpLockingContext *) pFile->lockingContext;
/* Check if a thread in this process holds such a lock */
if( pFile->locktype>SHARED_LOCK ){
- r = 1;
+ reserved = 1;
}
/* Otherwise see if some other process holds it.
*/
- if ( !r ) {
- /* lock the byte */
- int failed = _AFPFSSetLock(context->filePath, pFile->h, RESERVED_BYTE, 1,1);
- if (failed) {
- /* if we failed to get the lock then someone else must have it */
- r = 1;
- } else {
+ if( !reserved ){
+ /* lock the RESERVED byte */
+ int lrc = _AFPFSSetLock(context->filePath, pFile, RESERVED_BYTE, 1,1);
+ if( SQLITE_OK==lrc ){
/* if we succeeded in taking the reserved lock, unlock it to restore
** the original state */
- _AFPFSSetLock(context->filePath, pFile->h, RESERVED_BYTE, 1, 0);
+ lrc = _AFPFSSetLock(context->filePath, pFile, RESERVED_BYTE, 1, 0);
+ } else {
+ /* if we failed to get the lock then someone else must have it */
+ reserved = 1;
+ }
+ if( IS_LOCK_ERROR(lrc) ){
+ rc=lrc;
}
}
- OSTRACE3("TEST WR-LOCK %d %d\n", pFile->h, r);
- *pResOut = r;
- return SQLITE_OK;
+ OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved);
+
+ *pResOut = reserved;
+ return rc;
}
/* AFP-style locking following the behavior of unixLock, see the unixLock
@@ -22889,9 +23296,9 @@
|| (locktype==EXCLUSIVE_LOCK && pFile->locktype<PENDING_LOCK)
){
int failed;
- failed = _AFPFSSetLock(context->filePath, pFile->h, PENDING_BYTE, 1, 1);
+ failed = _AFPFSSetLock(context->filePath, pFile, PENDING_BYTE, 1, 1);
if (failed) {
- rc = SQLITE_BUSY;
+ rc = failed;
goto afp_end_lock;
}
}
@@ -22900,23 +23307,29 @@
** operating system calls for the specified lock.
*/
if( locktype==SHARED_LOCK ){
- int lk, failed;
+ int lk, lrc1, lrc2, lrc1Errno;
- /* Now get the read-lock */
+ /* Now get the read-lock SHARED_LOCK */
/* note that the quality of the randomness doesn't matter that much */
lk = random();
context->sharedLockByte = (lk & 0x7fffffff)%(SHARED_SIZE - 1);
- failed = _AFPFSSetLock(context->filePath, pFile->h,
- SHARED_FIRST+context->sharedLockByte, 1, 1);
-
- /* Drop the temporary PENDING lock */
- if (_AFPFSSetLock(context->filePath, pFile->h, PENDING_BYTE, 1, 0)) {
- rc = SQLITE_IOERR_UNLOCK; /* This should never happen */
- goto afp_end_lock;
+ lrc1 = _AFPFSSetLock(context->filePath, pFile,
+ SHARED_FIRST+context->sharedLockByte, 1, 1);
+ if( IS_LOCK_ERROR(lrc1) ){
+ lrc1Errno = pFile->lastErrno;
}
+ /* Drop the temporary PENDING lock */
+ lrc2 = _AFPFSSetLock(context->filePath, pFile, PENDING_BYTE, 1, 0);
- if( failed ){
- rc = SQLITE_BUSY;
+ if( IS_LOCK_ERROR(lrc1) ) {
+ pFile->lastErrno = lrc1Errno;
+ rc = lrc1;
+ goto afp_end_lock;
+ } else if( IS_LOCK_ERROR(lrc2) ){
+ rc = lrc2;
+ goto afp_end_lock;
+ } else if( lrc1 != SQLITE_OK ) {
+ rc = lrc1;
} else {
pFile->locktype = SHARED_LOCK;
}
@@ -22929,7 +23342,7 @@
assert( 0!=pFile->locktype );
if (locktype >= RESERVED_LOCK && pFile->locktype < RESERVED_LOCK) {
/* Acquire a RESERVED lock */
- failed = _AFPFSSetLock(context->filePath, pFile->h, RESERVED_BYTE, 1,1);
+ failed = _AFPFSSetLock(context->filePath, pFile, RESERVED_BYTE, 1,1);
}
if (!failed && locktype == EXCLUSIVE_LOCK) {
/* Acquire an EXCLUSIVE lock */
@@ -22937,22 +23350,21 @@
/* Remove the shared lock before trying the range. we'll need to
** reestablish the shared lock if we can't get the afpUnlock
*/
- if (!_AFPFSSetLock(context->filePath, pFile->h, SHARED_FIRST +
- context->sharedLockByte, 1, 0)) {
+ if (!(failed = _AFPFSSetLock(context->filePath, pFile, SHARED_FIRST +
+ context->sharedLockByte, 1, 0))) {
/* now attemmpt to get the exclusive lock range */
- failed = _AFPFSSetLock(context->filePath, pFile->h, SHARED_FIRST,
+ failed = _AFPFSSetLock(context->filePath, pFile, SHARED_FIRST,
SHARED_SIZE, 1);
- if (failed && _AFPFSSetLock(context->filePath, pFile->h, SHARED_FIRST +
- context->sharedLockByte, 1, 1)) {
- rc = SQLITE_IOERR_RDLOCK; /* this should never happen */
+ if (failed && (failed = _AFPFSSetLock(context->filePath, pFile,
+ SHARED_FIRST + context->sharedLockByte, 1, 1))) {
+ rc = failed;
}
} else {
- /* */
- rc = SQLITE_IOERR_UNLOCK; /* this should never happen */
+ rc = failed;
}
}
- if( failed && rc == SQLITE_OK){
- rc = SQLITE_BUSY;
+ if( failed ){
+ rc = failed;
}
}
@@ -22984,7 +23396,7 @@
assert( pFile );
OSTRACE5("UNLOCK %d %d was %d pid=%d\n", pFile->h, locktype,
pFile->locktype, getpid());
-
+
assert( locktype<=SHARED_LOCK );
if( pFile->locktype<=locktype ){
return SQLITE_OK;
@@ -22993,45 +23405,46 @@
return SQLITE_MISUSE;
}
enterMutex();
+ int failed = SQLITE_OK;
if( pFile->locktype>SHARED_LOCK ){
if( locktype==SHARED_LOCK ){
- int failed = 0;
/* unlock the exclusive range - then re-establish the shared lock */
if (pFile->locktype==EXCLUSIVE_LOCK) {
- failed = _AFPFSSetLock(context->filePath, pFile->h, SHARED_FIRST,
+ failed = _AFPFSSetLock(context->filePath, pFile, SHARED_FIRST,
SHARED_SIZE, 0);
if (!failed) {
/* successfully removed the exclusive lock */
- if (_AFPFSSetLock(context->filePath, pFile->h, SHARED_FIRST+
- context->sharedLockByte, 1, 1)) {
+ if ((failed = _AFPFSSetLock(context->filePath, pFile, SHARED_FIRST+
+ context->sharedLockByte, 1, 1))) {
/* failed to re-establish our shared lock */
- rc = SQLITE_IOERR_RDLOCK; /* This should never happen */
+ rc = failed;
}
} else {
- /* This should never happen - failed to unlock the exclusive range */
- rc = SQLITE_IOERR_UNLOCK;
+ rc = failed;
}
}
}
if (rc == SQLITE_OK && pFile->locktype>=PENDING_LOCK) {
- if (_AFPFSSetLock(context->filePath, pFile->h, PENDING_BYTE, 1, 0)){
+ if ((failed = _AFPFSSetLock(context->filePath, pFile,
+ PENDING_BYTE, 1, 0))){
/* failed to release the pending lock */
- rc = SQLITE_IOERR_UNLOCK; /* This should never happen */
+ rc = failed;
}
}
if (rc == SQLITE_OK && pFile->locktype>=RESERVED_LOCK) {
- if (_AFPFSSetLock(context->filePath, pFile->h, RESERVED_BYTE, 1, 0)) {
+ if ((failed = _AFPFSSetLock(context->filePath, pFile,
+ RESERVED_BYTE, 1, 0))) {
/* failed to release the reserved lock */
- rc = SQLITE_IOERR_UNLOCK; /* This should never happen */
+ rc = failed;
}
}
}
if( locktype==NO_LOCK ){
- int failed = _AFPFSSetLock(context->filePath, pFile->h,
+ int failed = _AFPFSSetLock(context->filePath, pFile,
SHARED_FIRST + context->sharedLockByte, 1, 0);
if (failed) {
- rc = SQLITE_IOERR_UNLOCK; /* This should never happen */
+ rc = failed;
}
}
if (rc == SQLITE_OK)
@@ -23060,27 +23473,62 @@
*/
typedef void flockLockingContext;
+/* flock-style reserved lock checking following the behavior of
+ ** unixCheckReservedLock, see the unixCheckReservedLock function comments */
static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){
- int r = 1;
+ int rc = SQLITE_OK;
+ int reserved = 0;
unixFile *pFile = (unixFile*)id;
- if (pFile->locktype != RESERVED_LOCK) {
+ SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
+
+ assert( pFile );
+
+ /* Check if a thread in this process holds such a lock */
+ if( pFile->locktype>SHARED_LOCK ){
+ reserved = 1;
+ }
+
+ /* Otherwise see if some other process holds it. */
+ if( !reserved ){
/* attempt to get the lock */
- int rc = flock(pFile->h, LOCK_EX | LOCK_NB);
- if (!rc) {
+ int lrc = flock(pFile->h, LOCK_EX | LOCK_NB);
+ if( !lrc ){
/* got the lock, unlock it */
- flock(pFile->h, LOCK_UN);
- r = 0; /* no one has it reserved */
+ lrc = flock(pFile->h, LOCK_UN);
+ if ( lrc ) {
+ int tErrno = errno;
+ /* unlock failed with an error */
+ lrc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
+ if( IS_LOCK_ERROR(lrc) ){
+ pFile->lastErrno = tErrno;
+ rc = lrc;
+ }
+ }
+ } else {
+ int tErrno = errno;
+ reserved = 1;
+ /* someone else might have it reserved */
+ lrc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
+ if( IS_LOCK_ERROR(lrc) ){
+ pFile->lastErrno = tErrno;
+ rc = lrc;
+ }
}
}
+ OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved);
- *pResOut = r;
- return SQLITE_OK;
+ *pResOut = reserved;
+ return rc;
}
static int flockLock(sqlite3_file *id, int locktype) {
+ int rc = SQLITE_OK;
+ int lrc;
unixFile *pFile = (unixFile*)id;
-
+
+ assert( pFile );
+
/* if we already have a lock, it is exclusive.
** Just adjust level and punt on outta here. */
if (pFile->locktype > NO_LOCK) {
@@ -23089,20 +23537,29 @@
}
/* grab an exclusive lock */
- int rc = flock(pFile->h, LOCK_EX | LOCK_NB);
- if (rc) {
+
+ if (flock(pFile->h, LOCK_EX | LOCK_NB)) {
+ int tErrno = errno;
/* didn't get, must be busy */
- return SQLITE_BUSY;
+ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
+ if( IS_LOCK_ERROR(rc) ){
+ pFile->lastErrno = tErrno;
+ }
} else {
/* got it, set the type and return ok */
pFile->locktype = locktype;
- return SQLITE_OK;
}
+ OSTRACE4("LOCK %d %s %s\n", pFile->h, locktypeName(locktype),
+ rc==SQLITE_OK ? "ok" : "failed");
+ return rc;
}
static int flockUnlock(sqlite3_file *id, int locktype) {
unixFile *pFile = (unixFile*)id;
+ assert( pFile );
+ OSTRACE5("UNLOCK %d %d was %d pid=%d\n", pFile->h, locktype,
+ pFile->locktype, getpid());
assert( locktype<=SHARED_LOCK );
/* no-op if possible */
@@ -23118,9 +23575,14 @@
/* no, really, unlock. */
int rc = flock(pFile->h, LOCK_UN);
- if (rc)
- return SQLITE_IOERR_UNLOCK;
- else {
+ if (rc) {
+ int r, tErrno = errno;
+ r = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
+ if( IS_LOCK_ERROR(r) ){
+ pFile->lastErrno = tErrno;
+ }
+ return r;
+ } else {
pFile->locktype = NO_LOCK;
return SQLITE_OK;
}
@@ -23138,27 +23600,50 @@
#pragma mark Old-School .lock file based locking
+/* Dotlock-style reserved lock checking following the behavior of
+** unixCheckReservedLock, see the unixCheckReservedLock function comments */
static int dotlockCheckReservedLock(sqlite3_file *id, int *pResOut) {
- int r = 1;
+ int rc = SQLITE_OK;
+ int reserved = 0;
unixFile *pFile = (unixFile*)id;
- char *zLockFile = (char *)pFile->lockingContext;
- if (pFile->locktype != RESERVED_LOCK) {
+ SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
+
+ assert( pFile );
+
+ /* Check if a thread in this process holds such a lock */
+ if( pFile->locktype>SHARED_LOCK ){
+ reserved = 1;
+ }
+
+ /* Otherwise see if some other process holds it. */
+ if( !reserved ){
+ char *zLockFile = (char *)pFile->lockingContext;
struct stat statBuf;
- if (lstat(zLockFile, &statBuf) != 0){
+
+ if( lstat(zLockFile, &statBuf)==0 ){
+ /* file exists, someone else has the lock */
+ reserved = 1;
+ }else{
/* file does not exist, we could have it if we want it */
- r = 0;
+ int tErrno = errno;
+ if( ENOENT != tErrno ){
+ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_CHECKRESERVEDLOCK);
+ pFile->lastErrno = tErrno;
+ }
}
}
+ OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved);
- *pResOut = r;
- return SQLITE_OK;
+ *pResOut = reserved;
+ return rc;
}
static int dotlockLock(sqlite3_file *id, int locktype) {
unixFile *pFile = (unixFile*)id;
int fd;
char *zLockFile = (char *)pFile->lockingContext;
+ int rc=SQLITE_OK;
/* if we already have a lock, it is exclusive.
** Just adjust level and punt on outta here. */
@@ -23167,32 +23652,48 @@
/* Always update the timestamp on the old file */
utimes(zLockFile, NULL);
- return SQLITE_OK;
+ rc = SQLITE_OK;
+ goto dotlock_end_lock;
}
/* check to see if lock file already exists */
struct stat statBuf;
if (lstat(zLockFile,&statBuf) == 0){
- return SQLITE_BUSY; /* it does, busy */
+ rc = SQLITE_BUSY; /* it does, busy */
+ goto dotlock_end_lock;
}
/* grab an exclusive lock */
fd = open(zLockFile,O_RDONLY|O_CREAT|O_EXCL,0600);
if( fd<0 ){
/* failed to open/create the file, someone else may have stolen the lock */
- return SQLITE_BUSY;
- }
+ int tErrno = errno;
+ if( EEXIST == tErrno ){
+ rc = SQLITE_BUSY;
+ } else {
+ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
+ if( IS_LOCK_ERROR(rc) ){
+ pFile->lastErrno = tErrno;
+ }
+ }
+ goto dotlock_end_lock;
+ }
close(fd);
/* got it, set the type and return ok */
pFile->locktype = locktype;
- return SQLITE_OK;
+
+ dotlock_end_lock:
+ return rc;
}
static int dotlockUnlock(sqlite3_file *id, int locktype) {
unixFile *pFile = (unixFile*)id;
char *zLockFile = (char *)pFile->lockingContext;
+ assert( pFile );
+ OSTRACE5("UNLOCK %d %d was %d pid=%d\n", pFile->h, locktype,
+ pFile->locktype, getpid());
assert( locktype<=SHARED_LOCK );
/* no-op if possible */
@@ -23207,7 +23708,16 @@
}
/* no, really, unlock. */
- unlink(zLockFile);
+ if (unlink(zLockFile) ) {
+ int rc, tErrno = errno;
+ if( ENOENT != tErrno ){
+ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
+ }
+ if( IS_LOCK_ERROR(rc) ){
+ pFile->lastErrno = tErrno;
+ }
+ return rc;
+ }
pFile->locktype = NO_LOCK;
return SQLITE_OK;
}
@@ -23409,7 +23919,8 @@
break;
#endif
}
-
+
+ pNew->lastErrno = 0;
if( rc!=SQLITE_OK ){
if( dirfd>=0 ) close(dirfd);
close(h);
@@ -26140,9 +26651,9 @@
#endif /* SQLITE_OMIT_BUILTIN_TEST */
/************** End of bitvec.c **********************************************/
-/************** Begin file pager.c *******************************************/
+/************** Begin file pcache.c ******************************************/
/*
-** 2001 September 15
+** 2008 August 05
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
@@ -26152,3413 +26663,3782 @@
** May you share freely, never taking more than you give.
**
*************************************************************************
-** This is the implementation of the page cache subsystem or "pager".
-**
-** The pager is used to access a database disk file. It implements
-** atomic commit and rollback through the use of a journal file that
-** is separate from the database file. The pager also implements file
-** locking to prevent two processes from writing the same database
-** file simultaneously, or one process from reading the database while
-** another is writing.
+** This file implements that page cache.
**
-** @(#) $Id: pager.c,v 1.469 2008/08/02 03:50:39 drh Exp $
+** @(#) $Id: pcache.c,v 1.24 2008/08/29 09:10:03 danielk1977 Exp $
*/
-#ifndef SQLITE_OMIT_DISKIO
/*
-** Macros for troubleshooting. Normally turned off
+** A complete page cache is an instance of this structure.
+**
+** A cache may only be deleted by its owner and while holding the
+** SQLITE_MUTEX_STATUS_LRU mutex.
+*/
+struct PCache {
+ /*********************************************************************
+ ** The first group of elements may be read or written at any time by
+ ** the cache owner without holding the mutex. No thread other than the
+ ** cache owner is permitted to access these elements at any time.
+ */
+ PgHdr *pDirty, *pDirtyTail; /* List of dirty pages in LRU order */
+ PgHdr *pSynced; /* Last synced page in dirty page list */
+ int nRef; /* Number of pinned pages */
+ int nPinned; /* Number of pinned and/or dirty pages */
+ int nMax; /* Configured cache size */
+ int nMin; /* Configured minimum cache size */
+ /**********************************************************************
+ ** The next group of elements are fixed when the cache is created and
+ ** may not be changed afterwards. These elements can read at any time by
+ ** the cache owner or by any thread holding the the mutex. Non-owner
+ ** threads must hold the mutex when reading these elements to prevent
+ ** the entire PCache object from being deleted during the read.
+ */
+ int szPage; /* Size of every page in this cache */
+ int szExtra; /* Size of extra space for each page */
+ int bPurgeable; /* True if pages are on backing store */
+ void (*xDestroy)(PgHdr*); /* Called when refcnt goes 1->0 */
+ int (*xStress)(void*,PgHdr*); /* Call to try make a page clean */
+ void *pStress; /* Argument to xStress */
+ /**********************************************************************
+ ** The final group of elements can only be accessed while holding the
+ ** mutex. Both the cache owner and any other thread must hold the mutex
+ ** to read or write any of these elements.
+ */
+ int nPage; /* Total number of pages in apHash */
+ int nHash; /* Number of slots in apHash[] */
+ PgHdr **apHash; /* Hash table for fast lookup by pgno */
+ PgHdr *pClean; /* List of clean pages in use */
+};
+
+/*
+** Free slots in the page block allocator
*/
-#if 0
-#define sqlite3DebugPrintf printf
-#define PAGERTRACE1(X) sqlite3DebugPrintf(X)
-#define PAGERTRACE2(X,Y) sqlite3DebugPrintf(X,Y)
-#define PAGERTRACE3(X,Y,Z) sqlite3DebugPrintf(X,Y,Z)
-#define PAGERTRACE4(X,Y,Z,W) sqlite3DebugPrintf(X,Y,Z,W)
-#define PAGERTRACE5(X,Y,Z,W,V) sqlite3DebugPrintf(X,Y,Z,W,V)
+typedef struct PgFreeslot PgFreeslot;
+struct PgFreeslot {
+ PgFreeslot *pNext; /* Next free slot */
+};
+
+/*
+** Global data for the page cache.
+*/
+static struct PCacheGlobal {
+ int isInit; /* True when initialized */
+ sqlite3_mutex *mutex; /* static mutex MUTEX_STATIC_LRU */
+
+ int nMaxPage; /* Sum of nMaxPage for purgeable caches */
+ int nMinPage; /* Sum of nMinPage for purgeable caches */
+ int nCurrentPage; /* Number of purgeable pages allocated */
+ PgHdr *pLruHead, *pLruTail; /* LRU list of unused clean pgs */
+
+ /* Variables related to SQLITE_CONFIG_PAGECACHE settings. */
+ int szSlot; /* Size of each free slot */
+ void *pStart, *pEnd; /* Bounds of pagecache malloc range */
+ PgFreeslot *pFree; /* Free page blocks */
+} pcache = {0};
+
+/*
+** All global variables used by this module (all of which are grouped
+** together in global structure "pcache" above) are protected by the static
+** SQLITE_MUTEX_STATIC_LRU mutex. A pointer to this mutex is stored in
+** variable "pcache.mutex".
+**
+** Some elements of the PCache and PgHdr structures are protected by the
+** SQLITE_MUTEX_STATUS_LRU mutex and other are not. The protected
+** elements are grouped at the end of the structures and are clearly
+** marked.
+**
+** Use the following macros must surround all access (read or write)
+** of protected elements. The mutex is not recursive and may not be
+** entered more than once. The pcacheMutexHeld() macro should only be
+** used within an assert() to verify that the mutex is being held.
+*/
+#define pcacheEnterMutex() sqlite3_mutex_enter(pcache.mutex)
+#define pcacheExitMutex() sqlite3_mutex_leave(pcache.mutex)
+#define pcacheMutexHeld() sqlite3_mutex_held(pcache.mutex)
+
+/*
+** Some of the assert() macros in this code are too expensive to run
+** even during normal debugging. Use them only rarely on long-running
+** tests. Enable the expensive asserts using the
+** -DSQLITE_ENABLE_EXPENSIVE_ASSERT=1 compile-time option.
+*/
+#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT
+# define expensive_assert(X) assert(X)
#else
-#define PAGERTRACE1(X)
-#define PAGERTRACE2(X,Y)
-#define PAGERTRACE3(X,Y,Z)
-#define PAGERTRACE4(X,Y,Z,W)
-#define PAGERTRACE5(X,Y,Z,W,V)
+# define expensive_assert(X)
#endif
+/********************************** Linked List Management ********************/
+
+#if !defined(NDEBUG) && defined(SQLITE_ENABLE_EXPENSIVE_ASSERT)
/*
-** The following two macros are used within the PAGERTRACEX() macros above
-** to print out file-descriptors.
-**
-** PAGERID() takes a pointer to a Pager struct as its argument. The
-** associated file-descriptor is returned. FILEHANDLEID() takes an sqlite3_file
-** struct as its argument.
+** This routine verifies that the number of entries in the hash table
+** is pCache->nPage. This routine is used within assert() statements
+** only and is therefore disabled during production builds.
*/
-#define PAGERID(p) ((int)(p->fd))
-#define FILEHANDLEID(fd) ((int)fd)
+static int pcacheCheckHashCount(PCache *pCache){
+ int i;
+ int nPage = 0;
+ for(i=0; i<pCache->nHash; i++){
+ PgHdr *p;
+ for(p=pCache->apHash[i]; p; p=p->pNextHash){
+ nPage++;
+ }
+ }
+ assert( nPage==pCache->nPage );
+ return 1;
+}
+#endif /* !NDEBUG && SQLITE_ENABLE_EXPENSIVE_ASSERT */
+
+#if !defined(NDEBUG) && defined(SQLITE_ENABLE_EXPENSIVE_ASSERT)
/*
-** The page cache as a whole is always in one of the following
-** states:
-**
-** PAGER_UNLOCK The page cache is not currently reading or
-** writing the database file. There is no
-** data held in memory. This is the initial
-** state.
-**
-** PAGER_SHARED The page cache is reading the database.
-** Writing is not permitted. There can be
-** multiple readers accessing the same database
-** file at the same time.
-**
-** PAGER_RESERVED This process has reserved the database for writing
-** but has not yet made any changes. Only one process
-** at a time can reserve the database. The original
-** database file has not been modified so other
-** processes may still be reading the on-disk
-** database file.
-**
-** PAGER_EXCLUSIVE The page cache is writing the database.
-** Access is exclusive. No other processes or
-** threads can be reading or writing while one
-** process is writing.
-**
-** PAGER_SYNCED The pager moves to this state from PAGER_EXCLUSIVE
-** after all dirty pages have been written to the
-** database file and the file has been synced to
-** disk. All that remains to do is to remove or
-** truncate the journal file and the transaction
-** will be committed.
+** Based on the current value of PCache.nRef and the contents of the
+** PCache.pDirty list, return the expected value of the PCache.nPinned
+** counter. This is only used in debugging builds, as follows:
**
-** The page cache comes up in PAGER_UNLOCK. The first time a
-** sqlite3PagerGet() occurs, the state transitions to PAGER_SHARED.
-** After all pages have been released using sqlite_page_unref(),
-** the state transitions back to PAGER_UNLOCK. The first time
-** that sqlite3PagerWrite() is called, the state transitions to
-** PAGER_RESERVED. (Note that sqlite3PagerWrite() can only be
-** called on an outstanding page which means that the pager must
-** be in PAGER_SHARED before it transitions to PAGER_RESERVED.)
-** PAGER_RESERVED means that there is an open rollback journal.
-** The transition to PAGER_EXCLUSIVE occurs before any changes
-** are made to the database file, though writes to the rollback
-** journal occurs with just PAGER_RESERVED. After an sqlite3PagerRollback()
-** or sqlite3PagerCommitPhaseTwo(), the state can go back to PAGER_SHARED,
-** or it can stay at PAGER_EXCLUSIVE if we are in exclusive access mode.
+** expensive_assert( pCache->nPinned==pcachePinnedCount(pCache) );
*/
-#define PAGER_UNLOCK 0
-#define PAGER_SHARED 1 /* same as SHARED_LOCK */
-#define PAGER_RESERVED 2 /* same as RESERVED_LOCK */
-#define PAGER_EXCLUSIVE 4 /* same as EXCLUSIVE_LOCK */
-#define PAGER_SYNCED 5
+static int pcachePinnedCount(PCache *pCache){
+ PgHdr *p;
+ int nPinned = pCache->nRef;
+ for(p=pCache->pDirty; p; p=p->pNext){
+ if( p->nRef==0 ){
+ nPinned++;
+ }
+ }
+ return nPinned;
+}
+#endif /* !NDEBUG && SQLITE_ENABLE_EXPENSIVE_ASSERT */
-/*
-** If the SQLITE_BUSY_RESERVED_LOCK macro is set to true at compile-time,
-** then failed attempts to get a reserved lock will invoke the busy callback.
-** This is off by default. To see why, consider the following scenario:
-**
-** Suppose thread A already has a shared lock and wants a reserved lock.
-** Thread B already has a reserved lock and wants an exclusive lock. If
-** both threads are using their busy callbacks, it might be a long time
-** be for one of the threads give up and allows the other to proceed.
-** But if the thread trying to get the reserved lock gives up quickly
-** (if it never invokes its busy callback) then the contention will be
-** resolved quickly.
-*/
-#ifndef SQLITE_BUSY_RESERVED_LOCK
-# define SQLITE_BUSY_RESERVED_LOCK 0
-#endif
+#if !defined(NDEBUG) && defined(SQLITE_ENABLE_EXPENSIVE_ASSERT)
/*
-** This macro rounds values up so that if the value is an address it
-** is guaranteed to be an address that is aligned to an 8-byte boundary.
+** Check that the pCache->pSynced variable is set correctly. If it
+** is not, either fail an assert or return zero. Otherwise, return
+** non-zero. This is only used in debugging builds, as follows:
+**
+** expensive_assert( pcacheCheckSynced(pCache) );
*/
-#define FORCE_ALIGNMENT(X) (((X)+7)&~7)
+static int pcacheCheckSynced(PCache *pCache){
+ PgHdr *p = pCache->pDirtyTail;
+ for(p=pCache->pDirtyTail; p!=pCache->pSynced; p=p->pPrev){
+ assert( p->nRef || (p->flags&PGHDR_NEED_SYNC) );
+ }
+ return (p==0 || p->nRef || (p->flags&PGHDR_NEED_SYNC)==0);
+}
+#endif /* !NDEBUG && SQLITE_ENABLE_EXPENSIVE_ASSERT */
+
-typedef struct PgHdr PgHdr;
/*
-** Each pager stores all currently unreferenced pages in a list sorted
-** in least-recently-used (LRU) order (i.e. the first item on the list has
-** not been referenced in a long time, the last item has been recently
-** used). An instance of this structure is included as part of each
-** pager structure for this purpose (variable Pager.lru).
-**
-** Additionally, if memory-management is enabled, all unreferenced pages
-** are stored in a global LRU list (global variable sqlite3LruPageList).
-**
-** In both cases, the PagerLruList.pFirstSynced variable points to
-** the first page in the corresponding list that does not require an
-** fsync() operation before its memory can be reclaimed. If no such
-** page exists, PagerLruList.pFirstSynced is set to NULL.
-*/
-typedef struct PagerLruList PagerLruList;
-struct PagerLruList {
- PgHdr *pFirst; /* First page in LRU list */
- PgHdr *pLast; /* Last page in LRU list (the most recently used) */
- PgHdr *pFirstSynced; /* First page in list with PgHdr.needSync==0 */
-};
+** Remove a page from its hash table (PCache.apHash[]).
+*/
+static void pcacheRemoveFromHash(PgHdr *pPage){
+ assert( pcacheMutexHeld() );
+ if( pPage->pPrevHash ){
+ pPage->pPrevHash->pNextHash = pPage->pNextHash;
+ }else{
+ PCache *pCache = pPage->pCache;
+ u32 h = pPage->pgno % pCache->nHash;
+ assert( pCache->apHash[h]==pPage );
+ pCache->apHash[h] = pPage->pNextHash;
+ }
+ if( pPage->pNextHash ){
+ pPage->pNextHash->pPrevHash = pPage->pPrevHash;
+ }
+ pPage->pCache->nPage--;
+ expensive_assert( pcacheCheckHashCount(pPage->pCache) );
+}
/*
-** The following structure contains the next and previous pointers used
-** to link a PgHdr structure into a PagerLruList linked list.
+** Insert a page into the hash table
+**
+** The mutex must be held by the caller.
*/
-typedef struct PagerLruLink PagerLruLink;
-struct PagerLruLink {
- PgHdr *pNext;
- PgHdr *pPrev;
-};
+static void pcacheAddToHash(PgHdr *pPage){
+ PCache *pCache = pPage->pCache;
+ u32 h = pPage->pgno % pCache->nHash;
+ assert( pcacheMutexHeld() );
+ pPage->pNextHash = pCache->apHash[h];
+ pPage->pPrevHash = 0;
+ if( pCache->apHash[h] ){
+ pCache->apHash[h]->pPrevHash = pPage;
+ }
+ pCache->apHash[h] = pPage;
+ pCache->nPage++;
+ expensive_assert( pcacheCheckHashCount(pCache) );
+}
/*
-** Each in-memory image of a page begins with the following header.
-** This header is only visible to this pager module. The client
-** code that calls pager sees only the data that follows the header.
-**
-** Client code should call sqlite3PagerWrite() on a page prior to making
-** any modifications to that page. The first time sqlite3PagerWrite()
-** is called, the original page contents are written into the rollback
-** journal and PgHdr.inJournal and PgHdr.needSync are set. Later, once
-** the journal page has made it onto the disk surface, PgHdr.needSync
-** is cleared. The modified page cannot be written back into the original
-** database file until the journal pages has been synced to disk and the
-** PgHdr.needSync has been cleared.
-**
-** The PgHdr.dirty flag is set when sqlite3PagerWrite() is called and
-** is cleared again when the page content is written back to the original
-** database file.
-**
-** Details of important structure elements:
-**
-** needSync
-**
-** If this is true, this means that it is not safe to write the page
-** content to the database because the original content needed
-** for rollback has not by synced to the main rollback journal.
-** The original content may have been written to the rollback journal
-** but it has not yet been synced. So we cannot write to the database
-** file because power failure might cause the page in the journal file
-** to never reach the disk. It is as if the write to the journal file
-** does not occur until the journal file is synced.
-**
-** This flag is false if the page content exactly matches what
-** currently exists in the database file. The needSync flag is also
-** false if the original content has been written to the main rollback
-** journal and synced. If the page represents a new page that has
-** been added onto the end of the database during the current
-** transaction, the needSync flag is true until the original database
-** size in the journal header has been synced to disk.
-**
-** inJournal
-**
-** This is true if the original page has been written into the main
-** rollback journal. This is always false for new pages added to
-** the end of the database file during the current transaction.
-** And this flag says nothing about whether or not the journal
-** has been synced to disk. For pages that are in the original
-** database file, the following expression should always be true:
-**
-** inJournal = sqlite3BitvecTest(pPager->pInJournal, pgno)
-**
-** The pPager->pInJournal object is only valid for the original
-** pages of the database, not new pages that are added to the end
-** of the database, so obviously the above expression cannot be
-** valid for new pages. For new pages inJournal is always 0.
-**
-** dirty
-**
-** When true, this means that the content of the page has been
-** modified and needs to be written back to the database file.
-** If false, it means that either the content of the page is
-** unchanged or else the content is unimportant and we do not
-** care whether or not it is preserved.
-**
-** alwaysRollback
-**
-** This means that the sqlite3PagerDontRollback() API should be
-** ignored for this page. The DontRollback() API attempts to say
-** that the content of the page on disk is unimportant (it is an
-** unused page on the freelist) so that it is unnecessary to
-** rollback changes to this page because the content of the page
-** can change without changing the meaning of the database. This
-** flag overrides any DontRollback() attempt. This flag is set
-** when a page that originally contained valid data is added to
-** the freelist. Later in the same transaction, this page might
-** be pulled from the freelist and reused for something different
-** and at that point the DontRollback() API will be called because
-** pages taken from the freelist do not need to be protected by
-** the rollback journal. But this flag says that the page was
-** not originally part of the freelist so that it still needs to
-** be rolled back in spite of any subsequent DontRollback() calls.
-**
-** needRead
-**
-** This flag means (when true) that the content of the page has
-** not yet been loaded from disk. The in-memory content is just
-** garbage. (Actually, we zero the content, but you should not
-** make any assumptions about the content nevertheless.) If the
-** content is needed in the future, it should be read from the
-** original database file.
+** Attempt to increase the size the hash table to contain
+** at least nHash buckets.
*/
-struct PgHdr {
- Pager *pPager; /* The pager to which this page belongs */
- Pgno pgno; /* The page number for this page */
- PgHdr *pNextHash, *pPrevHash; /* Hash collision chain for PgHdr.pgno */
- PagerLruLink free; /* Next and previous free pages */
- PgHdr *pNextAll; /* A list of all pages */
- u8 inJournal; /* TRUE if has been written to journal */
- u8 dirty; /* TRUE if we need to write back changes */
- u8 needSync; /* Sync journal before writing this page */
- u8 alwaysRollback; /* Disable DontRollback() for this page */
- u8 needRead; /* Read content if PagerWrite() is called */
- short int nRef; /* Number of users of this page */
- PgHdr *pDirty, *pPrevDirty; /* Dirty pages */
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
- PgHdr *pPrevAll; /* A list of all pages */
- PagerLruLink gfree; /* Global list of nRef==0 pages */
-#endif
-#ifdef SQLITE_CHECK_PAGES
- u32 pageHash;
+static int pcacheResizeHash(PCache *pCache, int nHash){
+ PgHdr *p;
+ PgHdr **pNew;
+ assert( pcacheMutexHeld() );
+#ifdef SQLITE_MALLOC_SOFT_LIMIT
+ if( nHash*sizeof(PgHdr*)>SQLITE_MALLOC_SOFT_LIMIT ){
+ nHash = SQLITE_MALLOC_SOFT_LIMIT/sizeof(PgHdr *);
+ }
#endif
- void *pData; /* Page data */
- /* Pager.nExtra bytes of local data appended to this header */
-};
+ pcacheExitMutex();
+ pNew = (PgHdr **)sqlite3Malloc(sizeof(PgHdr*)*nHash);
+ pcacheEnterMutex();
+ if( !pNew ){
+ return SQLITE_NOMEM;
+ }
+ memset(pNew, 0, sizeof(PgHdr *)*nHash);
+ sqlite3_free(pCache->apHash);
+ pCache->apHash = pNew;
+ pCache->nHash = nHash;
+ pCache->nPage = 0;
+
+ for(p=pCache->pClean; p; p=p->pNext){
+ pcacheAddToHash(p);
+ }
+ for(p=pCache->pDirty; p; p=p->pNext){
+ pcacheAddToHash(p);
+ }
+ return SQLITE_OK;
+}
/*
-** For an in-memory only database, some extra information is recorded about
-** each page so that changes can be rolled back. (Journal files are not
-** used for in-memory databases.) The following information is added to
-** the end of every EXTRA block for in-memory databases.
-**
-** This information could have been added directly to the PgHdr structure.
-** But then it would take up an extra 8 bytes of storage on every PgHdr
-** even for disk-based databases. Splitting it out saves 8 bytes. This
-** is only a savings of 0.8% but those percentages add up.
-*/
-typedef struct PgHistory PgHistory;
-struct PgHistory {
- u8 *pOrig; /* Original page text. Restore to this on a full rollback */
- u8 *pStmt; /* Text as it was at the beginning of the current statement */
- PgHdr *pNextStmt, *pPrevStmt; /* List of pages in the statement journal */
- u8 inStmt; /* TRUE if in the statement subjournal */
-};
+** Remove a page from a linked list that is headed by *ppHead.
+** *ppHead is either PCache.pClean or PCache.pDirty.
+*/
+static void pcacheRemoveFromList(PgHdr **ppHead, PgHdr *pPage){
+ int isDirtyList = (ppHead==&pPage->pCache->pDirty);
+ assert( ppHead==&pPage->pCache->pClean || ppHead==&pPage->pCache->pDirty );
+ assert( pcacheMutexHeld() || ppHead!=&pPage->pCache->pClean );
+
+ if( pPage->pPrev ){
+ pPage->pPrev->pNext = pPage->pNext;
+ }else{
+ assert( *ppHead==pPage );
+ *ppHead = pPage->pNext;
+ }
+ if( pPage->pNext ){
+ pPage->pNext->pPrev = pPage->pPrev;
+ }
+
+ if( isDirtyList ){
+ PCache *pCache = pPage->pCache;
+ assert( pPage->pNext || pCache->pDirtyTail==pPage );
+ if( !pPage->pNext ){
+ pCache->pDirtyTail = pPage->pPrev;
+ }
+ if( pCache->pSynced==pPage ){
+ PgHdr *pSynced = pPage->pPrev;
+ while( pSynced && (pSynced->flags&PGHDR_NEED_SYNC) ){
+ pSynced = pSynced->pPrev;
+ }
+ pCache->pSynced = pSynced;
+ }
+ }
+}
/*
-** A macro used for invoking the codec if there is one
+** Add a page from a linked list that is headed by *ppHead.
+** *ppHead is either PCache.pClean or PCache.pDirty.
*/
-#ifdef SQLITE_HAS_CODEC
-# define CODEC1(P,D,N,X) if( P->xCodec!=0 ){ P->xCodec(P->pCodecArg,D,N,X); }
-# define CODEC2(P,D,N,X) ((char*)(P->xCodec!=0?P->xCodec(P->pCodecArg,D,N,X):D))
-#else
-# define CODEC1(P,D,N,X) /* NO-OP */
-# define CODEC2(P,D,N,X) ((char*)D)
-#endif
+static void pcacheAddToList(PgHdr **ppHead, PgHdr *pPage){
+ int isDirtyList = (ppHead==&pPage->pCache->pDirty);
+ assert( ppHead==&pPage->pCache->pClean || ppHead==&pPage->pCache->pDirty );
+
+ if( (*ppHead) ){
+ (*ppHead)->pPrev = pPage;
+ }
+ pPage->pNext = *ppHead;
+ pPage->pPrev = 0;
+ *ppHead = pPage;
+
+ if( isDirtyList ){
+ PCache *pCache = pPage->pCache;
+ if( !pCache->pDirtyTail ){
+ assert( pPage->pNext==0 );
+ pCache->pDirtyTail = pPage;
+ }
+ if( !pCache->pSynced && 0==(pPage->flags&PGHDR_NEED_SYNC) ){
+ pCache->pSynced = pPage;
+ }
+ }
+}
/*
-** Convert a pointer to a PgHdr into a pointer to its data
-** and back again.
+** Remove a page from the global LRU list
*/
-#define PGHDR_TO_DATA(P) ((P)->pData)
-#define PGHDR_TO_EXTRA(G,P) ((void*)&((G)[1]))
-#define PGHDR_TO_HIST(P,PGR) \
- ((PgHistory*)&((char*)(&(P)[1]))[(PGR)->nExtra])
+static void pcacheRemoveFromLruList(PgHdr *pPage){
+ assert( sqlite3_mutex_held(pcache.mutex) );
+ assert( (pPage->flags&PGHDR_DIRTY)==0 );
+ if( pPage->pCache->bPurgeable==0 ) return;
+ if( pPage->pNextLru ){
+ assert( pcache.pLruTail!=pPage );
+ pPage->pNextLru->pPrevLru = pPage->pPrevLru;
+ }else{
+ assert( pcache.pLruTail==pPage );
+ pcache.pLruTail = pPage->pPrevLru;
+ }
+ if( pPage->pPrevLru ){
+ assert( pcache.pLruHead!=pPage );
+ pPage->pPrevLru->pNextLru = pPage->pNextLru;
+ }else{
+ assert( pcache.pLruHead==pPage );
+ pcache.pLruHead = pPage->pNextLru;
+ }
+}
/*
-** A open page cache is an instance of the following structure.
-**
-** Pager.errCode may be set to SQLITE_IOERR, SQLITE_CORRUPT, or
-** or SQLITE_FULL. Once one of the first three errors occurs, it persists
-** and is returned as the result of every major pager API call. The
-** SQLITE_FULL return code is slightly different. It persists only until the
-** next successful rollback is performed on the pager cache. Also,
-** SQLITE_FULL does not affect the sqlite3PagerGet() and sqlite3PagerLookup()
-** APIs, they may still be used successfully.
+** Add a page to the global LRU list. The page is normally added
+** to the front of the list so that it will be the last page recycled.
+** However, if the PGHDR_REUSE_UNLIKELY bit is set, the page is added
+** to the end of the LRU list so that it will be the next to be recycled.
*/
-struct Pager {
- sqlite3_vfs *pVfs; /* OS functions to use for IO */
- u8 journalOpen; /* True if journal file descriptors is valid */
- u8 journalStarted; /* True if header of journal is synced */
- u8 useJournal; /* Use a rollback journal on this file */
- u8 noReadlock; /* Do not bother to obtain readlocks */
- u8 stmtOpen; /* True if the statement subjournal is open */
- u8 stmtInUse; /* True we are in a statement subtransaction */
- u8 stmtAutoopen; /* Open stmt journal when main journal is opened*/
- u8 noSync; /* Do not sync the journal if true */
- u8 fullSync; /* Do extra syncs of the journal for robustness */
- u8 sync_flags; /* One of SYNC_NORMAL or SYNC_FULL */
- u8 state; /* PAGER_UNLOCK, _SHARED, _RESERVED, etc. */
- u8 tempFile; /* zFilename is a temporary file */
- u8 readOnly; /* True for a read-only database */
- u8 needSync; /* True if an fsync() is needed on the journal */
- u8 dirtyCache; /* True if cached pages have changed */
- u8 alwaysRollback; /* Disable DontRollback() for all pages */
- u8 memDb; /* True to inhibit all file I/O */
- u8 setMaster; /* True if a m-j name has been written to jrnl */
- u8 doNotSync; /* Boolean. While true, do not spill the cache */
- u8 exclusiveMode; /* Boolean. True if locking_mode==EXCLUSIVE */
- u8 journalMode; /* On of the PAGER_JOURNALMODE_* values */
- u8 dbModified; /* True if there are any changes to the Db */
- u8 changeCountDone; /* Set after incrementing the change-counter */
- u32 vfsFlags; /* Flags for sqlite3_vfs.xOpen() */
- int errCode; /* One of several kinds of errors */
- int dbSize; /* Number of pages in the file */
- int origDbSize; /* dbSize before the current change */
- int stmtSize; /* Size of database (in pages) at stmt_begin() */
- int nRec; /* Number of pages written to the journal */
- u32 cksumInit; /* Quasi-random value added to every checksum */
- int stmtNRec; /* Number of records in stmt subjournal */
- int nExtra; /* Add this many bytes to each in-memory page */
- int pageSize; /* Number of bytes in a page */
- int nPage; /* Total number of in-memory pages */
- int nRef; /* Number of in-memory pages with PgHdr.nRef>0 */
- int mxPage; /* Maximum number of pages to hold in cache */
- Pgno mxPgno; /* Maximum allowed size of the database */
- Bitvec *pInJournal; /* One bit for each page in the database file */
- Bitvec *pInStmt; /* One bit for each page in the database */
- char *zFilename; /* Name of the database file */
- char *zJournal; /* Name of the journal file */
- char *zDirectory; /* Directory hold database and journal files */
- sqlite3_file *fd, *jfd; /* File descriptors for database and journal */
- sqlite3_file *stfd; /* File descriptor for the statement subjournal*/
- BusyHandler *pBusyHandler; /* Pointer to sqlite.busyHandler */
- PagerLruList lru; /* LRU list of free pages */
- PgHdr *pAll; /* List of all pages */
- PgHdr *pStmt; /* List of pages in the statement subjournal */
- PgHdr *pDirty; /* List of all dirty pages */
- i64 journalOff; /* Current byte offset in the journal file */
- i64 journalHdr; /* Byte offset to previous journal header */
- i64 stmtHdrOff; /* First journal header written this statement */
- i64 stmtCksum; /* cksumInit when statement was started */
- i64 stmtJSize; /* Size of journal at stmt_begin() */
- int sectorSize; /* Assumed sector size during rollback */
-#ifdef SQLITE_TEST
- int nHit, nMiss; /* Cache hits and missing */
- int nRead, nWrite; /* Database pages read/written */
-#endif
- void (*xDestructor)(DbPage*,int); /* Call this routine when freeing pages */
- void (*xReiniter)(DbPage*,int); /* Call this routine when reloading pages */
-#ifdef SQLITE_HAS_CODEC
- void *(*xCodec)(void*,void*,Pgno,int); /* Routine for en/decoding data */
- void *pCodecArg; /* First argument to xCodec() */
-#endif
- int nHash; /* Size of the pager hash table */
- PgHdr **aHash; /* Hash table to map page number to PgHdr */
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
- Pager *pNext; /* Doubly linked list of pagers on which */
- Pager *pPrev; /* sqlite3_release_memory() will work */
- volatile int iInUseMM; /* Non-zero if unavailable to MM */
- volatile int iInUseDB; /* Non-zero if in sqlite3_release_memory() */
-#endif
- char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */
- char dbFileVers[16]; /* Changes whenever database file changes */
- i64 journalSizeLimit; /* Size limit for persistent journal files */
-};
+static void pcacheAddToLruList(PgHdr *pPage){
+ assert( sqlite3_mutex_held(pcache.mutex) );
+ assert( (pPage->flags&PGHDR_DIRTY)==0 );
+ if( pPage->pCache->bPurgeable==0 ) return;
+ if( pcache.pLruTail && (pPage->flags & PGHDR_REUSE_UNLIKELY)!=0 ){
+ /* If reuse is unlikely. Put the page at the end of the LRU list
+ ** where it will be recycled sooner rather than later.
+ */
+ assert( pcache.pLruHead );
+ pPage->pNextLru = 0;
+ pPage->pPrevLru = pcache.pLruTail;
+ pcache.pLruTail->pNextLru = pPage;
+ pcache.pLruTail = pPage;
+ pPage->flags &= ~PGHDR_REUSE_UNLIKELY;
+ }else{
+ /* If reuse is possible. the page goes at the beginning of the LRU
+ ** list so that it will be the last to be recycled.
+ */
+ if( pcache.pLruHead ){
+ pcache.pLruHead->pPrevLru = pPage;
+ }
+ pPage->pNextLru = pcache.pLruHead;
+ pcache.pLruHead = pPage;
+ pPage->pPrevLru = 0;
+ if( pcache.pLruTail==0 ){
+ pcache.pLruTail = pPage;
+ }
+ }
+}
-/*
-** The following global variables hold counters used for
-** testing purposes only. These variables do not exist in
-** a non-testing build. These variables are not thread-safe.
+/*********************************************** Memory Allocation ***********
+**
+** Initialize the page cache memory pool.
+**
+** This must be called at start-time when no page cache lines are
+** checked out. This function is not threadsafe.
*/
-#ifdef SQLITE_TEST
-SQLITE_API int sqlite3_pager_readdb_count = 0; /* Number of full pages read from DB */
-SQLITE_API int sqlite3_pager_writedb_count = 0; /* Number of full pages written to DB */
-SQLITE_API int sqlite3_pager_writej_count = 0; /* Number of pages written to journal */
-SQLITE_API int sqlite3_pager_pgfree_count = 0; /* Number of cache pages freed */
-# define PAGER_INCR(v) v++
-#else
-# define PAGER_INCR(v)
-#endif
+SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){
+ PgFreeslot *p;
+ sz &= ~7;
+ pcache.szSlot = sz;
+ pcache.pStart = pBuf;
+ pcache.pFree = 0;
+ while( n-- ){
+ p = (PgFreeslot*)pBuf;
+ p->pNext = pcache.pFree;
+ pcache.pFree = p;
+ pBuf = (void*)&((char*)pBuf)[sz];
+ }
+ pcache.pEnd = pBuf;
+}
/*
-** The following variable points to the head of a double-linked list
-** of all pagers that are eligible for page stealing by the
-** sqlite3_release_memory() interface. Access to this list is
-** protected by the SQLITE_MUTEX_STATIC_MEM2 mutex.
-*/
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
-static Pager *sqlite3PagerList = 0;
-static PagerLruList sqlite3LruPageList = {0, 0, 0};
-#endif
+** Allocate a page cache line. Look in the page cache memory pool first
+** and use an element from it first if available. If nothing is available
+** in the page cache memory pool, go to the general purpose memory allocator.
+*/
+void *pcacheMalloc(int sz, PCache *pCache){
+ assert( sqlite3_mutex_held(pcache.mutex) );
+ if( sz<=pcache.szSlot && pcache.pFree ){
+ PgFreeslot *p = pcache.pFree;
+ pcache.pFree = p->pNext;
+ sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, sz);
+ sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, 1);
+ return (void*)p;
+ }else{
+ void *p;
+
+ /* Allocate a new buffer using sqlite3Malloc. Before doing so, exit the
+ ** global pcache mutex and unlock the pager-cache object pCache. This is
+ ** so that if the attempt to allocate a new buffer causes the the
+ ** configured soft-heap-limit to be breached, it will be possible to
+ ** reclaim memory from this pager-cache.
+ */
+ pcacheExitMutex();
+ p = sqlite3Malloc(sz);
+ pcacheEnterMutex();
+ if( p ){
+ sz = sqlite3MallocSize(p);
+ sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz);
+ }
+ return p;
+ }
+}
+SQLITE_PRIVATE void *sqlite3PageMalloc(sz){
+ void *p;
+ pcacheEnterMutex();
+ p = pcacheMalloc(sz, 0);
+ pcacheExitMutex();
+ return p;
+}
/*
-** Journal files begin with the following magic string. The data
-** was obtained from /dev/random. It is used only as a sanity check.
-**
-** Since version 2.8.0, the journal format contains additional sanity
-** checking information. If the power fails while the journal is begin
-** written, semi-random garbage data might appear in the journal
-** file after power is restored. If an attempt is then made
-** to roll the journal back, the database could be corrupted. The additional
-** sanity checking data is an attempt to discover the garbage in the
-** journal and ignore it.
-**
-** The sanity checking information for the new journal format consists
-** of a 32-bit checksum on each page of data. The checksum covers both
-** the page number and the pPager->pageSize bytes of data for the page.
-** This cksum is initialized to a 32-bit random value that appears in the
-** journal file right after the header. The random initializer is important,
-** because garbage data that appears at the end of a journal is likely
-** data that was once in other files that have now been deleted. If the
-** garbage data came from an obsolete journal file, the checksums might
-** be correct. But by initializing the checksum to random value which
-** is different for every journal, we minimize that risk.
+** Release a pager memory allocation
*/
-static const unsigned char aJournalMagic[] = {
- 0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd7,
-};
+void pcacheFree(void *p){
+ assert( sqlite3_mutex_held(pcache.mutex) );
+ if( p==0 ) return;
+ if( p>=pcache.pStart && p<pcache.pEnd ){
+ PgFreeslot *pSlot;
+ sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, -1);
+ pSlot = (PgFreeslot*)p;
+ pSlot->pNext = pcache.pFree;
+ pcache.pFree = pSlot;
+ }else{
+ int iSize = sqlite3MallocSize(p);
+ sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize);
+ sqlite3_free(p);
+ }
+}
+SQLITE_PRIVATE void sqlite3PageFree(void *p){
+ pcacheEnterMutex();
+ pcacheFree(p);
+ pcacheExitMutex();
+}
/*
-** The size of the header and of each page in the journal is determined
-** by the following macros.
+** Allocate a new page.
*/
-#define JOURNAL_PG_SZ(pPager) ((pPager->pageSize) + 8)
+static PgHdr *pcachePageAlloc(PCache *pCache){
+ PgHdr *p;
+ int sz = sizeof(*p) + pCache->szPage + pCache->szExtra;
+ assert( sqlite3_mutex_held(pcache.mutex) );
+ p = pcacheMalloc(sz, pCache);
+ if( p==0 ) return 0;
+ memset(p, 0, sizeof(PgHdr));
+ p->pData = (void*)&p[1];
+ p->pExtra = (void*)&((char*)p->pData)[pCache->szPage];
+ if( pCache->bPurgeable ){
+ pcache.nCurrentPage++;
+ }
+ return p;
+}
/*
-** The journal header size for this pager. In the future, this could be
-** set to some value read from the disk controller. The important
-** characteristic is that it is the same size as a disk sector.
+** Deallocate a page
*/
-#define JOURNAL_HDR_SZ(pPager) (pPager->sectorSize)
+static void pcachePageFree(PgHdr *p){
+ assert( sqlite3_mutex_held(pcache.mutex) );
+ if( p->pCache->bPurgeable ){
+ pcache.nCurrentPage--;
+ }
+ pcacheFree(p->apSave[0]);
+ pcacheFree(p->apSave[1]);
+ pcacheFree(p);
+}
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
/*
-** The macro MEMDB is true if we are dealing with an in-memory database.
-** We do this as a macro so that if the SQLITE_OMIT_MEMORYDB macro is set,
-** the value of MEMDB will be a constant and the compiler will optimize
-** out code that would never execute.
+** Return the number of bytes that will be returned to the heap when
+** the argument is passed to pcachePageFree().
*/
-#ifdef SQLITE_OMIT_MEMORYDB
-# define MEMDB 0
-#else
-# define MEMDB pPager->memDb
+static int pcachePageSize(PgHdr *p){
+ assert( sqlite3_mutex_held(pcache.mutex) );
+ assert( !pcache.pStart );
+ assert( p->apSave[0]==0 );
+ assert( p->apSave[1]==0 );
+ assert( p && p->pCache );
+ return sqlite3MallocSize(p);
+}
#endif
/*
-** Page number PAGER_MJ_PGNO is never used in an SQLite database (it is
-** reserved for working around a windows/posix incompatibility). It is
-** used in the journal to signify that the remainder of the journal file
-** is devoted to storing a master journal name - there are no more pages to
-** roll back. See comments for function writeMasterJournal() for details.
+** Attempt to 'recycle' a page from the global LRU list. Only clean,
+** unreferenced pages from purgeable caches are eligible for recycling.
+**
+** This function removes page pcache.pLruTail from the global LRU list,
+** and from the hash-table and PCache.pClean list of the owner pcache.
+** There should be no other references to the page.
+**
+** A pointer to the recycled page is returned, or NULL if no page is
+** eligible for recycling.
*/
-/* #define PAGER_MJ_PGNO(x) (PENDING_BYTE/((x)->pageSize)) */
-#define PAGER_MJ_PGNO(x) ((PENDING_BYTE/((x)->pageSize))+1)
+static PgHdr *pcacheRecyclePage(){
+ PgHdr *p = 0;
+ assert( sqlite3_mutex_held(pcache.mutex) );
+
+ if( (p=pcache.pLruTail) ){
+ assert( (p->flags&PGHDR_DIRTY)==0 );
+ pcacheRemoveFromLruList(p);
+ pcacheRemoveFromHash(p);
+ pcacheRemoveFromList(&p->pCache->pClean, p);
+ }
+
+ return p;
+}
/*
-** The maximum legal page number is (2^31 - 1).
+** Obtain space for a page. Try to recycle an old page if the limit on the
+** number of pages has been reached. If the limit has not been reached or
+** there are no pages eligible for recycling, allocate a new page.
+**
+** Return a pointer to the new page, or NULL if an OOM condition occurs.
*/
-#define PAGER_MAX_PGNO 2147483647
+static int pcacheRecycleOrAlloc(PCache *pCache, PgHdr **ppPage){
+ PgHdr *p = 0;
-/*
-** The pagerEnter() and pagerLeave() routines acquire and release
-** a mutex on each pager. The mutex is recursive.
+ int szPage = pCache->szPage;
+ int szExtra = pCache->szExtra;
+
+ assert( pcache.isInit );
+ assert( sqlite3_mutex_held(pcache.mutex) );
+
+ *ppPage = 0;
+
+ /* If we have reached the limit for pinned/dirty pages, and there is at
+ ** least one dirty page, invoke the xStress callback to cause a page to
+ ** become clean.
+ */
+ expensive_assert( pCache->nPinned==pcachePinnedCount(pCache) );
+ expensive_assert( pcacheCheckSynced(pCache) );
+ if( pCache->xStress
+ && pCache->pDirty
+ && pCache->nPinned>=(pcache.nMaxPage+pCache->nMin-pcache.nMinPage)
+ ){
+ PgHdr *pPg;
+ assert(pCache->pDirtyTail);
+
+ for(pPg=pCache->pSynced;
+ pPg && (pPg->nRef || (pPg->flags&PGHDR_NEED_SYNC));
+ pPg=pPg->pPrev
+ );
+ if( !pPg ){
+ for(pPg=pCache->pDirtyTail; pPg && pPg->nRef; pPg=pPg->pPrev);
+ }
+ if( pPg ){
+ int rc;
+ pcacheExitMutex();
+ rc = pCache->xStress(pCache->pStress, pPg);
+ pcacheEnterMutex();
+ if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){
+ return rc;
+ }
+ }
+ }
+
+ /* If the global page limit has been reached, try to recycle a page. */
+ if( pCache->bPurgeable && pcache.nCurrentPage>=pcache.nMaxPage ){
+ p = pcacheRecyclePage();
+ }
+
+ /* If a page has been recycled but it is the wrong size, free it. */
+ if( p && (p->pCache->szPage!=szPage || p->pCache->szPage!=szExtra) ){
+ pcachePageFree(p);
+ p = 0;
+ }
+
+ if( !p ){
+ p = pcachePageAlloc(pCache);
+ }
+
+ *ppPage = p;
+ return (p?SQLITE_OK:SQLITE_NOMEM);
+}
+
+/*************************************************** General Interfaces ******
**
-** This is a special-purpose mutex. It only provides mutual exclusion
-** between the Btree and the Memory Management sqlite3_release_memory()
-** function. It does not prevent, for example, two Btrees from accessing
-** the same pager at the same time. Other general-purpose mutexes in
-** the btree layer handle that chore.
+** Initialize and shutdown the page cache subsystem. Neither of these
+** functions are threadsafe.
*/
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
- static void pagerEnter(Pager *p){
- p->iInUseDB++;
- if( p->iInUseMM && p->iInUseDB==1 ){
-#ifndef SQLITE_MUTEX_NOOP
- sqlite3_mutex *mutex;
- mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM2);
-#endif
- p->iInUseDB = 0;
- sqlite3_mutex_enter(mutex);
- p->iInUseDB = 1;
- sqlite3_mutex_leave(mutex);
- }
- assert( p->iInUseMM==0 );
- }
- static void pagerLeave(Pager *p){
- p->iInUseDB--;
- assert( p->iInUseDB>=0 );
+SQLITE_PRIVATE int sqlite3PcacheInitialize(void){
+ assert( pcache.isInit==0 );
+ memset(&pcache, 0, sizeof(pcache));
+ if( sqlite3Config.bCoreMutex ){
+ /* No need to check the return value of sqlite3_mutex_alloc().
+ ** Allocating a static mutex cannot fail.
+ */
+ pcache.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU);
}
-#else
-# define pagerEnter(X)
-# define pagerLeave(X)
-#endif
+ pcache.isInit = 1;
+ return SQLITE_OK;
+}
+SQLITE_PRIVATE void sqlite3PcacheShutdown(void){
+ memset(&pcache, 0, sizeof(pcache));
+}
/*
-** Add page pPg to the end of the linked list managed by structure
-** pList (pPg becomes the last entry in the list - the most recently
-** used). Argument pLink should point to either pPg->free or pPg->gfree,
-** depending on whether pPg is being added to the pager-specific or
-** global LRU list.
+** Return the size in bytes of a PCache object.
*/
-static void listAdd(PagerLruList *pList, PagerLruLink *pLink, PgHdr *pPg){
- pLink->pNext = 0;
- pLink->pPrev = pList->pLast;
+SQLITE_PRIVATE int sqlite3PcacheSize(void){ return sizeof(PCache); }
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
- assert(pLink==&pPg->free || pLink==&pPg->gfree);
- assert(pLink==&pPg->gfree || pList!=&sqlite3LruPageList);
-#endif
+/*
+** Create a new PCache object. Storage space to hold the object
+** has already been allocated and is passed in as the p pointer.
+*/
+SQLITE_PRIVATE void sqlite3PcacheOpen(
+ int szPage, /* Size of every page */
+ int szExtra, /* Extra space associated with each page */
+ int bPurgeable, /* True if pages are on backing store */
+ void (*xDestroy)(PgHdr*), /* Called to destroy a page */
+ int (*xStress)(void*,PgHdr*),/* Call to try to make pages clean */
+ void *pStress, /* Argument to xStress */
+ PCache *p /* Preallocated space for the PCache */
+){
+ assert( pcache.isInit );
+ memset(p, 0, sizeof(PCache));
+ p->szPage = szPage;
+ p->szExtra = szExtra;
+ p->bPurgeable = bPurgeable;
+ p->xDestroy = xDestroy;
+ p->xStress = xStress;
+ p->pStress = pStress;
+ p->nMax = 100;
+ p->nMin = 10;
- if( pList->pLast ){
- int iOff = (char *)pLink - (char *)pPg;
- PagerLruLink *pLastLink = (PagerLruLink *)(&((u8 *)pList->pLast)[iOff]);
- pLastLink->pNext = pPg;
- }else{
- assert(!pList->pFirst);
- pList->pFirst = pPg;
+ pcacheEnterMutex();
+ if( bPurgeable ){
+ pcache.nMaxPage += p->nMax;
+ pcache.nMinPage += p->nMin;
}
- pList->pLast = pPg;
- if( !pList->pFirstSynced && pPg->needSync==0 ){
- pList->pFirstSynced = pPg;
- }
+ pcacheExitMutex();
}
/*
-** Remove pPg from the list managed by the structure pointed to by pList.
-**
-** Argument pLink should point to either pPg->free or pPg->gfree, depending
-** on whether pPg is being added to the pager-specific or global LRU list.
+** Change the page size for PCache object. This can only happen
+** when the cache is empty.
*/
-static void listRemove(PagerLruList *pList, PagerLruLink *pLink, PgHdr *pPg){
- int iOff = (char *)pLink - (char *)pPg;
+SQLITE_PRIVATE void sqlite3PcacheSetPageSize(PCache *pCache, int szPage){
+ assert(pCache->nPage==0);
+ pCache->szPage = szPage;
+}
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
- assert(pLink==&pPg->free || pLink==&pPg->gfree);
- assert(pLink==&pPg->gfree || pList!=&sqlite3LruPageList);
-#endif
+/*
+** Try to obtain a page from the cache.
+*/
+SQLITE_PRIVATE int sqlite3PcacheFetch(
+ PCache *pCache, /* Obtain the page from this cache */
+ Pgno pgno, /* Page number to obtain */
+ int createFlag, /* If true, create page if it does not exist already */
+ PgHdr **ppPage /* Write the page here */
+){
+ int rc = SQLITE_OK;
+ PgHdr *pPage = 0;
- if( pPg==pList->pFirst ){
- pList->pFirst = pLink->pNext;
- }
- if( pPg==pList->pLast ){
- pList->pLast = pLink->pPrev;
- }
- if( pLink->pPrev ){
- PagerLruLink *pPrevLink = (PagerLruLink *)(&((u8 *)pLink->pPrev)[iOff]);
- pPrevLink->pNext = pLink->pNext;
- }
- if( pLink->pNext ){
- PagerLruLink *pNextLink = (PagerLruLink *)(&((u8 *)pLink->pNext)[iOff]);
- pNextLink->pPrev = pLink->pPrev;
+ assert( pcache.isInit );
+ assert( pCache!=0 );
+ assert( pgno>0 );
+ expensive_assert( pCache->nPinned==pcachePinnedCount(pCache) );
+
+ pcacheEnterMutex();
+
+ /* Search the hash table for the requested page. Exit early if it is found. */
+ if( pCache->apHash ){
+ u32 h = pgno % pCache->nHash;
+ for(pPage=pCache->apHash[h]; pPage; pPage=pPage->pNextHash){
+ if( pPage->pgno==pgno ){
+ if( pPage->nRef==0 ){
+ if( 0==(pPage->flags&PGHDR_DIRTY) ){
+ pcacheRemoveFromLruList(pPage);
+ pCache->nPinned++;
+ }
+ pCache->nRef++;
+ }
+ pPage->nRef++;
+ break;
+ }
+ }
}
- if( pPg==pList->pFirstSynced ){
- PgHdr *p = pLink->pNext;
- while( p && p->needSync ){
- PagerLruLink *pL = (PagerLruLink *)(&((u8 *)p)[iOff]);
- p = pL->pNext;
+
+ if( !pPage && createFlag ){
+ if( pCache->nHash<=pCache->nPage ){
+ rc = pcacheResizeHash(pCache, pCache->nHash<256 ? 256 : pCache->nHash*2);
+ }
+ if( rc==SQLITE_OK ){
+ rc = pcacheRecycleOrAlloc(pCache, &pPage);
+ }
+ if( rc==SQLITE_OK ){
+ pPage->pPager = 0;
+ pPage->flags = 0;
+ pPage->pDirty = 0;
+ pPage->pgno = pgno;
+ pPage->pCache = pCache;
+ pPage->nRef = 1;
+ pCache->nRef++;
+ pCache->nPinned++;
+ pcacheAddToList(&pCache->pClean, pPage);
+ pcacheAddToHash(pPage);
}
- pList->pFirstSynced = p;
}
- pLink->pNext = pLink->pPrev = 0;
+ pcacheExitMutex();
+
+ *ppPage = pPage;
+ expensive_assert( pCache->nPinned==pcachePinnedCount(pCache) );
+ assert( pPage || !createFlag || rc!=SQLITE_OK );
+ return rc;
}
-/*
-** Add page pPg to the list of free pages for the pager. If
-** memory-management is enabled, also add the page to the global
-** list of free pages.
+/*
+** Dereference a page. When the reference count reaches zero,
+** move the page to the LRU list if it is clean.
*/
-static void lruListAdd(PgHdr *pPg){
- listAdd(&pPg->pPager->lru, &pPg->free, pPg);
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
- if( !pPg->pPager->memDb ){
- sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU));
- listAdd(&sqlite3LruPageList, &pPg->gfree, pPg);
- sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU));
+SQLITE_PRIVATE void sqlite3PcacheRelease(PgHdr *p){
+ assert( p->nRef>0 );
+ p->nRef--;
+ if( p->nRef==0 ){
+ PCache *pCache = p->pCache;
+ if( p->pCache->xDestroy ){
+ p->pCache->xDestroy(p);
+ }
+ pCache->nRef--;
+ if( (p->flags&PGHDR_DIRTY)==0 ){
+ pCache->nPinned--;
+ pcacheEnterMutex();
+ if( pcache.nCurrentPage>pcache.nMaxPage ){
+ pcacheRemoveFromList(&pCache->pClean, p);
+ pcacheRemoveFromHash(p);
+ pcachePageFree(p);
+ }else{
+ pcacheAddToLruList(p);
+ }
+ pcacheExitMutex();
+ }else{
+ /* Move the page to the head of the caches dirty list. */
+ pcacheRemoveFromList(&pCache->pDirty, p);
+ pcacheAddToList(&pCache->pDirty, p);
+ }
}
-#endif
}
-/*
-** Remove page pPg from the list of free pages for the associated pager.
-** If memory-management is enabled, also remove pPg from the global list
-** of free pages.
+SQLITE_PRIVATE void sqlite3PcacheRef(PgHdr *p){
+ assert(p->nRef>0);
+ p->nRef++;
+}
+
+/*
+** Drop a page from the cache. There must be exactly one reference to the
+** page. This function deletes that reference, so after it returns the
+** page pointed to by p is invalid.
+*/
+SQLITE_PRIVATE void sqlite3PcacheDrop(PgHdr *p){
+ PCache *pCache;
+ assert( p->nRef==1 );
+ assert( 0==(p->flags&PGHDR_DIRTY) );
+ pCache = p->pCache;
+ pCache->nRef--;
+ pCache->nPinned--;
+ pcacheEnterMutex();
+ pcacheRemoveFromList(&pCache->pClean, p);
+ pcacheRemoveFromHash(p);
+ pcachePageFree(p);
+ pcacheExitMutex();
+}
+
+/*
+** Make sure the page is marked as dirty. If it isn't dirty already,
+** make it so.
+*/
+SQLITE_PRIVATE void sqlite3PcacheMakeDirty(PgHdr *p){
+ PCache *pCache;
+ p->flags &= ~PGHDR_DONT_WRITE;
+ if( p->flags & PGHDR_DIRTY ) return;
+ assert( (p->flags & PGHDR_DIRTY)==0 );
+ assert( p->nRef>0 );
+ pCache = p->pCache;
+ pcacheEnterMutex();
+ pcacheRemoveFromList(&pCache->pClean, p);
+ pcacheAddToList(&pCache->pDirty, p);
+ pcacheExitMutex();
+ p->flags |= PGHDR_DIRTY;
+}
+
+void pcacheMakeClean(PgHdr *p){
+ PCache *pCache = p->pCache;
+ assert( p->apSave[0]==0 && p->apSave[1]==0 );
+ assert( p->flags & PGHDR_DIRTY );
+ pcacheRemoveFromList(&pCache->pDirty, p);
+ pcacheAddToList(&pCache->pClean, p);
+ p->flags &= ~PGHDR_DIRTY;
+ if( p->nRef==0 ){
+ pcacheAddToLruList(p);
+ pCache->nPinned--;
+ }
+ expensive_assert( pCache->nPinned==pcachePinnedCount(pCache) );
+}
+
+/*
+** Make sure the page is marked as clean. If it isn't clean already,
+** make it so.
*/
-static void lruListRemove(PgHdr *pPg){
- listRemove(&pPg->pPager->lru, &pPg->free, pPg);
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
- if( !pPg->pPager->memDb ){
- sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU));
- listRemove(&sqlite3LruPageList, &pPg->gfree, pPg);
- sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU));
+SQLITE_PRIVATE void sqlite3PcacheMakeClean(PgHdr *p){
+ if( (p->flags & PGHDR_DIRTY) ){
+ pcacheEnterMutex();
+ pcacheMakeClean(p);
+ pcacheExitMutex();
}
-#endif
}
-/*
-** This function is called just after the needSync flag has been cleared
-** from all pages managed by pPager (usually because the journal file
-** has just been synced). It updates the pPager->lru.pFirstSynced variable
-** and, if memory-management is enabled, the sqlite3LruPageList.pFirstSynced
-** variable also.
+/*
+** Make every page in the cache clean.
*/
-static void lruListSetFirstSynced(Pager *pPager){
- pPager->lru.pFirstSynced = pPager->lru.pFirst;
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
- if( !pPager->memDb ){
- PgHdr *p;
- sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU));
- for(p=sqlite3LruPageList.pFirst; p && p->needSync; p=p->gfree.pNext);
- assert(p==pPager->lru.pFirstSynced || p==sqlite3LruPageList.pFirstSynced);
- sqlite3LruPageList.pFirstSynced = p;
- sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU));
+SQLITE_PRIVATE void sqlite3PcacheCleanAll(PCache *pCache){
+ PgHdr *p;
+ pcacheEnterMutex();
+ while( (p = pCache->pDirty)!=0 ){
+ assert( p->apSave[0]==0 && p->apSave[1]==0 );
+ pcacheRemoveFromList(&pCache->pDirty, p);
+ p->flags &= ~PGHDR_DIRTY;
+ pcacheAddToList(&pCache->pClean, p);
+ if( p->nRef==0 ){
+ pcacheAddToLruList(p);
+ pCache->nPinned--;
+ }
}
-#endif
+ sqlite3PcacheAssertFlags(pCache, 0, PGHDR_DIRTY);
+ expensive_assert( pCache->nPinned==pcachePinnedCount(pCache) );
+ pcacheExitMutex();
}
/*
-** Return true if page *pPg has already been written to the statement
-** journal (or statement snapshot has been created, if *pPg is part
-** of an in-memory database).
+** Change the page number of page p to newPgno. If newPgno is 0, then the
+** page object is added to the clean-list and the PGHDR_REUSE_UNLIKELY
+** flag set.
*/
-static int pageInStatement(PgHdr *pPg){
- Pager *pPager = pPg->pPager;
- if( MEMDB ){
- return PGHDR_TO_HIST(pPg, pPager)->inStmt;
- }else{
- return sqlite3BitvecTest(pPager->pInStmt, pPg->pgno);
+SQLITE_PRIVATE void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){
+ assert( p->nRef>0 );
+ pcacheEnterMutex();
+ pcacheRemoveFromHash(p);
+ p->pgno = newPgno;
+ if( newPgno==0 ){
+ p->flags |= PGHDR_REUSE_UNLIKELY;
+ pcacheFree(p->apSave[0]);
+ pcacheFree(p->apSave[1]);
+ p->apSave[0] = 0;
+ p->apSave[1] = 0;
+ if( (p->flags & PGHDR_DIRTY) ){
+ pcacheMakeClean(p);
+ }
}
+ pcacheAddToHash(p);
+ pcacheExitMutex();
}
/*
-** Change the size of the pager hash table to N. N must be a power
-** of two.
+** Remove all content from a page cache
*/
-static void pager_resize_hash_table(Pager *pPager, int N){
- PgHdr **aHash, *pPg;
- assert( N>0 && (N&(N-1))==0 );
-#ifdef SQLITE_MALLOC_SOFT_LIMIT
- if( N*sizeof(aHash[0])>SQLITE_MALLOC_SOFT_LIMIT ){
- N = SQLITE_MALLOC_SOFT_LIMIT/sizeof(aHash[0]);
+void pcacheClear(PCache *pCache){
+ PgHdr *p, *pNext;
+ assert( sqlite3_mutex_held(pcache.mutex) );
+ for(p=pCache->pClean; p; p=pNext){
+ pNext = p->pNext;
+ pcacheRemoveFromLruList(p);
+ pcachePageFree(p);
}
- if( N==pPager->nHash ) return;
-#endif
- pagerLeave(pPager);
- if( pPager->aHash!=0 ) sqlite3BeginBenignMalloc();
- aHash = sqlite3MallocZero( sizeof(aHash[0])*N );
- if( pPager->aHash!=0 ) sqlite3EndBenignMalloc();
- pagerEnter(pPager);
- if( aHash==0 ){
- /* Failure to rehash is not an error. It is only a performance hit. */
- return;
+ for(p=pCache->pDirty; p; p=pNext){
+ pNext = p->pNext;
+ pcachePageFree(p);
}
- sqlite3_free(pPager->aHash);
- pPager->nHash = N;
- pPager->aHash = aHash;
- for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
- int h;
- if( pPg->pgno==0 ){
- assert( pPg->pNextHash==0 && pPg->pPrevHash==0 );
- continue;
+ pCache->pClean = 0;
+ pCache->pDirty = 0;
+ pCache->pDirtyTail = 0;
+ pCache->nPage = 0;
+ pCache->nPinned = 0;
+ memset(pCache->apHash, 0, pCache->nHash*sizeof(pCache->apHash[0]));
+}
+
+
+/*
+** Drop every cache entry whose page number is greater than "pgno".
+*/
+SQLITE_PRIVATE void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){
+ PgHdr *p, *pNext;
+ PgHdr *pDirty = pCache->pDirty;
+ pcacheEnterMutex();
+ for(p=pCache->pClean; p||pDirty; p=pNext){
+ if( !p ){
+ p = pDirty;
+ pDirty = 0;
}
- h = pPg->pgno & (N-1);
- pPg->pNextHash = aHash[h];
- if( aHash[h] ){
- aHash[h]->pPrevHash = pPg;
+ pNext = p->pNext;
+ if( p->pgno>pgno ){
+ if( p->nRef==0 ){
+ pcacheRemoveFromHash(p);
+ if( p->flags&PGHDR_DIRTY ){
+ pcacheRemoveFromList(&pCache->pDirty, p);
+ pCache->nPinned--;
+ }else{
+ pcacheRemoveFromList(&pCache->pClean, p);
+ pcacheRemoveFromLruList(p);
+ }
+ pcachePageFree(p);
+ }else{
+ /* If there are references to the page, it cannot be freed. In this
+ ** case, zero the page content instead.
+ */
+ memset(p->pData, 0, pCache->szPage);
+ }
}
- aHash[h] = pPg;
- pPg->pPrevHash = 0;
}
+ pcacheExitMutex();
}
/*
-** Read a 32-bit integer from the given file descriptor. Store the integer
-** that is read in *pRes. Return SQLITE_OK if everything worked, or an
-** error code is something goes wrong.
-**
-** All values are stored on disk as big-endian.
+** If there are currently more than pcache.nMaxPage pages allocated, try
+** to recycle pages to reduce the number allocated to pcache.nMaxPage.
*/
-static int read32bits(sqlite3_file *fd, i64 offset, u32 *pRes){
- unsigned char ac[4];
- int rc = sqlite3OsRead(fd, ac, sizeof(ac), offset);
- if( rc==SQLITE_OK ){
- *pRes = sqlite3Get4byte(ac);
+static void pcacheEnforceMaxPage(){
+ PgHdr *p;
+ assert( sqlite3_mutex_held(pcache.mutex) );
+ while( pcache.nCurrentPage>pcache.nMaxPage && (p = pcacheRecyclePage()) ){
+ pcachePageFree(p);
}
- return rc;
}
/*
-** Write a 32-bit integer into a string buffer in big-endian byte order.
+** Close a cache.
*/
-#define put32bits(A,B) sqlite3Put4byte((u8*)A,B)
+SQLITE_PRIVATE void sqlite3PcacheClose(PCache *pCache){
+ pcacheEnterMutex();
+
+ /* Free all the pages used by this pager and remove them from the LRU list. */
+ pcacheClear(pCache);
+ if( pCache->bPurgeable ){
+ pcache.nMaxPage -= pCache->nMax;
+ pcache.nMinPage -= pCache->nMin;
+ pcacheEnforceMaxPage();
+ }
+ sqlite3_free(pCache->apHash);
+ pcacheExitMutex();
+}
/*
-** Write a 32-bit integer into the given file descriptor. Return SQLITE_OK
-** on success or an error code is something goes wrong.
+** Preserve the content of the page. It is assumed that the content
+** has not been preserved already.
+**
+** If idJournal==0 then this is for the overall transaction.
+** If idJournal==1 then this is for the statement journal.
+**
+** This routine is used for in-memory databases only.
+**
+** Return SQLITE_OK or SQLITE_NOMEM if a memory allocation fails.
*/
-static int write32bits(sqlite3_file *fd, i64 offset, u32 val){
- char ac[4];
- put32bits(ac, val);
- return sqlite3OsWrite(fd, ac, 4, offset);
+SQLITE_PRIVATE int sqlite3PcachePreserve(PgHdr *p, int idJournal){
+ void *x;
+ int sz;
+ assert( p->pCache->bPurgeable==0 );
+ assert( p->apSave[idJournal]==0 );
+ sz = p->pCache->szPage;
+ p->apSave[idJournal] = x = sqlite3PageMalloc( sz );
+ if( x==0 ) return SQLITE_NOMEM;
+ memcpy(x, p->pData, sz);
+ return SQLITE_OK;
}
/*
-** If file pFd is open, call sqlite3OsUnlock() on it.
+** Commit a change previously preserved.
*/
-static int osUnlock(sqlite3_file *pFd, int eLock){
- if( !pFd->pMethods ){
- return SQLITE_OK;
+SQLITE_PRIVATE void sqlite3PcacheCommit(PCache *pCache, int idJournal){
+ PgHdr *p;
+ pcacheEnterMutex(); /* Mutex is required to call pcacheFree() */
+ for(p=pCache->pDirty; p; p=p->pNext){
+ if( p->apSave[idJournal] ){
+ pcacheFree(p->apSave[idJournal]);
+ p->apSave[idJournal] = 0;
+ }
}
- return sqlite3OsUnlock(pFd, eLock);
+ pcacheExitMutex();
}
/*
-** This function determines whether or not the atomic-write optimization
-** can be used with this pager. The optimization can be used if:
-**
-** (a) the value returned by OsDeviceCharacteristics() indicates that
-** a database page may be written atomically, and
-** (b) the value returned by OsSectorSize() is less than or equal
-** to the page size.
-**
-** If the optimization cannot be used, 0 is returned. If it can be used,
-** then the value returned is the size of the journal file when it
-** contains rollback data for exactly one page.
+** Rollback a change previously preserved.
*/
-#ifdef SQLITE_ENABLE_ATOMIC_WRITE
-static int jrnlBufferSize(Pager *pPager){
- int dc; /* Device characteristics */
- int nSector; /* Sector size */
- int szPage; /* Page size */
- sqlite3_file *fd = pPager->fd;
+SQLITE_PRIVATE void sqlite3PcacheRollback(PCache *pCache, int idJournal){
+ PgHdr *p;
+ int sz;
+ pcacheEnterMutex(); /* Mutex is required to call pcacheFree() */
+ sz = pCache->szPage;
+ for(p=pCache->pDirty; p; p=p->pNext){
+ if( p->apSave[idJournal] ){
+ memcpy(p->pData, p->apSave[idJournal], sz);
+ pcacheFree(p->apSave[idJournal]);
+ p->apSave[idJournal] = 0;
+ }
+ }
+ pcacheExitMutex();
+}
- if( fd->pMethods ){
- dc = sqlite3OsDeviceCharacteristics(fd);
- nSector = sqlite3OsSectorSize(fd);
- szPage = pPager->pageSize;
+/*
+** Assert flags settings on all pages. Debugging only.
+*/
+SQLITE_PRIVATE void sqlite3PcacheAssertFlags(PCache *pCache, int trueMask, int falseMask){
+ PgHdr *p;
+ for(p=pCache->pDirty; p; p=p->pNext){
+ assert( (p->flags&trueMask)==trueMask );
+ assert( (p->flags&falseMask)==0 );
+ }
+ for(p=pCache->pClean; p; p=p->pNext){
+ assert( (p->flags&trueMask)==trueMask );
+ assert( (p->flags&falseMask)==0 );
}
+}
- assert(SQLITE_IOCAP_ATOMIC512==(512>>8));
- assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8));
+/*
+** Discard the contents of the cache.
+*/
+SQLITE_PRIVATE int sqlite3PcacheClear(PCache *pCache){
+ assert(pCache->nRef==0);
+ pcacheEnterMutex();
+ pcacheClear(pCache);
+ pcacheExitMutex();
+ return SQLITE_OK;
+}
- if( !fd->pMethods ||
- (dc & (SQLITE_IOCAP_ATOMIC|(szPage>>8)) && nSector<=szPage) ){
- return JOURNAL_HDR_SZ(pPager) + JOURNAL_PG_SZ(pPager);
+/*
+** Merge two lists of pages connected by pDirty and in pgno order.
+** Do not both fixing the pPrevDirty pointers.
+*/
+static PgHdr *pcacheMergeDirtyList(PgHdr *pA, PgHdr *pB){
+ PgHdr result, *pTail;
+ pTail = &result;
+ while( pA && pB ){
+ if( pA->pgno<pB->pgno ){
+ pTail->pDirty = pA;
+ pTail = pA;
+ pA = pA->pDirty;
+ }else{
+ pTail->pDirty = pB;
+ pTail = pB;
+ pB = pB->pDirty;
+ }
}
- return 0;
+ if( pA ){
+ pTail->pDirty = pA;
+ }else if( pB ){
+ pTail->pDirty = pB;
+ }else{
+ pTail->pDirty = 0;
+ }
+ return result.pDirty;
}
-#endif
/*
-** This function should be called when an error occurs within the pager
-** code. The first argument is a pointer to the pager structure, the
-** second the error-code about to be returned by a pager API function.
-** The value returned is a copy of the second argument to this function.
-**
-** If the second argument is SQLITE_IOERR, SQLITE_CORRUPT, or SQLITE_FULL
-** the error becomes persistent. Until the persisten error is cleared,
-** subsequent API calls on this Pager will immediately return the same
-** error code.
-**
-** A persistent error indicates that the contents of the pager-cache
-** cannot be trusted. This state can be cleared by completely discarding
-** the contents of the pager-cache. If a transaction was active when
-** the persistent error occured, then the rollback journal may need
-** to be replayed.
+** Sort the list of pages in accending order by pgno. Pages are
+** connected by pDirty pointers. The pPrevDirty pointers are
+** corrupted by this sort.
*/
-static void pager_unlock(Pager *pPager);
-static int pager_error(Pager *pPager, int rc){
- int rc2 = rc & 0xff;
- assert(
- pPager->errCode==SQLITE_FULL ||
- pPager->errCode==SQLITE_OK ||
- (pPager->errCode & 0xff)==SQLITE_IOERR
- );
- if(
- rc2==SQLITE_FULL ||
- rc2==SQLITE_IOERR ||
- rc2==SQLITE_CORRUPT
- ){
- pPager->errCode = rc;
- if( pPager->state==PAGER_UNLOCK && pPager->nRef==0 ){
- /* If the pager is already unlocked, call pager_unlock() now to
- ** clear the error state and ensure that the pager-cache is
- ** completely empty.
+#define N_SORT_BUCKET_ALLOC 25
+#define N_SORT_BUCKET 25
+#ifdef SQLITE_TEST
+ int sqlite3_pager_n_sort_bucket = 0;
+ #undef N_SORT_BUCKET
+ #define N_SORT_BUCKET \
+ (sqlite3_pager_n_sort_bucket?sqlite3_pager_n_sort_bucket:N_SORT_BUCKET_ALLOC)
+#endif
+static PgHdr *pcacheSortDirtyList(PgHdr *pIn){
+ PgHdr *a[N_SORT_BUCKET_ALLOC], *p;
+ int i;
+ memset(a, 0, sizeof(a));
+ while( pIn ){
+ p = pIn;
+ pIn = p->pDirty;
+ p->pDirty = 0;
+ for(i=0; i<N_SORT_BUCKET-1; i++){
+ if( a[i]==0 ){
+ a[i] = p;
+ break;
+ }else{
+ p = pcacheMergeDirtyList(a[i], p);
+ a[i] = 0;
+ }
+ }
+ if( i==N_SORT_BUCKET-1 ){
+ /* Coverage: To get here, there need to be 2^(N_SORT_BUCKET)
+ ** elements in the input list. This is possible, but impractical.
+ ** Testing this line is the point of global variable
+ ** sqlite3_pager_n_sort_bucket.
*/
- pager_unlock(pPager);
+ a[i] = pcacheMergeDirtyList(a[i], p);
}
}
- return rc;
+ p = a[0];
+ for(i=1; i<N_SORT_BUCKET; i++){
+ p = pcacheMergeDirtyList(p, a[i]);
+ }
+ return p;
}
/*
-** If SQLITE_CHECK_PAGES is defined then we do some sanity checking
-** on the cache using a hash function. This is used for testing
-** and debugging only.
-*/
-#ifdef SQLITE_CHECK_PAGES
-/*
-** Return a 32-bit hash of the page data for pPage.
+** Return a list of all dirty pages in the cache, sorted by page number.
*/
-static u32 pager_datahash(int nByte, unsigned char *pData){
- u32 hash = 0;
- int i;
- for(i=0; i<nByte; i++){
- hash = (hash*1039) + pData[i];
+SQLITE_PRIVATE PgHdr *sqlite3PcacheDirtyList(PCache *pCache){
+ PgHdr *p;
+ for(p=pCache->pDirty; p; p=p->pNext){
+ p->pDirty = p->pNext;
}
- return hash;
-}
-static u32 pager_pagehash(PgHdr *pPage){
- return pager_datahash(pPage->pPager->pageSize,
- (unsigned char *)PGHDR_TO_DATA(pPage));
+ return pcacheSortDirtyList(pCache->pDirty);
}
-/*
-** The CHECK_PAGE macro takes a PgHdr* as an argument. If SQLITE_CHECK_PAGES
-** is defined, and NDEBUG is not defined, an assert() statement checks
-** that the page is either dirty or still matches the calculated page-hash.
+/*
+** Return the total number of outstanding page references.
*/
-#define CHECK_PAGE(x) checkPage(x)
-static void checkPage(PgHdr *pPg){
- Pager *pPager = pPg->pPager;
- assert( !pPg->pageHash || pPager->errCode || MEMDB || pPg->dirty ||
- pPg->pageHash==pager_pagehash(pPg) );
+SQLITE_PRIVATE int sqlite3PcacheRefCount(PCache *pCache){
+ return pCache->nRef;
}
-#else
-#define pager_datahash(X,Y) 0
-#define pager_pagehash(X) 0
-#define CHECK_PAGE(x)
-#endif
+/*
+** Return the total number of pages in the cache.
+*/
+SQLITE_PRIVATE int sqlite3PcachePagecount(PCache *pCache){
+ assert( pCache->nPage>=0 );
+ return pCache->nPage;
+}
+#ifdef SQLITE_CHECK_PAGES
/*
-** When this is called the journal file for pager pPager must be open.
-** The master journal file name is read from the end of the file and
-** written into memory supplied by the caller.
-**
-** zMaster must point to a buffer of at least nMaster bytes allocated by
-** the caller. This should be sqlite3_vfs.mxPathname+1 (to ensure there is
-** enough space to write the master journal name). If the master journal
-** name in the journal is longer than nMaster bytes (including a
-** nul-terminator), then this is handled as if no master journal name
-** were present in the journal.
-**
-** If no master journal file name is present zMaster[0] is set to 0 and
-** SQLITE_OK returned.
+** This function is used by the pager.c module to iterate through all
+** pages in the cache. At present, this is only required if the
+** SQLITE_CHECK_PAGES macro (used for debugging) is specified.
*/
-static int readMasterJournal(sqlite3_file *pJrnl, char *zMaster, int nMaster){
- int rc;
- u32 len;
- i64 szJ;
- u32 cksum;
- u32 u; /* Unsigned loop counter */
- unsigned char aMagic[8]; /* A buffer to hold the magic header */
+SQLITE_PRIVATE void sqlite3PcacheIterate(PCache *pCache, void (*xIter)(PgHdr *)){
+ PgHdr *p;
+ for(p=pCache->pClean; p; p=p->pNext){
+ xIter(p);
+ }
+ for(p=pCache->pDirty; p; p=p->pNext){
+ xIter(p);
+ }
+}
+#endif
- zMaster[0] = '\0';
+/*
+** Set flags on all pages in the page cache
+*/
+SQLITE_PRIVATE void sqlite3PcacheSetFlags(PCache *pCache, int andMask, int orMask){
+ PgHdr *p;
- rc = sqlite3OsFileSize(pJrnl, &szJ);
- if( rc!=SQLITE_OK || szJ<16 ) return rc;
+ assert( (orMask&PGHDR_NEED_SYNC)==0 );
- rc = read32bits(pJrnl, szJ-16, &len);
- if( rc!=SQLITE_OK ) return rc;
+ /* Obtain the global mutex before modifying any PgHdr.flags variables
+ ** or traversing the LRU list.
+ */
+ pcacheEnterMutex();
- if( len>=nMaster ){
- return SQLITE_OK;
+ for(p=pCache->pDirty; p; p=p->pNext){
+ p->flags = (p->flags&andMask)|orMask;
}
-
- rc = read32bits(pJrnl, szJ-12, &cksum);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3OsRead(pJrnl, aMagic, 8, szJ-8);
- if( rc!=SQLITE_OK || memcmp(aMagic, aJournalMagic, 8) ) return rc;
-
- rc = sqlite3OsRead(pJrnl, zMaster, len, szJ-16-len);
- if( rc!=SQLITE_OK ){
- return rc;
+ for(p=pCache->pClean; p; p=p->pNext){
+ p->flags = (p->flags&andMask)|orMask;
}
- zMaster[len] = '\0';
- /* See if the checksum matches the master journal name */
- for(u=0; u<len; u++){
- cksum -= zMaster[u];
- }
- if( cksum ){
- /* If the checksum doesn't add up, then one or more of the disk sectors
- ** containing the master journal filename is corrupted. This means
- ** definitely roll back, so just return SQLITE_OK and report a (nul)
- ** master-journal filename.
- */
- zMaster[0] = '\0';
+ if( 0==(andMask&PGHDR_NEED_SYNC) ){
+ pCache->pSynced = pCache->pDirtyTail;
+ assert( !pCache->pSynced || (pCache->pSynced->flags&PGHDR_NEED_SYNC)==0 );
}
-
- return SQLITE_OK;
+
+ pcacheExitMutex();
}
/*
-** Seek the journal file descriptor to the next sector boundary where a
-** journal header may be read or written. Pager.journalOff is updated with
-** the new seek offset.
-**
-** i.e for a sector size of 512:
-**
-** Input Offset Output Offset
-** ---------------------------------------
-** 0 0
-** 512 512
-** 100 512
-** 2000 2048
-**
+** Set the suggested cache-size value.
*/
-static void seekJournalHdr(Pager *pPager){
- i64 offset = 0;
- i64 c = pPager->journalOff;
- if( c ){
- offset = ((c-1)/JOURNAL_HDR_SZ(pPager) + 1) * JOURNAL_HDR_SZ(pPager);
- }
- assert( offset%JOURNAL_HDR_SZ(pPager)==0 );
- assert( offset>=c );
- assert( (offset-c)<JOURNAL_HDR_SZ(pPager) );
- pPager->journalOff = offset;
+SQLITE_PRIVATE int sqlite3PcacheGetCachesize(PCache *pCache){
+ return pCache->nMax;
}
/*
-** Write zeros over the header of the journal file. This has the
-** effect of invalidating the journal file and committing the
-** transaction.
+** Set the suggested cache-size value.
*/
-static int zeroJournalHdr(Pager *pPager, int doTruncate){
- int rc = SQLITE_OK;
- static const char zeroHdr[28];
-
- if( pPager->journalOff ){
- i64 iLimit = pPager->journalSizeLimit;
-
- IOTRACE(("JZEROHDR %p\n", pPager))
- if( doTruncate || iLimit==0 ){
- rc = sqlite3OsTruncate(pPager->jfd, 0);
- }else{
- rc = sqlite3OsWrite(pPager->jfd, zeroHdr, sizeof(zeroHdr), 0);
- }
- if( rc==SQLITE_OK && !pPager->noSync ){
- rc = sqlite3OsSync(pPager->jfd, SQLITE_SYNC_DATAONLY|pPager->sync_flags);
- }
-
- /* At this point the transaction is committed but the write lock
- ** is still held on the file. If there is a size limit configured for
- ** the persistent journal and the journal file currently consumes more
- ** space than that limit allows for, truncate it now. There is no need
- ** to sync the file following this operation.
- */
- if( rc==SQLITE_OK && iLimit>0 ){
- i64 sz;
- rc = sqlite3OsFileSize(pPager->jfd, &sz);
- if( rc==SQLITE_OK && sz>iLimit ){
- rc = sqlite3OsTruncate(pPager->jfd, iLimit);
- }
- }
+SQLITE_PRIVATE void sqlite3PcacheSetCachesize(PCache *pCache, int mxPage){
+ if( mxPage<10 ){
+ mxPage = 10;
}
- return rc;
+ if( pCache->bPurgeable ){
+ pcacheEnterMutex();
+ pcache.nMaxPage -= pCache->nMax;
+ pcache.nMaxPage += mxPage;
+ pcacheEnforceMaxPage();
+ pcacheExitMutex();
+ }
+ pCache->nMax = mxPage;
}
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
/*
-** The journal file must be open when this routine is called. A journal
-** header (JOURNAL_HDR_SZ bytes) is written into the journal file at the
-** current location.
+** This function is called to free superfluous dynamically allocated memory
+** held by the pager system. Memory in use by any SQLite pager allocated
+** by the current thread may be sqlite3_free()ed.
**
-** The format for the journal header is as follows:
-** - 8 bytes: Magic identifying journal format.
-** - 4 bytes: Number of records in journal, or -1 no-sync mode is on.
-** - 4 bytes: Random number used for page hash.
-** - 4 bytes: Initial database page count.
-** - 4 bytes: Sector size used by the process that wrote this journal.
-** - 4 bytes: Database page size.
-**
-** Followed by (JOURNAL_HDR_SZ - 28) bytes of unused space.
+** nReq is the number of bytes of memory required. Once this much has
+** been released, the function returns. The return value is the total number
+** of bytes of memory released.
*/
-static int writeJournalHdr(Pager *pPager){
- int rc = SQLITE_OK;
- char *zHeader = pPager->pTmpSpace;
- int nHeader = pPager->pageSize;
- int nWrite;
-
- if( nHeader>JOURNAL_HDR_SZ(pPager) ){
- nHeader = JOURNAL_HDR_SZ(pPager);
- }
-
- if( pPager->stmtHdrOff==0 ){
- pPager->stmtHdrOff = pPager->journalOff;
- }
-
- seekJournalHdr(pPager);
- pPager->journalHdr = pPager->journalOff;
-
- memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic));
-
- /*
- ** Write the nRec Field - the number of page records that follow this
- ** journal header. Normally, zero is written to this value at this time.
- ** After the records are added to the journal (and the journal synced,
- ** if in full-sync mode), the zero is overwritten with the true number
- ** of records (see syncJournal()).
- **
- ** A faster alternative is to write 0xFFFFFFFF to the nRec field. When
- ** reading the journal this value tells SQLite to assume that the
- ** rest of the journal file contains valid page records. This assumption
- ** is dangerous, as if a failure occured whilst writing to the journal
- ** file it may contain some garbage data. There are two scenarios
- ** where this risk can be ignored:
- **
- ** * When the pager is in no-sync mode. Corruption can follow a
- ** power failure in this case anyway.
- **
- ** * When the SQLITE_IOCAP_SAFE_APPEND flag is set. This guarantees
- ** that garbage data is never appended to the journal file.
- */
- assert(pPager->fd->pMethods||pPager->noSync);
- if( (pPager->noSync)
- || (sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND)
- ){
- put32bits(&zHeader[sizeof(aJournalMagic)], 0xffffffff);
- }else{
- put32bits(&zHeader[sizeof(aJournalMagic)], 0);
- }
-
- /* The random check-hash initialiser */
- sqlite3_randomness(sizeof(pPager->cksumInit), &pPager->cksumInit);
- put32bits(&zHeader[sizeof(aJournalMagic)+4], pPager->cksumInit);
- /* The initial database size */
- put32bits(&zHeader[sizeof(aJournalMagic)+8], pPager->dbSize);
- /* The assumed sector size for this process */
- put32bits(&zHeader[sizeof(aJournalMagic)+12], pPager->sectorSize);
- if( pPager->journalHdr==0 ){
- /* The page size */
- put32bits(&zHeader[sizeof(aJournalMagic)+16], pPager->pageSize);
+SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int nReq){
+ int nFree = 0;
+ if( pcache.pStart==0 ){
+ PgHdr *p;
+ pcacheEnterMutex();
+ while( (nReq<0 || nFree<nReq) && (p=pcacheRecyclePage()) ){
+ nFree += pcachePageSize(p);
+ pcachePageFree(p);
+ }
+ pcacheExitMutex();
}
+ return nFree;
+}
+#endif /* SQLITE_ENABLE_MEMORY_MANAGEMENT */
- for(nWrite=0; rc==SQLITE_OK&&nWrite<JOURNAL_HDR_SZ(pPager); nWrite+=nHeader){
- IOTRACE(("JHDR %p %lld %d\n", pPager, pPager->journalHdr, nHeader))
- rc = sqlite3OsWrite(pPager->jfd, zHeader, nHeader, pPager->journalOff);
- pPager->journalOff += nHeader;
+#ifdef SQLITE_TEST
+SQLITE_PRIVATE void sqlite3PcacheStats(
+ int *pnCurrent,
+ int *pnMax,
+ int *pnMin,
+ int *pnRecyclable
+){
+ PgHdr *p;
+ int nRecyclable = 0;
+ for(p=pcache.pLruHead; p; p=p->pNextLru){
+ nRecyclable++;
}
- return rc;
+ *pnCurrent = pcache.nCurrentPage;
+ *pnMax = pcache.nMaxPage;
+ *pnMin = pcache.nMinPage;
+ *pnRecyclable = nRecyclable;
}
+#endif
+
+/************** End of pcache.c **********************************************/
+/************** Begin file pager.c *******************************************/
/*
-** The journal file must be open when this is called. A journal header file
-** (JOURNAL_HDR_SZ bytes) is read from the current location in the journal
-** file. See comments above function writeJournalHdr() for a description of
-** the journal header format.
+** 2001 September 15
**
-** If the header is read successfully, *nRec is set to the number of
-** page records following this header and *dbSize is set to the size of the
-** database before the transaction began, in pages. Also, pPager->cksumInit
-** is set to the value read from the journal header. SQLITE_OK is returned
-** in this case.
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
**
-** If the journal header file appears to be corrupted, SQLITE_DONE is
-** returned and *nRec and *dbSize are not set. If JOURNAL_HDR_SZ bytes
-** cannot be read from the journal file an error code is returned.
+** 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 the page cache subsystem or "pager".
+**
+** The pager is used to access a database disk file. It implements
+** atomic commit and rollback through the use of a journal file that
+** is separate from the database file. The pager also implements file
+** locking to prevent two processes from writing the same database
+** file simultaneously, or one process from reading the database while
+** another is writing.
+**
+** @(#) $Id: pager.c,v 1.485 2008/08/28 02:26:07 drh Exp $
*/
-static int readJournalHdr(
- Pager *pPager,
- i64 journalSize,
- u32 *pNRec,
- u32 *pDbSize
-){
- int rc;
- unsigned char aMagic[8]; /* A buffer to hold the magic header */
- i64 jrnlOff;
- int iPageSize;
-
- seekJournalHdr(pPager);
- if( pPager->journalOff+JOURNAL_HDR_SZ(pPager) > journalSize ){
- return SQLITE_DONE;
- }
- jrnlOff = pPager->journalOff;
-
- rc = sqlite3OsRead(pPager->jfd, aMagic, sizeof(aMagic), jrnlOff);
- if( rc ) return rc;
- jrnlOff += sizeof(aMagic);
-
- if( memcmp(aMagic, aJournalMagic, sizeof(aMagic))!=0 ){
- return SQLITE_DONE;
- }
-
- rc = read32bits(pPager->jfd, jrnlOff, pNRec);
- if( rc ) return rc;
-
- rc = read32bits(pPager->jfd, jrnlOff+4, &pPager->cksumInit);
- if( rc ) return rc;
-
- rc = read32bits(pPager->jfd, jrnlOff+8, pDbSize);
- if( rc ) return rc;
-
- rc = read32bits(pPager->jfd, jrnlOff+16, (u32 *)&iPageSize);
- if( rc==SQLITE_OK
- && iPageSize>=512
- && iPageSize<=SQLITE_MAX_PAGE_SIZE
- && ((iPageSize-1)&iPageSize)==0
- ){
- u16 pagesize = iPageSize;
- rc = sqlite3PagerSetPagesize(pPager, &pagesize);
- }
- if( rc ) return rc;
-
- /* Update the assumed sector-size to match the value used by
- ** the process that created this journal. If this journal was
- ** created by a process other than this one, then this routine
- ** is being called from within pager_playback(). The local value
- ** of Pager.sectorSize is restored at the end of that routine.
- */
- rc = read32bits(pPager->jfd, jrnlOff+12, (u32 *)&pPager->sectorSize);
- if( rc ) return rc;
+#ifndef SQLITE_OMIT_DISKIO
- pPager->journalOff += JOURNAL_HDR_SZ(pPager);
- return SQLITE_OK;
-}
+/*
+** Macros for troubleshooting. Normally turned off
+*/
+#if 0
+#define sqlite3DebugPrintf printf
+#define PAGERTRACE1(X) sqlite3DebugPrintf(X)
+#define PAGERTRACE2(X,Y) sqlite3DebugPrintf(X,Y)
+#define PAGERTRACE3(X,Y,Z) sqlite3DebugPrintf(X,Y,Z)
+#define PAGERTRACE4(X,Y,Z,W) sqlite3DebugPrintf(X,Y,Z,W)
+#define PAGERTRACE5(X,Y,Z,W,V) sqlite3DebugPrintf(X,Y,Z,W,V)
+#else
+#define PAGERTRACE1(X)
+#define PAGERTRACE2(X,Y)
+#define PAGERTRACE3(X,Y,Z)
+#define PAGERTRACE4(X,Y,Z,W)
+#define PAGERTRACE5(X,Y,Z,W,V)
+#endif
+/*
+** The following two macros are used within the PAGERTRACEX() macros above
+** to print out file-descriptors.
+**
+** PAGERID() takes a pointer to a Pager struct as its argument. The
+** associated file-descriptor is returned. FILEHANDLEID() takes an sqlite3_file
+** struct as its argument.
+*/
+#define PAGERID(p) ((int)(p->fd))
+#define FILEHANDLEID(fd) ((int)fd)
/*
-** Write the supplied master journal name into the journal file for pager
-** pPager at the current location. The master journal name must be the last
-** thing written to a journal file. If the pager is in full-sync mode, the
-** journal file descriptor is advanced to the next sector boundary before
-** anything is written. The format is:
+** The page cache as a whole is always in one of the following
+** states:
**
-** + 4 bytes: PAGER_MJ_PGNO.
-** + N bytes: length of master journal name.
-** + 4 bytes: N
-** + 4 bytes: Master journal name checksum.
-** + 8 bytes: aJournalMagic[].
+** PAGER_UNLOCK The page cache is not currently reading or
+** writing the database file. There is no
+** data held in memory. This is the initial
+** state.
**
-** The master journal page checksum is the sum of the bytes in the master
-** journal name.
+** PAGER_SHARED The page cache is reading the database.
+** Writing is not permitted. There can be
+** multiple readers accessing the same database
+** file at the same time.
**
-** If zMaster is a NULL pointer (occurs for a single database transaction),
-** this call is a no-op.
+** PAGER_RESERVED This process has reserved the database for writing
+** but has not yet made any changes. Only one process
+** at a time can reserve the database. The original
+** database file has not been modified so other
+** processes may still be reading the on-disk
+** database file.
+**
+** PAGER_EXCLUSIVE The page cache is writing the database.
+** Access is exclusive. No other processes or
+** threads can be reading or writing while one
+** process is writing.
+**
+** PAGER_SYNCED The pager moves to this state from PAGER_EXCLUSIVE
+** after all dirty pages have been written to the
+** database file and the file has been synced to
+** disk. All that remains to do is to remove or
+** truncate the journal file and the transaction
+** will be committed.
+**
+** The page cache comes up in PAGER_UNLOCK. The first time a
+** sqlite3PagerGet() occurs, the state transitions to PAGER_SHARED.
+** After all pages have been released using sqlite_page_unref(),
+** the state transitions back to PAGER_UNLOCK. The first time
+** that sqlite3PagerWrite() is called, the state transitions to
+** PAGER_RESERVED. (Note that sqlite3PagerWrite() can only be
+** called on an outstanding page which means that the pager must
+** be in PAGER_SHARED before it transitions to PAGER_RESERVED.)
+** PAGER_RESERVED means that there is an open rollback journal.
+** The transition to PAGER_EXCLUSIVE occurs before any changes
+** are made to the database file, though writes to the rollback
+** journal occurs with just PAGER_RESERVED. After an sqlite3PagerRollback()
+** or sqlite3PagerCommitPhaseTwo(), the state can go back to PAGER_SHARED,
+** or it can stay at PAGER_EXCLUSIVE if we are in exclusive access mode.
*/
-static int writeMasterJournal(Pager *pPager, const char *zMaster){
- int rc;
- int len;
- int i;
- i64 jrnlOff;
- i64 jrnlSize;
- u32 cksum = 0;
- char zBuf[sizeof(aJournalMagic)+2*4];
-
- if( !zMaster || pPager->setMaster) return SQLITE_OK;
- pPager->setMaster = 1;
-
- len = strlen(zMaster);
- for(i=0; i<len; i++){
- cksum += zMaster[i];
- }
-
- /* If in full-sync mode, advance to the next disk sector before writing
- ** the master journal name. This is in case the previous page written to
- ** the journal has already been synced.
- */
- if( pPager->fullSync ){
- seekJournalHdr(pPager);
- }
- jrnlOff = pPager->journalOff;
- pPager->journalOff += (len+20);
-
- rc = write32bits(pPager->jfd, jrnlOff, PAGER_MJ_PGNO(pPager));
- if( rc!=SQLITE_OK ) return rc;
- jrnlOff += 4;
-
- rc = sqlite3OsWrite(pPager->jfd, zMaster, len, jrnlOff);
- if( rc!=SQLITE_OK ) return rc;
- jrnlOff += len;
-
- put32bits(zBuf, len);
- put32bits(&zBuf[4], cksum);
- memcpy(&zBuf[8], aJournalMagic, sizeof(aJournalMagic));
- rc = sqlite3OsWrite(pPager->jfd, zBuf, 8+sizeof(aJournalMagic), jrnlOff);
- jrnlOff += 8+sizeof(aJournalMagic);
- pPager->needSync = !pPager->noSync;
-
- /* If the pager is in peristent-journal mode, then the physical
- ** journal-file may extend past the end of the master-journal name
- ** and 8 bytes of magic data just written to the file. This is
- ** dangerous because the code to rollback a hot-journal file
- ** will not be able to find the master-journal name to determine
- ** whether or not the journal is hot.
- **
- ** Easiest thing to do in this scenario is to truncate the journal
- ** file to the required size.
- */
- if( (rc==SQLITE_OK)
- && (rc = sqlite3OsFileSize(pPager->jfd, &jrnlSize))==SQLITE_OK
- && jrnlSize>jrnlOff
- ){
- rc = sqlite3OsTruncate(pPager->jfd, jrnlOff);
- }
- return rc;
-}
+#define PAGER_UNLOCK 0
+#define PAGER_SHARED 1 /* same as SHARED_LOCK */
+#define PAGER_RESERVED 2 /* same as RESERVED_LOCK */
+#define PAGER_EXCLUSIVE 4 /* same as EXCLUSIVE_LOCK */
+#define PAGER_SYNCED 5
/*
-** Add or remove a page from the list of all pages that are in the
-** statement journal.
-**
-** The Pager keeps a separate list of pages that are currently in
-** the statement journal. This helps the sqlite3PagerStmtCommit()
-** routine run MUCH faster for the common case where there are many
-** pages in memory but only a few are in the statement journal.
+** If the SQLITE_BUSY_RESERVED_LOCK macro is set to true at compile-time,
+** then failed attempts to get a reserved lock will invoke the busy callback.
+** This is off by default. To see why, consider the following scenario:
+**
+** Suppose thread A already has a shared lock and wants a reserved lock.
+** Thread B already has a reserved lock and wants an exclusive lock. If
+** both threads are using their busy callbacks, it might be a long time
+** be for one of the threads give up and allows the other to proceed.
+** But if the thread trying to get the reserved lock gives up quickly
+** (if it never invokes its busy callback) then the contention will be
+** resolved quickly.
*/
-static void page_add_to_stmt_list(PgHdr *pPg){
- Pager *pPager = pPg->pPager;
- PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager);
- assert( MEMDB );
- if( !pHist->inStmt ){
- assert( pHist->pPrevStmt==0 && pHist->pNextStmt==0 );
- if( pPager->pStmt ){
- PGHDR_TO_HIST(pPager->pStmt, pPager)->pPrevStmt = pPg;
- }
- pHist->pNextStmt = pPager->pStmt;
- pPager->pStmt = pPg;
- pHist->inStmt = 1;
- }
-}
+#ifndef SQLITE_BUSY_RESERVED_LOCK
+# define SQLITE_BUSY_RESERVED_LOCK 0
+#endif
/*
-** Find a page in the hash table given its page number. Return
-** a pointer to the page or NULL if not found.
+** This macro rounds values up so that if the value is an address it
+** is guaranteed to be an address that is aligned to an 8-byte boundary.
*/
-static PgHdr *pager_lookup(Pager *pPager, Pgno pgno){
- PgHdr *p;
- if( pPager->aHash==0 ) return 0;
- p = pPager->aHash[pgno & (pPager->nHash-1)];
- while( p && p->pgno!=pgno ){
- p = p->pNextHash;
- }
- return p;
-}
+#define FORCE_ALIGNMENT(X) (((X)+7)&~7)
/*
-** Clear the in-memory cache. This routine
-** sets the state of the pager back to what it was when it was first
-** opened. Any outstanding pages are invalidated and subsequent attempts
-** to access those pages will likely result in a coredump.
+** A macro used for invoking the codec if there is one
*/
-static void pager_reset(Pager *pPager){
- PgHdr *pPg, *pNext;
- if( pPager->errCode ) return;
- for(pPg=pPager->pAll; pPg; pPg=pNext){
- IOTRACE(("PGFREE %p %d\n", pPager, pPg->pgno));
- PAGER_INCR(sqlite3_pager_pgfree_count);
- pNext = pPg->pNextAll;
- lruListRemove(pPg);
- sqlite3PageFree(pPg->pData);
- sqlite3_free(pPg);
- }
- assert(pPager->lru.pFirst==0);
- assert(pPager->lru.pFirstSynced==0);
- assert(pPager->lru.pLast==0);
- pPager->pStmt = 0;
- pPager->pAll = 0;
- pPager->pDirty = 0;
- pPager->nHash = 0;
- sqlite3_free(pPager->aHash);
- pPager->nPage = 0;
- pPager->aHash = 0;
- pPager->nRef = 0;
-}
+#ifdef SQLITE_HAS_CODEC
+# define CODEC1(P,D,N,X) if( P->xCodec!=0 ){ P->xCodec(P->pCodecArg,D,N,X); }
+# define CODEC2(P,D,N,X) ((char*)(P->xCodec!=0?P->xCodec(P->pCodecArg,D,N,X):D))
+#else
+# define CODEC1(P,D,N,X) /* NO-OP */
+# define CODEC2(P,D,N,X) ((char*)D)
+#endif
/*
-** Unlock the database file.
+** A open page cache is an instance of the following structure.
**
-** If the pager is currently in error state, discard the contents of
-** the cache and reset the Pager structure internal state. If there is
-** an open journal-file, then the next time a shared-lock is obtained
-** on the pager file (by this or any other process), it will be
-** treated as a hot-journal and rolled back.
+** Pager.errCode may be set to SQLITE_IOERR, SQLITE_CORRUPT, or
+** or SQLITE_FULL. Once one of the first three errors occurs, it persists
+** and is returned as the result of every major pager API call. The
+** SQLITE_FULL return code is slightly different. It persists only until the
+** next successful rollback is performed on the pager cache. Also,
+** SQLITE_FULL does not affect the sqlite3PagerGet() and sqlite3PagerLookup()
+** APIs, they may still be used successfully.
*/
-static void pager_unlock(Pager *pPager){
- if( !pPager->exclusiveMode ){
- if( !MEMDB ){
- int rc = osUnlock(pPager->fd, NO_LOCK);
- if( rc ) pPager->errCode = rc;
- pPager->dbSize = -1;
- IOTRACE(("UNLOCK %p\n", pPager))
-
- /* Always close the journal file when dropping the database lock.
- ** Otherwise, another connection with journal_mode=delete might
- ** delete the file out from under us.
- */
- if( pPager->journalOpen ){
- sqlite3OsClose(pPager->jfd);
- pPager->journalOpen = 0;
- sqlite3BitvecDestroy(pPager->pInJournal);
- pPager->pInJournal = 0;
- }
+struct Pager {
+ sqlite3_vfs *pVfs; /* OS functions to use for IO */
+ u8 journalOpen; /* True if journal file descriptors is valid */
+ u8 journalStarted; /* True if header of journal is synced */
+ u8 useJournal; /* Use a rollback journal on this file */
+ u8 noReadlock; /* Do not bother to obtain readlocks */
+ u8 stmtOpen; /* True if the statement subjournal is open */
+ u8 stmtInUse; /* True we are in a statement subtransaction */
+ u8 stmtAutoopen; /* Open stmt journal when main journal is opened*/
+ u8 noSync; /* Do not sync the journal if true */
+ u8 fullSync; /* Do extra syncs of the journal for robustness */
+ u8 sync_flags; /* One of SYNC_NORMAL or SYNC_FULL */
+ u8 state; /* PAGER_UNLOCK, _SHARED, _RESERVED, etc. */
+ u8 tempFile; /* zFilename is a temporary file */
+ u8 readOnly; /* True for a read-only database */
+ u8 needSync; /* True if an fsync() is needed on the journal */
+ u8 dirtyCache; /* True if cached pages have changed */
+ u8 alwaysRollback; /* Disable DontRollback() for all pages */
+ u8 memDb; /* True to inhibit all file I/O */
+ u8 setMaster; /* True if a m-j name has been written to jrnl */
+ u8 doNotSync; /* Boolean. While true, do not spill the cache */
+ u8 exclusiveMode; /* Boolean. True if locking_mode==EXCLUSIVE */
+ u8 journalMode; /* On of the PAGER_JOURNALMODE_* values */
+ u8 dbModified; /* True if there are any changes to the Db */
+ u8 changeCountDone; /* Set after incrementing the change-counter */
+ u32 vfsFlags; /* Flags for sqlite3_vfs.xOpen() */
+ int errCode; /* One of several kinds of errors */
+ int dbSize; /* Number of pages in the file */
+ int origDbSize; /* dbSize before the current change */
+ int stmtSize; /* Size of database (in pages) at stmt_begin() */
+ int nRec; /* Number of pages written to the journal */
+ u32 cksumInit; /* Quasi-random value added to every checksum */
+ int stmtNRec; /* Number of records in stmt subjournal */
+ int nExtra; /* Add this many bytes to each in-memory page */
+ int pageSize; /* Number of bytes in a page */
+ int nPage; /* Total number of in-memory pages */
+ int mxPage; /* Maximum number of pages to hold in cache */
+ Pgno mxPgno; /* Maximum allowed size of the database */
+ Bitvec *pInJournal; /* One bit for each page in the database file */
+ Bitvec *pInStmt; /* One bit for each page in the database */
+ Bitvec *pAlwaysRollback; /* One bit for each page marked always-rollback */
+ char *zFilename; /* Name of the database file */
+ char *zJournal; /* Name of the journal file */
+ char *zDirectory; /* Directory hold database and journal files */
+ sqlite3_file *fd, *jfd; /* File descriptors for database and journal */
+ sqlite3_file *stfd; /* File descriptor for the statement subjournal*/
+ BusyHandler *pBusyHandler; /* Pointer to sqlite.busyHandler */
+ i64 journalOff; /* Current byte offset in the journal file */
+ i64 journalHdr; /* Byte offset to previous journal header */
+ i64 stmtHdrOff; /* First journal header written this statement */
+ i64 stmtCksum; /* cksumInit when statement was started */
+ i64 stmtJSize; /* Size of journal at stmt_begin() */
+ int sectorSize; /* Assumed sector size during rollback */
+#ifdef SQLITE_TEST
+ int nHit, nMiss; /* Cache hits and missing */
+ int nRead, nWrite; /* Database pages read/written */
+#endif
+ void (*xDestructor)(DbPage*,int); /* Call this routine when freeing pages */
+ void (*xReiniter)(DbPage*,int); /* Call this routine when reloading pages */
+#ifdef SQLITE_HAS_CODEC
+ void *(*xCodec)(void*,void*,Pgno,int); /* Routine for en/decoding data */
+ void *pCodecArg; /* First argument to xCodec() */
+#endif
+ char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */
+ char dbFileVers[16]; /* Changes whenever database file changes */
+ i64 journalSizeLimit; /* Size limit for persistent journal files */
+ PCache *pPCache; /* Pointer to page cache object */
+};
- /* If Pager.errCode is set, the contents of the pager cache cannot be
- ** trusted. Now that the pager file is unlocked, the contents of the
- ** cache can be discarded and the error code safely cleared.
- */
- if( pPager->errCode ){
- if( rc==SQLITE_OK ) pPager->errCode = SQLITE_OK;
- pager_reset(pPager);
- if( pPager->stmtOpen ){
- sqlite3OsClose(pPager->stfd);
- sqlite3BitvecDestroy(pPager->pInStmt);
- pPager->pInStmt = 0;
- }
- pPager->stmtOpen = 0;
- pPager->stmtInUse = 0;
- pPager->journalOff = 0;
- pPager->journalStarted = 0;
- pPager->stmtAutoopen = 0;
- pPager->origDbSize = 0;
- }
- }
+/*
+** The following global variables hold counters used for
+** testing purposes only. These variables do not exist in
+** a non-testing build. These variables are not thread-safe.
+*/
+#ifdef SQLITE_TEST
+SQLITE_API int sqlite3_pager_readdb_count = 0; /* Number of full pages read from DB */
+SQLITE_API int sqlite3_pager_writedb_count = 0; /* Number of full pages written to DB */
+SQLITE_API int sqlite3_pager_writej_count = 0; /* Number of pages written to journal */
+# define PAGER_INCR(v) v++
+#else
+# define PAGER_INCR(v)
+#endif
- if( !MEMDB || pPager->errCode==SQLITE_OK ){
- pPager->state = PAGER_UNLOCK;
- pPager->changeCountDone = 0;
- }
+
+
+/*
+** Journal files begin with the following magic string. The data
+** was obtained from /dev/random. It is used only as a sanity check.
+**
+** Since version 2.8.0, the journal format contains additional sanity
+** checking information. If the power fails while the journal is begin
+** written, semi-random garbage data might appear in the journal
+** file after power is restored. If an attempt is then made
+** to roll the journal back, the database could be corrupted. The additional
+** sanity checking data is an attempt to discover the garbage in the
+** journal and ignore it.
+**
+** The sanity checking information for the new journal format consists
+** of a 32-bit checksum on each page of data. The checksum covers both
+** the page number and the pPager->pageSize bytes of data for the page.
+** This cksum is initialized to a 32-bit random value that appears in the
+** journal file right after the header. The random initializer is important,
+** because garbage data that appears at the end of a journal is likely
+** data that was once in other files that have now been deleted. If the
+** garbage data came from an obsolete journal file, the checksums might
+** be correct. But by initializing the checksum to random value which
+** is different for every journal, we minimize that risk.
+*/
+static const unsigned char aJournalMagic[] = {
+ 0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd7,
+};
+
+/*
+** The size of the header and of each page in the journal is determined
+** by the following macros.
+*/
+#define JOURNAL_PG_SZ(pPager) ((pPager->pageSize) + 8)
+
+/*
+** The journal header size for this pager. In the future, this could be
+** set to some value read from the disk controller. The important
+** characteristic is that it is the same size as a disk sector.
+*/
+#define JOURNAL_HDR_SZ(pPager) (pPager->sectorSize)
+
+/*
+** The macro MEMDB is true if we are dealing with an in-memory database.
+** We do this as a macro so that if the SQLITE_OMIT_MEMORYDB macro is set,
+** the value of MEMDB will be a constant and the compiler will optimize
+** out code that would never execute.
+*/
+#ifdef SQLITE_OMIT_MEMORYDB
+# define MEMDB 0
+#else
+# define MEMDB pPager->memDb
+#endif
+
+/*
+** Page number PAGER_MJ_PGNO is never used in an SQLite database (it is
+** reserved for working around a windows/posix incompatibility). It is
+** used in the journal to signify that the remainder of the journal file
+** is devoted to storing a master journal name - there are no more pages to
+** roll back. See comments for function writeMasterJournal() for details.
+*/
+/* #define PAGER_MJ_PGNO(x) (PENDING_BYTE/((x)->pageSize)) */
+#define PAGER_MJ_PGNO(x) ((PENDING_BYTE/((x)->pageSize))+1)
+
+/*
+** The maximum legal page number is (2^31 - 1).
+*/
+#define PAGER_MAX_PGNO 2147483647
+
+/*
+** Return true if page *pPg has already been written to the statement
+** journal (or statement snapshot has been created, if *pPg is part
+** of an in-memory database).
+*/
+static int pageInStatement(PgHdr *pPg){
+ Pager *pPager = pPg->pPager;
+ if( MEMDB ){
+ return pPg->apSave[1]!=0;
+ }else{
+ return sqlite3BitvecTest(pPager->pInStmt, pPg->pgno);
}
}
/*
-** Execute a rollback if a transaction is active and unlock the
-** database file. If the pager has already entered the error state,
-** do not attempt the rollback.
+** Read a 32-bit integer from the given file descriptor. Store the integer
+** that is read in *pRes. Return SQLITE_OK if everything worked, or an
+** error code is something goes wrong.
+**
+** All values are stored on disk as big-endian.
*/
-static void pagerUnlockAndRollback(Pager *p){
- /* assert( p->state>=PAGER_RESERVED || p->journalOpen==0 ); */
- if( p->errCode==SQLITE_OK && p->state>=PAGER_RESERVED ){
- sqlite3BeginBenignMalloc();
- sqlite3PagerRollback(p);
- sqlite3EndBenignMalloc();
+static int read32bits(sqlite3_file *fd, i64 offset, u32 *pRes){
+ unsigned char ac[4];
+ int rc = sqlite3OsRead(fd, ac, sizeof(ac), offset);
+ if( rc==SQLITE_OK ){
+ *pRes = sqlite3Get4byte(ac);
}
- pager_unlock(p);
-#if 0
- assert( p->errCode || !p->journalOpen || (p->exclusiveMode&&!p->journalOff) );
- assert( p->errCode || !p->stmtOpen || p->exclusiveMode );
-#endif
+ return rc;
}
/*
-** This routine ends a transaction. A transaction is ended by either
-** a COMMIT or a ROLLBACK.
-**
-** When this routine is called, the pager has the journal file open and
-** a RESERVED or EXCLUSIVE lock on the database. This routine will release
-** the database lock and acquires a SHARED lock in its place if that is
-** the appropriate thing to do. Release locks usually is appropriate,
-** unless we are in exclusive access mode or unless this is a
-** COMMIT AND BEGIN or ROLLBACK AND BEGIN operation.
+** Write a 32-bit integer into a string buffer in big-endian byte order.
+*/
+#define put32bits(A,B) sqlite3Put4byte((u8*)A,B)
+
+/*
+** Write a 32-bit integer into the given file descriptor. Return SQLITE_OK
+** on success or an error code is something goes wrong.
+*/
+static int write32bits(sqlite3_file *fd, i64 offset, u32 val){
+ char ac[4];
+ put32bits(ac, val);
+ return sqlite3OsWrite(fd, ac, 4, offset);
+}
+
+/*
+** If file pFd is open, call sqlite3OsUnlock() on it.
+*/
+static int osUnlock(sqlite3_file *pFd, int eLock){
+ if( !pFd->pMethods ){
+ return SQLITE_OK;
+ }
+ return sqlite3OsUnlock(pFd, eLock);
+}
+
+/*
+** This function determines whether or not the atomic-write optimization
+** can be used with this pager. The optimization can be used if:
**
-** The journal file is either deleted or truncated.
+** (a) the value returned by OsDeviceCharacteristics() indicates that
+** a database page may be written atomically, and
+** (b) the value returned by OsSectorSize() is less than or equal
+** to the page size.
**
-** TODO: Consider keeping the journal file open for temporary databases.
-** This might give a performance improvement on windows where opening
-** a file is an expensive operation.
+** If the optimization cannot be used, 0 is returned. If it can be used,
+** then the value returned is the size of the journal file when it
+** contains rollback data for exactly one page.
*/
-static int pager_end_transaction(Pager *pPager, int hasMaster){
- PgHdr *pPg;
- int rc = SQLITE_OK;
- int rc2 = SQLITE_OK;
- assert( !MEMDB );
- if( pPager->state<PAGER_RESERVED ){
- return SQLITE_OK;
+#ifdef SQLITE_ENABLE_ATOMIC_WRITE
+static int jrnlBufferSize(Pager *pPager){
+ int dc; /* Device characteristics */
+ int nSector; /* Sector size */
+ int szPage; /* Page size */
+ sqlite3_file *fd = pPager->fd;
+
+ if( fd->pMethods ){
+ dc = sqlite3OsDeviceCharacteristics(fd);
+ nSector = sqlite3OsSectorSize(fd);
+ szPage = pPager->pageSize;
}
- sqlite3PagerStmtCommit(pPager);
- if( pPager->stmtOpen && !pPager->exclusiveMode ){
- sqlite3OsClose(pPager->stfd);
- pPager->stmtOpen = 0;
+
+ assert(SQLITE_IOCAP_ATOMIC512==(512>>8));
+ assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8));
+
+ if( !fd->pMethods ||
+ (dc & (SQLITE_IOCAP_ATOMIC|(szPage>>8)) && nSector<=szPage) ){
+ return JOURNAL_HDR_SZ(pPager) + JOURNAL_PG_SZ(pPager);
}
- if( pPager->journalOpen ){
- if( pPager->exclusiveMode
- || pPager->journalMode==PAGER_JOURNALMODE_PERSIST
- ){
- rc = zeroJournalHdr(pPager, hasMaster);
- pager_error(pPager, rc);
- pPager->journalOff = 0;
- pPager->journalStarted = 0;
- }else{
- sqlite3OsClose(pPager->jfd);
- pPager->journalOpen = 0;
- if( rc==SQLITE_OK && !pPager->tempFile ){
- rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
- }
- }
- sqlite3BitvecDestroy(pPager->pInJournal);
- pPager->pInJournal = 0;
- for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
- pPg->inJournal = 0;
- pPg->dirty = 0;
- pPg->needSync = 0;
- pPg->alwaysRollback = 0;
-#ifdef SQLITE_CHECK_PAGES
- pPg->pageHash = pager_pagehash(pPg);
+ return 0;
+}
#endif
+
+/*
+** This function should be called when an error occurs within the pager
+** code. The first argument is a pointer to the pager structure, the
+** second the error-code about to be returned by a pager API function.
+** The value returned is a copy of the second argument to this function.
+**
+** If the second argument is SQLITE_IOERR, SQLITE_CORRUPT, or SQLITE_FULL
+** the error becomes persistent. Until the persisten error is cleared,
+** subsequent API calls on this Pager will immediately return the same
+** error code.
+**
+** A persistent error indicates that the contents of the pager-cache
+** cannot be trusted. This state can be cleared by completely discarding
+** the contents of the pager-cache. If a transaction was active when
+** the persistent error occured, then the rollback journal may need
+** to be replayed.
+*/
+static void pager_unlock(Pager *pPager);
+static int pager_error(Pager *pPager, int rc){
+ int rc2 = rc & 0xff;
+ assert(
+ pPager->errCode==SQLITE_FULL ||
+ pPager->errCode==SQLITE_OK ||
+ (pPager->errCode & 0xff)==SQLITE_IOERR
+ );
+ if(
+ rc2==SQLITE_FULL ||
+ rc2==SQLITE_IOERR ||
+ rc2==SQLITE_CORRUPT
+ ){
+ pPager->errCode = rc;
+ if( pPager->state==PAGER_UNLOCK
+ && sqlite3PcacheRefCount(pPager->pPCache)==0
+ ){
+ /* If the pager is already unlocked, call pager_unlock() now to
+ ** clear the error state and ensure that the pager-cache is
+ ** completely empty.
+ */
+ pager_unlock(pPager);
}
- pPager->pDirty = 0;
- pPager->dirtyCache = 0;
- pPager->nRec = 0;
- }else{
- assert( pPager->pInJournal==0 );
}
+ return rc;
+}
- if( !pPager->exclusiveMode ){
- rc2 = osUnlock(pPager->fd, SHARED_LOCK);
- pPager->state = PAGER_SHARED;
- }else if( pPager->state==PAGER_SYNCED ){
- pPager->state = PAGER_EXCLUSIVE;
+/*
+** If SQLITE_CHECK_PAGES is defined then we do some sanity checking
+** on the cache using a hash function. This is used for testing
+** and debugging only.
+*/
+#ifdef SQLITE_CHECK_PAGES
+/*
+** Return a 32-bit hash of the page data for pPage.
+*/
+static u32 pager_datahash(int nByte, unsigned char *pData){
+ u32 hash = 0;
+ int i;
+ for(i=0; i<nByte; i++){
+ hash = (hash*1039) + pData[i];
}
- pPager->origDbSize = 0;
- pPager->setMaster = 0;
- pPager->needSync = 0;
- lruListSetFirstSynced(pPager);
- pPager->dbSize = -1;
- pPager->dbModified = 0;
-
- return (rc==SQLITE_OK?rc2:rc);
+ return hash;
+}
+static u32 pager_pagehash(PgHdr *pPage){
+ return pager_datahash(pPage->pPager->pageSize, (unsigned char *)pPage->pData);
+}
+static u32 pager_set_pagehash(PgHdr *pPage){
+ pPage->pageHash = pager_pagehash(pPage);
}
/*
-** Compute and return a checksum for the page of data.
-**
-** This is not a real checksum. It is really just the sum of the
-** random initial value and the page number. We experimented with
-** a checksum of the entire data, but that was found to be too slow.
-**
-** Note that the page number is stored at the beginning of data and
-** the checksum is stored at the end. This is important. If journal
-** corruption occurs due to a power failure, the most likely scenario
-** is that one end or the other of the record will be changed. It is
-** much less likely that the two ends of the journal record will be
-** correct and the middle be corrupt. Thus, this "checksum" scheme,
-** though fast and simple, catches the mostly likely kind of corruption.
-**
-** FIX ME: Consider adding every 200th (or so) byte of the data to the
-** checksum. That way if a single page spans 3 or more disk sectors and
-** only the middle sector is corrupt, we will still have a reasonable
-** chance of failing the checksum and thus detecting the problem.
+** The CHECK_PAGE macro takes a PgHdr* as an argument. If SQLITE_CHECK_PAGES
+** is defined, and NDEBUG is not defined, an assert() statement checks
+** that the page is either dirty or still matches the calculated page-hash.
*/
-static u32 pager_cksum(Pager *pPager, const u8 *aData){
- u32 cksum = pPager->cksumInit;
- int i = pPager->pageSize-200;
- while( i>0 ){
- cksum += aData[i];
- i -= 200;
- }
- return cksum;
+#define CHECK_PAGE(x) checkPage(x)
+static void checkPage(PgHdr *pPg){
+ Pager *pPager = pPg->pPager;
+ assert( !pPg->pageHash || pPager->errCode || MEMDB
+ || (pPg->flags&PGHDR_DIRTY) || pPg->pageHash==pager_pagehash(pPg) );
}
-/* Forward declaration */
-static void makeClean(PgHdr*);
+#else
+#define pager_datahash(X,Y) 0
+#define pager_pagehash(X) 0
+#define CHECK_PAGE(x)
+#endif /* SQLITE_CHECK_PAGES */
/*
-** Read a single page from the journal file opened on file descriptor
-** jfd. Playback this one page.
+** When this is called the journal file for pager pPager must be open.
+** The master journal file name is read from the end of the file and
+** written into memory supplied by the caller.
+**
+** zMaster must point to a buffer of at least nMaster bytes allocated by
+** the caller. This should be sqlite3_vfs.mxPathname+1 (to ensure there is
+** enough space to write the master journal name). If the master journal
+** name in the journal is longer than nMaster bytes (including a
+** nul-terminator), then this is handled as if no master journal name
+** were present in the journal.
**
-** If useCksum==0 it means this journal does not use checksums. Checksums
-** are not used in statement journals because statement journals do not
-** need to survive power failures.
+** If no master journal file name is present zMaster[0] is set to 0 and
+** SQLITE_OK returned.
*/
-static int pager_playback_one_page(
- Pager *pPager,
- sqlite3_file *jfd,
- i64 offset,
- int useCksum
-){
+static int readMasterJournal(sqlite3_file *pJrnl, char *zMaster, int nMaster){
int rc;
- PgHdr *pPg; /* An existing page in the cache */
- Pgno pgno; /* The page number of a page in journal */
- u32 cksum; /* Checksum used for sanity checking */
- u8 *aData = (u8 *)pPager->pTmpSpace; /* Temp storage for a page */
+ u32 len;
+ i64 szJ;
+ u32 cksum;
+ u32 u; /* Unsigned loop counter */
+ unsigned char aMagic[8]; /* A buffer to hold the magic header */
- /* useCksum should be true for the main journal and false for
- ** statement journals. Verify that this is always the case
- */
- assert( jfd == (useCksum ? pPager->jfd : pPager->stfd) );
- assert( aData );
+ zMaster[0] = '\0';
- rc = read32bits(jfd, offset, &pgno);
- if( rc!=SQLITE_OK ) return rc;
- rc = sqlite3OsRead(jfd, aData, pPager->pageSize, offset+4);
+ rc = sqlite3OsFileSize(pJrnl, &szJ);
+ if( rc!=SQLITE_OK || szJ<16 ) return rc;
+
+ rc = read32bits(pJrnl, szJ-16, &len);
if( rc!=SQLITE_OK ) return rc;
- pPager->journalOff += pPager->pageSize + 4;
- /* Sanity checking on the page. This is more important that I originally
- ** thought. If a power failure occurs while the journal is being written,
- ** it could cause invalid data to be written into the journal. We need to
- ** detect this invalid data (with high probability) and ignore it.
- */
- if( pgno==0 || pgno==PAGER_MJ_PGNO(pPager) ){
- return SQLITE_DONE;
- }
- if( pgno>(unsigned)pPager->dbSize ){
+ if( len>=nMaster ){
return SQLITE_OK;
}
- if( useCksum ){
- rc = read32bits(jfd, offset+pPager->pageSize+4, &cksum);
- if( rc ) return rc;
- pPager->journalOff += 4;
- if( pager_cksum(pPager, aData)!=cksum ){
- return SQLITE_DONE;
- }
+
+ rc = read32bits(pJrnl, szJ-12, &cksum);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3OsRead(pJrnl, aMagic, 8, szJ-8);
+ if( rc!=SQLITE_OK || memcmp(aMagic, aJournalMagic, 8) ) return rc;
+
+ rc = sqlite3OsRead(pJrnl, zMaster, len, szJ-16-len);
+ if( rc!=SQLITE_OK ){
+ return rc;
}
+ zMaster[len] = '\0';
- assert( pPager->state==PAGER_RESERVED || pPager->state>=PAGER_EXCLUSIVE );
+ /* See if the checksum matches the master journal name */
+ for(u=0; u<len; u++){
+ cksum -= zMaster[u];
+ }
+ if( cksum ){
+ /* If the checksum doesn't add up, then one or more of the disk sectors
+ ** containing the master journal filename is corrupted. This means
+ ** definitely roll back, so just return SQLITE_OK and report a (nul)
+ ** master-journal filename.
+ */
+ zMaster[0] = '\0';
+ }
+
+ return SQLITE_OK;
+}
- /* If the pager is in RESERVED state, then there must be a copy of this
- ** page in the pager cache. In this case just update the pager cache,
- ** not the database file. The page is left marked dirty in this case.
- **
- ** An exception to the above rule: If the database is in no-sync mode
- ** and a page is moved during an incremental vacuum then the page may
- ** not be in the pager cache. Later: if a malloc() or IO error occurs
- ** during a Movepage() call, then the page may not be in the cache
- ** either. So the condition described in the above paragraph is not
- ** assert()able.
- **
- ** If in EXCLUSIVE state, then we update the pager cache if it exists
- ** and the main file. The page is then marked not dirty.
- **
- ** Ticket #1171: The statement journal might contain page content that is
- ** different from the page content at the start of the transaction.
- ** This occurs when a page is changed prior to the start of a statement
- ** then changed again within the statement. When rolling back such a
- ** statement we must not write to the original database unless we know
- ** for certain that original page contents are synced into the main rollback
- ** journal. Otherwise, a power loss might leave modified data in the
- ** database file without an entry in the rollback journal that can
- ** restore the database to its original form. Two conditions must be
- ** met before writing to the database files. (1) the database must be
- ** locked. (2) we know that the original page content is fully synced
- ** in the main journal either because the page is not in cache or else
- ** the page is marked as needSync==0.
- **
- ** 2008-04-14: When attempting to vacuum a corrupt database file, it
- ** is possible to fail a statement on a database that does not yet exist.
- ** Do not attempt to write if database file has never been opened.
- */
- pPg = pager_lookup(pPager, pgno);
- PAGERTRACE4("PLAYBACK %d page %d hash(%08x)\n",
- PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, aData));
- if( pPager->state>=PAGER_EXCLUSIVE && (pPg==0 || pPg->needSync==0)
- && pPager->fd->pMethods ){
- i64 offset = (pgno-1)*(i64)pPager->pageSize;
- rc = sqlite3OsWrite(pPager->fd, aData, pPager->pageSize, offset);
- if( pPg ){
- makeClean(pPg);
- }
+/*
+** Seek the journal file descriptor to the next sector boundary where a
+** journal header may be read or written. Pager.journalOff is updated with
+** the new seek offset.
+**
+** i.e for a sector size of 512:
+**
+** Input Offset Output Offset
+** ---------------------------------------
+** 0 0
+** 512 512
+** 100 512
+** 2000 2048
+**
+*/
+static void seekJournalHdr(Pager *pPager){
+ i64 offset = 0;
+ i64 c = pPager->journalOff;
+ if( c ){
+ offset = ((c-1)/JOURNAL_HDR_SZ(pPager) + 1) * JOURNAL_HDR_SZ(pPager);
}
- if( pPg ){
- /* No page should ever be explicitly rolled back that is in use, except
- ** for page 1 which is held in use in order to keep the lock on the
- ** database active. However such a page may be rolled back as a result
- ** of an internal error resulting in an automatic call to
- ** sqlite3PagerRollback().
- */
- void *pData;
- /* assert( pPg->nRef==0 || pPg->pgno==1 ); */
- pData = PGHDR_TO_DATA(pPg);
- memcpy(pData, aData, pPager->pageSize);
- if( pPager->xReiniter ){
- pPager->xReiniter(pPg, pPager->pageSize);
+ assert( offset%JOURNAL_HDR_SZ(pPager)==0 );
+ assert( offset>=c );
+ assert( (offset-c)<JOURNAL_HDR_SZ(pPager) );
+ pPager->journalOff = offset;
+}
+
+/*
+** Write zeros over the header of the journal file. This has the
+** effect of invalidating the journal file and committing the
+** transaction.
+*/
+static int zeroJournalHdr(Pager *pPager, int doTruncate){
+ int rc = SQLITE_OK;
+ static const char zeroHdr[28];
+
+ if( pPager->journalOff ){
+ i64 iLimit = pPager->journalSizeLimit;
+
+ IOTRACE(("JZEROHDR %p\n", pPager))
+ if( doTruncate || iLimit==0 ){
+ rc = sqlite3OsTruncate(pPager->jfd, 0);
+ }else{
+ rc = sqlite3OsWrite(pPager->jfd, zeroHdr, sizeof(zeroHdr), 0);
}
-#ifdef SQLITE_CHECK_PAGES
- pPg->pageHash = pager_pagehash(pPg);
-#endif
- /* If this was page 1, then restore the value of Pager.dbFileVers.
- ** Do this before any decoding. */
- if( pgno==1 ){
- memcpy(&pPager->dbFileVers, &((u8*)pData)[24],sizeof(pPager->dbFileVers));
+ if( rc==SQLITE_OK && !pPager->noSync ){
+ rc = sqlite3OsSync(pPager->jfd, SQLITE_SYNC_DATAONLY|pPager->sync_flags);
}
- /* Decode the page just read from disk */
- CODEC1(pPager, pData, pPg->pgno, 3);
+ /* At this point the transaction is committed but the write lock
+ ** is still held on the file. If there is a size limit configured for
+ ** the persistent journal and the journal file currently consumes more
+ ** space than that limit allows for, truncate it now. There is no need
+ ** to sync the file following this operation.
+ */
+ if( rc==SQLITE_OK && iLimit>0 ){
+ i64 sz;
+ rc = sqlite3OsFileSize(pPager->jfd, &sz);
+ if( rc==SQLITE_OK && sz>iLimit ){
+ rc = sqlite3OsTruncate(pPager->jfd, iLimit);
+ }
+ }
}
return rc;
}
/*
-** Parameter zMaster is the name of a master journal file. A single journal
-** file that referred to the master journal file has just been rolled back.
-** This routine checks if it is possible to delete the master journal file,
-** and does so if it is.
-**
-** Argument zMaster may point to Pager.pTmpSpace. So that buffer is not
-** available for use within this function.
-**
+** The journal file must be open when this routine is called. A journal
+** header (JOURNAL_HDR_SZ bytes) is written into the journal file at the
+** current location.
**
-** The master journal file contains the names of all child journals.
-** To tell if a master journal can be deleted, check to each of the
-** children. If all children are either missing or do not refer to
-** a different master journal, then this master journal can be deleted.
+** The format for the journal header is as follows:
+** - 8 bytes: Magic identifying journal format.
+** - 4 bytes: Number of records in journal, or -1 no-sync mode is on.
+** - 4 bytes: Random number used for page hash.
+** - 4 bytes: Initial database page count.
+** - 4 bytes: Sector size used by the process that wrote this journal.
+** - 4 bytes: Database page size.
+**
+** Followed by (JOURNAL_HDR_SZ - 28) bytes of unused space.
*/
-static int pager_delmaster(Pager *pPager, const char *zMaster){
- sqlite3_vfs *pVfs = pPager->pVfs;
- int rc;
- int master_open = 0;
- sqlite3_file *pMaster;
- sqlite3_file *pJournal;
- char *zMasterJournal = 0; /* Contents of master journal file */
- i64 nMasterJournal; /* Size of master journal file */
+static int writeJournalHdr(Pager *pPager){
+ int rc = SQLITE_OK;
+ char *zHeader = pPager->pTmpSpace;
+ int nHeader = pPager->pageSize;
+ int nWrite;
- /* Open the master journal file exclusively in case some other process
- ** is running this routine also. Not that it makes too much difference.
- */
- pMaster = (sqlite3_file *)sqlite3Malloc(pVfs->szOsFile * 2);
- pJournal = (sqlite3_file *)(((u8 *)pMaster) + pVfs->szOsFile);
- if( !pMaster ){
- rc = SQLITE_NOMEM;
- }else{
- int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MASTER_JOURNAL);
- rc = sqlite3OsOpen(pVfs, zMaster, pMaster, flags, 0);
+ if( nHeader>JOURNAL_HDR_SZ(pPager) ){
+ nHeader = JOURNAL_HDR_SZ(pPager);
}
- if( rc!=SQLITE_OK ) goto delmaster_out;
- master_open = 1;
-
- rc = sqlite3OsFileSize(pMaster, &nMasterJournal);
- if( rc!=SQLITE_OK ) goto delmaster_out;
- if( nMasterJournal>0 ){
- char *zJournal;
- char *zMasterPtr = 0;
- int nMasterPtr = pPager->pVfs->mxPathname+1;
+ if( pPager->stmtHdrOff==0 ){
+ pPager->stmtHdrOff = pPager->journalOff;
+ }
- /* Load the entire master journal file into space obtained from
- ** sqlite3_malloc() and pointed to by zMasterJournal.
- */
- zMasterJournal = (char *)sqlite3Malloc(nMasterJournal + nMasterPtr);
- if( !zMasterJournal ){
- rc = SQLITE_NOMEM;
- goto delmaster_out;
- }
- zMasterPtr = &zMasterJournal[nMasterJournal];
- rc = sqlite3OsRead(pMaster, zMasterJournal, nMasterJournal, 0);
- if( rc!=SQLITE_OK ) goto delmaster_out;
+ seekJournalHdr(pPager);
+ pPager->journalHdr = pPager->journalOff;
- zJournal = zMasterJournal;
- while( (zJournal-zMasterJournal)<nMasterJournal ){
- int exists;
- rc = sqlite3OsAccess(pVfs, zJournal, SQLITE_ACCESS_EXISTS, &exists);
- if( rc!=SQLITE_OK ){
- goto delmaster_out;
- }
- if( exists ){
- /* One of the journals pointed to by the master journal exists.
- ** Open it and check if it points at the master journal. If
- ** so, return without deleting the master journal file.
- */
- int c;
- int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL);
- rc = sqlite3OsOpen(pVfs, zJournal, pJournal, flags, 0);
- if( rc!=SQLITE_OK ){
- goto delmaster_out;
- }
+ memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic));
- rc = readMasterJournal(pJournal, zMasterPtr, nMasterPtr);
- sqlite3OsClose(pJournal);
- if( rc!=SQLITE_OK ){
- goto delmaster_out;
- }
+ /*
+ ** Write the nRec Field - the number of page records that follow this
+ ** journal header. Normally, zero is written to this value at this time.
+ ** After the records are added to the journal (and the journal synced,
+ ** if in full-sync mode), the zero is overwritten with the true number
+ ** of records (see syncJournal()).
+ **
+ ** A faster alternative is to write 0xFFFFFFFF to the nRec field. When
+ ** reading the journal this value tells SQLite to assume that the
+ ** rest of the journal file contains valid page records. This assumption
+ ** is dangerous, as if a failure occured whilst writing to the journal
+ ** file it may contain some garbage data. There are two scenarios
+ ** where this risk can be ignored:
+ **
+ ** * When the pager is in no-sync mode. Corruption can follow a
+ ** power failure in this case anyway.
+ **
+ ** * When the SQLITE_IOCAP_SAFE_APPEND flag is set. This guarantees
+ ** that garbage data is never appended to the journal file.
+ */
+ assert(pPager->fd->pMethods||pPager->noSync);
+ if( (pPager->noSync)
+ || (sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND)
+ ){
+ put32bits(&zHeader[sizeof(aJournalMagic)], 0xffffffff);
+ }else{
+ put32bits(&zHeader[sizeof(aJournalMagic)], 0);
+ }
- c = zMasterPtr[0]!=0 && strcmp(zMasterPtr, zMaster)==0;
- if( c ){
- /* We have a match. Do not delete the master journal file. */
- goto delmaster_out;
- }
- }
- zJournal += (strlen(zJournal)+1);
- }
+ /* The random check-hash initialiser */
+ sqlite3_randomness(sizeof(pPager->cksumInit), &pPager->cksumInit);
+ put32bits(&zHeader[sizeof(aJournalMagic)+4], pPager->cksumInit);
+ /* The initial database size */
+ put32bits(&zHeader[sizeof(aJournalMagic)+8], pPager->dbSize);
+ /* The assumed sector size for this process */
+ put32bits(&zHeader[sizeof(aJournalMagic)+12], pPager->sectorSize);
+ if( pPager->journalHdr==0 ){
+ /* The page size */
+ put32bits(&zHeader[sizeof(aJournalMagic)+16], pPager->pageSize);
}
-
- rc = sqlite3OsDelete(pVfs, zMaster, 0);
-delmaster_out:
- if( zMasterJournal ){
- sqlite3_free(zMasterJournal);
- }
- if( master_open ){
- sqlite3OsClose(pMaster);
+ for(nWrite=0; rc==SQLITE_OK&&nWrite<JOURNAL_HDR_SZ(pPager); nWrite+=nHeader){
+ IOTRACE(("JHDR %p %lld %d\n", pPager, pPager->journalHdr, nHeader))
+ rc = sqlite3OsWrite(pPager->jfd, zHeader, nHeader, pPager->journalOff);
+ pPager->journalOff += nHeader;
}
- sqlite3_free(pMaster);
+
return rc;
}
-
-static void pager_truncate_cache(Pager *pPager);
-
/*
-** Truncate the main file of the given pager to the number of pages
-** indicated. Also truncate the cached representation of the file.
+** The journal file must be open when this is called. A journal header file
+** (JOURNAL_HDR_SZ bytes) is read from the current location in the journal
+** file. See comments above function writeJournalHdr() for a description of
+** the journal header format.
**
-** Might might be the case that the file on disk is smaller than nPage.
-** This can happen, for example, if we are in the middle of a transaction
-** which has extended the file size and the new pages are still all held
-** in cache, then an INSERT or UPDATE does a statement rollback. Some
-** operating system implementations can get confused if you try to
-** truncate a file to some size that is larger than it currently is,
-** so detect this case and write a single zero byte to the end of the new
-** file instead.
+** If the header is read successfully, *nRec is set to the number of
+** page records following this header and *dbSize is set to the size of the
+** database before the transaction began, in pages. Also, pPager->cksumInit
+** is set to the value read from the journal header. SQLITE_OK is returned
+** in this case.
+**
+** If the journal header file appears to be corrupted, SQLITE_DONE is
+** returned and *nRec and *dbSize are not set. If JOURNAL_HDR_SZ bytes
+** cannot be read from the journal file an error code is returned.
*/
-static int pager_truncate(Pager *pPager, int nPage){
- int rc = SQLITE_OK;
- if( pPager->state>=PAGER_EXCLUSIVE && pPager->fd->pMethods ){
- i64 currentSize, newSize;
- rc = sqlite3OsFileSize(pPager->fd, ¤tSize);
- newSize = pPager->pageSize*(i64)nPage;
- if( rc==SQLITE_OK && currentSize!=newSize ){
- if( currentSize>newSize ){
- rc = sqlite3OsTruncate(pPager->fd, newSize);
- }else{
- rc = sqlite3OsWrite(pPager->fd, "", 1, newSize-1);
- }
- }
- }
- if( rc==SQLITE_OK ){
- pPager->dbSize = nPage;
- pager_truncate_cache(pPager);
+static int readJournalHdr(
+ Pager *pPager,
+ i64 journalSize,
+ u32 *pNRec,
+ u32 *pDbSize
+){
+ int rc;
+ unsigned char aMagic[8]; /* A buffer to hold the magic header */
+ i64 jrnlOff;
+ int iPageSize;
+
+ seekJournalHdr(pPager);
+ if( pPager->journalOff+JOURNAL_HDR_SZ(pPager) > journalSize ){
+ return SQLITE_DONE;
}
- return rc;
-}
+ jrnlOff = pPager->journalOff;
-/*
-** Set the sectorSize for the given pager.
-**
-** The sector size is at least as big as the sector size reported
-** by sqlite3OsSectorSize(). The minimum sector size is 512.
-*/
-static void setSectorSize(Pager *pPager){
- assert(pPager->fd->pMethods||pPager->tempFile);
- if( !pPager->tempFile ){
- /* Sector size doesn't matter for temporary files. Also, the file
- ** may not have been opened yet, in whcih case the OsSectorSize()
- ** call will segfault.
- */
- pPager->sectorSize = sqlite3OsSectorSize(pPager->fd);
+ rc = sqlite3OsRead(pPager->jfd, aMagic, sizeof(aMagic), jrnlOff);
+ if( rc ) return rc;
+ jrnlOff += sizeof(aMagic);
+
+ if( memcmp(aMagic, aJournalMagic, sizeof(aMagic))!=0 ){
+ return SQLITE_DONE;
}
- if( pPager->sectorSize<512 ){
- pPager->sectorSize = 512;
+
+ rc = read32bits(pPager->jfd, jrnlOff, pNRec);
+ if( rc ) return rc;
+
+ rc = read32bits(pPager->jfd, jrnlOff+4, &pPager->cksumInit);
+ if( rc ) return rc;
+
+ rc = read32bits(pPager->jfd, jrnlOff+8, pDbSize);
+ if( rc ) return rc;
+
+ rc = read32bits(pPager->jfd, jrnlOff+16, (u32 *)&iPageSize);
+ if( rc==SQLITE_OK
+ && iPageSize>=512
+ && iPageSize<=SQLITE_MAX_PAGE_SIZE
+ && ((iPageSize-1)&iPageSize)==0
+ ){
+ u16 pagesize = iPageSize;
+ rc = sqlite3PagerSetPagesize(pPager, &pagesize);
}
+ if( rc ) return rc;
+
+ /* Update the assumed sector-size to match the value used by
+ ** the process that created this journal. If this journal was
+ ** created by a process other than this one, then this routine
+ ** is being called from within pager_playback(). The local value
+ ** of Pager.sectorSize is restored at the end of that routine.
+ */
+ rc = read32bits(pPager->jfd, jrnlOff+12, (u32 *)&pPager->sectorSize);
+ if( rc ) return rc;
+
+ pPager->journalOff += JOURNAL_HDR_SZ(pPager);
+ return SQLITE_OK;
}
+
/*
-** Playback the journal and thus restore the database file to
-** the state it was in before we started making changes.
-**
-** The journal file format is as follows:
-**
-** (1) 8 byte prefix. A copy of aJournalMagic[].
-** (2) 4 byte big-endian integer which is the number of valid page records
-** in the journal. If this value is 0xffffffff, then compute the
-** number of page records from the journal size.
-** (3) 4 byte big-endian integer which is the initial value for the
-** sanity checksum.
-** (4) 4 byte integer which is the number of pages to truncate the
-** database to during a rollback.
-** (5) 4 byte big-endian integer which is the sector size. The header
-** is this many bytes in size.
-** (6) 4 byte big-endian integer which is the page case.
-** (7) 4 byte integer which is the number of bytes in the master journal
-** name. The value may be zero (indicate that there is no master
-** journal.)
-** (8) N bytes of the master journal name. The name will be nul-terminated
-** and might be shorter than the value read from (5). If the first byte
-** of the name is \000 then there is no master journal. The master
-** journal name is stored in UTF-8.
-** (9) Zero or more pages instances, each as follows:
-** + 4 byte page number.
-** + pPager->pageSize bytes of data.
-** + 4 byte checksum
-**
-** When we speak of the journal header, we mean the first 8 items above.
-** Each entry in the journal is an instance of the 9th item.
-**
-** Call the value from the second bullet "nRec". nRec is the number of
-** valid page entries in the journal. In most cases, you can compute the
-** value of nRec from the size of the journal file. But if a power
-** failure occurred while the journal was being written, it could be the
-** case that the size of the journal file had already been increased but
-** the extra entries had not yet made it safely to disk. In such a case,
-** the value of nRec computed from the file size would be too large. For
-** that reason, we always use the nRec value in the header.
+** Write the supplied master journal name into the journal file for pager
+** pPager at the current location. The master journal name must be the last
+** thing written to a journal file. If the pager is in full-sync mode, the
+** journal file descriptor is advanced to the next sector boundary before
+** anything is written. The format is:
**
-** If the nRec value is 0xffffffff it means that nRec should be computed
-** from the file size. This value is used when the user selects the
-** no-sync option for the journal. A power failure could lead to corruption
-** in this case. But for things like temporary table (which will be
-** deleted when the power is restored) we don't care.
+** + 4 bytes: PAGER_MJ_PGNO.
+** + N bytes: length of master journal name.
+** + 4 bytes: N
+** + 4 bytes: Master journal name checksum.
+** + 8 bytes: aJournalMagic[].
**
-** If the file opened as the journal file is not a well-formed
-** journal file then all pages up to the first corrupted page are rolled
-** back (or no pages if the journal header is corrupted). The journal file
-** is then deleted and SQLITE_OK returned, just as if no corruption had
-** been encountered.
+** The master journal page checksum is the sum of the bytes in the master
+** journal name.
**
-** If an I/O or malloc() error occurs, the journal-file is not deleted
-** and an error code is returned.
+** If zMaster is a NULL pointer (occurs for a single database transaction),
+** this call is a no-op.
*/
-static int pager_playback(Pager *pPager, int isHot){
- sqlite3_vfs *pVfs = pPager->pVfs;
- i64 szJ; /* Size of the journal file in bytes */
- u32 nRec; /* Number of Records in the journal */
- u32 u; /* Unsigned loop counter */
- Pgno mxPg = 0; /* Size of the original file in pages */
- int rc; /* Result code of a subroutine */
- int res = 1; /* Value returned by sqlite3OsAccess() */
- char *zMaster = 0; /* Name of master journal file if any */
+static int writeMasterJournal(Pager *pPager, const char *zMaster){
+ int rc;
+ int len;
+ int i;
+ i64 jrnlOff;
+ i64 jrnlSize;
+ u32 cksum = 0;
+ char zBuf[sizeof(aJournalMagic)+2*4];
- /* Figure out how many records are in the journal. Abort early if
- ** the journal is empty.
- */
- assert( pPager->journalOpen );
- rc = sqlite3OsFileSize(pPager->jfd, &szJ);
- if( rc!=SQLITE_OK || szJ==0 ){
- goto end_playback;
+ if( !zMaster || pPager->setMaster) return SQLITE_OK;
+ pPager->setMaster = 1;
+
+ len = strlen(zMaster);
+ for(i=0; i<len; i++){
+ cksum += zMaster[i];
}
- /* Read the master journal name from the journal, if it is present.
- ** If a master journal file name is specified, but the file is not
- ** present on disk, then the journal is not hot and does not need to be
- ** played back.
+ /* If in full-sync mode, advance to the next disk sector before writing
+ ** the master journal name. This is in case the previous page written to
+ ** the journal has already been synced.
*/
- zMaster = pPager->pTmpSpace;
- rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1);
- if( rc==SQLITE_OK && zMaster[0] ){
- rc = sqlite3OsAccess(pVfs, zMaster, SQLITE_ACCESS_EXISTS, &res);
- }
- zMaster = 0;
- if( rc!=SQLITE_OK || !res ){
- goto end_playback;
+ if( pPager->fullSync ){
+ seekJournalHdr(pPager);
}
- pPager->journalOff = 0;
-
- /* This loop terminates either when the readJournalHdr() call returns
- ** SQLITE_DONE or an IO error occurs. */
- while( 1 ){
+ jrnlOff = pPager->journalOff;
+ pPager->journalOff += (len+20);
- /* Read the next journal header from the journal file. If there are
- ** not enough bytes left in the journal file for a complete header, or
- ** it is corrupted, then a process must of failed while writing it.
- ** This indicates nothing more needs to be rolled back.
- */
- rc = readJournalHdr(pPager, szJ, &nRec, &mxPg);
- if( rc!=SQLITE_OK ){
- if( rc==SQLITE_DONE ){
- rc = SQLITE_OK;
- }
- goto end_playback;
- }
+ rc = write32bits(pPager->jfd, jrnlOff, PAGER_MJ_PGNO(pPager));
+ if( rc!=SQLITE_OK ) return rc;
+ jrnlOff += 4;
- /* If nRec is 0xffffffff, then this journal was created by a process
- ** working in no-sync mode. This means that the rest of the journal
- ** file consists of pages, there are no more journal headers. Compute
- ** the value of nRec based on this assumption.
- */
- if( nRec==0xffffffff ){
- assert( pPager->journalOff==JOURNAL_HDR_SZ(pPager) );
- nRec = (szJ - JOURNAL_HDR_SZ(pPager))/JOURNAL_PG_SZ(pPager);
- }
+ rc = sqlite3OsWrite(pPager->jfd, zMaster, len, jrnlOff);
+ if( rc!=SQLITE_OK ) return rc;
+ jrnlOff += len;
- /* If nRec is 0 and this rollback is of a transaction created by this
- ** process and if this is the final header in the journal, then it means
- ** that this part of the journal was being filled but has not yet been
- ** synced to disk. Compute the number of pages based on the remaining
- ** size of the file.
- **
- ** The third term of the test was added to fix ticket #2565.
- */
- if( nRec==0 && !isHot &&
- pPager->journalHdr+JOURNAL_HDR_SZ(pPager)==pPager->journalOff ){
- nRec = (szJ - pPager->journalOff) / JOURNAL_PG_SZ(pPager);
- }
+ put32bits(zBuf, len);
+ put32bits(&zBuf[4], cksum);
+ memcpy(&zBuf[8], aJournalMagic, sizeof(aJournalMagic));
+ rc = sqlite3OsWrite(pPager->jfd, zBuf, 8+sizeof(aJournalMagic), jrnlOff);
+ jrnlOff += 8+sizeof(aJournalMagic);
+ pPager->needSync = !pPager->noSync;
- /* If this is the first header read from the journal, truncate the
- ** database file back to its original size.
- */
- if( pPager->journalOff==JOURNAL_HDR_SZ(pPager) ){
- rc = pager_truncate(pPager, mxPg);
- if( rc!=SQLITE_OK ){
- goto end_playback;
+ /* If the pager is in peristent-journal mode, then the physical
+ ** journal-file may extend past the end of the master-journal name
+ ** and 8 bytes of magic data just written to the file. This is
+ ** dangerous because the code to rollback a hot-journal file
+ ** will not be able to find the master-journal name to determine
+ ** whether or not the journal is hot.
+ **
+ ** Easiest thing to do in this scenario is to truncate the journal
+ ** file to the required size.
+ */
+ if( (rc==SQLITE_OK)
+ && (rc = sqlite3OsFileSize(pPager->jfd, &jrnlSize))==SQLITE_OK
+ && jrnlSize>jrnlOff
+ ){
+ rc = sqlite3OsTruncate(pPager->jfd, jrnlOff);
+ }
+ return rc;
+}
+
+/*
+** Find a page in the hash table given its page number. Return
+** a pointer to the page or NULL if not found.
+*/
+static PgHdr *pager_lookup(Pager *pPager, Pgno pgno){
+ PgHdr *p;
+ sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &p);
+ return p;
+}
+
+/*
+** Clear the in-memory cache. This routine
+** sets the state of the pager back to what it was when it was first
+** opened. Any outstanding pages are invalidated and subsequent attempts
+** to access those pages will likely result in a coredump.
+*/
+static void pager_reset(Pager *pPager){
+ if( pPager->errCode ) return;
+ sqlite3PcacheClear(pPager->pPCache);
+}
+
+/*
+** Unlock the database file.
+**
+** If the pager is currently in error state, discard the contents of
+** the cache and reset the Pager structure internal state. If there is
+** an open journal-file, then the next time a shared-lock is obtained
+** on the pager file (by this or any other process), it will be
+** treated as a hot-journal and rolled back.
+*/
+static void pager_unlock(Pager *pPager){
+ if( !pPager->exclusiveMode ){
+ if( !MEMDB ){
+ int rc = osUnlock(pPager->fd, NO_LOCK);
+ if( rc ) pPager->errCode = rc;
+ pPager->dbSize = -1;
+ IOTRACE(("UNLOCK %p\n", pPager))
+
+ /* Always close the journal file when dropping the database lock.
+ ** Otherwise, another connection with journal_mode=delete might
+ ** delete the file out from under us.
+ */
+ if( pPager->journalOpen ){
+ sqlite3OsClose(pPager->jfd);
+ pPager->journalOpen = 0;
+ sqlite3BitvecDestroy(pPager->pInJournal);
+ pPager->pInJournal = 0;
+ sqlite3BitvecDestroy(pPager->pAlwaysRollback);
+ pPager->pAlwaysRollback = 0;
}
- }
- /* Copy original pages out of the journal and back into the database file.
- */
- for(u=0; u<nRec; u++){
- rc = pager_playback_one_page(pPager, pPager->jfd, pPager->journalOff, 1);
- if( rc!=SQLITE_OK ){
- if( rc==SQLITE_DONE ){
- rc = SQLITE_OK;
- pPager->journalOff = szJ;
- break;
- }else{
- goto end_playback;
+ /* If Pager.errCode is set, the contents of the pager cache cannot be
+ ** trusted. Now that the pager file is unlocked, the contents of the
+ ** cache can be discarded and the error code safely cleared.
+ */
+ if( pPager->errCode ){
+ if( rc==SQLITE_OK ) pPager->errCode = SQLITE_OK;
+ pager_reset(pPager);
+ if( pPager->stmtOpen ){
+ sqlite3OsClose(pPager->stfd);
+ sqlite3BitvecDestroy(pPager->pInStmt);
+ pPager->pInStmt = 0;
}
+ pPager->stmtOpen = 0;
+ pPager->stmtInUse = 0;
+ pPager->journalOff = 0;
+ pPager->journalStarted = 0;
+ pPager->stmtAutoopen = 0;
+ pPager->origDbSize = 0;
}
}
- }
- /*NOTREACHED*/
- assert( 0 );
-end_playback:
- if( rc==SQLITE_OK ){
- zMaster = pPager->pTmpSpace;
- rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1);
- }
- if( rc==SQLITE_OK ){
- rc = pager_end_transaction(pPager, zMaster[0]!='\0');
- }
- if( rc==SQLITE_OK && zMaster[0] ){
- /* If there was a master journal and this routine will return success,
- ** see if it is possible to delete the master journal.
- */
- rc = pager_delmaster(pPager, zMaster);
+ if( !MEMDB || pPager->errCode==SQLITE_OK ){
+ pPager->state = PAGER_UNLOCK;
+ pPager->changeCountDone = 0;
+ }
}
+}
- /* The Pager.sectorSize variable may have been updated while rolling
- ** back a journal created by a process with a different sector size
- ** value. Reset it to the correct value for this process.
- */
- setSectorSize(pPager);
- return rc;
+/*
+** Execute a rollback if a transaction is active and unlock the
+** database file. If the pager has already entered the error state,
+** do not attempt the rollback.
+*/
+static void pagerUnlockAndRollback(Pager *p){
+ if( p->errCode==SQLITE_OK && p->state>=PAGER_RESERVED ){
+ sqlite3BeginBenignMalloc();
+ sqlite3PagerRollback(p);
+ sqlite3EndBenignMalloc();
+ }
+ pager_unlock(p);
}
/*
-** Playback the statement journal.
+** This routine ends a transaction. A transaction is ended by either
+** a COMMIT or a ROLLBACK.
**
-** This is similar to playing back the transaction journal but with
-** a few extra twists.
+** When this routine is called, the pager has the journal file open and
+** a RESERVED or EXCLUSIVE lock on the database. This routine will release
+** the database lock and acquires a SHARED lock in its place if that is
+** the appropriate thing to do. Release locks usually is appropriate,
+** unless we are in exclusive access mode or unless this is a
+** COMMIT AND BEGIN or ROLLBACK AND BEGIN operation.
**
-** (1) The number of pages in the database file at the start of
-** the statement is stored in pPager->stmtSize, not in the
-** journal file itself.
+** The journal file is either deleted or truncated.
**
-** (2) In addition to playing back the statement journal, also
-** playback all pages of the transaction journal beginning
-** at offset pPager->stmtJSize.
+** TODO: Consider keeping the journal file open for temporary databases.
+** This might give a performance improvement on windows where opening
+** a file is an expensive operation.
*/
-static int pager_stmt_playback(Pager *pPager){
- i64 szJ; /* Size of the full journal */
- i64 hdrOff;
- int nRec; /* Number of Records */
- int i; /* Loop counter */
- int rc;
-
- szJ = pPager->journalOff;
-
- /* Set hdrOff to be the offset just after the end of the last journal
- ** page written before the first journal-header for this statement
- ** transaction was written, or the end of the file if no journal
- ** header was written.
- */
- hdrOff = pPager->stmtHdrOff;
- assert( pPager->fullSync || !hdrOff );
- if( !hdrOff ){
- hdrOff = szJ;
- }
-
- /* Truncate the database back to its original size.
- */
- rc = pager_truncate(pPager, pPager->stmtSize);
- assert( pPager->state>=PAGER_SHARED );
-
- /* Figure out how many records are in the statement journal.
- */
- assert( pPager->stmtInUse && pPager->journalOpen );
- nRec = pPager->stmtNRec;
-
- /* Copy original pages out of the statement journal and back into the
- ** database file. Note that the statement journal omits checksums from
- ** each record since power-failure recovery is not important to statement
- ** journals.
- */
- for(i=0; i<nRec; i++){
- i64 offset = i*(4+pPager->pageSize);
- rc = pager_playback_one_page(pPager, pPager->stfd, offset, 0);
- assert( rc!=SQLITE_DONE );
- if( rc!=SQLITE_OK ) goto end_stmt_playback;
+static int pager_end_transaction(Pager *pPager, int hasMaster){
+ int rc = SQLITE_OK;
+ int rc2 = SQLITE_OK;
+ assert( !MEMDB );
+ if( pPager->state<PAGER_RESERVED ){
+ return SQLITE_OK;
}
-
- /* Now roll some pages back from the transaction journal. Pager.stmtJSize
- ** was the size of the journal file when this statement was started, so
- ** everything after that needs to be rolled back, either into the
- ** database, the memory cache, or both.
- **
- ** If it is not zero, then Pager.stmtHdrOff is the offset to the start
- ** of the first journal header written during this statement transaction.
- */
- pPager->journalOff = pPager->stmtJSize;
- pPager->cksumInit = pPager->stmtCksum;
- while( pPager->journalOff < hdrOff ){
- rc = pager_playback_one_page(pPager, pPager->jfd, pPager->journalOff, 1);
- assert( rc!=SQLITE_DONE );
- if( rc!=SQLITE_OK ) goto end_stmt_playback;
+ sqlite3PagerStmtCommit(pPager);
+ if( pPager->stmtOpen && !pPager->exclusiveMode ){
+ sqlite3OsClose(pPager->stfd);
+ pPager->stmtOpen = 0;
}
-
- while( pPager->journalOff < szJ ){
- u32 nJRec; /* Number of Journal Records */
- u32 dummy;
- rc = readJournalHdr(pPager, szJ, &nJRec, &dummy);
- if( rc!=SQLITE_OK ){
- assert( rc!=SQLITE_DONE );
- goto end_stmt_playback;
- }
- if( nJRec==0 ){
- nJRec = (szJ - pPager->journalOff) / (pPager->pageSize+8);
- }
- for(i=nJRec-1; i>=0 && pPager->journalOff < szJ; i--){
- rc = pager_playback_one_page(pPager, pPager->jfd, pPager->journalOff, 1);
- assert( rc!=SQLITE_DONE );
- if( rc!=SQLITE_OK ) goto end_stmt_playback;
+ if( pPager->journalOpen ){
+ if( pPager->exclusiveMode
+ || pPager->journalMode==PAGER_JOURNALMODE_PERSIST
+ ){
+ rc = zeroJournalHdr(pPager, hasMaster);
+ pager_error(pPager, rc);
+ pPager->journalOff = 0;
+ pPager->journalStarted = 0;
+ }else{
+ sqlite3OsClose(pPager->jfd);
+ pPager->journalOpen = 0;
+ if( rc==SQLITE_OK && !pPager->tempFile ){
+ rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
+ }
}
+ sqlite3BitvecDestroy(pPager->pInJournal);
+ pPager->pInJournal = 0;
+ sqlite3BitvecDestroy(pPager->pAlwaysRollback);
+ pPager->pAlwaysRollback = 0;
+ sqlite3PcacheCleanAll(pPager->pPCache);
+#ifdef SQLITE_CHECK_PAGES
+ sqlite3PcacheIterate(pPager->pPCache, pager_set_pagehash);
+#endif
+ sqlite3PcacheSetFlags(pPager->pPCache,
+ ~(PGHDR_IN_JOURNAL | PGHDR_NEED_SYNC), 0
+ );
+ pPager->dirtyCache = 0;
+ pPager->nRec = 0;
+ }else{
+ assert( pPager->pInJournal==0 );
}
- pPager->journalOff = szJ;
-
-end_stmt_playback:
- if( rc==SQLITE_OK) {
- pPager->journalOff = szJ;
- /* pager_reload_cache(pPager); */
+ if( !pPager->exclusiveMode ){
+ rc2 = osUnlock(pPager->fd, SHARED_LOCK);
+ pPager->state = PAGER_SHARED;
+ }else if( pPager->state==PAGER_SYNCED ){
+ pPager->state = PAGER_EXCLUSIVE;
}
- return rc;
-}
+ pPager->origDbSize = 0;
+ pPager->setMaster = 0;
+ pPager->needSync = 0;
+ /* lruListSetFirstSynced(pPager); */
+ pPager->dbSize = -1;
+ pPager->dbModified = 0;
-/*
-** Change the maximum number of in-memory pages that are allowed.
-*/
-SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager *pPager, int mxPage){
- if( mxPage>10 ){
- pPager->mxPage = mxPage;
- }else{
- pPager->mxPage = 10;
- }
+ return (rc==SQLITE_OK?rc2:rc);
}
/*
-** Adjust the robustness of the database to damage due to OS crashes
-** or power failures by changing the number of syncs()s when writing
-** the rollback journal. There are three levels:
-**
-** OFF sqlite3OsSync() is never called. This is the default
-** for temporary and transient files.
+** Compute and return a checksum for the page of data.
**
-** NORMAL The journal is synced once before writes begin on the
-** database. This is normally adequate protection, but
-** it is theoretically possible, though very unlikely,
-** that an inopertune power failure could leave the journal
-** in a state which would cause damage to the database
-** when it is rolled back.
+** This is not a real checksum. It is really just the sum of the
+** random initial value and the page number. We experimented with
+** a checksum of the entire data, but that was found to be too slow.
**
-** FULL The journal is synced twice before writes begin on the
-** database (with some additional information - the nRec field
-** of the journal header - being written in between the two
-** syncs). If we assume that writing a
-** single disk sector is atomic, then this mode provides
-** assurance that the journal will not be corrupted to the
-** point of causing damage to the database during rollback.
+** Note that the page number is stored at the beginning of data and
+** the checksum is stored at the end. This is important. If journal
+** corruption occurs due to a power failure, the most likely scenario
+** is that one end or the other of the record will be changed. It is
+** much less likely that the two ends of the journal record will be
+** correct and the middle be corrupt. Thus, this "checksum" scheme,
+** though fast and simple, catches the mostly likely kind of corruption.
**
-** Numeric values associated with these states are OFF==1, NORMAL=2,
-** and FULL=3.
+** FIX ME: Consider adding every 200th (or so) byte of the data to the
+** checksum. That way if a single page spans 3 or more disk sectors and
+** only the middle sector is corrupt, we will still have a reasonable
+** chance of failing the checksum and thus detecting the problem.
*/
-#ifndef SQLITE_OMIT_PAGER_PRAGMAS
-SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(Pager *pPager, int level, int full_fsync){
- pPager->noSync = level==1 || pPager->tempFile;
- pPager->fullSync = level==3 && !pPager->tempFile;
- pPager->sync_flags = (full_fsync?SQLITE_SYNC_FULL:SQLITE_SYNC_NORMAL);
- if( pPager->noSync ) pPager->needSync = 0;
+static u32 pager_cksum(Pager *pPager, const u8 *aData){
+ u32 cksum = pPager->cksumInit;
+ int i = pPager->pageSize-200;
+ while( i>0 ){
+ cksum += aData[i];
+ i -= 200;
+ }
+ return cksum;
}
-#endif
-/*
-** The following global variable is incremented whenever the library
-** attempts to open a temporary file. This information is used for
-** testing and analysis only.
-*/
-#ifdef SQLITE_TEST
-SQLITE_API int sqlite3_opentemp_count = 0;
-#endif
+/* Forward declaration */
+static void makeClean(PgHdr*);
/*
-** Open a temporary file.
+** Read a single page from the journal file opened on file descriptor
+** jfd. Playback this one page.
**
-** Write the file descriptor into *fd. Return SQLITE_OK on success or some
-** other error code if we fail. The OS will automatically delete the temporary
-** file when it is closed.
+** The isMainJrnl flag is true if this is the main rollback journal and
+** false for the statement journal. The main rollback journal uses
+** checksums - the statement journal does not.
*/
-static int sqlite3PagerOpentemp(
- Pager *pPager, /* The pager object */
- sqlite3_file *pFile, /* Write the file descriptor here */
- int vfsFlags /* Flags passed through to the VFS */
+static int pager_playback_one_page(
+ Pager *pPager, /* The pager being played back */
+ sqlite3_file *jfd, /* The file that is the journal being rolled back */
+ i64 offset, /* Offset of the page within the journal */
+ int isMainJrnl /* True for main rollback journal. False for Stmt jrnl */
){
int rc;
+ PgHdr *pPg; /* An existing page in the cache */
+ Pgno pgno; /* The page number of a page in journal */
+ u32 cksum; /* Checksum used for sanity checking */
+ u8 *aData = (u8 *)pPager->pTmpSpace; /* Temp storage for a page */
-#ifdef SQLITE_TEST
- sqlite3_opentemp_count++; /* Used for testing and analysis only */
+ /* isMainJrnl should be true for the main journal and false for
+ ** statement journals. Verify that this is always the case
+ */
+ assert( jfd == (isMainJrnl ? pPager->jfd : pPager->stfd) );
+ assert( aData );
+
+ rc = read32bits(jfd, offset, &pgno);
+ if( rc!=SQLITE_OK ) return rc;
+ rc = sqlite3OsRead(jfd, aData, pPager->pageSize, offset+4);
+ if( rc!=SQLITE_OK ) return rc;
+ pPager->journalOff += pPager->pageSize + 4;
+
+ /* Sanity checking on the page. This is more important that I originally
+ ** thought. If a power failure occurs while the journal is being written,
+ ** it could cause invalid data to be written into the journal. We need to
+ ** detect this invalid data (with high probability) and ignore it.
+ */
+ if( pgno==0 || pgno==PAGER_MJ_PGNO(pPager) ){
+ return SQLITE_DONE;
+ }
+ if( pgno>(unsigned)pPager->dbSize ){
+ return SQLITE_OK;
+ }
+ if( isMainJrnl ){
+ rc = read32bits(jfd, offset+pPager->pageSize+4, &cksum);
+ if( rc ) return rc;
+ pPager->journalOff += 4;
+ if( pager_cksum(pPager, aData)!=cksum ){
+ return SQLITE_DONE;
+ }
+ }
+
+ assert( pPager->state==PAGER_RESERVED || pPager->state>=PAGER_EXCLUSIVE );
+
+ /* If the pager is in RESERVED state, then there must be a copy of this
+ ** page in the pager cache. In this case just update the pager cache,
+ ** not the database file. The page is left marked dirty in this case.
+ **
+ ** An exception to the above rule: If the database is in no-sync mode
+ ** and a page is moved during an incremental vacuum then the page may
+ ** not be in the pager cache. Later: if a malloc() or IO error occurs
+ ** during a Movepage() call, then the page may not be in the cache
+ ** either. So the condition described in the above paragraph is not
+ ** assert()able.
+ **
+ ** If in EXCLUSIVE state, then we update the pager cache if it exists
+ ** and the main file. The page is then marked not dirty.
+ **
+ ** Ticket #1171: The statement journal might contain page content that is
+ ** different from the page content at the start of the transaction.
+ ** This occurs when a page is changed prior to the start of a statement
+ ** then changed again within the statement. When rolling back such a
+ ** statement we must not write to the original database unless we know
+ ** for certain that original page contents are synced into the main rollback
+ ** journal. Otherwise, a power loss might leave modified data in the
+ ** database file without an entry in the rollback journal that can
+ ** restore the database to its original form. Two conditions must be
+ ** met before writing to the database files. (1) the database must be
+ ** locked. (2) we know that the original page content is fully synced
+ ** in the main journal either because the page is not in cache or else
+ ** the page is marked as needSync==0.
+ **
+ ** 2008-04-14: When attempting to vacuum a corrupt database file, it
+ ** is possible to fail a statement on a database that does not yet exist.
+ ** Do not attempt to write if database file has never been opened.
+ */
+ pPg = pager_lookup(pPager, pgno);
+ PAGERTRACE4("PLAYBACK %d page %d hash(%08x)\n",
+ PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, aData));
+ if( (pPager->state>=PAGER_EXCLUSIVE)
+ && (pPg==0 || 0==(pPg->flags&PGHDR_NEED_SYNC))
+ && (pPager->fd->pMethods)
+ ){
+ i64 ofst = (pgno-1)*(i64)pPager->pageSize;
+ rc = sqlite3OsWrite(pPager->fd, aData, pPager->pageSize, ofst);
+ }
+ if( pPg ){
+ /* No page should ever be explicitly rolled back that is in use, except
+ ** for page 1 which is held in use in order to keep the lock on the
+ ** database active. However such a page may be rolled back as a result
+ ** of an internal error resulting in an automatic call to
+ ** sqlite3PagerRollback().
+ */
+ void *pData;
+ pData = pPg->pData;
+ memcpy(pData, aData, pPager->pageSize);
+ if( pPager->xReiniter ){
+ pPager->xReiniter(pPg, pPager->pageSize);
+ }
+ if( isMainJrnl ) makeClean(pPg);
+#ifdef SQLITE_CHECK_PAGES
+ pPg->pageHash = pager_pagehash(pPg);
#endif
+ /* If this was page 1, then restore the value of Pager.dbFileVers.
+ ** Do this before any decoding. */
+ if( pgno==1 ){
+ memcpy(&pPager->dbFileVers, &((u8*)pData)[24],sizeof(pPager->dbFileVers));
+ }
- vfsFlags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
- SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE;
- rc = sqlite3OsOpen(pPager->pVfs, 0, pFile, vfsFlags, 0);
- assert( rc!=SQLITE_OK || pFile->pMethods );
+ /* Decode the page just read from disk */
+ CODEC1(pPager, pData, pPg->pgno, 3);
+ sqlite3PcacheRelease(pPg);
+ }
return rc;
}
/*
-** Create a new page cache and put a pointer to the page cache in *ppPager.
-** The file to be cached need not exist. The file is not locked until
-** the first call to sqlite3PagerGet() and is only held open until the
-** last page is released using sqlite3PagerUnref().
+** Parameter zMaster is the name of a master journal file. A single journal
+** file that referred to the master journal file has just been rolled back.
+** This routine checks if it is possible to delete the master journal file,
+** and does so if it is.
**
-** If zFilename is NULL then a randomly-named temporary file is created
-** and used as the file to be cached. The file will be deleted
-** automatically when it is closed.
+** Argument zMaster may point to Pager.pTmpSpace. So that buffer is not
+** available for use within this function.
**
-** If zFilename is ":memory:" then all information is held in cache.
-** It is never written to disk. This can be used to implement an
-** in-memory database.
+**
+** The master journal file contains the names of all child journals.
+** To tell if a master journal can be deleted, check to each of the
+** children. If all children are either missing or do not refer to
+** a different master journal, then this master journal can be deleted.
*/
-SQLITE_PRIVATE int sqlite3PagerOpen(
- sqlite3_vfs *pVfs, /* The virtual file system to use */
- Pager **ppPager, /* Return the Pager structure here */
- const char *zFilename, /* Name of the database file to open */
- int nExtra, /* Extra bytes append to each in-memory page */
- int flags, /* flags controlling this file */
- int vfsFlags /* flags passed through to sqlite3_vfs.xOpen() */
-){
- u8 *pPtr;
- Pager *pPager = 0;
- int rc = SQLITE_OK;
- int i;
- int tempFile = 0;
- int memDb = 0;
- int readOnly = 0;
- int useJournal = (flags & PAGER_OMIT_JOURNAL)==0;
- int noReadlock = (flags & PAGER_NO_READLOCK)!=0;
- int journalFileSize = sqlite3JournalSize(pVfs);
- int szPageDflt = SQLITE_DEFAULT_PAGE_SIZE;
- char *zPathname = 0;
- int nPathname = 0;
-
- /* The default return is a NULL pointer */
- *ppPager = 0;
+static int pager_delmaster(Pager *pPager, const char *zMaster){
+ sqlite3_vfs *pVfs = pPager->pVfs;
+ int rc;
+ int master_open = 0;
+ sqlite3_file *pMaster;
+ sqlite3_file *pJournal;
+ char *zMasterJournal = 0; /* Contents of master journal file */
+ i64 nMasterJournal; /* Size of master journal file */
- /* Compute and store the full pathname in an allocated buffer pointed
- ** to by zPathname, length nPathname. Or, if this is a temporary file,
- ** leave both nPathname and zPathname set to 0.
+ /* Open the master journal file exclusively in case some other process
+ ** is running this routine also. Not that it makes too much difference.
*/
- if( zFilename && zFilename[0] ){
- nPathname = pVfs->mxPathname+1;
- zPathname = sqlite3Malloc(nPathname*2);
- if( zPathname==0 ){
- return SQLITE_NOMEM;
- }
-#ifndef SQLITE_OMIT_MEMORYDB
- if( strcmp(zFilename,":memory:")==0 ){
- memDb = 1;
- zPathname[0] = 0;
- }else
-#endif
- {
- rc = sqlite3OsFullPathname(pVfs, zFilename, nPathname, zPathname);
- }
- if( rc!=SQLITE_OK ){
- sqlite3_free(zPathname);
- return rc;
- }
- nPathname = strlen(zPathname);
+ pMaster = (sqlite3_file *)sqlite3Malloc(pVfs->szOsFile * 2);
+ pJournal = (sqlite3_file *)(((u8 *)pMaster) + pVfs->szOsFile);
+ if( !pMaster ){
+ rc = SQLITE_NOMEM;
+ }else{
+ int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MASTER_JOURNAL);
+ rc = sqlite3OsOpen(pVfs, zMaster, pMaster, flags, 0);
}
+ if( rc!=SQLITE_OK ) goto delmaster_out;
+ master_open = 1;
- /* Allocate memory for the pager structure */
- pPager = sqlite3MallocZero(
- sizeof(*pPager) + /* Pager structure */
- journalFileSize + /* The journal file structure */
- pVfs->szOsFile * 3 + /* The main db and two journal files */
- 3*nPathname + 40 /* zFilename, zDirectory, zJournal */
- );
- if( !pPager ){
- sqlite3_free(zPathname);
- return SQLITE_NOMEM;
- }
- pPtr = (u8 *)&pPager[1];
- pPager->vfsFlags = vfsFlags;
- pPager->fd = (sqlite3_file*)&pPtr[pVfs->szOsFile*0];
- pPager->stfd = (sqlite3_file*)&pPtr[pVfs->szOsFile*1];
- pPager->jfd = (sqlite3_file*)&pPtr[pVfs->szOsFile*2];
- pPager->zFilename = (char*)&pPtr[pVfs->szOsFile*2+journalFileSize];
- pPager->zDirectory = &pPager->zFilename[nPathname+1];
- pPager->zJournal = &pPager->zDirectory[nPathname+1];
- pPager->pVfs = pVfs;
- if( zPathname ){
- memcpy(pPager->zFilename, zPathname, nPathname+1);
- sqlite3_free(zPathname);
- }
+ rc = sqlite3OsFileSize(pMaster, &nMasterJournal);
+ if( rc!=SQLITE_OK ) goto delmaster_out;
- /* Open the pager file.
- */
- if( zFilename && zFilename[0] && !memDb ){
- if( nPathname>(pVfs->mxPathname - sizeof("-journal")) ){
- rc = SQLITE_CANTOPEN;
- }else{
- int fout = 0;
- rc = sqlite3OsOpen(pVfs, pPager->zFilename, pPager->fd,
- pPager->vfsFlags, &fout);
- readOnly = (fout&SQLITE_OPEN_READONLY);
+ if( nMasterJournal>0 ){
+ char *zJournal;
+ char *zMasterPtr = 0;
+ int nMasterPtr = pPager->pVfs->mxPathname+1;
- /* If the file was successfully opened for read/write access,
- ** choose a default page size in case we have to create the
- ** database file. The default page size is the maximum of:
- **
- ** + SQLITE_DEFAULT_PAGE_SIZE,
- ** + The value returned by sqlite3OsSectorSize()
- ** + The largest page size that can be written atomically.
- */
- if( rc==SQLITE_OK && !readOnly ){
- int iSectorSize = sqlite3OsSectorSize(pPager->fd);
- if( szPageDflt<iSectorSize ){
- szPageDflt = iSectorSize;
+ /* Load the entire master journal file into space obtained from
+ ** sqlite3_malloc() and pointed to by zMasterJournal.
+ */
+ zMasterJournal = (char *)sqlite3Malloc(nMasterJournal + nMasterPtr);
+ if( !zMasterJournal ){
+ rc = SQLITE_NOMEM;
+ goto delmaster_out;
+ }
+ zMasterPtr = &zMasterJournal[nMasterJournal];
+ rc = sqlite3OsRead(pMaster, zMasterJournal, nMasterJournal, 0);
+ if( rc!=SQLITE_OK ) goto delmaster_out;
+
+ zJournal = zMasterJournal;
+ while( (zJournal-zMasterJournal)<nMasterJournal ){
+ int exists;
+ rc = sqlite3OsAccess(pVfs, zJournal, SQLITE_ACCESS_EXISTS, &exists);
+ if( rc!=SQLITE_OK ){
+ goto delmaster_out;
+ }
+ if( exists ){
+ /* One of the journals pointed to by the master journal exists.
+ ** Open it and check if it points at the master journal. If
+ ** so, return without deleting the master journal file.
+ */
+ int c;
+ int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL);
+ rc = sqlite3OsOpen(pVfs, zJournal, pJournal, flags, 0);
+ if( rc!=SQLITE_OK ){
+ goto delmaster_out;
}
-#ifdef SQLITE_ENABLE_ATOMIC_WRITE
- {
- int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
- int ii;
- assert(SQLITE_IOCAP_ATOMIC512==(512>>8));
- assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8));
- assert(SQLITE_MAX_DEFAULT_PAGE_SIZE<=65536);
- for(ii=szPageDflt; ii<=SQLITE_MAX_DEFAULT_PAGE_SIZE; ii=ii*2){
- if( iDc&(SQLITE_IOCAP_ATOMIC|(ii>>8)) ) szPageDflt = ii;
- }
+
+ rc = readMasterJournal(pJournal, zMasterPtr, nMasterPtr);
+ sqlite3OsClose(pJournal);
+ if( rc!=SQLITE_OK ){
+ goto delmaster_out;
}
-#endif
- if( szPageDflt>SQLITE_MAX_DEFAULT_PAGE_SIZE ){
- szPageDflt = SQLITE_MAX_DEFAULT_PAGE_SIZE;
+
+ c = zMasterPtr[0]!=0 && strcmp(zMasterPtr, zMaster)==0;
+ if( c ){
+ /* We have a match. Do not delete the master journal file. */
+ goto delmaster_out;
}
}
+ zJournal += (strlen(zJournal)+1);
}
- }else if( !memDb ){
- /* If a temporary file is requested, it is not opened immediately.
- ** In this case we accept the default page size and delay actually
- ** opening the file until the first call to OsWrite().
- */
- tempFile = 1;
- pPager->state = PAGER_EXCLUSIVE;
- }
-
- if( pPager && rc==SQLITE_OK ){
- pPager->pTmpSpace = sqlite3PageMalloc(szPageDflt);
- }
-
- /* If an error occured in either of the blocks above.
- ** Free the Pager structure and close the file.
- ** Since the pager is not allocated there is no need to set
- ** any Pager.errMask variables.
- */
- if( !pPager || !pPager->pTmpSpace ){
- sqlite3OsClose(pPager->fd);
- sqlite3_free(pPager);
- return ((rc==SQLITE_OK)?SQLITE_NOMEM:rc);
- }
-
- PAGERTRACE3("OPEN %d %s\n", FILEHANDLEID(pPager->fd), pPager->zFilename);
- IOTRACE(("OPEN %p %s\n", pPager, pPager->zFilename))
-
- /* Fill in Pager.zDirectory[] */
- memcpy(pPager->zDirectory, pPager->zFilename, nPathname+1);
- for(i=strlen(pPager->zDirectory); i>0 && pPager->zDirectory[i-1]!='/'; i--){}
- if( i>0 ) pPager->zDirectory[i-1] = 0;
-
- /* Fill in Pager.zJournal[] */
- if( zPathname ){
- memcpy(pPager->zJournal, pPager->zFilename, nPathname);
- memcpy(&pPager->zJournal[nPathname], "-journal", 9);
- }else{
- pPager->zJournal = 0;
}
+
+ rc = sqlite3OsDelete(pVfs, zMaster, 0);
- /* pPager->journalOpen = 0; */
- pPager->useJournal = useJournal && !memDb;
- pPager->noReadlock = noReadlock && readOnly;
- /* pPager->stmtOpen = 0; */
- /* pPager->stmtInUse = 0; */
- /* pPager->nRef = 0; */
- pPager->dbSize = memDb-1;
- pPager->pageSize = szPageDflt;
- /* pPager->stmtSize = 0; */
- /* pPager->stmtJSize = 0; */
- /* pPager->nPage = 0; */
- pPager->mxPage = 100;
- pPager->mxPgno = SQLITE_MAX_PAGE_COUNT;
- /* pPager->state = PAGER_UNLOCK; */
- assert( pPager->state == (tempFile ? PAGER_EXCLUSIVE : PAGER_UNLOCK) );
- /* pPager->errMask = 0; */
- pPager->tempFile = tempFile;
- assert( tempFile==PAGER_LOCKINGMODE_NORMAL
- || tempFile==PAGER_LOCKINGMODE_EXCLUSIVE );
- assert( PAGER_LOCKINGMODE_EXCLUSIVE==1 );
- pPager->exclusiveMode = tempFile;
- pPager->memDb = memDb;
- pPager->readOnly = readOnly;
- /* pPager->needSync = 0; */
- pPager->noSync = pPager->tempFile || !useJournal;
- pPager->fullSync = (pPager->noSync?0:1);
- pPager->sync_flags = SQLITE_SYNC_NORMAL;
- /* pPager->pFirst = 0; */
- /* pPager->pFirstSynced = 0; */
- /* pPager->pLast = 0; */
- pPager->nExtra = FORCE_ALIGNMENT(nExtra);
- pPager->journalSizeLimit = SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT;
- assert(pPager->fd->pMethods||memDb||tempFile);
- if( !memDb ){
- setSectorSize(pPager);
- }
- /* pPager->pBusyHandler = 0; */
- /* memset(pPager->aHash, 0, sizeof(pPager->aHash)); */
- *ppPager = pPager;
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
- pPager->iInUseMM = 0;
- pPager->iInUseDB = 0;
- if( !memDb ){
-#ifndef SQLITE_MUTEX_NOOP
- sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM2);
-#endif
- sqlite3_mutex_enter(mutex);
- pPager->pNext = sqlite3PagerList;
- if( sqlite3PagerList ){
- assert( sqlite3PagerList->pPrev==0 );
- sqlite3PagerList->pPrev = pPager;
- }
- pPager->pPrev = 0;
- sqlite3PagerList = pPager;
- sqlite3_mutex_leave(mutex);
+delmaster_out:
+ if( zMasterJournal ){
+ sqlite3_free(zMasterJournal);
+ }
+ if( master_open ){
+ sqlite3OsClose(pMaster);
}
-#endif
- return SQLITE_OK;
-}
-
-/*
-** Set the busy handler function.
-*/
-SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(Pager *pPager, BusyHandler *pBusyHandler){
- pPager->pBusyHandler = pBusyHandler;
+ sqlite3_free(pMaster);
+ return rc;
}
-/*
-** Set the destructor for this pager. If not NULL, the destructor is called
-** when the reference count on each page reaches zero. The destructor can
-** be used to clean up information in the extra segment appended to each page.
-**
-** The destructor is not called as a result sqlite3PagerClose().
-** Destructors are only called by sqlite3PagerUnref().
-*/
-SQLITE_PRIVATE void sqlite3PagerSetDestructor(Pager *pPager, void (*xDesc)(DbPage*,int)){
- pPager->xDestructor = xDesc;
-}
-/*
-** Set the reinitializer for this pager. If not NULL, the reinitializer
-** is called when the content of a page in cache is restored to its original
-** value as a result of a rollback. The callback gives higher-level code
-** an opportunity to restore the EXTRA section to agree with the restored
-** page data.
-*/
-SQLITE_PRIVATE void sqlite3PagerSetReiniter(Pager *pPager, void (*xReinit)(DbPage*,int)){
- pPager->xReiniter = xReinit;
-}
+static void pager_truncate_cache(Pager *pPager);
/*
-** Set the page size to *pPageSize. If the suggest new page size is
-** inappropriate, then an alternative page size is set to that
-** value before returning.
+** Truncate the main file of the given pager to the number of pages
+** indicated. Also truncate the cached representation of the file.
+**
+** Might might be the case that the file on disk is smaller than nPage.
+** This can happen, for example, if we are in the middle of a transaction
+** which has extended the file size and the new pages are still all held
+** in cache, then an INSERT or UPDATE does a statement rollback. Some
+** operating system implementations can get confused if you try to
+** truncate a file to some size that is larger than it currently is,
+** so detect this case and write a single zero byte to the end of the new
+** file instead.
*/
-SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager *pPager, u16 *pPageSize){
+static int pager_truncate(Pager *pPager, int nPage){
int rc = SQLITE_OK;
- u16 pageSize = *pPageSize;
- assert( pageSize==0 || (pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE) );
- if( pageSize && pageSize!=pPager->pageSize
- && !pPager->memDb && pPager->nRef==0
- ){
- char *pNew = (char *)sqlite3PageMalloc(pageSize);
- if( !pNew ){
- rc = SQLITE_NOMEM;
- }else{
- pagerEnter(pPager);
- pager_reset(pPager);
- pPager->pageSize = pageSize;
- setSectorSize(pPager);
- sqlite3PageFree(pPager->pTmpSpace);
- pPager->pTmpSpace = pNew;
- pagerLeave(pPager);
+ if( pPager->state>=PAGER_EXCLUSIVE && pPager->fd->pMethods ){
+ i64 currentSize, newSize;
+ rc = sqlite3OsFileSize(pPager->fd, ¤tSize);
+ newSize = pPager->pageSize*(i64)nPage;
+ if( rc==SQLITE_OK && currentSize!=newSize ){
+ if( currentSize>newSize ){
+ rc = sqlite3OsTruncate(pPager->fd, newSize);
+ }else{
+ rc = sqlite3OsWrite(pPager->fd, "", 1, newSize-1);
+ }
}
}
- *pPageSize = pPager->pageSize;
+ if( rc==SQLITE_OK ){
+ pPager->dbSize = nPage;
+ pager_truncate_cache(pPager);
+ }
return rc;
}
/*
-** Return a pointer to the "temporary page" buffer held internally
-** by the pager. This is a buffer that is big enough to hold the
-** entire content of a database page. This buffer is used internally
-** during rollback and will be overwritten whenever a rollback
-** occurs. But other modules are free to use it too, as long as
-** no rollbacks are happening.
-*/
-SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager *pPager){
- return pPager->pTmpSpace;
-}
-
-/*
-** Attempt to set the maximum database page count if mxPage is positive.
-** Make no changes if mxPage is zero or negative. And never reduce the
-** maximum page count below the current size of the database.
+** Set the sectorSize for the given pager.
**
-** Regardless of mxPage, return the current maximum page count.
+** The sector size is at least as big as the sector size reported
+** by sqlite3OsSectorSize(). The minimum sector size is 512.
*/
-SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager *pPager, int mxPage){
- if( mxPage>0 ){
- pPager->mxPgno = mxPage;
+static void setSectorSize(Pager *pPager){
+ assert(pPager->fd->pMethods||pPager->tempFile);
+ if( !pPager->tempFile ){
+ /* Sector size doesn't matter for temporary files. Also, the file
+ ** may not have been opened yet, in whcih case the OsSectorSize()
+ ** call will segfault.
+ */
+ pPager->sectorSize = sqlite3OsSectorSize(pPager->fd);
+ }
+ if( pPager->sectorSize<512 ){
+ pPager->sectorSize = 512;
}
- sqlite3PagerPagecount(pPager, 0);
- return pPager->mxPgno;
}
/*
-** The following set of routines are used to disable the simulated
-** I/O error mechanism. These routines are used to avoid simulated
-** errors in places where we do not care about errors.
+** Playback the journal and thus restore the database file to
+** the state it was in before we started making changes.
**
-** Unless -DSQLITE_TEST=1 is used, these routines are all no-ops
-** and generate no code.
-*/
-#ifdef SQLITE_TEST
-SQLITE_API extern int sqlite3_io_error_pending;
-SQLITE_API extern int sqlite3_io_error_hit;
-static int saved_cnt;
-void disable_simulated_io_errors(void){
- saved_cnt = sqlite3_io_error_pending;
- sqlite3_io_error_pending = -1;
-}
-void enable_simulated_io_errors(void){
- sqlite3_io_error_pending = saved_cnt;
-}
-#else
-# define disable_simulated_io_errors()
-# define enable_simulated_io_errors()
-#endif
-
-/*
-** Read the first N bytes from the beginning of the file into memory
-** that pDest points to.
+** The journal file format is as follows:
**
-** No error checking is done. The rational for this is that this function
-** may be called even if the file does not exist or contain a header. In
-** these cases sqlite3OsRead() will return an error, to which the correct
-** response is to zero the memory at pDest and continue. A real IO error
-** will presumably recur and be picked up later (Todo: Think about this).
+** (1) 8 byte prefix. A copy of aJournalMagic[].
+** (2) 4 byte big-endian integer which is the number of valid page records
+** in the journal. If this value is 0xffffffff, then compute the
+** number of page records from the journal size.
+** (3) 4 byte big-endian integer which is the initial value for the
+** sanity checksum.
+** (4) 4 byte integer which is the number of pages to truncate the
+** database to during a rollback.
+** (5) 4 byte big-endian integer which is the sector size. The header
+** is this many bytes in size.
+** (6) 4 byte big-endian integer which is the page case.
+** (7) 4 byte integer which is the number of bytes in the master journal
+** name. The value may be zero (indicate that there is no master
+** journal.)
+** (8) N bytes of the master journal name. The name will be nul-terminated
+** and might be shorter than the value read from (5). If the first byte
+** of the name is \000 then there is no master journal. The master
+** journal name is stored in UTF-8.
+** (9) Zero or more pages instances, each as follows:
+** + 4 byte page number.
+** + pPager->pageSize bytes of data.
+** + 4 byte checksum
+**
+** When we speak of the journal header, we mean the first 8 items above.
+** Each entry in the journal is an instance of the 9th item.
+**
+** Call the value from the second bullet "nRec". nRec is the number of
+** valid page entries in the journal. In most cases, you can compute the
+** value of nRec from the size of the journal file. But if a power
+** failure occurred while the journal was being written, it could be the
+** case that the size of the journal file had already been increased but
+** the extra entries had not yet made it safely to disk. In such a case,
+** the value of nRec computed from the file size would be too large. For
+** that reason, we always use the nRec value in the header.
+**
+** If the nRec value is 0xffffffff it means that nRec should be computed
+** from the file size. This value is used when the user selects the
+** no-sync option for the journal. A power failure could lead to corruption
+** in this case. But for things like temporary table (which will be
+** deleted when the power is restored) we don't care.
+**
+** If the file opened as the journal file is not a well-formed
+** journal file then all pages up to the first corrupted page are rolled
+** back (or no pages if the journal header is corrupted). The journal file
+** is then deleted and SQLITE_OK returned, just as if no corruption had
+** been encountered.
+**
+** If an I/O or malloc() error occurs, the journal-file is not deleted
+** and an error code is returned.
*/
-SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager *pPager, int N, unsigned char *pDest){
- int rc = SQLITE_OK;
- memset(pDest, 0, N);
- assert(MEMDB||pPager->fd->pMethods||pPager->tempFile);
- if( pPager->fd->pMethods ){
- IOTRACE(("DBHDR %p 0 %d\n", pPager, N))
- rc = sqlite3OsRead(pPager->fd, pDest, N, 0);
- if( rc==SQLITE_IOERR_SHORT_READ ){
- rc = SQLITE_OK;
- }
+static int pager_playback(Pager *pPager, int isHot){
+ sqlite3_vfs *pVfs = pPager->pVfs;
+ i64 szJ; /* Size of the journal file in bytes */
+ u32 nRec; /* Number of Records in the journal */
+ u32 u; /* Unsigned loop counter */
+ Pgno mxPg = 0; /* Size of the original file in pages */
+ int rc; /* Result code of a subroutine */
+ int res = 1; /* Value returned by sqlite3OsAccess() */
+ char *zMaster = 0; /* Name of master journal file if any */
+
+ /* Figure out how many records are in the journal. Abort early if
+ ** the journal is empty.
+ */
+ assert( pPager->journalOpen );
+ rc = sqlite3OsFileSize(pPager->jfd, &szJ);
+ if( rc!=SQLITE_OK || szJ==0 ){
+ goto end_playback;
}
- return rc;
-}
-/*
-** Return the total number of pages in the disk file associated with
-** pPager.
-**
-** If the PENDING_BYTE lies on the page directly after the end of the
-** file, then consider this page part of the file too. For example, if
-** PENDING_BYTE is byte 4096 (the first byte of page 5) and the size of the
-** file is 4096 bytes, 5 is returned instead of 4.
-*/
-SQLITE_PRIVATE int sqlite3PagerPagecount(Pager *pPager, int *pnPage){
- i64 n = 0;
- int rc;
- assert( pPager!=0 );
- if( pPager->errCode ){
- return pPager->errCode;
+ /* Read the master journal name from the journal, if it is present.
+ ** If a master journal file name is specified, but the file is not
+ ** present on disk, then the journal is not hot and does not need to be
+ ** played back.
+ */
+ zMaster = pPager->pTmpSpace;
+ rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1);
+ if( rc==SQLITE_OK && zMaster[0] ){
+ rc = sqlite3OsAccess(pVfs, zMaster, SQLITE_ACCESS_EXISTS, &res);
}
- if( pPager->dbSize>=0 ){
- n = pPager->dbSize;
- } else {
- assert(pPager->fd->pMethods||pPager->tempFile);
- if( (pPager->fd->pMethods)
- && (rc = sqlite3OsFileSize(pPager->fd, &n))!=SQLITE_OK ){
- pPager->nRef++;
- pager_error(pPager, rc);
- pPager->nRef--;
- return rc;
- }
- if( n>0 && n<pPager->pageSize ){
- n = 1;
- }else{
- n /= pPager->pageSize;
- }
- if( pPager->state!=PAGER_UNLOCK ){
- pPager->dbSize = n;
- }
- }
- if( n==(PENDING_BYTE/pPager->pageSize) ){
- n++;
- }
- if( n>pPager->mxPgno ){
- pPager->mxPgno = n;
- }
- if( pnPage ){
- *pnPage = n;
+ zMaster = 0;
+ if( rc!=SQLITE_OK || !res ){
+ goto end_playback;
}
- return SQLITE_OK;
-}
+ pPager->journalOff = 0;
+ /* This loop terminates either when the readJournalHdr() call returns
+ ** SQLITE_DONE or an IO error occurs. */
+ while( 1 ){
-#ifndef SQLITE_OMIT_MEMORYDB
-/*
-** Clear a PgHistory block
-*/
-static void clearHistory(PgHistory *pHist){
- sqlite3PageFree(pHist->pOrig);
- sqlite3PageFree(pHist->pStmt);
- pHist->pOrig = 0;
- pHist->pStmt = 0;
-}
-#else
-#define clearHistory(x)
-#endif
+ /* Read the next journal header from the journal file. If there are
+ ** not enough bytes left in the journal file for a complete header, or
+ ** it is corrupted, then a process must of failed while writing it.
+ ** This indicates nothing more needs to be rolled back.
+ */
+ rc = readJournalHdr(pPager, szJ, &nRec, &mxPg);
+ if( rc!=SQLITE_OK ){
+ if( rc==SQLITE_DONE ){
+ rc = SQLITE_OK;
+ }
+ goto end_playback;
+ }
-/*
-** Forward declaration
-*/
-static int syncJournal(Pager*);
+ /* If nRec is 0xffffffff, then this journal was created by a process
+ ** working in no-sync mode. This means that the rest of the journal
+ ** file consists of pages, there are no more journal headers. Compute
+ ** the value of nRec based on this assumption.
+ */
+ if( nRec==0xffffffff ){
+ assert( pPager->journalOff==JOURNAL_HDR_SZ(pPager) );
+ nRec = (szJ - JOURNAL_HDR_SZ(pPager))/JOURNAL_PG_SZ(pPager);
+ }
-/*
-** Unlink pPg from its hash chain. Also set the page number to 0 to indicate
-** that the page is not part of any hash chain. This is required because the
-** sqlite3PagerMovepage() routine can leave a page in the
-** pNextFree/pPrevFree list that is not a part of any hash-chain.
-*/
-static void unlinkHashChain(Pager *pPager, PgHdr *pPg){
- if( pPg->pgno==0 ){
- assert( pPg->pNextHash==0 && pPg->pPrevHash==0 );
- return;
+ /* If nRec is 0 and this rollback is of a transaction created by this
+ ** process and if this is the final header in the journal, then it means
+ ** that this part of the journal was being filled but has not yet been
+ ** synced to disk. Compute the number of pages based on the remaining
+ ** size of the file.
+ **
+ ** The third term of the test was added to fix ticket #2565.
+ */
+ if( nRec==0 && !isHot &&
+ pPager->journalHdr+JOURNAL_HDR_SZ(pPager)==pPager->journalOff ){
+ nRec = (szJ - pPager->journalOff) / JOURNAL_PG_SZ(pPager);
+ }
+
+ /* If this is the first header read from the journal, truncate the
+ ** database file back to its original size.
+ */
+ if( pPager->journalOff==JOURNAL_HDR_SZ(pPager) ){
+ rc = pager_truncate(pPager, mxPg);
+ if( rc!=SQLITE_OK ){
+ goto end_playback;
+ }
+ }
+
+ /* Copy original pages out of the journal and back into the database file.
+ */
+ for(u=0; u<nRec; u++){
+ rc = pager_playback_one_page(pPager, pPager->jfd, pPager->journalOff, 1);
+ if( rc!=SQLITE_OK ){
+ if( rc==SQLITE_DONE ){
+ rc = SQLITE_OK;
+ pPager->journalOff = szJ;
+ break;
+ }else{
+ goto end_playback;
+ }
+ }
+ }
}
- if( pPg->pNextHash ){
- pPg->pNextHash->pPrevHash = pPg->pPrevHash;
+ /*NOTREACHED*/
+ assert( 0 );
+
+end_playback:
+ if( rc==SQLITE_OK ){
+ zMaster = pPager->pTmpSpace;
+ rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1);
}
- if( pPg->pPrevHash ){
- assert( pPager->aHash[pPg->pgno & (pPager->nHash-1)]!=pPg );
- pPg->pPrevHash->pNextHash = pPg->pNextHash;
- }else{
- int h = pPg->pgno & (pPager->nHash-1);
- pPager->aHash[h] = pPg->pNextHash;
+ if( rc==SQLITE_OK ){
+ rc = pager_end_transaction(pPager, zMaster[0]!='\0');
}
- if( MEMDB ){
- clearHistory(PGHDR_TO_HIST(pPg, pPager));
+ if( rc==SQLITE_OK && zMaster[0] ){
+ /* If there was a master journal and this routine will return success,
+ ** see if it is possible to delete the master journal.
+ */
+ rc = pager_delmaster(pPager, zMaster);
}
- pPg->pgno = 0;
- pPg->pNextHash = pPg->pPrevHash = 0;
-}
-
-/*
-** Unlink a page from the free list (the list of all pages where nRef==0)
-** and from its hash collision chain.
-*/
-static void unlinkPage(PgHdr *pPg){
- Pager *pPager = pPg->pPager;
- /* Unlink from free page list */
- lruListRemove(pPg);
-
- /* Unlink from the pgno hash table */
- unlinkHashChain(pPager, pPg);
+ /* The Pager.sectorSize variable may have been updated while rolling
+ ** back a journal created by a process with a different sector size
+ ** value. Reset it to the correct value for this process.
+ */
+ setSectorSize(pPager);
+ return rc;
}
/*
-** This routine is used to truncate the cache when a database
-** is truncated. Drop from the cache all pages whose pgno is
-** larger than pPager->dbSize and is unreferenced.
+** Playback the statement journal.
**
-** Referenced pages larger than pPager->dbSize are zeroed.
+** This is similar to playing back the transaction journal but with
+** a few extra twists.
**
-** Actually, at the point this routine is called, it would be
-** an error to have a referenced page. But rather than delete
-** that page and guarantee a subsequent segfault, it seems better
-** to zero it and hope that we error out sanely.
-*/
-static void pager_truncate_cache(Pager *pPager){
- PgHdr *pPg;
- PgHdr **ppPg;
- int dbSize = pPager->dbSize;
-
- ppPg = &pPager->pAll;
- while( (pPg = *ppPg)!=0 ){
- if( pPg->pgno<=dbSize ){
- ppPg = &pPg->pNextAll;
- }else if( pPg->nRef>0 ){
- memset(PGHDR_TO_DATA(pPg), 0, pPager->pageSize);
- ppPg = &pPg->pNextAll;
- }else{
- *ppPg = pPg->pNextAll;
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
- if( *ppPg ){
- (*ppPg)->pPrevAll = pPg->pPrevAll;
- }
-#endif
- IOTRACE(("PGFREE %p %d\n", pPager, pPg->pgno));
- PAGER_INCR(sqlite3_pager_pgfree_count);
- unlinkPage(pPg);
- makeClean(pPg);
- sqlite3PageFree(pPg->pData);
- sqlite3_free(pPg);
- pPager->nPage--;
- }
- }
-}
-
-/*
-** Try to obtain a lock on a file. Invoke the busy callback if the lock
-** is currently not available. Repeat until the busy callback returns
-** false or until the lock succeeds.
+** (1) The number of pages in the database file at the start of
+** the statement is stored in pPager->stmtSize, not in the
+** journal file itself.
**
-** Return SQLITE_OK on success and an error code if we cannot obtain
-** the lock.
+** (2) In addition to playing back the statement journal, also
+** playback all pages of the transaction journal beginning
+** at offset pPager->stmtJSize.
*/
-static int pager_wait_on_lock(Pager *pPager, int locktype){
+static int pager_stmt_playback(Pager *pPager){
+ i64 szJ; /* Size of the full journal */
+ i64 hdrOff;
+ int nRec; /* Number of Records */
+ int i; /* Loop counter */
int rc;
- /* The OS lock values must be the same as the Pager lock values */
- assert( PAGER_SHARED==SHARED_LOCK );
- assert( PAGER_RESERVED==RESERVED_LOCK );
- assert( PAGER_EXCLUSIVE==EXCLUSIVE_LOCK );
-
- /* If the file is currently unlocked then the size must be unknown */
- assert( pPager->state>=PAGER_SHARED || pPager->dbSize<0 || MEMDB );
+ szJ = pPager->journalOff;
- if( pPager->state>=locktype ){
- rc = SQLITE_OK;
- }else{
- if( pPager->pBusyHandler ) pPager->pBusyHandler->nBusy = 0;
- do {
- rc = sqlite3OsLock(pPager->fd, locktype);
- }while( rc==SQLITE_BUSY && sqlite3InvokeBusyHandler(pPager->pBusyHandler) );
- if( rc==SQLITE_OK ){
- pPager->state = locktype;
- IOTRACE(("LOCK %p %d\n", pPager, locktype))
- }
+ /* Set hdrOff to be the offset just after the end of the last journal
+ ** page written before the first journal-header for this statement
+ ** transaction was written, or the end of the file if no journal
+ ** header was written.
+ */
+ hdrOff = pPager->stmtHdrOff;
+ assert( pPager->fullSync || !hdrOff );
+ if( !hdrOff ){
+ hdrOff = szJ;
}
- return rc;
-}
+
+ /* Truncate the database back to its original size.
+ */
+ rc = pager_truncate(pPager, pPager->stmtSize);
+ assert( pPager->state>=PAGER_SHARED );
-/*
-** Truncate the file to the number of pages specified.
-*/
-SQLITE_PRIVATE int sqlite3PagerTruncate(Pager *pPager, Pgno nPage){
- int rc;
- assert( pPager->state>=PAGER_SHARED || MEMDB );
- sqlite3PagerPagecount(pPager, 0);
- if( pPager->errCode ){
- rc = pPager->errCode;
- return rc;
- }
- if( nPage>=(unsigned)pPager->dbSize ){
- return SQLITE_OK;
- }
- if( MEMDB ){
- pPager->dbSize = nPage;
- pager_truncate_cache(pPager);
- return SQLITE_OK;
- }
- pagerEnter(pPager);
- rc = syncJournal(pPager);
- pagerLeave(pPager);
- if( rc!=SQLITE_OK ){
- return rc;
+ /* Figure out how many records are in the statement journal.
+ */
+ assert( pPager->stmtInUse && pPager->journalOpen );
+ nRec = pPager->stmtNRec;
+
+ /* Copy original pages out of the statement journal and back into the
+ ** database file. Note that the statement journal omits checksums from
+ ** each record since power-failure recovery is not important to statement
+ ** journals.
+ */
+ for(i=0; i<nRec; i++){
+ i64 offset = i*(4+pPager->pageSize);
+ rc = pager_playback_one_page(pPager, pPager->stfd, offset, 0);
+ assert( rc!=SQLITE_DONE );
+ if( rc!=SQLITE_OK ) goto end_stmt_playback;
}
- /* Get an exclusive lock on the database before truncating. */
- pagerEnter(pPager);
- rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
- pagerLeave(pPager);
- if( rc!=SQLITE_OK ){
- return rc;
+ /* Now roll some pages back from the transaction journal. Pager.stmtJSize
+ ** was the size of the journal file when this statement was started, so
+ ** everything after that needs to be rolled back, either into the
+ ** database, the memory cache, or both.
+ **
+ ** If it is not zero, then Pager.stmtHdrOff is the offset to the start
+ ** of the first journal header written during this statement transaction.
+ */
+ pPager->journalOff = pPager->stmtJSize;
+ pPager->cksumInit = pPager->stmtCksum;
+ while( pPager->journalOff < hdrOff ){
+ rc = pager_playback_one_page(pPager, pPager->jfd, pPager->journalOff, 1);
+ assert( rc!=SQLITE_DONE );
+ if( rc!=SQLITE_OK ) goto end_stmt_playback;
}
- rc = pager_truncate(pPager, nPage);
- return rc;
-}
-
-/*
-** Shutdown the page cache. Free all memory and close all files.
-**
-** If a transaction was in progress when this routine is called, that
-** transaction is rolled back. All outstanding pages are invalidated
-** and their memory is freed. Any attempt to use a page associated
-** with this page cache after this function returns will likely
-** result in a coredump.
-**
-** This function always succeeds. If a transaction is active an attempt
-** is made to roll it back. If an error occurs during the rollback
-** a hot journal may be left in the filesystem but no error is returned
-** to the caller.
-*/
-SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager){
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
- if( !MEMDB ){
-#ifndef SQLITE_MUTEX_NOOP
- sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM2);
-#endif
- sqlite3_mutex_enter(mutex);
- if( pPager->pPrev ){
- pPager->pPrev->pNext = pPager->pNext;
- }else{
- sqlite3PagerList = pPager->pNext;
+ while( pPager->journalOff < szJ ){
+ u32 nJRec; /* Number of Journal Records */
+ u32 dummy;
+ rc = readJournalHdr(pPager, szJ, &nJRec, &dummy);
+ if( rc!=SQLITE_OK ){
+ assert( rc!=SQLITE_DONE );
+ goto end_stmt_playback;
}
- if( pPager->pNext ){
- pPager->pNext->pPrev = pPager->pPrev;
+ if( nJRec==0 ){
+ nJRec = (szJ - pPager->journalOff) / (pPager->pageSize+8);
+ }
+ for(i=nJRec-1; i>=0 && pPager->journalOff < szJ; i--){
+ rc = pager_playback_one_page(pPager, pPager->jfd, pPager->journalOff, 1);
+ assert( rc!=SQLITE_DONE );
+ if( rc!=SQLITE_OK ) goto end_stmt_playback;
}
- sqlite3_mutex_leave(mutex);
}
-#endif
- disable_simulated_io_errors();
- sqlite3BeginBenignMalloc();
- pPager->errCode = 0;
- pPager->exclusiveMode = 0;
- pager_reset(pPager);
- pagerUnlockAndRollback(pPager);
- enable_simulated_io_errors();
- sqlite3EndBenignMalloc();
- PAGERTRACE2("CLOSE %d\n", PAGERID(pPager));
- IOTRACE(("CLOSE %p\n", pPager))
- if( pPager->journalOpen ){
- sqlite3OsClose(pPager->jfd);
- }
- sqlite3BitvecDestroy(pPager->pInJournal);
- if( pPager->stmtOpen ){
- sqlite3OsClose(pPager->stfd);
+ pPager->journalOff = szJ;
+
+end_stmt_playback:
+ if( rc==SQLITE_OK) {
+ pPager->journalOff = szJ;
+ /* pager_reload_cache(pPager); */
}
- sqlite3OsClose(pPager->fd);
- /* Temp files are automatically deleted by the OS
- ** if( pPager->tempFile ){
- ** sqlite3OsDelete(pPager->zFilename);
- ** }
- */
-
- sqlite3_free(pPager->aHash);
- sqlite3PageFree(pPager->pTmpSpace);
- sqlite3_free(pPager);
- return SQLITE_OK;
+ return rc;
}
-#if !defined(NDEBUG) || defined(SQLITE_TEST)
/*
-** Return the page number for the given page data.
+** Change the maximum number of in-memory pages that are allowed.
*/
-SQLITE_PRIVATE Pgno sqlite3PagerPagenumber(DbPage *p){
- return p->pgno;
+SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager *pPager, int mxPage){
+ sqlite3PcacheSetCachesize(pPager->pPCache, mxPage);
}
-#endif
/*
-** The page_ref() function increments the reference count for a page.
-** If the page is currently on the freelist (the reference count is zero) then
-** remove it from the freelist.
-**
-** For non-test systems, page_ref() is a macro that calls _page_ref()
-** online of the reference count is zero. For test systems, page_ref()
-** is a real function so that we can set breakpoints and trace it.
-*/
-static void _page_ref(PgHdr *pPg){
- if( pPg->nRef==0 ){
- /* The page is currently on the freelist. Remove it. */
- lruListRemove(pPg);
- pPg->pPager->nRef++;
- }
- pPg->nRef++;
+** Adjust the robustness of the database to damage due to OS crashes
+** or power failures by changing the number of syncs()s when writing
+** the rollback journal. There are three levels:
+**
+** OFF sqlite3OsSync() is never called. This is the default
+** for temporary and transient files.
+**
+** NORMAL The journal is synced once before writes begin on the
+** database. This is normally adequate protection, but
+** it is theoretically possible, though very unlikely,
+** that an inopertune power failure could leave the journal
+** in a state which would cause damage to the database
+** when it is rolled back.
+**
+** FULL The journal is synced twice before writes begin on the
+** database (with some additional information - the nRec field
+** of the journal header - being written in between the two
+** syncs). If we assume that writing a
+** single disk sector is atomic, then this mode provides
+** assurance that the journal will not be corrupted to the
+** point of causing damage to the database during rollback.
+**
+** Numeric values associated with these states are OFF==1, NORMAL=2,
+** and FULL=3.
+*/
+#ifndef SQLITE_OMIT_PAGER_PRAGMAS
+SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(Pager *pPager, int level, int bFullFsync){
+ pPager->noSync = level==1 || pPager->tempFile;
+ pPager->fullSync = level==3 && !pPager->tempFile;
+ pPager->sync_flags = (bFullFsync?SQLITE_SYNC_FULL:SQLITE_SYNC_NORMAL);
+ if( pPager->noSync ) pPager->needSync = 0;
}
-#ifdef SQLITE_DEBUG
- static void page_ref(PgHdr *pPg){
- if( pPg->nRef==0 ){
- _page_ref(pPg);
- }else{
- pPg->nRef++;
- }
- }
-#else
-# define page_ref(P) ((P)->nRef==0?_page_ref(P):(void)(P)->nRef++)
#endif
/*
-** Increment the reference count for a page. The input pointer is
-** a reference to the page data.
+** The following global variable is incremented whenever the library
+** attempts to open a temporary file. This information is used for
+** testing and analysis only.
*/
-SQLITE_PRIVATE int sqlite3PagerRef(DbPage *pPg){
- pagerEnter(pPg->pPager);
- page_ref(pPg);
- pagerLeave(pPg->pPager);
- return SQLITE_OK;
-}
+#ifdef SQLITE_TEST
+SQLITE_API int sqlite3_opentemp_count = 0;
+#endif
/*
-** Sync the journal. In other words, make sure all the pages that have
-** been written to the journal have actually reached the surface of the
-** disk. It is not safe to modify the original database file until after
-** the journal has been synced. If the original database is modified before
-** the journal is synced and a power failure occurs, the unsynced journal
-** data would be lost and we would be unable to completely rollback the
-** database changes. Database corruption would occur.
-**
-** This routine also updates the nRec field in the header of the journal.
-** (See comments on the pager_playback() routine for additional information.)
-** If the sync mode is FULL, two syncs will occur. First the whole journal
-** is synced, then the nRec field is updated, then a second sync occurs.
-**
-** For temporary databases, we do not care if we are able to rollback
-** after a power failure, so no sync occurs.
-**
-** If the IOCAP_SEQUENTIAL flag is set for the persistent media on which
-** the database is stored, then OsSync() is never called on the journal
-** file. In this case all that is required is to update the nRec field in
-** the journal header.
+** Open a temporary file.
**
-** This routine clears the needSync field of every page current held in
-** memory.
+** Write the file descriptor into *fd. Return SQLITE_OK on success or some
+** other error code if we fail. The OS will automatically delete the temporary
+** file when it is closed.
*/
-static int syncJournal(Pager *pPager){
- PgHdr *pPg;
- int rc = SQLITE_OK;
-
- /* Sync the journal before modifying the main database
- ** (assuming there is a journal and it needs to be synced.)
- */
- if( pPager->needSync ){
- if( !pPager->tempFile ){
- int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
- assert( pPager->journalOpen );
-
- if( 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){
- /* Write the nRec value into the journal file header. If in
- ** full-synchronous mode, sync the journal first. This ensures that
- ** all data has really hit the disk before nRec is updated to mark
- ** it as a candidate for rollback.
- **
- ** This is not required if the persistent media supports the
- ** SAFE_APPEND property. Because in this case it is not possible
- ** for garbage data to be appended to the file, the nRec field
- ** is populated with 0xFFFFFFFF when the journal header is written
- ** and never needs to be updated.
- */
- i64 jrnlOff;
- if( pPager->fullSync && 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){
- PAGERTRACE2("SYNC journal of %d\n", PAGERID(pPager));
- IOTRACE(("JSYNC %p\n", pPager))
- rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags);
- if( rc!=0 ) return rc;
- }
-
- jrnlOff = pPager->journalHdr + sizeof(aJournalMagic);
- IOTRACE(("JHDR %p %lld %d\n", pPager, jrnlOff, 4));
- rc = write32bits(pPager->jfd, jrnlOff, pPager->nRec);
- if( rc ) return rc;
- }
- if( 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){
- PAGERTRACE2("SYNC journal of %d\n", PAGERID(pPager));
- IOTRACE(("JSYNC %p\n", pPager))
- rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags|
- (pPager->sync_flags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0)
- );
- if( rc!=0 ) return rc;
- }
- pPager->journalStarted = 1;
- }
- pPager->needSync = 0;
-
- /* Erase the needSync flag from every page.
- */
- for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
- pPg->needSync = 0;
- }
- lruListSetFirstSynced(pPager);
- }
+static int sqlite3PagerOpentemp(
+ Pager *pPager, /* The pager object */
+ sqlite3_file *pFile, /* Write the file descriptor here */
+ int vfsFlags /* Flags passed through to the VFS */
+){
+ int rc;
-#ifndef NDEBUG
- /* If the Pager.needSync flag is clear then the PgHdr.needSync
- ** flag must also be clear for all pages. Verify that this
- ** invariant is true.
- */
- else{
- for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
- assert( pPg->needSync==0 );
- }
- assert( pPager->lru.pFirstSynced==pPager->lru.pFirst );
- }
+#ifdef SQLITE_TEST
+ sqlite3_opentemp_count++; /* Used for testing and analysis only */
#endif
+ vfsFlags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
+ SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE;
+ rc = sqlite3OsOpen(pPager->pVfs, 0, pFile, vfsFlags, 0);
+ assert( rc!=SQLITE_OK || pFile->pMethods );
return rc;
}
-/*
-** Merge two lists of pages connected by pDirty and in pgno order.
-** Do not both fixing the pPrevDirty pointers.
-*/
-static PgHdr *merge_pagelist(PgHdr *pA, PgHdr *pB){
- PgHdr result, *pTail;
- pTail = &result;
- while( pA && pB ){
- if( pA->pgno<pB->pgno ){
- pTail->pDirty = pA;
- pTail = pA;
- pA = pA->pDirty;
- }else{
- pTail->pDirty = pB;
- pTail = pB;
- pB = pB->pDirty;
- }
- }
- if( pA ){
- pTail->pDirty = pA;
- }else if( pB ){
- pTail->pDirty = pB;
- }else{
- pTail->pDirty = 0;
- }
- return result.pDirty;
-}
+static int pagerStress(void *,PgHdr *);
/*
-** Sort the list of pages in accending order by pgno. Pages are
-** connected by pDirty pointers. The pPrevDirty pointers are
-** corrupted by this sort.
+** Create a new page cache and put a pointer to the page cache in *ppPager.
+** The file to be cached need not exist. The file is not locked until
+** the first call to sqlite3PagerGet() and is only held open until the
+** last page is released using sqlite3PagerUnref().
+**
+** If zFilename is NULL then a randomly-named temporary file is created
+** and used as the file to be cached. The file will be deleted
+** automatically when it is closed.
+**
+** If zFilename is ":memory:" then all information is held in cache.
+** It is never written to disk. This can be used to implement an
+** in-memory database.
*/
-#define N_SORT_BUCKET_ALLOC 25
-#define N_SORT_BUCKET 25
-#ifdef SQLITE_TEST
- int sqlite3_pager_n_sort_bucket = 0;
- #undef N_SORT_BUCKET
- #define N_SORT_BUCKET \
- (sqlite3_pager_n_sort_bucket?sqlite3_pager_n_sort_bucket:N_SORT_BUCKET_ALLOC)
-#endif
-static PgHdr *sort_pagelist(PgHdr *pIn){
- PgHdr *a[N_SORT_BUCKET_ALLOC], *p;
+SQLITE_PRIVATE int sqlite3PagerOpen(
+ sqlite3_vfs *pVfs, /* The virtual file system to use */
+ Pager **ppPager, /* Return the Pager structure here */
+ const char *zFilename, /* Name of the database file to open */
+ void (*xDesc)(DbPage*), /* Page destructor function */
+ int nExtra, /* Extra bytes append to each in-memory page */
+ int flags, /* flags controlling this file */
+ int vfsFlags /* flags passed through to sqlite3_vfs.xOpen() */
+){
+ u8 *pPtr;
+ Pager *pPager = 0;
+ int rc = SQLITE_OK;
int i;
- memset(a, 0, sizeof(a));
- while( pIn ){
- p = pIn;
- pIn = p->pDirty;
- p->pDirty = 0;
- for(i=0; i<N_SORT_BUCKET-1; i++){
- if( a[i]==0 ){
- a[i] = p;
- break;
- }else{
- p = merge_pagelist(a[i], p);
- a[i] = 0;
- }
+ int tempFile = 0;
+ int memDb = 0;
+ int readOnly = 0;
+ int useJournal = (flags & PAGER_OMIT_JOURNAL)==0;
+ int noReadlock = (flags & PAGER_NO_READLOCK)!=0;
+ int journalFileSize = sqlite3JournalSize(pVfs);
+ int pcacheSize = sqlite3PcacheSize();
+ int szPageDflt = SQLITE_DEFAULT_PAGE_SIZE;
+ char *zPathname = 0;
+ int nPathname = 0;
+
+ /* The default return is a NULL pointer */
+ *ppPager = 0;
+
+ /* Compute and store the full pathname in an allocated buffer pointed
+ ** to by zPathname, length nPathname. Or, if this is a temporary file,
+ ** leave both nPathname and zPathname set to 0.
+ */
+ if( zFilename && zFilename[0] ){
+ nPathname = pVfs->mxPathname+1;
+ zPathname = sqlite3Malloc(nPathname*2);
+ if( zPathname==0 ){
+ return SQLITE_NOMEM;
}
- if( i==N_SORT_BUCKET-1 ){
- /* Coverage: To get here, there need to be 2^(N_SORT_BUCKET)
- ** elements in the input list. This is possible, but impractical.
- ** Testing this line is the point of global variable
- ** sqlite3_pager_n_sort_bucket.
- */
- a[i] = merge_pagelist(a[i], p);
+#ifndef SQLITE_OMIT_MEMORYDB
+ if( strcmp(zFilename,":memory:")==0 ){
+ memDb = 1;
+ zPathname[0] = 0;
+ }else
+#endif
+ {
+ rc = sqlite3OsFullPathname(pVfs, zFilename, nPathname, zPathname);
+ }
+ if( rc!=SQLITE_OK ){
+ sqlite3_free(zPathname);
+ return rc;
}
+ nPathname = strlen(zPathname);
}
- p = a[0];
- for(i=1; i<N_SORT_BUCKET; i++){
- p = merge_pagelist(p, a[i]);
+
+ /* Allocate memory for the pager structure */
+ pPager = sqlite3MallocZero(
+ sizeof(*pPager) + /* Pager structure */
+ pcacheSize + /* PCache object */
+ journalFileSize + /* The journal file structure */
+ pVfs->szOsFile * 3 + /* The main db and two journal files */
+ 3*nPathname + 40 /* zFilename, zDirectory, zJournal */
+ );
+ if( !pPager ){
+ sqlite3_free(zPathname);
+ return SQLITE_NOMEM;
+ }
+ pPager->pPCache = (PCache *)&pPager[1];
+ pPtr = ((u8 *)&pPager[1]) + pcacheSize;
+ pPager->vfsFlags = vfsFlags;
+ pPager->fd = (sqlite3_file*)&pPtr[pVfs->szOsFile*0];
+ pPager->stfd = (sqlite3_file*)&pPtr[pVfs->szOsFile*1];
+ pPager->jfd = (sqlite3_file*)&pPtr[pVfs->szOsFile*2];
+ pPager->zFilename = (char*)&pPtr[pVfs->szOsFile*2+journalFileSize];
+ pPager->zDirectory = &pPager->zFilename[nPathname+1];
+ pPager->zJournal = &pPager->zDirectory[nPathname+1];
+ pPager->pVfs = pVfs;
+ if( zPathname ){
+ memcpy(pPager->zFilename, zPathname, nPathname+1);
+ sqlite3_free(zPathname);
}
- return p;
-}
-/*
-** Given a list of pages (connected by the PgHdr.pDirty pointer) write
-** every one of those pages out to the database file and mark them all
-** as clean.
-*/
-static int pager_write_pagelist(PgHdr *pList){
- Pager *pPager;
- PgHdr *p;
- int rc;
+ /* Open the pager file.
+ */
+ if( zFilename && zFilename[0] && !memDb ){
+ if( nPathname>(pVfs->mxPathname - sizeof("-journal")) ){
+ rc = SQLITE_CANTOPEN;
+ }else{
+ int fout = 0;
+ rc = sqlite3OsOpen(pVfs, pPager->zFilename, pPager->fd,
+ pPager->vfsFlags, &fout);
+ readOnly = (fout&SQLITE_OPEN_READONLY);
- if( pList==0 ) return SQLITE_OK;
- pPager = pList->pPager;
+ /* If the file was successfully opened for read/write access,
+ ** choose a default page size in case we have to create the
+ ** database file. The default page size is the maximum of:
+ **
+ ** + SQLITE_DEFAULT_PAGE_SIZE,
+ ** + The value returned by sqlite3OsSectorSize()
+ ** + The largest page size that can be written atomically.
+ */
+ if( rc==SQLITE_OK && !readOnly ){
+ int iSectorSize = sqlite3OsSectorSize(pPager->fd);
+ if( szPageDflt<iSectorSize ){
+ szPageDflt = iSectorSize;
+ }
+#ifdef SQLITE_ENABLE_ATOMIC_WRITE
+ {
+ int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
+ int ii;
+ assert(SQLITE_IOCAP_ATOMIC512==(512>>8));
+ assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8));
+ assert(SQLITE_MAX_DEFAULT_PAGE_SIZE<=65536);
+ for(ii=szPageDflt; ii<=SQLITE_MAX_DEFAULT_PAGE_SIZE; ii=ii*2){
+ if( iDc&(SQLITE_IOCAP_ATOMIC|(ii>>8)) ) szPageDflt = ii;
+ }
+ }
+#endif
+ if( szPageDflt>SQLITE_MAX_DEFAULT_PAGE_SIZE ){
+ szPageDflt = SQLITE_MAX_DEFAULT_PAGE_SIZE;
+ }
+ }
+ }
+ }else if( !memDb ){
+ /* If a temporary file is requested, it is not opened immediately.
+ ** In this case we accept the default page size and delay actually
+ ** opening the file until the first call to OsWrite().
+ */
+ tempFile = 1;
+ pPager->state = PAGER_EXCLUSIVE;
+ }
- /* At this point there may be either a RESERVED or EXCLUSIVE lock on the
- ** database file. If there is already an EXCLUSIVE lock, the following
- ** calls to sqlite3OsLock() are no-ops.
- **
- ** Moving the lock from RESERVED to EXCLUSIVE actually involves going
- ** through an intermediate state PENDING. A PENDING lock prevents new
- ** readers from attaching to the database but is unsufficient for us to
- ** write. The idea of a PENDING lock is to prevent new readers from
- ** coming in while we wait for existing readers to clear.
- **
- ** While the pager is in the RESERVED state, the original database file
- ** is unchanged and we can rollback without having to playback the
- ** journal into the original database file. Once we transition to
- ** EXCLUSIVE, it means the database file has been changed and any rollback
- ** will require a journal playback.
- */
- rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
- if( rc!=SQLITE_OK ){
- return rc;
+ if( pPager && rc==SQLITE_OK ){
+ pPager->pTmpSpace = sqlite3PageMalloc(szPageDflt);
}
- pList = sort_pagelist(pList);
- for(p=pList; p; p=p->pDirty){
- assert( p->dirty );
- p->dirty = 0;
+ /* If an error occured in either of the blocks above.
+ ** Free the Pager structure and close the file.
+ ** Since the pager is not allocated there is no need to set
+ ** any Pager.errMask variables.
+ */
+ if( !pPager || !pPager->pTmpSpace ){
+ sqlite3OsClose(pPager->fd);
+ sqlite3_free(pPager);
+ return ((rc==SQLITE_OK)?SQLITE_NOMEM:rc);
}
- while( pList ){
+ nExtra = FORCE_ALIGNMENT(nExtra);
+ sqlite3PcacheOpen(szPageDflt, nExtra, !memDb, xDesc,
+ !memDb?pagerStress:0, (void *)pPager, pPager->pPCache);
- /* If the file has not yet been opened, open it now. */
- if( !pPager->fd->pMethods ){
- assert(pPager->tempFile);
- rc = sqlite3PagerOpentemp(pPager, pPager->fd, pPager->vfsFlags);
- if( rc ) return rc;
- }
+ PAGERTRACE3("OPEN %d %s\n", FILEHANDLEID(pPager->fd), pPager->zFilename);
+ IOTRACE(("OPEN %p %s\n", pPager, pPager->zFilename))
- /* If there are dirty pages in the page cache with page numbers greater
- ** than Pager.dbSize, this means sqlite3PagerTruncate() was called to
- ** make the file smaller (presumably by auto-vacuum code). Do not write
- ** any such pages to the file.
- */
- if( pList->pgno<=pPager->dbSize ){
- i64 offset = (pList->pgno-1)*(i64)pPager->pageSize;
- char *pData = CODEC2(pPager, PGHDR_TO_DATA(pList), pList->pgno, 6);
- PAGERTRACE4("STORE %d page %d hash(%08x)\n",
- PAGERID(pPager), pList->pgno, pager_pagehash(pList));
- IOTRACE(("PGOUT %p %d\n", pPager, pList->pgno));
- rc = sqlite3OsWrite(pPager->fd, pData, pPager->pageSize, offset);
- PAGER_INCR(sqlite3_pager_writedb_count);
- PAGER_INCR(pPager->nWrite);
- if( pList->pgno==1 ){
- memcpy(&pPager->dbFileVers, &pData[24], sizeof(pPager->dbFileVers));
- }
- }
-#ifndef NDEBUG
- else{
- PAGERTRACE3("NOSTORE %d page %d\n", PAGERID(pPager), pList->pgno);
- }
-#endif
- if( rc ) return rc;
-#ifdef SQLITE_CHECK_PAGES
- pList->pageHash = pager_pagehash(pList);
-#endif
- pList = pList->pDirty;
+ /* Fill in Pager.zDirectory[] */
+ memcpy(pPager->zDirectory, pPager->zFilename, nPathname+1);
+ for(i=strlen(pPager->zDirectory); i>0 && pPager->zDirectory[i-1]!='/'; i--){}
+ if( i>0 ) pPager->zDirectory[i-1] = 0;
+
+ /* Fill in Pager.zJournal[] */
+ if( zPathname ){
+ memcpy(pPager->zJournal, pPager->zFilename, nPathname);
+ memcpy(&pPager->zJournal[nPathname], "-journal", 9);
+ }else{
+ pPager->zJournal = 0;
+ }
+
+ /* pPager->journalOpen = 0; */
+ pPager->useJournal = useJournal && !memDb;
+ pPager->noReadlock = noReadlock && readOnly;
+ /* pPager->stmtOpen = 0; */
+ /* pPager->stmtInUse = 0; */
+ /* pPager->nRef = 0; */
+ pPager->dbSize = memDb-1;
+ pPager->pageSize = szPageDflt;
+ /* pPager->stmtSize = 0; */
+ /* pPager->stmtJSize = 0; */
+ /* pPager->nPage = 0; */
+ pPager->mxPage = 100;
+ pPager->mxPgno = SQLITE_MAX_PAGE_COUNT;
+ /* pPager->state = PAGER_UNLOCK; */
+ assert( pPager->state == (tempFile ? PAGER_EXCLUSIVE : PAGER_UNLOCK) );
+ /* pPager->errMask = 0; */
+ pPager->tempFile = tempFile;
+ assert( tempFile==PAGER_LOCKINGMODE_NORMAL
+ || tempFile==PAGER_LOCKINGMODE_EXCLUSIVE );
+ assert( PAGER_LOCKINGMODE_EXCLUSIVE==1 );
+ pPager->exclusiveMode = tempFile;
+ pPager->memDb = memDb;
+ pPager->readOnly = readOnly;
+ /* pPager->needSync = 0; */
+ pPager->noSync = pPager->tempFile || !useJournal;
+ pPager->fullSync = (pPager->noSync?0:1);
+ pPager->sync_flags = SQLITE_SYNC_NORMAL;
+ /* pPager->pFirst = 0; */
+ /* pPager->pFirstSynced = 0; */
+ /* pPager->pLast = 0; */
+ pPager->nExtra = nExtra;
+ pPager->journalSizeLimit = SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT;
+ assert(pPager->fd->pMethods||memDb||tempFile);
+ if( !memDb ){
+ setSectorSize(pPager);
}
+ /* pPager->pBusyHandler = 0; */
+ /* memset(pPager->aHash, 0, sizeof(pPager->aHash)); */
+ *ppPager = pPager;
return SQLITE_OK;
}
/*
-** Collect every dirty page into a dirty list and
-** return a pointer to the head of that list. All pages are
-** collected even if they are still in use.
+** Set the busy handler function.
*/
-static PgHdr *pager_get_all_dirty_pages(Pager *pPager){
-
-#ifndef NDEBUG
- /* Verify the sanity of the dirty list when we are running
- ** in debugging mode. This is expensive, so do not
- ** do this on a normal build. */
- int n1 = 0;
- int n2 = 0;
- PgHdr *p;
- for(p=pPager->pAll; p; p=p->pNextAll){ if( p->dirty ) n1++; }
- for(p=pPager->pDirty; p; p=p->pDirty){ n2++; }
- assert( n1==n2 );
-#endif
+SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(Pager *pPager, BusyHandler *pBusyHandler){
+ pPager->pBusyHandler = pBusyHandler;
+}
- return pPager->pDirty;
+/*
+** Set the reinitializer for this pager. If not NULL, the reinitializer
+** is called when the content of a page in cache is restored to its original
+** value as a result of a rollback. The callback gives higher-level code
+** an opportunity to restore the EXTRA section to agree with the restored
+** page data.
+*/
+SQLITE_PRIVATE void sqlite3PagerSetReiniter(Pager *pPager, void (*xReinit)(DbPage*,int)){
+ pPager->xReiniter = xReinit;
}
/*
-** Return 1 if there is a hot journal on the given pager.
-** A hot journal is one that needs to be played back.
-**
-** If the current size of the database file is 0 but a journal file
-** exists, that is probably an old journal left over from a prior
-** database with the same name. Just delete the journal.
-**
-** Return negative if unable to determine the status of the journal.
-**
-** This routine does not open the journal file to examine its
-** content. Hence, the journal might contain the name of a master
-** journal file that has been deleted, and hence not be hot. Or
-** the header of the journal might be zeroed out. This routine
-** does not discover these cases of a non-hot journal - if the
-** journal file exists and is not empty this routine assumes it
-** is hot. The pager_playback() routine will discover that the
-** journal file is not really hot and will no-op.
+** Set the page size to *pPageSize. If the suggest new page size is
+** inappropriate, then an alternative page size is set to that
+** value before returning.
*/
-static int hasHotJournal(Pager *pPager, int *pExists){
- sqlite3_vfs *pVfs = pPager->pVfs;
+SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager *pPager, u16 *pPageSize){
int rc = SQLITE_OK;
- *pExists = 0;
- if( pPager->useJournal && pPager->fd->pMethods ){
- int exists;
- int locked;
-
- rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists);
- if( rc==SQLITE_OK && exists ){
- rc = sqlite3OsCheckReservedLock(pPager->fd, &locked);
- }
-
- if( rc==SQLITE_OK && exists && !locked ){
- int nPage;
- rc = sqlite3PagerPagecount(pPager, &nPage);
- if( rc==SQLITE_OK ){
- if( nPage==0 ){
- sqlite3OsDelete(pVfs, pPager->zJournal, 0);
- }else{
- *pExists = 1;
- }
- }
+ u16 pageSize = *pPageSize;
+ assert( pageSize==0 || (pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE) );
+ if( pageSize && pageSize!=pPager->pageSize
+ && (pPager->memDb==0 || pPager->dbSize==0)
+ && sqlite3PcacheRefCount(pPager->pPCache)==0
+ ){
+ char *pNew = (char *)sqlite3PageMalloc(pageSize);
+ if( !pNew ){
+ rc = SQLITE_NOMEM;
+ }else{
+ pager_reset(pPager);
+ pPager->pageSize = pageSize;
+ if( !pPager->memDb ) setSectorSize(pPager);
+ sqlite3PageFree(pPager->pTmpSpace);
+ pPager->pTmpSpace = pNew;
+ sqlite3PcacheSetPageSize(pPager->pPCache, pageSize);
}
}
-
+ *pPageSize = pPager->pageSize;
return rc;
}
/*
-** Try to find a page in the cache that can be recycled.
-**
-** This routine may return SQLITE_IOERR, SQLITE_FULL or SQLITE_OK. It
-** does not set the pPager->errCode variable.
+** Return a pointer to the "temporary page" buffer held internally
+** by the pager. This is a buffer that is big enough to hold the
+** entire content of a database page. This buffer is used internally
+** during rollback and will be overwritten whenever a rollback
+** occurs. But other modules are free to use it too, as long as
+** no rollbacks are happening.
*/
-static int pager_recycle(Pager *pPager, PgHdr **ppPg){
- PgHdr *pPg;
- *ppPg = 0;
-
- /* It is illegal to call this function unless the pager object
- ** pointed to by pPager has at least one free page (page with nRef==0).
- */
- assert(!MEMDB);
- assert(pPager->lru.pFirst);
-
- /* Find a page to recycle. Try to locate a page that does not
- ** require us to do an fsync() on the journal.
- */
- pPg = pPager->lru.pFirstSynced;
+SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager *pPager){
+ return pPager->pTmpSpace;
+}
- /* If we could not find a page that does not require an fsync()
- ** on the journal file then fsync the journal file. This is a
- ** very slow operation, so we work hard to avoid it. But sometimes
- ** it can't be helped.
- */
- if( pPg==0 && pPager->lru.pFirst ){
- if( !pPager->errCode ){
- int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
- int rc = syncJournal(pPager);
- if( rc!=0 ){
- return rc;
- }
- if( pPager->fullSync && 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){
- /* If in full-sync mode, write a new journal header into the
- ** journal file. This is done to avoid ever modifying a journal
- ** header that is involved in the rollback of pages that have
- ** already been written to the database (in case the header is
- ** trashed when the nRec field is updated).
- */
- pPager->nRec = 0;
- assert( pPager->journalOff > 0 );
- assert( pPager->doNotSync==0 );
- rc = writeJournalHdr(pPager);
- if( rc!=0 ){
- return rc;
- }
- }
- }
- pPg = pPager->lru.pFirst;
+/*
+** Attempt to set the maximum database page count if mxPage is positive.
+** Make no changes if mxPage is zero or negative. And never reduce the
+** maximum page count below the current size of the database.
+**
+** Regardless of mxPage, return the current maximum page count.
+*/
+SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager *pPager, int mxPage){
+ if( mxPage>0 ){
+ pPager->mxPgno = mxPage;
}
+ sqlite3PagerPagecount(pPager, 0);
+ return pPager->mxPgno;
+}
- assert( pPg->nRef==0 );
+/*
+** The following set of routines are used to disable the simulated
+** I/O error mechanism. These routines are used to avoid simulated
+** errors in places where we do not care about errors.
+**
+** Unless -DSQLITE_TEST=1 is used, these routines are all no-ops
+** and generate no code.
+*/
+#ifdef SQLITE_TEST
+SQLITE_API extern int sqlite3_io_error_pending;
+SQLITE_API extern int sqlite3_io_error_hit;
+static int saved_cnt;
+void disable_simulated_io_errors(void){
+ saved_cnt = sqlite3_io_error_pending;
+ sqlite3_io_error_pending = -1;
+}
+void enable_simulated_io_errors(void){
+ sqlite3_io_error_pending = saved_cnt;
+}
+#else
+# define disable_simulated_io_errors()
+# define enable_simulated_io_errors()
+#endif
- /* Write the page to the database file if it is dirty.
- */
- if( pPg->dirty && !pPager->errCode ){
- int rc;
- assert( pPg->needSync==0 );
- makeClean(pPg);
- pPg->dirty = 1;
- pPg->pDirty = 0;
- rc = pager_write_pagelist( pPg );
- pPg->dirty = 0;
- if( rc!=SQLITE_OK ){
- return rc;
+/*
+** Read the first N bytes from the beginning of the file into memory
+** that pDest points to.
+**
+** No error checking is done. The rational for this is that this function
+** may be called even if the file does not exist or contain a header. In
+** these cases sqlite3OsRead() will return an error, to which the correct
+** response is to zero the memory at pDest and continue. A real IO error
+** will presumably recur and be picked up later (Todo: Think about this).
+*/
+SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager *pPager, int N, unsigned char *pDest){
+ int rc = SQLITE_OK;
+ memset(pDest, 0, N);
+ assert(MEMDB||pPager->fd->pMethods||pPager->tempFile);
+ if( pPager->fd->pMethods ){
+ IOTRACE(("DBHDR %p 0 %d\n", pPager, N))
+ rc = sqlite3OsRead(pPager->fd, pDest, N, 0);
+ if( rc==SQLITE_IOERR_SHORT_READ ){
+ rc = SQLITE_OK;
}
}
- assert( pPg->dirty==0 || pPager->errCode );
-
- /* If the page we are recycling is marked as alwaysRollback, then
- ** set the global alwaysRollback flag, thus disabling the
- ** sqlite3PagerDontRollback() optimization for the rest of this transaction.
- ** It is necessary to do this because the page marked alwaysRollback
- ** might be reloaded at a later time but at that point we won't remember
- ** that is was marked alwaysRollback. This means that all pages must
- ** be marked as alwaysRollback from here on out.
- */
- if( pPg->alwaysRollback ){
- IOTRACE(("ALWAYS_ROLLBACK %p\n", pPager))
- pPager->alwaysRollback = 1;
- }
-
- /* Unlink the old page from the free list and the hash table
- */
- unlinkPage(pPg);
- assert( pPg->pgno==0 );
-
- *ppPg = pPg;
- return SQLITE_OK;
+ return rc;
}
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
/*
-** This function is called to free superfluous dynamically allocated memory
-** held by the pager system. Memory in use by any SQLite pager allocated
-** by the current thread may be sqlite3_free()ed.
+** Return the total number of pages in the disk file associated with
+** pPager.
**
-** nReq is the number of bytes of memory required. Once this much has
-** been released, the function returns. The return value is the total number
-** of bytes of memory released.
+** If the PENDING_BYTE lies on the page directly after the end of the
+** file, then consider this page part of the file too. For example, if
+** PENDING_BYTE is byte 4096 (the first byte of page 5) and the size of the
+** file is 4096 bytes, 5 is returned instead of 4.
*/
-SQLITE_PRIVATE int sqlite3PagerReleaseMemory(int nReq){
- int nReleased = 0; /* Bytes of memory released so far */
- Pager *pPager; /* For looping over pagers */
- BusyHandler *savedBusy; /* Saved copy of the busy handler */
- int rc = SQLITE_OK;
-
- /* Acquire the memory-management mutex
- */
-#ifndef SQLITE_MUTEX_NOOP
- sqlite3_mutex *mutex; /* The MEM2 mutex */
- mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM2);
-#endif
- sqlite3_mutex_enter(mutex);
-
- /* Signal all database connections that memory management wants
- ** to have access to the pagers.
- */
- for(pPager=sqlite3PagerList; pPager; pPager=pPager->pNext){
- pPager->iInUseMM = 1;
+SQLITE_PRIVATE int sqlite3PagerPagecount(Pager *pPager, int *pnPage){
+ i64 n = 0;
+ int rc;
+ assert( pPager!=0 );
+ if( pPager->errCode ){
+ rc = pPager->errCode;
+ return rc;
}
-
- while( rc==SQLITE_OK && (nReq<0 || nReleased<nReq) ){
- PgHdr *pPg;
- PgHdr *pRecycled;
-
- /* Try to find a page to recycle that does not require a sync(). If
- ** this is not possible, find one that does require a sync().
- */
- sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU));
- pPg = sqlite3LruPageList.pFirstSynced;
- while( pPg && (pPg->needSync || pPg->pPager->iInUseDB) ){
- pPg = pPg->gfree.pNext;
+ if( pPager->dbSize>=0 ){
+ n = pPager->dbSize;
+ } else {
+ assert(pPager->fd->pMethods||pPager->tempFile);
+ if( (pPager->fd->pMethods)
+ && (rc = sqlite3OsFileSize(pPager->fd, &n))!=SQLITE_OK ){
+ pager_error(pPager, rc);
+ return rc;
}
- if( !pPg ){
- pPg = sqlite3LruPageList.pFirst;
- while( pPg && pPg->pPager->iInUseDB ){
- pPg = pPg->gfree.pNext;
- }
+ if( n>0 && n<pPager->pageSize ){
+ n = 1;
+ }else{
+ n /= pPager->pageSize;
}
- sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU));
-
- /* If pPg==0, then the block above has failed to find a page to
- ** recycle. In this case return early - no further memory will
- ** be released.
- */
- if( !pPg ) break;
-
- pPager = pPg->pPager;
- assert(!pPg->needSync || pPg==pPager->lru.pFirst);
- assert(pPg->needSync || pPg==pPager->lru.pFirstSynced);
-
- savedBusy = pPager->pBusyHandler;
- pPager->pBusyHandler = 0;
- rc = pager_recycle(pPager, &pRecycled);
- pPager->pBusyHandler = savedBusy;
- assert(pRecycled==pPg || rc!=SQLITE_OK);
- if( rc==SQLITE_OK ){
- /* We've found a page to free. At this point the page has been
- ** removed from the page hash-table, free-list and synced-list
- ** (pFirstSynced). It is still in the all pages (pAll) list.
- ** Remove it from this list before freeing.
- **
- ** Todo: Check the Pager.pStmt list to make sure this is Ok. It
- ** probably is though.
- */
- PgHdr *pTmp;
- assert( pPg );
- if( pPg==pPager->pAll ){
- assert(pPg->pPrevAll==0);
- assert(pPg->pNextAll==0 || pPg->pNextAll->pPrevAll==pPg);
- pPager->pAll = pPg->pNextAll;
- if( pPager->pAll ){
- pPager->pAll->pPrevAll = 0;
- }
- }else{
- assert(pPg->pPrevAll);
- assert(pPg->pPrevAll->pNextAll==pPg);
- pTmp = pPg->pPrevAll;
- pTmp->pNextAll = pPg->pNextAll;
- if( pTmp->pNextAll ){
- pTmp->pNextAll->pPrevAll = pTmp;
- }
- }
- nReleased += (
- sizeof(*pPg) + pPager->pageSize
- + sizeof(u32) + pPager->nExtra
- + MEMDB*sizeof(PgHistory)
- );
- IOTRACE(("PGFREE %p %d *\n", pPager, pPg->pgno));
- PAGER_INCR(sqlite3_pager_pgfree_count);
- sqlite3PageFree(pPg->pData);
- sqlite3_free(pPg);
- pPager->nPage--;
- }else{
- /* An error occured whilst writing to the database file or
- ** journal in pager_recycle(). The error is not returned to the
- ** caller of this function. Instead, set the Pager.errCode variable.
- ** The error will be returned to the user (or users, in the case
- ** of a shared pager cache) of the pager for which the error occured.
- */
- assert(
- (rc&0xff)==SQLITE_IOERR ||
- rc==SQLITE_FULL ||
- rc==SQLITE_BUSY
- );
- assert( pPager->state>=PAGER_RESERVED );
- pager_error(pPager, rc);
+ if( pPager->state!=PAGER_UNLOCK ){
+ pPager->dbSize = n;
}
}
-
- /* Clear the memory management flags and release the mutex
- */
- for(pPager=sqlite3PagerList; pPager; pPager=pPager->pNext){
- pPager->iInUseMM = 0;
+ if( n==(PENDING_BYTE/pPager->pageSize) ){
+ n++;
}
- sqlite3_mutex_leave(mutex);
-
- /* Return the number of bytes released
- */
- return nReleased;
+ if( n>pPager->mxPgno ){
+ pPager->mxPgno = n;
+ }
+ if( pnPage ){
+ *pnPage = n;
+ }
+ return SQLITE_OK;
}
-#endif /* SQLITE_ENABLE_MEMORY_MANAGEMENT */
/*
-** Read the content of page pPg out of the database file.
+** Forward declaration
*/
-static int readDbPage(Pager *pPager, PgHdr *pPg, Pgno pgno){
- int rc;
- i64 offset;
- assert( MEMDB==0 );
- assert(pPager->fd->pMethods||pPager->tempFile);
- if( !pPager->fd->pMethods ){
- return SQLITE_IOERR_SHORT_READ;
- }
- offset = (pgno-1)*(i64)pPager->pageSize;
- rc = sqlite3OsRead(pPager->fd, PGHDR_TO_DATA(pPg), pPager->pageSize, offset);
- PAGER_INCR(sqlite3_pager_readdb_count);
+static int syncJournal(Pager*);
+
+/*
+** This routine is used to truncate the cache when a database
+** is truncated. Drop from the cache all pages whose pgno is
+** larger than pPager->dbSize and is unreferenced.
+**
+** Referenced pages larger than pPager->dbSize are zeroed.
+**
+** Actually, at the point this routine is called, it would be
+** an error to have a referenced page. But rather than delete
+** that page and guarantee a subsequent segfault, it seems better
+** to zero it and hope that we error out sanely.
+*/
+static void pager_truncate_cache(Pager *pPager){
+ sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize);
+}
+
+/*
+** Try to obtain a lock on a file. Invoke the busy callback if the lock
+** is currently not available. Repeat until the busy callback returns
+** false or until the lock succeeds.
+**
+** Return SQLITE_OK on success and an error code if we cannot obtain
+** the lock.
+*/
+static int pager_wait_on_lock(Pager *pPager, int locktype){
+ int rc;
+
+ /* The OS lock values must be the same as the Pager lock values */
+ assert( PAGER_SHARED==SHARED_LOCK );
+ assert( PAGER_RESERVED==RESERVED_LOCK );
+ assert( PAGER_EXCLUSIVE==EXCLUSIVE_LOCK );
+
+ /* If the file is currently unlocked then the size must be unknown */
+ assert( pPager->state>=PAGER_SHARED || pPager->dbSize<0 || MEMDB );
+
+ if( pPager->state>=locktype ){
+ rc = SQLITE_OK;
+ }else{
+ if( pPager->pBusyHandler ) pPager->pBusyHandler->nBusy = 0;
+ do {
+ rc = sqlite3OsLock(pPager->fd, locktype);
+ }while( rc==SQLITE_BUSY && sqlite3InvokeBusyHandler(pPager->pBusyHandler) );
+ if( rc==SQLITE_OK ){
+ pPager->state = locktype;
+ IOTRACE(("LOCK %p %d\n", pPager, locktype))
+ }
+ }
+ return rc;
+}
+
+/*
+** Truncate the file to the number of pages specified.
+*/
+SQLITE_PRIVATE int sqlite3PagerTruncate(Pager *pPager, Pgno nPage){
+ int rc = SQLITE_OK;
+ assert( pPager->state>=PAGER_SHARED || MEMDB );
+
+
+ sqlite3PagerPagecount(pPager, 0);
+ if( pPager->errCode ){
+ rc = pPager->errCode;
+ }else if( nPage<(unsigned)pPager->dbSize ){
+ if( MEMDB ){
+ pPager->dbSize = nPage;
+ pager_truncate_cache(pPager);
+ }else{
+ rc = syncJournal(pPager);
+ if( rc==SQLITE_OK ){
+ /* Get an exclusive lock on the database before truncating. */
+ rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
+ }
+ if( rc==SQLITE_OK ){
+ rc = pager_truncate(pPager, nPage);
+ }
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Shutdown the page cache. Free all memory and close all files.
+**
+** If a transaction was in progress when this routine is called, that
+** transaction is rolled back. All outstanding pages are invalidated
+** and their memory is freed. Any attempt to use a page associated
+** with this page cache after this function returns will likely
+** result in a coredump.
+**
+** This function always succeeds. If a transaction is active an attempt
+** is made to roll it back. If an error occurs during the rollback
+** a hot journal may be left in the filesystem but no error is returned
+** to the caller.
+*/
+SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager){
+
+ disable_simulated_io_errors();
+ sqlite3BeginBenignMalloc();
+ pPager->errCode = 0;
+ pPager->exclusiveMode = 0;
+ pager_reset(pPager);
+ pagerUnlockAndRollback(pPager);
+ enable_simulated_io_errors();
+ sqlite3EndBenignMalloc();
+ PAGERTRACE2("CLOSE %d\n", PAGERID(pPager));
+ IOTRACE(("CLOSE %p\n", pPager))
+ if( pPager->journalOpen ){
+ sqlite3OsClose(pPager->jfd);
+ }
+ sqlite3BitvecDestroy(pPager->pInJournal);
+ sqlite3BitvecDestroy(pPager->pAlwaysRollback);
+ if( pPager->stmtOpen ){
+ sqlite3OsClose(pPager->stfd);
+ }
+ sqlite3OsClose(pPager->fd);
+ /* Temp files are automatically deleted by the OS
+ ** if( pPager->tempFile ){
+ ** sqlite3OsDelete(pPager->zFilename);
+ ** }
+ */
+
+ sqlite3PageFree(pPager->pTmpSpace);
+ sqlite3PcacheClose(pPager->pPCache);
+ sqlite3_free(pPager);
+ return SQLITE_OK;
+}
+
+#if !defined(NDEBUG) || defined(SQLITE_TEST)
+/*
+** Return the page number for the given page data.
+*/
+SQLITE_PRIVATE Pgno sqlite3PagerPagenumber(DbPage *p){
+ return p->pgno;
+}
+#endif
+
+/*
+** Increment the reference count for a page. The input pointer is
+** a reference to the page data.
+*/
+SQLITE_PRIVATE int sqlite3PagerRef(DbPage *pPg){
+ sqlite3PcacheRef(pPg);
+ return SQLITE_OK;
+}
+
+/*
+** Sync the journal. In other words, make sure all the pages that have
+** been written to the journal have actually reached the surface of the
+** disk. It is not safe to modify the original database file until after
+** the journal has been synced. If the original database is modified before
+** the journal is synced and a power failure occurs, the unsynced journal
+** data would be lost and we would be unable to completely rollback the
+** database changes. Database corruption would occur.
+**
+** This routine also updates the nRec field in the header of the journal.
+** (See comments on the pager_playback() routine for additional information.)
+** If the sync mode is FULL, two syncs will occur. First the whole journal
+** is synced, then the nRec field is updated, then a second sync occurs.
+**
+** For temporary databases, we do not care if we are able to rollback
+** after a power failure, so no sync occurs.
+**
+** If the IOCAP_SEQUENTIAL flag is set for the persistent media on which
+** the database is stored, then OsSync() is never called on the journal
+** file. In this case all that is required is to update the nRec field in
+** the journal header.
+**
+** This routine clears the needSync field of every page current held in
+** memory.
+*/
+static int syncJournal(Pager *pPager){
+ int rc = SQLITE_OK;
+
+ /* Sync the journal before modifying the main database
+ ** (assuming there is a journal and it needs to be synced.)
+ */
+ if( pPager->needSync ){
+ if( !pPager->tempFile ){
+ int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
+ assert( pPager->journalOpen );
+
+ if( 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){
+ /* Write the nRec value into the journal file header. If in
+ ** full-synchronous mode, sync the journal first. This ensures that
+ ** all data has really hit the disk before nRec is updated to mark
+ ** it as a candidate for rollback.
+ **
+ ** This is not required if the persistent media supports the
+ ** SAFE_APPEND property. Because in this case it is not possible
+ ** for garbage data to be appended to the file, the nRec field
+ ** is populated with 0xFFFFFFFF when the journal header is written
+ ** and never needs to be updated.
+ */
+ i64 jrnlOff;
+ if( pPager->fullSync && 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){
+ PAGERTRACE2("SYNC journal of %d\n", PAGERID(pPager));
+ IOTRACE(("JSYNC %p\n", pPager))
+ rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags);
+ if( rc!=0 ) return rc;
+ }
+
+ jrnlOff = pPager->journalHdr + sizeof(aJournalMagic);
+ IOTRACE(("JHDR %p %lld %d\n", pPager, jrnlOff, 4));
+ rc = write32bits(pPager->jfd, jrnlOff, pPager->nRec);
+ if( rc ) return rc;
+ }
+ if( 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){
+ PAGERTRACE2("SYNC journal of %d\n", PAGERID(pPager));
+ IOTRACE(("JSYNC %p\n", pPager))
+ rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags|
+ (pPager->sync_flags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0)
+ );
+ if( rc!=0 ) return rc;
+ }
+ pPager->journalStarted = 1;
+ }
+ pPager->needSync = 0;
+
+ /* Erase the needSync flag from every page.
+ */
+ sqlite3PcacheSetFlags(pPager->pPCache, ~PGHDR_NEED_SYNC, 0);
+ /* lruListSetFirstSynced(pPager); */
+ }
+
+#ifndef NDEBUG
+ /* If the Pager.needSync flag is clear then the PgHdr.needSync
+ ** flag must also be clear for all pages. Verify that this
+ ** invariant is true.
+ */
+ else{
+ sqlite3PcacheAssertFlags(pPager->pPCache, 0, PGHDR_NEED_SYNC);
+ /* assert( pPager->lru.pFirstSynced==pPager->lru.pFirst ); */
+ }
+#endif
+
+ return rc;
+}
+
+/*
+** Given a list of pages (connected by the PgHdr.pDirty pointer) write
+** every one of those pages out to the database file. No calls are made
+** to the page-cache to mark the pages as clean. It is the responsibility
+** of the caller to use PcacheCleanAll() or PcacheMakeClean() to mark
+** the pages as clean.
+*/
+static int pager_write_pagelist(PgHdr *pList){
+ Pager *pPager;
+ int rc;
+
+ if( pList==0 ) return SQLITE_OK;
+ pPager = pList->pPager;
+
+ /* At this point there may be either a RESERVED or EXCLUSIVE lock on the
+ ** database file. If there is already an EXCLUSIVE lock, the following
+ ** calls to sqlite3OsLock() are no-ops.
+ **
+ ** Moving the lock from RESERVED to EXCLUSIVE actually involves going
+ ** through an intermediate state PENDING. A PENDING lock prevents new
+ ** readers from attaching to the database but is unsufficient for us to
+ ** write. The idea of a PENDING lock is to prevent new readers from
+ ** coming in while we wait for existing readers to clear.
+ **
+ ** While the pager is in the RESERVED state, the original database file
+ ** is unchanged and we can rollback without having to playback the
+ ** journal into the original database file. Once we transition to
+ ** EXCLUSIVE, it means the database file has been changed and any rollback
+ ** will require a journal playback.
+ */
+ rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+
+ while( pList ){
+
+ /* If the file has not yet been opened, open it now. */
+ if( !pPager->fd->pMethods ){
+ assert(pPager->tempFile);
+ rc = sqlite3PagerOpentemp(pPager, pPager->fd, pPager->vfsFlags);
+ if( rc ) return rc;
+ }
+
+ /* If there are dirty pages in the page cache with page numbers greater
+ ** than Pager.dbSize, this means sqlite3PagerTruncate() was called to
+ ** make the file smaller (presumably by auto-vacuum code). Do not write
+ ** any such pages to the file.
+ */
+ if( pList->pgno<=pPager->dbSize && 0==(pList->flags&PGHDR_DONT_WRITE) ){
+ i64 offset = (pList->pgno-1)*(i64)pPager->pageSize;
+ char *pData = CODEC2(pPager, pList->pData, pList->pgno, 6);
+ PAGERTRACE4("STORE %d page %d hash(%08x)\n",
+ PAGERID(pPager), pList->pgno, pager_pagehash(pList));
+ IOTRACE(("PGOUT %p %d\n", pPager, pList->pgno));
+ rc = sqlite3OsWrite(pPager->fd, pData, pPager->pageSize, offset);
+ PAGER_INCR(sqlite3_pager_writedb_count);
+ PAGER_INCR(pPager->nWrite);
+ if( pList->pgno==1 ){
+ memcpy(&pPager->dbFileVers, &pData[24], sizeof(pPager->dbFileVers));
+ }
+ }
+#ifndef NDEBUG
+ else{
+ PAGERTRACE3("NOSTORE %d page %d\n", PAGERID(pPager), pList->pgno);
+ }
+#endif
+ if( rc ) return rc;
+#ifdef SQLITE_CHECK_PAGES
+ pList->pageHash = pager_pagehash(pList);
+#endif
+ /* makeClean(pList); */
+ pList = pList->pDirty;
+ }
+
+ /* sqlite3PcacheCleanAll(pPager->pPCache); */
+ return SQLITE_OK;
+}
+
+/*
+** This function is called by the pcache layer when it has reached some
+** soft memory limit. The argument is a pointer to a purgeable Pager
+** object. This function attempts to make a single dirty page that has no
+** outstanding references (if one exists) clean so that it can be recycled
+** by the pcache layer.
+*/
+static int pagerStress(void *p, PgHdr *pPg){
+ Pager *pPager = (Pager *)p;
+ int rc = SQLITE_OK;
+
+ if( pPager->doNotSync ){
+ return SQLITE_OK;
+ }
+
+ assert( pPg->flags&PGHDR_DIRTY );
+ if( pPager->errCode==SQLITE_OK ){
+ if( pPg->flags&PGHDR_NEED_SYNC ){
+ rc = syncJournal(pPager);
+ if( rc==SQLITE_OK && pPager->fullSync &&
+ !(sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND)
+ ){
+ pPager->nRec = 0;
+ rc = writeJournalHdr(pPager);
+ }
+ }
+ if( rc==SQLITE_OK ){
+ pPg->pDirty = 0;
+ rc = pager_write_pagelist(pPg);
+ }
+ if( rc!=SQLITE_OK ){
+ pager_error(pPager, rc);
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ sqlite3PcacheMakeClean(pPg);
+ }
+ return rc;
+}
+
+
+/*
+** Return 1 if there is a hot journal on the given pager.
+** A hot journal is one that needs to be played back.
+**
+** If the current size of the database file is 0 but a journal file
+** exists, that is probably an old journal left over from a prior
+** database with the same name. Just delete the journal.
+**
+** Return negative if unable to determine the status of the journal.
+**
+** This routine does not open the journal file to examine its
+** content. Hence, the journal might contain the name of a master
+** journal file that has been deleted, and hence not be hot. Or
+** the header of the journal might be zeroed out. This routine
+** does not discover these cases of a non-hot journal - if the
+** journal file exists and is not empty this routine assumes it
+** is hot. The pager_playback() routine will discover that the
+** journal file is not really hot and will no-op.
+*/
+static int hasHotJournal(Pager *pPager, int *pExists){
+ sqlite3_vfs *pVfs = pPager->pVfs;
+ int rc = SQLITE_OK;
+ int exists;
+ int locked;
+ assert( pPager!=0 );
+ assert( pPager->useJournal );
+ assert( pPager->fd->pMethods );
+ *pExists = 0;
+ rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists);
+ if( rc==SQLITE_OK && exists ){
+ rc = sqlite3OsCheckReservedLock(pPager->fd, &locked);
+ }
+ if( rc==SQLITE_OK && exists && !locked ){
+ int nPage;
+ rc = sqlite3PagerPagecount(pPager, &nPage);
+ if( rc==SQLITE_OK ){
+ if( nPage==0 ){
+ sqlite3OsDelete(pVfs, pPager->zJournal, 0);
+ }else{
+ *pExists = 1;
+ }
+ }
+ }
+ return rc;
+}
+
+/*
+** Read the content of page pPg out of the database file.
+*/
+static int readDbPage(Pager *pPager, PgHdr *pPg, Pgno pgno){
+ int rc;
+ i64 offset;
+ assert( MEMDB==0 );
+ assert(pPager->fd->pMethods||pPager->tempFile);
+ if( !pPager->fd->pMethods ){
+ return SQLITE_IOERR_SHORT_READ;
+ }
+ offset = (pgno-1)*(i64)pPager->pageSize;
+ rc = sqlite3OsRead(pPager->fd, pPg->pData, pPager->pageSize, offset);
+ PAGER_INCR(sqlite3_pager_readdb_count);
PAGER_INCR(pPager->nRead);
IOTRACE(("PGIN %p %d\n", pPager, pgno));
if( pgno==1 ){
- memcpy(&pPager->dbFileVers, &((u8*)PGHDR_TO_DATA(pPg))[24],
+ memcpy(&pPager->dbFileVers, &((u8*)pPg->pData)[24],
sizeof(pPager->dbFileVers));
}
- CODEC1(pPager, PGHDR_TO_DATA(pPg), pPg->pgno, 3);
+ CODEC1(pPager, pPg->pData, pPg->pgno, 3);
PAGERTRACE4("FETCH %d page %d hash(%08x)\n",
PAGERID(pPager), pPg->pgno, pager_pagehash(pPg));
return rc;
@@ -29583,7 +30463,9 @@
** the error. Discard the contents of the pager-cache and treat any
** open journal file as a hot-journal.
*/
- if( !MEMDB && pPager->exclusiveMode && pPager->nRef==0 && pPager->errCode ){
+ if( !MEMDB && pPager->exclusiveMode
+ && sqlite3PcacheRefCount(pPager->pPCache)==0 && pPager->errCode
+ ){
if( pPager->journalOpen ){
isErrorReset = 1;
}
@@ -29603,7 +30485,7 @@
sqlite3_vfs *pVfs = pPager->pVfs;
if( !MEMDB ){
int isHotJournal;
- assert( pPager->nRef==0 );
+ assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
if( !pPager->noReadlock ){
rc = pager_wait_on_lock(pPager, SHARED_LOCK);
if( rc!=SQLITE_OK ){
@@ -29697,7 +30579,7 @@
);
}
- if( pPager->pAll ){
+ if( sqlite3PcachePagecount(pPager->pPCache)>0 ){
/* The shared-lock has just been acquired on the database file
** and there are already pages in the cache (from a previous
** read or write transaction). Check to see if the database
@@ -29752,104 +30634,6 @@
}
/*
-** Allocate a PgHdr object. Either create a new one or reuse
-** an existing one that is not otherwise in use.
-**
-** A new PgHdr structure is created if any of the following are
-** true:
-**
-** (1) We have not exceeded our maximum allocated cache size
-** as set by the "PRAGMA cache_size" command.
-**
-** (2) There are no unused PgHdr objects available at this time.
-**
-** (3) This is an in-memory database.
-**
-** (4) There are no PgHdr objects that do not require a journal
-** file sync and a sync of the journal file is currently
-** prohibited.
-**
-** Otherwise, reuse an existing PgHdr. In other words, reuse an
-** existing PgHdr if all of the following are true:
-**
-** (1) We have reached or exceeded the maximum cache size
-** allowed by "PRAGMA cache_size".
-**
-** (2) There is a PgHdr available with PgHdr->nRef==0
-**
-** (3) We are not in an in-memory database
-**
-** (4) Either there is an available PgHdr that does not need
-** to be synced to disk or else disk syncing is currently
-** allowed.
-*/
-static int pagerAllocatePage(Pager *pPager, PgHdr **ppPg){
- int rc = SQLITE_OK;
- PgHdr *pPg;
- int nByteHdr;
-
- /* Create a new PgHdr if any of the four conditions defined
- ** above are met: */
- if( pPager->nPage<pPager->mxPage
- || pPager->lru.pFirst==0
- || MEMDB
- || (pPager->lru.pFirstSynced==0 && pPager->doNotSync)
- ){
- void *pData;
- if( pPager->nPage>=pPager->nHash ){
- pager_resize_hash_table(pPager,
- pPager->nHash<256 ? 256 : pPager->nHash*2);
- if( pPager->nHash==0 ){
- rc = SQLITE_NOMEM;
- goto pager_allocate_out;
- }
- }
- pagerLeave(pPager);
- nByteHdr = sizeof(*pPg) + sizeof(u32) + pPager->nExtra
- + MEMDB*sizeof(PgHistory);
- pPg = sqlite3Malloc( nByteHdr );
- if( pPg ){
- pData = sqlite3PageMalloc( pPager->pageSize );
- if( pData==0 ){
- sqlite3_free(pPg);
- pPg = 0;
- }
- }
- pagerEnter(pPager);
- if( pPg==0 ){
- rc = SQLITE_NOMEM;
- goto pager_allocate_out;
- }
- memset(pPg, 0, nByteHdr);
- pPg->pData = pData;
- pPg->pPager = pPager;
- pPg->pNextAll = pPager->pAll;
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
- if( pPg->pNextAll ){
- pPg->pNextAll->pPrevAll = pPg;
- }
-#endif
- pPager->pAll = pPg;
- pPager->nPage++;
- }else{
- /* Recycle an existing page with a zero ref-count. */
- rc = pager_recycle(pPager, &pPg);
- if( rc==SQLITE_BUSY ){
- rc = SQLITE_IOERR_BLOCKED;
- }
- if( rc!=SQLITE_OK ){
- goto pager_allocate_out;
- }
- assert( pPager->state>=SHARED_LOCK );
- assert(pPg);
- }
- *ppPg = pPg;
-
-pager_allocate_out:
- return rc;
-}
-
-/*
** Make sure we have the content for a page. If the page was
** previously acquired with noContent==1, then the content was
** just initialized to zeros instead of being read from disk.
@@ -29857,10 +30641,10 @@
** have it. Read it in if we do not have it already.
*/
static int pager_get_content(PgHdr *pPg){
- if( pPg->needRead ){
+ if( pPg->flags&PGHDR_NEED_READ ){
int rc = readDbPage(pPg->pPager, pPg, pPg->pgno);
if( rc==SQLITE_OK ){
- pPg->needRead = 0;
+ pPg->flags &= ~PGHDR_NEED_READ;
}else{
return rc;
}
@@ -29869,6 +30653,30 @@
}
/*
+** If the reference count has reached zero, and the pager is not in the
+** middle of a write transaction or opened in exclusive mode, unlock it.
+*/
+static void pagerUnlockIfUnused(Pager *pPager){
+ if( (sqlite3PcacheRefCount(pPager->pPCache)==0)
+ && (!pPager->exclusiveMode || pPager->journalOff>0)
+ ){
+ pagerUnlockAndRollback(pPager);
+ }
+}
+
+/*
+** Drop a page from the cache using sqlite3PcacheDrop().
+**
+** If this means there are now no pages with references to them, a rollback
+** occurs and the lock on the database is removed.
+*/
+static void pagerDropPage(DbPage *pPg){
+ Pager *pPager = pPg->pPager;
+ sqlite3PcacheDrop(pPg);
+ pagerUnlockIfUnused(pPager);
+}
+
+/*
** Acquire a page.
**
** A read lock on the disk file is obtained when the first page is acquired.
@@ -29906,10 +30714,13 @@
DbPage **ppPage, /* Write a pointer to the page here */
int noContent /* Do not bother reading content from disk if true */
){
- PgHdr *pPg;
+ PgHdr *pPg = 0;
int rc;
- assert( pPager->state==PAGER_UNLOCK || pPager->nRef>0 || pgno==1 );
+ assert( pPager->state==PAGER_UNLOCK
+ || sqlite3PcacheRefCount(pPager->pPCache)>0
+ || pgno==1
+ );
/* The maximum page number is 2^31. Return SQLITE_CORRUPT if a page
** number greater than this, or zero, is requested.
@@ -29933,84 +30744,66 @@
}
assert( pPager->state!=PAGER_UNLOCK );
- pPg = pager_lookup(pPager, pgno);
- if( pPg==0 ){
- /* The requested page is not in the page cache. */
+ rc = sqlite3PcacheFetch(pPager->pPCache, pgno, 1, &pPg);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ if( pPg->pPager==0 ){
+ /* The pager cache has created a new page. Its content needs to
+ ** be initialized.
+ */
int nMax;
- int h;
PAGER_INCR(pPager->nMiss);
- rc = pagerAllocatePage(pPager, &pPg);
- if( rc!=SQLITE_OK ){
- return rc;
+ pPg->pPager = pPager;
+ if( sqlite3BitvecTest(pPager->pInJournal, pgno) ){
+ pPg->flags |= PGHDR_IN_JOURNAL;
}
+ memset(pPg->pExtra, 0, pPager->nExtra);
- pPg->pgno = pgno;
- assert( !MEMDB || pgno>pPager->stmtSize );
- pPg->inJournal = sqlite3BitvecTest(pPager->pInJournal, pgno);
- pPg->needSync = 0;
-
- makeClean(pPg);
- pPg->nRef = 1;
-
- pPager->nRef++;
- if( pPager->nExtra>0 ){
- memset(PGHDR_TO_EXTRA(pPg, pPager), 0, pPager->nExtra);
- }
rc = sqlite3PagerPagecount(pPager, &nMax);
if( rc!=SQLITE_OK ){
sqlite3PagerUnref(pPg);
return rc;
}
- /* Populate the page with data, either by reading from the database
- ** file, or by setting the entire page to zero.
- */
- if( nMax<(int)pgno || MEMDB || (noContent && !pPager->alwaysRollback) ){
+ if( nMax<(int)pgno || MEMDB || noContent ){
if( pgno>pPager->mxPgno ){
sqlite3PagerUnref(pPg);
return SQLITE_FULL;
}
- memset(PGHDR_TO_DATA(pPg), 0, pPager->pageSize);
- pPg->needRead = noContent && !pPager->alwaysRollback;
+ memset(pPg->pData, 0, pPager->pageSize);
+ if( noContent ){
+ pPg->flags |= PGHDR_NEED_READ;
+ }
IOTRACE(("ZERO %p %d\n", pPager, pgno));
}else{
rc = readDbPage(pPager, pPg, pgno);
if( rc!=SQLITE_OK && rc!=SQLITE_IOERR_SHORT_READ ){
- pPg->pgno = 0;
- sqlite3PagerUnref(pPg);
+ /* sqlite3PagerUnref(pPg); */
+ pagerDropPage(pPg);
return rc;
}
- pPg->needRead = 0;
- }
-
- /* Link the page into the page hash table */
- h = pgno & (pPager->nHash-1);
- assert( pgno!=0 );
- pPg->pNextHash = pPager->aHash[h];
- pPager->aHash[h] = pPg;
- if( pPg->pNextHash ){
- assert( pPg->pNextHash->pPrevHash==0 );
- pPg->pNextHash->pPrevHash = pPg;
}
-
#ifdef SQLITE_CHECK_PAGES
pPg->pageHash = pager_pagehash(pPg);
#endif
}else{
/* The requested page is in the page cache. */
- assert(pPager->nRef>0 || pgno==1);
+ assert(sqlite3PcacheRefCount(pPager->pPCache)>0 || pgno==1);
PAGER_INCR(pPager->nHit);
if( !noContent ){
rc = pager_get_content(pPg);
if( rc ){
+ sqlite3PagerUnref(pPg);
return rc;
}
}
- page_ref(pPg);
}
+
*ppPage = pPg;
return SQLITE_OK;
}
+
SQLITE_PRIVATE int sqlite3PagerAcquire(
Pager *pPager, /* The pager open on the database file */
Pgno pgno, /* Page number to fetch */
@@ -30018,9 +30811,7 @@
int noContent /* Do not bother reading content from disk if true */
){
int rc;
- pagerEnter(pPager);
rc = pagerAcquire(pPager, pgno, ppPage, noContent);
- pagerLeave(pPager);
return rc;
}
@@ -30038,19 +30829,15 @@
*/
SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){
PgHdr *pPg = 0;
-
assert( pPager!=0 );
assert( pgno!=0 );
- pagerEnter(pPager);
- if( pPager->state==PAGER_UNLOCK ){
- assert( !pPager->pAll || pPager->exclusiveMode );
- }else if( pPager->errCode && pPager->errCode!=SQLITE_FULL ){
- /* Do nothing */
- }else if( (pPg = pager_lookup(pPager, pgno))!=0 ){
- page_ref(pPg);
+ if( (pPager->state!=PAGER_UNLOCK)
+ && (pPager->errCode==SQLITE_OK || pPager->errCode==SQLITE_FULL)
+ ){
+ sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &pPg);
}
- pagerLeave(pPager);
+
return pPg;
}
@@ -30063,39 +30850,11 @@
** removed.
*/
SQLITE_PRIVATE int sqlite3PagerUnref(DbPage *pPg){
- Pager *pPager;
-
- if( pPg==0 ) return SQLITE_OK;
- pPager = pPg->pPager;
-
- /* Decrement the reference count for this page
- */
- assert( pPg->nRef>0 );
- pagerEnter(pPg->pPager);
- pPg->nRef--;
-
- CHECK_PAGE(pPg);
-
- /* When the number of references to a page reach 0, call the
- ** destructor and add the page to the freelist.
- */
- if( pPg->nRef==0 ){
-
- lruListAdd(pPg);
- if( pPager->xDestructor ){
- pPager->xDestructor(pPg, pPager->pageSize);
- }
-
- /* When all pages reach the freelist, drop the read lock from
- ** the database file.
- */
- pPager->nRef--;
- assert( pPager->nRef>=0 );
- if( pPager->nRef==0 && (!pPager->exclusiveMode || pPager->journalOff>0) ){
- pagerUnlockAndRollback(pPager);
- }
+ if( pPg ){
+ Pager *pPager = pPg->pPager;
+ sqlite3PcacheRelease(pPg);
+ pagerUnlockIfUnused(pPager);
}
- pagerLeave(pPager);
return SQLITE_OK;
}
@@ -30116,9 +30875,7 @@
assert( pPager->useJournal );
assert( pPager->pInJournal==0 );
sqlite3PagerPagecount(pPager, 0);
- pagerLeave(pPager);
pPager->pInJournal = sqlite3BitvecCreate(pPager->dbSize);
- pagerEnter(pPager);
if( pPager->pInJournal==0 ){
rc = SQLITE_NOMEM;
goto failed_to_open_journal;
@@ -30151,7 +30908,6 @@
pPager->journalOpen = 1;
pPager->journalStarted = 0;
pPager->needSync = 0;
- pPager->alwaysRollback = 0;
pPager->nRec = 0;
if( pPager->errCode ){
rc = pPager->errCode;
@@ -30208,11 +30964,11 @@
SQLITE_PRIVATE int sqlite3PagerBegin(DbPage *pPg, int exFlag){
Pager *pPager = pPg->pPager;
int rc = SQLITE_OK;
- pagerEnter(pPager);
assert( pPg->nRef>0 );
assert( pPager->state!=PAGER_UNLOCK );
if( pPager->state==PAGER_SHARED ){
assert( pPager->pInJournal==0 );
+ sqlite3PcacheAssertFlags(pPager->pPCache, 0, PGHDR_IN_JOURNAL);
if( MEMDB ){
pPager->state = PAGER_EXCLUSIVE;
pPager->origDbSize = pPager->dbSize;
@@ -30225,7 +30981,6 @@
}
}
if( rc!=SQLITE_OK ){
- pagerLeave(pPager);
return rc;
}
pPager->dirtyCache = 0;
@@ -30246,9 +31001,7 @@
assert( pPager->origDbSize==0 );
assert( pPager->pInJournal==0 );
sqlite3PagerPagecount(pPager, 0);
- pagerLeave(pPager);
pPager->pInJournal = sqlite3BitvecCreate( pPager->dbSize );
- pagerEnter(pPager);
if( !pPager->pInJournal ){
rc = SQLITE_NOMEM;
}else{
@@ -30257,7 +31010,6 @@
}
}
assert( !pPager->journalOpen || pPager->journalOff>0 || rc!=SQLITE_OK );
- pagerLeave(pPager);
return rc;
}
@@ -30266,16 +31018,7 @@
** page list.
*/
static void makeDirty(PgHdr *pPg){
- if( pPg->dirty==0 ){
- Pager *pPager = pPg->pPager;
- pPg->dirty = 1;
- pPg->pDirty = pPager->pDirty;
- if( pPager->pDirty ){
- pPager->pDirty->pPrevDirty = pPg;
- }
- pPg->pPrevDirty = 0;
- pPager->pDirty = pPg;
- }
+ sqlite3PcacheMakeDirty(pPg);
}
/*
@@ -30283,20 +31026,7 @@
** dirty page list.
*/
static void makeClean(PgHdr *pPg){
- if( pPg->dirty ){
- pPg->dirty = 0;
- if( pPg->pDirty ){
- assert( pPg->pDirty->pPrevDirty==pPg );
- pPg->pDirty->pPrevDirty = pPg->pPrevDirty;
- }
- if( pPg->pPrevDirty ){
- assert( pPg->pPrevDirty->pDirty==pPg );
- pPg->pPrevDirty->pDirty = pPg->pDirty;
- }else{
- assert( pPg->pPager->pDirty==pPg );
- pPg->pPager->pDirty = pPg->pDirty;
- }
- }
+ sqlite3PcacheMakeClean(pPg);
}
@@ -30318,7 +31048,7 @@
** reset.
*/
static int pager_write(PgHdr *pPg){
- void *pData = PGHDR_TO_DATA(pPg);
+ void *pData = pPg->pData;
Pager *pPager = pPg->pPager;
int rc = SQLITE_OK;
@@ -30352,7 +31082,9 @@
** to the journal then we can return right away.
*/
makeDirty(pPg);
- if( pPg->inJournal && (pageInStatement(pPg) || pPager->stmtInUse==0) ){
+ if( (pPg->flags&PGHDR_IN_JOURNAL)
+ && (pageInStatement(pPg) || pPager->stmtInUse==0)
+ ){
pPager->dirtyCache = 1;
pPager->dbModified = 1;
}else{
@@ -30382,17 +31114,14 @@
** EXCLUSIVE lock on the main database file. Write the current page to
** the transaction journal if it is not there already.
*/
- if( !pPg->inJournal && (pPager->journalOpen || MEMDB) ){
+ if( !(pPg->flags&PGHDR_IN_JOURNAL) && (pPager->journalOpen || MEMDB) ){
if( (int)pPg->pgno <= pPager->origDbSize ){
if( MEMDB ){
- PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager);
PAGERTRACE3("JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno);
- assert( pHist->pOrig==0 );
- pHist->pOrig = sqlite3PageMalloc( pPager->pageSize );
- if( !pHist->pOrig ){
- return SQLITE_NOMEM;
+ rc = sqlite3PcachePreserve(pPg, 0);
+ if( rc!=SQLITE_OK ){
+ return rc;
}
- memcpy(pHist->pOrig, PGHDR_TO_DATA(pPg), pPager->pageSize);
}else{
u32 cksum;
char *pData2;
@@ -30417,7 +31146,8 @@
pPager->journalOff, pPager->pageSize));
PAGER_INCR(sqlite3_pager_writej_count);
PAGERTRACE5("JOURNAL %d page %d needSync=%d hash(%08x)\n",
- PAGERID(pPager), pPg->pgno, pPg->needSync, pager_pagehash(pPg));
+ PAGERID(pPager), pPg->pgno,
+ ((pPg->flags&PGHDR_NEED_SYNC)?1:0), pager_pagehash(pPg));
/* An error has occured writing to the journal file. The
** transaction will be rolled back by the layer above.
@@ -30429,20 +31159,25 @@
pPager->nRec++;
assert( pPager->pInJournal!=0 );
sqlite3BitvecSet(pPager->pInJournal, pPg->pgno);
- pPg->needSync = !pPager->noSync;
+ if( !pPager->noSync ){
+ pPg->flags |= PGHDR_NEED_SYNC;
+ }
if( pPager->stmtInUse ){
sqlite3BitvecSet(pPager->pInStmt, pPg->pgno);
}
}
}else{
- pPg->needSync = !pPager->journalStarted && !pPager->noSync;
+ if( !pPager->journalStarted && !pPager->noSync ){
+ pPg->flags |= PGHDR_NEED_SYNC;
+ }
PAGERTRACE4("APPEND %d page %d needSync=%d\n",
- PAGERID(pPager), pPg->pgno, pPg->needSync);
+ PAGERID(pPager), pPg->pgno,
+ ((pPg->flags&PGHDR_NEED_SYNC)?1:0));
}
- if( pPg->needSync ){
+ if( pPg->flags&PGHDR_NEED_SYNC ){
pPager->needSync = 1;
}
- pPg->inJournal = 1;
+ pPg->flags |= PGHDR_IN_JOURNAL;
}
/* If the statement journal is open and the page is not in it,
@@ -30454,16 +31189,14 @@
&& !pageInStatement(pPg)
&& (int)pPg->pgno<=pPager->stmtSize
){
- assert( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize );
+ assert( (pPg->flags&PGHDR_IN_JOURNAL)
+ || (int)pPg->pgno>pPager->origDbSize );
if( MEMDB ){
- PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager);
- assert( pHist->pStmt==0 );
- pHist->pStmt = sqlite3PageMalloc( pPager->pageSize );
- if( pHist->pStmt ){
- memcpy(pHist->pStmt, PGHDR_TO_DATA(pPg), pPager->pageSize);
+ rc = sqlite3PcachePreserve(pPg, 1);
+ if( rc!=SQLITE_OK ){
+ return rc;
}
PAGERTRACE3("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno);
- page_add_to_stmt_list(pPg);
}else{
i64 offset = pPager->stmtNRec*(4+pPager->pageSize);
char *pData2 = CODEC2(pPager, pData, pPg->pgno, 7);
@@ -30511,7 +31244,6 @@
Pager *pPager = pPg->pPager;
Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize);
- pagerEnter(pPager);
if( !MEMDB && nPagePerSector>1 ){
Pgno nPageCount; /* Total number of pages in database file */
Pgno pg1; /* First page of the sector pPg is located on. */
@@ -30551,16 +31283,17 @@
rc = sqlite3PagerGet(pPager, pg, &pPage);
if( rc==SQLITE_OK ){
rc = pager_write(pPage);
- if( pPage->needSync ){
+ if( pPage->flags&PGHDR_NEED_SYNC ){
needSync = 1;
}
sqlite3PagerUnref(pPage);
}
}
}else if( (pPage = pager_lookup(pPager, pg))!=0 ){
- if( pPage->needSync ){
+ if( pPage->flags&PGHDR_NEED_SYNC ){
needSync = 1;
}
+ sqlite3PagerUnref(pPage);
}
}
@@ -30573,7 +31306,8 @@
if( needSync ){
for(ii=0; ii<nPage && needSync; ii++){
PgHdr *pPage = pager_lookup(pPager, pg1+ii);
- if( pPage ) pPage->needSync = 1;
+ if( pPage ) pPage->flags |= PGHDR_NEED_SYNC;
+ sqlite3PagerUnref(pPage);
}
assert(pPager->needSync);
}
@@ -30583,7 +31317,6 @@
}else{
rc = pager_write(pDbPage);
}
- pagerLeave(pPager);
return rc;
}
@@ -30594,7 +31327,7 @@
*/
#ifndef NDEBUG
SQLITE_PRIVATE int sqlite3PagerIswriteable(DbPage *pPg){
- return pPg->dirty;
+ return pPg->flags&PGHDR_DIRTY;
}
#endif
@@ -30622,14 +31355,24 @@
** page contains critical data, we still need to be sure it gets
** rolled back in spite of the sqlite3PagerDontRollback() call.
*/
-SQLITE_PRIVATE void sqlite3PagerDontWrite(DbPage *pDbPage){
+SQLITE_PRIVATE int sqlite3PagerDontWrite(DbPage *pDbPage){
PgHdr *pPg = pDbPage;
Pager *pPager = pPg->pPager;
+ int rc;
+
+ if( MEMDB || pPg->pgno>pPager->origDbSize ){
+ return SQLITE_OK;
+ }
+ if( pPager->pAlwaysRollback==0 ){
+ assert( pPager->pInJournal );
+ pPager->pAlwaysRollback = sqlite3BitvecCreate(pPager->origDbSize);
+ if( !pPager->pAlwaysRollback ){
+ return SQLITE_NOMEM;
+ }
+ }
+ rc = sqlite3BitvecSet(pPager->pAlwaysRollback, pPg->pgno);
- if( MEMDB ) return;
- pagerEnter(pPager);
- pPg->alwaysRollback = 1;
- if( pPg->dirty && !pPager->stmtInUse ){
+ if( rc==SQLITE_OK && (pPg->flags&PGHDR_DIRTY) && !pPager->stmtInUse ){
assert( pPager->state>=PAGER_SHARED );
if( pPager->dbSize==(int)pPg->pgno && pPager->origDbSize<pPager->dbSize ){
/* If this pages is the last page in the file and the file has grown
@@ -30643,13 +31386,13 @@
}else{
PAGERTRACE3("DONT_WRITE page %d of %d\n", pPg->pgno, PAGERID(pPager));
IOTRACE(("CLEAN %p %d\n", pPager, pPg->pgno))
- makeClean(pPg);
+ pPg->flags |= PGHDR_DONT_WRITE;
#ifdef SQLITE_CHECK_PAGES
pPg->pageHash = pager_pagehash(pPg);
#endif
}
}
- pagerLeave(pPager);
+ return rc;
}
/*
@@ -30666,21 +31409,22 @@
SQLITE_PRIVATE void sqlite3PagerDontRollback(DbPage *pPg){
Pager *pPager = pPg->pPager;
- pagerEnter(pPager);
assert( pPager->state>=PAGER_RESERVED );
/* If the journal file is not open, or DontWrite() has been called on
** this page (DontWrite() sets the alwaysRollback flag), then this
** function is a no-op.
*/
- if( pPager->journalOpen==0 || pPg->alwaysRollback || pPager->alwaysRollback ){
- pagerLeave(pPager);
+ if( pPager->journalOpen==0
+ || sqlite3BitvecTest(pPager->pAlwaysRollback, pPg->pgno)
+ || pPg->pgno>pPager->origDbSize
+ ){
return;
}
assert( !MEMDB ); /* For a memdb, pPager->journalOpen is always 0 */
#ifdef SQLITE_SECURE_DELETE
- if( pPg->inJournal || (int)pPg->pgno > pPager->origDbSize ){
+ if( (pPg->flags & PGHDR_IN_JOURNAL)!=0 || (int)pPg->pgno>pPager->origDbSize ){
return;
}
#endif
@@ -30699,15 +31443,14 @@
assert( pPager->pInJournal!=0 );
sqlite3BitvecSet(pPager->pInJournal, pPg->pgno);
- pPg->inJournal = 1;
- pPg->needRead = 0;
+ pPg->flags |= PGHDR_IN_JOURNAL;
+ pPg->flags &= ~PGHDR_NEED_READ;
if( pPager->stmtInUse ){
assert( pPager->stmtSize >= pPager->origDbSize );
sqlite3BitvecSet(pPager->pInStmt, pPg->pgno);
}
PAGERTRACE3("DONT_ROLLBACK page %d of %d\n", pPg->pgno, PAGERID(pPager));
IOTRACE(("GARBAGE %p %d\n", pPager, pPg->pgno))
- pagerLeave(pPager);
}
@@ -30739,11 +31482,11 @@
/* Increment the value just read and write it back to byte 24. */
change_counter = sqlite3Get4byte((u8*)pPager->dbFileVers);
change_counter++;
- put32bits(((char*)PGHDR_TO_DATA(pPgHdr))+24, change_counter);
+ put32bits(((char*)pPgHdr->pData)+24, change_counter);
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
if( isDirect && pPager->fd->pMethods ){
- const void *zBuf = PGHDR_TO_DATA(pPgHdr);
+ const void *zBuf = pPgHdr->pData;
rc = sqlite3OsWrite(pPager->fd, zBuf, pPager->pageSize, 0);
}
#endif
@@ -30760,9 +31503,11 @@
*/
SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager){
int rc;
- pagerEnter(pPager);
- rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
- pagerLeave(pPager);
+ if( MEMDB ){
+ rc = SQLITE_OK;
+ }else{
+ rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
+ }
return rc;
}
@@ -30811,7 +31556,6 @@
PAGERTRACE4("DATABASE SYNC: File=%s zMaster=%s nTrunc=%d\n",
pPager->zFilename, zMaster, nTrunc);
- pagerEnter(pPager);
/* If this is an in-memory db, or no pages have been written to, or this
** function has already been called, it is a no-op.
@@ -30831,12 +31575,13 @@
** If the optimization can be used, then the journal file will never
** be created for this transaction.
*/
+ pPg = sqlite3PcacheDirtyList(pPager->pPCache);
int useAtomicWrite = (
!zMaster &&
pPager->journalOpen &&
pPager->journalOff==jrnlBufferSize(pPager) &&
nTrunc==0 &&
- (0==pPager->pDirty || 0==pPager->pDirty->pDirty)
+ (pPg==0 || pPg->pDirty==0)
);
assert( pPager->journalOpen || pPager->journalMode==PAGER_JOURNALMODE_OFF );
if( useAtomicWrite ){
@@ -30905,7 +31650,7 @@
#endif
/* Write all dirty pages to the database file */
- pPg = pager_get_all_dirty_pages(pPager);
+ pPg = sqlite3PcacheDirtyList(pPager->pPCache);
rc = pager_write_pagelist(pPg);
if( rc!=SQLITE_OK ){
assert( rc!=SQLITE_IOERR_BLOCKED );
@@ -30918,7 +31663,7 @@
*/
goto sync_exit;
}
- pPager->pDirty = 0;
+ sqlite3PcacheCleanAll(pPager->pPCache);
/* Sync the database file. */
if( !pPager->noSync && !noSync ){
@@ -30940,7 +31685,6 @@
*/
rc = SQLITE_BUSY;
}
- pagerLeave(pPager);
return rc;
}
@@ -30953,8 +31697,7 @@
** is returned.
*/
SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager *pPager){
- int rc;
- PgHdr *pPg;
+ int rc = SQLITE_OK;
if( pPager->errCode ){
return pPager->errCode;
@@ -30968,38 +31711,19 @@
assert( pPager->dirtyCache==0 || pPager->journalOpen==0 );
return SQLITE_OK;
}
- pagerEnter(pPager);
PAGERTRACE2("COMMIT %d\n", PAGERID(pPager));
if( MEMDB ){
- pPg = pager_get_all_dirty_pages(pPager);
- while( pPg ){
- PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager);
- clearHistory(pHist);
- pPg->dirty = 0;
- pPg->inJournal = 0;
- pHist->inStmt = 0;
- pPg->needSync = 0;
- pHist->pPrevStmt = pHist->pNextStmt = 0;
- pPg = pPg->pDirty;
- }
- pPager->pDirty = 0;
-#ifndef NDEBUG
- for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
- PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager);
- assert( !pPg->alwaysRollback );
- assert( !pHist->pOrig );
- assert( !pHist->pStmt );
- }
-#endif
- pPager->pStmt = 0;
+ sqlite3PcacheCommit(pPager->pPCache, 0);
+ sqlite3PcacheCleanAll(pPager->pPCache);
+ sqlite3PcacheSetFlags(pPager->pPCache,
+ ~(PGHDR_IN_JOURNAL | PGHDR_NEED_SYNC), 0
+ );
pPager->state = PAGER_SHARED;
- pagerLeave(pPager);
- return SQLITE_OK;
+ }else{
+ assert( pPager->state==PAGER_SYNCED || !pPager->dirtyCache );
+ rc = pager_end_transaction(pPager, pPager->setMaster);
+ rc = pager_error(pPager, rc);
}
- assert( pPager->state==PAGER_SYNCED || !pPager->dirtyCache );
- rc = pager_end_transaction(pPager, pPager->setMaster);
- rc = pager_error(pPager, rc);
- pagerLeave(pPager);
return rc;
}
@@ -31016,77 +31740,46 @@
** SQLITE_OK is returned.
*/
SQLITE_PRIVATE int sqlite3PagerRollback(Pager *pPager){
- int rc;
+ int rc = SQLITE_OK;
PAGERTRACE2("ROLLBACK %d\n", PAGERID(pPager));
if( MEMDB ){
- PgHdr *p;
- for(p=pPager->pAll; p; p=p->pNextAll){
- PgHistory *pHist;
- assert( !p->alwaysRollback );
- if( !p->dirty ){
- assert( !((PgHistory *)PGHDR_TO_HIST(p, pPager))->pOrig );
- assert( !((PgHistory *)PGHDR_TO_HIST(p, pPager))->pStmt );
- continue;
- }
-
- pHist = PGHDR_TO_HIST(p, pPager);
- if( pHist->pOrig ){
- memcpy(PGHDR_TO_DATA(p), pHist->pOrig, pPager->pageSize);
- PAGERTRACE3("ROLLBACK-PAGE %d of %d\n", p->pgno, PAGERID(pPager));
- }else{
- PAGERTRACE3("PAGE %d is clean on %d\n", p->pgno, PAGERID(pPager));
- }
- clearHistory(pHist);
- p->dirty = 0;
- p->inJournal = 0;
- pHist->inStmt = 0;
- pHist->pPrevStmt = pHist->pNextStmt = 0;
- if( pPager->xReiniter ){
- pPager->xReiniter(p, pPager->pageSize);
- }
- }
- pPager->pDirty = 0;
- pPager->pStmt = 0;
+ sqlite3PcacheRollback(pPager->pPCache, 1);
+ sqlite3PcacheRollback(pPager->pPCache, 0);
+ sqlite3PcacheCleanAll(pPager->pPCache);
+ sqlite3PcacheSetFlags(pPager->pPCache,
+ ~(PGHDR_IN_JOURNAL | PGHDR_NEED_SYNC), 0
+ );
pPager->dbSize = pPager->origDbSize;
pager_truncate_cache(pPager);
pPager->stmtInUse = 0;
pPager->state = PAGER_SHARED;
- return SQLITE_OK;
- }
-
- pagerEnter(pPager);
- if( !pPager->dirtyCache || !pPager->journalOpen ){
+ }else if( !pPager->dirtyCache || !pPager->journalOpen ){
rc = pager_end_transaction(pPager, pPager->setMaster);
- pagerLeave(pPager);
- return rc;
- }
-
- if( pPager->errCode && pPager->errCode!=SQLITE_FULL ){
+ }else if( pPager->errCode && pPager->errCode!=SQLITE_FULL ){
if( pPager->state>=PAGER_EXCLUSIVE ){
pager_playback(pPager, 0);
}
- pagerLeave(pPager);
- return pPager->errCode;
- }
- if( pPager->state==PAGER_RESERVED ){
- int rc2;
- rc = pager_playback(pPager, 0);
- rc2 = pager_end_transaction(pPager, pPager->setMaster);
- if( rc==SQLITE_OK ){
- rc = rc2;
- }
+ rc = pPager->errCode;
}else{
- rc = pager_playback(pPager, 0);
- }
- /* pager_reset(pPager); */
- pPager->dbSize = -1;
+ if( pPager->state==PAGER_RESERVED ){
+ int rc2;
+ rc = pager_playback(pPager, 0);
+ rc2 = pager_end_transaction(pPager, pPager->setMaster);
+ if( rc==SQLITE_OK ){
+ rc = rc2;
+ }
+ }else{
+ rc = pager_playback(pPager, 0);
+ }
- /* If an error occurs during a ROLLBACK, we can no longer trust the pager
- ** cache. So call pager_error() on the way out to make any error
- ** persistent.
- */
- rc = pager_error(pPager, rc);
- pagerLeave(pPager);
+ pPager->dbSize = -1;
+
+ /* If an error occurs during a ROLLBACK, we can no longer trust the pager
+ ** cache. So call pager_error() on the way out to make any error
+ ** persistent.
+ */
+ rc = pager_error(pPager, rc);
+ }
return rc;
}
@@ -31102,7 +31795,7 @@
** Return the number of references to the pager.
*/
SQLITE_PRIVATE int sqlite3PagerRefcount(Pager *pPager){
- return pPager->nRef;
+ return sqlite3PcacheRefCount(pPager->pPCache);
}
#ifdef SQLITE_TEST
@@ -31111,9 +31804,9 @@
*/
SQLITE_PRIVATE int *sqlite3PagerStats(Pager *pPager){
static int a[11];
- a[0] = pPager->nRef;
- a[1] = pPager->nPage;
- a[2] = pPager->mxPage;
+ a[0] = sqlite3PcacheRefCount(pPager->pPCache);
+ a[1] = sqlite3PcachePagecount(pPager->pPCache);
+ a[2] = sqlite3PcacheGetCachesize(pPager->pPCache);
a[3] = pPager->dbSize;
a[4] = pPager->state;
a[5] = pPager->errCode;
@@ -31152,10 +31845,8 @@
return SQLITE_OK;
}
assert( pPager->journalOpen );
- pagerLeave(pPager);
assert( pPager->pInStmt==0 );
pPager->pInStmt = sqlite3BitvecCreate(pPager->dbSize);
- pagerEnter(pPager);
if( pPager->pInStmt==0 ){
/* sqlite3OsLock(pPager->fd, SHARED_LOCK); */
return SQLITE_NOMEM;
@@ -31184,9 +31875,7 @@
}
SQLITE_PRIVATE int sqlite3PagerStmtBegin(Pager *pPager){
int rc;
- pagerEnter(pPager);
rc = pagerStmtBegin(pPager);
- pagerLeave(pPager);
return rc;
}
@@ -31194,31 +31883,19 @@
** Commit a statement.
*/
SQLITE_PRIVATE int sqlite3PagerStmtCommit(Pager *pPager){
- pagerEnter(pPager);
if( pPager->stmtInUse ){
- PgHdr *pPg, *pNext;
PAGERTRACE2("STMT-COMMIT %d\n", PAGERID(pPager));
if( !MEMDB ){
/* sqlite3OsTruncate(pPager->stfd, 0); */
sqlite3BitvecDestroy(pPager->pInStmt);
pPager->pInStmt = 0;
}else{
- for(pPg=pPager->pStmt; pPg; pPg=pNext){
- PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager);
- pNext = pHist->pNextStmt;
- assert( pHist->inStmt );
- pHist->inStmt = 0;
- pHist->pPrevStmt = pHist->pNextStmt = 0;
- sqlite3PageFree(pHist->pStmt);
- pHist->pStmt = 0;
- }
+ sqlite3PcacheCommit(pPager->pPCache, 1);
}
pPager->stmtNRec = 0;
pPager->stmtInUse = 0;
- pPager->pStmt = 0;
}
pPager->stmtAutoopen = 0;
- pagerLeave(pPager);
return SQLITE_OK;
}
@@ -31227,20 +31904,10 @@
*/
SQLITE_PRIVATE int sqlite3PagerStmtRollback(Pager *pPager){
int rc;
- pagerEnter(pPager);
if( pPager->stmtInUse ){
PAGERTRACE2("STMT-ROLLBACK %d\n", PAGERID(pPager));
if( MEMDB ){
- PgHdr *pPg;
- PgHistory *pHist;
- for(pPg=pPager->pStmt; pPg; pPg=pHist->pNextStmt){
- pHist = PGHDR_TO_HIST(pPg, pPager);
- if( pHist->pStmt ){
- memcpy(PGHDR_TO_DATA(pPg), pHist->pStmt, pPager->pageSize);
- sqlite3PageFree(pHist->pStmt);
- pHist->pStmt = 0;
- }
- }
+ sqlite3PcacheRollback(pPager->pPCache, 1);
pPager->dbSize = pPager->stmtSize;
pager_truncate_cache(pPager);
rc = SQLITE_OK;
@@ -31252,7 +31919,6 @@
rc = SQLITE_OK;
}
pPager->stmtAutoopen = 0;
- pagerLeave(pPager);
return rc;
}
@@ -31340,14 +32006,12 @@
*/
SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
PgHdr *pPgOld; /* The page being overwritten. */
- int h;
Pgno needSyncPgno = 0;
- pagerEnter(pPager);
assert( pPg->nRef>0 );
PAGERTRACE5("MOVE %d page %d (needSync=%d) moves to %d\n",
- PAGERID(pPager), pPg->pgno, pPg->needSync, pgno);
+ PAGERID(pPager), pPg->pgno, (pPg->flags&PGHDR_NEED_SYNC)?1:0, pgno);
IOTRACE(("MOVE %p %d %d\n", pPager, pPg->pgno, pgno))
pager_get_content(pPg);
@@ -31359,44 +32023,33 @@
** the journal needs to be sync()ed before database page pPg->pgno
** can be written to. The caller has already promised not to write to it.
*/
- if( pPg->needSync && !isCommit ){
+ if( (pPg->flags&PGHDR_NEED_SYNC) && !isCommit ){
needSyncPgno = pPg->pgno;
- assert( pPg->inJournal || (int)pgno>pPager->origDbSize );
- assert( pPg->dirty );
+ assert( (pPg->flags&PGHDR_IN_JOURNAL) || (int)pgno>pPager->origDbSize );
+ assert( pPg->flags&PGHDR_DIRTY );
assert( pPager->needSync );
}
- /* Unlink pPg from its hash-chain */
- unlinkHashChain(pPager, pPg);
-
/* If the cache contains a page with page-number pgno, remove it
** from its hash chain. Also, if the PgHdr.needSync was set for
** page pgno before the 'move' operation, it needs to be retained
** for the page moved there.
*/
- pPg->needSync = 0;
+ pPg->flags &= ~(PGHDR_NEED_SYNC|PGHDR_IN_JOURNAL);
pPgOld = pager_lookup(pPager, pgno);
+ assert( !pPgOld || pPgOld->nRef==1 );
if( pPgOld ){
- assert( pPgOld->nRef==0 );
- unlinkHashChain(pPager, pPgOld);
- makeClean(pPgOld);
- pPg->needSync = pPgOld->needSync;
- }else{
- pPg->needSync = 0;
+ pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC);
+ }
+ if( sqlite3BitvecTest(pPager->pInJournal, pgno) ){
+ pPg->flags |= PGHDR_IN_JOURNAL;
}
- pPg->inJournal = sqlite3BitvecTest(pPager->pInJournal, pgno);
- /* Change the page number for pPg and insert it into the new hash-chain. */
- assert( pgno!=0 );
- pPg->pgno = pgno;
- h = pgno & (pPager->nHash-1);
- if( pPager->aHash[h] ){
- assert( pPager->aHash[h]->pPrevHash==0 );
- pPager->aHash[h]->pPrevHash = pPg;
- }
- pPg->pNextHash = pPager->aHash[h];
- pPager->aHash[h] = pPg;
- pPg->pPrevHash = 0;
+ sqlite3PcacheMove(pPg, pgno);
+ if( pPgOld ){
+ sqlite3PcacheMove(pPgOld, 0);
+ sqlite3PcacheRelease(pPgOld);
+ }
makeDirty(pPg);
pPager->dirtyCache = 1;
@@ -31428,17 +32081,15 @@
if( pPager->pInJournal && (int)needSyncPgno<=pPager->origDbSize ){
sqlite3BitvecClear(pPager->pInJournal, needSyncPgno);
}
- pagerLeave(pPager);
return rc;
}
pPager->needSync = 1;
- pPgHdr->needSync = 1;
- pPgHdr->inJournal = 1;
+ pPgHdr->flags |= PGHDR_NEED_SYNC;
+ pPgHdr->flags |= PGHDR_IN_JOURNAL;
makeDirty(pPgHdr);
sqlite3PagerUnref(pPgHdr);
}
- pagerLeave(pPager);
return SQLITE_OK;
}
#endif
@@ -31447,7 +32098,8 @@
** Return a pointer to the data for the specified page.
*/
SQLITE_PRIVATE void *sqlite3PagerGetData(DbPage *pPg){
- return PGHDR_TO_DATA(pPg);
+ assert( pPg->nRef>0 );
+ return pPg->pData;
}
/*
@@ -31456,7 +32108,7 @@
*/
SQLITE_PRIVATE void *sqlite3PagerGetExtra(DbPage *pPg){
Pager *pPager = pPg->pPager;
- return (pPager?PGHDR_TO_EXTRA(pPg, pPager):0);
+ return (pPager?pPg->pExtra:0);
}
/*
@@ -32475,7 +33127,7 @@
** May you share freely, never taking more than you give.
**
*************************************************************************
-** $Id: btree.c,v 1.495 2008/08/02 17:36:46 danielk1977 Exp $
+** $Id: btree.c,v 1.504 2008/08/27 15:16:34 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** See the header comment on "btreeInt.h" for additional information.
@@ -32840,7 +33492,7 @@
return pCur->skip;
}
pCur->eState = CURSOR_INVALID;
- rc = sqlite3BtreeMoveto(pCur, pCur->pKey, 0, pCur->nKey, 0, &pCur->skip);
+ rc = sqlite3BtreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &pCur->skip);
if( rc==SQLITE_OK ){
sqlite3_free(pCur->pKey);
pCur->pKey = 0;
@@ -33160,7 +33812,7 @@
int size; /* Size of a cell */
int usableSize; /* Number of usable bytes on a page */
int cellOffset; /* Offset to the cell pointer array */
- int brk; /* Offset to the cell content area */
+ int cbrk; /* Offset to the cell content area */
int nCell; /* Number of cells on the page */
unsigned char *data; /* The page data */
unsigned char *temp; /* Temp area for cell content */
@@ -33177,26 +33829,26 @@
nCell = pPage->nCell;
assert( nCell==get2byte(&data[hdr+3]) );
usableSize = pPage->pBt->usableSize;
- brk = get2byte(&data[hdr+5]);
- memcpy(&temp[brk], &data[brk], usableSize - brk);
- brk = usableSize;
+ cbrk = get2byte(&data[hdr+5]);
+ memcpy(&temp[cbrk], &data[cbrk], usableSize - cbrk);
+ cbrk = usableSize;
for(i=0; i<nCell; i++){
u8 *pAddr; /* The i-th cell pointer */
pAddr = &data[cellOffset + i*2];
pc = get2byte(pAddr);
assert( pc<pPage->pBt->usableSize );
size = cellSizePtr(pPage, &temp[pc]);
- brk -= size;
- memcpy(&data[brk], &temp[pc], size);
- put2byte(pAddr, brk);
+ cbrk -= size;
+ memcpy(&data[cbrk], &temp[pc], size);
+ put2byte(pAddr, cbrk);
}
- assert( brk>=cellOffset+2*nCell );
- put2byte(&data[hdr+5], brk);
+ assert( cbrk>=cellOffset+2*nCell );
+ put2byte(&data[hdr+5], cbrk);
data[hdr+1] = 0;
data[hdr+2] = 0;
data[hdr+7] = 0;
addr = cellOffset+2*nCell;
- memset(&data[addr], 0, brk-addr);
+ memset(&data[addr], 0, cbrk-addr);
}
/*
@@ -33597,18 +34249,19 @@
** reaches zero. We need to unref the pParent pointer when that
** happens.
*/
-static void pageDestructor(DbPage *pData, int pageSize){
+static void pageDestructor(DbPage *pData){
MemPage *pPage;
- assert( (pageSize & 7)==0 );
pPage = (MemPage *)sqlite3PagerGetExtra(pData);
- assert( pPage->isInit==0 || sqlite3_mutex_held(pPage->pBt->mutex) );
- if( pPage->pParent ){
- MemPage *pParent = pPage->pParent;
- assert( pParent->pBt==pPage->pBt );
- pPage->pParent = 0;
- releasePage(pParent);
+ if( pPage ){
+ assert( pPage->isInit==0 || sqlite3_mutex_held(pPage->pBt->mutex) );
+ if( pPage->pParent ){
+ MemPage *pParent = pPage->pParent;
+ assert( pParent->pBt==pPage->pBt );
+ pPage->pParent = 0;
+ releasePage(pParent);
+ }
+ pPage->isInit = 0;
}
- pPage->isInit = 0;
}
/*
@@ -33752,7 +34405,7 @@
}
pBt->busyHdr.xFunc = sqlite3BtreeInvokeBusyHandler;
pBt->busyHdr.pArg = pBt;
- rc = sqlite3PagerOpen(pVfs, &pBt->pPager, zFilename,
+ rc = sqlite3PagerOpen(pVfs, &pBt->pPager, zFilename, pageDestructor,
EXTRA_SIZE, flags, vfsFlags);
if( rc==SQLITE_OK ){
rc = sqlite3PagerReadFileheader(pBt->pPager,sizeof(zDbHeader),zDbHeader);
@@ -33763,7 +34416,6 @@
sqlite3PagerSetBusyhandler(pBt->pPager, &pBt->busyHdr);
p->pBt = pBt;
- sqlite3PagerSetDestructor(pBt->pPager, pageDestructor);
sqlite3PagerSetReiniter(pBt->pPager, pageReinit);
pBt->pCursor = 0;
pBt->pPage1 = 0;
@@ -35953,6 +36605,7 @@
assert( !sqlite3BtreeIsRootPage(pPage) );
pParent = pPage->pParent;
assert( pParent!=0 );
+ assert( pPage->pDbPage->nRef>0 );
idxParent = pPage->idxParent;
sqlite3PagerRef(pParent->pDbPage);
releasePage(pPage);
@@ -36113,12 +36766,11 @@
}
/* Move the cursor so that it points to an entry near the key
-** specified by pKey/nKey/pUnKey. Return a success code.
+** specified by pIdxKey or intKey. Return a success code.
**
-** For INTKEY tables, only the nKey parameter is used. pKey
-** and pUnKey must be NULL. For index tables, either pUnKey
-** must point to a key that has already been unpacked, or else
-** pKey/nKey describes a blob containing the key.
+** For INTKEY tables, the intKey parameter is used. pIdxKey
+** must be NULL. For index tables, pIdxKey is used and intKey
+** is ignored.
**
** If an exact match is not found, then the cursor is always
** left pointing at a leaf page which would hold the entry if it
@@ -36140,16 +36792,14 @@
** is larger than pKey.
**
*/
-SQLITE_PRIVATE int sqlite3BtreeMoveto(
- BtCursor *pCur, /* The cursor to be moved */
- const void *pKey, /* The key content for indices. Not used by tables */
- UnpackedRecord *pUnKey,/* Unpacked version of pKey */
- i64 nKey, /* Size of pKey. Or the key for tables */
- int biasRight, /* If true, bias the search to the high end */
- int *pRes /* Search result flag */
+SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
+ BtCursor *pCur, /* The cursor to be moved */
+ UnpackedRecord *pIdxKey, /* Unpacked index key */
+ i64 intKey, /* The table key */
+ int biasRight, /* If true, bias the search to the high end */
+ int *pRes /* Write search results here */
){
int rc;
- char aSpace[200];
assert( cursorHoldsMutex(pCur) );
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
@@ -36157,11 +36807,11 @@
/* If the cursor is already positioned at the point we are trying
** to move to, then just return without doing any work */
if( pCur->eState==CURSOR_VALID && pCur->validNKey && pCur->pPage->intKey ){
- if( pCur->info.nKey==nKey ){
+ if( pCur->info.nKey==intKey ){
*pRes = 0;
return SQLITE_OK;
}
- if( pCur->atLast && pCur->info.nKey<nKey ){
+ if( pCur->atLast && pCur->info.nKey<intKey ){
*pRes = -1;
return SQLITE_OK;
}
@@ -36179,24 +36829,7 @@
assert( pCur->pPage->nCell==0 );
return SQLITE_OK;
}
- if( pCur->pPage->intKey ){
- /* We are given an SQL table to search. The key is the integer
- ** rowid contained in nKey. pKey and pUnKey should both be NULL */
- assert( pUnKey==0 );
- assert( pKey==0 );
- }else if( pUnKey==0 ){
- /* We are to search an SQL index using a key encoded as a blob.
- ** The blob is found at pKey and is nKey bytes in length. Unpack
- ** this key so that we can use it. */
- assert( pKey!=0 );
- pUnKey = sqlite3VdbeRecordUnpack(pCur->pKeyInfo, nKey, pKey,
- aSpace, sizeof(aSpace));
- if( pUnKey==0 ) return SQLITE_NOMEM;
- }else{
- /* We are to search an SQL index using a key that is already unpacked
- ** and handed to us in pUnKey. */
- assert( pKey==0 );
- }
+ assert( pCur->pPage->intKey || pIdxKey );
for(;;){
int lwr, upr;
Pgno chldPg;
@@ -36204,7 +36837,7 @@
int c = -1; /* pRes return if table is empty must be -1 */
lwr = 0;
upr = pPage->nCell-1;
- if( !pPage->intKey && pUnKey==0 ){
+ if( !pPage->intKey && pIdxKey==0 ){
rc = SQLITE_CORRUPT_BKPT;
goto moveto_finish;
}
@@ -36226,12 +36859,12 @@
pCell += getVarint32(pCell, dummy);
}
getVarint(pCell, (u64*)&nCellKey);
- if( nCellKey==nKey ){
+ if( nCellKey==intKey ){
c = 0;
- }else if( nCellKey<nKey ){
+ }else if( nCellKey<intKey ){
c = -1;
}else{
- assert( nCellKey>nKey );
+ assert( nCellKey>intKey );
c = +1;
}
}else{
@@ -36239,7 +36872,7 @@
pCellKey = (void *)fetchPayload(pCur, &available, 0);
nCellKey = pCur->info.nKey;
if( available>=nCellKey ){
- c = sqlite3VdbeRecordCompare(nCellKey, pCellKey, pUnKey);
+ c = sqlite3VdbeRecordCompare(nCellKey, pCellKey, pIdxKey);
}else{
pCellKey = sqlite3Malloc( nCellKey );
if( pCellKey==0 ){
@@ -36247,7 +36880,7 @@
goto moveto_finish;
}
rc = sqlite3BtreeKey(pCur, 0, nCellKey, (void *)pCellKey);
- c = sqlite3VdbeRecordCompare(nCellKey, pCellKey, pUnKey);
+ c = sqlite3VdbeRecordCompare(nCellKey, pCellKey, pIdxKey);
sqlite3_free(pCellKey);
if( rc ) goto moveto_finish;
}
@@ -36297,10 +36930,35 @@
if( rc ) goto moveto_finish;
}
moveto_finish:
+ return rc;
+}
+
+/*
+** In this version of BtreeMoveto, pKey is a packed index record
+** such as is generated by the OP_MakeRecord opcode. Unpack the
+** record and then call BtreeMovetoUnpacked() to do the work.
+*/
+SQLITE_PRIVATE int sqlite3BtreeMoveto(
+ BtCursor *pCur, /* Cursor open on the btree to be searched */
+ const void *pKey, /* Packed key if the btree is an index */
+ i64 nKey, /* Integer key for tables. Size of pKey for indices */
+ int bias, /* Bias search to the high end */
+ int *pRes /* Write search results here */
+){
+ int rc; /* Status code */
+ UnpackedRecord *pIdxKey; /* Unpacked index key */
+ UnpackedRecord aSpace[16]; /* Temp space for pIdxKey - to avoid a malloc */
+
if( pKey ){
- /* If we created our own unpacked key at the top of this
- ** procedure, then destroy that key before returning. */
- sqlite3VdbeDeleteUnpackedRecord(pUnKey);
+ pIdxKey = sqlite3VdbeRecordUnpack(pCur->pKeyInfo, nKey, pKey,
+ aSpace, sizeof(aSpace));
+ if( pIdxKey==0 ) return SQLITE_NOMEM;
+ }else{
+ pIdxKey = 0;
+ }
+ rc = sqlite3BtreeMovetoUnpacked(pCur, pIdxKey, nKey, bias, pRes);
+ if( pKey ){
+ sqlite3VdbeDeleteUnpackedRecord(pIdxKey);
}
return rc;
}
@@ -36806,7 +37464,7 @@
put4byte(&pTrunk->aData[4], k+1);
put4byte(&pTrunk->aData[8+k*4], pPage->pgno);
#ifndef SQLITE_SECURE_DELETE
- sqlite3PagerDontWrite(pPage->pDbPage);
+ rc = sqlite3PagerDontWrite(pPage->pDbPage);
#endif
}
TRACE(("FREE-PAGE: %d leaf on trunk page %d\n",pPage->pgno,pTrunk->pgno));
@@ -38113,7 +38771,7 @@
u8 *data; /* Content of the parent page */
u8 *cdata; /* Content of the child page */
int hdr; /* Offset to page header in parent */
- int brk; /* Offset to content of first cell in parent */
+ int cbrk; /* Offset to content of first cell in parent */
assert( pPage->pParent==0 );
assert( pPage->nOverflow>0 );
@@ -38125,10 +38783,10 @@
usableSize = pBt->usableSize;
data = pPage->aData;
hdr = pPage->hdrOffset;
- brk = get2byte(&data[hdr+5]);
+ cbrk = get2byte(&data[hdr+5]);
cdata = pChild->aData;
memcpy(cdata, &data[hdr], pPage->cellOffset+2*pPage->nCell-hdr);
- memcpy(&cdata[brk], &data[brk], usableSize-brk);
+ memcpy(&cdata[cbrk], &data[cbrk], usableSize-cbrk);
if( pChild->isInit ) return SQLITE_CORRUPT;
rc = sqlite3BtreeInitPage(pChild, pPage);
if( rc ) goto balancedeeper_out;
@@ -38301,7 +38959,7 @@
clearCursorPosition(pCur);
if(
SQLITE_OK!=(rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur)) ||
- SQLITE_OK!=(rc = sqlite3BtreeMoveto(pCur, pKey, 0, nKey, appendBias, &loc))
+ SQLITE_OK!=(rc = sqlite3BtreeMoveto(pCur, pKey, nKey, appendBias, &loc))
){
return rc;
}
@@ -39217,8 +39875,11 @@
cellStart = hdr + 12 - 4*pPage->leaf;
for(i=0; i<nCell; i++){
int pc = get2byte(&data[cellStart+i*2]);
- u16 size = cellSizePtr(pPage, &data[pc]);
+ u16 size = 1024;
int j;
+ if( pc<=usableSize ){
+ size = cellSizePtr(pPage, &data[pc]);
+ }
if( (pc+size-1)>=usableSize || pc<0 ){
checkAppendMsg(pCheck, 0,
"Corruption detected in cell %d on page %d",i,iPage,0);
@@ -39495,7 +40156,7 @@
** page is still on the rollback journal, though. And that is the
** whole point of this block: to put pages on the rollback journal.
*/
- sqlite3PagerDontWrite(pDbPage);
+ rc = sqlite3PagerDontWrite(pDbPage);
}
sqlite3PagerUnref(pDbPage);
}
@@ -39604,7 +40265,7 @@
rc = sqlite3PagerGet(pBtFrom->pPager, iFrom, &pFromPage);
if( rc==SQLITE_OK ){
char *zFrom = sqlite3PagerGetData(pFromPage);
- rc = sqlite3OsWrite(pFile, zFrom, nFromPageSize, iOff);
+ rc = sqlite3OsWrite(pFile, zFrom, nFromPageSize, iOff);
sqlite3PagerUnref(pFromPage);
}
}
@@ -39940,7 +40601,7 @@
** only within the VDBE. Interface routines refer to a Mem using the
** name sqlite_value
**
-** $Id: vdbemem.c,v 1.121 2008/08/01 20:10:09 drh Exp $
+** $Id: vdbemem.c,v 1.122 2008/08/22 14:41:01 drh Exp $
*/
/*
@@ -40162,12 +40823,11 @@
sqlite3_context ctx;
assert( (pMem->flags & MEM_Null)!=0 || pFunc==pMem->u.pDef );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
+ memset(&ctx, 0, sizeof(ctx));
ctx.s.flags = MEM_Null;
ctx.s.db = pMem->db;
- ctx.s.zMalloc = 0;
ctx.pMem = pMem;
ctx.pFunc = pFunc;
- ctx.isError = 0;
pFunc->xFinalize(&ctx);
assert( 0==(pMem->flags&MEM_Dyn) && !pMem->xDel );
sqlite3DbFree(pMem->db, pMem->zMalloc);
@@ -40983,7 +41643,7 @@
** to version 2.8.7, all this code was combined into the vdbe.c source file.
** But that file was getting too big so this subroutines were split out.
**
-** $Id: vdbeaux.c,v 1.405 2008/08/02 03:50:39 drh Exp $
+** $Id: vdbeaux.c,v 1.409 2008/08/20 22:06:48 drh Exp $
*/
@@ -42816,9 +43476,8 @@
extern int sqlite3_search_count;
#endif
assert( p->isTable );
- rc = sqlite3BtreeMoveto(p->pCursor, 0, 0, p->movetoTarget, 0, &res);
+ rc = sqlite3BtreeMovetoUnpacked(p->pCursor, 0, p->movetoTarget, 0, &res);
if( rc ) return rc;
- *p->pIncrKey = 0;
p->lastRowid = keyToInt(p->movetoTarget);
p->rowidIsValid = res==0;
if( res<0 ){
@@ -43165,7 +43824,7 @@
KeyInfo *pKeyInfo, /* Information about the record format */
int nKey, /* Size of the binary record */
const void *pKey, /* The binary record */
- void *pSpace, /* Space available to hold resulting object */
+ UnpackedRecord *pSpace,/* Space available to hold resulting object */
int szSpace /* Size of pSpace[] in bytes */
){
const unsigned char *aKey = (const unsigned char *)pKey;
@@ -43181,14 +43840,13 @@
if( nByte>szSpace ){
p = sqlite3DbMallocRaw(pKeyInfo->db, nByte);
if( p==0 ) return 0;
- p->needFree = 1;
+ p->flags = UNPACKED_NEED_FREE | UNPACKED_NEED_DESTROY;
}else{
p = pSpace;
- p->needFree = 0;
+ p->flags = UNPACKED_NEED_DESTROY;
}
p->pKeyInfo = pKeyInfo;
p->nField = pKeyInfo->nField + 1;
- p->needDestroy = 1;
p->aMem = pMem = &((Mem*)p)[1];
idx = getVarint32(aKey, szHdr);
d = szHdr;
@@ -43206,6 +43864,7 @@
pMem++;
u++;
}
+ assert( u<=pKeyInfo->nField + 1 );
p->nField = u;
return (void*)p;
}
@@ -43215,7 +43874,7 @@
*/
SQLITE_PRIVATE void sqlite3VdbeDeleteUnpackedRecord(UnpackedRecord *p){
if( p ){
- if( p->needDestroy ){
+ if( p->flags & UNPACKED_NEED_DESTROY ){
int i;
Mem *pMem;
for(i=0, pMem=p->aMem; i<p->nField; i++, pMem++){
@@ -43224,7 +43883,7 @@
}
}
}
- if( p->needFree ){
+ if( p->flags & UNPACKED_NEED_FREE ){
sqlite3DbFree(p->pKeyInfo->db, p);
}
}
@@ -43233,25 +43892,32 @@
/*
** This function compares the two table rows or index records
** specified by {nKey1, pKey1} and pPKey2. It returns a negative, zero
-** or positive integer if {nKey1, pKey1} is less than, equal to or
-** greater than pPKey2. The {nKey1, pKey1} key must be a blob
+** or positive integer if key1 is less than, equal to or
+** greater than key2. The {nKey1, pKey1} key must be a blob
** created by th OP_MakeRecord opcode of the VDBE. The pPKey2
** key must be a parsed key such as obtained from
** sqlite3VdbeParseRecord.
**
** Key1 and Key2 do not have to contain the same number of fields.
-** But if the lengths differ, Key2 must be the shorter of the two.
-**
-** Historical note: In earlier versions of this routine both Key1
-** and Key2 were blobs obtained from OP_MakeRecord. But we found
-** that in typical use the same Key2 would be submitted multiple times
-** in a row. So an optimization was added to parse the Key2 key
-** separately and submit the parsed version. In this way, we avoid
-** parsing the same Key2 multiple times in a row.
+** The key with fewer fields is usually compares less than the
+** longer key. However if the UNPACKED_INCRKEY flags in pPKey2 is set
+** and the common prefixes are equal, then key1 is less than key2.
+** Or if the UNPACKED_MATCH_PREFIX flag is set and the prefixes are
+** equal, then the keys are considered to be equal and
+** the parts beyond the common prefix are ignored.
+**
+** If the UNPACKED_IGNORE_ROWID flag is set, then the last byte of
+** the header of pKey1 is ignored. It is assumed that pKey1 is
+** an index key, and thus ends with a rowid value. The last byte
+** of the header will therefore be the serial type of the rowid:
+** one of 1, 2, 3, 4, 5, 6, 8, or 9 - the integer serial types.
+** The serial type of the final rowid will always be a single byte.
+** By ignoring this last byte of the header, we force the comparison
+** to ignore the rowid at the end of key1.
*/
SQLITE_PRIVATE int sqlite3VdbeRecordCompare(
- int nKey1, const void *pKey1,
- UnpackedRecord *pPKey2
+ int nKey1, const void *pKey1, /* Left key */
+ UnpackedRecord *pPKey2 /* Right key */
){
u32 d1; /* Offset into aKey[] of next data element */
u32 idx1; /* Offset into aKey[] of next header element */
@@ -43271,6 +43937,9 @@
idx1 = getVarint32(aKey1, szHdr1);
d1 = szHdr1;
+ if( pPKey2->flags & UNPACKED_IGNORE_ROWID ){
+ szHdr1--;
+ }
nField = pKeyInfo->nField;
while( idx1<szHdr1 && i<pPKey2->nField ){
u32 serial_type1;
@@ -43294,17 +43963,21 @@
}
if( mem1.zMalloc ) sqlite3VdbeMemRelease(&mem1);
- /* One of the keys ran out of fields, but all the fields up to that point
- ** were equal. If the incrKey flag is true, then the second key is
- ** treated as larger.
- */
if( rc==0 ){
- if( pKeyInfo->incrKey ){
+ /* rc==0 here means that one of the keys ran out of fields and
+ ** all the fields up to that point were equal. If the UNPACKED_INCRKEY
+ ** flag is set, then break the tie by treating key2 as larger.
+ ** If the UPACKED_PREFIX_MATCH flag is set, then keys with common prefixes
+ ** are considered to be equal. Otherwise, the longer key is the
+ ** larger. As it happens, the pPKey2 will always be the longer
+ ** if there is a difference.
+ */
+ if( pPKey2->flags & UNPACKED_INCRKEY ){
rc = -1;
- }else if( !pKeyInfo->prefixIsEqual ){
- if( d1<nKey1 ){
- rc = 1;
- }
+ }else if( pPKey2->flags & UNPACKED_PREFIX_MATCH ){
+ /* Leave rc==0 */
+ }else if( idx1<szHdr1 ){
+ rc = 1;
}
}else if( pKeyInfo->aSortOrder && i<pKeyInfo->nField
&& pKeyInfo->aSortOrder[i] ){
@@ -43313,26 +43986,7 @@
return rc;
}
-
-/*
-** The argument is an index entry composed using the OP_MakeRecord opcode.
-** The last entry in this record should be an integer (specifically
-** an integer rowid). This routine returns the number of bytes in
-** that integer.
-*/
-SQLITE_PRIVATE int sqlite3VdbeIdxRowidLen(const u8 *aKey, int nKey, int *pRowidLen){
- u32 szHdr; /* Size of the header */
- u32 typeRowid; /* Serial type of the rowid */
-
- (void)getVarint32(aKey, szHdr);
- if( szHdr>nKey ){
- return SQLITE_CORRUPT_BKPT;
- }
- (void)getVarint32(&aKey[szHdr-1], typeRowid);
- *pRowidLen = sqlite3VdbeSerialTypeLen(typeRowid);
- return SQLITE_OK;
-}
-
+
/*
** pCur points at an index entry created using the OP_MakeRecord opcode.
@@ -43375,21 +44029,21 @@
**
** pKey is either created without a rowid or is truncated so that it
** omits the rowid at the end. The rowid at the end of the index entry
-** is ignored as well.
+** is ignored as well. Hence, this routine only compares the prefixes
+** of the keys prior to the final rowid, not the entire key.
+**
+** pUnpacked may be an unpacked version of pKey,nKey. If pUnpacked is
+** supplied it is used in place of pKey,nKey.
*/
SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(
Cursor *pC, /* The cursor to compare against */
- UnpackedRecord *pUnpacked,
- int nKey, const u8 *pKey, /* The key to compare */
+ UnpackedRecord *pUnpacked, /* Unpacked version of pKey and nKey */
int *res /* Write the comparison result here */
){
i64 nCellKey = 0;
int rc;
BtCursor *pCur = pC->pCursor;
- int lenRowid;
Mem m;
- UnpackedRecord *pRec;
- char zSpace[200];
sqlite3BtreeKeySize(pCur, &nCellKey);
if( nCellKey<=0 ){
@@ -43399,24 +44053,12 @@
m.db = 0;
m.flags = 0;
m.zMalloc = 0;
- if( (rc = sqlite3VdbeMemFromBtree(pC->pCursor, 0, nCellKey, 1, &m))
- || (rc = sqlite3VdbeIdxRowidLen((u8*)m.z, m.n, &lenRowid))
- ){
+ rc = sqlite3VdbeMemFromBtree(pC->pCursor, 0, nCellKey, 1, &m);
+ if( rc ){
return rc;
}
- if( !pUnpacked ){
- pRec = sqlite3VdbeRecordUnpack(pC->pKeyInfo, nKey, pKey,
- zSpace, sizeof(zSpace));
- }else{
- pRec = pUnpacked;
- }
- if( pRec==0 ){
- return SQLITE_NOMEM;
- }
- *res = sqlite3VdbeRecordCompare(m.n-lenRowid, m.z, pRec);
- if( !pUnpacked ){
- sqlite3VdbeDeleteUnpackedRecord(pRec);
- }
+ assert( pUnpacked->flags & UNPACKED_IGNORE_ROWID );
+ *res = sqlite3VdbeRecordCompare(m.n, m.z, pUnpacked);
sqlite3VdbeMemRelease(&m);
return SQLITE_OK;
}
@@ -43480,10 +44122,10 @@
** This file contains code use to implement APIs that are part of the
** VDBE.
**
-** $Id: vdbeapi.c,v 1.138 2008/08/02 03:50:39 drh Exp $
+** $Id: vdbeapi.c,v 1.140 2008/08/21 12:19:44 danielk1977 Exp $
*/
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
+#if 0 && defined(SQLITE_ENABLE_MEMORY_MANAGEMENT)
/*
** The following structure contains pointers to the end points of a
** doubly-linked list of all compiled SQL statements that may be holding
@@ -43898,7 +44540,9 @@
/* Assert that malloc() has not failed */
db = p->db;
- assert( !db->mallocFailed );
+ if( db->mallocFailed ){
+ return SQLITE_NOMEM;
+ }
if( p->pc<=0 && p->expired ){
if( p->rc==SQLITE_OK ){
@@ -44786,7 +45430,7 @@
** in this file for details. If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code.
**
-** $Id: vdbe.c,v 1.772 2008/08/02 15:10:09 danielk1977 Exp $
+** $Id: vdbe.c,v 1.777 2008/08/21 19:28:30 drh Exp $
*/
/*
@@ -45386,6 +46030,8 @@
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
int nProgressOps = 0; /* Opcodes executed since progress callback. */
#endif
+ UnpackedRecord aTempRec[16]; /* Space to hold a transient UnpackedRecord */
+
assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */
assert( db->magic==SQLITE_MAGIC_BUSY );
@@ -47536,11 +48182,9 @@
rc = sqlite3BtreeCursor(pX, p2, wrFlag, pOp->p4.p, pCur->pCursor);
if( pOp->p4type==P4_KEYINFO ){
pCur->pKeyInfo = pOp->p4.pKeyInfo;
- pCur->pIncrKey = &pCur->pKeyInfo->incrKey;
pCur->pKeyInfo->enc = ENC(p->db);
}else{
pCur->pKeyInfo = 0;
- pCur->pIncrKey = &pCur->bogusIncrKey;
}
switch( rc ){
case SQLITE_BUSY: {
@@ -47640,13 +48284,11 @@
(KeyInfo*)pOp->p4.z, pCx->pCursor);
pCx->pKeyInfo = pOp->p4.pKeyInfo;
pCx->pKeyInfo->enc = ENC(p->db);
- pCx->pIncrKey = &pCx->pKeyInfo->incrKey;
}
pCx->isTable = 0;
}else{
rc = sqlite3BtreeCursor(pCx->pBt, MASTER_ROOT, 1, 0, pCx->pCursor);
pCx->isTable = 1;
- pCx->pIncrKey = &pCx->bogusIncrKey;
}
}
pCx->isIndex = !pCx->isTable;
@@ -47682,7 +48324,6 @@
pCx->nullRow = 1;
pCx->pseudoTable = 1;
pCx->ephemPseudoTable = pOp->p2;
- pCx->pIncrKey = &pCx->bogusIncrKey;
pCx->isTable = 1;
pCx->isIndex = 0;
break;
@@ -47774,7 +48415,6 @@
int res, oc;
oc = pOp->opcode;
pC->nullRow = 0;
- *pC->pIncrKey = oc==OP_MoveGt || oc==OP_MoveLe;
if( pC->isTable ){
i64 iKey = sqlite3VdbeIntValue(pIn3);
if( pOp->p2==0 ){
@@ -47784,7 +48424,7 @@
pC->deferredMoveto = 1;
break;
}
- rc = sqlite3BtreeMoveto(pC->pCursor, 0, 0, (u64)iKey, 0, &res);
+ rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, 0, (u64)iKey, 0, &res);
if( rc!=SQLITE_OK ){
goto abort_due_to_error;
}
@@ -47797,10 +48437,13 @@
assert( nField>0 );
r.pKeyInfo = pC->pKeyInfo;
r.nField = nField;
- r.needFree = 0;
- r.needDestroy = 0;
+ if( oc==OP_MoveGt || oc==OP_MoveLe ){
+ r.flags = UNPACKED_INCRKEY;
+ }else{
+ r.flags = 0;
+ }
r.aMem = &p->aMem[pOp->p3];
- rc = sqlite3BtreeMoveto(pC->pCursor, 0, &r, 0, 0, &res);
+ rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, &r, 0, 0, &res);
if( rc!=SQLITE_OK ){
goto abort_due_to_error;
}
@@ -47808,7 +48451,6 @@
}
pC->deferredMoveto = 0;
pC->cacheStatus = CACHE_STALE;
- *pC->pIncrKey = 0;
#ifdef SQLITE_TEST
sqlite3_search_count++;
#endif
@@ -47866,7 +48508,7 @@
** in the record in register P3, where N is the total number of values in
** the P3 record (the P3 record is a prefix of the P1 record).
**
-** See also: NotFound, MoveTo, IsUnique, NotExists
+** See also: NotFound, IsUnique, NotExists
*/
/* Opcode: NotFound P1 P2 P3 * *
**
@@ -47875,7 +48517,7 @@
** to P2. If an entry does existing, fall through. The cursor is left
** pointing to the entry that matches.
**
-** See also: Found, MoveTo, NotExists, IsUnique
+** See also: Found, NotExists, IsUnique
*/
case OP_NotFound: /* jump, in3 */
case OP_Found: { /* jump, in3 */
@@ -47886,13 +48528,20 @@
assert( p->apCsr[i]!=0 );
if( (pC = p->apCsr[i])->pCursor!=0 ){
int res;
+ UnpackedRecord *pIdxKey;
+
assert( pC->isTable==0 );
assert( pIn3->flags & MEM_Blob );
+ pIdxKey = sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z,
+ aTempRec, sizeof(aTempRec));
+ if( pIdxKey==0 ){
+ goto no_mem;
+ }
if( pOp->opcode==OP_Found ){
- pC->pKeyInfo->prefixIsEqual = 1;
+ pIdxKey->flags |= UNPACKED_PREFIX_MATCH;
}
- rc = sqlite3BtreeMoveto(pC->pCursor, pIn3->z, 0, pIn3->n, 0, &res);
- pC->pKeyInfo->prefixIsEqual = 0;
+ rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, pIdxKey, 0, 0, &res);
+ sqlite3VdbeDeleteUnpackedRecord(pIdxKey);
if( rc!=SQLITE_OK ){
break;
}
@@ -47912,7 +48561,7 @@
**
** The P3 register contains an integer record number. Call this
** record number R. The P4 register contains an index key created
-** using MakeIdxRec. Call it K.
+** using MakeRecord. Call it K.
**
** P1 is an index. So it has no data and its key consists of a
** record generated by OP_MakeRecord where the last field is the
@@ -47948,45 +48597,39 @@
pCrsr = pCx->pCursor;
if( pCrsr!=0 ){
int res;
- i64 v; /* The record number on the P1 entry that matches K */
- char *zKey; /* The value of K */
- int nKey; /* Number of bytes in K */
- int len; /* Number of bytes in K without the rowid at the end */
- int szRowid; /* Size of the rowid column at the end of zKey */
+ i64 v; /* The record number that matches K */
+ UnpackedRecord *pIdxKey; /* Unpacked version of P4 */
/* Make sure K is a string and make zKey point to K
*/
assert( pK->flags & MEM_Blob );
- zKey = pK->z;
- nKey = pK->n;
-
- /* sqlite3VdbeIdxRowidLen() only returns other than SQLITE_OK when the
- ** record passed as an argument corrupt. Since the record in this case
- ** has just been created by an OP_MakeRecord instruction, and not loaded
- ** from the database file, it is not possible for it to be corrupt.
- ** Therefore, assert(rc==SQLITE_OK).
- */
- rc = sqlite3VdbeIdxRowidLen((u8*)zKey, nKey, &szRowid);
- assert(rc==SQLITE_OK);
- len = nKey-szRowid;
+ pIdxKey = sqlite3VdbeRecordUnpack(pCx->pKeyInfo, pK->n, pK->z,
+ aTempRec, sizeof(aTempRec));
+ if( pIdxKey==0 ){
+ goto no_mem;
+ }
+ pIdxKey->flags |= UNPACKED_IGNORE_ROWID;
- /* Search for an entry in P1 where all but the last four bytes match K.
+ /* Search for an entry in P1 where all but the last rowid match K
** If there is no such entry, jump immediately to P2.
*/
assert( pCx->deferredMoveto==0 );
pCx->cacheStatus = CACHE_STALE;
- rc = sqlite3BtreeMoveto(pCrsr, zKey, 0, len, 0, &res);
+ rc = sqlite3BtreeMovetoUnpacked(pCrsr, pIdxKey, 0, 0, &res);
if( rc!=SQLITE_OK ){
+ sqlite3VdbeDeleteUnpackedRecord(pIdxKey);
goto abort_due_to_error;
}
if( res<0 ){
rc = sqlite3BtreeNext(pCrsr, &res);
if( res ){
pc = pOp->p2 - 1;
+ sqlite3VdbeDeleteUnpackedRecord(pIdxKey);
break;
}
}
- rc = sqlite3VdbeIdxKeyCompare(pCx, 0, len, (u8*)zKey, &res);
+ rc = sqlite3VdbeIdxKeyCompare(pCx, pIdxKey, &res);
+ sqlite3VdbeDeleteUnpackedRecord(pIdxKey);
if( rc!=SQLITE_OK ) goto abort_due_to_error;
if( res>0 ){
pc = pOp->p2 - 1;
@@ -48029,7 +48672,7 @@
** NotFound assumes key is a blob constructed from MakeRecord and
** P1 is an index.
**
-** See also: Found, MoveTo, NotFound, IsUnique
+** See also: Found, NotFound, IsUnique
*/
case OP_NotExists: { /* jump, in3 */
int i = pOp->p1;
@@ -48043,7 +48686,7 @@
assert( pIn3->flags & MEM_Int );
assert( p->apCsr[i]->isTable );
iKey = intToKey(pIn3->u.i);
- rc = sqlite3BtreeMoveto(pCrsr, 0, 0, iKey, 0,&res);
+ rc = sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0,&res);
pC->lastRowid = pIn3->u.i;
pC->rowidIsValid = res==0;
pC->nullRow = 0;
@@ -48220,7 +48863,7 @@
}
if( v==0 ) continue;
x = intToKey(v);
- rx = sqlite3BtreeMoveto(pC->pCursor, 0, 0, (u64)x, 0, &res);
+ rx = sqlite3BtreeMovetoUnpacked(pC->pCursor, 0, (u64)x, 0, &res);
cnt++;
}while( cnt<100 && rx==SQLITE_OK && res==0 );
db->priorNewRowid = v;
@@ -48716,10 +49359,9 @@
UnpackedRecord r;
r.pKeyInfo = pC->pKeyInfo;
r.nField = pOp->p3;
- r.needFree = 0;
- r.needDestroy = 0;
+ r.flags = 0;
r.aMem = &p->aMem[pOp->p2];
- rc = sqlite3BtreeMoveto(pCrsr, 0, &r, 0, 0, &res);
+ rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &res);
if( rc==SQLITE_OK && res==0 ){
rc = sqlite3BtreeDelete(pCrsr);
}
@@ -48802,12 +49444,13 @@
assert( pOp->p4type==P4_INT32 );
r.pKeyInfo = pC->pKeyInfo;
r.nField = pOp->p4.i;
- r.needFree = 0;
- r.needDestroy = 0;
+ if( pOp->p5 ){
+ r.flags = UNPACKED_INCRKEY | UNPACKED_IGNORE_ROWID;
+ }else{
+ r.flags = UNPACKED_IGNORE_ROWID;
+ }
r.aMem = &p->aMem[pOp->p3];
- *pC->pIncrKey = pOp->p5;
- rc = sqlite3VdbeIdxKeyCompare(pC, &r, 0, 0, &res);
- *pC->pIncrKey = 0;
+ rc = sqlite3VdbeIdxKeyCompare(pC, &r, &res);
if( pOp->opcode==OP_IdxLT ){
res = -res;
}else{
@@ -48970,9 +49613,10 @@
(void)sqlite3SafetyOff(db);
assert( db->init.busy==0 );
db->init.busy = 1;
+ initData.rc = SQLITE_OK;
assert( !db->mallocFailed );
rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0);
- if( rc==SQLITE_ABORT ) rc = initData.rc;
+ if( rc==SQLITE_OK ) rc = initData.rc;
sqlite3DbFree(db, zSql);
db->init.busy = 0;
(void)sqlite3SafetyOn(db);
@@ -50265,280 +50909,1563 @@
v->rc = rc;
}
}
- rc = sqlite3ApiExit(db, rc);
- sqlite3_mutex_leave(db->mutex);
- return rc;
-}
-
-/*
-** Read data from a blob handle.
-*/
-SQLITE_API int sqlite3_blob_read(sqlite3_blob *pBlob, void *z, int n, int iOffset){
- return blobReadWrite(pBlob, z, n, iOffset, sqlite3BtreeData);
-}
-
-/*
-** Write data to a blob handle.
-*/
-SQLITE_API int sqlite3_blob_write(sqlite3_blob *pBlob, const void *z, int n, int iOffset){
- return blobReadWrite(pBlob, (void *)z, n, iOffset, sqlite3BtreePutData);
+ rc = sqlite3ApiExit(db, rc);
+ sqlite3_mutex_leave(db->mutex);
+ return rc;
+}
+
+/*
+** Read data from a blob handle.
+*/
+SQLITE_API int sqlite3_blob_read(sqlite3_blob *pBlob, void *z, int n, int iOffset){
+ return blobReadWrite(pBlob, z, n, iOffset, sqlite3BtreeData);
+}
+
+/*
+** Write data to a blob handle.
+*/
+SQLITE_API int sqlite3_blob_write(sqlite3_blob *pBlob, const void *z, int n, int iOffset){
+ return blobReadWrite(pBlob, (void *)z, n, iOffset, sqlite3BtreePutData);
+}
+
+/*
+** Query a blob handle for the size of the data.
+**
+** The Incrblob.nByte field is fixed for the lifetime of the Incrblob
+** so no mutex is required for access.
+*/
+SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *pBlob){
+ Incrblob *p = (Incrblob *)pBlob;
+ return p->nByte;
+}
+
+#endif /* #ifndef SQLITE_OMIT_INCRBLOB */
+
+/************** End of vdbeblob.c ********************************************/
+/************** Begin file journal.c *****************************************/
+/*
+** 2007 August 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.
+**
+*************************************************************************
+**
+** @(#) $Id: journal.c,v 1.8 2008/05/01 18:01:47 drh Exp $
+*/
+
+#ifdef SQLITE_ENABLE_ATOMIC_WRITE
+
+/*
+** This file implements a special kind of sqlite3_file object used
+** by SQLite to create journal files if the atomic-write optimization
+** is enabled.
+**
+** The distinctive characteristic of this sqlite3_file is that the
+** actual on disk file is created lazily. When the file is created,
+** the caller specifies a buffer size for an in-memory buffer to
+** be used to service read() and write() requests. The actual file
+** on disk is not created or populated until either:
+**
+** 1) The in-memory representation grows too large for the allocated
+** buffer, or
+** 2) The xSync() method is called.
+*/
+
+
+
+/*
+** A JournalFile object is a subclass of sqlite3_file used by
+** as an open file handle for journal files.
+*/
+struct JournalFile {
+ sqlite3_io_methods *pMethod; /* I/O methods on journal files */
+ int nBuf; /* Size of zBuf[] in bytes */
+ char *zBuf; /* Space to buffer journal writes */
+ int iSize; /* Amount of zBuf[] currently used */
+ int flags; /* xOpen flags */
+ sqlite3_vfs *pVfs; /* The "real" underlying VFS */
+ sqlite3_file *pReal; /* The "real" underlying file descriptor */
+ const char *zJournal; /* Name of the journal file */
+};
+typedef struct JournalFile JournalFile;
+
+/*
+** If it does not already exists, create and populate the on-disk file
+** for JournalFile p.
+*/
+static int createFile(JournalFile *p){
+ int rc = SQLITE_OK;
+ if( !p->pReal ){
+ sqlite3_file *pReal = (sqlite3_file *)&p[1];
+ rc = sqlite3OsOpen(p->pVfs, p->zJournal, pReal, p->flags, 0);
+ if( rc==SQLITE_OK ){
+ p->pReal = pReal;
+ if( p->iSize>0 ){
+ assert(p->iSize<=p->nBuf);
+ rc = sqlite3OsWrite(p->pReal, p->zBuf, p->iSize, 0);
+ }
+ }
+ }
+ return rc;
+}
+
+/*
+** Close the file.
+*/
+static int jrnlClose(sqlite3_file *pJfd){
+ JournalFile *p = (JournalFile *)pJfd;
+ if( p->pReal ){
+ sqlite3OsClose(p->pReal);
+ }
+ sqlite3_free(p->zBuf);
+ return SQLITE_OK;
+}
+
+/*
+** Read data from the file.
+*/
+static int jrnlRead(
+ sqlite3_file *pJfd, /* The journal file from which to read */
+ void *zBuf, /* Put the results here */
+ int iAmt, /* Number of bytes to read */
+ sqlite_int64 iOfst /* Begin reading at this offset */
+){
+ int rc = SQLITE_OK;
+ JournalFile *p = (JournalFile *)pJfd;
+ if( p->pReal ){
+ rc = sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst);
+ }else{
+ assert( iAmt+iOfst<=p->iSize );
+ memcpy(zBuf, &p->zBuf[iOfst], iAmt);
+ }
+ return rc;
+}
+
+/*
+** Write data to the file.
+*/
+static int jrnlWrite(
+ sqlite3_file *pJfd, /* The journal file into which to write */
+ const void *zBuf, /* Take data to be written from here */
+ int iAmt, /* Number of bytes to write */
+ sqlite_int64 iOfst /* Begin writing at this offset into the file */
+){
+ int rc = SQLITE_OK;
+ JournalFile *p = (JournalFile *)pJfd;
+ if( !p->pReal && (iOfst+iAmt)>p->nBuf ){
+ rc = createFile(p);
+ }
+ if( rc==SQLITE_OK ){
+ if( p->pReal ){
+ rc = sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst);
+ }else{
+ memcpy(&p->zBuf[iOfst], zBuf, iAmt);
+ if( p->iSize<(iOfst+iAmt) ){
+ p->iSize = (iOfst+iAmt);
+ }
+ }
+ }
+ return rc;
+}
+
+/*
+** Truncate the file.
+*/
+static int jrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
+ int rc = SQLITE_OK;
+ JournalFile *p = (JournalFile *)pJfd;
+ if( p->pReal ){
+ rc = sqlite3OsTruncate(p->pReal, size);
+ }else if( size<p->iSize ){
+ p->iSize = size;
+ }
+ return rc;
+}
+
+/*
+** Sync the file.
+*/
+static int jrnlSync(sqlite3_file *pJfd, int flags){
+ int rc;
+ JournalFile *p = (JournalFile *)pJfd;
+ if( p->pReal ){
+ rc = sqlite3OsSync(p->pReal, flags);
+ }else{
+ rc = SQLITE_OK;
+ }
+ return rc;
+}
+
+/*
+** Query the size of the file in bytes.
+*/
+static int jrnlFileSize(sqlite3_file *pJfd, sqlite_int64 *pSize){
+ int rc = SQLITE_OK;
+ JournalFile *p = (JournalFile *)pJfd;
+ if( p->pReal ){
+ rc = sqlite3OsFileSize(p->pReal, pSize);
+ }else{
+ *pSize = (sqlite_int64) p->iSize;
+ }
+ return rc;
+}
+
+/*
+** Table of methods for JournalFile sqlite3_file object.
+*/
+static struct sqlite3_io_methods JournalFileMethods = {
+ 1, /* iVersion */
+ jrnlClose, /* xClose */
+ jrnlRead, /* xRead */
+ jrnlWrite, /* xWrite */
+ jrnlTruncate, /* xTruncate */
+ jrnlSync, /* xSync */
+ jrnlFileSize, /* xFileSize */
+ 0, /* xLock */
+ 0, /* xUnlock */
+ 0, /* xCheckReservedLock */
+ 0, /* xFileControl */
+ 0, /* xSectorSize */
+ 0 /* xDeviceCharacteristics */
+};
+
+/*
+** Open a journal file.
+*/
+SQLITE_PRIVATE int sqlite3JournalOpen(
+ sqlite3_vfs *pVfs, /* The VFS to use for actual file I/O */
+ const char *zName, /* Name of the journal file */
+ sqlite3_file *pJfd, /* Preallocated, blank file handle */
+ int flags, /* Opening flags */
+ int nBuf /* Bytes buffered before opening the file */
+){
+ JournalFile *p = (JournalFile *)pJfd;
+ memset(p, 0, sqlite3JournalSize(pVfs));
+ if( nBuf>0 ){
+ p->zBuf = sqlite3MallocZero(nBuf);
+ if( !p->zBuf ){
+ return SQLITE_NOMEM;
+ }
+ }else{
+ return sqlite3OsOpen(pVfs, zName, pJfd, flags, 0);
+ }
+ p->pMethod = &JournalFileMethods;
+ p->nBuf = nBuf;
+ p->flags = flags;
+ p->zJournal = zName;
+ p->pVfs = pVfs;
+ return SQLITE_OK;
+}
+
+/*
+** If the argument p points to a JournalFile structure, and the underlying
+** file has not yet been created, create it now.
+*/
+SQLITE_PRIVATE int sqlite3JournalCreate(sqlite3_file *p){
+ if( p->pMethods!=&JournalFileMethods ){
+ return SQLITE_OK;
+ }
+ return createFile((JournalFile *)p);
+}
+
+/*
+** Return the number of bytes required to store a JournalFile that uses vfs
+** pVfs to create the underlying on-disk files.
+*/
+SQLITE_PRIVATE int sqlite3JournalSize(sqlite3_vfs *pVfs){
+ return (pVfs->szOsFile+sizeof(JournalFile));
+}
+#endif
+
+/************** End of journal.c *********************************************/
+/************** Begin file walker.c ******************************************/
+/*
+** 2008 August 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 file contains routines used for walking the parser tree for
+** an SQL statement.
+**
+** $Id: walker.c,v 1.1 2008/08/20 16:35:10 drh Exp $
+*/
+
+
+/*
+** Walk an expression tree. Invoke the callback once for each node
+** of the expression, while decending. (In other words, the callback
+** is invoked before visiting children.)
+**
+** The return value from the callback should be one of the WRC_*
+** constants to specify how to proceed with the walk.
+**
+** WRC_Continue Continue descending down the tree.
+**
+** WRC_Prune Do not descend into child nodes. But allow
+** the walk to continue with sibling nodes.
+**
+** WRC_Abort Do no more callbacks. Unwind the stack and
+** return the top-level walk call.
+**
+** The return value from this routine is WRC_Abort to abandon the tree walk
+** and WRC_Continue to continue.
+*/
+SQLITE_PRIVATE int sqlite3WalkExpr(Walker *pWalker, Expr *pExpr){
+ int rc;
+ if( pExpr==0 ) return WRC_Continue;
+ rc = pWalker->xExprCallback(pWalker, pExpr);
+ if( rc==WRC_Continue ){
+ if( sqlite3WalkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort;
+ if( sqlite3WalkExpr(pWalker, pExpr->pRight) ) return WRC_Abort;
+ if( sqlite3WalkExprList(pWalker, pExpr->pList) ) return WRC_Abort;
+ if( sqlite3WalkSelect(pWalker, pExpr->pSelect) ){
+ return WRC_Abort;
+ }
+ }
+ return rc & WRC_Abort;
+}
+
+/*
+** Call sqlite3WalkExpr() for every expression in list p or until
+** an abort request is seen.
+*/
+SQLITE_PRIVATE int sqlite3WalkExprList(Walker *pWalker, ExprList *p){
+ int i, rc = WRC_Continue;
+ struct ExprList_item *pItem;
+ if( p ){
+ for(i=p->nExpr, pItem=p->a; i>0; i--, pItem++){
+ if( sqlite3WalkExpr(pWalker, pItem->pExpr) ) return WRC_Abort;
+ }
+ }
+ return rc & WRC_Continue;
+}
+
+/*
+** Walk all expressions associated with SELECT statement p. Do
+** not invoke the SELECT callback on p, but do (of course) invoke
+** any expr callbacks and SELECT callbacks that come from subqueries.
+** Return WRC_Abort or WRC_Continue.
+*/
+SQLITE_PRIVATE int sqlite3WalkSelectExpr(Walker *pWalker, Select *p){
+ if( sqlite3WalkExprList(pWalker, p->pEList) ) return WRC_Abort;
+ if( sqlite3WalkExpr(pWalker, p->pWhere) ) return WRC_Abort;
+ if( sqlite3WalkExprList(pWalker, p->pGroupBy) ) return WRC_Abort;
+ if( sqlite3WalkExpr(pWalker, p->pHaving) ) return WRC_Abort;
+ if( sqlite3WalkExprList(pWalker, p->pOrderBy) ) return WRC_Abort;
+ if( sqlite3WalkExpr(pWalker, p->pLimit) ) return WRC_Abort;
+ if( sqlite3WalkExpr(pWalker, p->pOffset) ) return WRC_Abort;
+ return WRC_Continue;
+}
+
+/*
+** Walk the parse trees associated with all subqueries in the
+** FROM clause of SELECT statement p. Do not invoke the select
+** callback on p, but do invoke it on each FROM clause subquery
+** and on any subqueries further down in the tree. Return
+** WRC_Abort or WRC_Continue;
+*/
+SQLITE_PRIVATE int sqlite3WalkSelectFrom(Walker *pWalker, Select *p){
+ SrcList *pSrc;
+ int i;
+ struct SrcList_item *pItem;
+
+ pSrc = p->pSrc;
+ if( pSrc ){
+ for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){
+ if( sqlite3WalkSelect(pWalker, pItem->pSelect) ){
+ return WRC_Abort;
+ }
+ }
+ }
+ return WRC_Continue;
+}
+
+/*
+** Call sqlite3WalkExpr() for every expression in Select statement p.
+** Invoke sqlite3WalkSelect() for subqueries in the FROM clause and
+** on the compound select chain, p->pPrior.
+**
+** Return WRC_Continue under normal conditions. Return WRC_Abort if
+** there is an abort request.
+**
+** If the Walker does not have an xSelectCallback() then this routine
+** is a no-op returning WRC_Continue.
+*/
+SQLITE_PRIVATE int sqlite3WalkSelect(Walker *pWalker, Select *p){
+ int rc;
+ if( p==0 || pWalker->xSelectCallback==0 ) return WRC_Continue;
+ rc = WRC_Continue;
+ while( p ){
+ rc = pWalker->xSelectCallback(pWalker, p);
+ if( rc ) break;
+ if( sqlite3WalkSelectExpr(pWalker, p) ) return WRC_Abort;
+ if( sqlite3WalkSelectFrom(pWalker, p) ) return WRC_Abort;
+ p = p->pPrior;
+ }
+ return rc & WRC_Abort;
+}
+
+/************** End of walker.c **********************************************/
+/************** Begin file resolve.c *****************************************/
+/*
+** 2008 August 18
+**
+** 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 routines used for walking the parser tree and
+** resolve all identifiers by associating them with a particular
+** table and column.
+**
+** $Id: resolve.c,v 1.5 2008/08/29 02:14:03 drh Exp $
+*/
+
+/*
+** Turn the pExpr expression into an alias for the iCol-th column of the
+** result set in pEList.
+**
+** If the result set column is a simple column reference, then this routine
+** makes an exact copy. But for any other kind of expression, this
+** routine make a copy of the result set column as the argument to the
+** TK_AS operator. The TK_AS operator causes the expression to be
+** evaluated just once and then reused for each alias.
+**
+** The reason for suppressing the TK_AS term when the expression is a simple
+** column reference is so that the column reference will be recognized as
+** usable by indices within the WHERE clause processing logic.
+**
+** Hack: The TK_AS operator is inhibited if zType[0]=='G'. This means
+** that in a GROUP BY clause, the expression is evaluated twice. Hence:
+**
+** SELECT random()%5 AS x, count(*) FROM tab GROUP BY x
+**
+** Is equivalent to:
+**
+** SELECT random()%5 AS x, count(*) FROM tab GROUP BY random()%5
+**
+** The result of random()%5 in the GROUP BY clause is probably different
+** from the result in the result-set. We might fix this someday. Or
+** then again, we might not...
+*/
+static void resolveAlias(
+ Parse *pParse, /* Parsing context */
+ ExprList *pEList, /* A result set */
+ int iCol, /* A column in the result set. 0..pEList->nExpr-1 */
+ Expr *pExpr, /* Transform this into an alias to the result set */
+ const char *zType /* "GROUP" or "ORDER" or "" */
+){
+ Expr *pOrig; /* The iCol-th column of the result set */
+ Expr *pDup; /* Copy of pOrig */
+ sqlite3 *db; /* The database connection */
+
+ assert( iCol>=0 && iCol<pEList->nExpr );
+ pOrig = pEList->a[iCol].pExpr;
+ assert( pOrig!=0 );
+ assert( pOrig->flags & EP_Resolved );
+ db = pParse->db;
+ pDup = sqlite3ExprDup(db, pOrig);
+ if( pDup==0 ) return;
+ if( pDup->op!=TK_COLUMN && zType[0]!='G' ){
+ pDup = sqlite3PExpr(pParse, TK_AS, pDup, 0, 0);
+ if( pDup==0 ) return;
+ if( pEList->a[iCol].iAlias==0 ){
+ pEList->a[iCol].iAlias = ++pParse->nAlias;
+ }
+ pDup->iTable = pEList->a[iCol].iAlias;
+ }
+ if( pExpr->flags & EP_ExpCollate ){
+ pDup->pColl = pExpr->pColl;
+ pDup->flags |= EP_ExpCollate;
+ }
+ if( pExpr->span.dyn ) sqlite3DbFree(db, (char*)pExpr->span.z);
+ if( pExpr->token.dyn ) sqlite3DbFree(db, (char*)pExpr->token.z);
+ memcpy(pExpr, pDup, sizeof(*pExpr));
+ sqlite3DbFree(db, pDup);
+}
+
+/*
+** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up
+** that name in the set of source tables in pSrcList and make the pExpr
+** expression node refer back to that source column. The following changes
+** are made to pExpr:
+**
+** pExpr->iDb Set the index in db->aDb[] of the database X
+** (even if X is implied).
+** pExpr->iTable Set to the cursor number for the table obtained
+** from pSrcList.
+** pExpr->pTab Points to the Table structure of X.Y (even if
+** X and/or Y are implied.)
+** pExpr->iColumn Set to the column number within the table.
+** pExpr->op Set to TK_COLUMN.
+** pExpr->pLeft Any expression this points to is deleted
+** pExpr->pRight Any expression this points to is deleted.
+**
+** The pDbToken is the name of the database (the "X"). This value may be
+** NULL meaning that name is of the form Y.Z or Z. Any available database
+** can be used. The pTableToken is the name of the table (the "Y"). This
+** value can be NULL if pDbToken is also NULL. If pTableToken is NULL it
+** means that the form of the name is Z and that columns from any table
+** can be used.
+**
+** If the name cannot be resolved unambiguously, leave an error message
+** in pParse and return non-zero. Return zero on success.
+*/
+static int lookupName(
+ Parse *pParse, /* The parsing context */
+ Token *pDbToken, /* Name of the database containing table, or NULL */
+ Token *pTableToken, /* Name of table containing column, or NULL */
+ Token *pColumnToken, /* Name of the column. */
+ NameContext *pNC, /* The name context used to resolve the name */
+ Expr *pExpr /* Make this EXPR node point to the selected column */
+){
+ char *zDb = 0; /* Name of the database. The "X" in X.Y.Z */
+ char *zTab = 0; /* Name of the table. The "Y" in X.Y.Z or Y.Z */
+ char *zCol = 0; /* Name of the column. The "Z" */
+ int i, j; /* Loop counters */
+ int cnt = 0; /* Number of matching column names */
+ int cntTab = 0; /* Number of matching table names */
+ sqlite3 *db = pParse->db; /* The database connection */
+ struct SrcList_item *pItem; /* Use for looping over pSrcList items */
+ struct SrcList_item *pMatch = 0; /* The matching pSrcList item */
+ NameContext *pTopNC = pNC; /* First namecontext in the list */
+ Schema *pSchema = 0; /* Schema of the expression */
+
+ assert( pColumnToken && pColumnToken->z ); /* The Z in X.Y.Z cannot be NULL */
+
+ /* Dequote and zero-terminate the names */
+ zDb = sqlite3NameFromToken(db, pDbToken);
+ zTab = sqlite3NameFromToken(db, pTableToken);
+ zCol = sqlite3NameFromToken(db, pColumnToken);
+ if( db->mallocFailed ){
+ goto lookupname_end;
+ }
+
+ /* Initialize the node to no-match */
+ pExpr->iTable = -1;
+ pExpr->pTab = 0;
+
+ /* Start at the inner-most context and move outward until a match is found */
+ while( pNC && cnt==0 ){
+ ExprList *pEList;
+ SrcList *pSrcList = pNC->pSrcList;
+
+ if( pSrcList ){
+ for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){
+ Table *pTab;
+ int iDb;
+ Column *pCol;
+
+ pTab = pItem->pTab;
+ assert( pTab!=0 && pTab->zName!=0 );
+ iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
+ assert( pTab->nCol>0 );
+ if( zTab ){
+ if( pItem->zAlias ){
+ char *zTabName = pItem->zAlias;
+ if( sqlite3StrICmp(zTabName, zTab)!=0 ) continue;
+ }else{
+ char *zTabName = pTab->zName;
+ if( zTabName==0 || sqlite3StrICmp(zTabName, zTab)!=0 ) continue;
+ if( zDb!=0 && sqlite3StrICmp(db->aDb[iDb].zName, zDb)!=0 ){
+ continue;
+ }
+ }
+ }
+ if( 0==(cntTab++) ){
+ pExpr->iTable = pItem->iCursor;
+ pExpr->pTab = pTab;
+ pSchema = pTab->pSchema;
+ pMatch = pItem;
+ }
+ for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){
+ if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
+ IdList *pUsing;
+ cnt++;
+ pExpr->iTable = pItem->iCursor;
+ pExpr->pTab = pTab;
+ pMatch = pItem;
+ pSchema = pTab->pSchema;
+ /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */
+ pExpr->iColumn = j==pTab->iPKey ? -1 : j;
+ if( i<pSrcList->nSrc-1 ){
+ if( pItem[1].jointype & JT_NATURAL ){
+ /* If this match occurred in the left table of a natural join,
+ ** then skip the right table to avoid a duplicate match */
+ pItem++;
+ i++;
+ }else if( (pUsing = pItem[1].pUsing)!=0 ){
+ /* If this match occurs on a column that is in the USING clause
+ ** of a join, skip the search of the right table of the join
+ ** to avoid a duplicate match there. */
+ int k;
+ for(k=0; k<pUsing->nId; k++){
+ if( sqlite3StrICmp(pUsing->a[k].zName, zCol)==0 ){
+ pItem++;
+ i++;
+ break;
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+
+#ifndef SQLITE_OMIT_TRIGGER
+ /* If we have not already resolved the name, then maybe
+ ** it is a new.* or old.* trigger argument reference
+ */
+ if( zDb==0 && zTab!=0 && cnt==0 && pParse->trigStack!=0 ){
+ TriggerStack *pTriggerStack = pParse->trigStack;
+ Table *pTab = 0;
+ u32 *piColMask;
+ if( pTriggerStack->newIdx != -1 && sqlite3StrICmp("new", zTab) == 0 ){
+ pExpr->iTable = pTriggerStack->newIdx;
+ assert( pTriggerStack->pTab );
+ pTab = pTriggerStack->pTab;
+ piColMask = &(pTriggerStack->newColMask);
+ }else if( pTriggerStack->oldIdx != -1 && sqlite3StrICmp("old", zTab)==0 ){
+ pExpr->iTable = pTriggerStack->oldIdx;
+ assert( pTriggerStack->pTab );
+ pTab = pTriggerStack->pTab;
+ piColMask = &(pTriggerStack->oldColMask);
+ }
+
+ if( pTab ){
+ int iCol;
+ Column *pCol = pTab->aCol;
+
+ pSchema = pTab->pSchema;
+ cntTab++;
+ for(iCol=0; iCol < pTab->nCol; iCol++, pCol++) {
+ if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
+ cnt++;
+ pExpr->iColumn = iCol==pTab->iPKey ? -1 : iCol;
+ pExpr->pTab = pTab;
+ if( iCol>=0 ){
+ testcase( iCol==31 );
+ testcase( iCol==32 );
+ *piColMask |= ((u32)1<<iCol) | (iCol>=32?0xffffffff:0);
+ }
+ break;
+ }
+ }
+ }
+ }
+#endif /* !defined(SQLITE_OMIT_TRIGGER) */
+
+ /*
+ ** Perhaps the name is a reference to the ROWID
+ */
+ if( cnt==0 && cntTab==1 && sqlite3IsRowid(zCol) ){
+ cnt = 1;
+ pExpr->iColumn = -1;
+ pExpr->affinity = SQLITE_AFF_INTEGER;
+ }
+
+ /*
+ ** If the input is of the form Z (not Y.Z or X.Y.Z) then the name Z
+ ** might refer to an result-set alias. This happens, for example, when
+ ** we are resolving names in the WHERE clause of the following command:
+ **
+ ** SELECT a+b AS x FROM table WHERE x<10;
+ **
+ ** In cases like this, replace pExpr with a copy of the expression that
+ ** forms the result set entry ("a+b" in the example) and return immediately.
+ ** Note that the expression in the result set should have already been
+ ** resolved by the time the WHERE clause is resolved.
+ */
+ if( cnt==0 && (pEList = pNC->pEList)!=0 && zTab==0 ){
+ for(j=0; j<pEList->nExpr; j++){
+ char *zAs = pEList->a[j].zName;
+ if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){
+ Expr *pOrig;
+ assert( pExpr->pLeft==0 && pExpr->pRight==0 );
+ assert( pExpr->pList==0 );
+ assert( pExpr->pSelect==0 );
+ pOrig = pEList->a[j].pExpr;
+ if( !pNC->allowAgg && ExprHasProperty(pOrig, EP_Agg) ){
+ sqlite3ErrorMsg(pParse, "misuse of aliased aggregate %s", zAs);
+ sqlite3DbFree(db, zCol);
+ return 2;
+ }
+ resolveAlias(pParse, pEList, j, pExpr, "");
+ cnt = 1;
+ pMatch = 0;
+ assert( zTab==0 && zDb==0 );
+ goto lookupname_end_2;
+ }
+ }
+ }
+
+ /* Advance to the next name context. The loop will exit when either
+ ** we have a match (cnt>0) or when we run out of name contexts.
+ */
+ if( cnt==0 ){
+ pNC = pNC->pNext;
+ }
+ }
+
+ /*
+ ** If X and Y are NULL (in other words if only the column name Z is
+ ** supplied) and the value of Z is enclosed in double-quotes, then
+ ** Z is a string literal if it doesn't match any column names. In that
+ ** case, we need to return right away and not make any changes to
+ ** pExpr.
+ **
+ ** Because no reference was made to outer contexts, the pNC->nRef
+ ** fields are not changed in any context.
+ */
+ if( cnt==0 && zTab==0 && pColumnToken->z[0]=='"' ){
+ sqlite3DbFree(db, zCol);
+ pExpr->op = TK_STRING;
+ return 0;
+ }
+
+ /*
+ ** cnt==0 means there was not match. cnt>1 means there were two or
+ ** more matches. Either way, we have an error.
+ */
+ if( cnt!=1 ){
+ const char *zErr;
+ zErr = cnt==0 ? "no such column" : "ambiguous column name";
+ if( zDb ){
+ sqlite3ErrorMsg(pParse, "%s: %s.%s.%s", zErr, zDb, zTab, zCol);
+ }else if( zTab ){
+ sqlite3ErrorMsg(pParse, "%s: %s.%s", zErr, zTab, zCol);
+ }else{
+ sqlite3ErrorMsg(pParse, "%s: %s", zErr, zCol);
+ }
+ pTopNC->nErr++;
+ }
+
+ /* If a column from a table in pSrcList is referenced, then record
+ ** this fact in the pSrcList.a[].colUsed bitmask. Column 0 causes
+ ** bit 0 to be set. Column 1 sets bit 1. And so forth. If the
+ ** column number is greater than the number of bits in the bitmask
+ ** then set the high-order bit of the bitmask.
+ */
+ if( pExpr->iColumn>=0 && pMatch!=0 ){
+ int n = pExpr->iColumn;
+ testcase( n==sizeof(Bitmask)*8-1 );
+ if( n>=sizeof(Bitmask)*8 ){
+ n = sizeof(Bitmask)*8-1;
+ }
+ assert( pMatch->iCursor==pExpr->iTable );
+ pMatch->colUsed |= ((Bitmask)1)<<n;
+ }
+
+lookupname_end:
+ /* Clean up and return
+ */
+ sqlite3DbFree(db, zDb);
+ sqlite3DbFree(db, zTab);
+ sqlite3ExprDelete(db, pExpr->pLeft);
+ pExpr->pLeft = 0;
+ sqlite3ExprDelete(db, pExpr->pRight);
+ pExpr->pRight = 0;
+ pExpr->op = TK_COLUMN;
+lookupname_end_2:
+ sqlite3DbFree(db, zCol);
+ if( cnt==1 ){
+ assert( pNC!=0 );
+ sqlite3AuthRead(pParse, pExpr, pSchema, pNC->pSrcList);
+ /* Increment the nRef value on all name contexts from TopNC up to
+ ** the point where the name matched. */
+ for(;;){
+ assert( pTopNC!=0 );
+ pTopNC->nRef++;
+ if( pTopNC==pNC ) break;
+ pTopNC = pTopNC->pNext;
+ }
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+/*
+** This routine is callback for sqlite3WalkExpr().
+**
+** Resolve symbolic names into TK_COLUMN operators for the current
+** node in the expression tree. Return 0 to continue the search down
+** the tree or 2 to abort the tree walk.
+**
+** This routine also does error checking and name resolution for
+** function names. The operator for aggregate functions is changed
+** to TK_AGG_FUNCTION.
+*/
+static int resolveExprStep(Walker *pWalker, Expr *pExpr){
+ NameContext *pNC;
+ Parse *pParse;
+
+ pNC = pWalker->u.pNC;
+ assert( pNC!=0 );
+ pParse = pNC->pParse;
+ assert( pParse==pWalker->pParse );
+
+ if( ExprHasAnyProperty(pExpr, EP_Resolved) ) return WRC_Prune;
+ ExprSetProperty(pExpr, EP_Resolved);
+#ifndef NDEBUG
+ if( pNC->pSrcList && pNC->pSrcList->nAlloc>0 ){
+ SrcList *pSrcList = pNC->pSrcList;
+ int i;
+ for(i=0; i<pNC->pSrcList->nSrc; i++){
+ assert( pSrcList->a[i].iCursor>=0 && pSrcList->a[i].iCursor<pParse->nTab);
+ }
+ }
+#endif
+ switch( pExpr->op ){
+ /* A lone identifier is the name of a column.
+ */
+ case TK_ID: {
+ lookupName(pParse, 0, 0, &pExpr->token, pNC, pExpr);
+ return WRC_Prune;
+ }
+
+ /* A table name and column name: ID.ID
+ ** Or a database, table and column: ID.ID.ID
+ */
+ case TK_DOT: {
+ Token *pColumn;
+ Token *pTable;
+ Token *pDb;
+ Expr *pRight;
+
+ /* if( pSrcList==0 ) break; */
+ pRight = pExpr->pRight;
+ if( pRight->op==TK_ID ){
+ pDb = 0;
+ pTable = &pExpr->pLeft->token;
+ pColumn = &pRight->token;
+ }else{
+ assert( pRight->op==TK_DOT );
+ pDb = &pExpr->pLeft->token;
+ pTable = &pRight->pLeft->token;
+ pColumn = &pRight->pRight->token;
+ }
+ lookupName(pParse, pDb, pTable, pColumn, pNC, pExpr);
+ return WRC_Prune;
+ }
+
+ /* Resolve function names
+ */
+ case TK_CONST_FUNC:
+ case TK_FUNCTION: {
+ ExprList *pList = pExpr->pList; /* The argument list */
+ int n = pList ? pList->nExpr : 0; /* Number of arguments */
+ int no_such_func = 0; /* True if no such function exists */
+ int wrong_num_args = 0; /* True if wrong number of arguments */
+ int is_agg = 0; /* True if is an aggregate function */
+ int auth; /* Authorization to use the function */
+ int nId; /* Number of characters in function name */
+ const char *zId; /* The function name. */
+ FuncDef *pDef; /* Information about the function */
+ int enc = ENC(pParse->db); /* The database encoding */
+
+ zId = (char*)pExpr->token.z;
+ nId = pExpr->token.n;
+ pDef = sqlite3FindFunction(pParse->db, zId, nId, n, enc, 0);
+ if( pDef==0 ){
+ pDef = sqlite3FindFunction(pParse->db, zId, nId, -1, enc, 0);
+ if( pDef==0 ){
+ no_such_func = 1;
+ }else{
+ wrong_num_args = 1;
+ }
+ }else{
+ is_agg = pDef->xFunc==0;
+ }
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ if( pDef ){
+ auth = sqlite3AuthCheck(pParse, SQLITE_FUNCTION, 0, pDef->zName, 0);
+ if( auth!=SQLITE_OK ){
+ if( auth==SQLITE_DENY ){
+ sqlite3ErrorMsg(pParse, "not authorized to use function: %s",
+ pDef->zName);
+ pNC->nErr++;
+ }
+ pExpr->op = TK_NULL;
+ return WRC_Prune;
+ }
+ }
+#endif
+ if( is_agg && !pNC->allowAgg ){
+ sqlite3ErrorMsg(pParse, "misuse of aggregate function %.*s()", nId,zId);
+ pNC->nErr++;
+ is_agg = 0;
+ }else if( no_such_func ){
+ sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId);
+ pNC->nErr++;
+ }else if( wrong_num_args ){
+ sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()",
+ nId, zId);
+ pNC->nErr++;
+ }
+ if( is_agg ){
+ pExpr->op = TK_AGG_FUNCTION;
+ pNC->hasAgg = 1;
+ }
+ if( is_agg ) pNC->allowAgg = 0;
+ sqlite3WalkExprList(pWalker, pList);
+ if( is_agg ) pNC->allowAgg = 1;
+ /* FIX ME: Compute pExpr->affinity based on the expected return
+ ** type of the function
+ */
+ return WRC_Prune;
+ }
+#ifndef SQLITE_OMIT_SUBQUERY
+ case TK_SELECT:
+ case TK_EXISTS:
+#endif
+ case TK_IN: {
+ if( pExpr->pSelect ){
+ int nRef = pNC->nRef;
+#ifndef SQLITE_OMIT_CHECK
+ if( pNC->isCheck ){
+ sqlite3ErrorMsg(pParse,"subqueries prohibited in CHECK constraints");
+ }
+#endif
+ sqlite3WalkSelect(pWalker, pExpr->pSelect);
+ assert( pNC->nRef>=nRef );
+ if( nRef!=pNC->nRef ){
+ ExprSetProperty(pExpr, EP_VarSelect);
+ }
+ }
+ break;
+ }
+#ifndef SQLITE_OMIT_CHECK
+ case TK_VARIABLE: {
+ if( pNC->isCheck ){
+ sqlite3ErrorMsg(pParse,"parameters prohibited in CHECK constraints");
+ }
+ break;
+ }
+#endif
+ }
+ return (pParse->nErr || pParse->db->mallocFailed) ? WRC_Abort : WRC_Continue;
}
/*
-** Query a blob handle for the size of the data.
+** pEList is a list of expressions which are really the result set of the
+** a SELECT statement. pE is a term in an ORDER BY or GROUP BY clause.
+** This routine checks to see if pE is a simple identifier which corresponds
+** to the AS-name of one of the terms of the expression list. If it is,
+** this routine return an integer between 1 and N where N is the number of
+** elements in pEList, corresponding to the matching entry. If there is
+** no match, or if pE is not a simple identifier, then this routine
+** return 0.
**
-** The Incrblob.nByte field is fixed for the lifetime of the Incrblob
-** so no mutex is required for access.
+** pEList has been resolved. pE has not.
*/
-SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *pBlob){
- Incrblob *p = (Incrblob *)pBlob;
- return p->nByte;
-}
+static int resolveAsName(
+ Parse *pParse, /* Parsing context for error messages */
+ ExprList *pEList, /* List of expressions to scan */
+ Expr *pE /* Expression we are trying to match */
+){
+ int i; /* Loop counter */
-#endif /* #ifndef SQLITE_OMIT_INCRBLOB */
+ if( pE->op==TK_ID || (pE->op==TK_STRING && pE->token.z[0]!='\'') ){
+ sqlite3 *db = pParse->db;
+ char *zCol = sqlite3NameFromToken(db, &pE->token);
+ if( zCol==0 ){
+ return -1;
+ }
+ for(i=0; i<pEList->nExpr; i++){
+ char *zAs = pEList->a[i].zName;
+ if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){
+ sqlite3DbFree(db, zCol);
+ return i+1;
+ }
+ }
+ sqlite3DbFree(db, zCol);
+ }
+ return 0;
+}
-/************** End of vdbeblob.c ********************************************/
-/************** Begin file journal.c *****************************************/
/*
-** 2007 August 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.
-**
-*************************************************************************
+** pE is a pointer to an expression which is a single term in the
+** ORDER BY of a compound SELECT. The expression has not been
+** name resolved.
**
-** @(#) $Id: journal.c,v 1.8 2008/05/01 18:01:47 drh Exp $
-*/
-
-#ifdef SQLITE_ENABLE_ATOMIC_WRITE
-
-/*
-** This file implements a special kind of sqlite3_file object used
-** by SQLite to create journal files if the atomic-write optimization
-** is enabled.
+** At the point this routine is called, we already know that the
+** ORDER BY term is not an integer index into the result set. That
+** case is handled by the calling routine.
**
-** The distinctive characteristic of this sqlite3_file is that the
-** actual on disk file is created lazily. When the file is created,
-** the caller specifies a buffer size for an in-memory buffer to
-** be used to service read() and write() requests. The actual file
-** on disk is not created or populated until either:
+** Attempt to match pE against result set columns in the left-most
+** SELECT statement. Return the index i of the matching column,
+** as an indication to the caller that it should sort by the i-th column.
+** The left-most column is 1. In other words, the value returned is the
+** same integer value that would be used in the SQL statement to indicate
+** the column.
**
-** 1) The in-memory representation grows too large for the allocated
-** buffer, or
-** 2) The xSync() method is called.
+** If there is no match, return 0. Return -1 if an error occurs.
*/
+static int resolveOrderByTermToExprList(
+ Parse *pParse, /* Parsing context for error messages */
+ Select *pSelect, /* The SELECT statement with the ORDER BY clause */
+ Expr *pE /* The specific ORDER BY term */
+){
+ int i; /* Loop counter */
+ ExprList *pEList; /* The columns of the result set */
+ NameContext nc; /* Name context for resolving pE */
+ assert( sqlite3ExprIsInteger(pE, &i)==0 );
+ pEList = pSelect->pEList;
+ /* Resolve all names in the ORDER BY term expression
+ */
+ memset(&nc, 0, sizeof(nc));
+ nc.pParse = pParse;
+ nc.pSrcList = pSelect->pSrc;
+ nc.pEList = pEList;
+ nc.allowAgg = 1;
+ nc.nErr = 0;
+ if( sqlite3ResolveExprNames(&nc, pE) ){
+ sqlite3ErrorClear(pParse);
+ return 0;
+ }
-/*
-** A JournalFile object is a subclass of sqlite3_file used by
-** as an open file handle for journal files.
-*/
-struct JournalFile {
- sqlite3_io_methods *pMethod; /* I/O methods on journal files */
- int nBuf; /* Size of zBuf[] in bytes */
- char *zBuf; /* Space to buffer journal writes */
- int iSize; /* Amount of zBuf[] currently used */
- int flags; /* xOpen flags */
- sqlite3_vfs *pVfs; /* The "real" underlying VFS */
- sqlite3_file *pReal; /* The "real" underlying file descriptor */
- const char *zJournal; /* Name of the journal file */
-};
-typedef struct JournalFile JournalFile;
-
-/*
-** If it does not already exists, create and populate the on-disk file
-** for JournalFile p.
-*/
-static int createFile(JournalFile *p){
- int rc = SQLITE_OK;
- if( !p->pReal ){
- sqlite3_file *pReal = (sqlite3_file *)&p[1];
- rc = sqlite3OsOpen(p->pVfs, p->zJournal, pReal, p->flags, 0);
- if( rc==SQLITE_OK ){
- p->pReal = pReal;
- if( p->iSize>0 ){
- assert(p->iSize<=p->nBuf);
- rc = sqlite3OsWrite(p->pReal, p->zBuf, p->iSize, 0);
- }
+ /* Try to match the ORDER BY expression against an expression
+ ** in the result set. Return an 1-based index of the matching
+ ** result-set entry.
+ */
+ for(i=0; i<pEList->nExpr; i++){
+ if( sqlite3ExprCompare(pEList->a[i].pExpr, pE) ){
+ return i+1;
}
}
- return rc;
+
+ /* If no match, return 0. */
+ return 0;
}
/*
-** Close the file.
+** Generate an ORDER BY or GROUP BY term out-of-range error.
*/
-static int jrnlClose(sqlite3_file *pJfd){
- JournalFile *p = (JournalFile *)pJfd;
- if( p->pReal ){
- sqlite3OsClose(p->pReal);
- }
- sqlite3_free(p->zBuf);
- return SQLITE_OK;
+static void resolveOutOfRangeError(
+ Parse *pParse, /* The error context into which to write the error */
+ const char *zType, /* "ORDER" or "GROUP" */
+ int i, /* The index (1-based) of the term out of range */
+ int mx /* Largest permissible value of i */
+){
+ sqlite3ErrorMsg(pParse,
+ "%r %s BY term out of range - should be "
+ "between 1 and %d", i, zType, mx);
}
/*
-** Read data from the file.
+** Analyze the ORDER BY clause in a compound SELECT statement. Modify
+** each term of the ORDER BY clause is a constant integer between 1
+** and N where N is the number of columns in the compound SELECT.
+**
+** ORDER BY terms that are already an integer between 1 and N are
+** unmodified. ORDER BY terms that are integers outside the range of
+** 1 through N generate an error. ORDER BY terms that are expressions
+** are matched against result set expressions of compound SELECT
+** beginning with the left-most SELECT and working toward the right.
+** At the first match, the ORDER BY expression is transformed into
+** the integer column number.
+**
+** Return the number of errors seen.
*/
-static int jrnlRead(
- sqlite3_file *pJfd, /* The journal file from which to read */
- void *zBuf, /* Put the results here */
- int iAmt, /* Number of bytes to read */
- sqlite_int64 iOfst /* Begin reading at this offset */
+static int resolveCompoundOrderBy(
+ Parse *pParse, /* Parsing context. Leave error messages here */
+ Select *pSelect /* The SELECT statement containing the ORDER BY */
){
- int rc = SQLITE_OK;
- JournalFile *p = (JournalFile *)pJfd;
- if( p->pReal ){
- rc = sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst);
- }else{
- assert( iAmt+iOfst<=p->iSize );
- memcpy(zBuf, &p->zBuf[iOfst], iAmt);
+ int i;
+ ExprList *pOrderBy;
+ ExprList *pEList;
+ sqlite3 *db;
+ int moreToDo = 1;
+
+ pOrderBy = pSelect->pOrderBy;
+ if( pOrderBy==0 ) return 0;
+ db = pParse->db;
+#if SQLITE_MAX_COLUMN
+ if( pOrderBy->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){
+ sqlite3ErrorMsg(pParse, "too many terms in ORDER BY clause");
+ return 1;
}
- return rc;
+#endif
+ for(i=0; i<pOrderBy->nExpr; i++){
+ pOrderBy->a[i].done = 0;
+ }
+ pSelect->pNext = 0;
+ while( pSelect->pPrior ){
+ pSelect->pPrior->pNext = pSelect;
+ pSelect = pSelect->pPrior;
+ }
+ while( pSelect && moreToDo ){
+ struct ExprList_item *pItem;
+ moreToDo = 0;
+ pEList = pSelect->pEList;
+ assert( pEList!=0 );
+ for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
+ int iCol = -1;
+ Expr *pE, *pDup;
+ if( pItem->done ) continue;
+ pE = pItem->pExpr;
+ if( sqlite3ExprIsInteger(pE, &iCol) ){
+ if( iCol<0 || iCol>pEList->nExpr ){
+ resolveOutOfRangeError(pParse, "ORDER", i+1, pEList->nExpr);
+ return 1;
+ }
+ }else{
+ iCol = resolveAsName(pParse, pEList, pE);
+ if( iCol==0 ){
+ pDup = sqlite3ExprDup(db, pE);
+ if( !db->mallocFailed ){
+ assert(pDup);
+ iCol = resolveOrderByTermToExprList(pParse, pSelect, pDup);
+ }
+ sqlite3ExprDelete(db, pDup);
+ }
+ if( iCol<0 ){
+ return 1;
+ }
+ }
+ if( iCol>0 ){
+ CollSeq *pColl = pE->pColl;
+ int flags = pE->flags & EP_ExpCollate;
+ sqlite3ExprDelete(db, pE);
+ pItem->pExpr = pE = sqlite3Expr(db, TK_INTEGER, 0, 0, 0);
+ if( pE==0 ) return 1;
+ pE->pColl = pColl;
+ pE->flags |= EP_IntValue | flags;
+ pE->iTable = iCol;
+ pItem->iCol = iCol;
+ pItem->done = 1;
+ }else{
+ moreToDo = 1;
+ }
+ }
+ pSelect = pSelect->pNext;
+ }
+ for(i=0; i<pOrderBy->nExpr; i++){
+ if( pOrderBy->a[i].done==0 ){
+ sqlite3ErrorMsg(pParse, "%r ORDER BY term does not match any "
+ "column in the result set", i+1);
+ return 1;
+ }
+ }
+ return 0;
}
/*
-** Write data to the file.
+** Check every term in the ORDER BY or GROUP BY clause pOrderBy of
+** the SELECT statement pSelect. If any term is reference to a
+** result set expression (as determined by the ExprList.a.iCol field)
+** then convert that term into a copy of the corresponding result set
+** column.
+**
+** If any errors are detected, add an error message to pParse and
+** return non-zero. Return zero if no errors are seen.
*/
-static int jrnlWrite(
- sqlite3_file *pJfd, /* The journal file into which to write */
- const void *zBuf, /* Take data to be written from here */
- int iAmt, /* Number of bytes to write */
- sqlite_int64 iOfst /* Begin writing at this offset into the file */
+SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(
+ Parse *pParse, /* Parsing context. Leave error messages here */
+ Select *pSelect, /* The SELECT statement containing the clause */
+ ExprList *pOrderBy, /* The ORDER BY or GROUP BY clause to be processed */
+ const char *zType /* "ORDER" or "GROUP" */
){
- int rc = SQLITE_OK;
- JournalFile *p = (JournalFile *)pJfd;
- if( !p->pReal && (iOfst+iAmt)>p->nBuf ){
- rc = createFile(p);
+ int i;
+ sqlite3 *db = pParse->db;
+ ExprList *pEList;
+ struct ExprList_item *pItem;
+
+ if( pOrderBy==0 || pParse->db->mallocFailed ) return 0;
+#if SQLITE_MAX_COLUMN
+ if( pOrderBy->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){
+ sqlite3ErrorMsg(pParse, "too many terms in %s BY clause", zType);
+ return 1;
}
- if( rc==SQLITE_OK ){
- if( p->pReal ){
- rc = sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst);
- }else{
- memcpy(&p->zBuf[iOfst], zBuf, iAmt);
- if( p->iSize<(iOfst+iAmt) ){
- p->iSize = (iOfst+iAmt);
+#endif
+ pEList = pSelect->pEList;
+ assert( pEList!=0 ); /* sqlite3SelectNew() guarantees this */
+ for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
+ if( pItem->iCol ){
+ if( pItem->iCol>pEList->nExpr ){
+ resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr);
+ return 1;
}
+ resolveAlias(pParse, pEList, pItem->iCol-1, pItem->pExpr, zType);
}
}
- return rc;
+ return 0;
}
/*
-** Truncate the file.
-*/
-static int jrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
- int rc = SQLITE_OK;
- JournalFile *p = (JournalFile *)pJfd;
- if( p->pReal ){
- rc = sqlite3OsTruncate(p->pReal, size);
- }else if( size<p->iSize ){
- p->iSize = size;
+** pOrderBy is an ORDER BY or GROUP BY clause in SELECT statement pSelect.
+** The Name context of the SELECT statement is pNC. zType is either
+** "ORDER" or "GROUP" depending on which type of clause pOrderBy is.
+**
+** This routine resolves each term of the clause into an expression.
+** If the order-by term is an integer I between 1 and N (where N is the
+** number of columns in the result set of the SELECT) then the expression
+** in the resolution is a copy of the I-th result-set expression. If
+** the order-by term is an identify that corresponds to the AS-name of
+** a result-set expression, then the term resolves to a copy of the
+** result-set expression. Otherwise, the expression is resolved in
+** the usual way - using sqlite3ResolveExprNames().
+**
+** This routine returns the number of errors. If errors occur, then
+** an appropriate error message might be left in pParse. (OOM errors
+** excepted.)
+*/
+static int resolveOrderGroupBy(
+ NameContext *pNC, /* The name context of the SELECT statement */
+ Select *pSelect, /* The SELECT statement holding pOrderBy */
+ ExprList *pOrderBy, /* An ORDER BY or GROUP BY clause to resolve */
+ const char *zType /* Either "ORDER" or "GROUP", as appropriate */
+){
+ int i; /* Loop counter */
+ int iCol; /* Column number */
+ struct ExprList_item *pItem; /* A term of the ORDER BY clause */
+ Parse *pParse; /* Parsing context */
+ int nResult; /* Number of terms in the result set */
+
+ if( pOrderBy==0 ) return 0;
+ nResult = pSelect->pEList->nExpr;
+ pParse = pNC->pParse;
+ for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
+ Expr *pE = pItem->pExpr;
+ iCol = resolveAsName(pParse, pSelect->pEList, pE);
+ if( iCol<0 ){
+ return 1; /* OOM error */
+ }
+ if( iCol>0 ){
+ /* If an AS-name match is found, mark this ORDER BY column as being
+ ** a copy of the iCol-th result-set column. The subsequent call to
+ ** sqlite3ResolveOrderGroupBy() will convert the expression to a
+ ** copy of the iCol-th result-set expression. */
+ pItem->iCol = iCol;
+ continue;
+ }
+ if( sqlite3ExprIsInteger(pE, &iCol) ){
+ /* The ORDER BY term is an integer constant. Again, set the column
+ ** number so that sqlite3ResolveOrderGroupBy() will convert the
+ ** order-by term to a copy of the result-set expression */
+ if( iCol<1 ){
+ resolveOutOfRangeError(pParse, zType, i+1, nResult);
+ return 1;
+ }
+ pItem->iCol = iCol;
+ continue;
+ }
+
+ /* Otherwise, treat the ORDER BY term as an ordinary expression */
+ pItem->iCol = 0;
+ if( sqlite3ResolveExprNames(pNC, pE) ){
+ return 1;
+ }
}
- return rc;
+ return sqlite3ResolveOrderGroupBy(pParse, pSelect, pOrderBy, zType);
}
/*
-** Sync the file.
+** Resolve names in the SELECT statement p and all of its descendents.
*/
-static int jrnlSync(sqlite3_file *pJfd, int flags){
- int rc;
- JournalFile *p = (JournalFile *)pJfd;
- if( p->pReal ){
- rc = sqlite3OsSync(p->pReal, flags);
- }else{
- rc = SQLITE_OK;
+static int resolveSelectStep(Walker *pWalker, Select *p){
+ NameContext *pOuterNC; /* Context that contains this SELECT */
+ NameContext sNC; /* Name context of this SELECT */
+ int isCompound; /* True if p is a compound select */
+ int nCompound; /* Number of compound terms processed so far */
+ Parse *pParse; /* Parsing context */
+ ExprList *pEList; /* Result set expression list */
+ int i; /* Loop counter */
+ ExprList *pGroupBy; /* The GROUP BY clause */
+ Select *pLeftmost; /* Left-most of SELECT of a compound */
+ sqlite3 *db; /* Database connection */
+
+
+ assert( p!=0 );
+ if( p->selFlags & SF_Resolved ){
+ return WRC_Prune;
}
- return rc;
-}
+ pOuterNC = pWalker->u.pNC;
+ pParse = pWalker->pParse;
+ db = pParse->db;
-/*
-** Query the size of the file in bytes.
-*/
-static int jrnlFileSize(sqlite3_file *pJfd, sqlite_int64 *pSize){
- int rc = SQLITE_OK;
- JournalFile *p = (JournalFile *)pJfd;
- if( p->pReal ){
- rc = sqlite3OsFileSize(p->pReal, pSize);
- }else{
- *pSize = (sqlite_int64) p->iSize;
+ /* Normally sqlite3SelectExpand() will be called first and will have
+ ** already expanded this SELECT. However, if this is a subquery within
+ ** an expression, sqlite3ResolveExprNames() will be called without a
+ ** prior call to sqlite3SelectExpand(). When that happens, let
+ ** sqlite3SelectPrep() do all of the processing for this SELECT.
+ ** sqlite3SelectPrep() will invoke both sqlite3SelectExpand() and
+ ** this routine in the correct order.
+ */
+ if( (p->selFlags & SF_Expanded)==0 ){
+ sqlite3SelectPrep(pParse, p, pOuterNC);
+ return (pParse->nErr || db->mallocFailed) ? WRC_Abort : WRC_Prune;
+ }
+
+ isCompound = p->pPrior!=0;
+ nCompound = 0;
+ pLeftmost = p;
+ while( p ){
+ assert( (p->selFlags & SF_Expanded)!=0 );
+ assert( (p->selFlags & SF_Resolved)==0 );
+ p->selFlags |= SF_Resolved;
+
+ /* Resolve the expressions in the LIMIT and OFFSET clauses. These
+ ** are not allowed to refer to any names, so pass an empty NameContext.
+ */
+ memset(&sNC, 0, sizeof(sNC));
+ sNC.pParse = pParse;
+ if( sqlite3ResolveExprNames(&sNC, p->pLimit) ||
+ sqlite3ResolveExprNames(&sNC, p->pOffset) ){
+ return WRC_Abort;
+ }
+
+ /* Set up the local name-context to pass to sqlite3ResolveExprNames() to
+ ** resolve the result-set expression list.
+ */
+ sNC.allowAgg = 1;
+ sNC.pSrcList = p->pSrc;
+ sNC.pNext = pOuterNC;
+
+ /* Resolve names in the result set. */
+ pEList = p->pEList;
+ assert( pEList!=0 );
+ for(i=0; i<pEList->nExpr; i++){
+ Expr *pX = pEList->a[i].pExpr;
+ if( sqlite3ResolveExprNames(&sNC, pX) ){
+ return WRC_Abort;
+ }
+ }
+
+ /* Recursively resolve names in all subqueries
+ */
+ for(i=0; i<p->pSrc->nSrc; i++){
+ struct SrcList_item *pItem = &p->pSrc->a[i];
+ if( pItem->pSelect ){
+ const char *zSavedContext = pParse->zAuthContext;
+ if( pItem->zName ) pParse->zAuthContext = pItem->zName;
+ sqlite3ResolveSelectNames(pParse, pItem->pSelect, &sNC);
+ pParse->zAuthContext = zSavedContext;
+ if( pParse->nErr || db->mallocFailed ) return WRC_Abort;
+ }
+ }
+
+ /* If there are no aggregate functions in the result-set, and no GROUP BY
+ ** expression, do not allow aggregates in any of the other expressions.
+ */
+ assert( (p->selFlags & SF_Aggregate)==0 );
+ pGroupBy = p->pGroupBy;
+ if( pGroupBy || sNC.hasAgg ){
+ p->selFlags |= SF_Aggregate;
+ }else{
+ sNC.allowAgg = 0;
+ }
+
+ /* If a HAVING clause is present, then there must be a GROUP BY clause.
+ */
+ if( p->pHaving && !pGroupBy ){
+ sqlite3ErrorMsg(pParse, "a GROUP BY clause is required before HAVING");
+ return WRC_Abort;
+ }
+
+ /* Add the expression list to the name-context before parsing the
+ ** other expressions in the SELECT statement. This is so that
+ ** expressions in the WHERE clause (etc.) can refer to expressions by
+ ** aliases in the result set.
+ **
+ ** Minor point: If this is the case, then the expression will be
+ ** re-evaluated for each reference to it.
+ */
+ sNC.pEList = p->pEList;
+ if( sqlite3ResolveExprNames(&sNC, p->pWhere) ||
+ sqlite3ResolveExprNames(&sNC, p->pHaving)
+ ){
+ return WRC_Abort;
+ }
+
+ /* The ORDER BY and GROUP BY clauses may not refer to terms in
+ ** outer queries
+ */
+ sNC.pNext = 0;
+ sNC.allowAgg = 1;
+
+ /* Process the ORDER BY clause for singleton SELECT statements.
+ ** The ORDER BY clause for compounds SELECT statements is handled
+ ** below, after all of the result-sets for all of the elements of
+ ** the compound have been resolved.
+ */
+ if( !isCompound && resolveOrderGroupBy(&sNC, p, p->pOrderBy, "ORDER") ){
+ return WRC_Abort;
+ }
+ if( db->mallocFailed ){
+ return WRC_Abort;
+ }
+
+ /* Resolve the GROUP BY clause. At the same time, make sure
+ ** the GROUP BY clause does not contain aggregate functions.
+ */
+ if( pGroupBy ){
+ struct ExprList_item *pItem;
+
+ if( resolveOrderGroupBy(&sNC, p, pGroupBy, "GROUP") || db->mallocFailed ){
+ return WRC_Abort;
+ }
+ for(i=0, pItem=pGroupBy->a; i<pGroupBy->nExpr; i++, pItem++){
+ if( ExprHasProperty(pItem->pExpr, EP_Agg) ){
+ sqlite3ErrorMsg(pParse, "aggregate functions are not allowed in "
+ "the GROUP BY clause");
+ return WRC_Abort;
+ }
+ }
+ }
+
+ /* Advance to the next term of the compound
+ */
+ p = p->pPrior;
+ nCompound++;
}
- return rc;
+
+ /* Resolve the ORDER BY on a compound SELECT after all terms of
+ ** the compound have been resolved.
+ */
+ if( isCompound && resolveCompoundOrderBy(pParse, pLeftmost) ){
+ return WRC_Abort;
+ }
+
+ return WRC_Prune;
}
/*
-** Table of methods for JournalFile sqlite3_file object.
-*/
-static struct sqlite3_io_methods JournalFileMethods = {
- 1, /* iVersion */
- jrnlClose, /* xClose */
- jrnlRead, /* xRead */
- jrnlWrite, /* xWrite */
- jrnlTruncate, /* xTruncate */
- jrnlSync, /* xSync */
- jrnlFileSize, /* xFileSize */
- 0, /* xLock */
- 0, /* xUnlock */
- 0, /* xCheckReservedLock */
- 0, /* xFileControl */
- 0, /* xSectorSize */
- 0 /* xDeviceCharacteristics */
-};
-
-/*
-** Open a journal file.
+** This routine walks an expression tree and resolves references to
+** table columns and result-set columns. At the same time, do error
+** checking on function usage and set a flag if any aggregate functions
+** are seen.
+**
+** To resolve table columns references we look for nodes (or subtrees) of the
+** form X.Y.Z or Y.Z or just Z where
+**
+** X: The name of a database. Ex: "main" or "temp" or
+** the symbolic name assigned to an ATTACH-ed database.
+**
+** Y: The name of a table in a FROM clause. Or in a trigger
+** one of the special names "old" or "new".
+**
+** Z: The name of a column in table Y.
+**
+** The node at the root of the subtree is modified as follows:
+**
+** Expr.op Changed to TK_COLUMN
+** Expr.pTab Points to the Table object for X.Y
+** Expr.iColumn The column index in X.Y. -1 for the rowid.
+** Expr.iTable The VDBE cursor number for X.Y
+**
+**
+** To resolve result-set references, look for expression nodes of the
+** form Z (with no X and Y prefix) where the Z matches the right-hand
+** size of an AS clause in the result-set of a SELECT. The Z expression
+** is replaced by a copy of the left-hand side of the result-set expression.
+** Table-name and function resolution occurs on the substituted expression
+** tree. For example, in:
+**
+** SELECT a+b AS x, c+d AS y FROM t1 ORDER BY x;
+**
+** The "x" term of the order by is replaced by "a+b" to render:
+**
+** SELECT a+b AS x, c+d AS y FROM t1 ORDER BY a+b;
+**
+** Function calls are checked to make sure that the function is
+** defined and that the correct number of arguments are specified.
+** If the function is an aggregate function, then the pNC->hasAgg is
+** set and the opcode is changed from TK_FUNCTION to TK_AGG_FUNCTION.
+** If an expression contains aggregate functions then the EP_Agg
+** property on the expression is set.
+**
+** An error message is left in pParse if anything is amiss. The number
+** if errors is returned.
*/
-SQLITE_PRIVATE int sqlite3JournalOpen(
- sqlite3_vfs *pVfs, /* The VFS to use for actual file I/O */
- const char *zName, /* Name of the journal file */
- sqlite3_file *pJfd, /* Preallocated, blank file handle */
- int flags, /* Opening flags */
- int nBuf /* Bytes buffered before opening the file */
+SQLITE_PRIVATE int sqlite3ResolveExprNames(
+ NameContext *pNC, /* Namespace to resolve expressions in. */
+ Expr *pExpr /* The expression to be analyzed. */
){
- JournalFile *p = (JournalFile *)pJfd;
- memset(p, 0, sqlite3JournalSize(pVfs));
- if( nBuf>0 ){
- p->zBuf = sqlite3MallocZero(nBuf);
- if( !p->zBuf ){
- return SQLITE_NOMEM;
+ int savedHasAgg;
+ Walker w;
+
+ if( pExpr==0 ) return 0;
+#if SQLITE_MAX_EXPR_DEPTH>0
+ {
+ Parse *pParse = pNC->pParse;
+ if( sqlite3ExprCheckHeight(pParse, pExpr->nHeight+pNC->pParse->nHeight) ){
+ return 1;
}
- }else{
- return sqlite3OsOpen(pVfs, zName, pJfd, flags, 0);
+ pParse->nHeight += pExpr->nHeight;
}
- p->pMethod = &JournalFileMethods;
- p->nBuf = nBuf;
- p->flags = flags;
- p->zJournal = zName;
- p->pVfs = pVfs;
- return SQLITE_OK;
+#endif
+ savedHasAgg = pNC->hasAgg;
+ pNC->hasAgg = 0;
+ w.xExprCallback = resolveExprStep;
+ w.xSelectCallback = resolveSelectStep;
+ w.pParse = pNC->pParse;
+ w.u.pNC = pNC;
+ sqlite3WalkExpr(&w, pExpr);
+#if SQLITE_MAX_EXPR_DEPTH>0
+ pNC->pParse->nHeight -= pExpr->nHeight;
+#endif
+ if( pNC->nErr>0 ){
+ ExprSetProperty(pExpr, EP_Error);
+ }
+ if( pNC->hasAgg ){
+ ExprSetProperty(pExpr, EP_Agg);
+ }else if( savedHasAgg ){
+ pNC->hasAgg = 1;
+ }
+ return ExprHasProperty(pExpr, EP_Error);
}
+
/*
-** If the argument p points to a JournalFile structure, and the underlying
-** file has not yet been created, create it now.
+** Resolve all names in all expressions of a SELECT and in all
+** decendents of the SELECT, including compounds off of p->pPrior,
+** subqueries in expressions, and subqueries used as FROM clause
+** terms.
+**
+** See sqlite3ResolveExprNames() for a description of the kinds of
+** transformations that occur.
+**
+** All SELECT statements should have been expanded using
+** sqlite3SelectExpand() prior to invoking this routine.
*/
-SQLITE_PRIVATE int sqlite3JournalCreate(sqlite3_file *p){
- if( p->pMethods!=&JournalFileMethods ){
- return SQLITE_OK;
- }
- return createFile((JournalFile *)p);
-}
+SQLITE_PRIVATE void sqlite3ResolveSelectNames(
+ Parse *pParse, /* The parser context */
+ Select *p, /* The SELECT statement being coded. */
+ NameContext *pOuterNC /* Name context for parent SELECT statement */
+){
+ Walker w;
-/*
-** Return the number of bytes required to store a JournalFile that uses vfs
-** pVfs to create the underlying on-disk files.
-*/
-SQLITE_PRIVATE int sqlite3JournalSize(sqlite3_vfs *pVfs){
- return (pVfs->szOsFile+sizeof(JournalFile));
+ assert( p!=0 );
+ w.xExprCallback = resolveExprStep;
+ w.xSelectCallback = resolveSelectStep;
+ w.pParse = pParse;
+ w.u.pNC = pOuterNC;
+ sqlite3WalkSelect(&w, p);
}
-#endif
-/************** End of journal.c *********************************************/
+/************** End of resolve.c *********************************************/
/************** Begin file expr.c ********************************************/
/*
** 2001 September 15
@@ -50554,7 +52481,7 @@
** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions in SQLite.
**
-** $Id: expr.c,v 1.387 2008/07/28 19:34:53 drh Exp $
+** $Id: expr.c,v 1.392 2008/08/29 02:14:03 drh Exp $
*/
/*
@@ -50583,6 +52510,14 @@
return sqlite3AffinityType(&pExpr->token);
}
#endif
+ if( (op==TK_COLUMN || op==TK_REGISTER) && pExpr->pTab!=0 ){
+ /* op==TK_REGISTER && pExpr->pTab!=0 happens when pExpr was originally
+ ** a TK_COLUMN but was previously evaluated and cached in a register */
+ int j = pExpr->iColumn;
+ if( j<0 ) return SQLITE_AFF_INTEGER;
+ assert( pExpr->pTab && j<pExpr->pTab->nCol );
+ return pExpr->pTab->aCol[j].affinity;
+ }
return pExpr->affinity;
}
@@ -50593,11 +52528,11 @@
** flag. An explicit collating sequence will override implicit
** collating sequences.
*/
-SQLITE_PRIVATE Expr *sqlite3ExprSetColl(Parse *pParse, Expr *pExpr, Token *pName){
+SQLITE_PRIVATE Expr *sqlite3ExprSetColl(Parse *pParse, Expr *pExpr, Token *pCollName){
char *zColl = 0; /* Dequoted name of collation sequence */
CollSeq *pColl;
sqlite3 *db = pParse->db;
- zColl = sqlite3NameFromToken(db, pName);
+ zColl = sqlite3NameFromToken(db, pCollName);
if( pExpr && zColl ){
pColl = sqlite3LocateCollSeq(pParse, zColl, -1);
if( pColl ){
@@ -50615,13 +52550,29 @@
*/
SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){
CollSeq *pColl = 0;
- if( pExpr ){
+ Expr *p = pExpr;
+ while( p ){
int op;
- pColl = pExpr->pColl;
- op = pExpr->op;
- if( (op==TK_CAST || op==TK_UPLUS) && !pColl ){
- return sqlite3ExprCollSeq(pParse, pExpr->pLeft);
+ pColl = p->pColl;
+ if( pColl ) break;
+ op = p->op;
+ if( (op==TK_COLUMN || op==TK_REGISTER) && p->pTab!=0 ){
+ /* op==TK_REGISTER && p->pTab!=0 happens when pExpr was originally
+ ** a TK_COLUMN but was previously evaluated and cached in a register */
+ const char *zColl;
+ int j = p->iColumn;
+ if( j>=0 ){
+ sqlite3 *db = pParse->db;
+ zColl = p->pTab->aCol[j].zColl;
+ pColl = sqlite3FindCollSeq(db, ENC(db), zColl, -1, 0);
+ pExpr->pColl = pColl;
+ }
+ break;
}
+ if( op!=TK_CAST && op!=TK_UPLUS ){
+ break;
+ }
+ p = p->pLeft;
}
if( sqlite3CheckCollSeq(pParse, pColl) ){
pColl = 0;
@@ -50800,7 +52751,7 @@
** expression depth allowed. If it is not, leave an error message in
** pParse.
*/
-static int checkExprHeight(Parse *pParse, int nHeight){
+SQLITE_PRIVATE int sqlite3ExprCheckHeight(Parse *pParse, int nHeight){
int rc = SQLITE_OK;
int mxHeight = pParse->db->aLimit[SQLITE_LIMIT_EXPR_DEPTH];
if( nHeight>mxHeight ){
@@ -50872,7 +52823,7 @@
*/
SQLITE_PRIVATE void sqlite3ExprSetHeight(Parse *pParse, Expr *p){
exprSetHeight(p);
- checkExprHeight(pParse, p->nHeight);
+ sqlite3ExprCheckHeight(pParse, p->nHeight);
}
/*
@@ -50885,7 +52836,6 @@
return nHeight;
}
#else
- #define checkExprHeight(x,y)
#define exprSetHeight(y)
#endif /* SQLITE_MAX_EXPR_DEPTH>0 */
@@ -50953,7 +52903,7 @@
){
Expr *p = sqlite3Expr(pParse->db, op, pLeft, pRight, pToken);
if( p ){
- checkExprHeight(pParse, p->nHeight);
+ sqlite3ExprCheckHeight(pParse, p->nHeight);
}
return p;
}
@@ -51146,7 +53096,6 @@
sqlite3Dequote((char*)p->token.z);
}
-
/*
** The following group of routines make deep copies of expressions,
** expression lists, ID lists, and select statements. The copies can
@@ -51217,8 +53166,9 @@
|| db->mallocFailed );
pItem->zName = sqlite3DbStrDup(db, pOldItem->zName);
pItem->sortOrder = pOldItem->sortOrder;
- pItem->isAgg = pOldItem->isAgg;
pItem->done = 0;
+ pItem->iCol = pOldItem->iCol;
+ pItem->iAlias = pOldItem->iAlias;
}
return pNew;
}
@@ -51286,7 +53236,6 @@
if( p==0 ) return 0;
pNew = sqlite3DbMallocRaw(db, sizeof(*p) );
if( pNew==0 ) return 0;
- pNew->isDistinct = p->isDistinct;
pNew->pEList = sqlite3ExprListDup(db, p->pEList);
pNew->pSrc = sqlite3SrcListDup(db, p->pSrc);
pNew->pWhere = sqlite3ExprDup(db, p->pWhere);
@@ -51299,10 +53248,7 @@
pNew->pOffset = sqlite3ExprDup(db, p->pOffset);
pNew->iLimit = 0;
pNew->iOffset = 0;
- pNew->isResolved = p->isResolved;
- pNew->isAgg = p->isAgg;
- pNew->usesEphm = 0;
- pNew->disallowOrderBy = 0;
+ pNew->selFlags = p->selFlags & ~SF_UsesEphemeral;
pNew->pRightmost = 0;
pNew->addrOpenEphm[0] = -1;
pNew->addrOpenEphm[1] = -1;
@@ -51351,6 +53297,7 @@
memset(pItem, 0, sizeof(*pItem));
pItem->zName = sqlite3NameFromToken(db, pName);
pItem->pExpr = pExpr;
+ pItem->iAlias = 0;
}
return pList;
@@ -51379,109 +53326,50 @@
}
/*
-** Delete an entire expression list.
-*/
-SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){
- int i;
- struct ExprList_item *pItem;
- if( pList==0 ) return;
- assert( pList->a!=0 || (pList->nExpr==0 && pList->nAlloc==0) );
- assert( pList->nExpr<=pList->nAlloc );
- for(pItem=pList->a, i=0; i<pList->nExpr; i++, pItem++){
- sqlite3ExprDelete(db, pItem->pExpr);
- sqlite3DbFree(db, pItem->zName);
- }
- sqlite3DbFree(db, pList->a);
- sqlite3DbFree(db, pList);
-}
-
-/*
-** Walk an expression tree. Call xFunc for each node visited. xFunc
-** is called on the node before xFunc is called on the nodes children.
-**
-** The return value from xFunc determines whether the tree walk continues.
-** 0 means continue walking the tree. 1 means do not walk children
-** of the current node but continue with siblings. 2 means abandon
-** the tree walk completely.
-**
-** The return value from this routine is 1 to abandon the tree walk
-** and 0 to continue.
-**
-** NOTICE: This routine does *not* descend into subqueries.
-*/
-static int walkExprList(ExprList *, int (*)(void *, Expr*), void *);
-static int walkExprTree(Expr *pExpr, int (*xFunc)(void*,Expr*), void *pArg){
- int rc;
- if( pExpr==0 ) return 0;
- rc = (*xFunc)(pArg, pExpr);
- if( rc==0 ){
- if( walkExprTree(pExpr->pLeft, xFunc, pArg) ) return 1;
- if( walkExprTree(pExpr->pRight, xFunc, pArg) ) return 1;
- if( walkExprList(pExpr->pList, xFunc, pArg) ) return 1;
- }
- return rc>1;
-}
-
-/*
-** Call walkExprTree() for every expression in list p.
-*/
-static int walkExprList(ExprList *p, int (*xFunc)(void *, Expr*), void *pArg){
- int i;
- struct ExprList_item *pItem;
- if( !p ) return 0;
- for(i=p->nExpr, pItem=p->a; i>0; i--, pItem++){
- if( walkExprTree(pItem->pExpr, xFunc, pArg) ) return 1;
- }
- return 0;
-}
-
-/*
-** Call walkExprTree() for every expression in Select p, not including
-** expressions that are part of sub-selects in any FROM clause or the LIMIT
-** or OFFSET expressions..
-*/
-static int walkSelectExpr(Select *p, int (*xFunc)(void *, Expr*), void *pArg){
- walkExprList(p->pEList, xFunc, pArg);
- walkExprTree(p->pWhere, xFunc, pArg);
- walkExprList(p->pGroupBy, xFunc, pArg);
- walkExprTree(p->pHaving, xFunc, pArg);
- walkExprList(p->pOrderBy, xFunc, pArg);
- if( p->pPrior ){
- walkSelectExpr(p->pPrior, xFunc, pArg);
+** Delete an entire expression list.
+*/
+SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){
+ int i;
+ struct ExprList_item *pItem;
+ if( pList==0 ) return;
+ assert( pList->a!=0 || (pList->nExpr==0 && pList->nAlloc==0) );
+ assert( pList->nExpr<=pList->nAlloc );
+ for(pItem=pList->a, i=0; i<pList->nExpr; i++, pItem++){
+ sqlite3ExprDelete(db, pItem->pExpr);
+ sqlite3DbFree(db, pItem->zName);
}
- return 0;
+ sqlite3DbFree(db, pList->a);
+ sqlite3DbFree(db, pList);
}
-
/*
-** This routine is designed as an xFunc for walkExprTree().
+** These routines are Walker callbacks. Walker.u.pi is a pointer
+** to an integer. These routines are checking an expression to see
+** if it is a constant. Set *Walker.u.pi to 0 if the expression is
+** not constant.
+**
+** These callback routines are used to implement the following:
+**
+** sqlite3ExprIsConstant()
+** sqlite3ExprIsConstantNotJoin()
+** sqlite3ExprIsConstantOrFunction()
**
-** pArg is really a pointer to an integer. If we can tell by looking
-** at pExpr that the expression that contains pExpr is not a constant
-** expression, then set *pArg to 0 and return 2 to abandon the tree walk.
-** If pExpr does does not disqualify the expression from being a constant
-** then do nothing.
-**
-** After walking the whole tree, if no nodes are found that disqualify
-** the expression as constant, then we assume the whole expression
-** is constant. See sqlite3ExprIsConstant() for additional information.
*/
-static int exprNodeIsConstant(void *pArg, Expr *pExpr){
- int *pN = (int*)pArg;
+static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
- /* If *pArg is 3 then any term of the expression that comes from
+ /* If pWalker->u.i is 3 then any term of the expression that comes from
** the ON or USING clauses of a join disqualifies the expression
** from being considered constant. */
- if( (*pN)==3 && ExprHasAnyProperty(pExpr, EP_FromJoin) ){
- *pN = 0;
- return 2;
+ if( pWalker->u.i==3 && ExprHasAnyProperty(pExpr, EP_FromJoin) ){
+ pWalker->u.i = 0;
+ return WRC_Abort;
}
switch( pExpr->op ){
/* Consider functions to be constant if all their arguments are constant
- ** and *pArg==2 */
+ ** and pWalker->u.i==2 */
case TK_FUNCTION:
- if( (*pN)==2 ) return 0;
+ if( pWalker->u.i==2 ) return 0;
/* Fall through */
case TK_ID:
case TK_COLUMN:
@@ -51499,17 +53387,24 @@
testcase( pExpr->op==TK_DOT );
testcase( pExpr->op==TK_AGG_FUNCTION );
testcase( pExpr->op==TK_AGG_COLUMN );
- *pN = 0;
- return 2;
- case TK_IN:
- if( pExpr->pSelect ){
- *pN = 0;
- return 2;
- }
+ pWalker->u.i = 0;
+ return WRC_Abort;
default:
- return 0;
+ return WRC_Continue;
}
}
+static int selectNodeIsConstant(Walker *pWalker, Select *pSelect){
+ pWalker->u.i = 0;
+ return WRC_Abort;
+}
+static int exprIsConst(Expr *p, int initFlag){
+ Walker w;
+ w.u.i = initFlag;
+ w.xExprCallback = exprNodeIsConstant;
+ w.xSelectCallback = selectNodeIsConstant;
+ sqlite3WalkExpr(&w, p);
+ return w.u.i;
+}
/*
** Walk an expression tree. Return 1 if the expression is constant
@@ -51520,9 +53415,7 @@
** a constant.
*/
SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr *p){
- int isConst = 1;
- walkExprTree(p, exprNodeIsConstant, &isConst);
- return isConst;
+ return exprIsConst(p, 1);
}
/*
@@ -51532,9 +53425,7 @@
** an ON or USING clause.
*/
SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr *p){
- int isConst = 3;
- walkExprTree(p, exprNodeIsConstant, &isConst);
- return isConst!=0;
+ return exprIsConst(p, 3);
}
/*
@@ -51547,9 +53438,7 @@
** a constant.
*/
SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr *p){
- int isConst = 2;
- walkExprTree(p, exprNodeIsConstant, &isConst);
- return isConst!=0;
+ return exprIsConst(p, 2);
}
/*
@@ -51601,554 +53490,6 @@
return 0;
}
-/*
-** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up
-** that name in the set of source tables in pSrcList and make the pExpr
-** expression node refer back to that source column. The following changes
-** are made to pExpr:
-**
-** pExpr->iDb Set the index in db->aDb[] of the database holding
-** the table.
-** pExpr->iTable Set to the cursor number for the table obtained
-** from pSrcList.
-** pExpr->iColumn Set to the column number within the table.
-** pExpr->op Set to TK_COLUMN.
-** pExpr->pLeft Any expression this points to is deleted
-** pExpr->pRight Any expression this points to is deleted.
-**
-** The pDbToken is the name of the database (the "X"). This value may be
-** NULL meaning that name is of the form Y.Z or Z. Any available database
-** can be used. The pTableToken is the name of the table (the "Y"). This
-** value can be NULL if pDbToken is also NULL. If pTableToken is NULL it
-** means that the form of the name is Z and that columns from any table
-** can be used.
-**
-** If the name cannot be resolved unambiguously, leave an error message
-** in pParse and return non-zero. Return zero on success.
-*/
-static int lookupName(
- Parse *pParse, /* The parsing context */
- Token *pDbToken, /* Name of the database containing table, or NULL */
- Token *pTableToken, /* Name of table containing column, or NULL */
- Token *pColumnToken, /* Name of the column. */
- NameContext *pNC, /* The name context used to resolve the name */
- Expr *pExpr /* Make this EXPR node point to the selected column */
-){
- char *zDb = 0; /* Name of the database. The "X" in X.Y.Z */
- char *zTab = 0; /* Name of the table. The "Y" in X.Y.Z or Y.Z */
- char *zCol = 0; /* Name of the column. The "Z" */
- int i, j; /* Loop counters */
- int cnt = 0; /* Number of matching column names */
- int cntTab = 0; /* Number of matching table names */
- sqlite3 *db = pParse->db; /* The database */
- struct SrcList_item *pItem; /* Use for looping over pSrcList items */
- struct SrcList_item *pMatch = 0; /* The matching pSrcList item */
- NameContext *pTopNC = pNC; /* First namecontext in the list */
- Schema *pSchema = 0; /* Schema of the expression */
-
- assert( pColumnToken && pColumnToken->z ); /* The Z in X.Y.Z cannot be NULL */
- zDb = sqlite3NameFromToken(db, pDbToken);
- zTab = sqlite3NameFromToken(db, pTableToken);
- zCol = sqlite3NameFromToken(db, pColumnToken);
- if( db->mallocFailed ){
- goto lookupname_end;
- }
-
- pExpr->iTable = -1;
- while( pNC && cnt==0 ){
- ExprList *pEList;
- SrcList *pSrcList = pNC->pSrcList;
-
- if( pSrcList ){
- for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){
- Table *pTab;
- int iDb;
- Column *pCol;
-
- pTab = pItem->pTab;
- assert( pTab!=0 );
- iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
- assert( pTab->nCol>0 );
- if( zTab ){
- if( pItem->zAlias ){
- char *zTabName = pItem->zAlias;
- if( sqlite3StrICmp(zTabName, zTab)!=0 ) continue;
- }else{
- char *zTabName = pTab->zName;
- if( zTabName==0 || sqlite3StrICmp(zTabName, zTab)!=0 ) continue;
- if( zDb!=0 && sqlite3StrICmp(db->aDb[iDb].zName, zDb)!=0 ){
- continue;
- }
- }
- }
- if( 0==(cntTab++) ){
- pExpr->iTable = pItem->iCursor;
- pSchema = pTab->pSchema;
- pMatch = pItem;
- }
- for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){
- if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
- const char *zColl = pTab->aCol[j].zColl;
- IdList *pUsing;
- cnt++;
- pExpr->iTable = pItem->iCursor;
- pMatch = pItem;
- pSchema = pTab->pSchema;
- /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */
- pExpr->iColumn = j==pTab->iPKey ? -1 : j;
- pExpr->affinity = pTab->aCol[j].affinity;
- if( (pExpr->flags & EP_ExpCollate)==0 ){
- pExpr->pColl = sqlite3FindCollSeq(db, ENC(db), zColl,-1, 0);
- }
- if( i<pSrcList->nSrc-1 ){
- if( pItem[1].jointype & JT_NATURAL ){
- /* If this match occurred in the left table of a natural join,
- ** then skip the right table to avoid a duplicate match */
- pItem++;
- i++;
- }else if( (pUsing = pItem[1].pUsing)!=0 ){
- /* If this match occurs on a column that is in the USING clause
- ** of a join, skip the search of the right table of the join
- ** to avoid a duplicate match there. */
- int k;
- for(k=0; k<pUsing->nId; k++){
- if( sqlite3StrICmp(pUsing->a[k].zName, zCol)==0 ){
- pItem++;
- i++;
- break;
- }
- }
- }
- }
- break;
- }
- }
- }
- }
-
-#ifndef SQLITE_OMIT_TRIGGER
- /* If we have not already resolved the name, then maybe
- ** it is a new.* or old.* trigger argument reference
- */
- if( zDb==0 && zTab!=0 && cnt==0 && pParse->trigStack!=0 ){
- TriggerStack *pTriggerStack = pParse->trigStack;
- Table *pTab = 0;
- u32 *piColMask;
- if( pTriggerStack->newIdx != -1 && sqlite3StrICmp("new", zTab) == 0 ){
- pExpr->iTable = pTriggerStack->newIdx;
- assert( pTriggerStack->pTab );
- pTab = pTriggerStack->pTab;
- piColMask = &(pTriggerStack->newColMask);
- }else if( pTriggerStack->oldIdx != -1 && sqlite3StrICmp("old", zTab)==0 ){
- pExpr->iTable = pTriggerStack->oldIdx;
- assert( pTriggerStack->pTab );
- pTab = pTriggerStack->pTab;
- piColMask = &(pTriggerStack->oldColMask);
- }
-
- if( pTab ){
- int iCol;
- Column *pCol = pTab->aCol;
-
- pSchema = pTab->pSchema;
- cntTab++;
- for(iCol=0; iCol < pTab->nCol; iCol++, pCol++) {
- if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
- const char *zColl = pTab->aCol[iCol].zColl;
- cnt++;
- pExpr->iColumn = iCol==pTab->iPKey ? -1 : iCol;
- pExpr->affinity = pTab->aCol[iCol].affinity;
- if( (pExpr->flags & EP_ExpCollate)==0 ){
- pExpr->pColl = sqlite3FindCollSeq(db, ENC(db), zColl,-1, 0);
- }
- pExpr->pTab = pTab;
- if( iCol>=0 ){
- testcase( iCol==31 );
- testcase( iCol==32 );
- *piColMask |= ((u32)1<<iCol) | (iCol>=32?0xffffffff:0);
- }
- break;
- }
- }
- }
- }
-#endif /* !defined(SQLITE_OMIT_TRIGGER) */
-
- /*
- ** Perhaps the name is a reference to the ROWID
- */
- if( cnt==0 && cntTab==1 && sqlite3IsRowid(zCol) ){
- cnt = 1;
- pExpr->iColumn = -1;
- pExpr->affinity = SQLITE_AFF_INTEGER;
- }
-
- /*
- ** If the input is of the form Z (not Y.Z or X.Y.Z) then the name Z
- ** might refer to an result-set alias. This happens, for example, when
- ** we are resolving names in the WHERE clause of the following command:
- **
- ** SELECT a+b AS x FROM table WHERE x<10;
- **
- ** In cases like this, replace pExpr with a copy of the expression that
- ** forms the result set entry ("a+b" in the example) and return immediately.
- ** Note that the expression in the result set should have already been
- ** resolved by the time the WHERE clause is resolved.
- */
- if( cnt==0 && (pEList = pNC->pEList)!=0 && zTab==0 ){
- for(j=0; j<pEList->nExpr; j++){
- char *zAs = pEList->a[j].zName;
- if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){
- Expr *pDup, *pOrig;
- assert( pExpr->pLeft==0 && pExpr->pRight==0 );
- assert( pExpr->pList==0 );
- assert( pExpr->pSelect==0 );
- pOrig = pEList->a[j].pExpr;
- if( !pNC->allowAgg && ExprHasProperty(pOrig, EP_Agg) ){
- sqlite3ErrorMsg(pParse, "misuse of aliased aggregate %s", zAs);
- sqlite3DbFree(db, zCol);
- return 2;
- }
- pDup = sqlite3ExprDup(db, pOrig);
- if( pExpr->flags & EP_ExpCollate ){
- pDup->pColl = pExpr->pColl;
- pDup->flags |= EP_ExpCollate;
- }
- if( pExpr->span.dyn ) sqlite3DbFree(db, (char*)pExpr->span.z);
- if( pExpr->token.dyn ) sqlite3DbFree(db, (char*)pExpr->token.z);
- memcpy(pExpr, pDup, sizeof(*pExpr));
- sqlite3DbFree(db, pDup);
- cnt = 1;
- pMatch = 0;
- assert( zTab==0 && zDb==0 );
- goto lookupname_end_2;
- }
- }
- }
-
- /* Advance to the next name context. The loop will exit when either
- ** we have a match (cnt>0) or when we run out of name contexts.
- */
- if( cnt==0 ){
- pNC = pNC->pNext;
- }
- }
-
- /*
- ** If X and Y are NULL (in other words if only the column name Z is
- ** supplied) and the value of Z is enclosed in double-quotes, then
- ** Z is a string literal if it doesn't match any column names. In that
- ** case, we need to return right away and not make any changes to
- ** pExpr.
- **
- ** Because no reference was made to outer contexts, the pNC->nRef
- ** fields are not changed in any context.
- */
- if( cnt==0 && zTab==0 && pColumnToken->z[0]=='"' ){
- sqlite3DbFree(db, zCol);
- return 0;
- }
-
- /*
- ** cnt==0 means there was not match. cnt>1 means there were two or
- ** more matches. Either way, we have an error.
- */
- if( cnt!=1 ){
- const char *zErr;
- zErr = cnt==0 ? "no such column" : "ambiguous column name";
- if( zDb ){
- sqlite3ErrorMsg(pParse, "%s: %s.%s.%s", zErr, zDb, zTab, zCol);
- }else if( zTab ){
- sqlite3ErrorMsg(pParse, "%s: %s.%s", zErr, zTab, zCol);
- }else{
- sqlite3ErrorMsg(pParse, "%s: %s", zErr, zCol);
- }
- pTopNC->nErr++;
- }
-
- /* If a column from a table in pSrcList is referenced, then record
- ** this fact in the pSrcList.a[].colUsed bitmask. Column 0 causes
- ** bit 0 to be set. Column 1 sets bit 1. And so forth. If the
- ** column number is greater than the number of bits in the bitmask
- ** then set the high-order bit of the bitmask.
- */
- if( pExpr->iColumn>=0 && pMatch!=0 ){
- int n = pExpr->iColumn;
- testcase( n==sizeof(Bitmask)*8-1 );
- if( n>=sizeof(Bitmask)*8 ){
- n = sizeof(Bitmask)*8-1;
- }
- assert( pMatch->iCursor==pExpr->iTable );
- pMatch->colUsed |= ((Bitmask)1)<<n;
- }
-
-lookupname_end:
- /* Clean up and return
- */
- sqlite3DbFree(db, zDb);
- sqlite3DbFree(db, zTab);
- sqlite3ExprDelete(db, pExpr->pLeft);
- pExpr->pLeft = 0;
- sqlite3ExprDelete(db, pExpr->pRight);
- pExpr->pRight = 0;
- pExpr->op = TK_COLUMN;
-lookupname_end_2:
- sqlite3DbFree(db, zCol);
- if( cnt==1 ){
- assert( pNC!=0 );
- sqlite3AuthRead(pParse, pExpr, pSchema, pNC->pSrcList);
- if( pMatch && !pMatch->pSelect ){
- pExpr->pTab = pMatch->pTab;
- }
- /* Increment the nRef value on all name contexts from TopNC up to
- ** the point where the name matched. */
- for(;;){
- assert( pTopNC!=0 );
- pTopNC->nRef++;
- if( pTopNC==pNC ) break;
- pTopNC = pTopNC->pNext;
- }
- return 0;
- } else {
- return 1;
- }
-}
-
-/*
-** This routine is designed as an xFunc for walkExprTree().
-**
-** Resolve symbolic names into TK_COLUMN operators for the current
-** node in the expression tree. Return 0 to continue the search down
-** the tree or 2 to abort the tree walk.
-**
-** This routine also does error checking and name resolution for
-** function names. The operator for aggregate functions is changed
-** to TK_AGG_FUNCTION.
-*/
-static int nameResolverStep(void *pArg, Expr *pExpr){
- NameContext *pNC = (NameContext*)pArg;
- Parse *pParse;
-
- if( pExpr==0 ) return 1;
- assert( pNC!=0 );
- pParse = pNC->pParse;
-
- if( ExprHasAnyProperty(pExpr, EP_Resolved) ) return 1;
- ExprSetProperty(pExpr, EP_Resolved);
-#ifndef NDEBUG
- if( pNC->pSrcList && pNC->pSrcList->nAlloc>0 ){
- SrcList *pSrcList = pNC->pSrcList;
- int i;
- for(i=0; i<pNC->pSrcList->nSrc; i++){
- assert( pSrcList->a[i].iCursor>=0 && pSrcList->a[i].iCursor<pParse->nTab);
- }
- }
-#endif
- switch( pExpr->op ){
- /* Double-quoted strings (ex: "abc") are used as identifiers if
- ** possible. Otherwise they remain as strings. Single-quoted
- ** strings (ex: 'abc') are always string literals.
- */
- case TK_STRING: {
- if( pExpr->token.z[0]=='\'' ) break;
- /* Fall thru into the TK_ID case if this is a double-quoted string */
- }
- /* A lone identifier is the name of a column.
- */
- case TK_ID: {
- lookupName(pParse, 0, 0, &pExpr->token, pNC, pExpr);
- return 1;
- }
-
- /* A table name and column name: ID.ID
- ** Or a database, table and column: ID.ID.ID
- */
- case TK_DOT: {
- Token *pColumn;
- Token *pTable;
- Token *pDb;
- Expr *pRight;
-
- /* if( pSrcList==0 ) break; */
- pRight = pExpr->pRight;
- if( pRight->op==TK_ID ){
- pDb = 0;
- pTable = &pExpr->pLeft->token;
- pColumn = &pRight->token;
- }else{
- assert( pRight->op==TK_DOT );
- pDb = &pExpr->pLeft->token;
- pTable = &pRight->pLeft->token;
- pColumn = &pRight->pRight->token;
- }
- lookupName(pParse, pDb, pTable, pColumn, pNC, pExpr);
- return 1;
- }
-
- /* Resolve function names
- */
- case TK_CONST_FUNC:
- case TK_FUNCTION: {
- ExprList *pList = pExpr->pList; /* The argument list */
- int n = pList ? pList->nExpr : 0; /* Number of arguments */
- int no_such_func = 0; /* True if no such function exists */
- int wrong_num_args = 0; /* True if wrong number of arguments */
- int is_agg = 0; /* True if is an aggregate function */
- int i;
- int auth; /* Authorization to use the function */
- int nId; /* Number of characters in function name */
- const char *zId; /* The function name. */
- FuncDef *pDef; /* Information about the function */
- int enc = ENC(pParse->db); /* The database encoding */
-
- zId = (char*)pExpr->token.z;
- nId = pExpr->token.n;
- pDef = sqlite3FindFunction(pParse->db, zId, nId, n, enc, 0);
- if( pDef==0 ){
- pDef = sqlite3FindFunction(pParse->db, zId, nId, -1, enc, 0);
- if( pDef==0 ){
- no_such_func = 1;
- }else{
- wrong_num_args = 1;
- }
- }else{
- is_agg = pDef->xFunc==0;
- }
-#ifndef SQLITE_OMIT_AUTHORIZATION
- if( pDef ){
- auth = sqlite3AuthCheck(pParse, SQLITE_FUNCTION, 0, pDef->zName, 0);
- if( auth!=SQLITE_OK ){
- if( auth==SQLITE_DENY ){
- sqlite3ErrorMsg(pParse, "not authorized to use function: %s",
- pDef->zName);
- pNC->nErr++;
- }
- pExpr->op = TK_NULL;
- return 1;
- }
- }
-#endif
- if( is_agg && !pNC->allowAgg ){
- sqlite3ErrorMsg(pParse, "misuse of aggregate function %.*s()", nId,zId);
- pNC->nErr++;
- is_agg = 0;
- }else if( no_such_func ){
- sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId);
- pNC->nErr++;
- }else if( wrong_num_args ){
- sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()",
- nId, zId);
- pNC->nErr++;
- }
- if( is_agg ){
- pExpr->op = TK_AGG_FUNCTION;
- pNC->hasAgg = 1;
- }
- if( is_agg ) pNC->allowAgg = 0;
- for(i=0; pNC->nErr==0 && i<n; i++){
- walkExprTree(pList->a[i].pExpr, nameResolverStep, pNC);
- }
- if( is_agg ) pNC->allowAgg = 1;
- /* FIX ME: Compute pExpr->affinity based on the expected return
- ** type of the function
- */
- return is_agg;
- }
-#ifndef SQLITE_OMIT_SUBQUERY
- case TK_SELECT:
- case TK_EXISTS:
-#endif
- case TK_IN: {
- if( pExpr->pSelect ){
- int nRef = pNC->nRef;
-#ifndef SQLITE_OMIT_CHECK
- if( pNC->isCheck ){
- sqlite3ErrorMsg(pParse,"subqueries prohibited in CHECK constraints");
- }
-#endif
- sqlite3SelectResolve(pParse, pExpr->pSelect, pNC);
- assert( pNC->nRef>=nRef );
- if( nRef!=pNC->nRef ){
- ExprSetProperty(pExpr, EP_VarSelect);
- }
- }
- break;
- }
-#ifndef SQLITE_OMIT_CHECK
- case TK_VARIABLE: {
- if( pNC->isCheck ){
- sqlite3ErrorMsg(pParse,"parameters prohibited in CHECK constraints");
- }
- break;
- }
-#endif
- }
- return 0;
-}
-
-/*
-** This routine walks an expression tree and resolves references to
-** table columns. Nodes of the form ID.ID or ID resolve into an
-** index to the table in the table list and a column offset. The
-** Expr.opcode for such nodes is changed to TK_COLUMN. The Expr.iTable
-** value is changed to the index of the referenced table in pTabList
-** plus the "base" value. The base value will ultimately become the
-** VDBE cursor number for a cursor that is pointing into the referenced
-** table. The Expr.iColumn value is changed to the index of the column
-** of the referenced table. The Expr.iColumn value for the special
-** ROWID column is -1. Any INTEGER PRIMARY KEY column is tried as an
-** alias for ROWID.
-**
-** Also resolve function names and check the functions for proper
-** usage. Make sure all function names are recognized and all functions
-** have the correct number of arguments. Leave an error message
-** in pParse->zErrMsg if anything is amiss. Return the number of errors.
-**
-** If the expression contains aggregate functions then set the EP_Agg
-** property on the expression.
-*/
-SQLITE_PRIVATE int sqlite3ExprResolveNames(
- NameContext *pNC, /* Namespace to resolve expressions in. */
- Expr *pExpr /* The expression to be analyzed. */
-){
- int savedHasAgg;
-
- if( pExpr==0 ) return 0;
-#if SQLITE_MAX_EXPR_DEPTH>0
- {
- if( checkExprHeight(pNC->pParse, pExpr->nHeight + pNC->pParse->nHeight) ){
- return 1;
- }
- pNC->pParse->nHeight += pExpr->nHeight;
- }
-#endif
- savedHasAgg = pNC->hasAgg;
- pNC->hasAgg = 0;
- walkExprTree(pExpr, nameResolverStep, pNC);
-#if SQLITE_MAX_EXPR_DEPTH>0
- pNC->pParse->nHeight -= pExpr->nHeight;
-#endif
- if( pNC->nErr>0 ){
- ExprSetProperty(pExpr, EP_Error);
- }
- if( pNC->hasAgg ){
- ExprSetProperty(pExpr, EP_Agg);
- }else if( savedHasAgg ){
- pNC->hasAgg = 1;
- }
- return ExprHasProperty(pExpr, EP_Error);
-}
-
-/*
-** A pointer instance of this structure is used to pass information
-** through walkExprTree into codeSubqueryStep().
-*/
-typedef struct QueryCoder QueryCoder;
-struct QueryCoder {
- Parse *pParse; /* The parsing context */
- NameContext *pNC; /* Namespace of first enclosing query */
-};
-
#ifdef SQLITE_TEST
int sqlite3_enable_in_opt = 1;
#else
@@ -52173,8 +53514,9 @@
if( !sqlite3_enable_in_opt ) return 0; /* IN optimization must be enabled */
if( p==0 ) return 0; /* right-hand side of IN is SELECT */
if( p->pPrior ) return 0; /* Not a compound SELECT */
- if( p->isDistinct ) return 0; /* No DISTINCT keyword */
- if( p->isAgg ) return 0; /* Contains no aggregate functions */
+ if( p->selFlags & (SF_Distinct|SF_Aggregate) ){
+ return 0; /* No DISTINCT keyword and no aggregate functions */
+ }
if( p->pGroupBy ) return 0; /* Has no GROUP BY clause */
if( p->pLimit ) return 0; /* Has no LIMIT clause */
if( p->pOffset ) return 0;
@@ -52429,7 +53771,7 @@
sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable);
dest.affinity = (int)affinity;
assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );
- if( sqlite3Select(pParse, pExpr->pSelect, &dest, 0, 0, 0) ){
+ if( sqlite3Select(pParse, pExpr->pSelect, &dest) ){
return;
}
pEList = pExpr->pSelect->pEList;
@@ -52453,7 +53795,7 @@
if( !affinity ){
affinity = SQLITE_AFF_NONE;
}
- keyInfo.aColl[0] = pExpr->pLeft->pColl;
+ keyInfo.aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
/* Loop through each expression in <exprlist>. */
r1 = sqlite3GetTempReg(pParse);
@@ -52510,7 +53852,7 @@
}
sqlite3ExprDelete(pParse->db, pSel->pLimit);
pSel->pLimit = sqlite3PExpr(pParse, TK_INTEGER, 0, 0, &one);
- if( sqlite3Select(pParse, pSel, &dest, 0, 0, 0) ){
+ if( sqlite3Select(pParse, pSel, &dest) ){
return;
}
pExpr->iColumn = dest.iParm;
@@ -52792,11 +54134,46 @@
}
/*
+** Generate code to store the value of the iAlias-th alias in register
+** target. The first time this is called, pExpr is evaluated to compute
+** the value of the alias. The value is stored in an auxiliary register
+** and the number of that register is returned. On subsequent calls,
+** the register number is returned without generating any code.
+**
+** Note that in order for this to work, code must be generated in the
+** same order that it is executed.
+**
+** Aliases are numbered starting with 1. So iAlias is in the range
+** of 1 to pParse->nAlias inclusive.
+**
+** pParse->aAlias[iAlias-1] records the register number where the value
+** of the iAlias-th alias is stored. If zero, that means that the
+** alias has not yet been computed.
+*/
+static int codeAlias(Parse *pParse, int iAlias, Expr *pExpr){
+ sqlite3 *db = pParse->db;
+ int iReg;
+ if( pParse->aAlias==0 ){
+ pParse->aAlias = sqlite3DbMallocZero(db,
+ sizeof(pParse->aAlias[0])*pParse->nAlias );
+ if( db->mallocFailed ) return 0;
+ }
+ assert( iAlias>0 && iAlias<=pParse->nAlias );
+ iReg = pParse->aAlias[iAlias-1];
+ if( iReg==0 ){
+ iReg = ++pParse->nMem;
+ sqlite3ExprCode(pParse, pExpr, iReg);
+ pParse->aAlias[iAlias-1] = iReg;
+ }
+ return iReg;
+}
+
+/*
** Generate code into the current Vdbe to evaluate the given
** expression. Attempt to store the results in register "target".
** Return the register where results are stored.
**
-** With this routine, there is no guaranteed that results will
+** With this routine, there is no guarantee that results will
** be stored in target. The result might be stored in some other
** register if it is convenient to do so. The calling function
** must check the return code and move the results to the desired
@@ -52809,8 +54186,10 @@
int regFree1 = 0; /* If non-zero free this temporary register */
int regFree2 = 0; /* If non-zero free this temporary register */
int r1, r2, r3, r4; /* Various register numbers */
+ sqlite3 *db;
- assert( v!=0 || pParse->db->mallocFailed );
+ db = pParse->db;
+ assert( v!=0 || db->mallocFailed );
assert( target>0 && target<=pParse->nMem );
if( v==0 ) return 0;
@@ -52856,7 +54235,7 @@
break;
}
case TK_STRING: {
- sqlite3DequoteExpr(pParse->db, pExpr);
+ sqlite3DequoteExpr(db, pExpr);
sqlite3VdbeAddOp4(v,OP_String8, 0, target, 0,
(char*)pExpr->token.z, pExpr->token.n);
break;
@@ -52892,6 +54271,10 @@
inReg = pExpr->iTable;
break;
}
+ case TK_AS: {
+ inReg = codeAlias(pParse, pExpr->iTable, pExpr->pLeft);
+ break;
+ }
#ifndef SQLITE_OMIT_CAST
case TK_CAST: {
/* Expressions of the form: CAST(pLeft AS token) */
@@ -53048,7 +54431,6 @@
const char *zId;
int constMask = 0;
int i;
- sqlite3 *db = pParse->db;
u8 enc = ENC(db);
CollSeq *pColl = 0;
@@ -53056,7 +54438,7 @@
testcase( op==TK_FUNCTION );
zId = (char*)pExpr->token.z;
nId = pExpr->token.n;
- pDef = sqlite3FindFunction(pParse->db, zId, nId, nExpr, enc, 0);
+ pDef = sqlite3FindFunction(db, zId, nId, nExpr, enc, 0);
assert( pDef!=0 );
if( pList ){
nExpr = pList->nExpr;
@@ -53093,7 +54475,7 @@
}
}
if( pDef->needCollSeq ){
- if( !pColl ) pColl = pParse->db->pDfltColl;
+ if( !pColl ) pColl = db->pDfltColl;
sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ);
}
sqlite3VdbeAddOp4(v, OP_Function, constMask, r1, target,
@@ -53292,7 +54674,6 @@
cacheX.iTable = sqlite3ExprCodeTemp(pParse, pX, ®Free1);
testcase( regFree1==0 );
cacheX.op = TK_REGISTER;
- cacheX.iColumn = 0;
opCompare.op = TK_EQ;
opCompare.pLeft = &cacheX;
pTest = &opCompare;
@@ -53334,7 +54715,7 @@
assert( pExpr->iColumn==OE_Rollback ||
pExpr->iColumn == OE_Abort ||
pExpr->iColumn == OE_Fail );
- sqlite3DequoteExpr(pParse->db, pExpr);
+ sqlite3DequoteExpr(db, pExpr);
sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, pExpr->iColumn, 0,
(char*)pExpr->token.z, pExpr->token.n);
} else {
@@ -53412,7 +54793,6 @@
iMem = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Copy, inReg, iMem);
pExpr->iTable = iMem;
- pExpr->iColumn = pExpr->op;
pExpr->op = TK_REGISTER;
}
return inReg;
@@ -53483,8 +54863,8 @@
** into a register and convert the expression into a TK_REGISTER
** expression.
*/
-static int evalConstExpr(void *pArg, Expr *pExpr){
- Parse *pParse = (Parse*)pArg;
+static int evalConstExpr(Walker *pWalker, Expr *pExpr){
+ Parse *pParse = pWalker->pParse;
switch( pExpr->op ){
case TK_REGISTER: {
return 1;
@@ -53512,12 +54892,11 @@
int r2;
r2 = sqlite3ExprCodeTarget(pParse, pExpr, r1);
if( r1!=r2 ) sqlite3ReleaseTempReg(pParse, r1);
- pExpr->iColumn = pExpr->op;
pExpr->op = TK_REGISTER;
pExpr->iTable = r2;
- return 1;
+ return WRC_Prune;
}
- return 0;
+ return WRC_Continue;
}
/*
@@ -53526,7 +54905,11 @@
** are TK_REGISTER opcodes that refer to the precomputed values.
*/
SQLITE_PRIVATE void sqlite3ExprCodeConstants(Parse *pParse, Expr *pExpr){
- walkExprTree(pExpr, evalConstExpr, pParse);
+ Walker w;
+ w.xExprCallback = evalConstExpr;
+ w.xSelectCallback = 0;
+ w.pParse = pParse;
+ sqlite3WalkExpr(&w, pExpr);
}
@@ -53544,14 +54927,17 @@
){
struct ExprList_item *pItem;
int i, n;
- assert( pList!=0 || pParse->db->mallocFailed );
- if( pList==0 ){
- return 0;
- }
+ assert( pList!=0 );
assert( target>0 );
n = pList->nExpr;
for(pItem=pList->a, i=0; i<n; i++, pItem++){
- sqlite3ExprCode(pParse, pItem->pExpr, target+i);
+ if( pItem->iAlias ){
+ int iReg = codeAlias(pParse, pItem->iAlias, pItem->pExpr);
+ Vdbe *v = sqlite3GetVdbe(pParse);
+ sqlite3VdbeAddOp2(v, OP_SCopy, iReg, target+i);
+ }else{
+ sqlite3ExprCode(pParse, pItem->pExpr, target+i);
+ }
if( doHardCopy ) sqlite3ExprHardCopy(pParse, target, n);
}
return n;
@@ -53926,15 +55312,13 @@
}
/*
-** This is an xFunc for walkExprTree() used to implement
-** sqlite3ExprAnalyzeAggregates(). See sqlite3ExprAnalyzeAggregates
+** This is the xExprCallback for a tree walker. It is used to
+** implement sqlite3ExprAnalyzeAggregates(). See sqlite3ExprAnalyzeAggregates
** for additional information.
-**
-** This routine analyzes the aggregate function at pExpr.
*/
-static int analyzeAggregate(void *pArg, Expr *pExpr){
+static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
int i;
- NameContext *pNC = (NameContext *)pArg;
+ NameContext *pNC = pWalker->u.pNC;
Parse *pParse = pNC->pParse;
SrcList *pSrcList = pNC->pSrcList;
AggInfo *pAggInfo = pNC->pAggInfo;
@@ -53942,6 +55326,8 @@
switch( pExpr->op ){
case TK_AGG_COLUMN:
case TK_COLUMN: {
+ testcase( pExpr->op==TK_AGG_COLUMN );
+ testcase( pExpr->op==TK_COLUMN );
/* Check to see if the column is in one of the tables in the FROM
** clause of the aggregate query */
if( pSrcList ){
@@ -54003,7 +55389,7 @@
} /* endif pExpr->iTable==pItem->iCursor */
} /* end loop over pSrcList */
}
- return 1;
+ return WRC_Prune;
}
case TK_AGG_FUNCTION: {
/* The pNC->nDepth==0 test causes aggregate functions in subqueries
@@ -54041,21 +55427,22 @@
*/
pExpr->iAgg = i;
pExpr->pAggInfo = pAggInfo;
- return 1;
+ return WRC_Prune;
}
}
}
-
- /* Recursively walk subqueries looking for TK_COLUMN nodes that need
- ** to be changed to TK_AGG_COLUMN. But increment nDepth so that
- ** TK_AGG_FUNCTION nodes in subqueries will be unchanged.
- */
- if( pExpr->pSelect ){
+ return WRC_Continue;
+}
+static int analyzeAggregatesInSelect(Walker *pWalker, Select *pSelect){
+ NameContext *pNC = pWalker->u.pNC;
+ if( pNC->nDepth==0 ){
pNC->nDepth++;
- walkSelectExpr(pExpr->pSelect, analyzeAggregate, pNC);
+ sqlite3WalkSelect(pWalker, pSelect);
pNC->nDepth--;
+ return WRC_Prune;
+ }else{
+ return WRC_Continue;
}
- return 0;
}
/*
@@ -54064,10 +55451,14 @@
** Make additional entries to the pParse->aAgg[] array as necessary.
**
** This routine should only be called after the expression has been
-** analyzed by sqlite3ExprResolveNames().
+** analyzed by sqlite3ResolveExprNames().
*/
SQLITE_PRIVATE void sqlite3ExprAnalyzeAggregates(NameContext *pNC, Expr *pExpr){
- walkExprTree(pExpr, analyzeAggregate, pNC);
+ Walker w;
+ w.xExprCallback = analyzeAggregate;
+ w.xSelectCallback = analyzeAggregatesInSelect;
+ w.u.pNC = pNC;
+ sqlite3WalkExpr(&w, pExpr);
}
/*
@@ -54141,7 +55532,7 @@
** This file contains C code routines that used to generate VDBE code
** that implements the ALTER TABLE command.
**
-** $Id: alter.c,v 1.47 2008/07/28 19:34:53 drh Exp $
+** $Id: alter.c,v 1.48 2008/08/08 14:19:41 drh Exp $
*/
/*
@@ -54201,7 +55592,7 @@
do {
zCsr += len;
len = sqlite3GetToken(zCsr, &token);
- } while( token==TK_SPACE || token==TK_COMMENT );
+ } while( token==TK_SPACE );
assert( len>0 );
} while( token!=TK_LP && token!=TK_USING );
@@ -55189,7 +56580,7 @@
*************************************************************************
** This file contains code used to implement the ATTACH and DETACH commands.
**
-** $Id: attach.c,v 1.77 2008/07/28 19:34:53 drh Exp $
+** $Id: attach.c,v 1.78 2008/08/20 16:35:10 drh Exp $
*/
#ifndef SQLITE_OMIT_ATTACH
@@ -55216,7 +56607,7 @@
int rc = SQLITE_OK;
if( pExpr ){
if( pExpr->op!=TK_ID ){
- rc = sqlite3ExprResolveNames(pName, pExpr);
+ rc = sqlite3ResolveExprNames(pName, pExpr);
if( rc==SQLITE_OK && !sqlite3ExprIsConstant(pExpr) ){
sqlite3ErrorMsg(pName->pParse, "invalid name: \"%T\"", &pExpr->span);
return SQLITE_ERROR;
@@ -55962,7 +57353,7 @@
** COMMIT
** ROLLBACK
**
-** $Id: build.c,v 1.493 2008/08/04 04:39:49 danielk1977 Exp $
+** $Id: build.c,v 1.496 2008/08/20 16:35:10 drh Exp $
*/
/*
@@ -57078,12 +58469,12 @@
char *zType = 0;
int iCol = -1, i;
if( pTab==0 || IN_DECLARE_VTAB ) goto primary_key_exit;
- if( pTab->hasPrimKey ){
+ if( pTab->tabFlags & TF_HasPrimaryKey ){
sqlite3ErrorMsg(pParse,
"table \"%s\" has more than one primary key", pTab->zName);
goto primary_key_exit;
}
- pTab->hasPrimKey = 1;
+ pTab->tabFlags |= TF_HasPrimaryKey;
if( pList==0 ){
iCol = pTab->nCol - 1;
pTab->aCol[iCol].isPrimKey = 1;
@@ -57107,7 +58498,8 @@
&& sortOrder==SQLITE_SO_ASC ){
pTab->iPKey = iCol;
pTab->keyConf = onError;
- pTab->autoInc = autoInc;
+ assert( autoInc==0 || autoInc==1 );
+ pTab->tabFlags |= autoInc*TF_Autoincrement;
}else if( autoInc ){
#ifndef SQLITE_OMIT_AUTOINCREMENT
sqlite3ErrorMsg(pParse, "AUTOINCREMENT is only allowed on an "
@@ -57394,7 +58786,7 @@
sNC.pParse = pParse;
sNC.pSrcList = &sSrc;
sNC.isCheck = 1;
- if( sqlite3ExprResolveNames(&sNC, p->pCheck) ){
+ if( sqlite3ResolveExprNames(&sNC, p->pCheck) ){
return;
}
}
@@ -57467,10 +58859,10 @@
sqlite3VdbeChangeP5(v, 1);
pParse->nTab = 2;
sqlite3SelectDestInit(&dest, SRT_Table, 1);
- sqlite3Select(pParse, pSelect, &dest, 0, 0, 0);
+ sqlite3Select(pParse, pSelect, &dest);
sqlite3VdbeAddOp1(v, OP_Close, 1);
if( pParse->nErr==0 ){
- pSelTab = sqlite3ResultSetOfSelect(pParse, 0, pSelect);
+ pSelTab = sqlite3ResultSetOfSelect(pParse, pSelect);
if( pSelTab==0 ) return;
assert( p->aCol==0 );
p->nCol = pSelTab->nCol;
@@ -57516,7 +58908,7 @@
/* Check to see if we need to create an sqlite_sequence table for
** keeping track of autoincrement keys.
*/
- if( p->autoInc ){
+ if( p->tabFlags & TF_Autoincrement ){
Db *pDb = &db->aDb[iDb];
if( pDb->pSchema->pSeqTab==0 ){
sqlite3NestedParse(pParse,
@@ -57712,10 +59104,10 @@
#ifndef SQLITE_OMIT_AUTHORIZATION
xAuth = db->xAuth;
db->xAuth = 0;
- pSelTab = sqlite3ResultSetOfSelect(pParse, 0, pSel);
+ pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
db->xAuth = xAuth;
#else
- pSelTab = sqlite3ResultSetOfSelect(pParse, 0, pSel);
+ pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
#endif
pParse->nTab = n;
if( pSelTab ){
@@ -57949,7 +59341,7 @@
}
}
#endif
- if( pTab->readOnly || pTab==db->aDb[iDb].pSchema->pSeqTab ){
+ if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 ){
sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab->zName);
goto exit_drop_table;
}
@@ -58004,7 +59396,7 @@
** at the btree level, in case the sqlite_sequence table needs to
** move as a result of the drop (can happen in auto-vacuum mode).
*/
- if( pTab->autoInc ){
+ if( pTab->tabFlags & TF_Autoincrement ){
sqlite3NestedParse(pParse,
"DELETE FROM %s.sqlite_sequence WHERE name=%Q",
pDb->zName, pTab->zName
@@ -58074,6 +59466,7 @@
ExprList *pToCol, /* Columns in the other table */
int flags /* Conflict resolution algorithms. */
){
+ sqlite3 *db = pParse->db;
#ifndef SQLITE_OMIT_FOREIGN_KEY
FKey *pFKey = 0;
Table *p = pParse->pNewTable;
@@ -58081,10 +59474,8 @@
int i;
int nCol;
char *z;
- sqlite3 *db;
assert( pTo!=0 );
- db = pParse->db;
if( p==0 || pParse->nErr || IN_DECLARE_VTAB ) goto fk_end;
if( pFromCol==0 ){
int iCol = p->nCol-1;
@@ -58350,7 +59741,7 @@
pDb = &db->aDb[iDb];
if( pTab==0 || pParse->nErr ) goto exit_create_index;
- if( pTab->readOnly ){
+ if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 ){
sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName);
goto exit_create_index;
}
@@ -58442,9 +59833,10 @@
** specified collation sequence names.
*/
for(i=0; i<pList->nExpr; i++){
- Expr *pExpr = pList->a[i].pExpr;
- if( pExpr ){
- nExtra += (1 + strlen(pExpr->pColl->zName));
+ Expr *pExpr;
+ CollSeq *pColl;
+ if( (pExpr = pList->a[i].pExpr)!=0 && (pColl = pExpr->pColl)!=0 ){
+ nExtra += (1 + strlen(pColl->zName));
}
}
@@ -58511,7 +59903,7 @@
** break backwards compatibility - it needs to be a warning.
*/
pIndex->aiColumn[i] = j;
- if( pListItem->pExpr ){
+ if( pListItem->pExpr && pListItem->pExpr->pColl ){
assert( pListItem->pExpr->pColl );
zColl = zExtra;
sqlite3_snprintf(nExtra, zExtra, "%s", pListItem->pExpr->pColl->zName);
@@ -59450,7 +60842,7 @@
** This file contains functions used to access the internal hash tables
** of user defined functions and collation sequences.
**
-** $Id: callback.c,v 1.26 2008/07/28 19:34:53 drh Exp $
+** $Id: callback.c,v 1.29 2008/08/21 20:21:35 drh Exp $
*/
@@ -59658,6 +61050,87 @@
return pColl;
}
+/* During the search for the best function definition, this procedure
+** is called to test how well the function passed as the first argument
+** matches the request for a function with nArg arguments in a system
+** that uses encoding enc. The value returned indicates how well the
+** request is matched. A higher value indicates a better match.
+**
+** The returned value is always between 1 and 6, as follows:
+**
+** 1: A variable arguments function that prefers UTF-8 when a UTF-16
+** encoding is requested, or vice versa.
+** 2: A variable arguments function that uses UTF-16BE when UTF-16LE is
+** requested, or vice versa.
+** 3: A variable arguments function using the same text encoding.
+** 4: A function with the exact number of arguments requested that
+** prefers UTF-8 when a UTF-16 encoding is requested, or vice versa.
+** 5: A function with the exact number of arguments requested that
+** prefers UTF-16LE when UTF-16BE is requested, or vice versa.
+** 6: An exact match.
+**
+*/
+static int matchQuality(FuncDef *p, int nArg, u8 enc){
+ int match = 0;
+ if( p->nArg==-1 || p->nArg==nArg || nArg==-1 ){
+ match = 1;
+ if( p->nArg==nArg || nArg==-1 ){
+ match = 4;
+ }
+ if( enc==p->iPrefEnc ){
+ match += 2;
+ }
+ else if( (enc==SQLITE_UTF16LE && p->iPrefEnc==SQLITE_UTF16BE) ||
+ (enc==SQLITE_UTF16BE && p->iPrefEnc==SQLITE_UTF16LE) ){
+ match += 1;
+ }
+ }
+ return match;
+}
+
+/*
+** Search a FuncDefHash for a function with the given name. Return
+** a pointer to the matching FuncDef if found, or 0 if there is no match.
+*/
+static FuncDef *functionSearch(
+ FuncDefHash *pHash, /* Hash table to search */
+ int h, /* Hash of the name */
+ const char *zFunc, /* Name of function */
+ int nFunc /* Number of bytes in zFunc */
+){
+ FuncDef *p;
+ for(p=pHash->a[h]; p; p=p->pHash){
+ if( sqlite3StrNICmp(p->zName, zFunc, nFunc)==0 && p->zName[nFunc]==0 ){
+ return p;
+ }
+ }
+ return 0;
+}
+
+/*
+** Insert a new FuncDef into a FuncDefHash hash table.
+*/
+SQLITE_PRIVATE void sqlite3FuncDefInsert(
+ FuncDefHash *pHash, /* The hash table into which to insert */
+ FuncDef *pDef /* The function definition to insert */
+){
+ FuncDef *pOther;
+ int nName = strlen(pDef->zName);
+ u8 c1 = (u8)pDef->zName[0];
+ int h = (sqlite3UpperToLower[c1] + nName) % ArraySize(pHash->a);
+ pOther = functionSearch(pHash, h, pDef->zName, nName);
+ if( pOther ){
+ pDef->pNext = pOther->pNext;
+ pOther->pNext = pDef;
+ }else{
+ pDef->pNext = 0;
+ pDef->pHash = pHash->a[h];
+ pHash->a[h] = pDef;
+ }
+}
+
+
+
/*
** Locate a user function given a name, a number of arguments and a flag
** indicating whether the function prefers UTF-16 over UTF-8. Return a
@@ -59687,51 +61160,39 @@
int createFlag /* Create new entry if true and does not otherwise exist */
){
FuncDef *p; /* Iterator variable */
- FuncDef *pFirst; /* First function with this name */
FuncDef *pBest = 0; /* Best match found so far */
- int bestmatch = 0;
+ int bestScore = 0; /* Score of best match */
+ int h; /* Hash value */
assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
if( nArg<-1 ) nArg = -1;
+ h = (sqlite3UpperToLower[(u8)zName[0]] + nName) % ArraySize(db->aFunc.a);
- pFirst = (FuncDef*)sqlite3HashFind(&db->aFunc, zName, nName);
- for(p=pFirst; p; p=p->pNext){
- /* During the search for the best function definition, bestmatch is set
- ** as follows to indicate the quality of the match with the definition
- ** pointed to by pBest:
- **
- ** 0: pBest is NULL. No match has been found.
- ** 1: A variable arguments function that prefers UTF-8 when a UTF-16
- ** encoding is requested, or vice versa.
- ** 2: A variable arguments function that uses UTF-16BE when UTF-16LE is
- ** requested, or vice versa.
- ** 3: A variable arguments function using the same text encoding.
- ** 4: A function with the exact number of arguments requested that
- ** prefers UTF-8 when a UTF-16 encoding is requested, or vice versa.
- ** 5: A function with the exact number of arguments requested that
- ** prefers UTF-16LE when UTF-16BE is requested, or vice versa.
- ** 6: An exact match.
- **
- ** A larger value of 'matchqual' indicates a more desirable match.
- */
- if( p->nArg==-1 || p->nArg==nArg || nArg==-1 ){
- int match = 1; /* Quality of this match */
- if( p->nArg==nArg || nArg==-1 ){
- match = 4;
- }
- if( enc==p->iPrefEnc ){
- match += 2;
- }
- else if( (enc==SQLITE_UTF16LE && p->iPrefEnc==SQLITE_UTF16BE) ||
- (enc==SQLITE_UTF16BE && p->iPrefEnc==SQLITE_UTF16LE) ){
- match += 1;
- }
- if( match>bestmatch ){
+ p = functionSearch(&db->aFunc, h, zName, nName);
+ while( p ){
+ int score = matchQuality(p, nArg, enc);
+ if( score>bestScore ){
+ pBest = p;
+ bestScore = score;
+ }
+ p = p->pNext;
+ }
+
+ /* If the createFlag parameter is false and no match was found amongst
+ ** the custom functions stored in sqlite3.aFunc, try to find a built-in
+ ** function to use.
+ */
+ if( !createFlag && !pBest ){
+ p = functionSearch(&sqlite3GlobalFunctions, h, zName, nName);
+ while( p ){
+ int score = matchQuality(p, nArg, enc);
+ if( score>bestScore ){
pBest = p;
- bestmatch = match;
+ bestScore = score;
}
+ p = p->pNext;
}
}
@@ -59739,18 +61200,14 @@
** exact match for the name, number of arguments and encoding, then add a
** new entry to the hash table and return it.
*/
- if( createFlag && bestmatch<6 &&
- (pBest = sqlite3DbMallocZero(db, sizeof(*pBest)+nName))!=0 ){
+ if( createFlag && bestScore<6 &&
+ (pBest = sqlite3DbMallocZero(db, sizeof(*pBest)+nName+1))!=0 ){
+ pBest->zName = (char *)&pBest[1];
pBest->nArg = nArg;
- pBest->pNext = pFirst;
pBest->iPrefEnc = enc;
memcpy(pBest->zName, zName, nName);
pBest->zName[nName] = 0;
- if( pBest==sqlite3HashInsert(&db->aFunc,pBest->zName,nName,(void*)pBest) ){
- db->mallocFailed = 1;
- sqlite3DbFree(db, pBest);
- return 0;
- }
+ sqlite3FuncDefInsert(&db->aFunc, pBest);
}
if( pBest && (pBest->xStep || pBest->xFunc || createFlag) ){
@@ -59831,7 +61288,7 @@
** This file contains C code routines that are called by the parser
** in order to generate code for DELETE FROM statements.
**
-** $Id: delete.c,v 1.171 2008/07/28 19:34:53 drh Exp $
+** $Id: delete.c,v 1.174 2008/08/29 02:14:03 drh Exp $
*/
/*
@@ -59860,7 +61317,8 @@
** writable return 0;
*/
SQLITE_PRIVATE int sqlite3IsReadOnly(Parse *pParse, Table *pTab, int viewOk){
- if( (pTab->readOnly && (pParse->db->flags & SQLITE_WriteSchema)==0
+ if( ((pTab->tabFlags & TF_Readonly)!=0
+ && (pParse->db->flags & SQLITE_WriteSchema)==0
&& pParse->nested==0)
#ifndef SQLITE_OMIT_VIRTUALTABLE
|| (pTab->pMod && pTab->pMod->pModule->xUpdate==0)
@@ -59907,7 +61365,7 @@
*/
SQLITE_PRIVATE void sqlite3MaterializeView(
Parse *pParse, /* Parsing context */
- Select *pView, /* View definition */
+ Table *pView, /* View definition */
Expr *pWhere, /* Optional WHERE clause to be added */
int iCur /* Cursor number for ephemerial table */
){
@@ -59915,16 +61373,19 @@
Select *pDup;
sqlite3 *db = pParse->db;
- pDup = sqlite3SelectDup(db, pView);
+ pDup = sqlite3SelectDup(db, pView->pSelect);
if( pWhere ){
SrcList *pFrom;
+ Token viewName;
pWhere = sqlite3ExprDup(db, pWhere);
- pFrom = sqlite3SrcListAppendFromTerm(pParse, 0, 0, 0, 0, pDup, 0, 0);
+ viewName.z = (u8*)pView->zName;
+ viewName.n = (unsigned int)strlen((const char*)viewName.z);
+ pFrom = sqlite3SrcListAppendFromTerm(pParse, 0, 0, 0, &viewName, pDup, 0,0);
pDup = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, 0, 0, 0, 0);
}
sqlite3SelectDestInit(&dest, SRT_EphemTab, iCur);
- sqlite3Select(pParse, pDup, &dest, 0, 0, 0);
+ sqlite3Select(pParse, pDup, &dest);
sqlite3SelectDelete(db, pDup);
}
#endif /* !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) */
@@ -60064,7 +61525,7 @@
** a ephemeral table.
*/
if( isView ){
- sqlite3MaterializeView(pParse, pTab->pSelect, pWhere, iCur);
+ sqlite3MaterializeView(pParse, pTab, pWhere, iCur);
}
/* Resolve the column names in the WHERE clause.
@@ -60072,7 +61533,7 @@
memset(&sNC, 0, sizeof(sNC));
sNC.pParse = pParse;
sNC.pSrcList = pTabList;
- if( sqlite3ExprResolveNames(&sNC, pWhere) ){
+ if( sqlite3ResolveExprNames(&sNC, pWhere) ){
goto delete_from_cleanup;
}
@@ -60381,10 +61842,9 @@
** sqliteRegisterBuildinFunctions() found at the bottom of the file.
** All other code has file scope.
**
-** $Id: func.c,v 1.196 2008/07/28 19:34:53 drh Exp $
+** $Id: func.c,v 1.199 2008/08/21 20:21:35 drh Exp $
*/
-
/*
** Return the collating function associated with a function.
*/
@@ -61574,108 +63034,12 @@
** external linkage.
*/
SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
- static const struct {
- char *zName;
- signed char nArg;
- u8 argType; /* 1: 0, 2: 1, 3: 2,... N: N-1. */
- u8 eTextRep; /* 1: UTF-16. 0: UTF-8 */
- u8 needCollSeq;
- void (*xFunc)(sqlite3_context*,int,sqlite3_value **);
- } aFuncs[] = {
- { "min", -1, 0, SQLITE_UTF8, 1, minmaxFunc },
- { "min", 0, 0, SQLITE_UTF8, 1, 0 },
- { "max", -1, 1, SQLITE_UTF8, 1, minmaxFunc },
- { "max", 0, 1, SQLITE_UTF8, 1, 0 },
- { "typeof", 1, 0, SQLITE_UTF8, 0, typeofFunc },
- { "length", 1, 0, SQLITE_UTF8, 0, lengthFunc },
- { "substr", 2, 0, SQLITE_UTF8, 0, substrFunc },
- { "substr", 3, 0, SQLITE_UTF8, 0, substrFunc },
- { "abs", 1, 0, SQLITE_UTF8, 0, absFunc },
- { "round", 1, 0, SQLITE_UTF8, 0, roundFunc },
- { "round", 2, 0, SQLITE_UTF8, 0, roundFunc },
- { "upper", 1, 0, SQLITE_UTF8, 0, upperFunc },
- { "lower", 1, 0, SQLITE_UTF8, 0, lowerFunc },
- { "coalesce", -1, 0, SQLITE_UTF8, 0, ifnullFunc },
- { "coalesce", 0, 0, SQLITE_UTF8, 0, 0 },
- { "coalesce", 1, 0, SQLITE_UTF8, 0, 0 },
- { "hex", 1, 0, SQLITE_UTF8, 0, hexFunc },
- { "ifnull", 2, 0, SQLITE_UTF8, 1, ifnullFunc },
- { "random", -1, 0, SQLITE_UTF8, 0, randomFunc },
- { "randomblob", 1, 0, SQLITE_UTF8, 0, randomBlob },
- { "nullif", 2, 0, SQLITE_UTF8, 1, nullifFunc },
- { "sqlite_version", 0, 0, SQLITE_UTF8, 0, versionFunc},
- { "quote", 1, 0, SQLITE_UTF8, 0, quoteFunc },
- { "last_insert_rowid", 0, 0, SQLITE_UTF8, 0, last_insert_rowid },
- { "changes", 0, 0, SQLITE_UTF8, 0, changes },
- { "total_changes", 0, 0, SQLITE_UTF8, 0, total_changes },
- { "replace", 3, 0, SQLITE_UTF8, 0, replaceFunc },
- { "ltrim", 1, 1, SQLITE_UTF8, 0, trimFunc },
- { "ltrim", 2, 1, SQLITE_UTF8, 0, trimFunc },
- { "rtrim", 1, 2, SQLITE_UTF8, 0, trimFunc },
- { "rtrim", 2, 2, SQLITE_UTF8, 0, trimFunc },
- { "trim", 1, 3, SQLITE_UTF8, 0, trimFunc },
- { "trim", 2, 3, SQLITE_UTF8, 0, trimFunc },
- { "zeroblob", 1, 0, SQLITE_UTF8, 0, zeroblobFunc },
-#ifdef SQLITE_SOUNDEX
- { "soundex", 1, 0, SQLITE_UTF8, 0, soundexFunc},
-#endif
-#ifndef SQLITE_OMIT_LOAD_EXTENSION
- { "load_extension", 1, 0, SQLITE_UTF8, 0, loadExt },
- { "load_extension", 2, 0, SQLITE_UTF8, 0, loadExt },
-#endif
- };
- static const struct {
- char *zName;
- signed char nArg;
- u8 argType;
- u8 needCollSeq;
- void (*xStep)(sqlite3_context*,int,sqlite3_value**);
- void (*xFinalize)(sqlite3_context*);
- } aAggs[] = {
- { "min", 1, 0, 1, minmaxStep, minMaxFinalize },
- { "max", 1, 1, 1, minmaxStep, minMaxFinalize },
- { "sum", 1, 0, 0, sumStep, sumFinalize },
- { "total", 1, 0, 0, sumStep, totalFinalize },
- { "avg", 1, 0, 0, sumStep, avgFinalize },
- { "count", 0, 0, 0, countStep, countFinalize },
- { "count", 1, 0, 0, countStep, countFinalize },
- { "group_concat", -1, 0, 0, groupConcatStep, groupConcatFinalize },
- };
- int i;
-
- for(i=0; i<sizeof(aFuncs)/sizeof(aFuncs[0]); i++){
- void *pArg;
- u8 argType = aFuncs[i].argType;
- pArg = SQLITE_INT_TO_PTR(argType);
- sqlite3CreateFunc(db, aFuncs[i].zName, aFuncs[i].nArg,
- aFuncs[i].eTextRep, pArg, aFuncs[i].xFunc, 0, 0);
- if( aFuncs[i].needCollSeq ){
- FuncDef *pFunc = sqlite3FindFunction(db, aFuncs[i].zName,
- strlen(aFuncs[i].zName), aFuncs[i].nArg, aFuncs[i].eTextRep, 0);
- if( pFunc && aFuncs[i].needCollSeq ){
- pFunc->needCollSeq = 1;
- }
- }
- }
#ifndef SQLITE_OMIT_ALTERTABLE
sqlite3AlterFunctions(db);
#endif
#ifndef SQLITE_OMIT_PARSER
sqlite3AttachFunctions(db);
#endif
- for(i=0; i<sizeof(aAggs)/sizeof(aAggs[0]); i++){
- void *pArg = SQLITE_INT_TO_PTR(aAggs[i].argType);
- sqlite3CreateFunc(db, aAggs[i].zName, aAggs[i].nArg, SQLITE_UTF8,
- pArg, 0, aAggs[i].xStep, aAggs[i].xFinalize);
- if( aAggs[i].needCollSeq ){
- FuncDef *pFunc = sqlite3FindFunction( db, aAggs[i].zName,
- strlen(aAggs[i].zName), aAggs[i].nArg, SQLITE_UTF8, 0);
- if( pFunc && aAggs[i].needCollSeq ){
- pFunc->needCollSeq = 1;
- }
- }
- }
- sqlite3RegisterDateTimeFunctions(db);
if( !db->mallocFailed ){
int rc = sqlite3_overload_function(db, "MATCH", 2);
assert( rc==SQLITE_NOMEM || rc==SQLITE_OK );
@@ -61686,11 +63050,6 @@
#ifdef SQLITE_SSE
(void)sqlite3SseFunctions(db);
#endif
-#ifdef SQLITE_CASE_SENSITIVE_LIKE
- sqlite3RegisterLikeFunctions(db, 1);
-#else
- sqlite3RegisterLikeFunctions(db, 0);
-#endif
}
/*
@@ -61758,6 +63117,90 @@
return 1;
}
+/*
+** The following array holds FuncDef structures for all of the functions
+** defined in this file.
+**
+** The array cannot be constant since changes are made to the
+** FuncDef.pHash elements at start-time. The elements of this array
+** are read-only after initialization is complete.
+*/
+static FuncDef aBuiltinFunc[] = {
+ FUNCTION(ltrim, 1, 1, 0, trimFunc ),
+ FUNCTION(ltrim, 2, 1, 0, trimFunc ),
+ FUNCTION(rtrim, 1, 2, 0, trimFunc ),
+ FUNCTION(rtrim, 2, 2, 0, trimFunc ),
+ FUNCTION(trim, 1, 3, 0, trimFunc ),
+ FUNCTION(trim, 2, 3, 0, trimFunc ),
+ FUNCTION(min, -1, 0, 1, minmaxFunc ),
+ FUNCTION(min, 0, 0, 1, 0 ),
+ AGGREGATE(min, 1, 0, 1, minmaxStep, minMaxFinalize ),
+ FUNCTION(max, -1, 1, 1, minmaxFunc ),
+ FUNCTION(max, 0, 1, 1, 0 ),
+ AGGREGATE(max, 1, 1, 1, minmaxStep, minMaxFinalize ),
+ FUNCTION(typeof, 1, 0, 0, typeofFunc ),
+ FUNCTION(length, 1, 0, 0, lengthFunc ),
+ FUNCTION(substr, 2, 0, 0, substrFunc ),
+ FUNCTION(substr, 3, 0, 0, substrFunc ),
+ FUNCTION(abs, 1, 0, 0, absFunc ),
+ FUNCTION(round, 1, 0, 0, roundFunc ),
+ FUNCTION(round, 2, 0, 0, roundFunc ),
+ FUNCTION(upper, 1, 0, 0, upperFunc ),
+ FUNCTION(lower, 1, 0, 0, lowerFunc ),
+ FUNCTION(coalesce, 1, 0, 0, 0 ),
+ FUNCTION(coalesce, -1, 0, 0, ifnullFunc ),
+ FUNCTION(coalesce, 0, 0, 0, 0 ),
+ FUNCTION(hex, 1, 0, 0, hexFunc ),
+ FUNCTION(ifnull, 2, 0, 1, ifnullFunc ),
+ FUNCTION(random, -1, 0, 0, randomFunc ),
+ FUNCTION(randomblob, 1, 0, 0, randomBlob ),
+ FUNCTION(nullif, 2, 0, 1, nullifFunc ),
+ FUNCTION(sqlite_version, 0, 0, 0, versionFunc ),
+ FUNCTION(quote, 1, 0, 0, quoteFunc ),
+ FUNCTION(last_insert_rowid, 0, 0, 0, last_insert_rowid),
+ FUNCTION(changes, 0, 0, 0, changes ),
+ FUNCTION(total_changes, 0, 0, 0, total_changes ),
+ FUNCTION(replace, 3, 0, 0, replaceFunc ),
+ FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ),
+#ifdef SQLITE_SOUNDEX
+ FUNCTION(soundex, 1, 0, 0, soundexFunc ),
+#endif
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+ FUNCTION(load_extension, 1, 0, 0, loadExt ),
+ FUNCTION(load_extension, 2, 0, 0, loadExt ),
+#endif
+ AGGREGATE(sum, 1, 0, 0, sumStep, sumFinalize ),
+ AGGREGATE(total, 1, 0, 0, sumStep, totalFinalize ),
+ AGGREGATE(avg, 1, 0, 0, sumStep, avgFinalize ),
+ AGGREGATE(count, 0, 0, 0, countStep, countFinalize ),
+ AGGREGATE(count, 1, 0, 0, countStep, countFinalize ),
+ AGGREGATE(group_concat, -1, 0, 0, groupConcatStep, groupConcatFinalize),
+
+ LIKEFUNC(glob, 2, &globInfo, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
+#ifdef SQLITE_CASE_SENSITIVE_LIKE
+ LIKEFUNC(like, 2, &likeInfoAlt, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
+ LIKEFUNC(like, 3, &likeInfoAlt, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
+#else
+ LIKEFUNC(like, 2, &likeInfoNorm, SQLITE_FUNC_LIKE),
+ LIKEFUNC(like, 3, &likeInfoNorm, SQLITE_FUNC_LIKE),
+#endif
+};
+
+/*
+** All all of the FuncDef structures in the aBuiltinFunc[] array above
+** to the global function hash table. This occurs at start-time (as
+** a consequence of calling sqlite3_initialize()).
+**
+** After this routine runs
+*/
+SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void){
+ int i;
+ for(i=0; i<ArraySize(aBuiltinFunc); i++){
+ sqlite3FuncDefInsert(&sqlite3GlobalFunctions, &aBuiltinFunc[i]);
+ }
+ sqlite3RegisterDateTimeFunctions();
+}
+
/************** End of func.c ************************************************/
/************** Begin file insert.c ******************************************/
/*
@@ -61774,7 +63217,7 @@
** This file contains C code routines that are called by the parser
** to handle INSERT statements in SQLite.
**
-** $Id: insert.c,v 1.248 2008/07/28 19:34:53 drh Exp $
+** $Id: insert.c,v 1.249 2008/08/20 16:35:10 drh Exp $
*/
/*
@@ -61926,7 +63369,7 @@
Table *pTab /* The table we are writing to */
){
int memId = 0; /* Register holding maximum rowid */
- if( pTab->autoInc ){
+ if( pTab->tabFlags & TF_Autoincrement ){
Vdbe *v = pParse->pVdbe;
Db *pDb = &pParse->db->aDb[iDb];
int iCur = pParse->nTab;
@@ -61977,7 +63420,7 @@
Table *pTab, /* Table we are inserting into */
int memId /* Memory cell holding the maximum rowid */
){
- if( pTab->autoInc ){
+ if( pTab->tabFlags & TF_Autoincrement ){
int iCur = pParse->nTab;
Vdbe *v = pParse->pVdbe;
Db *pDb = &pParse->db->aDb[iDb];
@@ -62293,7 +63736,7 @@
VdbeComment((v, "Jump over SELECT coroutine"));
/* Resolve the expressions in the SELECT statement and execute it. */
- rc = sqlite3Select(pParse, pSelect, &dest, 0, 0, 0);
+ rc = sqlite3Select(pParse, pSelect, &dest);
if( rc || pParse->nErr || db->mallocFailed ){
goto insert_cleanup;
}
@@ -62363,7 +63806,7 @@
assert( useTempTable==0 );
nColumn = pList ? pList->nExpr : 0;
for(i=0; i<nColumn; i++){
- if( sqlite3ExprResolveNames(&sNC, pList->a[i].pExpr) ){
+ if( sqlite3ResolveExprNames(&sNC, pList->a[i].pExpr) ){
goto insert_cleanup;
}
}
@@ -63296,7 +64739,7 @@
return 0; /* tab1 must not have triggers */
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
- if( pDest->isVirtual ){
+ if( pDest->tabFlags & TF_Virtual ){
return 0; /* tab1 must not be a virtual table */
}
#endif
@@ -63331,7 +64774,7 @@
if( pSelect->pPrior ){
return 0; /* SELECT may not be a compound query */
}
- if( pSelect->isDistinct ){
+ if( pSelect->selFlags & SF_Distinct ){
return 0; /* SELECT may not be DISTINCT */
}
pEList = pSelect->pEList;
@@ -63357,7 +64800,7 @@
return 0; /* tab1 and tab2 may not be the same table */
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
- if( pSrc->isVirtual ){
+ if( pSrc->tabFlags & TF_Virtual ){
return 0; /* tab2 must not be a virtual table */
}
#endif
@@ -63448,7 +64891,7 @@
addr1 = sqlite3VdbeAddOp2(v, OP_NewRowid, iDest, regRowid);
}else{
addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid);
- assert( pDest->autoInc==0 );
+ assert( (pDest->tabFlags & TF_Autoincrement)==0 );
}
sqlite3VdbeAddOp2(v, OP_RowData, iSrc, regData);
sqlite3VdbeAddOp3(v, OP_Insert, iDest, regData, regRowid);
@@ -64604,7 +66047,7 @@
*************************************************************************
** This file contains code used to implement the PRAGMA command.
**
-** $Id: pragma.c,v 1.183 2008/07/28 19:34:53 drh Exp $
+** $Id: pragma.c,v 1.184 2008/08/20 16:34:24 danielk1977 Exp $
*/
/* Ignore this whole file if pragmas are disabled
@@ -64866,7 +66309,7 @@
zRight = sqlite3NameFromToken(db, pValue);
}
- zDb = ((iDb>0)?pDb->zName:0);
+ zDb = ((pId2 && pId2->n>0)?pDb->zName:0);
if( sqlite3AuthCheck(pParse, SQLITE_PRAGMA, zLeft, zRight, zDb) ){
goto pragma_out;
}
@@ -65978,7 +67421,7 @@
** interface, and routines that contribute to loading the database schema
** from disk.
**
-** $Id: prepare.c,v 1.91 2008/08/02 03:50:39 drh Exp $
+** $Id: prepare.c,v 1.93 2008/08/20 16:35:10 drh Exp $
*/
/*
@@ -65990,7 +67433,8 @@
const char *zObj, /* Object being parsed at the point of error */
const char *zExtra /* Error information */
){
- if( !pData->db->mallocFailed ){
+ sqlite3 *db = pData->db;
+ if( !db->mallocFailed && (db->flags & SQLITE_RecoveryMode)==0 ){
if( zObj==0 ) zObj = "?";
sqlite3SetString(pData->pzErrMsg, pData->db,
"malformed database schema (%s)", zObj);
@@ -66020,7 +67464,6 @@
int iDb = pData->iDb;
assert( sqlite3_mutex_held(db->mutex) );
- pData->rc = SQLITE_OK;
DbClearProperty(db, iDb, DB_Empty);
if( db->mallocFailed ){
corruptSchema(pData, argv[0], 0);
@@ -66060,7 +67503,6 @@
corruptSchema(pData, argv[0], zErr);
}
sqlite3DbFree(db, zErr);
- return 1;
}
}else if( argv[0]==0 ){
corruptSchema(pData, 0, 0);
@@ -66156,17 +67598,18 @@
azArg[3] = 0;
initData.db = db;
initData.iDb = iDb;
+ initData.rc = SQLITE_OK;
initData.pzErrMsg = pzErrMsg;
(void)sqlite3SafetyOff(db);
- rc = sqlite3InitCallback(&initData, 3, (char **)azArg, 0);
+ sqlite3InitCallback(&initData, 3, (char **)azArg, 0);
(void)sqlite3SafetyOn(db);
- if( rc ){
+ if( initData.rc ){
rc = initData.rc;
goto error_out;
}
pTab = sqlite3FindTable(db, zMasterName, db->aDb[iDb].zName);
if( pTab ){
- pTab->readOnly = 1;
+ pTab->tabFlags |= TF_Readonly;
}
/* Create a cursor to hold the database open
@@ -66301,7 +67744,7 @@
db->xAuth = xAuth;
}
#endif
- if( rc==SQLITE_ABORT ) rc = initData.rc;
+ if( rc==SQLITE_OK ) rc = initData.rc;
(void)sqlite3SafetyOn(db);
sqlite3DbFree(db, zSql);
#ifndef SQLITE_OMIT_ANALYZE
@@ -66791,7 +68234,7 @@
** This file contains C code routines that are called by the parser
** to handle SELECT statements in SQLite.
**
-** $Id: select.c,v 1.463 2008/08/04 03:51:24 danielk1977 Exp $
+** $Id: select.c,v 1.471 2008/08/26 12:56:14 drh Exp $
*/
@@ -66857,7 +68300,7 @@
pNew->pGroupBy = pGroupBy;
pNew->pHaving = pHaving;
pNew->pOrderBy = pOrderBy;
- pNew->isDistinct = isDistinct;
+ pNew->selFlags = isDistinct ? SF_Distinct : 0;
pNew->op = TK_SELECT;
assert( pOffset==0 || pLimit!=0 );
pNew->pLimit = pLimit;
@@ -66865,8 +68308,9 @@
pNew->addrOpenEphm[0] = -1;
pNew->addrOpenEphm[1] = -1;
pNew->addrOpenEphm[2] = -1;
- if( pNew==&standin) {
+ if( db->mallocFailed ) {
clearSelect(db, pNew);
+ if( pNew!=&standin ) sqlite3DbFree(db, pNew);
pNew = 0;
}
return pNew;
@@ -67351,7 +68795,7 @@
/* If the destination is an EXISTS(...) expression, the actual
** values returned by the SELECT are not required.
*/
- sqlite3ExprCodeExprList(pParse, pEList, regResult, eDest==SRT_Callback);
+ sqlite3ExprCodeExprList(pParse, pEList, regResult, eDest==SRT_Output);
}
nColumn = nResultCol;
@@ -67468,7 +68912,7 @@
** popping the data from the stack.
*/
case SRT_Coroutine:
- case SRT_Callback: {
+ case SRT_Output: {
if( pOrderBy ){
int r1 = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nColumn, r1);
@@ -67575,10 +69019,10 @@
int regRowid;
iTab = pOrderBy->iECursor;
- if( eDest==SRT_Callback || eDest==SRT_Coroutine ){
+ if( eDest==SRT_Output || eDest==SRT_Coroutine ){
pseudoTab = pParse->nTab++;
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, nColumn);
- sqlite3VdbeAddOp2(v, OP_OpenPseudo, pseudoTab, eDest==SRT_Callback);
+ sqlite3VdbeAddOp2(v, OP_OpenPseudo, pseudoTab, eDest==SRT_Output);
}
addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, brk);
codeOffset(v, p, cont);
@@ -67608,7 +69052,7 @@
break;
}
#endif
- case SRT_Callback:
+ case SRT_Output:
case SRT_Coroutine: {
int i;
sqlite3VdbeAddOp2(v, OP_Integer, 1, regRowid);
@@ -67617,7 +69061,7 @@
assert( regRow!=pDest->iMem+i );
sqlite3VdbeAddOp3(v, OP_Column, pseudoTab, i, pDest->iMem+i);
}
- if( eDest==SRT_Callback ){
+ if( eDest==SRT_Output ){
sqlite3VdbeAddOp2(v, OP_ResultRow, pDest->iMem, nColumn);
sqlite3ExprCacheAffinityChange(pParse, pDest->iMem, nColumn);
}else{
@@ -67642,7 +69086,7 @@
sqlite3VdbeResolveLabel(v, cont);
sqlite3VdbeAddOp2(v, OP_Next, iTab, addr);
sqlite3VdbeResolveLabel(v, brk);
- if( eDest==SRT_Callback || eDest==SRT_Coroutine ){
+ if( eDest==SRT_Output || eDest==SRT_Coroutine ){
sqlite3VdbeAddOp2(v, OP_Close, pseudoTab, 0);
}
@@ -67906,58 +69350,36 @@
#endif /* SQLITE_OMIT_COMPOUND_SELECT */
/*
-** Forward declaration
-*/
-static int prepSelectStmt(Parse*, Select*);
-
-/*
-** Given a SELECT statement, generate a Table structure that describes
-** the result set of that SELECT.
+** Given a an expression list (which is really the list of expressions
+** that form the result set of a SELECT statement) compute appropriate
+** column names for a table that would hold the expression list.
+**
+** All column names will be unique.
+**
+** Only the column names are computed. Column.zType, Column.zColl,
+** and other fields of Column are zeroed.
+**
+** Return SQLITE_OK on success. If a memory allocation error occurs,
+** store NULL in *paCol and 0 in *pnCol and return SQLITE_NOMEM.
*/
-SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse *pParse, char *zTabName, Select *pSelect){
- Table *pTab;
- int i, j, rc;
- ExprList *pEList;
- Column *aCol, *pCol;
+static int selectColumnsFromExprList(
+ Parse *pParse, /* Parsing context */
+ ExprList *pEList, /* Expr list from which to derive column names */
+ int *pnCol, /* Write the number of columns here */
+ Column **paCol /* Write the new column list here */
+){
sqlite3 *db = pParse->db;
- int savedFlags;
+ int i, j, cnt;
+ Column *aCol, *pCol;
+ int nCol;
+ Expr *p;
+ char *zName;
+ int nName;
- savedFlags = db->flags;
- db->flags &= ~SQLITE_FullColNames;
- db->flags |= SQLITE_ShortColNames;
- rc = sqlite3SelectResolve(pParse, pSelect, 0);
- if( rc==SQLITE_OK ){
- while( pSelect->pPrior ) pSelect = pSelect->pPrior;
- rc = prepSelectStmt(pParse, pSelect);
- if( rc==SQLITE_OK ){
- rc = sqlite3SelectResolve(pParse, pSelect, 0);
- }
- }
- db->flags = savedFlags;
- if( rc ){
- return 0;
- }
- pTab = sqlite3DbMallocZero(db, sizeof(Table) );
- if( pTab==0 ){
- return 0;
- }
- pTab->db = db;
- pTab->nRef = 1;
- pTab->zName = zTabName ? sqlite3DbStrDup(db, zTabName) : 0;
- pEList = pSelect->pEList;
- pTab->nCol = pEList->nExpr;
- assert( pTab->nCol>0 );
- pTab->aCol = aCol = sqlite3DbMallocZero(db, sizeof(pTab->aCol[0])*pTab->nCol);
- testcase( aCol==0 );
- for(i=0, pCol=aCol; i<pTab->nCol; i++, pCol++){
- Expr *p;
- char *zType;
- char *zName;
- int nName;
- CollSeq *pColl;
- int cnt;
- NameContext sNC;
-
+ *pnCol = nCol = pEList->nExpr;
+ aCol = *paCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol);
+ if( aCol==0 ) return SQLITE_NOMEM;
+ for(i=0, pCol=aCol; i<nCol; i++, pCol++){
/* Get an appropriate name for the column
*/
p = pEList->a[i].pExpr;
@@ -67965,14 +69387,20 @@
if( (zName = pEList->a[i].zName)!=0 ){
/* If the column contains an "AS <name>" phrase, use <name> as the name */
zName = sqlite3DbStrDup(db, zName);
- }else if( p->op==TK_COLUMN && p->pTab ){
- /* For columns use the column name name */
- int iCol = p->iColumn;
- if( iCol<0 ) iCol = p->pTab->iPKey;
- zName = sqlite3MPrintf(db, "%s", p->pTab->aCol[iCol].zName);
}else{
- /* Use the original text of the column expression as its name */
- zName = sqlite3MPrintf(db, "%T", &p->span);
+ Expr *pCol = p;
+ Table *pTab;
+ while( pCol->op==TK_DOT ) pCol = pCol->pRight;
+ if( pCol->op==TK_COLUMN && (pTab = pCol->pTab)!=0 ){
+ /* For columns use the column name name */
+ int iCol = pCol->iColumn;
+ if( iCol<0 ) iCol = pTab->iPKey;
+ zName = sqlite3MPrintf(db, "%s",
+ iCol>=0 ? pTab->aCol[iCol].zName : "rowid");
+ }else{
+ /* Use the original text of the column expression as its name */
+ zName = sqlite3MPrintf(db, "%T", &pCol->span);
+ }
}
if( db->mallocFailed ){
sqlite3DbFree(db, zName);
@@ -67989,525 +69417,101 @@
char *zNewName;
zName[nName] = 0;
zNewName = sqlite3MPrintf(db, "%s:%d", zName, ++cnt);
- sqlite3DbFree(db, zName);
- zName = zNewName;
- j = -1;
- if( zName==0 ) break;
- }
- }
- pCol->zName = zName;
-
- /* Get the typename, type affinity, and collating sequence for the
- ** column.
- */
- memset(&sNC, 0, sizeof(sNC));
- sNC.pSrcList = pSelect->pSrc;
- zType = sqlite3DbStrDup(db, columnType(&sNC, p, 0, 0, 0));
- pCol->zType = zType;
- pCol->affinity = sqlite3ExprAffinity(p);
- pColl = sqlite3ExprCollSeq(pParse, p);
- if( pColl ){
- pCol->zColl = sqlite3DbStrDup(db, pColl->zName);
- }
- }
- pTab->iPKey = -1;
- if( db->mallocFailed ){
- sqlite3DeleteTable(pTab);
- return 0;
- }
- return pTab;
-}
-
-/*
-** Prepare a SELECT statement for processing by doing the following
-** things:
-**
-** (1) Make sure VDBE cursor numbers have been assigned to every
-** element of the FROM clause.
-**
-** (2) Fill in the pTabList->a[].pTab fields in the SrcList that
-** defines FROM clause. When views appear in the FROM clause,
-** fill pTabList->a[].pSelect with a copy of the SELECT statement
-** that implements the view. A copy is made of the view's SELECT
-** statement so that we can freely modify or delete that statement
-** without worrying about messing up the presistent representation
-** of the view.
-**
-** (3) Add terms to the WHERE clause to accomodate the NATURAL keyword
-** on joins and the ON and USING clause of joins.
-**
-** (4) Scan the list of columns in the result set (pEList) looking
-** for instances of the "*" operator or the TABLE.* operator.
-** If found, expand each "*" to be every column in every table
-** and TABLE.* to be every column in TABLE.
-**
-** Return 0 on success. If there are problems, leave an error message
-** in pParse and return non-zero.
-*/
-static int prepSelectStmt(Parse *pParse, Select *p){
- int i, j, k, rc;
- SrcList *pTabList;
- ExprList *pEList;
- struct SrcList_item *pFrom;
- sqlite3 *db = pParse->db;
-
- if( p==0 || p->pSrc==0 || db->mallocFailed ){
- return 1;
- }
- pTabList = p->pSrc;
- pEList = p->pEList;
-
- /* Make sure cursor numbers have been assigned to all entries in
- ** the FROM clause of the SELECT statement.
- */
- sqlite3SrcListAssignCursors(pParse, p->pSrc);
-
- /* Look up every table named in the FROM clause of the select. If
- ** an entry of the FROM clause is a subquery instead of a table or view,
- ** then create a transient table structure to describe the subquery.
- */
- for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
- Table *pTab;
- if( pFrom->pTab!=0 ){
- /* This statement has already been prepared. There is no need
- ** to go further. */
- assert( i==0 );
- return 0;
- }
- if( pFrom->zName==0 ){
-#ifndef SQLITE_OMIT_SUBQUERY
- /* A sub-query in the FROM clause of a SELECT */
- assert( pFrom->pSelect!=0 );
- if( pFrom->zAlias==0 ){
- pFrom->zAlias =
- sqlite3MPrintf(db, "sqlite_subquery_%p_", (void*)pFrom->pSelect);
- }
- assert( pFrom->pTab==0 );
- pFrom->pTab = pTab =
- sqlite3ResultSetOfSelect(pParse, pFrom->zAlias, pFrom->pSelect);
- if( pTab==0 ){
- return 1;
- }
- /* The isEphem flag indicates that the Table structure has been
- ** dynamically allocated and may be freed at any time. In other words,
- ** pTab is not pointing to a persistent table structure that defines
- ** part of the schema. */
- pTab->isEphem = 1;
-#endif
- }else{
- /* An ordinary table or view name in the FROM clause */
- assert( pFrom->pTab==0 );
- pFrom->pTab = pTab =
- sqlite3LocateTable(pParse,0,pFrom->zName,pFrom->zDatabase);
- if( pTab==0 ){
- return 1;
- }
- pTab->nRef++;
-#if !defined(SQLITE_OMIT_VIEW) || !defined (SQLITE_OMIT_VIRTUALTABLE)
- if( pTab->pSelect || IsVirtual(pTab) ){
- /* We reach here if the named table is a really a view */
- if( sqlite3ViewGetColumnNames(pParse, pTab) ){
- return 1;
- }
- /* If pFrom->pSelect!=0 it means we are dealing with a
- ** view within a view. The SELECT structure has already been
- ** copied by the outer view so we can skip the copy step here
- ** in the inner view.
- */
- if( pFrom->pSelect==0 ){
- pFrom->pSelect = sqlite3SelectDup(db, pTab->pSelect);
- }
- }
-#endif
- }
- }
-
- /* Process NATURAL keywords, and ON and USING clauses of joins.
- */
- if( sqliteProcessJoin(pParse, p) ) return 1;
-
- /* For every "*" that occurs in the column list, insert the names of
- ** all columns in all tables. And for every TABLE.* insert the names
- ** of all columns in TABLE. The parser inserted a special expression
- ** with the TK_ALL operator for each "*" that it found in the column list.
- ** The following code just has to locate the TK_ALL expressions and expand
- ** each one to the list of all columns in all tables.
- **
- ** The first loop just checks to see if there are any "*" operators
- ** that need expanding.
- */
- for(k=0; k<pEList->nExpr; k++){
- Expr *pE = pEList->a[k].pExpr;
- if( pE->op==TK_ALL ) break;
- if( pE->op==TK_DOT && pE->pRight && pE->pRight->op==TK_ALL
- && pE->pLeft && pE->pLeft->op==TK_ID ) break;
- }
- rc = 0;
- if( k<pEList->nExpr ){
- /*
- ** If we get here it means the result set contains one or more "*"
- ** operators that need to be expanded. Loop through each expression
- ** in the result set and expand them one by one.
- */
- struct ExprList_item *a = pEList->a;
- ExprList *pNew = 0;
- int flags = pParse->db->flags;
- int longNames = (flags & SQLITE_FullColNames)!=0
- && (flags & SQLITE_ShortColNames)==0;
-
- for(k=0; k<pEList->nExpr; k++){
- Expr *pE = a[k].pExpr;
- if( pE->op!=TK_ALL &&
- (pE->op!=TK_DOT || pE->pRight==0 || pE->pRight->op!=TK_ALL) ){
- /* This particular expression does not need to be expanded.
- */
- pNew = sqlite3ExprListAppend(pParse, pNew, a[k].pExpr, 0);
- if( pNew ){
- pNew->a[pNew->nExpr-1].zName = a[k].zName;
- }else{
- rc = 1;
- }
- a[k].pExpr = 0;
- a[k].zName = 0;
- }else{
- /* This expression is a "*" or a "TABLE.*" and needs to be
- ** expanded. */
- int tableSeen = 0; /* Set to 1 when TABLE matches */
- char *zTName; /* text of name of TABLE */
- if( pE->op==TK_DOT && pE->pLeft ){
- zTName = sqlite3NameFromToken(db, &pE->pLeft->token);
- }else{
- zTName = 0;
- }
- for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
- Table *pTab = pFrom->pTab;
- char *zTabName = pFrom->zAlias;
- if( zTabName==0 || zTabName[0]==0 ){
- zTabName = pTab->zName;
- }
- assert( zTabName );
- if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){
- continue;
- }
- tableSeen = 1;
- for(j=0; j<pTab->nCol; j++){
- Expr *pExpr, *pRight;
- char *zName = pTab->aCol[j].zName;
-
- /* If a column is marked as 'hidden' (currently only possible
- ** for virtual tables), do not include it in the expanded
- ** result-set list.
- */
- if( IsHiddenColumn(&pTab->aCol[j]) ){
- assert(IsVirtual(pTab));
- continue;
- }
-
- if( i>0 ){
- struct SrcList_item *pLeft = &pTabList->a[i-1];
- if( (pLeft[1].jointype & JT_NATURAL)!=0 &&
- columnIndex(pLeft->pTab, zName)>=0 ){
- /* In a NATURAL join, omit the join columns from the
- ** table on the right */
- continue;
- }
- if( sqlite3IdListIndex(pLeft[1].pUsing, zName)>=0 ){
- /* In a join with a USING clause, omit columns in the
- ** using clause from the table on the right. */
- continue;
- }
- }
- pRight = sqlite3PExpr(pParse, TK_ID, 0, 0, 0);
- if( pRight==0 ) break;
- setQuotedToken(pParse, &pRight->token, zName);
- if( longNames || pTabList->nSrc>1 ){
- Expr *pLeft = sqlite3PExpr(pParse, TK_ID, 0, 0, 0);
- pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0);
- if( pExpr==0 ) break;
- setQuotedToken(pParse, &pLeft->token, zTabName);
-#if 1
- setToken(&pExpr->span,
- sqlite3MPrintf(db, "%s.%s", zTabName, zName));
- pExpr->span.dyn = 1;
-#else
- pExpr->span = pRight->token;
- pExpr->span.dyn = 0;
-#endif
- pExpr->token.z = 0;
- pExpr->token.n = 0;
- pExpr->token.dyn = 0;
- }else{
- pExpr = pRight;
- pExpr->span = pExpr->token;
- pExpr->span.dyn = 0;
- }
- if( longNames ){
- pNew = sqlite3ExprListAppend(pParse, pNew, pExpr, &pExpr->span);
- }else{
- pNew = sqlite3ExprListAppend(pParse, pNew, pExpr, &pRight->token);
- }
- }
- }
- if( !tableSeen ){
- if( zTName ){
- sqlite3ErrorMsg(pParse, "no such table: %s", zTName);
- }else{
- sqlite3ErrorMsg(pParse, "no tables specified");
- }
- rc = 1;
- }
- sqlite3DbFree(db, zTName);
- }
- }
- sqlite3ExprListDelete(db, pEList);
- p->pEList = pNew;
- }
-#if SQLITE_MAX_COLUMN
- if( p->pEList && p->pEList->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){
- sqlite3ErrorMsg(pParse, "too many columns in result set");
- rc = SQLITE_ERROR;
- }
-#endif
- if( db->mallocFailed ){
- rc = SQLITE_NOMEM;
- }
- return rc;
-}
-
-/*
-** pE is a pointer to an expression which is a single term in
-** ORDER BY or GROUP BY clause.
-**
-** At the point this routine is called, we already know that the
-** ORDER BY term is not an integer index into the result set. That
-** casee is handled by the calling routine.
-**
-** If pE is a well-formed expression and the SELECT statement
-** is not compound, then return 0. This indicates to the
-** caller that it should sort by the value of the ORDER BY
-** expression.
-**
-** If the SELECT is compound, then attempt to match pE against
-** result set columns in the left-most SELECT statement. Return
-** the index i of the matching column, as an indication to the
-** caller that it should sort by the i-th column. If there is
-** no match, return -1 and leave an error message in pParse.
-*/
-static int matchOrderByTermToExprList(
- Parse *pParse, /* Parsing context for error messages */
- Select *pSelect, /* The SELECT statement with the ORDER BY clause */
- Expr *pE, /* The specific ORDER BY term */
- int idx, /* When ORDER BY term is this */
- int isCompound, /* True if this is a compound SELECT */
- u8 *pHasAgg /* True if expression contains aggregate functions */
-){
- int i; /* Loop counter */
- ExprList *pEList; /* The columns of the result set */
- NameContext nc; /* Name context for resolving pE */
-
- assert( sqlite3ExprIsInteger(pE, &i)==0 );
- pEList = pSelect->pEList;
-
- /* If the term is a simple identifier that try to match that identifier
- ** against a column name in the result set.
- */
- if( pE->op==TK_ID || (pE->op==TK_STRING && pE->token.z[0]!='\'') ){
- sqlite3 *db = pParse->db;
- char *zCol = sqlite3NameFromToken(db, &pE->token);
- if( zCol==0 ){
- return -1;
- }
- for(i=0; i<pEList->nExpr; i++){
- char *zAs = pEList->a[i].zName;
- if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){
- sqlite3DbFree(db, zCol);
- return i+1;
- }
- }
- sqlite3DbFree(db, zCol);
- }
-
- /* Resolve all names in the ORDER BY term expression
- */
- memset(&nc, 0, sizeof(nc));
- nc.pParse = pParse;
- nc.pSrcList = pSelect->pSrc;
- nc.pEList = pEList;
- nc.allowAgg = 1;
- nc.nErr = 0;
- if( sqlite3ExprResolveNames(&nc, pE) ){
- if( isCompound ){
- sqlite3ErrorClear(pParse);
- return 0;
- }else{
- return -1;
- }
- }
- if( nc.hasAgg && pHasAgg ){
- *pHasAgg = 1;
- }
-
- /* For a compound SELECT, we need to try to match the ORDER BY
- ** expression against an expression in the result set
- */
- if( isCompound ){
- for(i=0; i<pEList->nExpr; i++){
- if( sqlite3ExprCompare(pEList->a[i].pExpr, pE) ){
- return i+1;
- }
- }
- }
- return 0;
-}
-
-
-/*
-** Analyze and ORDER BY or GROUP BY clause in a simple SELECT statement.
-** Return the number of errors seen.
-**
-** Every term of the ORDER BY or GROUP BY clause needs to be an
-** expression. If any expression is an integer constant, then
-** that expression is replaced by the corresponding
-** expression from the result set.
-*/
-static int processOrderGroupBy(
- Parse *pParse, /* Parsing context. Leave error messages here */
- Select *pSelect, /* The SELECT statement containing the clause */
- ExprList *pOrderBy, /* The ORDER BY or GROUP BY clause to be processed */
- int isOrder, /* 1 for ORDER BY. 0 for GROUP BY */
- u8 *pHasAgg /* Set to TRUE if any term contains an aggregate */
-){
- int i;
- sqlite3 *db = pParse->db;
- ExprList *pEList;
-
- if( pOrderBy==0 || pParse->db->mallocFailed ) return 0;
-#if SQLITE_MAX_COLUMN
- if( pOrderBy->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){
- const char *zType = isOrder ? "ORDER" : "GROUP";
- sqlite3ErrorMsg(pParse, "too many terms in %s BY clause", zType);
- return 1;
- }
-#endif
- pEList = pSelect->pEList;
- if( pEList==0 ){
- return 0;
- }
- for(i=0; i<pOrderBy->nExpr; i++){
- int iCol;
- Expr *pE = pOrderBy->a[i].pExpr;
- if( sqlite3ExprIsInteger(pE, &iCol) ){
- if( iCol<=0 || iCol>pEList->nExpr ){
- const char *zType = isOrder ? "ORDER" : "GROUP";
- sqlite3ErrorMsg(pParse,
- "%r %s BY term out of range - should be "
- "between 1 and %d", i+1, zType, pEList->nExpr);
- return 1;
- }
- }else{
- iCol = matchOrderByTermToExprList(pParse, pSelect, pE, i+1, 0, pHasAgg);
- if( iCol<0 ){
- return 1;
+ sqlite3DbFree(db, zName);
+ zName = zNewName;
+ j = -1;
+ if( zName==0 ) break;
}
}
- if( iCol>0 ){
- CollSeq *pColl = pE->pColl;
- int flags = pE->flags & EP_ExpCollate;
- sqlite3ExprDelete(db, pE);
- pE = sqlite3ExprDup(db, pEList->a[iCol-1].pExpr);
- pOrderBy->a[i].pExpr = pE;
- if( pE && pColl && flags ){
- pE->pColl = pColl;
- pE->flags |= flags;
- }
+ pCol->zName = zName;
+ }
+ if( db->mallocFailed ){
+ int j;
+ for(j=0; j<i; j++){
+ sqlite3DbFree(db, aCol[j].zName);
}
+ sqlite3DbFree(db, aCol);
+ *paCol = 0;
+ *pnCol = 0;
+ return SQLITE_NOMEM;
}
- return 0;
+ return SQLITE_OK;
}
/*
-** Analyze and ORDER BY or GROUP BY clause in a SELECT statement. Return
-** the number of errors seen.
-**
-** If iTable>0 then make the N-th term of the ORDER BY clause refer to
-** the N-th column of table iTable.
-**
-** If iTable==0 then transform each term of the ORDER BY clause to refer
-** to a column of the result set by number.
-*/
-static int processCompoundOrderBy(
- Parse *pParse, /* Parsing context. Leave error messages here */
- Select *pSelect /* The SELECT statement containing the ORDER BY */
+** Add type and collation information to a column list based on
+** a SELECT statement.
+**
+** The column list presumably came from selectColumnNamesFromExprList().
+** The column list has only names, not types or collations. This
+** routine goes through and adds the types and collations.
+**
+** This routine requires that all indentifiers in the SELECT
+** statement be resolved.
+*/
+static void selectAddColumnTypeAndCollation(
+ Parse *pParse, /* Parsing contexts */
+ int nCol, /* Number of columns */
+ Column *aCol, /* List of columns */
+ Select *pSelect /* SELECT used to determine types and collations */
){
+ sqlite3 *db = pParse->db;
+ NameContext sNC;
+ Column *pCol;
+ CollSeq *pColl;
int i;
- ExprList *pOrderBy;
- ExprList *pEList;
- sqlite3 *db;
- int moreToDo = 1;
+ Expr *p;
+ struct ExprList_item *a;
- pOrderBy = pSelect->pOrderBy;
- if( pOrderBy==0 ) return 0;
- db = pParse->db;
-#if SQLITE_MAX_COLUMN
- if( pOrderBy->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){
- sqlite3ErrorMsg(pParse, "too many terms in ORDER BY clause");
- return 1;
- }
-#endif
- for(i=0; i<pOrderBy->nExpr; i++){
- pOrderBy->a[i].done = 0;
- }
- while( pSelect->pPrior ){
- pSelect = pSelect->pPrior;
- }
- while( pSelect && moreToDo ){
- moreToDo = 0;
- pEList = pSelect->pEList;
- if( pEList==0 ){
- return 1;
- }
- for(i=0; i<pOrderBy->nExpr; i++){
- int iCol = -1;
- Expr *pE, *pDup;
- if( pOrderBy->a[i].done ) continue;
- pE = pOrderBy->a[i].pExpr;
- if( sqlite3ExprIsInteger(pE, &iCol) ){
- if( iCol<0 || iCol>pEList->nExpr ){
- sqlite3ErrorMsg(pParse,
- "%r ORDER BY term out of range - should be "
- "between 1 and %d", i+1, pEList->nExpr);
- return 1;
- }
- }else{
- pDup = sqlite3ExprDup(db, pE);
- if( !db->mallocFailed ){
- assert(pDup);
- iCol = matchOrderByTermToExprList(pParse, pSelect, pDup, i+1, 1, 0);
- }
- sqlite3ExprDelete(db, pDup);
- if( iCol<0 ){
- return 1;
- }
- }
- if( iCol>0 ){
- pE->op = TK_INTEGER;
- pE->flags |= EP_IntValue;
- pE->iTable = iCol;
- pOrderBy->a[i].done = 1;
- }else{
- moreToDo = 1;
- }
+ assert( pSelect!=0 );
+ assert( (pSelect->selFlags & SF_Resolved)!=0 );
+ assert( nCol==pSelect->pEList->nExpr || db->mallocFailed );
+ if( db->mallocFailed ) return;
+ memset(&sNC, 0, sizeof(sNC));
+ sNC.pSrcList = pSelect->pSrc;
+ a = pSelect->pEList->a;
+ for(i=0, pCol=aCol; i<nCol; i++, pCol++){
+ p = a[i].pExpr;
+ pCol->zType = sqlite3DbStrDup(db, columnType(&sNC, p, 0, 0, 0));
+ pCol->affinity = sqlite3ExprAffinity(p);
+ pColl = sqlite3ExprCollSeq(pParse, p);
+ if( pColl ){
+ pCol->zColl = sqlite3DbStrDup(db, pColl->zName);
}
- pSelect = pSelect->pNext;
}
- for(i=0; i<pOrderBy->nExpr; i++){
- if( pOrderBy->a[i].done==0 ){
- sqlite3ErrorMsg(pParse, "%r ORDER BY term does not match any "
- "column in the result set", i+1);
- return 1;
- }
+}
+
+/*
+** Given a SELECT statement, generate a Table structure that describes
+** the result set of that SELECT.
+*/
+SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
+ Table *pTab;
+ sqlite3 *db = pParse->db;
+ int savedFlags;
+
+ savedFlags = db->flags;
+ db->flags &= ~SQLITE_FullColNames;
+ db->flags |= SQLITE_ShortColNames;
+ sqlite3SelectPrep(pParse, pSelect, 0);
+ if( pParse->nErr ) return 0;
+ while( pSelect->pPrior ) pSelect = pSelect->pPrior;
+ db->flags = savedFlags;
+ pTab = sqlite3DbMallocZero(db, sizeof(Table) );
+ if( pTab==0 ){
+ return 0;
}
- return 0;
+ pTab->db = db;
+ pTab->nRef = 1;
+ pTab->zName = 0;
+ selectColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol);
+ selectAddColumnTypeAndCollation(pParse, pTab->nCol, pTab->aCol, pSelect);
+ pTab->iPKey = -1;
+ if( db->mallocFailed ){
+ sqlite3DeleteTable(pTab);
+ return 0;
+ }
+ return pTab;
}
/*
@@ -68674,6 +69678,7 @@
pPrior = p->pPrior;
assert( pPrior->pRightmost!=pPrior );
assert( pPrior->pRightmost==p->pRightmost );
+ dest = *pDest;
if( pPrior->pOrderBy ){
sqlite3ErrorMsg(pParse,"ORDER BY clause should come after %s not before",
selectOpName(p->op));
@@ -68692,7 +69697,6 @@
/* Create the destination temporary table if necessary
*/
- dest = *pDest;
if( dest.eDest==SRT_EphemTab ){
assert( p->pEList );
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, dest.iParm, p->pEList->nExpr);
@@ -68724,7 +69728,7 @@
assert( !pPrior->pLimit );
pPrior->pLimit = p->pLimit;
pPrior->pOffset = p->pOffset;
- rc = sqlite3Select(pParse, pPrior, &dest, 0, 0, 0);
+ rc = sqlite3Select(pParse, pPrior, &dest);
p->pLimit = 0;
p->pOffset = 0;
if( rc ){
@@ -68737,7 +69741,7 @@
addr = sqlite3VdbeAddOp1(v, OP_IfZero, p->iLimit);
VdbeComment((v, "Jump ahead if LIMIT reached"));
}
- rc = sqlite3Select(pParse, p, &dest, 0, 0, 0);
+ rc = sqlite3Select(pParse, p, &dest);
pDelete = p->pPrior;
p->pPrior = pPrior;
if( rc ){
@@ -68772,7 +69776,7 @@
addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, unionTab, 0);
assert( p->addrOpenEphm[0] == -1 );
p->addrOpenEphm[0] = addr;
- p->pRightmost->usesEphm = 1;
+ p->pRightmost->selFlags |= SF_UsesEphemeral;
assert( p->pEList );
}
@@ -68780,7 +69784,7 @@
*/
assert( !pPrior->pOrderBy );
sqlite3SelectDestInit(&uniondest, priorOp, unionTab);
- rc = sqlite3Select(pParse, pPrior, &uniondest, 0, 0, 0);
+ rc = sqlite3Select(pParse, pPrior, &uniondest);
if( rc ){
goto multi_select_end;
}
@@ -68794,13 +69798,12 @@
op = SRT_Union;
}
p->pPrior = 0;
- p->disallowOrderBy = 0;
pLimit = p->pLimit;
p->pLimit = 0;
pOffset = p->pOffset;
p->pOffset = 0;
uniondest.eDest = op;
- rc = sqlite3Select(pParse, p, &uniondest, 0, 0, 0);
+ rc = sqlite3Select(pParse, p, &uniondest);
/* Query flattening in sqlite3Select() might refill p->pOrderBy.
** Be sure to delete p->pOrderBy, therefore, to avoid a memory leak. */
sqlite3ExprListDelete(db, p->pOrderBy);
@@ -68823,7 +69826,7 @@
if( dest.eDest!=priorOp || unionTab!=dest.iParm ){
int iCont, iBreak, iStart;
assert( p->pEList );
- if( dest.eDest==SRT_Callback ){
+ if( dest.eDest==SRT_Output ){
Select *pFirst = p;
while( pFirst->pPrior ) pFirst = pFirst->pPrior;
generateColumnNames(pParse, 0, pFirst->pEList);
@@ -68861,13 +69864,13 @@
addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab1, 0);
assert( p->addrOpenEphm[0] == -1 );
p->addrOpenEphm[0] = addr;
- p->pRightmost->usesEphm = 1;
+ p->pRightmost->selFlags |= SF_UsesEphemeral;
assert( p->pEList );
/* Code the SELECTs to our left into temporary table "tab1".
*/
sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1);
- rc = sqlite3Select(pParse, pPrior, &intersectdest, 0, 0, 0);
+ rc = sqlite3Select(pParse, pPrior, &intersectdest);
if( rc ){
goto multi_select_end;
}
@@ -68883,7 +69886,7 @@
pOffset = p->pOffset;
p->pOffset = 0;
intersectdest.iParm = tab2;
- rc = sqlite3Select(pParse, p, &intersectdest, 0, 0, 0);
+ rc = sqlite3Select(pParse, p, &intersectdest);
pDelete = p->pPrior;
p->pPrior = pPrior;
sqlite3ExprDelete(db, p->pLimit);
@@ -68897,7 +69900,7 @@
** tables.
*/
assert( p->pEList );
- if( dest.eDest==SRT_Callback ){
+ if( dest.eDest==SRT_Output ){
Select *pFirst = p;
while( pFirst->pPrior ) pFirst = pFirst->pPrior;
generateColumnNames(pParse, 0, pFirst->pEList);
@@ -68930,7 +69933,7 @@
** SELECT might also skip this part if it has no ORDER BY clause and
** no temp tables are required.
*/
- if( p->usesEphm ){
+ if( p->selFlags & SF_UsesEphemeral ){
int i; /* Loop counter */
KeyInfo *pKeyInfo; /* Collating sequence for the result set */
Select *pLoop; /* For looping through SELECT statements */
@@ -69093,9 +70096,8 @@
}
#endif /* #ifndef SQLITE_OMIT_SUBQUERY */
- /* Send the data to the callback function or to a subroutine. In the
- ** case of a subroutine, the subroutine itself is responsible for
- ** popping the data from the stack.
+ /* The results are stored in a sequence of registers
+ ** starting at pDest->iMem. Then the co-routine yields.
*/
case SRT_Coroutine: {
if( pDest->iMem==0 ){
@@ -69107,7 +70109,11 @@
break;
}
- case SRT_Callback: {
+ /* Results are stored in a sequence of registers. Then the
+ ** OP_ResultRow opcode is used to cause sqlite3_step() to return
+ ** the next row of result.
+ */
+ case SRT_Output: {
sqlite3VdbeAddOp2(v, OP_ResultRow, pIn->iMem, pIn->nMem);
sqlite3ExprCacheAffinityChange(pParse, pIn->iMem, pIn->nMem);
break;
@@ -69266,7 +70272,6 @@
ExprList *pOrderBy; /* The ORDER BY clause */
int nOrderBy; /* Number of terms in the ORDER BY clause */
int *aPermute; /* Mapping from ORDER BY terms to result set columns */
- u8 NotUsed; /* Dummy variables */
assert( p->pOrderBy!=0 );
db = pParse->db;
@@ -69283,9 +70288,6 @@
assert( pPrior->pOrderBy==0 );
pOrderBy = p->pOrderBy;
assert( pOrderBy );
- if( processCompoundOrderBy(pParse, p) ){
- return SQLITE_ERROR;
- }
nOrderBy = pOrderBy->nExpr;
/* For operators other than UNION ALL we have to make sure that
@@ -69294,11 +70296,10 @@
*/
if( op!=TK_ALL ){
for(i=1; db->mallocFailed==0 && i<=p->pEList->nExpr; i++){
- for(j=0; j<nOrderBy; j++){
- Expr *pTerm = pOrderBy->a[j].pExpr;
- assert( pTerm->op==TK_INTEGER );
- assert( (pTerm->flags & EP_IntValue)!=0 );
- if( pTerm->iTable==i ) break;
+ struct ExprList_item *pItem;
+ for(j=0, pItem=pOrderBy->a; j<nOrderBy; j++, pItem++){
+ assert( pItem->iCol>0 );
+ if( pItem->iCol==i ) break;
}
if( j==nOrderBy ){
Expr *pNew = sqlite3PExpr(pParse, TK_INTEGER, 0, 0, 0);
@@ -69306,7 +70307,7 @@
pNew->flags |= EP_IntValue;
pNew->iTable = i;
pOrderBy = sqlite3ExprListAppend(pParse, pOrderBy, pNew, 0);
- nOrderBy++;
+ pOrderBy->a[nOrderBy++].iCol = i;
}
}
}
@@ -69320,12 +70321,10 @@
*/
aPermute = sqlite3DbMallocRaw(db, sizeof(int)*nOrderBy);
if( aPermute ){
- for(i=0; i<nOrderBy; i++){
- Expr *pTerm = pOrderBy->a[i].pExpr;
- assert( pTerm->op==TK_INTEGER );
- assert( (pTerm->flags & EP_IntValue)!=0 );
- aPermute[i] = pTerm->iTable-1;
- assert( aPermute[i]>=0 && aPermute[i]<p->pEList->nExpr );
+ struct ExprList_item *pItem;
+ for(i=0, pItem=pOrderBy->a; i<nOrderBy; i++, pItem++){
+ assert( pItem->iCol>0 && pItem->iCol<=p->pEList->nExpr );
+ aPermute[i] = pItem->iCol - 1;
}
pKeyMerge =
sqlite3DbMallocRaw(db, sizeof(*pKeyMerge)+nOrderBy*(sizeof(CollSeq*)+1));
@@ -69384,9 +70383,9 @@
*/
p->pPrior = 0;
pPrior->pRightmost = 0;
- processOrderGroupBy(pParse, p, p->pOrderBy, 1, &NotUsed);
+ sqlite3ResolveOrderGroupBy(pParse, p, p->pOrderBy, "ORDER");
if( pPrior->pPrior==0 ){
- processOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, 1, &NotUsed);
+ sqlite3ResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, "ORDER");
}
/* Compute the limit registers */
@@ -69426,7 +70425,7 @@
*/
VdbeNoopComment((v, "Begin coroutine for left SELECT"));
pPrior->iLimit = regLimitA;
- sqlite3Select(pParse, pPrior, &destA, 0, 0, 0);
+ sqlite3Select(pParse, pPrior, &destA);
sqlite3VdbeAddOp2(v, OP_Integer, 1, regEofA);
sqlite3VdbeAddOp1(v, OP_Yield, regAddrA);
VdbeNoopComment((v, "End coroutine for left SELECT"));
@@ -69440,7 +70439,7 @@
savedOffset = p->iOffset;
p->iLimit = regLimitB;
p->iOffset = 0;
- sqlite3Select(pParse, p, &destB, 0, 0, 0);
+ sqlite3Select(pParse, p, &destB);
p->iLimit = savedLimit;
p->iOffset = savedOffset;
sqlite3VdbeAddOp2(v, OP_Integer, 1, regEofB);
@@ -69555,7 +70554,7 @@
/* Set the number of output columns
*/
- if( pDest->eDest==SRT_Callback ){
+ if( pDest->eDest==SRT_Output ){
Select *pFirst = pPrior;
while( pFirst->pPrior ) pFirst = pFirst->pPrior;
generateColumnNames(pParse, 0, pFirst->pEList);
@@ -69693,8 +70692,8 @@
**
** (2) The subquery is not an aggregate or the outer query is not a join.
**
-** (3) The subquery is not the right operand of a left outer join, or
-** the subquery is not itself a join. (Ticket #306)
+** (3) The subquery is not the right operand of a left outer join
+** (Originally ticket #306. Strenghtened by ticket #3300)
**
** (4) The subquery is not DISTINCT or the outer query is not a join.
**
@@ -69716,8 +70715,8 @@
**
** (11) The subquery and the outer query do not both have ORDER BY clauses.
**
-** (12) The subquery is not the right term of a LEFT OUTER JOIN or the
-** subquery has no WHERE clause. (added by ticket #350)
+** (12) Not implemented. Subsumed into restriction (3). Was previously
+** a separate restriction deriving from ticket #350.
**
** (13) The subquery and outer query do not both use LIMIT
**
@@ -69747,6 +70746,9 @@
** ORDER by clause of the parent must be simple references to
** columns of the sub-query.
**
+** (19) The subquery does not use LIMIT or the outer query does not
+** have a WHERE clause.
+**
** In this routine, the "p" parameter is a pointer to the outer query.
** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query
** uses aggregates and subqueryIsAgg is true if the subquery uses aggregates.
@@ -69801,17 +70803,21 @@
return 0; /* Restriction (15) */
}
if( pSubSrc->nSrc==0 ) return 0; /* Restriction (7) */
- if( (pSub->isDistinct || pSub->pLimit)
+ if( ((pSub->selFlags & SF_Distinct)!=0 || pSub->pLimit)
&& (pSrc->nSrc>1 || isAgg) ){ /* Restrictions (4)(5)(8)(9) */
return 0;
}
- if( p->isDistinct && subqueryIsAgg ) return 0; /* Restriction (6) */
- if( (p->disallowOrderBy || p->pOrderBy) && pSub->pOrderBy ){
+ if( (p->selFlags & SF_Distinct)!=0 && subqueryIsAgg ){
+ return 0; /* Restriction (6) */
+ }
+ if( p->pOrderBy && pSub->pOrderBy ){
return 0; /* Restriction (11) */
}
if( isAgg && pSub->pOrderBy ) return 0; /* Restriction (16) */
+ if( pSub->pLimit && p->pWhere ) return 0; /* Restriction (19) */
- /* Restriction 3: If the subquery is a join, make sure the subquery is
+ /* OBSOLETE COMMENT 1:
+ ** Restriction 3: If the subquery is a join, make sure the subquery is
** not used as the right operand of an outer join. Examples of why this
** is not allowed:
**
@@ -69822,12 +70828,9 @@
** (t1 LEFT OUTER JOIN t2) JOIN t3
**
** which is not at all the same thing.
- */
- if( pSubSrc->nSrc>1 && (pSubitem->jointype & JT_OUTER)!=0 ){
- return 0;
- }
-
- /* Restriction 12: If the subquery is the right operand of a left outer
+ **
+ ** OBSOLETE COMMENT 2:
+ ** Restriction 12: If the subquery is the right operand of a left outer
** join, make sure the subquery has no WHERE clause.
** An examples of why this is not allowed:
**
@@ -69839,8 +70842,13 @@
**
** But the t2.x>0 test will always fail on a NULL row of t2, which
** effectively converts the OUTER JOIN into an INNER JOIN.
+ **
+ ** THIS OVERRIDES OBSOLETE COMMENTS 1 AND 2 ABOVE:
+ ** Ticket #3300 shows that flattening the right term of a LEFT JOIN
+ ** is fraught with danger. Best to avoid the whole thing. If the
+ ** subquery is the right term of a LEFT JOIN, then do not flatten.
*/
- if( (pSubitem->jointype & JT_OUTER)!=0 && pSub->pWhere!=0 ){
+ if( (pSubitem->jointype & JT_OUTER)!=0 ){
return 0;
}
@@ -69850,11 +70858,11 @@
** queries.
*/
if( pSub->pPrior ){
- if( p->pPrior || isAgg || p->isDistinct || pSrc->nSrc!=1 ){
+ if( p->pPrior || isAgg || (p->selFlags & SF_Distinct)!=0 || pSrc->nSrc!=1 ){
return 0;
}
for(pSub1=pSub; pSub1; pSub1=pSub1->pPrior){
- if( pSub1->isAgg || pSub1->isDistinct
+ if( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))!=0
|| (pSub1->pPrior && pSub1->op!=TK_ALL)
|| !pSub1->pSrc || pSub1->pSrc->nSrc!=1
){
@@ -69866,20 +70874,21 @@
if( p->pOrderBy ){
int ii;
for(ii=0; ii<p->pOrderBy->nExpr; ii++){
- Expr *pExpr = p->pOrderBy->a[ii].pExpr;
- if( pExpr->op!=TK_COLUMN || pExpr->iTable!=iParent ){
- return 0;
- }
+ if( p->pOrderBy->a[ii].iCol==0 ) return 0;
}
}
}
+ /***** If we reach this point, flattening is permitted. *****/
+
+ /* Authorize the subquery */
pParse->zAuthContext = pSubitem->zName;
sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0);
pParse->zAuthContext = zSavedAuthContext;
- /* If the sub-query is a compound SELECT statement, then it must be
- ** a UNION ALL and the parent query must be of the form:
+ /* If the sub-query is a compound SELECT statement, then (by restrictions
+ ** 17 and 18 above) it must be a UNION ALL and the parent query must
+ ** be of the form:
**
** SELECT <expr-list> FROM (<sub-query>) <where-clause>
**
@@ -69911,8 +70920,8 @@
pNew->pRightmost = 0;
}
- /* If we reach this point, it means flattening is permitted for the
- ** iFrom-th entry of the FROM clause in the outer query.
+ /* Begin flattening the iFrom-th entry of the FROM clause
+ ** in the outer query.
*/
pSub = pSub1 = pSubitem->pSelect;
for(pParent=p; pParent; pParent=pParent->pPrior, pSub=pSub->pPrior){
@@ -70016,7 +71025,7 @@
/* The flattened query is distinct if either the inner or the
** outer query is distinct.
*/
- pParent->isDistinct = pParent->isDistinct || pSub->isDistinct;
+ pParent->selFlags |= pSub->selFlags & SF_Distinct;
/*
** SELECT ... FROM (SELECT ... LIMIT a OFFSET b) LIMIT x OFFSET y;
@@ -70069,133 +71078,369 @@
}
/*
-** This routine resolves any names used in the result set of the
-** supplied SELECT statement. If the SELECT statement being resolved
-** is a sub-select, then pOuterNC is a pointer to the NameContext
-** of the parent SELECT.
+** This routine is a Walker callback for "expanding" a SELECT statement.
+** "Expanding" means to do the following:
+**
+** (1) Make sure VDBE cursor numbers have been assigned to every
+** element of the FROM clause.
+**
+** (2) Fill in the pTabList->a[].pTab fields in the SrcList that
+** defines FROM clause. When views appear in the FROM clause,
+** fill pTabList->a[].pSelect with a copy of the SELECT statement
+** that implements the view. A copy is made of the view's SELECT
+** statement so that we can freely modify or delete that statement
+** without worrying about messing up the presistent representation
+** of the view.
+**
+** (3) Add terms to the WHERE clause to accomodate the NATURAL keyword
+** on joins and the ON and USING clause of joins.
+**
+** (4) Scan the list of columns in the result set (pEList) looking
+** for instances of the "*" operator or the TABLE.* operator.
+** If found, expand each "*" to be every column in every table
+** and TABLE.* to be every column in TABLE.
+**
*/
-SQLITE_PRIVATE int sqlite3SelectResolve(
- Parse *pParse, /* The parser context */
- Select *p, /* The SELECT statement being coded. */
- NameContext *pOuterNC /* The outer name context. May be NULL. */
-){
- ExprList *pEList; /* Result set. */
- int i; /* For-loop variable used in multiple places */
- NameContext sNC; /* Local name-context */
- ExprList *pGroupBy; /* The group by clause */
-
- /* If this routine has run before, return immediately. */
- if( p->isResolved ){
- assert( !pOuterNC );
- return SQLITE_OK;
- }
- p->isResolved = 1;
+static int selectExpander(Walker *pWalker, Select *p){
+ Parse *pParse = pWalker->pParse;
+ int i, j, k;
+ SrcList *pTabList;
+ ExprList *pEList;
+ struct SrcList_item *pFrom;
+ sqlite3 *db = pParse->db;
- /* If there have already been errors, do nothing. */
- if( pParse->nErr>0 ){
- return SQLITE_ERROR;
+ if( db->mallocFailed ){
+ return WRC_Abort;
}
-
- /* Prepare the select statement. This call will allocate all cursors
- ** required to handle the tables and subqueries in the FROM clause.
- */
- if( prepSelectStmt(pParse, p) ){
- return SQLITE_ERROR;
+ if( p->pSrc==0 || (p->selFlags & SF_Expanded)!=0 ){
+ return WRC_Prune;
}
+ p->selFlags |= SF_Expanded;
+ pTabList = p->pSrc;
+ pEList = p->pEList;
- /* Resolve the expressions in the LIMIT and OFFSET clauses. These
- ** are not allowed to refer to any names, so pass an empty NameContext.
+ /* Make sure cursor numbers have been assigned to all entries in
+ ** the FROM clause of the SELECT statement.
*/
- memset(&sNC, 0, sizeof(sNC));
- sNC.pParse = pParse;
- if( sqlite3ExprResolveNames(&sNC, p->pLimit) ||
- sqlite3ExprResolveNames(&sNC, p->pOffset) ){
- return SQLITE_ERROR;
- }
+ sqlite3SrcListAssignCursors(pParse, pTabList);
- /* Set up the local name-context to pass to ExprResolveNames() to
- ** resolve the expression-list.
+ /* Look up every table named in the FROM clause of the select. If
+ ** an entry of the FROM clause is a subquery instead of a table or view,
+ ** then create a transient table structure to describe the subquery.
*/
- sNC.allowAgg = 1;
- sNC.pSrcList = p->pSrc;
- sNC.pNext = pOuterNC;
-
- /* Resolve names in the result set. */
- pEList = p->pEList;
- if( !pEList ) return SQLITE_ERROR;
- for(i=0; i<pEList->nExpr; i++){
- Expr *pX = pEList->a[i].pExpr;
- if( sqlite3ExprResolveNames(&sNC, pX) ){
- return SQLITE_ERROR;
+ for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
+ Table *pTab;
+ if( pFrom->pTab!=0 ){
+ /* This statement has already been prepared. There is no need
+ ** to go further. */
+ assert( i==0 );
+ return WRC_Prune;
}
- }
+ if( pFrom->zName==0 ){
+#ifndef SQLITE_OMIT_SUBQUERY
+ Select *pSel = pFrom->pSelect;
+ /* A sub-query in the FROM clause of a SELECT */
+ assert( pSel!=0 );
+ assert( pFrom->pTab==0 );
+ sqlite3WalkSelect(pWalker, pSel);
+ pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table));
+ if( pTab==0 ) return WRC_Abort;
+ pTab->db = db;
+ pTab->nRef = 1;
+ pTab->zName = sqlite3MPrintf(db, "sqlite_subquery_%p_", (void*)pTab);
+ while( pSel->pPrior ){ pSel = pSel->pPrior; }
+ selectColumnsFromExprList(pParse, pSel->pEList, &pTab->nCol, &pTab->aCol);
+ pTab->iPKey = -1;
+ pTab->tabFlags |= TF_Ephemeral;
+#endif
+ }else{
+ /* An ordinary table or view name in the FROM clause */
+ assert( pFrom->pTab==0 );
+ pFrom->pTab = pTab =
+ sqlite3LocateTable(pParse,0,pFrom->zName,pFrom->zDatabase);
+ if( pTab==0 ) return WRC_Abort;
+ pTab->nRef++;
+#if !defined(SQLITE_OMIT_VIEW) || !defined (SQLITE_OMIT_VIRTUALTABLE)
+ if( pTab->pSelect || IsVirtual(pTab) ){
+ /* We reach here if the named table is a really a view */
+ if( sqlite3ViewGetColumnNames(pParse, pTab) ) return WRC_Abort;
- /* If there are no aggregate functions in the result-set, and no GROUP BY
- ** expression, do not allow aggregates in any of the other expressions.
- */
- assert( !p->isAgg );
- pGroupBy = p->pGroupBy;
- if( pGroupBy || sNC.hasAgg ){
- p->isAgg = 1;
- }else{
- sNC.allowAgg = 0;
+ /* If pFrom->pSelect!=0 it means we are dealing with a
+ ** view within a view. The SELECT structure has already been
+ ** copied by the outer view so we can skip the copy step here
+ ** in the inner view.
+ */
+ if( pFrom->pSelect==0 ){
+ pFrom->pSelect = sqlite3SelectDup(db, pTab->pSelect);
+ sqlite3WalkSelect(pWalker, pFrom->pSelect);
+ }
+ }
+#endif
+ }
}
- /* If a HAVING clause is present, then there must be a GROUP BY clause.
+ /* Process NATURAL keywords, and ON and USING clauses of joins.
*/
- if( p->pHaving && !pGroupBy ){
- sqlite3ErrorMsg(pParse, "a GROUP BY clause is required before HAVING");
- return SQLITE_ERROR;
+ if( db->mallocFailed || sqliteProcessJoin(pParse, p) ){
+ return WRC_Abort;
}
- /* Add the expression list to the name-context before parsing the
- ** other expressions in the SELECT statement. This is so that
- ** expressions in the WHERE clause (etc.) can refer to expressions by
- ** aliases in the result set.
+ /* For every "*" that occurs in the column list, insert the names of
+ ** all columns in all tables. And for every TABLE.* insert the names
+ ** of all columns in TABLE. The parser inserted a special expression
+ ** with the TK_ALL operator for each "*" that it found in the column list.
+ ** The following code just has to locate the TK_ALL expressions and expand
+ ** each one to the list of all columns in all tables.
**
- ** Minor point: If this is the case, then the expression will be
- ** re-evaluated for each reference to it.
+ ** The first loop just checks to see if there are any "*" operators
+ ** that need expanding.
*/
- sNC.pEList = p->pEList;
- if( sqlite3ExprResolveNames(&sNC, p->pWhere) ||
- sqlite3ExprResolveNames(&sNC, p->pHaving) ){
- return SQLITE_ERROR;
+ for(k=0; k<pEList->nExpr; k++){
+ Expr *pE = pEList->a[k].pExpr;
+ if( pE->op==TK_ALL ) break;
+ if( pE->op==TK_DOT && pE->pRight && pE->pRight->op==TK_ALL
+ && pE->pLeft && pE->pLeft->op==TK_ID ) break;
}
- if( p->pPrior==0 ){
- if( processOrderGroupBy(pParse, p, p->pOrderBy, 1, &sNC.hasAgg) ){
- return SQLITE_ERROR;
+ if( k<pEList->nExpr ){
+ /*
+ ** If we get here it means the result set contains one or more "*"
+ ** operators that need to be expanded. Loop through each expression
+ ** in the result set and expand them one by one.
+ */
+ struct ExprList_item *a = pEList->a;
+ ExprList *pNew = 0;
+ int flags = pParse->db->flags;
+ int longNames = (flags & SQLITE_FullColNames)!=0
+ && (flags & SQLITE_ShortColNames)==0;
+
+ for(k=0; k<pEList->nExpr; k++){
+ Expr *pE = a[k].pExpr;
+ if( pE->op!=TK_ALL &&
+ (pE->op!=TK_DOT || pE->pRight==0 || pE->pRight->op!=TK_ALL) ){
+ /* This particular expression does not need to be expanded.
+ */
+ pNew = sqlite3ExprListAppend(pParse, pNew, a[k].pExpr, 0);
+ if( pNew ){
+ pNew->a[pNew->nExpr-1].zName = a[k].zName;
+ }
+ a[k].pExpr = 0;
+ a[k].zName = 0;
+ }else{
+ /* This expression is a "*" or a "TABLE.*" and needs to be
+ ** expanded. */
+ int tableSeen = 0; /* Set to 1 when TABLE matches */
+ char *zTName; /* text of name of TABLE */
+ if( pE->op==TK_DOT && pE->pLeft ){
+ zTName = sqlite3NameFromToken(db, &pE->pLeft->token);
+ }else{
+ zTName = 0;
+ }
+ for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
+ Table *pTab = pFrom->pTab;
+ char *zTabName = pFrom->zAlias;
+ if( zTabName==0 || zTabName[0]==0 ){
+ zTabName = pTab->zName;
+ }
+ if( db->mallocFailed ) break;
+ if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){
+ continue;
+ }
+ tableSeen = 1;
+ for(j=0; j<pTab->nCol; j++){
+ Expr *pExpr, *pRight;
+ char *zName = pTab->aCol[j].zName;
+
+ /* If a column is marked as 'hidden' (currently only possible
+ ** for virtual tables), do not include it in the expanded
+ ** result-set list.
+ */
+ if( IsHiddenColumn(&pTab->aCol[j]) ){
+ assert(IsVirtual(pTab));
+ continue;
+ }
+
+ if( i>0 ){
+ struct SrcList_item *pLeft = &pTabList->a[i-1];
+ if( (pLeft[1].jointype & JT_NATURAL)!=0 &&
+ columnIndex(pLeft->pTab, zName)>=0 ){
+ /* In a NATURAL join, omit the join columns from the
+ ** table on the right */
+ continue;
+ }
+ if( sqlite3IdListIndex(pLeft[1].pUsing, zName)>=0 ){
+ /* In a join with a USING clause, omit columns in the
+ ** using clause from the table on the right. */
+ continue;
+ }
+ }
+ pRight = sqlite3PExpr(pParse, TK_ID, 0, 0, 0);
+ if( pRight==0 ) break;
+ setQuotedToken(pParse, &pRight->token, zName);
+ if( longNames || pTabList->nSrc>1 ){
+ Expr *pLeft = sqlite3PExpr(pParse, TK_ID, 0, 0, 0);
+ pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0);
+ if( pExpr==0 ) break;
+ setQuotedToken(pParse, &pLeft->token, zTabName);
+ setToken(&pExpr->span,
+ sqlite3MPrintf(db, "%s.%s", zTabName, zName));
+ pExpr->span.dyn = 1;
+ pExpr->token.z = 0;
+ pExpr->token.n = 0;
+ pExpr->token.dyn = 0;
+ }else{
+ pExpr = pRight;
+ pExpr->span = pExpr->token;
+ pExpr->span.dyn = 0;
+ }
+ if( longNames ){
+ pNew = sqlite3ExprListAppend(pParse, pNew, pExpr, &pExpr->span);
+ }else{
+ pNew = sqlite3ExprListAppend(pParse, pNew, pExpr, &pRight->token);
+ }
+ }
+ }
+ if( !tableSeen ){
+ if( zTName ){
+ sqlite3ErrorMsg(pParse, "no such table: %s", zTName);
+ }else{
+ sqlite3ErrorMsg(pParse, "no tables specified");
+ }
+ }
+ sqlite3DbFree(db, zTName);
+ }
}
+ sqlite3ExprListDelete(db, pEList);
+ p->pEList = pNew;
}
- if( processOrderGroupBy(pParse, p, pGroupBy, 0, &sNC.hasAgg) ){
- return SQLITE_ERROR;
+#if SQLITE_MAX_COLUMN
+ if( p->pEList && p->pEList->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){
+ sqlite3ErrorMsg(pParse, "too many columns in result set");
}
+#endif
+ return WRC_Continue;
+}
- if( pParse->db->mallocFailed ){
- return SQLITE_NOMEM;
- }
+/*
+** No-op routine for the parse-tree walker.
+**
+** When this routine is the Walker.xExprCallback then expression trees
+** are walked without any actions being taken at each node. Presumably,
+** when this routine is used for Walker.xExprCallback then
+** Walker.xSelectCallback is set to do something useful for every
+** subquery in the parser tree.
+*/
+static int exprWalkNoop(Walker *pWalker, Expr *pExpr){
+ return WRC_Continue;
+}
- /* Make sure the GROUP BY clause does not contain aggregate functions.
- */
- if( pGroupBy ){
- struct ExprList_item *pItem;
-
- for(i=0, pItem=pGroupBy->a; i<pGroupBy->nExpr; i++, pItem++){
- if( ExprHasProperty(pItem->pExpr, EP_Agg) ){
- sqlite3ErrorMsg(pParse, "aggregate functions are not allowed in "
- "the GROUP BY clause");
- return SQLITE_ERROR;
+/*
+** This routine "expands" a SELECT statement and all of its subqueries.
+** For additional information on what it means to "expand" a SELECT
+** statement, see the comment on the selectExpand worker callback above.
+**
+** Expanding a SELECT statement is the first step in processing a
+** SELECT statement. The SELECT statement must be expanded before
+** name resolution is performed.
+**
+** If anything goes wrong, an error message is written into pParse.
+** The calling function can detect the problem by looking at pParse->nErr
+** and/or pParse->db->mallocFailed.
+*/
+static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){
+ Walker w;
+ w.xSelectCallback = selectExpander;
+ w.xExprCallback = exprWalkNoop;
+ w.pParse = pParse;
+ sqlite3WalkSelect(&w, pSelect);
+}
+
+
+#ifndef SQLITE_OMIT_SUBQUERY
+/*
+** This is a Walker.xSelectCallback callback for the sqlite3SelectTypeInfo()
+** interface.
+**
+** For each FROM-clause subquery, add Column.zType and Column.zColl
+** information to the Table structure that represents the result set
+** of that subquery.
+**
+** The Table structure that represents the result set was constructed
+** by selectExpander() but the type and collation information was omitted
+** at that point because identifiers had not yet been resolved. This
+** routine is called after identifier resolution.
+*/
+static int selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
+ Parse *pParse;
+ int i;
+ SrcList *pTabList;
+ struct SrcList_item *pFrom;
+
+ assert( p->selFlags & SF_Resolved );
+ if( (p->selFlags & SF_HasTypeInfo)==0 ){
+ p->selFlags |= SF_HasTypeInfo;
+ pParse = pWalker->pParse;
+ pTabList = p->pSrc;
+ for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
+ Table *pTab = pFrom->pTab;
+ if( pTab && (pTab->tabFlags & TF_Ephemeral)!=0 ){
+ /* A sub-query in the FROM clause of a SELECT */
+ Select *pSel = pFrom->pSelect;
+ assert( pSel );
+ while( pSel->pPrior ) pSel = pSel->pPrior;
+ selectAddColumnTypeAndCollation(pParse, pTab->nCol, pTab->aCol, pSel);
}
}
}
+ return WRC_Continue;
+}
+#endif
- /* If this is one SELECT of a compound, be sure to resolve names
- ** in the other SELECTs.
- */
- if( p->pPrior ){
- return sqlite3SelectResolve(pParse, p->pPrior, pOuterNC);
- }else{
- return SQLITE_OK;
- }
+
+/*
+** This routine adds datatype and collating sequence information to
+** the Table structures of all FROM-clause subqueries in a
+** SELECT statement.
+**
+** Use this routine after name resolution.
+*/
+static void sqlite3SelectAddTypeInfo(Parse *pParse, Select *pSelect){
+#ifndef SQLITE_OMIT_SUBQUERY
+ Walker w;
+ w.xSelectCallback = selectAddSubqueryTypeInfo;
+ w.xExprCallback = exprWalkNoop;
+ w.pParse = pParse;
+ sqlite3WalkSelect(&w, pSelect);
+#endif
+}
+
+
+/*
+** This routine sets of a SELECT statement for processing. The
+** following is accomplished:
+**
+** * VDBE Cursor numbers are assigned to all FROM-clause terms.
+** * Ephemeral Table objects are created for all FROM-clause subqueries.
+** * ON and USING clauses are shifted into WHERE statements
+** * Wildcards "*" and "TABLE.*" in result sets are expanded.
+** * Identifiers in expression are matched to tables.
+**
+** This routine acts recursively on all subqueries within the SELECT.
+*/
+SQLITE_PRIVATE void sqlite3SelectPrep(
+ Parse *pParse, /* The parser context */
+ Select *p, /* The SELECT statement being coded. */
+ NameContext *pOuterNC /* Name context for container */
+){
+ sqlite3 *db;
+ if( p==0 ) return;
+ db = pParse->db;
+ if( p->selFlags & SF_HasTypeInfo ) return;
+ if( pParse->nErr || db->mallocFailed ) return;
+ sqlite3SelectExpand(pParse, p);
+ if( pParse->nErr || db->mallocFailed ) return;
+ sqlite3ResolveSelectNames(pParse, p, pOuterNC);
+ if( pParse->nErr || db->mallocFailed ) return;
+ sqlite3SelectAddTypeInfo(pParse, p);
}
/*
@@ -70305,7 +71550,7 @@
}
/*
-** Generate code for the given SELECT statement.
+** Generate code for the SELECT statement given in the p argument.
**
** The results are distributed in various ways depending on the
** contents of the SelectDest structure pointed to by argument pDest
@@ -70313,33 +71558,43 @@
**
** pDest->eDest Result
** ------------ -------------------------------------------
-** SRT_Callback Invoke the callback for each row of the result.
-**
-** SRT_Mem Store first result in memory cell pDest->iParm
+** SRT_Output Generate a row of output (using the OP_ResultRow
+** opcode) for each row in the result set.
**
-** SRT_Set Store results as keys of table pDest->iParm.
-** Apply the affinity pDest->affinity before storing them.
+** SRT_Mem Only valid if the result is a single column.
+** Store the first column of the first result row
+** in register pDest->iParm then abandon the rest
+** of the query. This destination implies "LIMIT 1".
+**
+** SRT_Set The result must be a single column. Store each
+** row of result as the key in table pDest->iParm.
+** Apply the affinity pDest->affinity before storing
+** results. Used to implement "IN (SELECT ...)".
**
** SRT_Union Store results as a key in a temporary table pDest->iParm.
**
** SRT_Except Remove results from the temporary table pDest->iParm.
**
-** SRT_Table Store results in temporary table pDest->iParm
+** SRT_Table Store results in temporary table pDest->iParm.
+** This is like SRT_EphemTab except that the table
+** is assumed to already be open.
**
** SRT_EphemTab Create an temporary table pDest->iParm and store
** the result there. The cursor is left open after
-** returning.
-**
-** SRT_Coroutine Invoke a co-routine to compute a single row of
-** the result
+** returning. This is like SRT_Table except that
+** this destination uses OP_OpenEphemeral to create
+** the table first.
+**
+** SRT_Coroutine Generate a co-routine that returns a new row of
+** results each time it is invoked. The entry point
+** of the co-routine is stored in register pDest->iParm.
**
** SRT_Exists Store a 1 in memory cell pDest->iParm if the result
** set is not empty.
**
-** SRT_Discard Throw the results away.
-**
-** See the selectInnerLoop() function for a canonical listing of the
-** allowed values of eDest and their meanings.
+** SRT_Discard Throw the results away. This is used by SELECT
+** statements within triggers whose only purpose is
+** the side-effects of functions.
**
** This routine returns the number of errors. If any errors are
** encountered, then an appropriate error message is left in
@@ -70347,35 +71602,11 @@
**
** This routine does NOT free the Select structure passed in. The
** calling function needs to do that.
-**
-** The pParent, parentTab, and *pParentAgg fields are filled in if this
-** SELECT is a subquery. This routine may try to combine this SELECT
-** with its parent to form a single flat query. In so doing, it might
-** change the parent query from a non-aggregate to an aggregate query.
-** For that reason, the pParentAgg flag is passed as a pointer, so it
-** can be changed.
-**
-** Example 1: The meaning of the pParent parameter.
-**
-** SELECT * FROM t1 JOIN (SELECT x, count(*) FROM t2) JOIN t3;
-** \ \_______ subquery _______/ /
-** \ /
-** \____________________ outer query ___________________/
-**
-** This routine is called for the outer query first. For that call,
-** pParent will be NULL. During the processing of the outer query, this
-** routine is called recursively to handle the subquery. For the recursive
-** call, pParent will point to the outer query. Because the subquery is
-** the second element in a three-way join, the parentTab parameter will
-** be 1 (the 2nd value of a 0-indexed array.)
*/
SQLITE_PRIVATE int sqlite3Select(
Parse *pParse, /* The parser context */
Select *p, /* The SELECT statement being coded. */
- SelectDest *pDest, /* What to do with the query results */
- Select *pParent, /* Another SELECT for which this is a sub-query */
- int parentTab, /* Index in pParent->pSrc of this query */
- int *pParentAgg /* True if pParent uses aggregate functions */
+ SelectDest *pDest /* What to do with the query results */
){
int i, j; /* Loop counters */
WhereInfo *pWInfo; /* Return from sqlite3WhereBegin() */
@@ -70411,9 +71642,10 @@
*/
assert(pDest->eDest==SRT_Exists || pDest->eDest==SRT_Union ||
pDest->eDest==SRT_Except || pDest->eDest==SRT_Discard);
- p->isDistinct = 0;
+ p->selFlags &= ~SF_Distinct;
}
- if( sqlite3SelectResolve(pParse, p, 0) ){
+ sqlite3SelectPrep(pParse, p, 0);
+ if( pParse->nErr ){
goto select_end;
}
p->pOrderBy = pOrderBy;
@@ -70422,7 +71654,7 @@
/* Make local copies of the parameters for this query.
*/
pTabList = p->pSrc;
- isAgg = p->isAgg;
+ isAgg = (p->selFlags & SF_Aggregate)!=0;
pEList = p->pEList;
if( pEList==0 ) goto select_end;
@@ -70451,18 +71683,8 @@
SelectDest dest;
Select *pSub = pItem->pSelect;
int isAggSub;
- char *zName = pItem->zName;
if( pSub==0 || pItem->isPopulated ) continue;
- if( zName!=0 ){ /* An sql view */
- const char *zSavedAuthContext = pParse->zAuthContext;
- pParse->zAuthContext = zName;
- rc = sqlite3SelectResolve(pParse, pSub, 0);
- pParse->zAuthContext = zSavedAuthContext;
- if( rc ){
- goto select_end;
- }
- }
/* Increment Parse.nHeight by the height of the largest expression
** tree refered to by this, the parent select. The child select
@@ -70474,15 +71696,18 @@
pParse->nHeight += sqlite3SelectExprHeight(p);
/* Check to see if the subquery can be absorbed into the parent. */
- isAggSub = pSub->isAgg;
+ isAggSub = (pSub->selFlags & SF_Aggregate)!=0;
if( flattenSubquery(pParse, p, i, isAgg, isAggSub) ){
if( isAggSub ){
- p->isAgg = isAgg = 1;
+ isAgg = 1;
+ p->selFlags |= SF_Aggregate;
}
i = -1;
}else{
sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
- sqlite3Select(pParse, pSub, &dest, p, i, &isAgg);
+ assert( pItem->isPopulated==0 );
+ sqlite3Select(pParse, pSub, &dest);
+ pItem->isPopulated = 1;
}
if( pParse->nErr || db->mallocFailed ){
goto select_end;
@@ -70498,7 +71723,7 @@
pWhere = p->pWhere;
pGroupBy = p->pGroupBy;
pHaving = p->pHaving;
- isDistinct = p->isDistinct;
+ isDistinct = (p->selFlags & SF_Distinct)!=0;
#ifndef SQLITE_OMIT_COMPOUND_SELECT
/* If there is are a sequence of queries, do the earlier ones first.
@@ -70533,12 +71758,12 @@
#endif
/* If possible, rewrite the query to use GROUP BY instead of DISTINCT.
- ** GROUP BY may use an index, DISTINCT never does.
+ ** GROUP BY might use an index, DISTINCT never does.
*/
- if( p->isDistinct && !p->isAgg && !p->pGroupBy ){
+ if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct && !p->pGroupBy ){
p->pGroupBy = sqlite3ExprListDup(db, p->pEList);
pGroupBy = p->pGroupBy;
- p->isDistinct = 0;
+ p->selFlags &= ~SF_Distinct;
isDistinct = 0;
}
@@ -70772,20 +71997,14 @@
struct AggInfo_col *pCol = &sAggInfo.aCol[i];
if( pCol->iSorterColumn>=j ){
int r1 = j + regBase;
-#ifndef NDEBUG
- int r2 =
-#endif
- sqlite3ExprCodeGetColumn(pParse,
+ int r2;
+
+ r2 = sqlite3ExprCodeGetColumn(pParse,
pCol->pTab, pCol->iColumn, pCol->iTable, r1, 0);
+ if( r1!=r2 ){
+ sqlite3VdbeAddOp2(v, OP_SCopy, r2, r1);
+ }
j++;
-
- /* sAggInfo.aCol[] only contains one entry per column. So
- ** The reference to pCol->iColumn,pCol->iTable must have been
- ** the first reference to that column. Hence,
- ** sqliteExprCodeGetColumn is guaranteed to put the result in
- ** the column requested.
- */
- assert( r1==r2 );
}
}
regRecord = sqlite3GetTempReg(pParse);
@@ -70934,19 +72153,6 @@
generateSortTail(pParse, p, v, pEList->nExpr, pDest);
}
-#ifndef SQLITE_OMIT_SUBQUERY
- /* If this was a subquery, we have now converted the subquery into a
- ** temporary table. So set the SrcList_item.isPopulated flag to prevent
- ** this subquery from being evaluated again and to force the use of
- ** the temporary table.
- */
- if( pParent ){
- assert( pParent->pSrc->nSrc>parentTab );
- assert( pParent->pSrc->a[parentTab].pSelect==p );
- pParent->pSrc->a[parentTab].isPopulated = 1;
- }
-#endif
-
/* Jump here to skip this query
*/
sqlite3VdbeResolveLabel(v, iEnd);
@@ -70961,10 +72167,9 @@
*/
select_end:
- /* Identify column names if we will be using them in a callback. This
- ** step is skipped if the output is going to some other destination.
+ /* Identify column names if results of the SELECT are to be output.
*/
- if( rc==SQLITE_OK && pDest->eDest==SRT_Callback ){
+ if( rc==SQLITE_OK && pDest->eDest==SRT_Output ){
generateColumnNames(pParse, pTabList, pEList);
}
@@ -71284,7 +72489,7 @@
*************************************************************************
**
**
-** $Id: trigger.c,v 1.128 2008/07/28 19:34:54 drh Exp $
+** $Id: trigger.c,v 1.129 2008/08/20 16:35:10 drh Exp $
*/
#ifndef SQLITE_OMIT_TRIGGER
@@ -71952,8 +73157,7 @@
SelectDest dest;
sqlite3SelectDestInit(&dest, SRT_Discard, 0);
- sqlite3SelectResolve(pParse, ss, 0);
- sqlite3Select(pParse, ss, &dest, 0, 0, 0);
+ sqlite3Select(pParse, ss, &dest);
sqlite3SelectDelete(db, ss);
}
break;
@@ -72102,7 +73306,7 @@
/* code the WHEN clause */
endTrigger = sqlite3VdbeMakeLabel(pParse->pVdbe);
whenExpr = sqlite3ExprDup(db, p->pWhen);
- if( db->mallocFailed || sqlite3ExprResolveNames(&sNC, whenExpr) ){
+ if( db->mallocFailed || sqlite3ResolveExprNames(&sNC, whenExpr) ){
pParse->trigStack = trigStackEntry.pNext;
sqlite3ExprDelete(db, whenExpr);
return 1;
@@ -72141,7 +73345,7 @@
** This file contains C code routines that are called by the parser
** to handle UPDATE statements.
**
-** $Id: update.c,v 1.181 2008/07/28 19:34:54 drh Exp $
+** $Id: update.c,v 1.183 2008/08/22 12:30:52 drh Exp $
*/
#ifndef SQLITE_OMIT_VIRTUALTABLE
@@ -72322,7 +73526,7 @@
*/
chngRowid = 0;
for(i=0; i<pChanges->nExpr; i++){
- if( sqlite3ExprResolveNames(&sNC, pChanges->a[i].pExpr) ){
+ if( sqlite3ResolveExprNames(&sNC, pChanges->a[i].pExpr) ){
goto update_cleanup;
}
for(j=0; j<pTab->nCol; j++){
@@ -72457,13 +73661,13 @@
** a ephemeral table.
*/
if( isView ){
- sqlite3MaterializeView(pParse, pTab->pSelect, pWhere, iCur);
+ sqlite3MaterializeView(pParse, pTab, pWhere, iCur);
}
/* Resolve the column names in all the expressions in the
** WHERE clause.
*/
- if( sqlite3ExprResolveNames(&sNC, pWhere) ){
+ if( sqlite3ResolveExprNames(&sNC, pWhere) ){
goto update_cleanup;
}
@@ -72777,7 +73981,7 @@
/* fill the ephemeral table
*/
sqlite3SelectDestInit(&dest, SRT_Table, ephemTab);
- sqlite3Select(pParse, pSelect, &dest, 0, 0, 0);
+ sqlite3Select(pParse, pSelect, &dest);
/* Generate code to scan the ephemeral table and call VUpdate. */
iReg = ++pParse->nMem;
@@ -72823,7 +74027,7 @@
** Most of the code in this file may be omitted by defining the
** SQLITE_OMIT_VACUUM macro.
**
-** $Id: vacuum.c,v 1.81 2008/07/08 19:34:07 drh Exp $
+** $Id: vacuum.c,v 1.83 2008/08/26 21:07:27 drh Exp $
*/
#if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH)
@@ -72888,12 +74092,14 @@
SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
int rc = SQLITE_OK; /* Return code from service routines */
Btree *pMain; /* The database being vacuumed */
+ Pager *pMainPager; /* Pager for database being vacuumed */
Btree *pTemp; /* The temporary database we vacuum into */
char *zSql = 0; /* SQL statements */
int saved_flags; /* Saved value of the db->flags */
int saved_nChange; /* Saved value of db->nChange */
int saved_nTotalChange; /* Saved value of db->nTotalChange */
Db *pDb = 0; /* Database to detach at end of vacuum */
+ int isMemDb; /* True is vacuuming a :memory: database */
int nRes;
/* Save the current value of the write-schema flag before setting it. */
@@ -72908,6 +74114,8 @@
goto end_of_vacuum;
}
pMain = db->aDb[0].pBt;
+ pMainPager = sqlite3BtreePager(pMain);
+ isMemDb = sqlite3PagerFile(pMainPager)->pMethods==0;
/* Attach the temporary database as 'vacuum_db'. The synchronous pragma
** can be set to 'off' for this file, as it is not recovered if a crash
@@ -72944,7 +74152,7 @@
#endif
if( sqlite3BtreeSetPageSize(pTemp, sqlite3BtreeGetPageSize(pMain), nRes)
- || sqlite3BtreeSetPageSize(pTemp, db->nextPagesize, nRes)
+ || (!isMemDb && sqlite3BtreeSetPageSize(pTemp, db->nextPagesize, nRes))
|| db->mallocFailed
){
rc = SQLITE_NOMEM;
@@ -73065,6 +74273,9 @@
if( rc!=SQLITE_OK ) goto end_of_vacuum;
rc = sqlite3BtreeCommit(pTemp);
if( rc!=SQLITE_OK ) goto end_of_vacuum;
+#ifndef SQLITE_OMIT_AUTOVACUUM
+ sqlite3BtreeSetAutoVacuum(pMain, sqlite3BtreeGetAutoVacuum(pTemp));
+#endif
rc = sqlite3BtreeCommit(pMain);
}
@@ -73114,7 +74325,7 @@
*************************************************************************
** This file contains code used to help implement virtual tables.
**
-** $Id: vtab.c,v 1.74 2008/08/02 03:50:39 drh Exp $
+** $Id: vtab.c,v 1.76 2008/08/20 16:35:10 drh Exp $
*/
#ifndef SQLITE_OMIT_VIRTUALTABLE
@@ -73288,7 +74499,7 @@
iDb = sqlite3SchemaToIndex(db, pTable->pSchema);
assert( iDb>=0 );
- pTable->isVirtual = 1;
+ pTable->tabFlags |= TF_Virtual;
pTable->nModuleArg = 0;
addModuleArgument(db, pTable, sqlite3NameFromToken(db, pModuleName));
addModuleArgument(db, pTable, sqlite3DbStrDup(db, db->aDb[iDb].zName));
@@ -73544,7 +74755,7 @@
Module *pMod;
int rc = SQLITE_OK;
- if( !pTab || !pTab->isVirtual || pTab->pVtab ){
+ if( !pTab || (pTab->tabFlags & TF_Virtual)==0 || pTab->pVtab ){
return SQLITE_OK;
}
@@ -73605,7 +74816,7 @@
const char *zModule;
pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zName);
- assert(pTab && pTab->isVirtual && !pTab->pVtab);
+ assert(pTab && (pTab->tabFlags & TF_Virtual)!=0 && !pTab->pVtab);
pMod = pTab->pMod;
zModule = pTab->azModuleArg[0];
@@ -73646,7 +74857,7 @@
sqlite3_mutex_leave(db->mutex);
return SQLITE_MISUSE;
}
- assert(pTab->isVirtual && pTab->nCol==0 && pTab->aCol==0);
+ assert((pTab->tabFlags & TF_Virtual)!=0 && pTab->nCol==0 && pTab->aCol==0);
memset(&sParse, 0, sizeof(Parse));
sParse.declareVtab = 1;
@@ -73656,7 +74867,7 @@
SQLITE_OK == sqlite3RunParser(&sParse, zCreateTable, &zErr) &&
sParse.pNewTable &&
!sParse.pNewTable->pSelect &&
- !sParse.pNewTable->isVirtual
+ (sParse.pNewTable->tabFlags & TF_Virtual)==0
){
pTab->aCol = sParse.pNewTable->aCol;
pTab->nCol = sParse.pNewTable->nCol;
@@ -73875,7 +75086,7 @@
if( pExpr->op!=TK_COLUMN ) return pDef;
pTab = pExpr->pTab;
if( pTab==0 ) return pDef;
- if( !pTab->isVirtual ) return pDef;
+ if( (pTab->tabFlags & TF_Virtual)==0 ) return pDef;
pVtab = pTab->pVtab;
assert( pVtab!=0 );
assert( pVtab->pModule!=0 );
@@ -73909,6 +75120,7 @@
return pDef;
}
*pNew = *pDef;
+ pNew->zName = (char *)&pNew[1];
memcpy(pNew->zName, pDef->zName, strlen(pDef->zName)+1);
pNew->xFunc = xFunc;
pNew->pUserData = pArg;
@@ -73959,7 +75171,7 @@
** so is applicable. Because this module is responsible for selecting
** indices, you might also think of this module as the "query optimizer".
**
-** $Id: where.c,v 1.319 2008/08/01 17:37:41 danielk1977 Exp $
+** $Id: where.c,v 1.321 2008/08/25 12:08:22 drh Exp $
*/
/*
@@ -74262,9 +75474,9 @@
** tree.
**
** In order for this routine to work, the calling function must have
-** previously invoked sqlite3ExprResolveNames() on the expression. See
+** previously invoked sqlite3ResolveExprNames() on the expression. See
** the header comment on that routine for additional information.
-** The sqlite3ExprResolveNames() routines looks for column names and
+** The sqlite3ResolveExprNames() routines looks for column names and
** sets their opcodes to TK_COLUMN and their Expr.iTable fields to
** the VDBE cursor number of the table. This routine just has to
** translate the cursor numbers into bitmask values and OR all
@@ -74338,10 +75550,12 @@
** attached to the right. For the same reason the EP_ExpCollate flag
** is not commuted.
*/
-static void exprCommute(Expr *pExpr){
+static void exprCommute(Parse *pParse, Expr *pExpr){
u16 expRight = (pExpr->pRight->flags & EP_ExpCollate);
u16 expLeft = (pExpr->pLeft->flags & EP_ExpCollate);
assert( allowedOp(pExpr->op) && pExpr->op!=TK_IN );
+ pExpr->pRight->pColl = sqlite3ExprCollSeq(pParse, pExpr->pRight);
+ pExpr->pLeft->pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
SWAP(CollSeq*,pExpr->pRight->pColl,pExpr->pLeft->pColl);
pExpr->pRight->flags = (pExpr->pRight->flags & ~EP_ExpCollate) | expLeft;
pExpr->pLeft->flags = (pExpr->pLeft->flags & ~EP_ExpCollate) | expRight;
@@ -74461,7 +75675,7 @@
** literal that does not begin with a wildcard.
*/
static int isLikeOrGlob(
- sqlite3 *db, /* The database */
+ Parse *pParse, /* Parsing and code generating context */
Expr *pExpr, /* Test this expression */
int *pnPattern, /* Number of non-wildcard prefix characters */
int *pisComplete, /* True if the only wildcard is % in the last character */
@@ -74473,6 +75687,7 @@
int c, cnt;
char wc[3];
CollSeq *pColl;
+ sqlite3 *db = pParse->db;
if( !sqlite3IsLikeFunction(db, pExpr, pnoCase, wc) ){
return 0;
@@ -74490,7 +75705,7 @@
if( pLeft->op!=TK_COLUMN ){
return 0;
}
- pColl = pLeft->pColl;
+ pColl = sqlite3ExprCollSeq(pParse, pLeft);
assert( pColl!=0 || pLeft->iColumn==-1 );
if( pColl==0 ){
/* No collation is defined for the ROWID. Use the default. */
@@ -74730,7 +75945,7 @@
pDup = pExpr;
pNew = pTerm;
}
- exprCommute(pDup);
+ exprCommute(pParse, pDup);
pLeft = pDup->pLeft;
pNew->leftCursor = pLeft->iTable;
pNew->leftColumn = pLeft->iColumn;
@@ -74851,7 +76066,7 @@
** The last character of the prefix "abc" is incremented to form the
** termination condition "abd".
*/
- if( isLikeOrGlob(db, pExpr, &nPattern, &isComplete, &noCase) ){
+ if( isLikeOrGlob(pParse, pExpr, &nPattern, &isComplete, &noCase) ){
Expr *pLeft, *pRight;
Expr *pStr1, *pStr2;
Expr *pNewExpr1, *pNewExpr2;
@@ -75225,7 +76440,7 @@
** to this virtual table */
for(i=nTerm=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
if( pTerm->leftCursor != pSrc->iCursor ) continue;
- if( (pTerm->eOperator&(pTerm->eOperator-1))==0 );
+ assert( (pTerm->eOperator&(pTerm->eOperator-1))==0 );
testcase( pTerm->eOperator==WO_IN );
testcase( pTerm->eOperator==WO_ISNULL );
if( pTerm->eOperator & (WO_IN|WO_ISNULL) ) continue;
@@ -75275,7 +76490,7 @@
for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
if( pTerm->leftCursor != pSrc->iCursor ) continue;
- if( (pTerm->eOperator&(pTerm->eOperator-1))==0 );
+ assert( (pTerm->eOperator&(pTerm->eOperator-1))==0 );
testcase( pTerm->eOperator==WO_IN );
testcase( pTerm->eOperator==WO_ISNULL );
if( pTerm->eOperator & (WO_IN|WO_ISNULL) ) continue;
@@ -76220,7 +77435,7 @@
pTabItem = &pTabList->a[pLevel->iFrom];
pTab = pTabItem->pTab;
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
- if( pTab->isEphem || pTab->pSelect ) continue;
+ if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ) continue;
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( pLevel->pBestIdx ){
int iCur = pTabItem->iCursor;
@@ -76779,7 +77994,7 @@
struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom];
Table *pTab = pTabItem->pTab;
assert( pTab!=0 );
- if( pTab->isEphem || pTab->pSelect ) continue;
+ if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ) continue;
if( !pWInfo->okOnePass && (pLevel->flags & WHERE_IDX_ONLY)==0 ){
sqlite3VdbeAddOp1(v, OP_Close, pTabItem->iCursor);
}
@@ -76929,23 +78144,23 @@
** defined, then do no error processing.
*/
#define YYCODETYPE unsigned char
-#define YYNOCODE 248
+#define YYNOCODE 247
#define YYACTIONTYPE unsigned short int
#define YYWILDCARD 59
#define sqlite3ParserTOKENTYPE Token
typedef union {
sqlite3ParserTOKENTYPE yy0;
- int yy46;
- struct LikeOp yy72;
- Expr* yy172;
- ExprList* yy174;
- Select* yy219;
- struct LimitVal yy234;
- TriggerStep* yy243;
- struct TrigEvent yy370;
- SrcList* yy373;
- struct {int value; int mask;} yy405;
- IdList* yy432;
+ struct TrigEvent yy30;
+ Expr* yy62;
+ SrcList* yy151;
+ struct LimitVal yy220;
+ struct LikeOp yy222;
+ IdList* yy240;
+ int yy280;
+ struct {int value; int mask;} yy359;
+ TriggerStep* yy360;
+ Select* yy375;
+ ExprList* yy418;
} YYMINORTYPE;
#ifndef YYSTACKDEPTH
#define YYSTACKDEPTH 100
@@ -76954,8 +78169,8 @@
#define sqlite3ParserARG_PDECL ,Parse *pParse
#define sqlite3ParserARG_FETCH Parse *pParse = yypParser->pParse
#define sqlite3ParserARG_STORE yypParser->pParse = pParse
-#define YYNSTATE 589
-#define YYNRULE 313
+#define YYNSTATE 590
+#define YYNRULE 312
#define YYFALLBACK 1
#define YY_NO_ACTION (YYNSTATE+YYNRULE+2)
#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1)
@@ -76963,7 +78178,11 @@
/* The yyzerominor constant is used to initialize instances of
** YYMINORTYPE objects to zero. */
+#if 0
+static YYMINORTYPE yyzerominor;
+#else
static const YYMINORTYPE yyzerominor;
+#endif
/* Next are the tables used to determine what action to take based on the
** current state and lookahead token. These tables are used to implement
@@ -77013,416 +78232,417 @@
** yy_default[] Default action for each state.
*/
static const YYACTIONTYPE yy_action[] = {
- /* 0 */ 292, 903, 124, 588, 409, 172, 2, 418, 61, 61,
- /* 10 */ 61, 61, 519, 63, 63, 63, 63, 64, 64, 65,
- /* 20 */ 65, 65, 66, 210, 447, 212, 425, 431, 68, 63,
- /* 30 */ 63, 63, 63, 64, 64, 65, 65, 65, 66, 210,
- /* 40 */ 391, 388, 396, 451, 60, 59, 297, 435, 436, 432,
- /* 50 */ 432, 62, 62, 61, 61, 61, 61, 263, 63, 63,
- /* 60 */ 63, 63, 64, 64, 65, 65, 65, 66, 210, 292,
- /* 70 */ 493, 494, 418, 489, 208, 82, 67, 420, 69, 154,
- /* 80 */ 63, 63, 63, 63, 64, 64, 65, 65, 65, 66,
- /* 90 */ 210, 67, 462, 69, 154, 425, 431, 574, 264, 58,
- /* 100 */ 64, 64, 65, 65, 65, 66, 210, 397, 398, 422,
- /* 110 */ 422, 422, 292, 60, 59, 297, 435, 436, 432, 432,
- /* 120 */ 62, 62, 61, 61, 61, 61, 317, 63, 63, 63,
- /* 130 */ 63, 64, 64, 65, 65, 65, 66, 210, 425, 431,
- /* 140 */ 94, 65, 65, 65, 66, 210, 396, 210, 414, 34,
- /* 150 */ 56, 298, 442, 443, 410, 418, 60, 59, 297, 435,
- /* 160 */ 436, 432, 432, 62, 62, 61, 61, 61, 61, 208,
- /* 170 */ 63, 63, 63, 63, 64, 64, 65, 65, 65, 66,
- /* 180 */ 210, 292, 372, 524, 295, 572, 113, 408, 522, 451,
- /* 190 */ 331, 317, 407, 20, 244, 340, 519, 396, 478, 531,
- /* 200 */ 505, 447, 212, 571, 570, 245, 530, 425, 431, 149,
- /* 210 */ 150, 397, 398, 414, 41, 211, 151, 533, 488, 489,
- /* 220 */ 418, 568, 569, 420, 292, 60, 59, 297, 435, 436,
- /* 230 */ 432, 432, 62, 62, 61, 61, 61, 61, 317, 63,
- /* 240 */ 63, 63, 63, 64, 64, 65, 65, 65, 66, 210,
- /* 250 */ 425, 431, 447, 333, 215, 422, 422, 422, 363, 299,
- /* 260 */ 414, 41, 397, 398, 366, 567, 211, 292, 60, 59,
- /* 270 */ 297, 435, 436, 432, 432, 62, 62, 61, 61, 61,
- /* 280 */ 61, 396, 63, 63, 63, 63, 64, 64, 65, 65,
- /* 290 */ 65, 66, 210, 425, 431, 491, 300, 524, 474, 66,
- /* 300 */ 210, 214, 474, 229, 411, 286, 534, 20, 449, 523,
- /* 310 */ 168, 60, 59, 297, 435, 436, 432, 432, 62, 62,
- /* 320 */ 61, 61, 61, 61, 474, 63, 63, 63, 63, 64,
- /* 330 */ 64, 65, 65, 65, 66, 210, 209, 480, 317, 77,
- /* 340 */ 292, 239, 300, 55, 484, 490, 397, 398, 181, 547,
- /* 350 */ 494, 345, 348, 349, 67, 152, 69, 154, 339, 524,
- /* 360 */ 414, 35, 350, 241, 221, 370, 425, 431, 579, 20,
- /* 370 */ 164, 118, 243, 343, 248, 344, 176, 322, 442, 443,
- /* 380 */ 414, 3, 80, 252, 60, 59, 297, 435, 436, 432,
- /* 390 */ 432, 62, 62, 61, 61, 61, 61, 174, 63, 63,
- /* 400 */ 63, 63, 64, 64, 65, 65, 65, 66, 210, 292,
- /* 410 */ 221, 550, 236, 487, 510, 353, 317, 118, 243, 343,
- /* 420 */ 248, 344, 176, 181, 317, 532, 345, 348, 349, 252,
- /* 430 */ 223, 415, 155, 464, 511, 425, 431, 350, 414, 34,
- /* 440 */ 465, 211, 177, 175, 160, 525, 414, 34, 338, 549,
- /* 450 */ 449, 323, 168, 60, 59, 297, 435, 436, 432, 432,
- /* 460 */ 62, 62, 61, 61, 61, 61, 415, 63, 63, 63,
- /* 470 */ 63, 64, 64, 65, 65, 65, 66, 210, 292, 542,
- /* 480 */ 335, 517, 504, 541, 456, 572, 302, 19, 331, 144,
- /* 490 */ 317, 390, 317, 330, 2, 362, 457, 294, 483, 373,
- /* 500 */ 269, 268, 252, 571, 425, 431, 589, 391, 388, 458,
- /* 510 */ 208, 495, 414, 49, 414, 49, 303, 586, 894, 230,
- /* 520 */ 894, 496, 60, 59, 297, 435, 436, 432, 432, 62,
- /* 530 */ 62, 61, 61, 61, 61, 201, 63, 63, 63, 63,
- /* 540 */ 64, 64, 65, 65, 65, 66, 210, 292, 317, 181,
- /* 550 */ 439, 255, 345, 348, 349, 370, 153, 583, 308, 251,
- /* 560 */ 309, 452, 76, 350, 78, 382, 211, 426, 427, 415,
- /* 570 */ 414, 27, 319, 425, 431, 440, 1, 22, 586, 893,
- /* 580 */ 396, 893, 544, 478, 320, 263, 438, 438, 429, 430,
- /* 590 */ 415, 60, 59, 297, 435, 436, 432, 432, 62, 62,
- /* 600 */ 61, 61, 61, 61, 237, 63, 63, 63, 63, 64,
- /* 610 */ 64, 65, 65, 65, 66, 210, 292, 428, 583, 374,
- /* 620 */ 224, 93, 517, 9, 159, 396, 557, 396, 456, 67,
- /* 630 */ 396, 69, 154, 399, 400, 401, 320, 328, 438, 438,
- /* 640 */ 457, 336, 425, 431, 361, 397, 398, 320, 433, 438,
- /* 650 */ 438, 582, 291, 458, 238, 327, 318, 222, 546, 292,
- /* 660 */ 60, 59, 297, 435, 436, 432, 432, 62, 62, 61,
- /* 670 */ 61, 61, 61, 225, 63, 63, 63, 63, 64, 64,
- /* 680 */ 65, 65, 65, 66, 210, 425, 431, 482, 313, 392,
- /* 690 */ 397, 398, 397, 398, 207, 397, 398, 825, 273, 517,
- /* 700 */ 251, 200, 292, 60, 59, 297, 435, 436, 432, 432,
- /* 710 */ 62, 62, 61, 61, 61, 61, 470, 63, 63, 63,
- /* 720 */ 63, 64, 64, 65, 65, 65, 66, 210, 425, 431,
- /* 730 */ 171, 160, 263, 263, 304, 415, 276, 395, 274, 263,
- /* 740 */ 517, 517, 263, 517, 192, 292, 60, 70, 297, 435,
- /* 750 */ 436, 432, 432, 62, 62, 61, 61, 61, 61, 379,
- /* 760 */ 63, 63, 63, 63, 64, 64, 65, 65, 65, 66,
- /* 770 */ 210, 425, 431, 384, 559, 305, 306, 251, 415, 320,
- /* 780 */ 560, 438, 438, 561, 540, 360, 540, 387, 292, 196,
- /* 790 */ 59, 297, 435, 436, 432, 432, 62, 62, 61, 61,
- /* 800 */ 61, 61, 371, 63, 63, 63, 63, 64, 64, 65,
- /* 810 */ 65, 65, 66, 210, 425, 431, 396, 275, 251, 251,
- /* 820 */ 172, 250, 418, 415, 386, 367, 178, 179, 180, 469,
- /* 830 */ 311, 123, 156, 5, 297, 435, 436, 432, 432, 62,
- /* 840 */ 62, 61, 61, 61, 61, 317, 63, 63, 63, 63,
- /* 850 */ 64, 64, 65, 65, 65, 66, 210, 72, 324, 194,
- /* 860 */ 4, 317, 263, 317, 296, 263, 415, 414, 28, 317,
- /* 870 */ 257, 317, 321, 72, 324, 317, 4, 119, 165, 177,
- /* 880 */ 296, 397, 398, 414, 23, 414, 32, 418, 321, 326,
- /* 890 */ 421, 414, 53, 414, 52, 317, 158, 414, 98, 451,
- /* 900 */ 317, 263, 317, 277, 317, 326, 378, 471, 261, 317,
- /* 910 */ 259, 18, 478, 445, 445, 451, 317, 414, 96, 75,
- /* 920 */ 74, 469, 414, 101, 414, 102, 414, 112, 73, 315,
- /* 930 */ 316, 414, 114, 420, 294, 75, 74, 481, 414, 16,
- /* 940 */ 381, 317, 279, 467, 73, 315, 316, 72, 324, 420,
- /* 950 */ 4, 208, 317, 183, 296, 317, 186, 128, 84, 208,
- /* 960 */ 8, 341, 321, 414, 99, 422, 422, 422, 423, 424,
- /* 970 */ 11, 623, 380, 307, 414, 33, 413, 414, 97, 326,
- /* 980 */ 412, 422, 422, 422, 423, 424, 11, 415, 413, 451,
- /* 990 */ 415, 162, 412, 317, 499, 500, 226, 227, 228, 104,
- /* 1000 */ 448, 476, 317, 173, 507, 317, 509, 508, 317, 75,
- /* 1010 */ 74, 329, 205, 21, 281, 414, 24, 418, 73, 315,
- /* 1020 */ 316, 282, 317, 420, 414, 54, 460, 414, 115, 317,
- /* 1030 */ 414, 116, 502, 203, 147, 549, 514, 468, 128, 202,
- /* 1040 */ 317, 473, 204, 317, 414, 117, 317, 477, 317, 584,
- /* 1050 */ 317, 414, 25, 317, 249, 422, 422, 422, 423, 424,
- /* 1060 */ 11, 506, 414, 36, 512, 414, 37, 317, 414, 26,
- /* 1070 */ 414, 38, 414, 39, 526, 414, 40, 317, 254, 317,
- /* 1080 */ 128, 317, 418, 317, 256, 377, 278, 268, 585, 414,
- /* 1090 */ 42, 293, 317, 352, 317, 128, 208, 513, 258, 414,
- /* 1100 */ 43, 414, 44, 414, 29, 414, 30, 545, 260, 128,
- /* 1110 */ 317, 553, 317, 173, 414, 45, 414, 46, 317, 262,
- /* 1120 */ 383, 554, 317, 91, 564, 317, 91, 317, 581, 189,
- /* 1130 */ 290, 357, 414, 47, 414, 48, 267, 365, 368, 369,
- /* 1140 */ 414, 31, 270, 271, 414, 10, 272, 414, 50, 414,
- /* 1150 */ 51, 556, 566, 280, 283, 284, 578, 146, 419, 405,
- /* 1160 */ 231, 505, 444, 325, 516, 463, 163, 446, 552, 394,
- /* 1170 */ 466, 563, 246, 515, 518, 520, 402, 403, 404, 7,
- /* 1180 */ 314, 84, 232, 334, 347, 83, 332, 57, 170, 79,
- /* 1190 */ 213, 461, 125, 85, 337, 342, 492, 502, 497, 301,
- /* 1200 */ 498, 416, 105, 219, 247, 218, 503, 501, 233, 220,
- /* 1210 */ 287, 234, 527, 528, 235, 529, 417, 521, 354, 288,
- /* 1220 */ 184, 121, 185, 240, 535, 475, 242, 356, 187, 479,
- /* 1230 */ 188, 358, 537, 88, 190, 548, 364, 193, 132, 376,
- /* 1240 */ 555, 375, 133, 134, 135, 310, 562, 138, 136, 575,
- /* 1250 */ 576, 577, 580, 100, 393, 406, 217, 142, 624, 625,
- /* 1260 */ 103, 141, 265, 166, 167, 434, 71, 453, 441, 437,
- /* 1270 */ 450, 143, 538, 157, 120, 454, 161, 472, 455, 169,
- /* 1280 */ 459, 81, 6, 12, 13, 92, 95, 126, 216, 127,
- /* 1290 */ 111, 485, 486, 17, 86, 346, 106, 122, 253, 107,
- /* 1300 */ 87, 108, 182, 245, 355, 145, 351, 536, 129, 359,
- /* 1310 */ 312, 130, 543, 173, 539, 266, 191, 109, 289, 551,
- /* 1320 */ 195, 14, 131, 198, 197, 558, 137, 199, 139, 140,
- /* 1330 */ 15, 565, 89, 90, 573, 110, 385, 206, 148, 389,
- /* 1340 */ 285, 587,
+ /* 0 */ 292, 903, 120, 589, 2, 172, 419, 419, 62, 62,
+ /* 10 */ 62, 62, 209, 64, 64, 64, 64, 65, 65, 66,
+ /* 20 */ 66, 66, 67, 211, 392, 389, 426, 432, 69, 64,
+ /* 30 */ 64, 64, 64, 65, 65, 66, 66, 66, 67, 211,
+ /* 40 */ 448, 213, 397, 452, 61, 60, 297, 436, 437, 433,
+ /* 50 */ 433, 63, 63, 62, 62, 62, 62, 264, 64, 64,
+ /* 60 */ 64, 64, 65, 65, 66, 66, 66, 67, 211, 292,
+ /* 70 */ 317, 419, 419, 490, 211, 83, 68, 421, 70, 154,
+ /* 80 */ 64, 64, 64, 64, 65, 65, 66, 66, 66, 67,
+ /* 90 */ 211, 489, 415, 36, 181, 426, 432, 448, 265, 59,
+ /* 100 */ 65, 65, 66, 66, 66, 67, 211, 398, 399, 423,
+ /* 110 */ 423, 423, 292, 61, 60, 297, 436, 437, 433, 433,
+ /* 120 */ 63, 63, 62, 62, 62, 62, 317, 64, 64, 64,
+ /* 130 */ 64, 65, 65, 66, 66, 66, 67, 211, 426, 432,
+ /* 140 */ 95, 313, 394, 475, 237, 172, 208, 419, 415, 35,
+ /* 150 */ 57, 67, 211, 201, 411, 475, 61, 60, 297, 436,
+ /* 160 */ 437, 433, 433, 63, 63, 62, 62, 62, 62, 503,
+ /* 170 */ 64, 64, 64, 64, 65, 65, 66, 66, 66, 67,
+ /* 180 */ 211, 292, 481, 524, 542, 573, 109, 416, 541, 452,
+ /* 190 */ 331, 317, 408, 21, 240, 340, 409, 522, 317, 68,
+ /* 200 */ 362, 70, 154, 572, 571, 519, 492, 426, 432, 149,
+ /* 210 */ 150, 380, 419, 415, 42, 412, 151, 533, 202, 490,
+ /* 220 */ 415, 50, 532, 421, 292, 61, 60, 297, 436, 437,
+ /* 230 */ 433, 433, 63, 63, 62, 62, 62, 62, 388, 64,
+ /* 240 */ 64, 64, 64, 65, 65, 66, 66, 66, 67, 211,
+ /* 250 */ 426, 432, 416, 333, 216, 423, 423, 423, 66, 66,
+ /* 260 */ 66, 67, 211, 491, 568, 212, 308, 292, 61, 60,
+ /* 270 */ 297, 436, 437, 433, 433, 63, 63, 62, 62, 62,
+ /* 280 */ 62, 397, 64, 64, 64, 64, 65, 65, 66, 66,
+ /* 290 */ 66, 67, 211, 426, 432, 182, 300, 410, 345, 348,
+ /* 300 */ 349, 531, 506, 252, 68, 519, 70, 154, 530, 350,
+ /* 310 */ 231, 61, 60, 297, 436, 437, 433, 433, 63, 63,
+ /* 320 */ 62, 62, 62, 62, 575, 64, 64, 64, 64, 65,
+ /* 330 */ 65, 66, 66, 66, 67, 211, 525, 317, 303, 78,
+ /* 340 */ 292, 238, 300, 511, 485, 153, 398, 399, 182, 494,
+ /* 350 */ 495, 345, 348, 349, 320, 152, 439, 439, 339, 415,
+ /* 360 */ 28, 328, 350, 512, 222, 370, 426, 432, 547, 495,
+ /* 370 */ 164, 114, 244, 343, 249, 344, 176, 583, 291, 416,
+ /* 380 */ 415, 3, 81, 253, 61, 60, 297, 436, 437, 433,
+ /* 390 */ 433, 63, 63, 62, 62, 62, 62, 174, 64, 64,
+ /* 400 */ 64, 64, 65, 65, 66, 66, 66, 67, 211, 292,
+ /* 410 */ 222, 587, 894, 488, 894, 302, 573, 114, 244, 343,
+ /* 420 */ 249, 344, 176, 182, 317, 397, 345, 348, 349, 253,
+ /* 430 */ 224, 416, 155, 549, 572, 426, 432, 350, 68, 463,
+ /* 440 */ 70, 154, 397, 175, 160, 397, 415, 35, 338, 587,
+ /* 450 */ 893, 584, 893, 61, 60, 297, 436, 437, 433, 433,
+ /* 460 */ 63, 63, 62, 62, 62, 62, 416, 64, 64, 64,
+ /* 470 */ 64, 65, 65, 66, 66, 66, 67, 211, 292, 550,
+ /* 480 */ 448, 213, 505, 373, 270, 269, 171, 160, 331, 584,
+ /* 490 */ 398, 399, 317, 330, 209, 383, 212, 159, 427, 428,
+ /* 500 */ 569, 570, 483, 524, 426, 432, 336, 398, 399, 230,
+ /* 510 */ 398, 399, 534, 21, 415, 42, 239, 549, 479, 430,
+ /* 520 */ 431, 475, 61, 60, 297, 436, 437, 433, 433, 63,
+ /* 530 */ 63, 62, 62, 62, 62, 264, 64, 64, 64, 64,
+ /* 540 */ 65, 65, 66, 66, 66, 67, 211, 292, 429, 287,
+ /* 550 */ 457, 256, 450, 523, 168, 215, 197, 295, 317, 353,
+ /* 560 */ 242, 317, 458, 298, 443, 444, 468, 373, 270, 269,
+ /* 570 */ 322, 443, 444, 426, 432, 459, 558, 496, 209, 299,
+ /* 580 */ 415, 35, 544, 415, 50, 1, 177, 497, 479, 397,
+ /* 590 */ 416, 61, 60, 297, 436, 437, 433, 433, 63, 63,
+ /* 600 */ 62, 62, 62, 62, 484, 64, 64, 64, 64, 65,
+ /* 610 */ 65, 66, 66, 66, 67, 211, 292, 317, 524, 375,
+ /* 620 */ 457, 94, 335, 590, 392, 389, 212, 580, 21, 309,
+ /* 630 */ 10, 363, 458, 212, 397, 209, 366, 391, 2, 415,
+ /* 640 */ 29, 294, 426, 432, 195, 459, 253, 327, 372, 361,
+ /* 650 */ 440, 450, 323, 168, 398, 399, 252, 147, 546, 292,
+ /* 660 */ 61, 60, 297, 436, 437, 433, 433, 63, 63, 62,
+ /* 670 */ 62, 62, 62, 317, 64, 64, 64, 64, 65, 65,
+ /* 680 */ 66, 66, 66, 67, 211, 426, 432, 210, 318, 453,
+ /* 690 */ 320, 223, 439, 439, 56, 415, 24, 826, 252, 398,
+ /* 700 */ 399, 193, 292, 61, 60, 297, 436, 437, 433, 433,
+ /* 710 */ 63, 63, 62, 62, 62, 62, 226, 64, 64, 64,
+ /* 720 */ 64, 65, 65, 66, 66, 66, 67, 211, 426, 432,
+ /* 730 */ 311, 119, 264, 304, 396, 416, 320, 19, 439, 439,
+ /* 740 */ 400, 401, 402, 85, 274, 292, 61, 71, 297, 436,
+ /* 750 */ 437, 433, 433, 63, 63, 62, 62, 62, 62, 371,
+ /* 760 */ 64, 64, 64, 64, 65, 65, 66, 66, 66, 67,
+ /* 770 */ 211, 426, 432, 385, 115, 320, 18, 439, 439, 446,
+ /* 780 */ 446, 374, 277, 5, 275, 264, 8, 252, 292, 341,
+ /* 790 */ 60, 297, 436, 437, 433, 433, 63, 63, 62, 62,
+ /* 800 */ 62, 62, 397, 64, 64, 64, 64, 65, 65, 66,
+ /* 810 */ 66, 66, 67, 211, 426, 432, 414, 397, 422, 470,
+ /* 820 */ 413, 22, 305, 387, 252, 419, 560, 193, 414, 264,
+ /* 830 */ 264, 370, 413, 190, 297, 436, 437, 433, 433, 63,
+ /* 840 */ 63, 62, 62, 62, 62, 479, 64, 64, 64, 64,
+ /* 850 */ 65, 65, 66, 66, 66, 67, 211, 73, 324, 306,
+ /* 860 */ 4, 416, 264, 276, 296, 449, 177, 398, 399, 317,
+ /* 870 */ 561, 562, 321, 73, 324, 317, 4, 540, 360, 540,
+ /* 880 */ 296, 329, 398, 399, 461, 371, 158, 317, 321, 326,
+ /* 890 */ 419, 415, 33, 471, 317, 165, 225, 415, 54, 452,
+ /* 900 */ 317, 264, 317, 278, 317, 326, 307, 367, 472, 415,
+ /* 910 */ 53, 470, 178, 179, 180, 452, 415, 99, 317, 76,
+ /* 920 */ 75, 294, 415, 97, 415, 102, 415, 103, 74, 315,
+ /* 930 */ 316, 319, 264, 421, 469, 76, 75, 482, 317, 382,
+ /* 940 */ 415, 108, 379, 474, 74, 315, 316, 73, 324, 421,
+ /* 950 */ 4, 209, 317, 156, 296, 317, 184, 465, 209, 187,
+ /* 960 */ 415, 110, 321, 258, 466, 423, 423, 423, 424, 425,
+ /* 970 */ 12, 381, 478, 280, 415, 17, 250, 415, 100, 326,
+ /* 980 */ 507, 423, 423, 423, 424, 425, 12, 416, 624, 452,
+ /* 990 */ 416, 162, 508, 416, 317, 513, 227, 228, 229, 105,
+ /* 1000 */ 514, 262, 317, 260, 20, 317, 144, 434, 317, 76,
+ /* 1010 */ 75, 77, 206, 79, 282, 317, 415, 34, 74, 315,
+ /* 1020 */ 316, 283, 317, 421, 415, 98, 251, 415, 25, 526,
+ /* 1030 */ 415, 55, 441, 204, 23, 549, 257, 415, 111, 203,
+ /* 1040 */ 317, 477, 205, 173, 415, 112, 317, 259, 317, 515,
+ /* 1050 */ 317, 181, 317, 261, 245, 423, 423, 423, 424, 425,
+ /* 1060 */ 12, 263, 415, 113, 357, 246, 317, 268, 415, 26,
+ /* 1070 */ 415, 37, 415, 38, 415, 27, 317, 500, 501, 510,
+ /* 1080 */ 509, 317, 365, 317, 368, 378, 279, 269, 415, 39,
+ /* 1090 */ 369, 293, 317, 255, 317, 181, 209, 271, 415, 40,
+ /* 1100 */ 317, 272, 317, 415, 41, 415, 43, 352, 317, 181,
+ /* 1110 */ 317, 273, 557, 317, 415, 44, 415, 45, 317, 545,
+ /* 1120 */ 384, 181, 415, 30, 415, 31, 317, 585, 567, 317,
+ /* 1130 */ 415, 46, 415, 47, 317, 415, 48, 317, 281, 284,
+ /* 1140 */ 415, 49, 553, 554, 173, 92, 285, 579, 415, 32,
+ /* 1150 */ 406, 415, 11, 565, 420, 92, 415, 51, 146, 415,
+ /* 1160 */ 52, 582, 232, 290, 325, 517, 586, 445, 447, 464,
+ /* 1170 */ 467, 506, 520, 163, 247, 516, 395, 518, 552, 347,
+ /* 1180 */ 403, 404, 405, 564, 7, 314, 85, 334, 332, 233,
+ /* 1190 */ 84, 234, 80, 170, 58, 214, 417, 462, 121, 86,
+ /* 1200 */ 337, 342, 499, 493, 235, 301, 236, 503, 418, 498,
+ /* 1210 */ 248, 124, 504, 502, 220, 354, 288, 241, 527, 476,
+ /* 1220 */ 243, 528, 480, 521, 529, 289, 185, 358, 535, 186,
+ /* 1230 */ 89, 356, 189, 188, 117, 537, 364, 191, 548, 194,
+ /* 1240 */ 219, 132, 142, 221, 376, 377, 555, 133, 134, 310,
+ /* 1250 */ 135, 136, 266, 563, 538, 581, 576, 141, 93, 393,
+ /* 1260 */ 96, 138, 407, 577, 578, 107, 218, 101, 104, 118,
+ /* 1270 */ 312, 625, 626, 166, 435, 167, 438, 442, 72, 454,
+ /* 1280 */ 451, 143, 157, 169, 455, 456, 460, 6, 14, 82,
+ /* 1290 */ 473, 13, 122, 161, 123, 486, 487, 217, 87, 346,
+ /* 1300 */ 125, 126, 116, 254, 88, 127, 183, 246, 355, 145,
+ /* 1310 */ 536, 128, 173, 359, 192, 351, 267, 130, 9, 551,
+ /* 1320 */ 131, 196, 90, 539, 91, 129, 15, 198, 556, 543,
+ /* 1330 */ 199, 559, 200, 137, 139, 566, 16, 140, 106, 574,
+ /* 1340 */ 207, 148, 286, 390, 386, 588,
};
static const YYCODETYPE yy_lookahead[] = {
- /* 0 */ 16, 139, 140, 141, 168, 21, 144, 23, 69, 70,
- /* 10 */ 71, 72, 176, 74, 75, 76, 77, 78, 79, 80,
- /* 20 */ 81, 82, 83, 84, 78, 79, 42, 43, 73, 74,
+ /* 0 */ 16, 139, 140, 141, 142, 21, 23, 23, 69, 70,
+ /* 10 */ 71, 72, 110, 74, 75, 76, 77, 78, 79, 80,
+ /* 20 */ 81, 82, 83, 84, 1, 2, 42, 43, 73, 74,
/* 30 */ 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
- /* 40 */ 1, 2, 23, 58, 60, 61, 62, 63, 64, 65,
+ /* 40 */ 78, 79, 23, 58, 60, 61, 62, 63, 64, 65,
/* 50 */ 66, 67, 68, 69, 70, 71, 72, 147, 74, 75,
/* 60 */ 76, 77, 78, 79, 80, 81, 82, 83, 84, 16,
- /* 70 */ 185, 186, 88, 88, 110, 22, 217, 92, 219, 220,
+ /* 70 */ 147, 88, 88, 88, 84, 22, 217, 92, 219, 220,
/* 80 */ 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
- /* 90 */ 84, 217, 218, 219, 220, 42, 43, 238, 188, 46,
+ /* 90 */ 84, 169, 169, 170, 22, 42, 43, 78, 188, 46,
/* 100 */ 78, 79, 80, 81, 82, 83, 84, 88, 89, 124,
/* 110 */ 125, 126, 16, 60, 61, 62, 63, 64, 65, 66,
/* 120 */ 67, 68, 69, 70, 71, 72, 147, 74, 75, 76,
/* 130 */ 77, 78, 79, 80, 81, 82, 83, 84, 42, 43,
- /* 140 */ 44, 80, 81, 82, 83, 84, 23, 84, 169, 170,
- /* 150 */ 19, 164, 165, 166, 23, 23, 60, 61, 62, 63,
- /* 160 */ 64, 65, 66, 67, 68, 69, 70, 71, 72, 110,
+ /* 140 */ 44, 143, 144, 161, 221, 21, 148, 23, 169, 170,
+ /* 150 */ 19, 83, 84, 155, 23, 161, 60, 61, 62, 63,
+ /* 160 */ 64, 65, 66, 67, 68, 69, 70, 71, 72, 97,
/* 170 */ 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
- /* 180 */ 84, 16, 123, 147, 150, 147, 21, 167, 168, 58,
- /* 190 */ 211, 147, 156, 157, 92, 216, 176, 23, 147, 176,
- /* 200 */ 177, 78, 79, 165, 166, 103, 183, 42, 43, 78,
- /* 210 */ 79, 88, 89, 169, 170, 228, 180, 181, 169, 88,
- /* 220 */ 88, 98, 99, 92, 16, 60, 61, 62, 63, 64,
- /* 230 */ 65, 66, 67, 68, 69, 70, 71, 72, 147, 74,
+ /* 180 */ 84, 16, 200, 147, 25, 147, 21, 189, 29, 58,
+ /* 190 */ 211, 147, 156, 157, 200, 216, 167, 168, 147, 217,
+ /* 200 */ 41, 219, 220, 165, 166, 176, 160, 42, 43, 78,
+ /* 210 */ 79, 213, 88, 169, 170, 169, 180, 181, 155, 88,
+ /* 220 */ 169, 170, 181, 92, 16, 60, 61, 62, 63, 64,
+ /* 230 */ 65, 66, 67, 68, 69, 70, 71, 72, 240, 74,
/* 240 */ 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
- /* 250 */ 42, 43, 78, 209, 210, 124, 125, 126, 224, 208,
- /* 260 */ 169, 170, 88, 89, 230, 227, 228, 16, 60, 61,
+ /* 250 */ 42, 43, 189, 209, 210, 124, 125, 126, 80, 81,
+ /* 260 */ 82, 83, 84, 169, 226, 227, 215, 16, 60, 61,
/* 270 */ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
/* 280 */ 72, 23, 74, 75, 76, 77, 78, 79, 80, 81,
- /* 290 */ 82, 83, 84, 42, 43, 160, 16, 147, 161, 83,
- /* 300 */ 84, 210, 161, 153, 169, 158, 156, 157, 161, 162,
- /* 310 */ 163, 60, 61, 62, 63, 64, 65, 66, 67, 68,
- /* 320 */ 69, 70, 71, 72, 161, 74, 75, 76, 77, 78,
- /* 330 */ 79, 80, 81, 82, 83, 84, 192, 200, 147, 131,
- /* 340 */ 16, 200, 16, 199, 20, 169, 88, 89, 90, 185,
- /* 350 */ 186, 93, 94, 95, 217, 22, 219, 220, 147, 147,
- /* 360 */ 169, 170, 104, 200, 84, 147, 42, 43, 156, 157,
- /* 370 */ 90, 91, 92, 93, 94, 95, 96, 164, 165, 166,
+ /* 290 */ 82, 83, 84, 42, 43, 90, 16, 168, 93, 94,
+ /* 300 */ 95, 176, 177, 147, 217, 176, 219, 220, 183, 104,
+ /* 310 */ 190, 60, 61, 62, 63, 64, 65, 66, 67, 68,
+ /* 320 */ 69, 70, 71, 72, 237, 74, 75, 76, 77, 78,
+ /* 330 */ 79, 80, 81, 82, 83, 84, 181, 147, 182, 131,
+ /* 340 */ 16, 147, 16, 30, 20, 155, 88, 89, 90, 185,
+ /* 350 */ 186, 93, 94, 95, 106, 22, 108, 109, 147, 169,
+ /* 360 */ 170, 186, 104, 50, 84, 147, 42, 43, 185, 186,
+ /* 370 */ 90, 91, 92, 93, 94, 95, 96, 243, 244, 189,
/* 380 */ 169, 170, 131, 103, 60, 61, 62, 63, 64, 65,
/* 390 */ 66, 67, 68, 69, 70, 71, 72, 155, 74, 75,
/* 400 */ 76, 77, 78, 79, 80, 81, 82, 83, 84, 16,
- /* 410 */ 84, 11, 221, 20, 30, 16, 147, 91, 92, 93,
- /* 420 */ 94, 95, 96, 90, 147, 181, 93, 94, 95, 103,
- /* 430 */ 212, 189, 155, 27, 50, 42, 43, 104, 169, 170,
- /* 440 */ 34, 228, 43, 201, 202, 181, 169, 170, 206, 49,
- /* 450 */ 161, 162, 163, 60, 61, 62, 63, 64, 65, 66,
+ /* 410 */ 84, 19, 20, 20, 22, 102, 147, 91, 92, 93,
+ /* 420 */ 94, 95, 96, 90, 147, 23, 93, 94, 95, 103,
+ /* 430 */ 212, 189, 155, 49, 165, 42, 43, 104, 217, 218,
+ /* 440 */ 219, 220, 23, 201, 202, 23, 169, 170, 206, 19,
+ /* 450 */ 20, 59, 22, 60, 61, 62, 63, 64, 65, 66,
/* 460 */ 67, 68, 69, 70, 71, 72, 189, 74, 75, 76,
- /* 470 */ 77, 78, 79, 80, 81, 82, 83, 84, 16, 25,
- /* 480 */ 211, 147, 20, 29, 12, 147, 102, 19, 211, 21,
- /* 490 */ 147, 141, 147, 216, 144, 41, 24, 98, 20, 99,
- /* 500 */ 100, 101, 103, 165, 42, 43, 0, 1, 2, 37,
- /* 510 */ 110, 39, 169, 170, 169, 170, 182, 19, 20, 190,
- /* 520 */ 22, 49, 60, 61, 62, 63, 64, 65, 66, 67,
- /* 530 */ 68, 69, 70, 71, 72, 155, 74, 75, 76, 77,
- /* 540 */ 78, 79, 80, 81, 82, 83, 84, 16, 147, 90,
- /* 550 */ 20, 20, 93, 94, 95, 147, 155, 59, 215, 225,
- /* 560 */ 215, 20, 130, 104, 132, 227, 228, 42, 43, 189,
- /* 570 */ 169, 170, 16, 42, 43, 20, 19, 22, 19, 20,
- /* 580 */ 23, 22, 18, 147, 106, 147, 108, 109, 63, 64,
+ /* 470 */ 77, 78, 79, 80, 81, 82, 83, 84, 16, 11,
+ /* 480 */ 78, 79, 20, 99, 100, 101, 201, 202, 211, 59,
+ /* 490 */ 88, 89, 147, 216, 110, 226, 227, 147, 42, 43,
+ /* 500 */ 98, 99, 80, 147, 42, 43, 147, 88, 89, 153,
+ /* 510 */ 88, 89, 156, 157, 169, 170, 147, 49, 147, 63,
+ /* 520 */ 64, 161, 60, 61, 62, 63, 64, 65, 66, 67,
+ /* 530 */ 68, 69, 70, 71, 72, 147, 74, 75, 76, 77,
+ /* 540 */ 78, 79, 80, 81, 82, 83, 84, 16, 92, 158,
+ /* 550 */ 12, 20, 161, 162, 163, 210, 155, 150, 147, 16,
+ /* 560 */ 200, 147, 24, 164, 165, 166, 22, 99, 100, 101,
+ /* 570 */ 164, 165, 166, 42, 43, 37, 188, 39, 110, 208,
+ /* 580 */ 169, 170, 18, 169, 170, 19, 43, 49, 147, 23,
/* 590 */ 189, 60, 61, 62, 63, 64, 65, 66, 67, 68,
- /* 600 */ 69, 70, 71, 72, 147, 74, 75, 76, 77, 78,
- /* 610 */ 79, 80, 81, 82, 83, 84, 16, 92, 59, 55,
- /* 620 */ 212, 21, 147, 19, 147, 23, 188, 23, 12, 217,
- /* 630 */ 23, 219, 220, 7, 8, 9, 106, 186, 108, 109,
- /* 640 */ 24, 147, 42, 43, 208, 88, 89, 106, 92, 108,
- /* 650 */ 109, 244, 245, 37, 147, 39, 147, 182, 94, 16,
+ /* 600 */ 69, 70, 71, 72, 20, 74, 75, 76, 77, 78,
+ /* 610 */ 79, 80, 81, 82, 83, 84, 16, 147, 147, 55,
+ /* 620 */ 12, 21, 211, 0, 1, 2, 227, 156, 157, 215,
+ /* 630 */ 19, 224, 24, 227, 23, 110, 229, 141, 142, 169,
+ /* 640 */ 170, 98, 42, 43, 22, 37, 103, 39, 123, 208,
+ /* 650 */ 20, 161, 162, 163, 88, 89, 147, 113, 94, 16,
/* 660 */ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
- /* 670 */ 70, 71, 72, 145, 74, 75, 76, 77, 78, 79,
- /* 680 */ 80, 81, 82, 83, 84, 42, 43, 80, 142, 143,
- /* 690 */ 88, 89, 88, 89, 148, 88, 89, 133, 14, 147,
- /* 700 */ 225, 155, 16, 60, 61, 62, 63, 64, 65, 66,
- /* 710 */ 67, 68, 69, 70, 71, 72, 114, 74, 75, 76,
+ /* 670 */ 70, 71, 72, 147, 74, 75, 76, 77, 78, 79,
+ /* 680 */ 80, 81, 82, 83, 84, 42, 43, 192, 147, 20,
+ /* 690 */ 106, 182, 108, 109, 199, 169, 170, 133, 147, 88,
+ /* 700 */ 89, 155, 16, 60, 61, 62, 63, 64, 65, 66,
+ /* 710 */ 67, 68, 69, 70, 71, 72, 145, 74, 75, 76,
/* 720 */ 77, 78, 79, 80, 81, 82, 83, 84, 42, 43,
- /* 730 */ 201, 202, 147, 147, 182, 189, 52, 147, 54, 147,
- /* 740 */ 147, 147, 147, 147, 155, 16, 60, 61, 62, 63,
+ /* 730 */ 241, 242, 147, 182, 147, 189, 106, 19, 108, 109,
+ /* 740 */ 7, 8, 9, 121, 14, 16, 60, 61, 62, 63,
/* 750 */ 64, 65, 66, 67, 68, 69, 70, 71, 72, 213,
/* 760 */ 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
- /* 770 */ 84, 42, 43, 188, 188, 182, 182, 225, 189, 106,
- /* 780 */ 188, 108, 109, 188, 99, 100, 101, 241, 16, 155,
+ /* 770 */ 84, 42, 43, 188, 147, 106, 230, 108, 109, 124,
+ /* 780 */ 125, 235, 52, 191, 54, 147, 68, 147, 16, 80,
/* 790 */ 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
- /* 800 */ 71, 72, 213, 74, 75, 76, 77, 78, 79, 80,
- /* 810 */ 81, 82, 83, 84, 42, 43, 23, 133, 225, 225,
- /* 820 */ 21, 225, 23, 189, 239, 236, 99, 100, 101, 22,
- /* 830 */ 242, 243, 155, 191, 62, 63, 64, 65, 66, 67,
+ /* 800 */ 71, 72, 23, 74, 75, 76, 77, 78, 79, 80,
+ /* 810 */ 81, 82, 83, 84, 42, 43, 107, 23, 147, 22,
+ /* 820 */ 111, 19, 182, 238, 147, 23, 188, 155, 107, 147,
+ /* 830 */ 147, 147, 111, 231, 62, 63, 64, 65, 66, 67,
/* 840 */ 68, 69, 70, 71, 72, 147, 74, 75, 76, 77,
- /* 850 */ 78, 79, 80, 81, 82, 83, 84, 16, 17, 22,
- /* 860 */ 19, 147, 147, 147, 23, 147, 189, 169, 170, 147,
- /* 870 */ 14, 147, 31, 16, 17, 147, 19, 147, 19, 43,
- /* 880 */ 23, 88, 89, 169, 170, 169, 170, 88, 31, 48,
- /* 890 */ 147, 169, 170, 169, 170, 147, 89, 169, 170, 58,
- /* 900 */ 147, 147, 147, 188, 147, 48, 188, 114, 52, 147,
- /* 910 */ 54, 19, 147, 124, 125, 58, 147, 169, 170, 78,
- /* 920 */ 79, 114, 169, 170, 169, 170, 169, 170, 87, 88,
- /* 930 */ 89, 169, 170, 92, 98, 78, 79, 80, 169, 170,
- /* 940 */ 91, 147, 188, 22, 87, 88, 89, 16, 17, 92,
- /* 950 */ 19, 110, 147, 155, 23, 147, 155, 22, 121, 110,
- /* 960 */ 68, 80, 31, 169, 170, 124, 125, 126, 127, 128,
- /* 970 */ 129, 112, 123, 208, 169, 170, 107, 169, 170, 48,
- /* 980 */ 111, 124, 125, 126, 127, 128, 129, 189, 107, 58,
- /* 990 */ 189, 5, 111, 147, 7, 8, 10, 11, 12, 13,
- /* 1000 */ 161, 20, 147, 22, 178, 147, 91, 92, 147, 78,
- /* 1010 */ 79, 147, 26, 19, 28, 169, 170, 23, 87, 88,
+ /* 850 */ 78, 79, 80, 81, 82, 83, 84, 16, 17, 182,
+ /* 860 */ 19, 189, 147, 133, 23, 161, 43, 88, 89, 147,
+ /* 870 */ 188, 188, 31, 16, 17, 147, 19, 99, 100, 101,
+ /* 880 */ 23, 147, 88, 89, 147, 213, 89, 147, 31, 48,
+ /* 890 */ 88, 169, 170, 114, 147, 19, 212, 169, 170, 58,
+ /* 900 */ 147, 147, 147, 188, 147, 48, 208, 235, 114, 169,
+ /* 910 */ 170, 114, 99, 100, 101, 58, 169, 170, 147, 78,
+ /* 920 */ 79, 98, 169, 170, 169, 170, 169, 170, 87, 88,
+ /* 930 */ 89, 16, 147, 92, 203, 78, 79, 80, 147, 91,
+ /* 940 */ 169, 170, 188, 147, 87, 88, 89, 16, 17, 92,
+ /* 950 */ 19, 110, 147, 155, 23, 147, 155, 27, 110, 155,
+ /* 960 */ 169, 170, 31, 14, 34, 124, 125, 126, 127, 128,
+ /* 970 */ 129, 123, 147, 188, 169, 170, 147, 169, 170, 48,
+ /* 980 */ 147, 124, 125, 126, 127, 128, 129, 189, 112, 58,
+ /* 990 */ 189, 5, 178, 189, 147, 178, 10, 11, 12, 13,
+ /* 1000 */ 178, 52, 147, 54, 19, 147, 21, 92, 147, 78,
+ /* 1010 */ 79, 130, 26, 132, 28, 147, 169, 170, 87, 88,
/* 1020 */ 89, 35, 147, 92, 169, 170, 147, 169, 170, 147,
- /* 1030 */ 169, 170, 97, 47, 113, 49, 20, 203, 22, 53,
- /* 1040 */ 147, 147, 56, 147, 169, 170, 147, 147, 147, 20,
- /* 1050 */ 147, 169, 170, 147, 147, 124, 125, 126, 127, 128,
- /* 1060 */ 129, 147, 169, 170, 178, 169, 170, 147, 169, 170,
- /* 1070 */ 169, 170, 169, 170, 147, 169, 170, 147, 20, 147,
- /* 1080 */ 22, 147, 88, 147, 147, 99, 100, 101, 59, 169,
- /* 1090 */ 170, 105, 147, 20, 147, 22, 110, 178, 147, 169,
- /* 1100 */ 170, 169, 170, 169, 170, 169, 170, 20, 147, 22,
- /* 1110 */ 147, 20, 147, 22, 169, 170, 169, 170, 147, 147,
- /* 1120 */ 134, 20, 147, 22, 20, 147, 22, 147, 20, 232,
- /* 1130 */ 22, 233, 169, 170, 169, 170, 147, 147, 147, 147,
- /* 1140 */ 169, 170, 147, 147, 169, 170, 147, 169, 170, 169,
- /* 1150 */ 170, 147, 147, 147, 147, 147, 147, 191, 161, 149,
- /* 1160 */ 193, 177, 229, 223, 161, 172, 6, 229, 194, 146,
- /* 1170 */ 172, 194, 172, 172, 172, 161, 146, 146, 146, 22,
- /* 1180 */ 154, 121, 194, 118, 173, 119, 116, 120, 112, 130,
- /* 1190 */ 222, 152, 152, 98, 115, 98, 171, 97, 171, 40,
- /* 1200 */ 179, 189, 19, 84, 171, 226, 171, 173, 195, 226,
- /* 1210 */ 174, 196, 171, 171, 197, 171, 198, 179, 15, 174,
- /* 1220 */ 151, 60, 151, 204, 152, 205, 204, 152, 151, 205,
- /* 1230 */ 152, 38, 152, 130, 151, 184, 152, 184, 19, 15,
- /* 1240 */ 194, 152, 187, 187, 187, 152, 194, 184, 187, 33,
- /* 1250 */ 152, 152, 137, 159, 1, 20, 175, 214, 112, 112,
- /* 1260 */ 175, 214, 234, 112, 112, 92, 19, 11, 20, 107,
- /* 1270 */ 20, 19, 235, 19, 32, 20, 112, 114, 20, 22,
- /* 1280 */ 20, 22, 117, 22, 117, 237, 237, 19, 44, 20,
- /* 1290 */ 240, 20, 20, 231, 19, 44, 19, 243, 20, 19,
- /* 1300 */ 19, 19, 96, 103, 16, 21, 44, 17, 98, 36,
- /* 1310 */ 246, 45, 45, 22, 51, 133, 98, 19, 5, 1,
- /* 1320 */ 122, 19, 102, 14, 113, 17, 113, 115, 102, 122,
- /* 1330 */ 19, 123, 68, 68, 20, 14, 57, 135, 19, 3,
- /* 1340 */ 136, 4,
+ /* 1030 */ 169, 170, 20, 47, 22, 49, 147, 169, 170, 53,
+ /* 1040 */ 147, 20, 56, 22, 169, 170, 147, 147, 147, 20,
+ /* 1050 */ 147, 22, 147, 147, 92, 124, 125, 126, 127, 128,
+ /* 1060 */ 129, 147, 169, 170, 232, 103, 147, 147, 169, 170,
+ /* 1070 */ 169, 170, 169, 170, 169, 170, 147, 7, 8, 91,
+ /* 1080 */ 92, 147, 147, 147, 147, 99, 100, 101, 169, 170,
+ /* 1090 */ 147, 105, 147, 20, 147, 22, 110, 147, 169, 170,
+ /* 1100 */ 147, 147, 147, 169, 170, 169, 170, 20, 147, 22,
+ /* 1110 */ 147, 147, 147, 147, 169, 170, 169, 170, 147, 20,
+ /* 1120 */ 134, 22, 169, 170, 169, 170, 147, 20, 147, 147,
+ /* 1130 */ 169, 170, 169, 170, 147, 169, 170, 147, 147, 147,
+ /* 1140 */ 169, 170, 20, 20, 22, 22, 147, 147, 169, 170,
+ /* 1150 */ 149, 169, 170, 20, 161, 22, 169, 170, 191, 169,
+ /* 1160 */ 170, 20, 193, 22, 223, 161, 59, 228, 228, 172,
+ /* 1170 */ 172, 177, 161, 6, 172, 172, 146, 172, 194, 173,
+ /* 1180 */ 146, 146, 146, 194, 22, 154, 121, 118, 116, 194,
+ /* 1190 */ 119, 195, 130, 112, 120, 222, 189, 152, 152, 98,
+ /* 1200 */ 115, 98, 179, 171, 196, 40, 197, 97, 198, 171,
+ /* 1210 */ 171, 19, 171, 173, 84, 15, 174, 204, 171, 205,
+ /* 1220 */ 204, 171, 205, 179, 171, 174, 151, 38, 152, 151,
+ /* 1230 */ 130, 152, 152, 151, 60, 152, 152, 151, 184, 184,
+ /* 1240 */ 225, 19, 214, 225, 152, 15, 194, 187, 187, 152,
+ /* 1250 */ 187, 187, 233, 194, 234, 137, 33, 214, 236, 1,
+ /* 1260 */ 236, 184, 20, 152, 152, 239, 175, 159, 175, 242,
+ /* 1270 */ 245, 112, 112, 112, 92, 112, 107, 20, 19, 11,
+ /* 1280 */ 20, 19, 19, 22, 20, 20, 20, 117, 117, 22,
+ /* 1290 */ 114, 22, 19, 112, 20, 20, 20, 44, 19, 44,
+ /* 1300 */ 19, 19, 32, 20, 19, 19, 96, 103, 16, 21,
+ /* 1310 */ 17, 98, 22, 36, 98, 44, 133, 19, 5, 1,
+ /* 1320 */ 102, 122, 68, 51, 68, 45, 19, 113, 1, 45,
+ /* 1330 */ 14, 17, 115, 113, 102, 123, 19, 122, 14, 20,
+ /* 1340 */ 135, 19, 136, 3, 57, 4,
};
-#define YY_SHIFT_USE_DFLT (-62)
-#define YY_SHIFT_MAX 389
+#define YY_SHIFT_USE_DFLT (-99)
+#define YY_SHIFT_MAX 390
static const short yy_shift_ofst[] = {
- /* 0 */ 39, 841, 986, -16, 841, 931, 931, 258, 123, -36,
- /* 10 */ 96, 931, 931, 931, 931, 931, -45, 400, 174, 19,
- /* 20 */ 132, -54, -54, 53, 165, 208, 251, 324, 393, 462,
- /* 30 */ 531, 600, 643, 686, 643, 643, 643, 643, 643, 643,
+ /* 0 */ 23, 841, 986, -16, 841, 931, 931, 258, 402, 384,
+ /* 10 */ -98, 96, 931, 931, 931, 931, 931, -45, 468, 19,
+ /* 20 */ 419, -17, -38, -38, 53, 165, 208, 251, 324, 393,
+ /* 30 */ 462, 531, 600, 643, 686, 643, 643, 643, 643, 643,
/* 40 */ 643, 643, 643, 643, 643, 643, 643, 643, 643, 643,
- /* 50 */ 643, 643, 729, 772, 772, 857, 931, 931, 931, 931,
+ /* 50 */ 643, 643, 643, 729, 772, 772, 857, 931, 931, 931,
/* 60 */ 931, 931, 931, 931, 931, 931, 931, 931, 931, 931,
/* 70 */ 931, 931, 931, 931, 931, 931, 931, 931, 931, 931,
/* 80 */ 931, 931, 931, 931, 931, 931, 931, 931, 931, 931,
- /* 90 */ 931, 931, 931, 931, 931, 931, -61, -61, 6, 6,
- /* 100 */ 280, 22, 61, 399, 564, 19, 19, 19, 19, 19,
- /* 110 */ 19, 19, 216, 132, 63, -62, -62, -62, 131, 326,
- /* 120 */ 472, 472, 498, 559, 506, 799, 19, 799, 19, 19,
- /* 130 */ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- /* 140 */ 19, 849, 59, -36, -36, -36, -62, -62, -62, -15,
- /* 150 */ -15, 333, 459, 478, 557, 530, 541, 616, 602, 793,
- /* 160 */ 604, 607, 626, 19, 19, 881, 19, 19, 994, 19,
- /* 170 */ 19, 807, 19, 19, 673, 807, 19, 19, 384, 384,
- /* 180 */ 384, 19, 19, 673, 19, 19, 673, 19, 454, 685,
- /* 190 */ 19, 19, 673, 19, 19, 19, 673, 19, 19, 19,
- /* 200 */ 673, 673, 19, 19, 19, 19, 19, 468, 869, 921,
- /* 210 */ 132, 789, 789, 432, 406, 406, 406, 836, 406, 132,
- /* 220 */ 406, 132, 935, 837, 837, 1160, 1160, 1160, 1160, 1157,
- /* 230 */ -36, 1060, 1065, 1066, 1070, 1067, 1059, 1076, 1076, 1095,
- /* 240 */ 1079, 1095, 1079, 1097, 1097, 1159, 1097, 1100, 1097, 1183,
- /* 250 */ 1119, 1119, 1159, 1097, 1097, 1097, 1183, 1203, 1076, 1203,
- /* 260 */ 1076, 1203, 1076, 1076, 1193, 1103, 1203, 1076, 1161, 1161,
- /* 270 */ 1219, 1060, 1076, 1224, 1224, 1224, 1224, 1060, 1161, 1219,
- /* 280 */ 1076, 1216, 1216, 1076, 1076, 1115, -62, -62, -62, -62,
- /* 290 */ -62, -62, 525, 684, 727, 856, 859, 556, 555, 981,
- /* 300 */ 102, 987, 915, 1016, 1058, 1073, 1087, 1091, 1101, 1104,
- /* 310 */ 892, 1108, 1029, 1253, 1235, 1146, 1147, 1151, 1152, 1173,
- /* 320 */ 1162, 1247, 1248, 1250, 1252, 1256, 1254, 1255, 1257, 1258,
- /* 330 */ 1260, 1259, 1165, 1261, 1167, 1259, 1163, 1268, 1269, 1164,
- /* 340 */ 1271, 1272, 1242, 1244, 1275, 1251, 1277, 1278, 1280, 1281,
- /* 350 */ 1262, 1282, 1206, 1200, 1288, 1290, 1284, 1210, 1273, 1263,
- /* 360 */ 1266, 1291, 1267, 1182, 1218, 1298, 1313, 1318, 1220, 1264,
- /* 370 */ 1265, 1198, 1302, 1211, 1309, 1212, 1308, 1213, 1226, 1207,
- /* 380 */ 1311, 1208, 1314, 1321, 1279, 1202, 1204, 1319, 1336, 1337,
+ /* 90 */ 931, 931, 931, 931, 931, 931, 931, -61, -61, 6,
+ /* 100 */ 6, 280, 22, 178, 543, 564, 419, 419, 68, -17,
+ /* 110 */ -10, -99, -99, -99, 131, 326, 538, 538, 392, 430,
+ /* 120 */ 623, 124, 419, 124, 419, 419, 419, 419, 419, 419,
+ /* 130 */ 419, 419, 419, 419, 419, 419, 419, 419, 419, 419,
+ /* 140 */ 419, 848, 525, -98, -98, -98, -99, -99, -99, -15,
+ /* 150 */ -15, 333, 205, 584, 566, 630, 669, 608, 779, 794,
+ /* 160 */ 611, 422, 733, 419, 419, 709, 419, 419, 802, 419,
+ /* 170 */ 419, 797, 419, 419, 248, 797, 419, 419, 313, 313,
+ /* 180 */ 313, 419, 419, 419, 248, 419, 419, 248, 419, 159,
+ /* 190 */ 778, 419, 419, 248, 419, 419, 419, 248, 419, 419,
+ /* 200 */ 419, 248, 248, 419, 419, 419, 419, 419, 985, 721,
+ /* 210 */ 544, -17, 655, 655, 881, 930, 930, 930, 823, 930,
+ /* 220 */ -17, 930, -17, 72, 622, 622, 1167, 1167, 1167, 1167,
+ /* 230 */ 1162, -98, 1065, 1069, 1071, 1072, 1074, 1062, 1081, 1081,
+ /* 240 */ 1101, 1085, 1101, 1085, 1103, 1103, 1165, 1103, 1110, 1103,
+ /* 250 */ 1192, 1130, 1130, 1165, 1103, 1103, 1103, 1192, 1200, 1081,
+ /* 260 */ 1200, 1081, 1200, 1081, 1081, 1189, 1100, 1200, 1081, 1174,
+ /* 270 */ 1174, 1222, 1065, 1081, 1230, 1230, 1230, 1230, 1065, 1174,
+ /* 280 */ 1222, 1081, 1223, 1223, 1081, 1081, 1118, -99, -99, -99,
+ /* 290 */ -99, -99, 456, 730, 813, 949, 876, 915, 1012, 1021,
+ /* 300 */ 962, 1070, 988, 1029, 1073, 1087, 1099, 1122, 1123, 1133,
+ /* 310 */ 718, 1141, 1107, 1258, 1242, 1159, 1160, 1161, 1163, 1182,
+ /* 320 */ 1169, 1259, 1257, 1260, 1262, 1268, 1263, 1264, 1261, 1265,
+ /* 330 */ 1266, 1267, 1170, 1269, 1171, 1267, 1176, 1273, 1274, 1181,
+ /* 340 */ 1275, 1276, 1270, 1253, 1279, 1255, 1281, 1283, 1282, 1285,
+ /* 350 */ 1271, 1286, 1210, 1204, 1292, 1293, 1288, 1213, 1277, 1272,
+ /* 360 */ 1280, 1290, 1284, 1183, 1216, 1298, 1313, 1318, 1218, 1254,
+ /* 370 */ 1256, 1199, 1307, 1214, 1327, 1316, 1217, 1314, 1220, 1232,
+ /* 380 */ 1215, 1317, 1212, 1319, 1324, 1287, 1205, 1206, 1322, 1340,
+ /* 390 */ 1341,
};
-#define YY_REDUCE_USE_DFLT (-165)
+#define YY_REDUCE_USE_DFLT (-142)
#define YY_REDUCE_MAX 291
static const short yy_reduce_ofst[] = {
- /* 0 */ -138, 277, 546, 137, 401, -21, 44, 36, 38, 242,
- /* 10 */ -141, 191, 91, 269, 343, 345, -126, 589, 338, 150,
- /* 20 */ 147, -13, 213, 412, 412, 412, 412, 412, 412, 412,
- /* 30 */ 412, 412, 412, 412, 412, 412, 412, 412, 412, 412,
- /* 40 */ 412, 412, 412, 412, 412, 412, 412, 412, 412, 412,
- /* 50 */ 412, 412, 412, 412, 412, 211, 698, 714, 716, 722,
- /* 60 */ 724, 728, 748, 753, 755, 757, 762, 769, 794, 805,
- /* 70 */ 808, 846, 855, 858, 861, 875, 882, 893, 896, 899,
- /* 80 */ 901, 903, 906, 920, 930, 932, 934, 936, 945, 947,
- /* 90 */ 963, 965, 971, 975, 978, 980, 412, 412, 412, 412,
- /* 100 */ 20, 412, 412, 23, 34, 334, 475, 552, 593, 594,
- /* 110 */ 585, 212, 412, 289, 412, 412, 412, 412, 135, -164,
- /* 120 */ -115, 164, 407, 407, 350, 141, 51, 163, 596, -90,
- /* 130 */ 436, 218, 765, 438, 586, 592, 595, 715, 718, 408,
- /* 140 */ 754, 380, 634, 677, 798, 801, 144, 529, 588, 49,
- /* 150 */ 176, 244, 264, 329, 457, 329, 329, 451, 477, 494,
- /* 160 */ 507, 509, 528, 590, 730, 642, 509, 743, 839, 864,
- /* 170 */ 879, 834, 894, 900, 329, 834, 907, 914, 826, 886,
- /* 180 */ 919, 927, 937, 329, 951, 961, 329, 972, 897, 898,
- /* 190 */ 989, 990, 329, 991, 992, 995, 329, 996, 999, 1004,
- /* 200 */ 329, 329, 1005, 1006, 1007, 1008, 1009, 1010, 966, 967,
- /* 210 */ 997, 933, 938, 940, 993, 998, 1000, 984, 1001, 1003,
- /* 220 */ 1002, 1014, 1011, 974, 977, 1023, 1030, 1031, 1032, 1026,
- /* 230 */ 1012, 988, 1013, 1015, 1017, 1018, 968, 1039, 1040, 1019,
- /* 240 */ 1020, 1022, 1024, 1025, 1027, 1021, 1033, 1034, 1035, 1036,
- /* 250 */ 979, 983, 1038, 1041, 1042, 1044, 1045, 1069, 1072, 1071,
- /* 260 */ 1075, 1077, 1078, 1080, 1028, 1037, 1083, 1084, 1051, 1053,
- /* 270 */ 1043, 1046, 1089, 1055, 1056, 1057, 1061, 1052, 1063, 1047,
- /* 280 */ 1093, 1048, 1049, 1098, 1099, 1050, 1094, 1081, 1085, 1062,
- /* 290 */ 1054, 1064,
+ /* 0 */ -138, 277, -2, -18, 190, -21, 44, 36, 38, 546,
+ /* 10 */ 242, 87, -77, 345, 411, 51, 414, 221, 672, 269,
+ /* 20 */ 356, 391, 399, 406, -141, -141, -141, -141, -141, -141,
+ /* 30 */ -141, -141, -141, -141, -141, -141, -141, -141, -141, -141,
+ /* 40 */ -141, -141, -141, -141, -141, -141, -141, -141, -141, -141,
+ /* 50 */ -141, -141, -141, -141, -141, -141, 211, 470, 526, 722,
+ /* 60 */ 728, 740, 747, 753, 755, 757, 771, 791, 805, 808,
+ /* 70 */ 847, 855, 858, 861, 868, 875, 893, 899, 901, 903,
+ /* 80 */ 905, 919, 929, 934, 936, 945, 947, 953, 955, 961,
+ /* 90 */ 963, 966, 971, 979, 982, 987, 990, -141, -141, -141,
+ /* 100 */ -141, 29, -141, -141, 125, 407, 585, 471, -141, 490,
+ /* 110 */ -141, -141, -141, -141, 46, 129, 164, 183, 134, 134,
+ /* 120 */ 496, -6, 371, 360, 156, 509, 551, 640, -90, 441,
+ /* 130 */ 677, 218, 698, 388, 638, 682, 683, 715, 754, 684,
+ /* 140 */ 785, 63, 401, 798, 801, 804, 495, 285, 489, -78,
+ /* 150 */ 94, 41, 155, 120, 194, 120, 120, 175, 350, 359,
+ /* 160 */ 369, 541, 571, 587, 627, 592, 541, 671, 704, 734,
+ /* 170 */ 737, 731, 796, 825, 120, 731, 829, 833, 814, 817,
+ /* 180 */ 822, 879, 882, 889, 120, 900, 906, 120, 914, 602,
+ /* 190 */ 832, 920, 935, 120, 937, 943, 950, 120, 954, 964,
+ /* 200 */ 965, 120, 120, 981, 991, 992, 999, 1000, 1001, 967,
+ /* 210 */ 969, 993, 939, 940, 941, 997, 998, 1002, 994, 1003,
+ /* 220 */ 1004, 1005, 1011, 1006, 984, 989, 1030, 1034, 1035, 1036,
+ /* 230 */ 1031, 1007, 995, 996, 1008, 1009, 1010, 973, 1045, 1046,
+ /* 240 */ 1013, 1014, 1016, 1017, 1032, 1038, 1023, 1039, 1040, 1041,
+ /* 250 */ 1042, 1015, 1018, 1044, 1047, 1050, 1053, 1051, 1075, 1076,
+ /* 260 */ 1078, 1079, 1082, 1080, 1083, 1019, 1020, 1086, 1084, 1054,
+ /* 270 */ 1055, 1028, 1052, 1092, 1060, 1061, 1063, 1064, 1059, 1077,
+ /* 280 */ 1043, 1097, 1022, 1024, 1111, 1112, 1026, 1108, 1091, 1093,
+ /* 290 */ 1027, 1025,
};
static const YYACTIONTYPE yy_default[] = {
- /* 0 */ 595, 820, 902, 710, 902, 820, 902, 902, 848, 714,
- /* 10 */ 877, 818, 902, 902, 902, 902, 792, 902, 848, 902,
- /* 20 */ 626, 848, 848, 743, 902, 902, 902, 902, 902, 902,
- /* 30 */ 902, 902, 744, 902, 822, 817, 813, 815, 814, 821,
- /* 40 */ 745, 734, 741, 748, 726, 861, 750, 751, 757, 758,
- /* 50 */ 878, 876, 780, 779, 798, 902, 902, 902, 902, 902,
+ /* 0 */ 595, 821, 902, 711, 902, 821, 902, 902, 848, 902,
+ /* 10 */ 715, 877, 819, 902, 902, 902, 902, 793, 902, 848,
+ /* 20 */ 902, 627, 848, 848, 744, 902, 902, 902, 902, 902,
+ /* 30 */ 902, 902, 902, 745, 902, 823, 818, 814, 816, 815,
+ /* 40 */ 822, 746, 735, 742, 749, 727, 861, 751, 752, 758,
+ /* 50 */ 759, 878, 876, 781, 780, 799, 902, 902, 902, 902,
/* 60 */ 902, 902, 902, 902, 902, 902, 902, 902, 902, 902,
/* 70 */ 902, 902, 902, 902, 902, 902, 902, 902, 902, 902,
/* 80 */ 902, 902, 902, 902, 902, 902, 902, 902, 902, 902,
- /* 90 */ 902, 902, 902, 902, 902, 902, 782, 804, 781, 791,
- /* 100 */ 619, 783, 784, 679, 614, 902, 902, 902, 902, 902,
- /* 110 */ 902, 902, 785, 902, 786, 799, 800, 801, 902, 902,
- /* 120 */ 902, 902, 902, 902, 595, 710, 902, 710, 902, 902,
+ /* 90 */ 902, 902, 902, 902, 902, 902, 902, 783, 805, 782,
+ /* 100 */ 792, 620, 784, 785, 680, 615, 902, 902, 786, 902,
+ /* 110 */ 787, 800, 801, 802, 902, 902, 902, 902, 902, 902,
+ /* 120 */ 595, 711, 902, 711, 902, 902, 902, 902, 902, 902,
/* 130 */ 902, 902, 902, 902, 902, 902, 902, 902, 902, 902,
- /* 140 */ 902, 902, 902, 902, 902, 902, 704, 714, 895, 902,
- /* 150 */ 902, 670, 902, 902, 902, 902, 902, 902, 902, 902,
- /* 160 */ 902, 902, 602, 600, 902, 702, 902, 902, 628, 902,
- /* 170 */ 902, 712, 902, 902, 717, 718, 902, 902, 902, 902,
- /* 180 */ 902, 902, 902, 616, 902, 902, 691, 902, 854, 902,
- /* 190 */ 902, 902, 868, 902, 902, 902, 866, 902, 902, 902,
- /* 200 */ 693, 753, 834, 902, 881, 883, 902, 902, 702, 711,
- /* 210 */ 902, 902, 902, 816, 737, 737, 737, 649, 737, 902,
- /* 220 */ 737, 902, 652, 747, 747, 599, 599, 599, 599, 669,
- /* 230 */ 902, 747, 738, 740, 730, 742, 902, 719, 719, 727,
- /* 240 */ 729, 727, 729, 681, 681, 666, 681, 652, 681, 826,
- /* 250 */ 831, 831, 666, 681, 681, 681, 826, 611, 719, 611,
- /* 260 */ 719, 611, 719, 719, 858, 860, 611, 719, 683, 683,
- /* 270 */ 759, 747, 719, 690, 690, 690, 690, 747, 683, 759,
- /* 280 */ 719, 880, 880, 719, 719, 888, 636, 654, 654, 863,
- /* 290 */ 895, 900, 902, 902, 902, 902, 766, 902, 902, 902,
+ /* 140 */ 902, 902, 902, 902, 902, 902, 705, 715, 895, 902,
+ /* 150 */ 902, 671, 902, 902, 902, 902, 902, 902, 902, 902,
+ /* 160 */ 902, 902, 603, 601, 902, 703, 902, 902, 629, 902,
+ /* 170 */ 902, 713, 902, 902, 718, 719, 902, 902, 902, 902,
+ /* 180 */ 902, 902, 902, 902, 617, 902, 902, 692, 902, 854,
+ /* 190 */ 902, 902, 902, 868, 902, 902, 902, 866, 902, 902,
+ /* 200 */ 902, 694, 754, 834, 902, 881, 883, 902, 902, 703,
+ /* 210 */ 712, 902, 902, 902, 817, 738, 738, 738, 650, 738,
+ /* 220 */ 902, 738, 902, 653, 748, 748, 600, 600, 600, 600,
+ /* 230 */ 670, 902, 748, 739, 741, 731, 743, 902, 720, 720,
+ /* 240 */ 728, 730, 728, 730, 682, 682, 667, 682, 653, 682,
+ /* 250 */ 827, 831, 831, 667, 682, 682, 682, 827, 612, 720,
+ /* 260 */ 612, 720, 612, 720, 720, 858, 860, 612, 720, 684,
+ /* 270 */ 684, 760, 748, 720, 691, 691, 691, 691, 748, 684,
+ /* 280 */ 760, 720, 880, 880, 720, 720, 888, 637, 655, 655,
+ /* 290 */ 895, 900, 902, 902, 902, 902, 767, 902, 902, 902,
/* 300 */ 902, 902, 902, 902, 902, 902, 902, 902, 902, 902,
- /* 310 */ 841, 902, 902, 902, 902, 771, 767, 902, 768, 902,
- /* 320 */ 696, 902, 902, 902, 902, 902, 902, 902, 902, 902,
- /* 330 */ 902, 819, 902, 731, 902, 739, 902, 902, 902, 902,
+ /* 310 */ 841, 902, 902, 902, 902, 772, 768, 902, 769, 902,
+ /* 320 */ 697, 902, 902, 902, 902, 902, 902, 902, 902, 902,
+ /* 330 */ 902, 820, 902, 732, 902, 740, 902, 902, 902, 902,
/* 340 */ 902, 902, 902, 902, 902, 902, 902, 902, 902, 902,
/* 350 */ 902, 902, 902, 902, 902, 902, 902, 902, 902, 902,
/* 360 */ 856, 857, 902, 902, 902, 902, 902, 902, 902, 902,
/* 370 */ 902, 902, 902, 902, 902, 902, 902, 902, 902, 902,
- /* 380 */ 902, 902, 902, 902, 887, 902, 902, 890, 596, 902,
- /* 390 */ 590, 593, 592, 594, 598, 601, 623, 624, 625, 603,
- /* 400 */ 604, 605, 606, 607, 608, 609, 615, 617, 635, 637,
- /* 410 */ 621, 639, 700, 701, 763, 694, 695, 699, 622, 774,
- /* 420 */ 765, 769, 770, 772, 773, 787, 788, 790, 796, 803,
- /* 430 */ 806, 789, 794, 795, 797, 802, 805, 697, 698, 809,
- /* 440 */ 629, 630, 633, 634, 844, 846, 845, 847, 632, 631,
- /* 450 */ 775, 778, 811, 812, 869, 870, 871, 872, 873, 807,
- /* 460 */ 720, 810, 793, 732, 735, 736, 733, 703, 713, 722,
- /* 470 */ 723, 724, 725, 708, 709, 715, 728, 761, 762, 716,
- /* 480 */ 705, 706, 707, 808, 764, 776, 777, 640, 641, 771,
- /* 490 */ 642, 643, 644, 682, 685, 686, 687, 645, 664, 667,
- /* 500 */ 668, 646, 653, 647, 648, 655, 656, 657, 660, 661,
- /* 510 */ 662, 663, 658, 659, 827, 828, 832, 830, 829, 650,
- /* 520 */ 651, 665, 638, 627, 620, 671, 674, 675, 676, 677,
- /* 530 */ 678, 680, 672, 673, 618, 610, 612, 721, 850, 859,
- /* 540 */ 855, 851, 852, 853, 613, 823, 824, 684, 755, 756,
- /* 550 */ 849, 862, 864, 760, 865, 867, 892, 688, 689, 692,
- /* 560 */ 833, 874, 746, 749, 752, 754, 835, 836, 837, 838,
- /* 570 */ 839, 842, 843, 840, 875, 879, 882, 884, 885, 886,
- /* 580 */ 889, 891, 896, 897, 898, 901, 899, 597, 591,
+ /* 380 */ 902, 902, 902, 902, 902, 887, 902, 902, 890, 596,
+ /* 390 */ 902, 591, 593, 594, 598, 599, 602, 624, 625, 626,
+ /* 400 */ 604, 605, 606, 607, 608, 609, 610, 616, 618, 636,
+ /* 410 */ 638, 622, 640, 701, 702, 764, 695, 696, 700, 623,
+ /* 420 */ 775, 766, 770, 771, 773, 774, 788, 789, 791, 797,
+ /* 430 */ 804, 807, 790, 795, 796, 798, 803, 806, 698, 699,
+ /* 440 */ 810, 630, 631, 634, 635, 844, 846, 845, 847, 633,
+ /* 450 */ 632, 776, 779, 812, 813, 869, 870, 871, 872, 873,
+ /* 460 */ 808, 721, 811, 794, 733, 736, 737, 734, 704, 714,
+ /* 470 */ 723, 724, 725, 726, 709, 710, 716, 729, 762, 763,
+ /* 480 */ 717, 706, 707, 708, 809, 765, 777, 778, 641, 642,
+ /* 490 */ 772, 643, 644, 645, 683, 686, 687, 688, 646, 665,
+ /* 500 */ 668, 669, 647, 654, 648, 649, 656, 657, 658, 661,
+ /* 510 */ 662, 663, 664, 659, 660, 828, 829, 832, 830, 651,
+ /* 520 */ 652, 666, 639, 628, 621, 672, 675, 676, 677, 678,
+ /* 530 */ 679, 681, 673, 674, 619, 611, 613, 722, 850, 859,
+ /* 540 */ 855, 851, 852, 853, 614, 824, 825, 685, 756, 757,
+ /* 550 */ 849, 862, 864, 761, 865, 867, 863, 892, 689, 690,
+ /* 560 */ 693, 833, 874, 747, 750, 753, 755, 835, 836, 837,
+ /* 570 */ 838, 839, 842, 843, 840, 875, 879, 882, 884, 885,
+ /* 580 */ 886, 889, 891, 896, 897, 898, 901, 899, 597, 592,
};
#define YY_SZ_ACTTAB (int)(sizeof(yy_action)/sizeof(yy_action[0]))
@@ -77688,8 +78908,8 @@
"VARIABLE", "CASE", "WHEN", "THEN",
"ELSE", "INDEX", "ALTER", "TO",
"ADD", "COLUMNKW", "error", "input",
- "cmdlist", "ecmd", "cmdx", "cmd",
- "explain", "transtype", "trans_opt", "nm",
+ "cmdlist", "ecmd", "explain", "cmdx",
+ "cmd", "transtype", "trans_opt", "nm",
"create_table", "create_table_args", "temp", "ifnotexists",
"dbnm", "columnlist", "conslist_opt", "select",
"column", "columnid", "type", "carglist",
@@ -77709,12 +78929,12 @@
"setlist", "insert_cmd", "inscollist_opt", "itemlist",
"exprlist", "likeop", "escape", "between_op",
"in_op", "case_operand", "case_exprlist", "case_else",
- "uniqueflag", "idxitem", "collate", "nmnum",
- "plus_opt", "number", "trigger_decl", "trigger_cmd_list",
- "trigger_time", "trigger_event", "foreach_clause", "when_clause",
- "trigger_cmd", "database_kw_opt", "key_opt", "add_column_fullname",
- "kwcolumn_opt", "create_vtab", "vtabarglist", "vtabarg",
- "vtabargtoken", "lp", "anylist",
+ "uniqueflag", "collate", "nmnum", "plus_opt",
+ "number", "trigger_decl", "trigger_cmd_list", "trigger_time",
+ "trigger_event", "foreach_clause", "when_clause", "trigger_cmd",
+ "database_kw_opt", "key_opt", "add_column_fullname", "kwcolumn_opt",
+ "create_vtab", "vtabarglist", "vtabarg", "vtabargtoken",
+ "lp", "anylist",
};
#endif /* NDEBUG */
@@ -77725,12 +78945,12 @@
/* 0 */ "input ::= cmdlist",
/* 1 */ "cmdlist ::= cmdlist ecmd",
/* 2 */ "cmdlist ::= ecmd",
- /* 3 */ "cmdx ::= cmd",
- /* 4 */ "ecmd ::= SEMI",
- /* 5 */ "ecmd ::= explain cmdx SEMI",
- /* 6 */ "explain ::=",
- /* 7 */ "explain ::= EXPLAIN",
- /* 8 */ "explain ::= EXPLAIN QUERY PLAN",
+ /* 3 */ "ecmd ::= SEMI",
+ /* 4 */ "ecmd ::= explain cmdx SEMI",
+ /* 5 */ "explain ::=",
+ /* 6 */ "explain ::= EXPLAIN",
+ /* 7 */ "explain ::= EXPLAIN QUERY PLAN",
+ /* 8 */ "cmdx ::= cmd",
/* 9 */ "cmd ::= BEGIN transtype trans_opt",
/* 10 */ "trans_opt ::=",
/* 11 */ "trans_opt ::= TRANSACTION",
@@ -77961,80 +79181,79 @@
/* 236 */ "uniqueflag ::=",
/* 237 */ "idxlist_opt ::=",
/* 238 */ "idxlist_opt ::= LP idxlist RP",
- /* 239 */ "idxlist ::= idxlist COMMA idxitem collate sortorder",
- /* 240 */ "idxlist ::= idxitem collate sortorder",
- /* 241 */ "idxitem ::= nm",
- /* 242 */ "collate ::=",
- /* 243 */ "collate ::= COLLATE ids",
- /* 244 */ "cmd ::= DROP INDEX ifexists fullname",
- /* 245 */ "cmd ::= VACUUM",
- /* 246 */ "cmd ::= VACUUM nm",
- /* 247 */ "cmd ::= PRAGMA nm dbnm EQ nmnum",
- /* 248 */ "cmd ::= PRAGMA nm dbnm EQ ON",
- /* 249 */ "cmd ::= PRAGMA nm dbnm EQ DELETE",
- /* 250 */ "cmd ::= PRAGMA nm dbnm EQ minus_num",
- /* 251 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP",
- /* 252 */ "cmd ::= PRAGMA nm dbnm",
- /* 253 */ "nmnum ::= plus_num",
- /* 254 */ "nmnum ::= nm",
- /* 255 */ "plus_num ::= plus_opt number",
- /* 256 */ "minus_num ::= MINUS number",
- /* 257 */ "number ::= INTEGER|FLOAT",
- /* 258 */ "plus_opt ::= PLUS",
- /* 259 */ "plus_opt ::=",
- /* 260 */ "cmd ::= CREATE trigger_decl BEGIN trigger_cmd_list END",
- /* 261 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause",
- /* 262 */ "trigger_time ::= BEFORE",
- /* 263 */ "trigger_time ::= AFTER",
- /* 264 */ "trigger_time ::= INSTEAD OF",
- /* 265 */ "trigger_time ::=",
- /* 266 */ "trigger_event ::= DELETE|INSERT",
- /* 267 */ "trigger_event ::= UPDATE",
- /* 268 */ "trigger_event ::= UPDATE OF inscollist",
- /* 269 */ "foreach_clause ::=",
- /* 270 */ "foreach_clause ::= FOR EACH ROW",
- /* 271 */ "when_clause ::=",
- /* 272 */ "when_clause ::= WHEN expr",
- /* 273 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI",
- /* 274 */ "trigger_cmd_list ::=",
- /* 275 */ "trigger_cmd ::= UPDATE orconf nm SET setlist where_opt",
- /* 276 */ "trigger_cmd ::= insert_cmd INTO nm inscollist_opt VALUES LP itemlist RP",
- /* 277 */ "trigger_cmd ::= insert_cmd INTO nm inscollist_opt select",
- /* 278 */ "trigger_cmd ::= DELETE FROM nm where_opt",
- /* 279 */ "trigger_cmd ::= select",
- /* 280 */ "expr ::= RAISE LP IGNORE RP",
- /* 281 */ "expr ::= RAISE LP raisetype COMMA nm RP",
- /* 282 */ "raisetype ::= ROLLBACK",
- /* 283 */ "raisetype ::= ABORT",
- /* 284 */ "raisetype ::= FAIL",
- /* 285 */ "cmd ::= DROP TRIGGER ifexists fullname",
- /* 286 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt",
- /* 287 */ "cmd ::= DETACH database_kw_opt expr",
- /* 288 */ "key_opt ::=",
- /* 289 */ "key_opt ::= KEY expr",
- /* 290 */ "database_kw_opt ::= DATABASE",
- /* 291 */ "database_kw_opt ::=",
- /* 292 */ "cmd ::= REINDEX",
- /* 293 */ "cmd ::= REINDEX nm dbnm",
- /* 294 */ "cmd ::= ANALYZE",
- /* 295 */ "cmd ::= ANALYZE nm dbnm",
- /* 296 */ "cmd ::= ALTER TABLE fullname RENAME TO nm",
- /* 297 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column",
- /* 298 */ "add_column_fullname ::= fullname",
- /* 299 */ "kwcolumn_opt ::=",
- /* 300 */ "kwcolumn_opt ::= COLUMNKW",
- /* 301 */ "cmd ::= create_vtab",
- /* 302 */ "cmd ::= create_vtab LP vtabarglist RP",
- /* 303 */ "create_vtab ::= CREATE VIRTUAL TABLE nm dbnm USING nm",
- /* 304 */ "vtabarglist ::= vtabarg",
- /* 305 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
- /* 306 */ "vtabarg ::=",
- /* 307 */ "vtabarg ::= vtabarg vtabargtoken",
- /* 308 */ "vtabargtoken ::= ANY",
- /* 309 */ "vtabargtoken ::= lp anylist RP",
- /* 310 */ "lp ::= LP",
- /* 311 */ "anylist ::=",
- /* 312 */ "anylist ::= anylist ANY",
+ /* 239 */ "idxlist ::= idxlist COMMA nm collate sortorder",
+ /* 240 */ "idxlist ::= nm collate sortorder",
+ /* 241 */ "collate ::=",
+ /* 242 */ "collate ::= COLLATE ids",
+ /* 243 */ "cmd ::= DROP INDEX ifexists fullname",
+ /* 244 */ "cmd ::= VACUUM",
+ /* 245 */ "cmd ::= VACUUM nm",
+ /* 246 */ "cmd ::= PRAGMA nm dbnm EQ nmnum",
+ /* 247 */ "cmd ::= PRAGMA nm dbnm EQ ON",
+ /* 248 */ "cmd ::= PRAGMA nm dbnm EQ DELETE",
+ /* 249 */ "cmd ::= PRAGMA nm dbnm EQ minus_num",
+ /* 250 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP",
+ /* 251 */ "cmd ::= PRAGMA nm dbnm",
+ /* 252 */ "nmnum ::= plus_num",
+ /* 253 */ "nmnum ::= nm",
+ /* 254 */ "plus_num ::= plus_opt number",
+ /* 255 */ "minus_num ::= MINUS number",
+ /* 256 */ "number ::= INTEGER|FLOAT",
+ /* 257 */ "plus_opt ::= PLUS",
+ /* 258 */ "plus_opt ::=",
+ /* 259 */ "cmd ::= CREATE trigger_decl BEGIN trigger_cmd_list END",
+ /* 260 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause",
+ /* 261 */ "trigger_time ::= BEFORE",
+ /* 262 */ "trigger_time ::= AFTER",
+ /* 263 */ "trigger_time ::= INSTEAD OF",
+ /* 264 */ "trigger_time ::=",
+ /* 265 */ "trigger_event ::= DELETE|INSERT",
+ /* 266 */ "trigger_event ::= UPDATE",
+ /* 267 */ "trigger_event ::= UPDATE OF inscollist",
+ /* 268 */ "foreach_clause ::=",
+ /* 269 */ "foreach_clause ::= FOR EACH ROW",
+ /* 270 */ "when_clause ::=",
+ /* 271 */ "when_clause ::= WHEN expr",
+ /* 272 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI",
+ /* 273 */ "trigger_cmd_list ::= trigger_cmd SEMI",
+ /* 274 */ "trigger_cmd ::= UPDATE orconf nm SET setlist where_opt",
+ /* 275 */ "trigger_cmd ::= insert_cmd INTO nm inscollist_opt VALUES LP itemlist RP",
+ /* 276 */ "trigger_cmd ::= insert_cmd INTO nm inscollist_opt select",
+ /* 277 */ "trigger_cmd ::= DELETE FROM nm where_opt",
+ /* 278 */ "trigger_cmd ::= select",
+ /* 279 */ "expr ::= RAISE LP IGNORE RP",
+ /* 280 */ "expr ::= RAISE LP raisetype COMMA nm RP",
+ /* 281 */ "raisetype ::= ROLLBACK",
+ /* 282 */ "raisetype ::= ABORT",
+ /* 283 */ "raisetype ::= FAIL",
+ /* 284 */ "cmd ::= DROP TRIGGER ifexists fullname",
+ /* 285 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt",
+ /* 286 */ "cmd ::= DETACH database_kw_opt expr",
+ /* 287 */ "key_opt ::=",
+ /* 288 */ "key_opt ::= KEY expr",
+ /* 289 */ "database_kw_opt ::= DATABASE",
+ /* 290 */ "database_kw_opt ::=",
+ /* 291 */ "cmd ::= REINDEX",
+ /* 292 */ "cmd ::= REINDEX nm dbnm",
+ /* 293 */ "cmd ::= ANALYZE",
+ /* 294 */ "cmd ::= ANALYZE nm dbnm",
+ /* 295 */ "cmd ::= ALTER TABLE fullname RENAME TO nm",
+ /* 296 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column",
+ /* 297 */ "add_column_fullname ::= fullname",
+ /* 298 */ "kwcolumn_opt ::=",
+ /* 299 */ "kwcolumn_opt ::= COLUMNKW",
+ /* 300 */ "cmd ::= create_vtab",
+ /* 301 */ "cmd ::= create_vtab LP vtabarglist RP",
+ /* 302 */ "create_vtab ::= CREATE VIRTUAL TABLE nm dbnm USING nm",
+ /* 303 */ "vtabarglist ::= vtabarg",
+ /* 304 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
+ /* 305 */ "vtabarg ::=",
+ /* 306 */ "vtabarg ::= vtabarg vtabargtoken",
+ /* 307 */ "vtabargtoken ::= ANY",
+ /* 308 */ "vtabargtoken ::= lp anylist RP",
+ /* 309 */ "lp ::= LP",
+ /* 310 */ "anylist ::=",
+ /* 311 */ "anylist ::= anylist ANY",
};
#endif /* NDEBUG */
@@ -78115,7 +79334,7 @@
case 189: /* oneselect */
case 206: /* seltablist_paren */
{
-sqlite3SelectDelete(pParse->db, (yypminor->yy219));
+sqlite3SelectDelete(pParse->db, (yypminor->yy375));
}
break;
case 169: /* term */
@@ -78127,10 +79346,10 @@
case 218: /* escape */
case 221: /* case_operand */
case 223: /* case_else */
- case 235: /* when_clause */
- case 238: /* key_opt */
+ case 234: /* when_clause */
+ case 237: /* key_opt */
{
-sqlite3ExprDelete(pParse->db, (yypminor->yy172));
+sqlite3ExprDelete(pParse->db, (yypminor->yy62));
}
break;
case 174: /* idxlist_opt */
@@ -78146,7 +79365,7 @@
case 216: /* exprlist */
case 222: /* case_exprlist */
{
-sqlite3ExprListDelete(pParse->db, (yypminor->yy174));
+sqlite3ExprListDelete(pParse->db, (yypminor->yy418));
}
break;
case 188: /* fullname */
@@ -78154,25 +79373,25 @@
case 201: /* seltablist */
case 202: /* stl_prefix */
{
-sqlite3SrcListDelete(pParse->db, (yypminor->yy373));
+sqlite3SrcListDelete(pParse->db, (yypminor->yy151));
}
break;
case 205: /* using_opt */
case 208: /* inscollist */
case 214: /* inscollist_opt */
{
-sqlite3IdListDelete(pParse->db, (yypminor->yy432));
+sqlite3IdListDelete(pParse->db, (yypminor->yy240));
}
break;
- case 231: /* trigger_cmd_list */
- case 236: /* trigger_cmd */
+ case 230: /* trigger_cmd_list */
+ case 235: /* trigger_cmd */
{
-sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy243));
+sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy360));
}
break;
- case 233: /* trigger_event */
+ case 232: /* trigger_event */
{
-sqlite3IdListDelete(pParse->db, (yypminor->yy370).b);
+sqlite3IdListDelete(pParse->db, (yypminor->yy30).b);
}
break;
default: break; /* If no destructor action specified: do nothing */
@@ -78407,13 +79626,13 @@
{ 139, 1 },
{ 140, 2 },
{ 140, 1 },
- { 142, 1 },
{ 141, 1 },
{ 141, 3 },
- { 144, 0 },
- { 144, 1 },
+ { 142, 0 },
+ { 142, 1 },
+ { 142, 3 },
+ { 143, 1 },
{ 144, 3 },
- { 143, 3 },
{ 146, 0 },
{ 146, 1 },
{ 146, 2 },
@@ -78421,10 +79640,10 @@
{ 145, 1 },
{ 145, 1 },
{ 145, 1 },
- { 143, 2 },
- { 143, 2 },
- { 143, 2 },
- { 143, 2 },
+ { 144, 2 },
+ { 144, 2 },
+ { 144, 2 },
+ { 144, 2 },
{ 148, 6 },
{ 151, 0 },
{ 151, 3 },
@@ -78503,12 +79722,12 @@
{ 185, 1 },
{ 185, 1 },
{ 185, 1 },
- { 143, 4 },
+ { 144, 4 },
{ 187, 2 },
{ 187, 0 },
- { 143, 8 },
- { 143, 4 },
- { 143, 1 },
+ { 144, 8 },
+ { 144, 4 },
+ { 144, 1 },
{ 155, 1 },
{ 155, 3 },
{ 190, 1 },
@@ -78561,15 +79780,15 @@
{ 198, 2 },
{ 198, 4 },
{ 198, 4 },
- { 143, 4 },
+ { 144, 4 },
{ 194, 0 },
{ 194, 2 },
- { 143, 6 },
+ { 144, 6 },
{ 212, 5 },
{ 212, 3 },
- { 143, 8 },
- { 143, 5 },
- { 143, 6 },
+ { 144, 8 },
+ { 144, 5 },
+ { 144, 6 },
{ 213, 2 },
{ 213, 1 },
{ 215, 3 },
@@ -78638,85 +79857,84 @@
{ 216, 0 },
{ 211, 3 },
{ 211, 1 },
- { 143, 11 },
+ { 144, 11 },
{ 224, 1 },
{ 224, 0 },
{ 174, 0 },
{ 174, 3 },
{ 182, 5 },
{ 182, 3 },
- { 225, 1 },
- { 226, 0 },
- { 226, 2 },
- { 143, 4 },
- { 143, 1 },
- { 143, 2 },
- { 143, 5 },
- { 143, 5 },
- { 143, 5 },
- { 143, 5 },
- { 143, 6 },
- { 143, 3 },
- { 227, 1 },
- { 227, 1 },
+ { 225, 0 },
+ { 225, 2 },
+ { 144, 4 },
+ { 144, 1 },
+ { 144, 2 },
+ { 144, 5 },
+ { 144, 5 },
+ { 144, 5 },
+ { 144, 5 },
+ { 144, 6 },
+ { 144, 3 },
+ { 226, 1 },
+ { 226, 1 },
{ 165, 2 },
{ 166, 2 },
- { 229, 1 },
{ 228, 1 },
- { 228, 0 },
- { 143, 5 },
- { 230, 11 },
+ { 227, 1 },
+ { 227, 0 },
+ { 144, 5 },
+ { 229, 11 },
+ { 231, 1 },
+ { 231, 1 },
+ { 231, 2 },
+ { 231, 0 },
{ 232, 1 },
{ 232, 1 },
- { 232, 2 },
- { 232, 0 },
- { 233, 1 },
- { 233, 1 },
+ { 232, 3 },
+ { 233, 0 },
{ 233, 3 },
{ 234, 0 },
- { 234, 3 },
- { 235, 0 },
- { 235, 2 },
- { 231, 3 },
- { 231, 0 },
- { 236, 6 },
- { 236, 8 },
- { 236, 5 },
- { 236, 4 },
- { 236, 1 },
+ { 234, 2 },
+ { 230, 3 },
+ { 230, 2 },
+ { 235, 6 },
+ { 235, 8 },
+ { 235, 5 },
+ { 235, 4 },
+ { 235, 1 },
{ 170, 4 },
{ 170, 6 },
{ 186, 1 },
{ 186, 1 },
{ 186, 1 },
- { 143, 4 },
- { 143, 6 },
- { 143, 3 },
- { 238, 0 },
- { 238, 2 },
- { 237, 1 },
+ { 144, 4 },
+ { 144, 6 },
+ { 144, 3 },
{ 237, 0 },
- { 143, 1 },
- { 143, 3 },
- { 143, 1 },
- { 143, 3 },
- { 143, 6 },
- { 143, 6 },
+ { 237, 2 },
+ { 236, 1 },
+ { 236, 0 },
+ { 144, 1 },
+ { 144, 3 },
+ { 144, 1 },
+ { 144, 3 },
+ { 144, 6 },
+ { 144, 6 },
+ { 238, 1 },
+ { 239, 0 },
{ 239, 1 },
- { 240, 0 },
- { 240, 1 },
- { 143, 1 },
- { 143, 4 },
- { 241, 7 },
- { 242, 1 },
- { 242, 3 },
- { 243, 0 },
- { 243, 2 },
+ { 144, 1 },
+ { 144, 4 },
+ { 240, 7 },
+ { 241, 1 },
+ { 241, 3 },
+ { 242, 0 },
+ { 242, 2 },
+ { 243, 1 },
+ { 243, 3 },
{ 244, 1 },
- { 244, 3 },
- { 245, 1 },
- { 246, 0 },
- { 246, 2 },
+ { 245, 0 },
+ { 245, 2 },
};
static void yy_accept(yyParser*); /* Forward Declaration */
@@ -78774,8 +79992,8 @@
case 0: /* input ::= cmdlist */
case 1: /* cmdlist ::= cmdlist ecmd */
case 2: /* cmdlist ::= ecmd */
- case 4: /* ecmd ::= SEMI */
- case 5: /* ecmd ::= explain cmdx SEMI */
+ case 3: /* ecmd ::= SEMI */
+ case 4: /* ecmd ::= explain cmdx SEMI */
case 10: /* trans_opt ::= */
case 11: /* trans_opt ::= TRANSACTION */
case 12: /* trans_opt ::= TRANSACTION nm */
@@ -78794,45 +80012,45 @@
case 83: /* conslist ::= conslist tcons */
case 84: /* conslist ::= tcons */
case 85: /* tcons ::= CONSTRAINT nm */
- case 258: /* plus_opt ::= PLUS */
- case 259: /* plus_opt ::= */
- case 269: /* foreach_clause ::= */
- case 270: /* foreach_clause ::= FOR EACH ROW */
- case 290: /* database_kw_opt ::= DATABASE */
- case 291: /* database_kw_opt ::= */
- case 299: /* kwcolumn_opt ::= */
- case 300: /* kwcolumn_opt ::= COLUMNKW */
- case 304: /* vtabarglist ::= vtabarg */
- case 305: /* vtabarglist ::= vtabarglist COMMA vtabarg */
- case 307: /* vtabarg ::= vtabarg vtabargtoken */
- case 311: /* anylist ::= */
+ case 257: /* plus_opt ::= PLUS */
+ case 258: /* plus_opt ::= */
+ case 268: /* foreach_clause ::= */
+ case 269: /* foreach_clause ::= FOR EACH ROW */
+ case 289: /* database_kw_opt ::= DATABASE */
+ case 290: /* database_kw_opt ::= */
+ case 298: /* kwcolumn_opt ::= */
+ case 299: /* kwcolumn_opt ::= COLUMNKW */
+ case 303: /* vtabarglist ::= vtabarg */
+ case 304: /* vtabarglist ::= vtabarglist COMMA vtabarg */
+ case 306: /* vtabarg ::= vtabarg vtabargtoken */
+ case 310: /* anylist ::= */
{
}
break;
- case 3: /* cmdx ::= cmd */
-{ sqlite3FinishCoding(pParse); }
- break;
- case 6: /* explain ::= */
+ case 5: /* explain ::= */
{ sqlite3BeginParse(pParse, 0); }
break;
- case 7: /* explain ::= EXPLAIN */
+ case 6: /* explain ::= EXPLAIN */
{ sqlite3BeginParse(pParse, 1); }
break;
- case 8: /* explain ::= EXPLAIN QUERY PLAN */
+ case 7: /* explain ::= EXPLAIN QUERY PLAN */
{ sqlite3BeginParse(pParse, 2); }
break;
+ case 8: /* cmdx ::= cmd */
+{ sqlite3FinishCoding(pParse); }
+ break;
case 9: /* cmd ::= BEGIN transtype trans_opt */
-{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy46);}
+{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy280);}
break;
case 13: /* transtype ::= */
-{yygotominor.yy46 = TK_DEFERRED;}
+{yygotominor.yy280 = TK_DEFERRED;}
break;
case 14: /* transtype ::= DEFERRED */
case 15: /* transtype ::= IMMEDIATE */
case 16: /* transtype ::= EXCLUSIVE */
case 107: /* multiselect_op ::= UNION */
case 109: /* multiselect_op ::= EXCEPT|INTERSECT */
-{yygotominor.yy46 = yymsp[0].major;}
+{yygotominor.yy280 = yymsp[0].major;}
break;
case 17: /* cmd ::= COMMIT trans_opt */
case 18: /* cmd ::= END trans_opt */
@@ -78843,7 +80061,7 @@
break;
case 21: /* create_table ::= CREATE temp TABLE ifnotexists nm dbnm */
{
- sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy46,0,0,yymsp[-2].minor.yy46);
+ sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy280,0,0,yymsp[-2].minor.yy280);
}
break;
case 22: /* ifnotexists ::= */
@@ -78857,7 +80075,7 @@
case 113: /* distinct ::= */
case 213: /* between_op ::= BETWEEN */
case 216: /* in_op ::= IN */
-{yygotominor.yy46 = 0;}
+{yygotominor.yy280 = 0;}
break;
case 23: /* ifnotexists ::= IF NOT EXISTS */
case 24: /* temp ::= TEMP */
@@ -78867,7 +80085,7 @@
case 111: /* distinct ::= DISTINCT */
case 214: /* between_op ::= NOT BETWEEN */
case 217: /* in_op ::= NOT IN */
-{yygotominor.yy46 = 1;}
+{yygotominor.yy280 = 1;}
break;
case 26: /* create_table_args ::= LP columnlist conslist_opt RP */
{
@@ -78876,8 +80094,8 @@
break;
case 27: /* create_table_args ::= AS select */
{
- sqlite3EndTable(pParse,0,0,yymsp[0].minor.yy219);
- sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy219);
+ sqlite3EndTable(pParse,0,0,yymsp[0].minor.yy375);
+ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy375);
}
break;
case 30: /* column ::= columnid type carglist */
@@ -78902,13 +80120,12 @@
case 119: /* as ::= AS nm */
case 120: /* as ::= ids */
case 131: /* dbnm ::= DOT nm */
- case 241: /* idxitem ::= nm */
- case 243: /* collate ::= COLLATE ids */
- case 253: /* nmnum ::= plus_num */
- case 254: /* nmnum ::= nm */
- case 255: /* plus_num ::= plus_opt number */
- case 256: /* minus_num ::= MINUS number */
- case 257: /* number ::= INTEGER|FLOAT */
+ case 242: /* collate ::= COLLATE ids */
+ case 252: /* nmnum ::= plus_num */
+ case 253: /* nmnum ::= nm */
+ case 254: /* plus_num ::= plus_opt number */
+ case 255: /* minus_num ::= MINUS number */
+ case 256: /* number ::= INTEGER|FLOAT */
{yygotominor.yy0 = yymsp[0].minor.yy0;}
break;
case 38: /* type ::= typetoken */
@@ -78931,14 +80148,14 @@
break;
case 50: /* ccons ::= DEFAULT term */
case 52: /* ccons ::= DEFAULT PLUS term */
-{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy172);}
+{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy62);}
break;
case 51: /* ccons ::= DEFAULT LP expr RP */
-{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy172);}
+{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy62);}
break;
case 53: /* ccons ::= DEFAULT MINUS term */
{
- Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy172, 0, 0);
+ Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy62, 0, 0);
sqlite3AddDefaultValue(pParse,p);
}
break;
@@ -78949,55 +80166,55 @@
}
break;
case 56: /* ccons ::= NOT NULL onconf */
-{sqlite3AddNotNull(pParse, yymsp[0].minor.yy46);}
+{sqlite3AddNotNull(pParse, yymsp[0].minor.yy280);}
break;
case 57: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */
-{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy46,yymsp[0].minor.yy46,yymsp[-2].minor.yy46);}
+{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy280,yymsp[0].minor.yy280,yymsp[-2].minor.yy280);}
break;
case 58: /* ccons ::= UNIQUE onconf */
-{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy46,0,0,0,0);}
+{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy280,0,0,0,0);}
break;
case 59: /* ccons ::= CHECK LP expr RP */
-{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy172);}
+{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy62);}
break;
case 60: /* ccons ::= REFERENCES nm idxlist_opt refargs */
-{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy174,yymsp[0].minor.yy46);}
+{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy418,yymsp[0].minor.yy280);}
break;
case 61: /* ccons ::= defer_subclause */
-{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy46);}
+{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy280);}
break;
case 62: /* ccons ::= COLLATE ids */
{sqlite3AddCollateType(pParse, &yymsp[0].minor.yy0);}
break;
case 65: /* refargs ::= */
-{ yygotominor.yy46 = OE_Restrict * 0x010101; }
+{ yygotominor.yy280 = OE_Restrict * 0x010101; }
break;
case 66: /* refargs ::= refargs refarg */
-{ yygotominor.yy46 = (yymsp[-1].minor.yy46 & yymsp[0].minor.yy405.mask) | yymsp[0].minor.yy405.value; }
+{ yygotominor.yy280 = (yymsp[-1].minor.yy280 & yymsp[0].minor.yy359.mask) | yymsp[0].minor.yy359.value; }
break;
case 67: /* refarg ::= MATCH nm */
-{ yygotominor.yy405.value = 0; yygotominor.yy405.mask = 0x000000; }
+{ yygotominor.yy359.value = 0; yygotominor.yy359.mask = 0x000000; }
break;
case 68: /* refarg ::= ON DELETE refact */
-{ yygotominor.yy405.value = yymsp[0].minor.yy46; yygotominor.yy405.mask = 0x0000ff; }
+{ yygotominor.yy359.value = yymsp[0].minor.yy280; yygotominor.yy359.mask = 0x0000ff; }
break;
case 69: /* refarg ::= ON UPDATE refact */
-{ yygotominor.yy405.value = yymsp[0].minor.yy46<<8; yygotominor.yy405.mask = 0x00ff00; }
+{ yygotominor.yy359.value = yymsp[0].minor.yy280<<8; yygotominor.yy359.mask = 0x00ff00; }
break;
case 70: /* refarg ::= ON INSERT refact */
-{ yygotominor.yy405.value = yymsp[0].minor.yy46<<16; yygotominor.yy405.mask = 0xff0000; }
+{ yygotominor.yy359.value = yymsp[0].minor.yy280<<16; yygotominor.yy359.mask = 0xff0000; }
break;
case 71: /* refact ::= SET NULL */
-{ yygotominor.yy46 = OE_SetNull; }
+{ yygotominor.yy280 = OE_SetNull; }
break;
case 72: /* refact ::= SET DEFAULT */
-{ yygotominor.yy46 = OE_SetDflt; }
+{ yygotominor.yy280 = OE_SetDflt; }
break;
case 73: /* refact ::= CASCADE */
-{ yygotominor.yy46 = OE_Cascade; }
+{ yygotominor.yy280 = OE_Cascade; }
break;
case 74: /* refact ::= RESTRICT */
-{ yygotominor.yy46 = OE_Restrict; }
+{ yygotominor.yy280 = OE_Restrict; }
break;
case 75: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
case 76: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
@@ -79006,7 +80223,7 @@
case 95: /* orconf ::= OR resolvetype */
case 96: /* resolvetype ::= raisetype */
case 166: /* insert_cmd ::= INSERT orconf */
-{yygotominor.yy46 = yymsp[0].minor.yy46;}
+{yygotominor.yy280 = yymsp[0].minor.yy280;}
break;
case 80: /* conslist_opt ::= */
{yygotominor.yy0.n = 0; yygotominor.yy0.z = 0;}
@@ -79015,96 +80232,96 @@
{yygotominor.yy0 = yymsp[-1].minor.yy0;}
break;
case 86: /* tcons ::= PRIMARY KEY LP idxlist autoinc RP onconf */
-{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy174,yymsp[0].minor.yy46,yymsp[-2].minor.yy46,0);}
+{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy418,yymsp[0].minor.yy280,yymsp[-2].minor.yy280,0);}
break;
case 87: /* tcons ::= UNIQUE LP idxlist RP onconf */
-{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy174,yymsp[0].minor.yy46,0,0,0,0);}
+{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy418,yymsp[0].minor.yy280,0,0,0,0);}
break;
case 88: /* tcons ::= CHECK LP expr RP onconf */
-{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy172);}
+{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy62);}
break;
case 89: /* tcons ::= FOREIGN KEY LP idxlist RP REFERENCES nm idxlist_opt refargs defer_subclause_opt */
{
- sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy174, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy174, yymsp[-1].minor.yy46);
- sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy46);
+ sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy418, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy418, yymsp[-1].minor.yy280);
+ sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy280);
}
break;
case 92: /* onconf ::= */
case 94: /* orconf ::= */
-{yygotominor.yy46 = OE_Default;}
+{yygotominor.yy280 = OE_Default;}
break;
case 97: /* resolvetype ::= IGNORE */
-{yygotominor.yy46 = OE_Ignore;}
+{yygotominor.yy280 = OE_Ignore;}
break;
case 98: /* resolvetype ::= REPLACE */
case 167: /* insert_cmd ::= REPLACE */
-{yygotominor.yy46 = OE_Replace;}
+{yygotominor.yy280 = OE_Replace;}
break;
case 99: /* cmd ::= DROP TABLE ifexists fullname */
{
- sqlite3DropTable(pParse, yymsp[0].minor.yy373, 0, yymsp[-1].minor.yy46);
+ sqlite3DropTable(pParse, yymsp[0].minor.yy151, 0, yymsp[-1].minor.yy280);
}
break;
case 102: /* cmd ::= CREATE temp VIEW ifnotexists nm dbnm AS select */
{
- sqlite3CreateView(pParse, &yymsp[-7].minor.yy0, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, yymsp[0].minor.yy219, yymsp[-6].minor.yy46, yymsp[-4].minor.yy46);
+ sqlite3CreateView(pParse, &yymsp[-7].minor.yy0, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, yymsp[0].minor.yy375, yymsp[-6].minor.yy280, yymsp[-4].minor.yy280);
}
break;
case 103: /* cmd ::= DROP VIEW ifexists fullname */
{
- sqlite3DropTable(pParse, yymsp[0].minor.yy373, 1, yymsp[-1].minor.yy46);
+ sqlite3DropTable(pParse, yymsp[0].minor.yy151, 1, yymsp[-1].minor.yy280);
}
break;
case 104: /* cmd ::= select */
{
- SelectDest dest = {SRT_Callback, 0, 0, 0, 0};
- sqlite3Select(pParse, yymsp[0].minor.yy219, &dest, 0, 0, 0);
- sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy219);
+ SelectDest dest = {SRT_Output, 0, 0, 0, 0};
+ sqlite3Select(pParse, yymsp[0].minor.yy375, &dest);
+ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy375);
}
break;
case 105: /* select ::= oneselect */
case 128: /* seltablist_paren ::= select */
-{yygotominor.yy219 = yymsp[0].minor.yy219;}
+{yygotominor.yy375 = yymsp[0].minor.yy375;}
break;
case 106: /* select ::= select multiselect_op oneselect */
{
- if( yymsp[0].minor.yy219 ){
- yymsp[0].minor.yy219->op = yymsp[-1].minor.yy46;
- yymsp[0].minor.yy219->pPrior = yymsp[-2].minor.yy219;
+ if( yymsp[0].minor.yy375 ){
+ yymsp[0].minor.yy375->op = yymsp[-1].minor.yy280;
+ yymsp[0].minor.yy375->pPrior = yymsp[-2].minor.yy375;
}else{
- sqlite3SelectDelete(pParse->db, yymsp[-2].minor.yy219);
+ sqlite3SelectDelete(pParse->db, yymsp[-2].minor.yy375);
}
- yygotominor.yy219 = yymsp[0].minor.yy219;
+ yygotominor.yy375 = yymsp[0].minor.yy375;
}
break;
case 108: /* multiselect_op ::= UNION ALL */
-{yygotominor.yy46 = TK_ALL;}
+{yygotominor.yy280 = TK_ALL;}
break;
case 110: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
{
- yygotominor.yy219 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy174,yymsp[-5].minor.yy373,yymsp[-4].minor.yy172,yymsp[-3].minor.yy174,yymsp[-2].minor.yy172,yymsp[-1].minor.yy174,yymsp[-7].minor.yy46,yymsp[0].minor.yy234.pLimit,yymsp[0].minor.yy234.pOffset);
+ yygotominor.yy375 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy418,yymsp[-5].minor.yy151,yymsp[-4].minor.yy62,yymsp[-3].minor.yy418,yymsp[-2].minor.yy62,yymsp[-1].minor.yy418,yymsp[-7].minor.yy280,yymsp[0].minor.yy220.pLimit,yymsp[0].minor.yy220.pOffset);
}
break;
case 114: /* sclp ::= selcollist COMMA */
case 238: /* idxlist_opt ::= LP idxlist RP */
-{yygotominor.yy174 = yymsp[-1].minor.yy174;}
+{yygotominor.yy418 = yymsp[-1].minor.yy418;}
break;
case 115: /* sclp ::= */
case 141: /* orderby_opt ::= */
case 149: /* groupby_opt ::= */
case 231: /* exprlist ::= */
case 237: /* idxlist_opt ::= */
-{yygotominor.yy174 = 0;}
+{yygotominor.yy418 = 0;}
break;
case 116: /* selcollist ::= sclp expr as */
{
- yygotominor.yy174 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy174,yymsp[-1].minor.yy172,yymsp[0].minor.yy0.n?&yymsp[0].minor.yy0:0);
+ yygotominor.yy418 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy418,yymsp[-1].minor.yy62,yymsp[0].minor.yy0.n?&yymsp[0].minor.yy0:0);
}
break;
case 117: /* selcollist ::= sclp STAR */
{
Expr *p = sqlite3PExpr(pParse, TK_ALL, 0, 0, 0);
- yygotominor.yy174 = sqlite3ExprListAppend(pParse, yymsp[-1].minor.yy174, p, 0);
+ yygotominor.yy418 = sqlite3ExprListAppend(pParse, yymsp[-1].minor.yy418, p, 0);
}
break;
case 118: /* selcollist ::= sclp nm DOT STAR */
@@ -79112,63 +80329,63 @@
Expr *pRight = sqlite3PExpr(pParse, TK_ALL, 0, 0, &yymsp[0].minor.yy0);
Expr *pLeft = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-2].minor.yy0);
Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0);
- yygotominor.yy174 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy174, pDot, 0);
+ yygotominor.yy418 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy418, pDot, 0);
}
break;
case 121: /* as ::= */
{yygotominor.yy0.n = 0;}
break;
case 122: /* from ::= */
-{yygotominor.yy373 = sqlite3DbMallocZero(pParse->db, sizeof(*yygotominor.yy373));}
+{yygotominor.yy151 = sqlite3DbMallocZero(pParse->db, sizeof(*yygotominor.yy151));}
break;
case 123: /* from ::= FROM seltablist */
{
- yygotominor.yy373 = yymsp[0].minor.yy373;
- sqlite3SrcListShiftJoinType(yygotominor.yy373);
+ yygotominor.yy151 = yymsp[0].minor.yy151;
+ sqlite3SrcListShiftJoinType(yygotominor.yy151);
}
break;
case 124: /* stl_prefix ::= seltablist joinop */
{
- yygotominor.yy373 = yymsp[-1].minor.yy373;
- if( yygotominor.yy373 && yygotominor.yy373->nSrc>0 ) yygotominor.yy373->a[yygotominor.yy373->nSrc-1].jointype = yymsp[0].minor.yy46;
+ yygotominor.yy151 = yymsp[-1].minor.yy151;
+ if( yygotominor.yy151 && yygotominor.yy151->nSrc>0 ) yygotominor.yy151->a[yygotominor.yy151->nSrc-1].jointype = yymsp[0].minor.yy280;
}
break;
case 125: /* stl_prefix ::= */
-{yygotominor.yy373 = 0;}
+{yygotominor.yy151 = 0;}
break;
case 126: /* seltablist ::= stl_prefix nm dbnm as on_opt using_opt */
{
- yygotominor.yy373 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy373,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy172,yymsp[0].minor.yy432);
+ yygotominor.yy151 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy151,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy62,yymsp[0].minor.yy240);
}
break;
case 127: /* seltablist ::= stl_prefix LP seltablist_paren RP as on_opt using_opt */
{
- yygotominor.yy373 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy373,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy219,yymsp[-1].minor.yy172,yymsp[0].minor.yy432);
+ yygotominor.yy151 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy151,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy375,yymsp[-1].minor.yy62,yymsp[0].minor.yy240);
}
break;
case 129: /* seltablist_paren ::= seltablist */
{
- sqlite3SrcListShiftJoinType(yymsp[0].minor.yy373);
- yygotominor.yy219 = sqlite3SelectNew(pParse,0,yymsp[0].minor.yy373,0,0,0,0,0,0,0);
+ sqlite3SrcListShiftJoinType(yymsp[0].minor.yy151);
+ yygotominor.yy375 = sqlite3SelectNew(pParse,0,yymsp[0].minor.yy151,0,0,0,0,0,0,0);
}
break;
case 130: /* dbnm ::= */
{yygotominor.yy0.z=0; yygotominor.yy0.n=0;}
break;
case 132: /* fullname ::= nm dbnm */
-{yygotominor.yy373 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);}
+{yygotominor.yy151 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);}
break;
case 133: /* joinop ::= COMMA|JOIN */
-{ yygotominor.yy46 = JT_INNER; }
+{ yygotominor.yy280 = JT_INNER; }
break;
case 134: /* joinop ::= JOIN_KW JOIN */
-{ yygotominor.yy46 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); }
+{ yygotominor.yy280 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); }
break;
case 135: /* joinop ::= JOIN_KW nm JOIN */
-{ yygotominor.yy46 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); }
+{ yygotominor.yy280 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); }
break;
case 136: /* joinop ::= JOIN_KW nm nm JOIN */
-{ yygotominor.yy46 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0); }
+{ yygotominor.yy280 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0); }
break;
case 137: /* on_opt ::= ON expr */
case 145: /* sortitem ::= expr */
@@ -79178,7 +80395,7 @@
case 202: /* escape ::= ESCAPE expr */
case 226: /* case_else ::= ELSE expr */
case 228: /* case_operand ::= expr */
-{yygotominor.yy172 = yymsp[0].minor.yy172;}
+{yygotominor.yy62 = yymsp[0].minor.yy62;}
break;
case 138: /* on_opt ::= */
case 151: /* having_opt ::= */
@@ -79186,107 +80403,107 @@
case 203: /* escape ::= */
case 227: /* case_else ::= */
case 229: /* case_operand ::= */
-{yygotominor.yy172 = 0;}
+{yygotominor.yy62 = 0;}
break;
case 139: /* using_opt ::= USING LP inscollist RP */
case 171: /* inscollist_opt ::= LP inscollist RP */
-{yygotominor.yy432 = yymsp[-1].minor.yy432;}
+{yygotominor.yy240 = yymsp[-1].minor.yy240;}
break;
case 140: /* using_opt ::= */
case 170: /* inscollist_opt ::= */
-{yygotominor.yy432 = 0;}
+{yygotominor.yy240 = 0;}
break;
case 142: /* orderby_opt ::= ORDER BY sortlist */
case 150: /* groupby_opt ::= GROUP BY nexprlist */
case 230: /* exprlist ::= nexprlist */
-{yygotominor.yy174 = yymsp[0].minor.yy174;}
+{yygotominor.yy418 = yymsp[0].minor.yy418;}
break;
case 143: /* sortlist ::= sortlist COMMA sortitem sortorder */
{
- yygotominor.yy174 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy174,yymsp[-1].minor.yy172,0);
- if( yygotominor.yy174 ) yygotominor.yy174->a[yygotominor.yy174->nExpr-1].sortOrder = yymsp[0].minor.yy46;
+ yygotominor.yy418 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy418,yymsp[-1].minor.yy62,0);
+ if( yygotominor.yy418 ) yygotominor.yy418->a[yygotominor.yy418->nExpr-1].sortOrder = yymsp[0].minor.yy280;
}
break;
case 144: /* sortlist ::= sortitem sortorder */
{
- yygotominor.yy174 = sqlite3ExprListAppend(pParse,0,yymsp[-1].minor.yy172,0);
- if( yygotominor.yy174 && yygotominor.yy174->a ) yygotominor.yy174->a[0].sortOrder = yymsp[0].minor.yy46;
+ yygotominor.yy418 = sqlite3ExprListAppend(pParse,0,yymsp[-1].minor.yy62,0);
+ if( yygotominor.yy418 && yygotominor.yy418->a ) yygotominor.yy418->a[0].sortOrder = yymsp[0].minor.yy280;
}
break;
case 146: /* sortorder ::= ASC */
case 148: /* sortorder ::= */
-{yygotominor.yy46 = SQLITE_SO_ASC;}
+{yygotominor.yy280 = SQLITE_SO_ASC;}
break;
case 147: /* sortorder ::= DESC */
-{yygotominor.yy46 = SQLITE_SO_DESC;}
+{yygotominor.yy280 = SQLITE_SO_DESC;}
break;
case 153: /* limit_opt ::= */
-{yygotominor.yy234.pLimit = 0; yygotominor.yy234.pOffset = 0;}
+{yygotominor.yy220.pLimit = 0; yygotominor.yy220.pOffset = 0;}
break;
case 154: /* limit_opt ::= LIMIT expr */
-{yygotominor.yy234.pLimit = yymsp[0].minor.yy172; yygotominor.yy234.pOffset = 0;}
+{yygotominor.yy220.pLimit = yymsp[0].minor.yy62; yygotominor.yy220.pOffset = 0;}
break;
case 155: /* limit_opt ::= LIMIT expr OFFSET expr */
-{yygotominor.yy234.pLimit = yymsp[-2].minor.yy172; yygotominor.yy234.pOffset = yymsp[0].minor.yy172;}
+{yygotominor.yy220.pLimit = yymsp[-2].minor.yy62; yygotominor.yy220.pOffset = yymsp[0].minor.yy62;}
break;
case 156: /* limit_opt ::= LIMIT expr COMMA expr */
-{yygotominor.yy234.pOffset = yymsp[-2].minor.yy172; yygotominor.yy234.pLimit = yymsp[0].minor.yy172;}
+{yygotominor.yy220.pOffset = yymsp[-2].minor.yy62; yygotominor.yy220.pLimit = yymsp[0].minor.yy62;}
break;
case 157: /* cmd ::= DELETE FROM fullname where_opt */
-{sqlite3DeleteFrom(pParse,yymsp[-1].minor.yy373,yymsp[0].minor.yy172);}
+{sqlite3DeleteFrom(pParse,yymsp[-1].minor.yy151,yymsp[0].minor.yy62);}
break;
case 160: /* cmd ::= UPDATE orconf fullname SET setlist where_opt */
{
- sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy174,"set list");
- sqlite3Update(pParse,yymsp[-3].minor.yy373,yymsp[-1].minor.yy174,yymsp[0].minor.yy172,yymsp[-4].minor.yy46);
+ sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy418,"set list");
+ sqlite3Update(pParse,yymsp[-3].minor.yy151,yymsp[-1].minor.yy418,yymsp[0].minor.yy62,yymsp[-4].minor.yy280);
}
break;
case 161: /* setlist ::= setlist COMMA nm EQ expr */
-{yygotominor.yy174 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy174,yymsp[0].minor.yy172,&yymsp[-2].minor.yy0);}
+{yygotominor.yy418 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy418,yymsp[0].minor.yy62,&yymsp[-2].minor.yy0);}
break;
case 162: /* setlist ::= nm EQ expr */
-{yygotominor.yy174 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy172,&yymsp[-2].minor.yy0);}
+{yygotominor.yy418 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy62,&yymsp[-2].minor.yy0);}
break;
case 163: /* cmd ::= insert_cmd INTO fullname inscollist_opt VALUES LP itemlist RP */
-{sqlite3Insert(pParse, yymsp[-5].minor.yy373, yymsp[-1].minor.yy174, 0, yymsp[-4].minor.yy432, yymsp[-7].minor.yy46);}
+{sqlite3Insert(pParse, yymsp[-5].minor.yy151, yymsp[-1].minor.yy418, 0, yymsp[-4].minor.yy240, yymsp[-7].minor.yy280);}
break;
case 164: /* cmd ::= insert_cmd INTO fullname inscollist_opt select */
-{sqlite3Insert(pParse, yymsp[-2].minor.yy373, 0, yymsp[0].minor.yy219, yymsp[-1].minor.yy432, yymsp[-4].minor.yy46);}
+{sqlite3Insert(pParse, yymsp[-2].minor.yy151, 0, yymsp[0].minor.yy375, yymsp[-1].minor.yy240, yymsp[-4].minor.yy280);}
break;
case 165: /* cmd ::= insert_cmd INTO fullname inscollist_opt DEFAULT VALUES */
-{sqlite3Insert(pParse, yymsp[-3].minor.yy373, 0, 0, yymsp[-2].minor.yy432, yymsp[-5].minor.yy46);}
+{sqlite3Insert(pParse, yymsp[-3].minor.yy151, 0, 0, yymsp[-2].minor.yy240, yymsp[-5].minor.yy280);}
break;
case 168: /* itemlist ::= itemlist COMMA expr */
case 232: /* nexprlist ::= nexprlist COMMA expr */
-{yygotominor.yy174 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy174,yymsp[0].minor.yy172,0);}
+{yygotominor.yy418 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy418,yymsp[0].minor.yy62,0);}
break;
case 169: /* itemlist ::= expr */
case 233: /* nexprlist ::= expr */
-{yygotominor.yy174 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy172,0);}
+{yygotominor.yy418 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy62,0);}
break;
case 172: /* inscollist ::= inscollist COMMA nm */
-{yygotominor.yy432 = sqlite3IdListAppend(pParse->db,yymsp[-2].minor.yy432,&yymsp[0].minor.yy0);}
+{yygotominor.yy240 = sqlite3IdListAppend(pParse->db,yymsp[-2].minor.yy240,&yymsp[0].minor.yy0);}
break;
case 173: /* inscollist ::= nm */
-{yygotominor.yy432 = sqlite3IdListAppend(pParse->db,0,&yymsp[0].minor.yy0);}
+{yygotominor.yy240 = sqlite3IdListAppend(pParse->db,0,&yymsp[0].minor.yy0);}
break;
case 175: /* expr ::= LP expr RP */
-{yygotominor.yy172 = yymsp[-1].minor.yy172; sqlite3ExprSpan(yygotominor.yy172,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); }
+{yygotominor.yy62 = yymsp[-1].minor.yy62; sqlite3ExprSpan(yygotominor.yy62,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); }
break;
case 176: /* term ::= NULL */
case 181: /* term ::= INTEGER|FLOAT|BLOB */
case 182: /* term ::= STRING */
-{yygotominor.yy172 = sqlite3PExpr(pParse, yymsp[0].major, 0, 0, &yymsp[0].minor.yy0);}
+{yygotominor.yy62 = sqlite3PExpr(pParse, yymsp[0].major, 0, 0, &yymsp[0].minor.yy0);}
break;
case 177: /* expr ::= ID */
case 178: /* expr ::= JOIN_KW */
-{yygotominor.yy172 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[0].minor.yy0);}
+{yygotominor.yy62 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[0].minor.yy0);}
break;
case 179: /* expr ::= nm DOT nm */
{
Expr *temp1 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-2].minor.yy0);
Expr *temp2 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[0].minor.yy0);
- yygotominor.yy172 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2, 0);
+ yygotominor.yy62 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2, 0);
}
break;
case 180: /* expr ::= nm DOT nm DOT nm */
@@ -79295,56 +80512,56 @@
Expr *temp2 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-2].minor.yy0);
Expr *temp3 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[0].minor.yy0);
Expr *temp4 = sqlite3PExpr(pParse, TK_DOT, temp2, temp3, 0);
- yygotominor.yy172 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4, 0);
+ yygotominor.yy62 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4, 0);
}
break;
case 183: /* expr ::= REGISTER */
-{yygotominor.yy172 = sqlite3RegisterExpr(pParse, &yymsp[0].minor.yy0);}
+{yygotominor.yy62 = sqlite3RegisterExpr(pParse, &yymsp[0].minor.yy0);}
break;
case 184: /* expr ::= VARIABLE */
{
Token *pToken = &yymsp[0].minor.yy0;
- Expr *pExpr = yygotominor.yy172 = sqlite3PExpr(pParse, TK_VARIABLE, 0, 0, pToken);
+ Expr *pExpr = yygotominor.yy62 = sqlite3PExpr(pParse, TK_VARIABLE, 0, 0, pToken);
sqlite3ExprAssignVarNumber(pParse, pExpr);
}
break;
case 185: /* expr ::= expr COLLATE ids */
{
- yygotominor.yy172 = sqlite3ExprSetColl(pParse, yymsp[-2].minor.yy172, &yymsp[0].minor.yy0);
+ yygotominor.yy62 = sqlite3ExprSetColl(pParse, yymsp[-2].minor.yy62, &yymsp[0].minor.yy0);
}
break;
case 186: /* expr ::= CAST LP expr AS typetoken RP */
{
- yygotominor.yy172 = sqlite3PExpr(pParse, TK_CAST, yymsp[-3].minor.yy172, 0, &yymsp[-1].minor.yy0);
- sqlite3ExprSpan(yygotominor.yy172,&yymsp[-5].minor.yy0,&yymsp[0].minor.yy0);
+ yygotominor.yy62 = sqlite3PExpr(pParse, TK_CAST, yymsp[-3].minor.yy62, 0, &yymsp[-1].minor.yy0);
+ sqlite3ExprSpan(yygotominor.yy62,&yymsp[-5].minor.yy0,&yymsp[0].minor.yy0);
}
break;
case 187: /* expr ::= ID LP distinct exprlist RP */
{
- if( yymsp[-1].minor.yy174 && yymsp[-1].minor.yy174->nExpr>SQLITE_MAX_FUNCTION_ARG ){
+ if( yymsp[-1].minor.yy418 && yymsp[-1].minor.yy418->nExpr>SQLITE_MAX_FUNCTION_ARG ){
sqlite3ErrorMsg(pParse, "too many arguments on function %T", &yymsp[-4].minor.yy0);
}
- yygotominor.yy172 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy174, &yymsp[-4].minor.yy0);
- sqlite3ExprSpan(yygotominor.yy172,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0);
- if( yymsp[-2].minor.yy46 && yygotominor.yy172 ){
- yygotominor.yy172->flags |= EP_Distinct;
+ yygotominor.yy62 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy418, &yymsp[-4].minor.yy0);
+ sqlite3ExprSpan(yygotominor.yy62,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0);
+ if( yymsp[-2].minor.yy280 && yygotominor.yy62 ){
+ yygotominor.yy62->flags |= EP_Distinct;
}
}
break;
case 188: /* expr ::= ID LP STAR RP */
{
- yygotominor.yy172 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0);
- sqlite3ExprSpan(yygotominor.yy172,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0);
+ yygotominor.yy62 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0);
+ sqlite3ExprSpan(yygotominor.yy62,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0);
}
break;
case 189: /* term ::= CTIME_KW */
{
/* The CURRENT_TIME, CURRENT_DATE, and CURRENT_TIMESTAMP values are
** treated as functions that return constants */
- yygotominor.yy172 = sqlite3ExprFunction(pParse, 0,&yymsp[0].minor.yy0);
- if( yygotominor.yy172 ){
- yygotominor.yy172->op = TK_CONST_FUNC;
- yygotominor.yy172->span = yymsp[0].minor.yy0;
+ yygotominor.yy62 = sqlite3ExprFunction(pParse, 0,&yymsp[0].minor.yy0);
+ if( yygotominor.yy62 ){
+ yygotominor.yy62->op = TK_CONST_FUNC;
+ yygotominor.yy62->span = yymsp[0].minor.yy0;
}
}
break;
@@ -79356,391 +80573,400 @@
case 195: /* expr ::= expr PLUS|MINUS expr */
case 196: /* expr ::= expr STAR|SLASH|REM expr */
case 197: /* expr ::= expr CONCAT expr */
-{yygotominor.yy172 = sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy172,yymsp[0].minor.yy172,0);}
+{yygotominor.yy62 = sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy62,yymsp[0].minor.yy62,0);}
break;
case 198: /* likeop ::= LIKE_KW */
case 200: /* likeop ::= MATCH */
-{yygotominor.yy72.eOperator = yymsp[0].minor.yy0; yygotominor.yy72.not = 0;}
+{yygotominor.yy222.eOperator = yymsp[0].minor.yy0; yygotominor.yy222.not = 0;}
break;
case 199: /* likeop ::= NOT LIKE_KW */
case 201: /* likeop ::= NOT MATCH */
-{yygotominor.yy72.eOperator = yymsp[0].minor.yy0; yygotominor.yy72.not = 1;}
+{yygotominor.yy222.eOperator = yymsp[0].minor.yy0; yygotominor.yy222.not = 1;}
break;
case 204: /* expr ::= expr likeop expr escape */
{
ExprList *pList;
- pList = sqlite3ExprListAppend(pParse,0, yymsp[-1].minor.yy172, 0);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[-3].minor.yy172, 0);
- if( yymsp[0].minor.yy172 ){
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy172, 0);
- }
- yygotominor.yy172 = sqlite3ExprFunction(pParse, pList, &yymsp[-2].minor.yy72.eOperator);
- if( yymsp[-2].minor.yy72.not ) yygotominor.yy172 = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy172, 0, 0);
- sqlite3ExprSpan(yygotominor.yy172, &yymsp[-3].minor.yy172->span, &yymsp[-1].minor.yy172->span);
- if( yygotominor.yy172 ) yygotominor.yy172->flags |= EP_InfixFunc;
+ pList = sqlite3ExprListAppend(pParse,0, yymsp[-1].minor.yy62, 0);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[-3].minor.yy62, 0);
+ if( yymsp[0].minor.yy62 ){
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy62, 0);
+ }
+ yygotominor.yy62 = sqlite3ExprFunction(pParse, pList, &yymsp[-2].minor.yy222.eOperator);
+ if( yymsp[-2].minor.yy222.not ) yygotominor.yy62 = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy62, 0, 0);
+ sqlite3ExprSpan(yygotominor.yy62, &yymsp[-3].minor.yy62->span, &yymsp[-1].minor.yy62->span);
+ if( yygotominor.yy62 ) yygotominor.yy62->flags |= EP_InfixFunc;
}
break;
case 205: /* expr ::= expr ISNULL|NOTNULL */
{
- yygotominor.yy172 = sqlite3PExpr(pParse, yymsp[0].major, yymsp[-1].minor.yy172, 0, 0);
- sqlite3ExprSpan(yygotominor.yy172,&yymsp[-1].minor.yy172->span,&yymsp[0].minor.yy0);
+ yygotominor.yy62 = sqlite3PExpr(pParse, yymsp[0].major, yymsp[-1].minor.yy62, 0, 0);
+ sqlite3ExprSpan(yygotominor.yy62,&yymsp[-1].minor.yy62->span,&yymsp[0].minor.yy0);
}
break;
case 206: /* expr ::= expr IS NULL */
{
- yygotominor.yy172 = sqlite3PExpr(pParse, TK_ISNULL, yymsp[-2].minor.yy172, 0, 0);
- sqlite3ExprSpan(yygotominor.yy172,&yymsp[-2].minor.yy172->span,&yymsp[0].minor.yy0);
+ yygotominor.yy62 = sqlite3PExpr(pParse, TK_ISNULL, yymsp[-2].minor.yy62, 0, 0);
+ sqlite3ExprSpan(yygotominor.yy62,&yymsp[-2].minor.yy62->span,&yymsp[0].minor.yy0);
}
break;
case 207: /* expr ::= expr NOT NULL */
{
- yygotominor.yy172 = sqlite3PExpr(pParse, TK_NOTNULL, yymsp[-2].minor.yy172, 0, 0);
- sqlite3ExprSpan(yygotominor.yy172,&yymsp[-2].minor.yy172->span,&yymsp[0].minor.yy0);
+ yygotominor.yy62 = sqlite3PExpr(pParse, TK_NOTNULL, yymsp[-2].minor.yy62, 0, 0);
+ sqlite3ExprSpan(yygotominor.yy62,&yymsp[-2].minor.yy62->span,&yymsp[0].minor.yy0);
}
break;
case 208: /* expr ::= expr IS NOT NULL */
{
- yygotominor.yy172 = sqlite3PExpr(pParse, TK_NOTNULL, yymsp[-3].minor.yy172, 0, 0);
- sqlite3ExprSpan(yygotominor.yy172,&yymsp[-3].minor.yy172->span,&yymsp[0].minor.yy0);
+ yygotominor.yy62 = sqlite3PExpr(pParse, TK_NOTNULL, yymsp[-3].minor.yy62, 0, 0);
+ sqlite3ExprSpan(yygotominor.yy62,&yymsp[-3].minor.yy62->span,&yymsp[0].minor.yy0);
}
break;
case 209: /* expr ::= NOT expr */
case 210: /* expr ::= BITNOT expr */
{
- yygotominor.yy172 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy172, 0, 0);
- sqlite3ExprSpan(yygotominor.yy172,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy172->span);
+ yygotominor.yy62 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy62, 0, 0);
+ sqlite3ExprSpan(yygotominor.yy62,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy62->span);
}
break;
case 211: /* expr ::= MINUS expr */
{
- yygotominor.yy172 = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy172, 0, 0);
- sqlite3ExprSpan(yygotominor.yy172,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy172->span);
+ yygotominor.yy62 = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy62, 0, 0);
+ sqlite3ExprSpan(yygotominor.yy62,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy62->span);
}
break;
case 212: /* expr ::= PLUS expr */
{
- yygotominor.yy172 = sqlite3PExpr(pParse, TK_UPLUS, yymsp[0].minor.yy172, 0, 0);
- sqlite3ExprSpan(yygotominor.yy172,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy172->span);
+ yygotominor.yy62 = sqlite3PExpr(pParse, TK_UPLUS, yymsp[0].minor.yy62, 0, 0);
+ sqlite3ExprSpan(yygotominor.yy62,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy62->span);
}
break;
case 215: /* expr ::= expr between_op expr AND expr */
{
- ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy172, 0);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy172, 0);
- yygotominor.yy172 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy172, 0, 0);
- if( yygotominor.yy172 ){
- yygotominor.yy172->pList = pList;
+ ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy62, 0);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy62, 0);
+ yygotominor.yy62 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy62, 0, 0);
+ if( yygotominor.yy62 ){
+ yygotominor.yy62->pList = pList;
}else{
sqlite3ExprListDelete(pParse->db, pList);
}
- if( yymsp[-3].minor.yy46 ) yygotominor.yy172 = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy172, 0, 0);
- sqlite3ExprSpan(yygotominor.yy172,&yymsp[-4].minor.yy172->span,&yymsp[0].minor.yy172->span);
+ if( yymsp[-3].minor.yy280 ) yygotominor.yy62 = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy62, 0, 0);
+ sqlite3ExprSpan(yygotominor.yy62,&yymsp[-4].minor.yy62->span,&yymsp[0].minor.yy62->span);
}
break;
case 218: /* expr ::= expr in_op LP exprlist RP */
{
- yygotominor.yy172 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy172, 0, 0);
- if( yygotominor.yy172 ){
- yygotominor.yy172->pList = yymsp[-1].minor.yy174;
- sqlite3ExprSetHeight(pParse, yygotominor.yy172);
+ yygotominor.yy62 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy62, 0, 0);
+ if( yygotominor.yy62 ){
+ yygotominor.yy62->pList = yymsp[-1].minor.yy418;
+ sqlite3ExprSetHeight(pParse, yygotominor.yy62);
}else{
- sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy174);
+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy418);
}
- if( yymsp[-3].minor.yy46 ) yygotominor.yy172 = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy172, 0, 0);
- sqlite3ExprSpan(yygotominor.yy172,&yymsp[-4].minor.yy172->span,&yymsp[0].minor.yy0);
+ if( yymsp[-3].minor.yy280 ) yygotominor.yy62 = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy62, 0, 0);
+ sqlite3ExprSpan(yygotominor.yy62,&yymsp[-4].minor.yy62->span,&yymsp[0].minor.yy0);
}
break;
case 219: /* expr ::= LP select RP */
{
- yygotominor.yy172 = sqlite3PExpr(pParse, TK_SELECT, 0, 0, 0);
- if( yygotominor.yy172 ){
- yygotominor.yy172->pSelect = yymsp[-1].minor.yy219;
- sqlite3ExprSetHeight(pParse, yygotominor.yy172);
+ yygotominor.yy62 = sqlite3PExpr(pParse, TK_SELECT, 0, 0, 0);
+ if( yygotominor.yy62 ){
+ yygotominor.yy62->pSelect = yymsp[-1].minor.yy375;
+ sqlite3ExprSetHeight(pParse, yygotominor.yy62);
}else{
- sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy219);
+ sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy375);
}
- sqlite3ExprSpan(yygotominor.yy172,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);
+ sqlite3ExprSpan(yygotominor.yy62,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);
}
break;
case 220: /* expr ::= expr in_op LP select RP */
{
- yygotominor.yy172 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy172, 0, 0);
- if( yygotominor.yy172 ){
- yygotominor.yy172->pSelect = yymsp[-1].minor.yy219;
- sqlite3ExprSetHeight(pParse, yygotominor.yy172);
+ yygotominor.yy62 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy62, 0, 0);
+ if( yygotominor.yy62 ){
+ yygotominor.yy62->pSelect = yymsp[-1].minor.yy375;
+ sqlite3ExprSetHeight(pParse, yygotominor.yy62);
}else{
- sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy219);
+ sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy375);
}
- if( yymsp[-3].minor.yy46 ) yygotominor.yy172 = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy172, 0, 0);
- sqlite3ExprSpan(yygotominor.yy172,&yymsp[-4].minor.yy172->span,&yymsp[0].minor.yy0);
+ if( yymsp[-3].minor.yy280 ) yygotominor.yy62 = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy62, 0, 0);
+ sqlite3ExprSpan(yygotominor.yy62,&yymsp[-4].minor.yy62->span,&yymsp[0].minor.yy0);
}
break;
case 221: /* expr ::= expr in_op nm dbnm */
{
SrcList *pSrc = sqlite3SrcListAppend(pParse->db, 0,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);
- yygotominor.yy172 = sqlite3PExpr(pParse, TK_IN, yymsp[-3].minor.yy172, 0, 0);
- if( yygotominor.yy172 ){
- yygotominor.yy172->pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0);
- sqlite3ExprSetHeight(pParse, yygotominor.yy172);
+ yygotominor.yy62 = sqlite3PExpr(pParse, TK_IN, yymsp[-3].minor.yy62, 0, 0);
+ if( yygotominor.yy62 ){
+ yygotominor.yy62->pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0);
+ sqlite3ExprSetHeight(pParse, yygotominor.yy62);
}else{
sqlite3SrcListDelete(pParse->db, pSrc);
}
- if( yymsp[-2].minor.yy46 ) yygotominor.yy172 = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy172, 0, 0);
- sqlite3ExprSpan(yygotominor.yy172,&yymsp[-3].minor.yy172->span,yymsp[0].minor.yy0.z?&yymsp[0].minor.yy0:&yymsp[-1].minor.yy0);
+ if( yymsp[-2].minor.yy280 ) yygotominor.yy62 = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy62, 0, 0);
+ sqlite3ExprSpan(yygotominor.yy62,&yymsp[-3].minor.yy62->span,yymsp[0].minor.yy0.z?&yymsp[0].minor.yy0:&yymsp[-1].minor.yy0);
}
break;
case 222: /* expr ::= EXISTS LP select RP */
{
- Expr *p = yygotominor.yy172 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0, 0);
+ Expr *p = yygotominor.yy62 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0, 0);
if( p ){
- p->pSelect = yymsp[-1].minor.yy219;
+ p->pSelect = yymsp[-1].minor.yy375;
sqlite3ExprSpan(p,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0);
- sqlite3ExprSetHeight(pParse, yygotominor.yy172);
+ sqlite3ExprSetHeight(pParse, yygotominor.yy62);
}else{
- sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy219);
+ sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy375);
}
}
break;
case 223: /* expr ::= CASE case_operand case_exprlist case_else END */
{
- yygotominor.yy172 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy172, yymsp[-1].minor.yy172, 0);
- if( yygotominor.yy172 ){
- yygotominor.yy172->pList = yymsp[-2].minor.yy174;
- sqlite3ExprSetHeight(pParse, yygotominor.yy172);
+ yygotominor.yy62 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy62, yymsp[-1].minor.yy62, 0);
+ if( yygotominor.yy62 ){
+ yygotominor.yy62->pList = yymsp[-2].minor.yy418;
+ sqlite3ExprSetHeight(pParse, yygotominor.yy62);
}else{
- sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy174);
+ sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy418);
}
- sqlite3ExprSpan(yygotominor.yy172, &yymsp[-4].minor.yy0, &yymsp[0].minor.yy0);
+ sqlite3ExprSpan(yygotominor.yy62, &yymsp[-4].minor.yy0, &yymsp[0].minor.yy0);
}
break;
case 224: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
{
- yygotominor.yy174 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy174, yymsp[-2].minor.yy172, 0);
- yygotominor.yy174 = sqlite3ExprListAppend(pParse,yygotominor.yy174, yymsp[0].minor.yy172, 0);
+ yygotominor.yy418 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy418, yymsp[-2].minor.yy62, 0);
+ yygotominor.yy418 = sqlite3ExprListAppend(pParse,yygotominor.yy418, yymsp[0].minor.yy62, 0);
}
break;
case 225: /* case_exprlist ::= WHEN expr THEN expr */
{
- yygotominor.yy174 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy172, 0);
- yygotominor.yy174 = sqlite3ExprListAppend(pParse,yygotominor.yy174, yymsp[0].minor.yy172, 0);
+ yygotominor.yy418 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy62, 0);
+ yygotominor.yy418 = sqlite3ExprListAppend(pParse,yygotominor.yy418, yymsp[0].minor.yy62, 0);
}
break;
case 234: /* cmd ::= CREATE uniqueflag INDEX ifnotexists nm dbnm ON nm LP idxlist RP */
{
sqlite3CreateIndex(pParse, &yymsp[-6].minor.yy0, &yymsp[-5].minor.yy0,
- sqlite3SrcListAppend(pParse->db,0,&yymsp[-3].minor.yy0,0), yymsp[-1].minor.yy174, yymsp[-9].minor.yy46,
- &yymsp[-10].minor.yy0, &yymsp[0].minor.yy0, SQLITE_SO_ASC, yymsp[-7].minor.yy46);
+ sqlite3SrcListAppend(pParse->db,0,&yymsp[-3].minor.yy0,0), yymsp[-1].minor.yy418, yymsp[-9].minor.yy280,
+ &yymsp[-10].minor.yy0, &yymsp[0].minor.yy0, SQLITE_SO_ASC, yymsp[-7].minor.yy280);
}
break;
case 235: /* uniqueflag ::= UNIQUE */
- case 283: /* raisetype ::= ABORT */
-{yygotominor.yy46 = OE_Abort;}
+ case 282: /* raisetype ::= ABORT */
+{yygotominor.yy280 = OE_Abort;}
break;
case 236: /* uniqueflag ::= */
-{yygotominor.yy46 = OE_None;}
+{yygotominor.yy280 = OE_None;}
break;
- case 239: /* idxlist ::= idxlist COMMA idxitem collate sortorder */
+ case 239: /* idxlist ::= idxlist COMMA nm collate sortorder */
{
Expr *p = 0;
if( yymsp[-1].minor.yy0.n>0 ){
p = sqlite3PExpr(pParse, TK_COLUMN, 0, 0, 0);
sqlite3ExprSetColl(pParse, p, &yymsp[-1].minor.yy0);
}
- yygotominor.yy174 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy174, p, &yymsp[-2].minor.yy0);
- sqlite3ExprListCheckLength(pParse, yygotominor.yy174, "index");
- if( yygotominor.yy174 ) yygotominor.yy174->a[yygotominor.yy174->nExpr-1].sortOrder = yymsp[0].minor.yy46;
+ yygotominor.yy418 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy418, p, &yymsp[-2].minor.yy0);
+ sqlite3ExprListCheckLength(pParse, yygotominor.yy418, "index");
+ if( yygotominor.yy418 ) yygotominor.yy418->a[yygotominor.yy418->nExpr-1].sortOrder = yymsp[0].minor.yy280;
}
break;
- case 240: /* idxlist ::= idxitem collate sortorder */
+ case 240: /* idxlist ::= nm collate sortorder */
{
Expr *p = 0;
if( yymsp[-1].minor.yy0.n>0 ){
p = sqlite3PExpr(pParse, TK_COLUMN, 0, 0, 0);
sqlite3ExprSetColl(pParse, p, &yymsp[-1].minor.yy0);
}
- yygotominor.yy174 = sqlite3ExprListAppend(pParse,0, p, &yymsp[-2].minor.yy0);
- sqlite3ExprListCheckLength(pParse, yygotominor.yy174, "index");
- if( yygotominor.yy174 ) yygotominor.yy174->a[yygotominor.yy174->nExpr-1].sortOrder = yymsp[0].minor.yy46;
+ yygotominor.yy418 = sqlite3ExprListAppend(pParse,0, p, &yymsp[-2].minor.yy0);
+ sqlite3ExprListCheckLength(pParse, yygotominor.yy418, "index");
+ if( yygotominor.yy418 ) yygotominor.yy418->a[yygotominor.yy418->nExpr-1].sortOrder = yymsp[0].minor.yy280;
}
break;
- case 242: /* collate ::= */
+ case 241: /* collate ::= */
{yygotominor.yy0.z = 0; yygotominor.yy0.n = 0;}
break;
- case 244: /* cmd ::= DROP INDEX ifexists fullname */
-{sqlite3DropIndex(pParse, yymsp[0].minor.yy373, yymsp[-1].minor.yy46);}
+ case 243: /* cmd ::= DROP INDEX ifexists fullname */
+{sqlite3DropIndex(pParse, yymsp[0].minor.yy151, yymsp[-1].minor.yy280);}
break;
- case 245: /* cmd ::= VACUUM */
- case 246: /* cmd ::= VACUUM nm */
+ case 244: /* cmd ::= VACUUM */
+ case 245: /* cmd ::= VACUUM nm */
{sqlite3Vacuum(pParse);}
break;
- case 247: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
- case 248: /* cmd ::= PRAGMA nm dbnm EQ ON */
- case 249: /* cmd ::= PRAGMA nm dbnm EQ DELETE */
+ case 246: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
+ case 247: /* cmd ::= PRAGMA nm dbnm EQ ON */
+ case 248: /* cmd ::= PRAGMA nm dbnm EQ DELETE */
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);}
break;
- case 250: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
+ case 249: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
{
sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);
}
break;
- case 251: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
+ case 250: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);}
break;
- case 252: /* cmd ::= PRAGMA nm dbnm */
+ case 251: /* cmd ::= PRAGMA nm dbnm */
{sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);}
break;
- case 260: /* cmd ::= CREATE trigger_decl BEGIN trigger_cmd_list END */
+ case 259: /* cmd ::= CREATE trigger_decl BEGIN trigger_cmd_list END */
{
Token all;
all.z = yymsp[-3].minor.yy0.z;
all.n = (yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n;
- sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy243, &all);
+ sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy360, &all);
}
break;
- case 261: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
+ case 260: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
{
- sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy46, yymsp[-4].minor.yy370.a, yymsp[-4].minor.yy370.b, yymsp[-2].minor.yy373, yymsp[0].minor.yy172, yymsp[-10].minor.yy46, yymsp[-8].minor.yy46);
+ sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy280, yymsp[-4].minor.yy30.a, yymsp[-4].minor.yy30.b, yymsp[-2].minor.yy151, yymsp[0].minor.yy62, yymsp[-10].minor.yy280, yymsp[-8].minor.yy280);
yygotominor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0);
}
break;
- case 262: /* trigger_time ::= BEFORE */
- case 265: /* trigger_time ::= */
-{ yygotominor.yy46 = TK_BEFORE; }
+ case 261: /* trigger_time ::= BEFORE */
+ case 264: /* trigger_time ::= */
+{ yygotominor.yy280 = TK_BEFORE; }
break;
- case 263: /* trigger_time ::= AFTER */
-{ yygotominor.yy46 = TK_AFTER; }
+ case 262: /* trigger_time ::= AFTER */
+{ yygotominor.yy280 = TK_AFTER; }
break;
- case 264: /* trigger_time ::= INSTEAD OF */
-{ yygotominor.yy46 = TK_INSTEAD;}
+ case 263: /* trigger_time ::= INSTEAD OF */
+{ yygotominor.yy280 = TK_INSTEAD;}
break;
- case 266: /* trigger_event ::= DELETE|INSERT */
- case 267: /* trigger_event ::= UPDATE */
-{yygotominor.yy370.a = yymsp[0].major; yygotominor.yy370.b = 0;}
+ case 265: /* trigger_event ::= DELETE|INSERT */
+ case 266: /* trigger_event ::= UPDATE */
+{yygotominor.yy30.a = yymsp[0].major; yygotominor.yy30.b = 0;}
break;
- case 268: /* trigger_event ::= UPDATE OF inscollist */
-{yygotominor.yy370.a = TK_UPDATE; yygotominor.yy370.b = yymsp[0].minor.yy432;}
+ case 267: /* trigger_event ::= UPDATE OF inscollist */
+{yygotominor.yy30.a = TK_UPDATE; yygotominor.yy30.b = yymsp[0].minor.yy240;}
break;
- case 271: /* when_clause ::= */
- case 288: /* key_opt ::= */
-{ yygotominor.yy172 = 0; }
+ case 270: /* when_clause ::= */
+ case 287: /* key_opt ::= */
+{ yygotominor.yy62 = 0; }
break;
- case 272: /* when_clause ::= WHEN expr */
- case 289: /* key_opt ::= KEY expr */
-{ yygotominor.yy172 = yymsp[0].minor.yy172; }
+ case 271: /* when_clause ::= WHEN expr */
+ case 288: /* key_opt ::= KEY expr */
+{ yygotominor.yy62 = yymsp[0].minor.yy62; }
break;
- case 273: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
+ case 272: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
{
- if( yymsp[-2].minor.yy243 ){
- yymsp[-2].minor.yy243->pLast->pNext = yymsp[-1].minor.yy243;
+/*
+ if( yymsp[-2].minor.yy360 ){
+ yymsp[-2].minor.yy360->pLast->pNext = yymsp[-1].minor.yy360;
}else{
- yymsp[-2].minor.yy243 = yymsp[-1].minor.yy243;
+ yymsp[-2].minor.yy360 = yymsp[-1].minor.yy360;
}
- yymsp[-2].minor.yy243->pLast = yymsp[-1].minor.yy243;
- yygotominor.yy243 = yymsp[-2].minor.yy243;
+*/
+ assert( yymsp[-2].minor.yy360!=0 );
+ yymsp[-2].minor.yy360->pLast->pNext = yymsp[-1].minor.yy360;
+ yymsp[-2].minor.yy360->pLast = yymsp[-1].minor.yy360;
+ yygotominor.yy360 = yymsp[-2].minor.yy360;
}
break;
- case 274: /* trigger_cmd_list ::= */
-{ yygotominor.yy243 = 0; }
+ case 273: /* trigger_cmd_list ::= trigger_cmd SEMI */
+{
+ /* if( yymsp[-1].minor.yy360 ) */
+ assert( yymsp[-1].minor.yy360!=0 );
+ yymsp[-1].minor.yy360->pLast = yymsp[-1].minor.yy360;
+ yygotominor.yy360 = yymsp[-1].minor.yy360;
+}
break;
- case 275: /* trigger_cmd ::= UPDATE orconf nm SET setlist where_opt */
-{ yygotominor.yy243 = sqlite3TriggerUpdateStep(pParse->db, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy174, yymsp[0].minor.yy172, yymsp[-4].minor.yy46); }
+ case 274: /* trigger_cmd ::= UPDATE orconf nm SET setlist where_opt */
+{ yygotominor.yy360 = sqlite3TriggerUpdateStep(pParse->db, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy418, yymsp[0].minor.yy62, yymsp[-4].minor.yy280); }
break;
- case 276: /* trigger_cmd ::= insert_cmd INTO nm inscollist_opt VALUES LP itemlist RP */
-{yygotominor.yy243 = sqlite3TriggerInsertStep(pParse->db, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy432, yymsp[-1].minor.yy174, 0, yymsp[-7].minor.yy46);}
+ case 275: /* trigger_cmd ::= insert_cmd INTO nm inscollist_opt VALUES LP itemlist RP */
+{yygotominor.yy360 = sqlite3TriggerInsertStep(pParse->db, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy240, yymsp[-1].minor.yy418, 0, yymsp[-7].minor.yy280);}
break;
- case 277: /* trigger_cmd ::= insert_cmd INTO nm inscollist_opt select */
-{yygotominor.yy243 = sqlite3TriggerInsertStep(pParse->db, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy432, 0, yymsp[0].minor.yy219, yymsp[-4].minor.yy46);}
+ case 276: /* trigger_cmd ::= insert_cmd INTO nm inscollist_opt select */
+{yygotominor.yy360 = sqlite3TriggerInsertStep(pParse->db, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy240, 0, yymsp[0].minor.yy375, yymsp[-4].minor.yy280);}
break;
- case 278: /* trigger_cmd ::= DELETE FROM nm where_opt */
-{yygotominor.yy243 = sqlite3TriggerDeleteStep(pParse->db, &yymsp[-1].minor.yy0, yymsp[0].minor.yy172);}
+ case 277: /* trigger_cmd ::= DELETE FROM nm where_opt */
+{yygotominor.yy360 = sqlite3TriggerDeleteStep(pParse->db, &yymsp[-1].minor.yy0, yymsp[0].minor.yy62);}
break;
- case 279: /* trigger_cmd ::= select */
-{yygotominor.yy243 = sqlite3TriggerSelectStep(pParse->db, yymsp[0].minor.yy219); }
+ case 278: /* trigger_cmd ::= select */
+{yygotominor.yy360 = sqlite3TriggerSelectStep(pParse->db, yymsp[0].minor.yy375); }
break;
- case 280: /* expr ::= RAISE LP IGNORE RP */
+ case 279: /* expr ::= RAISE LP IGNORE RP */
{
- yygotominor.yy172 = sqlite3PExpr(pParse, TK_RAISE, 0, 0, 0);
- if( yygotominor.yy172 ){
- yygotominor.yy172->iColumn = OE_Ignore;
- sqlite3ExprSpan(yygotominor.yy172, &yymsp[-3].minor.yy0, &yymsp[0].minor.yy0);
+ yygotominor.yy62 = sqlite3PExpr(pParse, TK_RAISE, 0, 0, 0);
+ if( yygotominor.yy62 ){
+ yygotominor.yy62->iColumn = OE_Ignore;
+ sqlite3ExprSpan(yygotominor.yy62, &yymsp[-3].minor.yy0, &yymsp[0].minor.yy0);
}
}
break;
- case 281: /* expr ::= RAISE LP raisetype COMMA nm RP */
+ case 280: /* expr ::= RAISE LP raisetype COMMA nm RP */
{
- yygotominor.yy172 = sqlite3PExpr(pParse, TK_RAISE, 0, 0, &yymsp[-1].minor.yy0);
- if( yygotominor.yy172 ) {
- yygotominor.yy172->iColumn = yymsp[-3].minor.yy46;
- sqlite3ExprSpan(yygotominor.yy172, &yymsp[-5].minor.yy0, &yymsp[0].minor.yy0);
+ yygotominor.yy62 = sqlite3PExpr(pParse, TK_RAISE, 0, 0, &yymsp[-1].minor.yy0);
+ if( yygotominor.yy62 ) {
+ yygotominor.yy62->iColumn = yymsp[-3].minor.yy280;
+ sqlite3ExprSpan(yygotominor.yy62, &yymsp[-5].minor.yy0, &yymsp[0].minor.yy0);
}
}
break;
- case 282: /* raisetype ::= ROLLBACK */
-{yygotominor.yy46 = OE_Rollback;}
+ case 281: /* raisetype ::= ROLLBACK */
+{yygotominor.yy280 = OE_Rollback;}
break;
- case 284: /* raisetype ::= FAIL */
-{yygotominor.yy46 = OE_Fail;}
+ case 283: /* raisetype ::= FAIL */
+{yygotominor.yy280 = OE_Fail;}
break;
- case 285: /* cmd ::= DROP TRIGGER ifexists fullname */
+ case 284: /* cmd ::= DROP TRIGGER ifexists fullname */
{
- sqlite3DropTrigger(pParse,yymsp[0].minor.yy373,yymsp[-1].minor.yy46);
+ sqlite3DropTrigger(pParse,yymsp[0].minor.yy151,yymsp[-1].minor.yy280);
}
break;
- case 286: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
+ case 285: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
{
- sqlite3Attach(pParse, yymsp[-3].minor.yy172, yymsp[-1].minor.yy172, yymsp[0].minor.yy172);
+ sqlite3Attach(pParse, yymsp[-3].minor.yy62, yymsp[-1].minor.yy62, yymsp[0].minor.yy62);
}
break;
- case 287: /* cmd ::= DETACH database_kw_opt expr */
+ case 286: /* cmd ::= DETACH database_kw_opt expr */
{
- sqlite3Detach(pParse, yymsp[0].minor.yy172);
+ sqlite3Detach(pParse, yymsp[0].minor.yy62);
}
break;
- case 292: /* cmd ::= REINDEX */
+ case 291: /* cmd ::= REINDEX */
{sqlite3Reindex(pParse, 0, 0);}
break;
- case 293: /* cmd ::= REINDEX nm dbnm */
+ case 292: /* cmd ::= REINDEX nm dbnm */
{sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
break;
- case 294: /* cmd ::= ANALYZE */
+ case 293: /* cmd ::= ANALYZE */
{sqlite3Analyze(pParse, 0, 0);}
break;
- case 295: /* cmd ::= ANALYZE nm dbnm */
+ case 294: /* cmd ::= ANALYZE nm dbnm */
{sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
break;
- case 296: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
+ case 295: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
{
- sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy373,&yymsp[0].minor.yy0);
+ sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy151,&yymsp[0].minor.yy0);
}
break;
- case 297: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column */
+ case 296: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column */
{
sqlite3AlterFinishAddColumn(pParse, &yymsp[0].minor.yy0);
}
break;
- case 298: /* add_column_fullname ::= fullname */
+ case 297: /* add_column_fullname ::= fullname */
{
- sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy373);
+ sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy151);
}
break;
- case 301: /* cmd ::= create_vtab */
+ case 300: /* cmd ::= create_vtab */
{sqlite3VtabFinishParse(pParse,0);}
break;
- case 302: /* cmd ::= create_vtab LP vtabarglist RP */
+ case 301: /* cmd ::= create_vtab LP vtabarglist RP */
{sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);}
break;
- case 303: /* create_vtab ::= CREATE VIRTUAL TABLE nm dbnm USING nm */
+ case 302: /* create_vtab ::= CREATE VIRTUAL TABLE nm dbnm USING nm */
{
sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0);
}
break;
- case 306: /* vtabarg ::= */
+ case 305: /* vtabarg ::= */
{sqlite3VtabArgInit(pParse);}
break;
- case 308: /* vtabargtoken ::= ANY */
- case 309: /* vtabargtoken ::= lp anylist RP */
- case 310: /* lp ::= LP */
- case 312: /* anylist ::= anylist ANY */
+ case 307: /* vtabargtoken ::= ANY */
+ case 308: /* vtabargtoken ::= lp anylist RP */
+ case 309: /* lp ::= LP */
+ case 311: /* anylist ::= anylist ANY */
{sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);}
break;
};
@@ -80001,7 +81227,7 @@
** individual tokens and sends those tokens one-by-one over to the
** parser for analysis.
**
-** $Id: tokenize.c,v 1.148 2008/07/28 19:34:54 drh Exp $
+** $Id: tokenize.c,v 1.151 2008/08/29 02:14:03 drh Exp $
*/
/*
@@ -80230,7 +81456,7 @@
case '-': {
if( z[1]=='-' ){
for(i=2; (c=z[i])!=0 && c!='\n'; i++){}
- *tokenType = TK_COMMENT;
+ *tokenType = TK_SPACE;
return i;
}
*tokenType = TK_MINUS;
@@ -80263,7 +81489,7 @@
}
for(i=3, c=z[2]; (c!='*' || z[i]!='/') && (c=z[i])!=0; i++){}
if( c ) i++;
- *tokenType = TK_COMMENT;
+ *tokenType = TK_SPACE;
return i;
}
case '%': {
@@ -80344,9 +81570,12 @@
}
}
}
- if( c ){
+ if( c=='\'' ){
*tokenType = TK_STRING;
return i+1;
+ }else if( c!=0 ){
+ *tokenType = TK_ID;
+ return i+1;
}else{
*tokenType = TK_ILLEGAL;
return i;
@@ -80516,8 +81745,7 @@
break;
}
switch( tokenType ){
- case TK_SPACE:
- case TK_COMMENT: {
+ case TK_SPACE: {
if( db->u1.isInterrupted ){
pParse->rc = SQLITE_INTERRUPT;
sqlite3SetString(pzErrMsg, db, "interrupt");
@@ -80600,6 +81828,7 @@
sqlite3DeleteTrigger(db, pParse->pNewTrigger);
sqlite3DbFree(db, pParse->apVarExpr);
+ sqlite3DbFree(db, pParse->aAlias);
if( nErr>0 && (pParse->rc==SQLITE_OK || pParse->rc==SQLITE_DONE) ){
pParse->rc = SQLITE_ERROR;
}
@@ -80903,7 +82132,7 @@
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
-** $Id: main.c,v 1.486 2008/08/04 20:13:27 drh Exp $
+** $Id: main.c,v 1.494 2008/08/27 19:01:58 danielk1977 Exp $
*/
#ifdef SQLITE_ENABLE_FTS3
@@ -81002,68 +82231,126 @@
** Initialize SQLite.
**
** This routine must be called to initialize the memory allocation,
-** VFS, and mutex subsystesms prior to doing any serious work with
+** VFS, and mutex subsystems prior to doing any serious work with
** SQLite. But as long as you do not compile with SQLITE_OMIT_AUTOINIT
** this routine will be called automatically by key routines such as
** sqlite3_open().
**
** This routine is a no-op except on its very first call for the process,
** or for the first call after a call to sqlite3_shutdown.
+**
+** The first thread to call this routine runs the initialization to
+** completion. If subsequent threads call this routine before the first
+** thread has finished the initialization process, then the subsequent
+** threads must block until the first thread finishes with the initialization.
+**
+** The first thread might call this routine recursively. Recursive
+** calls to this routine should not block, of course. Otherwise the
+** initialization process would never complete.
+**
+** Let X be the first thread to enter this routine. Let Y be some other
+** thread. Then while the initial invocation of this routine by X is
+** incomplete, it is required that:
+**
+** * Calls to this routine from Y must block until the outer-most
+** call by X completes.
+**
+** * Recursive calls to this routine from thread X return immediately
+** without blocking.
*/
SQLITE_API int sqlite3_initialize(void){
- static int inProgress = 0;
- int rc;
-
- /* If SQLite is already initialized, this call is a no-op. */
+ static int inProgress = 0; /* Prevent recursion */
+ sqlite3_mutex *pMaster; /* The main static mutex */
+ int rc; /* Result code */
+
+ /* If SQLite is already completely initialized, then this call
+ ** to sqlite3_initialize() should be a no-op. But the initialization
+ ** must be complete. So isInit must not be set until the very end
+ ** of this routine.
+ */
if( sqlite3Config.isInit ) return SQLITE_OK;
- /* Make sure the mutex system is initialized. */
+ /* Make sure the mutex subsystem is initialized. If unable to
+ ** initialize the mutex subsystem, return early with the error.
+ ** If the system is so sick that we are unable to allocate a mutex,
+ ** there is not much SQLite is going to be able to do.
+ **
+ ** The mutex subsystem must take care of serializing its own
+ ** initialization.
+ */
rc = sqlite3MutexInit();
+ if( rc ) return rc;
+ /* Initialize the malloc() system and the recursive pInitMutex mutex.
+ ** This operation is protected by the STATIC_MASTER mutex. Note that
+ ** MutexAlloc() is called for a static mutex prior to initializing the
+ ** malloc subsystem - this implies that the allocation of a static
+ ** mutex must not require support from the malloc subsystem.
+ */
+ pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+ sqlite3_mutex_enter(pMaster);
+ if( !sqlite3Config.isMallocInit ){
+ rc = sqlite3MallocInit();
+ }
if( rc==SQLITE_OK ){
-
- /* Initialize the malloc() system and the recursive pInitMutex mutex.
- ** This operation is protected by the STATIC_MASTER mutex.
- */
- sqlite3_mutex *pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
- sqlite3_mutex_enter(pMaster);
- if( !sqlite3Config.isMallocInit ){
- rc = sqlite3MallocInit();
- }
- if( rc==SQLITE_OK ){
- sqlite3Config.isMallocInit = 1;
- if( !sqlite3Config.pInitMutex ){
- sqlite3Config.pInitMutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);
- if( sqlite3Config.bCoreMutex && !sqlite3Config.pInitMutex ){
- rc = SQLITE_NOMEM;
- }
+ sqlite3Config.isMallocInit = 1;
+ if( !sqlite3Config.pInitMutex ){
+ sqlite3Config.pInitMutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);
+ if( sqlite3Config.bCoreMutex && !sqlite3Config.pInitMutex ){
+ rc = SQLITE_NOMEM;
}
}
- sqlite3_mutex_leave(pMaster);
- if( rc!=SQLITE_OK ){
- return rc;
- }
+ sqlite3Config.nRefInitMutex++;
+ }
+ sqlite3_mutex_leave(pMaster);
- /* Enter the recursive pInitMutex mutex. After doing so, if the
- ** sqlite3Config.isInit flag is true, then some other thread has
- ** finished doing the initialization. If the inProgress flag is
- ** true, then this function is being called recursively from within
- ** the sqlite3_os_init() call below. In either case, exit early.
- */
- sqlite3_mutex_enter(sqlite3Config.pInitMutex);
- if( sqlite3Config.isInit || inProgress ){
- sqlite3_mutex_leave(sqlite3Config.pInitMutex);
- return SQLITE_OK;
- }
- sqlite3StatusReset();
+ /* If unable to initialize the malloc subsystem, then return early.
+ ** There is little hope of getting SQLite to run if the malloc
+ ** subsystem cannot be initialized.
+ */
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+
+ /* Do the rest of the initialization under the recursive mutex so
+ ** that we will be able to handle recursive calls into
+ ** sqlite3_initialize(). The recursive calls normally come through
+ ** sqlite3_os_init() when it invokes sqlite3_vfs_register(), but other
+ ** recursive calls might also be possible.
+ */
+ sqlite3_mutex_enter(sqlite3Config.pInitMutex);
+ if( sqlite3Config.isInit==0 && inProgress==0 ){
inProgress = 1;
+ memset(&sqlite3GlobalFunctions, 0, sizeof(sqlite3GlobalFunctions));
+ sqlite3RegisterGlobalFunctions();
rc = sqlite3_os_init();
+ if( rc==SQLITE_OK ){
+ rc = sqlite3PcacheInitialize();
+ sqlite3PCacheBufferSetup(sqlite3Config.pPage, sqlite3Config.szPage,
+ sqlite3Config.nPage);
+ }
inProgress = 0;
sqlite3Config.isInit = (rc==SQLITE_OK ? 1 : 0);
- sqlite3_mutex_leave(sqlite3Config.pInitMutex);
}
+ sqlite3_mutex_leave(sqlite3Config.pInitMutex);
- /* Check NaN support. */
+ /* Go back under the static mutex and clean up the recursive
+ ** mutex to prevent a resource leak.
+ */
+ sqlite3_mutex_enter(pMaster);
+ sqlite3Config.nRefInitMutex--;
+ if( sqlite3Config.nRefInitMutex<=0 ){
+ assert( sqlite3Config.nRefInitMutex==0 );
+ sqlite3_mutex_free(sqlite3Config.pInitMutex);
+ sqlite3Config.pInitMutex = 0;
+ }
+ sqlite3_mutex_leave(pMaster);
+
+ /* The following is just a sanity check to make sure SQLite has
+ ** been compiled correctly. It is important to run this code, but
+ ** we don't want to run it too often and soak up CPU cycles for no
+ ** reason. So we run it once during initialization.
+ */
#ifndef NDEBUG
/* This section of code's only "output" is via assert() statements. */
if ( rc==SQLITE_OK ){
@@ -81086,9 +82373,8 @@
** routine is not threadsafe. Not by a long shot.
*/
SQLITE_API int sqlite3_shutdown(void){
- sqlite3_mutex_free(sqlite3Config.pInitMutex);
- sqlite3Config.pInitMutex = 0;
sqlite3Config.isMallocInit = 0;
+ sqlite3PcacheShutdown();
if( sqlite3Config.isInit ){
sqlite3_os_end();
}
@@ -81253,12 +82539,13 @@
}
if( sz<0 ) sz = 0;
if( cnt<0 ) cnt = 0;
- sz = (sz+7)&~7;
if( pBuf==0 ){
+ sz = (sz + 7)&~7;
sqlite3BeginBenignMalloc();
pStart = sqlite3Malloc( sz*cnt );
sqlite3EndBenignMalloc();
}else{
+ sz = sz&~7;
pStart = pBuf;
}
if( db->lookaside.bMalloced ){
@@ -81455,14 +82742,17 @@
sqlite3ResetInternalSchema(db, 0);
assert( db->nDb<=2 );
assert( db->aDb==db->aDbStatic );
- for(i=sqliteHashFirst(&db->aFunc); i; i=sqliteHashNext(i)){
- FuncDef *pFunc, *pNext;
- for(pFunc = (FuncDef*)sqliteHashData(i); pFunc; pFunc=pNext){
- pNext = pFunc->pNext;
- sqlite3DbFree(db, pFunc);
+ for(j=0; j<ArraySize(db->aFunc.a); j++){
+ FuncDef *pNext, *pHash, *p;
+ for(p=db->aFunc.a[j]; p; p=pHash){
+ pHash = p->pHash;
+ while( p ){
+ pNext = p->pNext;
+ sqlite3DbFree(db, p);
+ p = pNext;
+ }
}
}
-
for(i=sqliteHashFirst(&db->aCollSeq); i; i=sqliteHashNext(i)){
CollSeq *pColl = (CollSeq *)sqliteHashData(i);
/* Invoke any destructors registered for collation sequence user data. */
@@ -81485,7 +82775,6 @@
sqlite3HashClear(&db->aModule);
#endif
- sqlite3HashClear(&db->aFunc);
sqlite3Error(db, SQLITE_OK, 0); /* Deallocates any cached error strings. */
if( db->pErr ){
sqlite3ValueFree(db->pErr);
@@ -82251,8 +83540,8 @@
#if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>127
# error SQLITE_MAX_FUNCTION_ARG must be between 0 and 127
#endif
-#if SQLITE_MAX_ATTACH<0 || SQLITE_MAX_ATTACH>30
-# error SQLITE_MAX_ATTACH must be between 0 and 30
+#if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>30
+# error SQLITE_MAX_ATTACHED must be between 0 and 30
#endif
#if SQLITE_MAX_LIKE_PATTERN_LENGTH<1
# error SQLITE_MAX_LIKE_PATTERN_LENGTH must be at least 1
@@ -82260,6 +83549,9 @@
#if SQLITE_MAX_VARIABLE_NUMBER<1
# error SQLITE_MAX_VARIABLE_NUMBER must be at least 1
#endif
+#if SQLITE_MAX_COLUMN>32767
+# error SQLITE_MAX_COLUMN must not exceed 32767
+#endif
/*
@@ -82355,7 +83647,6 @@
| SQLITE_LoadExtension
#endif
;
- sqlite3HashInit(&db->aFunc, SQLITE_HASH_STRING, 0);
sqlite3HashInit(&db->aCollSeq, SQLITE_HASH_STRING, 0);
#ifndef SQLITE_OMIT_VIRTUALTABLE
sqlite3HashInit(&db->aModule, SQLITE_HASH_STRING, 0);
@@ -82364,7 +83655,6 @@
db->pVfs = sqlite3_vfs_find(zVfs);
if( !db->pVfs ){
rc = SQLITE_ERROR;
- db->magic = SQLITE_MAGIC_SICK;
sqlite3Error(db, rc, "no such vfs: %s", zVfs);
goto opendb_out;
}
@@ -82378,7 +83668,6 @@
createCollation(db, "BINARY", SQLITE_UTF16LE, 0, binCollFunc, 0);
createCollation(db, "RTRIM", SQLITE_UTF8, (void*)1, binCollFunc, 0);
if( db->mallocFailed ){
- db->magic = SQLITE_MAGIC_SICK;
goto opendb_out;
}
db->pDfltColl = sqlite3FindCollSeq(db, SQLITE_UTF8, "BINARY", 6, 0);
@@ -82401,7 +83690,6 @@
&db->aDb[0].pBt);
if( rc!=SQLITE_OK ){
sqlite3Error(db, rc, 0);
- db->magic = SQLITE_MAGIC_SICK;
goto opendb_out;
}
db->aDb[0].pSchema = sqlite3SchemaGet(db, db->aDb[0].pBt);
@@ -82491,9 +83779,12 @@
assert( db->mutex!=0 || isThreadsafe==0 || sqlite3Config.bFullMutex==0 );
sqlite3_mutex_leave(db->mutex);
}
- if( SQLITE_NOMEM==(rc = sqlite3_errcode(db)) ){
+ rc = sqlite3_errcode(db);
+ if( rc==SQLITE_NOMEM ){
sqlite3_close(db);
db = 0;
+ }else if( rc!=SQLITE_OK ){
+ db->magic = SQLITE_MAGIC_SICK;
}
*ppDb = db;
return sqlite3ApiExit(0, rc);
@@ -82779,7 +84070,7 @@
zCollSeq = pCol->zColl;
notnull = pCol->notNull!=0;
primarykey = pCol->isPrimKey!=0;
- autoinc = pTab->iPKey==iCol && pTab->autoInc;
+ autoinc = pTab->iPKey==iCol && (pTab->tabFlags & TF_Autoincrement)!=0;
}else{
zDataType = "INTEGER";
primarykey = 1;
Modified: trunk/libgda/sqlite/sqlite-src/sqlite3.h
==============================================================================
--- trunk/libgda/sqlite/sqlite-src/sqlite3.h (original)
+++ trunk/libgda/sqlite/sqlite-src/sqlite3.h Mon Sep 15 18:30:05 2008
@@ -52,6 +52,31 @@
#endif
/*
+** Add the ability to mark interfaces as deprecated.
+*/
+#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
+ /* GCC added the deprecated attribute in version 3.1 */
+ #define SQLITE_DEPRECATED __attribute__ ((deprecated))
+#elif defined(_MSC_VER)
+ #define SQLITE_DEPRECATED __declspec(deprecated)
+#else
+ #define SQLITE_DEPRECATED
+#endif
+
+/*
+** Add the ability to mark interfaces as experimental.
+*/
+#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
+ /* I can confirm that it does not work on version 4.1.0... */
+ /* First appears in GCC docs for version 4.3.0 */
+ #define SQLITE_EXPERIMENTAL __attribute__ ((warning ("is experimental")))
+#elif defined(_MSC_VER)
+ #define SQLITE_EXPERIMENTAL __declspec(deprecated("was declared experimental"))
+#else
+ #define SQLITE_EXPERIMENTAL
+#endif
+
+/*
** Ensure these symbols were not defined by some previous header file.
*/
#ifdef SQLITE_VERSION
@@ -91,8 +116,8 @@
** with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z
** are the major version, minor version, and release number.
*/
-#define SQLITE_VERSION "3.6.1"
-#define SQLITE_VERSION_NUMBER 3006001
+#define SQLITE_VERSION "3.6.2"
+#define SQLITE_VERSION_NUMBER 3006002
/*
** CAPI3REF: Run-Time Library Version Numbers {H10020} <S60100>
@@ -491,6 +516,7 @@
#define SQLITE_IOERR_NOMEM (SQLITE_IOERR | (12<<8))
#define SQLITE_IOERR_ACCESS (SQLITE_IOERR | (13<<8))
#define SQLITE_IOERR_CHECKRESERVEDLOCK (SQLITE_IOERR | (14<<8))
+#define SQLITE_IOERR_LOCK (SQLITE_IOERR | (15<<8))
/*
** CAPI3REF: Flags For File Open Operations {H10230} <H11120> <H12700>
@@ -513,6 +539,7 @@
#define SQLITE_OPEN_SUBJOURNAL 0x00002000
#define SQLITE_OPEN_MASTER_JOURNAL 0x00004000
#define SQLITE_OPEN_NOMUTEX 0x00008000
+#define SQLITE_OPEN_FULLMUTEX 0x00010000
/*
** CAPI3REF: Device Characteristics {H10240} <H11120>
@@ -966,7 +993,7 @@
** If the option is unknown or SQLite is unable to set the option
** then this routine returns a non-zero [error code].
*/
-int sqlite3_config(int, ...);
+SQLITE_EXPERIMENTAL int sqlite3_config(int, ...);
/*
** CAPI3REF: Configure database connections {H10180} <S20000>
@@ -987,7 +1014,7 @@
** New verbs are likely to be added in future releases of SQLite.
** Additional arguments depend on the verb.
*/
-int sqlite3_db_config(sqlite3*, int op, ...);
+SQLITE_EXPERIMENTAL int sqlite3_db_config(sqlite3*, int op, ...);
/*
** CAPI3REF: Memory Allocation Routines {H10155} <S20120>
@@ -2076,6 +2103,11 @@
** previous call. Disable the authorizer by installing a NULL callback.
** The authorizer is disabled by default.
**
+** When [sqlite3_prepare_v2()] is used to prepare a statement, the
+** statement might be reprepared during [sqlite3_step()] due to a
+** schema change. Hence, the application should ensure that the
+** correct authorizer callback remains in place during the [sqlite3_step()].
+**
** Note that the authorizer callback is invoked only during
** [sqlite3_prepare()] or its variants. Authorization is not
** performed during statement evaluation in [sqlite3_step()].
@@ -2086,11 +2118,11 @@
** authorizer callback with database connection D.
**
** {H12502} The authorizer callback is invoked as SQL statements are
-** being compiled.
+** being parseed and compiled.
**
** {H12503} If the authorizer callback returns any value other than
** [SQLITE_IGNORE], [SQLITE_OK], or [SQLITE_DENY], then
-** the [sqlite3_prepare_v2()] or equivalent call that caused
+** the application interface call that caused
** the authorizer callback to run shall fail with an
** [SQLITE_ERROR] error code and an appropriate error message.
**
@@ -2098,7 +2130,7 @@
** described is processed normally.
**
** {H12505} When the authorizer callback returns [SQLITE_DENY], the
-** [sqlite3_prepare_v2()] or equivalent call that caused the
+** application interface call that caused the
** authorizer callback to run shall fail
** with an [SQLITE_ERROR] error code and an error message
** explaining that access is denied.
@@ -2172,21 +2204,21 @@
** INVARIANTS:
**
** {H12551} The second parameter to an
-** [sqlite3_set_authorizer | authorizer callback] is always an integer
+** [sqlite3_set_authorizer | authorizer callback] shall be an integer
** [SQLITE_COPY | authorizer code] that specifies what action
** is being authorized.
**
** {H12552} The 3rd and 4th parameters to the
** [sqlite3_set_authorizer | authorization callback]
-** will be parameters or NULL depending on which
+** shall be parameters or NULL depending on which
** [SQLITE_COPY | authorizer code] is used as the second parameter.
**
** {H12553} The 5th parameter to the
-** [sqlite3_set_authorizer | authorizer callback] is the name
+** [sqlite3_set_authorizer | authorizer callback] shall be the name
** of the database (example: "main", "temp", etc.) if applicable.
**
** {H12554} The 6th parameter to the
-** [sqlite3_set_authorizer | authorizer callback] is the name
+** [sqlite3_set_authorizer | authorizer callback] shall be 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
** top-level SQL code.
@@ -2246,16 +2278,17 @@
**
** INVARIANTS:
**
-** {H12281} The callback function registered by [sqlite3_trace()] is
+** {H12281} The callback function registered by [sqlite3_trace()]
+** shall be invoked
** whenever an SQL statement first begins to execute and
** whenever a trigger subprogram first begins to run.
**
-** {H12282} Each call to [sqlite3_trace()] overrides the previously
+** {H12282} Each call to [sqlite3_trace()] shall override the previously
** registered trace callback.
**
-** {H12283} A NULL trace callback disables tracing.
+** {H12283} A NULL trace callback shall disable tracing.
**
-** {H12284} The first argument to the trace callback is a copy of
+** {H12284} The first argument to the trace callback shall be a copy of
** the pointer which was the 3rd argument to [sqlite3_trace()].
**
** {H12285} The second argument to the trace callback is a
@@ -2279,8 +2312,8 @@
** of the number of nanoseconds of wall-clock time required to
** run the SQL statement from start to finish.
*/
-void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*);
-void *sqlite3_profile(sqlite3*,
+SQLITE_EXPERIMENTAL void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*);
+SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*,
void(*xProfile)(void*,const char*,sqlite3_uint64), void*);
/*
@@ -2449,11 +2482,11 @@
** reading and writing if possible, or for reading only if the
** file is write protected by the operating system.
**
-** {H12713} If the G parameter to [sqlite3_open(v2(F,D,G,V)] omits the
+** {H12713} If the G parameter to [sqlite3_open_v2(F,D,G,V)] omits the
** bit value [SQLITE_OPEN_CREATE] and the database does not
** previously exist, an error is returned.
**
-** {H12714} If the G parameter to [sqlite3_open(v2(F,D,G,V)] contains the
+** {H12714} If the G parameter to [sqlite3_open_v2(F,D,G,V)] contains the
** bit value [SQLITE_OPEN_CREATE] and the database does not
** previously exist, then an attempt is made to create and
** initialize the database.
@@ -3947,12 +3980,12 @@
** the use of these functions. To help encourage people to avoid
** using these functions, we are not going to tell you want they do.
*/
-int sqlite3_aggregate_count(sqlite3_context*);
-int sqlite3_expired(sqlite3_stmt*);
-int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*);
-int sqlite3_global_recover(void);
-void sqlite3_thread_cleanup(void);
-int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int),void*,sqlite3_int64);
+SQLITE_DEPRECATED int sqlite3_aggregate_count(sqlite3_context*);
+SQLITE_DEPRECATED int sqlite3_expired(sqlite3_stmt*);
+SQLITE_DEPRECATED int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*);
+SQLITE_DEPRECATED int sqlite3_global_recover(void);
+SQLITE_DEPRECATED void sqlite3_thread_cleanup(void);
+SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int),void*,sqlite3_int64);
/*
** CAPI3REF: Obtaining SQL Function Parameter Values {H15100} <S20200>
@@ -5377,7 +5410,7 @@
** This interface is experimental and is subject to change or
** removal in future releases of SQLite.
*/
-int sqlite3_create_module(
+SQLITE_EXPERIMENTAL int sqlite3_create_module(
sqlite3 *db, /* SQLite connection to register module with */
const char *zName, /* Name of the module */
const sqlite3_module *, /* Methods for the module */
@@ -5392,7 +5425,7 @@
** except that it allows a destructor function to be specified. It is
** even more experimental than the rest of the virtual tables API.
*/
-int sqlite3_create_module_v2(
+SQLITE_EXPERIMENTAL int sqlite3_create_module_v2(
sqlite3 *db, /* SQLite connection to register module with */
const char *zName, /* Name of the module */
const sqlite3_module *, /* Methods for the module */
@@ -5464,7 +5497,7 @@
** This interface is experimental and is subject to change or
** removal in future releases of SQLite.
*/
-int sqlite3_declare_vtab(sqlite3*, const char *zCreateTable);
+SQLITE_EXPERIMENTAL int sqlite3_declare_vtab(sqlite3*, const char *zCreateTable);
/*
** CAPI3REF: Overload A Function For A Virtual Table {H18300} <S20400>
@@ -5485,7 +5518,7 @@
** This API should be considered part of the virtual table interface,
** which is experimental and subject to change.
*/
-int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg);
+SQLITE_EXPERIMENTAL int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg);
/*
** The interface to the virtual-table mechanism defined above (back up
@@ -6142,7 +6175,7 @@
**
** See also: [sqlite3_db_status()]
*/
-int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag);
+SQLITE_EXPERIMENTAL int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag);
/*
** CAPI3REF: Database Connection Status {H17201} <S60200>
@@ -6162,7 +6195,7 @@
**
** See also: [sqlite3_status()].
*/
-int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
+SQLITE_EXPERIMENTAL int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
/*
** CAPI3REF: Status Parameters {H17250} <H17200>
Modified: trunk/libgda/sqlite/virtual/gda-vprovider-data-model.c
==============================================================================
--- trunk/libgda/sqlite/virtual/gda-vprovider-data-model.c (original)
+++ trunk/libgda/sqlite/virtual/gda-vprovider-data-model.c Mon Sep 15 18:30:05 2008
@@ -619,10 +619,12 @@
TRACE ();
- gda_data_model_iter_move_next (cursor->iter);
- while (gda_data_model_iter_is_valid (cursor->iter) &&
- gda_data_proxy_row_is_deleted (vtable->proxy, gda_data_model_iter_get_row (cursor->iter)))
- gda_data_model_iter_move_next (cursor->iter);
+ if (!gda_data_model_iter_move_next (cursor->iter)) {
+ if (gda_data_model_iter_is_valid (cursor->iter))
+ return SQLITE_IOERR;
+ else
+ return SQLITE_OK;
+ }
return SQLITE_OK;
}
Modified: trunk/libgda/sqlite/virtual/virtual-test.c
==============================================================================
--- trunk/libgda/sqlite/virtual/virtual-test.c (original)
+++ trunk/libgda/sqlite/virtual/virtual-test.c Mon Sep 15 18:30:05 2008
@@ -41,7 +41,7 @@
GdaDataModelIter *iter;
iter = gda_data_model_create_iter (xml_model);
- gda_data_model_iter_set_at_row (iter, -1);
+ gda_data_model_iter_move_at_row (iter, -1);
for (gda_data_model_iter_move_next (iter); gda_data_model_iter_is_valid (iter);
gda_data_model_iter_move_next (iter)) {
g_print ("has row %d\n", gda_data_model_iter_get_row (iter));
@@ -61,7 +61,7 @@
GdaDataModelIter *iter;
iter = gda_data_model_create_iter (csv_model);
- gda_data_model_iter_set_at_row (iter, -1);
+ gda_data_model_iter_move_at_row (iter, -1);
for (gda_data_model_iter_move_next (iter); gda_data_model_iter_is_valid (iter);
gda_data_model_iter_move_next (iter)) {
g_print ("has row %d\n", gda_data_model_iter_get_row (iter));
Modified: trunk/tests/value-holders/check_holder.c
==============================================================================
--- trunk/tests/value-holders/check_holder.c (original)
+++ trunk/tests/value-holders/check_holder.c Mon Sep 15 18:30:05 2008
@@ -784,11 +784,11 @@
}
/*
- * "before_change" signal
+ * "validate-change" signal
*/
static GError *
-t10_before_change_cb (GdaHolder *h, const GValue *value, gchar *token)
+t10_validate_change_cb (GdaHolder *h, const GValue *value, gchar *token)
{
/* only accept GDA_VALUE_NULL or the "hi!" G_TYPE_STRING value */
g_assert (!strcmp (token, "AToken"));
@@ -828,8 +828,8 @@
gda_value_free (value);
/***/
- g_signal_connect (G_OBJECT (h), "before-change",
- G_CALLBACK (t10_before_change_cb), "AToken");
+ g_signal_connect (G_OBJECT (h), "validate-change",
+ G_CALLBACK (t10_validate_change_cb), "AToken");
/***/
value = gda_value_new_from_string ("my other string", G_TYPE_STRING);
Modified: trunk/tests/value-holders/check_set.c
==============================================================================
--- trunk/tests/value-holders/check_set.c (original)
+++ trunk/tests/value-holders/check_set.c Mon Sep 15 18:30:05 2008
@@ -153,10 +153,10 @@
}
/*
- * "before_holder_change" signal
+ * "validate-holder-change" signal
*/
static GError *
-t3_before_holder_change (GdaSet *set, GdaHolder *h, const GValue *value, gchar *token)
+t3_validate_holder_change (GdaSet *set, GdaHolder *h, const GValue *value, gchar *token)
{
/* only accept GDA_VALUE_NULL or 444 value */
g_assert (!strcmp (token, "MyToken"));
@@ -192,8 +192,8 @@
"H2", G_TYPE_INT, 1234,
"H3", G_TYPE_CHAR, 'r');
- g_signal_connect (G_OBJECT (set), "before-holder-change",
- G_CALLBACK (t3_before_holder_change), "MyToken");
+ g_signal_connect (G_OBJECT (set), "validate-holder-change",
+ G_CALLBACK (t3_validate_holder_change), "MyToken");
h = gda_set_get_holder (set, "H2");
g_value_set_int ((value = gda_value_new (G_TYPE_INT)), 333);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]