From buildbot at python.org Sat Apr 1 00:30:36 2006 From: buildbot at python.org (buildbot at python.org) Date: Fri, 31 Mar 2006 22:30:36 +0000 Subject: [Python-checkins] buildbot warnings in g4 osx.4 trunk Message-ID: <20060331223036.6A3011E4003@bag.python.org> The Buildbot has detected a new failure of g4 osx.4 trunk. Full details are available at: http://www.python.org/dev/buildbot/all/g4%20osx.4%20trunk/builds/212 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: georg.brandl Build Had Warnings: warnings test sincerely, -The Buildbot From buildbot at python.org Sat Apr 1 00:35:38 2006 From: buildbot at python.org (buildbot at python.org) Date: Fri, 31 Mar 2006 22:35:38 +0000 Subject: [Python-checkins] buildbot warnings in alpha Tru64 5.1 2.4 Message-ID: <20060331223538.F24B51E4003@bag.python.org> The Buildbot has detected a new failure of alpha Tru64 5.1 2.4. Full details are available at: http://www.python.org/dev/buildbot/all/alpha%20Tru64%205.1%202.4/builds/10 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch branches/release24-maint] HEAD Blamelist: georg.brandl Build Had Warnings: warnings test sincerely, -The Buildbot From python-checkins at python.org Sat Apr 1 00:38:21 2006 From: python-checkins at python.org (thomas.wouters) Date: Sat, 1 Apr 2006 00:38:21 +0200 (CEST) Subject: [Python-checkins] r43510 - python/trunk/Modules/posixmodule.c Message-ID: <20060331223821.18D141E4003@bag.python.org> Author: thomas.wouters Date: Sat Apr 1 00:38:19 2006 New Revision: 43510 Modified: python/trunk/Modules/posixmodule.c Log: In the fdopen(fd, 'a') case on UNIX, don't try to set fd's flags to -1 if fcntl() and fdopen() both fail. Will backport. Modified: python/trunk/Modules/posixmodule.c ============================================================================== --- python/trunk/Modules/posixmodule.c (original) +++ python/trunk/Modules/posixmodule.c Sat Apr 1 00:38:19 2006 @@ -5777,7 +5777,7 @@ if (flags != -1) fcntl(fd, F_SETFL, flags | O_APPEND); fp = fdopen(fd, mode); - if (fp == NULL) + if (fp == NULL && flags != -1) /* restore old mode if fdopen failed */ fcntl(fd, F_SETFL, flags); } else { From neal at metaslash.com Sat Apr 1 00:32:10 2006 From: neal at metaslash.com (Neal Norwitz) Date: Fri, 31 Mar 2006 17:32:10 -0500 Subject: [Python-checkins] Python Regression Test Failures all (1) Message-ID: <20060331223210.GA7681@python.psfb.org> test_grammar test_opcodes test_operations test_builtin test_exceptions test_types test_MimeWriter test_StringIO test___all__ test___future__ test__locale test_aepack test_aepack skipped -- No module named aepack test_al test_al skipped -- No module named al test_anydbm test_applesingle test_applesingle skipped -- No module named macostools test_array test_ast test_asynchat test_atexit test_audioop test_augassign test_base64 test_bastion test_binascii test_binhex test_binop test_bisect test_bool test_bsddb test_bsddb185 test_bsddb185 skipped -- No module named bsddb185 test_bsddb3 Exception in thread reader 0: Traceback (most recent call last): File "/home/neal/python/trunk/Lib/threading.py", line 467, in __bootstrap self.run() File "/home/neal/python/trunk/Lib/threading.py", line 447, in run self.__target(*self.__args, **self.__kwargs) File "/home/neal/python/trunk/Lib/bsddb/test/test_thread.py", line 275, in readerThread rec = dbutils.DeadlockWrap(c.next, max_retries=10) File "/home/neal/python/trunk/Lib/bsddb/dbutils.py", line 62, in DeadlockWrap return function(*_args, **_kwargs) DBLockDeadlockError: (-30996, 'DB_LOCK_DEADLOCK: Locker killed to resolve a deadlock') Exception in thread reader 3: Traceback (most recent call last): File "/home/neal/python/trunk/Lib/threading.py", line 467, in __bootstrap self.run() File "/home/neal/python/trunk/Lib/threading.py", line 447, in run self.__target(*self.__args, **self.__kwargs) File "/home/neal/python/trunk/Lib/bsddb/test/test_thread.py", line 275, in readerThread rec = dbutils.DeadlockWrap(c.next, max_retries=10) File "/home/neal/python/trunk/Lib/bsddb/dbutils.py", line 62, in DeadlockWrap return function(*_args, **_kwargs) DBLockDeadlockError: (-30996, 'DB_LOCK_DEADLOCK: Locker killed to resolve a deadlock') test_bufio test_bz2 test_cProfile test_calendar test_call test_capi test_cd test_cd skipped -- No module named cd test_cfgparser test_cgi test_charmapcodec test_cl test_cl skipped -- No module named cl test_class test_cmath test_cmd_line test_code test_codeccallbacks test_codecencodings_cn test_codecencodings_hk test_codecencodings_jp test_codecencodings_kr test_codecencodings_tw test_codecmaps_cn test_codecmaps_hk test_codecmaps_jp test_codecmaps_kr test_codecmaps_tw test_codecs test_codeop test_coding test_coercion test_colorsys test_commands test_compare test_compile test_compiler test_complex test_contains test_contextlib test_cookie test_cookielib test_copy test_copy_reg test_cpickle test_crypt test_csv test_ctypes test_datetime test_dbm test_decimal test_decorators test_defaultdict test_deque test_descr test_descrtut test_dict test_difflib test_dircache test_dis test_distutils test_dl test_doctest test_doctest2 test_dumbdbm test_dummy_thread test_dummy_threading test_email test_email_codecs test_email_renamed test_enumerate test_eof test_errno test_exception_variations test_extcall test_fcntl test_file test_filecmp test_fileinput test_float test_fnmatch test_fork1 test_format test_fpformat test_frozen test_funcattrs test_functional test_future test_gc test_gdbm test_generators test_genexps test_getargs test_getargs2 test_getopt test_gettext test_gl test_gl skipped -- No module named gl test_glob test_global test_grp test_gzip test_hash test_hashlib test_heapq test_hexoct test_hmac test_hotshot test_htmllib test_htmlparser test_httplib test_imageop test_imaplib test_imgfile test_imgfile skipped -- No module named imgfile test_imp test_import test_importhooks test_index test_inspect test_ioctl test_ioctl skipped -- Unable to open /dev/tty test_isinstance test_iter test_iterlen test_itertools test_largefile test_list test_locale test_logging test_long test_long_future test_longexp test_macfs test_macfs skipped -- No module named macfs test_macostools test_macostools skipped -- No module named macostools test_macpath test_mailbox test_marshal test_math test_md5 test_mhlib test_mimetools test_mimetypes test_minidom test_mmap test_module test_multibytecodec test_multibytecodec_support test_multifile test_mutants test_netrc test_new test_nis test_nis skipped -- Local domain name not set test_normalization test_ntpath test_openpty test_operator test_optparse test_os test_parser test_peepholer test_pep247 test_pep263 test_pep277 test_pep277 skipped -- test works only on NT+ test_pep292 test_pep352 test_pickle test_pickletools test_pkg test_pkgimport test_platform test_plistlib test_plistlib skipped -- No module named plistlib test_poll test_popen [9898 refs] [9898 refs] [9898 refs] test_popen2 test_posix test_posixpath test_pow test_pprint test_profile test_profilehooks test_pty test_pwd test_pyclbr test_pyexpat test_queue test_quopri [10984 refs] [10984 refs] test_random test_re test_repr test_resource test_rfc822 test_rgbimg test_richcmp test_robotparser test_runpy test_sax test_scope test_scriptpackages test_scriptpackages skipped -- No module named aetools test_select test_set test_sets test_sgmllib test_sha test_shelve test_shlex test_shutil test_signal test_site test_slice test_socket test_socket_ssl test test_socket_ssl crashed -- : timed out test_socketserver test_softspace test_sort test_str test_strftime test_string test_stringprep test_strop test_strptime test_struct test_structseq test_subprocess [9893 refs] [9895 refs] [9893 refs] [9893 refs] [9893 refs] [9893 refs] [9893 refs] [9894 refs] [9894 refs] [9893 refs] [9894 refs] [9893 refs] [10110 refs] [9894 refs] [9894 refs] [9894 refs] [9894 refs] [9894 refs] [9894 refs] [9894 refs] this bit of output is from a test of stdout in a different process ... [9894 refs] [9893 refs] [10110 refs] test_sunaudiodev test_sunaudiodev skipped -- No module named sunaudiodev test_sundry test_symtable test_syntax test_sys [9893 refs] [9893 refs] test_tarfile test_tcl test_tcl skipped -- No module named _tkinter test_tempfile [9895 refs] test_textwrap test_thread test_threaded_import test_threadedtempfile test_threading test_threading_local test_threadsignals test_time test_timeout test_tokenize test_trace test_traceback test_transformer test_tuple test_ucn test_unary test_unicode test_unicode_file test_unicode_file skipped -- No Unicode filesystem semantics on this platform. test_unicodedata test_unittest test_univnewlines test_unpack test_urllib test_urllib2 test_urllib2net test_urllibnet test_urlparse test_userdict test_userlist test_userstring test_uu test_wait3 test_wait4 test_warnings test_wave test_weakref test_whichdb test_winreg test_winreg skipped -- No module named _winreg test_winsound test_winsound skipped -- No module named winsound test_with test_xdrlib test_xml_etree test_xml_etree_c test_xmllib test_xmlrpc test_xpickle test_xrange test_zipfile test_zipimport test_zlib 287 tests OK. 1 test failed: test_socket_ssl 20 tests skipped: test_aepack test_al test_applesingle test_bsddb185 test_cd test_cl test_gl test_imgfile test_ioctl test_macfs test_macostools test_nis test_pep277 test_plistlib test_scriptpackages test_sunaudiodev test_tcl test_unicode_file test_winreg test_winsound 1 skip unexpected on linux2: test_ioctl [415265 refs] From python-checkins at python.org Sat Apr 1 00:39:56 2006 From: python-checkins at python.org (thomas.wouters) Date: Sat, 1 Apr 2006 00:39:56 +0200 (CEST) Subject: [Python-checkins] r43511 - python/branches/release24-maint/Modules/posixmodule.c Message-ID: <20060331223956.0530E1E4003@bag.python.org> Author: thomas.wouters Date: Sat Apr 1 00:39:56 2006 New Revision: 43511 Modified: python/branches/release24-maint/Modules/posixmodule.c Log: Backport trunk's r43510: In the fdopen(fd, 'a') case on UNIX, don't try to set fd's flags to -1 if fcntl() and fdopen() both fail. Modified: python/branches/release24-maint/Modules/posixmodule.c ============================================================================== --- python/branches/release24-maint/Modules/posixmodule.c (original) +++ python/branches/release24-maint/Modules/posixmodule.c Sat Apr 1 00:39:56 2006 @@ -5467,7 +5467,7 @@ if (flags != -1) fcntl(fd, F_SETFL, flags | O_APPEND); fp = fdopen(fd, mode); - if (fp == NULL) + if (fp == NULL && flags != -1) /* restore old mode if fdopen failed */ fcntl(fd, F_SETFL, flags); } else { From buildbot at python.org Sat Apr 1 01:00:18 2006 From: buildbot at python.org (buildbot at python.org) Date: Fri, 31 Mar 2006 23:00:18 +0000 Subject: [Python-checkins] buildbot warnings in amd64 gentoo 2.4 Message-ID: <20060331230019.102671E4003@bag.python.org> The Buildbot has detected a new failure of amd64 gentoo 2.4. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20gentoo%202.4/builds/54 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch branches/release24-maint] HEAD Blamelist: thomas.wouters Build Had Warnings: warnings test sincerely, -The Buildbot From buildbot at python.org Sat Apr 1 01:02:02 2006 From: buildbot at python.org (buildbot at python.org) Date: Fri, 31 Mar 2006 23:02:02 +0000 Subject: [Python-checkins] buildbot warnings in amd64 gentoo trunk Message-ID: <20060331230203.64BB81E4003@bag.python.org> The Buildbot has detected a new failure of amd64 gentoo trunk. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20gentoo%20trunk/builds/242 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: thomas.wouters Build Had Warnings: warnings test sincerely, -The Buildbot From buildbot at python.org Sat Apr 1 01:28:45 2006 From: buildbot at python.org (buildbot at python.org) Date: Fri, 31 Mar 2006 23:28:45 +0000 Subject: [Python-checkins] buildbot warnings in x86 gentoo trunk Message-ID: <20060331232846.1BFCD1E4003@bag.python.org> The Buildbot has detected a new failure of x86 gentoo trunk. Full details are available at: http://www.python.org/dev/buildbot/all/x86%20gentoo%20trunk/builds/237 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: thomas.wouters Build Had Warnings: warnings test sincerely, -The Buildbot From buildbot at python.org Sat Apr 1 01:29:01 2006 From: buildbot at python.org (buildbot at python.org) Date: Fri, 31 Mar 2006 23:29:01 +0000 Subject: [Python-checkins] buildbot warnings in x86 gentoo 2.4 Message-ID: <20060331232901.66E711E4003@bag.python.org> The Buildbot has detected a new failure of x86 gentoo 2.4. Full details are available at: http://www.python.org/dev/buildbot/all/x86%20gentoo%202.4/builds/54 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch branches/release24-maint] HEAD Blamelist: thomas.wouters Build Had Warnings: warnings test sincerely, -The Buildbot From buildbot at python.org Sat Apr 1 01:38:25 2006 From: buildbot at python.org (buildbot at python.org) Date: Fri, 31 Mar 2006 23:38:25 +0000 Subject: [Python-checkins] buildbot warnings in g4 osx.4 2.4 Message-ID: <20060331233825.2C4EC1E4003@bag.python.org> The Buildbot has detected a new failure of g4 osx.4 2.4. Full details are available at: http://www.python.org/dev/buildbot/all/g4%20osx.4%202.4/builds/51 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch branches/release24-maint] HEAD Blamelist: thomas.wouters Build Had Warnings: warnings test sincerely, -The Buildbot From python-checkins at python.org Sat Apr 1 02:26:54 2006 From: python-checkins at python.org (tim.peters) Date: Sat, 1 Apr 2006 02:26:54 +0200 (CEST) Subject: [Python-checkins] r43512 - in python/trunk: Lib/random.py Lib/test/test_random.py Misc/NEWS Message-ID: <20060401002654.9CD1A1E4003@bag.python.org> Author: tim.peters Date: Sat Apr 1 02:26:53 2006 New Revision: 43512 Modified: python/trunk/Lib/random.py python/trunk/Lib/test/test_random.py python/trunk/Misc/NEWS Log: Another crack at bug #1460340: make random.sample(dict) work, this time by ugly brute force. Modified: python/trunk/Lib/random.py ============================================================================== --- python/trunk/Lib/random.py (original) +++ python/trunk/Lib/random.py Sat Apr 1 02:26:53 2006 @@ -285,6 +285,15 @@ large population: sample(xrange(10000000), 60) """ + # XXX Although the documentation says `population` is "a sequence", + # XXX attempts are made to cater to any iterable with a __len__ + # XXX method. This has had mixed success. Examples from both + # XXX sides: sets work fine, and should become officially supported; + # XXX dicts are much harder, and have failed in various subtle + # XXX ways across attempts. Support for mapping types should probably + # XXX be dropped (and users should pass mapping.keys() or .values() + # XXX explicitly). + # Sampling without replacement entails tracking either potential # selections (the pool) in a list or previous selections in a set. @@ -304,7 +313,9 @@ setsize = 21 # size of a small set minus size of an empty list if k > 5: setsize += 4 ** _ceil(_log(k * 3, 4)) # table size for big sets - if n <= setsize: # is an n-length list smaller than a k-length set + if n <= setsize or hasattr(population, "keys"): + # An n-length list is smaller than a k-length set, or this is a + # mapping type so the other algorithm wouldn't work. pool = list(population) for i in xrange(k): # invariant: non-selected at [0,n-i) j = _int(random() * (n-i)) @@ -320,10 +331,10 @@ j = _int(random() * n) selected_add(j) result[i] = population[j] - except (TypeError, KeyError): # handle sets and dictionaries + except (TypeError, KeyError): # handle (at least) sets if isinstance(population, list): raise - return self.sample(list(population), k) + return self.sample(tuple(population), k) return result ## -------------------- real-valued distributions ------------------- Modified: python/trunk/Lib/test/test_random.py ============================================================================== --- python/trunk/Lib/test/test_random.py (original) +++ python/trunk/Lib/test/test_random.py Sat Apr 1 02:26:53 2006 @@ -93,12 +93,28 @@ self.gen.sample(set(range(20)), 2) self.gen.sample(range(20), 2) self.gen.sample(xrange(20), 2) - self.gen.sample(dict.fromkeys('abcdefghijklmnopqrst'), 2) self.gen.sample(str('abcdefghijklmnopqrst'), 2) self.gen.sample(tuple('abcdefghijklmnopqrst'), 2) + + def test_sample_on_dicts(self): + self.gen.sample(dict.fromkeys('abcdefghijklmnopqrst'), 2) + # SF bug #1460340 -- random.sample can raise KeyError a = dict.fromkeys(range(10)+range(10,100,2)+range(100,110)) - self.gen.sample(a,3) + self.gen.sample(a, 3) + + # A followup to bug #1460340: sampling from a dict could return + # a subset of its keys or of its values, depending on the size of + # the subset requested. + N = 30 + d = dict((i, complex(i, i)) for i in xrange(N)) + for k in xrange(N+1): + samp = self.gen.sample(d, k) + # Verify that we got ints back (keys); the values are complex. + for x in samp: + self.assert_(type(x) is int) + samp.sort() + self.assertEqual(samp, range(N)) def test_gauss(self): # Ensure that the seed() method initializes all the hidden state. In Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Sat Apr 1 02:26:53 2006 @@ -489,6 +489,11 @@ Library ------- +- Bug #1460340: ``random.sample(dict)`` failed in various ways. Dicts + aren't officially supported here, and trying to use them will probably + raise an exception some day. But dicts have been allowed, and "mostly + worked", so support for them won't go away without warning. + - Bug #1445068: getpass.getpass() can now be given an explicit stream argument to specify where to write the prompt. From python-checkins at python.org Sat Apr 1 02:41:11 2006 From: python-checkins at python.org (tim.peters) Date: Sat, 1 Apr 2006 02:41:11 +0200 (CEST) Subject: [Python-checkins] r43513 - in python/branches/release24-maint: Lib/random.py Lib/test/test_random.py Misc/NEWS Message-ID: <20060401004111.D78E01E4014@bag.python.org> Author: tim.peters Date: Sat Apr 1 02:41:10 2006 New Revision: 43513 Modified: python/branches/release24-maint/Lib/random.py python/branches/release24-maint/Lib/test/test_random.py python/branches/release24-maint/Misc/NEWS Log: Merge rev 43511 from the trunk. Another crack at bug #1460340: make random.sample(dict) work, this time by ugly brute force. Modified: python/branches/release24-maint/Lib/random.py ============================================================================== --- python/branches/release24-maint/Lib/random.py (original) +++ python/branches/release24-maint/Lib/random.py Sat Apr 1 02:41:10 2006 @@ -285,6 +285,15 @@ large population: sample(xrange(10000000), 60) """ + # XXX Although the documentation says `population` is "a sequence", + # XXX attempts are made to cater to any iterable with a __len__ + # XXX method. This has had mixed success. Examples from both + # XXX sides: sets work fine, and should become officially supported; + # XXX dicts are much harder, and have failed in various subtle + # XXX ways across attempts. Support for mapping types should probably + # XXX be dropped (and users should pass mapping.keys() or .values() + # XXX explicitly). + # Sampling without replacement entails tracking either potential # selections (the pool) in a list or previous selections in a # dictionary. @@ -302,7 +311,9 @@ random = self.random _int = int result = [None] * k - if n < 6 * k: # if n len list takes less space than a k len dict + if n < 6 * k or hasattr(population, "keys"): + # An n-length list is smaller than a k-length set, or this is a + # mapping type so the other algorithm wouldn't work. pool = list(population) for i in xrange(k): # invariant: non-selected at [0,n-i) j = _int(random() * (n-i)) @@ -316,10 +327,10 @@ while j in selected: j = _int(random() * n) result[i] = selected[j] = population[j] - except (TypeError, KeyError): # handle sets and dictionaries + except (TypeError, KeyError): # handle (at least) sets if isinstance(population, list): raise - return self.sample(list(population), k) + return self.sample(tuple(population), k) return result ## -------------------- real-valued distributions ------------------- Modified: python/branches/release24-maint/Lib/test/test_random.py ============================================================================== --- python/branches/release24-maint/Lib/test/test_random.py (original) +++ python/branches/release24-maint/Lib/test/test_random.py Sat Apr 1 02:41:10 2006 @@ -93,12 +93,28 @@ self.gen.sample(set(range(20)), 2) self.gen.sample(range(20), 2) self.gen.sample(xrange(20), 2) - self.gen.sample(dict.fromkeys('abcdefghijklmnopqrst'), 2) self.gen.sample(str('abcdefghijklmnopqrst'), 2) self.gen.sample(tuple('abcdefghijklmnopqrst'), 2) + + def test_sample_on_dicts(self): + self.gen.sample(dict.fromkeys('abcdefghijklmnopqrst'), 2) + # SF bug #1460340 -- random.sample can raise KeyError a = dict.fromkeys(range(10)+range(10,100,2)+range(100,110)) - self.gen.sample(a,3) + self.gen.sample(a, 3) + + # A followup to bug #1460340: sampling from a dict could return + # a subset of its keys or of its values, depending on the size of + # the subset requested. + N = 30 + d = dict((i, complex(i, i)) for i in xrange(N)) + for k in xrange(N+1): + samp = self.gen.sample(d, k) + # Verify that we got ints back (keys); the values are complex. + for x in samp: + self.assert_(type(x) is int) + samp.sort() + self.assertEqual(samp, range(N)) def test_gauss(self): # Ensure that the seed() method initializes all the hidden state. In Modified: python/branches/release24-maint/Misc/NEWS ============================================================================== --- python/branches/release24-maint/Misc/NEWS (original) +++ python/branches/release24-maint/Misc/NEWS Sat Apr 1 02:41:10 2006 @@ -4,6 +4,20 @@ (editors: check NEWS.help for information about editing NEWS using ReST.) +What's New in Python 2.4.4c1? +============================= + +*Release date: DD-MMM-2006* + +Library +------- + +- Bug #1460340: ``random.sample(dict)`` failed in various ways. Dicts + aren't officially supported here, and trying to use them will probably + raise an exception some day. But dicts have been allowed, and "mostly + worked", so support for them won't go away without warning. + + What's New in Python 2.4.3? =========================== @@ -14,10 +28,10 @@ - A few reference leaks were squished. -- A threading issue that caused random segfaults on some platforms from +- A threading issue that caused random segfaults on some platforms from the testsuite was fixed in test_capi. -- Reverted fix for Bug #1379994: Builtin unicode_escape and +- Reverted fix for Bug #1379994: Builtin unicode_escape and raw_unicode_escape codec now encodes backslash correctly. This caused another issue for unicode repr strings being double-escaped (SF Bug #1459029). Correct fix will be in 2.5, but is too risky for 2.4.3. From python-checkins at python.org Sat Apr 1 02:57:33 2006 From: python-checkins at python.org (anthony.baxter) Date: Sat, 1 Apr 2006 02:57:33 +0200 (CEST) Subject: [Python-checkins] r43514 - in python/trunk: Lib/sqlite3 Lib/sqlite3/test Lib/test/regrtest.py Lib/test/test_sqlite.py Misc/NEWS Modules/_sqlite setup.py Message-ID: <20060401005733.117151E4003@bag.python.org> Author: anthony.baxter Date: Sat Apr 1 02:57:31 2006 New Revision: 43514 Added: python/trunk/Lib/sqlite3/ (props changed) - copied from r43513, python/branches/sqlite-integration/Lib/sqlite3/ python/trunk/Lib/test/test_sqlite.py - copied unchanged from r43513, python/branches/sqlite-integration/Lib/test/test_sqlite.py python/trunk/Modules/_sqlite/ - copied from r43513, python/branches/sqlite-integration/Modules/_sqlite/ Modified: python/trunk/Lib/sqlite3/test/ (props changed) python/trunk/Lib/test/regrtest.py python/trunk/Misc/NEWS python/trunk/setup.py Log: merged the sqlite-integration branch. This is based on pysqlite2.1.3, and provides a DB-API interface in the standard library. You'll need sqlite 3.2.2 or later to build this - if you have an earlier version, the C extension module will not be built. Modified: python/trunk/Lib/test/regrtest.py ============================================================================== --- python/trunk/Lib/test/regrtest.py (original) +++ python/trunk/Lib/test/regrtest.py Sat Apr 1 02:57:31 2006 @@ -741,6 +741,7 @@ test_pwd test_resource test_signal + test_sqlite test_sunaudiodev test_threadsignals test_timing @@ -763,6 +764,7 @@ test_nis test_ntpath test_ossaudiodev + test_sqlite test_sunaudiodev """, 'mac': @@ -802,6 +804,7 @@ test_pwd test_resource test_signal + test_sqlite test_sunaudiodev test_sundry test_tarfile @@ -826,6 +829,7 @@ test_openpty test_pyexpat test_sax + test_sqlite test_sunaudiodev test_sundry """, @@ -848,6 +852,7 @@ test_openpty test_pyexpat test_sax + test_sqlite test_sunaudiodev test_sundry """, @@ -875,6 +880,7 @@ test_pyexpat test_queue test_sax + test_sqlite test_sunaudiodev test_sundry test_thread @@ -915,6 +921,7 @@ test_pty test_pwd test_strop + test_sqlite test_sunaudiodev test_sundry test_thread @@ -944,6 +951,7 @@ test_ntpath test_ossaudiodev test_poll + test_sqlite test_sunaudiodev """, 'sunos5': @@ -962,6 +970,7 @@ test_imgfile test_linuxaudiodev test_openpty + test_sqlite test_zipfile test_zlib """, @@ -988,6 +997,7 @@ test_openpty test_pyexpat test_sax + test_sqlite test_sunaudiodev test_zipfile test_zlib @@ -1013,6 +1023,7 @@ test_poll test_popen2 test_resource + test_sqlite test_sunaudiodev """, 'cygwin': @@ -1034,6 +1045,7 @@ test_nis test_ossaudiodev test_socketserver + test_sqlite test_sunaudiodev """, 'os2emx': @@ -1060,6 +1072,7 @@ test_pty test_resource test_signal + test_sqlite test_sunaudiodev """, 'freebsd4': @@ -1086,6 +1099,7 @@ test_scriptpackages test_socket_ssl test_socketserver + test_sqlite test_sunaudiodev test_tcl test_timeout @@ -1115,6 +1129,7 @@ test_macostools test_nis test_ossaudiodev + test_sqlite test_sunaudiodev test_tcl test_winreg @@ -1147,6 +1162,7 @@ test_plistlib test_scriptpackages test_tcl + test_sqlite test_sunaudiodev test_unicode_file test_winreg Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Sat Apr 1 02:57:31 2006 @@ -489,6 +489,11 @@ Library ------- +- Added the sqlite3 package. This is based on pysqlite2.1.3, and provides + a DB-API interface in the standard library. You'll need sqlite 3.2.2 or + later to build this - if you have an earlier version, the C extension + module will not be built. + - Bug #1460340: ``random.sample(dict)`` failed in various ways. Dicts aren't officially supported here, and trying to use them will probably raise an exception some day. But dicts have been allowed, and "mostly Modified: python/trunk/setup.py ============================================================================== --- python/trunk/setup.py (original) +++ python/trunk/setup.py Sat Apr 1 02:57:31 2006 @@ -689,6 +689,81 @@ dblibs = [] dblib_dir = None + # The sqlite interface + + # We hunt for "#define SQLITE_VERSION_NUMBER nnnnn" + # We need to find a version >= 3002002 (> sqlite version 3.2.2) + sqlite_incdir = sqlite_libdir = None + sqlite_inc_paths = [ '/usr/include', + '/usr/include/sqlite', + '/usr/include/sqlite3', + '/usr/local/include', + '/usr/local/include/sqlite', + '/usr/local/include/sqlite3', + ] + MIN_SQLITE_VERSION = 3002002 + for d in sqlite_inc_paths + inc_dirs: + f = os.path.join(d, "sqlite3.h") + if os.path.exists(f): + if db_setup_debug: print "found %s"%f + f = open(f).read() + m = re.search(r"#define\WSQLITE_VERSION_NUMBER\W(\d+)", f) + if m: + sqlite_version = int(m.group(1)) + if sqlite_version >= MIN_SQLITE_VERSION: + # we win! + print "%s/sqlite3.h: version %s"%(d, sqlite_version) + sqlite_incdir = d + break + else: + if db_setup_debug: + print "%s: version %d is too old, need >= %s"%(d, + sqlite_version, MIN_SQLITE_VERSION) + + if sqlite_incdir: + sqlite_dirs_to_check = [ + os.path.join(sqlite_incdir, '..', 'lib64'), + os.path.join(sqlite_incdir, '..', 'lib'), + os.path.join(sqlite_incdir, '..', '..', 'lib64'), + os.path.join(sqlite_incdir, '..', '..', 'lib'), + ] + sqlite_libfile = self.compiler.find_library_file( + sqlite_dirs_to_check + lib_dirs, 'sqlite3') + sqlite_libdir = [os.path.abspath(os.path.dirname(sqlite_libfile))] + + if sqlite_incdir and sqlite_libdir: + sqlite_srcs = ['_sqlite/adapters.c', + '_sqlite/cache.c', + '_sqlite/connection.c', + '_sqlite/converters.c', + '_sqlite/cursor.c', + '_sqlite/microprotocols.c', + '_sqlite/module.c', + '_sqlite/prepare_protocol.c', + '_sqlite/row.c', + '_sqlite/statement.c', + '_sqlite/util.c', ] + + PYSQLITE_VERSION = "2.1.3" + sqlite_defines = [] + if sys.platform != "win32": + sqlite_defines.append(('PYSQLITE_VERSION', + '"%s"' % PYSQLITE_VERSION)) + else: + sqlite_defines.append(('PYSQLITE_VERSION', + '\\"'+PYSQLITE_VERSION+'\\"')) + sqlite_defines.append(('PY_MAJOR_VERSION', + str(sys.version_info[0]))) + sqlite_defines.append(('PY_MINOR_VERSION', + str(sys.version_info[1]))) + + exts.append(Extension('_sqlite3', sqlite_srcs, + define_macros=sqlite_defines, + include_dirs=["Modules/_sqlite", + sqlite_incdir], + library_dirs=sqlite_libdir, + runtime_library_dirs=sqlite_libdir, + libraries=["sqlite3",])) # Look for Berkeley db 1.85. Note that it is built as a different # module name so it can be included even when later versions are From python-checkins at python.org Sat Apr 1 03:08:30 2006 From: python-checkins at python.org (anthony.baxter) Date: Sat, 1 Apr 2006 03:08:30 +0200 (CEST) Subject: [Python-checkins] r43515 - python/trunk/Include/pyport.h Message-ID: <20060401010830.943831E4003@bag.python.org> Author: anthony.baxter Date: Sat Apr 1 03:08:29 2006 New Revision: 43515 Modified: python/trunk/Include/pyport.h Log: Add a clause to the PY_FORMAT_SIZE_T to stop warnings on 32 bit intel linux with gcc 4.0.2, after talking to Tim. But it won't break anything anywhere, so don't worry :-) Modified: python/trunk/Include/pyport.h ============================================================================== --- python/trunk/Include/pyport.h (original) +++ python/trunk/Include/pyport.h Sat Apr 1 03:08:29 2006 @@ -124,7 +124,9 @@ * Py_ssize_t on the platform. */ #ifndef PY_FORMAT_SIZE_T -# if SIZEOF_SIZE_T == SIZEOF_LONG +# if SIZEOF_SIZE_T == SIZEOF_INT +# define PY_FORMAT_SIZE_T "" +# elif SIZEOF_SIZE_T == SIZEOF_LONG # define PY_FORMAT_SIZE_T "l" # elif defined(MS_WINDOWS) # define PY_FORMAT_SIZE_T "I" From buildbot at python.org Sat Apr 1 03:25:27 2006 From: buildbot at python.org (buildbot at python.org) Date: Sat, 01 Apr 2006 01:25:27 +0000 Subject: [Python-checkins] buildbot warnings in alpha Tru64 5.1 2.4 Message-ID: <20060401012527.47AD21E4003@bag.python.org> The Buildbot has detected a new failure of alpha Tru64 5.1 2.4. Full details are available at: http://www.python.org/dev/buildbot/all/alpha%20Tru64%205.1%202.4/builds/12 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch branches/release24-maint] HEAD Blamelist: tim.peters Build Had Warnings: warnings test sincerely, -The Buildbot From python-checkins at python.org Sat Apr 1 03:29:05 2006 From: python-checkins at python.org (tim.peters) Date: Sat, 1 Apr 2006 03:29:05 +0200 (CEST) Subject: [Python-checkins] r43516 - python/trunk/Lib/test/test_socket_ssl.py Message-ID: <20060401012905.741EC1E4003@bag.python.org> Author: tim.peters Date: Sat Apr 1 03:28:51 2006 New Revision: 43516 Modified: python/trunk/Lib/test/test_socket_ssl.py Log: test_timeout(): Disable this new test on all platforms. The s.connect(("gmail.org", 995)) line has been timing out on all buildbot slaves for hours now, causing the test to fail. Modified: python/trunk/Lib/test/test_socket_ssl.py ============================================================================== --- python/trunk/Lib/test/test_socket_ssl.py (original) +++ python/trunk/Lib/test/test_socket_ssl.py Sat Apr 1 03:28:51 2006 @@ -27,23 +27,27 @@ buf = f.read() f.close() -if not sys.platform.startswith('win'): +# XXX Tim disabled this test on all platforms, for now, since the +# XXX s.connect(("gmail.org", 995)) +# XXX line starting timing out on all the builbot slaves. +if 0: not sys.platform.startswith('win'): def test_timeout(): test_support.requires('network') s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(30.0) - # connect to service which issues an welcome banner (without need to write anything) + # connect to service which issues an welcome banner (without need to + # write anything) s.connect(("gmail.org", 995)) ss = socket.ssl(s) - # read part of return welcome banner twice,# read part of return welcome banner twice + # read part of return welcome banner twice ss.read(1) ss.read(1) s.close() else: def test_timeout(): pass - + def test_rude_shutdown(): try: import threading From python-checkins at python.org Sat Apr 1 03:31:18 2006 From: python-checkins at python.org (tim.peters) Date: Sat, 1 Apr 2006 03:31:18 +0200 (CEST) Subject: [Python-checkins] r43517 - python/branches/release24-maint/Lib/test/test_socket_ssl.py Message-ID: <20060401013118.D54BF1E4006@bag.python.org> Author: tim.peters Date: Sat Apr 1 03:31:04 2006 New Revision: 43517 Modified: python/branches/release24-maint/Lib/test/test_socket_ssl.py Log: Merge rev 43516 from trunk. test_timeout(): Disable this new test on all platforms. The s.connect(("gmail.org", 995)) line has been timing out on all buildbot slaves for hours now, causing the test to fail. Modified: python/branches/release24-maint/Lib/test/test_socket_ssl.py ============================================================================== --- python/branches/release24-maint/Lib/test/test_socket_ssl.py (original) +++ python/branches/release24-maint/Lib/test/test_socket_ssl.py Sat Apr 1 03:31:04 2006 @@ -28,23 +28,27 @@ buf = f.read() f.close() -if not sys.platform.startswith('win'): +# XXX Tim disabled this test on all platforms, for now, since the +# XXX s.connect(("gmail.org", 995)) +# XXX line starting timing out on all the builbot slaves. +if 0: not sys.platform.startswith('win'): def test_timeout(): test_support.requires('network') s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(30.0) - # connect to service which issues an welcome banner (without need to write anything) + # connect to service which issues an welcome banner (without need to + # write anything) s.connect(("gmail.org", 995)) ss = socket.ssl(s) - # read part of return welcome banner twice,# read part of return welcome banner twice + # read part of return welcome banner twice ss.read(1) ss.read(1) s.close() else: def test_timeout(): pass - + def test_rude_shutdown(): try: import thread From python-checkins at python.org Sat Apr 1 03:32:19 2006 From: python-checkins at python.org (tim.peters) Date: Sat, 1 Apr 2006 03:32:19 +0200 (CEST) Subject: [Python-checkins] r43518 - python/trunk/Lib/test/test_socket_ssl.py Message-ID: <20060401013219.A193D1E4003@bag.python.org> Author: tim.peters Date: Sat Apr 1 03:32:13 2006 New Revision: 43518 Modified: python/trunk/Lib/test/test_socket_ssl.py Log: Fix stupid typo. Modified: python/trunk/Lib/test/test_socket_ssl.py ============================================================================== --- python/trunk/Lib/test/test_socket_ssl.py (original) +++ python/trunk/Lib/test/test_socket_ssl.py Sat Apr 1 03:32:13 2006 @@ -30,7 +30,7 @@ # XXX Tim disabled this test on all platforms, for now, since the # XXX s.connect(("gmail.org", 995)) # XXX line starting timing out on all the builbot slaves. -if 0: not sys.platform.startswith('win'): +if 0: #not sys.platform.startswith('win'): def test_timeout(): test_support.requires('network') From python-checkins at python.org Sat Apr 1 03:35:11 2006 From: python-checkins at python.org (tim.peters) Date: Sat, 1 Apr 2006 03:35:11 +0200 (CEST) Subject: [Python-checkins] r43519 - python/branches/release24-maint/Lib/test/test_socket_ssl.py Message-ID: <20060401013511.F30251E4003@bag.python.org> Author: tim.peters Date: Sat Apr 1 03:35:10 2006 New Revision: 43519 Modified: python/branches/release24-maint/Lib/test/test_socket_ssl.py Log: Merge rev 43518 from trunk. Fix stupid typo. Modified: python/branches/release24-maint/Lib/test/test_socket_ssl.py ============================================================================== --- python/branches/release24-maint/Lib/test/test_socket_ssl.py (original) +++ python/branches/release24-maint/Lib/test/test_socket_ssl.py Sat Apr 1 03:35:10 2006 @@ -31,7 +31,7 @@ # XXX Tim disabled this test on all platforms, for now, since the # XXX s.connect(("gmail.org", 995)) # XXX line starting timing out on all the builbot slaves. -if 0: not sys.platform.startswith('win'): +if 0: #not sys.platform.startswith('win'): def test_timeout(): test_support.requires('network') From python-checkins at python.org Sat Apr 1 08:11:08 2006 From: python-checkins at python.org (fred.drake) Date: Sat, 1 Apr 2006 08:11:08 +0200 (CEST) Subject: [Python-checkins] r43520 - in python/trunk: Doc/lib/liburlparse.tex Lib/urlparse.py Message-ID: <20060401061108.C98B61E4003@bag.python.org> Author: fred.drake Date: Sat Apr 1 08:11:07 2006 New Revision: 43520 Modified: python/trunk/Doc/lib/liburlparse.tex python/trunk/Lib/urlparse.py Log: add support for the sips: scheme (identical to sip: except for scheme name) Modified: python/trunk/Doc/lib/liburlparse.tex ============================================================================== --- python/trunk/Doc/lib/liburlparse.tex (original) +++ python/trunk/Doc/lib/liburlparse.tex Sat Apr 1 08:11:07 2006 @@ -23,9 +23,9 @@ \code{file}, \code{ftp}, \code{gopher}, \code{hdl}, \code{http}, \code{https}, \code{imap}, \code{mailto}, \code{mms}, \code{news}, \code{nntp}, \code{prospero}, \code{rsync}, \code{rtsp}, \code{rtspu}, -\code{sftp}, \code{shttp}, \code{sip}, \code{snews}, \code{svn}, +\code{sftp}, \code{shttp}, \code{sip}, \code{sips}, \code{snews}, \code{svn}, \code{svn+ssh}, \code{telnet}, \code{wais}. -\versionadded[Support for the \code{sftp} scheme]{2.5} +\versionadded[Support for the \code{sftp} and \code{sips} schemes]{2.5} The \module{urlparse} module defines the following functions: Modified: python/trunk/Lib/urlparse.py ============================================================================== --- python/trunk/Lib/urlparse.py (original) +++ python/trunk/Lib/urlparse.py Sat Apr 1 08:11:07 2006 @@ -16,12 +16,12 @@ 'snews', 'prospero', 'rtsp', 'rtspu', 'rsync', '', 'svn', 'svn+ssh', 'sftp'] non_hierarchical = ['gopher', 'hdl', 'mailto', 'news', - 'telnet', 'wais', 'imap', 'snews', 'sip'] + 'telnet', 'wais', 'imap', 'snews', 'sip', 'sips'] uses_params = ['ftp', 'hdl', 'prospero', 'http', 'imap', - 'https', 'shttp', 'rtsp', 'rtspu', 'sip', + 'https', 'shttp', 'rtsp', 'rtspu', 'sip', 'sips', 'mms', '', 'sftp'] uses_query = ['http', 'wais', 'imap', 'https', 'shttp', 'mms', - 'gopher', 'rtsp', 'rtspu', 'sip', ''] + 'gopher', 'rtsp', 'rtspu', 'sip', 'sips', ''] uses_fragment = ['ftp', 'hdl', 'http', 'gopher', 'news', 'nntp', 'wais', 'https', 'shttp', 'snews', 'file', 'prospero', ''] From python-checkins at python.org Sat Apr 1 09:23:09 2006 From: python-checkins at python.org (georg.brandl) Date: Sat, 1 Apr 2006 09:23:09 +0200 (CEST) Subject: [Python-checkins] r43521 - python/trunk/Doc/ref/ref3.tex Message-ID: <20060401072309.378A91E4003@bag.python.org> Author: georg.brandl Date: Sat Apr 1 09:23:08 2006 New Revision: 43521 Modified: python/trunk/Doc/ref/ref3.tex Log: bug #1462278: small fix in documentation of __op__ vs __rop__ methods Modified: python/trunk/Doc/ref/ref3.tex ============================================================================== --- python/trunk/Doc/ref/ref3.tex (original) +++ python/trunk/Doc/ref/ref3.tex Sat Apr 1 09:23:08 2006 @@ -2057,14 +2057,15 @@ \item Exception to the previous item: if the left operand is an instance of -a built-in type or a new-style class, and the right operand is an -instance of a proper subclass of that type or class, the right -operand's \method{__rop__()} method is tried \emph{before} the left -operand's \method{__op__()} method. This is done so that a subclass can -completely override binary operators. Otherwise, the left operand's -__op__ method would always accept the right operand: when an instance -of a given class is expected, an instance of a subclass of that class -is always acceptable. +a built-in type or a new-style class, and the right operand is an instance +of a proper subclass of that type or class and overrides the base's +\method{__rop__()} method, the right operand's \method{__rop__()} method +is tried \emph{before} the left operand's \method{__op__()} method. + +This is done so that a subclass can completely override binary operators. +Otherwise, the left operand's \method{__op__()} method would always +accept the right operand: when an instance of a given class is expected, +an instance of a subclass of that class is always acceptable. \item From python-checkins at python.org Sat Apr 1 09:23:12 2006 From: python-checkins at python.org (georg.brandl) Date: Sat, 1 Apr 2006 09:23:12 +0200 (CEST) Subject: [Python-checkins] r43522 - python/branches/release24-maint/Doc/ref/ref3.tex Message-ID: <20060401072312.95E981E4003@bag.python.org> Author: georg.brandl Date: Sat Apr 1 09:23:12 2006 New Revision: 43522 Modified: python/branches/release24-maint/Doc/ref/ref3.tex Log: bug #1462278: small fix in documentation of __op__ vs __rop__ methods (backport from rev. 43521) Modified: python/branches/release24-maint/Doc/ref/ref3.tex ============================================================================== --- python/branches/release24-maint/Doc/ref/ref3.tex (original) +++ python/branches/release24-maint/Doc/ref/ref3.tex Sat Apr 1 09:23:12 2006 @@ -2005,14 +2005,15 @@ \item Exception to the previous item: if the left operand is an instance of -a built-in type or a new-style class, and the right operand is an -instance of a proper subclass of that type or class, the right -operand's \method{__rop__()} method is tried \emph{before} the left -operand's \method{__op__()} method. This is done so that a subclass can -completely override binary operators. Otherwise, the left operand's -__op__ method would always accept the right operand: when an instance -of a given class is expected, an instance of a subclass of that class -is always acceptable. +a built-in type or a new-style class, and the right operand is an instance +of a proper subclass of that type or class and overrides the base's +\method{__rop__()} method, the right operand's \method{__rop__()} method +is tried \emph{before} the left operand's \method{__op__()} method. + +This is done so that a subclass can completely override binary operators. +Otherwise, the left operand's \method{__op__()} method would always +accept the right operand: when an instance of a given class is expected, +an instance of a subclass of that class is always acceptable. \item From python-checkins at python.org Sat Apr 1 09:33:18 2006 From: python-checkins at python.org (georg.brandl) Date: Sat, 1 Apr 2006 09:33:18 +0200 (CEST) Subject: [Python-checkins] r43523 - in python/trunk: Doc/lib/libsocket.tex Modules/socketmodule.c Message-ID: <20060401073318.5EAAF1E4003@bag.python.org> Author: georg.brandl Date: Sat Apr 1 09:33:08 2006 New Revision: 43523 Modified: python/trunk/Doc/lib/libsocket.tex python/trunk/Modules/socketmodule.c Log: Bug #1460564: document that socket.fromfd() duplicates the given file descriptor. Modified: python/trunk/Doc/lib/libsocket.tex ============================================================================== --- python/trunk/Doc/lib/libsocket.tex (original) +++ python/trunk/Doc/lib/libsocket.tex Sat Apr 1 09:33:08 2006 @@ -317,10 +317,11 @@ \end{funcdesc} \begin{funcdesc}{fromfd}{fd, family, type\optional{, proto}} -Build a socket object from an existing file descriptor (an integer as -returned by a file object's \method{fileno()} method). Address family, -socket type and protocol number are as for the \function{socket()} function -above. The file descriptor should refer to a socket, but this is not +Duplicate the file descriptor \var{fd} (an integer as returned by a file +object's \method{fileno()} method) and build a socket object from the +result. Address family, socket type and protocol number are as for the +\function{socket()} function above. +The file descriptor should refer to a socket, but this is not checked --- subsequent operations on the object may fail if the file descriptor is invalid. This function is rarely needed, but can be used to get or set socket options on a socket passed to a program as Modified: python/trunk/Modules/socketmodule.c ============================================================================== --- python/trunk/Modules/socketmodule.c (original) +++ python/trunk/Modules/socketmodule.c Sat Apr 1 09:33:08 2006 @@ -3168,7 +3168,8 @@ PyDoc_STRVAR(fromfd_doc, "fromfd(fd, family, type[, proto]) -> socket object\n\ \n\ -Create a socket object from the given file descriptor.\n\ +Create a socket object from a duplicate of the given\n\ +file descriptor.\n\ The remaining arguments are the same as for socket()."); #endif /* NO_DUP */ @@ -4052,7 +4053,7 @@ PyModule_AddIntConstant(m, "NETLINK_IP6_FW", NETLINK_IP6_FW); PyModule_AddIntConstant(m, "NETLINK_DNRTMSG", NETLINK_DNRTMSG); PyModule_AddIntConstant(m, "NETLINK_TAPBASE", NETLINK_TAPBASE); -#endif +#endif /* AF_NETLINK */ #ifdef AF_ROUTE /* Alias to emulate 4.4BSD */ PyModule_AddIntConstant(m, "AF_ROUTE", AF_ROUTE); From python-checkins at python.org Sat Apr 1 09:33:25 2006 From: python-checkins at python.org (georg.brandl) Date: Sat, 1 Apr 2006 09:33:25 +0200 (CEST) Subject: [Python-checkins] r43524 - in python/branches/release24-maint: Doc/lib/libsocket.tex Modules/socketmodule.c Message-ID: <20060401073325.209801E4003@bag.python.org> Author: georg.brandl Date: Sat Apr 1 09:33:22 2006 New Revision: 43524 Modified: python/branches/release24-maint/Doc/lib/libsocket.tex python/branches/release24-maint/Modules/socketmodule.c Log: Bug #1460564: document that socket.fromfd() duplicates the given file descriptor. (backport from rev. 43523) Modified: python/branches/release24-maint/Doc/lib/libsocket.tex ============================================================================== --- python/branches/release24-maint/Doc/lib/libsocket.tex (original) +++ python/branches/release24-maint/Doc/lib/libsocket.tex Sat Apr 1 09:33:22 2006 @@ -314,10 +314,11 @@ \end{funcdesc} \begin{funcdesc}{fromfd}{fd, family, type\optional{, proto}} -Build a socket object from an existing file descriptor (an integer as -returned by a file object's \method{fileno()} method). Address family, -socket type and protocol number are as for the \function{socket()} function -above. The file descriptor should refer to a socket, but this is not +Duplicate the file descriptor \var{fd} (an integer as returned by a file +object's \method{fileno()} method) and build a socket object from the +result. Address family, socket type and protocol number are as for the +\function{socket()} function above. +The file descriptor should refer to a socket, but this is not checked --- subsequent operations on the object may fail if the file descriptor is invalid. This function is rarely needed, but can be used to get or set socket options on a socket passed to a program as Modified: python/branches/release24-maint/Modules/socketmodule.c ============================================================================== --- python/branches/release24-maint/Modules/socketmodule.c (original) +++ python/branches/release24-maint/Modules/socketmodule.c Sat Apr 1 09:33:22 2006 @@ -3113,7 +3113,8 @@ PyDoc_STRVAR(fromfd_doc, "fromfd(fd, family, type[, proto]) -> socket object\n\ \n\ -Create a socket object from the given file descriptor.\n\ +Create a socket object from a duplicate of the given\n\ +file descriptor.\n\ The remaining arguments are the same as for socket()."); #endif /* NO_DUP */ From python-checkins at python.org Sat Apr 1 09:39:42 2006 From: python-checkins at python.org (georg.brandl) Date: Sat, 1 Apr 2006 09:39:42 +0200 (CEST) Subject: [Python-checkins] r43525 - in python/trunk: Doc/lib/libzlib.tex Modules/zlibmodule.c Message-ID: <20060401073942.71A361E4003@bag.python.org> Author: georg.brandl Date: Sat Apr 1 09:39:41 2006 New Revision: 43525 Modified: python/trunk/Doc/lib/libzlib.tex python/trunk/Modules/zlibmodule.c Log: Patch #1459631: documnent zlib.Decompress.flush() length parameter. Modified: python/trunk/Doc/lib/libzlib.tex ============================================================================== --- python/trunk/Doc/lib/libzlib.tex (original) +++ python/trunk/Doc/lib/libzlib.tex Sat Apr 1 09:39:41 2006 @@ -166,11 +166,14 @@ decompressed, and \member{unconsumed_tail} is an empty string. \end{methoddesc} -\begin{methoddesc}[Decompress]{flush}{} +\begin{methoddesc}[Decompress]{flush}{\optional{length}} All pending input is processed, and a string containing the remaining uncompressed output is returned. After calling \method{flush()}, the \method{decompress()} method cannot be called again; the only realistic action is to delete the object. + +The optional parameter \var{length} sets the initial size of the +output buffer. \end{methoddesc} \begin{seealso} Modified: python/trunk/Modules/zlibmodule.c ============================================================================== --- python/trunk/Modules/zlibmodule.c (original) +++ python/trunk/Modules/zlibmodule.c Sat Apr 1 09:39:41 2006 @@ -654,7 +654,9 @@ } PyDoc_STRVAR(decomp_flush__doc__, -"flush() -- Return a string containing any remaining decompressed data.\n" +"flush( [length] ) -- Return a string containing any remaining\n" +"decompressed data. length, if given, is the initial size of the\n" +"output buffer.\n" "\n" "The decompressor object can no longer be used after this call."); From python-checkins at python.org Sat Apr 1 09:39:45 2006 From: python-checkins at python.org (georg.brandl) Date: Sat, 1 Apr 2006 09:39:45 +0200 (CEST) Subject: [Python-checkins] r43526 - in python/branches/release24-maint: Doc/lib/libzlib.tex Modules/zlibmodule.c Message-ID: <20060401073945.C6DDA1E4003@bag.python.org> Author: georg.brandl Date: Sat Apr 1 09:39:45 2006 New Revision: 43526 Modified: python/branches/release24-maint/Doc/lib/libzlib.tex python/branches/release24-maint/Modules/zlibmodule.c Log: Patch #1459631: documnent zlib.Decompress.flush() length parameter. (backport from rev. 43525) Modified: python/branches/release24-maint/Doc/lib/libzlib.tex ============================================================================== --- python/branches/release24-maint/Doc/lib/libzlib.tex (original) +++ python/branches/release24-maint/Doc/lib/libzlib.tex Sat Apr 1 09:39:45 2006 @@ -162,11 +162,14 @@ decompressed, and \member{unconsumed_tail} is an empty string. \end{methoddesc} -\begin{methoddesc}[Decompress]{flush}{} +\begin{methoddesc}[Decompress]{flush}{\optional{length}} All pending input is processed, and a string containing the remaining uncompressed output is returned. After calling \method{flush()}, the \method{decompress()} method cannot be called again; the only realistic action is to delete the object. + +The optional parameter \var{length} sets the initial size of the +output buffer. \end{methoddesc} \begin{seealso} Modified: python/branches/release24-maint/Modules/zlibmodule.c ============================================================================== --- python/branches/release24-maint/Modules/zlibmodule.c (original) +++ python/branches/release24-maint/Modules/zlibmodule.c Sat Apr 1 09:39:45 2006 @@ -654,7 +654,9 @@ } PyDoc_STRVAR(decomp_flush__doc__, -"flush() -- Return a string containing any remaining decompressed data.\n" +"flush( [length] ) -- Return a string containing any remaining\n" +"decompressed data. length, if given, is the initial size of the\n" +"output buffer.\n" "\n" "The decompressor object can no longer be used after this call."); From python-checkins at python.org Sat Apr 1 09:42:42 2006 From: python-checkins at python.org (georg.brandl) Date: Sat, 1 Apr 2006 09:42:42 +0200 (CEST) Subject: [Python-checkins] r43527 - python/trunk/Doc/lib/libsignal.tex Message-ID: <20060401074242.04B7B1E4003@bag.python.org> Author: georg.brandl Date: Sat Apr 1 09:42:41 2006 New Revision: 43527 Modified: python/trunk/Doc/lib/libsignal.tex Log: Patch #1462496: typo in libsignal.tex Modified: python/trunk/Doc/lib/libsignal.tex ============================================================================== --- python/trunk/Doc/lib/libsignal.tex (original) +++ python/trunk/Doc/lib/libsignal.tex Sat Apr 1 09:42:41 2006 @@ -100,7 +100,7 @@ Any previously scheduled alarm is canceled (only one alarm can be scheduled at any time). The returned value is then the number of seconds before any previously set alarm was to have been delivered. - If \var{time} is zero, no alarm id scheduled, and any scheduled + If \var{time} is zero, no alarm is scheduled, and any scheduled alarm is canceled. The return value is the number of seconds remaining before a previously scheduled alarm. If the return value is zero, no alarm is currently scheduled. (See the \UNIX{} man page From python-checkins at python.org Sat Apr 1 09:42:44 2006 From: python-checkins at python.org (georg.brandl) Date: Sat, 1 Apr 2006 09:42:44 +0200 (CEST) Subject: [Python-checkins] r43528 - python/branches/release24-maint/Doc/lib/libsignal.tex Message-ID: <20060401074244.852C81E4010@bag.python.org> Author: georg.brandl Date: Sat Apr 1 09:42:44 2006 New Revision: 43528 Modified: python/branches/release24-maint/Doc/lib/libsignal.tex Log: Patch #1462496: typo in libsignal.tex (backport from rev. 43527) Modified: python/branches/release24-maint/Doc/lib/libsignal.tex ============================================================================== --- python/branches/release24-maint/Doc/lib/libsignal.tex (original) +++ python/branches/release24-maint/Doc/lib/libsignal.tex Sat Apr 1 09:42:44 2006 @@ -100,7 +100,7 @@ Any previously scheduled alarm is canceled (only one alarm can be scheduled at any time). The returned value is then the number of seconds before any previously set alarm was to have been delivered. - If \var{time} is zero, no alarm id scheduled, and any scheduled + If \var{time} is zero, no alarm is scheduled, and any scheduled alarm is canceled. The return value is the number of seconds remaining before a previously scheduled alarm. If the return value is zero, no alarm is currently scheduled. (See the \UNIX{} man page From python-checkins at python.org Sat Apr 1 09:46:54 2006 From: python-checkins at python.org (georg.brandl) Date: Sat, 1 Apr 2006 09:46:54 +0200 (CEST) Subject: [Python-checkins] r43529 - python/trunk/Lib/distutils/log.py Message-ID: <20060401074654.EDD1D1E4003@bag.python.org> Author: georg.brandl Date: Sat Apr 1 09:46:54 2006 New Revision: 43529 Modified: python/trunk/Lib/distutils/log.py Log: Bug #1458017: make distutils.Log._log more forgiving when passing in msg strings with '%', but without format args. Modified: python/trunk/Lib/distutils/log.py ============================================================================== --- python/trunk/Lib/distutils/log.py (original) +++ python/trunk/Lib/distutils/log.py Sat Apr 1 09:46:54 2006 @@ -20,7 +20,12 @@ def _log(self, level, msg, args): if level >= self.threshold: - print msg % args + if not args: + # msg may contain a '%'. If args is empty, + # don't even try to string-format + print msg + else: + print msg % args sys.stdout.flush() def log(self, level, msg, *args): From python-checkins at python.org Sat Apr 1 09:46:57 2006 From: python-checkins at python.org (georg.brandl) Date: Sat, 1 Apr 2006 09:46:57 +0200 (CEST) Subject: [Python-checkins] r43530 - python/branches/release24-maint/Lib/distutils/log.py Message-ID: <20060401074657.7F9AD1E4003@bag.python.org> Author: georg.brandl Date: Sat Apr 1 09:46:57 2006 New Revision: 43530 Modified: python/branches/release24-maint/Lib/distutils/log.py Log: Bug #1458017: make distutils.Log._log more forgiving when passing in msg strings with '%', but without format args. (backport from rev. 43529) Modified: python/branches/release24-maint/Lib/distutils/log.py ============================================================================== --- python/branches/release24-maint/Lib/distutils/log.py (original) +++ python/branches/release24-maint/Lib/distutils/log.py Sat Apr 1 09:46:57 2006 @@ -20,7 +20,12 @@ def _log(self, level, msg, args): if level >= self.threshold: - print msg % args + if not args: + # msg may contain a '%'. If args is empty, + # don't even try to string-format + print msg + else: + print msg % args sys.stdout.flush() def log(self, level, msg, *args): From python-checkins at python.org Sat Apr 1 09:57:02 2006 From: python-checkins at python.org (walter.doerwald) Date: Sat, 1 Apr 2006 09:57:02 +0200 (CEST) Subject: [Python-checkins] r43531 - in python/trunk: Doc/lib/libcalendar.tex Lib/calendar.py Misc/NEWS Message-ID: <20060401075702.1C28E1E4006@bag.python.org> Author: walter.doerwald Date: Sat Apr 1 09:57:00 2006 New Revision: 43531 Modified: python/trunk/Doc/lib/libcalendar.tex python/trunk/Lib/calendar.py python/trunk/Misc/NEWS Log: Bug #947906: Add classes LocaleTextCalendar and LocaleHTMLCalendar, that output localized month and weekday names and can cope with encodings. Modified: python/trunk/Doc/lib/libcalendar.tex ============================================================================== --- python/trunk/Doc/lib/libcalendar.tex (original) +++ python/trunk/Doc/lib/libcalendar.tex Sat Apr 1 09:57:00 2006 @@ -176,6 +176,24 @@ \end{methoddesc} +\begin{classdesc}{LocaleTextCalendar}{\optional{firstweekday\optional{, locale}}} +This subclass of \class{TextCalendar} can be passed a locale name in the +constructor and will return month and weekday names in the specified locale. +If this locale includes an encoding all strings containing month and weekday +names will be returned as unicode. +\versionadded{2.5} +\end{classdesc} + + +\begin{classdesc}{LocaleHTMLCalendar}{\optional{firstweekday\optional{, locale}}} +This subclass of \class{HTMLCalendar} can be passed a locale name in the +constructor and will return month and weekday names in the specified locale. +If this locale includes an encoding all strings containing month and weekday +names will be returned as unicode. +\versionadded{2.5} +\end{classdesc} + + For simple text calendars this module provides the following functions. \begin{funcdesc}{setfirstweekday}{weekday} Modified: python/trunk/Lib/calendar.py ============================================================================== --- python/trunk/Lib/calendar.py (original) +++ python/trunk/Lib/calendar.py Sat Apr 1 09:57:00 2006 @@ -5,7 +5,7 @@ Sunday as the last (the European convention). Use setfirstweekday() to set the first day of the week (0=Monday, 6=Sunday).""" -import sys, datetime +import sys, datetime, locale __all__ = ["IllegalMonthError", "IllegalWeekdayError", "setfirstweekday", "firstweekday", "isleap", "leapdays", "weekday", "monthrange", @@ -297,11 +297,13 @@ """ return ' '.join(self.formatweekday(i, width) for i in self.iterweekdays()) - def formatmonthname(self, theyear, themonth, width): + def formatmonthname(self, theyear, themonth, width, withyear=True): """ Return a formatted month name. """ - s = "%s %r" % (month_name[themonth], theyear) + s = month_name[themonth] + if withyear: + s = "%s %r" % (s, theyear) return s.center(width) def prmonth(self, theyear, themonth, w=0, l=0): @@ -343,9 +345,12 @@ # months in this row months = xrange(m*i+1, min(m*(i+1)+1, 13)) a('\n'*l) - a(formatstring((month_name[k] for k in months), colwidth, c).rstrip()) + names = (self.formatmonthname(theyear, k, colwidth, False) + for k in months) + a(formatstring(names, colwidth, c).rstrip()) a('\n'*l) - a(formatstring((header for k in months), colwidth, c).rstrip()) + headers = (header for k in months) + a(formatstring(headers, colwidth, c).rstrip()) a('\n'*l) # max number of weeks for this row height = max(len(cal) for cal in row) @@ -474,7 +479,92 @@ a(self.formatyear(theyear, width)) a('\n') a('\n') - return ''.join(v).encode(encoding) + return ''.join(v).encode(encoding, "xmlcharrefreplace") + + +class LocaleTextCalendar(TextCalendar): + """ + This class can be passed a locale name in the constructor and will return + month and weekday names in the specified locale. If this locale includes + an encoding all strings containing month and weekday names will be returned + as unicode. + """ + + def __init__(self, firstweekday=0, locale=None): + TextCalendar.__init__(self, firstweekday) + if locale is None: + locale = locale.getdefaultlocale() + self.locale = locale + + def formatweekday(self, day, width): + oldlocale = locale.setlocale(locale.LC_TIME, self.locale) + try: + encoding = locale.getlocale(locale.LC_TIME)[1] + if width >= 9: + names = day_name + else: + names = day_abbr + name = names[day] + if encoding is not None: + name = name.decode(encoding) + result = name[:width].center(width) + finally: + locale.setlocale(locale.LC_TIME, oldlocale) + return result + + def formatmonthname(self, theyear, themonth, width, withyear=True): + oldlocale = locale.setlocale(locale.LC_TIME, self.locale) + try: + encoding = locale.getlocale(locale.LC_TIME)[1] + s = month_name[themonth] + if encoding is not None: + s = s.decode(encoding) + if withyear: + s = "%s %r" % (s, theyear) + result = s.center(width) + finally: + locale.setlocale(locale.LC_TIME, oldlocale) + return result + + +class LocaleHTMLCalendar(HTMLCalendar): + """ + This class can be passed a locale name in the constructor and will return + month and weekday names in the specified locale. If this locale includes + an encoding all strings containing month and weekday names will be returned + as unicode. + """ + def __init__(self, firstweekday=0, locale=None): + HTMLCalendar.__init__(self, firstweekday) + if locale is None: + locale = locale.getdefaultlocale() + self.locale = locale + + def formatweekday(self, day): + oldlocale = locale.setlocale(locale.LC_TIME, self.locale) + try: + encoding = locale.getlocale(locale.LC_TIME)[1] + s = day_abbr[day] + if encoding is not None: + s = s.decode(encoding) + result = '%s' % (self.cssclasses[day], s) + finally: + locale.setlocale(locale.LC_TIME, oldlocale) + return result + + def formatmonthname(self, theyear, themonth, withyear=True): + oldlocale = locale.setlocale(locale.LC_TIME, self.locale) + try: + encoding = locale.getlocale(locale.LC_TIME)[1] + s = month_name[themonth] + if encoding is not None: + s = s.decode(encoding) + if withyear: + s = '%s %s' % (s, theyear) + result = '%s' % s + finally: + locale.setlocale(locale.LC_TIME, oldlocale) + return result # Support for old module level interface @@ -524,34 +614,60 @@ def main(args): import optparse - parser = optparse.OptionParser(usage="usage: %prog [options] [year] [month]") - parser.add_option("-w", "--width", - dest="width", type="int", default=2, - help="width of date column (default 2, text only)") - parser.add_option("-l", "--lines", - dest="lines", type="int", default=1, - help="number of lines for each week (default 1, text only)") - parser.add_option("-s", "--spacing", - dest="spacing", type="int", default=6, - help="spacing between months (default 6, text only)") - parser.add_option("-m", "--months", - dest="months", type="int", default=3, - help="months per row (default 3, text only)") - parser.add_option("-c", "--css", - dest="css", default="calendar.css", - help="CSS to use for page (html only)") - parser.add_option("-e", "--encoding", - dest="encoding", default=None, - help="Encoding to use for CSS output (html only)") - parser.add_option("-t", "--type", - dest="type", default="text", - choices=("text", "html"), - help="output type (text or html)") + parser = optparse.OptionParser(usage="usage: %prog [options] [year [month]]") + parser.add_option( + "-w", "--width", + dest="width", type="int", default=2, + help="width of date column (default 2, text only)" + ) + parser.add_option( + "-l", "--lines", + dest="lines", type="int", default=1, + help="number of lines for each week (default 1, text only)" + ) + parser.add_option( + "-s", "--spacing", + dest="spacing", type="int", default=6, + help="spacing between months (default 6, text only)" + ) + parser.add_option( + "-m", "--months", + dest="months", type="int", default=3, + help="months per row (default 3, text only)" + ) + parser.add_option( + "-c", "--css", + dest="css", default="calendar.css", + help="CSS to use for page (html only)" + ) + parser.add_option( + "-L", "--locale", + dest="locale", default=None, + help="locale to be used from month and weekday names" + ) + parser.add_option( + "-e", "--encoding", + dest="encoding", default=None, + help="Encoding to use for output" + ) + parser.add_option( + "-t", "--type", + dest="type", default="text", + choices=("text", "html"), + help="output type (text or html)" + ) (options, args) = parser.parse_args(args) + if options.locale and not options.encoding: + parser.error("if --locale is specified --encoding is required") + sys.exit(1) + if options.type == "html": - cal = HTMLCalendar() + if options.locale: + cal = LocaleHTMLCalendar(locale=options.locale) + else: + cal = HTMLCalendar() encoding = options.encoding if encoding is None: encoding = sys.getdefaultencoding() @@ -564,20 +680,26 @@ parser.error("incorrect number of arguments") sys.exit(1) else: - cal = TextCalendar() + if options.locale: + cal = LocaleTextCalendar(locale=options.locale) + else: + cal = TextCalendar() optdict = dict(w=options.width, l=options.lines) if len(args) != 3: optdict["c"] = options.spacing optdict["m"] = options.months if len(args) == 1: - print cal.formatyear(datetime.date.today().year, **optdict) + result = cal.formatyear(datetime.date.today().year, **optdict) elif len(args) == 2: - print cal.formatyear(int(args[1]), **optdict) + result = cal.formatyear(int(args[1]), **optdict) elif len(args) == 3: - print cal.formatmonth(int(args[1]), int(args[2]), **optdict) + result = cal.formatmonth(int(args[1]), int(args[2]), **optdict) else: parser.error("incorrect number of arguments") sys.exit(1) + if options.encoding: + result = result.encode(options.encoding) + print result if __name__ == "__main__": Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Sat Apr 1 09:57:00 2006 @@ -920,7 +920,9 @@ - Bug #947906: An object oriented interface has been added to the calendar module. It's possible to generate HTML calendar now and the module can be - called as a script (e.g. via ``python -mcalendar``). + called as a script (e.g. via ``python -mcalendar``). Localized month and + weekday names can be ouput (even if an exotic encoding is used) using + special classes that use unicode. Build ----- From g.brandl at gmx.net Sat Apr 1 09:57:29 2006 From: g.brandl at gmx.net (Georg Brandl) Date: Sat, 01 Apr 2006 09:57:29 +0200 Subject: [Python-checkins] r43514 - in python/trunk: Lib/sqlite3 Lib/sqlite3/test Lib/test/regrtest.py Lib/test/test_sqlite.py Misc/NEWS Modules/_sqlite setup.py In-Reply-To: <20060401005733.117151E4003@bag.python.org> References: <20060401005733.117151E4003@bag.python.org> Message-ID: anthony.baxter wrote: > Author: anthony.baxter > Date: Sat Apr 1 02:57:31 2006 > New Revision: 43514 > > Added: > python/trunk/Lib/sqlite3/ (props changed) > - copied from r43513, python/branches/sqlite-integration/Lib/sqlite3/ > python/trunk/Lib/test/test_sqlite.py > - copied unchanged from r43513, python/branches/sqlite-integration/Lib/test/test_sqlite.py > python/trunk/Modules/_sqlite/ > - copied from r43513, python/branches/sqlite-integration/Modules/_sqlite/ > Modified: > python/trunk/Lib/sqlite3/test/ (props changed) > python/trunk/Lib/test/regrtest.py > python/trunk/Misc/NEWS > python/trunk/setup.py > Log: > merged the sqlite-integration branch. > This is based on pysqlite2.1.3, and provides a DB-API interface in > the standard library. You'll need sqlite 3.2.2 or later to build > this - if you have an earlier version, the C extension module will > not be built. One minor nit: the tp_name of sqlite3.Connection etc. is still "pysqlite2.dbapi2.Connection" etc. Do we care? Georg From python-checkins at python.org Sat Apr 1 10:35:20 2006 From: python-checkins at python.org (georg.brandl) Date: Sat, 1 Apr 2006 10:35:20 +0200 (CEST) Subject: [Python-checkins] r43532 - in python/trunk: Doc/lib/libsgmllib.tex Lib/sgmllib.py Lib/test/test_sgmllib.py Misc/NEWS Message-ID: <20060401083520.64E9A1E4003@bag.python.org> Author: georg.brandl Date: Sat Apr 1 10:35:18 2006 New Revision: 43532 Modified: python/trunk/Doc/lib/libsgmllib.tex python/trunk/Lib/sgmllib.py python/trunk/Lib/test/test_sgmllib.py python/trunk/Misc/NEWS Log: patch #1462498: handle entityrefs in attribute values. Modified: python/trunk/Doc/lib/libsgmllib.tex ============================================================================== --- python/trunk/Doc/lib/libsgmllib.tex (original) +++ python/trunk/Doc/lib/libsgmllib.tex Sat Apr 1 10:35:18 2006 @@ -95,12 +95,15 @@ should be used to support semantic interpretation of the start tag. The \var{attributes} argument is a list of \code{(\var{name}, \var{value})} pairs containing the attributes found inside the tag's -\code{<>} brackets. The \var{name} has been translated to lower case -and double quotes and backslashes in the \var{value} have been interpreted. +\code{<>} brackets. The \var{name} has been translated to lower case. +Double quotes and backslashes in the \var{value} have been interpreted, +as well as known entity and character references. For instance, for the tag \code{}, this method would be called as \samp{unknown_starttag('a', [('href', 'http://www.cwi.nl/')])}. The base implementation simply calls \var{method} with \var{attributes} as the only argument. +\versionadded[Handling of entity and character references within + attribute values]{2.5} \end{methoddesc} \begin{methoddesc}{handle_endtag}{tag, method} Modified: python/trunk/Lib/sgmllib.py ============================================================================== --- python/trunk/Lib/sgmllib.py (original) +++ python/trunk/Lib/sgmllib.py Sat Apr 1 10:35:18 2006 @@ -269,9 +269,37 @@ attrname, rest, attrvalue = match.group(1, 2, 3) if not rest: attrvalue = attrname - elif attrvalue[:1] == '\'' == attrvalue[-1:] or \ - attrvalue[:1] == '"' == attrvalue[-1:]: - attrvalue = attrvalue[1:-1] + else: + if (attrvalue[:1] == "'" == attrvalue[-1:] or + attrvalue[:1] == '"' == attrvalue[-1:]): + # strip quotes + attrvalue = attrvalue[1:-1] + l = 0 + new_attrvalue = '' + while l < len(attrvalue): + av_match = entityref.match(attrvalue, l) + if (av_match and av_match.group(1) in self.entitydefs and + attrvalue[av_match.end(1)] == ';'): + # only substitute entityrefs ending in ';' since + # otherwise we may break + # which is very common + new_attrvalue += self.entitydefs[av_match.group(1)] + l = av_match.end(0) + continue + ch_match = charref.match(attrvalue, l) + if ch_match: + try: + char = chr(int(ch_match.group(1))) + new_attrvalue += char + l = ch_match.end(0) + continue + except ValueError: + # invalid character reference, don't substitute + pass + # all other cases + new_attrvalue += attrvalue[l] + l += 1 + attrvalue = new_attrvalue attrs.append((attrname.lower(), attrvalue)) k = match.end(0) if rawdata[j] == '>': Modified: python/trunk/Lib/test/test_sgmllib.py ============================================================================== --- python/trunk/Lib/test/test_sgmllib.py (original) +++ python/trunk/Lib/test/test_sgmllib.py Sat Apr 1 10:35:18 2006 @@ -214,6 +214,20 @@ ("starttag", "e", [("a", "rgb(1,2,3)")]), ]) + def test_attr_values_entities(self): + """Substitution of entities and charrefs in attribute values""" + # SF bug #1452246 + self.check_events("""""", + [("starttag", "a", [("b", "<"), + ("c", "<>"), + ("d", "<->"), + ("e", "< "), + ("f", "&xxx;"), + ("g", " !"), + ("h", "Ǵ"), + ("i", "x?a=b&c=d;"), ])]) + def test_attr_funky_names(self): self.check_events("""""", [ ("starttag", "a", [("a.b", "v"), ("c:d", "v"), ("e-f", "v")]), Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Sat Apr 1 10:35:18 2006 @@ -489,6 +489,9 @@ Library ------- +- Patch #1462498: sgmllib now handles entity and character references + in attribute values. + - Added the sqlite3 package. This is based on pysqlite2.1.3, and provides a DB-API interface in the standard library. You'll need sqlite 3.2.2 or later to build this - if you have an earlier version, the C extension From python-checkins at python.org Sat Apr 1 10:36:28 2006 From: python-checkins at python.org (anthony.baxter) Date: Sat, 1 Apr 2006 10:36:28 +0200 (CEST) Subject: [Python-checkins] r43533 - in python/trunk: Modules/_sqlite/cursor.c Modules/_sqlite/statement.c Modules/_sqlite/statement.h setup.py Message-ID: <20060401083628.EEBA11E4013@bag.python.org> Author: anthony.baxter Date: Sat Apr 1 10:36:27 2006 New Revision: 43533 Modified: python/trunk/Modules/_sqlite/cursor.c python/trunk/Modules/_sqlite/statement.c python/trunk/Modules/_sqlite/statement.h python/trunk/setup.py Log: backport r243 from the pysqlite2 svn repository - lowers the required version of SQLite3 from 3.2.2 to 3.0.8, by providing an alternative to sqlite3_transfer_bindings. setup.py also handles the common (in debian and ubuntu, at least) case of a buggy sqlite3.h SQLITE_VERSION_NUMBER. Modified: python/trunk/Modules/_sqlite/cursor.c ============================================================================== --- python/trunk/Modules/_sqlite/cursor.c (original) +++ python/trunk/Modules/_sqlite/cursor.c Sat Apr 1 10:36:27 2006 @@ -24,8 +24,6 @@ #include "cursor.h" #include "module.h" #include "util.h" -#include "microprotocols.h" -#include "prepare_protocol.h" /* used to decide wether to call PyInt_FromLong or PyLong_FromLongLong */ #define INT32_MIN (-2147483647 - 1) @@ -189,53 +187,6 @@ } } -int _bind_parameter(Cursor* self, int pos, PyObject* parameter) -{ - int rc = SQLITE_OK; - long longval; -#ifdef HAVE_LONG_LONG - PY_LONG_LONG longlongval; -#endif - const char* buffer; - char* string; - int buflen; - PyObject* stringval; - - if (parameter == Py_None) { - rc = sqlite3_bind_null(self->statement->st, pos); - } else if (PyInt_Check(parameter)) { - longval = PyInt_AsLong(parameter); - rc = sqlite3_bind_int64(self->statement->st, pos, (sqlite_int64)longval); -#ifdef HAVE_LONG_LONG - } else if (PyLong_Check(parameter)) { - longlongval = PyLong_AsLongLong(parameter); - /* in the overflow error case, longlongval is -1, and an exception is set */ - rc = sqlite3_bind_int64(self->statement->st, pos, (sqlite_int64)longlongval); -#endif - } else if (PyFloat_Check(parameter)) { - rc = sqlite3_bind_double(self->statement->st, pos, PyFloat_AsDouble(parameter)); - } else if (PyBuffer_Check(parameter)) { - if (PyObject_AsCharBuffer(parameter, &buffer, &buflen) == 0) { - rc = sqlite3_bind_blob(self->statement->st, pos, buffer, buflen, SQLITE_TRANSIENT); - } else { - PyErr_SetString(PyExc_ValueError, "could not convert BLOB to buffer"); - rc = -1; - } - } else if PyString_Check(parameter) { - string = PyString_AsString(parameter); - rc = sqlite3_bind_text(self->statement->st, pos, string, -1, SQLITE_TRANSIENT); - } else if PyUnicode_Check(parameter) { - stringval = PyUnicode_AsUTF8String(parameter); - string = PyString_AsString(stringval); - rc = sqlite3_bind_text(self->statement->st, pos, string, -1, SQLITE_TRANSIENT); - Py_DECREF(stringval); - } else { - rc = -1; - } - - return rc; -} - PyObject* _build_column_name(const char* colname) { const char* pos; @@ -394,7 +345,6 @@ PyObject* parameters_list = NULL; PyObject* parameters_iter = NULL; PyObject* parameters = NULL; - int num_params; int i; int rc; PyObject* func_args; @@ -403,11 +353,7 @@ PY_LONG_LONG lastrowid; int statement_type; PyObject* descriptor; - PyObject* current_param; - PyObject* adapted; PyObject* second_argument = NULL; - int num_params_needed; - const char* binding_name; long rowcount = 0; if (!check_thread(self->connection) || !check_connection(self->connection)) { @@ -557,10 +503,6 @@ statement_reset(self->statement); statement_mark_dirty(self->statement); - Py_BEGIN_ALLOW_THREADS - num_params_needed = sqlite3_bind_parameter_count(self->statement->st); - Py_END_ALLOW_THREADS - while (1) { parameters = PyIter_Next(parameters_iter); if (!parameters) { @@ -569,71 +511,9 @@ statement_mark_dirty(self->statement); - if (PyDict_Check(parameters)) { - /* parameters passed as dictionary */ - for (i = 1; i <= num_params_needed; i++) { - Py_BEGIN_ALLOW_THREADS - binding_name = sqlite3_bind_parameter_name(self->statement->st, i); - Py_END_ALLOW_THREADS - if (!binding_name) { - PyErr_Format(ProgrammingError, "Binding %d has no name, but you supplied a dictionary (which has only names).", i); - goto error; - } - - binding_name++; /* skip first char (the colon) */ - current_param = PyDict_GetItemString(parameters, binding_name); - if (!current_param) { - PyErr_Format(ProgrammingError, "You did not supply a value for binding %d.", i); - goto error; - } - - Py_INCREF(current_param); - adapted = microprotocols_adapt(current_param, (PyObject*)&SQLitePrepareProtocolType, NULL); - if (adapted) { - Py_DECREF(current_param); - } else { - PyErr_Clear(); - adapted = current_param; - } - - rc = _bind_parameter(self, i, adapted); - Py_DECREF(adapted); - - if (rc != SQLITE_OK) { - PyErr_Format(InterfaceError, "Error binding parameter :%s - probably unsupported type.", binding_name); - goto error; - } - } - } else { - /* parameters passed as sequence */ - num_params = PySequence_Length(parameters); - if (num_params != num_params_needed) { - PyErr_Format(ProgrammingError, "Incorrect number of bindings supplied. The current statement uses %d, and there are %d supplied.", - num_params_needed, num_params); - goto error; - } - for (i = 0; i < num_params; i++) { - current_param = PySequence_GetItem(parameters, i); - if (!current_param) { - goto error; - } - adapted = microprotocols_adapt(current_param, (PyObject*)&SQLitePrepareProtocolType, NULL); - - if (adapted) { - Py_DECREF(current_param); - } else { - PyErr_Clear(); - adapted = current_param; - } - - rc = _bind_parameter(self, i + 1, adapted); - Py_DECREF(adapted); - - if (rc != SQLITE_OK) { - PyErr_Format(InterfaceError, "Error binding parameter %d - probably unsupported type.", i); - goto error; - } - } + statement_bind_parameters(self->statement, parameters); + if (PyErr_Occurred()) { + goto error; } build_row_cast_map(self); @@ -642,7 +522,7 @@ if (rc != SQLITE_DONE && rc != SQLITE_ROW) { rc = statement_reset(self->statement); if (rc == SQLITE_SCHEMA) { - rc = statement_recompile(self->statement); + rc = statement_recompile(self->statement, parameters); if (rc == SQLITE_OK) { rc = _sqlite_step_with_busyhandler(self->statement->st, self->connection); } else { Modified: python/trunk/Modules/_sqlite/statement.c ============================================================================== --- python/trunk/Modules/_sqlite/statement.c (original) +++ python/trunk/Modules/_sqlite/statement.c Sat Apr 1 10:36:27 2006 @@ -22,7 +22,10 @@ */ #include "statement.h" +#include "cursor.h" #include "connection.h" +#include "microprotocols.h" +#include "prepare_protocol.h" /* prototypes */ int check_remaining_sql(const char* tail); @@ -82,7 +85,136 @@ return rc; } -int statement_recompile(Statement* self) +int statement_bind_parameter(Statement* self, int pos, PyObject* parameter) +{ + int rc = SQLITE_OK; + long longval; +#ifdef HAVE_LONG_LONG + PY_LONG_LONG longlongval; +#endif + const char* buffer; + char* string; + int buflen; + PyObject* stringval; + + if (parameter == Py_None) { + rc = sqlite3_bind_null(self->st, pos); + } else if (PyInt_Check(parameter)) { + longval = PyInt_AsLong(parameter); + rc = sqlite3_bind_int64(self->st, pos, (sqlite_int64)longval); +#ifdef HAVE_LONG_LONG + } else if (PyLong_Check(parameter)) { + longlongval = PyLong_AsLongLong(parameter); + /* in the overflow error case, longlongval is -1, and an exception is set */ + rc = sqlite3_bind_int64(self->st, pos, (sqlite_int64)longlongval); +#endif + } else if (PyFloat_Check(parameter)) { + rc = sqlite3_bind_double(self->st, pos, PyFloat_AsDouble(parameter)); + } else if (PyBuffer_Check(parameter)) { + if (PyObject_AsCharBuffer(parameter, &buffer, &buflen) == 0) { + rc = sqlite3_bind_blob(self->st, pos, buffer, buflen, SQLITE_TRANSIENT); + } else { + PyErr_SetString(PyExc_ValueError, "could not convert BLOB to buffer"); + rc = -1; + } + } else if PyString_Check(parameter) { + string = PyString_AsString(parameter); + rc = sqlite3_bind_text(self->st, pos, string, -1, SQLITE_TRANSIENT); + } else if PyUnicode_Check(parameter) { + stringval = PyUnicode_AsUTF8String(parameter); + string = PyString_AsString(stringval); + rc = sqlite3_bind_text(self->st, pos, string, -1, SQLITE_TRANSIENT); + Py_DECREF(stringval); + } else { + rc = -1; + } + + return rc; +} + +void statement_bind_parameters(Statement* self, PyObject* parameters) +{ + PyObject* current_param; + PyObject* adapted; + const char* binding_name; + int i; + int rc; + int num_params_needed; + int num_params; + + Py_BEGIN_ALLOW_THREADS + num_params_needed = sqlite3_bind_parameter_count(self->st); + Py_END_ALLOW_THREADS + + if (PyDict_Check(parameters)) { + /* parameters passed as dictionary */ + for (i = 1; i <= num_params_needed; i++) { + Py_BEGIN_ALLOW_THREADS + binding_name = sqlite3_bind_parameter_name(self->st, i); + Py_END_ALLOW_THREADS + if (!binding_name) { + PyErr_Format(ProgrammingError, "Binding %d has no name, but you supplied a dictionary (which has only names).", i); + return; + } + + binding_name++; /* skip first char (the colon) */ + current_param = PyDict_GetItemString(parameters, binding_name); + if (!current_param) { + PyErr_Format(ProgrammingError, "You did not supply a value for binding %d.", i); + return; + } + + Py_INCREF(current_param); + adapted = microprotocols_adapt(current_param, (PyObject*)&SQLitePrepareProtocolType, NULL); + if (adapted) { + Py_DECREF(current_param); + } else { + PyErr_Clear(); + adapted = current_param; + } + + rc = statement_bind_parameter(self, i, adapted); + Py_DECREF(adapted); + + if (rc != SQLITE_OK) { + PyErr_Format(InterfaceError, "Error binding parameter :%s - probably unsupported type.", binding_name); + return; + } + } + } else { + /* parameters passed as sequence */ + num_params = PySequence_Length(parameters); + if (num_params != num_params_needed) { + PyErr_Format(ProgrammingError, "Incorrect number of bindings supplied. The current statement uses %d, and there are %d supplied.", + num_params_needed, num_params); + return; + } + for (i = 0; i < num_params; i++) { + current_param = PySequence_GetItem(parameters, i); + if (!current_param) { + return; + } + adapted = microprotocols_adapt(current_param, (PyObject*)&SQLitePrepareProtocolType, NULL); + + if (adapted) { + Py_DECREF(current_param); + } else { + PyErr_Clear(); + adapted = current_param; + } + + rc = statement_bind_parameter(self, i + 1, adapted); + Py_DECREF(adapted); + + if (rc != SQLITE_OK) { + PyErr_Format(InterfaceError, "Error binding parameter %d - probably unsupported type.", i); + return; + } + } + } +} + +int statement_recompile(Statement* self, PyObject* params) { const char* tail; int rc; @@ -98,7 +230,17 @@ &tail); if (rc == SQLITE_OK) { + /* The efficient sqlite3_transfer_bindings is only available in SQLite + * version 3.2.2 or later. For older SQLite releases, that might not + * even define SQLITE_VERSION_NUMBER, we do it the manual way. + */ + #ifdef SQLITE_VERSION_NUMBER + #if SQLITE_VERSION_NUMBER >= 3002002 (void)sqlite3_transfer_bindings(self->st, new_st); + #endif + #else + statement_bind_parameters(self, params); + #endif (void)sqlite3_finalize(self->st); self->st = new_st; Modified: python/trunk/Modules/_sqlite/statement.h ============================================================================== --- python/trunk/Modules/_sqlite/statement.h (original) +++ python/trunk/Modules/_sqlite/statement.h Sat Apr 1 10:36:27 2006 @@ -45,7 +45,10 @@ int statement_create(Statement* self, Connection* connection, PyObject* sql); void statement_dealloc(Statement* self); -int statement_recompile(Statement* self); +int statement_bind_parameter(Statement* self, int pos, PyObject* parameter); +void statement_bind_parameters(Statement* self, PyObject* parameters); + +int statement_recompile(Statement* self, PyObject* parameters); int statement_finalize(Statement* self); int statement_reset(Statement* self); void statement_mark_dirty(Statement* self); Modified: python/trunk/setup.py ============================================================================== --- python/trunk/setup.py (original) +++ python/trunk/setup.py Sat Apr 1 10:36:27 2006 @@ -690,6 +690,7 @@ dblib_dir = None # The sqlite interface + sqlite_setup_debug = True # verbose debug prints from this script? # We hunt for "#define SQLITE_VERSION_NUMBER nnnnn" # We need to find a version >= 3002002 (> sqlite version 3.2.2) @@ -701,22 +702,37 @@ '/usr/local/include/sqlite', '/usr/local/include/sqlite3', ] - MIN_SQLITE_VERSION = 3002002 + MIN_SQLITE_VERSION_NUMBER = 3000008 + MIN_SQLITE_VERSION = "3.0.8" for d in sqlite_inc_paths + inc_dirs: f = os.path.join(d, "sqlite3.h") if os.path.exists(f): - if db_setup_debug: print "found %s"%f + if sqlite_setup_debug: print "sqlite: found %s"%f f = open(f).read() m = re.search(r"#define\WSQLITE_VERSION_NUMBER\W(\d+)", f) if m: sqlite_version = int(m.group(1)) - if sqlite_version >= MIN_SQLITE_VERSION: + if sqlite_version >= MIN_SQLITE_VERSION_NUMBER: # we win! print "%s/sqlite3.h: version %s"%(d, sqlite_version) sqlite_incdir = d break + elif sqlite_version == 3000000: + # Bug in a common version out there where + # SQLITE_VERSION_NUMBER was set incorrectly + if sqlite_setup_debug: + print "found buggy SQLITE_VERSION_NUMBER, checking" + m = re.search(r'#define\WSQLITE_VERSION\W+"([\.\d]+)"', + f) + if m: + sqlite_version = m.group(1) + if sqlite_version >= MIN_SQLITE_VERSION: + print "%s/sqlite3.h: version %s"%(d, + sqlite_version) + sqlite_incdir = d + break else: - if db_setup_debug: + if sqlite_setup_debug: print "%s: version %d is too old, need >= %s"%(d, sqlite_version, MIN_SQLITE_VERSION) From python-checkins at python.org Sat Apr 1 10:39:52 2006 From: python-checkins at python.org (georg.brandl) Date: Sat, 1 Apr 2006 10:39:52 +0200 (CEST) Subject: [Python-checkins] r43534 - python/trunk/Doc/lib/libsgmllib.tex Message-ID: <20060401083952.793AB1E4003@bag.python.org> Author: georg.brandl Date: Sat Apr 1 10:39:50 2006 New Revision: 43534 Modified: python/trunk/Doc/lib/libsgmllib.tex Log: clarify new docs on attr value entity expansion Modified: python/trunk/Doc/lib/libsgmllib.tex ============================================================================== --- python/trunk/Doc/lib/libsgmllib.tex (original) +++ python/trunk/Doc/lib/libsgmllib.tex Sat Apr 1 10:39:50 2006 @@ -95,9 +95,16 @@ should be used to support semantic interpretation of the start tag. The \var{attributes} argument is a list of \code{(\var{name}, \var{value})} pairs containing the attributes found inside the tag's -\code{<>} brackets. The \var{name} has been translated to lower case. +\code{<>} brackets. + +The \var{name} has been translated to lower case. Double quotes and backslashes in the \var{value} have been interpreted, -as well as known entity and character references. +as well as known character references and known entity references +terminated by a semicolon (normally, entity references can be terminated +by any non-alphanumerical character, but this would break the very +common case of \code{} when \code{eggs} +is a valid entity name). + For instance, for the tag \code{}, this method would be called as \samp{unknown_starttag('a', [('href', 'http://www.cwi.nl/')])}. The base implementation simply calls From python-checkins at python.org Sat Apr 1 10:50:37 2006 From: python-checkins at python.org (neal.norwitz) Date: Sat, 1 Apr 2006 10:50:37 +0200 (CEST) Subject: [Python-checkins] r43535 - in python/branches/release24-maint: Misc/NEWS configure configure.in Message-ID: <20060401085037.15AC91E401A@bag.python.org> Author: neal.norwitz Date: Sat Apr 1 10:50:35 2006 New Revision: 43535 Modified: python/branches/release24-maint/Misc/NEWS python/branches/release24-maint/configure python/branches/release24-maint/configure.in Log: Backport 43470: Get gcc to do strict IEEE math. This flag was already used for cc, just not gcc. Without this flag, doing pretty much anything with NaNs causes a Floating Point Exception signal. This causes the interpreter to quit. The failing tests this fixes are: test_long. This is somewhat equivalent to doing signal(SIGFPE, SIG_IGN). Modified: python/branches/release24-maint/Misc/NEWS ============================================================================== --- python/branches/release24-maint/Misc/NEWS (original) +++ python/branches/release24-maint/Misc/NEWS Sat Apr 1 10:50:35 2006 @@ -9,6 +9,12 @@ *Release date: DD-MMM-2006* +Core and builtins +----------------- + +Extension Modules +----------------- + Library ------- @@ -17,6 +23,14 @@ raise an exception some day. But dicts have been allowed, and "mostly worked", so support for them won't go away without warning. +Tools/Demos +----------- + +Build +----- + +- Fix test_long failure on Tru64 with gcc by using -mieee gcc option. + What's New in Python 2.4.3? =========================== Modified: python/branches/release24-maint/configure ============================================================================== --- python/branches/release24-maint/configure (original) +++ python/branches/release24-maint/configure Sat Apr 1 10:50:35 2006 @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.in Revision: 41985 . +# From configure.in Revision: 43158 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.59 for python 2.4. # @@ -3839,6 +3839,9 @@ Darwin*) BASECFLAGS="$BASECFLAGS -Wno-long-double -no-cpp-precomp -mno-fused-madd" ;; + OSF*) + BASECFLAGS="$BASECFLAGS -mieee" + ;; esac ;; Modified: python/branches/release24-maint/configure.in ============================================================================== --- python/branches/release24-maint/configure.in (original) +++ python/branches/release24-maint/configure.in Sat Apr 1 10:50:35 2006 @@ -737,6 +737,9 @@ Darwin*) BASECFLAGS="$BASECFLAGS -Wno-long-double -no-cpp-precomp -mno-fused-madd" ;; + OSF*) + BASECFLAGS="$BASECFLAGS -mieee" + ;; esac ;; From python-checkins at python.org Sat Apr 1 10:51:26 2006 From: python-checkins at python.org (georg.brandl) Date: Sat, 1 Apr 2006 10:51:26 +0200 (CEST) Subject: [Python-checkins] r43536 - python/trunk/configure python/trunk/configure.in Message-ID: <20060401085126.90ECE1E4003@bag.python.org> Author: georg.brandl Date: Sat Apr 1 10:51:25 2006 New Revision: 43536 Modified: python/trunk/configure python/trunk/configure.in Log: patch #1416559: don't define _XOPEN_SOURCE on Mac OS Panther Modified: python/trunk/configure ============================================================================== --- python/trunk/configure (original) +++ python/trunk/configure Sat Apr 1 10:51:25 2006 @@ -1513,7 +1513,9 @@ ;; # On Mac OS X 10.4, defining _POSIX_C_SOURCE or _XOPEN_SOURCE # disables platform specific features beyond repair. - Darwin/8.*) + # On Mac OS X 10.3, defining _POSIX_C_SOURCE or _XOPEN_SOURCE + # has no effect, don't bother defineing them + Darwin/[78].*) define_xopen_source=no ;; Modified: python/trunk/configure.in ============================================================================== --- python/trunk/configure.in (original) +++ python/trunk/configure.in Sat Apr 1 10:51:25 2006 @@ -173,7 +173,9 @@ ;; # On Mac OS X 10.4, defining _POSIX_C_SOURCE or _XOPEN_SOURCE # disables platform specific features beyond repair. - Darwin/8.*) + # On Mac OS X 10.3, defining _POSIX_C_SOURCE or _XOPEN_SOURCE + # has no effect, don't bother defineing them + Darwin/[78].*) define_xopen_source=no ;; From python-checkins at python.org Sat Apr 1 10:51:30 2006 From: python-checkins at python.org (georg.brandl) Date: Sat, 1 Apr 2006 10:51:30 +0200 (CEST) Subject: [Python-checkins] r43537 - python/branches/release24-maint/configure python/branches/release24-maint/configure.in Message-ID: <20060401085130.05F971E4003@bag.python.org> Author: georg.brandl Date: Sat Apr 1 10:51:29 2006 New Revision: 43537 Modified: python/branches/release24-maint/configure python/branches/release24-maint/configure.in Log: patch #1416559: don't define _XOPEN_SOURCE on Mac OS Panther (backport from rev. 43536) Modified: python/branches/release24-maint/configure ============================================================================== --- python/branches/release24-maint/configure (original) +++ python/branches/release24-maint/configure Sat Apr 1 10:51:29 2006 @@ -1513,7 +1513,9 @@ ;; # On Mac OS X 10.4, defining _POSIX_C_SOURCE or _XOPEN_SOURCE # disables platform specific features beyond repair. - Darwin/8.*) + # On Mac OS X 10.3, defining _POSIX_C_SOURCE or _XOPEN_SOURCE + # has no effect, don't bother defineing them + Darwin/[78].*) define_xopen_source=no ;; Modified: python/branches/release24-maint/configure.in ============================================================================== --- python/branches/release24-maint/configure.in (original) +++ python/branches/release24-maint/configure.in Sat Apr 1 10:51:29 2006 @@ -173,7 +173,9 @@ ;; # On Mac OS X 10.4, defining _POSIX_C_SOURCE or _XOPEN_SOURCE # disables platform specific features beyond repair. - Darwin/8.*) + # On Mac OS X 10.3, defining _POSIX_C_SOURCE or _XOPEN_SOURCE + # has no effect, don't bother defineing them + Darwin/[78].*) define_xopen_source=no ;; From buildbot at python.org Sat Apr 1 10:55:38 2006 From: buildbot at python.org (buildbot at python.org) Date: Sat, 01 Apr 2006 08:55:38 +0000 Subject: [Python-checkins] buildbot warnings in amd64 gentoo trunk Message-ID: <20060401085538.24E2F1E4003@bag.python.org> The Buildbot has detected a new failure of amd64 gentoo trunk. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20gentoo%20trunk/builds/253 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: The web-page 'force build' button was pressed by 'anthony baxter': sqlite testing Build Source Stamp: [branch trunk] HEAD Blamelist: Build Had Warnings: warnings test sincerely, -The Buildbot From anthony at interlink.com.au Sat Apr 1 10:56:07 2006 From: anthony at interlink.com.au (Anthony Baxter) Date: Sat, 1 Apr 2006 19:56:07 +1100 Subject: [Python-checkins] r43514 - in python/trunk: Lib/sqlite3 Lib/sqlite3/test Lib/test/regrtest.py Lib/test/test_sqlite.py Misc/NEWS Modules/_sqlite setup.py In-Reply-To: References: <20060401005733.117151E4003@bag.python.org> Message-ID: <200604011956.09808.anthony@interlink.com.au> On Saturday 01 April 2006 18:57, Georg Brandl wrote: > One minor nit: the tp_name of sqlite3.Connection etc. is still > "pysqlite2.dbapi2.Connection" etc. Do we care? Should be fixed. I'll do that tomorrow if no-one beats me to it. Actually, that stuff should really be a #define, so that the pysqlite2 and Python sqlite modules don't diverge too far. From python-checkins at python.org Sat Apr 1 10:59:04 2006 From: python-checkins at python.org (georg.brandl) Date: Sat, 1 Apr 2006 10:59:04 +0200 (CEST) Subject: [Python-checkins] r43538 - python/trunk/Misc/NEWS Message-ID: <20060401085904.3DFB71E4003@bag.python.org> Author: georg.brandl Date: Sat Apr 1 10:59:03 2006 New Revision: 43538 Modified: python/trunk/Misc/NEWS Log: Update SQLite version requirement. Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Sat Apr 1 10:59:03 2006 @@ -493,7 +493,7 @@ in attribute values. - Added the sqlite3 package. This is based on pysqlite2.1.3, and provides - a DB-API interface in the standard library. You'll need sqlite 3.2.2 or + a DB-API interface in the standard library. You'll need sqlite 3.0.8 or later to build this - if you have an earlier version, the C extension module will not be built. From python-checkins at python.org Sat Apr 1 11:08:06 2006 From: python-checkins at python.org (neal.norwitz) Date: Sat, 1 Apr 2006 11:08:06 +0200 (CEST) Subject: [Python-checkins] r43539 - python/trunk/Modules/_sqlite/connection.c python/trunk/Modules/_sqlite/cursor.c python/trunk/Modules/_sqlite/row.c python/trunk/Modules/_sqlite/statement.c Message-ID: <20060401090806.E4FEA1E4003@bag.python.org> Author: neal.norwitz Date: Sat Apr 1 11:08:06 2006 New Revision: 43539 Modified: python/trunk/Modules/_sqlite/connection.c python/trunk/Modules/_sqlite/cursor.c python/trunk/Modules/_sqlite/row.c python/trunk/Modules/_sqlite/statement.c Log: Make ssize_t clean Modified: python/trunk/Modules/_sqlite/connection.c ============================================================================== --- python/trunk/Modules/_sqlite/connection.c (original) +++ python/trunk/Modules/_sqlite/connection.c Sat Apr 1 11:08:06 2006 @@ -377,7 +377,7 @@ { long longval; const char* buffer; - int buflen; + Py_ssize_t buflen; PyObject* stringval; if (PyErr_Occurred()) { @@ -416,7 +416,7 @@ PyObject* cur_py_value; const char* val_str; PY_LONG_LONG val_int; - int buflen; + Py_ssize_t buflen; void* raw_buffer; args = PyTuple_New(argc); Modified: python/trunk/Modules/_sqlite/cursor.c ============================================================================== --- python/trunk/Modules/_sqlite/cursor.c (original) +++ python/trunk/Modules/_sqlite/cursor.c Sat Apr 1 11:08:06 2006 @@ -239,7 +239,7 @@ PY_LONG_LONG intval; PyObject* converter; PyObject* converted; - int nbytes; + Py_ssize_t nbytes; PyObject* buffer; void* raw_buffer; const char* val_str; Modified: python/trunk/Modules/_sqlite/row.c ============================================================================== --- python/trunk/Modules/_sqlite/row.c (original) +++ python/trunk/Modules/_sqlite/row.c Sat Apr 1 11:08:06 2006 @@ -126,7 +126,7 @@ } } -int row_length(Row* self, PyObject* args, PyObject* kwargs) +Py_ssize_t row_length(Row* self, PyObject* args, PyObject* kwargs) { return PyTuple_GET_SIZE(self->data); } @@ -138,7 +138,7 @@ PyMappingMethods row_as_mapping = { - /* mp_length */ (inquiry)row_length, + /* mp_length */ (lenfunc)row_length, /* mp_subscript */ (binaryfunc)row_subscript, /* mp_ass_subscript */ (objobjargproc)0, }; Modified: python/trunk/Modules/_sqlite/statement.c ============================================================================== --- python/trunk/Modules/_sqlite/statement.c (original) +++ python/trunk/Modules/_sqlite/statement.c Sat Apr 1 11:08:06 2006 @@ -94,7 +94,7 @@ #endif const char* buffer; char* string; - int buflen; + Py_ssize_t buflen; PyObject* stringval; if (parameter == Py_None) { From buildbot at python.org Sat Apr 1 11:39:55 2006 From: buildbot at python.org (buildbot at python.org) Date: Sat, 01 Apr 2006 09:39:55 +0000 Subject: [Python-checkins] buildbot warnings in x86 gentoo 2.4 Message-ID: <20060401093955.3D8FF1E4003@bag.python.org> The Buildbot has detected a new failure of x86 gentoo 2.4. Full details are available at: http://www.python.org/dev/buildbot/all/x86%20gentoo%202.4/builds/60 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch branches/release24-maint] HEAD Blamelist: georg.brandl,neal.norwitz Build Had Warnings: warnings test sincerely, -The Buildbot From python-checkins at python.org Sat Apr 1 12:32:14 2006 From: python-checkins at python.org (andrew.kuchling) Date: Sat, 1 Apr 2006 12:32:14 +0200 (CEST) Subject: [Python-checkins] r43540 - python/trunk/Doc/whatsnew/whatsnew24.tex Message-ID: <20060401103214.06B891E4003@bag.python.org> Author: andrew.kuchling Date: Sat Apr 1 12:32:13 2006 New Revision: 43540 Modified: python/trunk/Doc/whatsnew/whatsnew24.tex Log: Update status of this PEP Modified: python/trunk/Doc/whatsnew/whatsnew24.tex ============================================================================== --- python/trunk/Doc/whatsnew/whatsnew24.tex (original) +++ python/trunk/Doc/whatsnew/whatsnew24.tex Sat Apr 1 12:32:13 2006 @@ -803,8 +803,8 @@ The PEP also proposes that all \keyword{import} statements be absolute imports, with a leading \samp{.} character to indicate a relative -import. This part of the PEP is not yet implemented, and will have to -wait for Python 2.5 or some other future version. +import. This part of the PEP was not implemented for Python 2.4, +but was completed for Python 2.5. \begin{seealso} \seepep{328}{Imports: Multi-Line and Absolute/Relative} From neal at metaslash.com Sat Apr 1 12:36:50 2006 From: neal at metaslash.com (Neal Norwitz) Date: Sat, 1 Apr 2006 05:36:50 -0500 Subject: [Python-checkins] Python Regression Test Failures doc (1) Message-ID: <20060401103650.GA832@python.psfb.org> TEXINPUTS=/home/neal/python/trunk/Doc/commontex: python /home/neal/python/trunk/Doc/tools/mkhowto --html --about html/stdabout.dat --iconserver ../icons --favicon ../icons/pyfav.png --address "See About this document... for information on suggesting changes." --up-link ../index.html --up-title "Python Documentation Index" --global-module-index "../modindex.html" --dvips-safe --dir html/lib lib/lib.tex *** Session transcript and error messages are in /home/neal/python/trunk/Doc/html/lib/lib.how. *** Exited with status 1. The relevant lines from the transcript are: ------------------------------------------------------------------------ +++ latex lib This is TeX, Version 3.14159 (Web2C 7.4.5) (/home/neal/python/trunk/Doc/lib/lib.tex LaTeX2e <2001/06/01> Babel and hyphenation patterns for american, french, german, ngerman, n ohyphenation, loaded. (/home/neal/python/trunk/Doc/texinputs/manual.cls Document Class: manual 1998/03/03 Document class (Python manual) (/home/neal/python/trunk/Doc/texinputs/pypaper.sty (/usr/share/texmf/tex/latex/psnfss/times.sty) Using Times instead of Computer Modern. ) (/usr/share/texmf/tex/latex/misc/fancybox.sty Style option: `fancybox' v1.3 <2000/09/19> (tvz) ) (/usr/share/texmf/tex/latex/base/report.cls Document Class: report 2001/04/21 v1.4e Standard LaTeX document class (/usr/share/texmf/tex/latex/base/size10.clo)) (/home/neal/python/trunk/Doc/texinputs/fancyhdr.sty) Using fancier footers than usual. (/home/neal/python/trunk/Doc/texinputs/fncychap.sty) Using fancy chapter headings. (/home/neal/python/trunk/Doc/texinputs/python.sty (/usr/share/texmf/tex/latex/tools/longtable.sty) (/home/neal/python/trunk/Doc/texinputs/underscore.sty) (/usr/share/texmf/tex/latex/tools/verbatim.sty) (/usr/share/texmf/tex/latex/base/alltt.sty))) (/home/neal/python/trunk/Doc/commontex/boilerplate.tex (/home/neal/python/trunk/Doc/commontex/patchlevel.tex)) Writing index file lib.idx No file lib.aux. (/usr/share/texmf/tex/latex/psnfss/ot1ptm.fd) (/usr/share/texmf/tex/latex/psnfss/ot1phv.fd) [1] (/home/neal/python/trunk/Doc/commontex/copyright.tex (/usr/share/texmf/tex/latex/psnfss/omsptm.fd)) [2] Adding blank page after the abstract. [1] [2] No file lib.toc. Adding blank page after the table of contents. [1] [2] (/home/neal/python/trunk/Doc/lib/libintro.tex Chapter 1. (/usr/share/texmf/tex/latex/psnfss/ot1pcr.fd) LaTeX Warning: Reference `builtin' on page 1 undefined on input line 49. ) (/home/neal/python/trunk/Doc/lib/libobjs.tex [1] [2] Chapter 2. ) (/home/neal/python/trunk/Doc/lib/libfuncs.tex Underfull \hbox (badness 6188) in paragraph at lines 21--33 \OT1/pcr/m/n/10 _-import_-_-('spam.ham', globals(), locals(), ['eggs'])\OT1/ptm /m/n/10 '. Note that even though [3] [4] [5] [6] LaTeX Warning: Reference `bltin-file-objects' on page 7 undefined on input line 396. [7] Underfull \hbox (badness 10000) in paragraph at lines 466--470 []\OT1/ptm/m/n/10 Note that \OT1/pcr/m/n/10 filter(function, \OT1/ptm/m/it/10 l ist\OT1/pcr/m/n/10 ) \OT1/ptm/m/n/10 is equiv-a-lent to \OT1/pcr/m/n/10 [item f or item in \OT1/ptm/m/it/10 list \OT1/pcr/m/n/10 if [8] [9] [10] [11] [12] [13] [14] [15]) (/home/neal/python/trunk/Doc/lib/libstdtypes.tex [16] [17] [18] LaTeX Warning: Reference `built-in-funcs' on page 19 undefined on input line 29 6. [19] [20] [21] LaTeX Warning: Reference `codec-base-classes' on page 22 undefined on input lin e 600. [22] LaTeX Warning: Reference `codec-base-classes' on page 23 undefined on input lin e 613. LaTeX Warning: Reference `standard-encodings' on page 23 undefined on input lin e 614. [23] [24] [25] Overfull \hbox (8.48134pt too wide) in paragraph at lines 950--960 [] [26] [27] Overfull \hbox (21.88011pt too wide) in paragraph at lines 1057--1088 [] [28] [29] Overfull \hbox (98.60141pt too wide) in paragraph at lines 1268--1296 [] [30] [31] LaTeX Warning: Reference `built-in-funcs' on page 32 undefined on input line 14 83. LaTeX Warning: Reference `context-closing' on page 32 undefined on input line 1 535. [32] [33] [34] [35] [36]) (/home/neal/python/trunk/Doc/lib/libexcs.tex [37] Underfull \hbox (badness 10000) in paragraph at lines 94--98 \OT1/ptm/m/n/10 The base class for all built-in ex-cep-tions ex-cept \OT1/pcr/m /n/10 StopIteration\OT1/ptm/m/n/10 , \OT1/pcr/m/n/10 GeneratorExit\OT1/ptm/m/n/ 10 , [38] [39] [40] No file ../../Lib/test/exception_hierarchy.txt. ) (/home/neal/python/trunk/Doc/lib/libconsts.tex [41]) (/home/neal/python/trunk/Doc/lib/libstrings.tex [42] Chapter 3. LaTeX Warning: Reference `string-methods' on page 43 undefined on input line 10 . ) (/home/neal/python/trunk/Doc/lib/libstring.tex [43] [44] [45] LaTeX Warning: Reference `string-methods' on page 46 undefined on input line 23 7. [46] [47]) (/home/neal/python/trunk/Doc/lib/libre.tex [48] [49] [50] [51] [52] [53] [54] [55] [56] [57]) (/home/neal/python/trunk/Doc/lib/libstruct.tex [58] [59] LaTeX Warning: Reference `module-array' on page 60 undefined on input line 195. LaTeX Warning: Reference `module-xdrlib' on page 60 undefined on input line 196 . ) (/home/neal/python/trunk/Doc/lib/libdifflib.tex [60] [61] [62] [63] [64] [65] [66] [67]) (/home/neal/python/trunk/Doc/lib/libstringio.tex [68] LaTeX Warning: Reference `bltin-file-objects' on page 69 undefined on input lin e 11. [69]) (/home/neal/python/trunk/Doc/lib/libtextwrap.tex [70] [71]) (/home/neal/python/trunk/Doc/lib/libcodecs.tex Underfull \hbox (badness 5161) in paragraph at lines 52--56 []\OT1/ptm/m/n/10 The fac-tory func-tions must re-turn ob-jects pro-vid-ing the in-ter-faces de-fined by the base classes [72] [73] [74] Overfull \hbox (405.07822pt too wide) in paragraph at lines 288--300 [] [75] [76] [77] [78] [79] [80] Overfull \hbox (11.18082pt too wide) in alignment at lines 820--901 [] [] [] Overfull \hbox (11.18082pt too wide) in alignment at lines 901--981 [] [] [] Overfull \hbox (254.7505pt too wide) in alignment at lines 981--1062 [] [] [] [81] Overfull \hbox (254.7505pt too wide) in alignment at lines 1062--1142 [] [] [] Overfull \hbox (254.7505pt too wide) in alignment at lines 1142--1167 [] [] [] Package longtable Warning: Column widths have changed (longtable) in table 3.1 on input line 1167. [82] Overfull \hbox (465.03658pt too wide) in paragraph at lines 1179--1270 [] [83]) (/home/neal/python/trunk/Doc/lib/libunicodedata.tex [84]) (/home/neal/python/trunk/Doc/lib/libstringprep.tex [85] [86]) (/home/neal/python/trunk/Doc/lib/libfpformat.tex) Underfull \hbox (badness 10000) in paragraph at lines 50--98 (/home/neal/python/trunk/Doc/lib/datatypes.tex [87] [88] Chapter 4. ) (/home/neal/python/trunk/Doc/lib/libdatetime.tex LaTeX Warning: Reference `module-calendar' on page 89 undefined on input line 5 6. LaTeX Warning: Reference `module-time' on page 89 undefined on input line 57. [89] [90] Underfull \hbox (badness 10000) in paragraph at lines 185--187 \OT1/ptm/m/n/10 The small-est pos-si-ble dif-fer-ence be-tween non-equal \OT1/p cr/m/n/10 timedelta \OT1/ptm/m/n/10 ob-jects, (/usr/share/texmf/tex/latex/psnfss/omlptm.fd) Overfull \hbox (44.65097pt too wide) in paragraph at lines 204--235 [] [91] [92] Underfull \hbox (badness 10000) in paragraph at lines 430--439 \OT1/ptm/m/it/10 d\OT1/pcr/m/n/10 .month, \OT1/ptm/m/it/10 d\OT1/pcr/m/n/10 .da y, 0, 0, 0, \OT1/ptm/m/it/10 d\OT1/pcr/m/n/10 .weekday(), \OT1/ptm/m/it/10 d\OT 1/pcr/m/n/10 .toordinal() - date(\OT1/ptm/m/it/10 d\OT1/pcr/m/n/10 .year, 1, [93] Underfull \hbox (badness 10000) in paragraph at lines 464--467 []\OT1/ptm/m/n/10 The ISO cal-en-dar is a widely used vari-ant of the Gre-go-ri an cal-en-dar. See LaTeX Warning: Reference `strftime-behavior' on page 94 undefined on input line 507. [94] Underfull \hbox (badness 10000) in paragraph at lines 547--551 \OT1/ptm/m/n/10 Return the cur-rent lo-cal date-time, with \OT1/pcr/m/n/10 tzin fo None\OT1/ptm/m/n/10 . This is equiv-a-lent to Underfull \hbox (badness 10000) in paragraph at lines 561--566 []\OT1/ptm/m/n/10 Else \OT1/ptm/m/it/10 tz \OT1/ptm/m/n/10 must be an in-stance of a class \OT1/pcr/m/n/10 tzinfo \OT1/ptm/m/n/10 sub-class, and the cur-rent date Underfull \hbox (badness 10000) in paragraph at lines 561--566 \OT1/ptm/m/n/10 and time are con-verted to \OT1/ptm/m/it/10 tz\OT1/ptm/m/n/10 ' s time zone. In this case the re-sult is equiv-a-lent to Underfull \hbox (badness 10000) in paragraph at lines 582--586 []\OT1/ptm/m/n/10 Else \OT1/ptm/m/it/10 tz \OT1/ptm/m/n/10 must be an in-stance of a class \OT1/pcr/m/n/10 tzinfo \OT1/ptm/m/n/10 sub-class, and the times- Underfull \hbox (badness 10000) in paragraph at lines 582--586 \OT1/ptm/m/n/10 tamp is con-verted to \OT1/ptm/m/it/10 tz\OT1/ptm/m/n/10 's tim e zone. In this case the re-sult is equiv-a-lent to [95] Underfull \hbox (badness 5519) in paragraph at lines 646--648 \OT1/ptm/m/n/10 The lat-est rep-re-sentable \OT1/pcr/m/n/10 datetime\OT1/ptm/m/ n/10 , \OT1/pcr/m/n/10 datetime(MAXYEAR, 12, 31, 23, 59, 59, 999999, [96] [97] Underfull \hbox (badness 10000) in paragraph at lines 874--888 \OT1/ptm/m/it/10 d\OT1/pcr/m/n/10 .weekday(), \OT1/ptm/m/it/10 d\OT1/pcr/m/n/10 .toordinal() - date(\OT1/ptm/m/it/10 d\OT1/pcr/m/n/10 .year, 1, 1).toordinal() + 1, dst)) \OT1/ptm/m/n/10 The Underfull \hbox (badness 10000) in paragraph at lines 923--925 \OT1/ptm/m/n/10 Return a 3-tuple, (ISO year, ISO week num-ber, ISO week-day). T he same as [98] Underfull \hbox (badness 5064) in paragraph at lines 960--969 \OT1/ptm/m/n/10 Return a string rep-re-sent-ing the date and time, for ex-am-pl e \OT1/pcr/m/n/10 datetime(2002, 12, 4, 20, Underfull \hbox (badness 10000) in paragraph at lines 960--969 \OT1/pcr/m/n/10 30, 40).ctime() == 'Wed Dec 4 20:30:40 2002'\OT1/ptm/m/n/10 . \ OT1/pcr/m/n/10 d.ctime() \OT1/ptm/m/n/10 is equiv-a-lent to LaTeX Warning: Reference `strftime-behavior' on page 99 undefined on input line 973. [99] LaTeX Warning: Reference `strftime-behavior' on page 100 undefined on input lin e 1103. [100] [101] [102] [103] [104] [105]) (/home/neal/python/trunk/Doc/lib/libcalendar.tex [106] [107] [108] [109] LaTeX Warning: Reference `module-datetime' on page 110 undefined on input line 311. LaTeX Warning: Reference `module-time' on page 110 undefined on input line 312. ) (/home/neal/python/trunk/Doc/lib/libcollections.tex [110] [111] [112] [113] [114] Overfull \hbox (6.89723pt too wide) in paragraph at lines 337--337 []\OT1/pcr/m/n/9 >>> s = [('red', 1), ('blue', 2), ('red', 3), ('blue', 4), ('r ed', 1), ('blue', 4)][] ) (/home/neal/python/trunk/Doc/lib/libheapq.tex [115] [116] [117]) (/home/neal/python/trunk/Doc/lib/libbisect.tex) (/home/neal/python/trunk/Doc/lib/libarray.tex [118] [119] [120] LaTeX Warning: Reference `module-struct' on page 121 undefined on input line 23 0. LaTeX Warning: Reference `module-xdrlib' on page 121 undefined on input line 23 3. [121]) (/home/neal/python/trunk/Doc/lib/libsets.tex LaTeX Warning: Reference `immutable-transforms' on page 122 undefined on input line 49. LaTeX Warning: Reference `immutable-transforms' on page 122 undefined on input line 57. [122] Overfull \hbox (98.60141pt too wide) in paragraph at lines 135--163 [] [123] [124]) (/home/neal/python/trunk/Doc/lib/libsched.tex [125]) (/home/neal/python/trunk/Doc/lib/libmutex.tex [126]) (/home/neal/python/trunk/Doc/lib/libqueue.tex [127]) (/home/neal/python/trunk/Doc/lib/libweakref.tex [128] LaTeX Warning: Reference `weakref-extension' on page 129 undefined on input lin e 69. [129] [130] [131] [132]) (/home/neal/python/trunk/Doc/lib/libuserdict.tex [133] LaTeX Warning: Reference `typesmapping' on page 134 undefined on input line 40. LaTeX Warning: Reference `typesseq' on page 134 undefined on input line 97. [134] LaTeX Warning: Reference `string-methods' on page 135 undefined on input line 1 74. ) (/home/neal/python/trunk/Doc/lib/libtypes.tex [135] [136] [137]) (/home/neal/python/trunk/Doc/lib/libnew.tex) (/home/neal/python/trunk/Doc/lib/libcopy.tex [138] LaTeX Warning: Reference `module-pickle' on page 139 undefined on input line 96 . ) (/home/neal/python/trunk/Doc/lib/libpprint.tex [139] [140]) (/home/neal/python/trunk/Doc/lib/librepr.tex [141] [142]) Underfull \hbox (badness 10000) in paragraph at lines 120--121 (/home/neal/python/trunk/Doc/lib/numeric.tex [143] [144] Chapter 5. ) (/home/neal/python/trunk/Doc/lib/libmath.tex [145] [146] LaTeX Warning: Reference `module-cmath' on page 147 undefined on input line 208 . ) (/home/neal/python/trunk/Doc/lib/libcmath.tex [147]) (/home/neal/python/trunk/Doc/lib/libdecimal.tex [148] [149] [150] [151] [152] Overfull \hbox (21.09727pt too wide) in paragraph at lines 305--305 [] \OT1/pcr/m/n/9 digit ::= '0' | '1' | '2' | '3' | '4' | '5' | '6 ' | '7' | '8' | '9'[] [153] [154] [155] [156] [157] [158] [159] [160] [161] Overfull \vbox (959.57pt too high) has occurred while \output is active [162] [163] [164]) (/home/neal/python/trunk/Doc/lib/librandom.tex [165] Underfull \hbox (badness 6575) in paragraph at lines 119--124 \OT1/ptm/m/n/10 Return a ran-domly se-lected el-e-ment from \OT1/pcr/m/n/10 ran ge(\OT1/ptm/m/it/10 start\OT1/pcr/m/n/10 , \OT1/ptm/m/it/10 stop\OT1/pcr/m/n/10 , \OT1/ptm/m/it/10 step\OT1/pcr/m/n/10 )\OT1/ptm/m/n/10 . This is equiv-a-lent to [166] [167]) (/home/neal/python/trunk/Doc/lib/libitertools.tex [168] [169] [170] [171] [172] [173] [174]) (/home/neal/python/trunk/Doc/lib/libfunctional.tex [175] Overfull \vbox (255.57pt too high) has occurred while \output is active [176]) (/home/neal/python/trunk/Doc/lib/liboperator.tex [177] [178] [179] [180] [181] [182] [183]) (/home/neal/python/trunk/Doc/lib/netdata.tex [184] Chapter 6. ) (/home/neal/python/trunk/Doc/lib/email.tex LaTeX Warning: Reference `module-smtplib' on page 185 undefined on input line 5 8. LaTeX Warning: Reference `module-nntplib' on page 185 undefined on input line 5 9. (/home/neal/python/trunk/Doc/lib/emailmessage.tex [185] [186] [187] [188] [189] [190]) (/home/neal/python/trunk/Doc/lib/emailparser.tex [191] [192] [193]) (/home/neal/python/trunk/Doc/lib/emailgenerator.tex [194]) (/home/neal/python/trunk/Doc/lib/emailmimebase.tex [195] [196]) (/home/neal/python/trunk/Doc/lib/emailheaders.tex [197] [198]) (/home/neal/python/trunk/Doc/lib/emailcharsets.tex [199] [200]) (/home/neal/python/trunk/Doc/lib/emailencoders.tex [201]) (/home/neal/python/trunk/Doc/lib/emailexc.tex [202]) (/home/neal/python/trunk/Doc/lib/emailutil.tex [203] [204]) (/home/neal/python/trunk/Doc/lib/emailiter.tex [205]) [206] [207] [208] [209] [210] [211] [212]) (/home/neal/python/trunk/Doc/lib/libmailcap.tex [213]) (/home/neal/python/trunk/Doc/lib/libmailbox.tex [214] [215]) (/home/neal/python/trunk/Doc/lib/libmhlib.tex [216] [217]) (/home/neal/python/trunk/Doc/lib/libmimetools.tex [218] LaTeX Warning: Reference `module-email' on page 219 undefined on input line 61. LaTeX Warning: Reference `module-rfc822' on page 219 undefined on input line 63 . LaTeX Warning: Reference `module-multifile' on page 219 undefined on input line 65. [219]) (/home/neal/python/trunk/Doc/lib/libmimetypes.tex [220] [221]) (/home/neal/python/trunk/Doc/lib/libmimewriter.tex [222]) (/home/neal/python/trunk/Doc/lib/libmimify.tex [223] LaTeX Warning: Reference `module-quopri' on page 224 undefined on input line 93 . ) (/home/neal/python/trunk/Doc/lib/libmultifile.tex LaTeX Warning: Reference `module-email' on page 224 undefined on input line 43. [224]) (/home/neal/python/trunk/Doc/lib/librfc822.tex [225] [226] LaTeX Warning: Reference `module-email' on page 227 undefined on input line 133 . LaTeX Warning: Reference `module-mailbox' on page 227 undefined on input line 1 35. [227] LaTeX Warning: Reference `module-mimetools' on page 228 undefined on input line 137. [228] Underfull \hbox (badness 7379) in paragraph at lines 254--270 []\OT1/pcr/m/n/10 Message \OT1/ptm/m/n/10 in-stances also sup-port a lim-ited m ap-ping in-ter-face. In par-tic-u-lar: \OT1/ptm/m/it/10 m\OT1/pcr/m/n/10 [name] \OT1/ptm/m/n/10 is like [229]) (/home/neal/python/trunk/Doc/lib/libbase64.tex [230] LaTeX Warning: Reference `module-binascii' on page 231 undefined on input line 151. ) (/home/neal/python/trunk/Doc/lib/libbinascii.tex [231] [232] LaTeX Warning: Reference `module-base64' on page 233 undefined on input line 13 9. LaTeX Warning: Reference `module-binhex' on page 233 undefined on input line 14 1. LaTeX Warning: Reference `module-uu' on page 233 undefined on input line 143. LaTeX Warning: Reference `module-quopri' on page 233 undefined on input line 14 5. ) (/home/neal/python/trunk/Doc/lib/libbinhex.tex LaTeX Warning: Reference `module-binascii' on page 233 undefined on input line 41. [233]) (/home/neal/python/trunk/Doc/lib/libquopri.tex LaTeX Warning: Reference `module-mimify' on page 234 undefined on input line 59 . LaTeX Warning: Reference `module-base64' on page 234 undefined on input line 60 . ) (/home/neal/python/trunk/Doc/lib/libuu.tex [234] LaTeX Warning: Reference `module-binascii' on page 235 undefined on input line 57. ) (/home/neal/python/trunk/Doc/lib/markup.tex [235] [236] Chapter 7. ) (/home/neal/python/trunk/Doc/lib/libhtmlparser.tex [237] [238]) (/home/neal/python/trunk/Doc/lib/libsgmllib.tex [239] ! Misplaced alignment tab character &. l.105 ...se of \code{} when \code{eggs} ? ! Emergency stop. l.105 ...se of \code{} when \code{eggs} Output written on lib.dvi (245 pages, 878844 bytes). Transcript written on lib.log. *** Session transcript and error messages are in /home/neal/python/trunk/Doc/html/lib/lib.how. *** Exited with status 1. +++ TEXINPUTS=/home/neal/python/trunk/Doc/lib:/home/neal/python/trunk/Doc/commontex:/home/neal/python/trunk/Doc/paper-letter:/home/neal/python/trunk/Doc/texinputs: +++ latex lib make: *** [html/lib/lib.html] Error 1 From python-checkins at python.org Sat Apr 1 12:49:14 2006 From: python-checkins at python.org (andrew.kuchling) Date: Sat, 1 Apr 2006 12:49:14 +0200 (CEST) Subject: [Python-checkins] r43541 - peps/trunk/pep-0357.txt Message-ID: <20060401104914.6837D1E4003@bag.python.org> Author: andrew.kuchling Date: Sat Apr 1 12:49:12 2006 New Revision: 43541 Modified: peps/trunk/pep-0357.txt Log: Typo fixes Modified: peps/trunk/pep-0357.txt ============================================================================== --- peps/trunk/pep-0357.txt (original) +++ peps/trunk/pep-0357.txt Sat Apr 1 12:49:12 2006 @@ -83,7 +83,7 @@ 2) Change the ISINT macro in ceval.c to ISINDEX and alter it to accomodate objects with the index slot defined. - 3) Change the _PyEval_SliceIndex function to accomodate objects + 3) Change the _PyEval_SliceIndex function to accommodate objects with the index slot defined. 4) Change all builtin objects (e.g. lists) that use the as_mapping @@ -149,4 +149,4 @@ Copyright - This document is placed in the public domain + This document is placed in the public domain. From python-checkins at python.org Sat Apr 1 12:50:08 2006 From: python-checkins at python.org (andrew.kuchling) Date: Sat, 1 Apr 2006 12:50:08 +0200 (CEST) Subject: [Python-checkins] r43542 - python/trunk/Misc/NEWS Message-ID: <20060401105008.BBE2B1E4003@bag.python.org> Author: andrew.kuchling Date: Sat Apr 1 12:50:08 2006 New Revision: 43542 Modified: python/trunk/Misc/NEWS Log: Some typo & grammar fixes Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Sat Apr 1 12:50:08 2006 @@ -64,10 +64,10 @@ to "error" is triggered) when raising a warning for raising string exceptions. -- CO_GENERATOR_ALLOWED is no longer defined, this behavior is the default. +- CO_GENERATOR_ALLOWED is no longer defined. This behavior is the default. The name was removed from Include/code.h. -- PEP 308: conditional expressions were added (x if cond else y). +- PEP 308: conditional expressions were added: (x if cond else y). - Patch 1433928: - The copy module now "copies" function objects (as atomic objects). @@ -125,12 +125,12 @@ - Patch #1350409: Work around signal handling bug in Visual Studio 2005. -- Bug #1281408: Py_BuildValue now works correct even with unsigned longs +- Bug #1281408: Py_BuildValue now works correctly even with unsigned longs and long longs. - SF Bug #1350188, "setdlopenflags" leads to crash upon "import" - It was possible dlerror() returns a NULL pointer, use a default error - message in this case. + It was possible for dlerror() to return a NULL pointer, so + it will now use a default error message in this case. - Replaced most Unicode charmap codecs with new ones using the new Unicode translate string feature in the builtin charmap @@ -140,13 +140,13 @@ - Added a few more codecs for Mac OS encodings -- Speed up some Unicode operations. +- Sped up some Unicode operations. - A new AST parser implementation was completed. The abstract syntax tree is available for read-only (non-compile) access to Python code; an _ast module was added. -- SF bug #1167751: fix incorrect code being for generator expressions. +- SF bug #1167751: fix incorrect code being produced for generator expressions. The following code now raises a SyntaxError: foo(a = i for i in range(10)) - SF Bug #976608: fix SystemError when mtime of an imported file is -1. @@ -158,7 +158,7 @@ - SF bug #772896: unknown encoding results in MemoryError. -- All iterators now have a Boolean value of true. Formerly, some iterators +- All iterators now have a Boolean value of True. Formerly, some iterators supported a __len__() method which evaluated to False when the iterator was empty. @@ -198,7 +198,7 @@ - SF bug #1185883: Python's small-object memory allocator took over a block managed by the platform C library whenever a realloc specified a small new size. However, there's no portable way to know then how - much of the address space following the pointer is valid, so no + much of the address space following the pointer is valid, so there's no portable way to copy data from the C-managed block into Python's small-object space without risking a memory fault. Python's small-object realloc now leaves such blocks under the control of the platform C @@ -669,7 +669,7 @@ - Bug #1163178: Make IDNA return an empty string when the input is empty. - Patch #848017: Make Cookie more RFC-compliant. Use CRLF as default output - separator and do not output trailing semicola. + separator and do not output trailing semicolon. - Patch #1062060: urllib.urlretrieve() now raises a new exception, named ContentTooShortException, when the actually downloaded size does not @@ -707,7 +707,7 @@ - Bug #1177468: Don't cache the /dev/urandom file descriptor for os.urandom, as this can cause problems with apps closing all file descriptors. -- Bug #839151: Fix an attempt to access sys.argv in the warnings module +- Bug #839151: Fix an attempt to access sys.argv in the warnings module; it can be missing in embedded interpreters - Bug #1155638: Fix a bug which affected HTTP 0.9 responses in httplib. @@ -721,7 +721,7 @@ Bug #1224621. - The tokenize module has a new untokenize() function to support a full - roundtrip from lexed tokens back to Python sourcecode. In addition, + roundtrip from lexed tokens back to Python source code. In addition, the generate_tokens() function now accepts a callable argument that terminates by raising StopIteration. From thomas at python.org Sat Apr 1 16:43:23 2006 From: thomas at python.org (Thomas Wouters) Date: Sat, 1 Apr 2006 16:43:23 +0200 Subject: [Python-checkins] r43529 - python/trunk/Lib/distutils/log.py In-Reply-To: <20060401074654.EDD1D1E4003@bag.python.org> References: <20060401074654.EDD1D1E4003@bag.python.org> Message-ID: <9e804ac0604010643w19a5f196s6b98a94bc004e65@mail.gmail.com> On 4/1/06, georg.brandl wrote: > > Author: georg.brandl > Date: Sat Apr 1 09:46:54 2006 > New Revision: 43529 > > Modified: > python/trunk/Lib/distutils/log.py > Log: > Bug #1458017: make distutils.Log._log more forgiving when passing in > msg strings with '%', but without format args. I'm not sure if this is the right thing to do. It may be more forgiving, but anyone who passes a string to a function that expects a format shouldn't be looking for forgiveness. They should be made aware that %'s are special, or they will be bitten by it when they _do_ pass arguments. Modified: python/trunk/Lib/distutils/log.py > > ============================================================================== > --- python/trunk/Lib/distutils/log.py (original) > +++ python/trunk/Lib/distutils/log.py Sat Apr 1 09:46:54 2006 > @@ -20,7 +20,12 @@ > > def _log(self, level, msg, args): > if level >= self.threshold: > - print msg % args > + if not args: > + # msg may contain a '%'. If args is empty, > + # don't even try to string-format > + print msg > + else: > + print msg % args > sys.stdout.flush() > > def log(self, level, msg, *args): -- Thomas Wouters Hi! I'm a .signature virus! copy me into your .signature file to help me spread! -------------- next part -------------- An HTML attachment was scrubbed... URL: http://mail.python.org/pipermail/python-checkins/attachments/20060401/36e19ea5/attachment.htm From python-checkins at python.org Sat Apr 1 18:18:03 2006 From: python-checkins at python.org (jeremy.hylton) Date: Sat, 1 Apr 2006 18:18:03 +0200 (CEST) Subject: [Python-checkins] r43543 - python/trunk/Python/compile.c Message-ID: <20060401161803.3EB8F1E4003@bag.python.org> Author: jeremy.hylton Date: Sat Apr 1 18:18:02 2006 New Revision: 43543 Modified: python/trunk/Python/compile.c Log: Expand comments on line numbers and blocks. Reorder compiler_set_lineno() call for consistency. Modified: python/trunk/Python/compile.c ============================================================================== --- python/trunk/Python/compile.c (original) +++ python/trunk/Python/compile.c Sat Apr 1 18:18:02 2006 @@ -58,8 +58,9 @@ }; typedef struct basicblock_ { - /* next block in the list of blocks for a unit (don't confuse with - * b_next) */ + /* Each basicblock in a compilation unit is linked via b_list in the + reverse order that the block are allocated. b_list points to the next + block, not to be confused with b_next, which is next by control flow. */ struct basicblock_ *b_list; /* number of instructions used */ int b_iused; @@ -114,7 +115,9 @@ PyObject *u_private; /* for private name mangling */ int u_argcount; /* number of arguments for block */ - basicblock *u_blocks; /* pointer to list of blocks */ + /* Pointer to the most recently allocated block. By following b_list + members, you can reach all early allocated blocks. */ + basicblock *u_blocks; basicblock *u_curblock; /* pointer to current block */ int u_tmpname; /* temporary variables for list comps */ @@ -1194,7 +1197,7 @@ return NULL; } memset((void *)b, 0, sizeof(basicblock)); - assert (b->b_next == NULL); + /* Extend the singly linked list of blocks with new block. */ b->b_list = u->u_blocks; u->u_blocks = b; return b; @@ -1267,6 +1270,13 @@ return b->b_iused++; } +/* Set the i_lineno member of the instruction at offse off if the + line number for the current expression/statement (?) has not + already been set. If it has been set, the call has no effect. + + Every time a new node is b + */ + static void compiler_set_lineno(struct compiler *c, int off) { @@ -1609,7 +1619,6 @@ off = compiler_next_instr(c, c->u->u_curblock); if (off < 0) return 0; - compiler_set_lineno(c, off); i = &c->u->u_curblock->b_instr[off]; i->i_opcode = opcode; i->i_target = b; @@ -1618,6 +1627,7 @@ i->i_jabs = 1; else i->i_jrel = 1; + compiler_set_lineno(c, off); return 1; } @@ -2230,7 +2240,7 @@ ADDOP(c, POP_BLOCK); } compiler_pop_fblock(c, LOOP, loop); - if (orelse != NULL) + if (orelse != NULL) /* what if orelse is just pass? */ VISIT_SEQ(c, stmt, s->v.While.orelse); compiler_use_next_block(c, end); @@ -2610,8 +2620,10 @@ { int i, n; + /* Always assign a lineno to the next instruction for a stmt. */ c->u->u_lineno = s->lineno; c->u->u_lineno_set = false; + switch (s->kind) { case FunctionDef_kind: return compiler_function(c, s); @@ -3486,6 +3498,9 @@ { int i, n; + /* If expr e has a different line number than the last expr/stmt, + set a new line number for the next instruction. + */ if (e->lineno > c->u->u_lineno) { c->u->u_lineno = e->lineno; c->u->u_lineno_set = false; From dynkin at gmail.com Sat Apr 1 21:19:28 2006 From: dynkin at gmail.com (George Yoshida) Date: Sun, 2 Apr 2006 04:19:28 +0900 Subject: [Python-checkins] Python Regression Test Failures doc (1) In-Reply-To: <20060401103650.GA832@python.psfb.org> References: <20060401103650.GA832@python.psfb.org> Message-ID: <2f188ee80604011119i5dd372c1q6ccc230e25aeb597@mail.gmail.com> On 4/1/06, Neal Norwitz wrote: > TEXINPUTS=/home/neal/python/trunk/Doc/commontex: python /home/neal/python/trunk/Doc/tools/mkhowto --html --about html/stdabout.dat --iconserver ../icons --favicon ../icons/pyfav.png --address "See About this document... for information on suggesting changes." --up-link ../index.html --up-title "Python Documentation Index" --global-module-index "../modindex.html" --dvips-safe --dir html/lib lib/lib.tex > *** Session transcript and error messages are in /home/neal/python/trunk/Doc/html/lib/lib.how. > *** Exited with status 1. Maybe the culprit is this? > r43534 - python/trunk/Doc/lib/libsgmllib.tex Here's a diff against r43534. Index: Doc/lib/libsgmllib.tex =================================================================== --- Doc/lib/libsgmllib.tex (revision 43543) +++ Doc/lib/libsgmllib.tex (working copy) @@ -102,7 +102,7 @@ as well as known character references and known entity references terminated by a semicolon (normally, entity references can be terminated by any non-alphanumerical character, but this would break the very -common case of \code{} when \code{eggs} +common case of \code{} when \code{eggs} is a valid entity name). For instance, for the tag \code{}, this -- george From python-checkins at python.org Sat Apr 1 22:40:16 2006 From: python-checkins at python.org (georg.brandl) Date: Sat, 1 Apr 2006 22:40:16 +0200 (CEST) Subject: [Python-checkins] r43544 - python/trunk/Doc/lib/libsgmllib.tex Message-ID: <20060401204016.B995B1E4008@bag.python.org> Author: georg.brandl Date: Sat Apr 1 22:40:16 2006 New Revision: 43544 Modified: python/trunk/Doc/lib/libsgmllib.tex Log: Fix LaTeX oversight. Modified: python/trunk/Doc/lib/libsgmllib.tex ============================================================================== --- python/trunk/Doc/lib/libsgmllib.tex (original) +++ python/trunk/Doc/lib/libsgmllib.tex Sat Apr 1 22:40:16 2006 @@ -102,7 +102,7 @@ as well as known character references and known entity references terminated by a semicolon (normally, entity references can be terminated by any non-alphanumerical character, but this would break the very -common case of \code{} when \code{eggs} +common case of \code{} when \code{eggs} is a valid entity name). For instance, for the tag \code{}, this From python-checkins at python.org Sat Apr 1 22:40:23 2006 From: python-checkins at python.org (walter.doerwald) Date: Sat, 1 Apr 2006 22:40:23 +0200 (CEST) Subject: [Python-checkins] r43545 - in python/trunk: Doc/lib/libcalendar.tex Lib/calendar.py Message-ID: <20060401204023.804801E4006@bag.python.org> Author: walter.doerwald Date: Sat Apr 1 22:40:23 2006 New Revision: 43545 Modified: python/trunk/Doc/lib/libcalendar.tex python/trunk/Lib/calendar.py Log: Make firstweekday a simple attribute instead of hiding it behind a setter and a getter. Modified: python/trunk/Doc/lib/libcalendar.tex ============================================================================== --- python/trunk/Doc/lib/libcalendar.tex (original) +++ python/trunk/Doc/lib/libcalendar.tex Sat Apr 1 22:40:23 2006 @@ -36,15 +36,6 @@ \class{Calendar} instances have the following methods: -\begin{methoddesc}{firstweekday}{} -Return the first day of the week (as specified in the constructor -or changed via \method{setfirstweekday()}). -\end{methoddesc} - -\begin{methoddesc}{setfirstweekday}{weekday} -Set the first day of the week. -\end{methoddesc} - \begin{methoddesc}{iterweekdays}{weekday} Return an iterator for the week day numbers that will be used for one week. The first number from the iterator will be the Modified: python/trunk/Lib/calendar.py ============================================================================== --- python/trunk/Lib/calendar.py (original) +++ python/trunk/Lib/calendar.py Sat Apr 1 22:40:23 2006 @@ -128,25 +128,14 @@ """ def __init__(self, firstweekday=0): - self._firstweekday = firstweekday # 0 = Monday, 6 = Sunday - - def firstweekday(self): - return self._firstweekday - - def setfirstweekday(self, weekday): - """ - Set weekday (Monday=0, Sunday=6) to start each week. - """ - if not MONDAY <= weekday <= SUNDAY: - raise IllegalWeekdayError(weekday) - self._firstweekday = weekday + self.firstweekday = firstweekday # 0 = Monday, 6 = Sunday def iterweekdays(self): """ Return a iterator for one week of weekday numbers starting with the configured first one. """ - for i in xrange(self._firstweekday, self._firstweekday + 7): + for i in xrange(self.firstweekday, self.firstweekday + 7): yield i%7 def itermonthdates(self, year, month): @@ -157,13 +146,13 @@ """ date = datetime.date(year, month, 1) # Go back to the beginning of the week - days = (date.weekday() - self._firstweekday) % 7 + days = (date.weekday() - self.firstweekday) % 7 date -= datetime.timedelta(days=days) oneday = datetime.timedelta(days=1) while True: yield date date += oneday - if date.month != month and date.weekday() == self._firstweekday: + if date.month != month and date.weekday() == self.firstweekday: break def itermonthdays2(self, year, month): @@ -570,8 +559,14 @@ # Support for old module level interface c = TextCalendar() -firstweekday = c.firstweekday -setfirstweekday = c.setfirstweekday +def firstweekday(): + return c.firstweekday + +def setfirstweekday(firstweekday): + if not MONDAY <= firstweekday <= SUNDAY: + raise IllegalWeekdayError(firstweekday) + c.firstweekday = firstweekday + monthcalendar = c.monthdayscalendar prweek = c.prweek week = c.formatweek From g.brandl at gmx.net Sat Apr 1 22:40:27 2006 From: g.brandl at gmx.net (Georg Brandl) Date: Sat, 01 Apr 2006 22:40:27 +0200 Subject: [Python-checkins] Python Regression Test Failures doc (1) In-Reply-To: <2f188ee80604011119i5dd372c1q6ccc230e25aeb597@mail.gmail.com> References: <20060401103650.GA832@python.psfb.org> <2f188ee80604011119i5dd372c1q6ccc230e25aeb597@mail.gmail.com> Message-ID: George Yoshida wrote: > On 4/1/06, Neal Norwitz wrote: >> TEXINPUTS=/home/neal/python/trunk/Doc/commontex: python /home/neal/python/trunk/Doc/tools/mkhowto --html --about html/stdabout.dat --iconserver ../icons --favicon ../icons/pyfav.png --address "See About this document... for information on suggesting changes." --up-link ../index.html --up-title "Python Documentation Index" --global-module-index "../modindex.html" --dvips-safe --dir html/lib lib/lib.tex >> *** Session transcript and error messages are in /home/neal/python/trunk/Doc/html/lib/lib.how. >> *** Exited with status 1. > > > Maybe the culprit is this? >> r43534 - python/trunk/Doc/lib/libsgmllib.tex > Here's a diff against r43534. Thanks, fixed. Georg From buildbot at python.org Sat Apr 1 23:25:32 2006 From: buildbot at python.org (buildbot at python.org) Date: Sat, 01 Apr 2006 21:25:32 +0000 Subject: [Python-checkins] buildbot warnings in alpha Tru64 5.1 trunk Message-ID: <20060401212532.437261E4006@bag.python.org> The Buildbot has detected a new failure of alpha Tru64 5.1 trunk. Full details are available at: http://www.python.org/dev/buildbot/all/alpha%20Tru64%205.1%20trunk/builds/65 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: georg.brandl,walter.doerwald Build Had Warnings: warnings test sincerely, -The Buildbot From python-checkins at python.org Sun Apr 2 00:14:44 2006 From: python-checkins at python.org (fred.drake) Date: Sun, 2 Apr 2006 00:14:44 +0200 (CEST) Subject: [Python-checkins] r43546 - in python/trunk: Doc/lib/liburlparse.tex Lib/test/test_urlparse.py Lib/urlparse.py Misc/NEWS Message-ID: <20060401221444.8A1AA1E4006@bag.python.org> Author: fred.drake Date: Sun Apr 2 00:14:43 2006 New Revision: 43546 Modified: python/trunk/Doc/lib/liburlparse.tex python/trunk/Lib/test/test_urlparse.py python/trunk/Lib/urlparse.py python/trunk/Misc/NEWS Log: Patch #624325: urlparse.urlparse() and urlparse.urlsplit() results now sport attributes that provide access to the parts of the result. Modified: python/trunk/Doc/lib/liburlparse.tex ============================================================================== --- python/trunk/Doc/lib/liburlparse.tex (original) +++ python/trunk/Doc/lib/liburlparse.tex Sun Apr 2 00:14:43 2006 @@ -25,48 +25,74 @@ \code{nntp}, \code{prospero}, \code{rsync}, \code{rtsp}, \code{rtspu}, \code{sftp}, \code{shttp}, \code{sip}, \code{sips}, \code{snews}, \code{svn}, \code{svn+ssh}, \code{telnet}, \code{wais}. + \versionadded[Support for the \code{sftp} and \code{sips} schemes]{2.5} The \module{urlparse} module defines the following functions: -\begin{funcdesc}{urlparse}{urlstring\optional{, default_scheme\optional{, allow_fragments}}} -Parse a URL into 6 components, returning a 6-tuple: (addressing -scheme, network location, path, parameters, query, fragment -identifier). This corresponds to the general structure of a URL: +\begin{funcdesc}{urlparse}{urlstring\optional{, + default_scheme\optional{, allow_fragments}}} +Parse a URL into six components, returning a 6-tuple. This +corresponds to the general structure of a URL: \code{\var{scheme}://\var{netloc}/\var{path};\var{parameters}?\var{query}\#\var{fragment}}. Each tuple item is a string, possibly empty. -The components are not broken up in smaller parts (e.g. the network +The components are not broken up in smaller parts (for example, the network location is a single string), and \% escapes are not expanded. -The delimiters as shown above are not part of the tuple items, +The delimiters as shown above are not part of the result, except for a leading slash in the \var{path} component, which is -retained if present. - -Example: - -\begin{verbatim} -urlparse('http://www.cwi.nl:80/%7Eguido/Python.html') -\end{verbatim} - -yields the tuple +retained if present. For example: \begin{verbatim} +>>> from urlparse import urlparse +>>> o = urlparse('http://www.cwi.nl:80/%7Eguido/Python.html') +>>> o ('http', 'www.cwi.nl:80', '/%7Eguido/Python.html', '', '', '') +>>> o.scheme +'http' +>>> o.port +80 +>>> o.geturl() +'http://www.cwi.nl:80/%7Eguido/Python.html' \end{verbatim} If the \var{default_scheme} argument is specified, it gives the -default addressing scheme, to be used only if the URL string does not +default addressing scheme, to be used only if the URL does not specify one. The default value for this argument is the empty string. -If the \var{allow_fragments} argument is zero, fragment identifiers +If the \var{allow_fragments} argument is false, fragment identifiers are not allowed, even if the URL's addressing scheme normally does -support them. The default value for this argument is \code{1}. -\end{funcdesc} +support them. The default value for this argument is \constant{True}. -\begin{funcdesc}{urlunparse}{tuple} -Construct a URL string from a tuple as returned by \code{urlparse()}. +The return value is actually an instance of a subclass of +\pytype{tuple}. This class has the following additional read-only +convenience attributes: + +\begin{tableiv}{l|c|l|c}{member}{Attribute}{Index}{Value}{Value if not present} + \lineiv{scheme} {0} {URL scheme specifier} {empty string} + \lineiv{netloc} {1} {Network location part} {empty string} + \lineiv{path} {2} {Hierarchical path} {empty string} + \lineiv{params} {3} {Parameters for last path element} {empty string} + \lineiv{query} {4} {Query component} {empty string} + \lineiv{fragment}{5} {Fragment identifier} {empty string} + \lineiv{username}{ } {User name} {\constant{None}} + \lineiv{password}{ } {Password} {\constant{None}} + \lineiv{hostname}{ } {Host name (lower case)} {\constant{None}} + \lineiv{port} { } {Port number as integer, if present} {\constant{None}} +\end{tableiv} + +See section~\ref{urlparse-result-object}, ``Results of +\function{urlparse()} and \function{urlsplit()},'' for more +information on the result object. + +\versionchanged[Added attributes to return value]{2.5} +\end{funcdesc} + +\begin{funcdesc}{urlunparse}{parts} +Construct a URL from a tuple as returned by \code{urlparse()}. +The \var{parts} argument be any six-item iterable. This may result in a slightly different, but equivalent URL, if the -URL that was parsed originally had redundant delimiters, e.g. a ? with -an empty query (the draft states that these are equivalent). +URL that was parsed originally had unnecessary delimiters (for example, +a ? with an empty query; the RFC states that these are equivalent). \end{funcdesc} \begin{funcdesc}{urlsplit}{urlstring\optional{, @@ -79,12 +105,38 @@ separate the path segments and parameters. This function returns a 5-tuple: (addressing scheme, network location, path, query, fragment identifier). + +The return value is actually an instance of a subclass of +\pytype{tuple}. This class has the following additional read-only +convenience attributes: + +\begin{tableiv}{l|c|l|c}{member}{Attribute}{Index}{Value}{Value if not present} + \lineiv{scheme} {0} {URL scheme specifier} {empty string} + \lineiv{netloc} {1} {Network location part} {empty string} + \lineiv{path} {2} {Hierarchical path} {empty string} + \lineiv{query} {3} {Query component} {empty string} + \lineiv{fragment} {4} {Fragment identifier} {empty string} + \lineiv{username} { } {User name} {\constant{None}} + \lineiv{password} { } {Password} {\constant{None}} + \lineiv{hostname} { } {Host name (lower case)} {\constant{None}} + \lineiv{port} { } {Port number as integer, if present} {\constant{None}} +\end{tableiv} + +See section~\ref{urlparse-result-object}, ``Results of +\function{urlparse()} and \function{urlsplit()},'' for more +information on the result object. + \versionadded{2.2} +\versionchanged[Added attributes to return value]{2.5} \end{funcdesc} -\begin{funcdesc}{urlunsplit}{tuple} +\begin{funcdesc}{urlunsplit}{parts} Combine the elements of a tuple as returned by \function{urlsplit()} into a complete URL as a string. +The \var{parts} argument be any five-item iterable. +This may result in a slightly different, but equivalent URL, if the +URL that was parsed originally had unnecessary delimiters (for example, +a ? with an empty query; the RFC states that these are equivalent). \versionadded{2.2} \end{funcdesc} @@ -93,22 +145,16 @@ (\var{base}) with a ``relative URL'' (\var{url}). Informally, this uses components of the base URL, in particular the addressing scheme, the network location and (part of) the path, to provide missing -components in the relative URL. - -Example: - -\begin{verbatim} -urljoin('http://www.cwi.nl/%7Eguido/Python.html', 'FAQ.html') -\end{verbatim} - -yields the string +components in the relative URL. For example: \begin{verbatim} +>>> from urlparse import urljoin +>>> urljoin('http://www.cwi.nl/%7Eguido/Python.html', 'FAQ.html') 'http://www.cwi.nl/%7Eguido/FAQ.html' \end{verbatim} -The \var{allow_fragments} argument has the same meaning as for -\code{urlparse()}. +The \var{allow_fragments} argument has the same meaning and default as +for \function{urlparse()}. \end{funcdesc} \begin{funcdesc}{urldefrag}{url} @@ -133,3 +179,61 @@ both Uniform Resource Names (URNs) and Uniform Resource Locators (URLs).} \end{seealso} + + +\subsection{Results of \function{urlparse()} and \function{urlsplit()} + \label{urlparse-result-object}} + +The result objects from the \function{urlparse()} and +\function{urlsplit()} functions are subclasses of the \pytype{tuple} +type. These subclasses add the attributes described in those +functions, as well as provide an additional method: + +\begin{methoddesc}[ParseResult]{geturl}{} + Return the re-combined version of the original URL as a string. + This may differ from the original URL in that the scheme will always + be normalized to lower case and empty components may be dropped. + Specifically, empty parameters, queries, and fragment identifiers + will be removed. + + The result of this method is a fixpoint if passed back through the + original parsing function: + +\begin{verbatim} +>>> import urlparse +>>> url = 'HTTP://www.Python.org/doc/#' + +>>> r1 = urlparse.urlsplit(url) +>>> r1.geturl() +'http://www.Python.org/doc/' + +>>> r2 = urlparse.urlsplit(r1.geturl()) +>>> r2.geturl() +'http://www.Python.org/doc/' +\end{verbatim} + +\versionadded{2.5} +\end{methoddesc} + +The following classes provide the implementations of the parse results:: + +\begin{classdesc*}{BaseResult} + Base class for the concrete result classes. This provides most of + the attribute definitions. It does not provide a \method{geturl()} + method. It is derived from \class{tuple}, but does not override the + \method{__init__()} or \method{__new__()} methods. +\end{classdesc*} + + +\begin{classdesc}{ParseResult}{scheme, netloc, path, params, query, fragment} + Concrete class for \function{urlparse()} results. The + \method{__new__()} method is overridden to support checking that the + right number of arguments are passed. +\end{classdesc} + + +\begin{classdesc}{SplitResult}{scheme, netloc, path, query, fragment} + Concrete class for \function{urlsplit()} results. The + \method{__new__()} method is overridden to support checking that the + right number of arguments are passed. +\end{classdesc} Modified: python/trunk/Lib/test/test_urlparse.py ============================================================================== --- python/trunk/Lib/test/test_urlparse.py (original) +++ python/trunk/Lib/test/test_urlparse.py Sun Apr 2 00:14:43 2006 @@ -12,15 +12,53 @@ def checkRoundtrips(self, url, parsed, split): result = urlparse.urlparse(url) self.assertEqual(result, parsed) + t = (result.scheme, result.netloc, result.path, + result.params, result.query, result.fragment) + self.assertEqual(t, parsed) # put it back together and it should be the same result2 = urlparse.urlunparse(result) self.assertEqual(result2, url) + self.assertEqual(result2, result.geturl()) + + # the result of geturl() is a fixpoint; we can always parse it + # again to get the same result: + result3 = urlparse.urlparse(result.geturl()) + self.assertEqual(result3.geturl(), result.geturl()) + self.assertEqual(result3, result) + self.assertEqual(result3.scheme, result.scheme) + self.assertEqual(result3.netloc, result.netloc) + self.assertEqual(result3.path, result.path) + self.assertEqual(result3.params, result.params) + self.assertEqual(result3.query, result.query) + self.assertEqual(result3.fragment, result.fragment) + self.assertEqual(result3.username, result.username) + self.assertEqual(result3.password, result.password) + self.assertEqual(result3.hostname, result.hostname) + self.assertEqual(result3.port, result.port) # check the roundtrip using urlsplit() as well result = urlparse.urlsplit(url) self.assertEqual(result, split) + t = (result.scheme, result.netloc, result.path, + result.query, result.fragment) + self.assertEqual(t, split) result2 = urlparse.urlunsplit(result) self.assertEqual(result2, url) + self.assertEqual(result2, result.geturl()) + + # check the fixpoint property of re-parsing the result of geturl() + result3 = urlparse.urlsplit(result.geturl()) + self.assertEqual(result3.geturl(), result.geturl()) + self.assertEqual(result3, result) + self.assertEqual(result3.scheme, result.scheme) + self.assertEqual(result3.netloc, result.netloc) + self.assertEqual(result3.path, result.path) + self.assertEqual(result3.query, result.query) + self.assertEqual(result3.fragment, result.fragment) + self.assertEqual(result3.username, result.username) + self.assertEqual(result3.password, result.password) + self.assertEqual(result3.hostname, result.hostname) + self.assertEqual(result3.port, result.port) def test_roundtrips(self): testcases = [ @@ -187,6 +225,69 @@ ]: self.assertEqual(urlparse.urldefrag(url), (defrag, frag)) + def test_urlsplit_attributes(self): + url = "HTTP://WWW.PYTHON.ORG/doc/#frag" + p = urlparse.urlsplit(url) + self.assertEqual(p.scheme, "http") + self.assertEqual(p.netloc, "WWW.PYTHON.ORG") + self.assertEqual(p.path, "/doc/") + self.assertEqual(p.query, "") + self.assertEqual(p.fragment, "frag") + self.assertEqual(p.username, None) + self.assertEqual(p.password, None) + self.assertEqual(p.hostname, "www.python.org") + self.assertEqual(p.port, None) + # geturl() won't return exactly the original URL in this case + # since the scheme is always case-normalized + #self.assertEqual(p.geturl(), url) + + url = "http://User:Pass at www.python.org:080/doc/?query=yes#frag" + p = urlparse.urlsplit(url) + self.assertEqual(p.scheme, "http") + self.assertEqual(p.netloc, "User:Pass at www.python.org:080") + self.assertEqual(p.path, "/doc/") + self.assertEqual(p.query, "query=yes") + self.assertEqual(p.fragment, "frag") + self.assertEqual(p.username, "User") + self.assertEqual(p.password, "Pass") + self.assertEqual(p.hostname, "www.python.org") + self.assertEqual(p.port, 80) + self.assertEqual(p.geturl(), url) + + def test_attributes_bad_port(self): + """Check handling of non-integer ports.""" + p = urlparse.urlsplit("http://www.example.net:foo") + self.assertEqual(p.netloc, "www.example.net:foo") + self.assertRaises(ValueError, lambda: p.port) + + p = urlparse.urlparse("http://www.example.net:foo") + self.assertEqual(p.netloc, "www.example.net:foo") + self.assertRaises(ValueError, lambda: p.port) + + def test_attributes_without_netloc(self): + # This example is straight from RFC 3261. It looks like it + # should allow the username, hostname, and port to be filled + # in, but doesn't. Since it's a URI and doesn't use the + # scheme://netloc syntax, the netloc and related attributes + # should be left empty. + uri = "sip:alice at atlanta.com;maddr=239.255.255.1;ttl=15" + p = urlparse.urlsplit(uri) + self.assertEqual(p.netloc, "") + self.assertEqual(p.username, None) + self.assertEqual(p.password, None) + self.assertEqual(p.hostname, None) + self.assertEqual(p.port, None) + self.assertEqual(p.geturl(), uri) + + p = urlparse.urlparse(uri) + self.assertEqual(p.netloc, "") + self.assertEqual(p.username, None) + self.assertEqual(p.password, None) + self.assertEqual(p.hostname, None) + self.assertEqual(p.port, None) + self.assertEqual(p.geturl(), uri) + + def test_main(): test_support.run_unittest(UrlParseTestCase) Modified: python/trunk/Lib/urlparse.py ============================================================================== --- python/trunk/Lib/urlparse.py (original) +++ python/trunk/Lib/urlparse.py Sun Apr 2 00:14:43 2006 @@ -41,7 +41,111 @@ _parse_cache = {} -def urlparse(url, scheme='', allow_fragments=1): +class BaseResult(tuple): + """Base class for the parsed result objects. + + This provides the attributes shared by the two derived result + objects as read-only properties. The derived classes are + responsible for checking the right number of arguments were + supplied to the constructor. + + """ + + __slots__ = () + + # Attributes that access the basic components of the URL: + + @property + def scheme(self): + return self[0] + + @property + def netloc(self): + return self[1] + + @property + def path(self): + return self[2] + + @property + def query(self): + return self[-2] + + @property + def fragment(self): + return self[-1] + + # Additional attributes that provide access to parsed-out portions + # of the netloc: + + @property + def username(self): + netloc = self.netloc + if "@" in netloc: + userinfo = netloc.split("@", 1)[0] + if ":" in userinfo: + userinfo = userinfo.split(":", 1)[0] + return userinfo + return None + + @property + def password(self): + netloc = self.netloc + if "@" in netloc: + userinfo = netloc.split("@", 1)[0] + if ":" in userinfo: + return userinfo.split(":", 1)[1] + return None + + @property + def hostname(self): + netloc = self.netloc + if "@" in netloc: + netloc = netloc.split("@", 1)[1] + if ":" in netloc: + netloc = netloc.split(":", 1)[0] + return netloc.lower() or None + + @property + def port(self): + netloc = self.netloc + if "@" in netloc: + netloc = netloc.split("@", 1)[1] + if ":" in netloc: + port = netloc.split(":", 1)[1] + return int(port, 10) + return None + + +class SplitResult(BaseResult): + + __slots__ = () + + def __new__(cls, scheme, netloc, path, query, fragment): + return BaseResult.__new__( + cls, (scheme, netloc, path, query, fragment)) + + def geturl(self): + return urlunsplit(self) + + +class ParseResult(BaseResult): + + __slots__ = () + + def __new__(cls, scheme, netloc, path, params, query, fragment): + return BaseResult.__new__( + cls, (scheme, netloc, path, params, query, fragment)) + + @property + def params(self): + return self[3] + + def geturl(self): + return urlunparse(self) + + +def urlparse(url, scheme='', allow_fragments=True): """Parse a URL into 6 components: :///;?# Return a 6-tuple: (scheme, netloc, path, params, query, fragment). @@ -53,7 +157,7 @@ url, params = _splitparams(url) else: params = '' - return scheme, netloc, url, params, query, fragment + return ParseResult(scheme, netloc, url, params, query, fragment) def _splitparams(url): if '/' in url: @@ -73,12 +177,13 @@ delim = len(url) return url[start:delim], url[delim:] -def urlsplit(url, scheme='', allow_fragments=1): +def urlsplit(url, scheme='', allow_fragments=True): """Parse a URL into 5 components: :///?# Return a 5-tuple: (scheme, netloc, path, query, fragment). Note that we don't break the components up in smaller bits (e.g. netloc is a single string) and we don't expand % escapes.""" + allow_fragments = bool(allow_fragments) key = url, scheme, allow_fragments cached = _parse_cache.get(key, None) if cached: @@ -97,9 +202,9 @@ url, fragment = url.split('#', 1) if '?' in url: url, query = url.split('?', 1) - tuple = scheme, netloc, url, query, fragment - _parse_cache[key] = tuple - return tuple + v = SplitResult(scheme, netloc, url, query, fragment) + _parse_cache[key] = v + return v for c in url[:i]: if c not in scheme_chars: break @@ -111,9 +216,9 @@ url, fragment = url.split('#', 1) if scheme in uses_query and '?' in url: url, query = url.split('?', 1) - tuple = scheme, netloc, url, query, fragment - _parse_cache[key] = tuple - return tuple + v = SplitResult(scheme, netloc, url, query, fragment) + _parse_cache[key] = v + return v def urlunparse((scheme, netloc, url, params, query, fragment)): """Put a parsed URL back together again. This may result in a @@ -136,7 +241,7 @@ url = url + '#' + fragment return url -def urljoin(base, url, allow_fragments = 1): +def urljoin(base, url, allow_fragments=True): """Join a base URL and a possibly relative URL to form an absolute interpretation of the latter.""" if not base: Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Sun Apr 2 00:14:43 2006 @@ -489,6 +489,9 @@ Library ------- +- Patch #624325: urlparse.urlparse() and urlparse.urlsplit() results + now sport attributes that provide access to the parts of the result. + - Patch #1462498: sgmllib now handles entity and character references in attribute values. From python-checkins at python.org Sun Apr 2 03:46:33 2006 From: python-checkins at python.org (andrew.kuchling) Date: Sun, 2 Apr 2006 03:46:33 +0200 (CEST) Subject: [Python-checkins] r43547 - python/trunk/Doc/whatsnew/whatsnew25.tex Message-ID: <20060402014633.98C301E4008@bag.python.org> Author: andrew.kuchling Date: Sun Apr 2 03:46:32 2006 New Revision: 43547 Modified: python/trunk/Doc/whatsnew/whatsnew25.tex Log: Write various sections; I haven't been able to check whether the TeX markup is correct Modified: python/trunk/Doc/whatsnew/whatsnew25.tex ============================================================================== --- python/trunk/Doc/whatsnew/whatsnew25.tex (original) +++ python/trunk/Doc/whatsnew/whatsnew25.tex Sun Apr 2 03:46:32 2006 @@ -2,6 +2,11 @@ \usepackage{distutils} % $Id$ +% Fix XXX comments +% Distutils upload +% The easy_install stuff +% xml.etree section +% added sqlite3 \title{What's New in Python 2.5} \release{0.0} @@ -55,11 +60,12 @@ x = true_value if condition else false_value \end{verbatim} -Evaluation is still lazy as in existing Boolean expression, so the -evaluation jumps around a bit. The \var{condition} expression is -evaluated first, and the \var{true_value} expression is evaluated only -if the condition was true. Similarly, the \var{false_value} -expression is only evaluated when the condition is false. +Evaluation is still lazy as in existing Boolean expressions, so the +order of evaluation jumps around a bit. The \var{condition} +expression in the middle is evaluated first, and the \var{true_value} +expression is evaluated only if the condition was true. Similarly, +the \var{false_value} expression is only evaluated when the condition +is false. This syntax may seem strange and backwards; why does the condition go in the \emph{middle} of the expression, and not in the front as in C's @@ -206,7 +212,96 @@ %====================================================================== \section{PEP 328: Absolute and Relative Imports} -% XXX write this +The simpler part of PEP 328 was implemented in Python 2.4: parentheses +could now be used to enclose the names imported from a module using +the \code{from ... import ...} statement, making it easier to import +many different names. + +The more complicated part has been implemented in Python 2.5: +importing a module can be specified to use absolute or +package-relative imports. The plan is to move toward making absolute +imports the default in future versions of Python. + +Let's say you have a package directory like this: +\begin{verbatim} +pkg/ +pkg/__init__.py +pkg/main.py +pkg/string.py +\end{verbatim} + +This defines a package named \module{pkg} containing the +\module{pkg.main} and \module{pkg.string} submodules. + +Consider the code in the \file{main.py} module. What happens if it +executes the statement \code{import string}? In Python 2.4 and +earlier, it will first look in the package's directory to perform a +relative import, finds \file{pkg/string.py}, imports the contents of +that file as the \module{pkg.string} module, and that module is bound +to the name \samp{string} in the \module{pkg.main} module's namespace. + +That's fine if \module{pkg.string} was what you wanted. But what if +you wanted Python's standard \module{string} module? There's no clean +way to ignore \module{pkg.string} and look for the standard module; +generally you had to look at the contents of \code{sys.modules}, which +is slightly unclean. +Holger Krekel's py.std package provides a tidier way to perform +imports from the standard library, \code{from py.std import string}, +% XXX correct attribution? +% XXX is that import correct? +but that package isn't available on all Python installations. + +Reading code which relies on relative imports is also less clear, +because a reader may be confused about which module, \module{string} +or \module{pkg.string}, is intended to be used. Python users soon +learned not to duplicate the names of standard library modules in the +names of their packages' submodules, but you can't protect against +having your submodule's name being used for a new module added in a +future version of Python. + +In Python 2.5, you can switch \keyword{import}'s behaviour to +absolute imports using a \code{from __future__ import absolute_import} +directive. This absolute-import behaviour will become the default in +a future version (probably Python 2.6). Once absolute-imports +are the default, \code{import string} will +always find the standard library's version. +It's suggested that users should begin using absolute imports as much +as possible, so it's preferable to begin writing \code{from pkg import +string} in your code. + +Relative imports are still possible by adding a leading period +to the module name when using the \code{from ... import} form: + +\begin{verbatim} +# Import names from pkg.string +from .string import name1, name2 +# Import pkg.string +from . import string +\end{verbatim} + +This imports the \module{string} module relative to the current +package, so in \module{pkg.main} this will import \var{name1} and +\var{name2} from \module{pkg.string}. Additional leading periods +perform the relative import starting from the parent of the current +package. For example, code in the \module{A.B.C} module can do: + +\begin{verbatim} +from . import D # Imports A.B.D +from .. import E # Imports A.E +from ..F import G # Imports A.F.G +\end{verbatim} + +Leading periods cannot be used with the \code{import \var{modname}} +form of the import statement, only the \code{from ... import} form. + +\begin{seealso} + +\seepep{328}{Imports: Multi-Line and Absolute/Relative}{PEP written +by Aahz; implemented by XXX.} + +\seeurl{XXX}{py.std} + +\end{seealso} %====================================================================== @@ -236,19 +331,62 @@ %====================================================================== \section{PEP 341: Unified try/except/finally} -% XXX write this +Until Python 2.5, the \keyword{try} statement came in two +flavours. You could use a \keyword{finally} block to ensure that code +is always executed, or a number of \keyword{except} blocks to catch an +exception. You couldn't combine both \keyword{except} blocks and a +\keyword{finally} block, because generating the right bytecode for the +combined version was complicated and it wasn't clear what the +semantics of the combined should be. + +GvR spent some time working with Java, which does support the +equivalent of combining \keyword{except} blocks and a +\keyword{finally} block, and this clarified what the statement should +mean. In Python 2.5, you can now write: + +\begin{verbatim} +try: + block-1 ... +except Exception1: + handler-1 ... +except Exception2: + handler-2 ... +else: + else-block +finally: + final-block +\end{verbatim} + +The code in \var{block-1} is executed. If the code raises an +exception, the handlers are tried in order: \var{handler-1}, +\var{handler-2}, ... If no exception is raised, the \var{else-block} +is executed. No matter what happened previously, the +\var{final-block} is executed once the code block is complete and any +raised exceptions handled. Even if there's an error in an exception +handler or the \var{else-block} and a new exception is raised, the +\var{final-block} is still executed. + +\begin{seealso} + +\seepep{341}{Unifying try-except and try-finally}{PEP written by Georg Brandl; +implementation by Thomas Lee. +XXX check implementor -- http://python.org/sf/1355913 +} + +\end{seealso} %====================================================================== \section{PEP 342: New Generator Features} +Python 2.5 adds a simple way to pass values \emph{into} a generator. As introduced in Python 2.3, generators only produce output; once a generator's code is invoked to create an iterator, there's no way to pass any new information into the function when its execution is -resumed. Hackish solutions to this include making the generator's -code look at a global variable and then changing the global variable's +resumed. Sometimes the ability to pass in some information would be +useful. Hackish solutions to this include making the generator's code +look at a global variable and then changing the global variable's value, or passing in some mutable object that callers then modify. -Python 2.5 adds the ability to pass values \emph{into} a generator. To refresh your memory of basic generators, here's a simple example: @@ -362,8 +500,22 @@ subroutines. Subroutines are entered at one point and exited at another point (the top of the function, and a \keyword{return statement}), but coroutines can be entered, exited, and resumed at -many different points (the \keyword{yield} statements). +many different points (the \keyword{yield} statements). We'll have to +figure out patterns for using coroutines effectively in Python. +The addition of the \method{close()} method has one side effect that +isn't obvious. \method{close()} is called when a generator is +garbage-collected, so this means the generator's code gets one last +chance to run before the generator is destroyed, and this last chance +means that \code{try...finally} statements in generators can now be +guaranteed to work; the \keyword{finally} clause will now always get a +chance to run. The syntactic restriction that you couldn't mix +\keyword{yield} statements with a \code{try...finally} suite has +therefore been removed. This seems like a minor bit of language +trivia, but using generators and \code{try...finally} is actually +necessary in order to implement the \keyword{with} statement +described by PEP 343. We'll look at this new statement in the following +section. \begin{seealso} @@ -385,14 +537,104 @@ %====================================================================== \section{PEP 343: The 'with' statement} +The \keyword{with} statement allows a clearer +version of code that uses \code{try...finally} blocks + +First, I'll discuss the statement as it will commonly be used, and +then I'll discuss the detailed implementation and how to write objects +(called ``context managers'') that can be used with this statement. +Most people, who will only use \keyword{with} in company with an +existing object, don't need to know these details, but can +Authors of new context managers will need to understand the + +The \keyword{with} statement is a new control-flow structure whose +basic structure is: + +\begin{verbatim} +with expression as variable: + with-block +\end{verbatim} + +The expression is evaluated, and it should result in a type of object +that's called a context manager. The context manager can return a +value that will be bound to the name \var{variable}. (Note carefully: +\var{variable} is \emph{not} assigned the result of \var{expression}. +One method of the context manager is run before \var{with-block} is +executed, and another method is run after the block is done, even if +the block raised an exception. + +To enable the statement in Python 2.5, you need +to add the following directive to your module: + +\begin{verbatim} +from __future__ import with_statement +\end{verbatim} + +Some standard Python objects can now behave as context managers. For +example, file objects: + +\begin{verbatim} +with open('/etc/passwd', 'r') as f: + for line in f: + print line + +# f has been automatically closed at this point. +\end{verbatim} + +The \module{threading} module's locks and condition variables +also support the new statement: + +\begin{verbatim} +lock = threading.Lock() +with lock: + # Critical section of code + ... +\end{verbatim} + +The lock is acquired before the block is executed, and released once +the block is complete. + +The \module{decimal} module's contexts, which encapsulate the desired +precision and rounding characteristics for computations, can also be +used as context managers. + +\begin{verbatim} +import decimal + +v1 = decimal.Decimal('578') + +# Displays with default precision of 28 digits +print v1.sqrt() + +with decimal.Context(prec=16): + # All code in this block uses a precision of 16 digits. + # The original context is restored on exiting the block. + print v1.sqrt() +\end{verbatim} + +\subsection{Writing Context Managers} + % XXX write this +The new \module{contextlib} module provides some functions and a +decorator that are useful for writing context managers. +% XXX describe further + +\begin{seealso} + +\seepep{343}{The ``with'' statement}{PEP written by +Guido van Rossum and Nick Coghlan. } + +\end{seealso} + %====================================================================== \section{PEP 352: Exceptions as New-Style Classes} -Exception classes can now be new-style classes, not just classic classes, -and the built-in \exception{Exception} class and all +Exception classes can now be new-style classes, not just classic +classes, and the built-in \exception{Exception} class and all the +standard built-in exceptions (\exception{NameError}, +\exception{ValueError}, etc.) are now new-style classes. The inheritance hierarchy for exceptions has been rearranged a bit. In 2.5, the inheritance relationships are: @@ -455,7 +697,47 @@ %====================================================================== \section{PEP 357: The '__index__' method} -% XXX write this +The NumPy developers had a problem that could only be solved by adding +a new special method, \method{__index__}. When using slice notation, +as in \code{[\var{start}:\var{stop}:\var{step}], the values of the +\var{start}, \var{stop}, and \var{step} indexes must all be either +integers or long integers. NumPy defines a variety of specialized +integer types corresponding to unsigned and signed integers of 8, 16, +32, and 64 bits, but there was no way to signal that these types could +be used as slice indexes. + +Slicing can't just use the existing \method{__int__} method because +that method is also used to implement coercion to integers. If +slicing used \method{__int__}, floating-point numbers would also +become legal slice indexes and that's clearly an undesirable +behaviour. + +Instead, a new special method called \method{__index__} was added. It +takes no arguments and returns an integer giving the slice index to +use. For example: + +\begin{verbatim} +class C: + def __index__ (self): + return self.value +\end{verbatim} + +The return value must be either a Python integer or long integer. +The interpreter will check that the type returned is correct, and +raises a \exception{TypeError} if this requirement isn't met. + +A corresponding \member{nb_index} slot was added to the C-level +\ctype{PyNumberMethods} structure to let C extensions implement this +protocol. \cfunction{PyNumber_Index(\var{obj})} can be used in +extension code to call the \method{__index__} function and retrieve +its result. + +\begin{seealso} + +\seepep{357}{Allowing Any Object to be Used for Slicing}{PEP written +(XXX and implemented?) by Travis Oliphant.} + +\end{seealso} %====================================================================== @@ -503,6 +785,8 @@ \end{verbatim} (Implemented by Brett Cannon.) +% XXX __missing__ hook in dictionaries + \end{itemize} @@ -536,6 +820,14 @@ and as a result sets will use a third less memory and are somewhat faster. (Implemented by Raymond Hettinger.) +\item The performance of some Unicode operations has been improved. +% XXX provide details? + +\item The code generator's peephole optimizer now performs +simple constant folding in expressions. If you write something like +\code{a = 2+3}, the code generator will do the arithmetic and produce +code corresponding to \code{a = 5}. + \end{itemize} The net result of the 2.5 optimizations is that Python 2.5 runs the @@ -557,6 +849,7 @@ % ctypes added % collections.deque now has .remove() +% collections.defaultdict % the cPickle module no longer accepts the deprecated None option in the % args tuple returned by __reduce__(). @@ -566,6 +859,11 @@ % datetime.datetime() now has a strptime class method which can be used to % create datetime object using a string and format. +% fileinput: opening hook used to control how files are opened. +% .input() now has a mode parameter +% now has a fileno() function +% accepts Unicode filenames + \item In the \module{gc} module, the new \function{get_count()} function returns a 3-tuple containing the current collection counts for the three GC generations. This is accounting information for the garbage @@ -574,14 +872,6 @@ function now takes an optional \var{generation} argument of 0, 1, or 2 to specify which generation to collect. -\item A new \module{hashlib} module has been added to replace the -\module{md5} and \module{sha} modules. \module{hashlib} adds support -for additional secure hashes (SHA-224, SHA-256, SHA-384, and SHA-512). -When available, the module uses OpenSSL for fast platform optimized -implementations of algorithms. The old \module{md5} and \module{sha} -modules still exist as wrappers around hashlib to preserve backwards -compatibility. (Contributed by Gregory P. Smith.) - \item The \function{nsmallest()} and \function{nlargest()} functions in the \module{heapq} module now support a \code{key} keyword argument similar to the one @@ -635,6 +925,17 @@ \function{os.lseek()} function. Two new constants for locking are \member{os.O_SHLOCK} and \member{os.O_EXLOCK}. +Two new functions, \function{wait3()} and \function{wait4()}, were +added. They're similar the \function{waitpid()} function which waits +for a child process to exit and returns a tuple of the process ID and +its exit status, but \function{wait3()} and \function{wait4()} return +additional information. \function{wait3()} doesn't take a process ID +as input, so it waits for any child process to exit and returns a +3-tuple of \var{process-id}, \var{exit-status}, \var{resource-usage} +as returned from the \function{resource.getrusage()} function. +\function{wait4(\var{pid})} does take a process ID. +(Contributed by XXX.) + On FreeBSD, the \function{os.stat()} function now returns times with nanosecond resolution, and the returned object now has \member{st_gen} and \member{st_birthtime}. @@ -660,10 +961,16 @@ In Python code, netlink addresses are represented as a tuple of 2 integers, \code{(\var{pid}, \var{group_mask})}. +Socket objects also gained accessor methods \method{getfamily()}, +\method{gettype()}, and \method{getproto()} methods to retrieve the +family, type, and protocol values for the socket. + \item New module: \module{spwd} provides functions for accessing the shadow password database on systems that support it. % XXX give example +% XXX patch #1382163: sys.subversion, Py_GetBuildNumber() + \item The \class{TarFile} class in the \module{tarfile} module now has an \method{extractall()} method that extracts all members from the archive into the current working directory. It's also possible to set @@ -680,6 +987,8 @@ by some specifications, so it's still available as \member{unicodedata.db_3_2_0}. +% patch #754022: Greatly enhanced webbrowser.py (by Oleg Broytmann). + \item A new package \module{xml.etree} has been added, which contains a subset of the ElementTree XML library. Available modules are \module{ElementTree}, \module{ElementPath}, and @@ -698,14 +1007,63 @@ %====================================================================== -% whole new modules get described in \subsections here +% whole new modules get described in subsections here % XXX new distutils features: upload -% XXX should hashlib perhaps be described here instead? -% XXX should xml.etree perhaps be described here instead? +%\subsection{The ElementTree package} + +\subsection{The hashlib package} +A new \module{hashlib} module has been added to replace the +\module{md5} and \module{sha} modules. \module{hashlib} adds support +for additional secure hashes (SHA-224, SHA-256, SHA-384, and SHA-512). +When available, the module uses OpenSSL for fast platform optimized +implementations of algorithms. + +The old \module{md5} and \module{sha} modules still exist as wrappers +around hashlib to preserve backwards compatibility. The new module's +interface is very close to that of the old modules, but not identical. +The most significant difference is that the constructor functions +for creating new hashing objects are named differently. +\begin{verbatim} +# Old versions +h = md5.md5() +h = md5.new() + +# New version +h = hashlib.md5() + +# Old versions +h = sha.sha() +h = sha.new() + +# New version +h = hashlib.sha1() + +# Hash that weren't previously available +h = hashlib.sha224() +h = hashlib.sha256() +h = hashlib.sha384() +h = hashlib.sha512() + +# Alternative form +h = hashlib.new('md5') # Provide algorithm as a string +\end{verbatim} + +Once a hash object has been created, its methods are the same as before: +\method{update(\var{string})} hashes the specified string into the +current digest state, \method{digest()} and \method{hexdigest()} +return the digest value as a binary string or a string of hex digits, +and \method{copy()} returns a new hashing object with the same digest state. + +This module was contributed by Gregory P. Smith. + + +%\subsection{The sqlite3 package} + +% XXX write these sections % ====================================================================== \section{Build and C API Changes} @@ -714,8 +1072,9 @@ \begin{itemize} -\item The design of the bytecode compiler has changed a great deal, no -longer generating bytecode by traversing the parse tree. Instead +% XXX PEP 353: ssize_t +\item The design of the bytecode compiler has changed a great deal, to +no longer generate bytecode by traversing the parse tree. Instead the parse tree is converted to an abstract syntax tree (or AST), and it is the abstract syntax tree that's traversed to produce the bytecode. @@ -753,9 +1112,9 @@ %====================================================================== -\subsection{Port-Specific Changes} +%\subsection{Port-Specific Changes} -Platform-specific changes go here. +%Platform-specific changes go here. %====================================================================== @@ -779,6 +1138,12 @@ the operating system. (Implemented by Evan Jones, and reworked by Tim Peters.) +\item Coverity, a company that markets a source code analysis tool + called Prevent, provided the results of their examination of the Python + source code. The analysis found a number of refcounting bugs, often + in error-handling code. These bugs have been fixed. + % XXX provide reference? + \end{itemize} From python-checkins at python.org Sun Apr 2 03:47:39 2006 From: python-checkins at python.org (andrew.kuchling) Date: Sun, 2 Apr 2006 03:47:39 +0200 (CEST) Subject: [Python-checkins] r43548 - python/trunk/Doc/lib/libhashlib.tex Message-ID: <20060402014739.1A61B1E400D@bag.python.org> Author: andrew.kuchling Date: Sun Apr 2 03:47:38 2006 New Revision: 43548 Modified: python/trunk/Doc/lib/libhashlib.tex Log: Grammar fix Modified: python/trunk/Doc/lib/libhashlib.tex ============================================================================== --- python/trunk/Doc/lib/libhashlib.tex (original) +++ python/trunk/Doc/lib/libhashlib.tex Sun Apr 2 03:47:38 2006 @@ -31,7 +31,7 @@ Constructors for hash algorithms that are always present in this module are \function{md5()}, \function{sha1()}, \function{sha224()}, \function{sha256()}, \function{sha384()}, and \function{sha512()}. Additional algorithms may also -be available depending upon the OpenSSL library python uses on your platform. +be available depending upon the OpenSSL library that Python uses on your platform. \index{OpenSSL} For example, to obtain the digest of the string \code{'Nobody inspects From dynkin at gmail.com Sun Apr 2 04:46:57 2006 From: dynkin at gmail.com (George Yoshida) Date: Sun, 2 Apr 2006 11:46:57 +0900 Subject: [Python-checkins] r43547 - python/trunk/Doc/whatsnew/whatsnew25.tex In-Reply-To: <20060402014633.98C301E4008@bag.python.org> References: <20060402014633.98C301E4008@bag.python.org> Message-ID: <2f188ee80604011846r559fa5e9ted0fb508d186d002@mail.gmail.com> On 4/2/06, andrew.kuchling wrote: > Author: andrew.kuchling > Date: Sun Apr 2 03:46:32 2006 > New Revision: 43547 > > Modified: > python/trunk/Doc/whatsnew/whatsnew25.tex > Log: > Write various sections; I haven't been able to check whether the TeX markup is correct > With this fix, at least the compilation should succeed, although I haven't checked every piece of the update in detail. Index: Doc/whatsnew/whatsnew25.tex =================================================================== --- Doc/whatsnew/whatsnew25.tex (revision 43548) +++ Doc/whatsnew/whatsnew25.tex (working copy) @@ -699,7 +699,7 @@ The NumPy developers had a problem that could only be solved by adding a new special method, \method{__index__}. When using slice notation, -as in \code{[\var{start}:\var{stop}:\var{step}], the values of the +as in \code{[\var{start}:\var{stop}:\var{step}]}, the values of the \var{start}, \var{stop}, and \var{step} indexes must all be either integers or long integers. NumPy defines a variety of specialized integer types corresponding to unsigned and signed integers of 8, 16, george at arp:/opt/python$ From python-checkins at python.org Sun Apr 2 05:30:07 2006 From: python-checkins at python.org (fred.drake) Date: Sun, 2 Apr 2006 05:30:07 +0200 (CEST) Subject: [Python-checkins] r43549 - python/trunk/Doc/whatsnew/whatsnew25.tex Message-ID: <20060402033007.780581E4008@bag.python.org> Author: fred.drake Date: Sun Apr 2 05:30:06 2006 New Revision: 43549 Modified: python/trunk/Doc/whatsnew/whatsnew25.tex Log: fix markup error Modified: python/trunk/Doc/whatsnew/whatsnew25.tex ============================================================================== --- python/trunk/Doc/whatsnew/whatsnew25.tex (original) +++ python/trunk/Doc/whatsnew/whatsnew25.tex Sun Apr 2 05:30:06 2006 @@ -699,7 +699,7 @@ The NumPy developers had a problem that could only be solved by adding a new special method, \method{__index__}. When using slice notation, -as in \code{[\var{start}:\var{stop}:\var{step}], the values of the +as in \code{[\var{start}:\var{stop}:\var{step}]}, the values of the \var{start}, \var{stop}, and \var{step} indexes must all be either integers or long integers. NumPy defines a variety of specialized integer types corresponding to unsigned and signed integers of 8, 16, From fdrake at acm.org Sun Apr 2 05:30:16 2006 From: fdrake at acm.org (Fred L. Drake, Jr.) Date: Sat, 1 Apr 2006 22:30:16 -0500 Subject: [Python-checkins] r43547 - python/trunk/Doc/whatsnew/whatsnew25.tex In-Reply-To: <2f188ee80604011846r559fa5e9ted0fb508d186d002@mail.gmail.com> References: <20060402014633.98C301E4008@bag.python.org> <2f188ee80604011846r559fa5e9ted0fb508d186d002@mail.gmail.com> Message-ID: <200604012230.17192.fdrake@acm.org> On Saturday 01 April 2006 21:46, George Yoshida wrote: > With this fix, at least the compilation should succeed, although I > haven't checked every piece of the update in detail. Committed, thanks! -Fred -- Fred L. Drake, Jr. From python-checkins at python.org Sun Apr 2 09:59:56 2006 From: python-checkins at python.org (fredrik.lundh) Date: Sun, 2 Apr 2006 09:59:56 +0200 (CEST) Subject: [Python-checkins] r43550 - python/trunk/Doc/tut/tut.tex Message-ID: <20060402075956.7D06A1E4005@bag.python.org> Author: fredrik.lundh Date: Sun Apr 2 09:59:55 2006 New Revision: 43550 Modified: python/trunk/Doc/tut/tut.tex Log: end the sentence... Modified: python/trunk/Doc/tut/tut.tex ============================================================================== --- python/trunk/Doc/tut/tut.tex (original) +++ python/trunk/Doc/tut/tut.tex Sun Apr 2 09:59:55 2006 @@ -5347,7 +5347,7 @@ \item \citetitle[../ref/ref.html]{Language Reference}: A detailed explanation of Python's syntax and semantics. It's heavy reading, -but is useful as a +but is useful as a complete guide to the language itself. \end{itemize} From python-checkins at python.org Sun Apr 2 12:48:08 2006 From: python-checkins at python.org (armin.rigo) Date: Sun, 2 Apr 2006 12:48:08 +0200 (CEST) Subject: [Python-checkins] r43551 - python/branches/release24-maint/Lib/inspect.py Message-ID: <20060402104808.643181E4005@bag.python.org> Author: armin.rigo Date: Sun Apr 2 12:48:07 2006 New Revision: 43551 Modified: python/branches/release24-maint/Lib/inspect.py Log: Backport of r39612: some more fixes for inspect.getsource(). Tests not included (the format changes and I can't be bothered to port them). Modified: python/branches/release24-maint/Lib/inspect.py ============================================================================== --- python/branches/release24-maint/Lib/inspect.py (original) +++ python/branches/release24-maint/Lib/inspect.py Sun Apr 2 12:48:07 2006 @@ -484,19 +484,6 @@ comments[-1:] = [] return string.join(comments, '') -class ListReader: - """Provide a readline() method to return lines from a list of strings.""" - def __init__(self, lines): - self.lines = lines - self.index = 0 - - def readline(self): - i = self.index - if i < len(self.lines): - self.index = i + 1 - return self.lines[i] - else: return '' - class EndOfBlock(Exception): pass class BlockFinder: @@ -506,40 +493,46 @@ self.islambda = False self.started = False self.passline = False - self.last = 0 + self.last = 1 def tokeneater(self, type, token, (srow, scol), (erow, ecol), line): if not self.started: + # look for the first "def", "class" or "lambda" if token in ("def", "class", "lambda"): if token == "lambda": self.islambda = True self.started = True - self.passline = True + self.passline = True # skip to the end of the line elif type == tokenize.NEWLINE: - self.passline = False + self.passline = False # stop skipping when a NEWLINE is seen self.last = srow + if self.islambda: # lambdas always end at the first NEWLINE + raise EndOfBlock elif self.passline: pass - elif self.islambda: - raise EndOfBlock, self.last elif type == tokenize.INDENT: self.indent = self.indent + 1 self.passline = True elif type == tokenize.DEDENT: self.indent = self.indent - 1 - if self.indent == 0: - raise EndOfBlock, self.last - elif type == tokenize.NAME and scol == 0: - raise EndOfBlock, self.last + # the end of matching indent/dedent pairs end a block + # (note that this only works for "def"/"class" blocks, + # not e.g. for "if: else:" or "try: finally:" blocks) + if self.indent <= 0: + raise EndOfBlock + elif self.indent == 0 and type not in (tokenize.COMMENT, tokenize.NL): + # any other token on the same indentation level end the previous + # block as well, except the pseudo-tokens COMMENT and NL. + raise EndOfBlock def getblock(lines): """Extract the block of code at the top of the given list of lines.""" + blockfinder = BlockFinder() try: - tokenize.tokenize(ListReader(lines).readline, BlockFinder().tokeneater) - except EndOfBlock, eob: - return lines[:eob.args[0]] - # Fooling the indent/dedent logic implies a one-line definition - return lines[:1] + tokenize.tokenize(iter(lines).next, blockfinder.tokeneater) + except (EndOfBlock, IndentationError): + pass + return lines[:blockfinder.last] def getsourcelines(object): """Return a list of source lines and starting line number for an object. From python-checkins at python.org Sun Apr 2 22:37:17 2006 From: python-checkins at python.org (georg.brandl) Date: Sun, 2 Apr 2006 22:37:17 +0200 (CEST) Subject: [Python-checkins] r43552 - python/trunk/Lib/urllib2.py Message-ID: <20060402203717.8CE381E4004@bag.python.org> Author: georg.brandl Date: Sun Apr 2 22:37:17 2006 New Revision: 43552 Modified: python/trunk/Lib/urllib2.py Log: bug #1462706: guard against host not having FQDN hostname Modified: python/trunk/Lib/urllib2.py ============================================================================== --- python/trunk/Lib/urllib2.py (original) +++ python/trunk/Lib/urllib2.py Sun Apr 2 22:37:17 2006 @@ -1130,8 +1130,11 @@ names = None def get_names(self): if FileHandler.names is None: - FileHandler.names = (socket.gethostbyname('localhost'), - socket.gethostbyname(socket.gethostname())) + try: + FileHandler.names = (socket.gethostbyname('localhost'), + socket.gethostbyname(socket.gethostname())) + except socket.gaierror: + FileHandler.names = (socket.gethostbyname('localhost'),) return FileHandler.names # not entirely sure what the rules are here From python-checkins at python.org Sun Apr 2 22:45:35 2006 From: python-checkins at python.org (georg.brandl) Date: Sun, 2 Apr 2006 22:45:35 +0200 (CEST) Subject: [Python-checkins] r43553 - in python/trunk/Lib: test/test_urllib2.py urllib2.py Message-ID: <20060402204535.840571E401A@bag.python.org> Author: georg.brandl Date: Sun Apr 2 22:45:34 2006 New Revision: 43553 Modified: python/trunk/Lib/test/test_urllib2.py python/trunk/Lib/urllib2.py Log: Patch #1462790: fix urllib2 ProxyHandler for host:port proxies Modified: python/trunk/Lib/test/test_urllib2.py ============================================================================== --- python/trunk/Lib/test/test_urllib2.py (original) +++ python/trunk/Lib/test/test_urllib2.py Sun Apr 2 22:45:34 2006 @@ -13,8 +13,7 @@ # parse_keqv_list, parse_http_list (I'm leaving this for Anthony Baxter # and Greg Stein, since they're doing Digest Authentication) # Authentication stuff (ditto) -# ProxyHandler, CustomProxy, CustomProxyHandler (I don't use a proxy) -# GopherHandler (haven't used gopher for a decade or so...) +# CustomProxy, CustomProxyHandler class TrivialTests(unittest.TestCase): def test_trivial(self): @@ -90,6 +89,7 @@ return self.handle(self.meth_name, self.action, *args) class MockHandler: + handler_order = 500 def __init__(self, methods): self._define_methods(methods) def _define_methods(self, methods): @@ -154,7 +154,7 @@ for meths in meth_spec: class MockHandlerSubclass(MockHandler): pass h = MockHandlerSubclass(meths) - h.handler_order = count + h.handler_order += count h.add_parent(opener) count = count + 1 handlers.append(h) @@ -642,6 +642,23 @@ o.open("http://www.example.com/") self.assert_(not hh.req.has_header("Cookie")) + def test_proxy(self): + o = OpenerDirector() + ph = urllib2.ProxyHandler(dict(http="proxy.example.com:3128")) + o.add_handler(ph) + meth_spec = [ + [("http_open", "return response")] + ] + handlers = add_ordered_mock_handlers(o, meth_spec) + + req = Request("http://acme.example.com/") + self.assertEqual(req.get_host(), "acme.example.com") + r = o.open(req) + self.assertEqual(req.get_host(), "proxy.example.com:3128") + + self.assertEqual([(handlers[0], "http_open")], + [tup[0:2] for tup in o.calls]) + class MiscTests(unittest.TestCase): @@ -827,6 +844,7 @@ def test_main(verbose=None): + test_support.run_doctest(urllib2, verbose) tests = (TrivialTests, OpenerDirectorTests, HandlerTests, Modified: python/trunk/Lib/urllib2.py ============================================================================== --- python/trunk/Lib/urllib2.py (original) +++ python/trunk/Lib/urllib2.py Sun Apr 2 22:45:34 2006 @@ -119,7 +119,8 @@ # support for FileHandler, proxies via environment variables from urllib import localhost, url2pathname, getproxies -__version__ = "2.5" +# used in User-Agent header sent +__version__ = sys.version[:3] _opener = None def urlopen(url, data=None): @@ -563,6 +564,80 @@ "lead to an infinite loop.\n" \ "The last 30x error message was:\n" + +def _parse_proxy(proxy): + """Return (scheme, user, password, host/port) given a URL or an authority. + + If a URL is supplied, it must have an authority (host:port) component. + According to RFC 3986, having an authority component means the URL must + have two slashes after the scheme: + + >>> _parse_proxy('file:/ftp.example.com/') + Traceback (most recent call last): + ValueError: proxy URL with no authority: 'file:/ftp.example.com/' + + The first three items of the returned tuple may be None. + + Examples of authority parsing: + + >>> _parse_proxy('proxy.example.com') + (None, None, None, 'proxy.example.com') + >>> _parse_proxy('proxy.example.com:3128') + (None, None, None, 'proxy.example.com:3128') + + The authority component may optionally include userinfo (assumed to be + username:password): + + >>> _parse_proxy('joe:password at proxy.example.com') + (None, 'joe', 'password', 'proxy.example.com') + >>> _parse_proxy('joe:password at proxy.example.com:3128') + (None, 'joe', 'password', 'proxy.example.com:3128') + + Same examples, but with URLs instead: + + >>> _parse_proxy('http://proxy.example.com/') + ('http', None, None, 'proxy.example.com') + >>> _parse_proxy('http://proxy.example.com:3128/') + ('http', None, None, 'proxy.example.com:3128') + >>> _parse_proxy('http://joe:password at proxy.example.com/') + ('http', 'joe', 'password', 'proxy.example.com') + >>> _parse_proxy('http://joe:password at proxy.example.com:3128') + ('http', 'joe', 'password', 'proxy.example.com:3128') + + Everything after the authority is ignored: + + >>> _parse_proxy('ftp://joe:password at proxy.example.com/rubbish:3128') + ('ftp', 'joe', 'password', 'proxy.example.com') + + Test for no trailing '/' case: + + >>> _parse_proxy('http://joe:password at proxy.example.com') + ('http', 'joe', 'password', 'proxy.example.com') + + """ + from urlparse import _splitnetloc + scheme, r_scheme = splittype(proxy) + if not r_scheme.startswith("/"): + # authority + scheme = None + authority = proxy + else: + # URL + if not r_scheme.startswith("//"): + raise ValueError("proxy URL with no authority: %r" % proxy) + # We have an authority, so for RFC 3986-compliant URLs (by ss 3. + # and 3.3.), path is empty or starts with '/' + end = r_scheme.find("/", 2) + if end == -1: + end = None + authority = r_scheme[2:end] + userinfo, hostport = splituser(authority) + if userinfo is not None: + user, password = splitpasswd(userinfo) + else: + user = password = None + return scheme, user, password, hostport + class ProxyHandler(BaseHandler): # Proxies must be in front handler_order = 100 @@ -579,30 +654,25 @@ def proxy_open(self, req, proxy, type): orig_type = req.get_type() - type, r_type = splittype(proxy) - if not type or r_type.isdigit(): - # proxy is specified without protocol - type = orig_type - host = proxy - else: - host, r_host = splithost(r_type) - user_pass, host = splituser(host) - user, password = splitpasswd(user_pass) + proxy_type, user, password, hostport = _parse_proxy(proxy) + if proxy_type is None: + proxy_type = orig_type if user and password: - user, password = user_pass.split(':', 1) - user_pass = base64.encodestring('%s:%s' % (unquote(user), - unquote(password))).strip() - req.add_header('Proxy-authorization', 'Basic ' + user_pass) - host = unquote(host) - req.set_proxy(host, type) - if orig_type == type: + user_pass = '%s:%s' % (unquote(user), unquote(password)) + creds = base64.encodestring(user_pass).strip() + req.add_header('Proxy-authorization', 'Basic ' + creds) + hostport = unquote(hostport) + req.set_proxy(hostport, proxy_type) + if orig_type == proxy_type: # let other handlers take care of it - # XXX this only makes sense if the proxy is before the - # other handlers return None else: # need to start over, because the other handlers don't # grok the proxy's URL type + # e.g. if we have a constructor arg proxies like so: + # {'http': 'ftp://proxy.example.com'}, we may end up turning + # a request for http://acme.example.com/a into one for + # ftp://proxy.example.com/a return self.parent.open(req) # feature suggested by Duncan Booth From python-checkins at python.org Sun Apr 2 22:48:12 2006 From: python-checkins at python.org (georg.brandl) Date: Sun, 2 Apr 2006 22:48:12 +0200 (CEST) Subject: [Python-checkins] r43554 - python/trunk/Lib/urllib2.py Message-ID: <20060402204812.2D1571E4004@bag.python.org> Author: georg.brandl Date: Sun Apr 2 22:48:11 2006 New Revision: 43554 Modified: python/trunk/Lib/urllib2.py Log: Patch #1463012: remove not working undocumented classes from urllib2 Modified: python/trunk/Lib/urllib2.py ============================================================================== --- python/trunk/Lib/urllib2.py (original) +++ python/trunk/Lib/urllib2.py Sun Apr 2 22:48:11 2006 @@ -14,7 +14,7 @@ HTTP 301, 302, 303 and 307 redirect errors, and the HTTPDigestAuthHandler deals with digest authentication. -urlopen(url, data=None) -- basic usage is that same as original +urlopen(url, data=None) -- basic usage is the same as original urllib. pass the url and optionally data to post to an HTTP URL, and get a file-like object back. One difference is that you can also pass a Request instance instead of URL. Raises a URLError (subclass of @@ -77,16 +77,13 @@ # the handler knows that the problem was, e.g., that it didn't know # that hash algo that requested in the challenge, it would be good to # pass that information along to the client, too. - -# XXX to do: -# name! -# documentation (getting there) -# complex proxies -# abstract factory for opener # ftp errors aren't handled cleanly -# gopher can return a socket.error # check digest against correct (i.e. non-apache) implementation +# Possible extensions: +# complex proxies XXX not sure what exactly was meant by this +# abstract factory for opener + import base64 import ftplib import httplib @@ -111,8 +108,7 @@ except ImportError: from StringIO import StringIO -# not sure how many of these need to be gotten rid of -from urllib import (unwrap, unquote, splittype, splithost, quote, +from urllib import (unwrap, unquote, splittype, splithost, addinfourl, splitport, splitgophertype, splitquery, splitattr, ftpwrapper, noheaders, splituser, splitpasswd, splitvalue) @@ -331,8 +327,9 @@ pass def _call_chain(self, chain, kind, meth_name, *args): - # XXX raise an exception if no one else should try to handle - # this url. return None if you can't but someone else could. + # Handlers raise an exception if no one else should try to handle + # the request, or return None if they can't but another handler + # could. Otherwise, they return the response. handlers = chain.get(kind, ()) for handler in handlers: func = getattr(handler, meth_name) @@ -675,50 +672,6 @@ # ftp://proxy.example.com/a return self.parent.open(req) -# feature suggested by Duncan Booth -# XXX custom is not a good name -class CustomProxy: - # either pass a function to the constructor or override handle - def __init__(self, proto, func=None, proxy_addr=None): - self.proto = proto - self.func = func - self.addr = proxy_addr - - def handle(self, req): - if self.func and self.func(req): - return 1 - - def get_proxy(self): - return self.addr - -class CustomProxyHandler(BaseHandler): - # Proxies must be in front - handler_order = 100 - - def __init__(self, *proxies): - self.proxies = {} - - def proxy_open(self, req): - proto = req.get_type() - try: - proxies = self.proxies[proto] - except KeyError: - return None - for p in proxies: - if p.handle(req): - req.set_proxy(p.get_proxy()) - return self.parent.open(req) - return None - - def do_proxy(self, p, req): - return self.parent.open(req) - - def add_proxy(self, cpo): - if cpo.proto in self.proxies: - self.proxies[cpo.proto].append(cpo) - else: - self.proxies[cpo.proto] = [cpo] - class HTTPPasswordMgr: def __init__(self): self.passwd = {} @@ -1333,6 +1286,7 @@ class GopherHandler(BaseHandler): def gopher_open(self, req): + # XXX can raise socket.error import gopherlib # this raises DeprecationWarning in 2.5 host = req.get_host() if not host: @@ -1348,25 +1302,3 @@ else: fp = gopherlib.send_selector(selector, host) return addinfourl(fp, noheaders(), req.get_full_url()) - -#bleck! don't use this yet -class OpenerFactory: - - default_handlers = [UnknownHandler, HTTPHandler, - HTTPDefaultErrorHandler, HTTPRedirectHandler, - FTPHandler, FileHandler] - handlers = [] - replacement_handlers = [] - - def add_handler(self, h): - self.handlers = self.handlers + [h] - - def replace_handler(self, h): - pass - - def build_opener(self): - opener = OpenerDirector() - for ph in self.default_handlers: - if inspect.isclass(ph): - ph = ph() - opener.add_handler(ph) From nnorwitz at gmail.com Sun Apr 2 22:54:41 2006 From: nnorwitz at gmail.com (Neal Norwitz) Date: Sun, 2 Apr 2006 12:54:41 -0800 Subject: [Python-checkins] r43554 - python/trunk/Lib/urllib2.py In-Reply-To: <20060402204812.2D1571E4004@bag.python.org> References: <20060402204812.2D1571E4004@bag.python.org> Message-ID: On 4/2/06, georg.brandl wrote: > Author: georg.brandl > Date: Sun Apr 2 22:48:11 2006 > New Revision: 43554 > > Modified: > python/trunk/Lib/urllib2.py > Log: > Patch #1463012: remove not working undocumented classes from urllib2 > > > > Modified: python/trunk/Lib/urllib2.py > ============================================================================== > --- python/trunk/Lib/urllib2.py (original) > +++ python/trunk/Lib/urllib2.py Sun Apr 2 22:48:11 2006 > @@ -111,8 +108,7 @@ > except ImportError: > from StringIO import StringIO > > -# not sure how many of these need to be gotten rid of > -from urllib import (unwrap, unquote, splittype, splithost, quote, > +from urllib import (unwrap, unquote, splittype, splithost, I'm curious why quote was removed? ISTM that people might be relying on urllib2.quote, even if by mistake (ie, they meant urllib.quote). Although, Google didn't seem to find anything when I searched for urllib2.quote. n From python-checkins at python.org Sun Apr 2 23:09:52 2006 From: python-checkins at python.org (georg.brandl) Date: Sun, 2 Apr 2006 23:09:52 +0200 (CEST) Subject: [Python-checkins] r43555 - python/trunk/Lib/lib-tk/Tkinter.py Message-ID: <20060402210952.39C461E4004@bag.python.org> Author: georg.brandl Date: Sun Apr 2 23:09:51 2006 New Revision: 43555 Modified: python/trunk/Lib/lib-tk/Tkinter.py Log: Remove "disgusting hack" in favour of closure (patch #1462235) Modified: python/trunk/Lib/lib-tk/Tkinter.py ============================================================================== --- python/trunk/Lib/lib-tk/Tkinter.py (original) +++ python/trunk/Lib/lib-tk/Tkinter.py Sun Apr 2 23:09:51 2006 @@ -449,18 +449,15 @@ # I'd rather use time.sleep(ms*0.001) self.tk.call('after', ms) else: - # XXX Disgusting hack to clean up after calling func - tmp = [] - def callit(func=func, args=args, self=self, tmp=tmp): + def callit(): try: func(*args) finally: try: - self.deletecommand(tmp[0]) + self.deletecommand(name) except TclError: pass name = self._register(callit) - tmp.append(name) return self.tk.call('after', ms, name) def after_idle(self, func, *args): """Call FUNC once if the Tcl main loop has no event to From python-checkins at python.org Sun Apr 2 23:13:13 2006 From: python-checkins at python.org (georg.brandl) Date: Sun, 2 Apr 2006 23:13:13 +0200 (CEST) Subject: [Python-checkins] r43556 - python/trunk/Lib/urllib2.py Message-ID: <20060402211313.7E94B1E4004@bag.python.org> Author: georg.brandl Date: Sun Apr 2 23:13:13 2006 New Revision: 43556 Modified: python/trunk/Lib/urllib2.py Log: Readd urllib.quote import as it doesn't cause any harm. Modified: python/trunk/Lib/urllib2.py ============================================================================== --- python/trunk/Lib/urllib2.py (original) +++ python/trunk/Lib/urllib2.py Sun Apr 2 23:13:13 2006 @@ -108,7 +108,7 @@ except ImportError: from StringIO import StringIO -from urllib import (unwrap, unquote, splittype, splithost, +from urllib import (unwrap, unquote, splittype, splithost, quote, addinfourl, splitport, splitgophertype, splitquery, splitattr, ftpwrapper, noheaders, splituser, splitpasswd, splitvalue) From g.brandl at gmx.net Sun Apr 2 23:13:23 2006 From: g.brandl at gmx.net (Georg Brandl) Date: Sun, 02 Apr 2006 23:13:23 +0200 Subject: [Python-checkins] r43554 - python/trunk/Lib/urllib2.py In-Reply-To: References: <20060402204812.2D1571E4004@bag.python.org> Message-ID: Neal Norwitz wrote: > On 4/2/06, georg.brandl wrote: >> Author: georg.brandl >> Date: Sun Apr 2 22:48:11 2006 >> New Revision: 43554 >> >> Modified: >> python/trunk/Lib/urllib2.py >> Log: >> Patch #1463012: remove not working undocumented classes from urllib2 >> >> >> >> Modified: python/trunk/Lib/urllib2.py >> ============================================================================== >> --- python/trunk/Lib/urllib2.py (original) >> +++ python/trunk/Lib/urllib2.py Sun Apr 2 22:48:11 2006 >> @@ -111,8 +108,7 @@ >> except ImportError: >> from StringIO import StringIO >> >> -# not sure how many of these need to be gotten rid of >> -from urllib import (unwrap, unquote, splittype, splithost, quote, >> +from urllib import (unwrap, unquote, splittype, splithost, > > I'm curious why quote was removed? ISTM that people might be relying > on urllib2.quote, even if by mistake (ie, they meant urllib.quote). > Although, Google didn't seem to find anything when I searched for > urllib2.quote. Granted, that would be gratuitous breakage. Readded in rev. 43556. Georg From python-checkins at python.org Sun Apr 2 23:18:27 2006 From: python-checkins at python.org (georg.brandl) Date: Sun, 2 Apr 2006 23:18:27 +0200 (CEST) Subject: [Python-checkins] r43557 - python/trunk/Doc/lib/libos.tex Message-ID: <20060402211827.7F8591E4004@bag.python.org> Author: georg.brandl Date: Sun Apr 2 23:18:27 2006 New Revision: 43557 Modified: python/trunk/Doc/lib/libos.tex Log: Document recent fdopen() change. Modified: python/trunk/Doc/lib/libos.tex ============================================================================== --- python/trunk/Doc/lib/libos.tex (original) +++ python/trunk/Doc/lib/libos.tex Sun Apr 2 23:18:27 2006 @@ -343,6 +343,10 @@ \versionchanged[When specified, the \var{mode} argument must now start with one of the letters \character{r}, \character{w}, or \character{a}, otherwise a \exception{ValueError} is raised]{2.3} +\versionchanged[On \UNIX, when the \var{mode} argument starts with + \character{a}, the \var{O_APPEND} flag is set on the file descriptor + (which the \cfunction{fdopen()} implementation already does on most + platforms)]{2.5} \end{funcdesc} \begin{funcdesc}{popen}{command\optional{, mode\optional{, bufsize}}} @@ -1732,7 +1736,7 @@ return suitable process handles. \end{funcdesc} -\begin{funcdesc}{wait3}{\{optional{options}} +\begin{funcdesc}{wait3}{\optional{options}} Similar to \function{waitpid()}, except no process id argument is given and a 3-element tuple containing the child's process id, exit status indication, and resource usage information is returned. Refer to From buildbot at python.org Sun Apr 2 23:35:36 2006 From: buildbot at python.org (buildbot at python.org) Date: Sun, 02 Apr 2006 21:35:36 +0000 Subject: [Python-checkins] buildbot failure in x86 XP-2 trunk Message-ID: <20060402213537.16C551E4004@bag.python.org> The Buildbot has detected a new failure of x86 XP-2 trunk. Full details are available at: http://www.python.org/dev/buildbot/all/x86%20XP-2%20trunk/builds/169 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: georg.brandl BUILD FAILED: failed failed slave lost sincerely, -The Buildbot From python-checkins at python.org Mon Apr 3 00:11:10 2006 From: python-checkins at python.org (walter.doerwald) Date: Mon, 3 Apr 2006 00:11:10 +0200 (CEST) Subject: [Python-checkins] r43558 - python/trunk/Lib/calendar.py Message-ID: <20060402221110.522991E4004@bag.python.org> Author: walter.doerwald Date: Mon Apr 3 00:11:10 2006 New Revision: 43558 Modified: python/trunk/Lib/calendar.py Log: Always use firstweekday module 7. Modified: python/trunk/Lib/calendar.py ============================================================================== --- python/trunk/Lib/calendar.py (original) +++ python/trunk/Lib/calendar.py Mon Apr 3 00:11:10 2006 @@ -152,7 +152,7 @@ while True: yield date date += oneday - if date.month != month and date.weekday() == self.firstweekday: + if date.month != month and date.weekday() == self.firstweekday%7: break def itermonthdays2(self, year, month): From neal at metaslash.com Mon Apr 3 03:09:47 2006 From: neal at metaslash.com (Neal Norwitz) Date: Sun, 2 Apr 2006 21:09:47 -0400 Subject: [Python-checkins] Python Regression Test Failures all (1) Message-ID: <20060403010947.GA9396@python.psfb.org> test_grammar test_opcodes test_operations test_builtin test_exceptions test_types test_MimeWriter test_StringIO test___all__ test___future__ test__locale test_aepack test_aepack skipped -- No module named aepack test_al test_al skipped -- No module named al test_anydbm test_applesingle test_applesingle skipped -- No module named macostools test_array test_ast test_asynchat test_atexit test_audioop test_augassign test_base64 test_bastion test_binascii test_binhex test_binop test_bisect test_bool test_bsddb test_bsddb185 test_bsddb185 skipped -- No module named bsddb185 test_bsddb3 test_unicode_file test_unicode_file skipped -- No Unicode filesystem semantics on this platform. test_unicodedata test_unittest test_univnewlines test_unpack test_urllib test_urllib2 test_urllib2net test_urllibnet test_urlparse test_userdict test_userlist test_userstring test_uu test_wait3 test_wait4 test_warnings test_wave test_weakref test_whichdb test_winreg test_winreg skipped -- No module named _winreg test_winsound test_winsound skipped -- No module named winsound test_with test_xdrlib test_xml_etree test_xml_etree_c test_xmllib test_xmlrpc test_xpickle test_xrange test_zipfile test_zipimport test_zlib 288 tests OK. 1 test failed: test_bsddb3 20 tests skipped: test_aepack test_al test_applesingle test_bsddb185 test_cd test_cl test_gl test_imgfile test_ioctl test_macfs test_macostools test_nis test_pep277 test_plistlib test_scriptpackages test_sunaudiodev test_tcl test_unicode_file test_winreg test_winsound 1 skip unexpected on linux2: test_ioctl [424302 refs] From neal at metaslash.com Mon Apr 3 03:21:49 2006 From: neal at metaslash.com (Neal Norwitz) Date: Sun, 2 Apr 2006 21:21:49 -0400 Subject: [Python-checkins] Python Regression Test Failures all (1) Message-ID: <20060403012149.GA11058@python.psfb.org> test_grammar test_opcodes test_operations test_builtin test_exceptions test_types test_MimeWriter test_StringIO test___all__ test___future__ test__locale test_aepack test_aepack skipped -- No module named aepack test_al test_al skipped -- No module named al test_anydbm test_applesingle test_applesingle skipped -- No module named macostools test_array test_ast test_asynchat test_atexit test_audioop test_augassign test_base64 test_bastion test_binascii test_binhex test_binop test_bisect test_bool test_bsddb test_bsddb185 test_bsddb185 skipped -- No module named bsddb185 test_bsddb3 test_bufio test_bz2 test_cProfile test_calendar test_call test_capi test_cd test_cd skipped -- No module named cd test_cfgparser test_cgi test_charmapcodec test_cl test_cl skipped -- No module named cl test_class test_cmath test_cmd_line test_code test_codeccallbacks test_codecencodings_cn test_codecencodings_hk test_codecencodings_jp test_codecencodings_kr test_codecencodings_tw test_codecmaps_cn test_codecmaps_hk test_codecmaps_jp test_codecmaps_kr test_codecmaps_tw test_codecs test_codeop test_coding test_coercion test_colorsys test_commands test_compare test_compile test_compiler test_complex test_contains test_contextlib test_cookie test_cookielib test_copy test_copy_reg test_cpickle test_crypt test_csv test_ctypes test_datetime test_dbm test_decimal test_decorators test_defaultdict test_deque test_descr test_descrtut test_dict test_difflib test_dircache test_dis test_distutils test_dl test_doctest test_doctest2 test_dumbdbm test_dummy_thread test_dummy_threading test_email test_email_codecs test_email_renamed test_enumerate test_eof test_errno test_exception_variations test_extcall test_fcntl test_file test_filecmp test_fileinput test_float test_fnmatch test_fork1 test_format test_fpformat test_frozen test_funcattrs test_functional test_future test_gc test_gdbm test_generators test_genexps test_getargs test_getargs2 test_getopt test_gettext test_gl test_gl skipped -- No module named gl test_glob test_global test_grp test_gzip test_hash test_hashlib test_heapq test_hexoct test_hmac test_hotshot test_htmllib test_htmlparser test_httplib test_imageop test_imaplib test_imgfile test_imgfile skipped -- No module named imgfile test_imp test_import test_importhooks test_index test_inspect test_ioctl test_ioctl skipped -- Unable to open /dev/tty test_isinstance test_iter test_iterlen test_itertools test_largefile test_list test_locale test_logging test_long test_long_future test_longexp test_macfs test_macfs skipped -- No module named macfs test_macostools test_macostools skipped -- No module named macostools test_macpath test_mailbox test_marshal test_math test_md5 test_mhlib test_mimetools test_mimetypes test_minidom test_mmap test_module test_multibytecodec test_multibytecodec_support test_multifile test_mutants test_netrc test_new test_nis test_nis skipped -- Local domain name not set test_normalization test_ntpath test_openpty test_operator test_optparse test_os test_parser test_peepholer test_pep247 test_pep263 test_pep277 test_pep277 skipped -- test works only on NT+ test_pep292 test_pep352 test_pickle test_pickletools test_pkg test_pkgimport test_platform test_plistlib test_plistlib skipped -- No module named plistlib test_poll test_popen [9898 refs] [9898 refs] [9898 refs] test_popen2 test_posix test_posixpath test_pow test_pprint test_profile test_profilehooks test_pty test_pwd test_pyclbr test_pyexpat test_queue test_quopri [10984 refs] [10984 refs] test_random test_re test_repr test_resource test_rfc822 test_rgbimg test_richcmp test_robotparser test_runpy test_sax test_scope test_scriptpackages test_scriptpackages skipped -- No module named aetools test_select test_set test_sets test_sgmllib test_sha test_shelve test_shlex test_shutil test_signal test_site test_slice test_socket test_socket_ssl test_socketserver test_softspace test_sort test_sqlite test_str test_strftime test_string test_stringprep test_strop test_strptime test_struct test_structseq test_subprocess [9893 refs] [9895 refs] [9893 refs] [9893 refs] [9893 refs] [9893 refs] [9893 refs] [9894 refs] [9894 refs] [9893 refs] [9894 refs] [9893 refs] [10110 refs] [9894 refs] [9894 refs] [9894 refs] [9894 refs] [9894 refs] [9894 refs] [9894 refs] this bit of output is from a test of stdout in a different process ... [9894 refs] [9893 refs] [10110 refs] test_sunaudiodev test_sunaudiodev skipped -- No module named sunaudiodev test_sundry test_symtable test_syntax test_sys [9893 refs] [9893 refs] test_tarfile test_tcl test_tcl skipped -- No module named _tkinter test_tempfile [9895 refs] test_textwrap test_thread test_threaded_import test_threadedtempfile test_threading test_threading_local test_threadsignals test_time test_timeout test_tokenize test_trace test_traceback test_transformer test_tuple test_ucn test_unary test_unicode test_unicode_file test_unicode_file skipped -- No Unicode filesystem semantics on this platform. test_unicodedata test_unittest test_univnewlines test_unpack test_urllib test_urllib2 test_urllib2net test_urllibnet test_urlparse test_userdict test_userlist test_userstring test_uu test_wait3 test_wait4 test_warnings test_wave test_weakref test_whichdb test_winreg test_winreg skipped -- No module named _winreg test_winsound test_winsound skipped -- No module named winsound test_with test_xdrlib test_xml_etree test_xml_etree_c test_xmllib test_xmlrpc test_xpickle test_xrange test_zipfile test_zipimport test_zlib 289 tests OK. 20 tests skipped: test_aepack test_al test_applesingle test_bsddb185 test_cd test_cl test_gl test_imgfile test_ioctl test_macfs test_macostools test_nis test_pep277 test_plistlib test_scriptpackages test_sunaudiodev test_tcl test_unicode_file test_winreg test_winsound 1 skip unexpected on linux2: test_ioctl [424300 refs] _uu test_wait3 test_wait4 test_warnings test_wave test_weakref test_whichdb test_winreg test_winreg skipped -- No module named _winreg test_winsound test_winsound skipped -- No module named winsound test_with test_xdrlib test_xml_etree test_xml_etree_c test_xmllib test_xmlrpc test_xpickle test_xrange test_zipfile test_zipimport test_zlib 288 tests OK. 1 test failed: test_bsddb3 20 tests skipped: test_aepack test_al test_applesingle test_bsddb185 test_cd test_cl test_gl test_imgfile test_ioctl test_macfs test_macostools test_nis test_pep277 test_plistlib test_scriptpackages test_sunaudiodev test_tcl test_unicode_file test_winreg test_winsound 1 skip unexpected on linux2: test_ioctl [424302 refs] From anthony at interlink.com.au Mon Apr 3 03:47:25 2006 From: anthony at interlink.com.au (Anthony Baxter) Date: Mon, 3 Apr 2006 12:47:25 +1100 Subject: [Python-checkins] TRUNK FREEZE. 2.5a1, 00:00 UTC, Wednesday 5th of April. Message-ID: <200604031147.27455.anthony@interlink.com.au> Now that the bug day has been and gone, it's time to cut 2.5a1. Please consider the trunk FROZEN from 00:00 UTC/GMT on Wednesday the 5th of April. I'll post again when it's unfrozen. Please help in not making the release manager cry because the trunk is broken. Thanks, Anthony From python-checkins at python.org Mon Apr 3 04:20:50 2006 From: python-checkins at python.org (anthony.baxter) Date: Mon, 3 Apr 2006 04:20:50 +0200 (CEST) Subject: [Python-checkins] r43559 - python/trunk/setup.py Message-ID: <20060403022050.06B0E1E4004@bag.python.org> Author: anthony.baxter Date: Mon Apr 3 04:20:49 2006 New Revision: 43559 Modified: python/trunk/setup.py Log: cleaned up setup.py code for sqlite3, based on patch from Gerhard Haering. Modified: python/trunk/setup.py ============================================================================== --- python/trunk/setup.py (original) +++ python/trunk/setup.py Mon Apr 3 04:20:49 2006 @@ -692,50 +692,42 @@ # The sqlite interface sqlite_setup_debug = True # verbose debug prints from this script? - # We hunt for "#define SQLITE_VERSION_NUMBER nnnnn" - # We need to find a version >= 3002002 (> sqlite version 3.2.2) + # We hunt for #define SQLITE_VERSION "n.n.n" + # We need to find >= sqlite version 3.0.8 sqlite_incdir = sqlite_libdir = None - sqlite_inc_paths = [ '/usr/include', + sqlite_inc_paths = [ '/usr/include', '/usr/include/sqlite', '/usr/include/sqlite3', '/usr/local/include', '/usr/local/include/sqlite', '/usr/local/include/sqlite3', ] - MIN_SQLITE_VERSION_NUMBER = 3000008 - MIN_SQLITE_VERSION = "3.0.8" + MIN_SQLITE_VERSION_NUMBER = (3, 0, 8) + MIN_SQLITE_VERSION = ".".join([str(x) + for x in MIN_SQLITE_VERSION_NUMBER]) for d in sqlite_inc_paths + inc_dirs: f = os.path.join(d, "sqlite3.h") if os.path.exists(f): if sqlite_setup_debug: print "sqlite: found %s"%f - f = open(f).read() - m = re.search(r"#define\WSQLITE_VERSION_NUMBER\W(\d+)", f) + incf = open(f).read() + m = re.search( + r'\s*.*#\s*.*define\s.*SQLITE_VERSION\W*"(.*)"', incf) if m: - sqlite_version = int(m.group(1)) - if sqlite_version >= MIN_SQLITE_VERSION_NUMBER: + sqlite_version = m.group(1) + sqlite_version_tuple = tuple([int(x) + for x in sqlite_version.split(".")]) + if sqlite_version_tuple >= MIN_SQLITE_VERSION_NUMBER: # we win! print "%s/sqlite3.h: version %s"%(d, sqlite_version) sqlite_incdir = d break - elif sqlite_version == 3000000: - # Bug in a common version out there where - # SQLITE_VERSION_NUMBER was set incorrectly - if sqlite_setup_debug: - print "found buggy SQLITE_VERSION_NUMBER, checking" - m = re.search(r'#define\WSQLITE_VERSION\W+"([\.\d]+)"', - f) - if m: - sqlite_version = m.group(1) - if sqlite_version >= MIN_SQLITE_VERSION: - print "%s/sqlite3.h: version %s"%(d, - sqlite_version) - sqlite_incdir = d - break else: - if sqlite_setup_debug: + if sqlite_setup_debug: print "%s: version %d is too old, need >= %s"%(d, sqlite_version, MIN_SQLITE_VERSION) - + elif sqlite_setup_debug: + print "sqlite: %s had no SQLITE_VERSION"%(f,) + if sqlite_incdir: sqlite_dirs_to_check = [ os.path.join(sqlite_incdir, '..', 'lib64'), @@ -763,19 +755,19 @@ PYSQLITE_VERSION = "2.1.3" sqlite_defines = [] if sys.platform != "win32": - sqlite_defines.append(('PYSQLITE_VERSION', + sqlite_defines.append(('PYSQLITE_VERSION', '"%s"' % PYSQLITE_VERSION)) else: - sqlite_defines.append(('PYSQLITE_VERSION', + sqlite_defines.append(('PYSQLITE_VERSION', '\\"'+PYSQLITE_VERSION+'\\"')) - sqlite_defines.append(('PY_MAJOR_VERSION', + sqlite_defines.append(('PY_MAJOR_VERSION', str(sys.version_info[0]))) - sqlite_defines.append(('PY_MINOR_VERSION', + sqlite_defines.append(('PY_MINOR_VERSION', str(sys.version_info[1]))) exts.append(Extension('_sqlite3', sqlite_srcs, define_macros=sqlite_defines, - include_dirs=["Modules/_sqlite", + include_dirs=["Modules/_sqlite", sqlite_incdir], library_dirs=sqlite_libdir, runtime_library_dirs=sqlite_libdir, From neal at metaslash.com Mon Apr 3 04:19:16 2006 From: neal at metaslash.com (Neal Norwitz) Date: Sun, 2 Apr 2006 22:19:16 -0400 Subject: [Python-checkins] Python Regression Test Failures all (1) Message-ID: <20060403021916.GA11331@python.psfb.org> test_grammar test_opcodes test_operations test_builtin test_exceptions test_types test_MimeWriter test_StringIO test___all__ test___future__ test__locale test_aepack test_aepack skipped -- No module named aepack test_al test_al skipped -- No module named al test_anydbm test_applesingle test_applesingle skipped -- No module named macostools test_array test_ast test_asynchat test_atexit test_audioop test_augassign test_base64 test_bastion test_binascii test_binhex test_binop test_bisect test_bool test_bsddb test_bsddb185 test_bsddb185 skipped -- No module named bsddb185 test_bsddb3 test_bufio test_bz2 test_cProfile test_calendar test_call test_capi test_cd test_cd skipped -- No module named cd test_cfgparser test_cgi test_charmapcodec test_cl test_cl skipped -- No module named cl test_class test_cmath test_cmd_line test_code test_codeccallbacks test_codecencodings_cn test_codecencodings_hk test_codecencodings_jp test_codecencodings_kr test_codecencodings_tw test_codecmaps_cn test_codecmaps_hk test_codecmaps_jp test_codecmaps_kr test_codecmaps_tw test_codecs test_codeop test_coding test_coercion test_colorsys test_commands test_compare test_compile test_compiler test_complex test_contains test_contextlib test_cookie test_cookielib test_copy test_copy_reg test_cpickle test_crypt test_csv test_ctypes test_datetime test_dbm test_decimal test_decorators test_defaultdict test_deque test_descr test_descrtut test_dict test_difflib test_dircache test_dis test_distutils test_dl test_doctest test_doctest2 test_dumbdbm test_dummy_thread test_dummy_threading test_email test_email_codecs test_email_renamed test_enumerate test_eof test_errno test_exception_variations test_extcall test_fcntl test_file test_filecmp test_fileinput test_float test_fnmatch test_fork1 test_format test_fpformat test_frozen test_funcattrs test_functional test_future test_gc test_gdbm test_generators test_genexps test_getargs test_getargs2 test_getopt test_gettext test_gl test_gl skipped -- No module named gl test_glob test_global test_grp test_gzip test_hash test_hashlib test_heapq test_hexoct test_hmac test_hotshot test_htmllib test_htmlparser test_httplib test_imageop test_imaplib test_imgfile test_imgfile skipped -- No module named imgfile test_imp test_import test_importhooks test_index test_inspect test_ioctl test_ioctl skipped -- Unable to open /dev/tty test_isinstance test_iter test_iterlen test_itertools test_largefile test_list test_locale test_logging test_long test_long_future test_longexp test_macfs test_macfs skipped -- No module named macfs test_macostools test_macostools skipped -- No module named macostools test_macpath test_mailbox test_marshal test_math test_md5 test_mhlib test_mimetools test_mimetypes test_minidom test_mmap test_module test_multibytecodec test_multibytecodec_support test_multifile test_mutants test_netrc test_new test_nis test_nis skipped -- Local domain name not set test_normalization test_ntpath test_openpty test_operator test_optparse test_os test_parser test_peepholer test_pep247 test_pep263 test_pep277 test_pep277 skipped -- test works only on NT+ test_pep292 test_pep352 test_pickle test_pickletools test_pkg test_pkgimport test_platform test_plistlib test_plistlib skipped -- No module named plistlib test_poll test_popen [9898 refs] [9898 refs] [9898 refs] test_popen2 test_posix test_posixpath test_pow test_pprint test_profile test_profilehooks test_pty test_pwd test_pyclbr test_pyexpat test_queue test_quopri [10984 refs] [10984 refs] test_random test_re test_repr test_resource test_rfc822 test_rgbimg test_richcmp test_robotparser test_runpy test_sax test_scope test_scriptpackages test_scriptpackages skipped -- No module named aetools test_select test_set test_sets test_sgmllib test_sha test_shelve test_shlex test_shutil test_signal test_site test_slice test_socket test_socket_ssl test_socketserver test_softspace test_sort test_sqlite test_str test_strftime test_string test_stringprep test_strop test_strptime test_struct test_structseq test_subprocess [9893 refs] [9895 refs] [9893 refs] [9893 refs] [9893 refs] [9893 refs] [9893 refs] [9894 refs] [9894 refs] [9893 refs] [9894 refs] [9893 refs] [10110 refs] [9894 refs] [9894 refs] [9894 refs] [9894 refs] [9894 refs] [9894 refs] [9894 refs] this bit of output is from a test of stdout in a different process ... [9894 refs] [9893 refs] [10110 refs] test_sunaudiodev test_sunaudiodev skipped -- No module named sunaudiodev test_sundry test_symtable test_syntax test_sys [9893 refs] [9893 refs] test_tarfile test_tcl test_tcl skipped -- No module named _tkinter test_tempfile [9895 refs] test_textwrap test_thread test_threaded_import test_threadedtempfile test_threading test_threading_local test_threadsignals test_time test_timeout test_tokenize test_trace test_traceback test_transformer test_tuple test_ucn test_unary test_unicode test_unicode_file test_unicode_file skipped -- No Unicode filesystem semantics on this platform. test_unicodedata test_unittest test_univnewlines test_unpack test_urllib test_urllib2 test_urllib2net test_urllibnet test_urlparse test_userdict test_userlist test_userstring test_uu test_wait3 test_wait4 test_warnings test_wave test_weakref test_whichdb test_winreg test_winreg skipped -- No module named _winreg test_winsound test_winsound skipped -- No module named winsound test_with test_xdrlib test_xml_etree test_xml_etree_c test_xmllib test_xmlrpc test_xpickle test_xrange test_zipfile test_zipimport test_zlib 289 tests OK. 20 tests skipped: test_aepack test_al test_applesingle test_bsddb185 test_cd test_cl test_gl test_imgfile test_ioctl test_macfs test_macostools test_nis test_pep277 test_plistlib test_scriptpackages test_sunaudiodev test_tcl test_unicode_file test_winreg test_winsound 1 skip unexpected on linux2: test_ioctl [424300 refs] _uu test_wait3 test_wait4 test_warnings test_wave test_weakref test_whichdb test_winreg test_winreg skipped -- No module named _winreg test_winsound test_winsound skipped -- No module named winsound test_with test_xdrlib test_xml_etree test_xml_etree_c test_xmllib test_xmlrpc test_xpickle test_xrange test_zipfile test_zipimport test_zlib 288 tests OK. 1 test failed: test_bsddb3 20 tests skipped: test_aepack test_al test_applesingle test_bsddb185 test_cd test_cl test_gl test_imgfile test_ioctl test_macfs test_macostools test_nis test_pep277 test_plistlib test_scriptpackages test_sunaudiodev test_tcl test_unicode_file test_winreg test_winsound 1 skip unexpected on linux2: test_ioctl [424302 refs] From python-checkins at python.org Mon Apr 3 04:46:45 2006 From: python-checkins at python.org (tim.peters) Date: Mon, 3 Apr 2006 04:46:45 +0200 (CEST) Subject: [Python-checkins] r43560 - in python/trunk/Lib: sgmllib.py test/test_traceback.py test/test_urllib2.py Message-ID: <20060403024645.036D01E4004@bag.python.org> Author: tim.peters Date: Mon Apr 3 04:46:44 2006 New Revision: 43560 Modified: python/trunk/Lib/sgmllib.py python/trunk/Lib/test/test_traceback.py python/trunk/Lib/test/test_urllib2.py Log: Whitespace normalization. Modified: python/trunk/Lib/sgmllib.py ============================================================================== --- python/trunk/Lib/sgmllib.py (original) +++ python/trunk/Lib/sgmllib.py Mon Apr 3 04:46:44 2006 @@ -270,7 +270,7 @@ if not rest: attrvalue = attrname else: - if (attrvalue[:1] == "'" == attrvalue[-1:] or + if (attrvalue[:1] == "'" == attrvalue[-1:] or attrvalue[:1] == '"' == attrvalue[-1:]): # strip quotes attrvalue = attrvalue[1:-1] Modified: python/trunk/Lib/test/test_traceback.py ============================================================================== --- python/trunk/Lib/test/test_traceback.py (original) +++ python/trunk/Lib/test/test_traceback.py Mon Apr 3 04:46:44 2006 @@ -23,7 +23,7 @@ def syntax_error_without_caret(self): # XXX why doesn't compile raise the same traceback? import test.badsyntax_nocaret - + def syntax_error_bad_indentation(self): compile("def spam():\n print 1\n print 2", "?", "exec") Modified: python/trunk/Lib/test/test_urllib2.py ============================================================================== --- python/trunk/Lib/test/test_urllib2.py (original) +++ python/trunk/Lib/test/test_urllib2.py Mon Apr 3 04:46:44 2006 @@ -355,12 +355,12 @@ "file://%s%s" % (socket.gethostbyname('localhost'), urlpath), ] try: - localaddr = socket.gethostbyname(socket.gethostname()) + localaddr = socket.gethostbyname(socket.gethostname()) except socket.gaierror: localaddr = '' if localaddr: urls.append("file://%s%s" % (localaddr, urlpath)) - + for url in urls: f = open(TESTFN, "wb") try: From buildbot at python.org Mon Apr 3 04:56:40 2006 From: buildbot at python.org (buildbot at python.org) Date: Mon, 03 Apr 2006 02:56:40 +0000 Subject: [Python-checkins] buildbot warnings in alpha Tru64 5.1 trunk Message-ID: <20060403025640.662681E4004@bag.python.org> The Buildbot has detected a new failure of alpha Tru64 5.1 trunk. Full details are available at: http://www.python.org/dev/buildbot/all/alpha%20Tru64%205.1%20trunk/builds/74 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: anthony.baxter Build Had Warnings: warnings test sincerely, -The Buildbot From python-checkins at python.org Mon Apr 3 06:45:36 2006 From: python-checkins at python.org (neal.norwitz) Date: Mon, 3 Apr 2006 06:45:36 +0200 (CEST) Subject: [Python-checkins] r43561 - in python/trunk/Lib: compiler/__init__.py compiler/ast.py compiler/transformer.py test/test_dict.py test/test_import.py test/test_sets.py test/test_urllib2.py Message-ID: <20060403044536.711181E4004@bag.python.org> Author: neal.norwitz Date: Mon Apr 3 06:45:34 2006 New Revision: 43561 Modified: python/trunk/Lib/compiler/__init__.py python/trunk/Lib/compiler/ast.py python/trunk/Lib/compiler/transformer.py python/trunk/Lib/test/test_dict.py python/trunk/Lib/test/test_import.py python/trunk/Lib/test/test_sets.py python/trunk/Lib/test/test_urllib2.py Log: Use absolute imports Modified: python/trunk/Lib/compiler/__init__.py ============================================================================== --- python/trunk/Lib/compiler/__init__.py (original) +++ python/trunk/Lib/compiler/__init__.py Mon Apr 3 06:45:34 2006 @@ -21,6 +21,6 @@ Generates a .pyc file by compiling filename. """ -from transformer import parse, parseFile -from visitor import walk -from pycodegen import compile, compileFile +from compiler.transformer import parse, parseFile +from compiler.visitor import walk +from compiler.pycodegen import compile, compileFile Modified: python/trunk/Lib/compiler/ast.py ============================================================================== --- python/trunk/Lib/compiler/ast.py (original) +++ python/trunk/Lib/compiler/ast.py Mon Apr 3 06:45:34 2006 @@ -2,7 +2,7 @@ This file is automatically generated by Tools/compiler/astgen.py """ -from consts import CO_VARARGS, CO_VARKEYWORDS +from compiler.consts import CO_VARARGS, CO_VARKEYWORDS def flatten(seq): l = [] Modified: python/trunk/Lib/compiler/transformer.py ============================================================================== --- python/trunk/Lib/compiler/transformer.py (original) +++ python/trunk/Lib/compiler/transformer.py Mon Apr 3 06:45:34 2006 @@ -34,8 +34,8 @@ class WalkerError(StandardError): pass -from consts import CO_VARARGS, CO_VARKEYWORDS -from consts import OP_ASSIGN, OP_DELETE, OP_APPLY +from compiler.consts import CO_VARARGS, CO_VARKEYWORDS +from compiler.consts import OP_ASSIGN, OP_DELETE, OP_APPLY def parseFile(path): f = open(path, "U") Modified: python/trunk/Lib/test/test_dict.py ============================================================================== --- python/trunk/Lib/test/test_dict.py (original) +++ python/trunk/Lib/test/test_dict.py Mon Apr 3 06:45:34 2006 @@ -445,7 +445,7 @@ self.fail_("g[42] didn't raise KeyError") -import mapping_tests +from test import mapping_tests class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol): type2test = dict Modified: python/trunk/Lib/test/test_import.py ============================================================================== --- python/trunk/Lib/test/test_import.py (original) +++ python/trunk/Lib/test/test_import.py Mon Apr 3 06:45:34 2006 @@ -15,7 +15,7 @@ raise TestFailed("import of RAnDoM should have failed (case mismatch)") # Another brief digression to test the accuracy of manifest float constants. -import double_const # don't blink -- that *was* the test +from test import double_const # don't blink -- that *was* the test def remove_files(name): for f in (name + os.extsep + "py", Modified: python/trunk/Lib/test/test_sets.py ============================================================================== --- python/trunk/Lib/test/test_sets.py (original) +++ python/trunk/Lib/test/test_sets.py Mon Apr 3 06:45:34 2006 @@ -819,7 +819,8 @@ __test__ = {'libreftest' : libreftest} def test_main(verbose=None): - import test_sets, doctest + import doctest + from test import test_sets test_support.run_unittest( TestSetOfSets, TestExceptionPropagation, Modified: python/trunk/Lib/test/test_urllib2.py ============================================================================== --- python/trunk/Lib/test/test_urllib2.py (original) +++ python/trunk/Lib/test/test_urllib2.py Mon Apr 3 06:45:34 2006 @@ -632,7 +632,7 @@ from urllib2 import build_opener, HTTPHandler, HTTPError, \ HTTPCookieProcessor - from test_cookielib import interact_netscape + from test.test_cookielib import interact_netscape cj = CookieJar() interact_netscape(cj, "http://www.example.com/", "spam=eggs") From python-checkins at python.org Mon Apr 3 06:46:05 2006 From: python-checkins at python.org (neal.norwitz) Date: Mon, 3 Apr 2006 06:46:05 +0200 (CEST) Subject: [Python-checkins] r43562 - python/trunk/Objects/sliceobject.c Message-ID: <20060403044605.1CFCA1E4004@bag.python.org> Author: neal.norwitz Date: Mon Apr 3 06:46:04 2006 New Revision: 43562 Modified: python/trunk/Objects/sliceobject.c Log: Use Py_ssize_t in slices Modified: python/trunk/Objects/sliceobject.c ============================================================================== --- python/trunk/Objects/sliceobject.c (original) +++ python/trunk/Objects/sliceobject.c Mon Apr 3 06:46:04 2006 @@ -107,20 +107,20 @@ *step = 1; } else { if (!PyInt_Check(r->step)) return -1; - *step = PyInt_AsLong(r->step); + *step = PyInt_AsSsize_t(r->step); } if (r->start == Py_None) { *start = *step < 0 ? length-1 : 0; } else { if (!PyInt_Check(r->start)) return -1; - *start = PyInt_AsLong(r->start); + *start = PyInt_AsSsize_t(r->start); if (*start < 0) *start += length; } if (r->stop == Py_None) { *stop = *step < 0 ? -1 : length; } else { if (!PyInt_Check(r->stop)) return -1; - *stop = PyInt_AsLong(r->stop); + *stop = PyInt_AsSsize_t(r->stop); if (*stop < 0) *stop += length; } if (*stop > length) return -1; @@ -252,7 +252,7 @@ { Py_ssize_t ilen, start, stop, step, slicelength; - ilen = PyInt_AsLong(len); + ilen = PyInt_AsSsize_t(len); if (ilen == -1 && PyErr_Occurred()) { return NULL; @@ -263,7 +263,7 @@ return NULL; } - return Py_BuildValue("(iii)", start, stop, step); + return Py_BuildValue("(nnn)", start, stop, step); } PyDoc_STRVAR(slice_indices_doc, From python-checkins at python.org Mon Apr 3 06:46:29 2006 From: python-checkins at python.org (neal.norwitz) Date: Mon, 3 Apr 2006 06:46:29 +0200 (CEST) Subject: [Python-checkins] r43563 - python/trunk/Modules/arraymodule.c Message-ID: <20060403044629.18B831E4004@bag.python.org> Author: neal.norwitz Date: Mon Apr 3 06:46:28 2006 New Revision: 43563 Modified: python/trunk/Modules/arraymodule.c Log: Whitespace: break long line Modified: python/trunk/Modules/arraymodule.c ============================================================================== --- python/trunk/Modules/arraymodule.c (original) +++ python/trunk/Modules/arraymodule.c Mon Apr 3 06:46:28 2006 @@ -186,7 +186,8 @@ if (!PyArg_Parse(v, "u#;array item must be unicode character", &p, &len)) return -1; if (len != 1) { - PyErr_SetString(PyExc_TypeError, "array item must be unicode character"); + PyErr_SetString(PyExc_TypeError, + "array item must be unicode character"); return -1; } if (i >= 0) From python-checkins at python.org Mon Apr 3 06:48:38 2006 From: python-checkins at python.org (neal.norwitz) Date: Mon, 3 Apr 2006 06:48:38 +0200 (CEST) Subject: [Python-checkins] r43564 - in python/trunk: Doc/lib/libfuncs.tex Misc/NEWS Python/bltinmodule.c Message-ID: <20060403044838.514C71E4004@bag.python.org> Author: neal.norwitz Date: Mon Apr 3 06:48:37 2006 New Revision: 43564 Modified: python/trunk/Doc/lib/libfuncs.tex python/trunk/Misc/NEWS python/trunk/Python/bltinmodule.c Log: Accept keyword arguments for __import__ and doc the addition of the level param from PEP 328. Modified: python/trunk/Doc/lib/libfuncs.tex ============================================================================== --- python/trunk/Doc/lib/libfuncs.tex (original) +++ python/trunk/Doc/lib/libfuncs.tex Mon Apr 3 06:48:37 2006 @@ -6,7 +6,7 @@ \setindexsubitem{(built-in function)} -\begin{funcdesc}{__import__}{name\optional{, globals\optional{, locals\optional{, fromlist}}}} +\begin{funcdesc}{__import__}{name\optional{, globals\optional{, locals\optional{, fromlist\optional{, level}}}}} This function is invoked by the \keyword{import}\stindex{import} statement. It mainly exists so that you can replace it with another function that has a compatible interface, in order to change the @@ -20,9 +20,9 @@ For example, the statement \samp{import spam} results in the following call: \code{__import__('spam',} \code{globals(),} - \code{locals(), [])}; the statement \samp{from spam.ham import eggs} + \code{locals(), [], -1)}; the statement \samp{from spam.ham import eggs} results in \samp{__import__('spam.ham', globals(), locals(), - ['eggs'])}. Note that even though \code{locals()} and + ['eggs'], -1)}. Note that even though \code{locals()} and \code{['eggs']} are passed in as arguments, the \function{__import__()} function does not set the local variable named \code{eggs}; this is done by subsequent code that is generated @@ -52,6 +52,15 @@ mod = getattr(mod, comp) return mod \end{verbatim} + + \var{level} specifies whether to use absolute or relative imports. + The default is \code{-1} which indicates both absolute and relative + imports will be attempted. \code{0} means only perform absolute imports. + Positive values for \var{level} indicate the number of parent directories + to search relative to the directory of the module calling + \function{__import__}. +\versionchanged[The level parameter was added]{2.5} +\versionchanged[Keyword support for parameters was added]{2.5} \end{funcdesc} \begin{funcdesc}{abs}{x} Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Mon Apr 3 06:48:37 2006 @@ -12,6 +12,8 @@ Core and builtins ----------------- +- __import__ accepts keyword arguments. + - Patch #1460496: round() now accepts keyword arguments. - Fixed bug #1459029 - unicode reprs were double-escaped. Modified: python/trunk/Python/bltinmodule.c ============================================================================== --- python/trunk/Python/bltinmodule.c (original) +++ python/trunk/Python/bltinmodule.c Mon Apr 3 06:48:37 2006 @@ -31,23 +31,25 @@ static PyObject *filtertuple (PyObject *, PyObject *); static PyObject * -builtin___import__(PyObject *self, PyObject *args) +builtin___import__(PyObject *self, PyObject *args, PyObject *kwds) { + static char *kwlist[] = {"name", "globals", "locals", "fromlist", + "level", 0}; char *name; PyObject *globals = NULL; PyObject *locals = NULL; PyObject *fromlist = NULL; int level = -1; - if (!PyArg_ParseTuple(args, "s|OOOi:__import__", - &name, &globals, &locals, &fromlist, &level)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|OOOi:__import__", + kwlist, &name, &globals, &locals, &fromlist, &level)) return NULL; return PyImport_ImportModuleLevel(name, globals, locals, fromlist, level); } PyDoc_STRVAR(import_doc, -"__import__(name, globals, locals, fromlist) -> module\n\ +"__import__(name, globals={}, locals={}, fromlist=[], level=-1) -> module\n\ \n\ Import a module. The globals are only used to determine the context;\n\ they are not modified. The locals are currently unused. The fromlist\n\ @@ -55,7 +57,10 @@ empty list to emulate ``import name''.\n\ When importing a module from a package, note that __import__('A.B', ...)\n\ returns package A when fromlist is empty, but its submodule B when\n\ -fromlist is not empty."); +fromlist is not empty. Level is used to determine whether to perform \n\ +absolute or relative imports. -1 is the original strategy of attempting\n\ +both absolute and relative imports, 0 is absolute, a positive number\n\ +is the number of parent directories to search relative to the current module."); static PyObject * @@ -2210,7 +2215,7 @@ static PyMethodDef builtin_methods[] = { - {"__import__", builtin___import__, METH_VARARGS, import_doc}, + {"__import__", (PyCFunction)builtin___import__, METH_VARARGS | METH_KEYWORDS, import_doc}, {"abs", builtin_abs, METH_O, abs_doc}, {"all", builtin_all, METH_O, all_doc}, {"any", builtin_any, METH_O, any_doc}, From python-checkins at python.org Mon Apr 3 06:50:59 2006 From: python-checkins at python.org (neal.norwitz) Date: Mon, 3 Apr 2006 06:50:59 +0200 (CEST) Subject: [Python-checkins] r43565 - in python/trunk: Parser/asdl_c.py Python/Python-ast.c Message-ID: <20060403045059.9A4811E4004@bag.python.org> Author: neal.norwitz Date: Mon Apr 3 06:50:58 2006 New Revision: 43565 Modified: python/trunk/Parser/asdl_c.py python/trunk/Python/Python-ast.c Log: * Fix a refleak of *_attributes. * Cleanup formatting a bit (add spaces). * Move static var initialized inside init_types() since that's the only place it's used. Modified: python/trunk/Parser/asdl_c.py ============================================================================== --- python/trunk/Parser/asdl_c.py (original) +++ python/trunk/Parser/asdl_c.py Mon Apr 3 06:50:58 2006 @@ -413,10 +413,10 @@ static int add_attributes(PyTypeObject* type, char**attrs, int num_fields) { - int i; + int i, result; PyObject *s, *l = PyList_New(num_fields); if (!l) return 0; - for(i=0; i < num_fields; i++) { + for(i = 0; i < num_fields; i++) { s = PyString_FromString(attrs[i]); if (!s) { Py_DECREF(l); @@ -424,7 +424,9 @@ } PyList_SET_ITEM(l, i, s); } - return PyObject_SetAttrString((PyObject*)type, "_attributes", l) >=0; + result = PyObject_SetAttrString((PyObject*)type, "_attributes", l) >= 0; + Py_DECREF(l); + return result; } static PyObject* ast2obj_list(asdl_seq *seq, PyObject* (*func)(void*)) @@ -465,9 +467,9 @@ } """, 0, reflow=False) - self.emit("static int initialized;", 0) self.emit("static int init_types(void)",0) self.emit("{", 0) + self.emit("static int initialized;", 1) self.emit("if (initialized) return 1;", 1) self.emit('AST_type = make_type("AST", &PyBaseObject_Type, NULL, 0);', 1) for dfn in mod.dfns: @@ -543,7 +545,7 @@ self.addObj(cons.name) def addObj(self, name): - self.emit('if(PyDict_SetItemString(d, "%s", (PyObject*)%s_type) < 0) return;' % (name, name), 1) + self.emit('if (PyDict_SetItemString(d, "%s", (PyObject*)%s_type) < 0) return;' % (name, name), 1) _SPECIALIZED_SEQUENCES = ('stmt', 'expr') Modified: python/trunk/Python/Python-ast.c ============================================================================== --- python/trunk/Python/Python-ast.c (original) +++ python/trunk/Python/Python-ast.c Mon Apr 3 06:50:58 2006 @@ -381,10 +381,10 @@ static int add_attributes(PyTypeObject* type, char**attrs, int num_fields) { - int i; + int i, result; PyObject *s, *l = PyList_New(num_fields); if (!l) return 0; - for(i=0; i < num_fields; i++) { + for(i = 0; i < num_fields; i++) { s = PyString_FromString(attrs[i]); if (!s) { Py_DECREF(l); @@ -392,7 +392,9 @@ } PyList_SET_ITEM(l, i, s); } - return PyObject_SetAttrString((PyObject*)type, "_attributes", l) >=0; + result = PyObject_SetAttrString((PyObject*)type, "_attributes", l) >= 0; + Py_DECREF(l); + return result; } static PyObject* ast2obj_list(asdl_seq *seq, PyObject* (*func)(void*)) @@ -432,9 +434,9 @@ return PyInt_FromLong(b); } -static int initialized; static int init_types(void) { + static int initialized; if (initialized) return 1; AST_type = make_type("AST", &PyBaseObject_Type, NULL, 0); mod_type = make_type("mod", AST_type, NULL, 0); @@ -3033,146 +3035,146 @@ return; if (PyModule_AddStringConstant(m, "__version__", "42753") < 0) return; - if(PyDict_SetItemString(d, "mod", (PyObject*)mod_type) < 0) return; - if(PyDict_SetItemString(d, "Module", (PyObject*)Module_type) < 0) - return; - if(PyDict_SetItemString(d, "Interactive", (PyObject*)Interactive_type) - < 0) return; - if(PyDict_SetItemString(d, "Expression", (PyObject*)Expression_type) < - 0) return; - if(PyDict_SetItemString(d, "Suite", (PyObject*)Suite_type) < 0) return; - if(PyDict_SetItemString(d, "stmt", (PyObject*)stmt_type) < 0) return; - if(PyDict_SetItemString(d, "FunctionDef", (PyObject*)FunctionDef_type) - < 0) return; - if(PyDict_SetItemString(d, "ClassDef", (PyObject*)ClassDef_type) < 0) - return; - if(PyDict_SetItemString(d, "Return", (PyObject*)Return_type) < 0) - return; - if(PyDict_SetItemString(d, "Delete", (PyObject*)Delete_type) < 0) - return; - if(PyDict_SetItemString(d, "Assign", (PyObject*)Assign_type) < 0) - return; - if(PyDict_SetItemString(d, "AugAssign", (PyObject*)AugAssign_type) < 0) - return; - if(PyDict_SetItemString(d, "Print", (PyObject*)Print_type) < 0) return; - if(PyDict_SetItemString(d, "For", (PyObject*)For_type) < 0) return; - if(PyDict_SetItemString(d, "While", (PyObject*)While_type) < 0) return; - if(PyDict_SetItemString(d, "If", (PyObject*)If_type) < 0) return; - if(PyDict_SetItemString(d, "With", (PyObject*)With_type) < 0) return; - if(PyDict_SetItemString(d, "Raise", (PyObject*)Raise_type) < 0) return; - if(PyDict_SetItemString(d, "TryExcept", (PyObject*)TryExcept_type) < 0) - return; - if(PyDict_SetItemString(d, "TryFinally", (PyObject*)TryFinally_type) < - 0) return; - if(PyDict_SetItemString(d, "Assert", (PyObject*)Assert_type) < 0) - return; - if(PyDict_SetItemString(d, "Import", (PyObject*)Import_type) < 0) - return; - if(PyDict_SetItemString(d, "ImportFrom", (PyObject*)ImportFrom_type) < - 0) return; - if(PyDict_SetItemString(d, "Exec", (PyObject*)Exec_type) < 0) return; - if(PyDict_SetItemString(d, "Global", (PyObject*)Global_type) < 0) - return; - if(PyDict_SetItemString(d, "Expr", (PyObject*)Expr_type) < 0) return; - if(PyDict_SetItemString(d, "Pass", (PyObject*)Pass_type) < 0) return; - if(PyDict_SetItemString(d, "Break", (PyObject*)Break_type) < 0) return; - if(PyDict_SetItemString(d, "Continue", (PyObject*)Continue_type) < 0) - return; - if(PyDict_SetItemString(d, "expr", (PyObject*)expr_type) < 0) return; - if(PyDict_SetItemString(d, "BoolOp", (PyObject*)BoolOp_type) < 0) - return; - if(PyDict_SetItemString(d, "BinOp", (PyObject*)BinOp_type) < 0) return; - if(PyDict_SetItemString(d, "UnaryOp", (PyObject*)UnaryOp_type) < 0) - return; - if(PyDict_SetItemString(d, "Lambda", (PyObject*)Lambda_type) < 0) - return; - if(PyDict_SetItemString(d, "IfExp", (PyObject*)IfExp_type) < 0) return; - if(PyDict_SetItemString(d, "Dict", (PyObject*)Dict_type) < 0) return; - if(PyDict_SetItemString(d, "ListComp", (PyObject*)ListComp_type) < 0) - return; - if(PyDict_SetItemString(d, "GeneratorExp", - (PyObject*)GeneratorExp_type) < 0) return; - if(PyDict_SetItemString(d, "Yield", (PyObject*)Yield_type) < 0) return; - if(PyDict_SetItemString(d, "Compare", (PyObject*)Compare_type) < 0) - return; - if(PyDict_SetItemString(d, "Call", (PyObject*)Call_type) < 0) return; - if(PyDict_SetItemString(d, "Repr", (PyObject*)Repr_type) < 0) return; - if(PyDict_SetItemString(d, "Num", (PyObject*)Num_type) < 0) return; - if(PyDict_SetItemString(d, "Str", (PyObject*)Str_type) < 0) return; - if(PyDict_SetItemString(d, "Attribute", (PyObject*)Attribute_type) < 0) - return; - if(PyDict_SetItemString(d, "Subscript", (PyObject*)Subscript_type) < 0) - return; - if(PyDict_SetItemString(d, "Name", (PyObject*)Name_type) < 0) return; - if(PyDict_SetItemString(d, "List", (PyObject*)List_type) < 0) return; - if(PyDict_SetItemString(d, "Tuple", (PyObject*)Tuple_type) < 0) return; - if(PyDict_SetItemString(d, "expr_context", - (PyObject*)expr_context_type) < 0) return; - if(PyDict_SetItemString(d, "Load", (PyObject*)Load_type) < 0) return; - if(PyDict_SetItemString(d, "Store", (PyObject*)Store_type) < 0) return; - if(PyDict_SetItemString(d, "Del", (PyObject*)Del_type) < 0) return; - if(PyDict_SetItemString(d, "AugLoad", (PyObject*)AugLoad_type) < 0) - return; - if(PyDict_SetItemString(d, "AugStore", (PyObject*)AugStore_type) < 0) - return; - if(PyDict_SetItemString(d, "Param", (PyObject*)Param_type) < 0) return; - if(PyDict_SetItemString(d, "slice", (PyObject*)slice_type) < 0) return; - if(PyDict_SetItemString(d, "Ellipsis", (PyObject*)Ellipsis_type) < 0) - return; - if(PyDict_SetItemString(d, "Slice", (PyObject*)Slice_type) < 0) return; - if(PyDict_SetItemString(d, "ExtSlice", (PyObject*)ExtSlice_type) < 0) - return; - if(PyDict_SetItemString(d, "Index", (PyObject*)Index_type) < 0) return; - if(PyDict_SetItemString(d, "boolop", (PyObject*)boolop_type) < 0) - return; - if(PyDict_SetItemString(d, "And", (PyObject*)And_type) < 0) return; - if(PyDict_SetItemString(d, "Or", (PyObject*)Or_type) < 0) return; - if(PyDict_SetItemString(d, "operator", (PyObject*)operator_type) < 0) - return; - if(PyDict_SetItemString(d, "Add", (PyObject*)Add_type) < 0) return; - if(PyDict_SetItemString(d, "Sub", (PyObject*)Sub_type) < 0) return; - if(PyDict_SetItemString(d, "Mult", (PyObject*)Mult_type) < 0) return; - if(PyDict_SetItemString(d, "Div", (PyObject*)Div_type) < 0) return; - if(PyDict_SetItemString(d, "Mod", (PyObject*)Mod_type) < 0) return; - if(PyDict_SetItemString(d, "Pow", (PyObject*)Pow_type) < 0) return; - if(PyDict_SetItemString(d, "LShift", (PyObject*)LShift_type) < 0) - return; - if(PyDict_SetItemString(d, "RShift", (PyObject*)RShift_type) < 0) - return; - if(PyDict_SetItemString(d, "BitOr", (PyObject*)BitOr_type) < 0) return; - if(PyDict_SetItemString(d, "BitXor", (PyObject*)BitXor_type) < 0) - return; - if(PyDict_SetItemString(d, "BitAnd", (PyObject*)BitAnd_type) < 0) - return; - if(PyDict_SetItemString(d, "FloorDiv", (PyObject*)FloorDiv_type) < 0) - return; - if(PyDict_SetItemString(d, "unaryop", (PyObject*)unaryop_type) < 0) - return; - if(PyDict_SetItemString(d, "Invert", (PyObject*)Invert_type) < 0) - return; - if(PyDict_SetItemString(d, "Not", (PyObject*)Not_type) < 0) return; - if(PyDict_SetItemString(d, "UAdd", (PyObject*)UAdd_type) < 0) return; - if(PyDict_SetItemString(d, "USub", (PyObject*)USub_type) < 0) return; - if(PyDict_SetItemString(d, "cmpop", (PyObject*)cmpop_type) < 0) return; - if(PyDict_SetItemString(d, "Eq", (PyObject*)Eq_type) < 0) return; - if(PyDict_SetItemString(d, "NotEq", (PyObject*)NotEq_type) < 0) return; - if(PyDict_SetItemString(d, "Lt", (PyObject*)Lt_type) < 0) return; - if(PyDict_SetItemString(d, "LtE", (PyObject*)LtE_type) < 0) return; - if(PyDict_SetItemString(d, "Gt", (PyObject*)Gt_type) < 0) return; - if(PyDict_SetItemString(d, "GtE", (PyObject*)GtE_type) < 0) return; - if(PyDict_SetItemString(d, "Is", (PyObject*)Is_type) < 0) return; - if(PyDict_SetItemString(d, "IsNot", (PyObject*)IsNot_type) < 0) return; - if(PyDict_SetItemString(d, "In", (PyObject*)In_type) < 0) return; - if(PyDict_SetItemString(d, "NotIn", (PyObject*)NotIn_type) < 0) return; - if(PyDict_SetItemString(d, "comprehension", - (PyObject*)comprehension_type) < 0) return; - if(PyDict_SetItemString(d, "excepthandler", - (PyObject*)excepthandler_type) < 0) return; - if(PyDict_SetItemString(d, "arguments", (PyObject*)arguments_type) < 0) - return; - if(PyDict_SetItemString(d, "keyword", (PyObject*)keyword_type) < 0) - return; - if(PyDict_SetItemString(d, "alias", (PyObject*)alias_type) < 0) return; + if (PyDict_SetItemString(d, "mod", (PyObject*)mod_type) < 0) return; + if (PyDict_SetItemString(d, "Module", (PyObject*)Module_type) < 0) + return; + if (PyDict_SetItemString(d, "Interactive", (PyObject*)Interactive_type) + < 0) return; + if (PyDict_SetItemString(d, "Expression", (PyObject*)Expression_type) < + 0) return; + if (PyDict_SetItemString(d, "Suite", (PyObject*)Suite_type) < 0) return; + if (PyDict_SetItemString(d, "stmt", (PyObject*)stmt_type) < 0) return; + if (PyDict_SetItemString(d, "FunctionDef", (PyObject*)FunctionDef_type) + < 0) return; + if (PyDict_SetItemString(d, "ClassDef", (PyObject*)ClassDef_type) < 0) + return; + if (PyDict_SetItemString(d, "Return", (PyObject*)Return_type) < 0) + return; + if (PyDict_SetItemString(d, "Delete", (PyObject*)Delete_type) < 0) + return; + if (PyDict_SetItemString(d, "Assign", (PyObject*)Assign_type) < 0) + return; + if (PyDict_SetItemString(d, "AugAssign", (PyObject*)AugAssign_type) < + 0) return; + if (PyDict_SetItemString(d, "Print", (PyObject*)Print_type) < 0) return; + if (PyDict_SetItemString(d, "For", (PyObject*)For_type) < 0) return; + if (PyDict_SetItemString(d, "While", (PyObject*)While_type) < 0) return; + if (PyDict_SetItemString(d, "If", (PyObject*)If_type) < 0) return; + if (PyDict_SetItemString(d, "With", (PyObject*)With_type) < 0) return; + if (PyDict_SetItemString(d, "Raise", (PyObject*)Raise_type) < 0) return; + if (PyDict_SetItemString(d, "TryExcept", (PyObject*)TryExcept_type) < + 0) return; + if (PyDict_SetItemString(d, "TryFinally", (PyObject*)TryFinally_type) < + 0) return; + if (PyDict_SetItemString(d, "Assert", (PyObject*)Assert_type) < 0) + return; + if (PyDict_SetItemString(d, "Import", (PyObject*)Import_type) < 0) + return; + if (PyDict_SetItemString(d, "ImportFrom", (PyObject*)ImportFrom_type) < + 0) return; + if (PyDict_SetItemString(d, "Exec", (PyObject*)Exec_type) < 0) return; + if (PyDict_SetItemString(d, "Global", (PyObject*)Global_type) < 0) + return; + if (PyDict_SetItemString(d, "Expr", (PyObject*)Expr_type) < 0) return; + if (PyDict_SetItemString(d, "Pass", (PyObject*)Pass_type) < 0) return; + if (PyDict_SetItemString(d, "Break", (PyObject*)Break_type) < 0) return; + if (PyDict_SetItemString(d, "Continue", (PyObject*)Continue_type) < 0) + return; + if (PyDict_SetItemString(d, "expr", (PyObject*)expr_type) < 0) return; + if (PyDict_SetItemString(d, "BoolOp", (PyObject*)BoolOp_type) < 0) + return; + if (PyDict_SetItemString(d, "BinOp", (PyObject*)BinOp_type) < 0) return; + if (PyDict_SetItemString(d, "UnaryOp", (PyObject*)UnaryOp_type) < 0) + return; + if (PyDict_SetItemString(d, "Lambda", (PyObject*)Lambda_type) < 0) + return; + if (PyDict_SetItemString(d, "IfExp", (PyObject*)IfExp_type) < 0) return; + if (PyDict_SetItemString(d, "Dict", (PyObject*)Dict_type) < 0) return; + if (PyDict_SetItemString(d, "ListComp", (PyObject*)ListComp_type) < 0) + return; + if (PyDict_SetItemString(d, "GeneratorExp", + (PyObject*)GeneratorExp_type) < 0) return; + if (PyDict_SetItemString(d, "Yield", (PyObject*)Yield_type) < 0) return; + if (PyDict_SetItemString(d, "Compare", (PyObject*)Compare_type) < 0) + return; + if (PyDict_SetItemString(d, "Call", (PyObject*)Call_type) < 0) return; + if (PyDict_SetItemString(d, "Repr", (PyObject*)Repr_type) < 0) return; + if (PyDict_SetItemString(d, "Num", (PyObject*)Num_type) < 0) return; + if (PyDict_SetItemString(d, "Str", (PyObject*)Str_type) < 0) return; + if (PyDict_SetItemString(d, "Attribute", (PyObject*)Attribute_type) < + 0) return; + if (PyDict_SetItemString(d, "Subscript", (PyObject*)Subscript_type) < + 0) return; + if (PyDict_SetItemString(d, "Name", (PyObject*)Name_type) < 0) return; + if (PyDict_SetItemString(d, "List", (PyObject*)List_type) < 0) return; + if (PyDict_SetItemString(d, "Tuple", (PyObject*)Tuple_type) < 0) return; + if (PyDict_SetItemString(d, "expr_context", + (PyObject*)expr_context_type) < 0) return; + if (PyDict_SetItemString(d, "Load", (PyObject*)Load_type) < 0) return; + if (PyDict_SetItemString(d, "Store", (PyObject*)Store_type) < 0) return; + if (PyDict_SetItemString(d, "Del", (PyObject*)Del_type) < 0) return; + if (PyDict_SetItemString(d, "AugLoad", (PyObject*)AugLoad_type) < 0) + return; + if (PyDict_SetItemString(d, "AugStore", (PyObject*)AugStore_type) < 0) + return; + if (PyDict_SetItemString(d, "Param", (PyObject*)Param_type) < 0) return; + if (PyDict_SetItemString(d, "slice", (PyObject*)slice_type) < 0) return; + if (PyDict_SetItemString(d, "Ellipsis", (PyObject*)Ellipsis_type) < 0) + return; + if (PyDict_SetItemString(d, "Slice", (PyObject*)Slice_type) < 0) return; + if (PyDict_SetItemString(d, "ExtSlice", (PyObject*)ExtSlice_type) < 0) + return; + if (PyDict_SetItemString(d, "Index", (PyObject*)Index_type) < 0) return; + if (PyDict_SetItemString(d, "boolop", (PyObject*)boolop_type) < 0) + return; + if (PyDict_SetItemString(d, "And", (PyObject*)And_type) < 0) return; + if (PyDict_SetItemString(d, "Or", (PyObject*)Or_type) < 0) return; + if (PyDict_SetItemString(d, "operator", (PyObject*)operator_type) < 0) + return; + if (PyDict_SetItemString(d, "Add", (PyObject*)Add_type) < 0) return; + if (PyDict_SetItemString(d, "Sub", (PyObject*)Sub_type) < 0) return; + if (PyDict_SetItemString(d, "Mult", (PyObject*)Mult_type) < 0) return; + if (PyDict_SetItemString(d, "Div", (PyObject*)Div_type) < 0) return; + if (PyDict_SetItemString(d, "Mod", (PyObject*)Mod_type) < 0) return; + if (PyDict_SetItemString(d, "Pow", (PyObject*)Pow_type) < 0) return; + if (PyDict_SetItemString(d, "LShift", (PyObject*)LShift_type) < 0) + return; + if (PyDict_SetItemString(d, "RShift", (PyObject*)RShift_type) < 0) + return; + if (PyDict_SetItemString(d, "BitOr", (PyObject*)BitOr_type) < 0) return; + if (PyDict_SetItemString(d, "BitXor", (PyObject*)BitXor_type) < 0) + return; + if (PyDict_SetItemString(d, "BitAnd", (PyObject*)BitAnd_type) < 0) + return; + if (PyDict_SetItemString(d, "FloorDiv", (PyObject*)FloorDiv_type) < 0) + return; + if (PyDict_SetItemString(d, "unaryop", (PyObject*)unaryop_type) < 0) + return; + if (PyDict_SetItemString(d, "Invert", (PyObject*)Invert_type) < 0) + return; + if (PyDict_SetItemString(d, "Not", (PyObject*)Not_type) < 0) return; + if (PyDict_SetItemString(d, "UAdd", (PyObject*)UAdd_type) < 0) return; + if (PyDict_SetItemString(d, "USub", (PyObject*)USub_type) < 0) return; + if (PyDict_SetItemString(d, "cmpop", (PyObject*)cmpop_type) < 0) return; + if (PyDict_SetItemString(d, "Eq", (PyObject*)Eq_type) < 0) return; + if (PyDict_SetItemString(d, "NotEq", (PyObject*)NotEq_type) < 0) return; + if (PyDict_SetItemString(d, "Lt", (PyObject*)Lt_type) < 0) return; + if (PyDict_SetItemString(d, "LtE", (PyObject*)LtE_type) < 0) return; + if (PyDict_SetItemString(d, "Gt", (PyObject*)Gt_type) < 0) return; + if (PyDict_SetItemString(d, "GtE", (PyObject*)GtE_type) < 0) return; + if (PyDict_SetItemString(d, "Is", (PyObject*)Is_type) < 0) return; + if (PyDict_SetItemString(d, "IsNot", (PyObject*)IsNot_type) < 0) return; + if (PyDict_SetItemString(d, "In", (PyObject*)In_type) < 0) return; + if (PyDict_SetItemString(d, "NotIn", (PyObject*)NotIn_type) < 0) return; + if (PyDict_SetItemString(d, "comprehension", + (PyObject*)comprehension_type) < 0) return; + if (PyDict_SetItemString(d, "excepthandler", + (PyObject*)excepthandler_type) < 0) return; + if (PyDict_SetItemString(d, "arguments", (PyObject*)arguments_type) < + 0) return; + if (PyDict_SetItemString(d, "keyword", (PyObject*)keyword_type) < 0) + return; + if (PyDict_SetItemString(d, "alias", (PyObject*)alias_type) < 0) return; } From python-checkins at python.org Mon Apr 3 06:52:08 2006 From: python-checkins at python.org (neal.norwitz) Date: Mon, 3 Apr 2006 06:52:08 +0200 (CEST) Subject: [Python-checkins] r43566 - python/trunk/setup.py Message-ID: <20060403045208.D89F81E4004@bag.python.org> Author: neal.norwitz Date: Mon Apr 3 06:52:05 2006 New Revision: 43566 Modified: python/trunk/setup.py Log: Remove some duplicated code for handling Mac modules. No functional change (intended). Also stoped setting srcdir twice. Modified: python/trunk/setup.py ============================================================================== --- python/trunk/setup.py (original) +++ python/trunk/setup.py Mon Apr 3 06:52:05 2006 @@ -999,115 +999,73 @@ carbon_extra_compile_args = [] # Mac OS X specific modules. - exts.append( Extension('_CF', ['cf/_CFmodule.c', 'cf/pycfbridge.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'CoreFoundation']) ) - exts.append( Extension('ColorPicker', ['ColorPickermodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('autoGIL', ['autoGIL.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'CoreFoundation']) ) - exts.append( Extension('gestalt', ['gestaltmodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('MacOS', ['macosmodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('OSATerminology', ['OSATerminology.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('icglue', ['icgluemodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Res', ['res/_Resmodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Snd', ['snd/_Sndmodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('Nav', ['Nav.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_AE', ['ae/_AEmodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_AH', ['ah/_AHmodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_App', ['app/_Appmodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_CarbonEvt', ['carbonevt/_CarbonEvtmodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_CG', ['cg/_CGmodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'ApplicationServices']) ) - exts.append( Extension('_Cm', ['cm/_Cmmodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Ctl', ['ctl/_Ctlmodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Dlg', ['dlg/_Dlgmodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Drag', ['drag/_Dragmodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Evt', ['evt/_Evtmodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_File', ['file/_Filemodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Folder', ['folder/_Foldermodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Fm', ['fm/_Fmmodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Help', ['help/_Helpmodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Icn', ['icn/_Icnmodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_IBCarbon', ['ibcarbon/_IBCarbon.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Launch', ['launch/_Launchmodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'ApplicationServices']) ) - exts.append( Extension('_List', ['list/_Listmodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Menu', ['menu/_Menumodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Mlte', ['mlte/_Mltemodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_OSA', ['osa/_OSAmodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Qd', ['qd/_Qdmodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Qdoffs', ['qdoffs/_Qdoffsmodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) + def macSrcExists(name1, name2=''): + if not name1: + return None + names = (name1,) + if name2: + names = (name1, name2) + path = os.path.join(srcdir, 'Mac', 'Modules', *names) + return os.path.exists(path) + + def addMacExtension(name, kwds, extra_srcs=[]): + dirname = '' + if name[0] == '_': + dirname = name[1:].lower() + cname = name + '.c' + cmodulename = name + 'module.c' + # Check for NNN.c, NNNmodule.c, _nnn/NNN.c, _nnn/NNNmodule.c + if macSrcExists(cname): + srcs = [cname] + elif macSrcExists(cmodulename): + srcs = [cmodulename] + elif macSrcExists(dirname, cname): + # XXX(nnorwitz): If all the names ended with module, we + # wouldn't need this condition. ibcarbon is the only one. + srcs = [os.path.join(dirname, cname)] + elif macSrcExists(dirname, cmodulename): + srcs = [os.path.join(dirname, cmodulename)] + else: + raise RuntimeError("%s not found" % name) + + # Here's the whole point: add the extension with sources + exts.append(Extension(name, srcs + extra_srcs, **kwds)) + + # Core Foundation + core_kwds = {'extra_compile_args': carbon_extra_compile_args, + 'extra_link_args': ['-framework', 'CoreFoundation'], + } + addMacExtension('_CF', core_kwds, ['cf/pycfbridge.c']) + addMacExtension('autoGIL', core_kwds) + + # Carbon + carbon_kwds = {'extra_compile_args': carbon_extra_compile_args, + 'extra_link_args': ['-framework', 'Carbon'], + } + CARBON_EXTS = ['ColorPicker', 'gestalt', 'MacOS', 'Nav', + 'OSATerminology', 'icglue', + # All these are in subdirs + '_AE', '_AH', '_App', '_CarbonEvt', '_Cm', '_Ctl', + '_Dlg', '_Drag', '_Evt', '_File', '_Folder', '_Fm', + '_Help', '_Icn', '_IBCarbon', '_List', + '_Menu', '_Mlte', '_OSA', '_Res', '_Qd', '_Qdoffs', + '_Scrap', '_Snd', '_TE', '_Win', + ] + for name in CARBON_EXTS: + addMacExtension(name, carbon_kwds) + + # Application Services & QuickTime + app_kwds = {'extra_compile_args': carbon_extra_compile_args, + 'extra_link_args': ['-framework','ApplicationServices'], + } + addMacExtension('_Launch', app_kwds) + addMacExtension('_CG', app_kwds) + exts.append( Extension('_Qt', ['qt/_Qtmodule.c'], extra_compile_args=carbon_extra_compile_args, extra_link_args=['-framework', 'QuickTime', '-framework', 'Carbon']) ) - exts.append( Extension('_Scrap', ['scrap/_Scrapmodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_TE', ['te/_TEmodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) + # As there is no standardized place (yet) to put # user-installed Mac libraries on OSX, we search for "waste" # in parent directories of the Python source tree. You @@ -1119,7 +1077,6 @@ waste_libs = find_library_file(self.compiler, "WASTE", [], ["../"*n + "waste/Static Libraries" for n in (0,1,2,3,4)]) if waste_incs != None and waste_libs != None: - (srcdir,) = sysconfig.get_config_vars('srcdir') exts.append( Extension('waste', ['waste/wastemodule.c'] + [ os.path.join(srcdir, d) for d in @@ -1132,9 +1089,6 @@ libraries = ['WASTE'], extra_link_args = ['-framework', 'Carbon'], ) ) - exts.append( Extension('_Win', ['win/_Winmodule.c'], - extra_compile_args=carbon_extra_compile_args, - extra_link_args=['-framework', 'Carbon']) ) self.extensions.extend(exts) From python-checkins at python.org Mon Apr 3 07:27:06 2006 From: python-checkins at python.org (neal.norwitz) Date: Mon, 3 Apr 2006 07:27:06 +0200 (CEST) Subject: [Python-checkins] r43567 - python/trunk/Lib/test/test_file.py Message-ID: <20060403052706.678B71E4004@bag.python.org> Author: neal.norwitz Date: Mon Apr 3 07:27:05 2006 New Revision: 43567 Modified: python/trunk/Lib/test/test_file.py Log: Skip the test for sys.stdin.seek(-1) on OSF/1 (Tru64) since it does Bad Things like cause the interpreter to exit abruptly. If there's a way to fix this, it would be good to really fix it. It could just be the operation of the std C library and we just aren't supposed to do that. When the test case is skipped, we print a message so the user can check for themselves. Modified: python/trunk/Lib/test/test_file.py ============================================================================== --- python/trunk/Lib/test/test_file.py (original) +++ python/trunk/Lib/test/test_file.py Mon Apr 3 07:27:05 2006 @@ -100,12 +100,18 @@ print "writelines accepted sequence of non-string objects" f.close() -try: - sys.stdin.seek(-1) -except IOError: - pass +# This causes the interpreter to exit on OSF1 v5.1. +if sys.platform != 'osf1V5': + try: + sys.stdin.seek(-1) + except IOError: + pass + else: + print "should not be able to seek on sys.stdin" else: - print "should not be able to seek on sys.stdin" + print >>sys.__stdout__, ( + ' Skipping sys.stdin.seek(-1), it may crash the interpreter.' + ' Test manually.') try: sys.stdin.truncate() From python-checkins at python.org Mon Apr 3 07:28:31 2006 From: python-checkins at python.org (neal.norwitz) Date: Mon, 3 Apr 2006 07:28:31 +0200 (CEST) Subject: [Python-checkins] r43568 - python/trunk/Lib/test/test_pty.py Message-ID: <20060403052831.EDD6D1E4004@bag.python.org> Author: neal.norwitz Date: Mon Apr 3 07:28:31 2006 New Revision: 43568 Modified: python/trunk/Lib/test/test_pty.py Log: Fix test_pty on OSF/1 (Tru64). The problem is that the newline gets converted to CR CR NL. There may be a way to fix this with tcsetattr, but I couldn't find it. There was a similar problem on IRIX. Just normalize the output and compare that. Will backport. Modified: python/trunk/Lib/test/test_pty.py ============================================================================== --- python/trunk/Lib/test/test_pty.py (original) +++ python/trunk/Lib/test/test_pty.py Mon Apr 3 07:28:31 2006 @@ -18,6 +18,27 @@ def debug(msg): pass +def normalize_output(data): + # Some operating systems do conversions on newline. We could possibly + # fix that by doing the appropriate termios.tcsetattr()s. I couldn't + # figure out the right combo on Tru64 and I don't have an IRIX box. + # So just normalize the output and doc the problem O/Ses by allowing + # certain combinations for some platforms, but avoid allowing other + # differences (like extra whitespace, trailing garbage, etc.) + + # This is about the best we can do without getting some feedback + # from someone more knowledgable. + + # OSF/1 (Tru64) apparently turns \n into \r\r\n. + if data.endswith('\r\r\n'): + return data[:-3] + '\n' + + # IRIX apparently turns \n into \r\n. + if data.endswith('\r\n'): + return data[:-2] + '\n' + + return data + # Marginal testing of pty suite. Cannot do extensive 'do or fail' testing # because pty code is not too portable. @@ -36,19 +57,16 @@ if not os.isatty(slave_fd) and sys.platform not in fickle_isatty: raise TestFailed, "slave_fd is not a tty" - # IRIX apparently turns \n into \r\n. Allow that, but avoid allowing other - # differences (like extra whitespace, trailing garbage, etc.) - debug("Writing to slave_fd") os.write(slave_fd, TEST_STRING_1) s1 = os.read(master_fd, 1024) - sys.stdout.write(s1.replace("\r\n", "\n")) + sys.stdout.write(normalize_output(s1)) debug("Writing chunked output") os.write(slave_fd, TEST_STRING_2[:5]) os.write(slave_fd, TEST_STRING_2[5:]) s2 = os.read(master_fd, 1024) - sys.stdout.write(s2.replace("\r\n", "\n")) + sys.stdout.write(normalize_output(s2)) os.close(slave_fd) os.close(master_fd) From python-checkins at python.org Mon Apr 3 07:29:03 2006 From: python-checkins at python.org (martin.v.loewis) Date: Mon, 3 Apr 2006 07:29:03 +0200 (CEST) Subject: [Python-checkins] r43569 - python/trunk/PCbuild/pcbuild.sln python/trunk/PCbuild/pythoncore.vcproj Message-ID: <20060403052903.BC7751E4004@bag.python.org> Author: martin.v.loewis Date: Mon Apr 3 07:29:03 2006 New Revision: 43569 Modified: python/trunk/PCbuild/pcbuild.sln python/trunk/PCbuild/pythoncore.vcproj Log: Patch #1462700: Make _ctypes_test depend on _ctypes. Use same GUID for pythoncore in project and solution. Modified: python/trunk/PCbuild/pcbuild.sln ============================================================================== --- python/trunk/PCbuild/pcbuild.sln (original) +++ python/trunk/PCbuild/pcbuild.sln Mon Apr 3 07:29:03 2006 @@ -96,6 +96,7 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_ctypes_test", "_ctypes_test.vcproj", "{8CF334D9-4F82-42EB-97AF-83592C5AFD2F}" ProjectSection(ProjectDependencies) = postProject + {F22F40F4-D318-40DC-96B3-88DC81CE0894} = {F22F40F4-D318-40DC-96B3-88DC81CE0894} EndProjectSection EndProject Global Modified: python/trunk/PCbuild/pythoncore.vcproj ============================================================================== --- python/trunk/PCbuild/pythoncore.vcproj (original) +++ python/trunk/PCbuild/pythoncore.vcproj Mon Apr 3 07:29:03 2006 @@ -3,7 +3,7 @@ ProjectType="Visual C++" Version="7.10" Name="pythoncore" - ProjectGUID="{7AFA1F0B-A8A1-455A-A832-BF263404BBEF}" + ProjectGUID="{CF7AC3D1-E2DF-41D2-BEA6-1E2556CDEA26}" RootNamespace="pythoncore" SccProjectName="pythoncore" SccLocalPath=".."> From python-checkins at python.org Mon Apr 3 07:30:46 2006 From: python-checkins at python.org (neal.norwitz) Date: Mon, 3 Apr 2006 07:30:46 +0200 (CEST) Subject: [Python-checkins] r43570 - python/branches/release24-maint/Lib/test/test_pty.py Message-ID: <20060403053046.0A6681E4004@bag.python.org> Author: neal.norwitz Date: Mon Apr 3 07:30:45 2006 New Revision: 43570 Modified: python/branches/release24-maint/Lib/test/test_pty.py Log: Backport 43568: Fix test_pty on OSF/1 (Tru64). The problem is that the newline gets converted to CR CR NL. There may be a way to fix this with tcsetattr, but I couldn't find it. There was a similar problem on IRIX. Just normalize the output and compare that. Modified: python/branches/release24-maint/Lib/test/test_pty.py ============================================================================== --- python/branches/release24-maint/Lib/test/test_pty.py (original) +++ python/branches/release24-maint/Lib/test/test_pty.py Mon Apr 3 07:30:45 2006 @@ -18,6 +18,27 @@ def debug(msg): pass +def normalize_output(data): + # Some operating systems do conversions on newline. We could possibly + # fix that by doing the appropriate termios.tcsetattr()s. I couldn't + # figure out the right combo on Tru64 and I don't have an IRIX box. + # So just normalize the output and doc the problem O/Ses by allowing + # certain combinations for some platforms, but avoid allowing other + # differences (like extra whitespace, trailing garbage, etc.) + + # This is about the best we can do without getting some feedback + # from someone more knowledgable. + + # OSF/1 (Tru64) apparently turns \n into \r\r\n. + if data.endswith('\r\r\n'): + return data[:-3] + '\n' + + # IRIX apparently turns \n into \r\n. + if data.endswith('\r\n'): + return data[:-2] + '\n' + + return data + # Marginal testing of pty suite. Cannot do extensive 'do or fail' testing # because pty code is not too portable. @@ -36,19 +57,16 @@ if not os.isatty(slave_fd) and sys.platform not in fickle_isatty: raise TestFailed, "slave_fd is not a tty" - # IRIX apparently turns \n into \r\n. Allow that, but avoid allowing other - # differences (like extra whitespace, trailing garbage, etc.) - debug("Writing to slave_fd") os.write(slave_fd, TEST_STRING_1) s1 = os.read(master_fd, 1024) - sys.stdout.write(s1.replace("\r\n", "\n")) + sys.stdout.write(normalize_output(s1)) debug("Writing chunked output") os.write(slave_fd, TEST_STRING_2[:5]) os.write(slave_fd, TEST_STRING_2[5:]) s2 = os.read(master_fd, 1024) - sys.stdout.write(s2.replace("\r\n", "\n")) + sys.stdout.write(normalize_output(s2)) os.close(slave_fd) os.close(master_fd) From python-checkins at python.org Mon Apr 3 07:55:10 2006 From: python-checkins at python.org (martin.v.loewis) Date: Mon, 3 Apr 2006 07:55:10 +0200 (CEST) Subject: [Python-checkins] r43571 - external/sqlite-source-3.3.4 external/sqlite-source-3.3.4/alter.c external/sqlite-source-3.3.4/analyze.c external/sqlite-source-3.3.4/attach.c external/sqlite-source-3.3.4/auth.c external/sqlite-source-3.3.4/btree.c external/sqlite-source-3.3.4/btree.h external/sqlite-source-3.3.4/build.c external/sqlite-source-3.3.4/callback.c external/sqlite-source-3.3.4/complete.c external/sqlite-source-3.3.4/date.c external/sqlite-source-3.3.4/delete.c external/sqlite-source-3.3.4/expr.c external/sqlite-source-3.3.4/func.c external/sqlite-source-3.3.4/hash.c external/sqlite-source-3.3.4/hash.h external/sqlite-source-3.3.4/insert.c external/sqlite-source-3.3.4/keywordhash.h external/sqlite-source-3.3.4/legacy.c external/sqlite-source-3.3.4/main.c external/sqlite-source-3.3.4/opcodes.c external/sqlite-source-3.3.4/opcodes.h external/sqlite-source-3.3.4/os.c external/sqlite-source-3.3.4/os.h external/sqlite-source-3.3.4/os_common.h external/sqlite-source-3.3.4/os_unix.c external/sqlite-source-3.3.4/os_win.c external/sqlite-source-3.3.4/pager.c external/sqlite-source-3.3.4/pager.h external/sqlite-source-3.3.4/parse.c external/sqlite-source-3.3.4/parse.h external/sqlite-source-3.3.4/pragma.c external/sqlite-source-3.3.4/prepare.c external/sqlite-source-3.3.4/printf.c external/sqlite-source-3.3.4/random.c external/sqlite-source-3.3.4/select.c external/sqlite-source-3.3.4/shell.c external/sqlite-source-3.3.4/sqlite3.def external/sqlite-source-3.3.4/sqlite3.h external/sqlite-source-3.3.4/sqliteInt.h external/sqlite-source-3.3.4/table.c external/sqlite-source-3.3.4/tclsqlite.c external/sqlite-source-3.3.4/tokenize.c external/sqlite-source-3.3.4/trigger.c external/sqlite-source-3.3.4/update.c external/sqlite-source-3.3.4/utf.c external/sqlite-source-3.3.4/util.c external/sqlite-source-3.3.4/vacuum.c external/sqlite-source-3.3.4/vdbe.c external/sqlite-source-3.3.4/vdbe.h external/sqlite-source-3.3.4/vdbeInt.h external/sqlite-source-3.3.4/vdbeapi.c external/sqlite-source-3.3.4/vdbeaux.c external/sqlite-source-3.3.4/vdbefifo.c external/sqlite-source-3.3.4/vdbemem.c external/sqlite-source-3.3.4/where.c Message-ID: <20060403055510.CE0931E400D@bag.python.org> Author: martin.v.loewis Date: Mon Apr 3 07:54:59 2006 New Revision: 43571 Added: external/sqlite-source-3.3.4/ external/sqlite-source-3.3.4/alter.c external/sqlite-source-3.3.4/analyze.c external/sqlite-source-3.3.4/attach.c external/sqlite-source-3.3.4/auth.c external/sqlite-source-3.3.4/btree.c external/sqlite-source-3.3.4/btree.h external/sqlite-source-3.3.4/build.c external/sqlite-source-3.3.4/callback.c external/sqlite-source-3.3.4/complete.c external/sqlite-source-3.3.4/date.c external/sqlite-source-3.3.4/delete.c external/sqlite-source-3.3.4/expr.c external/sqlite-source-3.3.4/func.c external/sqlite-source-3.3.4/hash.c external/sqlite-source-3.3.4/hash.h external/sqlite-source-3.3.4/insert.c external/sqlite-source-3.3.4/keywordhash.h external/sqlite-source-3.3.4/legacy.c external/sqlite-source-3.3.4/main.c external/sqlite-source-3.3.4/opcodes.c external/sqlite-source-3.3.4/opcodes.h external/sqlite-source-3.3.4/os.c external/sqlite-source-3.3.4/os.h external/sqlite-source-3.3.4/os_common.h external/sqlite-source-3.3.4/os_unix.c external/sqlite-source-3.3.4/os_win.c external/sqlite-source-3.3.4/pager.c external/sqlite-source-3.3.4/pager.h external/sqlite-source-3.3.4/parse.c external/sqlite-source-3.3.4/parse.h external/sqlite-source-3.3.4/pragma.c external/sqlite-source-3.3.4/prepare.c external/sqlite-source-3.3.4/printf.c external/sqlite-source-3.3.4/random.c external/sqlite-source-3.3.4/select.c external/sqlite-source-3.3.4/shell.c external/sqlite-source-3.3.4/sqlite3.def external/sqlite-source-3.3.4/sqlite3.h external/sqlite-source-3.3.4/sqliteInt.h external/sqlite-source-3.3.4/table.c external/sqlite-source-3.3.4/tclsqlite.c external/sqlite-source-3.3.4/tokenize.c external/sqlite-source-3.3.4/trigger.c external/sqlite-source-3.3.4/update.c external/sqlite-source-3.3.4/utf.c external/sqlite-source-3.3.4/util.c external/sqlite-source-3.3.4/vacuum.c external/sqlite-source-3.3.4/vdbe.c external/sqlite-source-3.3.4/vdbe.h external/sqlite-source-3.3.4/vdbeInt.h external/sqlite-source-3.3.4/vdbeapi.c external/sqlite-source-3.3.4/vdbeaux.c external/sqlite-source-3.3.4/vdbefifo.c external/sqlite-source-3.3.4/vdbemem.c external/sqlite-source-3.3.4/where.c Log: Import http://www.sqlite.org/sqlite-source-3_3_4.zip Added: external/sqlite-source-3.3.4/alter.c ============================================================================== --- (empty file) +++ external/sqlite-source-3.3.4/alter.c Mon Apr 3 07:54:59 2006 @@ -0,0 +1,562 @@ +/* +** 2005 February 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains C code routines that used to generate VDBE code +** that implements the ALTER TABLE command. +** +** $Id: alter.c,v 1.20 2006/02/09 02:56:03 drh Exp $ +*/ +#include "sqliteInt.h" +#include + +/* +** The code in this file only exists if we are not omitting the +** ALTER TABLE logic from the build. +*/ +#ifndef SQLITE_OMIT_ALTERTABLE + + +/* +** This function is used by SQL generated to implement the +** ALTER TABLE command. The first argument is the text of a CREATE TABLE or +** CREATE INDEX command. The second is a table name. The table name in +** the CREATE TABLE or CREATE INDEX statement is replaced with the second +** argument and the result returned. Examples: +** +** sqlite_rename_table('CREATE TABLE abc(a, b, c)', 'def') +** -> 'CREATE TABLE def(a, b, c)' +** +** sqlite_rename_table('CREATE INDEX i ON abc(a)', 'def') +** -> 'CREATE INDEX i ON def(a, b, c)' +*/ +static void renameTableFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + unsigned char const *zSql = sqlite3_value_text(argv[0]); + unsigned char const *zTableName = sqlite3_value_text(argv[1]); + + int token; + Token tname; + unsigned char const *zCsr = zSql; + int len = 0; + char *zRet; + + /* The principle used to locate the table name in the CREATE TABLE + ** statement is that the table name is the first token that is immediatedly + ** followed by a left parenthesis - TK_LP. + */ + if( zSql ){ + do { + /* Store the token that zCsr points to in tname. */ + tname.z = zCsr; + tname.n = len; + + /* Advance zCsr to the next token. Store that token type in 'token', + ** and it's length in 'len' (to be used next iteration of this loop). + */ + do { + zCsr += len; + len = sqlite3GetToken(zCsr, &token); + } while( token==TK_SPACE ); + assert( len>0 ); + } while( token!=TK_LP ); + + zRet = sqlite3MPrintf("%.*s%Q%s", tname.z - zSql, zSql, + zTableName, tname.z+tname.n); + sqlite3_result_text(context, zRet, -1, sqlite3FreeX); + } +} + +#ifndef SQLITE_OMIT_TRIGGER +/* This function is used by SQL generated to implement the ALTER TABLE +** ALTER TABLE command. The first argument is the text of a CREATE TRIGGER +** statement. The second is a table name. The table name in the CREATE +** TRIGGER statement is replaced with the second argument and the result +** returned. This is analagous to renameTableFunc() above, except for CREATE +** TRIGGER, not CREATE INDEX and CREATE TABLE. +*/ +static void renameTriggerFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + unsigned char const *zSql = sqlite3_value_text(argv[0]); + unsigned char const *zTableName = sqlite3_value_text(argv[1]); + + int token; + Token tname; + int dist = 3; + unsigned char const *zCsr = zSql; + int len = 0; + char *zRet; + + /* The principle used to locate the table name in the CREATE TRIGGER + ** statement is that the table name is the first token that is immediatedly + ** preceded by either TK_ON or TK_DOT and immediatedly followed by one + ** of TK_WHEN, TK_BEGIN or TK_FOR. + */ + if( zSql ){ + do { + /* Store the token that zCsr points to in tname. */ + tname.z = zCsr; + tname.n = len; + + /* Advance zCsr to the next token. Store that token type in 'token', + ** and it's length in 'len' (to be used next iteration of this loop). + */ + do { + zCsr += len; + len = sqlite3GetToken(zCsr, &token); + }while( token==TK_SPACE ); + assert( len>0 ); + + /* Variable 'dist' stores the number of tokens read since the most + ** recent TK_DOT or TK_ON. This means that when a WHEN, FOR or BEGIN + ** token is read and 'dist' equals 2, the condition stated above + ** to be met. + ** + ** Note that ON cannot be a database, table or column name, so + ** there is no need to worry about syntax like + ** "CREATE TRIGGER ... ON ON.ON BEGIN ..." etc. + */ + dist++; + if( token==TK_DOT || token==TK_ON ){ + dist = 0; + } + } while( dist!=2 || (token!=TK_WHEN && token!=TK_FOR && token!=TK_BEGIN) ); + + /* Variable tname now contains the token that is the old table-name + ** in the CREATE TRIGGER statement. + */ + zRet = sqlite3MPrintf("%.*s%Q%s", tname.z - zSql, zSql, + zTableName, tname.z+tname.n); + sqlite3_result_text(context, zRet, -1, sqlite3FreeX); + } +} +#endif /* !SQLITE_OMIT_TRIGGER */ + +/* +** Register built-in functions used to help implement ALTER TABLE +*/ +void sqlite3AlterFunctions(sqlite3 *db){ + static const struct { + char *zName; + signed char nArg; + void (*xFunc)(sqlite3_context*,int,sqlite3_value **); + } aFuncs[] = { + { "sqlite_rename_table", 2, renameTableFunc}, +#ifndef SQLITE_OMIT_TRIGGER + { "sqlite_rename_trigger", 2, renameTriggerFunc}, +#endif + }; + int i; + + for(i=0; idb->aDb[1].pSchema; /* Temp db schema */ + + /* If the table is not located in the temp-db (in which case NULL is + ** returned, loop through the tables list of triggers. For each trigger + ** that is not part of the temp-db schema, add a clause to the WHERE + ** expression being built up in zWhere. + */ + if( pTab->pSchema!=pTempSchema ){ + for( pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext ){ + if( pTrig->pSchema==pTempSchema ){ + if( !zWhere ){ + zWhere = sqlite3MPrintf("name=%Q", pTrig->name); + }else{ + tmp = zWhere; + zWhere = sqlite3MPrintf("%s OR name=%Q", zWhere, pTrig->name); + sqliteFree(tmp); + } + } + } + } + return zWhere; +} + +/* +** Generate code to drop and reload the internal representation of table +** pTab from the database, including triggers and temporary triggers. +** Argument zName is the name of the table in the database schema at +** the time the generated code is executed. This can be different from +** pTab->zName if this function is being called to code part of an +** "ALTER TABLE RENAME TO" statement. +*/ +static void reloadTableSchema(Parse *pParse, Table *pTab, const char *zName){ + Vdbe *v; + char *zWhere; + int iDb; /* Index of database containing pTab */ +#ifndef SQLITE_OMIT_TRIGGER + Trigger *pTrig; +#endif + + v = sqlite3GetVdbe(pParse); + if( !v ) return; + iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); + assert( iDb>=0 ); + +#ifndef SQLITE_OMIT_TRIGGER + /* Drop any table triggers from the internal schema. */ + for(pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext){ + int iTrigDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema); + assert( iTrigDb==iDb || iTrigDb==1 ); + sqlite3VdbeOp3(v, OP_DropTrigger, iTrigDb, 0, pTrig->name, 0); + } +#endif + + /* Drop the table and index from the internal schema */ + sqlite3VdbeOp3(v, OP_DropTable, iDb, 0, pTab->zName, 0); + + /* Reload the table, index and permanent trigger schemas. */ + zWhere = sqlite3MPrintf("tbl_name=%Q", zName); + if( !zWhere ) return; + sqlite3VdbeOp3(v, OP_ParseSchema, iDb, 0, zWhere, P3_DYNAMIC); + +#ifndef SQLITE_OMIT_TRIGGER + /* Now, if the table is not stored in the temp database, reload any temp + ** triggers. Don't use IN(...) in case SQLITE_OMIT_SUBQUERY is defined. + */ + if( (zWhere=whereTempTriggers(pParse, pTab))!=0 ){ + sqlite3VdbeOp3(v, OP_ParseSchema, 1, 0, zWhere, P3_DYNAMIC); + } +#endif +} + +/* +** Generate code to implement the "ALTER TABLE xxx RENAME TO yyy" +** command. +*/ +void sqlite3AlterRenameTable( + Parse *pParse, /* Parser context. */ + SrcList *pSrc, /* The table to rename. */ + Token *pName /* The new table name. */ +){ + int iDb; /* Database that contains the table */ + char *zDb; /* Name of database iDb */ + Table *pTab; /* Table being renamed */ + char *zName = 0; /* NULL-terminated version of pName */ + sqlite3 *db = pParse->db; /* Database connection */ + Vdbe *v; +#ifndef SQLITE_OMIT_TRIGGER + char *zWhere = 0; /* Where clause to locate temp triggers */ +#endif + + if( sqlite3MallocFailed() ) goto exit_rename_table; + assert( pSrc->nSrc==1 ); + + pTab = sqlite3LocateTable(pParse, pSrc->a[0].zName, pSrc->a[0].zDatabase); + if( !pTab ) goto exit_rename_table; + iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); + zDb = db->aDb[iDb].zName; + + /* Get a NULL terminated version of the new table name. */ + zName = sqlite3NameFromToken(pName); + if( !zName ) goto exit_rename_table; + + /* Check that a table or index named 'zName' does not already exist + ** in database iDb. If so, this is an error. + */ + if( sqlite3FindTable(db, zName, zDb) || sqlite3FindIndex(db, zName, zDb) ){ + sqlite3ErrorMsg(pParse, + "there is already another table or index with this name: %s", zName); + goto exit_rename_table; + } + + /* Make sure it is not a system table being altered, or a reserved name + ** that the table is being renamed to. + */ + if( strlen(pTab->zName)>6 && 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7) ){ + sqlite3ErrorMsg(pParse, "table %s may not be altered", pTab->zName); + goto exit_rename_table; + } + if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ + goto exit_rename_table; + } + +#ifndef SQLITE_OMIT_AUTHORIZATION + /* Invoke the authorization callback. */ + if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, 0) ){ + goto exit_rename_table; + } +#endif + + /* Begin a transaction and code the VerifyCookie for database iDb. + ** Then modify the schema cookie (since the ALTER TABLE modifies the + ** schema). + */ + v = sqlite3GetVdbe(pParse); + if( v==0 ){ + goto exit_rename_table; + } + sqlite3BeginWriteOperation(pParse, 0, iDb); + sqlite3ChangeCookie(db, v, iDb); + + /* Modify the sqlite_master table to use the new table name. */ + sqlite3NestedParse(pParse, + "UPDATE %Q.%s SET " +#ifdef SQLITE_OMIT_TRIGGER + "sql = sqlite_rename_table(sql, %Q), " +#else + "sql = CASE " + "WHEN type = 'trigger' THEN sqlite_rename_trigger(sql, %Q)" + "ELSE sqlite_rename_table(sql, %Q) END, " +#endif + "tbl_name = %Q, " + "name = CASE " + "WHEN type='table' THEN %Q " + "WHEN name LIKE 'sqlite_autoindex%%' AND type='index' THEN " + "'sqlite_autoindex_' || %Q || substr(name, %d+18,10) " + "ELSE name END " + "WHERE tbl_name=%Q AND " + "(type='table' OR type='index' OR type='trigger');", + zDb, SCHEMA_TABLE(iDb), zName, zName, zName, +#ifndef SQLITE_OMIT_TRIGGER + zName, +#endif + zName, strlen(pTab->zName), pTab->zName + ); + +#ifndef SQLITE_OMIT_AUTOINCREMENT + /* If the sqlite_sequence table exists in this database, then update + ** it with the new table name. + */ + if( sqlite3FindTable(db, "sqlite_sequence", zDb) ){ + sqlite3NestedParse(pParse, + "UPDATE %Q.sqlite_sequence set name = %Q WHERE name = %Q", + zDb, zName, pTab->zName); + } +#endif + +#ifndef SQLITE_OMIT_TRIGGER + /* If there are TEMP triggers on this table, modify the sqlite_temp_master + ** table. Don't do this if the table being ALTERed is itself located in + ** the temp database. + */ + if( (zWhere=whereTempTriggers(pParse, pTab))!=0 ){ + sqlite3NestedParse(pParse, + "UPDATE sqlite_temp_master SET " + "sql = sqlite_rename_trigger(sql, %Q), " + "tbl_name = %Q " + "WHERE %s;", zName, zName, zWhere); + sqliteFree(zWhere); + } +#endif + + /* Drop and reload the internal table schema. */ + reloadTableSchema(pParse, pTab, zName); + +exit_rename_table: + sqlite3SrcListDelete(pSrc); + sqliteFree(zName); +} + + +/* +** This function is called after an "ALTER TABLE ... ADD" statement +** has been parsed. Argument pColDef contains the text of the new +** column definition. +** +** The Table structure pParse->pNewTable was extended to include +** the new column during parsing. +*/ +void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ + Table *pNew; /* Copy of pParse->pNewTable */ + Table *pTab; /* Table being altered */ + int iDb; /* Database number */ + const char *zDb; /* Database name */ + const char *zTab; /* Table name */ + char *zCol; /* Null-terminated column definition */ + Column *pCol; /* The new column */ + Expr *pDflt; /* Default value for the new column */ + + if( pParse->nErr ) return; + pNew = pParse->pNewTable; + assert( pNew ); + + iDb = sqlite3SchemaToIndex(pParse->db, pNew->pSchema); + zDb = pParse->db->aDb[iDb].zName; + zTab = pNew->zName; + pCol = &pNew->aCol[pNew->nCol-1]; + pDflt = pCol->pDflt; + pTab = sqlite3FindTable(pParse->db, zTab, zDb); + assert( pTab ); + +#ifndef SQLITE_OMIT_AUTHORIZATION + /* Invoke the authorization callback. */ + if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, 0) ){ + return; + } +#endif + + /* If the default value for the new column was specified with a + ** literal NULL, then set pDflt to 0. This simplifies checking + ** for an SQL NULL default below. + */ + if( pDflt && pDflt->op==TK_NULL ){ + pDflt = 0; + } + + /* Check that the new column is not specified as PRIMARY KEY or UNIQUE. + ** If there is a NOT NULL constraint, then the default value for the + ** column must not be NULL. + */ + if( pCol->isPrimKey ){ + sqlite3ErrorMsg(pParse, "Cannot add a PRIMARY KEY column"); + return; + } + if( pNew->pIndex ){ + sqlite3ErrorMsg(pParse, "Cannot add a UNIQUE column"); + return; + } + if( pCol->notNull && !pDflt ){ + sqlite3ErrorMsg(pParse, + "Cannot add a NOT NULL column with default value NULL"); + return; + } + + /* Ensure the default expression is something that sqlite3ValueFromExpr() + ** can handle (i.e. not CURRENT_TIME etc.) + */ + if( pDflt ){ + sqlite3_value *pVal; + if( sqlite3ValueFromExpr(pDflt, SQLITE_UTF8, SQLITE_AFF_NONE, &pVal) ){ + /* malloc() has failed */ + return; + } + if( !pVal ){ + sqlite3ErrorMsg(pParse, "Cannot add a column with non-constant default"); + return; + } + sqlite3ValueFree(pVal); + } + + /* Modify the CREATE TABLE statement. */ + zCol = sqliteStrNDup((char*)pColDef->z, pColDef->n); + if( zCol ){ + char *zEnd = &zCol[pColDef->n-1]; + while( (zEnd>zCol && *zEnd==';') || isspace(*(unsigned char *)zEnd) ){ + *zEnd-- = '\0'; + } + sqlite3NestedParse(pParse, + "UPDATE %Q.%s SET " + "sql = substr(sql,1,%d) || ', ' || %Q || substr(sql,%d,length(sql)) " + "WHERE type = 'table' AND name = %Q", + zDb, SCHEMA_TABLE(iDb), pNew->addColOffset, zCol, pNew->addColOffset+1, + zTab + ); + sqliteFree(zCol); + } + + /* If the default value of the new column is NULL, then set the file + ** format to 2. If the default value of the new column is not NULL, + ** the file format becomes 3. + */ + sqlite3MinimumFileFormat(pParse, iDb, pDflt ? 3 : 2); + + /* Reload the schema of the modified table. */ + reloadTableSchema(pParse, pTab, pTab->zName); +} + +/* +** This function is called by the parser after the table-name in +** an "ALTER TABLE ADD" statement is parsed. Argument +** pSrc is the full-name of the table being altered. +** +** This routine makes a (partial) copy of the Table structure +** for the table being altered and sets Parse.pNewTable to point +** to it. Routines called by the parser as the column definition +** is parsed (i.e. sqlite3AddColumn()) add the new Column data to +** the copy. The copy of the Table structure is deleted by tokenize.c +** after parsing is finished. +** +** Routine sqlite3AlterFinishAddColumn() will be called to complete +** coding the "ALTER TABLE ... ADD" statement. +*/ +void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){ + Table *pNew; + Table *pTab; + Vdbe *v; + int iDb; + int i; + int nAlloc; + + /* Look up the table being altered. */ + assert( pParse->pNewTable==0 ); + if( sqlite3MallocFailed() ) goto exit_begin_add_column; + pTab = sqlite3LocateTable(pParse, pSrc->a[0].zName, pSrc->a[0].zDatabase); + if( !pTab ) goto exit_begin_add_column; + + /* Make sure this is not an attempt to ALTER a view. */ + if( pTab->pSelect ){ + sqlite3ErrorMsg(pParse, "Cannot add a column to a view"); + goto exit_begin_add_column; + } + + assert( pTab->addColOffset>0 ); + iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); + + /* Put a copy of the Table struct in Parse.pNewTable for the + ** sqlite3AddColumn() function and friends to modify. + */ + pNew = (Table *)sqliteMalloc(sizeof(Table)); + if( !pNew ) goto exit_begin_add_column; + pParse->pNewTable = pNew; + pNew->nRef = 1; + pNew->nCol = pTab->nCol; + assert( pNew->nCol>0 ); + nAlloc = (((pNew->nCol-1)/8)*8)+8; + assert( nAlloc>=pNew->nCol && nAlloc%8==0 && nAlloc-pNew->nCol<8 ); + pNew->aCol = (Column *)sqliteMalloc(sizeof(Column)*nAlloc); + pNew->zName = sqliteStrDup(pTab->zName); + if( !pNew->aCol || !pNew->zName ){ + goto exit_begin_add_column; + } + memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*pNew->nCol); + for(i=0; inCol; i++){ + Column *pCol = &pNew->aCol[i]; + pCol->zName = sqliteStrDup(pCol->zName); + pCol->zColl = 0; + pCol->zType = 0; + pCol->pDflt = 0; + } + pNew->pSchema = pParse->db->aDb[iDb].pSchema; + pNew->addColOffset = pTab->addColOffset; + pNew->nRef = 1; + + /* Begin a transaction and increment the schema cookie. */ + sqlite3BeginWriteOperation(pParse, 0, iDb); + v = sqlite3GetVdbe(pParse); + if( !v ) goto exit_begin_add_column; + sqlite3ChangeCookie(pParse->db, v, iDb); + +exit_begin_add_column: + sqlite3SrcListDelete(pSrc); + return; +} +#endif /* SQLITE_ALTER_TABLE */ Added: external/sqlite-source-3.3.4/analyze.c ============================================================================== --- (empty file) +++ external/sqlite-source-3.3.4/analyze.c Mon Apr 3 07:54:59 2006 @@ -0,0 +1,403 @@ +/* +** 2005 July 8 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains code associated with the ANALYZE command. +** +** @(#) $Id: analyze.c,v 1.16 2006/01/10 17:58:23 danielk1977 Exp $ +*/ +#ifndef SQLITE_OMIT_ANALYZE +#include "sqliteInt.h" + +/* +** This routine generates code that opens the sqlite_stat1 table on cursor +** iStatCur. +** +** If the sqlite_stat1 tables does not previously exist, it is created. +** If it does previously exist, all entires associated with table zWhere +** are removed. If zWhere==0 then all entries are removed. +*/ +static void openStatTable( + Parse *pParse, /* Parsing context */ + int iDb, /* The database we are looking in */ + int iStatCur, /* Open the sqlite_stat1 table on this cursor */ + const char *zWhere /* Delete entries associated with this table */ +){ + sqlite3 *db = pParse->db; + Db *pDb; + int iRootPage; + Table *pStat; + Vdbe *v = sqlite3GetVdbe(pParse); + + pDb = &db->aDb[iDb]; + if( (pStat = sqlite3FindTable(db, "sqlite_stat1", pDb->zName))==0 ){ + /* The sqlite_stat1 tables does not exist. Create it. + ** Note that a side-effect of the CREATE TABLE statement is to leave + ** the rootpage of the new table on the top of the stack. This is + ** important because the OpenWrite opcode below will be needing it. */ + sqlite3NestedParse(pParse, + "CREATE TABLE %Q.sqlite_stat1(tbl,idx,stat)", + pDb->zName + ); + iRootPage = 0; /* Cause rootpage to be taken from top of stack */ + }else if( zWhere ){ + /* The sqlite_stat1 table exists. Delete all entries associated with + ** the table zWhere. */ + sqlite3NestedParse(pParse, + "DELETE FROM %Q.sqlite_stat1 WHERE tbl=%Q", + pDb->zName, zWhere + ); + iRootPage = pStat->tnum; + }else{ + /* The sqlite_stat1 table already exists. Delete all rows. */ + iRootPage = pStat->tnum; + sqlite3VdbeAddOp(v, OP_Clear, pStat->tnum, iDb); + } + + /* Open the sqlite_stat1 table for writing. Unless it was created + ** by this vdbe program, lock it for writing at the shared-cache level. + ** If this vdbe did create the sqlite_stat1 table, then it must have + ** already obtained a schema-lock, making the write-lock redundant. + */ + if( iRootPage>0 ){ + sqlite3TableLock(pParse, iDb, iRootPage, 1, "sqlite_stat1"); + } + sqlite3VdbeAddOp(v, OP_Integer, iDb, 0); + sqlite3VdbeAddOp(v, OP_OpenWrite, iStatCur, iRootPage); + sqlite3VdbeAddOp(v, OP_SetNumColumns, iStatCur, 3); +} + +/* +** Generate code to do an analysis of all indices associated with +** a single table. +*/ +static void analyzeOneTable( + Parse *pParse, /* Parser context */ + Table *pTab, /* Table whose indices are to be analyzed */ + int iStatCur, /* Cursor that writes to the sqlite_stat1 table */ + int iMem /* Available memory locations begin here */ +){ + Index *pIdx; /* An index to being analyzed */ + int iIdxCur; /* Cursor number for index being analyzed */ + int nCol; /* Number of columns in the index */ + Vdbe *v; /* The virtual machine being built up */ + int i; /* Loop counter */ + int topOfLoop; /* The top of the loop */ + int endOfLoop; /* The end of the loop */ + int addr; /* The address of an instruction */ + int iDb; /* Index of database containing pTab */ + + v = sqlite3GetVdbe(pParse); + if( pTab==0 || pTab->pIndex==0 ){ + /* Do no analysis for tables that have no indices */ + return; + } + + iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); + assert( iDb>=0 ); +#ifndef SQLITE_OMIT_AUTHORIZATION + if( sqlite3AuthCheck(pParse, SQLITE_ANALYZE, pTab->zName, 0, + pParse->db->aDb[iDb].zName ) ){ + return; + } +#endif + + /* Establish a read-lock on the table at the shared-cache level. */ + sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); + + iIdxCur = pParse->nTab; + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); + + /* Open a cursor to the index to be analyzed + */ + assert( iDb==sqlite3SchemaToIndex(pParse->db, pIdx->pSchema) ); + sqlite3VdbeAddOp(v, OP_Integer, iDb, 0); + VdbeComment((v, "# %s", pIdx->zName)); + sqlite3VdbeOp3(v, OP_OpenRead, iIdxCur, pIdx->tnum, + (char *)pKey, P3_KEYINFO_HANDOFF); + nCol = pIdx->nColumn; + if( iMem+nCol*2>=pParse->nMem ){ + pParse->nMem = iMem+nCol*2+1; + } + sqlite3VdbeAddOp(v, OP_SetNumColumns, iIdxCur, nCol+1); + + /* Memory cells are used as follows: + ** + ** mem[iMem]: The total number of rows in the table. + ** mem[iMem+1]: Number of distinct values in column 1 + ** ... + ** mem[iMem+nCol]: Number of distinct values in column N + ** mem[iMem+nCol+1] Last observed value of column 1 + ** ... + ** mem[iMem+nCol+nCol]: Last observed value of column N + ** + ** Cells iMem through iMem+nCol are initialized to 0. The others + ** are initialized to NULL. + */ + for(i=0; i<=nCol; i++){ + sqlite3VdbeAddOp(v, OP_MemInt, 0, iMem+i); + } + for(i=0; i0 then it is always the case the D>0 so division by zero + ** is never possible. + */ + sqlite3VdbeAddOp(v, OP_MemLoad, iMem, 0); + addr = sqlite3VdbeAddOp(v, OP_IfNot, 0, 0); + sqlite3VdbeAddOp(v, OP_NewRowid, iStatCur, 0); + sqlite3VdbeOp3(v, OP_String8, 0, 0, pTab->zName, 0); + sqlite3VdbeOp3(v, OP_String8, 0, 0, pIdx->zName, 0); + sqlite3VdbeAddOp(v, OP_MemLoad, iMem, 0); + sqlite3VdbeOp3(v, OP_String8, 0, 0, " ", 0); + for(i=0; idb; + Schema *pSchema = db->aDb[iDb].pSchema; /* Schema of database iDb */ + HashElem *k; + int iStatCur; + int iMem; + + sqlite3BeginWriteOperation(pParse, 0, iDb); + iStatCur = pParse->nTab++; + openStatTable(pParse, iDb, iStatCur, 0); + iMem = pParse->nMem; + for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ + Table *pTab = (Table*)sqliteHashData(k); + analyzeOneTable(pParse, pTab, iStatCur, iMem); + } + loadAnalysis(pParse, iDb); +} + +/* +** Generate code that will do an analysis of a single table in +** a database. +*/ +static void analyzeTable(Parse *pParse, Table *pTab){ + int iDb; + int iStatCur; + + assert( pTab!=0 ); + iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); + sqlite3BeginWriteOperation(pParse, 0, iDb); + iStatCur = pParse->nTab++; + openStatTable(pParse, iDb, iStatCur, pTab->zName); + analyzeOneTable(pParse, pTab, iStatCur, pParse->nMem); + loadAnalysis(pParse, iDb); +} + +/* +** Generate code for the ANALYZE command. The parser calls this routine +** when it recognizes an ANALYZE command. +** +** ANALYZE -- 1 +** ANALYZE -- 2 +** ANALYZE ?.? -- 3 +** +** Form 1 causes all indices in all attached databases to be analyzed. +** Form 2 analyzes all indices the single database named. +** Form 3 analyzes all indices associated with the named table. +*/ +void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){ + sqlite3 *db = pParse->db; + int iDb; + int i; + char *z, *zDb; + Table *pTab; + Token *pTableName; + + /* Read the database schema. If an error occurs, leave an error message + ** and code in pParse and return NULL. */ + if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ + return; + } + + if( pName1==0 ){ + /* Form 1: Analyze everything */ + for(i=0; inDb; i++){ + if( i==1 ) continue; /* Do not analyze the TEMP database */ + analyzeDatabase(pParse, i); + } + }else if( pName2==0 || pName2->n==0 ){ + /* Form 2: Analyze the database or table named */ + iDb = sqlite3FindDb(db, pName1); + if( iDb>=0 ){ + analyzeDatabase(pParse, iDb); + }else{ + z = sqlite3NameFromToken(pName1); + pTab = sqlite3LocateTable(pParse, z, 0); + sqliteFree(z); + if( pTab ){ + analyzeTable(pParse, pTab); + } + } + }else{ + /* Form 3: Analyze the fully qualified table name */ + iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pTableName); + if( iDb>=0 ){ + zDb = db->aDb[iDb].zName; + z = sqlite3NameFromToken(pTableName); + pTab = sqlite3LocateTable(pParse, z, zDb); + sqliteFree(z); + if( pTab ){ + analyzeTable(pParse, pTab); + } + } + } +} + +/* +** Used to pass information from the analyzer reader through to the +** callback routine. +*/ +typedef struct analysisInfo analysisInfo; +struct analysisInfo { + sqlite3 *db; + const char *zDatabase; +}; + +/* +** This callback is invoked once for each index when reading the +** sqlite_stat1 table. +** +** argv[0] = name of the index +** argv[1] = results of analysis - on integer for each column +*/ +static int analysisLoader(void *pData, int argc, char **argv, char **azNotUsed){ + analysisInfo *pInfo = (analysisInfo*)pData; + Index *pIndex; + int i, c; + unsigned int v; + const char *z; + + assert( argc==2 ); + if( argv==0 || argv[0]==0 || argv[1]==0 ){ + return 0; + } + pIndex = sqlite3FindIndex(pInfo->db, argv[0], pInfo->zDatabase); + if( pIndex==0 ){ + return 0; + } + z = argv[1]; + for(i=0; *z && i<=pIndex->nColumn; i++){ + v = 0; + while( (c=z[0])>='0' && c<='9' ){ + v = v*10 + c - '0'; + z++; + } + pIndex->aiRowEst[i] = v; + if( *z==' ' ) z++; + } + return 0; +} + +/* +** Load the content of the sqlite_stat1 table into the index hash tables. +*/ +void sqlite3AnalysisLoad(sqlite3 *db, int iDb){ + analysisInfo sInfo; + HashElem *i; + char *zSql; + + /* Clear any prior statistics */ + for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){ + Index *pIdx = sqliteHashData(i); + sqlite3DefaultRowEst(pIdx); + } + + /* Check to make sure the sqlite_stat1 table existss */ + sInfo.db = db; + sInfo.zDatabase = db->aDb[iDb].zName; + if( sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)==0 ){ + return; + } + + + /* Load new statistics out of the sqlite_stat1 table */ + zSql = sqlite3MPrintf("SELECT idx, stat FROM %Q.sqlite_stat1", + sInfo.zDatabase); + sqlite3SafetyOff(db); + sqlite3_exec(db, zSql, analysisLoader, &sInfo, 0); + sqlite3SafetyOn(db); + sqliteFree(zSql); +} + + +#endif /* SQLITE_OMIT_ANALYZE */ Added: external/sqlite-source-3.3.4/attach.c ============================================================================== --- (empty file) +++ external/sqlite-source-3.3.4/attach.c Mon Apr 3 07:54:59 2006 @@ -0,0 +1,493 @@ +/* +** 2003 April 6 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains code used to implement the ATTACH and DETACH commands. +** +** $Id: attach.c,v 1.49 2006/01/24 12:09:18 danielk1977 Exp $ +*/ +#include "sqliteInt.h" + +/* +** Resolve an expression that was part of an ATTACH or DETACH statement. This +** is slightly different from resolving a normal SQL expression, because simple +** identifiers are treated as strings, not possible column names or aliases. +** +** i.e. if the parser sees: +** +** ATTACH DATABASE abc AS def +** +** it treats the two expressions as literal strings 'abc' and 'def' instead of +** looking for columns of the same name. +** +** This only applies to the root node of pExpr, so the statement: +** +** ATTACH DATABASE abc||def AS 'db2' +** +** will fail because neither abc or def can be resolved. +*/ +static int resolveAttachExpr(NameContext *pName, Expr *pExpr) +{ + int rc = SQLITE_OK; + if( pExpr ){ + if( pExpr->op!=TK_ID ){ + rc = sqlite3ExprResolveNames(pName, pExpr); + }else{ + pExpr->op = TK_STRING; + } + } + return rc; +} + +/* +** An SQL user-function registered to do the work of an ATTACH statement. The +** three arguments to the function come directly from an attach statement: +** +** ATTACH DATABASE x AS y KEY z +** +** SELECT sqlite_attach(x, y, z) +** +** If the optional "KEY z" syntax is omitted, an SQL NULL is passed as the +** third argument. +*/ +static void attachFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + int i; + int rc = 0; + sqlite3 *db = sqlite3_user_data(context); + const char *zName; + const char *zFile; + Db *aNew; + char zErr[128]; + char *zErrDyn = 0; + + zFile = (const char *)sqlite3_value_text(argv[0]); + zName = (const char *)sqlite3_value_text(argv[1]); + + /* Check for the following errors: + ** + ** * Too many attached databases, + ** * Transaction currently open + ** * Specified database name already being used. + */ + if( db->nDb>=MAX_ATTACHED+2 ){ + sqlite3_snprintf( + 127, zErr, "too many attached databases - max %d", MAX_ATTACHED + ); + goto attach_error; + } + if( !db->autoCommit ){ + strcpy(zErr, "cannot ATTACH database within transaction"); + goto attach_error; + } + for(i=0; inDb; i++){ + char *z = db->aDb[i].zName; + if( z && sqlite3StrICmp(z, zName)==0 ){ + sqlite3_snprintf(127, zErr, "database %s is already in use", zName); + goto attach_error; + } + } + + /* Allocate the new entry in the db->aDb[] array and initialise the schema + ** hash tables. + */ + if( db->aDb==db->aDbStatic ){ + aNew = sqliteMalloc( sizeof(db->aDb[0])*3 ); + if( aNew==0 ){ + return; + } + memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2); + }else{ + aNew = sqliteRealloc(db->aDb, sizeof(db->aDb[0])*(db->nDb+1) ); + if( aNew==0 ){ + return; + } + } + db->aDb = aNew; + aNew = &db->aDb[db->nDb++]; + memset(aNew, 0, sizeof(*aNew)); + + /* Open the database file. If the btree is successfully opened, use + ** it to obtain the database schema. At this point the schema may + ** or may not be initialised. + */ + rc = sqlite3BtreeFactory(db, zFile, 0, MAX_PAGES, &aNew->pBt); + if( rc==SQLITE_OK ){ + aNew->pSchema = sqlite3SchemaGet(aNew->pBt); + if( !aNew->pSchema ){ + rc = SQLITE_NOMEM; + }else if( aNew->pSchema->file_format && aNew->pSchema->enc!=ENC(db) ){ + strcpy(zErr, + "attached databases must use the same text encoding as main database"); + goto attach_error; + } + } + aNew->zName = sqliteStrDup(zName); + aNew->safety_level = 3; + +#if SQLITE_HAS_CODEC + { + extern int sqlite3CodecAttach(sqlite3*, int, void*, int); + extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*); + int nKey; + char *zKey; + int t = sqlite3_value_type(argv[2]); + switch( t ){ + case SQLITE_INTEGER: + case SQLITE_FLOAT: + zErrDyn = sqliteStrDup("Invalid key value"); + rc = SQLITE_ERROR; + break; + + case SQLITE_TEXT: + case SQLITE_BLOB: + nKey = sqlite3_value_bytes(argv[2]); + zKey = (char *)sqlite3_value_blob(argv[2]); + sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); + break; + + case SQLITE_NULL: + /* No key specified. Use the key from the main database */ + sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey); + sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); + break; + } + } +#endif + + /* If the file was opened successfully, read the schema for the new database. + ** If this fails, or if opening the file failed, then close the file and + ** remove the entry from the db->aDb[] array. i.e. put everything back the way + ** we found it. + */ + if( rc==SQLITE_OK ){ + sqlite3SafetyOn(db); + rc = sqlite3Init(db, &zErrDyn); + sqlite3SafetyOff(db); + } + if( rc ){ + int iDb = db->nDb - 1; + assert( iDb>=2 ); + if( db->aDb[iDb].pBt ){ + sqlite3BtreeClose(db->aDb[iDb].pBt); + db->aDb[iDb].pBt = 0; + db->aDb[iDb].pSchema = 0; + } + sqlite3ResetInternalSchema(db, 0); + db->nDb = iDb; + sqlite3_snprintf(127, zErr, "unable to open database: %s", zFile); + goto attach_error; + } + + return; + +attach_error: + /* Return an error if we get here */ + if( zErrDyn ){ + sqlite3_result_error(context, zErrDyn, -1); + sqliteFree(zErrDyn); + }else{ + zErr[sizeof(zErr)-1] = 0; + sqlite3_result_error(context, zErr, -1); + } +} + +/* +** An SQL user-function registered to do the work of an DETACH statement. The +** three arguments to the function come directly from a detach statement: +** +** DETACH DATABASE x +** +** SELECT sqlite_detach(x) +*/ +static void detachFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + const char *zName = (const char *)sqlite3_value_text(argv[0]); + sqlite3 *db = sqlite3_user_data(context); + int i; + Db *pDb = 0; + char zErr[128]; + + assert(zName); + for(i=0; inDb; i++){ + pDb = &db->aDb[i]; + if( pDb->pBt==0 ) continue; + if( sqlite3StrICmp(pDb->zName, zName)==0 ) break; + } + + if( i>=db->nDb ){ + sqlite3_snprintf(sizeof(zErr), zErr, "no such database: %s", zName); + goto detach_error; + } + if( i<2 ){ + sqlite3_snprintf(sizeof(zErr), zErr, "cannot detach database %s", zName); + goto detach_error; + } + if( !db->autoCommit ){ + strcpy(zErr, "cannot DETACH database within transaction"); + goto detach_error; + } + + sqlite3BtreeClose(pDb->pBt); + pDb->pBt = 0; + pDb->pSchema = 0; + sqlite3ResetInternalSchema(db, 0); + return; + +detach_error: + sqlite3_result_error(context, zErr, -1); +} + +/* +** This procedure generates VDBE code for a single invocation of either the +** sqlite_detach() or sqlite_attach() SQL user functions. +*/ +static void codeAttach( + Parse *pParse, /* The parser context */ + int type, /* Either SQLITE_ATTACH or SQLITE_DETACH */ + const char *zFunc, /* Either "sqlite_attach" or "sqlite_detach */ + int nFunc, /* Number of args to pass to zFunc */ + Expr *pAuthArg, /* Expression to pass to authorization callback */ + Expr *pFilename, /* Name of database file */ + Expr *pDbname, /* Name of the database to use internally */ + Expr *pKey /* Database key for encryption extension */ +){ + int rc; + NameContext sName; + Vdbe *v; + FuncDef *pFunc; + sqlite3* db = pParse->db; + +#ifndef SQLITE_OMIT_AUTHORIZATION + assert( sqlite3MallocFailed() || pAuthArg ); + if( pAuthArg ){ + char *zAuthArg = sqlite3NameFromToken(&pAuthArg->span); + if( !zAuthArg ){ + goto attach_end; + } + rc = sqlite3AuthCheck(pParse, type, zAuthArg, 0, 0); + sqliteFree(zAuthArg); + if(rc!=SQLITE_OK ){ + goto attach_end; + } + } +#endif /* SQLITE_OMIT_AUTHORIZATION */ + + memset(&sName, 0, sizeof(NameContext)); + sName.pParse = pParse; + + if( + SQLITE_OK!=(rc = resolveAttachExpr(&sName, pFilename)) || + SQLITE_OK!=(rc = resolveAttachExpr(&sName, pDbname)) || + SQLITE_OK!=(rc = resolveAttachExpr(&sName, pKey)) + ){ + pParse->nErr++; + goto attach_end; + } + + v = sqlite3GetVdbe(pParse); + sqlite3ExprCode(pParse, pFilename); + sqlite3ExprCode(pParse, pDbname); + sqlite3ExprCode(pParse, pKey); + + assert( v || sqlite3MallocFailed() ); + if( v ){ + sqlite3VdbeAddOp(v, OP_Function, 0, nFunc); + pFunc = sqlite3FindFunction(db, zFunc, strlen(zFunc), nFunc, SQLITE_UTF8,0); + sqlite3VdbeChangeP3(v, -1, (char *)pFunc, P3_FUNCDEF); + + /* Code an OP_Expire. For an ATTACH statement, set P1 to true (expire this + ** statement only). For DETACH, set it to false (expire all existing + ** statements). + */ + sqlite3VdbeAddOp(v, OP_Expire, (type==SQLITE_ATTACH), 0); + } + +attach_end: + sqlite3ExprDelete(pFilename); + sqlite3ExprDelete(pDbname); + sqlite3ExprDelete(pKey); +} + +/* +** Called by the parser to compile a DETACH statement. +** +** DETACH pDbname +*/ +void sqlite3Detach(Parse *pParse, Expr *pDbname){ + codeAttach(pParse, SQLITE_DETACH, "sqlite_detach", 1, pDbname, 0, 0, pDbname); +} + +/* +** Called by the parser to compile an ATTACH statement. +** +** ATTACH p AS pDbname KEY pKey +*/ +void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *pKey){ + codeAttach(pParse, SQLITE_ATTACH, "sqlite_attach", 3, p, p, pDbname, pKey); +} + +/* +** Register the functions sqlite_attach and sqlite_detach. +*/ +void sqlite3AttachFunctions(sqlite3 *db){ + static const int enc = SQLITE_UTF8; + sqlite3CreateFunc(db, "sqlite_attach", 3, enc, db, attachFunc, 0, 0); + sqlite3CreateFunc(db, "sqlite_detach", 1, enc, db, detachFunc, 0, 0); +} + +/* +** Initialize a DbFixer structure. This routine must be called prior +** to passing the structure to one of the sqliteFixAAAA() routines below. +** +** The return value indicates whether or not fixation is required. TRUE +** means we do need to fix the database references, FALSE means we do not. +*/ +int sqlite3FixInit( + DbFixer *pFix, /* The fixer to be initialized */ + Parse *pParse, /* Error messages will be written here */ + int iDb, /* This is the database that must be used */ + const char *zType, /* "view", "trigger", or "index" */ + const Token *pName /* Name of the view, trigger, or index */ +){ + sqlite3 *db; + + if( iDb<0 || iDb==1 ) return 0; + db = pParse->db; + assert( db->nDb>iDb ); + pFix->pParse = pParse; + pFix->zDb = db->aDb[iDb].zName; + pFix->zType = zType; + pFix->pName = pName; + return 1; +} + +/* +** The following set of routines walk through the parse tree and assign +** a specific database to all table references where the database name +** was left unspecified in the original SQL statement. The pFix structure +** must have been initialized by a prior call to sqlite3FixInit(). +** +** These routines are used to make sure that an index, trigger, or +** view in one database does not refer to objects in a different database. +** (Exception: indices, triggers, and views in the TEMP database are +** allowed to refer to anything.) If a reference is explicitly made +** to an object in a different database, an error message is added to +** pParse->zErrMsg and these routines return non-zero. If everything +** checks out, these routines return 0. +*/ +int sqlite3FixSrcList( + DbFixer *pFix, /* Context of the fixation */ + SrcList *pList /* The Source list to check and modify */ +){ + int i; + const char *zDb; + struct SrcList_item *pItem; + + if( pList==0 ) return 0; + zDb = pFix->zDb; + for(i=0, pItem=pList->a; inSrc; i++, pItem++){ + if( pItem->zDatabase==0 ){ + pItem->zDatabase = sqliteStrDup(zDb); + }else if( sqlite3StrICmp(pItem->zDatabase,zDb)!=0 ){ + sqlite3ErrorMsg(pFix->pParse, + "%s %T cannot reference objects in database %s", + pFix->zType, pFix->pName, pItem->zDatabase); + return 1; + } +#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) + if( sqlite3FixSelect(pFix, pItem->pSelect) ) return 1; + if( sqlite3FixExpr(pFix, pItem->pOn) ) return 1; +#endif + } + return 0; +} +#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) +int sqlite3FixSelect( + DbFixer *pFix, /* Context of the fixation */ + Select *pSelect /* The SELECT statement to be fixed to one database */ +){ + while( pSelect ){ + if( sqlite3FixExprList(pFix, pSelect->pEList) ){ + return 1; + } + if( sqlite3FixSrcList(pFix, pSelect->pSrc) ){ + return 1; + } + if( sqlite3FixExpr(pFix, pSelect->pWhere) ){ + return 1; + } + if( sqlite3FixExpr(pFix, pSelect->pHaving) ){ + return 1; + } + pSelect = pSelect->pPrior; + } + return 0; +} +int sqlite3FixExpr( + DbFixer *pFix, /* Context of the fixation */ + Expr *pExpr /* The expression to be fixed to one database */ +){ + while( pExpr ){ + if( sqlite3FixSelect(pFix, pExpr->pSelect) ){ + return 1; + } + if( sqlite3FixExprList(pFix, pExpr->pList) ){ + return 1; + } + if( sqlite3FixExpr(pFix, pExpr->pRight) ){ + return 1; + } + pExpr = pExpr->pLeft; + } + return 0; +} +int sqlite3FixExprList( + DbFixer *pFix, /* Context of the fixation */ + ExprList *pList /* The expression to be fixed to one database */ +){ + int i; + struct ExprList_item *pItem; + if( pList==0 ) return 0; + for(i=0, pItem=pList->a; inExpr; i++, pItem++){ + if( sqlite3FixExpr(pFix, pItem->pExpr) ){ + return 1; + } + } + return 0; +} +#endif + +#ifndef SQLITE_OMIT_TRIGGER +int sqlite3FixTriggerStep( + DbFixer *pFix, /* Context of the fixation */ + TriggerStep *pStep /* The trigger step be fixed to one database */ +){ + while( pStep ){ + if( sqlite3FixSelect(pFix, pStep->pSelect) ){ + return 1; + } + if( sqlite3FixExpr(pFix, pStep->pWhere) ){ + return 1; + } + if( sqlite3FixExprList(pFix, pStep->pExprList) ){ + return 1; + } + pStep = pStep->pNext; + } + return 0; +} +#endif Added: external/sqlite-source-3.3.4/auth.c ============================================================================== --- (empty file) +++ external/sqlite-source-3.3.4/auth.c Mon Apr 3 07:54:59 2006 @@ -0,0 +1,232 @@ +/* +** 2003 January 11 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains code used to implement the sqlite3_set_authorizer() +** API. This facility is an optional feature of the library. Embedded +** systems that do not need this facility may omit it by recompiling +** the library with -DSQLITE_OMIT_AUTHORIZATION=1 +** +** $Id: auth.c,v 1.24 2006/01/13 13:55:45 drh Exp $ +*/ +#include "sqliteInt.h" + +/* +** All of the code in this file may be omitted by defining a single +** macro. +*/ +#ifndef SQLITE_OMIT_AUTHORIZATION + +/* +** Set or clear the access authorization function. +** +** The access authorization function is be called during the compilation +** phase to verify that the user has read and/or write access permission on +** various fields of the database. The first argument to the auth function +** is a copy of the 3rd argument to this routine. The second argument +** to the auth function is one of these constants: +** +** SQLITE_CREATE_INDEX +** SQLITE_CREATE_TABLE +** SQLITE_CREATE_TEMP_INDEX +** SQLITE_CREATE_TEMP_TABLE +** SQLITE_CREATE_TEMP_TRIGGER +** SQLITE_CREATE_TEMP_VIEW +** SQLITE_CREATE_TRIGGER +** SQLITE_CREATE_VIEW +** SQLITE_DELETE +** SQLITE_DROP_INDEX +** SQLITE_DROP_TABLE +** SQLITE_DROP_TEMP_INDEX +** SQLITE_DROP_TEMP_TABLE +** SQLITE_DROP_TEMP_TRIGGER +** SQLITE_DROP_TEMP_VIEW +** SQLITE_DROP_TRIGGER +** SQLITE_DROP_VIEW +** SQLITE_INSERT +** SQLITE_PRAGMA +** SQLITE_READ +** SQLITE_SELECT +** SQLITE_TRANSACTION +** SQLITE_UPDATE +** +** The third and fourth arguments to the auth function are the name of +** the table and the column that are being accessed. The auth function +** should return either SQLITE_OK, SQLITE_DENY, or SQLITE_IGNORE. If +** SQLITE_OK is returned, it means that access is allowed. SQLITE_DENY +** means that the SQL statement will never-run - the sqlite3_exec() call +** will return with an error. SQLITE_IGNORE means that the SQL statement +** should run but attempts to read the specified column will return NULL +** and attempts to write the column will be ignored. +** +** Setting the auth function to NULL disables this hook. The default +** setting of the auth function is NULL. +*/ +int sqlite3_set_authorizer( + sqlite3 *db, + int (*xAuth)(void*,int,const char*,const char*,const char*,const char*), + void *pArg +){ + db->xAuth = xAuth; + db->pAuthArg = pArg; + sqlite3ExpirePreparedStatements(db); + return SQLITE_OK; +} + +/* +** Write an error message into pParse->zErrMsg that explains that the +** user-supplied authorization function returned an illegal value. +*/ +static void sqliteAuthBadReturnCode(Parse *pParse, int rc){ + sqlite3ErrorMsg(pParse, "illegal return value (%d) from the " + "authorization function - should be SQLITE_OK, SQLITE_IGNORE, " + "or SQLITE_DENY", rc); + pParse->rc = SQLITE_ERROR; +} + +/* +** The pExpr should be a TK_COLUMN expression. The table referred to +** is in pTabList or else it is the NEW or OLD table of a trigger. +** Check to see if it is OK to read this particular column. +** +** If the auth function returns SQLITE_IGNORE, change the TK_COLUMN +** instruction into a TK_NULL. If the auth function returns SQLITE_DENY, +** then generate an error. +*/ +void sqlite3AuthRead( + Parse *pParse, /* The parser context */ + Expr *pExpr, /* The expression to check authorization on */ + SrcList *pTabList /* All table that pExpr might refer to */ +){ + sqlite3 *db = pParse->db; + int rc; + Table *pTab; /* The table being read */ + const char *zCol; /* Name of the column of the table */ + int iSrc; /* Index in pTabList->a[] of table being read */ + const char *zDBase; /* Name of database being accessed */ + TriggerStack *pStack; /* The stack of current triggers */ + int iDb; /* The index of the database the expression refers to */ + + if( db->xAuth==0 ) return; + if( pExpr->op==TK_AS ) return; + assert( pExpr->op==TK_COLUMN ); + iDb = sqlite3SchemaToIndex(pParse->db, pExpr->pSchema); + if( iDb<0 ){ + /* An attempt to read a column out of a subquery or other + ** temporary table. */ + return; + } + for(iSrc=0; pTabList && iSrcnSrc; iSrc++){ + if( pExpr->iTable==pTabList->a[iSrc].iCursor ) break; + } + if( iSrc>=0 && pTabList && iSrcnSrc ){ + pTab = pTabList->a[iSrc].pTab; + }else if( (pStack = pParse->trigStack)!=0 ){ + /* This must be an attempt to read the NEW or OLD pseudo-tables + ** of a trigger. + */ + assert( pExpr->iTable==pStack->newIdx || pExpr->iTable==pStack->oldIdx ); + pTab = pStack->pTab; + }else{ + return; + } + if( pTab==0 ) return; + if( pExpr->iColumn>=0 ){ + assert( pExpr->iColumnnCol ); + zCol = pTab->aCol[pExpr->iColumn].zName; + }else if( pTab->iPKey>=0 ){ + assert( pTab->iPKeynCol ); + zCol = pTab->aCol[pTab->iPKey].zName; + }else{ + zCol = "ROWID"; + } + assert( iDb>=0 && iDbnDb ); + zDBase = db->aDb[iDb].zName; + rc = db->xAuth(db->pAuthArg, SQLITE_READ, pTab->zName, zCol, zDBase, + pParse->zAuthContext); + if( rc==SQLITE_IGNORE ){ + pExpr->op = TK_NULL; + }else if( rc==SQLITE_DENY ){ + if( db->nDb>2 || iDb!=0 ){ + sqlite3ErrorMsg(pParse, "access to %s.%s.%s is prohibited", + zDBase, pTab->zName, zCol); + }else{ + sqlite3ErrorMsg(pParse, "access to %s.%s is prohibited",pTab->zName,zCol); + } + pParse->rc = SQLITE_AUTH; + }else if( rc!=SQLITE_OK ){ + sqliteAuthBadReturnCode(pParse, rc); + } +} + +/* +** Do an authorization check using the code and arguments given. Return +** either SQLITE_OK (zero) or SQLITE_IGNORE or SQLITE_DENY. If SQLITE_DENY +** is returned, then the error count and error message in pParse are +** modified appropriately. +*/ +int sqlite3AuthCheck( + Parse *pParse, + int code, + const char *zArg1, + const char *zArg2, + const char *zArg3 +){ + sqlite3 *db = pParse->db; + int rc; + + /* Don't do any authorization checks if the database is initialising. */ + if( db->init.busy ){ + return SQLITE_OK; + } + + if( db->xAuth==0 ){ + return SQLITE_OK; + } + rc = db->xAuth(db->pAuthArg, code, zArg1, zArg2, zArg3, pParse->zAuthContext); + if( rc==SQLITE_DENY ){ + sqlite3ErrorMsg(pParse, "not authorized"); + pParse->rc = SQLITE_AUTH; + }else if( rc!=SQLITE_OK && rc!=SQLITE_IGNORE ){ + rc = SQLITE_DENY; + sqliteAuthBadReturnCode(pParse, rc); + } + return rc; +} + +/* +** Push an authorization context. After this routine is called, the +** zArg3 argument to authorization callbacks will be zContext until +** popped. Or if pParse==0, this routine is a no-op. +*/ +void sqlite3AuthContextPush( + Parse *pParse, + AuthContext *pContext, + const char *zContext +){ + pContext->pParse = pParse; + if( pParse ){ + pContext->zAuthContext = pParse->zAuthContext; + pParse->zAuthContext = zContext; + } +} + +/* +** Pop an authorization context that was previously pushed +** by sqlite3AuthContextPush +*/ +void sqlite3AuthContextPop(AuthContext *pContext){ + if( pContext->pParse ){ + pContext->pParse->zAuthContext = pContext->zAuthContext; + pContext->pParse = 0; + } +} + +#endif /* SQLITE_OMIT_AUTHORIZATION */ Added: external/sqlite-source-3.3.4/btree.c ============================================================================== --- (empty file) +++ external/sqlite-source-3.3.4/btree.c Mon Apr 3 07:54:59 2006 @@ -0,0 +1,6639 @@ +/* +** 2004 April 6 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** $Id: btree.c,v 1.314 2006/02/11 01:25:51 drh Exp $ +** +** This file implements a external (disk-based) database using BTrees. +** For a detailed discussion of BTrees, refer to +** +** Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3: +** "Sorting And Searching", pages 473-480. Addison-Wesley +** Publishing Company, Reading, Massachusetts. +** +** The basic idea is that each page of the file contains N database +** entries and N+1 pointers to subpages. +** +** ---------------------------------------------------------------- +** | Ptr(0) | Key(0) | Ptr(1) | Key(1) | ... | Key(N) | Ptr(N+1) | +** ---------------------------------------------------------------- +** +** All of the keys on the page that Ptr(0) points to have values less +** than Key(0). All of the keys on page Ptr(1) and its subpages have +** values greater than Key(0) and less than Key(1). All of the keys +** on Ptr(N+1) and its subpages have values greater than Key(N). And +** so forth. +** +** Finding a particular key requires reading O(log(M)) pages from the +** disk where M is the number of entries in the tree. +** +** In this implementation, a single file can hold one or more separate +** BTrees. Each BTree is identified by the index of its root page. The +** key and data for any entry are combined to form the "payload". A +** fixed amount of payload can be carried directly on the database +** page. If the payload is larger than the preset amount then surplus +** bytes are stored on overflow pages. The payload for an entry +** and the preceding pointer are combined to form a "Cell". Each +** page has a small header which contains the Ptr(N+1) pointer and other +** information such as the size of key and data. +** +** FORMAT DETAILS +** +** The file is divided into pages. The first page is called page 1, +** the second is page 2, and so forth. A page number of zero indicates +** "no such page". The page size can be anything between 512 and 65536. +** Each page can be either a btree page, a freelist page or an overflow +** page. +** +** The first page is always a btree page. The first 100 bytes of the first +** page contain a special header (the "file header") that describes the file. +** The format of the file header is as follows: +** +** OFFSET SIZE DESCRIPTION +** 0 16 Header string: "SQLite format 3\000" +** 16 2 Page size in bytes. +** 18 1 File format write version +** 19 1 File format read version +** 20 1 Bytes of unused space at the end of each page +** 21 1 Max embedded payload fraction +** 22 1 Min embedded payload fraction +** 23 1 Min leaf payload fraction +** 24 4 File change counter +** 28 4 Reserved for future use +** 32 4 First freelist page +** 36 4 Number of freelist pages in the file +** 40 60 15 4-byte meta values passed to higher layers +** +** All of the integer values are big-endian (most significant byte first). +** +** The file change counter is incremented when the database is changed more +** than once within the same second. This counter, together with the +** modification time of the file, allows other processes to know +** when the file has changed and thus when they need to flush their +** cache. +** +** The max embedded payload fraction is the amount of the total usable +** space in a page that can be consumed by a single cell for standard +** B-tree (non-LEAFDATA) tables. A value of 255 means 100%. The default +** is to limit the maximum cell size so that at least 4 cells will fit +** on one page. Thus the default max embedded payload fraction is 64. +** +** If the payload for a cell is larger than the max payload, then extra +** payload is spilled to overflow pages. Once an overflow page is allocated, +** as many bytes as possible are moved into the overflow pages without letting +** the cell size drop below the min embedded payload fraction. +** +** The min leaf payload fraction is like the min embedded payload fraction +** except that it applies to leaf nodes in a LEAFDATA tree. The maximum +** payload fraction for a LEAFDATA tree is always 100% (or 255) and it +** not specified in the header. +** +** Each btree pages is divided into three sections: The header, the +** cell pointer array, and the cell area area. Page 1 also has a 100-byte +** file header that occurs before the page header. +** +** |----------------| +** | file header | 100 bytes. Page 1 only. +** |----------------| +** | page header | 8 bytes for leaves. 12 bytes for interior nodes +** |----------------| +** | cell pointer | | 2 bytes per cell. Sorted order. +** | array | | Grows downward +** | | v +** |----------------| +** | unallocated | +** | space | +** |----------------| ^ Grows upwards +** | cell content | | Arbitrary order interspersed with freeblocks. +** | area | | and free space fragments. +** |----------------| +** +** The page headers looks like this: +** +** OFFSET SIZE DESCRIPTION +** 0 1 Flags. 1: intkey, 2: zerodata, 4: leafdata, 8: leaf +** 1 2 byte offset to the first freeblock +** 3 2 number of cells on this page +** 5 2 first byte of the cell content area +** 7 1 number of fragmented free bytes +** 8 4 Right child (the Ptr(N+1) value). Omitted on leaves. +** +** The flags define the format of this btree page. The leaf flag means that +** this page has no children. The zerodata flag means that this page carries +** only keys and no data. The intkey flag means that the key is a integer +** which is stored in the key size entry of the cell header rather than in +** the payload area. +** +** The cell pointer array begins on the first byte after the page header. +** The cell pointer array contains zero or more 2-byte numbers which are +** offsets from the beginning of the page to the cell content in the cell +** content area. The cell pointers occur in sorted order. The system strives +** to keep free space after the last cell pointer so that new cells can +** be easily added without having to defragment the page. +** +** Cell content is stored at the very end of the page and grows toward the +** beginning of the page. +** +** Unused space within the cell content area is collected into a linked list of +** freeblocks. Each freeblock is at least 4 bytes in size. The byte offset +** to the first freeblock is given in the header. Freeblocks occur in +** increasing order. Because a freeblock must be at least 4 bytes in size, +** any group of 3 or fewer unused bytes in the cell content area cannot +** exist on the freeblock chain. A group of 3 or fewer free bytes is called +** a fragment. The total number of bytes in all fragments is recorded. +** in the page header at offset 7. +** +** SIZE DESCRIPTION +** 2 Byte offset of the next freeblock +** 2 Bytes in this freeblock +** +** Cells are of variable length. Cells are stored in the cell content area at +** the end of the page. Pointers to the cells are in the cell pointer array +** that immediately follows the page header. Cells is not necessarily +** contiguous or in order, but cell pointers are contiguous and in order. +** +** Cell content makes use of variable length integers. A variable +** length integer is 1 to 9 bytes where the lower 7 bits of each +** byte are used. The integer consists of all bytes that have bit 8 set and +** the first byte with bit 8 clear. The most significant byte of the integer +** appears first. A variable-length integer may not be more than 9 bytes long. +** As a special case, all 8 bytes of the 9th byte are used as data. This +** allows a 64-bit integer to be encoded in 9 bytes. +** +** 0x00 becomes 0x00000000 +** 0x7f becomes 0x0000007f +** 0x81 0x00 becomes 0x00000080 +** 0x82 0x00 becomes 0x00000100 +** 0x80 0x7f becomes 0x0000007f +** 0x8a 0x91 0xd1 0xac 0x78 becomes 0x12345678 +** 0x81 0x81 0x81 0x81 0x01 becomes 0x10204081 +** +** Variable length integers are used for rowids and to hold the number of +** bytes of key and data in a btree cell. +** +** The content of a cell looks like this: +** +** SIZE DESCRIPTION +** 4 Page number of the left child. Omitted if leaf flag is set. +** var Number of bytes of data. Omitted if the zerodata flag is set. +** var Number of bytes of key. Or the key itself if intkey flag is set. +** * Payload +** 4 First page of the overflow chain. Omitted if no overflow +** +** Overflow pages form a linked list. Each page except the last is completely +** filled with data (pagesize - 4 bytes). The last page can have as little +** as 1 byte of data. +** +** SIZE DESCRIPTION +** 4 Page number of next overflow page +** * Data +** +** Freelist pages come in two subtypes: trunk pages and leaf pages. The +** file header points to first in a linked list of trunk page. Each trunk +** page points to multiple leaf pages. The content of a leaf page is +** unspecified. A trunk page looks like this: +** +** SIZE DESCRIPTION +** 4 Page number of next trunk page +** 4 Number of leaf pointers on this page +** * zero or more pages numbers of leaves +*/ +#include "sqliteInt.h" +#include "pager.h" +#include "btree.h" +#include "os.h" +#include + +/* Round up a number to the next larger multiple of 8. This is used +** to force 8-byte alignment on 64-bit architectures. +*/ +#define ROUND8(x) ((x+7)&~7) + + +/* The following value is the maximum cell size assuming a maximum page +** size give above. +*/ +#define MX_CELL_SIZE(pBt) (pBt->pageSize-8) + +/* The maximum number of cells on a single page of the database. This +** assumes a minimum cell size of 3 bytes. Such small cells will be +** exceedingly rare, but they are possible. +*/ +#define MX_CELL(pBt) ((pBt->pageSize-8)/3) + +/* Forward declarations */ +typedef struct MemPage MemPage; +typedef struct BtLock BtLock; + +/* +** This is a magic string that appears at the beginning of every +** SQLite database in order to identify the file as a real database. +** +** You can change this value at compile-time by specifying a +** -DSQLITE_FILE_HEADER="..." on the compiler command-line. The +** header must be exactly 16 bytes including the zero-terminator so +** the string itself should be 15 characters long. If you change +** the header, then your custom library will not be able to read +** databases generated by the standard tools and the standard tools +** will not be able to read databases created by your custom library. +*/ +#ifndef SQLITE_FILE_HEADER /* 123456789 123456 */ +# define SQLITE_FILE_HEADER "SQLite format 3" +#endif +static const char zMagicHeader[] = SQLITE_FILE_HEADER; + +/* +** Page type flags. An ORed combination of these flags appear as the +** first byte of every BTree page. +*/ +#define PTF_INTKEY 0x01 +#define PTF_ZERODATA 0x02 +#define PTF_LEAFDATA 0x04 +#define PTF_LEAF 0x08 + +/* +** As each page of the file is loaded into memory, an instance of the following +** structure is appended and initialized to zero. This structure stores +** information about the page that is decoded from the raw file page. +** +** The pParent field points back to the parent page. This allows us to +** walk up the BTree from any leaf to the root. Care must be taken to +** unref() the parent page pointer when this page is no longer referenced. +** The pageDestructor() routine handles that chore. +*/ +struct MemPage { + u8 isInit; /* True if previously initialized. MUST BE FIRST! */ + u8 idxShift; /* True if Cell indices have changed */ + u8 nOverflow; /* Number of overflow cell bodies in aCell[] */ + u8 intKey; /* True if intkey flag is set */ + u8 leaf; /* True if leaf flag is set */ + u8 zeroData; /* True if table stores keys only */ + u8 leafData; /* True if tables stores data on leaves only */ + u8 hasData; /* True if this page stores data */ + u8 hdrOffset; /* 100 for page 1. 0 otherwise */ + u8 childPtrSize; /* 0 if leaf==1. 4 if leaf==0 */ + u16 maxLocal; /* Copy of Btree.maxLocal or Btree.maxLeaf */ + u16 minLocal; /* Copy of Btree.minLocal or Btree.minLeaf */ + u16 cellOffset; /* Index in aData of first cell pointer */ + u16 idxParent; /* Index in parent of this node */ + u16 nFree; /* Number of free bytes on the page */ + u16 nCell; /* Number of cells on this page, local and ovfl */ + struct _OvflCell { /* Cells that will not fit on aData[] */ + u8 *pCell; /* Pointers to the body of the overflow cell */ + u16 idx; /* Insert this cell before idx-th non-overflow cell */ + } aOvfl[5]; + BtShared *pBt; /* Pointer back to BTree structure */ + u8 *aData; /* Pointer back to the start of the page */ + Pgno pgno; /* Page number for this page */ + MemPage *pParent; /* The parent of this page. NULL for root */ +}; + +/* +** The in-memory image of a disk page has the auxiliary information appended +** to the end. EXTRA_SIZE is the number of bytes of space needed to hold +** that extra information. +*/ +#define EXTRA_SIZE sizeof(MemPage) + +/* Btree handle */ +struct Btree { + sqlite3 *pSqlite; + BtShared *pBt; + u8 inTrans; /* TRANS_NONE, TRANS_READ or TRANS_WRITE */ +}; + +/* +** Btree.inTrans may take one of the following values. +** +** If the shared-data extension is enabled, there may be multiple users +** of the Btree structure. At most one of these may open a write transaction, +** but any number may have active read transactions. Variable Btree.pDb +** points to the handle that owns any current write-transaction. +*/ +#define TRANS_NONE 0 +#define TRANS_READ 1 +#define TRANS_WRITE 2 + +/* +** Everything we need to know about an open database +*/ +struct BtShared { + Pager *pPager; /* The page cache */ + BtCursor *pCursor; /* A list of all open cursors */ + MemPage *pPage1; /* First page of the database */ + u8 inStmt; /* True if we are in a statement subtransaction */ + u8 readOnly; /* True if the underlying file is readonly */ + u8 maxEmbedFrac; /* Maximum payload as % of total page size */ + u8 minEmbedFrac; /* Minimum payload as % of total page size */ + u8 minLeafFrac; /* Minimum leaf payload as % of total page size */ + u8 pageSizeFixed; /* True if the page size can no longer be changed */ +#ifndef SQLITE_OMIT_AUTOVACUUM + u8 autoVacuum; /* True if database supports auto-vacuum */ +#endif + u16 pageSize; /* Total number of bytes on a page */ + u16 usableSize; /* Number of usable bytes on each page */ + int maxLocal; /* Maximum local payload in non-LEAFDATA tables */ + int minLocal; /* Minimum local payload in non-LEAFDATA tables */ + int maxLeaf; /* Maximum local payload in a LEAFDATA table */ + int minLeaf; /* Minimum local payload in a LEAFDATA table */ + BusyHandler *pBusyHandler; /* Callback for when there is lock contention */ + u8 inTransaction; /* Transaction state */ + int nRef; /* Number of references to this structure */ + int nTransaction; /* Number of open transactions (read + write) */ + void *pSchema; /* Pointer to space allocated by sqlite3BtreeSchema() */ + void (*xFreeSchema)(void*); /* Destructor for BtShared.pSchema */ +#ifndef SQLITE_OMIT_SHARED_CACHE + BtLock *pLock; /* List of locks held on this shared-btree struct */ + BtShared *pNext; /* Next in ThreadData.pBtree linked list */ +#endif +}; + +/* +** An instance of the following structure is used to hold information +** about a cell. The parseCellPtr() function fills in this structure +** based on information extract from the raw disk page. +*/ +typedef struct CellInfo CellInfo; +struct CellInfo { + u8 *pCell; /* Pointer to the start of cell content */ + i64 nKey; /* The key for INTKEY tables, or number of bytes in key */ + u32 nData; /* Number of bytes of data */ + u16 nHeader; /* Size of the cell content header in bytes */ + u16 nLocal; /* Amount of payload held locally */ + u16 iOverflow; /* Offset to overflow page number. Zero if no overflow */ + u16 nSize; /* Size of the cell content on the main b-tree page */ +}; + +/* +** A cursor is a pointer to a particular entry in the BTree. +** The entry is identified by its MemPage and the index in +** MemPage.aCell[] of the entry. +*/ +struct BtCursor { + Btree *pBtree; /* The Btree to which this cursor belongs */ + BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */ + int (*xCompare)(void*,int,const void*,int,const void*); /* Key comp func */ + void *pArg; /* First arg to xCompare() */ + Pgno pgnoRoot; /* The root page of this tree */ + MemPage *pPage; /* Page that contains the entry */ + int idx; /* Index of the entry in pPage->aCell[] */ + CellInfo info; /* A parse of the cell we are pointing at */ + u8 wrFlag; /* True if writable */ + u8 eState; /* One of the CURSOR_XXX constants (see below) */ +#ifndef SQLITE_OMIT_SHARED_CACHE + void *pKey; + i64 nKey; + int skip; /* (skip<0) -> Prev() is a no-op. (skip>0) -> Next() is */ +#endif +}; + +/* +** Potential values for BtCursor.eState. The first two values (VALID and +** INVALID) may occur in any build. The third (REQUIRESEEK) may only occur +** if sqlite was compiled without the OMIT_SHARED_CACHE symbol defined. +** +** CURSOR_VALID: +** Cursor points to a valid entry. getPayload() etc. may be called. +** +** CURSOR_INVALID: +** Cursor does not point to a valid entry. This can happen (for example) +** because the table is empty or because BtreeCursorFirst() has not been +** called. +** +** CURSOR_REQUIRESEEK: +** The table that this cursor was opened on still exists, but has been +** modified since the cursor was last used. The cursor position is saved +** in variables BtCursor.pKey and BtCursor.nKey. When a cursor is in +** this state, restoreOrClearCursorPosition() can be called to attempt to +** seek the cursor to the saved position. +*/ +#define CURSOR_INVALID 0 +#define CURSOR_VALID 1 +#define CURSOR_REQUIRESEEK 2 + +/* +** The TRACE macro will print high-level status information about the +** btree operation when the global variable sqlite3_btree_trace is +** enabled. +*/ +#if SQLITE_TEST +# define TRACE(X) if( sqlite3_btree_trace )\ + { sqlite3DebugPrintf X; fflush(stdout); } +#else +# define TRACE(X) +#endif +int sqlite3_btree_trace=0; /* True to enable tracing */ + +/* +** Forward declaration +*/ +static int checkReadLocks(BtShared*,Pgno,BtCursor*); + +/* +** Read or write a two- and four-byte big-endian integer values. +*/ +static u32 get2byte(unsigned char *p){ + return (p[0]<<8) | p[1]; +} +static u32 get4byte(unsigned char *p){ + return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3]; +} +static void put2byte(unsigned char *p, u32 v){ + p[0] = v>>8; + p[1] = v; +} +static void put4byte(unsigned char *p, u32 v){ + p[0] = v>>24; + p[1] = v>>16; + p[2] = v>>8; + p[3] = v; +} + +/* +** Routines to read and write variable-length integers. These used to +** be defined locally, but now we use the varint routines in the util.c +** file. +*/ +#define getVarint sqlite3GetVarint +/* #define getVarint32 sqlite3GetVarint32 */ +#define getVarint32(A,B) ((*B=*(A))<=0x7f?1:sqlite3GetVarint32(A,B)) +#define putVarint sqlite3PutVarint + +/* The database page the PENDING_BYTE occupies. This page is never used. +** TODO: This macro is very similary to PAGER_MJ_PGNO() in pager.c. They +** should possibly be consolidated (presumably in pager.h). +*/ +#define PENDING_BYTE_PAGE(pBt) ((PENDING_BYTE/(pBt)->pageSize)+1) + +/* +** A linked list of the following structures is stored at BtShared.pLock. +** Locks are added (or upgraded from READ_LOCK to WRITE_LOCK) when a cursor +** is opened on the table with root page BtShared.iTable. Locks are removed +** from this list when a transaction is committed or rolled back, or when +** a btree handle is closed. +*/ +struct BtLock { + Btree *pBtree; /* Btree handle holding this lock */ + Pgno iTable; /* Root page of table */ + u8 eLock; /* READ_LOCK or WRITE_LOCK */ + BtLock *pNext; /* Next in BtShared.pLock list */ +}; + +/* Candidate values for BtLock.eLock */ +#define READ_LOCK 1 +#define WRITE_LOCK 2 + +#ifdef SQLITE_OMIT_SHARED_CACHE + /* + ** The functions queryTableLock(), lockTable() and unlockAllTables() + ** manipulate entries in the BtShared.pLock linked list used to store + ** shared-cache table level locks. If the library is compiled with the + ** shared-cache feature disabled, then there is only ever one user + ** of each BtShared structure and so this locking is not necessary. + ** So define the lock related functions as no-ops. + */ + #define queryTableLock(a,b,c) SQLITE_OK + #define lockTable(a,b,c) SQLITE_OK + #define unlockAllTables(a) + #define restoreOrClearCursorPosition(a,b) SQLITE_OK + #define saveAllCursors(a,b,c) SQLITE_OK + +#else + +static void releasePage(MemPage *pPage); + +/* +** Save the current cursor position in the variables BtCursor.nKey +** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK. +*/ +static int saveCursorPosition(BtCursor *pCur){ + int rc; + + assert( CURSOR_VALID==pCur->eState ); + assert( 0==pCur->pKey ); + + rc = sqlite3BtreeKeySize(pCur, &pCur->nKey); + + /* If this is an intKey table, then the above call to BtreeKeySize() + ** stores the integer key in pCur->nKey. In this case this value is + ** all that is required. Otherwise, if pCur is not open on an intKey + ** table, then malloc space for and store the pCur->nKey bytes of key + ** data. + */ + if( rc==SQLITE_OK && 0==pCur->pPage->intKey){ + void *pKey = sqliteMalloc(pCur->nKey); + if( pKey ){ + rc = sqlite3BtreeKey(pCur, 0, pCur->nKey, pKey); + if( rc==SQLITE_OK ){ + pCur->pKey = pKey; + }else{ + sqliteFree(pKey); + } + }else{ + rc = SQLITE_NOMEM; + } + } + assert( !pCur->pPage->intKey || !pCur->pKey ); + + if( rc==SQLITE_OK ){ + releasePage(pCur->pPage); + pCur->pPage = 0; + pCur->eState = CURSOR_REQUIRESEEK; + } + + return rc; +} + +/* +** Save the positions of all cursors except pExcept open on the table +** with root-page iRoot. Usually, this is called just before cursor +** pExcept is used to modify the table (BtreeDelete() or BtreeInsert()). +*/ +static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){ + BtCursor *p; + if( sqlite3ThreadDataReadOnly()->useSharedData ){ + for(p=pBt->pCursor; p; p=p->pNext){ + if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) && + p->eState==CURSOR_VALID ){ + int rc = saveCursorPosition(p); + if( SQLITE_OK!=rc ){ + return rc; + } + } + } + } + return SQLITE_OK; +} + +/* +** Restore the cursor to the position it was in (or as close to as possible) +** when saveCursorPosition() was called. Note that this call deletes the +** saved position info stored by saveCursorPosition(), so there can be +** at most one effective restoreOrClearCursorPosition() call after each +** saveCursorPosition(). +** +** If the second argument argument - doSeek - is false, then instead of +** returning the cursor to it's saved position, any saved position is deleted +** and the cursor state set to CURSOR_INVALID. +*/ +static int restoreOrClearCursorPositionX(BtCursor *pCur, int doSeek){ + int rc = SQLITE_OK; + assert( sqlite3ThreadDataReadOnly()->useSharedData ); + assert( pCur->eState==CURSOR_REQUIRESEEK ); + pCur->eState = CURSOR_INVALID; + if( doSeek ){ + rc = sqlite3BtreeMoveto(pCur, pCur->pKey, pCur->nKey, &pCur->skip); + } + if( rc==SQLITE_OK ){ + sqliteFree(pCur->pKey); + pCur->pKey = 0; + assert( CURSOR_VALID==pCur->eState || CURSOR_INVALID==pCur->eState ); + } + return rc; +} + +#define restoreOrClearCursorPosition(p,x) \ + (p->eState==CURSOR_REQUIRESEEK?restoreOrClearCursorPositionX(p,x):SQLITE_OK) + +/* +** Query to see if btree handle p may obtain a lock of type eLock +** (READ_LOCK or WRITE_LOCK) on the table with root-page iTab. Return +** SQLITE_OK if the lock may be obtained (by calling lockTable()), or +** SQLITE_LOCKED if not. +*/ +static int queryTableLock(Btree *p, Pgno iTab, u8 eLock){ + BtShared *pBt = p->pBt; + BtLock *pIter; + + /* This is a no-op if the shared-cache is not enabled */ + if( 0==sqlite3ThreadDataReadOnly()->useSharedData ){ + return SQLITE_OK; + } + + /* This (along with lockTable()) is where the ReadUncommitted flag is + ** dealt with. If the caller is querying for a read-lock and the flag is + ** set, it is unconditionally granted - even if there are write-locks + ** on the table. If a write-lock is requested, the ReadUncommitted flag + ** is not considered. + ** + ** In function lockTable(), if a read-lock is demanded and the + ** ReadUncommitted flag is set, no entry is added to the locks list + ** (BtShared.pLock). + ** + ** To summarize: If the ReadUncommitted flag is set, then read cursors do + ** not create or respect table locks. The locking procedure for a + ** write-cursor does not change. + */ + if( + !p->pSqlite || + 0==(p->pSqlite->flags&SQLITE_ReadUncommitted) || + eLock==WRITE_LOCK || + iTab==MASTER_ROOT + ){ + for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ + if( pIter->pBtree!=p && pIter->iTable==iTab && + (pIter->eLock!=eLock || eLock!=READ_LOCK) ){ + return SQLITE_LOCKED; + } + } + } + return SQLITE_OK; +} + +/* +** Add a lock on the table with root-page iTable to the shared-btree used +** by Btree handle p. Parameter eLock must be either READ_LOCK or +** WRITE_LOCK. +** +** SQLITE_OK is returned if the lock is added successfully. SQLITE_BUSY and +** SQLITE_NOMEM may also be returned. +*/ +static int lockTable(Btree *p, Pgno iTable, u8 eLock){ + BtShared *pBt = p->pBt; + BtLock *pLock = 0; + BtLock *pIter; + + /* This is a no-op if the shared-cache is not enabled */ + if( 0==sqlite3ThreadDataReadOnly()->useSharedData ){ + return SQLITE_OK; + } + + assert( SQLITE_OK==queryTableLock(p, iTable, eLock) ); + + /* If the read-uncommitted flag is set and a read-lock is requested, + ** return early without adding an entry to the BtShared.pLock list. See + ** comment in function queryTableLock() for more info on handling + ** the ReadUncommitted flag. + */ + if( + (p->pSqlite) && + (p->pSqlite->flags&SQLITE_ReadUncommitted) && + (eLock==READ_LOCK) && + iTable!=MASTER_ROOT + ){ + return SQLITE_OK; + } + + /* First search the list for an existing lock on this table. */ + for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ + if( pIter->iTable==iTable && pIter->pBtree==p ){ + pLock = pIter; + break; + } + } + + /* If the above search did not find a BtLock struct associating Btree p + ** with table iTable, allocate one and link it into the list. + */ + if( !pLock ){ + pLock = (BtLock *)sqliteMalloc(sizeof(BtLock)); + if( !pLock ){ + return SQLITE_NOMEM; + } + pLock->iTable = iTable; + pLock->pBtree = p; + pLock->pNext = pBt->pLock; + pBt->pLock = pLock; + } + + /* Set the BtLock.eLock variable to the maximum of the current lock + ** and the requested lock. This means if a write-lock was already held + ** and a read-lock requested, we don't incorrectly downgrade the lock. + */ + assert( WRITE_LOCK>READ_LOCK ); + if( eLock>pLock->eLock ){ + pLock->eLock = eLock; + } + + return SQLITE_OK; +} + +/* +** Release all the table locks (locks obtained via calls to the lockTable() +** procedure) held by Btree handle p. +*/ +static void unlockAllTables(Btree *p){ + BtLock **ppIter = &p->pBt->pLock; + + /* If the shared-cache extension is not enabled, there should be no + ** locks in the BtShared.pLock list, making this procedure a no-op. Assert + ** that this is the case. + */ + assert( sqlite3ThreadDataReadOnly()->useSharedData || 0==*ppIter ); + + while( *ppIter ){ + BtLock *pLock = *ppIter; + if( pLock->pBtree==p ){ + *ppIter = pLock->pNext; + sqliteFree(pLock); + }else{ + ppIter = &pLock->pNext; + } + } +} +#endif /* SQLITE_OMIT_SHARED_CACHE */ + +#ifndef SQLITE_OMIT_AUTOVACUUM +/* +** These macros define the location of the pointer-map entry for a +** database page. The first argument to each is the number of usable +** bytes on each page of the database (often 1024). The second is the +** page number to look up in the pointer map. +** +** PTRMAP_PAGENO returns the database page number of the pointer-map +** page that stores the required pointer. PTRMAP_PTROFFSET returns +** the offset of the requested map entry. +** +** If the pgno argument passed to PTRMAP_PAGENO is a pointer-map page, +** then pgno is returned. So (pgno==PTRMAP_PAGENO(pgsz, pgno)) can be +** used to test if pgno is a pointer-map page. PTRMAP_ISPAGE implements +** this test. +*/ +#define PTRMAP_PAGENO(pBt, pgno) ptrmapPageno(pBt, pgno) +#define PTRMAP_PTROFFSET(pBt, pgno) (5*(pgno-ptrmapPageno(pBt, pgno)-1)) +#define PTRMAP_ISPAGE(pBt, pgno) (PTRMAP_PAGENO((pBt),(pgno))==(pgno)) + +static Pgno ptrmapPageno(BtShared *pBt, Pgno pgno){ + int nPagesPerMapPage = (pBt->usableSize/5)+1; + int iPtrMap = (pgno-2)/nPagesPerMapPage; + int ret = (iPtrMap*nPagesPerMapPage) + 2; + if( ret==PENDING_BYTE_PAGE(pBt) ){ + ret++; + } + return ret; +} + +/* +** The pointer map is a lookup table that identifies the parent page for +** each child page in the database file. The parent page is the page that +** contains a pointer to the child. Every page in the database contains +** 0 or 1 parent pages. (In this context 'database page' refers +** to any page that is not part of the pointer map itself.) Each pointer map +** entry consists of a single byte 'type' and a 4 byte parent page number. +** The PTRMAP_XXX identifiers below are the valid types. +** +** The purpose of the pointer map is to facility moving pages from one +** position in the file to another as part of autovacuum. When a page +** is moved, the pointer in its parent must be updated to point to the +** new location. The pointer map is used to locate the parent page quickly. +** +** PTRMAP_ROOTPAGE: The database page is a root-page. The page-number is not +** used in this case. +** +** PTRMAP_FREEPAGE: The database page is an unused (free) page. The page-number +** is not used in this case. +** +** PTRMAP_OVERFLOW1: The database page is the first page in a list of +** overflow pages. The page number identifies the page that +** contains the cell with a pointer to this overflow page. +** +** PTRMAP_OVERFLOW2: The database page is the second or later page in a list of +** overflow pages. The page-number identifies the previous +** page in the overflow page list. +** +** PTRMAP_BTREE: The database page is a non-root btree page. The page number +** identifies the parent page in the btree. +*/ +#define PTRMAP_ROOTPAGE 1 +#define PTRMAP_FREEPAGE 2 +#define PTRMAP_OVERFLOW1 3 +#define PTRMAP_OVERFLOW2 4 +#define PTRMAP_BTREE 5 + +/* +** Write an entry into the pointer map. +** +** This routine updates the pointer map entry for page number 'key' +** so that it maps to type 'eType' and parent page number 'pgno'. +** An error code is returned if something goes wrong, otherwise SQLITE_OK. +*/ +static int ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent){ + u8 *pPtrmap; /* The pointer map page */ + Pgno iPtrmap; /* The pointer map page number */ + int offset; /* Offset in pointer map page */ + int rc; + + /* The master-journal page number must never be used as a pointer map page */ + assert( 0==PTRMAP_ISPAGE(pBt, PENDING_BYTE_PAGE(pBt)) ); + + assert( pBt->autoVacuum ); + if( key==0 ){ + return SQLITE_CORRUPT_BKPT; + } + iPtrmap = PTRMAP_PAGENO(pBt, key); + rc = sqlite3pager_get(pBt->pPager, iPtrmap, (void **)&pPtrmap); + if( rc!=SQLITE_OK ){ + return rc; + } + offset = PTRMAP_PTROFFSET(pBt, key); + + if( eType!=pPtrmap[offset] || get4byte(&pPtrmap[offset+1])!=parent ){ + TRACE(("PTRMAP_UPDATE: %d->(%d,%d)\n", key, eType, parent)); + rc = sqlite3pager_write(pPtrmap); + if( rc==SQLITE_OK ){ + pPtrmap[offset] = eType; + put4byte(&pPtrmap[offset+1], parent); + } + } + + sqlite3pager_unref(pPtrmap); + return rc; +} + +/* +** Read an entry from the pointer map. +** +** This routine retrieves the pointer map entry for page 'key', writing +** the type and parent page number to *pEType and *pPgno respectively. +** An error code is returned if something goes wrong, otherwise SQLITE_OK. +*/ +static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){ + int iPtrmap; /* Pointer map page index */ + u8 *pPtrmap; /* Pointer map page data */ + int offset; /* Offset of entry in pointer map */ + int rc; + + iPtrmap = PTRMAP_PAGENO(pBt, key); + rc = sqlite3pager_get(pBt->pPager, iPtrmap, (void **)&pPtrmap); + if( rc!=0 ){ + return rc; + } + + offset = PTRMAP_PTROFFSET(pBt, key); + if( pEType ) *pEType = pPtrmap[offset]; + if( pPgno ) *pPgno = get4byte(&pPtrmap[offset+1]); + + sqlite3pager_unref(pPtrmap); + if( *pEType<1 || *pEType>5 ) return SQLITE_CORRUPT_BKPT; + return SQLITE_OK; +} + +#endif /* SQLITE_OMIT_AUTOVACUUM */ + +/* +** Given a btree page and a cell index (0 means the first cell on +** the page, 1 means the second cell, and so forth) return a pointer +** to the cell content. +** +** This routine works only for pages that do not contain overflow cells. +*/ +static u8 *findCell(MemPage *pPage, int iCell){ + u8 *data = pPage->aData; + assert( iCell>=0 ); + assert( iCellhdrOffset+3]) ); + return data + get2byte(&data[pPage->cellOffset+2*iCell]); +} + +/* +** This a more complex version of findCell() that works for +** pages that do contain overflow cells. See insert +*/ +static u8 *findOverflowCell(MemPage *pPage, int iCell){ + int i; + for(i=pPage->nOverflow-1; i>=0; i--){ + int k; + struct _OvflCell *pOvfl; + pOvfl = &pPage->aOvfl[i]; + k = pOvfl->idx; + if( k<=iCell ){ + if( k==iCell ){ + return pOvfl->pCell; + } + iCell--; + } + } + return findCell(pPage, iCell); +} + +/* +** Parse a cell content block and fill in the CellInfo structure. There +** are two versions of this function. parseCell() takes a cell index +** as the second argument and parseCellPtr() takes a pointer to the +** body of the cell as its second argument. +*/ +static void parseCellPtr( + MemPage *pPage, /* Page containing the cell */ + u8 *pCell, /* Pointer to the cell text. */ + CellInfo *pInfo /* Fill in this structure */ +){ + int n; /* Number bytes in cell content header */ + u32 nPayload; /* Number of bytes of cell payload */ + + pInfo->pCell = pCell; + assert( pPage->leaf==0 || pPage->leaf==1 ); + n = pPage->childPtrSize; + assert( n==4-4*pPage->leaf ); + if( pPage->hasData ){ + n += getVarint32(&pCell[n], &nPayload); + }else{ + nPayload = 0; + } + pInfo->nData = nPayload; + if( pPage->intKey ){ + n += getVarint(&pCell[n], (u64 *)&pInfo->nKey); + }else{ + u32 x; + n += getVarint32(&pCell[n], &x); + pInfo->nKey = x; + nPayload += x; + } + pInfo->nHeader = n; + if( nPayload<=pPage->maxLocal ){ + /* This is the (easy) common case where the entire payload fits + ** on the local page. No overflow is required. + */ + int nSize; /* Total size of cell content in bytes */ + pInfo->nLocal = nPayload; + pInfo->iOverflow = 0; + nSize = nPayload + n; + if( nSize<4 ){ + nSize = 4; /* Minimum cell size is 4 */ + } + pInfo->nSize = nSize; + }else{ + /* If the payload will not fit completely on the local page, we have + ** to decide how much to store locally and how much to spill onto + ** overflow pages. The strategy is to minimize the amount of unused + ** space on overflow pages while keeping the amount of local storage + ** in between minLocal and maxLocal. + ** + ** Warning: changing the way overflow payload is distributed in any + ** way will result in an incompatible file format. + */ + int minLocal; /* Minimum amount of payload held locally */ + int maxLocal; /* Maximum amount of payload held locally */ + int surplus; /* Overflow payload available for local storage */ + + minLocal = pPage->minLocal; + maxLocal = pPage->maxLocal; + surplus = minLocal + (nPayload - minLocal)%(pPage->pBt->usableSize - 4); + if( surplus <= maxLocal ){ + pInfo->nLocal = surplus; + }else{ + pInfo->nLocal = minLocal; + } + pInfo->iOverflow = pInfo->nLocal + n; + pInfo->nSize = pInfo->iOverflow + 4; + } +} +static void parseCell( + MemPage *pPage, /* Page containing the cell */ + int iCell, /* The cell index. First cell is 0 */ + CellInfo *pInfo /* Fill in this structure */ +){ + parseCellPtr(pPage, findCell(pPage, iCell), pInfo); +} + +/* +** Compute the total number of bytes that a Cell needs in the cell +** data area of the btree-page. The return number includes the cell +** data header and the local payload, but not any overflow page or +** the space used by the cell pointer. +*/ +#ifndef NDEBUG +static int cellSize(MemPage *pPage, int iCell){ + CellInfo info; + parseCell(pPage, iCell, &info); + return info.nSize; +} +#endif +static int cellSizePtr(MemPage *pPage, u8 *pCell){ + CellInfo info; + parseCellPtr(pPage, pCell, &info); + return info.nSize; +} + +#ifndef SQLITE_OMIT_AUTOVACUUM +/* +** If the cell pCell, part of page pPage contains a pointer +** to an overflow page, insert an entry into the pointer-map +** for the overflow page. +*/ +static int ptrmapPutOvflPtr(MemPage *pPage, u8 *pCell){ + if( pCell ){ + CellInfo info; + parseCellPtr(pPage, pCell, &info); + if( (info.nData+(pPage->intKey?0:info.nKey))>info.nLocal ){ + Pgno ovfl = get4byte(&pCell[info.iOverflow]); + return ptrmapPut(pPage->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno); + } + } + return SQLITE_OK; +} +/* +** If the cell with index iCell on page pPage contains a pointer +** to an overflow page, insert an entry into the pointer-map +** for the overflow page. +*/ +static int ptrmapPutOvfl(MemPage *pPage, int iCell){ + u8 *pCell; + pCell = findOverflowCell(pPage, iCell); + return ptrmapPutOvflPtr(pPage, pCell); +} +#endif + + +/* +** Do sanity checking on a page. Throw an exception if anything is +** not right. +** +** This routine is used for internal error checking only. It is omitted +** from most builds. +*/ +#if defined(BTREE_DEBUG) && !defined(NDEBUG) && 0 +static void _pageIntegrity(MemPage *pPage){ + int usableSize; + u8 *data; + int i, j, idx, c, pc, hdr, nFree; + int cellOffset; + int nCell, cellLimit; + u8 *used; + + used = sqliteMallocRaw( pPage->pBt->pageSize ); + if( used==0 ) return; + usableSize = pPage->pBt->usableSize; + assert( pPage->aData==&((unsigned char*)pPage)[-pPage->pBt->pageSize] ); + hdr = pPage->hdrOffset; + assert( hdr==(pPage->pgno==1 ? 100 : 0) ); + assert( pPage->pgno==sqlite3pager_pagenumber(pPage->aData) ); + c = pPage->aData[hdr]; + if( pPage->isInit ){ + assert( pPage->leaf == ((c & PTF_LEAF)!=0) ); + assert( pPage->zeroData == ((c & PTF_ZERODATA)!=0) ); + assert( pPage->leafData == ((c & PTF_LEAFDATA)!=0) ); + assert( pPage->intKey == ((c & (PTF_INTKEY|PTF_LEAFDATA))!=0) ); + assert( pPage->hasData == + !(pPage->zeroData || (!pPage->leaf && pPage->leafData)) ); + assert( pPage->cellOffset==pPage->hdrOffset+12-4*pPage->leaf ); + assert( pPage->nCell = get2byte(&pPage->aData[hdr+3]) ); + } + data = pPage->aData; + memset(used, 0, usableSize); + for(i=0; ileaf*4; i++) used[i] = 1; + nFree = 0; + pc = get2byte(&data[hdr+1]); + while( pc ){ + int size; + assert( pc>0 && pcisInit==0 + || pPage->nFree==nFree+data[hdr+7]+cellLimit-(cellOffset+2*nCell) ); + cellOffset = pPage->cellOffset; + for(i=0; i0 && pcinTrans!=TRANS_NONE || p->pBt->nTransactionpBt->nRef ); \ + assert( p->pBt->nTransaction<=p->pBt->nRef ); \ + assert( p->pBt->inTransaction!=TRANS_NONE || p->pBt->nTransaction==0 ); \ + assert( p->pBt->inTransaction>=p->inTrans ); + +/* +** Defragment the page given. All Cells are moved to the +** end of the page and all free space is collected into one +** big FreeBlk that occurs in between the header and cell +** pointer array and the cell content area. +*/ +static int defragmentPage(MemPage *pPage){ + int i; /* Loop counter */ + int pc; /* Address of a i-th cell */ + int addr; /* Offset of first byte after cell pointer array */ + int hdr; /* Offset to the page header */ + 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 nCell; /* Number of cells on the page */ + unsigned char *data; /* The page data */ + unsigned char *temp; /* Temp area for cell content */ + + assert( sqlite3pager_iswriteable(pPage->aData) ); + assert( pPage->pBt!=0 ); + assert( pPage->pBt->usableSize <= SQLITE_MAX_PAGE_SIZE ); + assert( pPage->nOverflow==0 ); + temp = sqliteMalloc( pPage->pBt->pageSize ); + if( temp==0 ) return SQLITE_NOMEM; + data = pPage->aData; + hdr = pPage->hdrOffset; + cellOffset = pPage->cellOffset; + 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; + for(i=0; ipBt->usableSize ); + size = cellSizePtr(pPage, &temp[pc]); + brk -= size; + memcpy(&data[brk], &temp[pc], size); + put2byte(pAddr, brk); + } + assert( brk>=cellOffset+2*nCell ); + put2byte(&data[hdr+5], brk); + data[hdr+1] = 0; + data[hdr+2] = 0; + data[hdr+7] = 0; + addr = cellOffset+2*nCell; + memset(&data[addr], 0, brk-addr); + sqliteFree(temp); + return SQLITE_OK; +} + +/* +** Allocate nByte bytes of space on a page. +** +** Return the index into pPage->aData[] of the first byte of +** the new allocation. Or return 0 if there is not enough free +** space on the page to satisfy the allocation request. +** +** If the page contains nBytes of free space but does not contain +** nBytes of contiguous free space, then this routine automatically +** calls defragementPage() to consolidate all free space before +** allocating the new chunk. +*/ +static int allocateSpace(MemPage *pPage, int nByte){ + int addr, pc, hdr; + int size; + int nFrag; + int top; + int nCell; + int cellOffset; + unsigned char *data; + + data = pPage->aData; + assert( sqlite3pager_iswriteable(data) ); + assert( pPage->pBt ); + if( nByte<4 ) nByte = 4; + if( pPage->nFreenOverflow>0 ) return 0; + pPage->nFree -= nByte; + hdr = pPage->hdrOffset; + + nFrag = data[hdr+7]; + if( nFrag<60 ){ + /* Search the freelist looking for a slot big enough to satisfy the + ** space request. */ + addr = hdr+1; + while( (pc = get2byte(&data[addr]))>0 ){ + size = get2byte(&data[pc+2]); + if( size>=nByte ){ + if( sizecellOffset; + if( nFrag>=60 || cellOffset + 2*nCell > top - nByte ){ + if( defragmentPage(pPage) ) return 0; + top = get2byte(&data[hdr+5]); + } + top -= nByte; + assert( cellOffset + 2*nCell <= top ); + put2byte(&data[hdr+5], top); + return top; +} + +/* +** Return a section of the pPage->aData to the freelist. +** The first byte of the new free block is pPage->aDisk[start] +** and the size of the block is "size" bytes. +** +** Most of the effort here is involved in coalesing adjacent +** free blocks into a single big free block. +*/ +static void freeSpace(MemPage *pPage, int start, int size){ + int addr, pbegin, hdr; + unsigned char *data = pPage->aData; + + assert( pPage->pBt!=0 ); + assert( sqlite3pager_iswriteable(data) ); + assert( start>=pPage->hdrOffset+6+(pPage->leaf?0:4) ); + assert( (start + size)<=pPage->pBt->usableSize ); + if( size<4 ) size = 4; + + /* Add the space back into the linked list of freeblocks */ + hdr = pPage->hdrOffset; + addr = hdr + 1; + while( (pbegin = get2byte(&data[addr]))0 ){ + assert( pbegin<=pPage->pBt->usableSize-4 ); + assert( pbegin>addr ); + addr = pbegin; + } + assert( pbegin<=pPage->pBt->usableSize-4 ); + assert( pbegin>addr || pbegin==0 ); + put2byte(&data[addr], start); + put2byte(&data[start], pbegin); + put2byte(&data[start+2], size); + pPage->nFree += size; + + /* Coalesce adjacent free blocks */ + addr = pPage->hdrOffset + 1; + while( (pbegin = get2byte(&data[addr]))>0 ){ + int pnext, psize; + assert( pbegin>addr ); + assert( pbegin<=pPage->pBt->usableSize-4 ); + pnext = get2byte(&data[pbegin]); + psize = get2byte(&data[pbegin+2]); + if( pbegin + psize + 3 >= pnext && pnext>0 ){ + int frag = pnext - (pbegin+psize); + assert( frag<=data[pPage->hdrOffset+7] ); + data[pPage->hdrOffset+7] -= frag; + put2byte(&data[pbegin], get2byte(&data[pnext])); + put2byte(&data[pbegin+2], pnext+get2byte(&data[pnext+2])-pbegin); + }else{ + addr = pbegin; + } + } + + /* If the cell content area begins with a freeblock, remove it. */ + if( data[hdr+1]==data[hdr+5] && data[hdr+2]==data[hdr+6] ){ + int top; + pbegin = get2byte(&data[hdr+1]); + memcpy(&data[hdr+1], &data[pbegin], 2); + top = get2byte(&data[hdr+5]); + put2byte(&data[hdr+5], top + get2byte(&data[pbegin+2])); + } +} + +/* +** Decode the flags byte (the first byte of the header) for a page +** and initialize fields of the MemPage structure accordingly. +*/ +static void decodeFlags(MemPage *pPage, int flagByte){ + BtShared *pBt; /* A copy of pPage->pBt */ + + assert( pPage->hdrOffset==(pPage->pgno==1 ? 100 : 0) ); + pPage->intKey = (flagByte & (PTF_INTKEY|PTF_LEAFDATA))!=0; + pPage->zeroData = (flagByte & PTF_ZERODATA)!=0; + pPage->leaf = (flagByte & PTF_LEAF)!=0; + pPage->childPtrSize = 4*(pPage->leaf==0); + pBt = pPage->pBt; + if( flagByte & PTF_LEAFDATA ){ + pPage->leafData = 1; + pPage->maxLocal = pBt->maxLeaf; + pPage->minLocal = pBt->minLeaf; + }else{ + pPage->leafData = 0; + pPage->maxLocal = pBt->maxLocal; + pPage->minLocal = pBt->minLocal; + } + pPage->hasData = !(pPage->zeroData || (!pPage->leaf && pPage->leafData)); +} + +/* +** Initialize the auxiliary information for a disk block. +** +** The pParent parameter must be a pointer to the MemPage which +** is the parent of the page being initialized. The root of a +** BTree has no parent and so for that page, pParent==NULL. +** +** Return SQLITE_OK on success. If we see that the page does +** not contain a well-formed database page, then return +** SQLITE_CORRUPT. Note that a return of SQLITE_OK does not +** guarantee that the page is well-formed. It only shows that +** we failed to detect any corruption. +*/ +static int initPage( + MemPage *pPage, /* The page to be initialized */ + MemPage *pParent /* The parent. Might be NULL */ +){ + int pc; /* Address of a freeblock within pPage->aData[] */ + int hdr; /* Offset to beginning of page header */ + u8 *data; /* Equal to pPage->aData */ + BtShared *pBt; /* The main btree structure */ + int usableSize; /* Amount of usable space on each page */ + int cellOffset; /* Offset from start of page to first cell pointer */ + int nFree; /* Number of unused bytes on the page */ + int top; /* First byte of the cell content area */ + + pBt = pPage->pBt; + assert( pBt!=0 ); + assert( pParent==0 || pParent->pBt==pBt ); + assert( pPage->pgno==sqlite3pager_pagenumber(pPage->aData) ); + assert( pPage->aData == &((unsigned char*)pPage)[-pBt->pageSize] ); + if( pPage->pParent!=pParent && (pPage->pParent!=0 || pPage->isInit) ){ + /* The parent page should never change unless the file is corrupt */ + return SQLITE_CORRUPT_BKPT; + } + if( pPage->isInit ) return SQLITE_OK; + if( pPage->pParent==0 && pParent!=0 ){ + pPage->pParent = pParent; + sqlite3pager_ref(pParent->aData); + } + hdr = pPage->hdrOffset; + data = pPage->aData; + decodeFlags(pPage, data[hdr]); + pPage->nOverflow = 0; + pPage->idxShift = 0; + usableSize = pBt->usableSize; + pPage->cellOffset = cellOffset = hdr + 12 - 4*pPage->leaf; + top = get2byte(&data[hdr+5]); + pPage->nCell = get2byte(&data[hdr+3]); + if( pPage->nCell>MX_CELL(pBt) ){ + /* To many cells for a single page. The page must be corrupt */ + return SQLITE_CORRUPT_BKPT; + } + if( pPage->nCell==0 && pParent!=0 && pParent->pgno!=1 ){ + /* All pages must have at least one cell, except for root pages */ + return SQLITE_CORRUPT_BKPT; + } + + /* Compute the total free space on the page */ + pc = get2byte(&data[hdr+1]); + nFree = data[hdr+7] + top - (cellOffset + 2*pPage->nCell); + while( pc>0 ){ + int next, size; + if( pc>usableSize-4 ){ + /* Free block is off the page */ + return SQLITE_CORRUPT_BKPT; + } + next = get2byte(&data[pc]); + size = get2byte(&data[pc+2]); + if( next>0 && next<=pc+size+3 ){ + /* Free blocks must be in accending order */ + return SQLITE_CORRUPT_BKPT; + } + nFree += size; + pc = next; + } + pPage->nFree = nFree; + if( nFree>=usableSize ){ + /* Free space cannot exceed total page size */ + return SQLITE_CORRUPT_BKPT; + } + + pPage->isInit = 1; + pageIntegrity(pPage); + return SQLITE_OK; +} + +/* +** Set up a raw page so that it looks like a database page holding +** no entries. +*/ +static void zeroPage(MemPage *pPage, int flags){ + unsigned char *data = pPage->aData; + BtShared *pBt = pPage->pBt; + int hdr = pPage->hdrOffset; + int first; + + assert( sqlite3pager_pagenumber(data)==pPage->pgno ); + assert( &data[pBt->pageSize] == (unsigned char*)pPage ); + assert( sqlite3pager_iswriteable(data) ); + memset(&data[hdr], 0, pBt->usableSize - hdr); + data[hdr] = flags; + first = hdr + 8 + 4*((flags&PTF_LEAF)==0); + memset(&data[hdr+1], 0, 4); + data[hdr+7] = 0; + put2byte(&data[hdr+5], pBt->usableSize); + pPage->nFree = pBt->usableSize - first; + decodeFlags(pPage, flags); + pPage->hdrOffset = hdr; + pPage->cellOffset = first; + pPage->nOverflow = 0; + pPage->idxShift = 0; + pPage->nCell = 0; + pPage->isInit = 1; + pageIntegrity(pPage); +} + +/* +** Get a page from the pager. Initialize the MemPage.pBt and +** MemPage.aData elements if needed. +*/ +static int getPage(BtShared *pBt, Pgno pgno, MemPage **ppPage){ + int rc; + unsigned char *aData; + MemPage *pPage; + rc = sqlite3pager_get(pBt->pPager, pgno, (void**)&aData); + if( rc ) return rc; + pPage = (MemPage*)&aData[pBt->pageSize]; + pPage->aData = aData; + pPage->pBt = pBt; + pPage->pgno = pgno; + pPage->hdrOffset = pPage->pgno==1 ? 100 : 0; + *ppPage = pPage; + return SQLITE_OK; +} + +/* +** Get a page from the pager and initialize it. This routine +** is just a convenience wrapper around separate calls to +** getPage() and initPage(). +*/ +static int getAndInitPage( + BtShared *pBt, /* The database file */ + Pgno pgno, /* Number of the page to get */ + MemPage **ppPage, /* Write the page pointer here */ + MemPage *pParent /* Parent of the page */ +){ + int rc; + if( pgno==0 ){ + return SQLITE_CORRUPT_BKPT; + } + rc = getPage(pBt, pgno, ppPage); + if( rc==SQLITE_OK && (*ppPage)->isInit==0 ){ + rc = initPage(*ppPage, pParent); + } + return rc; +} + +/* +** Release a MemPage. This should be called once for each prior +** call to getPage. +*/ +static void releasePage(MemPage *pPage){ + if( pPage ){ + assert( pPage->aData ); + assert( pPage->pBt ); + assert( &pPage->aData[pPage->pBt->pageSize]==(unsigned char*)pPage ); + sqlite3pager_unref(pPage->aData); + } +} + +/* +** This routine is called when the reference count for a page +** reaches zero. We need to unref the pParent pointer when that +** happens. +*/ +static void pageDestructor(void *pData, int pageSize){ + MemPage *pPage; + assert( (pageSize & 7)==0 ); + pPage = (MemPage*)&((char*)pData)[pageSize]; + if( pPage->pParent ){ + MemPage *pParent = pPage->pParent; + pPage->pParent = 0; + releasePage(pParent); + } + pPage->isInit = 0; +} + +/* +** During a rollback, when the pager reloads information into the cache +** so that the cache is restored to its original state at the start of +** the transaction, for each page restored this routine is called. +** +** This routine needs to reset the extra data section at the end of the +** page to agree with the restored data. +*/ +static void pageReinit(void *pData, int pageSize){ + MemPage *pPage; + assert( (pageSize & 7)==0 ); + pPage = (MemPage*)&((char*)pData)[pageSize]; + if( pPage->isInit ){ + pPage->isInit = 0; + initPage(pPage, pPage->pParent); + } +} + +/* +** Open a database file. +** +** zFilename is the name of the database file. If zFilename is NULL +** a new database with a random name is created. This randomly named +** database file will be deleted when sqlite3BtreeClose() is called. +*/ +int sqlite3BtreeOpen( + const char *zFilename, /* Name of the file containing the BTree database */ + sqlite3 *pSqlite, /* Associated database handle */ + Btree **ppBtree, /* Pointer to new Btree object written here */ + int flags /* Options */ +){ + BtShared *pBt; /* Shared part of btree structure */ + Btree *p; /* Handle to return */ + int rc; + int nReserve; + unsigned char zDbHeader[100]; +#if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO) + const ThreadData *pTsdro; +#endif + + /* Set the variable isMemdb to true for an in-memory database, or + ** false for a file-based database. This symbol is only required if + ** either of the shared-data or autovacuum features are compiled + ** into the library. + */ +#if !defined(SQLITE_OMIT_SHARED_CACHE) || !defined(SQLITE_OMIT_AUTOVACUUM) + #ifdef SQLITE_OMIT_MEMORYDB + const int isMemdb = !zFilename; + #else + const int isMemdb = !zFilename || (strcmp(zFilename, ":memory:")?0:1); + #endif +#endif + + p = sqliteMalloc(sizeof(Btree)); + if( !p ){ + return SQLITE_NOMEM; + } + p->inTrans = TRANS_NONE; + p->pSqlite = pSqlite; + + /* Try to find an existing Btree structure opened on zFilename. */ +#if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO) + pTsdro = sqlite3ThreadDataReadOnly(); + if( pTsdro->useSharedData && zFilename && !isMemdb ){ + char *zFullPathname = sqlite3OsFullPathname(zFilename); + if( !zFullPathname ){ + sqliteFree(p); + return SQLITE_NOMEM; + } + for(pBt=pTsdro->pBtree; pBt; pBt=pBt->pNext){ + assert( pBt->nRef>0 ); + if( 0==strcmp(zFullPathname, sqlite3pager_filename(pBt->pPager)) ){ + p->pBt = pBt; + *ppBtree = p; + pBt->nRef++; + sqliteFree(zFullPathname); + return SQLITE_OK; + } + } + sqliteFree(zFullPathname); + } +#endif + + /* + ** The following asserts make sure that structures used by the btree are + ** the right size. This is to guard against size changes that result + ** when compiling on a different architecture. + */ + assert( sizeof(i64)==8 ); + assert( sizeof(u64)==8 ); + assert( sizeof(u32)==4 ); + assert( sizeof(u16)==2 ); + assert( sizeof(Pgno)==4 ); + + pBt = sqliteMalloc( sizeof(*pBt) ); + if( pBt==0 ){ + *ppBtree = 0; + sqliteFree(p); + return SQLITE_NOMEM; + } + rc = sqlite3pager_open(&pBt->pPager, zFilename, EXTRA_SIZE, flags); + if( rc!=SQLITE_OK ){ + if( pBt->pPager ) sqlite3pager_close(pBt->pPager); + sqliteFree(pBt); + sqliteFree(p); + *ppBtree = 0; + return rc; + } + p->pBt = pBt; + + sqlite3pager_set_destructor(pBt->pPager, pageDestructor); + sqlite3pager_set_reiniter(pBt->pPager, pageReinit); + pBt->pCursor = 0; + pBt->pPage1 = 0; + pBt->readOnly = sqlite3pager_isreadonly(pBt->pPager); + sqlite3pager_read_fileheader(pBt->pPager, sizeof(zDbHeader), zDbHeader); + pBt->pageSize = get2byte(&zDbHeader[16]); + if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE + || ((pBt->pageSize-1)&pBt->pageSize)!=0 ){ + pBt->pageSize = SQLITE_DEFAULT_PAGE_SIZE; + pBt->maxEmbedFrac = 64; /* 25% */ + pBt->minEmbedFrac = 32; /* 12.5% */ + pBt->minLeafFrac = 32; /* 12.5% */ +#ifndef SQLITE_OMIT_AUTOVACUUM + /* If the magic name ":memory:" will create an in-memory database, then + ** do not set the auto-vacuum flag, even if SQLITE_DEFAULT_AUTOVACUUM + ** is true. On the other hand, if SQLITE_OMIT_MEMORYDB has been defined, + ** then ":memory:" is just a regular file-name. Respect the auto-vacuum + ** default in this case. + */ + if( zFilename && !isMemdb ){ + pBt->autoVacuum = SQLITE_DEFAULT_AUTOVACUUM; + } +#endif + nReserve = 0; + }else{ + nReserve = zDbHeader[20]; + pBt->maxEmbedFrac = zDbHeader[21]; + pBt->minEmbedFrac = zDbHeader[22]; + pBt->minLeafFrac = zDbHeader[23]; + pBt->pageSizeFixed = 1; +#ifndef SQLITE_OMIT_AUTOVACUUM + pBt->autoVacuum = (get4byte(&zDbHeader[36 + 4*4])?1:0); +#endif + } + pBt->usableSize = pBt->pageSize - nReserve; + assert( (pBt->pageSize & 7)==0 ); /* 8-byte alignment of pageSize */ + sqlite3pager_set_pagesize(pBt->pPager, pBt->pageSize); + +#ifndef SQLITE_OMIT_SHARED_CACHE + /* Add the new btree to the linked list starting at ThreadData.pBtree. + ** There is no chance that a malloc() may fail inside of the + ** sqlite3ThreadData() call, as the ThreadData structure must have already + ** been allocated for pTsdro->useSharedData to be non-zero. + */ + if( pTsdro->useSharedData && zFilename && !isMemdb ){ + pBt->pNext = pTsdro->pBtree; + sqlite3ThreadData()->pBtree = pBt; + } +#endif + pBt->nRef = 1; + *ppBtree = p; + return SQLITE_OK; +} + +/* +** Close an open database and invalidate all cursors. +*/ +int sqlite3BtreeClose(Btree *p){ + BtShared *pBt = p->pBt; + BtCursor *pCur; + +#ifndef SQLITE_OMIT_SHARED_CACHE + ThreadData *pTsd; +#endif + + /* Close all cursors opened via this handle. */ + pCur = pBt->pCursor; + while( pCur ){ + BtCursor *pTmp = pCur; + pCur = pCur->pNext; + if( pTmp->pBtree==p ){ + sqlite3BtreeCloseCursor(pTmp); + } + } + + /* Rollback any active transaction and free the handle structure. + ** The call to sqlite3BtreeRollback() drops any table-locks held by + ** this handle. + */ + sqlite3BtreeRollback(p); + sqliteFree(p); + +#ifndef SQLITE_OMIT_SHARED_CACHE + /* If there are still other outstanding references to the shared-btree + ** structure, return now. The remainder of this procedure cleans + ** up the shared-btree. + */ + assert( pBt->nRef>0 ); + pBt->nRef--; + if( pBt->nRef ){ + return SQLITE_OK; + } + + /* Remove the shared-btree from the thread wide list. Call + ** ThreadDataReadOnly() and then cast away the const property of the + ** pointer to avoid allocating thread data if it is not really required. + */ + pTsd = (ThreadData *)sqlite3ThreadDataReadOnly(); + if( pTsd->pBtree==pBt ){ + assert( pTsd==sqlite3ThreadData() ); + pTsd->pBtree = pBt->pNext; + }else{ + BtShared *pPrev; + for(pPrev=pTsd->pBtree; pPrev && pPrev->pNext!=pBt; pPrev=pPrev->pNext); + if( pPrev ){ + assert( pTsd==sqlite3ThreadData() ); + pPrev->pNext = pBt->pNext; + } + } +#endif + + /* Close the pager and free the shared-btree structure */ + assert( !pBt->pCursor ); + sqlite3pager_close(pBt->pPager); + if( pBt->xFreeSchema && pBt->pSchema ){ + pBt->xFreeSchema(pBt->pSchema); + } + sqliteFree(pBt->pSchema); + sqliteFree(pBt); + return SQLITE_OK; +} + +/* +** Change the busy handler callback function. +*/ +int sqlite3BtreeSetBusyHandler(Btree *p, BusyHandler *pHandler){ + BtShared *pBt = p->pBt; + pBt->pBusyHandler = pHandler; + sqlite3pager_set_busyhandler(pBt->pPager, pHandler); + return SQLITE_OK; +} + +/* +** Change the limit on the number of pages allowed in the cache. +** +** The maximum number of cache pages is set to the absolute +** value of mxPage. If mxPage is negative, the pager will +** operate asynchronously - it will not stop to do fsync()s +** to insure data is written to the disk surface before +** continuing. Transactions still work if synchronous is off, +** and the database cannot be corrupted if this program +** crashes. But if the operating system crashes or there is +** an abrupt power failure when synchronous is off, the database +** could be left in an inconsistent and unrecoverable state. +** Synchronous is on by default so database corruption is not +** normally a worry. +*/ +int sqlite3BtreeSetCacheSize(Btree *p, int mxPage){ + BtShared *pBt = p->pBt; + sqlite3pager_set_cachesize(pBt->pPager, mxPage); + return SQLITE_OK; +} + +/* +** Change the way data is synced to disk in order to increase or decrease +** how well the database resists damage due to OS crashes and power +** failures. Level 1 is the same as asynchronous (no syncs() occur and +** there is a high probability of damage) Level 2 is the default. There +** is a very low but non-zero probability of damage. Level 3 reduces the +** probability of damage to near zero but with a write performance reduction. +*/ +#ifndef SQLITE_OMIT_PAGER_PRAGMAS +int sqlite3BtreeSetSafetyLevel(Btree *p, int level, int fullSync){ + BtShared *pBt = p->pBt; + sqlite3pager_set_safety_level(pBt->pPager, level, fullSync); + return SQLITE_OK; +} +#endif + +/* +** Return TRUE if the given btree is set to safety level 1. In other +** words, return TRUE if no sync() occurs on the disk files. +*/ +int sqlite3BtreeSyncDisabled(Btree *p){ + BtShared *pBt = p->pBt; + assert( pBt && pBt->pPager ); + return sqlite3pager_nosync(pBt->pPager); +} + +#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM) +/* +** Change the default pages size and the number of reserved bytes per page. +** +** The page size must be a power of 2 between 512 and 65536. If the page +** size supplied does not meet this constraint then the page size is not +** changed. +** +** Page sizes are constrained to be a power of two so that the region +** of the database file used for locking (beginning at PENDING_BYTE, +** the first byte past the 1GB boundary, 0x40000000) needs to occur +** at the beginning of a page. +** +** If parameter nReserve is less than zero, then the number of reserved +** bytes per page is left unchanged. +*/ +int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve){ + BtShared *pBt = p->pBt; + if( pBt->pageSizeFixed ){ + return SQLITE_READONLY; + } + if( nReserve<0 ){ + nReserve = pBt->pageSize - pBt->usableSize; + } + if( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE && + ((pageSize-1)&pageSize)==0 ){ + assert( (pageSize & 7)==0 ); + assert( !pBt->pPage1 && !pBt->pCursor ); + pBt->pageSize = sqlite3pager_set_pagesize(pBt->pPager, pageSize); + } + pBt->usableSize = pBt->pageSize - nReserve; + return SQLITE_OK; +} + +/* +** Return the currently defined page size +*/ +int sqlite3BtreeGetPageSize(Btree *p){ + return p->pBt->pageSize; +} +int sqlite3BtreeGetReserve(Btree *p){ + return p->pBt->pageSize - p->pBt->usableSize; +} +#endif /* !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM) */ + +/* +** Change the 'auto-vacuum' property of the database. If the 'autoVacuum' +** parameter is non-zero, then auto-vacuum mode is enabled. If zero, it +** is disabled. The default value for the auto-vacuum property is +** determined by the SQLITE_DEFAULT_AUTOVACUUM macro. +*/ +int sqlite3BtreeSetAutoVacuum(Btree *p, int autoVacuum){ + BtShared *pBt = p->pBt;; +#ifdef SQLITE_OMIT_AUTOVACUUM + return SQLITE_READONLY; +#else + if( pBt->pageSizeFixed ){ + return SQLITE_READONLY; + } + pBt->autoVacuum = (autoVacuum?1:0); + return SQLITE_OK; +#endif +} + +/* +** Return the value of the 'auto-vacuum' property. If auto-vacuum is +** enabled 1 is returned. Otherwise 0. +*/ +int sqlite3BtreeGetAutoVacuum(Btree *p){ +#ifdef SQLITE_OMIT_AUTOVACUUM + return 0; +#else + return p->pBt->autoVacuum; +#endif +} + + +/* +** Get a reference to pPage1 of the database file. This will +** also acquire a readlock on that file. +** +** SQLITE_OK is returned on success. If the file is not a +** well-formed database file, then SQLITE_CORRUPT is returned. +** SQLITE_BUSY is returned if the database is locked. SQLITE_NOMEM +** is returned if we run out of memory. SQLITE_PROTOCOL is returned +** if there is a locking protocol violation. +*/ +static int lockBtree(BtShared *pBt){ + int rc, pageSize; + MemPage *pPage1; + if( pBt->pPage1 ) return SQLITE_OK; + rc = getPage(pBt, 1, &pPage1); + if( rc!=SQLITE_OK ) return rc; + + + /* Do some checking to help insure the file we opened really is + ** a valid database file. + */ + rc = SQLITE_NOTADB; + if( sqlite3pager_pagecount(pBt->pPager)>0 ){ + u8 *page1 = pPage1->aData; + if( memcmp(page1, zMagicHeader, 16)!=0 ){ + goto page1_init_failed; + } + if( page1[18]>1 || page1[19]>1 ){ + goto page1_init_failed; + } + pageSize = get2byte(&page1[16]); + if( ((pageSize-1)&pageSize)!=0 ){ + goto page1_init_failed; + } + assert( (pageSize & 7)==0 ); + pBt->pageSize = pageSize; + pBt->usableSize = pageSize - page1[20]; + if( pBt->usableSize<500 ){ + goto page1_init_failed; + } + pBt->maxEmbedFrac = page1[21]; + pBt->minEmbedFrac = page1[22]; + pBt->minLeafFrac = page1[23]; +#ifndef SQLITE_OMIT_AUTOVACUUM + pBt->autoVacuum = (get4byte(&page1[36 + 4*4])?1:0); +#endif + } + + /* maxLocal is the maximum amount of payload to store locally for + ** a cell. Make sure it is small enough so that at least minFanout + ** cells can will fit on one page. We assume a 10-byte page header. + ** Besides the payload, the cell must store: + ** 2-byte pointer to the cell + ** 4-byte child pointer + ** 9-byte nKey value + ** 4-byte nData value + ** 4-byte overflow page pointer + ** So a cell consists of a 2-byte poiner, a header which is as much as + ** 17 bytes long, 0 to N bytes of payload, and an optional 4 byte overflow + ** page pointer. + */ + pBt->maxLocal = (pBt->usableSize-12)*pBt->maxEmbedFrac/255 - 23; + pBt->minLocal = (pBt->usableSize-12)*pBt->minEmbedFrac/255 - 23; + pBt->maxLeaf = pBt->usableSize - 35; + pBt->minLeaf = (pBt->usableSize-12)*pBt->minLeafFrac/255 - 23; + if( pBt->minLocal>pBt->maxLocal || pBt->maxLocal<0 ){ + goto page1_init_failed; + } + assert( pBt->maxLeaf + 23 <= MX_CELL_SIZE(pBt) ); + pBt->pPage1 = pPage1; + return SQLITE_OK; + +page1_init_failed: + releasePage(pPage1); + pBt->pPage1 = 0; + return rc; +} + +/* +** This routine works like lockBtree() except that it also invokes the +** busy callback if there is lock contention. +*/ +static int lockBtreeWithRetry(Btree *pRef){ + int rc = SQLITE_OK; + if( pRef->inTrans==TRANS_NONE ){ + u8 inTransaction = pRef->pBt->inTransaction; + btreeIntegrity(pRef); + rc = sqlite3BtreeBeginTrans(pRef, 0); + pRef->pBt->inTransaction = inTransaction; + pRef->inTrans = TRANS_NONE; + if( rc==SQLITE_OK ){ + pRef->pBt->nTransaction--; + } + btreeIntegrity(pRef); + } + return rc; +} + + +/* +** If there are no outstanding cursors and we are not in the middle +** of a transaction but there is a read lock on the database, then +** this routine unrefs the first page of the database file which +** has the effect of releasing the read lock. +** +** If there are any outstanding cursors, this routine is a no-op. +** +** If there is a transaction in progress, this routine is a no-op. +*/ +static void unlockBtreeIfUnused(BtShared *pBt){ + if( pBt->inTransaction==TRANS_NONE && pBt->pCursor==0 && pBt->pPage1!=0 ){ + if( pBt->pPage1->aData==0 ){ + MemPage *pPage = pBt->pPage1; + pPage->aData = &((u8*)pPage)[-pBt->pageSize]; + pPage->pBt = pBt; + pPage->pgno = 1; + } + releasePage(pBt->pPage1); + pBt->pPage1 = 0; + pBt->inStmt = 0; + } +} + +/* +** Create a new database by initializing the first page of the +** file. +*/ +static int newDatabase(BtShared *pBt){ + MemPage *pP1; + unsigned char *data; + int rc; + if( sqlite3pager_pagecount(pBt->pPager)>0 ) return SQLITE_OK; + pP1 = pBt->pPage1; + assert( pP1!=0 ); + data = pP1->aData; + rc = sqlite3pager_write(data); + if( rc ) return rc; + memcpy(data, zMagicHeader, sizeof(zMagicHeader)); + assert( sizeof(zMagicHeader)==16 ); + put2byte(&data[16], pBt->pageSize); + data[18] = 1; + data[19] = 1; + data[20] = pBt->pageSize - pBt->usableSize; + data[21] = pBt->maxEmbedFrac; + data[22] = pBt->minEmbedFrac; + data[23] = pBt->minLeafFrac; + memset(&data[24], 0, 100-24); + zeroPage(pP1, PTF_INTKEY|PTF_LEAF|PTF_LEAFDATA ); + pBt->pageSizeFixed = 1; +#ifndef SQLITE_OMIT_AUTOVACUUM + if( pBt->autoVacuum ){ + put4byte(&data[36 + 4*4], 1); + } +#endif + return SQLITE_OK; +} + +/* +** Attempt to start a new transaction. A write-transaction +** is started if the second argument is nonzero, otherwise a read- +** transaction. If the second argument is 2 or more and exclusive +** transaction is started, meaning that no other process is allowed +** to access the database. A preexisting transaction may not be +** upgraded to exclusive by calling this routine a second time - the +** exclusivity flag only works for a new transaction. +** +** A write-transaction must be started before attempting any +** changes to the database. None of the following routines +** will work unless a transaction is started first: +** +** sqlite3BtreeCreateTable() +** sqlite3BtreeCreateIndex() +** sqlite3BtreeClearTable() +** sqlite3BtreeDropTable() +** sqlite3BtreeInsert() +** sqlite3BtreeDelete() +** sqlite3BtreeUpdateMeta() +** +** If an initial attempt to acquire the lock fails because of lock contention +** and the database was previously unlocked, then invoke the busy handler +** if there is one. But if there was previously a read-lock, do not +** invoke the busy handler - just return SQLITE_BUSY. SQLITE_BUSY is +** returned when there is already a read-lock in order to avoid a deadlock. +** +** Suppose there are two processes A and B. A has a read lock and B has +** a reserved lock. B tries to promote to exclusive but is blocked because +** of A's read lock. A tries to promote to reserved but is blocked by B. +** One or the other of the two processes must give way or there can be +** no progress. By returning SQLITE_BUSY and not invoking the busy callback +** when A already has a read lock, we encourage A to give up and let B +** proceed. +*/ +int sqlite3BtreeBeginTrans(Btree *p, int wrflag){ + BtShared *pBt = p->pBt; + int rc = SQLITE_OK; + + btreeIntegrity(p); + + /* If the btree is already in a write-transaction, or it + ** is already in a read-transaction and a read-transaction + ** is requested, this is a no-op. + */ + if( p->inTrans==TRANS_WRITE || (p->inTrans==TRANS_READ && !wrflag) ){ + return SQLITE_OK; + } + + /* Write transactions are not possible on a read-only database */ + if( pBt->readOnly && wrflag ){ + return SQLITE_READONLY; + } + + /* If another database handle has already opened a write transaction + ** on this shared-btree structure and a second write transaction is + ** requested, return SQLITE_BUSY. + */ + if( pBt->inTransaction==TRANS_WRITE && wrflag ){ + return SQLITE_BUSY; + } + + do { + if( pBt->pPage1==0 ){ + rc = lockBtree(pBt); + } + + if( rc==SQLITE_OK && wrflag ){ + rc = sqlite3pager_begin(pBt->pPage1->aData, wrflag>1); + if( rc==SQLITE_OK ){ + rc = newDatabase(pBt); + } + } + + if( rc==SQLITE_OK ){ + if( wrflag ) pBt->inStmt = 0; + }else{ + unlockBtreeIfUnused(pBt); + } + }while( rc==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE && + sqlite3InvokeBusyHandler(pBt->pBusyHandler) ); + + if( rc==SQLITE_OK ){ + if( p->inTrans==TRANS_NONE ){ + pBt->nTransaction++; + } + p->inTrans = (wrflag?TRANS_WRITE:TRANS_READ); + if( p->inTrans>pBt->inTransaction ){ + pBt->inTransaction = p->inTrans; + } + } + + btreeIntegrity(p); + return rc; +} + +#ifndef SQLITE_OMIT_AUTOVACUUM + +/* +** Set the pointer-map entries for all children of page pPage. Also, if +** pPage contains cells that point to overflow pages, set the pointer +** map entries for the overflow pages as well. +*/ +static int setChildPtrmaps(MemPage *pPage){ + int i; /* Counter variable */ + int nCell; /* Number of cells in page pPage */ + int rc = SQLITE_OK; /* Return code */ + BtShared *pBt = pPage->pBt; + int isInitOrig = pPage->isInit; + Pgno pgno = pPage->pgno; + + initPage(pPage, 0); + nCell = pPage->nCell; + + for(i=0; ileaf ){ + Pgno childPgno = get4byte(pCell); + rc = ptrmapPut(pBt, childPgno, PTRMAP_BTREE, pgno); + if( rc!=SQLITE_OK ) goto set_child_ptrmaps_out; + } + } + + if( !pPage->leaf ){ + Pgno childPgno = get4byte(&pPage->aData[pPage->hdrOffset+8]); + rc = ptrmapPut(pBt, childPgno, PTRMAP_BTREE, pgno); + } + +set_child_ptrmaps_out: + pPage->isInit = isInitOrig; + return rc; +} + +/* +** Somewhere on pPage, which is guarenteed to be a btree page, not an overflow +** page, is a pointer to page iFrom. Modify this pointer so that it points to +** iTo. Parameter eType describes the type of pointer to be modified, as +** follows: +** +** PTRMAP_BTREE: pPage is a btree-page. The pointer points at a child +** page of pPage. +** +** PTRMAP_OVERFLOW1: pPage is a btree-page. The pointer points at an overflow +** page pointed to by one of the cells on pPage. +** +** PTRMAP_OVERFLOW2: pPage is an overflow-page. The pointer points at the next +** overflow page in the list. +*/ +static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){ + if( eType==PTRMAP_OVERFLOW2 ){ + /* The pointer is always the first 4 bytes of the page in this case. */ + if( get4byte(pPage->aData)!=iFrom ){ + return SQLITE_CORRUPT_BKPT; + } + put4byte(pPage->aData, iTo); + }else{ + int isInitOrig = pPage->isInit; + int i; + int nCell; + + initPage(pPage, 0); + nCell = pPage->nCell; + + for(i=0; iaData[pPage->hdrOffset+8])!=iFrom ){ + return SQLITE_CORRUPT_BKPT; + } + put4byte(&pPage->aData[pPage->hdrOffset+8], iTo); + } + + pPage->isInit = isInitOrig; + } + return SQLITE_OK; +} + + +/* +** Move the open database page pDbPage to location iFreePage in the +** database. The pDbPage reference remains valid. +*/ +static int relocatePage( + BtShared *pBt, /* Btree */ + MemPage *pDbPage, /* Open page to move */ + u8 eType, /* Pointer map 'type' entry for pDbPage */ + Pgno iPtrPage, /* Pointer map 'page-no' entry for pDbPage */ + Pgno iFreePage /* The location to move pDbPage to */ +){ + MemPage *pPtrPage; /* The page that contains a pointer to pDbPage */ + Pgno iDbPage = pDbPage->pgno; + Pager *pPager = pBt->pPager; + int rc; + + assert( eType==PTRMAP_OVERFLOW2 || eType==PTRMAP_OVERFLOW1 || + eType==PTRMAP_BTREE || eType==PTRMAP_ROOTPAGE ); + + /* Move page iDbPage from it's current location to page number iFreePage */ + TRACE(("AUTOVACUUM: Moving %d to free page %d (ptr page %d type %d)\n", + iDbPage, iFreePage, iPtrPage, eType)); + rc = sqlite3pager_movepage(pPager, pDbPage->aData, iFreePage); + if( rc!=SQLITE_OK ){ + return rc; + } + pDbPage->pgno = iFreePage; + + /* If pDbPage was a btree-page, then it may have child pages and/or cells + ** that point to overflow pages. The pointer map entries for all these + ** pages need to be changed. + ** + ** If pDbPage is an overflow page, then the first 4 bytes may store a + ** pointer to a subsequent overflow page. If this is the case, then + ** the pointer map needs to be updated for the subsequent overflow page. + */ + if( eType==PTRMAP_BTREE || eType==PTRMAP_ROOTPAGE ){ + rc = setChildPtrmaps(pDbPage); + if( rc!=SQLITE_OK ){ + return rc; + } + }else{ + Pgno nextOvfl = get4byte(pDbPage->aData); + if( nextOvfl!=0 ){ + rc = ptrmapPut(pBt, nextOvfl, PTRMAP_OVERFLOW2, iFreePage); + if( rc!=SQLITE_OK ){ + return rc; + } + } + } + + /* Fix the database pointer on page iPtrPage that pointed at iDbPage so + ** that it points at iFreePage. Also fix the pointer map entry for + ** iPtrPage. + */ + if( eType!=PTRMAP_ROOTPAGE ){ + rc = getPage(pBt, iPtrPage, &pPtrPage); + if( rc!=SQLITE_OK ){ + return rc; + } + rc = sqlite3pager_write(pPtrPage->aData); + if( rc!=SQLITE_OK ){ + releasePage(pPtrPage); + return rc; + } + rc = modifyPagePointer(pPtrPage, iDbPage, iFreePage, eType); + releasePage(pPtrPage); + if( rc==SQLITE_OK ){ + rc = ptrmapPut(pBt, iFreePage, eType, iPtrPage); + } + } + return rc; +} + +/* Forward declaration required by autoVacuumCommit(). */ +static int allocatePage(BtShared *, MemPage **, Pgno *, Pgno, u8); + +/* +** This routine is called prior to sqlite3pager_commit when a transaction +** is commited for an auto-vacuum database. +*/ +static int autoVacuumCommit(BtShared *pBt, Pgno *nTrunc){ + Pager *pPager = pBt->pPager; + Pgno nFreeList; /* Number of pages remaining on the free-list. */ + int nPtrMap; /* Number of pointer-map pages deallocated */ + Pgno origSize; /* Pages in the database file */ + Pgno finSize; /* Pages in the database file after truncation */ + int rc; /* Return code */ + u8 eType; + int pgsz = pBt->pageSize; /* Page size for this database */ + Pgno iDbPage; /* The database page to move */ + MemPage *pDbMemPage = 0; /* "" */ + Pgno iPtrPage; /* The page that contains a pointer to iDbPage */ + Pgno iFreePage; /* The free-list page to move iDbPage to */ + MemPage *pFreeMemPage = 0; /* "" */ + +#ifndef NDEBUG + int nRef = *sqlite3pager_stats(pPager); +#endif + + assert( pBt->autoVacuum ); + if( PTRMAP_ISPAGE(pBt, sqlite3pager_pagecount(pPager)) ){ + return SQLITE_CORRUPT_BKPT; + } + + /* Figure out how many free-pages are in the database. If there are no + ** free pages, then auto-vacuum is a no-op. + */ + nFreeList = get4byte(&pBt->pPage1->aData[36]); + if( nFreeList==0 ){ + *nTrunc = 0; + return SQLITE_OK; + } + + /* This block figures out how many pages there are in the database + ** now (variable origSize), and how many there will be after the + ** truncation (variable finSize). + ** + ** The final size is the original size, less the number of free pages + ** in the database, less any pointer-map pages that will no longer + ** be required, less 1 if the pending-byte page was part of the database + ** but is not after the truncation. + **/ + origSize = sqlite3pager_pagecount(pPager); + if( origSize==PENDING_BYTE_PAGE(pBt) ){ + origSize--; + } + nPtrMap = (nFreeList-origSize+PTRMAP_PAGENO(pBt, origSize)+pgsz/5)/(pgsz/5); + finSize = origSize - nFreeList - nPtrMap; + if( origSize>PENDING_BYTE_PAGE(pBt) && finSize<=PENDING_BYTE_PAGE(pBt) ){ + finSize--; + } + while( PTRMAP_ISPAGE(pBt, finSize) || finSize==PENDING_BYTE_PAGE(pBt) ){ + finSize--; + } + TRACE(("AUTOVACUUM: Begin (db size %d->%d)\n", origSize, finSize)); + + /* Variable 'finSize' will be the size of the file in pages after + ** the auto-vacuum has completed (the current file size minus the number + ** of pages on the free list). Loop through the pages that lie beyond + ** this mark, and if they are not already on the free list, move them + ** to a free page earlier in the file (somewhere before finSize). + */ + for( iDbPage=finSize+1; iDbPage<=origSize; iDbPage++ ){ + /* If iDbPage is a pointer map page, or the pending-byte page, skip it. */ + if( PTRMAP_ISPAGE(pBt, iDbPage) || iDbPage==PENDING_BYTE_PAGE(pBt) ){ + continue; + } + + rc = ptrmapGet(pBt, iDbPage, &eType, &iPtrPage); + if( rc!=SQLITE_OK ) goto autovacuum_out; + if( eType==PTRMAP_ROOTPAGE ){ + rc = SQLITE_CORRUPT_BKPT; + goto autovacuum_out; + } + + /* If iDbPage is free, do not swap it. */ + if( eType==PTRMAP_FREEPAGE ){ + continue; + } + rc = getPage(pBt, iDbPage, &pDbMemPage); + if( rc!=SQLITE_OK ) goto autovacuum_out; + + /* Find the next page in the free-list that is not already at the end + ** of the file. A page can be pulled off the free list using the + ** allocatePage() routine. + */ + do{ + if( pFreeMemPage ){ + releasePage(pFreeMemPage); + pFreeMemPage = 0; + } + rc = allocatePage(pBt, &pFreeMemPage, &iFreePage, 0, 0); + if( rc!=SQLITE_OK ){ + releasePage(pDbMemPage); + goto autovacuum_out; + } + assert( iFreePage<=origSize ); + }while( iFreePage>finSize ); + releasePage(pFreeMemPage); + pFreeMemPage = 0; + + /* Relocate the page into the body of the file. Note that although the + ** page has moved within the database file, the pDbMemPage pointer + ** remains valid. This means that this function can run without + ** invalidating cursors open on the btree. This is important in + ** shared-cache mode. + */ + rc = relocatePage(pBt, pDbMemPage, eType, iPtrPage, iFreePage); + releasePage(pDbMemPage); + if( rc!=SQLITE_OK ) goto autovacuum_out; + } + + /* The entire free-list has been swapped to the end of the file. So + ** truncate the database file to finSize pages and consider the + ** free-list empty. + */ + rc = sqlite3pager_write(pBt->pPage1->aData); + if( rc!=SQLITE_OK ) goto autovacuum_out; + put4byte(&pBt->pPage1->aData[32], 0); + put4byte(&pBt->pPage1->aData[36], 0); + if( rc!=SQLITE_OK ) goto autovacuum_out; + *nTrunc = finSize; + assert( finSize!=PENDING_BYTE_PAGE(pBt) ); + +autovacuum_out: + assert( nRef==*sqlite3pager_stats(pPager) ); + if( rc!=SQLITE_OK ){ + sqlite3pager_rollback(pPager); + } + return rc; +} +#endif + +/* +** Commit the transaction currently in progress. +** +** This will release the write lock on the database file. If there +** are no active cursors, it also releases the read lock. +*/ +int sqlite3BtreeCommit(Btree *p){ + BtShared *pBt = p->pBt; + + btreeIntegrity(p); + + /* If the handle has a write-transaction open, commit the shared-btrees + ** transaction and set the shared state to TRANS_READ. + */ + if( p->inTrans==TRANS_WRITE ){ + int rc; + assert( pBt->inTransaction==TRANS_WRITE ); + assert( pBt->nTransaction>0 ); + rc = sqlite3pager_commit(pBt->pPager); + if( rc!=SQLITE_OK ){ + return rc; + } + pBt->inTransaction = TRANS_READ; + pBt->inStmt = 0; + } + unlockAllTables(p); + + /* If the handle has any kind of transaction open, decrement the transaction + ** count of the shared btree. If the transaction count reaches 0, set + ** the shared state to TRANS_NONE. The unlockBtreeIfUnused() call below + ** will unlock the pager. + */ + if( p->inTrans!=TRANS_NONE ){ + pBt->nTransaction--; + if( 0==pBt->nTransaction ){ + pBt->inTransaction = TRANS_NONE; + } + } + + /* Set the handles current transaction state to TRANS_NONE and unlock + ** the pager if this call closed the only read or write transaction. + */ + p->inTrans = TRANS_NONE; + unlockBtreeIfUnused(pBt); + + btreeIntegrity(p); + return SQLITE_OK; +} + +#ifndef NDEBUG +/* +** Return the number of write-cursors open on this handle. This is for use +** in assert() expressions, so it is only compiled if NDEBUG is not +** defined. +*/ +static int countWriteCursors(BtShared *pBt){ + BtCursor *pCur; + int r = 0; + for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ + if( pCur->wrFlag ) r++; + } + return r; +} +#endif + +#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) +/* +** Print debugging information about all cursors to standard output. +*/ +void sqlite3BtreeCursorList(Btree *p){ + BtCursor *pCur; + BtShared *pBt = p->pBt; + for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ + MemPage *pPage = pCur->pPage; + char *zMode = pCur->wrFlag ? "rw" : "ro"; + sqlite3DebugPrintf("CURSOR %p rooted at %4d(%s) currently at %d.%d%s\n", + pCur, pCur->pgnoRoot, zMode, + pPage ? pPage->pgno : 0, pCur->idx, + (pCur->eState==CURSOR_VALID) ? "" : " eof" + ); + } +} +#endif + +/* +** Rollback the transaction in progress. All cursors will be +** invalided by this operation. Any attempt to use a cursor +** that was open at the beginning of this operation will result +** in an error. +** +** This will release the write lock on the database file. If there +** are no active cursors, it also releases the read lock. +*/ +int sqlite3BtreeRollback(Btree *p){ + int rc; + BtShared *pBt = p->pBt; + MemPage *pPage1; + + rc = saveAllCursors(pBt, 0, 0); +#ifndef SQLITE_OMIT_SHARED_CACHE + if( rc!=SQLITE_OK ){ + /* This is a horrible situation. An IO or malloc() error occured whilst + ** trying to save cursor positions. If this is an automatic rollback (as + ** the result of a constraint, malloc() failure or IO error) then + ** the cache may be internally inconsistent (not contain valid trees) so + ** we cannot simply return the error to the caller. Instead, abort + ** all queries that may be using any of the cursors that failed to save. + */ + while( pBt->pCursor ){ + sqlite3 *db = pBt->pCursor->pBtree->pSqlite; + if( db ){ + sqlite3AbortOtherActiveVdbes(db, 0); + } + } + } +#endif + btreeIntegrity(p); + unlockAllTables(p); + + if( p->inTrans==TRANS_WRITE ){ + int rc2; + + assert( TRANS_WRITE==pBt->inTransaction ); + rc2 = sqlite3pager_rollback(pBt->pPager); + if( rc2!=SQLITE_OK ){ + rc = rc2; + } + + /* The rollback may have destroyed the pPage1->aData value. So + ** call getPage() on page 1 again to make sure pPage1->aData is + ** set correctly. */ + if( getPage(pBt, 1, &pPage1)==SQLITE_OK ){ + releasePage(pPage1); + } + assert( countWriteCursors(pBt)==0 ); + pBt->inTransaction = TRANS_READ; + } + + if( p->inTrans!=TRANS_NONE ){ + assert( pBt->nTransaction>0 ); + pBt->nTransaction--; + if( 0==pBt->nTransaction ){ + pBt->inTransaction = TRANS_NONE; + } + } + + p->inTrans = TRANS_NONE; + pBt->inStmt = 0; + unlockBtreeIfUnused(pBt); + + btreeIntegrity(p); + return rc; +} + +/* +** Start a statement subtransaction. The subtransaction can +** can be rolled back independently of the main transaction. +** You must start a transaction before starting a subtransaction. +** The subtransaction is ended automatically if the main transaction +** commits or rolls back. +** +** Only one subtransaction may be active at a time. It is an error to try +** to start a new subtransaction if another subtransaction is already active. +** +** Statement subtransactions are used around individual SQL statements +** that are contained within a BEGIN...COMMIT block. If a constraint +** error occurs within the statement, the effect of that one statement +** can be rolled back without having to rollback the entire transaction. +*/ +int sqlite3BtreeBeginStmt(Btree *p){ + int rc; + BtShared *pBt = p->pBt; + if( (p->inTrans!=TRANS_WRITE) || pBt->inStmt ){ + return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; + } + assert( pBt->inTransaction==TRANS_WRITE ); + rc = pBt->readOnly ? SQLITE_OK : sqlite3pager_stmt_begin(pBt->pPager); + pBt->inStmt = 1; + return rc; +} + + +/* +** Commit the statment subtransaction currently in progress. If no +** subtransaction is active, this is a no-op. +*/ +int sqlite3BtreeCommitStmt(Btree *p){ + int rc; + BtShared *pBt = p->pBt; + if( pBt->inStmt && !pBt->readOnly ){ + rc = sqlite3pager_stmt_commit(pBt->pPager); + }else{ + rc = SQLITE_OK; + } + pBt->inStmt = 0; + return rc; +} + +/* +** Rollback the active statement subtransaction. If no subtransaction +** is active this routine is a no-op. +** +** All cursors will be invalidated by this operation. Any attempt +** to use a cursor that was open at the beginning of this operation +** will result in an error. +*/ +int sqlite3BtreeRollbackStmt(Btree *p){ + int rc = SQLITE_OK; + BtShared *pBt = p->pBt; + sqlite3MallocDisallow(); + if( pBt->inStmt && !pBt->readOnly ){ + rc = sqlite3pager_stmt_rollback(pBt->pPager); + assert( countWriteCursors(pBt)==0 ); + pBt->inStmt = 0; + } + sqlite3MallocAllow(); + return rc; +} + +/* +** Default key comparison function to be used if no comparison function +** is specified on the sqlite3BtreeCursor() call. +*/ +static int dfltCompare( + void *NotUsed, /* User data is not used */ + int n1, const void *p1, /* First key to compare */ + int n2, const void *p2 /* Second key to compare */ +){ + int c; + c = memcmp(p1, p2, n1pBt; + + *ppCur = 0; + if( wrFlag ){ + if( pBt->readOnly ){ + return SQLITE_READONLY; + } + if( checkReadLocks(pBt, iTable, 0) ){ + return SQLITE_LOCKED; + } + } + + if( pBt->pPage1==0 ){ + rc = lockBtreeWithRetry(p); + if( rc!=SQLITE_OK ){ + return rc; + } + } + pCur = sqliteMalloc( sizeof(*pCur) ); + if( pCur==0 ){ + rc = SQLITE_NOMEM; + goto create_cursor_exception; + } + pCur->pgnoRoot = (Pgno)iTable; + if( iTable==1 && sqlite3pager_pagecount(pBt->pPager)==0 ){ + rc = SQLITE_EMPTY; + goto create_cursor_exception; + } + rc = getAndInitPage(pBt, pCur->pgnoRoot, &pCur->pPage, 0); + if( rc!=SQLITE_OK ){ + goto create_cursor_exception; + } + + /* Now that no other errors can occur, finish filling in the BtCursor + ** variables, link the cursor into the BtShared list and set *ppCur (the + ** output argument to this function). + */ + pCur->xCompare = xCmp ? xCmp : dfltCompare; + pCur->pArg = pArg; + pCur->pBtree = p; + pCur->wrFlag = wrFlag; + pCur->pNext = pBt->pCursor; + if( pCur->pNext ){ + pCur->pNext->pPrev = pCur; + } + pBt->pCursor = pCur; + pCur->eState = CURSOR_INVALID; + *ppCur = pCur; + + return SQLITE_OK; +create_cursor_exception: + if( pCur ){ + releasePage(pCur->pPage); + sqliteFree(pCur); + } + unlockBtreeIfUnused(pBt); + return rc; +} + +#if 0 /* Not Used */ +/* +** Change the value of the comparison function used by a cursor. +*/ +void sqlite3BtreeSetCompare( + BtCursor *pCur, /* The cursor to whose comparison function is changed */ + int(*xCmp)(void*,int,const void*,int,const void*), /* New comparison func */ + void *pArg /* First argument to xCmp() */ +){ + pCur->xCompare = xCmp ? xCmp : dfltCompare; + pCur->pArg = pArg; +} +#endif + +/* +** Close a cursor. The read lock on the database file is released +** when the last cursor is closed. +*/ +int sqlite3BtreeCloseCursor(BtCursor *pCur){ + BtShared *pBt = pCur->pBtree->pBt; + restoreOrClearCursorPosition(pCur, 0); + if( pCur->pPrev ){ + pCur->pPrev->pNext = pCur->pNext; + }else{ + pBt->pCursor = pCur->pNext; + } + if( pCur->pNext ){ + pCur->pNext->pPrev = pCur->pPrev; + } + releasePage(pCur->pPage); + unlockBtreeIfUnused(pBt); + sqliteFree(pCur); + return SQLITE_OK; +} + +/* +** Make a temporary cursor by filling in the fields of pTempCur. +** The temporary cursor is not on the cursor list for the Btree. +*/ +static void getTempCursor(BtCursor *pCur, BtCursor *pTempCur){ + memcpy(pTempCur, pCur, sizeof(*pCur)); + pTempCur->pNext = 0; + pTempCur->pPrev = 0; + if( pTempCur->pPage ){ + sqlite3pager_ref(pTempCur->pPage->aData); + } +} + +/* +** Delete a temporary cursor such as was made by the CreateTemporaryCursor() +** function above. +*/ +static void releaseTempCursor(BtCursor *pCur){ + if( pCur->pPage ){ + sqlite3pager_unref(pCur->pPage->aData); + } +} + +/* +** Make sure the BtCursor.info field of the given cursor is valid. +** If it is not already valid, call parseCell() to fill it in. +** +** BtCursor.info is a cache of the information in the current cell. +** Using this cache reduces the number of calls to parseCell(). +*/ +static void getCellInfo(BtCursor *pCur){ + if( pCur->info.nSize==0 ){ + parseCell(pCur->pPage, pCur->idx, &pCur->info); + }else{ +#ifndef NDEBUG + CellInfo info; + memset(&info, 0, sizeof(info)); + parseCell(pCur->pPage, pCur->idx, &info); + assert( memcmp(&info, &pCur->info, sizeof(info))==0 ); +#endif + } +} + +/* +** Set *pSize to the size of the buffer needed to hold the value of +** the key for the current entry. If the cursor is not pointing +** to a valid entry, *pSize is set to 0. +** +** For a table with the INTKEY flag set, this routine returns the key +** itself, not the number of bytes in the key. +*/ +int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){ + int rc = restoreOrClearCursorPosition(pCur, 1); + if( rc==SQLITE_OK ){ + assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID ); + if( pCur->eState==CURSOR_INVALID ){ + *pSize = 0; + }else{ + getCellInfo(pCur); + *pSize = pCur->info.nKey; + } + } + return rc; +} + +/* +** Set *pSize to the number of bytes of data in the entry the +** cursor currently points to. Always return SQLITE_OK. +** Failure is not possible. If the cursor is not currently +** pointing to an entry (which can happen, for example, if +** the database is empty) then *pSize is set to 0. +*/ +int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){ + int rc = restoreOrClearCursorPosition(pCur, 1); + if( rc==SQLITE_OK ){ + assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID ); + if( pCur->eState==CURSOR_INVALID ){ + /* Not pointing at a valid entry - set *pSize to 0. */ + *pSize = 0; + }else{ + getCellInfo(pCur); + *pSize = pCur->info.nData; + } + } + return rc; +} + +/* +** Read payload information from the entry that the pCur cursor is +** pointing to. Begin reading the payload at "offset" and read +** a total of "amt" bytes. Put the result in zBuf. +** +** This routine does not make a distinction between key and data. +** It just reads bytes from the payload area. Data might appear +** on the main page or be scattered out on multiple overflow pages. +*/ +static int getPayload( + BtCursor *pCur, /* Cursor pointing to entry to read from */ + int offset, /* Begin reading this far into payload */ + int amt, /* Read this many bytes */ + unsigned char *pBuf, /* Write the bytes into this buffer */ + int skipKey /* offset begins at data if this is true */ +){ + unsigned char *aPayload; + Pgno nextPage; + int rc; + MemPage *pPage; + BtShared *pBt; + int ovflSize; + u32 nKey; + + assert( pCur!=0 && pCur->pPage!=0 ); + assert( pCur->eState==CURSOR_VALID ); + pBt = pCur->pBtree->pBt; + pPage = pCur->pPage; + pageIntegrity(pPage); + assert( pCur->idx>=0 && pCur->idxnCell ); + getCellInfo(pCur); + aPayload = pCur->info.pCell + pCur->info.nHeader; + if( pPage->intKey ){ + nKey = 0; + }else{ + nKey = pCur->info.nKey; + } + assert( offset>=0 ); + if( skipKey ){ + offset += nKey; + } + if( offset+amt > nKey+pCur->info.nData ){ + return SQLITE_ERROR; + } + if( offsetinfo.nLocal ){ + int a = amt; + if( a+offset>pCur->info.nLocal ){ + a = pCur->info.nLocal - offset; + } + memcpy(pBuf, &aPayload[offset], a); + if( a==amt ){ + return SQLITE_OK; + } + offset = 0; + pBuf += a; + amt -= a; + }else{ + offset -= pCur->info.nLocal; + } + ovflSize = pBt->usableSize - 4; + if( amt>0 ){ + nextPage = get4byte(&aPayload[pCur->info.nLocal]); + while( amt>0 && nextPage ){ + rc = sqlite3pager_get(pBt->pPager, nextPage, (void**)&aPayload); + if( rc!=0 ){ + return rc; + } + nextPage = get4byte(aPayload); + if( offset ovflSize ){ + a = ovflSize - offset; + } + memcpy(pBuf, &aPayload[offset+4], a); + offset = 0; + amt -= a; + pBuf += a; + }else{ + offset -= ovflSize; + } + sqlite3pager_unref(aPayload); + } + } + + if( amt>0 ){ + return SQLITE_CORRUPT_BKPT; + } + return SQLITE_OK; +} + +/* +** Read part of the key associated with cursor pCur. Exactly +** "amt" bytes will be transfered into pBuf[]. The transfer +** begins at "offset". +** +** Return SQLITE_OK on success or an error code if anything goes +** wrong. An error is returned if "offset+amt" is larger than +** the available payload. +*/ +int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ + int rc = restoreOrClearCursorPosition(pCur, 1); + if( rc==SQLITE_OK ){ + assert( pCur->eState==CURSOR_VALID ); + assert( pCur->pPage!=0 ); + if( pCur->pPage->intKey ){ + return SQLITE_CORRUPT_BKPT; + } + assert( pCur->pPage->intKey==0 ); + assert( pCur->idx>=0 && pCur->idxpPage->nCell ); + rc = getPayload(pCur, offset, amt, (unsigned char*)pBuf, 0); + } + return rc; +} + +/* +** Read part of the data associated with cursor pCur. Exactly +** "amt" bytes will be transfered into pBuf[]. The transfer +** begins at "offset". +** +** Return SQLITE_OK on success or an error code if anything goes +** wrong. An error is returned if "offset+amt" is larger than +** the available payload. +*/ +int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ + int rc = restoreOrClearCursorPosition(pCur, 1); + if( rc==SQLITE_OK ){ + assert( pCur->eState==CURSOR_VALID ); + assert( pCur->pPage!=0 ); + assert( pCur->idx>=0 && pCur->idxpPage->nCell ); + rc = getPayload(pCur, offset, amt, pBuf, 1); + } + return rc; +} + +/* +** Return a pointer to payload information from the entry that the +** pCur cursor is pointing to. The pointer is to the beginning of +** the key if skipKey==0 and it points to the beginning of data if +** skipKey==1. The number of bytes of available key/data is written +** into *pAmt. If *pAmt==0, then the value returned will not be +** a valid pointer. +** +** This routine is an optimization. It is common for the entire key +** and data to fit on the local page and for there to be no overflow +** pages. When that is so, this routine can be used to access the +** key and data without making a copy. If the key and/or data spills +** onto overflow pages, then getPayload() must be used to reassembly +** the key/data and copy it into a preallocated buffer. +** +** The pointer returned by this routine looks directly into the cached +** page of the database. The data might change or move the next time +** any btree routine is called. +*/ +static const unsigned char *fetchPayload( + BtCursor *pCur, /* Cursor pointing to entry to read from */ + int *pAmt, /* Write the number of available bytes here */ + int skipKey /* read beginning at data if this is true */ +){ + unsigned char *aPayload; + MemPage *pPage; + u32 nKey; + int nLocal; + + assert( pCur!=0 && pCur->pPage!=0 ); + assert( pCur->eState==CURSOR_VALID ); + pPage = pCur->pPage; + pageIntegrity(pPage); + assert( pCur->idx>=0 && pCur->idxnCell ); + getCellInfo(pCur); + aPayload = pCur->info.pCell; + aPayload += pCur->info.nHeader; + if( pPage->intKey ){ + nKey = 0; + }else{ + nKey = pCur->info.nKey; + } + if( skipKey ){ + aPayload += nKey; + nLocal = pCur->info.nLocal - nKey; + }else{ + nLocal = pCur->info.nLocal; + if( nLocal>nKey ){ + nLocal = nKey; + } + } + *pAmt = nLocal; + return aPayload; +} + + +/* +** For the entry that cursor pCur is point to, return as +** many bytes of the key or data as are available on the local +** b-tree page. Write the number of available bytes into *pAmt. +** +** The pointer returned is ephemeral. The key/data may move +** or be destroyed on the next call to any Btree routine. +** +** These routines is used to get quick access to key and data +** in the common case where no overflow pages are used. +*/ +const void *sqlite3BtreeKeyFetch(BtCursor *pCur, int *pAmt){ + if( pCur->eState==CURSOR_VALID ){ + return (const void*)fetchPayload(pCur, pAmt, 0); + } + return 0; +} +const void *sqlite3BtreeDataFetch(BtCursor *pCur, int *pAmt){ + if( pCur->eState==CURSOR_VALID ){ + return (const void*)fetchPayload(pCur, pAmt, 1); + } + return 0; +} + + +/* +** Move the cursor down to a new child page. The newPgno argument is the +** page number of the child page to move to. +*/ +static int moveToChild(BtCursor *pCur, u32 newPgno){ + int rc; + MemPage *pNewPage; + MemPage *pOldPage; + BtShared *pBt = pCur->pBtree->pBt; + + assert( pCur->eState==CURSOR_VALID ); + rc = getAndInitPage(pBt, newPgno, &pNewPage, pCur->pPage); + if( rc ) return rc; + pageIntegrity(pNewPage); + pNewPage->idxParent = pCur->idx; + pOldPage = pCur->pPage; + pOldPage->idxShift = 0; + releasePage(pOldPage); + pCur->pPage = pNewPage; + pCur->idx = 0; + pCur->info.nSize = 0; + if( pNewPage->nCell<1 ){ + return SQLITE_CORRUPT_BKPT; + } + return SQLITE_OK; +} + +/* +** Return true if the page is the virtual root of its table. +** +** The virtual root page is the root page for most tables. But +** for the table rooted on page 1, sometime the real root page +** is empty except for the right-pointer. In such cases the +** virtual root page is the page that the right-pointer of page +** 1 is pointing to. +*/ +static int isRootPage(MemPage *pPage){ + MemPage *pParent = pPage->pParent; + if( pParent==0 ) return 1; + if( pParent->pgno>1 ) return 0; + if( get2byte(&pParent->aData[pParent->hdrOffset+3])==0 ) return 1; + return 0; +} + +/* +** Move the cursor up to the parent page. +** +** pCur->idx is set to the cell index that contains the pointer +** to the page we are coming from. If we are coming from the +** right-most child page then pCur->idx is set to one more than +** the largest cell index. +*/ +static void moveToParent(BtCursor *pCur){ + MemPage *pParent; + MemPage *pPage; + int idxParent; + + assert( pCur->eState==CURSOR_VALID ); + pPage = pCur->pPage; + assert( pPage!=0 ); + assert( !isRootPage(pPage) ); + pageIntegrity(pPage); + pParent = pPage->pParent; + assert( pParent!=0 ); + pageIntegrity(pParent); + idxParent = pPage->idxParent; + sqlite3pager_ref(pParent->aData); + releasePage(pPage); + pCur->pPage = pParent; + pCur->info.nSize = 0; + assert( pParent->idxShift==0 ); + pCur->idx = idxParent; +} + +/* +** Move the cursor to the root page +*/ +static int moveToRoot(BtCursor *pCur){ + MemPage *pRoot; + int rc = SQLITE_OK; + BtShared *pBt = pCur->pBtree->pBt; + + restoreOrClearCursorPosition(pCur, 0); + pRoot = pCur->pPage; + if( pRoot && pRoot->pgno==pCur->pgnoRoot ){ + assert( pRoot->isInit ); + }else{ + if( + SQLITE_OK!=(rc = getAndInitPage(pBt, pCur->pgnoRoot, &pRoot, 0)) + ){ + pCur->eState = CURSOR_INVALID; + return rc; + } + releasePage(pCur->pPage); + pageIntegrity(pRoot); + pCur->pPage = pRoot; + } + pCur->idx = 0; + pCur->info.nSize = 0; + if( pRoot->nCell==0 && !pRoot->leaf ){ + Pgno subpage; + assert( pRoot->pgno==1 ); + subpage = get4byte(&pRoot->aData[pRoot->hdrOffset+8]); + assert( subpage>0 ); + pCur->eState = CURSOR_VALID; + rc = moveToChild(pCur, subpage); + } + pCur->eState = ((pCur->pPage->nCell>0)?CURSOR_VALID:CURSOR_INVALID); + return rc; +} + +/* +** Move the cursor down to the left-most leaf entry beneath the +** entry to which it is currently pointing. +** +** The left-most leaf is the one with the smallest key - the first +** in ascending order. +*/ +static int moveToLeftmost(BtCursor *pCur){ + Pgno pgno; + int rc; + MemPage *pPage; + + assert( pCur->eState==CURSOR_VALID ); + while( !(pPage = pCur->pPage)->leaf ){ + assert( pCur->idx>=0 && pCur->idxnCell ); + pgno = get4byte(findCell(pPage, pCur->idx)); + rc = moveToChild(pCur, pgno); + if( rc ) return rc; + } + return SQLITE_OK; +} + +/* +** Move the cursor down to the right-most leaf entry beneath the +** page to which it is currently pointing. Notice the difference +** between moveToLeftmost() and moveToRightmost(). moveToLeftmost() +** finds the left-most entry beneath the *entry* whereas moveToRightmost() +** finds the right-most entry beneath the *page*. +** +** The right-most entry is the one with the largest key - the last +** key in ascending order. +*/ +static int moveToRightmost(BtCursor *pCur){ + Pgno pgno; + int rc; + MemPage *pPage; + + assert( pCur->eState==CURSOR_VALID ); + while( !(pPage = pCur->pPage)->leaf ){ + pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]); + pCur->idx = pPage->nCell; + rc = moveToChild(pCur, pgno); + if( rc ) return rc; + } + pCur->idx = pPage->nCell - 1; + pCur->info.nSize = 0; + return SQLITE_OK; +} + +/* Move the cursor to the first entry in the table. Return SQLITE_OK +** on success. Set *pRes to 0 if the cursor actually points to something +** or set *pRes to 1 if the table is empty. +*/ +int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){ + int rc; + rc = moveToRoot(pCur); + if( rc ) return rc; + if( pCur->eState==CURSOR_INVALID ){ + assert( pCur->pPage->nCell==0 ); + *pRes = 1; + return SQLITE_OK; + } + assert( pCur->pPage->nCell>0 ); + *pRes = 0; + rc = moveToLeftmost(pCur); + return rc; +} + +/* Move the cursor to the last entry in the table. Return SQLITE_OK +** on success. Set *pRes to 0 if the cursor actually points to something +** or set *pRes to 1 if the table is empty. +*/ +int sqlite3BtreeLast(BtCursor *pCur, int *pRes){ + int rc; + rc = moveToRoot(pCur); + if( rc ) return rc; + if( CURSOR_INVALID==pCur->eState ){ + assert( pCur->pPage->nCell==0 ); + *pRes = 1; + return SQLITE_OK; + } + assert( pCur->eState==CURSOR_VALID ); + *pRes = 0; + rc = moveToRightmost(pCur); + return rc; +} + +/* Move the cursor so that it points to an entry near pKey/nKey. +** Return a success code. +** +** For INTKEY tables, only the nKey parameter is used. pKey is +** ignored. For other tables, nKey is the number of bytes of data +** in pKey. The comparison function specified when the cursor was +** created is used to compare keys. +** +** 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 +** were present. The cursor might point to an entry that comes +** before or after the key. +** +** The result of comparing the key with the entry to which the +** cursor is written to *pRes if pRes!=NULL. The meaning of +** this value is as follows: +** +** *pRes<0 The cursor is left pointing at an entry that +** is smaller than pKey or if the table is empty +** and the cursor is therefore left point to nothing. +** +** *pRes==0 The cursor is left pointing at an entry that +** exactly matches pKey. +** +** *pRes>0 The cursor is left pointing at an entry that +** is larger than pKey. +*/ +int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, i64 nKey, int *pRes){ + int rc; + int tryRightmost; + rc = moveToRoot(pCur); + if( rc ) return rc; + assert( pCur->pPage ); + assert( pCur->pPage->isInit ); + tryRightmost = pCur->pPage->intKey; + if( pCur->eState==CURSOR_INVALID ){ + *pRes = -1; + assert( pCur->pPage->nCell==0 ); + return SQLITE_OK; + } + for(;;){ + int lwr, upr; + Pgno chldPg; + MemPage *pPage = pCur->pPage; + int c = -1; /* pRes return if table is empty must be -1 */ + lwr = 0; + upr = pPage->nCell-1; + if( !pPage->intKey && pKey==0 ){ + return SQLITE_CORRUPT_BKPT; + } + pageIntegrity(pPage); + while( lwr<=upr ){ + void *pCellKey; + i64 nCellKey; + pCur->idx = (lwr+upr)/2; + pCur->info.nSize = 0; + if( pPage->intKey ){ + u8 *pCell; + if( tryRightmost ){ + pCur->idx = upr; + } + pCell = findCell(pPage, pCur->idx) + pPage->childPtrSize; + if( pPage->hasData ){ + u32 dummy; + pCell += getVarint32(pCell, &dummy); + } + getVarint(pCell, (u64 *)&nCellKey); + if( nCellKeynKey ){ + c = +1; + tryRightmost = 0; + }else{ + c = 0; + } + }else{ + int available; + pCellKey = (void *)fetchPayload(pCur, &available, 0); + nCellKey = pCur->info.nKey; + if( available>=nCellKey ){ + c = pCur->xCompare(pCur->pArg, nCellKey, pCellKey, nKey, pKey); + }else{ + pCellKey = sqliteMallocRaw( nCellKey ); + if( pCellKey==0 ) return SQLITE_NOMEM; + rc = sqlite3BtreeKey(pCur, 0, nCellKey, (void *)pCellKey); + c = pCur->xCompare(pCur->pArg, nCellKey, pCellKey, nKey, pKey); + sqliteFree(pCellKey); + if( rc ) return rc; + } + } + if( c==0 ){ + if( pPage->leafData && !pPage->leaf ){ + lwr = pCur->idx; + upr = lwr - 1; + break; + }else{ + if( pRes ) *pRes = 0; + return SQLITE_OK; + } + } + if( c<0 ){ + lwr = pCur->idx+1; + }else{ + upr = pCur->idx-1; + } + } + assert( lwr==upr+1 ); + assert( pPage->isInit ); + if( pPage->leaf ){ + chldPg = 0; + }else if( lwr>=pPage->nCell ){ + chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]); + }else{ + chldPg = get4byte(findCell(pPage, lwr)); + } + if( chldPg==0 ){ + assert( pCur->idx>=0 && pCur->idxpPage->nCell ); + if( pRes ) *pRes = c; + return SQLITE_OK; + } + pCur->idx = lwr; + pCur->info.nSize = 0; + rc = moveToChild(pCur, chldPg); + if( rc ){ + return rc; + } + } + /* NOT REACHED */ +} + +/* +** Return TRUE if the cursor is not pointing at an entry of the table. +** +** TRUE will be returned after a call to sqlite3BtreeNext() moves +** past the last entry in the table or sqlite3BtreePrev() moves past +** the first entry. TRUE is also returned if the table is empty. +*/ +int sqlite3BtreeEof(BtCursor *pCur){ + /* TODO: What if the cursor is in CURSOR_REQUIRESEEK but all table entries + ** have been deleted? This API will need to change to return an error code + ** as well as the boolean result value. + */ + return (CURSOR_VALID!=pCur->eState); +} + +/* +** Advance the cursor to the next entry in the database. If +** successful then set *pRes=0. If the cursor +** was already pointing to the last entry in the database before +** this routine was called, then set *pRes=1. +*/ +int sqlite3BtreeNext(BtCursor *pCur, int *pRes){ + int rc; + MemPage *pPage; + +#ifndef SQLITE_OMIT_SHARED_CACHE + rc = restoreOrClearCursorPosition(pCur, 1); + if( rc!=SQLITE_OK ){ + return rc; + } + if( pCur->skip>0 ){ + pCur->skip = 0; + *pRes = 0; + return SQLITE_OK; + } + pCur->skip = 0; +#endif + + assert( pRes!=0 ); + pPage = pCur->pPage; + if( CURSOR_INVALID==pCur->eState ){ + *pRes = 1; + return SQLITE_OK; + } + assert( pPage->isInit ); + assert( pCur->idxnCell ); + + pCur->idx++; + pCur->info.nSize = 0; + if( pCur->idx>=pPage->nCell ){ + if( !pPage->leaf ){ + rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8])); + if( rc ) return rc; + rc = moveToLeftmost(pCur); + *pRes = 0; + return rc; + } + do{ + if( isRootPage(pPage) ){ + *pRes = 1; + pCur->eState = CURSOR_INVALID; + return SQLITE_OK; + } + moveToParent(pCur); + pPage = pCur->pPage; + }while( pCur->idx>=pPage->nCell ); + *pRes = 0; + if( pPage->leafData ){ + rc = sqlite3BtreeNext(pCur, pRes); + }else{ + rc = SQLITE_OK; + } + return rc; + } + *pRes = 0; + if( pPage->leaf ){ + return SQLITE_OK; + } + rc = moveToLeftmost(pCur); + return rc; +} + +/* +** Step the cursor to the back to the previous entry in the database. If +** successful then set *pRes=0. If the cursor +** was already pointing to the first entry in the database before +** this routine was called, then set *pRes=1. +*/ +int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){ + int rc; + Pgno pgno; + MemPage *pPage; + +#ifndef SQLITE_OMIT_SHARED_CACHE + rc = restoreOrClearCursorPosition(pCur, 1); + if( rc!=SQLITE_OK ){ + return rc; + } + if( pCur->skip<0 ){ + pCur->skip = 0; + *pRes = 0; + return SQLITE_OK; + } + pCur->skip = 0; +#endif + + if( CURSOR_INVALID==pCur->eState ){ + *pRes = 1; + return SQLITE_OK; + } + + pPage = pCur->pPage; + assert( pPage->isInit ); + assert( pCur->idx>=0 ); + if( !pPage->leaf ){ + pgno = get4byte( findCell(pPage, pCur->idx) ); + rc = moveToChild(pCur, pgno); + if( rc ) return rc; + rc = moveToRightmost(pCur); + }else{ + while( pCur->idx==0 ){ + if( isRootPage(pPage) ){ + pCur->eState = CURSOR_INVALID; + *pRes = 1; + return SQLITE_OK; + } + moveToParent(pCur); + pPage = pCur->pPage; + } + pCur->idx--; + pCur->info.nSize = 0; + if( pPage->leafData && !pPage->leaf ){ + rc = sqlite3BtreePrevious(pCur, pRes); + }else{ + rc = SQLITE_OK; + } + } + *pRes = 0; + return rc; +} + +/* +** Allocate a new page from the database file. +** +** The new page is marked as dirty. (In other words, sqlite3pager_write() +** has already been called on the new page.) The new page has also +** been referenced and the calling routine is responsible for calling +** sqlite3pager_unref() on the new page when it is done. +** +** SQLITE_OK is returned on success. Any other return value indicates +** an error. *ppPage and *pPgno are undefined in the event of an error. +** Do not invoke sqlite3pager_unref() on *ppPage if an error is returned. +** +** If the "nearby" parameter is not 0, then a (feeble) effort is made to +** locate a page close to the page number "nearby". This can be used in an +** attempt to keep related pages close to each other in the database file, +** which in turn can make database access faster. +** +** If the "exact" parameter is not 0, and the page-number nearby exists +** anywhere on the free-list, then it is guarenteed to be returned. This +** is only used by auto-vacuum databases when allocating a new table. +*/ +static int allocatePage( + BtShared *pBt, + MemPage **ppPage, + Pgno *pPgno, + Pgno nearby, + u8 exact +){ + MemPage *pPage1; + int rc; + int n; /* Number of pages on the freelist */ + int k; /* Number of leaves on the trunk of the freelist */ + + pPage1 = pBt->pPage1; + n = get4byte(&pPage1->aData[36]); + if( n>0 ){ + /* There are pages on the freelist. Reuse one of those pages. */ + MemPage *pTrunk = 0; + Pgno iTrunk; + MemPage *pPrevTrunk = 0; + u8 searchList = 0; /* If the free-list must be searched for 'nearby' */ + + /* If the 'exact' parameter was true and a query of the pointer-map + ** shows that the page 'nearby' is somewhere on the free-list, then + ** the entire-list will be searched for that page. + */ +#ifndef SQLITE_OMIT_AUTOVACUUM + if( exact ){ + u8 eType; + assert( nearby>0 ); + assert( pBt->autoVacuum ); + rc = ptrmapGet(pBt, nearby, &eType, 0); + if( rc ) return rc; + if( eType==PTRMAP_FREEPAGE ){ + searchList = 1; + } + *pPgno = nearby; + } +#endif + + /* Decrement the free-list count by 1. Set iTrunk to the index of the + ** first free-list trunk page. iPrevTrunk is initially 1. + */ + rc = sqlite3pager_write(pPage1->aData); + if( rc ) return rc; + put4byte(&pPage1->aData[36], n-1); + + /* The code within this loop is run only once if the 'searchList' variable + ** is not true. Otherwise, it runs once for each trunk-page on the + ** free-list until the page 'nearby' is located. + */ + do { + pPrevTrunk = pTrunk; + if( pPrevTrunk ){ + iTrunk = get4byte(&pPrevTrunk->aData[0]); + }else{ + iTrunk = get4byte(&pPage1->aData[32]); + } + rc = getPage(pBt, iTrunk, &pTrunk); + if( rc ){ + releasePage(pPrevTrunk); + return rc; + } + + /* TODO: This should move to after the loop? */ + rc = sqlite3pager_write(pTrunk->aData); + if( rc ){ + releasePage(pTrunk); + releasePage(pPrevTrunk); + return rc; + } + + k = get4byte(&pTrunk->aData[4]); + if( k==0 && !searchList ){ + /* The trunk has no leaves and the list is not being searched. + ** So extract the trunk page itself and use it as the newly + ** allocated page */ + assert( pPrevTrunk==0 ); + *pPgno = iTrunk; + memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4); + *ppPage = pTrunk; + pTrunk = 0; + TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1)); + }else if( k>pBt->usableSize/4 - 8 ){ + /* Value of k is out of range. Database corruption */ + return SQLITE_CORRUPT_BKPT; +#ifndef SQLITE_OMIT_AUTOVACUUM + }else if( searchList && nearby==iTrunk ){ + /* The list is being searched and this trunk page is the page + ** to allocate, regardless of whether it has leaves. + */ + assert( *pPgno==iTrunk ); + *ppPage = pTrunk; + searchList = 0; + if( k==0 ){ + if( !pPrevTrunk ){ + memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4); + }else{ + memcpy(&pPrevTrunk->aData[0], &pTrunk->aData[0], 4); + } + }else{ + /* The trunk page is required by the caller but it contains + ** pointers to free-list leaves. The first leaf becomes a trunk + ** page in this case. + */ + MemPage *pNewTrunk; + Pgno iNewTrunk = get4byte(&pTrunk->aData[8]); + rc = getPage(pBt, iNewTrunk, &pNewTrunk); + if( rc!=SQLITE_OK ){ + releasePage(pTrunk); + releasePage(pPrevTrunk); + return rc; + } + rc = sqlite3pager_write(pNewTrunk->aData); + if( rc!=SQLITE_OK ){ + releasePage(pNewTrunk); + releasePage(pTrunk); + releasePage(pPrevTrunk); + return rc; + } + memcpy(&pNewTrunk->aData[0], &pTrunk->aData[0], 4); + put4byte(&pNewTrunk->aData[4], k-1); + memcpy(&pNewTrunk->aData[8], &pTrunk->aData[12], (k-1)*4); + if( !pPrevTrunk ){ + put4byte(&pPage1->aData[32], iNewTrunk); + }else{ + put4byte(&pPrevTrunk->aData[0], iNewTrunk); + } + releasePage(pNewTrunk); + } + pTrunk = 0; + TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1)); +#endif + }else{ + /* Extract a leaf from the trunk */ + int closest; + Pgno iPage; + unsigned char *aData = pTrunk->aData; + if( nearby>0 ){ + int i, dist; + closest = 0; + dist = get4byte(&aData[8]) - nearby; + if( dist<0 ) dist = -dist; + for(i=1; isqlite3pager_pagecount(pBt->pPager) ){ + /* Free page off the end of the file */ + return SQLITE_CORRUPT_BKPT; + } + TRACE(("ALLOCATE: %d was leaf %d of %d on trunk %d" + ": %d more free pages\n", + *pPgno, closest+1, k, pTrunk->pgno, n-1)); + if( closestaData); + rc = sqlite3pager_write((*ppPage)->aData); + if( rc!=SQLITE_OK ){ + releasePage(*ppPage); + } + } + searchList = 0; + } + } + releasePage(pPrevTrunk); + }while( searchList ); + releasePage(pTrunk); + }else{ + /* There are no pages on the freelist, so create a new page at the + ** end of the file */ + *pPgno = sqlite3pager_pagecount(pBt->pPager) + 1; + +#ifndef SQLITE_OMIT_AUTOVACUUM + if( pBt->autoVacuum && PTRMAP_ISPAGE(pBt, *pPgno) ){ + /* If *pPgno refers to a pointer-map page, allocate two new pages + ** at the end of the file instead of one. The first allocated page + ** becomes a new pointer-map page, the second is used by the caller. + */ + TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", *pPgno)); + assert( *pPgno!=PENDING_BYTE_PAGE(pBt) ); + (*pPgno)++; + } +#endif + + assert( *pPgno!=PENDING_BYTE_PAGE(pBt) ); + rc = getPage(pBt, *pPgno, ppPage); + if( rc ) return rc; + rc = sqlite3pager_write((*ppPage)->aData); + if( rc!=SQLITE_OK ){ + releasePage(*ppPage); + } + TRACE(("ALLOCATE: %d from end of file\n", *pPgno)); + } + + assert( *pPgno!=PENDING_BYTE_PAGE(pBt) ); + return rc; +} + +/* +** Add a page of the database file to the freelist. +** +** sqlite3pager_unref() is NOT called for pPage. +*/ +static int freePage(MemPage *pPage){ + BtShared *pBt = pPage->pBt; + MemPage *pPage1 = pBt->pPage1; + int rc, n, k; + + /* Prepare the page for freeing */ + assert( pPage->pgno>1 ); + pPage->isInit = 0; + releasePage(pPage->pParent); + pPage->pParent = 0; + + /* Increment the free page count on pPage1 */ + rc = sqlite3pager_write(pPage1->aData); + if( rc ) return rc; + n = get4byte(&pPage1->aData[36]); + put4byte(&pPage1->aData[36], n+1); + +#ifndef SQLITE_OMIT_AUTOVACUUM + /* If the database supports auto-vacuum, write an entry in the pointer-map + ** to indicate that the page is free. + */ + if( pBt->autoVacuum ){ + rc = ptrmapPut(pBt, pPage->pgno, PTRMAP_FREEPAGE, 0); + if( rc ) return rc; + } +#endif + + if( n==0 ){ + /* This is the first free page */ + rc = sqlite3pager_write(pPage->aData); + if( rc ) return rc; + memset(pPage->aData, 0, 8); + put4byte(&pPage1->aData[32], pPage->pgno); + TRACE(("FREE-PAGE: %d first\n", pPage->pgno)); + }else{ + /* Other free pages already exist. Retrive the first trunk page + ** of the freelist and find out how many leaves it has. */ + MemPage *pTrunk; + rc = getPage(pBt, get4byte(&pPage1->aData[32]), &pTrunk); + if( rc ) return rc; + k = get4byte(&pTrunk->aData[4]); + if( k>=pBt->usableSize/4 - 8 ){ + /* The trunk is full. Turn the page being freed into a new + ** trunk page with no leaves. */ + rc = sqlite3pager_write(pPage->aData); + if( rc ) return rc; + put4byte(pPage->aData, pTrunk->pgno); + put4byte(&pPage->aData[4], 0); + put4byte(&pPage1->aData[32], pPage->pgno); + TRACE(("FREE-PAGE: %d new trunk page replacing %d\n", + pPage->pgno, pTrunk->pgno)); + }else{ + /* Add the newly freed page as a leaf on the current trunk */ + rc = sqlite3pager_write(pTrunk->aData); + if( rc ) return rc; + put4byte(&pTrunk->aData[4], k+1); + put4byte(&pTrunk->aData[8+k*4], pPage->pgno); + sqlite3pager_dont_write(pBt->pPager, pPage->pgno); + TRACE(("FREE-PAGE: %d leaf on trunk page %d\n",pPage->pgno,pTrunk->pgno)); + } + releasePage(pTrunk); + } + return rc; +} + +/* +** Free any overflow pages associated with the given Cell. +*/ +static int clearCell(MemPage *pPage, unsigned char *pCell){ + BtShared *pBt = pPage->pBt; + CellInfo info; + Pgno ovflPgno; + int rc; + + parseCellPtr(pPage, pCell, &info); + if( info.iOverflow==0 ){ + return SQLITE_OK; /* No overflow pages. Return without doing anything */ + } + ovflPgno = get4byte(&pCell[info.iOverflow]); + while( ovflPgno!=0 ){ + MemPage *pOvfl; + if( ovflPgno>sqlite3pager_pagecount(pBt->pPager) ){ + return SQLITE_CORRUPT_BKPT; + } + rc = getPage(pBt, ovflPgno, &pOvfl); + if( rc ) return rc; + ovflPgno = get4byte(pOvfl->aData); + rc = freePage(pOvfl); + sqlite3pager_unref(pOvfl->aData); + if( rc ) return rc; + } + return SQLITE_OK; +} + +/* +** Create the byte sequence used to represent a cell on page pPage +** and write that byte sequence into pCell[]. Overflow pages are +** allocated and filled in as necessary. The calling procedure +** is responsible for making sure sufficient space has been allocated +** for pCell[]. +** +** Note that pCell does not necessary need to point to the pPage->aData +** area. pCell might point to some temporary storage. The cell will +** be constructed in this temporary area then copied into pPage->aData +** later. +*/ +static int fillInCell( + MemPage *pPage, /* The page that contains the cell */ + unsigned char *pCell, /* Complete text of the cell */ + const void *pKey, i64 nKey, /* The key */ + const void *pData,int nData, /* The data */ + int *pnSize /* Write cell size here */ +){ + int nPayload; + const u8 *pSrc; + int nSrc, n, rc; + int spaceLeft; + MemPage *pOvfl = 0; + MemPage *pToRelease = 0; + unsigned char *pPrior; + unsigned char *pPayload; + BtShared *pBt = pPage->pBt; + Pgno pgnoOvfl = 0; + int nHeader; + CellInfo info; + + /* Fill in the header. */ + nHeader = 0; + if( !pPage->leaf ){ + nHeader += 4; + } + if( pPage->hasData ){ + nHeader += putVarint(&pCell[nHeader], nData); + }else{ + nData = 0; + } + nHeader += putVarint(&pCell[nHeader], *(u64*)&nKey); + parseCellPtr(pPage, pCell, &info); + assert( info.nHeader==nHeader ); + assert( info.nKey==nKey ); + assert( info.nData==nData ); + + /* Fill in the payload */ + nPayload = nData; + if( pPage->intKey ){ + pSrc = pData; + nSrc = nData; + nData = 0; + }else{ + nPayload += nKey; + pSrc = pKey; + nSrc = nKey; + } + *pnSize = info.nSize; + spaceLeft = info.nLocal; + pPayload = &pCell[nHeader]; + pPrior = &pCell[info.iOverflow]; + + while( nPayload>0 ){ + if( spaceLeft==0 ){ +#ifndef SQLITE_OMIT_AUTOVACUUM + Pgno pgnoPtrmap = pgnoOvfl; /* Overflow page pointer-map entry page */ +#endif + rc = allocatePage(pBt, &pOvfl, &pgnoOvfl, pgnoOvfl, 0); +#ifndef SQLITE_OMIT_AUTOVACUUM + /* If the database supports auto-vacuum, and the second or subsequent + ** overflow page is being allocated, add an entry to the pointer-map + ** for that page now. The entry for the first overflow page will be + ** added later, by the insertCell() routine. + */ + if( pBt->autoVacuum && pgnoPtrmap!=0 && rc==SQLITE_OK ){ + rc = ptrmapPut(pBt, pgnoOvfl, PTRMAP_OVERFLOW2, pgnoPtrmap); + } +#endif + if( rc ){ + releasePage(pToRelease); + /* clearCell(pPage, pCell); */ + return rc; + } + put4byte(pPrior, pgnoOvfl); + releasePage(pToRelease); + pToRelease = pOvfl; + pPrior = pOvfl->aData; + put4byte(pPrior, 0); + pPayload = &pOvfl->aData[4]; + spaceLeft = pBt->usableSize - 4; + } + n = nPayload; + if( n>spaceLeft ) n = spaceLeft; + if( n>nSrc ) n = nSrc; + memcpy(pPayload, pSrc, n); + nPayload -= n; + pPayload += n; + pSrc += n; + nSrc -= n; + spaceLeft -= n; + if( nSrc==0 ){ + nSrc = nData; + pSrc = pData; + } + } + releasePage(pToRelease); + return SQLITE_OK; +} + +/* +** Change the MemPage.pParent pointer on the page whose number is +** given in the second argument so that MemPage.pParent holds the +** pointer in the third argument. +*/ +static int reparentPage(BtShared *pBt, Pgno pgno, MemPage *pNewParent, int idx){ + MemPage *pThis; + unsigned char *aData; + + if( pgno==0 ) return SQLITE_OK; + assert( pBt->pPager!=0 ); + aData = sqlite3pager_lookup(pBt->pPager, pgno); + if( aData ){ + pThis = (MemPage*)&aData[pBt->pageSize]; + assert( pThis->aData==aData ); + if( pThis->isInit ){ + if( pThis->pParent!=pNewParent ){ + if( pThis->pParent ) sqlite3pager_unref(pThis->pParent->aData); + pThis->pParent = pNewParent; + if( pNewParent ) sqlite3pager_ref(pNewParent->aData); + } + pThis->idxParent = idx; + } + sqlite3pager_unref(aData); + } + +#ifndef SQLITE_OMIT_AUTOVACUUM + if( pBt->autoVacuum ){ + return ptrmapPut(pBt, pgno, PTRMAP_BTREE, pNewParent->pgno); + } +#endif + return SQLITE_OK; +} + + + +/* +** Change the pParent pointer of all children of pPage to point back +** to pPage. +** +** In other words, for every child of pPage, invoke reparentPage() +** to make sure that each child knows that pPage is its parent. +** +** This routine gets called after you memcpy() one page into +** another. +*/ +static int reparentChildPages(MemPage *pPage){ + int i; + BtShared *pBt = pPage->pBt; + int rc = SQLITE_OK; + + if( pPage->leaf ) return SQLITE_OK; + + for(i=0; inCell; i++){ + u8 *pCell = findCell(pPage, i); + if( !pPage->leaf ){ + rc = reparentPage(pBt, get4byte(pCell), pPage, i); + if( rc!=SQLITE_OK ) return rc; + } + } + if( !pPage->leaf ){ + rc = reparentPage(pBt, get4byte(&pPage->aData[pPage->hdrOffset+8]), + pPage, i); + pPage->idxShift = 0; + } + return rc; +} + +/* +** Remove the i-th cell from pPage. This routine effects pPage only. +** The cell content is not freed or deallocated. It is assumed that +** the cell content has been copied someplace else. This routine just +** removes the reference to the cell from pPage. +** +** "sz" must be the number of bytes in the cell. +*/ +static void dropCell(MemPage *pPage, int idx, int sz){ + int i; /* Loop counter */ + int pc; /* Offset to cell content of cell being deleted */ + u8 *data; /* pPage->aData */ + u8 *ptr; /* Used to move bytes around within data[] */ + + assert( idx>=0 && idxnCell ); + assert( sz==cellSize(pPage, idx) ); + assert( sqlite3pager_iswriteable(pPage->aData) ); + data = pPage->aData; + ptr = &data[pPage->cellOffset + 2*idx]; + pc = get2byte(ptr); + assert( pc>10 && pc+sz<=pPage->pBt->usableSize ); + freeSpace(pPage, pc, sz); + for(i=idx+1; inCell; i++, ptr+=2){ + ptr[0] = ptr[2]; + ptr[1] = ptr[3]; + } + pPage->nCell--; + put2byte(&data[pPage->hdrOffset+3], pPage->nCell); + pPage->nFree += 2; + pPage->idxShift = 1; +} + +/* +** Insert a new cell on pPage at cell index "i". pCell points to the +** content of the cell. +** +** If the cell content will fit on the page, then put it there. If it +** will not fit, then make a copy of the cell content into pTemp if +** pTemp is not null. Regardless of pTemp, allocate a new entry +** in pPage->aOvfl[] and make it point to the cell content (either +** in pTemp or the original pCell) and also record its index. +** Allocating a new entry in pPage->aCell[] implies that +** pPage->nOverflow is incremented. +** +** If nSkip is non-zero, then do not copy the first nSkip bytes of the +** cell. The caller will overwrite them after this function returns. If +** nSkip is non-zero, then pCell may not point to an invalid memory location +** (but pCell+nSkip is always valid). +*/ +static int insertCell( + MemPage *pPage, /* Page into which we are copying */ + int i, /* New cell becomes the i-th cell of the page */ + u8 *pCell, /* Content of the new cell */ + int sz, /* Bytes of content in pCell */ + u8 *pTemp, /* Temp storage space for pCell, if needed */ + u8 nSkip /* Do not write the first nSkip bytes of the cell */ +){ + int idx; /* Where to write new cell content in data[] */ + int j; /* Loop counter */ + int top; /* First byte of content for any cell in data[] */ + int end; /* First byte past the last cell pointer in data[] */ + int ins; /* Index in data[] where new cell pointer is inserted */ + int hdr; /* Offset into data[] of the page header */ + int cellOffset; /* Address of first cell pointer in data[] */ + u8 *data; /* The content of the whole page */ + u8 *ptr; /* Used for moving information around in data[] */ + + assert( i>=0 && i<=pPage->nCell+pPage->nOverflow ); + assert( sz==cellSizePtr(pPage, pCell) ); + assert( sqlite3pager_iswriteable(pPage->aData) ); + if( pPage->nOverflow || sz+2>pPage->nFree ){ + if( pTemp ){ + memcpy(pTemp+nSkip, pCell+nSkip, sz-nSkip); + pCell = pTemp; + } + j = pPage->nOverflow++; + assert( jaOvfl)/sizeof(pPage->aOvfl[0]) ); + pPage->aOvfl[j].pCell = pCell; + pPage->aOvfl[j].idx = i; + pPage->nFree = 0; + }else{ + data = pPage->aData; + hdr = pPage->hdrOffset; + top = get2byte(&data[hdr+5]); + cellOffset = pPage->cellOffset; + end = cellOffset + 2*pPage->nCell + 2; + ins = cellOffset + 2*i; + if( end > top - sz ){ + int rc = defragmentPage(pPage); + if( rc!=SQLITE_OK ) return rc; + top = get2byte(&data[hdr+5]); + assert( end + sz <= top ); + } + idx = allocateSpace(pPage, sz); + assert( idx>0 ); + assert( end <= get2byte(&data[hdr+5]) ); + pPage->nCell++; + pPage->nFree -= 2; + memcpy(&data[idx+nSkip], pCell+nSkip, sz-nSkip); + for(j=end-2, ptr=&data[j]; j>ins; j-=2, ptr-=2){ + ptr[0] = ptr[-2]; + ptr[1] = ptr[-1]; + } + put2byte(&data[ins], idx); + put2byte(&data[hdr+3], pPage->nCell); + pPage->idxShift = 1; + pageIntegrity(pPage); +#ifndef SQLITE_OMIT_AUTOVACUUM + if( pPage->pBt->autoVacuum ){ + /* The cell may contain a pointer to an overflow page. If so, write + ** the entry for the overflow page into the pointer map. + */ + CellInfo info; + parseCellPtr(pPage, pCell, &info); + if( (info.nData+(pPage->intKey?0:info.nKey))>info.nLocal ){ + Pgno pgnoOvfl = get4byte(&pCell[info.iOverflow]); + int rc = ptrmapPut(pPage->pBt, pgnoOvfl, PTRMAP_OVERFLOW1, pPage->pgno); + if( rc!=SQLITE_OK ) return rc; + } + } +#endif + } + + return SQLITE_OK; +} + +/* +** Add a list of cells to a page. The page should be initially empty. +** The cells are guaranteed to fit on the page. +*/ +static void assemblePage( + MemPage *pPage, /* The page to be assemblied */ + int nCell, /* The number of cells to add to this page */ + u8 **apCell, /* Pointers to cell bodies */ + int *aSize /* Sizes of the cells */ +){ + int i; /* Loop counter */ + int totalSize; /* Total size of all cells */ + int hdr; /* Index of page header */ + int cellptr; /* Address of next cell pointer */ + int cellbody; /* Address of next cell body */ + u8 *data; /* Data for the page */ + + assert( pPage->nOverflow==0 ); + totalSize = 0; + for(i=0; inFree ); + assert( pPage->nCell==0 ); + cellptr = pPage->cellOffset; + data = pPage->aData; + hdr = pPage->hdrOffset; + put2byte(&data[hdr+3], nCell); + if( nCell ){ + cellbody = allocateSpace(pPage, totalSize); + assert( cellbody>0 ); + assert( pPage->nFree >= 2*nCell ); + pPage->nFree -= 2*nCell; + for(i=0; ipBt->usableSize ); + } + pPage->nCell = nCell; +} + +/* +** The following parameters determine how many adjacent pages get involved +** in a balancing operation. NN is the number of neighbors on either side +** of the page that participate in the balancing operation. NB is the +** total number of pages that participate, including the target page and +** NN neighbors on either side. +** +** The minimum value of NN is 1 (of course). Increasing NN above 1 +** (to 2 or 3) gives a modest improvement in SELECT and DELETE performance +** in exchange for a larger degradation in INSERT and UPDATE performance. +** The value of NN appears to give the best results overall. +*/ +#define NN 1 /* Number of neighbors on either side of pPage */ +#define NB (NN*2+1) /* Total pages involved in the balance */ + +/* Forward reference */ +static int balance(MemPage*, int); + +#ifndef SQLITE_OMIT_QUICKBALANCE +/* +** This version of balance() handles the common special case where +** a new entry is being inserted on the extreme right-end of the +** tree, in other words, when the new entry will become the largest +** entry in the tree. +** +** Instead of trying balance the 3 right-most leaf pages, just add +** a new page to the right-hand side and put the one new entry in +** that page. This leaves the right side of the tree somewhat +** unbalanced. But odds are that we will be inserting new entries +** at the end soon afterwards so the nearly empty page will quickly +** fill up. On average. +** +** pPage is the leaf page which is the right-most page in the tree. +** pParent is its parent. pPage must have a single overflow entry +** which is also the right-most entry on the page. +*/ +static int balance_quick(MemPage *pPage, MemPage *pParent){ + int rc; + MemPage *pNew; + Pgno pgnoNew; + u8 *pCell; + int szCell; + CellInfo info; + BtShared *pBt = pPage->pBt; + int parentIdx = pParent->nCell; /* pParent new divider cell index */ + int parentSize; /* Size of new divider cell */ + u8 parentCell[64]; /* Space for the new divider cell */ + + /* Allocate a new page. Insert the overflow cell from pPage + ** into it. Then remove the overflow cell from pPage. + */ + rc = allocatePage(pBt, &pNew, &pgnoNew, 0, 0); + if( rc!=SQLITE_OK ){ + return rc; + } + pCell = pPage->aOvfl[0].pCell; + szCell = cellSizePtr(pPage, pCell); + zeroPage(pNew, pPage->aData[0]); + assemblePage(pNew, 1, &pCell, &szCell); + pPage->nOverflow = 0; + + /* Set the parent of the newly allocated page to pParent. */ + pNew->pParent = pParent; + sqlite3pager_ref(pParent->aData); + + /* pPage is currently the right-child of pParent. Change this + ** so that the right-child is the new page allocated above and + ** pPage is the next-to-right child. + */ + assert( pPage->nCell>0 ); + parseCellPtr(pPage, findCell(pPage, pPage->nCell-1), &info); + rc = fillInCell(pParent, parentCell, 0, info.nKey, 0, 0, &parentSize); + if( rc!=SQLITE_OK ){ + return rc; + } + assert( parentSize<64 ); + rc = insertCell(pParent, parentIdx, parentCell, parentSize, 0, 4); + if( rc!=SQLITE_OK ){ + return rc; + } + put4byte(findOverflowCell(pParent,parentIdx), pPage->pgno); + put4byte(&pParent->aData[pParent->hdrOffset+8], pgnoNew); + +#ifndef SQLITE_OMIT_AUTOVACUUM + /* If this is an auto-vacuum database, update the pointer map + ** with entries for the new page, and any pointer from the + ** cell on the page to an overflow page. + */ + if( pBt->autoVacuum ){ + rc = ptrmapPut(pBt, pgnoNew, PTRMAP_BTREE, pParent->pgno); + if( rc!=SQLITE_OK ){ + return rc; + } + rc = ptrmapPutOvfl(pNew, 0); + if( rc!=SQLITE_OK ){ + return rc; + } + } +#endif + + /* Release the reference to the new page and balance the parent page, + ** in case the divider cell inserted caused it to become overfull. + */ + releasePage(pNew); + return balance(pParent, 0); +} +#endif /* SQLITE_OMIT_QUICKBALANCE */ + +/* +** The ISAUTOVACUUM macro is used within balance_nonroot() to determine +** if the database supports auto-vacuum or not. Because it is used +** within an expression that is an argument to another macro +** (sqliteMallocRaw), it is not possible to use conditional compilation. +** So, this macro is defined instead. +*/ +#ifndef SQLITE_OMIT_AUTOVACUUM +#define ISAUTOVACUUM (pBt->autoVacuum) +#else +#define ISAUTOVACUUM 0 +#endif + +/* +** This routine redistributes Cells on pPage and up to NN*2 siblings +** of pPage so that all pages have about the same amount of free space. +** Usually NN siblings on either side of pPage is used in the balancing, +** though more siblings might come from one side if pPage is the first +** or last child of its parent. If pPage has fewer than 2*NN siblings +** (something which can only happen if pPage is the root page or a +** child of root) then all available siblings participate in the balancing. +** +** The number of siblings of pPage might be increased or decreased by one or +** two in an effort to keep pages nearly full but not over full. The root page +** is special and is allowed to be nearly empty. If pPage is +** the root page, then the depth of the tree might be increased +** or decreased by one, as necessary, to keep the root page from being +** overfull or completely empty. +** +** Note that when this routine is called, some of the Cells on pPage +** might not actually be stored in pPage->aData[]. This can happen +** if the page is overfull. Part of the job of this routine is to +** make sure all Cells for pPage once again fit in pPage->aData[]. +** +** In the course of balancing the siblings of pPage, the parent of pPage +** might become overfull or underfull. If that happens, then this routine +** is called recursively on the parent. +** +** If this routine fails for any reason, it might leave the database +** in a corrupted state. So if this routine fails, the database should +** be rolled back. +*/ +static int balance_nonroot(MemPage *pPage){ + MemPage *pParent; /* The parent of pPage */ + BtShared *pBt; /* The whole database */ + int nCell = 0; /* Number of cells in apCell[] */ + int nMaxCells = 0; /* Allocated size of apCell, szCell, aFrom. */ + int nOld; /* Number of pages in apOld[] */ + int nNew; /* Number of pages in apNew[] */ + int nDiv; /* Number of cells in apDiv[] */ + int i, j, k; /* Loop counters */ + int idx; /* Index of pPage in pParent->aCell[] */ + int nxDiv; /* Next divider slot in pParent->aCell[] */ + int rc; /* The return code */ + int leafCorrection; /* 4 if pPage is a leaf. 0 if not */ + int leafData; /* True if pPage is a leaf of a LEAFDATA tree */ + int usableSpace; /* Bytes in pPage beyond the header */ + int pageFlags; /* Value of pPage->aData[0] */ + int subtotal; /* Subtotal of bytes in cells on one page */ + int iSpace = 0; /* First unused byte of aSpace[] */ + MemPage *apOld[NB]; /* pPage and up to two siblings */ + Pgno pgnoOld[NB]; /* Page numbers for each page in apOld[] */ + MemPage *apCopy[NB]; /* Private copies of apOld[] pages */ + MemPage *apNew[NB+2]; /* pPage and up to NB siblings after balancing */ + Pgno pgnoNew[NB+2]; /* Page numbers for each page in apNew[] */ + u8 *apDiv[NB]; /* Divider cells in pParent */ + int cntNew[NB+2]; /* Index in aCell[] of cell after i-th page */ + int szNew[NB+2]; /* Combined size of cells place on i-th page */ + u8 **apCell = 0; /* All cells begin balanced */ + int *szCell; /* Local size of all cells in apCell[] */ + u8 *aCopy[NB]; /* Space for holding data of apCopy[] */ + u8 *aSpace; /* Space to hold copies of dividers cells */ +#ifndef SQLITE_OMIT_AUTOVACUUM + u8 *aFrom = 0; +#endif + + /* + ** Find the parent page. + */ + assert( pPage->isInit ); + assert( sqlite3pager_iswriteable(pPage->aData) ); + pBt = pPage->pBt; + pParent = pPage->pParent; + assert( pParent ); + if( SQLITE_OK!=(rc = sqlite3pager_write(pParent->aData)) ){ + return rc; + } + TRACE(("BALANCE: begin page %d child of %d\n", pPage->pgno, pParent->pgno)); + +#ifndef SQLITE_OMIT_QUICKBALANCE + /* + ** A special case: If a new entry has just been inserted into a + ** table (that is, a btree with integer keys and all data at the leaves) + ** and the new entry is the right-most entry in the tree (it has the + ** largest key) then use the special balance_quick() routine for + ** balancing. balance_quick() is much faster and results in a tighter + ** packing of data in the common case. + */ + if( pPage->leaf && + pPage->intKey && + pPage->leafData && + pPage->nOverflow==1 && + pPage->aOvfl[0].idx==pPage->nCell && + pPage->pParent->pgno!=1 && + get4byte(&pParent->aData[pParent->hdrOffset+8])==pPage->pgno + ){ + /* + ** TODO: Check the siblings to the left of pPage. It may be that + ** they are not full and no new page is required. + */ + return balance_quick(pPage, pParent); + } +#endif + + /* + ** Find the cell in the parent page whose left child points back + ** to pPage. The "idx" variable is the index of that cell. If pPage + ** is the rightmost child of pParent then set idx to pParent->nCell + */ + if( pParent->idxShift ){ + Pgno pgno; + pgno = pPage->pgno; + assert( pgno==sqlite3pager_pagenumber(pPage->aData) ); + for(idx=0; idxnCell; idx++){ + if( get4byte(findCell(pParent, idx))==pgno ){ + break; + } + } + assert( idxnCell + || get4byte(&pParent->aData[pParent->hdrOffset+8])==pgno ); + }else{ + idx = pPage->idxParent; + } + + /* + ** Initialize variables so that it will be safe to jump + ** directly to balance_cleanup at any moment. + */ + nOld = nNew = 0; + sqlite3pager_ref(pParent->aData); + + /* + ** Find sibling pages to pPage and the cells in pParent that divide + ** the siblings. An attempt is made to find NN siblings on either + ** side of pPage. More siblings are taken from one side, however, if + ** pPage there are fewer than NN siblings on the other side. If pParent + ** has NB or fewer children then all children of pParent are taken. + */ + nxDiv = idx - NN; + if( nxDiv + NB > pParent->nCell ){ + nxDiv = pParent->nCell - NB + 1; + } + if( nxDiv<0 ){ + nxDiv = 0; + } + nDiv = 0; + for(i=0, k=nxDiv; inCell ){ + apDiv[i] = findCell(pParent, k); + nDiv++; + assert( !pParent->leaf ); + pgnoOld[i] = get4byte(apDiv[i]); + }else if( k==pParent->nCell ){ + pgnoOld[i] = get4byte(&pParent->aData[pParent->hdrOffset+8]); + }else{ + break; + } + rc = getAndInitPage(pBt, pgnoOld[i], &apOld[i], pParent); + if( rc ) goto balance_cleanup; + apOld[i]->idxParent = k; + apCopy[i] = 0; + assert( i==nOld ); + nOld++; + nMaxCells += 1+apOld[i]->nCell+apOld[i]->nOverflow; + } + + /* Make nMaxCells a multiple of 2 in order to preserve 8-byte + ** alignment */ + nMaxCells = (nMaxCells + 1)&~1; + + /* + ** Allocate space for memory structures + */ + apCell = sqliteMallocRaw( + nMaxCells*sizeof(u8*) /* apCell */ + + nMaxCells*sizeof(int) /* szCell */ + + ROUND8(sizeof(MemPage))*NB /* aCopy */ + + pBt->pageSize*(5+NB) /* aSpace */ + + (ISAUTOVACUUM ? nMaxCells : 0) /* aFrom */ + ); + if( apCell==0 ){ + rc = SQLITE_NOMEM; + goto balance_cleanup; + } + szCell = (int*)&apCell[nMaxCells]; + aCopy[0] = (u8*)&szCell[nMaxCells]; + assert( ((aCopy[0] - (u8*)apCell) & 7)==0 ); /* 8-byte alignment required */ + for(i=1; ipageSize+ROUND8(sizeof(MemPage))]; + assert( ((aCopy[i] - (u8*)apCell) & 7)==0 ); /* 8-byte alignment required */ + } + aSpace = &aCopy[NB-1][pBt->pageSize+ROUND8(sizeof(MemPage))]; + assert( ((aSpace - (u8*)apCell) & 7)==0 ); /* 8-byte alignment required */ +#ifndef SQLITE_OMIT_AUTOVACUUM + if( pBt->autoVacuum ){ + aFrom = &aSpace[5*pBt->pageSize]; + } +#endif + + /* + ** Make copies of the content of pPage and its siblings into aOld[]. + ** The rest of this function will use data from the copies rather + ** that the original pages since the original pages will be in the + ** process of being overwritten. + */ + for(i=0; ipageSize]; + p->aData = &((u8*)p)[-pBt->pageSize]; + memcpy(p->aData, apOld[i]->aData, pBt->pageSize + sizeof(MemPage)); + /* The memcpy() above changes the value of p->aData so we have to + ** set it again. */ + p->aData = &((u8*)p)[-pBt->pageSize]; + } + + /* + ** Load pointers to all cells on sibling pages and the divider cells + ** into the local apCell[] array. Make copies of the divider cells + ** into space obtained form aSpace[] and remove the the divider Cells + ** from pParent. + ** + ** If the siblings are on leaf pages, then the child pointers of the + ** divider cells are stripped from the cells before they are copied + ** into aSpace[]. In this way, all cells in apCell[] are without + ** child pointers. If siblings are not leaves, then all cell in + ** apCell[] include child pointers. Either way, all cells in apCell[] + ** are alike. + ** + ** leafCorrection: 4 if pPage is a leaf. 0 if pPage is not a leaf. + ** leafData: 1 if pPage holds key+data and pParent holds only keys. + */ + nCell = 0; + leafCorrection = pPage->leaf*4; + leafData = pPage->leafData && pPage->leaf; + for(i=0; inCell+pOld->nOverflow; + for(j=0; jautoVacuum ){ + int a; + aFrom[nCell] = i; + for(a=0; anOverflow; a++){ + if( pOld->aOvfl[a].pCell==apCell[nCell] ){ + aFrom[nCell] = 0xFF; + break; + } + } + } +#endif + nCell++; + } + if( ipageSize*5 ); + memcpy(pTemp, apDiv[i], sz); + apCell[nCell] = pTemp+leafCorrection; +#ifndef SQLITE_OMIT_AUTOVACUUM + if( pBt->autoVacuum ){ + aFrom[nCell] = 0xFF; + } +#endif + dropCell(pParent, nxDiv, sz); + szCell[nCell] -= leafCorrection; + assert( get4byte(pTemp)==pgnoOld[i] ); + if( !pOld->leaf ){ + assert( leafCorrection==0 ); + /* The right pointer of the child page pOld becomes the left + ** pointer of the divider cell */ + memcpy(apCell[nCell], &pOld->aData[pOld->hdrOffset+8], 4); + }else{ + assert( leafCorrection==4 ); + } + nCell++; + } + } + } + + /* + ** Figure out the number of pages needed to hold all nCell cells. + ** Store this number in "k". Also compute szNew[] which is the total + ** size of all cells on the i-th page and cntNew[] which is the index + ** in apCell[] of the cell that divides page i from page i+1. + ** cntNew[k] should equal nCell. + ** + ** Values computed by this block: + ** + ** k: The total number of sibling pages + ** szNew[i]: Spaced used on the i-th sibling page. + ** cntNew[i]: Index in apCell[] and szCell[] for the first cell to + ** the right of the i-th sibling page. + ** usableSpace: Number of bytes of space available on each sibling. + ** + */ + usableSpace = pBt->usableSize - 12 + leafCorrection; + for(subtotal=k=i=0; i usableSpace ){ + szNew[k] = subtotal - szCell[i]; + cntNew[k] = i; + if( leafData ){ i--; } + subtotal = 0; + k++; + } + } + szNew[k] = subtotal; + cntNew[k] = nCell; + k++; + + /* + ** The packing computed by the previous block is biased toward the siblings + ** on the left side. The left siblings are always nearly full, while the + ** right-most sibling might be nearly empty. This block of code attempts + ** to adjust the packing of siblings to get a better balance. + ** + ** This adjustment is more than an optimization. The packing above might + ** be so out of balance as to be illegal. For example, the right-most + ** sibling might be completely empty. This adjustment is not optional. + */ + for(i=k-1; i>0; i--){ + int szRight = szNew[i]; /* Size of sibling on the right */ + int szLeft = szNew[i-1]; /* Size of sibling on the left */ + int r; /* Index of right-most cell in left sibling */ + int d; /* Index of first cell to the left of right sibling */ + + r = cntNew[i-1] - 1; + d = r + 1 - leafData; + assert( d0) or we are the + ** a virtual root page. A virtual root page is when the real root + ** page is page 1 and we are the only child of that page. + */ + assert( cntNew[0]>0 || (pParent->pgno==1 && pParent->nCell==0) ); + + /* + ** Allocate k new pages. Reuse old pages where possible. + */ + assert( pPage->pgno>1 ); + pageFlags = pPage->aData[0]; + for(i=0; iaData); + if( rc ) goto balance_cleanup; + }else{ + rc = allocatePage(pBt, &pNew, &pgnoNew[i], pgnoNew[i-1], 0); + if( rc ) goto balance_cleanup; + apNew[i] = pNew; + } + nNew++; + zeroPage(pNew, pageFlags); + } + + /* Free any old pages that were not reused as new pages. + */ + while( ii ){ + int t; + MemPage *pT; + t = pgnoNew[i]; + pT = apNew[i]; + pgnoNew[i] = pgnoNew[minI]; + apNew[i] = apNew[minI]; + pgnoNew[minI] = t; + apNew[minI] = pT; + } + } + TRACE(("BALANCE: old: %d %d %d new: %d(%d) %d(%d) %d(%d) %d(%d) %d(%d)\n", + pgnoOld[0], + nOld>=2 ? pgnoOld[1] : 0, + nOld>=3 ? pgnoOld[2] : 0, + pgnoNew[0], szNew[0], + nNew>=2 ? pgnoNew[1] : 0, nNew>=2 ? szNew[1] : 0, + nNew>=3 ? pgnoNew[2] : 0, nNew>=3 ? szNew[2] : 0, + nNew>=4 ? pgnoNew[3] : 0, nNew>=4 ? szNew[3] : 0, + nNew>=5 ? pgnoNew[4] : 0, nNew>=5 ? szNew[4] : 0)); + + /* + ** Evenly distribute the data in apCell[] across the new pages. + ** Insert divider cells into pParent as necessary. + */ + j = 0; + for(i=0; ipgno==pgnoNew[i] ); + assemblePage(pNew, cntNew[i]-j, &apCell[j], &szCell[j]); + assert( pNew->nCell>0 || (nNew==1 && cntNew[0]==0) ); + assert( pNew->nOverflow==0 ); + +#ifndef SQLITE_OMIT_AUTOVACUUM + /* If this is an auto-vacuum database, update the pointer map entries + ** that point to the siblings that were rearranged. These can be: left + ** children of cells, the right-child of the page, or overflow pages + ** pointed to by cells. + */ + if( pBt->autoVacuum ){ + for(k=j; kpgno!=pNew->pgno ){ + rc = ptrmapPutOvfl(pNew, k-j); + if( rc!=SQLITE_OK ){ + goto balance_cleanup; + } + } + } + } +#endif + + j = cntNew[i]; + + /* If the sibling page assembled above was not the right-most sibling, + ** insert a divider cell into the parent page. + */ + if( ileaf ){ + memcpy(&pNew->aData[8], pCell, 4); + pTemp = 0; + }else if( leafData ){ + /* If the tree is a leaf-data tree, and the siblings are leaves, + ** then there is no divider cell in apCell[]. Instead, the divider + ** cell consists of the integer key for the right-most cell of + ** the sibling-page assembled above only. + */ + CellInfo info; + j--; + parseCellPtr(pNew, apCell[j], &info); + pCell = &aSpace[iSpace]; + fillInCell(pParent, pCell, 0, info.nKey, 0, 0, &sz); + iSpace += sz; + assert( iSpace<=pBt->pageSize*5 ); + pTemp = 0; + }else{ + pCell -= 4; + pTemp = &aSpace[iSpace]; + iSpace += sz; + assert( iSpace<=pBt->pageSize*5 ); + } + rc = insertCell(pParent, nxDiv, pCell, sz, pTemp, 4); + if( rc!=SQLITE_OK ) goto balance_cleanup; + put4byte(findOverflowCell(pParent,nxDiv), pNew->pgno); +#ifndef SQLITE_OMIT_AUTOVACUUM + /* If this is an auto-vacuum database, and not a leaf-data tree, + ** then update the pointer map with an entry for the overflow page + ** that the cell just inserted points to (if any). + */ + if( pBt->autoVacuum && !leafData ){ + rc = ptrmapPutOvfl(pParent, nxDiv); + if( rc!=SQLITE_OK ){ + goto balance_cleanup; + } + } +#endif + j++; + nxDiv++; + } + } + assert( j==nCell ); + if( (pageFlags & PTF_LEAF)==0 ){ + memcpy(&apNew[nNew-1]->aData[8], &apCopy[nOld-1]->aData[8], 4); + } + if( nxDiv==pParent->nCell+pParent->nOverflow ){ + /* Right-most sibling is the right-most child of pParent */ + put4byte(&pParent->aData[pParent->hdrOffset+8], pgnoNew[nNew-1]); + }else{ + /* Right-most sibling is the left child of the first entry in pParent + ** past the right-most divider entry */ + put4byte(findOverflowCell(pParent, nxDiv), pgnoNew[nNew-1]); + } + + /* + ** Reparent children of all cells. + */ + for(i=0; iisInit ); + /* assert( pPage->isInit ); // No! pPage might have been added to freelist */ + /* pageIntegrity(pPage); // No! pPage might have been added to freelist */ + rc = balance(pParent, 0); + + /* + ** Cleanup before returning. + */ +balance_cleanup: + sqliteFree(apCell); + for(i=0; ipgno, nOld, nNew, nCell)); + return rc; +} + +/* +** This routine is called for the root page of a btree when the root +** page contains no cells. This is an opportunity to make the tree +** shallower by one level. +*/ +static int balance_shallower(MemPage *pPage){ + MemPage *pChild; /* The only child page of pPage */ + Pgno pgnoChild; /* Page number for pChild */ + int rc = SQLITE_OK; /* Return code from subprocedures */ + BtShared *pBt; /* The main BTree structure */ + int mxCellPerPage; /* Maximum number of cells per page */ + u8 **apCell; /* All cells from pages being balanced */ + int *szCell; /* Local size of all cells */ + + assert( pPage->pParent==0 ); + assert( pPage->nCell==0 ); + pBt = pPage->pBt; + mxCellPerPage = MX_CELL(pBt); + apCell = sqliteMallocRaw( mxCellPerPage*(sizeof(u8*)+sizeof(int)) ); + if( apCell==0 ) return SQLITE_NOMEM; + szCell = (int*)&apCell[mxCellPerPage]; + if( pPage->leaf ){ + /* The table is completely empty */ + TRACE(("BALANCE: empty table %d\n", pPage->pgno)); + }else{ + /* The root page is empty but has one child. Transfer the + ** information from that one child into the root page if it + ** will fit. This reduces the depth of the tree by one. + ** + ** If the root page is page 1, it has less space available than + ** its child (due to the 100 byte header that occurs at the beginning + ** of the database fle), so it might not be able to hold all of the + ** information currently contained in the child. If this is the + ** case, then do not do the transfer. Leave page 1 empty except + ** for the right-pointer to the child page. The child page becomes + ** the virtual root of the tree. + */ + pgnoChild = get4byte(&pPage->aData[pPage->hdrOffset+8]); + assert( pgnoChild>0 ); + assert( pgnoChild<=sqlite3pager_pagecount(pPage->pBt->pPager) ); + rc = getPage(pPage->pBt, pgnoChild, &pChild); + if( rc ) goto end_shallow_balance; + if( pPage->pgno==1 ){ + rc = initPage(pChild, pPage); + if( rc ) goto end_shallow_balance; + assert( pChild->nOverflow==0 ); + if( pChild->nFree>=100 ){ + /* The child information will fit on the root page, so do the + ** copy */ + int i; + zeroPage(pPage, pChild->aData[0]); + for(i=0; inCell; i++){ + apCell[i] = findCell(pChild,i); + szCell[i] = cellSizePtr(pChild, apCell[i]); + } + assemblePage(pPage, pChild->nCell, apCell, szCell); + /* Copy the right-pointer of the child to the parent. */ + put4byte(&pPage->aData[pPage->hdrOffset+8], + get4byte(&pChild->aData[pChild->hdrOffset+8])); + freePage(pChild); + TRACE(("BALANCE: child %d transfer to page 1\n", pChild->pgno)); + }else{ + /* The child has more information that will fit on the root. + ** The tree is already balanced. Do nothing. */ + TRACE(("BALANCE: child %d will not fit on page 1\n", pChild->pgno)); + } + }else{ + memcpy(pPage->aData, pChild->aData, pPage->pBt->usableSize); + pPage->isInit = 0; + pPage->pParent = 0; + rc = initPage(pPage, 0); + assert( rc==SQLITE_OK ); + freePage(pChild); + TRACE(("BALANCE: transfer child %d into root %d\n", + pChild->pgno, pPage->pgno)); + } + rc = reparentChildPages(pPage); + assert( pPage->nOverflow==0 ); +#ifndef SQLITE_OMIT_AUTOVACUUM + if( pBt->autoVacuum ){ + int i; + for(i=0; inCell; i++){ + rc = ptrmapPutOvfl(pPage, i); + if( rc!=SQLITE_OK ){ + goto end_shallow_balance; + } + } + } +#endif + if( rc!=SQLITE_OK ) goto end_shallow_balance; + releasePage(pChild); + } +end_shallow_balance: + sqliteFree(apCell); + return rc; +} + + +/* +** The root page is overfull +** +** When this happens, Create a new child page and copy the +** contents of the root into the child. Then make the root +** page an empty page with rightChild pointing to the new +** child. Finally, call balance_internal() on the new child +** to cause it to split. +*/ +static int balance_deeper(MemPage *pPage){ + int rc; /* Return value from subprocedures */ + MemPage *pChild; /* Pointer to a new child page */ + Pgno pgnoChild; /* Page number of the new child page */ + BtShared *pBt; /* The BTree */ + int usableSize; /* Total usable size of a page */ + 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 */ + + assert( pPage->pParent==0 ); + assert( pPage->nOverflow>0 ); + pBt = pPage->pBt; + rc = allocatePage(pBt, &pChild, &pgnoChild, pPage->pgno, 0); + if( rc ) return rc; + assert( sqlite3pager_iswriteable(pChild->aData) ); + usableSize = pBt->usableSize; + data = pPage->aData; + hdr = pPage->hdrOffset; + brk = get2byte(&data[hdr+5]); + cdata = pChild->aData; + memcpy(cdata, &data[hdr], pPage->cellOffset+2*pPage->nCell-hdr); + memcpy(&cdata[brk], &data[brk], usableSize-brk); + assert( pChild->isInit==0 ); + rc = initPage(pChild, pPage); + if( rc ) goto balancedeeper_out; + memcpy(pChild->aOvfl, pPage->aOvfl, pPage->nOverflow*sizeof(pPage->aOvfl[0])); + pChild->nOverflow = pPage->nOverflow; + if( pChild->nOverflow ){ + pChild->nFree = 0; + } + assert( pChild->nCell==pPage->nCell ); + zeroPage(pPage, pChild->aData[0] & ~PTF_LEAF); + put4byte(&pPage->aData[pPage->hdrOffset+8], pgnoChild); + TRACE(("BALANCE: copy root %d into %d\n", pPage->pgno, pChild->pgno)); +#ifndef SQLITE_OMIT_AUTOVACUUM + if( pBt->autoVacuum ){ + int i; + rc = ptrmapPut(pBt, pChild->pgno, PTRMAP_BTREE, pPage->pgno); + if( rc ) goto balancedeeper_out; + for(i=0; inCell; i++){ + rc = ptrmapPutOvfl(pChild, i); + if( rc!=SQLITE_OK ){ + return rc; + } + } + } +#endif + rc = balance_nonroot(pChild); + +balancedeeper_out: + releasePage(pChild); + return rc; +} + +/* +** Decide if the page pPage needs to be balanced. If balancing is +** required, call the appropriate balancing routine. +*/ +static int balance(MemPage *pPage, int insert){ + int rc = SQLITE_OK; + if( pPage->pParent==0 ){ + if( pPage->nOverflow>0 ){ + rc = balance_deeper(pPage); + } + if( rc==SQLITE_OK && pPage->nCell==0 ){ + rc = balance_shallower(pPage); + } + }else{ + if( pPage->nOverflow>0 || + (!insert && pPage->nFree>pPage->pBt->usableSize*2/3) ){ + rc = balance_nonroot(pPage); + } + } + return rc; +} + +/* +** This routine checks all cursors that point to table pgnoRoot. +** If any of those cursors other than pExclude were opened with +** wrFlag==0 then this routine returns SQLITE_LOCKED. If all +** cursors that point to pgnoRoot were opened with wrFlag==1 +** then this routine returns SQLITE_OK. +** +** In addition to checking for read-locks (where a read-lock +** means a cursor opened with wrFlag==0) this routine also moves +** all cursors other than pExclude so that they are pointing to the +** first Cell on root page. This is necessary because an insert +** or delete might change the number of cells on a page or delete +** a page entirely and we do not want to leave any cursors +** pointing to non-existant pages or cells. +*/ +static int checkReadLocks(BtShared *pBt, Pgno pgnoRoot, BtCursor *pExclude){ + BtCursor *p; + for(p=pBt->pCursor; p; p=p->pNext){ + u32 flags = (p->pBtree->pSqlite ? p->pBtree->pSqlite->flags : 0); + if( p->pgnoRoot!=pgnoRoot || p==pExclude ) continue; + if( p->wrFlag==0 && flags&SQLITE_ReadUncommitted ) continue; + if( p->wrFlag==0 ) return SQLITE_LOCKED; + if( p->pPage->pgno!=p->pgnoRoot ){ + moveToRoot(p); + } + } + return SQLITE_OK; +} + +/* +** Insert a new record into the BTree. The key is given by (pKey,nKey) +** and the data is given by (pData,nData). The cursor is used only to +** define what table the record should be inserted into. The cursor +** is left pointing at a random location. +** +** For an INTKEY table, only the nKey value of the key is used. pKey is +** ignored. For a ZERODATA table, the pData and nData are both ignored. +*/ +int sqlite3BtreeInsert( + BtCursor *pCur, /* Insert data into the table of this cursor */ + const void *pKey, i64 nKey, /* The key of the new record */ + const void *pData, int nData /* The data of the new record */ +){ + int rc; + int loc; + int szNew; + MemPage *pPage; + BtShared *pBt = pCur->pBtree->pBt; + unsigned char *oldCell; + unsigned char *newCell = 0; + + if( pBt->inTransaction!=TRANS_WRITE ){ + /* Must start a transaction before doing an insert */ + return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; + } + assert( !pBt->readOnly ); + if( !pCur->wrFlag ){ + return SQLITE_PERM; /* Cursor not open for writing */ + } + if( checkReadLocks(pBt, pCur->pgnoRoot, pCur) ){ + return SQLITE_LOCKED; /* The table pCur points to has a read lock */ + } + + /* Save the positions of any other cursors open on this table */ + restoreOrClearCursorPosition(pCur, 0); + if( + SQLITE_OK!=(rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur)) || + SQLITE_OK!=(rc = sqlite3BtreeMoveto(pCur, pKey, nKey, &loc)) + ){ + return rc; + } + + pPage = pCur->pPage; + assert( pPage->intKey || nKey>=0 ); + assert( pPage->leaf || !pPage->leafData ); + TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n", + pCur->pgnoRoot, nKey, nData, pPage->pgno, + loc==0 ? "overwrite" : "new entry")); + assert( pPage->isInit ); + rc = sqlite3pager_write(pPage->aData); + if( rc ) return rc; + newCell = sqliteMallocRaw( MX_CELL_SIZE(pBt) ); + if( newCell==0 ) return SQLITE_NOMEM; + rc = fillInCell(pPage, newCell, pKey, nKey, pData, nData, &szNew); + if( rc ) goto end_insert; + assert( szNew==cellSizePtr(pPage, newCell) ); + assert( szNew<=MX_CELL_SIZE(pBt) ); + if( loc==0 && CURSOR_VALID==pCur->eState ){ + int szOld; + assert( pCur->idx>=0 && pCur->idxnCell ); + oldCell = findCell(pPage, pCur->idx); + if( !pPage->leaf ){ + memcpy(newCell, oldCell, 4); + } + szOld = cellSizePtr(pPage, oldCell); + rc = clearCell(pPage, oldCell); + if( rc ) goto end_insert; + dropCell(pPage, pCur->idx, szOld); + }else if( loc<0 && pPage->nCell>0 ){ + assert( pPage->leaf ); + pCur->idx++; + pCur->info.nSize = 0; + }else{ + assert( pPage->leaf ); + } + rc = insertCell(pPage, pCur->idx, newCell, szNew, 0, 0); + if( rc!=SQLITE_OK ) goto end_insert; + rc = balance(pPage, 1); + /* sqlite3BtreePageDump(pCur->pBt, pCur->pgnoRoot, 1); */ + /* fflush(stdout); */ + if( rc==SQLITE_OK ){ + moveToRoot(pCur); + } +end_insert: + sqliteFree(newCell); + return rc; +} + +/* +** Delete the entry that the cursor is pointing to. The cursor +** is left pointing at a random location. +*/ +int sqlite3BtreeDelete(BtCursor *pCur){ + MemPage *pPage = pCur->pPage; + unsigned char *pCell; + int rc; + Pgno pgnoChild = 0; + BtShared *pBt = pCur->pBtree->pBt; + + assert( pPage->isInit ); + if( pBt->inTransaction!=TRANS_WRITE ){ + /* Must start a transaction before doing a delete */ + return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; + } + assert( !pBt->readOnly ); + if( pCur->idx >= pPage->nCell ){ + return SQLITE_ERROR; /* The cursor is not pointing to anything */ + } + if( !pCur->wrFlag ){ + return SQLITE_PERM; /* Did not open this cursor for writing */ + } + if( checkReadLocks(pBt, pCur->pgnoRoot, pCur) ){ + return SQLITE_LOCKED; /* The table pCur points to has a read lock */ + } + + /* Restore the current cursor position (a no-op if the cursor is not in + ** CURSOR_REQUIRESEEK state) and save the positions of any other cursors + ** open on the same table. Then call sqlite3pager_write() on the page + ** that the entry will be deleted from. + */ + if( + (rc = restoreOrClearCursorPosition(pCur, 1))!=0 || + (rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur))!=0 || + (rc = sqlite3pager_write(pPage->aData))!=0 + ){ + return rc; + } + + /* Locate the cell within it's page and leave pCell pointing to the + ** data. The clearCell() call frees any overflow pages associated with the + ** cell. The cell itself is still intact. + */ + pCell = findCell(pPage, pCur->idx); + if( !pPage->leaf ){ + pgnoChild = get4byte(pCell); + } + rc = clearCell(pPage, pCell); + if( rc ) return rc; + + if( !pPage->leaf ){ + /* + ** The entry we are about to delete is not a leaf so if we do not + ** do something we will leave a hole on an internal page. + ** We have to fill the hole by moving in a cell from a leaf. The + ** next Cell after the one to be deleted is guaranteed to exist and + ** to be a leaf so we can use it. + */ + BtCursor leafCur; + unsigned char *pNext; + int szNext; /* The compiler warning is wrong: szNext is always + ** initialized before use. Adding an extra initialization + ** to silence the compiler slows down the code. */ + int notUsed; + unsigned char *tempCell = 0; + assert( !pPage->leafData ); + getTempCursor(pCur, &leafCur); + rc = sqlite3BtreeNext(&leafCur, ¬Used); + if( rc!=SQLITE_OK ){ + if( rc!=SQLITE_NOMEM ){ + rc = SQLITE_CORRUPT_BKPT; + } + } + if( rc==SQLITE_OK ){ + rc = sqlite3pager_write(leafCur.pPage->aData); + } + if( rc==SQLITE_OK ){ + TRACE(("DELETE: table=%d delete internal from %d replace from leaf %d\n", + pCur->pgnoRoot, pPage->pgno, leafCur.pPage->pgno)); + dropCell(pPage, pCur->idx, cellSizePtr(pPage, pCell)); + pNext = findCell(leafCur.pPage, leafCur.idx); + szNext = cellSizePtr(leafCur.pPage, pNext); + assert( MX_CELL_SIZE(pBt)>=szNext+4 ); + tempCell = sqliteMallocRaw( MX_CELL_SIZE(pBt) ); + if( tempCell==0 ){ + rc = SQLITE_NOMEM; + } + } + if( rc==SQLITE_OK ){ + rc = insertCell(pPage, pCur->idx, pNext-4, szNext+4, tempCell, 0); + } + if( rc==SQLITE_OK ){ + put4byte(findOverflowCell(pPage, pCur->idx), pgnoChild); + rc = balance(pPage, 0); + } + if( rc==SQLITE_OK ){ + dropCell(leafCur.pPage, leafCur.idx, szNext); + rc = balance(leafCur.pPage, 0); + } + sqliteFree(tempCell); + releaseTempCursor(&leafCur); + }else{ + TRACE(("DELETE: table=%d delete from leaf %d\n", + pCur->pgnoRoot, pPage->pgno)); + dropCell(pPage, pCur->idx, cellSizePtr(pPage, pCell)); + rc = balance(pPage, 0); + } + if( rc==SQLITE_OK ){ + moveToRoot(pCur); + } + return rc; +} + +/* +** Create a new BTree table. Write into *piTable the page +** number for the root page of the new table. +** +** The type of type is determined by the flags parameter. Only the +** following values of flags are currently in use. Other values for +** flags might not work: +** +** BTREE_INTKEY|BTREE_LEAFDATA Used for SQL tables with rowid keys +** BTREE_ZERODATA Used for SQL indices +*/ +int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){ + BtShared *pBt = p->pBt; + MemPage *pRoot; + Pgno pgnoRoot; + int rc; + if( pBt->inTransaction!=TRANS_WRITE ){ + /* Must start a transaction first */ + return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; + } + assert( !pBt->readOnly ); + + /* It is illegal to create a table if any cursors are open on the + ** database. This is because in auto-vacuum mode the backend may + ** need to move a database page to make room for the new root-page. + ** If an open cursor was using the page a problem would occur. + */ + if( pBt->pCursor ){ + return SQLITE_LOCKED; + } + +#ifdef SQLITE_OMIT_AUTOVACUUM + rc = allocatePage(pBt, &pRoot, &pgnoRoot, 1, 0); + if( rc ) return rc; +#else + if( pBt->autoVacuum ){ + Pgno pgnoMove; /* Move a page here to make room for the root-page */ + MemPage *pPageMove; /* The page to move to. */ + + /* Read the value of meta[3] from the database to determine where the + ** root page of the new table should go. meta[3] is the largest root-page + ** created so far, so the new root-page is (meta[3]+1). + */ + rc = sqlite3BtreeGetMeta(p, 4, &pgnoRoot); + if( rc!=SQLITE_OK ) return rc; + pgnoRoot++; + + /* The new root-page may not be allocated on a pointer-map page, or the + ** PENDING_BYTE page. + */ + if( pgnoRoot==PTRMAP_PAGENO(pBt, pgnoRoot) || + pgnoRoot==PENDING_BYTE_PAGE(pBt) ){ + pgnoRoot++; + } + assert( pgnoRoot>=3 ); + + /* Allocate a page. The page that currently resides at pgnoRoot will + ** be moved to the allocated page (unless the allocated page happens + ** to reside at pgnoRoot). + */ + rc = allocatePage(pBt, &pPageMove, &pgnoMove, pgnoRoot, 1); + if( rc!=SQLITE_OK ){ + return rc; + } + + if( pgnoMove!=pgnoRoot ){ + u8 eType; + Pgno iPtrPage; + + releasePage(pPageMove); + rc = getPage(pBt, pgnoRoot, &pRoot); + if( rc!=SQLITE_OK ){ + return rc; + } + rc = ptrmapGet(pBt, pgnoRoot, &eType, &iPtrPage); + if( rc!=SQLITE_OK || eType==PTRMAP_ROOTPAGE || eType==PTRMAP_FREEPAGE ){ + releasePage(pRoot); + return rc; + } + assert( eType!=PTRMAP_ROOTPAGE ); + assert( eType!=PTRMAP_FREEPAGE ); + rc = sqlite3pager_write(pRoot->aData); + if( rc!=SQLITE_OK ){ + releasePage(pRoot); + return rc; + } + rc = relocatePage(pBt, pRoot, eType, iPtrPage, pgnoMove); + releasePage(pRoot); + if( rc!=SQLITE_OK ){ + return rc; + } + rc = getPage(pBt, pgnoRoot, &pRoot); + if( rc!=SQLITE_OK ){ + return rc; + } + rc = sqlite3pager_write(pRoot->aData); + if( rc!=SQLITE_OK ){ + releasePage(pRoot); + return rc; + } + }else{ + pRoot = pPageMove; + } + + /* Update the pointer-map and meta-data with the new root-page number. */ + rc = ptrmapPut(pBt, pgnoRoot, PTRMAP_ROOTPAGE, 0); + if( rc ){ + releasePage(pRoot); + return rc; + } + rc = sqlite3BtreeUpdateMeta(p, 4, pgnoRoot); + if( rc ){ + releasePage(pRoot); + return rc; + } + + }else{ + rc = allocatePage(pBt, &pRoot, &pgnoRoot, 1, 0); + if( rc ) return rc; + } +#endif + assert( sqlite3pager_iswriteable(pRoot->aData) ); + zeroPage(pRoot, flags | PTF_LEAF); + sqlite3pager_unref(pRoot->aData); + *piTable = (int)pgnoRoot; + return SQLITE_OK; +} + +/* +** Erase the given database page and all its children. Return +** the page to the freelist. +*/ +static int clearDatabasePage( + BtShared *pBt, /* The BTree that contains the table */ + Pgno pgno, /* Page number to clear */ + MemPage *pParent, /* Parent page. NULL for the root */ + int freePageFlag /* Deallocate page if true */ +){ + MemPage *pPage = 0; + int rc; + unsigned char *pCell; + int i; + + if( pgno>sqlite3pager_pagecount(pBt->pPager) ){ + return SQLITE_CORRUPT_BKPT; + } + + rc = getAndInitPage(pBt, pgno, &pPage, pParent); + if( rc ) goto cleardatabasepage_out; + rc = sqlite3pager_write(pPage->aData); + if( rc ) goto cleardatabasepage_out; + for(i=0; inCell; i++){ + pCell = findCell(pPage, i); + if( !pPage->leaf ){ + rc = clearDatabasePage(pBt, get4byte(pCell), pPage->pParent, 1); + if( rc ) goto cleardatabasepage_out; + } + rc = clearCell(pPage, pCell); + if( rc ) goto cleardatabasepage_out; + } + if( !pPage->leaf ){ + rc = clearDatabasePage(pBt, get4byte(&pPage->aData[8]), pPage->pParent, 1); + if( rc ) goto cleardatabasepage_out; + } + if( freePageFlag ){ + rc = freePage(pPage); + }else{ + zeroPage(pPage, pPage->aData[0] | PTF_LEAF); + } + +cleardatabasepage_out: + releasePage(pPage); + return rc; +} + +/* +** Delete all information from a single table in the database. iTable is +** the page number of the root of the table. After this routine returns, +** the root page is empty, but still exists. +** +** This routine will fail with SQLITE_LOCKED if there are any open +** read cursors on the table. Open write cursors are moved to the +** root of the table. +*/ +int sqlite3BtreeClearTable(Btree *p, int iTable){ + int rc; + BtCursor *pCur; + BtShared *pBt = p->pBt; + sqlite3 *db = p->pSqlite; + if( p->inTrans!=TRANS_WRITE ){ + return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; + } + + /* If this connection is not in read-uncommitted mode and currently has + ** a read-cursor open on the table being cleared, return SQLITE_LOCKED. + */ + if( 0==db || 0==(db->flags&SQLITE_ReadUncommitted) ){ + for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ + if( pCur->pBtree==p && pCur->pgnoRoot==(Pgno)iTable ){ + if( 0==pCur->wrFlag ){ + return SQLITE_LOCKED; + } + moveToRoot(pCur); + } + } + } + + /* Save the position of all cursors open on this table */ + if( SQLITE_OK!=(rc = saveAllCursors(pBt, iTable, 0)) ){ + return rc; + } + + return clearDatabasePage(pBt, (Pgno)iTable, 0, 0); +} + +/* +** Erase all information in a table and add the root of the table to +** the freelist. Except, the root of the principle table (the one on +** page 1) is never added to the freelist. +** +** This routine will fail with SQLITE_LOCKED if there are any open +** cursors on the table. +** +** If AUTOVACUUM is enabled and the page at iTable is not the last +** root page in the database file, then the last root page +** in the database file is moved into the slot formerly occupied by +** iTable and that last slot formerly occupied by the last root page +** is added to the freelist instead of iTable. In this say, all +** root pages are kept at the beginning of the database file, which +** is necessary for AUTOVACUUM to work right. *piMoved is set to the +** page number that used to be the last root page in the file before +** the move. If no page gets moved, *piMoved is set to 0. +** The last root page is recorded in meta[3] and the value of +** meta[3] is updated by this procedure. +*/ +int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){ + int rc; + MemPage *pPage = 0; + BtShared *pBt = p->pBt; + + if( p->inTrans!=TRANS_WRITE ){ + return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; + } + + /* It is illegal to drop a table if any cursors are open on the + ** database. This is because in auto-vacuum mode the backend may + ** need to move another root-page to fill a gap left by the deleted + ** root page. If an open cursor was using this page a problem would + ** occur. + */ + if( pBt->pCursor ){ + return SQLITE_LOCKED; + } + + rc = getPage(pBt, (Pgno)iTable, &pPage); + if( rc ) return rc; + rc = sqlite3BtreeClearTable(p, iTable); + if( rc ){ + releasePage(pPage); + return rc; + } + + *piMoved = 0; + + if( iTable>1 ){ +#ifdef SQLITE_OMIT_AUTOVACUUM + rc = freePage(pPage); + releasePage(pPage); +#else + if( pBt->autoVacuum ){ + Pgno maxRootPgno; + rc = sqlite3BtreeGetMeta(p, 4, &maxRootPgno); + if( rc!=SQLITE_OK ){ + releasePage(pPage); + return rc; + } + + if( iTable==maxRootPgno ){ + /* If the table being dropped is the table with the largest root-page + ** number in the database, put the root page on the free list. + */ + rc = freePage(pPage); + releasePage(pPage); + if( rc!=SQLITE_OK ){ + return rc; + } + }else{ + /* The table being dropped does not have the largest root-page + ** number in the database. So move the page that does into the + ** gap left by the deleted root-page. + */ + MemPage *pMove; + releasePage(pPage); + rc = getPage(pBt, maxRootPgno, &pMove); + if( rc!=SQLITE_OK ){ + return rc; + } + rc = relocatePage(pBt, pMove, PTRMAP_ROOTPAGE, 0, iTable); + releasePage(pMove); + if( rc!=SQLITE_OK ){ + return rc; + } + rc = getPage(pBt, maxRootPgno, &pMove); + if( rc!=SQLITE_OK ){ + return rc; + } + rc = freePage(pMove); + releasePage(pMove); + if( rc!=SQLITE_OK ){ + return rc; + } + *piMoved = maxRootPgno; + } + + /* Set the new 'max-root-page' value in the database header. This + ** is the old value less one, less one more if that happens to + ** be a root-page number, less one again if that is the + ** PENDING_BYTE_PAGE. + */ + maxRootPgno--; + if( maxRootPgno==PENDING_BYTE_PAGE(pBt) ){ + maxRootPgno--; + } + if( maxRootPgno==PTRMAP_PAGENO(pBt, maxRootPgno) ){ + maxRootPgno--; + } + assert( maxRootPgno!=PENDING_BYTE_PAGE(pBt) ); + + rc = sqlite3BtreeUpdateMeta(p, 4, maxRootPgno); + }else{ + rc = freePage(pPage); + releasePage(pPage); + } +#endif + }else{ + /* If sqlite3BtreeDropTable was called on page 1. */ + zeroPage(pPage, PTF_INTKEY|PTF_LEAF ); + releasePage(pPage); + } + return rc; +} + + +/* +** Read the meta-information out of a database file. Meta[0] +** is the number of free pages currently in the database. Meta[1] +** through meta[15] are available for use by higher layers. Meta[0] +** is read-only, the others are read/write. +** +** The schema layer numbers meta values differently. At the schema +** layer (and the SetCookie and ReadCookie opcodes) the number of +** free pages is not visible. So Cookie[0] is the same as Meta[1]. +*/ +int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){ + int rc; + unsigned char *pP1; + BtShared *pBt = p->pBt; + + /* Reading a meta-data value requires a read-lock on page 1 (and hence + ** the sqlite_master table. We grab this lock regardless of whether or + ** not the SQLITE_ReadUncommitted flag is set (the table rooted at page + ** 1 is treated as a special case by queryTableLock() and lockTable()). + */ + rc = queryTableLock(p, 1, READ_LOCK); + if( rc!=SQLITE_OK ){ + return rc; + } + + assert( idx>=0 && idx<=15 ); + rc = sqlite3pager_get(pBt->pPager, 1, (void**)&pP1); + if( rc ) return rc; + *pMeta = get4byte(&pP1[36 + idx*4]); + sqlite3pager_unref(pP1); + + /* If autovacuumed is disabled in this build but we are trying to + ** access an autovacuumed database, then make the database readonly. + */ +#ifdef SQLITE_OMIT_AUTOVACUUM + if( idx==4 && *pMeta>0 ) pBt->readOnly = 1; +#endif + + /* Grab the read-lock on page 1. */ + rc = lockTable(p, 1, READ_LOCK); + return rc; +} + +/* +** Write meta-information back into the database. Meta[0] is +** read-only and may not be written. +*/ +int sqlite3BtreeUpdateMeta(Btree *p, int idx, u32 iMeta){ + BtShared *pBt = p->pBt; + unsigned char *pP1; + int rc; + assert( idx>=1 && idx<=15 ); + if( p->inTrans!=TRANS_WRITE ){ + return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; + } + assert( pBt->pPage1!=0 ); + pP1 = pBt->pPage1->aData; + rc = sqlite3pager_write(pP1); + if( rc ) return rc; + put4byte(&pP1[36 + idx*4], iMeta); + return SQLITE_OK; +} + +/* +** Return the flag byte at the beginning of the page that the cursor +** is currently pointing to. +*/ +int sqlite3BtreeFlags(BtCursor *pCur){ + /* TODO: What about CURSOR_REQUIRESEEK state? Probably need to call + ** restoreOrClearCursorPosition() here. + */ + MemPage *pPage = pCur->pPage; + return pPage ? pPage->aData[pPage->hdrOffset] : 0; +} + +#ifdef SQLITE_DEBUG +/* +** Print a disassembly of the given page on standard output. This routine +** is used for debugging and testing only. +*/ +static int btreePageDump(BtShared *pBt, int pgno, int recursive, MemPage *pParent){ + int rc; + MemPage *pPage; + int i, j, c; + int nFree; + u16 idx; + int hdr; + int nCell; + int isInit; + unsigned char *data; + char range[20]; + unsigned char payload[20]; + + rc = getPage(pBt, (Pgno)pgno, &pPage); + isInit = pPage->isInit; + if( pPage->isInit==0 ){ + initPage(pPage, pParent); + } + if( rc ){ + return rc; + } + hdr = pPage->hdrOffset; + data = pPage->aData; + c = data[hdr]; + pPage->intKey = (c & (PTF_INTKEY|PTF_LEAFDATA))!=0; + pPage->zeroData = (c & PTF_ZERODATA)!=0; + pPage->leafData = (c & PTF_LEAFDATA)!=0; + pPage->leaf = (c & PTF_LEAF)!=0; + pPage->hasData = !(pPage->zeroData || (!pPage->leaf && pPage->leafData)); + nCell = get2byte(&data[hdr+3]); + sqlite3DebugPrintf("PAGE %d: flags=0x%02x frag=%d parent=%d\n", pgno, + data[hdr], data[hdr+7], + (pPage->isInit && pPage->pParent) ? pPage->pParent->pgno : 0); + assert( hdr == (pgno==1 ? 100 : 0) ); + idx = hdr + 12 - pPage->leaf*4; + for(i=0; ileaf ){ + child = 0; + }else{ + child = get4byte(pCell); + } + sz = info.nData; + if( !pPage->intKey ) sz += info.nKey; + if( sz>sizeof(payload)-1 ) sz = sizeof(payload)-1; + memcpy(payload, &pCell[info.nHeader], sz); + for(j=0; j0x7f ) payload[j] = '.'; + } + payload[sz] = 0; + sqlite3DebugPrintf( + "cell %2d: i=%-10s chld=%-4d nk=%-4lld nd=%-4d payload=%s\n", + i, range, child, info.nKey, info.nData, payload + ); + } + if( !pPage->leaf ){ + sqlite3DebugPrintf("right_child: %d\n", get4byte(&data[hdr+8])); + } + nFree = 0; + i = 0; + idx = get2byte(&data[hdr+1]); + while( idx>0 && idxpBt->usableSize ){ + int sz = get2byte(&data[idx+2]); + sprintf(range,"%d..%d", idx, idx+sz-1); + nFree += sz; + sqlite3DebugPrintf("freeblock %2d: i=%-10s size=%-4d total=%d\n", + i, range, sz, nFree); + idx = get2byte(&data[idx]); + i++; + } + if( idx!=0 ){ + sqlite3DebugPrintf("ERROR: next freeblock index out of range: %d\n", idx); + } + if( recursive && !pPage->leaf ){ + for(i=0; iisInit = isInit; + sqlite3pager_unref(data); + fflush(stdout); + return SQLITE_OK; +} +int sqlite3BtreePageDump(Btree *p, int pgno, int recursive){ + return btreePageDump(p->pBt, pgno, recursive, 0); +} +#endif + +#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) +/* +** Fill aResult[] with information about the entry and page that the +** cursor is pointing to. +** +** aResult[0] = The page number +** aResult[1] = The entry number +** aResult[2] = Total number of entries on this page +** aResult[3] = Cell size (local payload + header) +** aResult[4] = Number of free bytes on this page +** aResult[5] = Number of free blocks on the page +** aResult[6] = Total payload size (local + overflow) +** aResult[7] = Header size in bytes +** aResult[8] = Local payload size +** aResult[9] = Parent page number +** +** This routine is used for testing and debugging only. +*/ +int sqlite3BtreeCursorInfo(BtCursor *pCur, int *aResult, int upCnt){ + int cnt, idx; + MemPage *pPage = pCur->pPage; + BtCursor tmpCur; + + int rc = restoreOrClearCursorPosition(pCur, 1); + if( rc!=SQLITE_OK ){ + return rc; + } + + pageIntegrity(pPage); + assert( pPage->isInit ); + getTempCursor(pCur, &tmpCur); + while( upCnt-- ){ + moveToParent(&tmpCur); + } + pPage = tmpCur.pPage; + pageIntegrity(pPage); + aResult[0] = sqlite3pager_pagenumber(pPage->aData); + assert( aResult[0]==pPage->pgno ); + aResult[1] = tmpCur.idx; + aResult[2] = pPage->nCell; + if( tmpCur.idx>=0 && tmpCur.idxnCell ){ + getCellInfo(&tmpCur); + aResult[3] = tmpCur.info.nSize; + aResult[6] = tmpCur.info.nData; + aResult[7] = tmpCur.info.nHeader; + aResult[8] = tmpCur.info.nLocal; + }else{ + aResult[3] = 0; + aResult[6] = 0; + aResult[7] = 0; + aResult[8] = 0; + } + aResult[4] = pPage->nFree; + cnt = 0; + idx = get2byte(&pPage->aData[pPage->hdrOffset+1]); + while( idx>0 && idxpBt->usableSize ){ + cnt++; + idx = get2byte(&pPage->aData[idx]); + } + aResult[5] = cnt; + if( pPage->pParent==0 || isRootPage(pPage) ){ + aResult[9] = 0; + }else{ + aResult[9] = pPage->pParent->pgno; + } + releaseTempCursor(&tmpCur); + return SQLITE_OK; +} +#endif + +/* +** Return the pager associated with a BTree. This routine is used for +** testing and debugging only. +*/ +Pager *sqlite3BtreePager(Btree *p){ + return p->pBt->pPager; +} + +/* +** This structure is passed around through all the sanity checking routines +** in order to keep track of some global state information. +*/ +typedef struct IntegrityCk IntegrityCk; +struct IntegrityCk { + BtShared *pBt; /* The tree being checked out */ + Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */ + int nPage; /* Number of pages in the database */ + int *anRef; /* Number of times each page is referenced */ + char *zErrMsg; /* An error message. NULL of no errors seen. */ +}; + +#ifndef SQLITE_OMIT_INTEGRITY_CHECK +/* +** Append a message to the error message string. +*/ +static void checkAppendMsg( + IntegrityCk *pCheck, + char *zMsg1, + const char *zFormat, + ... +){ + va_list ap; + char *zMsg2; + va_start(ap, zFormat); + zMsg2 = sqlite3VMPrintf(zFormat, ap); + va_end(ap); + if( zMsg1==0 ) zMsg1 = ""; + if( pCheck->zErrMsg ){ + char *zOld = pCheck->zErrMsg; + pCheck->zErrMsg = 0; + sqlite3SetString(&pCheck->zErrMsg, zOld, "\n", zMsg1, zMsg2, (char*)0); + sqliteFree(zOld); + }else{ + sqlite3SetString(&pCheck->zErrMsg, zMsg1, zMsg2, (char*)0); + } + sqliteFree(zMsg2); +} +#endif /* SQLITE_OMIT_INTEGRITY_CHECK */ + +#ifndef SQLITE_OMIT_INTEGRITY_CHECK +/* +** Add 1 to the reference count for page iPage. If this is the second +** reference to the page, add an error message to pCheck->zErrMsg. +** Return 1 if there are 2 ore more references to the page and 0 if +** if this is the first reference to the page. +** +** Also check that the page number is in bounds. +*/ +static int checkRef(IntegrityCk *pCheck, int iPage, char *zContext){ + if( iPage==0 ) return 1; + if( iPage>pCheck->nPage || iPage<0 ){ + checkAppendMsg(pCheck, zContext, "invalid page number %d", iPage); + return 1; + } + if( pCheck->anRef[iPage]==1 ){ + checkAppendMsg(pCheck, zContext, "2nd reference to page %d", iPage); + return 1; + } + return (pCheck->anRef[iPage]++)>1; +} + +#ifndef SQLITE_OMIT_AUTOVACUUM +/* +** Check that the entry in the pointer-map for page iChild maps to +** page iParent, pointer type ptrType. If not, append an error message +** to pCheck. +*/ +static void checkPtrmap( + IntegrityCk *pCheck, /* Integrity check context */ + Pgno iChild, /* Child page number */ + u8 eType, /* Expected pointer map type */ + Pgno iParent, /* Expected pointer map parent page number */ + char *zContext /* Context description (used for error msg) */ +){ + int rc; + u8 ePtrmapType; + Pgno iPtrmapParent; + + rc = ptrmapGet(pCheck->pBt, iChild, &ePtrmapType, &iPtrmapParent); + if( rc!=SQLITE_OK ){ + checkAppendMsg(pCheck, zContext, "Failed to read ptrmap key=%d", iChild); + return; + } + + if( ePtrmapType!=eType || iPtrmapParent!=iParent ){ + checkAppendMsg(pCheck, zContext, + "Bad ptr map entry key=%d expected=(%d,%d) got=(%d,%d)", + iChild, eType, iParent, ePtrmapType, iPtrmapParent); + } +} +#endif + +/* +** Check the integrity of the freelist or of an overflow page list. +** Verify that the number of pages on the list is N. +*/ +static void checkList( + IntegrityCk *pCheck, /* Integrity checking context */ + int isFreeList, /* True for a freelist. False for overflow page list */ + int iPage, /* Page number for first page in the list */ + int N, /* Expected number of pages in the list */ + char *zContext /* Context for error messages */ +){ + int i; + int expected = N; + int iFirst = iPage; + while( N-- > 0 ){ + unsigned char *pOvfl; + if( iPage<1 ){ + checkAppendMsg(pCheck, zContext, + "%d of %d pages missing from overflow list starting at %d", + N+1, expected, iFirst); + break; + } + if( checkRef(pCheck, iPage, zContext) ) break; + if( sqlite3pager_get(pCheck->pPager, (Pgno)iPage, (void**)&pOvfl) ){ + checkAppendMsg(pCheck, zContext, "failed to get page %d", iPage); + break; + } + if( isFreeList ){ + int n = get4byte(&pOvfl[4]); +#ifndef SQLITE_OMIT_AUTOVACUUM + if( pCheck->pBt->autoVacuum ){ + checkPtrmap(pCheck, iPage, PTRMAP_FREEPAGE, 0, zContext); + } +#endif + if( n>pCheck->pBt->usableSize/4-8 ){ + checkAppendMsg(pCheck, zContext, + "freelist leaf count too big on page %d", iPage); + N--; + }else{ + for(i=0; ipBt->autoVacuum ){ + checkPtrmap(pCheck, iFreePage, PTRMAP_FREEPAGE, 0, zContext); + } +#endif + checkRef(pCheck, iFreePage, zContext); + } + N -= n; + } + } +#ifndef SQLITE_OMIT_AUTOVACUUM + else{ + /* If this database supports auto-vacuum and iPage is not the last + ** page in this overflow list, check that the pointer-map entry for + ** the following page matches iPage. + */ + if( pCheck->pBt->autoVacuum && N>0 ){ + i = get4byte(pOvfl); + checkPtrmap(pCheck, i, PTRMAP_OVERFLOW2, iPage, zContext); + } + } +#endif + iPage = get4byte(pOvfl); + sqlite3pager_unref(pOvfl); + } +} +#endif /* SQLITE_OMIT_INTEGRITY_CHECK */ + +#ifndef SQLITE_OMIT_INTEGRITY_CHECK +/* +** Do various sanity checks on a single page of a tree. Return +** the tree depth. Root pages return 0. Parents of root pages +** return 1, and so forth. +** +** These checks are done: +** +** 1. Make sure that cells and freeblocks do not overlap +** but combine to completely cover the page. +** NO 2. Make sure cell keys are in order. +** NO 3. Make sure no key is less than or equal to zLowerBound. +** NO 4. Make sure no key is greater than or equal to zUpperBound. +** 5. Check the integrity of overflow pages. +** 6. Recursively call checkTreePage on all children. +** 7. Verify that the depth of all children is the same. +** 8. Make sure this page is at least 33% full or else it is +** the root of the tree. +*/ +static int checkTreePage( + IntegrityCk *pCheck, /* Context for the sanity check */ + int iPage, /* Page number of the page to check */ + MemPage *pParent, /* Parent page */ + char *zParentContext, /* Parent context */ + char *zLowerBound, /* All keys should be greater than this, if not NULL */ + int nLower, /* Number of characters in zLowerBound */ + char *zUpperBound, /* All keys should be less than this, if not NULL */ + int nUpper /* Number of characters in zUpperBound */ +){ + MemPage *pPage; + int i, rc, depth, d2, pgno, cnt; + int hdr, cellStart; + int nCell; + u8 *data; + BtShared *pBt; + int usableSize; + char zContext[100]; + char *hit; + + sprintf(zContext, "Page %d: ", iPage); + + /* Check that the page exists + */ + pBt = pCheck->pBt; + usableSize = pBt->usableSize; + if( iPage==0 ) return 0; + if( checkRef(pCheck, iPage, zParentContext) ) return 0; + if( (rc = getPage(pBt, (Pgno)iPage, &pPage))!=0 ){ + checkAppendMsg(pCheck, zContext, + "unable to get the page. error code=%d", rc); + return 0; + } + if( (rc = initPage(pPage, pParent))!=0 ){ + checkAppendMsg(pCheck, zContext, "initPage() returns error code %d", rc); + releasePage(pPage); + return 0; + } + + /* Check out all the cells. + */ + depth = 0; + for(i=0; inCell; i++){ + u8 *pCell; + int sz; + CellInfo info; + + /* Check payload overflow pages + */ + sprintf(zContext, "On tree page %d cell %d: ", iPage, i); + pCell = findCell(pPage,i); + parseCellPtr(pPage, pCell, &info); + sz = info.nData; + if( !pPage->intKey ) sz += info.nKey; + if( sz>info.nLocal ){ + int nPage = (sz - info.nLocal + usableSize - 5)/(usableSize - 4); + Pgno pgnoOvfl = get4byte(&pCell[info.iOverflow]); +#ifndef SQLITE_OMIT_AUTOVACUUM + if( pBt->autoVacuum ){ + checkPtrmap(pCheck, pgnoOvfl, PTRMAP_OVERFLOW1, iPage, zContext); + } +#endif + checkList(pCheck, 0, pgnoOvfl, nPage, zContext); + } + + /* Check sanity of left child page. + */ + if( !pPage->leaf ){ + pgno = get4byte(pCell); +#ifndef SQLITE_OMIT_AUTOVACUUM + if( pBt->autoVacuum ){ + checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage, zContext); + } +#endif + d2 = checkTreePage(pCheck,pgno,pPage,zContext,0,0,0,0); + if( i>0 && d2!=depth ){ + checkAppendMsg(pCheck, zContext, "Child page depth differs"); + } + depth = d2; + } + } + if( !pPage->leaf ){ + pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]); + sprintf(zContext, "On page %d at right child: ", iPage); +#ifndef SQLITE_OMIT_AUTOVACUUM + if( pBt->autoVacuum ){ + checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage, 0); + } +#endif + checkTreePage(pCheck, pgno, pPage, zContext,0,0,0,0); + } + + /* Check for complete coverage of the page + */ + data = pPage->aData; + hdr = pPage->hdrOffset; + hit = sqliteMalloc( usableSize ); + if( hit ){ + memset(hit, 1, get2byte(&data[hdr+5])); + nCell = get2byte(&data[hdr+3]); + cellStart = hdr + 12 - 4*pPage->leaf; + for(i=0; i=usableSize || pc<0 ){ + checkAppendMsg(pCheck, 0, + "Corruption detected in cell %d on page %d",i,iPage,0); + }else{ + for(j=pc+size-1; j>=pc; j--) hit[j]++; + } + } + for(cnt=0, i=get2byte(&data[hdr+1]); i>0 && i=usableSize || i<0 ){ + checkAppendMsg(pCheck, 0, + "Corruption detected in cell %d on page %d",i,iPage,0); + }else{ + for(j=i+size-1; j>=i; j--) hit[j]++; + } + i = get2byte(&data[i]); + } + for(i=cnt=0; i1 ){ + checkAppendMsg(pCheck, 0, + "Multiple uses for byte %d of page %d", i, iPage); + break; + } + } + if( cnt!=data[hdr+7] ){ + checkAppendMsg(pCheck, 0, + "Fragmented space is %d byte reported as %d on page %d", + cnt, data[hdr+7], iPage); + } + } + sqliteFree(hit); + + releasePage(pPage); + return depth+1; +} +#endif /* SQLITE_OMIT_INTEGRITY_CHECK */ + +#ifndef SQLITE_OMIT_INTEGRITY_CHECK +/* +** This routine does a complete check of the given BTree file. aRoot[] is +** an array of pages numbers were each page number is the root page of +** a table. nRoot is the number of entries in aRoot. +** +** If everything checks out, this routine returns NULL. If something is +** amiss, an error message is written into memory obtained from malloc() +** and a pointer to that error message is returned. The calling function +** is responsible for freeing the error message when it is done. +*/ +char *sqlite3BtreeIntegrityCheck(Btree *p, int *aRoot, int nRoot){ + int i; + int nRef; + IntegrityCk sCheck; + BtShared *pBt = p->pBt; + + nRef = *sqlite3pager_stats(pBt->pPager); + if( lockBtreeWithRetry(p)!=SQLITE_OK ){ + return sqliteStrDup("Unable to acquire a read lock on the database"); + } + sCheck.pBt = pBt; + sCheck.pPager = pBt->pPager; + sCheck.nPage = sqlite3pager_pagecount(sCheck.pPager); + if( sCheck.nPage==0 ){ + unlockBtreeIfUnused(pBt); + return 0; + } + sCheck.anRef = sqliteMallocRaw( (sCheck.nPage+1)*sizeof(sCheck.anRef[0]) ); + if( !sCheck.anRef ){ + unlockBtreeIfUnused(pBt); + return sqlite3MPrintf("Unable to malloc %d bytes", + (sCheck.nPage+1)*sizeof(sCheck.anRef[0])); + } + for(i=0; i<=sCheck.nPage; i++){ sCheck.anRef[i] = 0; } + i = PENDING_BYTE_PAGE(pBt); + if( i<=sCheck.nPage ){ + sCheck.anRef[i] = 1; + } + sCheck.zErrMsg = 0; + + /* Check the integrity of the freelist + */ + checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]), + get4byte(&pBt->pPage1->aData[36]), "Main freelist: "); + + /* Check all the tables. + */ + for(i=0; iautoVacuum && aRoot[i]>1 ){ + checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0, 0); + } +#endif + checkTreePage(&sCheck, aRoot[i], 0, "List of tree roots: ", 0,0,0,0); + } + + /* Make sure every page in the file is referenced + */ + for(i=1; i<=sCheck.nPage; i++){ +#ifdef SQLITE_OMIT_AUTOVACUUM + if( sCheck.anRef[i]==0 ){ + checkAppendMsg(&sCheck, 0, "Page %d is never used", i); + } +#else + /* If the database supports auto-vacuum, make sure no tables contain + ** references to pointer-map pages. + */ + if( sCheck.anRef[i]==0 && + (PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){ + checkAppendMsg(&sCheck, 0, "Page %d is never used", i); + } + if( sCheck.anRef[i]!=0 && + (PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){ + checkAppendMsg(&sCheck, 0, "Pointer map page %d is referenced", i); + } +#endif + } + + /* Make sure this analysis did not leave any unref() pages + */ + unlockBtreeIfUnused(pBt); + if( nRef != *sqlite3pager_stats(pBt->pPager) ){ + checkAppendMsg(&sCheck, 0, + "Outstanding page count goes from %d to %d during this analysis", + nRef, *sqlite3pager_stats(pBt->pPager) + ); + } + + /* Clean up and report errors. + */ + sqliteFree(sCheck.anRef); + return sCheck.zErrMsg; +} +#endif /* SQLITE_OMIT_INTEGRITY_CHECK */ + +/* +** Return the full pathname of the underlying database file. +*/ +const char *sqlite3BtreeGetFilename(Btree *p){ + assert( p->pBt->pPager!=0 ); + return sqlite3pager_filename(p->pBt->pPager); +} + +/* +** Return the pathname of the directory that contains the database file. +*/ +const char *sqlite3BtreeGetDirname(Btree *p){ + assert( p->pBt->pPager!=0 ); + return sqlite3pager_dirname(p->pBt->pPager); +} + +/* +** Return the pathname of the journal file for this database. The return +** value of this routine is the same regardless of whether the journal file +** has been created or not. +*/ +const char *sqlite3BtreeGetJournalname(Btree *p){ + assert( p->pBt->pPager!=0 ); + return sqlite3pager_journalname(p->pBt->pPager); +} + +#ifndef SQLITE_OMIT_VACUUM +/* +** Copy the complete content of pBtFrom into pBtTo. A transaction +** must be active for both files. +** +** The size of file pBtFrom may be reduced by this operation. +** If anything goes wrong, the transaction on pBtFrom is rolled back. +*/ +int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){ + int rc = SQLITE_OK; + Pgno i, nPage, nToPage, iSkip; + + BtShared *pBtTo = pTo->pBt; + BtShared *pBtFrom = pFrom->pBt; + + if( pTo->inTrans!=TRANS_WRITE || pFrom->inTrans!=TRANS_WRITE ){ + return SQLITE_ERROR; + } + if( pBtTo->pCursor ) return SQLITE_BUSY; + nToPage = sqlite3pager_pagecount(pBtTo->pPager); + nPage = sqlite3pager_pagecount(pBtFrom->pPager); + iSkip = PENDING_BYTE_PAGE(pBtTo); + for(i=1; rc==SQLITE_OK && i<=nPage; i++){ + void *pPage; + if( i==iSkip ) continue; + rc = sqlite3pager_get(pBtFrom->pPager, i, &pPage); + if( rc ) break; + rc = sqlite3pager_overwrite(pBtTo->pPager, i, pPage); + if( rc ) break; + sqlite3pager_unref(pPage); + } + for(i=nPage+1; rc==SQLITE_OK && i<=nToPage; i++){ + void *pPage; + if( i==iSkip ) continue; + rc = sqlite3pager_get(pBtTo->pPager, i, &pPage); + if( rc ) break; + rc = sqlite3pager_write(pPage); + sqlite3pager_unref(pPage); + sqlite3pager_dont_write(pBtTo->pPager, i); + } + if( !rc && nPagepPager, nPage); + } + if( rc ){ + sqlite3BtreeRollback(pTo); + } + return rc; +} +#endif /* SQLITE_OMIT_VACUUM */ + +/* +** Return non-zero if a transaction is active. +*/ +int sqlite3BtreeIsInTrans(Btree *p){ + return (p && (p->inTrans==TRANS_WRITE)); +} + +/* +** Return non-zero if a statement transaction is active. +*/ +int sqlite3BtreeIsInStmt(Btree *p){ + return (p->pBt && p->pBt->inStmt); +} + +/* +** This call is a no-op if no write-transaction is currently active on pBt. +** +** Otherwise, sync the database file for the btree pBt. zMaster points to +** the name of a master journal file that should be written into the +** individual journal file, or is NULL, indicating no master journal file +** (single database transaction). +** +** When this is called, the master journal should already have been +** created, populated with this journal pointer and synced to disk. +** +** Once this is routine has returned, the only thing required to commit +** the write-transaction for this database file is to delete the journal. +*/ +int sqlite3BtreeSync(Btree *p, const char *zMaster){ + int rc = SQLITE_OK; + if( p->inTrans==TRANS_WRITE ){ + BtShared *pBt = p->pBt; + Pgno nTrunc = 0; +#ifndef SQLITE_OMIT_AUTOVACUUM + if( pBt->autoVacuum ){ + rc = autoVacuumCommit(pBt, &nTrunc); + if( rc!=SQLITE_OK ){ + return rc; + } + } +#endif + rc = sqlite3pager_sync(pBt->pPager, zMaster, nTrunc); + } + return rc; +} + +/* +** This function returns a pointer to a blob of memory associated with +** a single shared-btree. The memory is used by client code for it's own +** purposes (for example, to store a high-level schema associated with +** the shared-btree). The btree layer manages reference counting issues. +** +** The first time this is called on a shared-btree, nBytes bytes of memory +** are allocated, zeroed, and returned to the caller. For each subsequent +** call the nBytes parameter is ignored and a pointer to the same blob +** of memory returned. +** +** Just before the shared-btree is closed, the function passed as the +** xFree argument when the memory allocation was made is invoked on the +** blob of allocated memory. This function should not call sqliteFree() +** on the memory, the btree layer does that. +*/ +void *sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){ + BtShared *pBt = p->pBt; + if( !pBt->pSchema ){ + pBt->pSchema = sqliteMalloc(nBytes); + pBt->xFreeSchema = xFree; + } + return pBt->pSchema; +} + +/* +** Return true if another user of the same shared btree as the argument +** handle holds an exclusive lock on the sqlite_master table. +*/ +int sqlite3BtreeSchemaLocked(Btree *p){ + return (queryTableLock(p, MASTER_ROOT, READ_LOCK)!=SQLITE_OK); +} + +int sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){ + int rc = SQLITE_OK; +#ifndef SQLITE_OMIT_SHARED_CACHE + u8 lockType = (isWriteLock?WRITE_LOCK:READ_LOCK); + rc = queryTableLock(p, iTab, lockType); + if( rc==SQLITE_OK ){ + rc = lockTable(p, iTab, lockType); + } +#endif + return rc; +} + +/* +** The following debugging interface has to be in this file (rather +** than in, for example, test1.c) so that it can get access to +** the definition of BtShared. +*/ +#if defined(SQLITE_DEBUG) && defined(TCLSH) +#include +int sqlite3_shared_cache_report( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + const ThreadData *pTd = sqlite3ThreadDataReadOnly(); + if( pTd->useSharedData ){ + BtShared *pBt; + Tcl_Obj *pRet = Tcl_NewObj(); + for(pBt=pTd->pBtree; pBt; pBt=pBt->pNext){ + const char *zFile = sqlite3pager_filename(pBt->pPager); + Tcl_ListObjAppendElement(interp, pRet, Tcl_NewStringObj(zFile, -1)); + Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(pBt->nRef)); + } + Tcl_SetObjResult(interp, pRet); + } + return TCL_OK; +} +#endif Added: external/sqlite-source-3.3.4/btree.h ============================================================================== --- (empty file) +++ external/sqlite-source-3.3.4/btree.h Mon Apr 3 07:54:59 2006 @@ -0,0 +1,148 @@ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This header file defines the interface that the sqlite B-Tree file +** subsystem. See comments in the source code for a detailed description +** of what each interface routine does. +** +** @(#) $Id: btree.h,v 1.70 2006/02/11 01:25:51 drh Exp $ +*/ +#ifndef _BTREE_H_ +#define _BTREE_H_ + +/* TODO: This definition is just included so other modules compile. It +** needs to be revisited. +*/ +#define SQLITE_N_BTREE_META 10 + +/* +** If defined as non-zero, auto-vacuum is enabled by default. Otherwise +** it must be turned on for each database using "PRAGMA auto_vacuum = 1". +*/ +#ifndef SQLITE_DEFAULT_AUTOVACUUM + #define SQLITE_DEFAULT_AUTOVACUUM 0 +#endif + +/* +** Forward declarations of structure +*/ +typedef struct Btree Btree; +typedef struct BtCursor BtCursor; +typedef struct BtShared BtShared; + + +int sqlite3BtreeOpen( + const char *zFilename, /* Name of database file to open */ + sqlite3 *db, /* Associated database connection */ + Btree **, /* Return open Btree* here */ + int flags /* Flags */ +); + +/* The flags parameter to sqlite3BtreeOpen can be the bitwise or of the +** following values. +** +** NOTE: These values must match the corresponding PAGER_ values in +** pager.h. +*/ +#define BTREE_OMIT_JOURNAL 1 /* Do not use journal. No argument */ +#define BTREE_NO_READLOCK 2 /* Omit readlocks on readonly files */ +#define BTREE_MEMORY 4 /* In-memory DB. No argument */ + +int sqlite3BtreeClose(Btree*); +int sqlite3BtreeSetBusyHandler(Btree*,BusyHandler*); +int sqlite3BtreeSetCacheSize(Btree*,int); +int sqlite3BtreeSetSafetyLevel(Btree*,int,int); +int sqlite3BtreeSyncDisabled(Btree*); +int sqlite3BtreeSetPageSize(Btree*,int,int); +int sqlite3BtreeGetPageSize(Btree*); +int sqlite3BtreeGetReserve(Btree*); +int sqlite3BtreeSetAutoVacuum(Btree *, int); +int sqlite3BtreeGetAutoVacuum(Btree *); +int sqlite3BtreeBeginTrans(Btree*,int); +int sqlite3BtreeCommit(Btree*); +int sqlite3BtreeRollback(Btree*); +int sqlite3BtreeBeginStmt(Btree*); +int sqlite3BtreeCommitStmt(Btree*); +int sqlite3BtreeRollbackStmt(Btree*); +int sqlite3BtreeCreateTable(Btree*, int*, int flags); +int sqlite3BtreeIsInTrans(Btree*); +int sqlite3BtreeIsInStmt(Btree*); +int sqlite3BtreeSync(Btree*, const char *zMaster); +void *sqlite3BtreeSchema(Btree *, int, void(*)(void *)); +int sqlite3BtreeSchemaLocked(Btree *); +int sqlite3BtreeLockTable(Btree *, int, u8); + +const char *sqlite3BtreeGetFilename(Btree *); +const char *sqlite3BtreeGetDirname(Btree *); +const char *sqlite3BtreeGetJournalname(Btree *); +int sqlite3BtreeCopyFile(Btree *, Btree *); + +/* The flags parameter to sqlite3BtreeCreateTable can be the bitwise OR +** of the following flags: +*/ +#define BTREE_INTKEY 1 /* Table has only 64-bit signed integer keys */ +#define BTREE_ZERODATA 2 /* Table has keys only - no data */ +#define BTREE_LEAFDATA 4 /* Data stored in leaves only. Implies INTKEY */ + +int sqlite3BtreeDropTable(Btree*, int, int*); +int sqlite3BtreeClearTable(Btree*, int); +int sqlite3BtreeGetMeta(Btree*, int idx, u32 *pValue); +int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value); + +int sqlite3BtreeCursor( + Btree*, /* BTree containing table to open */ + int iTable, /* Index of root page */ + int wrFlag, /* 1 for writing. 0 for read-only */ + int(*)(void*,int,const void*,int,const void*), /* Key comparison function */ + void*, /* First argument to compare function */ + BtCursor **ppCursor /* Returned cursor */ +); + +void sqlite3BtreeSetCompare( + BtCursor *, + int(*)(void*,int,const void*,int,const void*), + void* +); + +int sqlite3BtreeCloseCursor(BtCursor*); +int sqlite3BtreeMoveto(BtCursor*, const void *pKey, i64 nKey, int *pRes); +int sqlite3BtreeDelete(BtCursor*); +int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey, + const void *pData, int nData); +int sqlite3BtreeFirst(BtCursor*, int *pRes); +int sqlite3BtreeLast(BtCursor*, int *pRes); +int sqlite3BtreeNext(BtCursor*, int *pRes); +int sqlite3BtreeEof(BtCursor*); +int sqlite3BtreeFlags(BtCursor*); +int sqlite3BtreePrevious(BtCursor*, int *pRes); +int sqlite3BtreeKeySize(BtCursor*, i64 *pSize); +int sqlite3BtreeKey(BtCursor*, u32 offset, u32 amt, void*); +const void *sqlite3BtreeKeyFetch(BtCursor*, int *pAmt); +const void *sqlite3BtreeDataFetch(BtCursor*, int *pAmt); +int sqlite3BtreeDataSize(BtCursor*, u32 *pSize); +int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*); + +char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot); +struct Pager *sqlite3BtreePager(Btree*); + + +#ifdef SQLITE_TEST +int sqlite3BtreeCursorInfo(BtCursor*, int*, int); +void sqlite3BtreeCursorList(Btree*); +#endif + +#ifdef SQLITE_DEBUG +int sqlite3BtreePageDump(Btree*, int, int recursive); +#else +#define sqlite3BtreePageDump(X,Y,Z) SQLITE_OK +#endif + +#endif /* _BTREE_H_ */ Added: external/sqlite-source-3.3.4/build.c ============================================================================== --- (empty file) +++ external/sqlite-source-3.3.4/build.c Mon Apr 3 07:54:59 2006 @@ -0,0 +1,3221 @@ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains C code routines that are called by the SQLite parser +** when syntax rules are reduced. The routines in this file handle the +** following kinds of SQL syntax: +** +** CREATE TABLE +** DROP TABLE +** CREATE INDEX +** DROP INDEX +** creating ID lists +** BEGIN TRANSACTION +** COMMIT +** ROLLBACK +** +** $Id: build.c,v 1.386 2006/02/10 18:08:09 drh Exp $ +*/ +#include "sqliteInt.h" +#include + +/* +** This routine is called when a new SQL statement is beginning to +** be parsed. Initialize the pParse structure as needed. +*/ +void sqlite3BeginParse(Parse *pParse, int explainFlag){ + pParse->explain = explainFlag; + pParse->nVar = 0; +} + +#ifndef SQLITE_OMIT_SHARED_CACHE +/* +** The TableLock structure is only used by the sqlite3TableLock() and +** codeTableLocks() functions. +*/ +struct TableLock { + int iDb; + int iTab; + u8 isWriteLock; + const char *zName; +}; + +/* +** Have the compiled statement lock the table with rootpage iTab in database +** iDb at the shared-cache level when executed. The isWriteLock argument +** is zero for a read-lock, or non-zero for a write-lock. +** +** The zName parameter should point to the unqualified table name. This is +** used to provide a more informative error message should the lock fail. +*/ +void sqlite3TableLock( + Parse *pParse, + int iDb, + int iTab, + u8 isWriteLock, + const char *zName +){ + int i; + int nBytes; + TableLock *p; + + if( 0==sqlite3ThreadDataReadOnly()->useSharedData || iDb<0 ){ + return; + } + + for(i=0; inTableLock; i++){ + p = &pParse->aTableLock[i]; + if( p->iDb==iDb && p->iTab==iTab ){ + p->isWriteLock = (p->isWriteLock || isWriteLock); + return; + } + } + + nBytes = sizeof(TableLock) * (pParse->nTableLock+1); + sqliteReallocOrFree((void **)&pParse->aTableLock, nBytes); + if( pParse->aTableLock ){ + p = &pParse->aTableLock[pParse->nTableLock++]; + p->iDb = iDb; + p->iTab = iTab; + p->isWriteLock = isWriteLock; + p->zName = zName; + } +} + +/* +** Code an OP_TableLock instruction for each table locked by the +** statement (configured by calls to sqlite3TableLock()). +*/ +static void codeTableLocks(Parse *pParse){ + int i; + Vdbe *pVdbe; + assert( sqlite3ThreadDataReadOnly()->useSharedData || pParse->nTableLock==0 ); + + if( 0==(pVdbe = sqlite3GetVdbe(pParse)) ){ + return; + } + + for(i=0; inTableLock; i++){ + TableLock *p = &pParse->aTableLock[i]; + int p1 = p->iDb; + if( p->isWriteLock ){ + p1 = -1*(p1+1); + } + sqlite3VdbeOp3(pVdbe, OP_TableLock, p1, p->iTab, p->zName, P3_STATIC); + } +} +#else + #define codeTableLocks(x) +#endif + +/* +** This routine is called after a single SQL statement has been +** parsed and a VDBE program to execute that statement has been +** prepared. This routine puts the finishing touches on the +** VDBE program and resets the pParse structure for the next +** parse. +** +** Note that if an error occurred, it might be the case that +** no VDBE code was generated. +*/ +void sqlite3FinishCoding(Parse *pParse){ + sqlite3 *db; + Vdbe *v; + + if( sqlite3MallocFailed() ) return; + if( pParse->nested ) return; + if( !pParse->pVdbe ){ + if( pParse->rc==SQLITE_OK && pParse->nErr ){ + pParse->rc = SQLITE_ERROR; + } + return; + } + + /* Begin by generating some termination code at the end of the + ** vdbe program + */ + db = pParse->db; + v = sqlite3GetVdbe(pParse); + if( v ){ + sqlite3VdbeAddOp(v, OP_Halt, 0, 0); + + /* The cookie mask contains one bit for each database file open. + ** (Bit 0 is for main, bit 1 is for temp, and so forth.) Bits are + ** set for each database that is used. Generate code to start a + ** transaction on each used database and to verify the schema cookie + ** on each used database. + */ + if( pParse->cookieGoto>0 ){ + u32 mask; + int iDb; + sqlite3VdbeJumpHere(v, pParse->cookieGoto-1); + for(iDb=0, mask=1; iDbnDb; mask<<=1, iDb++){ + if( (mask & pParse->cookieMask)==0 ) continue; + sqlite3VdbeAddOp(v, OP_Transaction, iDb, (mask & pParse->writeMask)!=0); + sqlite3VdbeAddOp(v, OP_VerifyCookie, iDb, pParse->cookieValue[iDb]); + } + + /* Once all the cookies have been verified and transactions opened, + ** obtain the required table-locks. This is a no-op unless the + ** shared-cache feature is enabled. + */ + codeTableLocks(pParse); + sqlite3VdbeAddOp(v, OP_Goto, 0, pParse->cookieGoto); + } + +#ifndef SQLITE_OMIT_TRACE + /* Add a No-op that contains the complete text of the compiled SQL + ** statement as its P3 argument. This does not change the functionality + ** of the program. + ** + ** This is used to implement sqlite3_trace(). + */ + sqlite3VdbeOp3(v, OP_Noop, 0, 0, pParse->zSql, pParse->zTail-pParse->zSql); +#endif /* SQLITE_OMIT_TRACE */ + } + + + /* Get the VDBE program ready for execution + */ + if( v && pParse->nErr==0 ){ + FILE *trace = (db->flags & SQLITE_VdbeTrace)!=0 ? stdout : 0; + sqlite3VdbeTrace(v, trace); + sqlite3VdbeMakeReady(v, pParse->nVar, pParse->nMem+3, + pParse->nTab+3, pParse->explain); + pParse->rc = SQLITE_DONE; + pParse->colNamesSet = 0; + }else if( pParse->rc==SQLITE_OK ){ + pParse->rc = SQLITE_ERROR; + } + pParse->nTab = 0; + pParse->nMem = 0; + pParse->nSet = 0; + pParse->nVar = 0; + pParse->cookieMask = 0; + pParse->cookieGoto = 0; +} + +/* +** Run the parser and code generator recursively in order to generate +** code for the SQL statement given onto the end of the pParse context +** currently under construction. When the parser is run recursively +** this way, the final OP_Halt is not appended and other initialization +** and finalization steps are omitted because those are handling by the +** outermost parser. +** +** Not everything is nestable. This facility is designed to permit +** INSERT, UPDATE, and DELETE operations against SQLITE_MASTER. Use +** care if you decide to try to use this routine for some other purposes. +*/ +void sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){ + va_list ap; + char *zSql; +# define SAVE_SZ (sizeof(Parse) - offsetof(Parse,nVar)) + char saveBuf[SAVE_SZ]; + + if( pParse->nErr ) return; + assert( pParse->nested<10 ); /* Nesting should only be of limited depth */ + va_start(ap, zFormat); + zSql = sqlite3VMPrintf(zFormat, ap); + va_end(ap); + if( zSql==0 ){ + return; /* A malloc must have failed */ + } + pParse->nested++; + memcpy(saveBuf, &pParse->nVar, SAVE_SZ); + memset(&pParse->nVar, 0, SAVE_SZ); + sqlite3RunParser(pParse, zSql, 0); + sqliteFree(zSql); + memcpy(&pParse->nVar, saveBuf, SAVE_SZ); + pParse->nested--; +} + +/* +** Locate the in-memory structure that describes a particular database +** table given the name of that table and (optionally) the name of the +** database containing the table. Return NULL if not found. +** +** If zDatabase is 0, all databases are searched for the table and the +** first matching table is returned. (No checking for duplicate table +** names is done.) The search order is TEMP first, then MAIN, then any +** auxiliary databases added using the ATTACH command. +** +** See also sqlite3LocateTable(). +*/ +Table *sqlite3FindTable(sqlite3 *db, const char *zName, const char *zDatabase){ + Table *p = 0; + int i; + assert( zName!=0 ); + for(i=OMIT_TEMPDB; inDb; i++){ + int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ + if( zDatabase!=0 && sqlite3StrICmp(zDatabase, db->aDb[j].zName) ) continue; + p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName, strlen(zName)+1); + if( p ) break; + } + return p; +} + +/* +** Locate the in-memory structure that describes a particular database +** table given the name of that table and (optionally) the name of the +** database containing the table. Return NULL if not found. Also leave an +** error message in pParse->zErrMsg. +** +** The difference between this routine and sqlite3FindTable() is that this +** routine leaves an error message in pParse->zErrMsg where +** sqlite3FindTable() does not. +*/ +Table *sqlite3LocateTable(Parse *pParse, const char *zName, const char *zDbase){ + Table *p; + + /* Read the database schema. If an error occurs, leave an error message + ** and code in pParse and return NULL. */ + if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ + return 0; + } + + p = sqlite3FindTable(pParse->db, zName, zDbase); + if( p==0 ){ + if( zDbase ){ + sqlite3ErrorMsg(pParse, "no such table: %s.%s", zDbase, zName); + }else{ + sqlite3ErrorMsg(pParse, "no such table: %s", zName); + } + pParse->checkSchema = 1; + } + return p; +} + +/* +** Locate the in-memory structure that describes +** a particular index given the name of that index +** and the name of the database that contains the index. +** Return NULL if not found. +** +** If zDatabase is 0, all databases are searched for the +** table and the first matching index is returned. (No checking +** for duplicate index names is done.) The search order is +** TEMP first, then MAIN, then any auxiliary databases added +** using the ATTACH command. +*/ +Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const char *zDb){ + Index *p = 0; + int i; + for(i=OMIT_TEMPDB; inDb; i++){ + int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ + Schema *pSchema = db->aDb[j].pSchema; + if( zDb && sqlite3StrICmp(zDb, db->aDb[j].zName) ) continue; + assert( pSchema || (j==1 && !db->aDb[1].pBt) ); + if( pSchema ){ + p = sqlite3HashFind(&pSchema->idxHash, zName, strlen(zName)+1); + } + if( p ) break; + } + return p; +} + +/* +** Reclaim the memory used by an index +*/ +static void freeIndex(Index *p){ + sqliteFree(p->zColAff); + sqliteFree(p); +} + +/* +** Remove the given index from the index hash table, and free +** its memory structures. +** +** The index is removed from the database hash tables but +** it is not unlinked from the Table that it indexes. +** Unlinking from the Table must be done by the calling function. +*/ +static void sqliteDeleteIndex(sqlite3 *db, Index *p){ + Index *pOld; + const char *zName = p->zName; + + pOld = sqlite3HashInsert(&p->pSchema->idxHash, zName, strlen( zName)+1, 0); + assert( pOld==0 || pOld==p ); + freeIndex(p); +} + +/* +** For the index called zIdxName which is found in the database iDb, +** unlike that index from its Table then remove the index from +** the index hash table and free all memory structures associated +** with the index. +*/ +void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char *zIdxName){ + Index *pIndex; + int len; + Hash *pHash = &db->aDb[iDb].pSchema->idxHash; + + len = strlen(zIdxName); + pIndex = sqlite3HashInsert(pHash, zIdxName, len+1, 0); + if( pIndex ){ + if( pIndex->pTable->pIndex==pIndex ){ + pIndex->pTable->pIndex = pIndex->pNext; + }else{ + Index *p; + for(p=pIndex->pTable->pIndex; p && p->pNext!=pIndex; p=p->pNext){} + if( p && p->pNext==pIndex ){ + p->pNext = pIndex->pNext; + } + } + freeIndex(pIndex); + } + db->flags |= SQLITE_InternChanges; +} + +/* +** Erase all schema information from the in-memory hash tables of +** a single database. This routine is called to reclaim memory +** before the database closes. It is also called during a rollback +** if there were schema changes during the transaction or if a +** schema-cookie mismatch occurs. +** +** If iDb<=0 then reset the internal schema tables for all database +** files. If iDb>=2 then reset the internal schema for only the +** single file indicated. +*/ +void sqlite3ResetInternalSchema(sqlite3 *db, int iDb){ + int i, j; + + assert( iDb>=0 && iDbnDb ); + for(i=iDb; inDb; i++){ + Db *pDb = &db->aDb[i]; + if( pDb->pSchema ){ + sqlite3SchemaFree(pDb->pSchema); + } + if( iDb>0 ) return; + } + assert( iDb==0 ); + db->flags &= ~SQLITE_InternChanges; + + /* If one or more of the auxiliary database files has been closed, + ** then remove them from the auxiliary database list. We take the + ** opportunity to do this here since we have just deleted all of the + ** schema hash tables and therefore do not have to make any changes + ** to any of those tables. + */ + for(i=0; inDb; i++){ + struct Db *pDb = &db->aDb[i]; + if( pDb->pBt==0 ){ + if( pDb->pAux && pDb->xFreeAux ) pDb->xFreeAux(pDb->pAux); + pDb->pAux = 0; + } + } + for(i=j=2; inDb; i++){ + struct Db *pDb = &db->aDb[i]; + if( pDb->pBt==0 ){ + sqliteFree(pDb->zName); + pDb->zName = 0; + continue; + } + if( jaDb[j] = db->aDb[i]; + } + j++; + } + memset(&db->aDb[j], 0, (db->nDb-j)*sizeof(db->aDb[j])); + db->nDb = j; + if( db->nDb<=2 && db->aDb!=db->aDbStatic ){ + memcpy(db->aDbStatic, db->aDb, 2*sizeof(db->aDb[0])); + sqliteFree(db->aDb); + db->aDb = db->aDbStatic; + } +} + +/* +** This routine is called whenever a rollback occurs. If there were +** schema changes during the transaction, then we have to reset the +** internal hash tables and reload them from disk. +*/ +void sqlite3RollbackInternalChanges(sqlite3 *db){ + if( db->flags & SQLITE_InternChanges ){ + sqlite3ResetInternalSchema(db, 0); + } +} + +/* +** This routine is called when a commit occurs. +*/ +void sqlite3CommitInternalChanges(sqlite3 *db){ + db->flags &= ~SQLITE_InternChanges; +} + +/* +** Clear the column names from a table or view. +*/ +static void sqliteResetColumnNames(Table *pTable){ + int i; + Column *pCol; + assert( pTable!=0 ); + if( (pCol = pTable->aCol)!=0 ){ + for(i=0; inCol; i++, pCol++){ + sqliteFree(pCol->zName); + sqlite3ExprDelete(pCol->pDflt); + sqliteFree(pCol->zType); + sqliteFree(pCol->zColl); + } + sqliteFree(pTable->aCol); + } + pTable->aCol = 0; + pTable->nCol = 0; +} + +/* +** Remove the memory data structures associated with the given +** Table. No changes are made to disk by this routine. +** +** This routine just deletes the data structure. It does not unlink +** the table data structure from the hash table. Nor does it remove +** foreign keys from the sqlite.aFKey hash table. But it does destroy +** memory structures of the indices and foreign keys associated with +** the table. +** +** Indices associated with the table are unlinked from the "db" +** data structure if db!=NULL. If db==NULL, indices attached to +** the table are deleted, but it is assumed they have already been +** unlinked. +*/ +void sqlite3DeleteTable(sqlite3 *db, Table *pTable){ + Index *pIndex, *pNext; + FKey *pFKey, *pNextFKey; + + db = 0; + + if( pTable==0 ) return; + + /* Do not delete the table until the reference count reaches zero. */ + pTable->nRef--; + if( pTable->nRef>0 ){ + return; + } + assert( pTable->nRef==0 ); + + /* Delete all indices associated with this table + */ + for(pIndex = pTable->pIndex; pIndex; pIndex=pNext){ + pNext = pIndex->pNext; + assert( pIndex->pSchema==pTable->pSchema ); + sqliteDeleteIndex(db, pIndex); + } + +#ifndef SQLITE_OMIT_FOREIGN_KEY + /* Delete all foreign keys associated with this table. The keys + ** should have already been unlinked from the db->aFKey hash table + */ + for(pFKey=pTable->pFKey; pFKey; pFKey=pNextFKey){ + pNextFKey = pFKey->pNextFrom; + assert( sqlite3HashFind(&pTable->pSchema->aFKey, + pFKey->zTo, strlen(pFKey->zTo)+1)!=pFKey ); + sqliteFree(pFKey); + } +#endif + + /* Delete the Table structure itself. + */ + sqliteResetColumnNames(pTable); + sqliteFree(pTable->zName); + sqliteFree(pTable->zColAff); + sqlite3SelectDelete(pTable->pSelect); +#ifndef SQLITE_OMIT_CHECK + sqlite3ExprDelete(pTable->pCheck); +#endif + sqliteFree(pTable); +} + +/* +** Unlink the given table from the hash tables and the delete the +** table structure with all its indices and foreign keys. +*/ +void sqlite3UnlinkAndDeleteTable(sqlite3 *db, int iDb, const char *zTabName){ + Table *p; + FKey *pF1, *pF2; + Db *pDb; + + assert( db!=0 ); + assert( iDb>=0 && iDbnDb ); + assert( zTabName && zTabName[0] ); + pDb = &db->aDb[iDb]; + p = sqlite3HashInsert(&pDb->pSchema->tblHash, zTabName, strlen(zTabName)+1,0); + if( p ){ +#ifndef SQLITE_OMIT_FOREIGN_KEY + for(pF1=p->pFKey; pF1; pF1=pF1->pNextFrom){ + int nTo = strlen(pF1->zTo) + 1; + pF2 = sqlite3HashFind(&pDb->pSchema->aFKey, pF1->zTo, nTo); + if( pF2==pF1 ){ + sqlite3HashInsert(&pDb->pSchema->aFKey, pF1->zTo, nTo, pF1->pNextTo); + }else{ + while( pF2 && pF2->pNextTo!=pF1 ){ pF2=pF2->pNextTo; } + if( pF2 ){ + pF2->pNextTo = pF1->pNextTo; + } + } + } +#endif + sqlite3DeleteTable(db, p); + } + db->flags |= SQLITE_InternChanges; +} + +/* +** Given a token, return a string that consists of the text of that +** token with any quotations removed. Space to hold the returned string +** is obtained from sqliteMalloc() and must be freed by the calling +** function. +** +** Tokens are often just pointers into the original SQL text and so +** are not \000 terminated and are not persistent. The returned string +** is \000 terminated and is persistent. +*/ +char *sqlite3NameFromToken(Token *pName){ + char *zName; + if( pName ){ + zName = sqliteStrNDup((char*)pName->z, pName->n); + sqlite3Dequote(zName); + }else{ + zName = 0; + } + return zName; +} + +/* +** Open the sqlite_master table stored in database number iDb for +** writing. The table is opened using cursor 0. +*/ +void sqlite3OpenMasterTable(Parse *p, int iDb){ + Vdbe *v = sqlite3GetVdbe(p); + sqlite3TableLock(p, iDb, MASTER_ROOT, 1, SCHEMA_TABLE(iDb)); + sqlite3VdbeAddOp(v, OP_Integer, iDb, 0); + sqlite3VdbeAddOp(v, OP_OpenWrite, 0, MASTER_ROOT); + sqlite3VdbeAddOp(v, OP_SetNumColumns, 0, 5); /* sqlite_master has 5 columns */ +} + +/* +** The token *pName contains the name of a database (either "main" or +** "temp" or the name of an attached db). This routine returns the +** index of the named database in db->aDb[], or -1 if the named db +** does not exist. +*/ +int sqlite3FindDb(sqlite3 *db, Token *pName){ + int i = -1; /* Database number */ + int n; /* Number of characters in the name */ + Db *pDb; /* A database whose name space is being searched */ + char *zName; /* Name we are searching for */ + + zName = sqlite3NameFromToken(pName); + if( zName ){ + n = strlen(zName); + for(i=(db->nDb-1), pDb=&db->aDb[i]; i>=0; i--, pDb--){ + if( (!OMIT_TEMPDB || i!=1 ) && n==strlen(pDb->zName) && + 0==sqlite3StrICmp(pDb->zName, zName) ){ + break; + } + } + sqliteFree(zName); + } + return i; +} + +/* The table or view or trigger name is passed to this routine via tokens +** pName1 and pName2. If the table name was fully qualified, for example: +** +** CREATE TABLE xxx.yyy (...); +** +** Then pName1 is set to "xxx" and pName2 "yyy". On the other hand if +** the table name is not fully qualified, i.e.: +** +** CREATE TABLE yyy(...); +** +** Then pName1 is set to "yyy" and pName2 is "". +** +** This routine sets the *ppUnqual pointer to point at the token (pName1 or +** pName2) that stores the unqualified table name. The index of the +** database "xxx" is returned. +*/ +int sqlite3TwoPartName( + Parse *pParse, /* Parsing and code generating context */ + Token *pName1, /* The "xxx" in the name "xxx.yyy" or "xxx" */ + Token *pName2, /* The "yyy" in the name "xxx.yyy" */ + Token **pUnqual /* Write the unqualified object name here */ +){ + int iDb; /* Database holding the object */ + sqlite3 *db = pParse->db; + + if( pName2 && pName2->n>0 ){ + assert( !db->init.busy ); + *pUnqual = pName2; + iDb = sqlite3FindDb(db, pName1); + if( iDb<0 ){ + sqlite3ErrorMsg(pParse, "unknown database %T", pName1); + pParse->nErr++; + return -1; + } + }else{ + assert( db->init.iDb==0 || db->init.busy ); + iDb = db->init.iDb; + *pUnqual = pName1; + } + return iDb; +} + +/* +** This routine is used to check if the UTF-8 string zName is a legal +** unqualified name for a new schema object (table, index, view or +** trigger). All names are legal except those that begin with the string +** "sqlite_" (in upper, lower or mixed case). This portion of the namespace +** is reserved for internal use. +*/ +int sqlite3CheckObjectName(Parse *pParse, const char *zName){ + if( !pParse->db->init.busy && pParse->nested==0 + && (pParse->db->flags & SQLITE_WriteSchema)==0 + && 0==sqlite3StrNICmp(zName, "sqlite_", 7) ){ + sqlite3ErrorMsg(pParse, "object name reserved for internal use: %s", zName); + return SQLITE_ERROR; + } + return SQLITE_OK; +} + +/* +** Begin constructing a new table representation in memory. This is +** the first of several action routines that get called in response +** to a CREATE TABLE statement. In particular, this routine is called +** after seeing tokens "CREATE" and "TABLE" and the table name. The +** pStart token is the CREATE and pName is the table name. The isTemp +** flag is true if the table should be stored in the auxiliary database +** file instead of in the main database file. This is normally the case +** when the "TEMP" or "TEMPORARY" keyword occurs in between +** CREATE and TABLE. +** +** The new table record is initialized and put in pParse->pNewTable. +** As more of the CREATE TABLE statement is parsed, additional action +** routines will be called to add more information to this record. +** At the end of the CREATE TABLE statement, the sqlite3EndTable() routine +** is called to complete the construction of the new table record. +*/ +void sqlite3StartTable( + Parse *pParse, /* Parser context */ + Token *pStart, /* The "CREATE" token */ + Token *pName1, /* First part of the name of the table or view */ + Token *pName2, /* Second part of the name of the table or view */ + int isTemp, /* True if this is a TEMP table */ + int isView, /* True if this is a VIEW */ + int noErr /* Do nothing if table already exists */ +){ + Table *pTable; + char *zName = 0; /* The name of the new table */ + sqlite3 *db = pParse->db; + Vdbe *v; + int iDb; /* Database number to create the table in */ + Token *pName; /* Unqualified name of the table to create */ + + /* The table or view name to create is passed to this routine via tokens + ** pName1 and pName2. If the table name was fully qualified, for example: + ** + ** CREATE TABLE xxx.yyy (...); + ** + ** Then pName1 is set to "xxx" and pName2 "yyy". On the other hand if + ** the table name is not fully qualified, i.e.: + ** + ** CREATE TABLE yyy(...); + ** + ** Then pName1 is set to "yyy" and pName2 is "". + ** + ** The call below sets the pName pointer to point at the token (pName1 or + ** pName2) that stores the unqualified table name. The variable iDb is + ** set to the index of the database that the table or view is to be + ** created in. + */ + iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName); + if( iDb<0 ) return; + if( !OMIT_TEMPDB && isTemp && iDb>1 ){ + /* If creating a temp table, the name may not be qualified */ + sqlite3ErrorMsg(pParse, "temporary table name must be unqualified"); + return; + } + if( !OMIT_TEMPDB && isTemp ) iDb = 1; + + pParse->sNameToken = *pName; + zName = sqlite3NameFromToken(pName); + if( zName==0 ) return; + if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ + goto begin_table_error; + } + if( db->init.iDb==1 ) isTemp = 1; +#ifndef SQLITE_OMIT_AUTHORIZATION + assert( (isTemp & 1)==isTemp ); + { + int code; + char *zDb = db->aDb[iDb].zName; + if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0, zDb) ){ + goto begin_table_error; + } + if( isView ){ + if( !OMIT_TEMPDB && isTemp ){ + code = SQLITE_CREATE_TEMP_VIEW; + }else{ + code = SQLITE_CREATE_VIEW; + } + }else{ + if( !OMIT_TEMPDB && isTemp ){ + code = SQLITE_CREATE_TEMP_TABLE; + }else{ + code = SQLITE_CREATE_TABLE; + } + } + if( sqlite3AuthCheck(pParse, code, zName, 0, zDb) ){ + goto begin_table_error; + } + } +#endif + + /* Make sure the new table name does not collide with an existing + ** index or table name in the same database. Issue an error message if + ** it does. + */ + if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ + goto begin_table_error; + } + pTable = sqlite3FindTable(db, zName, db->aDb[iDb].zName); + if( pTable ){ + if( !noErr ){ + sqlite3ErrorMsg(pParse, "table %T already exists", pName); + } + goto begin_table_error; + } + if( sqlite3FindIndex(db, zName, 0)!=0 && (iDb==0 || !db->init.busy) ){ + sqlite3ErrorMsg(pParse, "there is already an index named %s", zName); + goto begin_table_error; + } + pTable = sqliteMalloc( sizeof(Table) ); + if( pTable==0 ){ + pParse->rc = SQLITE_NOMEM; + pParse->nErr++; + goto begin_table_error; + } + pTable->zName = zName; + pTable->nCol = 0; + pTable->aCol = 0; + pTable->iPKey = -1; + pTable->pIndex = 0; + pTable->pSchema = db->aDb[iDb].pSchema; + pTable->nRef = 1; + if( pParse->pNewTable ) sqlite3DeleteTable(db, pParse->pNewTable); + pParse->pNewTable = pTable; + + /* If this is the magic sqlite_sequence table used by autoincrement, + ** then record a pointer to this table in the main database structure + ** so that INSERT can find the table easily. + */ +#ifndef SQLITE_OMIT_AUTOINCREMENT + if( !pParse->nested && strcmp(zName, "sqlite_sequence")==0 ){ + pTable->pSchema->pSeqTab = pTable; + } +#endif + + /* Begin generating the code that will insert the table record into + ** the SQLITE_MASTER table. Note in particular that we must go ahead + ** and allocate the record number for the table entry now. Before any + ** PRIMARY KEY or UNIQUE keywords are parsed. Those keywords will cause + ** indices to be created and the table record must come before the + ** indices. Hence, the record number for the table must be allocated + ** now. + */ + if( !db->init.busy && (v = sqlite3GetVdbe(pParse))!=0 ){ + int lbl; + int fileFormat; + sqlite3BeginWriteOperation(pParse, 0, iDb); + + /* If the file format and encoding in the database have not been set, + ** set them now. + */ + sqlite3VdbeAddOp(v, OP_ReadCookie, iDb, 1); /* file_format */ + lbl = sqlite3VdbeMakeLabel(v); + sqlite3VdbeAddOp(v, OP_If, 0, lbl); + fileFormat = (db->flags & SQLITE_LegacyFileFmt)!=0 ? + 1 : SQLITE_DEFAULT_FILE_FORMAT; + sqlite3VdbeAddOp(v, OP_Integer, fileFormat, 0); + sqlite3VdbeAddOp(v, OP_SetCookie, iDb, 1); + sqlite3VdbeAddOp(v, OP_Integer, ENC(db), 0); + sqlite3VdbeAddOp(v, OP_SetCookie, iDb, 4); + sqlite3VdbeResolveLabel(v, lbl); + + /* This just creates a place-holder record in the sqlite_master table. + ** The record created does not contain anything yet. It will be replaced + ** by the real entry in code generated at sqlite3EndTable(). + ** + ** The rowid for the new entry is left on the top of the stack. + ** The rowid value is needed by the code that sqlite3EndTable will + ** generate. + */ +#ifndef SQLITE_OMIT_VIEW + if( isView ){ + sqlite3VdbeAddOp(v, OP_Integer, 0, 0); + }else +#endif + { + sqlite3VdbeAddOp(v, OP_CreateTable, iDb, 0); + } + sqlite3OpenMasterTable(pParse, iDb); + sqlite3VdbeAddOp(v, OP_NewRowid, 0, 0); + sqlite3VdbeAddOp(v, OP_Dup, 0, 0); + sqlite3VdbeAddOp(v, OP_Null, 0, 0); + sqlite3VdbeAddOp(v, OP_Insert, 0, 0); + sqlite3VdbeAddOp(v, OP_Close, 0, 0); + sqlite3VdbeAddOp(v, OP_Pull, 1, 0); + } + + /* Normal (non-error) return. */ + return; + + /* If an error occurs, we jump here */ +begin_table_error: + sqliteFree(zName); + return; +} + +/* +** This macro is used to compare two strings in a case-insensitive manner. +** It is slightly faster than calling sqlite3StrICmp() directly, but +** produces larger code. +** +** WARNING: This macro is not compatible with the strcmp() family. It +** returns true if the two strings are equal, otherwise false. +*/ +#define STRICMP(x, y) (\ +sqlite3UpperToLower[*(unsigned char *)(x)]== \ +sqlite3UpperToLower[*(unsigned char *)(y)] \ +&& sqlite3StrICmp((x)+1,(y)+1)==0 ) + +/* +** Add a new column to the table currently being constructed. +** +** The parser calls this routine once for each column declaration +** in a CREATE TABLE statement. sqlite3StartTable() gets called +** first to get things going. Then this routine is called for each +** column. +*/ +void sqlite3AddColumn(Parse *pParse, Token *pName){ + Table *p; + int i; + char *z; + Column *pCol; + if( (p = pParse->pNewTable)==0 ) return; + z = sqlite3NameFromToken(pName); + if( z==0 ) return; + for(i=0; inCol; i++){ + if( STRICMP(z, p->aCol[i].zName) ){ + sqlite3ErrorMsg(pParse, "duplicate column name: %s", z); + sqliteFree(z); + return; + } + } + if( (p->nCol & 0x7)==0 ){ + Column *aNew; + aNew = sqliteRealloc( p->aCol, (p->nCol+8)*sizeof(p->aCol[0])); + if( aNew==0 ){ + sqliteFree(z); + return; + } + p->aCol = aNew; + } + pCol = &p->aCol[p->nCol]; + memset(pCol, 0, sizeof(p->aCol[0])); + pCol->zName = z; + + /* If there is no type specified, columns have the default affinity + ** 'NONE'. If there is a type specified, then sqlite3AddColumnType() will + ** be called next to set pCol->affinity correctly. + */ + pCol->affinity = SQLITE_AFF_NONE; + p->nCol++; +} + +/* +** This routine is called by the parser while in the middle of +** parsing a CREATE TABLE statement. A "NOT NULL" constraint has +** been seen on a column. This routine sets the notNull flag on +** the column currently under construction. +*/ +void sqlite3AddNotNull(Parse *pParse, int onError){ + Table *p; + int i; + if( (p = pParse->pNewTable)==0 ) return; + i = p->nCol-1; + if( i>=0 ) p->aCol[i].notNull = onError; +} + +/* +** Scan the column type name zType (length nType) and return the +** associated affinity type. +** +** This routine does a case-independent search of zType for the +** substrings in the following table. If one of the substrings is +** found, the corresponding affinity is returned. If zType contains +** more than one of the substrings, entries toward the top of +** the table take priority. For example, if zType is 'BLOBINT', +** SQLITE_AFF_INTEGER is returned. +** +** Substring | Affinity +** -------------------------------- +** 'INT' | SQLITE_AFF_INTEGER +** 'CHAR' | SQLITE_AFF_TEXT +** 'CLOB' | SQLITE_AFF_TEXT +** 'TEXT' | SQLITE_AFF_TEXT +** 'BLOB' | SQLITE_AFF_NONE +** 'REAL' | SQLITE_AFF_REAL +** 'FLOA' | SQLITE_AFF_REAL +** 'DOUB' | SQLITE_AFF_REAL +** +** If none of the substrings in the above table are found, +** SQLITE_AFF_NUMERIC is returned. +*/ +char sqlite3AffinityType(const Token *pType){ + u32 h = 0; + char aff = SQLITE_AFF_NUMERIC; + const unsigned char *zIn = pType->z; + const unsigned char *zEnd = &pType->z[pType->n]; + + while( zIn!=zEnd ){ + h = (h<<8) + sqlite3UpperToLower[*zIn]; + zIn++; + if( h==(('c'<<24)+('h'<<16)+('a'<<8)+'r') ){ /* CHAR */ + aff = SQLITE_AFF_TEXT; + }else if( h==(('c'<<24)+('l'<<16)+('o'<<8)+'b') ){ /* CLOB */ + aff = SQLITE_AFF_TEXT; + }else if( h==(('t'<<24)+('e'<<16)+('x'<<8)+'t') ){ /* TEXT */ + aff = SQLITE_AFF_TEXT; + }else if( h==(('b'<<24)+('l'<<16)+('o'<<8)+'b') /* BLOB */ + && (aff==SQLITE_AFF_NUMERIC || aff==SQLITE_AFF_REAL) ){ + aff = SQLITE_AFF_NONE; +#ifndef SQLITE_OMIT_FLOATING_POINT + }else if( h==(('r'<<24)+('e'<<16)+('a'<<8)+'l') /* REAL */ + && aff==SQLITE_AFF_NUMERIC ){ + aff = SQLITE_AFF_REAL; + }else if( h==(('f'<<24)+('l'<<16)+('o'<<8)+'a') /* FLOA */ + && aff==SQLITE_AFF_NUMERIC ){ + aff = SQLITE_AFF_REAL; + }else if( h==(('d'<<24)+('o'<<16)+('u'<<8)+'b') /* DOUB */ + && aff==SQLITE_AFF_NUMERIC ){ + aff = SQLITE_AFF_REAL; +#endif + }else if( (h&0x00FFFFFF)==(('i'<<16)+('n'<<8)+'t') ){ /* INT */ + aff = SQLITE_AFF_INTEGER; + break; + } + } + + return aff; +} + +/* +** This routine is called by the parser while in the middle of +** parsing a CREATE TABLE statement. The pFirst token is the first +** token in the sequence of tokens that describe the type of the +** column currently under construction. pLast is the last token +** in the sequence. Use this information to construct a string +** that contains the typename of the column and store that string +** in zType. +*/ +void sqlite3AddColumnType(Parse *pParse, Token *pType){ + Table *p; + int i; + Column *pCol; + + if( (p = pParse->pNewTable)==0 ) return; + i = p->nCol-1; + if( i<0 ) return; + pCol = &p->aCol[i]; + sqliteFree(pCol->zType); + pCol->zType = sqlite3NameFromToken(pType); + pCol->affinity = sqlite3AffinityType(pType); +} + +/* +** The expression is the default value for the most recently added column +** of the table currently under construction. +** +** Default value expressions must be constant. Raise an exception if this +** is not the case. +** +** This routine is called by the parser while in the middle of +** parsing a CREATE TABLE statement. +*/ +void sqlite3AddDefaultValue(Parse *pParse, Expr *pExpr){ + Table *p; + Column *pCol; + if( (p = pParse->pNewTable)!=0 ){ + pCol = &(p->aCol[p->nCol-1]); + if( !sqlite3ExprIsConstantOrFunction(pExpr) ){ + sqlite3ErrorMsg(pParse, "default value of column [%s] is not constant", + pCol->zName); + }else{ + sqlite3ExprDelete(pCol->pDflt); + pCol->pDflt = sqlite3ExprDup(pExpr); + } + } + sqlite3ExprDelete(pExpr); +} + +/* +** Designate the PRIMARY KEY for the table. pList is a list of names +** of columns that form the primary key. If pList is NULL, then the +** most recently added column of the table is the primary key. +** +** A table can have at most one primary key. If the table already has +** a primary key (and this is the second primary key) then create an +** error. +** +** If the PRIMARY KEY is on a single column whose datatype is INTEGER, +** then we will try to use that column as the rowid. Set the Table.iPKey +** field of the table under construction to be the index of the +** INTEGER PRIMARY KEY column. Table.iPKey is set to -1 if there is +** no INTEGER PRIMARY KEY. +** +** If the key is not an INTEGER PRIMARY KEY, then create a unique +** index for the key. No index is created for INTEGER PRIMARY KEYs. +*/ +void sqlite3AddPrimaryKey( + Parse *pParse, /* Parsing context */ + ExprList *pList, /* List of field names to be indexed */ + int onError, /* What to do with a uniqueness conflict */ + int autoInc, /* True if the AUTOINCREMENT keyword is present */ + int sortOrder /* SQLITE_SO_ASC or SQLITE_SO_DESC */ +){ + Table *pTab = pParse->pNewTable; + char *zType = 0; + int iCol = -1, i; + if( pTab==0 ) goto primary_key_exit; + if( pTab->hasPrimKey ){ + sqlite3ErrorMsg(pParse, + "table \"%s\" has more than one primary key", pTab->zName); + goto primary_key_exit; + } + pTab->hasPrimKey = 1; + if( pList==0 ){ + iCol = pTab->nCol - 1; + pTab->aCol[iCol].isPrimKey = 1; + }else{ + for(i=0; inExpr; i++){ + for(iCol=0; iColnCol; iCol++){ + if( sqlite3StrICmp(pList->a[i].zName, pTab->aCol[iCol].zName)==0 ){ + break; + } + } + if( iColnCol ){ + pTab->aCol[iCol].isPrimKey = 1; + } + } + if( pList->nExpr>1 ) iCol = -1; + } + if( iCol>=0 && iColnCol ){ + zType = pTab->aCol[iCol].zType; + } + if( zType && sqlite3StrICmp(zType, "INTEGER")==0 + && sortOrder==SQLITE_SO_ASC ){ + pTab->iPKey = iCol; + pTab->keyConf = onError; + pTab->autoInc = autoInc; + }else if( autoInc ){ +#ifndef SQLITE_OMIT_AUTOINCREMENT + sqlite3ErrorMsg(pParse, "AUTOINCREMENT is only allowed on an " + "INTEGER PRIMARY KEY"); +#endif + }else{ + sqlite3CreateIndex(pParse, 0, 0, 0, pList, onError, 0, 0, sortOrder, 0); + pList = 0; + } + +primary_key_exit: + sqlite3ExprListDelete(pList); + return; +} + +/* +** Add a new CHECK constraint to the table currently under construction. +*/ +void sqlite3AddCheckConstraint( + Parse *pParse, /* Parsing context */ + Expr *pCheckExpr /* The check expression */ +){ +#ifndef SQLITE_OMIT_CHECK + Table *pTab = pParse->pNewTable; + if( pTab ){ + /* The CHECK expression must be duplicated so that tokens refer + ** to malloced space and not the (ephemeral) text of the CREATE TABLE + ** statement */ + pTab->pCheck = sqlite3ExprAnd(pTab->pCheck, sqlite3ExprDup(pCheckExpr)); + } +#endif + sqlite3ExprDelete(pCheckExpr); +} + +/* +** Set the collation function of the most recently parsed table column +** to the CollSeq given. +*/ +void sqlite3AddCollateType(Parse *pParse, const char *zType, int nType){ + Table *p; + int i; + + if( (p = pParse->pNewTable)==0 ) return; + i = p->nCol-1; + + if( sqlite3LocateCollSeq(pParse, zType, nType) ){ + Index *pIdx; + p->aCol[i].zColl = sqliteStrNDup(zType, nType); + + /* If the column is declared as " PRIMARY KEY COLLATE ", + ** then an index may have been created on this column before the + ** collation type was added. Correct this if it is the case. + */ + for(pIdx=p->pIndex; pIdx; pIdx=pIdx->pNext){ + assert( pIdx->nColumn==1 ); + if( pIdx->aiColumn[0]==i ){ + pIdx->azColl[0] = p->aCol[i].zColl; + } + } + } +} + +/* +** This function returns the collation sequence for database native text +** encoding identified by the string zName, length nName. +** +** If the requested collation sequence is not available, or not available +** in the database native encoding, the collation factory is invoked to +** request it. If the collation factory does not supply such a sequence, +** and the sequence is available in another text encoding, then that is +** returned instead. +** +** If no versions of the requested collations sequence are available, or +** another error occurs, NULL is returned and an error message written into +** pParse. +*/ +CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName, int nName){ + sqlite3 *db = pParse->db; + u8 enc = ENC(db); + u8 initbusy = db->init.busy; + CollSeq *pColl; + + pColl = sqlite3FindCollSeq(db, enc, zName, nName, initbusy); + if( !initbusy && (!pColl || !pColl->xCmp) ){ + pColl = sqlite3GetCollSeq(db, pColl, zName, nName); + if( !pColl ){ + if( nName<0 ){ + nName = strlen(zName); + } + sqlite3ErrorMsg(pParse, "no such collation sequence: %.*s", nName, zName); + pColl = 0; + } + } + + return pColl; +} + + +/* +** Generate code that will increment the schema cookie. +** +** The schema cookie is used to determine when the schema for the +** database changes. After each schema change, the cookie value +** changes. When a process first reads the schema it records the +** cookie. Thereafter, whenever it goes to access the database, +** it checks the cookie to make sure the schema has not changed +** since it was last read. +** +** This plan is not completely bullet-proof. It is possible for +** the schema to change multiple times and for the cookie to be +** set back to prior value. But schema changes are infrequent +** and the probability of hitting the same cookie value is only +** 1 chance in 2^32. So we're safe enough. +*/ +void sqlite3ChangeCookie(sqlite3 *db, Vdbe *v, int iDb){ + sqlite3VdbeAddOp(v, OP_Integer, db->aDb[iDb].pSchema->schema_cookie+1, 0); + sqlite3VdbeAddOp(v, OP_SetCookie, iDb, 0); +} + +/* +** Measure the number of characters needed to output the given +** identifier. The number returned includes any quotes used +** but does not include the null terminator. +** +** The estimate is conservative. It might be larger that what is +** really needed. +*/ +static int identLength(const char *z){ + int n; + for(n=0; *z; n++, z++){ + if( *z=='"' ){ n++; } + } + return n + 2; +} + +/* +** Write an identifier onto the end of the given string. Add +** quote characters as needed. +*/ +static void identPut(char *z, int *pIdx, char *zSignedIdent){ + unsigned char *zIdent = (unsigned char*)zSignedIdent; + int i, j, needQuote; + i = *pIdx; + for(j=0; zIdent[j]; j++){ + if( !isalnum(zIdent[j]) && zIdent[j]!='_' ) break; + } + needQuote = zIdent[j]!=0 || isdigit(zIdent[0]) + || sqlite3KeywordCode(zIdent, j)!=TK_ID; + if( needQuote ) z[i++] = '"'; + for(j=0; zIdent[j]; j++){ + z[i++] = zIdent[j]; + if( zIdent[j]=='"' ) z[i++] = '"'; + } + if( needQuote ) z[i++] = '"'; + z[i] = 0; + *pIdx = i; +} + +/* +** Generate a CREATE TABLE statement appropriate for the given +** table. Memory to hold the text of the statement is obtained +** from sqliteMalloc() and must be freed by the calling function. +*/ +static char *createTableStmt(Table *p, int isTemp){ + int i, k, n; + char *zStmt; + char *zSep, *zSep2, *zEnd, *z; + Column *pCol; + n = 0; + for(pCol = p->aCol, i=0; inCol; i++, pCol++){ + n += identLength(pCol->zName); + z = pCol->zType; + if( z ){ + n += (strlen(z) + 1); + } + } + n += identLength(p->zName); + if( n<50 ){ + zSep = ""; + zSep2 = ","; + zEnd = ")"; + }else{ + zSep = "\n "; + zSep2 = ",\n "; + zEnd = "\n)"; + } + n += 35 + 6*p->nCol; + zStmt = sqliteMallocRaw( n ); + if( zStmt==0 ) return 0; + strcpy(zStmt, !OMIT_TEMPDB&&isTemp ? "CREATE TEMP TABLE ":"CREATE TABLE "); + k = strlen(zStmt); + identPut(zStmt, &k, p->zName); + zStmt[k++] = '('; + for(pCol=p->aCol, i=0; inCol; i++, pCol++){ + strcpy(&zStmt[k], zSep); + k += strlen(&zStmt[k]); + zSep = zSep2; + identPut(zStmt, &k, pCol->zName); + if( (z = pCol->zType)!=0 ){ + zStmt[k++] = ' '; + strcpy(&zStmt[k], z); + k += strlen(z); + } + } + strcpy(&zStmt[k], zEnd); + return zStmt; +} + +/* +** This routine is called to report the final ")" that terminates +** a CREATE TABLE statement. +** +** The table structure that other action routines have been building +** is added to the internal hash tables, assuming no errors have +** occurred. +** +** An entry for the table is made in the master table on disk, unless +** this is a temporary table or db->init.busy==1. When db->init.busy==1 +** it means we are reading the sqlite_master table because we just +** connected to the database or because the sqlite_master table has +** recently changed, so the entry for this table already exists in +** the sqlite_master table. We do not want to create it again. +** +** If the pSelect argument is not NULL, it means that this routine +** was called to create a table generated from a +** "CREATE TABLE ... AS SELECT ..." statement. The column names of +** the new table will match the result set of the SELECT. +*/ +void sqlite3EndTable( + Parse *pParse, /* Parse context */ + Token *pCons, /* The ',' token after the last column defn. */ + Token *pEnd, /* The final ')' token in the CREATE TABLE */ + Select *pSelect /* Select from a "CREATE ... AS SELECT" */ +){ + Table *p; + sqlite3 *db = pParse->db; + int iDb; + + if( (pEnd==0 && pSelect==0) || pParse->nErr || sqlite3MallocFailed() ) { + return; + } + p = pParse->pNewTable; + if( p==0 ) return; + + assert( !db->init.busy || !pSelect ); + + iDb = sqlite3SchemaToIndex(pParse->db, p->pSchema); + +#ifndef SQLITE_OMIT_CHECK + /* Resolve names in all CHECK constraint expressions. + */ + if( p->pCheck ){ + SrcList sSrc; /* Fake SrcList for pParse->pNewTable */ + NameContext sNC; /* Name context for pParse->pNewTable */ + + memset(&sNC, 0, sizeof(sNC)); + memset(&sSrc, 0, sizeof(sSrc)); + sSrc.nSrc = 1; + sSrc.a[0].zName = p->zName; + sSrc.a[0].pTab = p; + sSrc.a[0].iCursor = -1; + sNC.pParse = pParse; + sNC.pSrcList = &sSrc; + sNC.isCheck = 1; + if( sqlite3ExprResolveNames(&sNC, p->pCheck) ){ + return; + } + } +#endif /* !defined(SQLITE_OMIT_CHECK) */ + + /* If the db->init.busy is 1 it means we are reading the SQL off the + ** "sqlite_master" or "sqlite_temp_master" table on the disk. + ** So do not write to the disk again. Extract the root page number + ** for the table from the db->init.newTnum field. (The page number + ** should have been put there by the sqliteOpenCb routine.) + */ + if( db->init.busy ){ + p->tnum = db->init.newTnum; + } + + /* If not initializing, then create a record for the new table + ** in the SQLITE_MASTER table of the database. The record number + ** for the new table entry should already be on the stack. + ** + ** If this is a TEMPORARY table, write the entry into the auxiliary + ** file instead of into the main database file. + */ + if( !db->init.busy ){ + int n; + Vdbe *v; + char *zType; /* "view" or "table" */ + char *zType2; /* "VIEW" or "TABLE" */ + char *zStmt; /* Text of the CREATE TABLE or CREATE VIEW statement */ + + v = sqlite3GetVdbe(pParse); + if( v==0 ) return; + + sqlite3VdbeAddOp(v, OP_Close, 0, 0); + + /* Create the rootpage for the new table and push it onto the stack. + ** A view has no rootpage, so just push a zero onto the stack for + ** views. Initialize zType at the same time. + */ + if( p->pSelect==0 ){ + /* A regular table */ + zType = "table"; + zType2 = "TABLE"; +#ifndef SQLITE_OMIT_VIEW + }else{ + /* A view */ + zType = "view"; + zType2 = "VIEW"; +#endif + } + + /* If this is a CREATE TABLE xx AS SELECT ..., execute the SELECT + ** statement to populate the new table. The root-page number for the + ** new table is on the top of the vdbe stack. + ** + ** Once the SELECT has been coded by sqlite3Select(), it is in a + ** suitable state to query for the column names and types to be used + ** by the new table. + ** + ** A shared-cache write-lock is not required to write to the new table, + ** as a schema-lock must have already been obtained to create it. Since + ** a schema-lock excludes all other database users, the write-lock would + ** be redundant. + */ + if( pSelect ){ + Table *pSelTab; + sqlite3VdbeAddOp(v, OP_Dup, 0, 0); + sqlite3VdbeAddOp(v, OP_Integer, iDb, 0); + sqlite3VdbeAddOp(v, OP_OpenWrite, 1, 0); + pParse->nTab = 2; + sqlite3Select(pParse, pSelect, SRT_Table, 1, 0, 0, 0, 0); + sqlite3VdbeAddOp(v, OP_Close, 1, 0); + if( pParse->nErr==0 ){ + pSelTab = sqlite3ResultSetOfSelect(pParse, 0, pSelect); + if( pSelTab==0 ) return; + assert( p->aCol==0 ); + p->nCol = pSelTab->nCol; + p->aCol = pSelTab->aCol; + pSelTab->nCol = 0; + pSelTab->aCol = 0; + sqlite3DeleteTable(0, pSelTab); + } + } + + /* Compute the complete text of the CREATE statement */ + if( pSelect ){ + zStmt = createTableStmt(p, p->pSchema==pParse->db->aDb[1].pSchema); + }else{ + n = pEnd->z - pParse->sNameToken.z + 1; + zStmt = sqlite3MPrintf("CREATE %s %.*s", zType2, n, pParse->sNameToken.z); + } + + /* A slot for the record has already been allocated in the + ** SQLITE_MASTER table. We just need to update that slot with all + ** the information we've collected. The rowid for the preallocated + ** slot is the 2nd item on the stack. The top of the stack is the + ** root page for the new table (or a 0 if this is a view). + */ + sqlite3NestedParse(pParse, + "UPDATE %Q.%s " + "SET type='%s', name=%Q, tbl_name=%Q, rootpage=#0, sql=%Q " + "WHERE rowid=#1", + db->aDb[iDb].zName, SCHEMA_TABLE(iDb), + zType, + p->zName, + p->zName, + zStmt + ); + sqliteFree(zStmt); + sqlite3ChangeCookie(db, v, iDb); + +#ifndef SQLITE_OMIT_AUTOINCREMENT + /* Check to see if we need to create an sqlite_sequence table for + ** keeping track of autoincrement keys. + */ + if( p->autoInc ){ + Db *pDb = &db->aDb[iDb]; + if( pDb->pSchema->pSeqTab==0 ){ + sqlite3NestedParse(pParse, + "CREATE TABLE %Q.sqlite_sequence(name,seq)", + pDb->zName + ); + } + } +#endif + + /* Reparse everything to update our internal data structures */ + sqlite3VdbeOp3(v, OP_ParseSchema, iDb, 0, + sqlite3MPrintf("tbl_name='%q'",p->zName), P3_DYNAMIC); + } + + + /* Add the table to the in-memory representation of the database. + */ + if( db->init.busy && pParse->nErr==0 ){ + Table *pOld; + FKey *pFKey; + Schema *pSchema = p->pSchema; + pOld = sqlite3HashInsert(&pSchema->tblHash, p->zName, strlen(p->zName)+1,p); + if( pOld ){ + assert( p==pOld ); /* Malloc must have failed inside HashInsert() */ + return; + } +#ifndef SQLITE_OMIT_FOREIGN_KEY + for(pFKey=p->pFKey; pFKey; pFKey=pFKey->pNextFrom){ + int nTo = strlen(pFKey->zTo) + 1; + pFKey->pNextTo = sqlite3HashFind(&pSchema->aFKey, pFKey->zTo, nTo); + sqlite3HashInsert(&pSchema->aFKey, pFKey->zTo, nTo, pFKey); + } +#endif + pParse->pNewTable = 0; + db->nTable++; + db->flags |= SQLITE_InternChanges; + +#ifndef SQLITE_OMIT_ALTERTABLE + if( !p->pSelect ){ + const char *zName = (const char *)pParse->sNameToken.z; + int nName; + assert( !pSelect && pCons && pEnd ); + if( pCons->z==0 ){ + pCons = pEnd; + } + nName = (const char *)pCons->z - zName; + p->addColOffset = 13 + sqlite3utf8CharLen(zName, nName); + } +#endif + } +} + +#ifndef SQLITE_OMIT_VIEW +/* +** The parser calls this routine in order to create a new VIEW +*/ +void sqlite3CreateView( + Parse *pParse, /* The parsing context */ + Token *pBegin, /* The CREATE token that begins the statement */ + Token *pName1, /* The token that holds the name of the view */ + Token *pName2, /* The token that holds the name of the view */ + Select *pSelect, /* A SELECT statement that will become the new view */ + int isTemp /* TRUE for a TEMPORARY view */ +){ + Table *p; + int n; + const unsigned char *z; + Token sEnd; + DbFixer sFix; + Token *pName; + int iDb; + + if( pParse->nVar>0 ){ + sqlite3ErrorMsg(pParse, "parameters are not allowed in views"); + sqlite3SelectDelete(pSelect); + return; + } + sqlite3StartTable(pParse, pBegin, pName1, pName2, isTemp, 1, 0); + p = pParse->pNewTable; + if( p==0 || pParse->nErr ){ + sqlite3SelectDelete(pSelect); + return; + } + sqlite3TwoPartName(pParse, pName1, pName2, &pName); + iDb = sqlite3SchemaToIndex(pParse->db, p->pSchema); + if( sqlite3FixInit(&sFix, pParse, iDb, "view", pName) + && sqlite3FixSelect(&sFix, pSelect) + ){ + sqlite3SelectDelete(pSelect); + return; + } + + /* Make a copy of the entire SELECT statement that defines the view. + ** This will force all the Expr.token.z values to be dynamically + ** allocated rather than point to the input string - which means that + ** they will persist after the current sqlite3_exec() call returns. + */ + p->pSelect = sqlite3SelectDup(pSelect); + sqlite3SelectDelete(pSelect); + if( sqlite3MallocFailed() ){ + return; + } + if( !pParse->db->init.busy ){ + sqlite3ViewGetColumnNames(pParse, p); + } + + /* Locate the end of the CREATE VIEW statement. Make sEnd point to + ** the end. + */ + sEnd = pParse->sLastToken; + if( sEnd.z[0]!=0 && sEnd.z[0]!=';' ){ + sEnd.z += sEnd.n; + } + sEnd.n = 0; + n = sEnd.z - pBegin->z; + z = (const unsigned char*)pBegin->z; + while( n>0 && (z[n-1]==';' || isspace(z[n-1])) ){ n--; } + sEnd.z = &z[n-1]; + sEnd.n = 1; + + /* Use sqlite3EndTable() to add the view to the SQLITE_MASTER table */ + sqlite3EndTable(pParse, 0, &sEnd, 0); + return; +} +#endif /* SQLITE_OMIT_VIEW */ + +#ifndef SQLITE_OMIT_VIEW +/* +** The Table structure pTable is really a VIEW. Fill in the names of +** the columns of the view in the pTable structure. Return the number +** of errors. If an error is seen leave an error message in pParse->zErrMsg. +*/ +int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ + Table *pSelTab; /* A fake table from which we get the result set */ + Select *pSel; /* Copy of the SELECT that implements the view */ + int nErr = 0; /* Number of errors encountered */ + int n; /* Temporarily holds the number of cursors assigned */ + + assert( pTable ); + + /* A positive nCol means the columns names for this view are + ** already known. + */ + if( pTable->nCol>0 ) return 0; + + /* A negative nCol is a special marker meaning that we are currently + ** trying to compute the column names. If we enter this routine with + ** a negative nCol, it means two or more views form a loop, like this: + ** + ** CREATE VIEW one AS SELECT * FROM two; + ** CREATE VIEW two AS SELECT * FROM one; + ** + ** Actually, this error is caught previously and so the following test + ** should always fail. But we will leave it in place just to be safe. + */ + if( pTable->nCol<0 ){ + sqlite3ErrorMsg(pParse, "view %s is circularly defined", pTable->zName); + return 1; + } + assert( pTable->nCol>=0 ); + + /* If we get this far, it means we need to compute the table names. + ** Note that the call to sqlite3ResultSetOfSelect() will expand any + ** "*" elements in the results set of the view and will assign cursors + ** to the elements of the FROM clause. But we do not want these changes + ** to be permanent. So the computation is done on a copy of the SELECT + ** statement that defines the view. + */ + assert( pTable->pSelect ); + pSel = sqlite3SelectDup(pTable->pSelect); + if( pSel ){ + n = pParse->nTab; + sqlite3SrcListAssignCursors(pParse, pSel->pSrc); + pTable->nCol = -1; + pSelTab = sqlite3ResultSetOfSelect(pParse, 0, pSel); + pParse->nTab = n; + if( pSelTab ){ + assert( pTable->aCol==0 ); + pTable->nCol = pSelTab->nCol; + pTable->aCol = pSelTab->aCol; + pSelTab->nCol = 0; + pSelTab->aCol = 0; + sqlite3DeleteTable(0, pSelTab); + pTable->pSchema->flags |= DB_UnresetViews; + }else{ + pTable->nCol = 0; + nErr++; + } + sqlite3SelectDelete(pSel); + } else { + nErr++; + } + return nErr; +} +#endif /* SQLITE_OMIT_VIEW */ + +#ifndef SQLITE_OMIT_VIEW +/* +** Clear the column names from every VIEW in database idx. +*/ +static void sqliteViewResetAll(sqlite3 *db, int idx){ + HashElem *i; + if( !DbHasProperty(db, idx, DB_UnresetViews) ) return; + for(i=sqliteHashFirst(&db->aDb[idx].pSchema->tblHash); i;i=sqliteHashNext(i)){ + Table *pTab = sqliteHashData(i); + if( pTab->pSelect ){ + sqliteResetColumnNames(pTab); + } + } + DbClearProperty(db, idx, DB_UnresetViews); +} +#else +# define sqliteViewResetAll(A,B) +#endif /* SQLITE_OMIT_VIEW */ + +/* +** This function is called by the VDBE to adjust the internal schema +** used by SQLite when the btree layer moves a table root page. The +** root-page of a table or index in database iDb has changed from iFrom +** to iTo. +*/ +#ifndef SQLITE_OMIT_AUTOVACUUM +void sqlite3RootPageMoved(Db *pDb, int iFrom, int iTo){ + HashElem *pElem; + Hash *pHash; + + pHash = &pDb->pSchema->tblHash; + for(pElem=sqliteHashFirst(pHash); pElem; pElem=sqliteHashNext(pElem)){ + Table *pTab = sqliteHashData(pElem); + if( pTab->tnum==iFrom ){ + pTab->tnum = iTo; + return; + } + } + pHash = &pDb->pSchema->idxHash; + for(pElem=sqliteHashFirst(pHash); pElem; pElem=sqliteHashNext(pElem)){ + Index *pIdx = sqliteHashData(pElem); + if( pIdx->tnum==iFrom ){ + pIdx->tnum = iTo; + return; + } + } + assert(0); +} +#endif + +/* +** Write code to erase the table with root-page iTable from database iDb. +** Also write code to modify the sqlite_master table and internal schema +** if a root-page of another table is moved by the btree-layer whilst +** erasing iTable (this can happen with an auto-vacuum database). +*/ +static void destroyRootPage(Parse *pParse, int iTable, int iDb){ + Vdbe *v = sqlite3GetVdbe(pParse); + sqlite3VdbeAddOp(v, OP_Destroy, iTable, iDb); +#ifndef SQLITE_OMIT_AUTOVACUUM + /* OP_Destroy pushes an integer onto the stack. If this integer + ** is non-zero, then it is the root page number of a table moved to + ** location iTable. The following code modifies the sqlite_master table to + ** reflect this. + ** + ** The "#0" in the SQL is a special constant that means whatever value + ** is on the top of the stack. See sqlite3RegisterExpr(). + */ + sqlite3NestedParse(pParse, + "UPDATE %Q.%s SET rootpage=%d WHERE #0 AND rootpage=#0", + pParse->db->aDb[iDb].zName, SCHEMA_TABLE(iDb), iTable); +#endif +} + +/* +** Write VDBE code to erase table pTab and all associated indices on disk. +** Code to update the sqlite_master tables and internal schema definitions +** in case a root-page belonging to another table is moved by the btree layer +** is also added (this can happen with an auto-vacuum database). +*/ +static void destroyTable(Parse *pParse, Table *pTab){ +#ifdef SQLITE_OMIT_AUTOVACUUM + Index *pIdx; + int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); + destroyRootPage(pParse, pTab->tnum, iDb); + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + destroyRootPage(pParse, pIdx->tnum, iDb); + } +#else + /* If the database may be auto-vacuum capable (if SQLITE_OMIT_AUTOVACUUM + ** is not defined), then it is important to call OP_Destroy on the + ** table and index root-pages in order, starting with the numerically + ** largest root-page number. This guarantees that none of the root-pages + ** to be destroyed is relocated by an earlier OP_Destroy. i.e. if the + ** following were coded: + ** + ** OP_Destroy 4 0 + ** ... + ** OP_Destroy 5 0 + ** + ** and root page 5 happened to be the largest root-page number in the + ** database, then root page 5 would be moved to page 4 by the + ** "OP_Destroy 4 0" opcode. The subsequent "OP_Destroy 5 0" would hit + ** a free-list page. + */ + int iTab = pTab->tnum; + int iDestroyed = 0; + + while( 1 ){ + Index *pIdx; + int iLargest = 0; + + if( iDestroyed==0 || iTabpIndex; pIdx; pIdx=pIdx->pNext){ + int iIdx = pIdx->tnum; + assert( pIdx->pSchema==pTab->pSchema ); + if( (iDestroyed==0 || (iIdxiLargest ){ + iLargest = iIdx; + } + } + if( iLargest==0 ){ + return; + }else{ + int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); + destroyRootPage(pParse, iLargest, iDb); + iDestroyed = iLargest; + } + } +#endif +} + +/* +** This routine is called to do the work of a DROP TABLE statement. +** pName is the name of the table to be dropped. +*/ +void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){ + Table *pTab; + Vdbe *v; + sqlite3 *db = pParse->db; + int iDb; + + if( pParse->nErr || sqlite3MallocFailed() ){ + goto exit_drop_table; + } + assert( pName->nSrc==1 ); + pTab = sqlite3LocateTable(pParse, pName->a[0].zName, pName->a[0].zDatabase); + + if( pTab==0 ){ + if( noErr ){ + sqlite3ErrorClear(pParse); + } + goto exit_drop_table; + } + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); + assert( iDb>=0 && iDbnDb ); +#ifndef SQLITE_OMIT_AUTHORIZATION + { + int code; + const char *zTab = SCHEMA_TABLE(iDb); + const char *zDb = db->aDb[iDb].zName; + if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb)){ + goto exit_drop_table; + } + if( isView ){ + if( !OMIT_TEMPDB && iDb==1 ){ + code = SQLITE_DROP_TEMP_VIEW; + }else{ + code = SQLITE_DROP_VIEW; + } + }else{ + if( !OMIT_TEMPDB && iDb==1 ){ + code = SQLITE_DROP_TEMP_TABLE; + }else{ + code = SQLITE_DROP_TABLE; + } + } + if( sqlite3AuthCheck(pParse, code, pTab->zName, 0, zDb) ){ + goto exit_drop_table; + } + if( sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){ + goto exit_drop_table; + } + } +#endif + if( pTab->readOnly || pTab==db->aDb[iDb].pSchema->pSeqTab ){ + sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab->zName); + goto exit_drop_table; + } + +#ifndef SQLITE_OMIT_VIEW + /* Ensure DROP TABLE is not used on a view, and DROP VIEW is not used + ** on a table. + */ + if( isView && pTab->pSelect==0 ){ + sqlite3ErrorMsg(pParse, "use DROP TABLE to delete table %s", pTab->zName); + goto exit_drop_table; + } + if( !isView && pTab->pSelect ){ + sqlite3ErrorMsg(pParse, "use DROP VIEW to delete view %s", pTab->zName); + goto exit_drop_table; + } +#endif + + /* Generate code to remove the table from the master table + ** on disk. + */ + v = sqlite3GetVdbe(pParse); + if( v ){ + Trigger *pTrigger; + Db *pDb = &db->aDb[iDb]; + sqlite3BeginWriteOperation(pParse, 0, iDb); + + /* Drop all triggers associated with the table being dropped. Code + ** is generated to remove entries from sqlite_master and/or + ** sqlite_temp_master if required. + */ + pTrigger = pTab->pTrigger; + while( pTrigger ){ + assert( pTrigger->pSchema==pTab->pSchema || + pTrigger->pSchema==db->aDb[1].pSchema ); + sqlite3DropTriggerPtr(pParse, pTrigger, 1); + pTrigger = pTrigger->pNext; + } + +#ifndef SQLITE_OMIT_AUTOINCREMENT + /* Remove any entries of the sqlite_sequence table associated with + ** the table being dropped. This is done before the table is dropped + ** 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 ){ + sqlite3NestedParse(pParse, + "DELETE FROM %s.sqlite_sequence WHERE name=%Q", + pDb->zName, pTab->zName + ); + } +#endif + + /* Drop all SQLITE_MASTER table and index entries that refer to the + ** table. The program name loops through the master table and deletes + ** every row that refers to a table of the same name as the one being + ** dropped. Triggers are handled seperately because a trigger can be + ** created in the temp database that refers to a table in another + ** database. + */ + sqlite3NestedParse(pParse, + "DELETE FROM %Q.%s WHERE tbl_name=%Q and type!='trigger'", + pDb->zName, SCHEMA_TABLE(iDb), pTab->zName); + if( !isView ){ + destroyTable(pParse, pTab); + } + + /* Remove the table entry from SQLite's internal schema and modify + ** the schema cookie. + */ + sqlite3VdbeOp3(v, OP_DropTable, iDb, 0, pTab->zName, 0); + sqlite3ChangeCookie(db, v, iDb); + } + sqliteViewResetAll(db, iDb); + +exit_drop_table: + sqlite3SrcListDelete(pName); +} + +/* +** This routine is called to create a new foreign key on the table +** currently under construction. pFromCol determines which columns +** in the current table point to the foreign key. If pFromCol==0 then +** connect the key to the last column inserted. pTo is the name of +** the table referred to. pToCol is a list of tables in the other +** pTo table that the foreign key points to. flags contains all +** information about the conflict resolution algorithms specified +** in the ON DELETE, ON UPDATE and ON INSERT clauses. +** +** An FKey structure is created and added to the table currently +** under construction in the pParse->pNewTable field. The new FKey +** is not linked into db->aFKey at this point - that does not happen +** until sqlite3EndTable(). +** +** The foreign key is set for IMMEDIATE processing. A subsequent call +** to sqlite3DeferForeignKey() might change this to DEFERRED. +*/ +void sqlite3CreateForeignKey( + Parse *pParse, /* Parsing context */ + ExprList *pFromCol, /* Columns in this table that point to other table */ + Token *pTo, /* Name of the other table */ + ExprList *pToCol, /* Columns in the other table */ + int flags /* Conflict resolution algorithms. */ +){ +#ifndef SQLITE_OMIT_FOREIGN_KEY + FKey *pFKey = 0; + Table *p = pParse->pNewTable; + int nByte; + int i; + int nCol; + char *z; + + assert( pTo!=0 ); + if( p==0 || pParse->nErr ) goto fk_end; + if( pFromCol==0 ){ + int iCol = p->nCol-1; + if( iCol<0 ) goto fk_end; + if( pToCol && pToCol->nExpr!=1 ){ + sqlite3ErrorMsg(pParse, "foreign key on %s" + " should reference only one column of table %T", + p->aCol[iCol].zName, pTo); + goto fk_end; + } + nCol = 1; + }else if( pToCol && pToCol->nExpr!=pFromCol->nExpr ){ + sqlite3ErrorMsg(pParse, + "number of columns in foreign key does not match the number of " + "columns in the referenced table"); + goto fk_end; + }else{ + nCol = pFromCol->nExpr; + } + nByte = sizeof(*pFKey) + nCol*sizeof(pFKey->aCol[0]) + pTo->n + 1; + if( pToCol ){ + for(i=0; inExpr; i++){ + nByte += strlen(pToCol->a[i].zName) + 1; + } + } + pFKey = sqliteMalloc( nByte ); + if( pFKey==0 ) goto fk_end; + pFKey->pFrom = p; + pFKey->pNextFrom = p->pFKey; + z = (char*)&pFKey[1]; + pFKey->aCol = (struct sColMap*)z; + z += sizeof(struct sColMap)*nCol; + pFKey->zTo = z; + memcpy(z, pTo->z, pTo->n); + z[pTo->n] = 0; + z += pTo->n+1; + pFKey->pNextTo = 0; + pFKey->nCol = nCol; + if( pFromCol==0 ){ + pFKey->aCol[0].iFrom = p->nCol-1; + }else{ + for(i=0; inCol; j++){ + if( sqlite3StrICmp(p->aCol[j].zName, pFromCol->a[i].zName)==0 ){ + pFKey->aCol[i].iFrom = j; + break; + } + } + if( j>=p->nCol ){ + sqlite3ErrorMsg(pParse, + "unknown column \"%s\" in foreign key definition", + pFromCol->a[i].zName); + goto fk_end; + } + } + } + if( pToCol ){ + for(i=0; ia[i].zName); + pFKey->aCol[i].zCol = z; + memcpy(z, pToCol->a[i].zName, n); + z[n] = 0; + z += n+1; + } + } + pFKey->isDeferred = 0; + pFKey->deleteConf = flags & 0xff; + pFKey->updateConf = (flags >> 8 ) & 0xff; + pFKey->insertConf = (flags >> 16 ) & 0xff; + + /* Link the foreign key to the table as the last step. + */ + p->pFKey = pFKey; + pFKey = 0; + +fk_end: + sqliteFree(pFKey); +#endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */ + sqlite3ExprListDelete(pFromCol); + sqlite3ExprListDelete(pToCol); +} + +/* +** This routine is called when an INITIALLY IMMEDIATE or INITIALLY DEFERRED +** clause is seen as part of a foreign key definition. The isDeferred +** parameter is 1 for INITIALLY DEFERRED and 0 for INITIALLY IMMEDIATE. +** The behavior of the most recently created foreign key is adjusted +** accordingly. +*/ +void sqlite3DeferForeignKey(Parse *pParse, int isDeferred){ +#ifndef SQLITE_OMIT_FOREIGN_KEY + Table *pTab; + FKey *pFKey; + if( (pTab = pParse->pNewTable)==0 || (pFKey = pTab->pFKey)==0 ) return; + pFKey->isDeferred = isDeferred; +#endif +} + +/* +** Generate code that will erase and refill index *pIdx. This is +** used to initialize a newly created index or to recompute the +** content of an index in response to a REINDEX command. +** +** if memRootPage is not negative, it means that the index is newly +** created. The memory cell specified by memRootPage contains the +** root page number of the index. If memRootPage is negative, then +** the index already exists and must be cleared before being refilled and +** the root page number of the index is taken from pIndex->tnum. +*/ +static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ + Table *pTab = pIndex->pTable; /* The table that is indexed */ + int iTab = pParse->nTab; /* Btree cursor used for pTab */ + int iIdx = pParse->nTab+1; /* Btree cursor used for pIndex */ + int addr1; /* Address of top of loop */ + int tnum; /* Root page of index */ + Vdbe *v; /* Generate code into this virtual machine */ + KeyInfo *pKey; /* KeyInfo for index */ + int iDb = sqlite3SchemaToIndex(pParse->db, pIndex->pSchema); + +#ifndef SQLITE_OMIT_AUTHORIZATION + if( sqlite3AuthCheck(pParse, SQLITE_REINDEX, pIndex->zName, 0, + pParse->db->aDb[iDb].zName ) ){ + return; + } +#endif + + /* Require a write-lock on the table to perform this operation */ + sqlite3TableLock(pParse, iDb, pTab->tnum, 1, pTab->zName); + + v = sqlite3GetVdbe(pParse); + if( v==0 ) return; + if( memRootPage>=0 ){ + sqlite3VdbeAddOp(v, OP_MemLoad, memRootPage, 0); + tnum = 0; + }else{ + tnum = pIndex->tnum; + sqlite3VdbeAddOp(v, OP_Clear, tnum, iDb); + } + sqlite3VdbeAddOp(v, OP_Integer, iDb, 0); + pKey = sqlite3IndexKeyinfo(pParse, pIndex); + sqlite3VdbeOp3(v, OP_OpenWrite, iIdx, tnum, (char *)pKey, P3_KEYINFO_HANDOFF); + sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead); + addr1 = sqlite3VdbeAddOp(v, OP_Rewind, iTab, 0); + sqlite3GenerateIndexKey(v, pIndex, iTab); + if( pIndex->onError!=OE_None ){ + int curaddr = sqlite3VdbeCurrentAddr(v); + int addr2 = curaddr+4; + sqlite3VdbeChangeP2(v, curaddr-1, addr2); + sqlite3VdbeAddOp(v, OP_Rowid, iTab, 0); + sqlite3VdbeAddOp(v, OP_AddImm, 1, 0); + sqlite3VdbeAddOp(v, OP_IsUnique, iIdx, addr2); + sqlite3VdbeOp3(v, OP_Halt, SQLITE_CONSTRAINT, OE_Abort, + "indexed columns are not unique", P3_STATIC); + assert( addr2==sqlite3VdbeCurrentAddr(v) ); + } + sqlite3VdbeAddOp(v, OP_IdxInsert, iIdx, 0); + sqlite3VdbeAddOp(v, OP_Next, iTab, addr1+1); + sqlite3VdbeJumpHere(v, addr1); + sqlite3VdbeAddOp(v, OP_Close, iTab, 0); + sqlite3VdbeAddOp(v, OP_Close, iIdx, 0); +} + +/* +** Create a new index for an SQL table. pName1.pName2 is the name of the index +** and pTblList is the name of the table that is to be indexed. Both will +** be NULL for a primary key or an index that is created to satisfy a +** UNIQUE constraint. If pTable and pIndex are NULL, use pParse->pNewTable +** as the table to be indexed. pParse->pNewTable is a table that is +** currently being constructed by a CREATE TABLE statement. +** +** pList is a list of columns to be indexed. pList will be NULL if this +** is a primary key or unique-constraint on the most recent column added +** to the table currently under construction. +*/ +void sqlite3CreateIndex( + Parse *pParse, /* All information about this parse */ + Token *pName1, /* First part of index name. May be NULL */ + Token *pName2, /* Second part of index name. May be NULL */ + SrcList *pTblName, /* Table to index. Use pParse->pNewTable if 0 */ + ExprList *pList, /* A list of columns to be indexed */ + int onError, /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ + Token *pStart, /* The CREATE token that begins a CREATE TABLE statement */ + Token *pEnd, /* The ")" that closes the CREATE INDEX statement */ + int sortOrder, /* Sort order of primary key when pList==NULL */ + int ifNotExist /* Omit error if index already exists */ +){ + Table *pTab = 0; /* Table to be indexed */ + Index *pIndex = 0; /* The index to be created */ + char *zName = 0; /* Name of the index */ + int nName; /* Number of characters in zName */ + int i, j; + Token nullId; /* Fake token for an empty ID list */ + DbFixer sFix; /* For assigning database names to pTable */ + int sortOrderMask; /* 1 to honor DESC in index. 0 to ignore. */ + sqlite3 *db = pParse->db; + Db *pDb; /* The specific table containing the indexed database */ + int iDb; /* Index of the database that is being written */ + Token *pName = 0; /* Unqualified name of the index to create */ + struct ExprList_item *pListItem; /* For looping over pList */ + int nCol; + int nExtra = 0; + char *zExtra; + + if( pParse->nErr || sqlite3MallocFailed() ){ + goto exit_create_index; + } + + /* + ** Find the table that is to be indexed. Return early if not found. + */ + if( pTblName!=0 ){ + + /* Use the two-part index name to determine the database + ** to search for the table. 'Fix' the table name to this db + ** before looking up the table. + */ + assert( pName1 && pName2 ); + iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName); + if( iDb<0 ) goto exit_create_index; + +#ifndef SQLITE_OMIT_TEMPDB + /* If the index name was unqualified, check if the the table + ** is a temp table. If so, set the database to 1. + */ + pTab = sqlite3SrcListLookup(pParse, pTblName); + if( pName2 && pName2->n==0 && pTab && pTab->pSchema==db->aDb[1].pSchema ){ + iDb = 1; + } +#endif + + if( sqlite3FixInit(&sFix, pParse, iDb, "index", pName) && + sqlite3FixSrcList(&sFix, pTblName) + ){ + /* Because the parser constructs pTblName from a single identifier, + ** sqlite3FixSrcList can never fail. */ + assert(0); + } + pTab = sqlite3LocateTable(pParse, pTblName->a[0].zName, + pTblName->a[0].zDatabase); + if( !pTab ) goto exit_create_index; + assert( db->aDb[iDb].pSchema==pTab->pSchema ); + }else{ + assert( pName==0 ); + pTab = pParse->pNewTable; + if( !pTab ) goto exit_create_index; + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); + } + pDb = &db->aDb[iDb]; + + if( pTab==0 || pParse->nErr ) goto exit_create_index; + if( pTab->readOnly ){ + sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName); + goto exit_create_index; + } +#ifndef SQLITE_OMIT_VIEW + if( pTab->pSelect ){ + sqlite3ErrorMsg(pParse, "views may not be indexed"); + goto exit_create_index; + } +#endif + + /* + ** Find the name of the index. Make sure there is not already another + ** index or table with the same name. + ** + ** Exception: If we are reading the names of permanent indices from the + ** sqlite_master table (because some other process changed the schema) and + ** one of the index names collides with the name of a temporary table or + ** index, then we will continue to process this index. + ** + ** If pName==0 it means that we are + ** dealing with a primary key or UNIQUE constraint. We have to invent our + ** own name. + */ + if( pName ){ + zName = sqlite3NameFromToken(pName); + if( SQLITE_OK!=sqlite3ReadSchema(pParse) ) goto exit_create_index; + if( zName==0 ) goto exit_create_index; + if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ + goto exit_create_index; + } + if( !db->init.busy ){ + if( SQLITE_OK!=sqlite3ReadSchema(pParse) ) goto exit_create_index; + if( sqlite3FindIndex(db, zName, pDb->zName)!=0 ){ + if( !ifNotExist ){ + sqlite3ErrorMsg(pParse, "index %s already exists", zName); + } + goto exit_create_index; + } + if( sqlite3FindTable(db, zName, 0)!=0 ){ + sqlite3ErrorMsg(pParse, "there is already a table named %s", zName); + goto exit_create_index; + } + } + }else{ + char zBuf[30]; + int n; + Index *pLoop; + for(pLoop=pTab->pIndex, n=1; pLoop; pLoop=pLoop->pNext, n++){} + sprintf(zBuf,"_%d",n); + zName = 0; + sqlite3SetString(&zName, "sqlite_autoindex_", pTab->zName, zBuf, (char*)0); + if( zName==0 ) goto exit_create_index; + } + + /* Check for authorization to create an index. + */ +#ifndef SQLITE_OMIT_AUTHORIZATION + { + const char *zDb = pDb->zName; + if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(iDb), 0, zDb) ){ + goto exit_create_index; + } + i = SQLITE_CREATE_INDEX; + if( !OMIT_TEMPDB && iDb==1 ) i = SQLITE_CREATE_TEMP_INDEX; + if( sqlite3AuthCheck(pParse, i, zName, pTab->zName, zDb) ){ + goto exit_create_index; + } + } +#endif + + /* If pList==0, it means this routine was called to make a primary + ** key out of the last column added to the table under construction. + ** So create a fake list to simulate this. + */ + if( pList==0 ){ + nullId.z = (u8*)pTab->aCol[pTab->nCol-1].zName; + nullId.n = strlen((char*)nullId.z); + pList = sqlite3ExprListAppend(0, 0, &nullId); + if( pList==0 ) goto exit_create_index; + pList->a[0].sortOrder = sortOrder; + } + + /* Figure out how many bytes of space are required to store explicitly + ** specified collation sequence names. + */ + for(i=0; inExpr; i++){ + Expr *pExpr = pList->a[i].pExpr; + if( pExpr ){ + nExtra += (1 + strlen(pExpr->pColl->zName)); + } + } + + /* + ** Allocate the index structure. + */ + nName = strlen(zName); + nCol = pList->nExpr; + pIndex = sqliteMalloc( + sizeof(Index) + /* Index structure */ + sizeof(int)*nCol + /* Index.aiColumn */ + sizeof(int)*(nCol+1) + /* Index.aiRowEst */ + sizeof(char *)*nCol + /* Index.azColl */ + sizeof(u8)*nCol + /* Index.aSortOrder */ + nName + 1 + /* Index.zName */ + nExtra /* Collation sequence names */ + ); + if( sqlite3MallocFailed() ) goto exit_create_index; + pIndex->azColl = (char**)(&pIndex[1]); + pIndex->aiColumn = (int *)(&pIndex->azColl[nCol]); + pIndex->aiRowEst = (unsigned *)(&pIndex->aiColumn[nCol]); + pIndex->aSortOrder = (u8 *)(&pIndex->aiRowEst[nCol+1]); + pIndex->zName = (char *)(&pIndex->aSortOrder[nCol]); + zExtra = (char *)(&pIndex->zName[nName+1]); + strcpy(pIndex->zName, zName); + pIndex->pTable = pTab; + pIndex->nColumn = pList->nExpr; + pIndex->onError = onError; + pIndex->autoIndex = pName==0; + pIndex->pSchema = db->aDb[iDb].pSchema; + + /* Check to see if we should honor DESC requests on index columns + */ + if( pDb->pSchema->file_format>=4 ){ + sortOrderMask = -1; /* Honor DESC */ + }else{ + sortOrderMask = 0; /* Ignore DESC */ + } + + /* Scan the names of the columns of the table to be indexed and + ** load the column indices into the Index structure. Report an error + ** if any column is not found. + */ + for(i=0, pListItem=pList->a; inExpr; i++, pListItem++){ + const char *zColName = pListItem->zName; + Column *pTabCol; + int requestedSortOrder; + char *zColl; /* Collation sequence */ + + for(j=0, pTabCol=pTab->aCol; jnCol; j++, pTabCol++){ + if( sqlite3StrICmp(zColName, pTabCol->zName)==0 ) break; + } + if( j>=pTab->nCol ){ + sqlite3ErrorMsg(pParse, "table %s has no column named %s", + pTab->zName, zColName); + goto exit_create_index; + } + pIndex->aiColumn[i] = j; + if( pListItem->pExpr ){ + assert( pListItem->pExpr->pColl ); + zColl = zExtra; + strcpy(zExtra, pListItem->pExpr->pColl->zName); + zExtra += (strlen(zColl) + 1); + }else{ + zColl = pTab->aCol[j].zColl; + if( !zColl ){ + zColl = db->pDfltColl->zName; + } + } + if( !db->init.busy && !sqlite3LocateCollSeq(pParse, zColl, -1) ){ + goto exit_create_index; + } + pIndex->azColl[i] = zColl; + requestedSortOrder = pListItem->sortOrder & sortOrderMask; + pIndex->aSortOrder[i] = requestedSortOrder; + } + sqlite3DefaultRowEst(pIndex); + + if( pTab==pParse->pNewTable ){ + /* This routine has been called to create an automatic index as a + ** result of a PRIMARY KEY or UNIQUE clause on a column definition, or + ** a PRIMARY KEY or UNIQUE clause following the column definitions. + ** i.e. one of: + ** + ** CREATE TABLE t(x PRIMARY KEY, y); + ** CREATE TABLE t(x, y, UNIQUE(x, y)); + ** + ** Either way, check to see if the table already has such an index. If + ** so, don't bother creating this one. This only applies to + ** automatically created indices. Users can do as they wish with + ** explicit indices. + */ + Index *pIdx; + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + int k; + assert( pIdx->onError!=OE_None ); + assert( pIdx->autoIndex ); + assert( pIndex->onError!=OE_None ); + + if( pIdx->nColumn!=pIndex->nColumn ) continue; + for(k=0; knColumn; k++){ + const char *z1 = pIdx->azColl[k]; + const char *z2 = pIndex->azColl[k]; + if( pIdx->aiColumn[k]!=pIndex->aiColumn[k] ) break; + if( pIdx->aSortOrder[k]!=pIndex->aSortOrder[k] ) break; + if( z1!=z2 && sqlite3StrICmp(z1, z2) ) break; + } + if( k==pIdx->nColumn ){ + if( pIdx->onError!=pIndex->onError ){ + /* This constraint creates the same index as a previous + ** constraint specified somewhere in the CREATE TABLE statement. + ** However the ON CONFLICT clauses are different. If both this + ** constraint and the previous equivalent constraint have explicit + ** ON CONFLICT clauses this is an error. Otherwise, use the + ** explicitly specified behaviour for the index. + */ + if( !(pIdx->onError==OE_Default || pIndex->onError==OE_Default) ){ + sqlite3ErrorMsg(pParse, + "conflicting ON CONFLICT clauses specified", 0); + } + if( pIdx->onError==OE_Default ){ + pIdx->onError = pIndex->onError; + } + } + goto exit_create_index; + } + } + } + + /* Link the new Index structure to its table and to the other + ** in-memory database structures. + */ + if( db->init.busy ){ + Index *p; + p = sqlite3HashInsert(&pIndex->pSchema->idxHash, + pIndex->zName, strlen(pIndex->zName)+1, pIndex); + if( p ){ + assert( p==pIndex ); /* Malloc must have failed */ + goto exit_create_index; + } + db->flags |= SQLITE_InternChanges; + if( pTblName!=0 ){ + pIndex->tnum = db->init.newTnum; + } + } + + /* If the db->init.busy is 0 then create the index on disk. This + ** involves writing the index into the master table and filling in the + ** index with the current table contents. + ** + ** The db->init.busy is 0 when the user first enters a CREATE INDEX + ** command. db->init.busy is 1 when a database is opened and + ** CREATE INDEX statements are read out of the master table. In + ** the latter case the index already exists on disk, which is why + ** we don't want to recreate it. + ** + ** If pTblName==0 it means this index is generated as a primary key + ** or UNIQUE constraint of a CREATE TABLE statement. Since the table + ** has just been created, it contains no data and the index initialization + ** step can be skipped. + */ + else if( db->init.busy==0 ){ + Vdbe *v; + char *zStmt; + int iMem = pParse->nMem++; + + v = sqlite3GetVdbe(pParse); + if( v==0 ) goto exit_create_index; + + + /* Create the rootpage for the index + */ + sqlite3BeginWriteOperation(pParse, 1, iDb); + sqlite3VdbeAddOp(v, OP_CreateIndex, iDb, 0); + sqlite3VdbeAddOp(v, OP_MemStore, iMem, 0); + + /* Gather the complete text of the CREATE INDEX statement into + ** the zStmt variable + */ + if( pStart && pEnd ){ + /* A named index with an explicit CREATE INDEX statement */ + zStmt = sqlite3MPrintf("CREATE%s INDEX %.*s", + onError==OE_None ? "" : " UNIQUE", + pEnd->z - pName->z + 1, + pName->z); + }else{ + /* An automatic index created by a PRIMARY KEY or UNIQUE constraint */ + /* zStmt = sqlite3MPrintf(""); */ + zStmt = 0; + } + + /* Add an entry in sqlite_master for this index + */ + sqlite3NestedParse(pParse, + "INSERT INTO %Q.%s VALUES('index',%Q,%Q,#0,%Q);", + db->aDb[iDb].zName, SCHEMA_TABLE(iDb), + pIndex->zName, + pTab->zName, + zStmt + ); + sqlite3VdbeAddOp(v, OP_Pop, 1, 0); + sqliteFree(zStmt); + + /* Fill the index with data and reparse the schema. Code an OP_Expire + ** to invalidate all pre-compiled statements. + */ + if( pTblName ){ + sqlite3RefillIndex(pParse, pIndex, iMem); + sqlite3ChangeCookie(db, v, iDb); + sqlite3VdbeOp3(v, OP_ParseSchema, iDb, 0, + sqlite3MPrintf("name='%q'", pIndex->zName), P3_DYNAMIC); + sqlite3VdbeAddOp(v, OP_Expire, 0, 0); + } + } + + /* When adding an index to the list of indices for a table, make + ** sure all indices labeled OE_Replace come after all those labeled + ** OE_Ignore. This is necessary for the correct operation of UPDATE + ** and INSERT. + */ + if( db->init.busy || pTblName==0 ){ + if( onError!=OE_Replace || pTab->pIndex==0 + || pTab->pIndex->onError==OE_Replace){ + pIndex->pNext = pTab->pIndex; + pTab->pIndex = pIndex; + }else{ + Index *pOther = pTab->pIndex; + while( pOther->pNext && pOther->pNext->onError!=OE_Replace ){ + pOther = pOther->pNext; + } + pIndex->pNext = pOther->pNext; + pOther->pNext = pIndex; + } + pIndex = 0; + } + + /* Clean up before exiting */ +exit_create_index: + if( pIndex ){ + freeIndex(pIndex); + } + sqlite3ExprListDelete(pList); + sqlite3SrcListDelete(pTblName); + sqliteFree(zName); + return; +} + +/* +** Generate code to make sure the file format number is at least minFormat. +** The generated code will increase the file format number if necessary. +*/ +void sqlite3MinimumFileFormat(Parse *pParse, int iDb, int minFormat){ + Vdbe *v; + v = sqlite3GetVdbe(pParse); + if( v ){ + sqlite3VdbeAddOp(v, OP_ReadCookie, iDb, 1); + sqlite3VdbeAddOp(v, OP_Integer, minFormat, 0); + sqlite3VdbeAddOp(v, OP_Ge, 0, sqlite3VdbeCurrentAddr(v)+3); + sqlite3VdbeAddOp(v, OP_Integer, minFormat, 0); + sqlite3VdbeAddOp(v, OP_SetCookie, iDb, 1); + } +} + +/* +** Fill the Index.aiRowEst[] array with default information - information +** to be used when we have not run the ANALYZE command. +** +** aiRowEst[0] is suppose to contain the number of elements in the index. +** Since we do not know, guess 1 million. aiRowEst[1] is an estimate of the +** number of rows in the table that match any particular value of the +** first column of the index. aiRowEst[2] is an estimate of the number +** of rows that match any particular combiniation of the first 2 columns +** of the index. And so forth. It must always be the case that +* +** aiRowEst[N]<=aiRowEst[N-1] +** aiRowEst[N]>=1 +** +** Apart from that, we have little to go on besides intuition as to +** how aiRowEst[] should be initialized. The numbers generated here +** are based on typical values found in actual indices. +*/ +void sqlite3DefaultRowEst(Index *pIdx){ + unsigned *a = pIdx->aiRowEst; + int i; + assert( a!=0 ); + a[0] = 1000000; + for(i=pIdx->nColumn; i>=1; i--){ + a[i] = 10; + } + if( pIdx->onError!=OE_None ){ + a[pIdx->nColumn] = 1; + } +} + +/* +** This routine will drop an existing named index. This routine +** implements the DROP INDEX statement. +*/ +void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){ + Index *pIndex; + Vdbe *v; + sqlite3 *db = pParse->db; + int iDb; + + if( pParse->nErr || sqlite3MallocFailed() ){ + goto exit_drop_index; + } + assert( pName->nSrc==1 ); + if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ + goto exit_drop_index; + } + pIndex = sqlite3FindIndex(db, pName->a[0].zName, pName->a[0].zDatabase); + if( pIndex==0 ){ + if( !ifExists ){ + sqlite3ErrorMsg(pParse, "no such index: %S", pName, 0); + } + pParse->checkSchema = 1; + goto exit_drop_index; + } + if( pIndex->autoIndex ){ + sqlite3ErrorMsg(pParse, "index associated with UNIQUE " + "or PRIMARY KEY constraint cannot be dropped", 0); + goto exit_drop_index; + } + iDb = sqlite3SchemaToIndex(db, pIndex->pSchema); +#ifndef SQLITE_OMIT_AUTHORIZATION + { + int code = SQLITE_DROP_INDEX; + Table *pTab = pIndex->pTable; + const char *zDb = db->aDb[iDb].zName; + const char *zTab = SCHEMA_TABLE(iDb); + if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){ + goto exit_drop_index; + } + if( !OMIT_TEMPDB && iDb ) code = SQLITE_DROP_TEMP_INDEX; + if( sqlite3AuthCheck(pParse, code, pIndex->zName, pTab->zName, zDb) ){ + goto exit_drop_index; + } + } +#endif + + /* Generate code to remove the index and from the master table */ + v = sqlite3GetVdbe(pParse); + if( v ){ + sqlite3NestedParse(pParse, + "DELETE FROM %Q.%s WHERE name=%Q", + db->aDb[iDb].zName, SCHEMA_TABLE(iDb), + pIndex->zName + ); + sqlite3ChangeCookie(db, v, iDb); + destroyRootPage(pParse, pIndex->tnum, iDb); + sqlite3VdbeOp3(v, OP_DropIndex, iDb, 0, pIndex->zName, 0); + } + +exit_drop_index: + sqlite3SrcListDelete(pName); +} + +/* +** ppArray points into a structure where there is an array pointer +** followed by two integers. The first integer is the +** number of elements in the structure array. The second integer +** is the number of allocated slots in the array. +** +** In other words, the structure looks something like this: +** +** struct Example1 { +** struct subElem *aEntry; +** int nEntry; +** int nAlloc; +** } +** +** The pnEntry parameter points to the equivalent of Example1.nEntry. +** +** This routine allocates a new slot in the array, zeros it out, +** and returns its index. If malloc fails a negative number is returned. +** +** szEntry is the sizeof of a single array entry. initSize is the +** number of array entries allocated on the initial allocation. +*/ +int sqlite3ArrayAllocate(void **ppArray, int szEntry, int initSize){ + char *p; + int *an = (int*)&ppArray[1]; + if( an[0]>=an[1] ){ + void *pNew; + int newSize; + newSize = an[1]*2 + initSize; + pNew = sqliteRealloc(*ppArray, newSize*szEntry); + if( pNew==0 ){ + return -1; + } + an[1] = newSize; + *ppArray = pNew; + } + p = *ppArray; + memset(&p[an[0]*szEntry], 0, szEntry); + return an[0]++; +} + +/* +** Append a new element to the given IdList. Create a new IdList if +** need be. +** +** A new IdList is returned, or NULL if malloc() fails. +*/ +IdList *sqlite3IdListAppend(IdList *pList, Token *pToken){ + int i; + if( pList==0 ){ + pList = sqliteMalloc( sizeof(IdList) ); + if( pList==0 ) return 0; + pList->nAlloc = 0; + } + i = sqlite3ArrayAllocate((void**)&pList->a, sizeof(pList->a[0]), 5); + if( i<0 ){ + sqlite3IdListDelete(pList); + return 0; + } + pList->a[i].zName = sqlite3NameFromToken(pToken); + return pList; +} + +/* +** Delete an IdList. +*/ +void sqlite3IdListDelete(IdList *pList){ + int i; + if( pList==0 ) return; + for(i=0; inId; i++){ + sqliteFree(pList->a[i].zName); + } + sqliteFree(pList->a); + sqliteFree(pList); +} + +/* +** Return the index in pList of the identifier named zId. Return -1 +** if not found. +*/ +int sqlite3IdListIndex(IdList *pList, const char *zName){ + int i; + if( pList==0 ) return -1; + for(i=0; inId; i++){ + if( sqlite3StrICmp(pList->a[i].zName, zName)==0 ) return i; + } + return -1; +} + +/* +** Append a new table name to the given SrcList. Create a new SrcList if +** need be. A new entry is created in the SrcList even if pToken is NULL. +** +** A new SrcList is returned, or NULL if malloc() fails. +** +** If pDatabase is not null, it means that the table has an optional +** database name prefix. Like this: "database.table". The pDatabase +** points to the table name and the pTable points to the database name. +** The SrcList.a[].zName field is filled with the table name which might +** come from pTable (if pDatabase is NULL) or from pDatabase. +** SrcList.a[].zDatabase is filled with the database name from pTable, +** or with NULL if no database is specified. +** +** In other words, if call like this: +** +** sqlite3SrcListAppend(A,B,0); +** +** Then B is a table name and the database name is unspecified. If called +** like this: +** +** sqlite3SrcListAppend(A,B,C); +** +** Then C is the table name and B is the database name. +*/ +SrcList *sqlite3SrcListAppend(SrcList *pList, Token *pTable, Token *pDatabase){ + struct SrcList_item *pItem; + if( pList==0 ){ + pList = sqliteMalloc( sizeof(SrcList) ); + if( pList==0 ) return 0; + pList->nAlloc = 1; + } + if( pList->nSrc>=pList->nAlloc ){ + SrcList *pNew; + pList->nAlloc *= 2; + pNew = sqliteRealloc(pList, + sizeof(*pList) + (pList->nAlloc-1)*sizeof(pList->a[0]) ); + if( pNew==0 ){ + sqlite3SrcListDelete(pList); + return 0; + } + pList = pNew; + } + pItem = &pList->a[pList->nSrc]; + memset(pItem, 0, sizeof(pList->a[0])); + if( pDatabase && pDatabase->z==0 ){ + pDatabase = 0; + } + if( pDatabase && pTable ){ + Token *pTemp = pDatabase; + pDatabase = pTable; + pTable = pTemp; + } + pItem->zName = sqlite3NameFromToken(pTable); + pItem->zDatabase = sqlite3NameFromToken(pDatabase); + pItem->iCursor = -1; + pItem->isPopulated = 0; + pList->nSrc++; + return pList; +} + +/* +** Assign cursors to all tables in a SrcList +*/ +void sqlite3SrcListAssignCursors(Parse *pParse, SrcList *pList){ + int i; + struct SrcList_item *pItem; + assert(pList || sqlite3MallocFailed() ); + if( pList ){ + for(i=0, pItem=pList->a; inSrc; i++, pItem++){ + if( pItem->iCursor>=0 ) break; + pItem->iCursor = pParse->nTab++; + if( pItem->pSelect ){ + sqlite3SrcListAssignCursors(pParse, pItem->pSelect->pSrc); + } + } + } +} + +/* +** Add an alias to the last identifier on the given identifier list. +*/ +void sqlite3SrcListAddAlias(SrcList *pList, Token *pToken){ + if( pList && pList->nSrc>0 ){ + pList->a[pList->nSrc-1].zAlias = sqlite3NameFromToken(pToken); + } +} + +/* +** Delete an entire SrcList including all its substructure. +*/ +void sqlite3SrcListDelete(SrcList *pList){ + int i; + struct SrcList_item *pItem; + if( pList==0 ) return; + for(pItem=pList->a, i=0; inSrc; i++, pItem++){ + sqliteFree(pItem->zDatabase); + sqliteFree(pItem->zName); + sqliteFree(pItem->zAlias); + sqlite3DeleteTable(0, pItem->pTab); + sqlite3SelectDelete(pItem->pSelect); + sqlite3ExprDelete(pItem->pOn); + sqlite3IdListDelete(pItem->pUsing); + } + sqliteFree(pList); +} + +/* +** Begin a transaction +*/ +void sqlite3BeginTransaction(Parse *pParse, int type){ + sqlite3 *db; + Vdbe *v; + int i; + + if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return; + if( pParse->nErr || sqlite3MallocFailed() ) return; + if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "BEGIN", 0, 0) ) return; + + v = sqlite3GetVdbe(pParse); + if( !v ) return; + if( type!=TK_DEFERRED ){ + for(i=0; inDb; i++){ + sqlite3VdbeAddOp(v, OP_Transaction, i, (type==TK_EXCLUSIVE)+1); + } + } + sqlite3VdbeAddOp(v, OP_AutoCommit, 0, 0); +} + +/* +** Commit a transaction +*/ +void sqlite3CommitTransaction(Parse *pParse){ + sqlite3 *db; + Vdbe *v; + + if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return; + if( pParse->nErr || sqlite3MallocFailed() ) return; + if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "COMMIT", 0, 0) ) return; + + v = sqlite3GetVdbe(pParse); + if( v ){ + sqlite3VdbeAddOp(v, OP_AutoCommit, 1, 0); + } +} + +/* +** Rollback a transaction +*/ +void sqlite3RollbackTransaction(Parse *pParse){ + sqlite3 *db; + Vdbe *v; + + if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return; + if( pParse->nErr || sqlite3MallocFailed() ) return; + if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "ROLLBACK", 0, 0) ) return; + + v = sqlite3GetVdbe(pParse); + if( v ){ + sqlite3VdbeAddOp(v, OP_AutoCommit, 1, 1); + } +} + +/* +** Make sure the TEMP database is open and available for use. Return +** the number of errors. Leave any error messages in the pParse structure. +*/ +static int sqlite3OpenTempDatabase(Parse *pParse){ + sqlite3 *db = pParse->db; + if( db->aDb[1].pBt==0 && !pParse->explain ){ + int rc = sqlite3BtreeFactory(db, 0, 0, MAX_PAGES, &db->aDb[1].pBt); + if( rc!=SQLITE_OK ){ + sqlite3ErrorMsg(pParse, "unable to open a temporary database " + "file for storing temporary tables"); + pParse->rc = rc; + return 1; + } + if( db->flags & !db->autoCommit ){ + rc = sqlite3BtreeBeginTrans(db->aDb[1].pBt, 1); + if( rc!=SQLITE_OK ){ + sqlite3ErrorMsg(pParse, "unable to get a write lock on " + "the temporary database file"); + pParse->rc = rc; + return 1; + } + } + assert( db->aDb[1].pSchema ); + } + return 0; +} + +/* +** Generate VDBE code that will verify the schema cookie and start +** a read-transaction for all named database files. +** +** It is important that all schema cookies be verified and all +** read transactions be started before anything else happens in +** the VDBE program. But this routine can be called after much other +** code has been generated. So here is what we do: +** +** The first time this routine is called, we code an OP_Goto that +** will jump to a subroutine at the end of the program. Then we +** record every database that needs its schema verified in the +** pParse->cookieMask field. Later, after all other code has been +** generated, the subroutine that does the cookie verifications and +** starts the transactions will be coded and the OP_Goto P2 value +** will be made to point to that subroutine. The generation of the +** cookie verification subroutine code happens in sqlite3FinishCoding(). +** +** If iDb<0 then code the OP_Goto only - don't set flag to verify the +** schema on any databases. This can be used to position the OP_Goto +** early in the code, before we know if any database tables will be used. +*/ +void sqlite3CodeVerifySchema(Parse *pParse, int iDb){ + sqlite3 *db; + Vdbe *v; + int mask; + + v = sqlite3GetVdbe(pParse); + if( v==0 ) return; /* This only happens if there was a prior error */ + db = pParse->db; + if( pParse->cookieGoto==0 ){ + pParse->cookieGoto = sqlite3VdbeAddOp(v, OP_Goto, 0, 0)+1; + } + if( iDb>=0 ){ + assert( iDbnDb ); + assert( db->aDb[iDb].pBt!=0 || iDb==1 ); + assert( iDb<32 ); + mask = 1<cookieMask & mask)==0 ){ + pParse->cookieMask |= mask; + pParse->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie; + if( !OMIT_TEMPDB && iDb==1 ){ + sqlite3OpenTempDatabase(pParse); + } + } + } +} + +/* +** Generate VDBE code that prepares for doing an operation that +** might change the database. +** +** This routine starts a new transaction if we are not already within +** a transaction. If we are already within a transaction, then a checkpoint +** is set if the setStatement parameter is true. A checkpoint should +** be set for operations that might fail (due to a constraint) part of +** the way through and which will need to undo some writes without having to +** rollback the whole transaction. For operations where all constraints +** can be checked before any changes are made to the database, it is never +** necessary to undo a write and the checkpoint should not be set. +** +** Only database iDb and the temp database are made writable by this call. +** If iDb==0, then the main and temp databases are made writable. If +** iDb==1 then only the temp database is made writable. If iDb>1 then the +** specified auxiliary database and the temp database are made writable. +*/ +void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){ + Vdbe *v = sqlite3GetVdbe(pParse); + if( v==0 ) return; + sqlite3CodeVerifySchema(pParse, iDb); + pParse->writeMask |= 1<nested==0 ){ + sqlite3VdbeAddOp(v, OP_Statement, iDb, 0); + } + if( (OMIT_TEMPDB || iDb!=1) && pParse->db->aDb[1].pBt!=0 ){ + sqlite3BeginWriteOperation(pParse, setStatement, 1); + } +} + +/* +** Check to see if pIndex uses the collating sequence pColl. Return +** true if it does and false if it does not. +*/ +#ifndef SQLITE_OMIT_REINDEX +static int collationMatch(const char *zColl, Index *pIndex){ + int i; + for(i=0; inColumn; i++){ + const char *z = pIndex->azColl[i]; + if( z==zColl || (z && zColl && 0==sqlite3StrICmp(z, zColl)) ){ + return 1; + } + } + return 0; +} +#endif + +/* +** Recompute all indices of pTab that use the collating sequence pColl. +** If pColl==0 then recompute all indices of pTab. +*/ +#ifndef SQLITE_OMIT_REINDEX +static void reindexTable(Parse *pParse, Table *pTab, char const *zColl){ + Index *pIndex; /* An index associated with pTab */ + + for(pIndex=pTab->pIndex; pIndex; pIndex=pIndex->pNext){ + if( zColl==0 || collationMatch(zColl, pIndex) ){ + int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); + sqlite3BeginWriteOperation(pParse, 0, iDb); + sqlite3RefillIndex(pParse, pIndex, -1); + } + } +} +#endif + +/* +** Recompute all indices of all tables in all databases where the +** indices use the collating sequence pColl. If pColl==0 then recompute +** all indices everywhere. +*/ +#ifndef SQLITE_OMIT_REINDEX +static void reindexDatabases(Parse *pParse, char const *zColl){ + Db *pDb; /* A single database */ + int iDb; /* The database index number */ + sqlite3 *db = pParse->db; /* The database connection */ + HashElem *k; /* For looping over tables in pDb */ + Table *pTab; /* A table in the database */ + + for(iDb=0, pDb=db->aDb; iDbnDb; iDb++, pDb++){ + if( pDb==0 ) continue; + for(k=sqliteHashFirst(&pDb->pSchema->tblHash); k; k=sqliteHashNext(k)){ + pTab = (Table*)sqliteHashData(k); + reindexTable(pParse, pTab, zColl); + } + } +} +#endif + +/* +** Generate code for the REINDEX command. +** +** REINDEX -- 1 +** REINDEX -- 2 +** REINDEX ?.? -- 3 +** REINDEX ?.? -- 4 +** +** Form 1 causes all indices in all attached databases to be rebuilt. +** Form 2 rebuilds all indices in all databases that use the named +** collating function. Forms 3 and 4 rebuild the named index or all +** indices associated with the named table. +*/ +#ifndef SQLITE_OMIT_REINDEX +void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){ + CollSeq *pColl; /* Collating sequence to be reindexed, or NULL */ + char *z; /* Name of a table or index */ + const char *zDb; /* Name of the database */ + Table *pTab; /* A table in the database */ + Index *pIndex; /* An index associated with pTab */ + int iDb; /* The database index number */ + sqlite3 *db = pParse->db; /* The database connection */ + Token *pObjName; /* Name of the table or index to be reindexed */ + + /* Read the database schema. If an error occurs, leave an error message + ** and code in pParse and return NULL. */ + if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ + return; + } + + if( pName1==0 || pName1->z==0 ){ + reindexDatabases(pParse, 0); + return; + }else if( pName2==0 || pName2->z==0 ){ + assert( pName1->z ); + pColl = sqlite3FindCollSeq(db, ENC(db), (char*)pName1->z, pName1->n, 0); + if( pColl ){ + char *zColl = sqliteStrNDup((const char *)pName1->z, pName1->n); + if( zColl ){ + reindexDatabases(pParse, zColl); + sqliteFree(zColl); + } + return; + } + } + iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pObjName); + if( iDb<0 ) return; + z = sqlite3NameFromToken(pObjName); + zDb = db->aDb[iDb].zName; + pTab = sqlite3FindTable(db, z, zDb); + if( pTab ){ + reindexTable(pParse, pTab, 0); + sqliteFree(z); + return; + } + pIndex = sqlite3FindIndex(db, z, zDb); + sqliteFree(z); + if( pIndex ){ + sqlite3BeginWriteOperation(pParse, 0, iDb); + sqlite3RefillIndex(pParse, pIndex, -1); + return; + } + sqlite3ErrorMsg(pParse, "unable to identify the object to be reindexed"); +} +#endif + +/* +** Return a dynamicly allocated KeyInfo structure that can be used +** with OP_OpenRead or OP_OpenWrite to access database index pIdx. +** +** If successful, a pointer to the new structure is returned. In this case +** the caller is responsible for calling sqliteFree() on the returned +** pointer. If an error occurs (out of memory or missing collation +** sequence), NULL is returned and the state of pParse updated to reflect +** the error. +*/ +KeyInfo *sqlite3IndexKeyinfo(Parse *pParse, Index *pIdx){ + int i; + int nCol = pIdx->nColumn; + int nBytes = sizeof(KeyInfo) + (nCol-1)*sizeof(CollSeq*) + nCol; + KeyInfo *pKey = (KeyInfo *)sqliteMalloc(nBytes); + + if( pKey ){ + pKey->aSortOrder = (u8 *)&(pKey->aColl[nCol]); + assert( &pKey->aSortOrder[nCol]==&(((u8 *)pKey)[nBytes]) ); + for(i=0; iazColl[i]; + assert( zColl ); + pKey->aColl[i] = sqlite3LocateCollSeq(pParse, zColl, -1); + pKey->aSortOrder[i] = pIdx->aSortOrder[i]; + } + pKey->nField = nCol; + } + + if( pParse->nErr ){ + sqliteFree(pKey); + pKey = 0; + } + return pKey; +} Added: external/sqlite-source-3.3.4/callback.c ============================================================================== --- (empty file) +++ external/sqlite-source-3.3.4/callback.c Mon Apr 3 07:54:59 2006 @@ -0,0 +1,314 @@ +/* +** 2005 May 23 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This file contains functions used to access the internal hash tables +** of user defined functions and collation sequences. +** +** $Id: callback.c,v 1.12 2006/01/18 16:51:35 danielk1977 Exp $ +*/ + +#include "sqliteInt.h" + +/* +** Invoke the 'collation needed' callback to request a collation sequence +** in the database text encoding of name zName, length nName. +** If the collation sequence +*/ +static void callCollNeeded(sqlite3 *db, const char *zName, int nName){ + assert( !db->xCollNeeded || !db->xCollNeeded16 ); + if( nName<0 ) nName = strlen(zName); + if( db->xCollNeeded ){ + char *zExternal = sqliteStrNDup(zName, nName); + if( !zExternal ) return; + db->xCollNeeded(db->pCollNeededArg, db, (int)ENC(db), zExternal); + sqliteFree(zExternal); + } +#ifndef SQLITE_OMIT_UTF16 + if( db->xCollNeeded16 ){ + char const *zExternal; + sqlite3_value *pTmp = sqlite3ValueNew(); + sqlite3ValueSetStr(pTmp, nName, zName, SQLITE_UTF8, SQLITE_STATIC); + zExternal = sqlite3ValueText(pTmp, SQLITE_UTF16NATIVE); + if( zExternal ){ + db->xCollNeeded16(db->pCollNeededArg, db, (int)ENC(db), zExternal); + } + sqlite3ValueFree(pTmp); + } +#endif +} + +/* +** This routine is called if the collation factory fails to deliver a +** collation function in the best encoding but there may be other versions +** of this collation function (for other text encodings) available. Use one +** of these instead if they exist. Avoid a UTF-8 <-> UTF-16 conversion if +** possible. +*/ +static int synthCollSeq(sqlite3 *db, CollSeq *pColl){ + CollSeq *pColl2; + char *z = pColl->zName; + int n = strlen(z); + int i; + static const u8 aEnc[] = { SQLITE_UTF16BE, SQLITE_UTF16LE, SQLITE_UTF8 }; + for(i=0; i<3; i++){ + pColl2 = sqlite3FindCollSeq(db, aEnc[i], z, n, 0); + if( pColl2->xCmp!=0 ){ + memcpy(pColl, pColl2, sizeof(CollSeq)); + return SQLITE_OK; + } + } + return SQLITE_ERROR; +} + +/* +** This function is responsible for invoking the collation factory callback +** or substituting a collation sequence of a different encoding when the +** requested collation sequence is not available in the database native +** encoding. +** +** If it is not NULL, then pColl must point to the database native encoding +** collation sequence with name zName, length nName. +** +** The return value is either the collation sequence to be used in database +** db for collation type name zName, length nName, or NULL, if no collation +** sequence can be found. +*/ +CollSeq *sqlite3GetCollSeq( + sqlite3* db, + CollSeq *pColl, + const char *zName, + int nName +){ + CollSeq *p; + + p = pColl; + if( !p ){ + p = sqlite3FindCollSeq(db, ENC(db), zName, nName, 0); + } + if( !p || !p->xCmp ){ + /* No collation sequence of this type for this encoding is registered. + ** Call the collation factory to see if it can supply us with one. + */ + callCollNeeded(db, zName, nName); + p = sqlite3FindCollSeq(db, ENC(db), zName, nName, 0); + } + if( p && !p->xCmp && synthCollSeq(db, p) ){ + p = 0; + } + assert( !p || p->xCmp ); + return p; +} + +/* +** This routine is called on a collation sequence before it is used to +** check that it is defined. An undefined collation sequence exists when +** a database is loaded that contains references to collation sequences +** that have not been defined by sqlite3_create_collation() etc. +** +** If required, this routine calls the 'collation needed' callback to +** request a definition of the collating sequence. If this doesn't work, +** an equivalent collating sequence that uses a text encoding different +** from the main database is substituted, if one is available. +*/ +int sqlite3CheckCollSeq(Parse *pParse, CollSeq *pColl){ + if( pColl ){ + const char *zName = pColl->zName; + CollSeq *p = sqlite3GetCollSeq(pParse->db, pColl, zName, -1); + if( !p ){ + if( pParse->nErr==0 ){ + sqlite3ErrorMsg(pParse, "no such collation sequence: %s", zName); + } + pParse->nErr++; + return SQLITE_ERROR; + } + assert( p==pColl ); + } + return SQLITE_OK; +} + + + +/* +** Locate and return an entry from the db.aCollSeq hash table. If the entry +** specified by zName and nName is not found and parameter 'create' is +** true, then create a new entry. Otherwise return NULL. +** +** Each pointer stored in the sqlite3.aCollSeq hash table contains an +** array of three CollSeq structures. The first is the collation sequence +** prefferred for UTF-8, the second UTF-16le, and the third UTF-16be. +** +** Stored immediately after the three collation sequences is a copy of +** the collation sequence name. A pointer to this string is stored in +** each collation sequence structure. +*/ +static CollSeq *findCollSeqEntry( + sqlite3 *db, + const char *zName, + int nName, + int create +){ + CollSeq *pColl; + if( nName<0 ) nName = strlen(zName); + pColl = sqlite3HashFind(&db->aCollSeq, zName, nName); + + if( 0==pColl && create ){ + pColl = sqliteMalloc( 3*sizeof(*pColl) + nName + 1 ); + if( pColl ){ + CollSeq *pDel = 0; + pColl[0].zName = (char*)&pColl[3]; + pColl[0].enc = SQLITE_UTF8; + pColl[1].zName = (char*)&pColl[3]; + pColl[1].enc = SQLITE_UTF16LE; + pColl[2].zName = (char*)&pColl[3]; + pColl[2].enc = SQLITE_UTF16BE; + memcpy(pColl[0].zName, zName, nName); + pColl[0].zName[nName] = 0; + pDel = sqlite3HashInsert(&db->aCollSeq, pColl[0].zName, nName, pColl); + + /* If a malloc() failure occured in sqlite3HashInsert(), it will + ** return the pColl pointer to be deleted (because it wasn't added + ** to the hash table). + */ + assert( !pDel || + (sqlite3MallocFailed() && pDel==pColl) ); + sqliteFree(pDel); + } + } + return pColl; +} + +/* +** Parameter zName points to a UTF-8 encoded string nName bytes long. +** Return the CollSeq* pointer for the collation sequence named zName +** for the encoding 'enc' from the database 'db'. +** +** If the entry specified is not found and 'create' is true, then create a +** new entry. Otherwise return NULL. +*/ +CollSeq *sqlite3FindCollSeq( + sqlite3 *db, + u8 enc, + const char *zName, + int nName, + int create +){ + CollSeq *pColl; + if( zName ){ + pColl = findCollSeqEntry(db, zName, nName, create); + }else{ + pColl = db->pDfltColl; + } + assert( SQLITE_UTF8==1 && SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 ); + assert( enc>=SQLITE_UTF8 && enc<=SQLITE_UTF16BE ); + if( pColl ) pColl += enc-1; + return pColl; +} + +/* +** 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 +** pointer to the FuncDef structure that defines that function, or return +** NULL if the function does not exist. +** +** If the createFlag argument is true, then a new (blank) FuncDef +** structure is created and liked into the "db" structure if a +** no matching function previously existed. When createFlag is true +** and the nArg parameter is -1, then only a function that accepts +** any number of arguments will be returned. +** +** If createFlag is false and nArg is -1, then the first valid +** function found is returned. A function is valid if either xFunc +** or xStep is non-zero. +** +** If createFlag is false, then a function with the required name and +** number of arguments may be returned even if the eTextRep flag does not +** match that requested. +*/ +FuncDef *sqlite3FindFunction( + sqlite3 *db, /* An open database */ + const char *zName, /* Name of the function. Not null-terminated */ + int nName, /* Number of characters in the name */ + int nArg, /* Number of arguments. -1 means any number */ + u8 enc, /* Preferred text encoding */ + 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; + + + assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE ); + if( nArg<-1 ) nArg = -1; + + 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 ){ + pBest = p; + bestmatch = match; + } + } + } + + /* If the createFlag parameter is true, and the seach did not reveal an + ** 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 = sqliteMalloc(sizeof(*pBest)+nName))!=0 ){ + 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) ){ + sqliteFree(pBest); + return 0; + } + } + + if( pBest && (pBest->xStep || pBest->xFunc || createFlag) ){ + return pBest; + } + return 0; +} Added: external/sqlite-source-3.3.4/complete.c ============================================================================== --- (empty file) +++ external/sqlite-source-3.3.4/complete.c Mon Apr 3 07:54:59 2006 @@ -0,0 +1,263 @@ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** An tokenizer for SQL +** +** This file contains C code that implements the sqlite3_complete() API. +** This code used to be part of the tokenizer.c source file. But by +** separating it out, the code will be automatically omitted from +** static links that do not use it. +** +** $Id: complete.c,v 1.3 2006/01/18 15:25:17 danielk1977 Exp $ +*/ +#include "sqliteInt.h" +#ifndef SQLITE_OMIT_COMPLETE + +/* +** This is defined in tokenize.c. We just have to import the definition. +*/ +extern const char sqlite3IsIdChar[]; +#define IdChar(C) (((c=C)&0x80)!=0 || (c>0x1f && sqlite3IsIdChar[c-0x20])) + + +/* +** Token types used by the sqlite3_complete() routine. See the header +** comments on that procedure for additional information. +*/ +#define tkSEMI 0 +#define tkWS 1 +#define tkOTHER 2 +#define tkEXPLAIN 3 +#define tkCREATE 4 +#define tkTEMP 5 +#define tkTRIGGER 6 +#define tkEND 7 + +/* +** Return TRUE if the given SQL string ends in a semicolon. +** +** Special handling is require for CREATE TRIGGER statements. +** Whenever the CREATE TRIGGER keywords are seen, the statement +** must end with ";END;". +** +** This implementation uses a state machine with 7 states: +** +** (0) START At the beginning or end of an SQL statement. This routine +** returns 1 if it ends in the START state and 0 if it ends +** in any other state. +** +** (1) NORMAL We are in the middle of statement which ends with a single +** semicolon. +** +** (2) EXPLAIN The keyword EXPLAIN has been seen at the beginning of +** a statement. +** +** (3) CREATE The keyword CREATE has been seen at the beginning of a +** statement, possibly preceeded by EXPLAIN and/or followed by +** TEMP or TEMPORARY +** +** (4) TRIGGER We are in the middle of a trigger definition that must be +** ended by a semicolon, the keyword END, and another semicolon. +** +** (5) SEMI We've seen the first semicolon in the ";END;" that occurs at +** the end of a trigger definition. +** +** (6) END We've seen the ";END" of the ";END;" that occurs at the end +** of a trigger difinition. +** +** Transitions between states above are determined by tokens extracted +** from the input. The following tokens are significant: +** +** (0) tkSEMI A semicolon. +** (1) tkWS Whitespace +** (2) tkOTHER Any other SQL token. +** (3) tkEXPLAIN The "explain" keyword. +** (4) tkCREATE The "create" keyword. +** (5) tkTEMP The "temp" or "temporary" keyword. +** (6) tkTRIGGER The "trigger" keyword. +** (7) tkEND The "end" keyword. +** +** Whitespace never causes a state transition and is always ignored. +** +** If we compile with SQLITE_OMIT_TRIGGER, all of the computation needed +** to recognize the end of a trigger can be omitted. All we have to do +** is look for a semicolon that is not part of an string or comment. +*/ +int sqlite3_complete(const char *zSql){ + u8 state = 0; /* Current state, using numbers defined in header comment */ + u8 token; /* Value of the next token */ + +#ifndef SQLITE_OMIT_TRIGGER + /* A complex statement machine used to detect the end of a CREATE TRIGGER + ** statement. This is the normal case. + */ + static const u8 trans[7][8] = { + /* Token: */ + /* State: ** SEMI WS OTHER EXPLAIN CREATE TEMP TRIGGER END */ + /* 0 START: */ { 0, 0, 1, 2, 3, 1, 1, 1, }, + /* 1 NORMAL: */ { 0, 1, 1, 1, 1, 1, 1, 1, }, + /* 2 EXPLAIN: */ { 0, 2, 1, 1, 3, 1, 1, 1, }, + /* 3 CREATE: */ { 0, 3, 1, 1, 1, 3, 4, 1, }, + /* 4 TRIGGER: */ { 5, 4, 4, 4, 4, 4, 4, 4, }, + /* 5 SEMI: */ { 5, 5, 4, 4, 4, 4, 4, 6, }, + /* 6 END: */ { 0, 6, 4, 4, 4, 4, 4, 4, }, + }; +#else + /* If triggers are not suppored by this compile then the statement machine + ** used to detect the end of a statement is much simplier + */ + static const u8 trans[2][3] = { + /* Token: */ + /* State: ** SEMI WS OTHER */ + /* 0 START: */ { 0, 0, 1, }, + /* 1 NORMAL: */ { 0, 1, 1, }, + }; +#endif /* SQLITE_OMIT_TRIGGER */ + + while( *zSql ){ + switch( *zSql ){ + case ';': { /* A semicolon */ + token = tkSEMI; + break; + } + case ' ': + case '\r': + case '\t': + case '\n': + case '\f': { /* White space is ignored */ + token = tkWS; + break; + } + case '/': { /* C-style comments */ + if( zSql[1]!='*' ){ + token = tkOTHER; + break; + } + zSql += 2; + while( zSql[0] && (zSql[0]!='*' || zSql[1]!='/') ){ zSql++; } + if( zSql[0]==0 ) return 0; + zSql++; + token = tkWS; + break; + } + case '-': { /* SQL-style comments from "--" to end of line */ + if( zSql[1]!='-' ){ + token = tkOTHER; + break; + } + while( *zSql && *zSql!='\n' ){ zSql++; } + if( *zSql==0 ) return state==0; + token = tkWS; + break; + } + case '[': { /* Microsoft-style identifiers in [...] */ + zSql++; + while( *zSql && *zSql!=']' ){ zSql++; } + if( *zSql==0 ) return 0; + token = tkOTHER; + break; + } + case '`': /* Grave-accent quoted symbols used by MySQL */ + case '"': /* single- and double-quoted strings */ + case '\'': { + int c = *zSql; + zSql++; + while( *zSql && *zSql!=c ){ zSql++; } + if( *zSql==0 ) return 0; + token = tkOTHER; + break; + } + default: { + int c; + if( IdChar((u8)*zSql) ){ + /* Keywords and unquoted identifiers */ + int nId; + for(nId=1; IdChar(zSql[nId]); nId++){} +#ifdef SQLITE_OMIT_TRIGGER + token = tkOTHER; +#else + switch( *zSql ){ + case 'c': case 'C': { + if( nId==6 && sqlite3StrNICmp(zSql, "create", 6)==0 ){ + token = tkCREATE; + }else{ + token = tkOTHER; + } + break; + } + case 't': case 'T': { + if( nId==7 && sqlite3StrNICmp(zSql, "trigger", 7)==0 ){ + token = tkTRIGGER; + }else if( nId==4 && sqlite3StrNICmp(zSql, "temp", 4)==0 ){ + token = tkTEMP; + }else if( nId==9 && sqlite3StrNICmp(zSql, "temporary", 9)==0 ){ + token = tkTEMP; + }else{ + token = tkOTHER; + } + break; + } + case 'e': case 'E': { + if( nId==3 && sqlite3StrNICmp(zSql, "end", 3)==0 ){ + token = tkEND; + }else +#ifndef SQLITE_OMIT_EXPLAIN + if( nId==7 && sqlite3StrNICmp(zSql, "explain", 7)==0 ){ + token = tkEXPLAIN; + }else +#endif + { + token = tkOTHER; + } + break; + } + default: { + token = tkOTHER; + break; + } + } +#endif /* SQLITE_OMIT_TRIGGER */ + zSql += nId-1; + }else{ + /* Operators and special symbols */ + token = tkOTHER; + } + break; + } + } + state = trans[state][token]; + zSql++; + } + return state==0; +} + +#ifndef SQLITE_OMIT_UTF16 +/* +** This routine is the same as the sqlite3_complete() routine described +** above, except that the parameter is required to be UTF-16 encoded, not +** UTF-8. +*/ +int sqlite3_complete16(const void *zSql){ + sqlite3_value *pVal; + char const *zSql8; + int rc = 0; + + pVal = sqlite3ValueNew(); + sqlite3ValueSetStr(pVal, -1, zSql, SQLITE_UTF16NATIVE, SQLITE_STATIC); + zSql8 = sqlite3ValueText(pVal, SQLITE_UTF8); + if( zSql8 ){ + rc = sqlite3_complete(zSql8); + } + sqlite3ValueFree(pVal); + return sqlite3ApiExit(0, rc); +} +#endif /* SQLITE_OMIT_UTF16 */ +#endif /* SQLITE_OMIT_COMPLETE */ Added: external/sqlite-source-3.3.4/date.c ============================================================================== --- (empty file) +++ external/sqlite-source-3.3.4/date.c Mon Apr 3 07:54:59 2006 @@ -0,0 +1,998 @@ +/* +** 2003 October 31 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains the C functions that implement date and time +** functions for SQLite. +** +** There is only one exported symbol in this file - the function +** sqlite3RegisterDateTimeFunctions() found at the bottom of the file. +** All other code has file scope. +** +** $Id: date.c,v 1.54 2006/01/31 20:49:13 drh Exp $ +** +** NOTES: +** +** SQLite processes all times and dates as Julian Day numbers. The +** dates and times are stored as the number of days since noon +** in Greenwich on November 24, 4714 B.C. according to the Gregorian +** calendar system. +** +** 1970-01-01 00:00:00 is JD 2440587.5 +** 2000-01-01 00:00:00 is JD 2451544.5 +** +** This implemention requires years to be expressed as a 4-digit number +** which means that only dates between 0000-01-01 and 9999-12-31 can +** be represented, even though julian day numbers allow a much wider +** range of dates. +** +** The Gregorian calendar system is used for all dates and times, +** even those that predate the Gregorian calendar. Historians usually +** use the Julian calendar for dates prior to 1582-10-15 and for some +** dates afterwards, depending on locale. Beware of this difference. +** +** The conversion algorithms are implemented based on descriptions +** in the following text: +** +** Jean Meeus +** Astronomical Algorithms, 2nd Edition, 1998 +** ISBM 0-943396-61-1 +** Willmann-Bell, Inc +** Richmond, Virginia (USA) +*/ +#include "sqliteInt.h" +#include "os.h" +#include +#include +#include +#include + +#ifndef SQLITE_OMIT_DATETIME_FUNCS + +/* +** A structure for holding a single date and time. +*/ +typedef struct DateTime DateTime; +struct DateTime { + double rJD; /* The julian day number */ + int Y, M, D; /* Year, month, and day */ + int h, m; /* Hour and minutes */ + int tz; /* Timezone offset in minutes */ + double s; /* Seconds */ + char validYMD; /* True if Y,M,D are valid */ + char validHMS; /* True if h,m,s are valid */ + char validJD; /* True if rJD is valid */ + char validTZ; /* True if tz is valid */ +}; + + +/* +** Convert zDate into one or more integers. Additional arguments +** come in groups of 5 as follows: +** +** N number of digits in the integer +** min minimum allowed value of the integer +** max maximum allowed value of the integer +** nextC first character after the integer +** pVal where to write the integers value. +** +** Conversions continue until one with nextC==0 is encountered. +** The function returns the number of successful conversions. +*/ +static int getDigits(const char *zDate, ...){ + va_list ap; + int val; + int N; + int min; + int max; + int nextC; + int *pVal; + int cnt = 0; + va_start(ap, zDate); + do{ + N = va_arg(ap, int); + min = va_arg(ap, int); + max = va_arg(ap, int); + nextC = va_arg(ap, int); + pVal = va_arg(ap, int*); + val = 0; + while( N-- ){ + if( !isdigit(*(u8*)zDate) ){ + goto end_getDigits; + } + val = val*10 + *zDate - '0'; + zDate++; + } + if( valmax || (nextC!=0 && nextC!=*zDate) ){ + goto end_getDigits; + } + *pVal = val; + zDate++; + cnt++; + }while( nextC ); +end_getDigits: + va_end(ap); + return cnt; +} + +/* +** Read text from z[] and convert into a floating point number. Return +** the number of digits converted. +*/ +#define getValue sqlite3AtoF + +/* +** Parse a timezone extension on the end of a date-time. +** The extension is of the form: +** +** (+/-)HH:MM +** +** If the parse is successful, write the number of minutes +** of change in *pnMin and return 0. If a parser error occurs, +** return 0. +** +** A missing specifier is not considered an error. +*/ +static int parseTimezone(const char *zDate, DateTime *p){ + int sgn = 0; + int nHr, nMn; + while( isspace(*(u8*)zDate) ){ zDate++; } + p->tz = 0; + if( *zDate=='-' ){ + sgn = -1; + }else if( *zDate=='+' ){ + sgn = +1; + }else{ + return *zDate!=0; + } + zDate++; + if( getDigits(zDate, 2, 0, 14, ':', &nHr, 2, 0, 59, 0, &nMn)!=2 ){ + return 1; + } + zDate += 5; + p->tz = sgn*(nMn + nHr*60); + while( isspace(*(u8*)zDate) ){ zDate++; } + return *zDate!=0; +} + +/* +** Parse times of the form HH:MM or HH:MM:SS or HH:MM:SS.FFFF. +** The HH, MM, and SS must each be exactly 2 digits. The +** fractional seconds FFFF can be one or more digits. +** +** Return 1 if there is a parsing error and 0 on success. +*/ +static int parseHhMmSs(const char *zDate, DateTime *p){ + int h, m, s; + double ms = 0.0; + if( getDigits(zDate, 2, 0, 24, ':', &h, 2, 0, 59, 0, &m)!=2 ){ + return 1; + } + zDate += 5; + if( *zDate==':' ){ + zDate++; + if( getDigits(zDate, 2, 0, 59, 0, &s)!=1 ){ + return 1; + } + zDate += 2; + if( *zDate=='.' && isdigit((u8)zDate[1]) ){ + double rScale = 1.0; + zDate++; + while( isdigit(*(u8*)zDate) ){ + ms = ms*10.0 + *zDate - '0'; + rScale *= 10.0; + zDate++; + } + ms /= rScale; + } + }else{ + s = 0; + } + p->validJD = 0; + p->validHMS = 1; + p->h = h; + p->m = m; + p->s = s + ms; + if( parseTimezone(zDate, p) ) return 1; + p->validTZ = p->tz!=0; + return 0; +} + +/* +** Convert from YYYY-MM-DD HH:MM:SS to julian day. We always assume +** that the YYYY-MM-DD is according to the Gregorian calendar. +** +** Reference: Meeus page 61 +*/ +static void computeJD(DateTime *p){ + int Y, M, D, A, B, X1, X2; + + if( p->validJD ) return; + if( p->validYMD ){ + Y = p->Y; + M = p->M; + D = p->D; + }else{ + Y = 2000; /* If no YMD specified, assume 2000-Jan-01 */ + M = 1; + D = 1; + } + if( M<=2 ){ + Y--; + M += 12; + } + A = Y/100; + B = 2 - A + (A/4); + X1 = 365.25*(Y+4716); + X2 = 30.6001*(M+1); + p->rJD = X1 + X2 + D + B - 1524.5; + p->validJD = 1; + p->validYMD = 0; + if( p->validHMS ){ + p->rJD += (p->h*3600.0 + p->m*60.0 + p->s)/86400.0; + if( p->validTZ ){ + p->rJD -= p->tz*60/86400.0; + p->validHMS = 0; + p->validTZ = 0; + } + } +} + +/* +** Parse dates of the form +** +** YYYY-MM-DD HH:MM:SS.FFF +** YYYY-MM-DD HH:MM:SS +** YYYY-MM-DD HH:MM +** YYYY-MM-DD +** +** Write the result into the DateTime structure and return 0 +** on success and 1 if the input string is not a well-formed +** date. +*/ +static int parseYyyyMmDd(const char *zDate, DateTime *p){ + int Y, M, D, neg; + + if( zDate[0]=='-' ){ + zDate++; + neg = 1; + }else{ + neg = 0; + } + if( getDigits(zDate,4,0,9999,'-',&Y,2,1,12,'-',&M,2,1,31,0,&D)!=3 ){ + return 1; + } + zDate += 10; + while( isspace(*(u8*)zDate) || 'T'==*(u8*)zDate ){ zDate++; } + if( parseHhMmSs(zDate, p)==0 ){ + /* We got the time */ + }else if( *zDate==0 ){ + p->validHMS = 0; + }else{ + return 1; + } + p->validJD = 0; + p->validYMD = 1; + p->Y = neg ? -Y : Y; + p->M = M; + p->D = D; + if( p->validTZ ){ + computeJD(p); + } + return 0; +} + +/* +** Attempt to parse the given string into a Julian Day Number. Return +** the number of errors. +** +** The following are acceptable forms for the input string: +** +** YYYY-MM-DD HH:MM:SS.FFF +/-HH:MM +** DDDD.DD +** now +** +** In the first form, the +/-HH:MM is always optional. The fractional +** seconds extension (the ".FFF") is optional. The seconds portion +** (":SS.FFF") is option. The year and date can be omitted as long +** as there is a time string. The time string can be omitted as long +** as there is a year and date. +*/ +static int parseDateOrTime(const char *zDate, DateTime *p){ + memset(p, 0, sizeof(*p)); + if( parseYyyyMmDd(zDate,p)==0 ){ + return 0; + }else if( parseHhMmSs(zDate, p)==0 ){ + return 0; + }else if( sqlite3StrICmp(zDate,"now")==0){ + double r; + sqlite3OsCurrentTime(&r); + p->rJD = r; + p->validJD = 1; + return 0; + }else if( sqlite3IsNumber(zDate, 0, SQLITE_UTF8) ){ + getValue(zDate, &p->rJD); + p->validJD = 1; + return 0; + } + return 1; +} + +/* +** Compute the Year, Month, and Day from the julian day number. +*/ +static void computeYMD(DateTime *p){ + int Z, A, B, C, D, E, X1; + if( p->validYMD ) return; + if( !p->validJD ){ + p->Y = 2000; + p->M = 1; + p->D = 1; + }else{ + Z = p->rJD + 0.5; + A = (Z - 1867216.25)/36524.25; + A = Z + 1 + A - (A/4); + B = A + 1524; + C = (B - 122.1)/365.25; + D = 365.25*C; + E = (B-D)/30.6001; + X1 = 30.6001*E; + p->D = B - D - X1; + p->M = E<14 ? E-1 : E-13; + p->Y = p->M>2 ? C - 4716 : C - 4715; + } + p->validYMD = 1; +} + +/* +** Compute the Hour, Minute, and Seconds from the julian day number. +*/ +static void computeHMS(DateTime *p){ + int Z, s; + if( p->validHMS ) return; + Z = p->rJD + 0.5; + s = (p->rJD + 0.5 - Z)*86400000.0 + 0.5; + p->s = 0.001*s; + s = p->s; + p->s -= s; + p->h = s/3600; + s -= p->h*3600; + p->m = s/60; + p->s += s - p->m*60; + p->validHMS = 1; +} + +/* +** Compute both YMD and HMS +*/ +static void computeYMD_HMS(DateTime *p){ + computeYMD(p); + computeHMS(p); +} + +/* +** Clear the YMD and HMS and the TZ +*/ +static void clearYMD_HMS_TZ(DateTime *p){ + p->validYMD = 0; + p->validHMS = 0; + p->validTZ = 0; +} + +/* +** Compute the difference (in days) between localtime and UTC (a.k.a. GMT) +** for the time value p where p is in UTC. +*/ +static double localtimeOffset(DateTime *p){ + DateTime x, y; + time_t t; + struct tm *pTm; + x = *p; + computeYMD_HMS(&x); + if( x.Y<1971 || x.Y>=2038 ){ + x.Y = 2000; + x.M = 1; + x.D = 1; + x.h = 0; + x.m = 0; + x.s = 0.0; + } else { + int s = x.s + 0.5; + x.s = s; + } + x.tz = 0; + x.validJD = 0; + computeJD(&x); + t = (x.rJD-2440587.5)*86400.0 + 0.5; + sqlite3OsEnterMutex(); + pTm = localtime(&t); + y.Y = pTm->tm_year + 1900; + y.M = pTm->tm_mon + 1; + y.D = pTm->tm_mday; + y.h = pTm->tm_hour; + y.m = pTm->tm_min; + y.s = pTm->tm_sec; + sqlite3OsLeaveMutex(); + y.validYMD = 1; + y.validHMS = 1; + y.validJD = 0; + y.validTZ = 0; + computeJD(&y); + return y.rJD - x.rJD; +} + +/* +** Process a modifier to a date-time stamp. The modifiers are +** as follows: +** +** NNN days +** NNN hours +** NNN minutes +** NNN.NNNN seconds +** NNN months +** NNN years +** start of month +** start of year +** start of week +** start of day +** weekday N +** unixepoch +** localtime +** utc +** +** Return 0 on success and 1 if there is any kind of error. +*/ +static int parseModifier(const char *zMod, DateTime *p){ + int rc = 1; + int n; + double r; + char *z, zBuf[30]; + z = zBuf; + for(n=0; nrJD += localtimeOffset(p); + clearYMD_HMS_TZ(p); + rc = 0; + } + break; + } + case 'u': { + /* + ** unixepoch + ** + ** Treat the current value of p->rJD as the number of + ** seconds since 1970. Convert to a real julian day number. + */ + if( strcmp(z, "unixepoch")==0 && p->validJD ){ + p->rJD = p->rJD/86400.0 + 2440587.5; + clearYMD_HMS_TZ(p); + rc = 0; + }else if( strcmp(z, "utc")==0 ){ + double c1; + computeJD(p); + c1 = localtimeOffset(p); + p->rJD -= c1; + clearYMD_HMS_TZ(p); + p->rJD += c1 - localtimeOffset(p); + rc = 0; + } + break; + } + case 'w': { + /* + ** weekday N + ** + ** Move the date to the same time on the next occurrence of + ** weekday N where 0==Sunday, 1==Monday, and so forth. If the + ** date is already on the appropriate weekday, this is a no-op. + */ + if( strncmp(z, "weekday ", 8)==0 && getValue(&z[8],&r)>0 + && (n=r)==r && n>=0 && r<7 ){ + int Z; + computeYMD_HMS(p); + p->validTZ = 0; + p->validJD = 0; + computeJD(p); + Z = p->rJD + 1.5; + Z %= 7; + if( Z>n ) Z -= 7; + p->rJD += n - Z; + clearYMD_HMS_TZ(p); + rc = 0; + } + break; + } + case 's': { + /* + ** start of TTTTT + ** + ** Move the date backwards to the beginning of the current day, + ** or month or year. + */ + if( strncmp(z, "start of ", 9)!=0 ) break; + z += 9; + computeYMD(p); + p->validHMS = 1; + p->h = p->m = 0; + p->s = 0.0; + p->validTZ = 0; + p->validJD = 0; + if( strcmp(z,"month")==0 ){ + p->D = 1; + rc = 0; + }else if( strcmp(z,"year")==0 ){ + computeYMD(p); + p->M = 1; + p->D = 1; + rc = 0; + }else if( strcmp(z,"day")==0 ){ + rc = 0; + } + break; + } + case '+': + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + n = getValue(z, &r); + if( n<=0 ) break; + if( z[n]==':' ){ + /* A modifier of the form (+|-)HH:MM:SS.FFF adds (or subtracts) the + ** specified number of hours, minutes, seconds, and fractional seconds + ** to the time. The ".FFF" may be omitted. The ":SS.FFF" may be + ** omitted. + */ + const char *z2 = z; + DateTime tx; + int day; + if( !isdigit(*(u8*)z2) ) z2++; + memset(&tx, 0, sizeof(tx)); + if( parseHhMmSs(z2, &tx) ) break; + computeJD(&tx); + tx.rJD -= 0.5; + day = (int)tx.rJD; + tx.rJD -= day; + if( z[0]=='-' ) tx.rJD = -tx.rJD; + computeJD(p); + clearYMD_HMS_TZ(p); + p->rJD += tx.rJD; + rc = 0; + break; + } + z += n; + while( isspace(*(u8*)z) ) z++; + n = strlen(z); + if( n>10 || n<3 ) break; + if( z[n-1]=='s' ){ z[n-1] = 0; n--; } + computeJD(p); + rc = 0; + if( n==3 && strcmp(z,"day")==0 ){ + p->rJD += r; + }else if( n==4 && strcmp(z,"hour")==0 ){ + p->rJD += r/24.0; + }else if( n==6 && strcmp(z,"minute")==0 ){ + p->rJD += r/(24.0*60.0); + }else if( n==6 && strcmp(z,"second")==0 ){ + p->rJD += r/(24.0*60.0*60.0); + }else if( n==5 && strcmp(z,"month")==0 ){ + int x, y; + computeYMD_HMS(p); + p->M += r; + x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12; + p->Y += x; + p->M -= x*12; + p->validJD = 0; + computeJD(p); + y = r; + if( y!=r ){ + p->rJD += (r - y)*30.0; + } + }else if( n==4 && strcmp(z,"year")==0 ){ + computeYMD_HMS(p); + p->Y += r; + p->validJD = 0; + computeJD(p); + }else{ + rc = 1; + } + clearYMD_HMS_TZ(p); + break; + } + default: { + break; + } + } + return rc; +} + +/* +** Process time function arguments. argv[0] is a date-time stamp. +** argv[1] and following are modifiers. Parse them all and write +** the resulting time into the DateTime structure p. Return 0 +** on success and 1 if there are any errors. +*/ +static int isDate(int argc, sqlite3_value **argv, DateTime *p){ + int i; + if( argc==0 ) return 1; + if( SQLITE_NULL==sqlite3_value_type(argv[0]) || + parseDateOrTime((char*)sqlite3_value_text(argv[0]), p) ) return 1; + for(i=1; izErrMsg and return NULL. If all tables +** are found, return a pointer to the last table. +*/ +Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){ + Table *pTab = 0; + int i; + struct SrcList_item *pItem; + for(i=0, pItem=pSrc->a; inSrc; i++, pItem++){ + pTab = sqlite3LocateTable(pParse, pItem->zName, pItem->zDatabase); + sqlite3DeleteTable(pParse->db, pItem->pTab); + pItem->pTab = pTab; + if( pTab ){ + pTab->nRef++; + } + } + return pTab; +} + +/* +** Check to make sure the given table is writable. If it is not +** writable, generate an error message and return 1. If it is +** writable return 0; +*/ +int sqlite3IsReadOnly(Parse *pParse, Table *pTab, int viewOk){ + if( pTab->readOnly && (pParse->db->flags & SQLITE_WriteSchema)==0 + && pParse->nested==0 ){ + sqlite3ErrorMsg(pParse, "table %s may not be modified", pTab->zName); + return 1; + } +#ifndef SQLITE_OMIT_VIEW + if( !viewOk && pTab->pSelect ){ + sqlite3ErrorMsg(pParse,"cannot modify %s because it is a view",pTab->zName); + return 1; + } +#endif + return 0; +} + +/* +** Generate code that will open a table for reading. +*/ +void sqlite3OpenTable( + Parse *p, /* Generate code into this VDBE */ + int iCur, /* The cursor number of the table */ + int iDb, /* The database index in sqlite3.aDb[] */ + Table *pTab, /* The table to be opened */ + int opcode /* OP_OpenRead or OP_OpenWrite */ +){ + Vdbe *v = sqlite3GetVdbe(p); + assert( opcode==OP_OpenWrite || opcode==OP_OpenRead ); + sqlite3TableLock(p, iDb, pTab->tnum, (opcode==OP_OpenWrite), pTab->zName); + sqlite3VdbeAddOp(v, OP_Integer, iDb, 0); + VdbeComment((v, "# %s", pTab->zName)); + sqlite3VdbeAddOp(v, opcode, iCur, pTab->tnum); + sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, pTab->nCol); +} + + +/* +** Generate code for a DELETE FROM statement. +** +** DELETE FROM table_wxyz WHERE a<5 AND b NOT NULL; +** \________/ \________________/ +** pTabList pWhere +*/ +void sqlite3DeleteFrom( + Parse *pParse, /* The parser context */ + SrcList *pTabList, /* The table from which we should delete things */ + Expr *pWhere /* The WHERE clause. May be null */ +){ + Vdbe *v; /* The virtual database engine */ + Table *pTab; /* The table from which records will be deleted */ + const char *zDb; /* Name of database holding pTab */ + int end, addr = 0; /* A couple addresses of generated code */ + int i; /* Loop counter */ + WhereInfo *pWInfo; /* Information about the WHERE clause */ + Index *pIdx; /* For looping over indices of the table */ + int iCur; /* VDBE Cursor number for pTab */ + sqlite3 *db; /* Main database structure */ + AuthContext sContext; /* Authorization context */ + int oldIdx = -1; /* Cursor for the OLD table of AFTER triggers */ + NameContext sNC; /* Name context to resolve expressions in */ + int iDb; + +#ifndef SQLITE_OMIT_TRIGGER + int isView; /* True if attempting to delete from a view */ + int triggers_exist = 0; /* True if any triggers exist */ +#endif + + sContext.pParse = 0; + if( pParse->nErr || sqlite3MallocFailed() ){ + goto delete_from_cleanup; + } + db = pParse->db; + assert( pTabList->nSrc==1 ); + + /* Locate the table which we want to delete. This table has to be + ** put in an SrcList structure because some of the subroutines we + ** will be calling are designed to work with multiple tables and expect + ** an SrcList* parameter instead of just a Table* parameter. + */ + pTab = sqlite3SrcListLookup(pParse, pTabList); + if( pTab==0 ) goto delete_from_cleanup; + + /* Figure out if we have any triggers and if the table being + ** deleted from is a view + */ +#ifndef SQLITE_OMIT_TRIGGER + triggers_exist = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0); + isView = pTab->pSelect!=0; +#else +# define triggers_exist 0 +# define isView 0 +#endif +#ifdef SQLITE_OMIT_VIEW +# undef isView +# define isView 0 +#endif + + if( sqlite3IsReadOnly(pParse, pTab, triggers_exist) ){ + goto delete_from_cleanup; + } + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); + assert( iDbnDb ); + zDb = db->aDb[iDb].zName; + if( sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){ + goto delete_from_cleanup; + } + + /* If pTab is really a view, make sure it has been initialized. + */ + if( isView && sqlite3ViewGetColumnNames(pParse, pTab) ){ + goto delete_from_cleanup; + } + + /* Allocate a cursor used to store the old.* data for a trigger. + */ + if( triggers_exist ){ + oldIdx = pParse->nTab++; + } + + /* Resolve the column names in the WHERE clause. + */ + assert( pTabList->nSrc==1 ); + iCur = pTabList->a[0].iCursor = pParse->nTab++; + memset(&sNC, 0, sizeof(sNC)); + sNC.pParse = pParse; + sNC.pSrcList = pTabList; + if( sqlite3ExprResolveNames(&sNC, pWhere) ){ + goto delete_from_cleanup; + } + + /* Start the view context + */ + if( isView ){ + sqlite3AuthContextPush(pParse, &sContext, pTab->zName); + } + + /* Begin generating code. + */ + v = sqlite3GetVdbe(pParse); + if( v==0 ){ + goto delete_from_cleanup; + } + if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); + sqlite3BeginWriteOperation(pParse, triggers_exist, iDb); + + /* If we are trying to delete from a view, realize that view into + ** a ephemeral table. + */ + if( isView ){ + Select *pView = sqlite3SelectDup(pTab->pSelect); + sqlite3Select(pParse, pView, SRT_VirtualTab, iCur, 0, 0, 0, 0); + sqlite3SelectDelete(pView); + } + + /* Initialize the counter of the number of rows deleted, if + ** we are counting rows. + */ + if( db->flags & SQLITE_CountRows ){ + sqlite3VdbeAddOp(v, OP_Integer, 0, 0); + } + + /* Special case: A DELETE without a WHERE clause deletes everything. + ** It is easier just to erase the whole table. Note, however, that + ** this means that the row change count will be incorrect. + */ + if( pWhere==0 && !triggers_exist ){ + if( db->flags & SQLITE_CountRows ){ + /* If counting rows deleted, just count the total number of + ** entries in the table. */ + int endOfLoop = sqlite3VdbeMakeLabel(v); + int addr2; + if( !isView ){ + sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead); + } + sqlite3VdbeAddOp(v, OP_Rewind, iCur, sqlite3VdbeCurrentAddr(v)+2); + addr2 = sqlite3VdbeAddOp(v, OP_AddImm, 1, 0); + sqlite3VdbeAddOp(v, OP_Next, iCur, addr2); + sqlite3VdbeResolveLabel(v, endOfLoop); + sqlite3VdbeAddOp(v, OP_Close, iCur, 0); + } + if( !isView ){ + sqlite3VdbeAddOp(v, OP_Clear, pTab->tnum, iDb); + if( !pParse->nested ){ + sqlite3VdbeChangeP3(v, -1, pTab->zName, P3_STATIC); + } + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + assert( pIdx->pSchema==pTab->pSchema ); + sqlite3VdbeAddOp(v, OP_Clear, pIdx->tnum, iDb); + } + } + } + + /* The usual case: There is a WHERE clause so we have to scan through + ** the table and pick which records to delete. + */ + else{ + /* Begin the database scan + */ + pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0); + if( pWInfo==0 ) goto delete_from_cleanup; + + /* Remember the rowid of every item to be deleted. + */ + sqlite3VdbeAddOp(v, OP_Rowid, iCur, 0); + sqlite3VdbeAddOp(v, OP_FifoWrite, 0, 0); + if( db->flags & SQLITE_CountRows ){ + sqlite3VdbeAddOp(v, OP_AddImm, 1, 0); + } + + /* End the database scan loop. + */ + sqlite3WhereEnd(pWInfo); + + /* Open the pseudo-table used to store OLD if there are triggers. + */ + if( triggers_exist ){ + sqlite3VdbeAddOp(v, OP_OpenPseudo, oldIdx, 0); + sqlite3VdbeAddOp(v, OP_SetNumColumns, oldIdx, pTab->nCol); + } + + /* Delete every item whose key was written to the list during the + ** database scan. We have to delete items after the scan is complete + ** because deleting an item can change the scan order. + */ + end = sqlite3VdbeMakeLabel(v); + + /* This is the beginning of the delete loop when there are + ** row triggers. + */ + if( triggers_exist ){ + addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, end); + if( !isView ){ + sqlite3VdbeAddOp(v, OP_Dup, 0, 0); + sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead); + } + sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0); + sqlite3VdbeAddOp(v, OP_Rowid, iCur, 0); + sqlite3VdbeAddOp(v, OP_RowData, iCur, 0); + sqlite3VdbeAddOp(v, OP_Insert, oldIdx, 0); + if( !isView ){ + sqlite3VdbeAddOp(v, OP_Close, iCur, 0); + } + + (void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TRIGGER_BEFORE, pTab, + -1, oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default, + addr); + } + + if( !isView ){ + /* Open cursors for the table we are deleting from and all its + ** indices. If there are row triggers, this happens inside the + ** OP_FifoRead loop because the cursor have to all be closed + ** before the trigger fires. If there are no row triggers, the + ** cursors are opened only once on the outside the loop. + */ + sqlite3OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite); + + /* This is the beginning of the delete loop when there are no + ** row triggers */ + if( !triggers_exist ){ + addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, end); + } + + /* Delete the row */ + sqlite3GenerateRowDelete(db, v, pTab, iCur, pParse->nested==0); + } + + /* If there are row triggers, close all cursors then invoke + ** the AFTER triggers + */ + if( triggers_exist ){ + if( !isView ){ + for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ + sqlite3VdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum); + } + sqlite3VdbeAddOp(v, OP_Close, iCur, 0); + } + (void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TRIGGER_AFTER, pTab, -1, + oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default, + addr); + } + + /* End of the delete loop */ + sqlite3VdbeAddOp(v, OP_Goto, 0, addr); + sqlite3VdbeResolveLabel(v, end); + + /* Close the cursors after the loop if there are no row triggers */ + if( !triggers_exist ){ + for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ + sqlite3VdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum); + } + sqlite3VdbeAddOp(v, OP_Close, iCur, 0); + } + } + + /* + ** Return the number of rows that were deleted. If this routine is + ** generating code because of a call to sqlite3NestedParse(), do not + ** invoke the callback function. + */ + if( db->flags & SQLITE_CountRows && pParse->nested==0 && !pParse->trigStack ){ + sqlite3VdbeAddOp(v, OP_Callback, 1, 0); + sqlite3VdbeSetNumCols(v, 1); + sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", P3_STATIC); + } + +delete_from_cleanup: + sqlite3AuthContextPop(&sContext); + sqlite3SrcListDelete(pTabList); + sqlite3ExprDelete(pWhere); + return; +} + +/* +** This routine generates VDBE code that causes a single row of a +** single table to be deleted. +** +** The VDBE must be in a particular state when this routine is called. +** These are the requirements: +** +** 1. A read/write cursor pointing to pTab, the table containing the row +** to be deleted, must be opened as cursor number "base". +** +** 2. Read/write cursors for all indices of pTab must be open as +** cursor number base+i for the i-th index. +** +** 3. The record number of the row to be deleted must be on the top +** of the stack. +** +** This routine pops the top of the stack to remove the record number +** and then generates code to remove both the table record and all index +** entries that point to that record. +*/ +void sqlite3GenerateRowDelete( + sqlite3 *db, /* The database containing the index */ + Vdbe *v, /* Generate code into this VDBE */ + Table *pTab, /* Table containing the row to be deleted */ + int iCur, /* Cursor number for the table */ + int count /* Increment the row change counter */ +){ + int addr; + addr = sqlite3VdbeAddOp(v, OP_NotExists, iCur, 0); + sqlite3GenerateRowIndexDelete(db, v, pTab, iCur, 0); + sqlite3VdbeAddOp(v, OP_Delete, iCur, (count?OPFLAG_NCHANGE:0)); + if( count ){ + sqlite3VdbeChangeP3(v, -1, pTab->zName, P3_STATIC); + } + sqlite3VdbeJumpHere(v, addr); +} + +/* +** This routine generates VDBE code that causes the deletion of all +** index entries associated with a single row of a single table. +** +** The VDBE must be in a particular state when this routine is called. +** These are the requirements: +** +** 1. A read/write cursor pointing to pTab, the table containing the row +** to be deleted, must be opened as cursor number "iCur". +** +** 2. Read/write cursors for all indices of pTab must be open as +** cursor number iCur+i for the i-th index. +** +** 3. The "iCur" cursor must be pointing to the row that is to be +** deleted. +*/ +void sqlite3GenerateRowIndexDelete( + sqlite3 *db, /* The database containing the index */ + Vdbe *v, /* Generate code into this VDBE */ + Table *pTab, /* Table containing the row to be deleted */ + int iCur, /* Cursor number for the table */ + char *aIdxUsed /* Only delete if aIdxUsed!=0 && aIdxUsed[i]!=0 */ +){ + int i; + Index *pIdx; + + for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ + if( aIdxUsed!=0 && aIdxUsed[i-1]==0 ) continue; + sqlite3GenerateIndexKey(v, pIdx, iCur); + sqlite3VdbeAddOp(v, OP_IdxDelete, iCur+i, 0); + } +} + +/* +** Generate code that will assemble an index key and put it on the top +** of the tack. The key with be for index pIdx which is an index on pTab. +** iCur is the index of a cursor open on the pTab table and pointing to +** the entry that needs indexing. +*/ +void sqlite3GenerateIndexKey( + Vdbe *v, /* Generate code into this VDBE */ + Index *pIdx, /* The index for which to generate a key */ + int iCur /* Cursor number for the pIdx->pTable table */ +){ + int j; + Table *pTab = pIdx->pTable; + + sqlite3VdbeAddOp(v, OP_Rowid, iCur, 0); + for(j=0; jnColumn; j++){ + int idx = pIdx->aiColumn[j]; + if( idx==pTab->iPKey ){ + sqlite3VdbeAddOp(v, OP_Dup, j, 0); + }else{ + sqlite3VdbeAddOp(v, OP_Column, iCur, idx); + sqlite3ColumnDefault(v, pTab, idx); + } + } + sqlite3VdbeAddOp(v, OP_MakeIdxRec, pIdx->nColumn, 0); + sqlite3IndexAffinityStr(v, pIdx); +} Added: external/sqlite-source-3.3.4/expr.c ============================================================================== --- (empty file) +++ external/sqlite-source-3.3.4/expr.c Mon Apr 3 07:54:59 2006 @@ -0,0 +1,2306 @@ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains routines used for analyzing expressions and +** for generating VDBE code that evaluates expressions in SQLite. +** +** $Id: expr.c,v 1.254 2006/02/10 07:07:15 danielk1977 Exp $ +*/ +#include "sqliteInt.h" +#include + +/* +** Return the 'affinity' of the expression pExpr if any. +** +** If pExpr is a column, a reference to a column via an 'AS' alias, +** or a sub-select with a column as the return value, then the +** affinity of that column is returned. Otherwise, 0x00 is returned, +** indicating no affinity for the expression. +** +** i.e. the WHERE clause expresssions in the following statements all +** have an affinity: +** +** CREATE TABLE t1(a); +** SELECT * FROM t1 WHERE a; +** SELECT a AS b FROM t1 WHERE b; +** SELECT * FROM t1 WHERE (select a from t1); +*/ +char sqlite3ExprAffinity(Expr *pExpr){ + int op = pExpr->op; + if( op==TK_AS ){ + return sqlite3ExprAffinity(pExpr->pLeft); + } + if( op==TK_SELECT ){ + return sqlite3ExprAffinity(pExpr->pSelect->pEList->a[0].pExpr); + } +#ifndef SQLITE_OMIT_CAST + if( op==TK_CAST ){ + return sqlite3AffinityType(&pExpr->token); + } +#endif + return pExpr->affinity; +} + +/* +** Return the default collation sequence for the expression pExpr. If +** there is no default collation type, return 0. +*/ +CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){ + CollSeq *pColl = 0; + if( pExpr ){ + pColl = pExpr->pColl; + if( (pExpr->op==TK_AS || pExpr->op==TK_CAST) && !pColl ){ + return sqlite3ExprCollSeq(pParse, pExpr->pLeft); + } + } + if( sqlite3CheckCollSeq(pParse, pColl) ){ + pColl = 0; + } + return pColl; +} + +/* +** pExpr is an operand of a comparison operator. aff2 is the +** type affinity of the other operand. This routine returns the +** type affinity that should be used for the comparison operator. +*/ +char sqlite3CompareAffinity(Expr *pExpr, char aff2){ + char aff1 = sqlite3ExprAffinity(pExpr); + if( aff1 && aff2 ){ + /* Both sides of the comparison are columns. If one has numeric + ** affinity, use that. Otherwise use no affinity. + */ + if( sqlite3IsNumericAffinity(aff1) || sqlite3IsNumericAffinity(aff2) ){ + return SQLITE_AFF_NUMERIC; + }else{ + return SQLITE_AFF_NONE; + } + }else if( !aff1 && !aff2 ){ + /* Neither side of the comparison is a column. Compare the + ** results directly. + */ + return SQLITE_AFF_NONE; + }else{ + /* One side is a column, the other is not. Use the columns affinity. */ + assert( aff1==0 || aff2==0 ); + return (aff1 + aff2); + } +} + +/* +** pExpr is a comparison operator. Return the type affinity that should +** be applied to both operands prior to doing the comparison. +*/ +static char comparisonAffinity(Expr *pExpr){ + char aff; + assert( pExpr->op==TK_EQ || pExpr->op==TK_IN || pExpr->op==TK_LT || + pExpr->op==TK_GT || pExpr->op==TK_GE || pExpr->op==TK_LE || + pExpr->op==TK_NE ); + assert( pExpr->pLeft ); + aff = sqlite3ExprAffinity(pExpr->pLeft); + if( pExpr->pRight ){ + aff = sqlite3CompareAffinity(pExpr->pRight, aff); + } + else if( pExpr->pSelect ){ + aff = sqlite3CompareAffinity(pExpr->pSelect->pEList->a[0].pExpr, aff); + } + else if( !aff ){ + aff = SQLITE_AFF_NUMERIC; + } + return aff; +} + +/* +** pExpr is a comparison expression, eg. '=', '<', IN(...) etc. +** idx_affinity is the affinity of an indexed column. Return true +** if the index with affinity idx_affinity may be used to implement +** the comparison in pExpr. +*/ +int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity){ + char aff = comparisonAffinity(pExpr); + switch( aff ){ + case SQLITE_AFF_NONE: + return 1; + case SQLITE_AFF_TEXT: + return idx_affinity==SQLITE_AFF_TEXT; + default: + return sqlite3IsNumericAffinity(idx_affinity); + } +} + +/* +** Return the P1 value that should be used for a binary comparison +** opcode (OP_Eq, OP_Ge etc.) used to compare pExpr1 and pExpr2. +** If jumpIfNull is true, then set the low byte of the returned +** P1 value to tell the opcode to jump if either expression +** evaluates to NULL. +*/ +static int binaryCompareP1(Expr *pExpr1, Expr *pExpr2, int jumpIfNull){ + char aff = sqlite3ExprAffinity(pExpr2); + return ((int)sqlite3CompareAffinity(pExpr1, aff))+(jumpIfNull?0x100:0); +} + +/* +** Return a pointer to the collation sequence that should be used by +** a binary comparison operator comparing pLeft and pRight. +** +** If the left hand expression has a collating sequence type, then it is +** used. Otherwise the collation sequence for the right hand expression +** is used, or the default (BINARY) if neither expression has a collating +** type. +*/ +static CollSeq* binaryCompareCollSeq(Parse *pParse, Expr *pLeft, Expr *pRight){ + CollSeq *pColl = sqlite3ExprCollSeq(pParse, pLeft); + if( !pColl ){ + pColl = sqlite3ExprCollSeq(pParse, pRight); + } + return pColl; +} + +/* +** Generate code for a comparison operator. +*/ +static int codeCompare( + Parse *pParse, /* The parsing (and code generating) context */ + Expr *pLeft, /* The left operand */ + Expr *pRight, /* The right operand */ + int opcode, /* The comparison opcode */ + int dest, /* Jump here if true. */ + int jumpIfNull /* If true, jump if either operand is NULL */ +){ + int p1 = binaryCompareP1(pLeft, pRight, jumpIfNull); + CollSeq *p3 = binaryCompareCollSeq(pParse, pLeft, pRight); + return sqlite3VdbeOp3(pParse->pVdbe, opcode, p1, dest, (void*)p3, P3_COLLSEQ); +} + +/* +** Construct a new expression node and return a pointer to it. Memory +** for this node is obtained from sqliteMalloc(). The calling function +** is responsible for making sure the node eventually gets freed. +*/ +Expr *sqlite3Expr(int op, Expr *pLeft, Expr *pRight, const Token *pToken){ + Expr *pNew; + pNew = sqliteMalloc( sizeof(Expr) ); + if( pNew==0 ){ + /* When malloc fails, delete pLeft and pRight. Expressions passed to + ** this function must always be allocated with sqlite3Expr() for this + ** reason. + */ + sqlite3ExprDelete(pLeft); + sqlite3ExprDelete(pRight); + return 0; + } + pNew->op = op; + pNew->pLeft = pLeft; + pNew->pRight = pRight; + pNew->iAgg = -1; + if( pToken ){ + assert( pToken->dyn==0 ); + pNew->span = pNew->token = *pToken; + }else if( pLeft && pRight ){ + sqlite3ExprSpan(pNew, &pLeft->span, &pRight->span); + } + return pNew; +} + +/* +** When doing a nested parse, you can include terms in an expression +** that look like this: #0 #1 #2 ... These terms refer to elements +** on the stack. "#0" means the top of the stack. +** "#1" means the next down on the stack. And so forth. +** +** This routine is called by the parser to deal with on of those terms. +** It immediately generates code to store the value in a memory location. +** The returns an expression that will code to extract the value from +** that memory location as needed. +*/ +Expr *sqlite3RegisterExpr(Parse *pParse, Token *pToken){ + Vdbe *v = pParse->pVdbe; + Expr *p; + int depth; + if( pParse->nested==0 ){ + sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", pToken); + return 0; + } + if( v==0 ) return 0; + p = sqlite3Expr(TK_REGISTER, 0, 0, pToken); + if( p==0 ){ + return 0; /* Malloc failed */ + } + depth = atoi((char*)&pToken->z[1]); + p->iTable = pParse->nMem++; + sqlite3VdbeAddOp(v, OP_Dup, depth, 0); + sqlite3VdbeAddOp(v, OP_MemStore, p->iTable, 1); + return p; +} + +/* +** Join two expressions using an AND operator. If either expression is +** NULL, then just return the other expression. +*/ +Expr *sqlite3ExprAnd(Expr *pLeft, Expr *pRight){ + if( pLeft==0 ){ + return pRight; + }else if( pRight==0 ){ + return pLeft; + }else{ + return sqlite3Expr(TK_AND, pLeft, pRight, 0); + } +} + +/* +** Set the Expr.span field of the given expression to span all +** text between the two given tokens. +*/ +void sqlite3ExprSpan(Expr *pExpr, Token *pLeft, Token *pRight){ + assert( pRight!=0 ); + assert( pLeft!=0 ); + if( !sqlite3MallocFailed() && pRight->z && pLeft->z ){ + assert( pLeft->dyn==0 || pLeft->z[pLeft->n]==0 ); + if( pLeft->dyn==0 && pRight->dyn==0 ){ + pExpr->span.z = pLeft->z; + pExpr->span.n = pRight->n + (pRight->z - pLeft->z); + }else{ + pExpr->span.z = 0; + } + } +} + +/* +** Construct a new expression node for a function with multiple +** arguments. +*/ +Expr *sqlite3ExprFunction(ExprList *pList, Token *pToken){ + Expr *pNew; + assert( pToken ); + pNew = sqliteMalloc( sizeof(Expr) ); + if( pNew==0 ){ + sqlite3ExprListDelete(pList); /* Avoid leaking memory when malloc fails */ + return 0; + } + pNew->op = TK_FUNCTION; + pNew->pList = pList; + assert( pToken->dyn==0 ); + pNew->token = *pToken; + pNew->span = pNew->token; + return pNew; +} + +/* +** Assign a variable number to an expression that encodes a wildcard +** in the original SQL statement. +** +** Wildcards consisting of a single "?" are assigned the next sequential +** variable number. +** +** Wildcards of the form "?nnn" are assigned the number "nnn". We make +** sure "nnn" is not too be to avoid a denial of service attack when +** the SQL statement comes from an external source. +** +** Wildcards of the form ":aaa" or "$aaa" are assigned the same number +** as the previous instance of the same wildcard. Or if this is the first +** instance of the wildcard, the next sequenial variable number is +** assigned. +*/ +void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){ + Token *pToken; + if( pExpr==0 ) return; + pToken = &pExpr->token; + assert( pToken->n>=1 ); + assert( pToken->z!=0 ); + assert( pToken->z[0]!=0 ); + if( pToken->n==1 ){ + /* Wildcard of the form "?". Assign the next variable number */ + pExpr->iTable = ++pParse->nVar; + }else if( pToken->z[0]=='?' ){ + /* Wildcard of the form "?nnn". Convert "nnn" to an integer and + ** use it as the variable number */ + int i; + pExpr->iTable = i = atoi((char*)&pToken->z[1]); + if( i<1 || i>SQLITE_MAX_VARIABLE_NUMBER ){ + sqlite3ErrorMsg(pParse, "variable number must be between ?1 and ?%d", + SQLITE_MAX_VARIABLE_NUMBER); + } + if( i>pParse->nVar ){ + pParse->nVar = i; + } + }else{ + /* Wildcards of the form ":aaa" or "$aaa". Reuse the same variable + ** number as the prior appearance of the same name, or if the name + ** has never appeared before, reuse the same variable number + */ + int i, n; + n = pToken->n; + for(i=0; inVarExpr; i++){ + Expr *pE; + if( (pE = pParse->apVarExpr[i])!=0 + && pE->token.n==n + && memcmp(pE->token.z, pToken->z, n)==0 ){ + pExpr->iTable = pE->iTable; + break; + } + } + if( i>=pParse->nVarExpr ){ + pExpr->iTable = ++pParse->nVar; + if( pParse->nVarExpr>=pParse->nVarExprAlloc-1 ){ + pParse->nVarExprAlloc += pParse->nVarExprAlloc + 10; + sqliteReallocOrFree((void**)&pParse->apVarExpr, + pParse->nVarExprAlloc*sizeof(pParse->apVarExpr[0]) ); + } + if( !sqlite3MallocFailed() ){ + assert( pParse->apVarExpr!=0 ); + pParse->apVarExpr[pParse->nVarExpr++] = pExpr; + } + } + } +} + +/* +** Recursively delete an expression tree. +*/ +void sqlite3ExprDelete(Expr *p){ + if( p==0 ) return; + if( p->span.dyn ) sqliteFree((char*)p->span.z); + if( p->token.dyn ) sqliteFree((char*)p->token.z); + sqlite3ExprDelete(p->pLeft); + sqlite3ExprDelete(p->pRight); + sqlite3ExprListDelete(p->pList); + sqlite3SelectDelete(p->pSelect); + sqliteFree(p); +} + +/* +** The Expr.token field might be a string literal that is quoted. +** If so, remove the quotation marks. +*/ +void sqlite3DequoteExpr(Expr *p){ + if( ExprHasAnyProperty(p, EP_Dequoted) ){ + return; + } + ExprSetProperty(p, EP_Dequoted); + if( p->token.dyn==0 ){ + sqlite3TokenCopy(&p->token, &p->token); + } + 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 +** be deleted (by being passed to their respective ...Delete() routines) +** without effecting the originals. +** +** The expression list, ID, and source lists return by sqlite3ExprListDup(), +** sqlite3IdListDup(), and sqlite3SrcListDup() can not be further expanded +** by subsequent calls to sqlite*ListAppend() routines. +** +** Any tables that the SrcList might point to are not duplicated. +*/ +Expr *sqlite3ExprDup(Expr *p){ + Expr *pNew; + if( p==0 ) return 0; + pNew = sqliteMallocRaw( sizeof(*p) ); + if( pNew==0 ) return 0; + memcpy(pNew, p, sizeof(*pNew)); + if( p->token.z!=0 ){ + pNew->token.z = (u8*)sqliteStrNDup((char*)p->token.z, p->token.n); + pNew->token.dyn = 1; + }else{ + assert( pNew->token.z==0 ); + } + pNew->span.z = 0; + pNew->pLeft = sqlite3ExprDup(p->pLeft); + pNew->pRight = sqlite3ExprDup(p->pRight); + pNew->pList = sqlite3ExprListDup(p->pList); + pNew->pSelect = sqlite3SelectDup(p->pSelect); + pNew->pTab = p->pTab; + return pNew; +} +void sqlite3TokenCopy(Token *pTo, Token *pFrom){ + if( pTo->dyn ) sqliteFree((char*)pTo->z); + if( pFrom->z ){ + pTo->n = pFrom->n; + pTo->z = (u8*)sqliteStrNDup((char*)pFrom->z, pFrom->n); + pTo->dyn = 1; + }else{ + pTo->z = 0; + } +} +ExprList *sqlite3ExprListDup(ExprList *p){ + ExprList *pNew; + struct ExprList_item *pItem, *pOldItem; + int i; + if( p==0 ) return 0; + pNew = sqliteMalloc( sizeof(*pNew) ); + if( pNew==0 ) return 0; + pNew->nExpr = pNew->nAlloc = p->nExpr; + pNew->a = pItem = sqliteMalloc( p->nExpr*sizeof(p->a[0]) ); + if( pItem==0 ){ + sqliteFree(pNew); + return 0; + } + pOldItem = p->a; + for(i=0; inExpr; i++, pItem++, pOldItem++){ + Expr *pNewExpr, *pOldExpr; + pItem->pExpr = pNewExpr = sqlite3ExprDup(pOldExpr = pOldItem->pExpr); + if( pOldExpr->span.z!=0 && pNewExpr ){ + /* Always make a copy of the span for top-level expressions in the + ** expression list. The logic in SELECT processing that determines + ** the names of columns in the result set needs this information */ + sqlite3TokenCopy(&pNewExpr->span, &pOldExpr->span); + } + assert( pNewExpr==0 || pNewExpr->span.z!=0 + || pOldExpr->span.z==0 + || sqlite3MallocFailed() ); + pItem->zName = sqliteStrDup(pOldItem->zName); + pItem->sortOrder = pOldItem->sortOrder; + pItem->isAgg = pOldItem->isAgg; + pItem->done = 0; + } + return pNew; +} + +/* +** If cursors, triggers, views and subqueries are all omitted from +** the build, then none of the following routines, except for +** sqlite3SelectDup(), can be called. sqlite3SelectDup() is sometimes +** called with a NULL argument. +*/ +#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) \ + || !defined(SQLITE_OMIT_SUBQUERY) +SrcList *sqlite3SrcListDup(SrcList *p){ + SrcList *pNew; + int i; + int nByte; + if( p==0 ) return 0; + nByte = sizeof(*p) + (p->nSrc>0 ? sizeof(p->a[0]) * (p->nSrc-1) : 0); + pNew = sqliteMallocRaw( nByte ); + if( pNew==0 ) return 0; + pNew->nSrc = pNew->nAlloc = p->nSrc; + for(i=0; inSrc; i++){ + struct SrcList_item *pNewItem = &pNew->a[i]; + struct SrcList_item *pOldItem = &p->a[i]; + Table *pTab; + pNewItem->zDatabase = sqliteStrDup(pOldItem->zDatabase); + pNewItem->zName = sqliteStrDup(pOldItem->zName); + pNewItem->zAlias = sqliteStrDup(pOldItem->zAlias); + pNewItem->jointype = pOldItem->jointype; + pNewItem->iCursor = pOldItem->iCursor; + pNewItem->isPopulated = pOldItem->isPopulated; + pTab = pNewItem->pTab = pOldItem->pTab; + if( pTab ){ + pTab->nRef++; + } + pNewItem->pSelect = sqlite3SelectDup(pOldItem->pSelect); + pNewItem->pOn = sqlite3ExprDup(pOldItem->pOn); + pNewItem->pUsing = sqlite3IdListDup(pOldItem->pUsing); + pNewItem->colUsed = pOldItem->colUsed; + } + return pNew; +} +IdList *sqlite3IdListDup(IdList *p){ + IdList *pNew; + int i; + if( p==0 ) return 0; + pNew = sqliteMallocRaw( sizeof(*pNew) ); + if( pNew==0 ) return 0; + pNew->nId = pNew->nAlloc = p->nId; + pNew->a = sqliteMallocRaw( p->nId*sizeof(p->a[0]) ); + if( pNew->a==0 ){ + sqliteFree(pNew); + return 0; + } + for(i=0; inId; i++){ + struct IdList_item *pNewItem = &pNew->a[i]; + struct IdList_item *pOldItem = &p->a[i]; + pNewItem->zName = sqliteStrDup(pOldItem->zName); + pNewItem->idx = pOldItem->idx; + } + return pNew; +} +Select *sqlite3SelectDup(Select *p){ + Select *pNew; + if( p==0 ) return 0; + pNew = sqliteMallocRaw( sizeof(*p) ); + if( pNew==0 ) return 0; + pNew->isDistinct = p->isDistinct; + pNew->pEList = sqlite3ExprListDup(p->pEList); + pNew->pSrc = sqlite3SrcListDup(p->pSrc); + pNew->pWhere = sqlite3ExprDup(p->pWhere); + pNew->pGroupBy = sqlite3ExprListDup(p->pGroupBy); + pNew->pHaving = sqlite3ExprDup(p->pHaving); + pNew->pOrderBy = sqlite3ExprListDup(p->pOrderBy); + pNew->op = p->op; + pNew->pPrior = sqlite3SelectDup(p->pPrior); + pNew->pLimit = sqlite3ExprDup(p->pLimit); + pNew->pOffset = sqlite3ExprDup(p->pOffset); + pNew->iLimit = -1; + pNew->iOffset = -1; + pNew->isResolved = p->isResolved; + pNew->isAgg = p->isAgg; + pNew->usesVirt = 0; + pNew->disallowOrderBy = 0; + pNew->pRightmost = 0; + pNew->addrOpenVirt[0] = -1; + pNew->addrOpenVirt[1] = -1; + pNew->addrOpenVirt[2] = -1; + return pNew; +} +#else +Select *sqlite3SelectDup(Select *p){ + assert( p==0 ); + return 0; +} +#endif + + +/* +** Add a new element to the end of an expression list. If pList is +** initially NULL, then create a new expression list. +*/ +ExprList *sqlite3ExprListAppend(ExprList *pList, Expr *pExpr, Token *pName){ + if( pList==0 ){ + pList = sqliteMalloc( sizeof(ExprList) ); + if( pList==0 ){ + goto no_mem; + } + assert( pList->nAlloc==0 ); + } + if( pList->nAlloc<=pList->nExpr ){ + struct ExprList_item *a; + int n = pList->nAlloc*2 + 4; + a = sqliteRealloc(pList->a, n*sizeof(pList->a[0])); + if( a==0 ){ + goto no_mem; + } + pList->a = a; + pList->nAlloc = n; + } + assert( pList->a!=0 ); + if( pExpr || pName ){ + struct ExprList_item *pItem = &pList->a[pList->nExpr++]; + memset(pItem, 0, sizeof(*pItem)); + pItem->zName = sqlite3NameFromToken(pName); + pItem->pExpr = pExpr; + } + return pList; + +no_mem: + /* Avoid leaking memory if malloc has failed. */ + sqlite3ExprDelete(pExpr); + sqlite3ExprListDelete(pList); + return 0; +} + +/* +** Delete an entire expression list. +*/ +void sqlite3ExprListDelete(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; inExpr; i++, pItem++){ + sqlite3ExprDelete(pItem->pExpr); + sqliteFree(pItem->zName); + } + sqliteFree(pList->a); + sqliteFree(pList); +} + +/* +** Walk an expression tree. Call xFunc for each node visited. +** +** 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); + return 0; +} + + +/* +** This routine is designed as an xFunc for walkExprTree(). +** +** 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){ + switch( pExpr->op ){ + /* Consider functions to be constant if all their arguments are constant + ** and *pArg==2 */ + case TK_FUNCTION: + if( *((int*)pArg)==2 ) return 0; + /* Fall through */ + case TK_ID: + case TK_COLUMN: + case TK_DOT: + case TK_AGG_FUNCTION: + case TK_AGG_COLUMN: +#ifndef SQLITE_OMIT_SUBQUERY + case TK_SELECT: + case TK_EXISTS: +#endif + *((int*)pArg) = 0; + return 2; + case TK_IN: + if( pExpr->pSelect ){ + *((int*)pArg) = 0; + return 2; + } + default: + return 0; + } +} + +/* +** Walk an expression tree. Return 1 if the expression is constant +** and 0 if it involves variables or function calls. +** +** For the purposes of this function, a double-quoted string (ex: "abc") +** is considered a variable but a single-quoted string (ex: 'abc') is +** a constant. +*/ +int sqlite3ExprIsConstant(Expr *p){ + int isConst = 1; + walkExprTree(p, exprNodeIsConstant, &isConst); + return isConst; +} + +/* +** Walk an expression tree. Return 1 if the expression is constant +** or a function call with constant arguments. Return and 0 if there +** are any variables. +** +** For the purposes of this function, a double-quoted string (ex: "abc") +** is considered a variable but a single-quoted string (ex: 'abc') is +** a constant. +*/ +int sqlite3ExprIsConstantOrFunction(Expr *p){ + int isConst = 2; + walkExprTree(p, exprNodeIsConstant, &isConst); + return isConst!=0; +} + +/* +** If the expression p codes a constant integer that is small enough +** to fit in a 32-bit integer, return 1 and put the value of the integer +** in *pValue. If the expression is not an integer or if it is too big +** to fit in a signed 32-bit integer, return 0 and leave *pValue unchanged. +*/ +int sqlite3ExprIsInteger(Expr *p, int *pValue){ + switch( p->op ){ + case TK_INTEGER: { + if( sqlite3GetInt32((char*)p->token.z, pValue) ){ + return 1; + } + break; + } + case TK_UPLUS: { + return sqlite3ExprIsInteger(p->pLeft, pValue); + } + case TK_UMINUS: { + int v; + if( sqlite3ExprIsInteger(p->pLeft, &v) ){ + *pValue = -v; + return 1; + } + break; + } + default: break; + } + return 0; +} + +/* +** Return TRUE if the given string is a row-id column name. +*/ +int sqlite3IsRowid(const char *z){ + if( sqlite3StrICmp(z, "_ROWID_")==0 ) return 1; + if( sqlite3StrICmp(z, "ROWID")==0 ) return 1; + if( sqlite3StrICmp(z, "OID")==0 ) return 1; + 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 */ + + assert( pColumnToken && pColumnToken->z ); /* The Z in X.Y.Z cannot be NULL */ + zDb = sqlite3NameFromToken(pDbToken); + zTab = sqlite3NameFromToken(pTableToken); + zCol = sqlite3NameFromToken(pColumnToken); + if( sqlite3MallocFailed() ){ + goto lookupname_end; + } + + pExpr->iTable = -1; + while( pNC && cnt==0 ){ + ExprList *pEList; + SrcList *pSrcList = pNC->pSrcList; + + if( pSrcList ){ + for(i=0, pItem=pSrcList->a; inSrc; i++, pItem++){ + Table *pTab = pItem->pTab; + int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); + Column *pCol; + + if( pTab==0 ) continue; + 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->pSchema = pTab->pSchema; + pMatch = pItem; + } + for(j=0, pCol=pTab->aCol; jnCol; j++, pCol++){ + if( sqlite3StrICmp(pCol->zName, zCol)==0 ){ + const char *zColl = pTab->aCol[j].zColl; + IdList *pUsing; + cnt++; + pExpr->iTable = pItem->iCursor; + pMatch = pItem; + pExpr->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; + pExpr->pColl = sqlite3FindCollSeq(db, ENC(db), zColl,-1, 0); + if( pItem->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++; + } + if( (pUsing = pItem->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; knId; 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; + if( pTriggerStack->newIdx != -1 && sqlite3StrICmp("new", zTab) == 0 ){ + pExpr->iTable = pTriggerStack->newIdx; + assert( pTriggerStack->pTab ); + pTab = pTriggerStack->pTab; + }else if( pTriggerStack->oldIdx != -1 && sqlite3StrICmp("old", zTab)==0 ){ + pExpr->iTable = pTriggerStack->oldIdx; + assert( pTriggerStack->pTab ); + pTab = pTriggerStack->pTab; + } + + if( pTab ){ + int iCol; + Column *pCol = pTab->aCol; + + pExpr->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; + pExpr->pColl = sqlite3FindCollSeq(db, ENC(db), zColl,-1, 0); + pExpr->pTab = pTab; + 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; jnExpr; j++){ + char *zAs = pEList->a[j].zName; + if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){ + assert( pExpr->pLeft==0 && pExpr->pRight==0 ); + pExpr->op = TK_AS; + pExpr->iColumn = j; + pExpr->pLeft = sqlite3ExprDup(pEList->a[j].pExpr); + cnt = 1; + 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]=='"' ){ + sqliteFree(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 ){ + char *z = 0; + char *zErr; + zErr = cnt==0 ? "no such column: %s" : "ambiguous column name: %s"; + if( zDb ){ + sqlite3SetString(&z, zDb, ".", zTab, ".", zCol, (char*)0); + }else if( zTab ){ + sqlite3SetString(&z, zTab, ".", zCol, (char*)0); + }else{ + z = sqliteStrDup(zCol); + } + sqlite3ErrorMsg(pParse, zErr, z); + sqliteFree(z); + 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; + if( n>=sizeof(Bitmask)*8 ){ + n = sizeof(Bitmask)*8-1; + } + assert( pMatch->iCursor==pExpr->iTable ); + pMatch->colUsed |= 1<pLeft); + pExpr->pLeft = 0; + sqlite3ExprDelete(pExpr->pRight); + pExpr->pRight = 0; + pExpr->op = TK_COLUMN; +lookupname_end_2: + sqliteFree(zCol); + if( cnt==1 ){ + assert( pNC!=0 ); + sqlite3AuthRead(pParse, pExpr, 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; ipSrcList->nSrc; i++){ + assert( pSrcList->a[i].iCursor>=0 && pSrcList->a[i].iCursornTab); + } + } +#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 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; + } + 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 && ia[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. +*/ +int sqlite3ExprResolveNames( + NameContext *pNC, /* Namespace to resolve expressions in. */ + Expr *pExpr /* The expression to be analyzed. */ +){ + int savedHasAgg; + if( pExpr==0 ) return 0; + savedHasAgg = pNC->hasAgg; + pNC->hasAgg = 0; + walkExprTree(pExpr, nameResolverStep, pNC); + 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 */ +}; + + +/* +** Generate code for scalar subqueries used as an expression +** and IN operators. Examples: +** +** (SELECT a FROM b) -- subquery +** EXISTS (SELECT a FROM b) -- EXISTS subquery +** x IN (4,5,11) -- IN operator with list on right-hand side +** x IN (SELECT a FROM b) -- IN operator with subquery on the right +** +** The pExpr parameter describes the expression that contains the IN +** operator or subquery. +*/ +#ifndef SQLITE_OMIT_SUBQUERY +void sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ + int testAddr = 0; /* One-time test address */ + Vdbe *v = sqlite3GetVdbe(pParse); + if( v==0 ) return; + + /* This code must be run in its entirety every time it is encountered + ** if any of the following is true: + ** + ** * The right-hand side is a correlated subquery + ** * The right-hand side is an expression list containing variables + ** * We are inside a trigger + ** + ** If all of the above are false, then we can run this code just once + ** save the results, and reuse the same result on subsequent invocations. + */ + if( !ExprHasAnyProperty(pExpr, EP_VarSelect) && !pParse->trigStack ){ + int mem = pParse->nMem++; + sqlite3VdbeAddOp(v, OP_MemLoad, mem, 0); + testAddr = sqlite3VdbeAddOp(v, OP_If, 0, 0); + assert( testAddr>0 || sqlite3MallocFailed() ); + sqlite3VdbeAddOp(v, OP_MemInt, 1, mem); + } + + switch( pExpr->op ){ + case TK_IN: { + char affinity; + KeyInfo keyInfo; + int addr; /* Address of OP_OpenVirtual instruction */ + + affinity = sqlite3ExprAffinity(pExpr->pLeft); + + /* Whether this is an 'x IN(SELECT...)' or an 'x IN()' + ** expression it is handled the same way. A virtual table is + ** filled with single-field index keys representing the results + ** from the SELECT or the . + ** + ** If the 'x' expression is a column value, or the SELECT... + ** statement returns a column value, then the affinity of that + ** column is used to build the index keys. If both 'x' and the + ** SELECT... statement are columns, then numeric affinity is used + ** if either column has NUMERIC or INTEGER affinity. If neither + ** 'x' nor the SELECT... statement are columns, then numeric affinity + ** is used. + */ + pExpr->iTable = pParse->nTab++; + addr = sqlite3VdbeAddOp(v, OP_OpenVirtual, pExpr->iTable, 0); + memset(&keyInfo, 0, sizeof(keyInfo)); + keyInfo.nField = 1; + sqlite3VdbeAddOp(v, OP_SetNumColumns, pExpr->iTable, 1); + + if( pExpr->pSelect ){ + /* Case 1: expr IN (SELECT ...) + ** + ** Generate code to write the results of the select into the temporary + ** table allocated and opened above. + */ + int iParm = pExpr->iTable + (((int)affinity)<<16); + ExprList *pEList; + assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable ); + sqlite3Select(pParse, pExpr->pSelect, SRT_Set, iParm, 0, 0, 0, 0); + pEList = pExpr->pSelect->pEList; + if( pEList && pEList->nExpr>0 ){ + keyInfo.aColl[0] = binaryCompareCollSeq(pParse, pExpr->pLeft, + pEList->a[0].pExpr); + } + }else if( pExpr->pList ){ + /* Case 2: expr IN (exprlist) + ** + ** For each expression, build an index key from the evaluation and + ** store it in the temporary table. If is a column, then use + ** that columns affinity when building index keys. If is not + ** a column, use numeric affinity. + */ + int i; + ExprList *pList = pExpr->pList; + struct ExprList_item *pItem; + + if( !affinity ){ + affinity = SQLITE_AFF_NUMERIC; + } + keyInfo.aColl[0] = pExpr->pLeft->pColl; + + /* Loop through each expression in . */ + for(i=pList->nExpr, pItem=pList->a; i>0; i--, pItem++){ + Expr *pE2 = pItem->pExpr; + + /* If the expression is not constant then we will need to + ** disable the test that was generated above that makes sure + ** this code only executes once. Because for a non-constant + ** expression we need to rerun this code each time. + */ + if( testAddr>0 && !sqlite3ExprIsConstant(pE2) ){ + VdbeOp *aOp = sqlite3VdbeGetOp(v, testAddr-1); + int j; + for(j=0; j<3; j++){ + aOp[j].opcode = OP_Noop; + } + testAddr = 0; + } + + /* Evaluate the expression and insert it into the temp table */ + sqlite3ExprCode(pParse, pE2); + sqlite3VdbeOp3(v, OP_MakeRecord, 1, 0, &affinity, 1); + sqlite3VdbeAddOp(v, OP_IdxInsert, pExpr->iTable, 0); + } + } + sqlite3VdbeChangeP3(v, addr, (void *)&keyInfo, P3_KEYINFO); + break; + } + + case TK_EXISTS: + case TK_SELECT: { + /* This has to be a scalar SELECT. Generate code to put the + ** value of this select in a memory cell and record the number + ** of the memory cell in iColumn. + */ + static const Token one = { (u8*)"1", 0, 1 }; + Select *pSel; + int iMem; + int sop; + + pExpr->iColumn = iMem = pParse->nMem++; + pSel = pExpr->pSelect; + if( pExpr->op==TK_SELECT ){ + sop = SRT_Mem; + sqlite3VdbeAddOp(v, OP_MemNull, iMem, 0); + VdbeComment((v, "# Init subquery result")); + }else{ + sop = SRT_Exists; + sqlite3VdbeAddOp(v, OP_MemInt, 0, iMem); + VdbeComment((v, "# Init EXISTS result")); + } + sqlite3ExprDelete(pSel->pLimit); + pSel->pLimit = sqlite3Expr(TK_INTEGER, 0, 0, &one); + sqlite3Select(pParse, pSel, sop, iMem, 0, 0, 0, 0); + break; + } + } + + if( testAddr ){ + sqlite3VdbeJumpHere(v, testAddr); + } + return; +} +#endif /* SQLITE_OMIT_SUBQUERY */ + +/* +** Generate an instruction that will put the integer describe by +** text z[0..n-1] on the stack. +*/ +static void codeInteger(Vdbe *v, const char *z, int n){ + int i; + if( sqlite3GetInt32(z, &i) ){ + sqlite3VdbeAddOp(v, OP_Integer, i, 0); + }else if( sqlite3FitsIn64Bits(z) ){ + sqlite3VdbeOp3(v, OP_Int64, 0, 0, z, n); + }else{ + sqlite3VdbeOp3(v, OP_Real, 0, 0, z, n); + } +} + +/* +** Generate code into the current Vdbe to evaluate the given +** expression and leave the result on the top of stack. +** +** This code depends on the fact that certain token values (ex: TK_EQ) +** are the same as opcode values (ex: OP_Eq) that implement the corresponding +** operation. Special comments in vdbe.c and the mkopcodeh.awk script in +** the make process cause these values to align. Assert()s in the code +** below verify that the numbers are aligned correctly. +*/ +void sqlite3ExprCode(Parse *pParse, Expr *pExpr){ + Vdbe *v = pParse->pVdbe; + int op; + int stackChng = 1; /* Amount of change to stack depth */ + + if( v==0 ) return; + if( pExpr==0 ){ + sqlite3VdbeAddOp(v, OP_Null, 0, 0); + return; + } + op = pExpr->op; + switch( op ){ + case TK_AGG_COLUMN: { + AggInfo *pAggInfo = pExpr->pAggInfo; + struct AggInfo_col *pCol = &pAggInfo->aCol[pExpr->iAgg]; + if( !pAggInfo->directMode ){ + sqlite3VdbeAddOp(v, OP_MemLoad, pCol->iMem, 0); + break; + }else if( pAggInfo->useSortingIdx ){ + sqlite3VdbeAddOp(v, OP_Column, pAggInfo->sortingIdx, + pCol->iSorterColumn); + break; + } + /* Otherwise, fall thru into the TK_COLUMN case */ + } + case TK_COLUMN: { + if( pExpr->iTable<0 ){ + /* This only happens when coding check constraints */ + assert( pParse->ckOffset>0 ); + sqlite3VdbeAddOp(v, OP_Dup, pParse->ckOffset-pExpr->iColumn-1, 1); + }else if( pExpr->iColumn>=0 ){ + Table *pTab = pExpr->pTab; + int iCol = pExpr->iColumn; + sqlite3VdbeAddOp(v, OP_Column, pExpr->iTable, iCol); + sqlite3ColumnDefault(v, pTab, iCol); +#ifndef SQLITE_OMIT_FLOATING_POINT + if( pTab && pTab->aCol[iCol].affinity==SQLITE_AFF_REAL ){ + sqlite3VdbeAddOp(v, OP_RealAffinity, 0, 0); + } +#endif + }else{ + sqlite3VdbeAddOp(v, OP_Rowid, pExpr->iTable, 0); + } + break; + } + case TK_INTEGER: { + codeInteger(v, (char*)pExpr->token.z, pExpr->token.n); + break; + } + case TK_FLOAT: + case TK_STRING: { + assert( TK_FLOAT==OP_Real ); + assert( TK_STRING==OP_String8 ); + sqlite3DequoteExpr(pExpr); + sqlite3VdbeOp3(v, op, 0, 0, (char*)pExpr->token.z, pExpr->token.n); + break; + } + case TK_NULL: { + sqlite3VdbeAddOp(v, OP_Null, 0, 0); + break; + } +#ifndef SQLITE_OMIT_BLOB_LITERAL + case TK_BLOB: { + int n; + const char *z; + assert( TK_BLOB==OP_HexBlob ); + n = pExpr->token.n - 3; + z = (char*)pExpr->token.z + 2; + assert( n>=0 ); + if( n==0 ){ + z = ""; + } + sqlite3VdbeOp3(v, op, 0, 0, z, n); + break; + } +#endif + case TK_VARIABLE: { + sqlite3VdbeAddOp(v, OP_Variable, pExpr->iTable, 0); + if( pExpr->token.n>1 ){ + sqlite3VdbeChangeP3(v, -1, (char*)pExpr->token.z, pExpr->token.n); + } + break; + } + case TK_REGISTER: { + sqlite3VdbeAddOp(v, OP_MemLoad, pExpr->iTable, 0); + break; + } +#ifndef SQLITE_OMIT_CAST + case TK_CAST: { + /* Expressions of the form: CAST(pLeft AS token) */ + int aff, to_op; + sqlite3ExprCode(pParse, pExpr->pLeft); + aff = sqlite3AffinityType(&pExpr->token); + to_op = aff - SQLITE_AFF_TEXT + OP_ToText; + assert( to_op==OP_ToText || aff!=SQLITE_AFF_TEXT ); + assert( to_op==OP_ToBlob || aff!=SQLITE_AFF_NONE ); + assert( to_op==OP_ToNumeric || aff!=SQLITE_AFF_NUMERIC ); + assert( to_op==OP_ToInt || aff!=SQLITE_AFF_INTEGER ); + assert( to_op==OP_ToReal || aff!=SQLITE_AFF_REAL ); + sqlite3VdbeAddOp(v, to_op, 0, 0); + stackChng = 0; + break; + } +#endif /* SQLITE_OMIT_CAST */ + case TK_LT: + case TK_LE: + case TK_GT: + case TK_GE: + case TK_NE: + case TK_EQ: { + assert( TK_LT==OP_Lt ); + assert( TK_LE==OP_Le ); + assert( TK_GT==OP_Gt ); + assert( TK_GE==OP_Ge ); + assert( TK_EQ==OP_Eq ); + assert( TK_NE==OP_Ne ); + sqlite3ExprCode(pParse, pExpr->pLeft); + sqlite3ExprCode(pParse, pExpr->pRight); + codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, 0, 0); + stackChng = -1; + break; + } + case TK_AND: + case TK_OR: + case TK_PLUS: + case TK_STAR: + case TK_MINUS: + case TK_REM: + case TK_BITAND: + case TK_BITOR: + case TK_SLASH: + case TK_LSHIFT: + case TK_RSHIFT: + case TK_CONCAT: { + assert( TK_AND==OP_And ); + assert( TK_OR==OP_Or ); + assert( TK_PLUS==OP_Add ); + assert( TK_MINUS==OP_Subtract ); + assert( TK_REM==OP_Remainder ); + assert( TK_BITAND==OP_BitAnd ); + assert( TK_BITOR==OP_BitOr ); + assert( TK_SLASH==OP_Divide ); + assert( TK_LSHIFT==OP_ShiftLeft ); + assert( TK_RSHIFT==OP_ShiftRight ); + assert( TK_CONCAT==OP_Concat ); + sqlite3ExprCode(pParse, pExpr->pLeft); + sqlite3ExprCode(pParse, pExpr->pRight); + sqlite3VdbeAddOp(v, op, 0, 0); + stackChng = -1; + break; + } + case TK_UMINUS: { + Expr *pLeft = pExpr->pLeft; + assert( pLeft ); + if( pLeft->op==TK_FLOAT || pLeft->op==TK_INTEGER ){ + Token *p = &pLeft->token; + char *z = sqlite3MPrintf("-%.*s", p->n, p->z); + if( pLeft->op==TK_FLOAT ){ + sqlite3VdbeOp3(v, OP_Real, 0, 0, z, p->n+1); + }else{ + codeInteger(v, z, p->n+1); + } + sqliteFree(z); + break; + } + /* Fall through into TK_NOT */ + } + case TK_BITNOT: + case TK_NOT: { + assert( TK_BITNOT==OP_BitNot ); + assert( TK_NOT==OP_Not ); + sqlite3ExprCode(pParse, pExpr->pLeft); + sqlite3VdbeAddOp(v, op, 0, 0); + stackChng = 0; + break; + } + case TK_ISNULL: + case TK_NOTNULL: { + int dest; + assert( TK_ISNULL==OP_IsNull ); + assert( TK_NOTNULL==OP_NotNull ); + sqlite3VdbeAddOp(v, OP_Integer, 1, 0); + sqlite3ExprCode(pParse, pExpr->pLeft); + dest = sqlite3VdbeCurrentAddr(v) + 2; + sqlite3VdbeAddOp(v, op, 1, dest); + sqlite3VdbeAddOp(v, OP_AddImm, -1, 0); + stackChng = 0; + break; + } + case TK_AGG_FUNCTION: { + AggInfo *pInfo = pExpr->pAggInfo; + if( pInfo==0 ){ + sqlite3ErrorMsg(pParse, "misuse of aggregate: %T", + &pExpr->span); + }else{ + sqlite3VdbeAddOp(v, OP_MemLoad, pInfo->aFunc[pExpr->iAgg].iMem, 0); + } + break; + } + case TK_CONST_FUNC: + case TK_FUNCTION: { + ExprList *pList = pExpr->pList; + int nExpr = pList ? pList->nExpr : 0; + FuncDef *pDef; + int nId; + const char *zId; + int constMask = 0; + int i; + u8 enc = ENC(pParse->db); + CollSeq *pColl = 0; + zId = (char*)pExpr->token.z; + nId = pExpr->token.n; + pDef = sqlite3FindFunction(pParse->db, zId, nId, nExpr, enc, 0); + assert( pDef!=0 ); + nExpr = sqlite3ExprCodeExprList(pParse, pList); + for(i=0; ia[i].pExpr) ){ + constMask |= (1<needCollSeq && !pColl ){ + pColl = sqlite3ExprCollSeq(pParse, pList->a[i].pExpr); + } + } + if( pDef->needCollSeq ){ + if( !pColl ) pColl = pParse->db->pDfltColl; + sqlite3VdbeOp3(v, OP_CollSeq, 0, 0, (char *)pColl, P3_COLLSEQ); + } + sqlite3VdbeOp3(v, OP_Function, constMask, nExpr, (char*)pDef, P3_FUNCDEF); + stackChng = 1-nExpr; + break; + } +#ifndef SQLITE_OMIT_SUBQUERY + case TK_EXISTS: + case TK_SELECT: { + sqlite3CodeSubselect(pParse, pExpr); + sqlite3VdbeAddOp(v, OP_MemLoad, pExpr->iColumn, 0); + VdbeComment((v, "# load subquery result")); + break; + } + case TK_IN: { + int addr; + char affinity; + int ckOffset = pParse->ckOffset; + sqlite3CodeSubselect(pParse, pExpr); + + /* Figure out the affinity to use to create a key from the results + ** of the expression. affinityStr stores a static string suitable for + ** P3 of OP_MakeRecord. + */ + affinity = comparisonAffinity(pExpr); + + sqlite3VdbeAddOp(v, OP_Integer, 1, 0); + pParse->ckOffset = ckOffset+1; + + /* Code the from " IN (...)". The temporary table + ** pExpr->iTable contains the values that make up the (...) set. + */ + sqlite3ExprCode(pParse, pExpr->pLeft); + addr = sqlite3VdbeCurrentAddr(v); + sqlite3VdbeAddOp(v, OP_NotNull, -1, addr+4); /* addr + 0 */ + sqlite3VdbeAddOp(v, OP_Pop, 2, 0); + sqlite3VdbeAddOp(v, OP_Null, 0, 0); + sqlite3VdbeAddOp(v, OP_Goto, 0, addr+7); + sqlite3VdbeOp3(v, OP_MakeRecord, 1, 0, &affinity, 1); /* addr + 4 */ + sqlite3VdbeAddOp(v, OP_Found, pExpr->iTable, addr+7); + sqlite3VdbeAddOp(v, OP_AddImm, -1, 0); /* addr + 6 */ + + break; + } +#endif + case TK_BETWEEN: { + Expr *pLeft = pExpr->pLeft; + struct ExprList_item *pLItem = pExpr->pList->a; + Expr *pRight = pLItem->pExpr; + sqlite3ExprCode(pParse, pLeft); + sqlite3VdbeAddOp(v, OP_Dup, 0, 0); + sqlite3ExprCode(pParse, pRight); + codeCompare(pParse, pLeft, pRight, OP_Ge, 0, 0); + sqlite3VdbeAddOp(v, OP_Pull, 1, 0); + pLItem++; + pRight = pLItem->pExpr; + sqlite3ExprCode(pParse, pRight); + codeCompare(pParse, pLeft, pRight, OP_Le, 0, 0); + sqlite3VdbeAddOp(v, OP_And, 0, 0); + break; + } + case TK_UPLUS: + case TK_AS: { + sqlite3ExprCode(pParse, pExpr->pLeft); + stackChng = 0; + break; + } + case TK_CASE: { + int expr_end_label; + int jumpInst; + int nExpr; + int i; + ExprList *pEList; + struct ExprList_item *aListelem; + + assert(pExpr->pList); + assert((pExpr->pList->nExpr % 2) == 0); + assert(pExpr->pList->nExpr > 0); + pEList = pExpr->pList; + aListelem = pEList->a; + nExpr = pEList->nExpr; + expr_end_label = sqlite3VdbeMakeLabel(v); + if( pExpr->pLeft ){ + sqlite3ExprCode(pParse, pExpr->pLeft); + } + for(i=0; ipLeft ){ + sqlite3VdbeAddOp(v, OP_Dup, 1, 1); + jumpInst = codeCompare(pParse, pExpr->pLeft, aListelem[i].pExpr, + OP_Ne, 0, 1); + sqlite3VdbeAddOp(v, OP_Pop, 1, 0); + }else{ + jumpInst = sqlite3VdbeAddOp(v, OP_IfNot, 1, 0); + } + sqlite3ExprCode(pParse, aListelem[i+1].pExpr); + sqlite3VdbeAddOp(v, OP_Goto, 0, expr_end_label); + sqlite3VdbeJumpHere(v, jumpInst); + } + if( pExpr->pLeft ){ + sqlite3VdbeAddOp(v, OP_Pop, 1, 0); + } + if( pExpr->pRight ){ + sqlite3ExprCode(pParse, pExpr->pRight); + }else{ + sqlite3VdbeAddOp(v, OP_Null, 0, 0); + } + sqlite3VdbeResolveLabel(v, expr_end_label); + break; + } +#ifndef SQLITE_OMIT_TRIGGER + case TK_RAISE: { + if( !pParse->trigStack ){ + sqlite3ErrorMsg(pParse, + "RAISE() may only be used within a trigger-program"); + return; + } + if( pExpr->iColumn!=OE_Ignore ){ + assert( pExpr->iColumn==OE_Rollback || + pExpr->iColumn == OE_Abort || + pExpr->iColumn == OE_Fail ); + sqlite3DequoteExpr(pExpr); + sqlite3VdbeOp3(v, OP_Halt, SQLITE_CONSTRAINT, pExpr->iColumn, + (char*)pExpr->token.z, pExpr->token.n); + } else { + assert( pExpr->iColumn == OE_Ignore ); + sqlite3VdbeAddOp(v, OP_ContextPop, 0, 0); + sqlite3VdbeAddOp(v, OP_Goto, 0, pParse->trigStack->ignoreJump); + VdbeComment((v, "# raise(IGNORE)")); + } + stackChng = 0; + break; + } +#endif + } + + if( pParse->ckOffset ){ + pParse->ckOffset += stackChng; + assert( pParse->ckOffset ); + } +} + +#ifndef SQLITE_OMIT_TRIGGER +/* +** Generate code that evalutes the given expression and leaves the result +** on the stack. See also sqlite3ExprCode(). +** +** This routine might also cache the result and modify the pExpr tree +** so that it will make use of the cached result on subsequent evaluations +** rather than evaluate the whole expression again. Trivial expressions are +** not cached. If the expression is cached, its result is stored in a +** memory location. +*/ +void sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr){ + Vdbe *v = pParse->pVdbe; + int iMem; + int addr1, addr2; + if( v==0 ) return; + addr1 = sqlite3VdbeCurrentAddr(v); + sqlite3ExprCode(pParse, pExpr); + addr2 = sqlite3VdbeCurrentAddr(v); + if( addr2>addr1+1 || sqlite3VdbeGetOp(v, addr1)->opcode==OP_Function ){ + iMem = pExpr->iTable = pParse->nMem++; + sqlite3VdbeAddOp(v, OP_MemStore, iMem, 0); + pExpr->op = TK_REGISTER; + } +} +#endif + +/* +** Generate code that pushes the value of every element of the given +** expression list onto the stack. +** +** Return the number of elements pushed onto the stack. +*/ +int sqlite3ExprCodeExprList( + Parse *pParse, /* Parsing context */ + ExprList *pList /* The expression list to be coded */ +){ + struct ExprList_item *pItem; + int i, n; + if( pList==0 ) return 0; + n = pList->nExpr; + for(pItem=pList->a, i=n; i>0; i--, pItem++){ + sqlite3ExprCode(pParse, pItem->pExpr); + } + return n; +} + +/* +** Generate code for a boolean expression such that a jump is made +** to the label "dest" if the expression is true but execution +** continues straight thru if the expression is false. +** +** If the expression evaluates to NULL (neither true nor false), then +** take the jump if the jumpIfNull flag is true. +** +** This code depends on the fact that certain token values (ex: TK_EQ) +** are the same as opcode values (ex: OP_Eq) that implement the corresponding +** operation. Special comments in vdbe.c and the mkopcodeh.awk script in +** the make process cause these values to align. Assert()s in the code +** below verify that the numbers are aligned correctly. +*/ +void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ + Vdbe *v = pParse->pVdbe; + int op = 0; + int ckOffset = pParse->ckOffset; + if( v==0 || pExpr==0 ) return; + op = pExpr->op; + switch( op ){ + case TK_AND: { + int d2 = sqlite3VdbeMakeLabel(v); + sqlite3ExprIfFalse(pParse, pExpr->pLeft, d2, !jumpIfNull); + sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull); + sqlite3VdbeResolveLabel(v, d2); + break; + } + case TK_OR: { + sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull); + sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull); + break; + } + case TK_NOT: { + sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull); + break; + } + case TK_LT: + case TK_LE: + case TK_GT: + case TK_GE: + case TK_NE: + case TK_EQ: { + assert( TK_LT==OP_Lt ); + assert( TK_LE==OP_Le ); + assert( TK_GT==OP_Gt ); + assert( TK_GE==OP_Ge ); + assert( TK_EQ==OP_Eq ); + assert( TK_NE==OP_Ne ); + sqlite3ExprCode(pParse, pExpr->pLeft); + sqlite3ExprCode(pParse, pExpr->pRight); + codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, dest, jumpIfNull); + break; + } + case TK_ISNULL: + case TK_NOTNULL: { + assert( TK_ISNULL==OP_IsNull ); + assert( TK_NOTNULL==OP_NotNull ); + sqlite3ExprCode(pParse, pExpr->pLeft); + sqlite3VdbeAddOp(v, op, 1, dest); + break; + } + case TK_BETWEEN: { + /* The expression "x BETWEEN y AND z" is implemented as: + ** + ** 1 IF (x < y) GOTO 3 + ** 2 IF (x <= z) GOTO + ** 3 ... + */ + int addr; + Expr *pLeft = pExpr->pLeft; + Expr *pRight = pExpr->pList->a[0].pExpr; + sqlite3ExprCode(pParse, pLeft); + sqlite3VdbeAddOp(v, OP_Dup, 0, 0); + sqlite3ExprCode(pParse, pRight); + addr = codeCompare(pParse, pLeft, pRight, OP_Lt, 0, !jumpIfNull); + + pRight = pExpr->pList->a[1].pExpr; + sqlite3ExprCode(pParse, pRight); + codeCompare(pParse, pLeft, pRight, OP_Le, dest, jumpIfNull); + + sqlite3VdbeAddOp(v, OP_Integer, 0, 0); + sqlite3VdbeJumpHere(v, addr); + sqlite3VdbeAddOp(v, OP_Pop, 1, 0); + break; + } + default: { + sqlite3ExprCode(pParse, pExpr); + sqlite3VdbeAddOp(v, OP_If, jumpIfNull, dest); + break; + } + } + pParse->ckOffset = ckOffset; +} + +/* +** Generate code for a boolean expression such that a jump is made +** to the label "dest" if the expression is false but execution +** continues straight thru if the expression is true. +** +** If the expression evaluates to NULL (neither true nor false) then +** jump if jumpIfNull is true or fall through if jumpIfNull is false. +*/ +void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ + Vdbe *v = pParse->pVdbe; + int op = 0; + int ckOffset = pParse->ckOffset; + if( v==0 || pExpr==0 ) return; + + /* The value of pExpr->op and op are related as follows: + ** + ** pExpr->op op + ** --------- ---------- + ** TK_ISNULL OP_NotNull + ** TK_NOTNULL OP_IsNull + ** TK_NE OP_Eq + ** TK_EQ OP_Ne + ** TK_GT OP_Le + ** TK_LE OP_Gt + ** TK_GE OP_Lt + ** TK_LT OP_Ge + ** + ** For other values of pExpr->op, op is undefined and unused. + ** The value of TK_ and OP_ constants are arranged such that we + ** can compute the mapping above using the following expression. + ** Assert()s verify that the computation is correct. + */ + op = ((pExpr->op+(TK_ISNULL&1))^1)-(TK_ISNULL&1); + + /* Verify correct alignment of TK_ and OP_ constants + */ + assert( pExpr->op!=TK_ISNULL || op==OP_NotNull ); + assert( pExpr->op!=TK_NOTNULL || op==OP_IsNull ); + assert( pExpr->op!=TK_NE || op==OP_Eq ); + assert( pExpr->op!=TK_EQ || op==OP_Ne ); + assert( pExpr->op!=TK_LT || op==OP_Ge ); + assert( pExpr->op!=TK_LE || op==OP_Gt ); + assert( pExpr->op!=TK_GT || op==OP_Le ); + assert( pExpr->op!=TK_GE || op==OP_Lt ); + + switch( pExpr->op ){ + case TK_AND: { + sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull); + sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull); + break; + } + case TK_OR: { + int d2 = sqlite3VdbeMakeLabel(v); + sqlite3ExprIfTrue(pParse, pExpr->pLeft, d2, !jumpIfNull); + sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull); + sqlite3VdbeResolveLabel(v, d2); + break; + } + case TK_NOT: { + sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull); + break; + } + case TK_LT: + case TK_LE: + case TK_GT: + case TK_GE: + case TK_NE: + case TK_EQ: { + sqlite3ExprCode(pParse, pExpr->pLeft); + sqlite3ExprCode(pParse, pExpr->pRight); + codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, dest, jumpIfNull); + break; + } + case TK_ISNULL: + case TK_NOTNULL: { + sqlite3ExprCode(pParse, pExpr->pLeft); + sqlite3VdbeAddOp(v, op, 1, dest); + break; + } + case TK_BETWEEN: { + /* The expression is "x BETWEEN y AND z". It is implemented as: + ** + ** 1 IF (x >= y) GOTO 3 + ** 2 GOTO + ** 3 IF (x > z) GOTO + */ + int addr; + Expr *pLeft = pExpr->pLeft; + Expr *pRight = pExpr->pList->a[0].pExpr; + sqlite3ExprCode(pParse, pLeft); + sqlite3VdbeAddOp(v, OP_Dup, 0, 0); + sqlite3ExprCode(pParse, pRight); + addr = sqlite3VdbeCurrentAddr(v); + codeCompare(pParse, pLeft, pRight, OP_Ge, addr+3, !jumpIfNull); + + sqlite3VdbeAddOp(v, OP_Pop, 1, 0); + sqlite3VdbeAddOp(v, OP_Goto, 0, dest); + pRight = pExpr->pList->a[1].pExpr; + sqlite3ExprCode(pParse, pRight); + codeCompare(pParse, pLeft, pRight, OP_Gt, dest, jumpIfNull); + break; + } + default: { + sqlite3ExprCode(pParse, pExpr); + sqlite3VdbeAddOp(v, OP_IfNot, jumpIfNull, dest); + break; + } + } + pParse->ckOffset = ckOffset; +} + +/* +** Do a deep comparison of two expression trees. Return TRUE (non-zero) +** if they are identical and return FALSE if they differ in any way. +*/ +int sqlite3ExprCompare(Expr *pA, Expr *pB){ + int i; + if( pA==0||pB==0 ){ + return pB==pA; + } + if( pA->op!=pB->op ) return 0; + if( (pA->flags & EP_Distinct)!=(pB->flags & EP_Distinct) ) return 0; + if( !sqlite3ExprCompare(pA->pLeft, pB->pLeft) ) return 0; + if( !sqlite3ExprCompare(pA->pRight, pB->pRight) ) return 0; + if( pA->pList ){ + if( pB->pList==0 ) return 0; + if( pA->pList->nExpr!=pB->pList->nExpr ) return 0; + for(i=0; ipList->nExpr; i++){ + if( !sqlite3ExprCompare(pA->pList->a[i].pExpr, pB->pList->a[i].pExpr) ){ + return 0; + } + } + }else if( pB->pList ){ + return 0; + } + if( pA->pSelect || pB->pSelect ) return 0; + if( pA->iTable!=pB->iTable || pA->iColumn!=pB->iColumn ) return 0; + if( pA->token.z ){ + if( pB->token.z==0 ) return 0; + if( pB->token.n!=pA->token.n ) return 0; + if( sqlite3StrNICmp((char*)pA->token.z,(char*)pB->token.z,pB->token.n)!=0 ){ + return 0; + } + } + return 1; +} + + +/* +** Add a new element to the pAggInfo->aCol[] array. Return the index of +** the new element. Return a negative number if malloc fails. +*/ +static int addAggInfoColumn(AggInfo *pInfo){ + int i; + i = sqlite3ArrayAllocate((void**)&pInfo->aCol, sizeof(pInfo->aCol[0]), 3); + if( i<0 ){ + return -1; + } + return i; +} + +/* +** Add a new element to the pAggInfo->aFunc[] array. Return the index of +** the new element. Return a negative number if malloc fails. +*/ +static int addAggInfoFunc(AggInfo *pInfo){ + int i; + i = sqlite3ArrayAllocate((void**)&pInfo->aFunc, sizeof(pInfo->aFunc[0]), 2); + if( i<0 ){ + return -1; + } + return i; +} + +/* +** This is an xFunc for walkExprTree() used to implement +** sqlite3ExprAnalyzeAggregates(). See sqlite3ExprAnalyzeAggregates +** for additional information. +** +** This routine analyzes the aggregate function at pExpr. +*/ +static int analyzeAggregate(void *pArg, Expr *pExpr){ + int i; + NameContext *pNC = (NameContext *)pArg; + Parse *pParse = pNC->pParse; + SrcList *pSrcList = pNC->pSrcList; + AggInfo *pAggInfo = pNC->pAggInfo; + + + switch( pExpr->op ){ + case TK_COLUMN: { + /* Check to see if the column is in one of the tables in the FROM + ** clause of the aggregate query */ + if( pSrcList ){ + struct SrcList_item *pItem = pSrcList->a; + for(i=0; inSrc; i++, pItem++){ + struct AggInfo_col *pCol; + if( pExpr->iTable==pItem->iCursor ){ + /* If we reach this point, it means that pExpr refers to a table + ** that is in the FROM clause of the aggregate query. + ** + ** Make an entry for the column in pAggInfo->aCol[] if there + ** is not an entry there already. + */ + pCol = pAggInfo->aCol; + for(i=0; inColumn; i++, pCol++){ + if( pCol->iTable==pExpr->iTable && + pCol->iColumn==pExpr->iColumn ){ + break; + } + } + if( i>=pAggInfo->nColumn && (i = addAggInfoColumn(pAggInfo))>=0 ){ + pCol = &pAggInfo->aCol[i]; + pCol->iTable = pExpr->iTable; + pCol->iColumn = pExpr->iColumn; + pCol->iMem = pParse->nMem++; + pCol->iSorterColumn = -1; + pCol->pExpr = pExpr; + if( pAggInfo->pGroupBy ){ + int j, n; + ExprList *pGB = pAggInfo->pGroupBy; + struct ExprList_item *pTerm = pGB->a; + n = pGB->nExpr; + for(j=0; jpExpr; + if( pE->op==TK_COLUMN && pE->iTable==pExpr->iTable && + pE->iColumn==pExpr->iColumn ){ + pCol->iSorterColumn = j; + break; + } + } + } + if( pCol->iSorterColumn<0 ){ + pCol->iSorterColumn = pAggInfo->nSortingColumn++; + } + } + /* There is now an entry for pExpr in pAggInfo->aCol[] (either + ** because it was there before or because we just created it). + ** Convert the pExpr to be a TK_AGG_COLUMN referring to that + ** pAggInfo->aCol[] entry. + */ + pExpr->pAggInfo = pAggInfo; + pExpr->op = TK_AGG_COLUMN; + pExpr->iAgg = i; + break; + } /* endif pExpr->iTable==pItem->iCursor */ + } /* end loop over pSrcList */ + } + return 1; + } + case TK_AGG_FUNCTION: { + /* The pNC->nDepth==0 test causes aggregate functions in subqueries + ** to be ignored */ + if( pNC->nDepth==0 ){ + /* Check to see if pExpr is a duplicate of another aggregate + ** function that is already in the pAggInfo structure + */ + struct AggInfo_func *pItem = pAggInfo->aFunc; + for(i=0; inFunc; i++, pItem++){ + if( sqlite3ExprCompare(pItem->pExpr, pExpr) ){ + break; + } + } + if( i>=pAggInfo->nFunc ){ + /* pExpr is original. Make a new entry in pAggInfo->aFunc[] + */ + u8 enc = ENC(pParse->db); + i = addAggInfoFunc(pAggInfo); + if( i>=0 ){ + pItem = &pAggInfo->aFunc[i]; + pItem->pExpr = pExpr; + pItem->iMem = pParse->nMem++; + pItem->pFunc = sqlite3FindFunction(pParse->db, + (char*)pExpr->token.z, pExpr->token.n, + pExpr->pList ? pExpr->pList->nExpr : 0, enc, 0); + if( pExpr->flags & EP_Distinct ){ + pItem->iDistinct = pParse->nTab++; + }else{ + pItem->iDistinct = -1; + } + } + } + /* Make pExpr point to the appropriate pAggInfo->aFunc[] entry + */ + pExpr->iAgg = i; + pExpr->pAggInfo = pAggInfo; + return 1; + } + } + } + + /* 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 ){ + pNC->nDepth++; + walkSelectExpr(pExpr->pSelect, analyzeAggregate, pNC); + pNC->nDepth--; + } + return 0; +} + +/* +** Analyze the given expression looking for aggregate functions and +** for variables that need to be added to the pParse->aAgg[] array. +** Make additional entries to the pParse->aAgg[] array as necessary. +** +** This routine should only be called after the expression has been +** analyzed by sqlite3ExprResolveNames(). +** +** If errors are seen, leave an error message in zErrMsg and return +** the number of errors. +*/ +int sqlite3ExprAnalyzeAggregates(NameContext *pNC, Expr *pExpr){ + int nErr = pNC->pParse->nErr; + walkExprTree(pExpr, analyzeAggregate, pNC); + return pNC->pParse->nErr - nErr; +} + +/* +** Call sqlite3ExprAnalyzeAggregates() for every expression in an +** expression list. Return the number of errors. +** +** If an error is found, the analysis is cut short. +*/ +int sqlite3ExprAnalyzeAggList(NameContext *pNC, ExprList *pList){ + struct ExprList_item *pItem; + int i; + int nErr = 0; + if( pList ){ + for(pItem=pList->a, i=0; nErr==0 && inExpr; i++, pItem++){ + nErr += sqlite3ExprAnalyzeAggregates(pNC, pItem->pExpr); + } + } + return nErr; +} Added: external/sqlite-source-3.3.4/func.c ============================================================================== --- (empty file) +++ external/sqlite-source-3.3.4/func.c Mon Apr 3 07:54:59 2006 @@ -0,0 +1,1137 @@ +/* +** 2002 February 23 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains the C functions that implement various SQL +** functions of SQLite. +** +** There is only one exported symbol in this file - the function +** sqliteRegisterBuildinFunctions() found at the bottom of the file. +** All other code has file scope. +** +** $Id: func.c,v 1.121 2006/02/09 22:13:42 drh Exp $ +*/ +#include "sqliteInt.h" +#include +/* #include */ +#include +#include +#include "vdbeInt.h" +#include "os.h" + +/* +** Return the collating function associated with a function. +*/ +static CollSeq *sqlite3GetFuncCollSeq(sqlite3_context *context){ + return context->pColl; +} + +/* +** Implementation of the non-aggregate min() and max() functions +*/ +static void minmaxFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + int i; + int mask; /* 0 for min() or 0xffffffff for max() */ + int iBest; + CollSeq *pColl; + + if( argc==0 ) return; + mask = sqlite3_user_data(context)==0 ? 0 : -1; + pColl = sqlite3GetFuncCollSeq(context); + assert( pColl ); + assert( mask==-1 || mask==0 ); + iBest = 0; + if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; + for(i=1; i=0 ){ + iBest = i; + } + } + sqlite3_result_value(context, argv[iBest]); +} + +/* +** Return the type of the argument. +*/ +static void typeofFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + const char *z = 0; + switch( sqlite3_value_type(argv[0]) ){ + case SQLITE_NULL: z = "null"; break; + case SQLITE_INTEGER: z = "integer"; break; + case SQLITE_TEXT: z = "text"; break; + case SQLITE_FLOAT: z = "real"; break; + case SQLITE_BLOB: z = "blob"; break; + } + sqlite3_result_text(context, z, -1, SQLITE_STATIC); +} + + +/* +** Implementation of the length() function +*/ +static void lengthFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + int len; + + assert( argc==1 ); + switch( sqlite3_value_type(argv[0]) ){ + case SQLITE_BLOB: + case SQLITE_INTEGER: + case SQLITE_FLOAT: { + sqlite3_result_int(context, sqlite3_value_bytes(argv[0])); + break; + } + case SQLITE_TEXT: { + const unsigned char *z = sqlite3_value_text(argv[0]); + for(len=0; *z; z++){ if( (0xc0&*z)!=0x80 ) len++; } + sqlite3_result_int(context, len); + break; + } + default: { + sqlite3_result_null(context); + break; + } + } +} + +/* +** Implementation of the abs() function +*/ +static void absFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ + assert( argc==1 ); + switch( sqlite3_value_type(argv[0]) ){ + case SQLITE_INTEGER: { + i64 iVal = sqlite3_value_int64(argv[0]); + if( iVal<0 ) iVal = iVal * -1; + sqlite3_result_int64(context, iVal); + break; + } + case SQLITE_NULL: { + sqlite3_result_null(context); + break; + } + default: { + double rVal = sqlite3_value_double(argv[0]); + if( rVal<0 ) rVal = rVal * -1.0; + sqlite3_result_double(context, rVal); + break; + } + } +} + +/* +** Implementation of the substr() function +*/ +static void substrFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + const unsigned char *z; + const unsigned char *z2; + int i; + int p1, p2, len; + + assert( argc==3 ); + z = sqlite3_value_text(argv[0]); + if( z==0 ) return; + p1 = sqlite3_value_int(argv[1]); + p2 = sqlite3_value_int(argv[2]); + for(len=0, z2=z; *z2; z2++){ if( (0xc0&*z2)!=0x80 ) len++; } + if( p1<0 ){ + p1 += len; + if( p1<0 ){ + p2 += p1; + p1 = 0; + } + }else if( p1>0 ){ + p1--; + } + if( p1+p2>len ){ + p2 = len-p1; + } + for(i=0; i30 ) n = 30; + if( n<0 ) n = 0; + } + if( SQLITE_NULL==sqlite3_value_type(argv[0]) ) return; + r = sqlite3_value_double(argv[0]); + sqlite3_snprintf(sizeof(zBuf),zBuf,"%.*f",n,r); + sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); +} + +/* +** Implementation of the upper() and lower() SQL functions. +*/ +static void upperFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ + unsigned char *z; + int i; + if( argc<1 || SQLITE_NULL==sqlite3_value_type(argv[0]) ) return; + z = sqliteMalloc(sqlite3_value_bytes(argv[0])+1); + if( z==0 ) return; + strcpy((char*)z, (char*)sqlite3_value_text(argv[0])); + for(i=0; z[i]; i++){ + z[i] = toupper(z[i]); + } + sqlite3_result_text(context, (char*)z, -1, SQLITE_TRANSIENT); + sqliteFree(z); +} +static void lowerFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ + unsigned char *z; + int i; + if( argc<1 || SQLITE_NULL==sqlite3_value_type(argv[0]) ) return; + z = sqliteMalloc(sqlite3_value_bytes(argv[0])+1); + if( z==0 ) return; + strcpy((char*)z, (char*)sqlite3_value_text(argv[0])); + for(i=0; z[i]; i++){ + z[i] = tolower(z[i]); + } + sqlite3_result_text(context, (char*)z, -1, SQLITE_TRANSIENT); + sqliteFree(z); +} + +/* +** Implementation of the IFNULL(), NVL(), and COALESCE() functions. +** All three do the same thing. They return the first non-NULL +** argument. +*/ +static void ifnullFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + int i; + for(i=0; imatchOne; + u8 matchAll = pInfo->matchAll; + u8 matchSet = pInfo->matchSet; + u8 noCase = pInfo->noCase; + int prevEscape = 0; /* True if the previous character was 'escape' */ + + while( (c = *zPattern)!=0 ){ + if( !prevEscape && c==matchAll ){ + while( (c=zPattern[1]) == matchAll || c == matchOne ){ + if( c==matchOne ){ + if( *zString==0 ) return 0; + sqliteNextChar(zString); + } + zPattern++; + } + if( c && esc && sqlite3ReadUtf8(&zPattern[1])==esc ){ + u8 const *zTemp = &zPattern[1]; + sqliteNextChar(zTemp); + c = *zTemp; + } + if( c==0 ) return 1; + if( c==matchSet ){ + assert( esc==0 ); /* This is GLOB, not LIKE */ + while( *zString && patternCompare(&zPattern[1],zString,pInfo,esc)==0 ){ + sqliteNextChar(zString); + } + return *zString!=0; + }else{ + while( (c2 = *zString)!=0 ){ + if( noCase ){ + c2 = sqlite3UpperToLower[c2]; + c = sqlite3UpperToLower[c]; + while( c2 != 0 && c2 != c ){ c2 = sqlite3UpperToLower[*++zString]; } + }else{ + while( c2 != 0 && c2 != c ){ c2 = *++zString; } + } + if( c2==0 ) return 0; + if( patternCompare(&zPattern[1],zString,pInfo,esc) ) return 1; + sqliteNextChar(zString); + } + return 0; + } + }else if( !prevEscape && c==matchOne ){ + if( *zString==0 ) return 0; + sqliteNextChar(zString); + zPattern++; + }else if( c==matchSet ){ + int prior_c = 0; + assert( esc==0 ); /* This only occurs for GLOB, not LIKE */ + seen = 0; + invert = 0; + c = sqliteCharVal(zString); + if( c==0 ) return 0; + c2 = *++zPattern; + if( c2=='^' ){ invert = 1; c2 = *++zPattern; } + if( c2==']' ){ + if( c==']' ) seen = 1; + c2 = *++zPattern; + } + while( (c2 = sqliteCharVal(zPattern))!=0 && c2!=']' ){ + if( c2=='-' && zPattern[1]!=']' && zPattern[1]!=0 && prior_c>0 ){ + zPattern++; + c2 = sqliteCharVal(zPattern); + if( c>=prior_c && c<=c2 ) seen = 1; + prior_c = 0; + }else if( c==c2 ){ + seen = 1; + prior_c = c2; + }else{ + prior_c = c2; + } + sqliteNextChar(zPattern); + } + if( c2==0 || (seen ^ invert)==0 ) return 0; + sqliteNextChar(zString); + zPattern++; + }else if( esc && !prevEscape && sqlite3ReadUtf8(zPattern)==esc){ + prevEscape = 1; + sqliteNextChar(zPattern); + }else{ + if( noCase ){ + if( sqlite3UpperToLower[c] != sqlite3UpperToLower[*zString] ) return 0; + }else{ + if( c != *zString ) return 0; + } + zPattern++; + zString++; + prevEscape = 0; + } + } + return *zString==0; +} + +/* +** Count the number of times that the LIKE operator (or GLOB which is +** just a variation of LIKE) gets called. This is used for testing +** only. +*/ +#ifdef SQLITE_TEST +int sqlite3_like_count = 0; +#endif + + +/* +** Implementation of the like() SQL function. This function implements +** the build-in LIKE operator. The first argument to the function is the +** pattern and the second argument is the string. So, the SQL statements: +** +** A LIKE B +** +** is implemented as like(B,A). +** +** This same function (with a different compareInfo structure) computes +** the GLOB operator. +*/ +static void likeFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + const unsigned char *zA = sqlite3_value_text(argv[0]); + const unsigned char *zB = sqlite3_value_text(argv[1]); + int escape = 0; + if( argc==3 ){ + /* The escape character string must consist of a single UTF-8 character. + ** Otherwise, return an error. + */ + const unsigned char *zEsc = sqlite3_value_text(argv[2]); + if( sqlite3utf8CharLen((char*)zEsc, -1)!=1 ){ + sqlite3_result_error(context, + "ESCAPE expression must be a single character", -1); + return; + } + escape = sqlite3ReadUtf8(zEsc); + } + if( zA && zB ){ + struct compareInfo *pInfo = sqlite3_user_data(context); +#ifdef SQLITE_TEST + sqlite3_like_count++; +#endif + sqlite3_result_int(context, patternCompare(zA, zB, pInfo, escape)); + } +} + +/* +** Implementation of the NULLIF(x,y) function. The result is the first +** argument if the arguments are different. The result is NULL if the +** arguments are equal to each other. +*/ +static void nullifFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + CollSeq *pColl = sqlite3GetFuncCollSeq(context); + if( sqlite3MemCompare(argv[0], argv[1], pColl)!=0 ){ + sqlite3_result_value(context, argv[0]); + } +} + +/* +** Implementation of the VERSION(*) function. The result is the version +** of the SQLite library that is running. +*/ +static void versionFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + sqlite3_result_text(context, sqlite3_version, -1, SQLITE_STATIC); +} + + +/* +** EXPERIMENTAL - This is not an official function. The interface may +** change. This function may disappear. Do not write code that depends +** on this function. +** +** Implementation of the QUOTE() function. This function takes a single +** argument. If the argument is numeric, the return value is the same as +** the argument. If the argument is NULL, the return value is the string +** "NULL". Otherwise, the argument is enclosed in single quotes with +** single-quote escapes. +*/ +static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ + if( argc<1 ) return; + switch( sqlite3_value_type(argv[0]) ){ + case SQLITE_NULL: { + sqlite3_result_text(context, "NULL", 4, SQLITE_STATIC); + break; + } + case SQLITE_INTEGER: + case SQLITE_FLOAT: { + sqlite3_result_value(context, argv[0]); + break; + } + case SQLITE_BLOB: { + static const char hexdigits[] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + char *zText = 0; + int nBlob = sqlite3_value_bytes(argv[0]); + char const *zBlob = sqlite3_value_blob(argv[0]); + + zText = (char *)sqliteMalloc((2*nBlob)+4); + if( !zText ){ + sqlite3_result_error(context, "out of memory", -1); + }else{ + int i; + for(i=0; i>4)&0x0F]; + zText[(i*2)+3] = hexdigits[(zBlob[i])&0x0F]; + } + zText[(nBlob*2)+2] = '\''; + zText[(nBlob*2)+3] = '\0'; + zText[0] = 'X'; + zText[1] = '\''; + sqlite3_result_text(context, zText, -1, SQLITE_TRANSIENT); + sqliteFree(zText); + } + break; + } + case SQLITE_TEXT: { + int i,j,n; + const unsigned char *zArg = sqlite3_value_text(argv[0]); + char *z; + + for(i=n=0; zArg[i]; i++){ if( zArg[i]=='\'' ) n++; } + z = sqliteMalloc( i+n+3 ); + if( z==0 ) return; + z[0] = '\''; + for(i=0, j=1; zArg[i]; i++){ + z[j++] = zArg[i]; + if( zArg[i]=='\'' ){ + z[j++] = '\''; + } + } + z[j++] = '\''; + z[j] = 0; + sqlite3_result_text(context, z, j, SQLITE_TRANSIENT); + sqliteFree(z); + } + } +} + +#ifdef SQLITE_SOUNDEX +/* +** Compute the soundex encoding of a word. +*/ +static void soundexFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ + char zResult[8]; + const u8 *zIn; + int i, j; + static const unsigned char iCode[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0, + 1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0, + 0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0, + 1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0, + }; + assert( argc==1 ); + zIn = (u8*)sqlite3_value_text(argv[0]); + for(i=0; zIn[i] && !isalpha(zIn[i]); i++){} + if( zIn[i] ){ + zResult[0] = toupper(zIn[i]); + for(j=1; j<4 && zIn[i]; i++){ + int code = iCode[zIn[i]&0x7f]; + if( code>0 ){ + zResult[j++] = code + '0'; + } + } + while( j<4 ){ + zResult[j++] = '0'; + } + zResult[j] = 0; + sqlite3_result_text(context, zResult, 4, SQLITE_TRANSIENT); + }else{ + sqlite3_result_text(context, "?000", 4, SQLITE_STATIC); + } +} +#endif + +#ifdef SQLITE_TEST +/* +** This function generates a string of random characters. Used for +** generating test data. +*/ +static void randStr(sqlite3_context *context, int argc, sqlite3_value **argv){ + static const unsigned char zSrc[] = + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" + ".-!,:*^+=_|?/<> "; + int iMin, iMax, n, r, i; + unsigned char zBuf[1000]; + if( argc>=1 ){ + iMin = sqlite3_value_int(argv[0]); + if( iMin<0 ) iMin = 0; + if( iMin>=sizeof(zBuf) ) iMin = sizeof(zBuf)-1; + }else{ + iMin = 1; + } + if( argc>=2 ){ + iMax = sqlite3_value_int(argv[1]); + if( iMax=sizeof(zBuf) ) iMax = sizeof(zBuf)-1; + }else{ + iMax = 50; + } + n = iMin; + if( iMax>iMin ){ + sqlite3Randomness(sizeof(r), &r); + r &= 0x7fffffff; + n += r%(iMax + 1 - iMin); + } + assert( ncnt++; + if( type==SQLITE_INTEGER ){ + p->sum += sqlite3_value_int64(argv[0]); + if( !p->approx ){ + i64 iVal; + p->approx = p->sum!=(LONGDOUBLE_TYPE)(iVal = (i64)p->sum); + } + }else{ + p->sum += sqlite3_value_double(argv[0]); + p->approx = 1; + } + } +} +static void sumFinalize(sqlite3_context *context){ + SumCtx *p; + p = sqlite3_aggregate_context(context, 0); + if( p && p->cnt>0 ){ + if( p->approx ){ + sqlite3_result_double(context, p->sum); + }else{ + sqlite3_result_int64(context, (i64)p->sum); + } + } +} +static void avgFinalize(sqlite3_context *context){ + SumCtx *p; + p = sqlite3_aggregate_context(context, 0); + if( p && p->cnt>0 ){ + sqlite3_result_double(context, p->sum/(double)p->cnt); + } +} +static void totalFinalize(sqlite3_context *context){ + SumCtx *p; + p = sqlite3_aggregate_context(context, 0); + sqlite3_result_double(context, p ? p->sum : 0.0); +} + +/* +** The following structure keeps track of state information for the +** count() aggregate function. +*/ +typedef struct CountCtx CountCtx; +struct CountCtx { + i64 n; +}; + +/* +** Routines to implement the count() aggregate function. +*/ +static void countStep(sqlite3_context *context, int argc, sqlite3_value **argv){ + CountCtx *p; + p = sqlite3_aggregate_context(context, sizeof(*p)); + if( (argc==0 || SQLITE_NULL!=sqlite3_value_type(argv[0])) && p ){ + p->n++; + } +} +static void countFinalize(sqlite3_context *context){ + CountCtx *p; + p = sqlite3_aggregate_context(context, 0); + sqlite3_result_int64(context, p ? p->n : 0); +} + +/* +** Routines to implement min() and max() aggregate functions. +*/ +static void minmaxStep(sqlite3_context *context, int argc, sqlite3_value **argv){ + Mem *pArg = (Mem *)argv[0]; + Mem *pBest; + + if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; + pBest = (Mem *)sqlite3_aggregate_context(context, sizeof(*pBest)); + if( !pBest ) return; + + if( pBest->flags ){ + int max; + int cmp; + CollSeq *pColl = sqlite3GetFuncCollSeq(context); + /* This step function is used for both the min() and max() aggregates, + ** the only difference between the two being that the sense of the + ** comparison is inverted. For the max() aggregate, the + ** sqlite3_user_data() function returns (void *)-1. For min() it + ** returns (void *)db, where db is the sqlite3* database pointer. + ** Therefore the next statement sets variable 'max' to 1 for the max() + ** aggregate, or 0 for min(). + */ + max = ((sqlite3_user_data(context)==(void *)-1)?1:0); + cmp = sqlite3MemCompare(pBest, pArg, pColl); + if( (max && cmp<0) || (!max && cmp>0) ){ + sqlite3VdbeMemCopy(pBest, pArg); + } + }else{ + sqlite3VdbeMemCopy(pBest, pArg); + } +} +static void minMaxFinalize(sqlite3_context *context){ + sqlite3_value *pRes; + pRes = (sqlite3_value *)sqlite3_aggregate_context(context, 0); + if( pRes ){ + if( pRes->flags ){ + sqlite3_result_value(context, pRes); + } + sqlite3VdbeMemRelease(pRes); + } +} + + +/* +** This function registered all of the above C functions as SQL +** functions. This should be the only routine in this file with +** external linkage. +*/ +void sqlite3RegisterBuiltinFunctions(sqlite3 *db){ + static const struct { + char *zName; + signed char nArg; + u8 argType; /* 0: none. 1: db 2: (-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, 2, SQLITE_UTF8, 1, minmaxFunc }, + { "max", 0, 2, SQLITE_UTF8, 1, 0 }, + { "typeof", 1, 0, SQLITE_UTF8, 0, typeofFunc }, + { "length", 1, 0, SQLITE_UTF8, 0, lengthFunc }, + { "substr", 3, 0, SQLITE_UTF8, 0, substrFunc }, +#ifndef SQLITE_OMIT_UTF16 + { "substr", 3, 0, SQLITE_UTF16LE, 0, sqlite3utf16Substr }, +#endif + { "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 }, + { "ifnull", 2, 0, SQLITE_UTF8, 1, ifnullFunc }, + { "random", -1, 0, SQLITE_UTF8, 0, randomFunc }, + { "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, 1, SQLITE_UTF8, 0, last_insert_rowid }, + { "changes", 0, 1, SQLITE_UTF8, 0, changes }, + { "total_changes", 0, 1, SQLITE_UTF8, 0, total_changes }, +#ifdef SQLITE_SOUNDEX + { "soundex", 1, 0, SQLITE_UTF8, 0, soundexFunc}, +#endif +#ifdef SQLITE_TEST + { "randstr", 2, 0, SQLITE_UTF8, 0, randStr }, + { "test_destructor", 1, 1, SQLITE_UTF8, 0, test_destructor}, + { "test_destructor_count", 0, 0, SQLITE_UTF8, 0, test_destructor_count}, + { "test_auxdata", -1, 0, SQLITE_UTF8, 0, test_auxdata}, + { "test_error", 1, 0, SQLITE_UTF8, 0, test_error}, +#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, 2, 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 }, + }; + int i; + + for(i=0; ineedCollSeq = 1; + } + } + } +#ifndef SQLITE_OMIT_ALTERTABLE + sqlite3AlterFunctions(db); +#endif +#ifndef SQLITE_OMIT_PARSER + sqlite3AttachFunctions(db); +#endif + for(i=0; ineedCollSeq = 1; + } + } + } + sqlite3RegisterDateTimeFunctions(db); +#ifdef SQLITE_SSE + sqlite3SseFunctions(db); +#endif +#ifdef SQLITE_CASE_SENSITIVE_LIKE + sqlite3RegisterLikeFunctions(db, 1); +#else + sqlite3RegisterLikeFunctions(db, 0); +#endif +} + +/* +** Set the LIKEOPT flag on the 2-argument function with the given name. +*/ +static void setLikeOptFlag(sqlite3 *db, const char *zName, int flagVal){ + FuncDef *pDef; + pDef = sqlite3FindFunction(db, zName, strlen(zName), 2, SQLITE_UTF8, 0); + if( pDef ){ + pDef->flags = flagVal; + } +} + +/* +** Register the built-in LIKE and GLOB functions. The caseSensitive +** parameter determines whether or not the LIKE operator is case +** sensitive. GLOB is always case sensitive. +*/ +void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){ + struct compareInfo *pInfo; + if( caseSensitive ){ + pInfo = (struct compareInfo*)&likeInfoAlt; + }else{ + pInfo = (struct compareInfo*)&likeInfoNorm; + } + sqlite3CreateFunc(db, "like", 2, SQLITE_UTF8, pInfo, likeFunc, 0, 0); + sqlite3CreateFunc(db, "like", 3, SQLITE_UTF8, pInfo, likeFunc, 0, 0); + sqlite3CreateFunc(db, "glob", 2, SQLITE_UTF8, + (struct compareInfo*)&globInfo, likeFunc, 0,0); + setLikeOptFlag(db, "glob", SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE); + setLikeOptFlag(db, "like", + caseSensitive ? (SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE) : SQLITE_FUNC_LIKE); +} + +/* +** pExpr points to an expression which implements a function. If +** it is appropriate to apply the LIKE optimization to that function +** then set aWc[0] through aWc[2] to the wildcard characters and +** return TRUE. If the function is not a LIKE-style function then +** return FALSE. +*/ +int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){ + FuncDef *pDef; + if( pExpr->op!=TK_FUNCTION ){ + return 0; + } + if( pExpr->pList->nExpr!=2 ){ + return 0; + } + pDef = sqlite3FindFunction(db, (char*)pExpr->token.z, pExpr->token.n, 2, + SQLITE_UTF8, 0); + if( pDef==0 || (pDef->flags & SQLITE_FUNC_LIKE)==0 ){ + return 0; + } + + /* The memcpy() statement assumes that the wildcard characters are + ** the first three statements in the compareInfo structure. The + ** asserts() that follow verify that assumption + */ + memcpy(aWc, pDef->pUserData, 3); + assert( (char*)&likeInfoAlt == (char*)&likeInfoAlt.matchAll ); + assert( &((char*)&likeInfoAlt)[1] == (char*)&likeInfoAlt.matchOne ); + assert( &((char*)&likeInfoAlt)[2] == (char*)&likeInfoAlt.matchSet ); + *pIsNocase = (pDef->flags & SQLITE_FUNC_CASE)==0; + return 1; +} Added: external/sqlite-source-3.3.4/hash.c ============================================================================== --- (empty file) +++ external/sqlite-source-3.3.4/hash.c Mon Apr 3 07:54:59 2006 @@ -0,0 +1,392 @@ +/* +** 2001 September 22 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This is the implementation of generic hash-tables +** used in SQLite. +** +** $Id: hash.c,v 1.17 2005/10/03 15:11:09 drh Exp $ +*/ +#include "sqliteInt.h" +#include + +/* Turn bulk memory into a hash table object by initializing the +** fields of the Hash structure. +** +** "pNew" is a pointer to the hash table that is to be initialized. +** keyClass is one of the constants SQLITE_HASH_INT, SQLITE_HASH_POINTER, +** SQLITE_HASH_BINARY, or SQLITE_HASH_STRING. The value of keyClass +** determines what kind of key the hash table will use. "copyKey" is +** true if the hash table should make its own private copy of keys and +** false if it should just use the supplied pointer. CopyKey only makes +** sense for SQLITE_HASH_STRING and SQLITE_HASH_BINARY and is ignored +** for other key classes. +*/ +void sqlite3HashInit(Hash *pNew, int keyClass, int copyKey){ + assert( pNew!=0 ); + assert( keyClass>=SQLITE_HASH_STRING && keyClass<=SQLITE_HASH_BINARY ); + pNew->keyClass = keyClass; +#if 0 + if( keyClass==SQLITE_HASH_POINTER || keyClass==SQLITE_HASH_INT ) copyKey = 0; +#endif + pNew->copyKey = copyKey; + pNew->first = 0; + pNew->count = 0; + pNew->htsize = 0; + pNew->ht = 0; +} + +/* Remove all entries from a hash table. Reclaim all memory. +** Call this routine to delete a hash table or to reset a hash table +** to the empty state. +*/ +void sqlite3HashClear(Hash *pH){ + HashElem *elem; /* For looping over all elements of the table */ + + assert( pH!=0 ); + elem = pH->first; + pH->first = 0; + if( pH->ht ) sqliteFree(pH->ht); + pH->ht = 0; + pH->htsize = 0; + while( elem ){ + HashElem *next_elem = elem->next; + if( pH->copyKey && elem->pKey ){ + sqliteFree(elem->pKey); + } + sqliteFree(elem); + elem = next_elem; + } + pH->count = 0; +} + +#if 0 /* NOT USED */ +/* +** Hash and comparison functions when the mode is SQLITE_HASH_INT +*/ +static int intHash(const void *pKey, int nKey){ + return nKey ^ (nKey<<8) ^ (nKey>>8); +} +static int intCompare(const void *pKey1, int n1, const void *pKey2, int n2){ + return n2 - n1; +} +#endif + +#if 0 /* NOT USED */ +/* +** Hash and comparison functions when the mode is SQLITE_HASH_POINTER +*/ +static int ptrHash(const void *pKey, int nKey){ + uptr x = Addr(pKey); + return x ^ (x<<8) ^ (x>>8); +} +static int ptrCompare(const void *pKey1, int n1, const void *pKey2, int n2){ + if( pKey1==pKey2 ) return 0; + if( pKey1 0 ){ + h = (h<<3) ^ h ^ sqlite3UpperToLower[(unsigned char)*z++]; + nKey--; + } + return h & 0x7fffffff; +} +static int strCompare(const void *pKey1, int n1, const void *pKey2, int n2){ + if( n1!=n2 ) return 1; + return sqlite3StrNICmp((const char*)pKey1,(const char*)pKey2,n1); +} + +/* +** Hash and comparison functions when the mode is SQLITE_HASH_BINARY +*/ +static int binHash(const void *pKey, int nKey){ + int h = 0; + const char *z = (const char *)pKey; + while( nKey-- > 0 ){ + h = (h<<3) ^ h ^ *(z++); + } + return h & 0x7fffffff; +} +static int binCompare(const void *pKey1, int n1, const void *pKey2, int n2){ + if( n1!=n2 ) return 1; + return memcmp(pKey1,pKey2,n1); +} + +/* +** Return a pointer to the appropriate hash function given the key class. +** +** The C syntax in this function definition may be unfamilar to some +** programmers, so we provide the following additional explanation: +** +** The name of the function is "hashFunction". The function takes a +** single parameter "keyClass". The return value of hashFunction() +** is a pointer to another function. Specifically, the return value +** of hashFunction() is a pointer to a function that takes two parameters +** with types "const void*" and "int" and returns an "int". +*/ +static int (*hashFunction(int keyClass))(const void*,int){ +#if 0 /* HASH_INT and HASH_POINTER are never used */ + switch( keyClass ){ + case SQLITE_HASH_INT: return &intHash; + case SQLITE_HASH_POINTER: return &ptrHash; + case SQLITE_HASH_STRING: return &strHash; + case SQLITE_HASH_BINARY: return &binHash;; + default: break; + } + return 0; +#else + if( keyClass==SQLITE_HASH_STRING ){ + return &strHash; + }else{ + assert( keyClass==SQLITE_HASH_BINARY ); + return &binHash; + } +#endif +} + +/* +** Return a pointer to the appropriate hash function given the key class. +** +** For help in interpreted the obscure C code in the function definition, +** see the header comment on the previous function. +*/ +static int (*compareFunction(int keyClass))(const void*,int,const void*,int){ +#if 0 /* HASH_INT and HASH_POINTER are never used */ + switch( keyClass ){ + case SQLITE_HASH_INT: return &intCompare; + case SQLITE_HASH_POINTER: return &ptrCompare; + case SQLITE_HASH_STRING: return &strCompare; + case SQLITE_HASH_BINARY: return &binCompare; + default: break; + } + return 0; +#else + if( keyClass==SQLITE_HASH_STRING ){ + return &strCompare; + }else{ + assert( keyClass==SQLITE_HASH_BINARY ); + return &binCompare; + } +#endif +} + +/* Link an element into the hash table +*/ +static void insertElement( + Hash *pH, /* The complete hash table */ + struct _ht *pEntry, /* The entry into which pNew is inserted */ + HashElem *pNew /* The element to be inserted */ +){ + HashElem *pHead; /* First element already in pEntry */ + pHead = pEntry->chain; + if( pHead ){ + pNew->next = pHead; + pNew->prev = pHead->prev; + if( pHead->prev ){ pHead->prev->next = pNew; } + else { pH->first = pNew; } + pHead->prev = pNew; + }else{ + pNew->next = pH->first; + if( pH->first ){ pH->first->prev = pNew; } + pNew->prev = 0; + pH->first = pNew; + } + pEntry->count++; + pEntry->chain = pNew; +} + + +/* Resize the hash table so that it cantains "new_size" buckets. +** "new_size" must be a power of 2. The hash table might fail +** to resize if sqliteMalloc() fails. +*/ +static void rehash(Hash *pH, int new_size){ + struct _ht *new_ht; /* The new hash table */ + HashElem *elem, *next_elem; /* For looping over existing elements */ + int (*xHash)(const void*,int); /* The hash function */ + + assert( (new_size & (new_size-1))==0 ); + new_ht = (struct _ht *)sqliteMalloc( new_size*sizeof(struct _ht) ); + if( new_ht==0 ) return; + if( pH->ht ) sqliteFree(pH->ht); + pH->ht = new_ht; + pH->htsize = new_size; + xHash = hashFunction(pH->keyClass); + for(elem=pH->first, pH->first=0; elem; elem = next_elem){ + int h = (*xHash)(elem->pKey, elem->nKey) & (new_size-1); + next_elem = elem->next; + insertElement(pH, &new_ht[h], elem); + } +} + +/* This function (for internal use only) locates an element in an +** hash table that matches the given key. The hash for this key has +** already been computed and is passed as the 4th parameter. +*/ +static HashElem *findElementGivenHash( + const Hash *pH, /* The pH to be searched */ + const void *pKey, /* The key we are searching for */ + int nKey, + int h /* The hash for this key. */ +){ + HashElem *elem; /* Used to loop thru the element list */ + int count; /* Number of elements left to test */ + int (*xCompare)(const void*,int,const void*,int); /* comparison function */ + + if( pH->ht ){ + struct _ht *pEntry = &pH->ht[h]; + elem = pEntry->chain; + count = pEntry->count; + xCompare = compareFunction(pH->keyClass); + while( count-- && elem ){ + if( (*xCompare)(elem->pKey,elem->nKey,pKey,nKey)==0 ){ + return elem; + } + elem = elem->next; + } + } + return 0; +} + +/* Remove a single entry from the hash table given a pointer to that +** element and a hash on the element's key. +*/ +static void removeElementGivenHash( + Hash *pH, /* The pH containing "elem" */ + HashElem* elem, /* The element to be removed from the pH */ + int h /* Hash value for the element */ +){ + struct _ht *pEntry; + if( elem->prev ){ + elem->prev->next = elem->next; + }else{ + pH->first = elem->next; + } + if( elem->next ){ + elem->next->prev = elem->prev; + } + pEntry = &pH->ht[h]; + if( pEntry->chain==elem ){ + pEntry->chain = elem->next; + } + pEntry->count--; + if( pEntry->count<=0 ){ + pEntry->chain = 0; + } + if( pH->copyKey && elem->pKey ){ + sqliteFree(elem->pKey); + } + sqliteFree( elem ); + pH->count--; + if( pH->count<=0 ){ + assert( pH->first==0 ); + assert( pH->count==0 ); + sqlite3HashClear(pH); + } +} + +/* Attempt to locate an element of the hash table pH with a key +** that matches pKey,nKey. Return the data for this element if it is +** found, or NULL if there is no match. +*/ +void *sqlite3HashFind(const Hash *pH, const void *pKey, int nKey){ + int h; /* A hash on key */ + HashElem *elem; /* The element that matches key */ + int (*xHash)(const void*,int); /* The hash function */ + + if( pH==0 || pH->ht==0 ) return 0; + xHash = hashFunction(pH->keyClass); + assert( xHash!=0 ); + h = (*xHash)(pKey,nKey); + assert( (pH->htsize & (pH->htsize-1))==0 ); + elem = findElementGivenHash(pH,pKey,nKey, h & (pH->htsize-1)); + return elem ? elem->data : 0; +} + +/* Insert an element into the hash table pH. The key is pKey,nKey +** and the data is "data". +** +** If no element exists with a matching key, then a new +** element is created. A copy of the key is made if the copyKey +** flag is set. NULL is returned. +** +** If another element already exists with the same key, then the +** new data replaces the old data and the old data is returned. +** The key is not copied in this instance. If a malloc fails, then +** the new data is returned and the hash table is unchanged. +** +** If the "data" parameter to this function is NULL, then the +** element corresponding to "key" is removed from the hash table. +*/ +void *sqlite3HashInsert(Hash *pH, const void *pKey, int nKey, void *data){ + int hraw; /* Raw hash value of the key */ + int h; /* the hash of the key modulo hash table size */ + HashElem *elem; /* Used to loop thru the element list */ + HashElem *new_elem; /* New element added to the pH */ + int (*xHash)(const void*,int); /* The hash function */ + + assert( pH!=0 ); + xHash = hashFunction(pH->keyClass); + assert( xHash!=0 ); + hraw = (*xHash)(pKey, nKey); + assert( (pH->htsize & (pH->htsize-1))==0 ); + h = hraw & (pH->htsize-1); + elem = findElementGivenHash(pH,pKey,nKey,h); + if( elem ){ + void *old_data = elem->data; + if( data==0 ){ + removeElementGivenHash(pH,elem,h); + }else{ + elem->data = data; + } + return old_data; + } + if( data==0 ) return 0; + new_elem = (HashElem*)sqliteMalloc( sizeof(HashElem) ); + if( new_elem==0 ) return data; + if( pH->copyKey && pKey!=0 ){ + new_elem->pKey = sqliteMallocRaw( nKey ); + if( new_elem->pKey==0 ){ + sqliteFree(new_elem); + return data; + } + memcpy((void*)new_elem->pKey, pKey, nKey); + }else{ + new_elem->pKey = (void*)pKey; + } + new_elem->nKey = nKey; + pH->count++; + if( pH->htsize==0 ){ + rehash(pH,8); + if( pH->htsize==0 ){ + pH->count = 0; + sqliteFree(new_elem); + return data; + } + } + if( pH->count > pH->htsize ){ + rehash(pH,pH->htsize*2); + } + assert( pH->htsize>0 ); + assert( (pH->htsize & (pH->htsize-1))==0 ); + h = hraw & (pH->htsize-1); + insertElement(pH, &pH->ht[h], new_elem); + new_elem->data = data; + return 0; +} Added: external/sqlite-source-3.3.4/hash.h ============================================================================== --- (empty file) +++ external/sqlite-source-3.3.4/hash.h Mon Apr 3 07:54:59 2006 @@ -0,0 +1,109 @@ +/* +** 2001 September 22 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This is the header file for the generic hash-table implemenation +** used in SQLite. +** +** $Id: hash.h,v 1.8 2004/08/20 14:08:51 drh Exp $ +*/ +#ifndef _SQLITE_HASH_H_ +#define _SQLITE_HASH_H_ + +/* Forward declarations of structures. */ +typedef struct Hash Hash; +typedef struct HashElem HashElem; + +/* A complete hash table is an instance of the following structure. +** The internals of this structure are intended to be opaque -- client +** code should not attempt to access or modify the fields of this structure +** directly. Change this structure only by using the routines below. +** However, many of the "procedures" and "functions" for modifying and +** accessing this structure are really macros, so we can't really make +** this structure opaque. +*/ +struct Hash { + char keyClass; /* SQLITE_HASH_INT, _POINTER, _STRING, _BINARY */ + char copyKey; /* True if copy of key made on insert */ + int count; /* Number of entries in this table */ + HashElem *first; /* The first element of the array */ + int htsize; /* Number of buckets in the hash table */ + struct _ht { /* the hash table */ + int count; /* Number of entries with this hash */ + HashElem *chain; /* Pointer to first entry with this hash */ + } *ht; +}; + +/* Each element in the hash table is an instance of the following +** structure. All elements are stored on a single doubly-linked list. +** +** Again, this structure is intended to be opaque, but it can't really +** be opaque because it is used by macros. +*/ +struct HashElem { + HashElem *next, *prev; /* Next and previous elements in the table */ + void *data; /* Data associated with this element */ + void *pKey; int nKey; /* Key associated with this element */ +}; + +/* +** There are 4 different modes of operation for a hash table: +** +** SQLITE_HASH_INT nKey is used as the key and pKey is ignored. +** +** SQLITE_HASH_POINTER pKey is used as the key and nKey is ignored. +** +** SQLITE_HASH_STRING pKey points to a string that is nKey bytes long +** (including the null-terminator, if any). Case +** is ignored in comparisons. +** +** SQLITE_HASH_BINARY pKey points to binary data nKey bytes long. +** memcmp() is used to compare keys. +** +** A copy of the key is made for SQLITE_HASH_STRING and SQLITE_HASH_BINARY +** if the copyKey parameter to HashInit is 1. +*/ +/* #define SQLITE_HASH_INT 1 // NOT USED */ +/* #define SQLITE_HASH_POINTER 2 // NOT USED */ +#define SQLITE_HASH_STRING 3 +#define SQLITE_HASH_BINARY 4 + +/* +** Access routines. To delete, insert a NULL pointer. +*/ +void sqlite3HashInit(Hash*, int keytype, int copyKey); +void *sqlite3HashInsert(Hash*, const void *pKey, int nKey, void *pData); +void *sqlite3HashFind(const Hash*, const void *pKey, int nKey); +void sqlite3HashClear(Hash*); + +/* +** Macros for looping over all elements of a hash table. The idiom is +** like this: +** +** Hash h; +** HashElem *p; +** ... +** for(p=sqliteHashFirst(&h); p; p=sqliteHashNext(p)){ +** SomeStructure *pData = sqliteHashData(p); +** // do something with pData +** } +*/ +#define sqliteHashFirst(H) ((H)->first) +#define sqliteHashNext(E) ((E)->next) +#define sqliteHashData(E) ((E)->data) +#define sqliteHashKey(E) ((E)->pKey) +#define sqliteHashKeysize(E) ((E)->nKey) + +/* +** Number of entries in a hash table +*/ +#define sqliteHashCount(H) ((H)->count) + +#endif /* _SQLITE_HASH_H_ */ Added: external/sqlite-source-3.3.4/insert.c ============================================================================== --- (empty file) +++ external/sqlite-source-3.3.4/insert.c Mon Apr 3 07:54:59 2006 @@ -0,0 +1,1117 @@ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains C code routines that are called by the parser +** to handle INSERT statements in SQLite. +** +** $Id: insert.c,v 1.161 2006/02/10 02:27:43 danielk1977 Exp $ +*/ +#include "sqliteInt.h" + +/* +** Set P3 of the most recently inserted opcode to a column affinity +** string for index pIdx. A column affinity string has one character +** for each column in the table, according to the affinity of the column: +** +** Character Column affinity +** ------------------------------ +** 'a' TEXT +** 'b' NONE +** 'c' NUMERIC +** 'd' INTEGER +** 'e' REAL +*/ +void sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){ + if( !pIdx->zColAff ){ + /* The first time a column affinity string for a particular index is + ** required, it is allocated and populated here. It is then stored as + ** a member of the Index structure for subsequent use. + ** + ** The column affinity string will eventually be deleted by + ** sqliteDeleteIndex() when the Index structure itself is cleaned + ** up. + */ + int n; + Table *pTab = pIdx->pTable; + pIdx->zColAff = (char *)sqliteMalloc(pIdx->nColumn+1); + if( !pIdx->zColAff ){ + return; + } + for(n=0; nnColumn; n++){ + pIdx->zColAff[n] = pTab->aCol[pIdx->aiColumn[n]].affinity; + } + pIdx->zColAff[pIdx->nColumn] = '\0'; + } + + sqlite3VdbeChangeP3(v, -1, pIdx->zColAff, 0); +} + +/* +** Set P3 of the most recently inserted opcode to a column affinity +** string for table pTab. A column affinity string has one character +** for each column indexed by the index, according to the affinity of the +** column: +** +** Character Column affinity +** ------------------------------ +** 'a' TEXT +** 'b' NONE +** 'c' NUMERIC +** 'd' INTEGER +** 'e' REAL +*/ +void sqlite3TableAffinityStr(Vdbe *v, Table *pTab){ + /* The first time a column affinity string for a particular table + ** is required, it is allocated and populated here. It is then + ** stored as a member of the Table structure for subsequent use. + ** + ** The column affinity string will eventually be deleted by + ** sqlite3DeleteTable() when the Table structure itself is cleaned up. + */ + if( !pTab->zColAff ){ + char *zColAff; + int i; + + zColAff = (char *)sqliteMalloc(pTab->nCol+1); + if( !zColAff ){ + return; + } + + for(i=0; inCol; i++){ + zColAff[i] = pTab->aCol[i].affinity; + } + zColAff[pTab->nCol] = '\0'; + + pTab->zColAff = zColAff; + } + + sqlite3VdbeChangeP3(v, -1, pTab->zColAff, 0); +} + +/* +** Return non-zero if SELECT statement p opens the table with rootpage +** iTab in database iDb. This is used to see if a statement of the form +** "INSERT INTO SELECT ..." can run without using temporary +** table for the results of the SELECT. +** +** No checking is done for sub-selects that are part of expressions. +*/ +static int selectReadsTable(Select *p, Schema *pSchema, int iTab){ + int i; + struct SrcList_item *pItem; + if( p->pSrc==0 ) return 0; + for(i=0, pItem=p->pSrc->a; ipSrc->nSrc; i++, pItem++){ + if( pItem->pSelect ){ + if( selectReadsTable(pItem->pSelect, pSchema, iTab) ) return 1; + }else{ + if( pItem->pTab->pSchema==pSchema && pItem->pTab->tnum==iTab ) return 1; + } + } + return 0; +} + +/* +** This routine is call to handle SQL of the following forms: +** +** insert into TABLE (IDLIST) values(EXPRLIST) +** insert into TABLE (IDLIST) select +** +** The IDLIST following the table name is always optional. If omitted, +** then a list of all columns for the table is substituted. The IDLIST +** appears in the pColumn parameter. pColumn is NULL if IDLIST is omitted. +** +** The pList parameter holds EXPRLIST in the first form of the INSERT +** statement above, and pSelect is NULL. For the second form, pList is +** NULL and pSelect is a pointer to the select statement used to generate +** data for the insert. +** +** The code generated follows one of three templates. For a simple +** select with data coming from a VALUES clause, the code executes +** once straight down through. The template looks like this: +** +** open write cursor to and its indices +** puts VALUES clause expressions onto the stack +** write the resulting record into
+** cleanup +** +** If the statement is of the form +** +** INSERT INTO
SELECT ... +** +** And the SELECT clause does not read from
at any time, then +** the generated code follows this template: +** +** goto B +** A: setup for the SELECT +** loop over the tables in the SELECT +** gosub C +** end loop +** cleanup after the SELECT +** goto D +** B: open write cursor to
and its indices +** goto A +** C: insert the select result into
+** return +** D: cleanup +** +** The third template is used if the insert statement takes its +** values from a SELECT but the data is being inserted into a table +** that is also read as part of the SELECT. In the third form, +** we have to use a intermediate table to store the results of +** the select. The template is like this: +** +** goto B +** A: setup for the SELECT +** loop over the tables in the SELECT +** gosub C +** end loop +** cleanup after the SELECT +** goto D +** C: insert the select result into the intermediate table +** return +** B: open a cursor to an intermediate table +** goto A +** D: open write cursor to
and its indices +** loop over the intermediate table +** transfer values form intermediate table into
+** end the loop +** cleanup +*/ +void sqlite3Insert( + Parse *pParse, /* Parser context */ + SrcList *pTabList, /* Name of table into which we are inserting */ + ExprList *pList, /* List of values to be inserted */ + Select *pSelect, /* A SELECT statement to use as the data source */ + IdList *pColumn, /* Column names corresponding to IDLIST. */ + int onError /* How to handle constraint errors */ +){ + Table *pTab; /* The table to insert into */ + char *zTab; /* Name of the table into which we are inserting */ + const char *zDb; /* Name of the database holding this table */ + int i, j, idx; /* Loop counters */ + Vdbe *v; /* Generate code into this virtual machine */ + Index *pIdx; /* For looping over indices of the table */ + int nColumn; /* Number of columns in the data */ + int base = 0; /* VDBE Cursor number for pTab */ + int iCont=0,iBreak=0; /* Beginning and end of the loop over srcTab */ + sqlite3 *db; /* The main database structure */ + int keyColumn = -1; /* Column that is the INTEGER PRIMARY KEY */ + int endOfLoop; /* Label for the end of the insertion loop */ + int useTempTable = 0; /* Store SELECT results in intermediate table */ + int srcTab = 0; /* Data comes from this temporary cursor if >=0 */ + int iSelectLoop = 0; /* Address of code that implements the SELECT */ + int iCleanup = 0; /* Address of the cleanup code */ + int iInsertBlock = 0; /* Address of the subroutine used to insert data */ + int iCntMem = 0; /* Memory cell used for the row counter */ + int newIdx = -1; /* Cursor for the NEW table */ + Db *pDb; /* The database containing table being inserted into */ + int counterMem = 0; /* Memory cell holding AUTOINCREMENT counter */ + int iDb; + +#ifndef SQLITE_OMIT_TRIGGER + int isView; /* True if attempting to insert into a view */ + int triggers_exist = 0; /* True if there are FOR EACH ROW triggers */ +#endif + +#ifndef SQLITE_OMIT_AUTOINCREMENT + int counterRowid = 0; /* Memory cell holding rowid of autoinc counter */ +#endif + + if( pParse->nErr || sqlite3MallocFailed() ){ + goto insert_cleanup; + } + db = pParse->db; + + /* Locate the table into which we will be inserting new information. + */ + assert( pTabList->nSrc==1 ); + zTab = pTabList->a[0].zName; + if( zTab==0 ) goto insert_cleanup; + pTab = sqlite3SrcListLookup(pParse, pTabList); + if( pTab==0 ){ + goto insert_cleanup; + } + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); + assert( iDbnDb ); + pDb = &db->aDb[iDb]; + zDb = pDb->zName; + if( sqlite3AuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb) ){ + goto insert_cleanup; + } + + /* Figure out if we have any triggers and if the table being + ** inserted into is a view + */ +#ifndef SQLITE_OMIT_TRIGGER + triggers_exist = sqlite3TriggersExist(pParse, pTab, TK_INSERT, 0); + isView = pTab->pSelect!=0; +#else +# define triggers_exist 0 +# define isView 0 +#endif +#ifdef SQLITE_OMIT_VIEW +# undef isView +# define isView 0 +#endif + + /* Ensure that: + * (a) the table is not read-only, + * (b) that if it is a view then ON INSERT triggers exist + */ + if( sqlite3IsReadOnly(pParse, pTab, triggers_exist) ){ + goto insert_cleanup; + } + if( pTab==0 ) goto insert_cleanup; + + /* If pTab is really a view, make sure it has been initialized. + */ + if( isView && sqlite3ViewGetColumnNames(pParse, pTab) ){ + goto insert_cleanup; + } + + /* Allocate a VDBE + */ + v = sqlite3GetVdbe(pParse); + if( v==0 ) goto insert_cleanup; + if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); + sqlite3BeginWriteOperation(pParse, pSelect || triggers_exist, iDb); + + /* if there are row triggers, allocate a temp table for new.* references. */ + if( triggers_exist ){ + newIdx = pParse->nTab++; + } + +#ifndef SQLITE_OMIT_AUTOINCREMENT + /* If this is an AUTOINCREMENT table, look up the sequence number in the + ** sqlite_sequence table and store it in memory cell counterMem. Also + ** remember the rowid of the sqlite_sequence table entry in memory cell + ** counterRowid. + */ + if( pTab->autoInc ){ + int iCur = pParse->nTab; + int addr = sqlite3VdbeCurrentAddr(v); + counterRowid = pParse->nMem++; + counterMem = pParse->nMem++; + sqlite3OpenTable(pParse, iCur, iDb, pDb->pSchema->pSeqTab, OP_OpenRead); + sqlite3VdbeAddOp(v, OP_Rewind, iCur, addr+13); + sqlite3VdbeAddOp(v, OP_Column, iCur, 0); + sqlite3VdbeOp3(v, OP_String8, 0, 0, pTab->zName, 0); + sqlite3VdbeAddOp(v, OP_Ne, 0x100, addr+12); + sqlite3VdbeAddOp(v, OP_Rowid, iCur, 0); + sqlite3VdbeAddOp(v, OP_MemStore, counterRowid, 1); + sqlite3VdbeAddOp(v, OP_Column, iCur, 1); + sqlite3VdbeAddOp(v, OP_MemStore, counterMem, 1); + sqlite3VdbeAddOp(v, OP_Goto, 0, addr+13); + sqlite3VdbeAddOp(v, OP_Next, iCur, addr+4); + sqlite3VdbeAddOp(v, OP_Close, iCur, 0); + } +#endif /* SQLITE_OMIT_AUTOINCREMENT */ + + /* Figure out how many columns of data are supplied. If the data + ** is coming from a SELECT statement, then this step also generates + ** all the code to implement the SELECT statement and invoke a subroutine + ** to process each row of the result. (Template 2.) If the SELECT + ** statement uses the the table that is being inserted into, then the + ** subroutine is also coded here. That subroutine stores the SELECT + ** results in a temporary table. (Template 3.) + */ + if( pSelect ){ + /* Data is coming from a SELECT. Generate code to implement that SELECT + */ + int rc, iInitCode; + iInitCode = sqlite3VdbeAddOp(v, OP_Goto, 0, 0); + iSelectLoop = sqlite3VdbeCurrentAddr(v); + iInsertBlock = sqlite3VdbeMakeLabel(v); + + /* Resolve the expressions in the SELECT statement and execute it. */ + rc = sqlite3Select(pParse, pSelect, SRT_Subroutine, iInsertBlock,0,0,0,0); + if( rc || pParse->nErr || sqlite3MallocFailed() ){ + goto insert_cleanup; + } + + iCleanup = sqlite3VdbeMakeLabel(v); + sqlite3VdbeAddOp(v, OP_Goto, 0, iCleanup); + assert( pSelect->pEList ); + nColumn = pSelect->pEList->nExpr; + + /* Set useTempTable to TRUE if the result of the SELECT statement + ** should be written into a temporary table. Set to FALSE if each + ** row of the SELECT can be written directly into the result table. + ** + ** A temp table must be used if the table being updated is also one + ** of the tables being read by the SELECT statement. Also use a + ** temp table in the case of row triggers. + */ + if( triggers_exist || selectReadsTable(pSelect,pTab->pSchema,pTab->tnum) ){ + useTempTable = 1; + } + + if( useTempTable ){ + /* Generate the subroutine that SELECT calls to process each row of + ** the result. Store the result in a temporary table + */ + srcTab = pParse->nTab++; + sqlite3VdbeResolveLabel(v, iInsertBlock); + sqlite3VdbeAddOp(v, OP_MakeRecord, nColumn, 0); + sqlite3VdbeAddOp(v, OP_NewRowid, srcTab, 0); + sqlite3VdbeAddOp(v, OP_Pull, 1, 0); + sqlite3VdbeAddOp(v, OP_Insert, srcTab, 0); + sqlite3VdbeAddOp(v, OP_Return, 0, 0); + + /* The following code runs first because the GOTO at the very top + ** of the program jumps to it. Create the temporary table, then jump + ** back up and execute the SELECT code above. + */ + sqlite3VdbeJumpHere(v, iInitCode); + sqlite3VdbeAddOp(v, OP_OpenVirtual, srcTab, 0); + sqlite3VdbeAddOp(v, OP_SetNumColumns, srcTab, nColumn); + sqlite3VdbeAddOp(v, OP_Goto, 0, iSelectLoop); + sqlite3VdbeResolveLabel(v, iCleanup); + }else{ + sqlite3VdbeJumpHere(v, iInitCode); + } + }else{ + /* This is the case if the data for the INSERT is coming from a VALUES + ** clause + */ + NameContext sNC; + memset(&sNC, 0, sizeof(sNC)); + sNC.pParse = pParse; + assert( pList!=0 ); + srcTab = -1; + useTempTable = 0; + assert( pList ); + nColumn = pList->nExpr; + for(i=0; ia[i].pExpr) ){ + goto insert_cleanup; + } + } + } + + /* Make sure the number of columns in the source data matches the number + ** of columns to be inserted into the table. + */ + if( pColumn==0 && nColumn!=pTab->nCol ){ + sqlite3ErrorMsg(pParse, + "table %S has %d columns but %d values were supplied", + pTabList, 0, pTab->nCol, nColumn); + goto insert_cleanup; + } + if( pColumn!=0 && nColumn!=pColumn->nId ){ + sqlite3ErrorMsg(pParse, "%d values for %d columns", nColumn, pColumn->nId); + goto insert_cleanup; + } + + /* If the INSERT statement included an IDLIST term, then make sure + ** all elements of the IDLIST really are columns of the table and + ** remember the column indices. + ** + ** If the table has an INTEGER PRIMARY KEY column and that column + ** is named in the IDLIST, then record in the keyColumn variable + ** the index into IDLIST of the primary key column. keyColumn is + ** the index of the primary key as it appears in IDLIST, not as + ** is appears in the original table. (The index of the primary + ** key in the original table is pTab->iPKey.) + */ + if( pColumn ){ + for(i=0; inId; i++){ + pColumn->a[i].idx = -1; + } + for(i=0; inId; i++){ + for(j=0; jnCol; j++){ + if( sqlite3StrICmp(pColumn->a[i].zName, pTab->aCol[j].zName)==0 ){ + pColumn->a[i].idx = j; + if( j==pTab->iPKey ){ + keyColumn = i; + } + break; + } + } + if( j>=pTab->nCol ){ + if( sqlite3IsRowid(pColumn->a[i].zName) ){ + keyColumn = i; + }else{ + sqlite3ErrorMsg(pParse, "table %S has no column named %s", + pTabList, 0, pColumn->a[i].zName); + pParse->nErr++; + goto insert_cleanup; + } + } + } + } + + /* If there is no IDLIST term but the table has an integer primary + ** key, the set the keyColumn variable to the primary key column index + ** in the original table definition. + */ + if( pColumn==0 ){ + keyColumn = pTab->iPKey; + } + + /* Open the temp table for FOR EACH ROW triggers + */ + if( triggers_exist ){ + sqlite3VdbeAddOp(v, OP_OpenPseudo, newIdx, 0); + sqlite3VdbeAddOp(v, OP_SetNumColumns, newIdx, pTab->nCol); + } + + /* Initialize the count of rows to be inserted + */ + if( db->flags & SQLITE_CountRows ){ + iCntMem = pParse->nMem++; + sqlite3VdbeAddOp(v, OP_MemInt, 0, iCntMem); + } + + /* Open tables and indices if there are no row triggers */ + if( !triggers_exist ){ + base = pParse->nTab; + sqlite3OpenTableAndIndices(pParse, pTab, base, OP_OpenWrite); + } + + /* If the data source is a temporary table, then we have to create + ** a loop because there might be multiple rows of data. If the data + ** source is a subroutine call from the SELECT statement, then we need + ** to launch the SELECT statement processing. + */ + if( useTempTable ){ + iBreak = sqlite3VdbeMakeLabel(v); + sqlite3VdbeAddOp(v, OP_Rewind, srcTab, iBreak); + iCont = sqlite3VdbeCurrentAddr(v); + }else if( pSelect ){ + sqlite3VdbeAddOp(v, OP_Goto, 0, iSelectLoop); + sqlite3VdbeResolveLabel(v, iInsertBlock); + } + + /* Run the BEFORE and INSTEAD OF triggers, if there are any + */ + endOfLoop = sqlite3VdbeMakeLabel(v); + if( triggers_exist & TRIGGER_BEFORE ){ + + /* build the NEW.* reference row. Note that if there is an INTEGER + ** PRIMARY KEY into which a NULL is being inserted, that NULL will be + ** translated into a unique ID for the row. But on a BEFORE trigger, + ** we do not know what the unique ID will be (because the insert has + ** not happened yet) so we substitute a rowid of -1 + */ + if( keyColumn<0 ){ + sqlite3VdbeAddOp(v, OP_Integer, -1, 0); + }else if( useTempTable ){ + sqlite3VdbeAddOp(v, OP_Column, srcTab, keyColumn); + }else{ + assert( pSelect==0 ); /* Otherwise useTempTable is true */ + sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr); + sqlite3VdbeAddOp(v, OP_NotNull, -1, sqlite3VdbeCurrentAddr(v)+3); + sqlite3VdbeAddOp(v, OP_Pop, 1, 0); + sqlite3VdbeAddOp(v, OP_Integer, -1, 0); + sqlite3VdbeAddOp(v, OP_MustBeInt, 0, 0); + } + + /* Create the new column data + */ + for(i=0; inCol; i++){ + if( pColumn==0 ){ + j = i; + }else{ + for(j=0; jnId; j++){ + if( pColumn->a[j].idx==i ) break; + } + } + if( pColumn && j>=pColumn->nId ){ + sqlite3ExprCode(pParse, pTab->aCol[i].pDflt); + }else if( useTempTable ){ + sqlite3VdbeAddOp(v, OP_Column, srcTab, j); + }else{ + assert( pSelect==0 ); /* Otherwise useTempTable is true */ + sqlite3ExprCodeAndCache(pParse, pList->a[j].pExpr); + } + } + sqlite3VdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0); + + /* If this is an INSERT on a view with an INSTEAD OF INSERT trigger, + ** do not attempt any conversions before assembling the record. + ** If this is a real table, attempt conversions as required by the + ** table column affinities. + */ + if( !isView ){ + sqlite3TableAffinityStr(v, pTab); + } + sqlite3VdbeAddOp(v, OP_Insert, newIdx, 0); + + /* Fire BEFORE or INSTEAD OF triggers */ + if( sqlite3CodeRowTrigger(pParse, TK_INSERT, 0, TRIGGER_BEFORE, pTab, + newIdx, -1, onError, endOfLoop) ){ + goto insert_cleanup; + } + } + + /* If any triggers exists, the opening of tables and indices is deferred + ** until now. + */ + if( triggers_exist && !isView ){ + base = pParse->nTab; + sqlite3OpenTableAndIndices(pParse, pTab, base, OP_OpenWrite); + } + + /* Push the record number for the new entry onto the stack. The + ** record number is a randomly generate integer created by NewRowid + ** except when the table has an INTEGER PRIMARY KEY column, in which + ** case the record number is the same as that column. + */ + if( !isView ){ + if( keyColumn>=0 ){ + if( useTempTable ){ + sqlite3VdbeAddOp(v, OP_Column, srcTab, keyColumn); + }else if( pSelect ){ + sqlite3VdbeAddOp(v, OP_Dup, nColumn - keyColumn - 1, 1); + }else{ + sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr); + } + /* If the PRIMARY KEY expression is NULL, then use OP_NewRowid + ** to generate a unique primary key value. + */ + sqlite3VdbeAddOp(v, OP_NotNull, -1, sqlite3VdbeCurrentAddr(v)+3); + sqlite3VdbeAddOp(v, OP_Pop, 1, 0); + sqlite3VdbeAddOp(v, OP_NewRowid, base, counterMem); + sqlite3VdbeAddOp(v, OP_MustBeInt, 0, 0); + }else{ + sqlite3VdbeAddOp(v, OP_NewRowid, base, counterMem); + } +#ifndef SQLITE_OMIT_AUTOINCREMENT + if( pTab->autoInc ){ + sqlite3VdbeAddOp(v, OP_MemMax, counterMem, 0); + } +#endif /* SQLITE_OMIT_AUTOINCREMENT */ + + /* Push onto the stack, data for all columns of the new entry, beginning + ** with the first column. + */ + for(i=0; inCol; i++){ + if( i==pTab->iPKey ){ + /* The value of the INTEGER PRIMARY KEY column is always a NULL. + ** Whenever this column is read, the record number will be substituted + ** in its place. So will fill this column with a NULL to avoid + ** taking up data space with information that will never be used. */ + sqlite3VdbeAddOp(v, OP_Null, 0, 0); + continue; + } + if( pColumn==0 ){ + j = i; + }else{ + for(j=0; jnId; j++){ + if( pColumn->a[j].idx==i ) break; + } + } + if( pColumn && j>=pColumn->nId ){ + sqlite3ExprCode(pParse, pTab->aCol[i].pDflt); + }else if( useTempTable ){ + sqlite3VdbeAddOp(v, OP_Column, srcTab, j); + }else if( pSelect ){ + sqlite3VdbeAddOp(v, OP_Dup, i+nColumn-j, 1); + }else{ + sqlite3ExprCode(pParse, pList->a[j].pExpr); + } + } + + /* Generate code to check constraints and generate index keys and + ** do the insertion. + */ + sqlite3GenerateConstraintChecks(pParse, pTab, base, 0, keyColumn>=0, + 0, onError, endOfLoop); + sqlite3CompleteInsertion(pParse, pTab, base, 0,0,0, + (triggers_exist & TRIGGER_AFTER)!=0 ? newIdx : -1); + } + + /* Update the count of rows that are inserted + */ + if( (db->flags & SQLITE_CountRows)!=0 ){ + sqlite3VdbeAddOp(v, OP_MemIncr, 1, iCntMem); + } + + if( triggers_exist ){ + /* Close all tables opened */ + if( !isView ){ + sqlite3VdbeAddOp(v, OP_Close, base, 0); + for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){ + sqlite3VdbeAddOp(v, OP_Close, idx+base, 0); + } + } + + /* Code AFTER triggers */ + if( sqlite3CodeRowTrigger(pParse, TK_INSERT, 0, TRIGGER_AFTER, pTab, + newIdx, -1, onError, endOfLoop) ){ + goto insert_cleanup; + } + } + + /* The bottom of the loop, if the data source is a SELECT statement + */ + sqlite3VdbeResolveLabel(v, endOfLoop); + if( useTempTable ){ + sqlite3VdbeAddOp(v, OP_Next, srcTab, iCont); + sqlite3VdbeResolveLabel(v, iBreak); + sqlite3VdbeAddOp(v, OP_Close, srcTab, 0); + }else if( pSelect ){ + sqlite3VdbeAddOp(v, OP_Pop, nColumn, 0); + sqlite3VdbeAddOp(v, OP_Return, 0, 0); + sqlite3VdbeResolveLabel(v, iCleanup); + } + + if( !triggers_exist ){ + /* Close all tables opened */ + sqlite3VdbeAddOp(v, OP_Close, base, 0); + for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){ + sqlite3VdbeAddOp(v, OP_Close, idx+base, 0); + } + } + +#ifndef SQLITE_OMIT_AUTOINCREMENT + /* Update the sqlite_sequence table by storing the content of the + ** counter value in memory counterMem back into the sqlite_sequence + ** table. + */ + if( pTab->autoInc ){ + int iCur = pParse->nTab; + int addr = sqlite3VdbeCurrentAddr(v); + sqlite3OpenTable(pParse, iCur, iDb, pDb->pSchema->pSeqTab, OP_OpenWrite); + sqlite3VdbeAddOp(v, OP_MemLoad, counterRowid, 0); + sqlite3VdbeAddOp(v, OP_NotNull, -1, addr+7); + sqlite3VdbeAddOp(v, OP_Pop, 1, 0); + sqlite3VdbeAddOp(v, OP_NewRowid, iCur, 0); + sqlite3VdbeOp3(v, OP_String8, 0, 0, pTab->zName, 0); + sqlite3VdbeAddOp(v, OP_MemLoad, counterMem, 0); + sqlite3VdbeAddOp(v, OP_MakeRecord, 2, 0); + sqlite3VdbeAddOp(v, OP_Insert, iCur, 0); + sqlite3VdbeAddOp(v, OP_Close, iCur, 0); + } +#endif + + /* + ** Return the number of rows inserted. If this routine is + ** generating code because of a call to sqlite3NestedParse(), do not + ** invoke the callback function. + */ + if( db->flags & SQLITE_CountRows && pParse->nested==0 && !pParse->trigStack ){ + sqlite3VdbeAddOp(v, OP_MemLoad, iCntMem, 0); + sqlite3VdbeAddOp(v, OP_Callback, 1, 0); + sqlite3VdbeSetNumCols(v, 1); + sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows inserted", P3_STATIC); + } + +insert_cleanup: + sqlite3SrcListDelete(pTabList); + sqlite3ExprListDelete(pList); + sqlite3SelectDelete(pSelect); + sqlite3IdListDelete(pColumn); +} + +/* +** Generate code to do a constraint check prior to an INSERT or an UPDATE. +** +** When this routine is called, the stack contains (from bottom to top) +** the following values: +** +** 1. The rowid of the row to be updated before the update. This +** value is omitted unless we are doing an UPDATE that involves a +** change to the record number. +** +** 2. The rowid of the row after the update. +** +** 3. The data in the first column of the entry after the update. +** +** i. Data from middle columns... +** +** N. The data in the last column of the entry after the update. +** +** The old rowid shown as entry (1) above is omitted unless both isUpdate +** and rowidChng are 1. isUpdate is true for UPDATEs and false for +** INSERTs and rowidChng is true if the record number is being changed. +** +** The code generated by this routine pushes additional entries onto +** the stack which are the keys for new index entries for the new record. +** The order of index keys is the same as the order of the indices on +** the pTable->pIndex list. A key is only created for index i if +** aIdxUsed!=0 and aIdxUsed[i]!=0. +** +** This routine also generates code to check constraints. NOT NULL, +** CHECK, and UNIQUE constraints are all checked. If a constraint fails, +** then the appropriate action is performed. There are five possible +** actions: ROLLBACK, ABORT, FAIL, REPLACE, and IGNORE. +** +** Constraint type Action What Happens +** --------------- ---------- ---------------------------------------- +** any ROLLBACK The current transaction is rolled back and +** sqlite3_exec() returns immediately with a +** return code of SQLITE_CONSTRAINT. +** +** any ABORT Back out changes from the current command +** only (do not do a complete rollback) then +** cause sqlite3_exec() to return immediately +** with SQLITE_CONSTRAINT. +** +** any FAIL Sqlite_exec() returns immediately with a +** return code of SQLITE_CONSTRAINT. The +** transaction is not rolled back and any +** prior changes are retained. +** +** any IGNORE The record number and data is popped from +** the stack and there is an immediate jump +** to label ignoreDest. +** +** NOT NULL REPLACE The NULL value is replace by the default +** value for that column. If the default value +** is NULL, the action is the same as ABORT. +** +** UNIQUE REPLACE The other row that conflicts with the row +** being inserted is removed. +** +** CHECK REPLACE Illegal. The results in an exception. +** +** Which action to take is determined by the overrideError parameter. +** Or if overrideError==OE_Default, then the pParse->onError parameter +** is used. Or if pParse->onError==OE_Default then the onError value +** for the constraint is used. +** +** The calling routine must open a read/write cursor for pTab with +** cursor number "base". All indices of pTab must also have open +** read/write cursors with cursor number base+i for the i-th cursor. +** Except, if there is no possibility of a REPLACE action then +** cursors do not need to be open for indices where aIdxUsed[i]==0. +** +** If the isUpdate flag is true, it means that the "base" cursor is +** initially pointing to an entry that is being updated. The isUpdate +** flag causes extra code to be generated so that the "base" cursor +** is still pointing at the same entry after the routine returns. +** Without the isUpdate flag, the "base" cursor might be moved. +*/ +void sqlite3GenerateConstraintChecks( + Parse *pParse, /* The parser context */ + Table *pTab, /* the table into which we are inserting */ + int base, /* Index of a read/write cursor pointing at pTab */ + char *aIdxUsed, /* Which indices are used. NULL means all are used */ + int rowidChng, /* True if the record number will change */ + int isUpdate, /* True for UPDATE, False for INSERT */ + int overrideError, /* Override onError to this if not OE_Default */ + int ignoreDest /* Jump to this label on an OE_Ignore resolution */ +){ + int i; + Vdbe *v; + int nCol; + int onError; + int addr; + int extra; + int iCur; + Index *pIdx; + int seenReplace = 0; + int jumpInst1=0, jumpInst2; + int hasTwoRowids = (isUpdate && rowidChng); + + v = sqlite3GetVdbe(pParse); + assert( v!=0 ); + assert( pTab->pSelect==0 ); /* This table is not a VIEW */ + nCol = pTab->nCol; + + /* Test all NOT NULL constraints. + */ + for(i=0; iiPKey ){ + continue; + } + onError = pTab->aCol[i].notNull; + if( onError==OE_None ) continue; + if( overrideError!=OE_Default ){ + onError = overrideError; + }else if( onError==OE_Default ){ + onError = OE_Abort; + } + if( onError==OE_Replace && pTab->aCol[i].pDflt==0 ){ + onError = OE_Abort; + } + sqlite3VdbeAddOp(v, OP_Dup, nCol-1-i, 1); + addr = sqlite3VdbeAddOp(v, OP_NotNull, 1, 0); + assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail + || onError==OE_Ignore || onError==OE_Replace ); + switch( onError ){ + case OE_Rollback: + case OE_Abort: + case OE_Fail: { + char *zMsg = 0; + sqlite3VdbeAddOp(v, OP_Halt, SQLITE_CONSTRAINT, onError); + sqlite3SetString(&zMsg, pTab->zName, ".", pTab->aCol[i].zName, + " may not be NULL", (char*)0); + sqlite3VdbeChangeP3(v, -1, zMsg, P3_DYNAMIC); + break; + } + case OE_Ignore: { + sqlite3VdbeAddOp(v, OP_Pop, nCol+1+hasTwoRowids, 0); + sqlite3VdbeAddOp(v, OP_Goto, 0, ignoreDest); + break; + } + case OE_Replace: { + sqlite3ExprCode(pParse, pTab->aCol[i].pDflt); + sqlite3VdbeAddOp(v, OP_Push, nCol-i, 0); + break; + } + } + sqlite3VdbeJumpHere(v, addr); + } + + /* Test all CHECK constraints + */ +#ifndef SQLITE_OMIT_CHECK + if( pTab->pCheck && (pParse->db->flags & SQLITE_IgnoreChecks)==0 ){ + int allOk = sqlite3VdbeMakeLabel(v); + assert( pParse->ckOffset==0 ); + pParse->ckOffset = nCol; + sqlite3ExprIfTrue(pParse, pTab->pCheck, allOk, 1); + assert( pParse->ckOffset==nCol ); + pParse->ckOffset = 0; + sqlite3VdbeAddOp(v, OP_Halt, SQLITE_CONSTRAINT, OE_Abort); + sqlite3VdbeResolveLabel(v, allOk); + } +#endif /* !defined(SQLITE_OMIT_CHECK) */ + + /* If we have an INTEGER PRIMARY KEY, make sure the primary key + ** of the new record does not previously exist. Except, if this + ** is an UPDATE and the primary key is not changing, that is OK. + */ + if( rowidChng ){ + onError = pTab->keyConf; + if( overrideError!=OE_Default ){ + onError = overrideError; + }else if( onError==OE_Default ){ + onError = OE_Abort; + } + + if( isUpdate ){ + sqlite3VdbeAddOp(v, OP_Dup, nCol+1, 1); + sqlite3VdbeAddOp(v, OP_Dup, nCol+1, 1); + jumpInst1 = sqlite3VdbeAddOp(v, OP_Eq, 0, 0); + } + sqlite3VdbeAddOp(v, OP_Dup, nCol, 1); + jumpInst2 = sqlite3VdbeAddOp(v, OP_NotExists, base, 0); + switch( onError ){ + default: { + onError = OE_Abort; + /* Fall thru into the next case */ + } + case OE_Rollback: + case OE_Abort: + case OE_Fail: { + sqlite3VdbeOp3(v, OP_Halt, SQLITE_CONSTRAINT, onError, + "PRIMARY KEY must be unique", P3_STATIC); + break; + } + case OE_Replace: { + sqlite3GenerateRowIndexDelete(pParse->db, v, pTab, base, 0); + if( isUpdate ){ + sqlite3VdbeAddOp(v, OP_Dup, nCol+hasTwoRowids, 1); + sqlite3VdbeAddOp(v, OP_MoveGe, base, 0); + } + seenReplace = 1; + break; + } + case OE_Ignore: { + assert( seenReplace==0 ); + sqlite3VdbeAddOp(v, OP_Pop, nCol+1+hasTwoRowids, 0); + sqlite3VdbeAddOp(v, OP_Goto, 0, ignoreDest); + break; + } + } + sqlite3VdbeJumpHere(v, jumpInst2); + if( isUpdate ){ + sqlite3VdbeJumpHere(v, jumpInst1); + sqlite3VdbeAddOp(v, OP_Dup, nCol+1, 1); + sqlite3VdbeAddOp(v, OP_MoveGe, base, 0); + } + } + + /* Test all UNIQUE constraints by creating entries for each UNIQUE + ** index and making sure that duplicate entries do not already exist. + ** Add the new records to the indices as we go. + */ + extra = -1; + for(iCur=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCur++){ + if( aIdxUsed && aIdxUsed[iCur]==0 ) continue; /* Skip unused indices */ + extra++; + + /* Create a key for accessing the index entry */ + sqlite3VdbeAddOp(v, OP_Dup, nCol+extra, 1); + for(i=0; inColumn; i++){ + int idx = pIdx->aiColumn[i]; + if( idx==pTab->iPKey ){ + sqlite3VdbeAddOp(v, OP_Dup, i+extra+nCol+1, 1); + }else{ + sqlite3VdbeAddOp(v, OP_Dup, i+extra+nCol-idx, 1); + } + } + jumpInst1 = sqlite3VdbeAddOp(v, OP_MakeIdxRec, pIdx->nColumn, 0); + sqlite3IndexAffinityStr(v, pIdx); + + /* Find out what action to take in case there is an indexing conflict */ + onError = pIdx->onError; + if( onError==OE_None ) continue; /* pIdx is not a UNIQUE index */ + if( overrideError!=OE_Default ){ + onError = overrideError; + }else if( onError==OE_Default ){ + onError = OE_Abort; + } + if( seenReplace ){ + if( onError==OE_Ignore ) onError = OE_Replace; + else if( onError==OE_Fail ) onError = OE_Abort; + } + + + /* Check to see if the new index entry will be unique */ + sqlite3VdbeAddOp(v, OP_Dup, extra+nCol+1+hasTwoRowids, 1); + jumpInst2 = sqlite3VdbeAddOp(v, OP_IsUnique, base+iCur+1, 0); + + /* Generate code that executes if the new index entry is not unique */ + assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail + || onError==OE_Ignore || onError==OE_Replace ); + switch( onError ){ + case OE_Rollback: + case OE_Abort: + case OE_Fail: { + int j, n1, n2; + char zErrMsg[200]; + strcpy(zErrMsg, pIdx->nColumn>1 ? "columns " : "column "); + n1 = strlen(zErrMsg); + for(j=0; jnColumn && n1aCol[pIdx->aiColumn[j]].zName; + n2 = strlen(zCol); + if( j>0 ){ + strcpy(&zErrMsg[n1], ", "); + n1 += 2; + } + if( n1+n2>sizeof(zErrMsg)-30 ){ + strcpy(&zErrMsg[n1], "..."); + n1 += 3; + break; + }else{ + strcpy(&zErrMsg[n1], zCol); + n1 += n2; + } + } + strcpy(&zErrMsg[n1], + pIdx->nColumn>1 ? " are not unique" : " is not unique"); + sqlite3VdbeOp3(v, OP_Halt, SQLITE_CONSTRAINT, onError, zErrMsg, 0); + break; + } + case OE_Ignore: { + assert( seenReplace==0 ); + sqlite3VdbeAddOp(v, OP_Pop, nCol+extra+3+hasTwoRowids, 0); + sqlite3VdbeAddOp(v, OP_Goto, 0, ignoreDest); + break; + } + case OE_Replace: { + sqlite3GenerateRowDelete(pParse->db, v, pTab, base, 0); + if( isUpdate ){ + sqlite3VdbeAddOp(v, OP_Dup, nCol+extra+1+hasTwoRowids, 1); + sqlite3VdbeAddOp(v, OP_MoveGe, base, 0); + } + seenReplace = 1; + break; + } + } +#if NULL_DISTINCT_FOR_UNIQUE + sqlite3VdbeJumpHere(v, jumpInst1); +#endif + sqlite3VdbeJumpHere(v, jumpInst2); + } +} + +/* +** This routine generates code to finish the INSERT or UPDATE operation +** that was started by a prior call to sqlite3GenerateConstraintChecks. +** The stack must contain keys for all active indices followed by data +** and the rowid for the new entry. This routine creates the new +** entries in all indices and in the main table. +** +** The arguments to this routine should be the same as the first six +** arguments to sqlite3GenerateConstraintChecks. +*/ +void sqlite3CompleteInsertion( + Parse *pParse, /* The parser context */ + Table *pTab, /* the table into which we are inserting */ + int base, /* Index of a read/write cursor pointing at pTab */ + char *aIdxUsed, /* Which indices are used. NULL means all are used */ + int rowidChng, /* True if the record number will change */ + int isUpdate, /* True for UPDATE, False for INSERT */ + int newIdx /* Index of NEW table for triggers. -1 if none */ +){ + int i; + Vdbe *v; + int nIdx; + Index *pIdx; + int pik_flags; + + v = sqlite3GetVdbe(pParse); + assert( v!=0 ); + assert( pTab->pSelect==0 ); /* This table is not a VIEW */ + for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){} + for(i=nIdx-1; i>=0; i--){ + if( aIdxUsed && aIdxUsed[i]==0 ) continue; + sqlite3VdbeAddOp(v, OP_IdxInsert, base+i+1, 0); + } + sqlite3VdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0); + sqlite3TableAffinityStr(v, pTab); +#ifndef SQLITE_OMIT_TRIGGER + if( newIdx>=0 ){ + sqlite3VdbeAddOp(v, OP_Dup, 1, 0); + sqlite3VdbeAddOp(v, OP_Dup, 1, 0); + sqlite3VdbeAddOp(v, OP_Insert, newIdx, 0); + } +#endif + if( pParse->nested ){ + pik_flags = 0; + }else{ + pik_flags = OPFLAG_NCHANGE; + pik_flags |= (isUpdate?OPFLAG_ISUPDATE:OPFLAG_LASTROWID); + } + sqlite3VdbeAddOp(v, OP_Insert, base, pik_flags); + if( !pParse->nested ){ + sqlite3VdbeChangeP3(v, -1, pTab->zName, P3_STATIC); + } + + if( isUpdate && rowidChng ){ + sqlite3VdbeAddOp(v, OP_Pop, 1, 0); + } +} + +/* +** Generate code that will open cursors for a table and for all +** indices of that table. The "base" parameter is the cursor number used +** for the table. Indices are opened on subsequent cursors. +*/ +void sqlite3OpenTableAndIndices( + Parse *pParse, /* Parsing context */ + Table *pTab, /* Table to be opened */ + int base, /* Cursor number assigned to the table */ + int op /* OP_OpenRead or OP_OpenWrite */ +){ + int i; + int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); + Index *pIdx; + Vdbe *v = sqlite3GetVdbe(pParse); + assert( v!=0 ); + sqlite3OpenTable(pParse, base, iDb, pTab, op); + for(i=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ + KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); + assert( pIdx->pSchema==pTab->pSchema ); + sqlite3VdbeAddOp(v, OP_Integer, iDb, 0); + VdbeComment((v, "# %s", pIdx->zName)); + sqlite3VdbeOp3(v, op, i+base, pIdx->tnum, (char*)pKey, P3_KEYINFO_HANDOFF); + } + if( pParse->nTab<=base+i ){ + pParse->nTab = base+i; + } +} Added: external/sqlite-source-3.3.4/keywordhash.h ============================================================================== --- (empty file) +++ external/sqlite-source-3.3.4/keywordhash.h Mon Apr 3 07:54:59 2006 @@ -0,0 +1,98 @@ +/* Hash score: 159 */ +static int keywordCode(const char *z, int n){ + static const char zText[537] = + "ABORTABLEFTEMPORARYADDATABASELECTHENDEFAULTRANSACTIONATURALTER" + "AISEACHECKEYAFTEREFERENCESCAPELSEXCEPTRIGGEREGEXPLAINITIALLYANALYZE" + "XCLUSIVEXISTSTATEMENTANDEFERRABLEATTACHAVINGLOBEFOREIGNOREINDEX" + "AUTOINCREMENTBEGINNERENAMEBETWEENOTNULLIKEBYCASCADEFERREDELETE" + "CASECASTCOLLATECOLUMNCOMMITCONFLICTCONSTRAINTERSECTCREATECROSS" + "CURRENT_DATECURRENT_TIMESTAMPLANDESCDETACHDISTINCTDROPRAGMATCH" + "FAILIMITFROMFULLGROUPDATEIFIMMEDIATEINSERTINSTEADINTOFFSETISNULL" + "JOINORDEREPLACEOUTERESTRICTPRIMARYQUERYRIGHTROLLBACKROWHENUNION" + "UNIQUEUSINGVACUUMVALUESVIEWHERE"; + static const unsigned char aHash[127] = { + 92, 80, 107, 91, 0, 4, 0, 0, 114, 0, 83, 0, 0, + 95, 44, 76, 93, 0, 106, 109, 97, 90, 0, 10, 0, 0, + 113, 0, 110, 103, 0, 28, 48, 0, 41, 0, 0, 65, 71, + 0, 63, 19, 0, 105, 36, 104, 0, 108, 74, 0, 0, 33, + 0, 61, 37, 0, 8, 0, 115, 38, 12, 0, 77, 40, 25, + 66, 0, 0, 31, 81, 53, 30, 50, 20, 88, 0, 34, 0, + 75, 26, 0, 72, 0, 0, 0, 64, 47, 67, 22, 87, 29, + 69, 86, 0, 1, 0, 9, 101, 58, 18, 0, 112, 82, 99, + 54, 6, 85, 0, 0, 49, 94, 0, 102, 0, 70, 0, 0, + 15, 0, 116, 51, 56, 0, 2, 55, 0, 111, + }; + static const unsigned char aNext[116] = { + 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, + 0, 11, 0, 0, 0, 0, 5, 13, 0, 7, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 43, 0, 0, 0, 0, 0, + 0, 0, 16, 0, 23, 52, 0, 0, 0, 0, 45, 0, 59, + 0, 0, 0, 0, 0, 0, 0, 0, 73, 42, 0, 24, 60, + 21, 0, 79, 0, 0, 68, 0, 0, 84, 46, 0, 0, 0, + 0, 0, 0, 0, 0, 39, 96, 98, 0, 0, 100, 0, 32, + 0, 14, 27, 78, 0, 57, 89, 0, 35, 0, 62, 0, + }; + static const unsigned char aLen[116] = { + 5, 5, 4, 4, 9, 2, 3, 8, 2, 6, 4, 3, 7, + 11, 2, 7, 5, 5, 4, 5, 3, 5, 10, 6, 4, 6, + 7, 6, 7, 9, 3, 7, 9, 6, 9, 3, 10, 6, 6, + 4, 6, 3, 7, 6, 7, 5, 13, 2, 2, 5, 5, 6, + 7, 3, 7, 4, 4, 2, 7, 3, 8, 6, 4, 4, 7, + 6, 6, 8, 10, 9, 6, 5, 12, 12, 17, 4, 4, 6, + 8, 2, 4, 6, 5, 4, 5, 4, 4, 5, 6, 2, 9, + 6, 7, 4, 2, 6, 3, 6, 4, 5, 7, 5, 8, 7, + 5, 5, 8, 3, 4, 5, 6, 5, 6, 6, 4, 5, + }; + static const unsigned short int aOffset[116] = { + 0, 4, 7, 10, 10, 14, 19, 21, 26, 27, 32, 34, 36, + 42, 51, 52, 57, 61, 65, 67, 71, 74, 78, 86, 91, 94, + 99, 105, 108, 113, 118, 122, 128, 136, 141, 150, 152, 162, 167, + 172, 175, 177, 177, 181, 185, 187, 192, 194, 196, 205, 208, 212, + 218, 224, 224, 227, 230, 234, 236, 237, 241, 248, 254, 258, 262, + 269, 275, 281, 289, 296, 305, 311, 316, 328, 328, 344, 348, 352, + 358, 359, 366, 369, 373, 378, 381, 386, 390, 394, 397, 403, 405, + 414, 420, 427, 430, 430, 433, 436, 442, 446, 450, 457, 461, 469, + 476, 481, 486, 494, 496, 500, 505, 511, 516, 522, 528, 531, + }; + static const unsigned char aCode[116] = { + TK_ABORT, TK_TABLE, TK_JOIN_KW, TK_TEMP, TK_TEMP, + TK_OR, TK_ADD, TK_DATABASE, TK_AS, TK_SELECT, + TK_THEN, TK_END, TK_DEFAULT, TK_TRANSACTION,TK_ON, + TK_JOIN_KW, TK_ALTER, TK_RAISE, TK_EACH, TK_CHECK, + TK_KEY, TK_AFTER, TK_REFERENCES, TK_ESCAPE, TK_ELSE, + TK_EXCEPT, TK_TRIGGER, TK_LIKE_KW, TK_EXPLAIN, TK_INITIALLY, + TK_ALL, TK_ANALYZE, TK_EXCLUSIVE, TK_EXISTS, TK_STATEMENT, + TK_AND, TK_DEFERRABLE, TK_ATTACH, TK_HAVING, TK_LIKE_KW, + TK_BEFORE, TK_FOR, TK_FOREIGN, TK_IGNORE, TK_REINDEX, + TK_INDEX, TK_AUTOINCR, TK_TO, TK_IN, TK_BEGIN, + TK_JOIN_KW, TK_RENAME, TK_BETWEEN, TK_NOT, TK_NOTNULL, + TK_NULL, TK_LIKE_KW, TK_BY, TK_CASCADE, TK_ASC, + TK_DEFERRED, TK_DELETE, TK_CASE, TK_CAST, TK_COLLATE, + TK_COLUMNKW, TK_COMMIT, TK_CONFLICT, TK_CONSTRAINT, TK_INTERSECT, + TK_CREATE, TK_JOIN_KW, TK_CTIME_KW, TK_CTIME_KW, TK_CTIME_KW, + TK_PLAN, TK_DESC, TK_DETACH, TK_DISTINCT, TK_IS, + TK_DROP, TK_PRAGMA, TK_MATCH, TK_FAIL, TK_LIMIT, + TK_FROM, TK_JOIN_KW, TK_GROUP, TK_UPDATE, TK_IF, + TK_IMMEDIATE, TK_INSERT, TK_INSTEAD, TK_INTO, TK_OF, + TK_OFFSET, TK_SET, TK_ISNULL, TK_JOIN, TK_ORDER, + TK_REPLACE, TK_JOIN_KW, TK_RESTRICT, TK_PRIMARY, TK_QUERY, + TK_JOIN_KW, TK_ROLLBACK, TK_ROW, TK_WHEN, TK_UNION, + TK_UNIQUE, TK_USING, TK_VACUUM, TK_VALUES, TK_VIEW, + TK_WHERE, + }; + int h, i; + if( n<2 ) return TK_ID; + h = ((sqlite3UpperToLower[((unsigned char*)z)[0]]*4) ^ + (sqlite3UpperToLower[((unsigned char*)z)[n-1]]*3) ^ + n) % 127; + for(i=((int)aHash[h])-1; i>=0; i=((int)aNext[i])-1){ + if( aLen[i]==n && sqlite3StrNICmp(&zText[aOffset[i]],z,n)==0 ){ + return aCode[i]; + } + } + return TK_ID; +} +int sqlite3KeywordCode(const unsigned char *z, int n){ + return keywordCode((char*)z, n); +} Added: external/sqlite-source-3.3.4/legacy.c ============================================================================== --- (empty file) +++ external/sqlite-source-3.3.4/legacy.c Mon Apr 3 07:54:59 2006 @@ -0,0 +1,135 @@ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** Main file for the SQLite library. The routines in this file +** implement the programmer interface to the library. Routines in +** other files are for internal use by SQLite and should not be +** accessed by users of the library. +** +** $Id: legacy.c,v 1.13 2006/01/23 13:14:55 drh Exp $ +*/ + +#include "sqliteInt.h" +#include "os.h" +#include + +/* +** Execute SQL code. Return one of the SQLITE_ success/failure +** codes. Also write an error message into memory obtained from +** malloc() and make *pzErrMsg point to that message. +** +** If the SQL is a query, then for each row in the query result +** the xCallback() function is called. pArg becomes the first +** argument to xCallback(). If xCallback=NULL then no callback +** is invoked, even for queries. +*/ +int sqlite3_exec( + sqlite3 *db, /* The database on which the SQL executes */ + const char *zSql, /* The SQL to be executed */ + sqlite3_callback xCallback, /* Invoke this callback routine */ + void *pArg, /* First argument to xCallback() */ + char **pzErrMsg /* Write error messages here */ +){ + int rc = SQLITE_OK; + const char *zLeftover; + sqlite3_stmt *pStmt = 0; + char **azCols = 0; + + int nRetry = 0; + int nChange = 0; + int nCallback; + + if( zSql==0 ) return SQLITE_OK; + while( (rc==SQLITE_OK || (rc==SQLITE_SCHEMA && (++nRetry)<2)) && zSql[0] ){ + int nCol; + char **azVals = 0; + + pStmt = 0; + rc = sqlite3_prepare(db, zSql, -1, &pStmt, &zLeftover); + if( rc!=SQLITE_OK ){ + if( pStmt ) sqlite3_finalize(pStmt); + continue; + } + if( !pStmt ){ + /* this happens for a comment or white-space */ + zSql = zLeftover; + continue; + } + + db->nChange += nChange; + nCallback = 0; + + nCol = sqlite3_column_count(pStmt); + azCols = sqliteMalloc(2*nCol*sizeof(const char *) + 1); + if( azCols==0 ){ + goto exec_out; + } + + while( 1 ){ + int i; + rc = sqlite3_step(pStmt); + + /* Invoke the callback function if required */ + if( xCallback && (SQLITE_ROW==rc || + (SQLITE_DONE==rc && !nCallback && db->flags&SQLITE_NullCallback)) ){ + if( 0==nCallback ){ + for(i=0; ipVdbe==0 ){ + nChange = db->nChange; + } + if( rc!=SQLITE_SCHEMA ){ + nRetry = 0; + zSql = zLeftover; + while( isspace((unsigned char)zSql[0]) ) zSql++; + } + break; + } + } + + sqliteFree(azCols); + azCols = 0; + } + +exec_out: + if( pStmt ) sqlite3_finalize(pStmt); + if( azCols ) sqliteFree(azCols); + + rc = sqlite3ApiExit(0, rc); + if( rc!=SQLITE_OK && rc==sqlite3_errcode(db) && pzErrMsg ){ + *pzErrMsg = malloc(1+strlen(sqlite3_errmsg(db))); + if( *pzErrMsg ){ + strcpy(*pzErrMsg, sqlite3_errmsg(db)); + } + }else if( pzErrMsg ){ + *pzErrMsg = 0; + } + + return rc; +} Added: external/sqlite-source-3.3.4/main.c ============================================================================== --- (empty file) +++ external/sqlite-source-3.3.4/main.c Mon Apr 3 07:54:59 2006 @@ -0,0 +1,1233 @@ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** Main file for the SQLite library. The routines in this file +** implement the programmer interface to the library. Routines in +** other files are for internal use by SQLite and should not be +** accessed by users of the library. +** +** $Id: main.c,v 1.334 2006/02/09 13:43:29 danielk1977 Exp $ +*/ +#include "sqliteInt.h" +#include "os.h" +#include + +/* +** The following constant value is used by the SQLITE_BIGENDIAN and +** SQLITE_LITTLEENDIAN macros. +*/ +const int sqlite3one = 1; + +/* +** The version of the library +*/ +const char sqlite3_version[] = SQLITE_VERSION; +const char *sqlite3_libversion(void){ return sqlite3_version; } +int sqlite3_libversion_number(void){ return SQLITE_VERSION_NUMBER; } + +/* +** This is the default collating function named "BINARY" which is always +** available. +*/ +static int binCollFunc( + void *NotUsed, + int nKey1, const void *pKey1, + int nKey2, const void *pKey2 +){ + int rc, n; + n = nKey1lastRowid; +} + +/* +** Return the number of changes in the most recent call to sqlite3_exec(). +*/ +int sqlite3_changes(sqlite3 *db){ + return db->nChange; +} + +/* +** Return the number of changes since the database handle was opened. +*/ +int sqlite3_total_changes(sqlite3 *db){ + return db->nTotalChange; +} + +/* +** Close an existing SQLite database +*/ +int sqlite3_close(sqlite3 *db){ + HashElem *i; + int j; + + if( !db ){ + return SQLITE_OK; + } + if( sqlite3SafetyCheck(db) ){ + return SQLITE_MISUSE; + } + +#ifdef SQLITE_SSE + sqlite3_finalize(db->pFetch); +#endif + + /* If there are any outstanding VMs, return SQLITE_BUSY. */ + if( db->pVdbe ){ + sqlite3Error(db, SQLITE_BUSY, + "Unable to close due to unfinalised statements"); + return SQLITE_BUSY; + } + assert( !sqlite3SafetyCheck(db) ); + + /* FIX ME: db->magic may be set to SQLITE_MAGIC_CLOSED if the database + ** cannot be opened for some reason. So this routine needs to run in + ** that case. But maybe there should be an extra magic value for the + ** "failed to open" state. + */ + if( db->magic!=SQLITE_MAGIC_CLOSED && sqlite3SafetyOn(db) ){ + /* printf("DID NOT CLOSE\n"); fflush(stdout); */ + return SQLITE_ERROR; + } + + for(j=0; jnDb; j++){ + struct Db *pDb = &db->aDb[j]; + if( pDb->pBt ){ + sqlite3BtreeClose(pDb->pBt); + pDb->pBt = 0; + if( j!=1 ){ + pDb->pSchema = 0; + } + } + } + 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; + sqliteFree(pFunc); + } + } + + for(i=sqliteHashFirst(&db->aCollSeq); i; i=sqliteHashNext(i)){ + CollSeq *pColl = (CollSeq *)sqliteHashData(i); + sqliteFree(pColl); + } + sqlite3HashClear(&db->aCollSeq); + + sqlite3HashClear(&db->aFunc); + sqlite3Error(db, SQLITE_OK, 0); /* Deallocates any cached error strings. */ + if( db->pErr ){ + sqlite3ValueFree(db->pErr); + } + + db->magic = SQLITE_MAGIC_ERROR; + + /* The temp-database schema is allocated differently from the other schema + ** objects (using sqliteMalloc() directly, instead of sqlite3BtreeSchema()). + ** So it needs to be freed here. Todo: Why not roll the temp schema into + ** the same sqliteMalloc() as the one that allocates the database + ** structure? + */ + sqliteFree(db->aDb[1].pSchema); + sqliteFree(db); + sqlite3ReleaseThreadData(); + return SQLITE_OK; +} + +/* +** Rollback all database files. +*/ +void sqlite3RollbackAll(sqlite3 *db){ + int i; + int inTrans = 0; + for(i=0; inDb; i++){ + if( db->aDb[i].pBt ){ + if( sqlite3BtreeIsInTrans(db->aDb[i].pBt) ){ + inTrans = 1; + } + sqlite3BtreeRollback(db->aDb[i].pBt); + db->aDb[i].inTrans = 0; + } + } + if( db->flags&SQLITE_InternChanges ){ + sqlite3ResetInternalSchema(db, 0); + } + + /* If one has been configured, invoke the rollback-hook callback */ + if( db->xRollbackCallback && (inTrans || !db->autoCommit) ){ + db->xRollbackCallback(db->pRollbackArg); + } +} + +/* +** Return a static string that describes the kind of error specified in the +** argument. +*/ +const char *sqlite3ErrStr(int rc){ + const char *z; + switch( rc ){ + case SQLITE_ROW: + case SQLITE_DONE: + case SQLITE_OK: z = "not an error"; break; + case SQLITE_ERROR: z = "SQL logic error or missing database"; break; + case SQLITE_PERM: z = "access permission denied"; break; + case SQLITE_ABORT: z = "callback requested query abort"; break; + case SQLITE_BUSY: z = "database is locked"; break; + case SQLITE_LOCKED: z = "database table is locked"; break; + case SQLITE_NOMEM: z = "out of memory"; break; + case SQLITE_READONLY: z = "attempt to write a readonly database"; break; + case SQLITE_INTERRUPT: z = "interrupted"; break; + case SQLITE_IOERR: z = "disk I/O error"; break; + case SQLITE_CORRUPT: z = "database disk image is malformed"; break; + case SQLITE_FULL: z = "database or disk is full"; break; + case SQLITE_CANTOPEN: z = "unable to open database file"; break; + case SQLITE_PROTOCOL: z = "database locking protocol failure"; break; + case SQLITE_EMPTY: z = "table contains no data"; break; + case SQLITE_SCHEMA: z = "database schema has changed"; break; + case SQLITE_CONSTRAINT: z = "constraint failed"; break; + case SQLITE_MISMATCH: z = "datatype mismatch"; break; + case SQLITE_MISUSE: z = "library routine called out of sequence";break; + case SQLITE_NOLFS: z = "kernel lacks large file support"; break; + case SQLITE_AUTH: z = "authorization denied"; break; + case SQLITE_FORMAT: z = "auxiliary database format error"; break; + case SQLITE_RANGE: z = "bind or column index out of range"; break; + case SQLITE_NOTADB: z = "file is encrypted or is not a database";break; + default: z = "unknown error"; break; + } + return z; +} + +/* +** This routine implements a busy callback that sleeps and tries +** again until a timeout value is reached. The timeout value is +** an integer number of milliseconds passed in as the first +** argument. +*/ +static int sqliteDefaultBusyCallback( + void *ptr, /* Database connection */ + int count /* Number of times table has been busy */ +){ +#if SQLITE_MIN_SLEEP_MS==1 + static const u8 delays[] = + { 1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 100 }; + static const u8 totals[] = + { 0, 1, 3, 8, 18, 33, 53, 78, 103, 128, 178, 228 }; +# define NDELAY (sizeof(delays)/sizeof(delays[0])) + int timeout = ((sqlite3 *)ptr)->busyTimeout; + int delay, prior; + + assert( count>=0 ); + if( count < NDELAY ){ + delay = delays[count]; + prior = totals[count]; + }else{ + delay = delays[NDELAY-1]; + prior = totals[NDELAY-1] + delay*(count-(NDELAY-1)); + } + if( prior + delay > timeout ){ + delay = timeout - prior; + if( delay<=0 ) return 0; + } + sqlite3OsSleep(delay); + return 1; +#else + int timeout = ((sqlite3 *)ptr)->busyTimeout; + if( (count+1)*1000 > timeout ){ + return 0; + } + sqlite3OsSleep(1000); + return 1; +#endif +} + +/* +** Invoke the given busy handler. +** +** This routine is called when an operation failed with a lock. +** If this routine returns non-zero, the lock is retried. If it +** returns 0, the operation aborts with an SQLITE_BUSY error. +*/ +int sqlite3InvokeBusyHandler(BusyHandler *p){ + int rc; + if( p==0 || p->xFunc==0 || p->nBusy<0 ) return 0; + rc = p->xFunc(p->pArg, p->nBusy); + if( rc==0 ){ + p->nBusy = -1; + }else{ + p->nBusy++; + } + return rc; +} + +/* +** This routine sets the busy callback for an Sqlite database to the +** given callback function with the given argument. +*/ +int sqlite3_busy_handler( + sqlite3 *db, + int (*xBusy)(void*,int), + void *pArg +){ + if( sqlite3SafetyCheck(db) ){ + return SQLITE_MISUSE; + } + db->busyHandler.xFunc = xBusy; + db->busyHandler.pArg = pArg; + db->busyHandler.nBusy = 0; + return SQLITE_OK; +} + +#ifndef SQLITE_OMIT_PROGRESS_CALLBACK +/* +** This routine sets the progress callback for an Sqlite database to the +** given callback function with the given argument. The progress callback will +** be invoked every nOps opcodes. +*/ +void sqlite3_progress_handler( + sqlite3 *db, + int nOps, + int (*xProgress)(void*), + void *pArg +){ + if( !sqlite3SafetyCheck(db) ){ + if( nOps>0 ){ + db->xProgress = xProgress; + db->nProgressOps = nOps; + db->pProgressArg = pArg; + }else{ + db->xProgress = 0; + db->nProgressOps = 0; + db->pProgressArg = 0; + } + } +} +#endif + + +/* +** This routine installs a default busy handler that waits for the +** specified number of milliseconds before returning 0. +*/ +int sqlite3_busy_timeout(sqlite3 *db, int ms){ + if( ms>0 ){ + db->busyTimeout = ms; + sqlite3_busy_handler(db, sqliteDefaultBusyCallback, (void*)db); + }else{ + sqlite3_busy_handler(db, 0, 0); + } + return SQLITE_OK; +} + +/* +** Cause any pending operation to stop at its earliest opportunity. +*/ +void sqlite3_interrupt(sqlite3 *db){ + if( !sqlite3SafetyCheck(db) ){ + db->flags |= SQLITE_Interrupt; + } +} + +/* +** Windows systems should call this routine to free memory that +** is returned in the in the errmsg parameter of sqlite3_open() when +** SQLite is a DLL. For some reason, it does not work to call free() +** directly. +** +** Note that we need to call free() not sqliteFree() here. +*/ +void sqlite3_free(char *p){ free(p); } + +/* +** This function is exactly the same as sqlite3_create_function(), except +** that it is designed to be called by internal code. The difference is +** that if a malloc() fails in sqlite3_create_function(), an error code +** is returned and the mallocFailed flag cleared. +*/ +int sqlite3CreateFunc( + sqlite3 *db, + const char *zFunctionName, + int nArg, + int enc, + void *pUserData, + void (*xFunc)(sqlite3_context*,int,sqlite3_value **), + void (*xStep)(sqlite3_context*,int,sqlite3_value **), + void (*xFinal)(sqlite3_context*) +){ + FuncDef *p; + int nName; + + if( sqlite3SafetyCheck(db) ){ + return SQLITE_MISUSE; + } + if( zFunctionName==0 || + (xFunc && (xFinal || xStep)) || + (!xFunc && (xFinal && !xStep)) || + (!xFunc && (!xFinal && xStep)) || + (nArg<-1 || nArg>127) || + (255<(nName = strlen(zFunctionName))) ){ + return SQLITE_ERROR; + } + +#ifndef SQLITE_OMIT_UTF16 + /* If SQLITE_UTF16 is specified as the encoding type, transform this + ** to one of SQLITE_UTF16LE or SQLITE_UTF16BE using the + ** SQLITE_UTF16NATIVE macro. SQLITE_UTF16 is not used internally. + ** + ** If SQLITE_ANY is specified, add three versions of the function + ** to the hash table. + */ + if( enc==SQLITE_UTF16 ){ + enc = SQLITE_UTF16NATIVE; + }else if( enc==SQLITE_ANY ){ + int rc; + rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF8, + pUserData, xFunc, xStep, xFinal); + if( rc!=SQLITE_OK ) return rc; + rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF16LE, + pUserData, xFunc, xStep, xFinal); + if( rc!=SQLITE_OK ) return rc; + enc = SQLITE_UTF16BE; + } +#else + enc = SQLITE_UTF8; +#endif + + /* Check if an existing function is being overridden or deleted. If so, + ** and there are active VMs, then return SQLITE_BUSY. If a function + ** is being overridden/deleted but there are no active VMs, allow the + ** operation to continue but invalidate all precompiled statements. + */ + p = sqlite3FindFunction(db, zFunctionName, nName, nArg, enc, 0); + if( p && p->iPrefEnc==enc && p->nArg==nArg ){ + if( db->activeVdbeCnt ){ + sqlite3Error(db, SQLITE_BUSY, + "Unable to delete/modify user-function due to active statements"); + assert( !sqlite3MallocFailed() ); + return SQLITE_BUSY; + }else{ + sqlite3ExpirePreparedStatements(db); + } + } + + p = sqlite3FindFunction(db, zFunctionName, nName, nArg, enc, 1); + if( p ){ + p->flags = 0; + p->xFunc = xFunc; + p->xStep = xStep; + p->xFinalize = xFinal; + p->pUserData = pUserData; + } + return SQLITE_OK; +} + +/* +** Create new user functions. +*/ +int sqlite3_create_function( + sqlite3 *db, + const char *zFunctionName, + int nArg, + int enc, + void *p, + void (*xFunc)(sqlite3_context*,int,sqlite3_value **), + void (*xStep)(sqlite3_context*,int,sqlite3_value **), + void (*xFinal)(sqlite3_context*) +){ + int rc; + assert( !sqlite3MallocFailed() ); + rc = sqlite3CreateFunc(db, zFunctionName, nArg, enc, p, xFunc, xStep, xFinal); + + return sqlite3ApiExit(db, rc); +} + +#ifndef SQLITE_OMIT_UTF16 +int sqlite3_create_function16( + sqlite3 *db, + const void *zFunctionName, + int nArg, + int eTextRep, + void *p, + void (*xFunc)(sqlite3_context*,int,sqlite3_value**), + void (*xStep)(sqlite3_context*,int,sqlite3_value**), + void (*xFinal)(sqlite3_context*) +){ + int rc; + char *zFunc8; + assert( !sqlite3MallocFailed() ); + + zFunc8 = sqlite3utf16to8(zFunctionName, -1); + rc = sqlite3CreateFunc(db, zFunc8, nArg, eTextRep, p, xFunc, xStep, xFinal); + sqliteFree(zFunc8); + + return sqlite3ApiExit(db, rc); +} +#endif + +#ifndef SQLITE_OMIT_TRACE +/* +** Register a trace function. The pArg from the previously registered trace +** is returned. +** +** A NULL trace function means that no tracing is executes. A non-NULL +** trace is a pointer to a function that is invoked at the start of each +** SQL statement. +*/ +void *sqlite3_trace(sqlite3 *db, void (*xTrace)(void*,const char*), void *pArg){ + void *pOld = db->pTraceArg; + db->xTrace = xTrace; + db->pTraceArg = pArg; + return pOld; +} +/* +** Register a profile function. The pArg from the previously registered +** profile function is returned. +** +** A NULL profile function means that no profiling is executes. A non-NULL +** profile is a pointer to a function that is invoked at the conclusion of +** each SQL statement that is run. +*/ +void *sqlite3_profile( + sqlite3 *db, + void (*xProfile)(void*,const char*,sqlite_uint64), + void *pArg +){ + void *pOld = db->pProfileArg; + db->xProfile = xProfile; + db->pProfileArg = pArg; + return pOld; +} +#endif /* SQLITE_OMIT_TRACE */ + +/*** EXPERIMENTAL *** +** +** Register a function to be invoked when a transaction comments. +** If the invoked function returns non-zero, then the commit becomes a +** rollback. +*/ +void *sqlite3_commit_hook( + sqlite3 *db, /* Attach the hook to this database */ + int (*xCallback)(void*), /* Function to invoke on each commit */ + void *pArg /* Argument to the function */ +){ + void *pOld = db->pCommitArg; + db->xCommitCallback = xCallback; + db->pCommitArg = pArg; + return pOld; +} + +/* +** Register a callback to be invoked each time a row is updated, +** inserted or deleted using this database connection. +*/ +void *sqlite3_update_hook( + sqlite3 *db, /* Attach the hook to this database */ + void (*xCallback)(void*,int,char const *,char const *,sqlite_int64), + void *pArg /* Argument to the function */ +){ + void *pRet = db->pUpdateArg; + db->xUpdateCallback = xCallback; + db->pUpdateArg = pArg; + return pRet; +} + +/* +** Register a callback to be invoked each time a transaction is rolled +** back by this database connection. +*/ +void *sqlite3_rollback_hook( + sqlite3 *db, /* Attach the hook to this database */ + void (*xCallback)(void*), /* Callback function */ + void *pArg /* Argument to the function */ +){ + void *pRet = db->pRollbackArg; + db->xRollbackCallback = xCallback; + db->pRollbackArg = pArg; + return pRet; +} + +/* +** This routine is called to create a connection to a database BTree +** driver. If zFilename is the name of a file, then that file is +** opened and used. If zFilename is the magic name ":memory:" then +** the database is stored in memory (and is thus forgotten as soon as +** the connection is closed.) If zFilename is NULL then the database +** is a "virtual" database for transient use only and is deleted as +** soon as the connection is closed. +** +** A virtual database can be either a disk file (that is automatically +** deleted when the file is closed) or it an be held entirely in memory, +** depending on the values of the TEMP_STORE compile-time macro and the +** db->temp_store variable, according to the following chart: +** +** TEMP_STORE db->temp_store Location of temporary database +** ---------- -------------- ------------------------------ +** 0 any file +** 1 1 file +** 1 2 memory +** 1 0 file +** 2 1 file +** 2 2 memory +** 2 0 memory +** 3 any memory +*/ +int sqlite3BtreeFactory( + const sqlite3 *db, /* Main database when opening aux otherwise 0 */ + const char *zFilename, /* Name of the file containing the BTree database */ + int omitJournal, /* if TRUE then do not journal this file */ + int nCache, /* How many pages in the page cache */ + Btree **ppBtree /* Pointer to new Btree object written here */ +){ + int btree_flags = 0; + int rc; + + assert( ppBtree != 0); + if( omitJournal ){ + btree_flags |= BTREE_OMIT_JOURNAL; + } + if( db->flags & SQLITE_NoReadlock ){ + btree_flags |= BTREE_NO_READLOCK; + } + if( zFilename==0 ){ +#if TEMP_STORE==0 + /* Do nothing */ +#endif +#ifndef SQLITE_OMIT_MEMORYDB +#if TEMP_STORE==1 + if( db->temp_store==2 ) zFilename = ":memory:"; +#endif +#if TEMP_STORE==2 + if( db->temp_store!=1 ) zFilename = ":memory:"; +#endif +#if TEMP_STORE==3 + zFilename = ":memory:"; +#endif +#endif /* SQLITE_OMIT_MEMORYDB */ + } + + rc = sqlite3BtreeOpen(zFilename, (sqlite3 *)db, ppBtree, btree_flags); + if( rc==SQLITE_OK ){ + sqlite3BtreeSetBusyHandler(*ppBtree, (void*)&db->busyHandler); + sqlite3BtreeSetCacheSize(*ppBtree, nCache); + } + return rc; +} + +/* +** Return UTF-8 encoded English language explanation of the most recent +** error. +*/ +const char *sqlite3_errmsg(sqlite3 *db){ + const char *z; + if( !db || sqlite3MallocFailed() ){ + return sqlite3ErrStr(SQLITE_NOMEM); + } + if( sqlite3SafetyCheck(db) || db->errCode==SQLITE_MISUSE ){ + return sqlite3ErrStr(SQLITE_MISUSE); + } + z = (char*)sqlite3_value_text(db->pErr); + if( z==0 ){ + z = sqlite3ErrStr(db->errCode); + } + return z; +} + +#ifndef SQLITE_OMIT_UTF16 +/* +** Return UTF-16 encoded English language explanation of the most recent +** error. +*/ +const void *sqlite3_errmsg16(sqlite3 *db){ + /* Because all the characters in the string are in the unicode + ** range 0x00-0xFF, if we pad the big-endian string with a + ** zero byte, we can obtain the little-endian string with + ** &big_endian[1]. + */ + static const char outOfMemBe[] = { + 0, 'o', 0, 'u', 0, 't', 0, ' ', + 0, 'o', 0, 'f', 0, ' ', + 0, 'm', 0, 'e', 0, 'm', 0, 'o', 0, 'r', 0, 'y', 0, 0, 0 + }; + static const char misuseBe [] = { + 0, 'l', 0, 'i', 0, 'b', 0, 'r', 0, 'a', 0, 'r', 0, 'y', 0, ' ', + 0, 'r', 0, 'o', 0, 'u', 0, 't', 0, 'i', 0, 'n', 0, 'e', 0, ' ', + 0, 'c', 0, 'a', 0, 'l', 0, 'l', 0, 'e', 0, 'd', 0, ' ', + 0, 'o', 0, 'u', 0, 't', 0, ' ', + 0, 'o', 0, 'f', 0, ' ', + 0, 's', 0, 'e', 0, 'q', 0, 'u', 0, 'e', 0, 'n', 0, 'c', 0, 'e', 0, 0, 0 + }; + + const void *z; + if( sqlite3MallocFailed() ){ + return (void *)(&outOfMemBe[SQLITE_UTF16NATIVE==SQLITE_UTF16LE?1:0]); + } + if( sqlite3SafetyCheck(db) || db->errCode==SQLITE_MISUSE ){ + return (void *)(&misuseBe[SQLITE_UTF16NATIVE==SQLITE_UTF16LE?1:0]); + } + z = sqlite3_value_text16(db->pErr); + if( z==0 ){ + sqlite3ValueSetStr(db->pErr, -1, sqlite3ErrStr(db->errCode), + SQLITE_UTF8, SQLITE_STATIC); + z = sqlite3_value_text16(db->pErr); + } + sqlite3ApiExit(0, 0); + return z; +} +#endif /* SQLITE_OMIT_UTF16 */ + +/* +** Return the most recent error code generated by an SQLite routine. If NULL is +** passed to this function, we assume a malloc() failed during sqlite3_open(). +*/ +int sqlite3_errcode(sqlite3 *db){ + if( !db || sqlite3MallocFailed() ){ + return SQLITE_NOMEM; + } + if( sqlite3SafetyCheck(db) ){ + return SQLITE_MISUSE; + } + return db->errCode; +} + +/* +** Create a new collating function for database "db". The name is zName +** and the encoding is enc. +*/ +static int createCollation( + sqlite3* db, + const char *zName, + int enc, + void* pCtx, + int(*xCompare)(void*,int,const void*,int,const void*) +){ + CollSeq *pColl; + + if( sqlite3SafetyCheck(db) ){ + return SQLITE_MISUSE; + } + + /* If SQLITE_UTF16 is specified as the encoding type, transform this + ** to one of SQLITE_UTF16LE or SQLITE_UTF16BE using the + ** SQLITE_UTF16NATIVE macro. SQLITE_UTF16 is not used internally. + */ + if( enc==SQLITE_UTF16 ){ + enc = SQLITE_UTF16NATIVE; + } + + if( enc!=SQLITE_UTF8 && enc!=SQLITE_UTF16LE && enc!=SQLITE_UTF16BE ){ + sqlite3Error(db, SQLITE_ERROR, + "Param 3 to sqlite3_create_collation() must be one of " + "SQLITE_UTF8, SQLITE_UTF16, SQLITE_UTF16LE or SQLITE_UTF16BE" + ); + return SQLITE_ERROR; + } + + /* Check if this call is removing or replacing an existing collation + ** sequence. If so, and there are active VMs, return busy. If there + ** are no active VMs, invalidate any pre-compiled statements. + */ + pColl = sqlite3FindCollSeq(db, (u8)enc, zName, strlen(zName), 0); + if( pColl && pColl->xCmp ){ + if( db->activeVdbeCnt ){ + sqlite3Error(db, SQLITE_BUSY, + "Unable to delete/modify collation sequence due to active statements"); + return SQLITE_BUSY; + } + sqlite3ExpirePreparedStatements(db); + } + + pColl = sqlite3FindCollSeq(db, (u8)enc, zName, strlen(zName), 1); + if( pColl ){ + pColl->xCmp = xCompare; + pColl->pUser = pCtx; + pColl->enc = enc; + } + sqlite3Error(db, SQLITE_OK, 0); + return SQLITE_OK; +} + + +/* +** This routine does the work of opening a database on behalf of +** sqlite3_open() and sqlite3_open16(). The database filename "zFilename" +** is UTF-8 encoded. +*/ +static int openDatabase( + const char *zFilename, /* Database filename UTF-8 encoded */ + sqlite3 **ppDb /* OUT: Returned database handle */ +){ + sqlite3 *db; + int rc; + CollSeq *pColl; + + assert( !sqlite3MallocFailed() ); + + /* Allocate the sqlite data structure */ + db = sqliteMalloc( sizeof(sqlite3) ); + if( db==0 ) goto opendb_out; + db->priorNewRowid = 0; + db->magic = SQLITE_MAGIC_BUSY; + db->nDb = 2; + db->aDb = db->aDbStatic; + db->autoCommit = 1; + db->flags |= SQLITE_ShortColNames; + sqlite3HashInit(&db->aFunc, SQLITE_HASH_STRING, 0); + sqlite3HashInit(&db->aCollSeq, SQLITE_HASH_STRING, 0); + + /* Add the default collation sequence BINARY. BINARY works for both UTF-8 + ** and UTF-16, so add a version for each to avoid any unnecessary + ** conversions. The only error that can occur here is a malloc() failure. + */ + if( createCollation(db, "BINARY", SQLITE_UTF8, 0, binCollFunc) || + createCollation(db, "BINARY", SQLITE_UTF16BE, 0, binCollFunc) || + createCollation(db, "BINARY", SQLITE_UTF16LE, 0, binCollFunc) || + (db->pDfltColl = sqlite3FindCollSeq(db, SQLITE_UTF8, "BINARY", 6, 0))==0 + ){ + assert( sqlite3MallocFailed() ); + db->magic = SQLITE_MAGIC_CLOSED; + goto opendb_out; + } + + /* Also add a UTF-8 case-insensitive collation sequence. */ + createCollation(db, "NOCASE", SQLITE_UTF8, 0, nocaseCollatingFunc); + + /* Set flags on the built-in collating sequences */ + db->pDfltColl->type = SQLITE_COLL_BINARY; + pColl = sqlite3FindCollSeq(db, SQLITE_UTF8, "NOCASE", 6, 0); + if( pColl ){ + pColl->type = SQLITE_COLL_NOCASE; + } + + /* Open the backend database driver */ + rc = sqlite3BtreeFactory(db, zFilename, 0, MAX_PAGES, &db->aDb[0].pBt); + if( rc!=SQLITE_OK ){ + sqlite3Error(db, rc, 0); + db->magic = SQLITE_MAGIC_CLOSED; + goto opendb_out; + } +#ifndef SQLITE_OMIT_PARSER + db->aDb[0].pSchema = sqlite3SchemaGet(db->aDb[0].pBt); + db->aDb[1].pSchema = sqlite3SchemaGet(0); +#endif + + if( db->aDb[0].pSchema ){ + ENC(db) = SQLITE_UTF8; + } + + /* The default safety_level for the main database is 'full'; for the temp + ** database it is 'NONE'. This matches the pager layer defaults. + */ + db->aDb[0].zName = "main"; + db->aDb[0].safety_level = 3; +#ifndef SQLITE_OMIT_TEMPDB + db->aDb[1].zName = "temp"; + db->aDb[1].safety_level = 1; +#endif + + /* Register all built-in functions, but do not attempt to read the + ** database schema yet. This is delayed until the first time the database + ** is accessed. + */ + if( !sqlite3MallocFailed() ){ + sqlite3RegisterBuiltinFunctions(db); + sqlite3Error(db, SQLITE_OK, 0); + } + db->magic = SQLITE_MAGIC_OPEN; + +opendb_out: + if( SQLITE_NOMEM==(rc = sqlite3_errcode(db)) ){ + sqlite3_close(db); + db = 0; + } + *ppDb = db; + return sqlite3ApiExit(0, rc); +} + +/* +** Open a new database handle. +*/ +int sqlite3_open( + const char *zFilename, + sqlite3 **ppDb +){ + return openDatabase(zFilename, ppDb); +} + +#ifndef SQLITE_OMIT_UTF16 +/* +** Open a new database handle. +*/ +int sqlite3_open16( + const void *zFilename, + sqlite3 **ppDb +){ + char const *zFilename8; /* zFilename encoded in UTF-8 instead of UTF-16 */ + int rc = SQLITE_OK; + sqlite3_value *pVal; + + assert( zFilename ); + assert( ppDb ); + *ppDb = 0; + pVal = sqlite3ValueNew(); + sqlite3ValueSetStr(pVal, -1, zFilename, SQLITE_UTF16NATIVE, SQLITE_STATIC); + zFilename8 = sqlite3ValueText(pVal, SQLITE_UTF8); + if( zFilename8 ){ + rc = openDatabase(zFilename8, ppDb); + if( rc==SQLITE_OK && *ppDb ){ + rc = sqlite3_exec(*ppDb, "PRAGMA encoding = 'UTF-16'", 0, 0, 0); + if( rc!=SQLITE_OK ){ + sqlite3_close(*ppDb); + *ppDb = 0; + } + } + } + sqlite3ValueFree(pVal); + + return sqlite3ApiExit(0, rc); +} +#endif /* SQLITE_OMIT_UTF16 */ + +/* +** The following routine destroys a virtual machine that is created by +** the sqlite3_compile() routine. The integer returned is an SQLITE_ +** success/failure code that describes the result of executing the virtual +** machine. +** +** This routine sets the error code and string returned by +** sqlite3_errcode(), sqlite3_errmsg() and sqlite3_errmsg16(). +*/ +int sqlite3_finalize(sqlite3_stmt *pStmt){ + int rc; + if( pStmt==0 ){ + rc = SQLITE_OK; + }else{ + rc = sqlite3VdbeFinalize((Vdbe*)pStmt); + } + return rc; +} + +/* +** Terminate the current execution of an SQL statement and reset it +** back to its starting state so that it can be reused. A success code from +** the prior execution is returned. +** +** This routine sets the error code and string returned by +** sqlite3_errcode(), sqlite3_errmsg() and sqlite3_errmsg16(). +*/ +int sqlite3_reset(sqlite3_stmt *pStmt){ + int rc; + if( pStmt==0 ){ + rc = SQLITE_OK; + }else{ + rc = sqlite3VdbeReset((Vdbe*)pStmt); + sqlite3VdbeMakeReady((Vdbe*)pStmt, -1, 0, 0, 0); + } + return rc; +} + +/* +** Register a new collation sequence with the database handle db. +*/ +int sqlite3_create_collation( + sqlite3* db, + const char *zName, + int enc, + void* pCtx, + int(*xCompare)(void*,int,const void*,int,const void*) +){ + int rc; + assert( !sqlite3MallocFailed() ); + rc = createCollation(db, zName, enc, pCtx, xCompare); + return sqlite3ApiExit(db, rc); +} + +#ifndef SQLITE_OMIT_UTF16 +/* +** Register a new collation sequence with the database handle db. +*/ +int sqlite3_create_collation16( + sqlite3* db, + const char *zName, + int enc, + void* pCtx, + int(*xCompare)(void*,int,const void*,int,const void*) +){ + int rc = SQLITE_OK; + char *zName8; + assert( !sqlite3MallocFailed() ); + zName8 = sqlite3utf16to8(zName, -1); + if( zName8 ){ + rc = createCollation(db, zName8, enc, pCtx, xCompare); + sqliteFree(zName8); + } + return sqlite3ApiExit(db, rc); +} +#endif /* SQLITE_OMIT_UTF16 */ + +/* +** Register a collation sequence factory callback with the database handle +** db. Replace any previously installed collation sequence factory. +*/ +int sqlite3_collation_needed( + sqlite3 *db, + void *pCollNeededArg, + void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*) +){ + if( sqlite3SafetyCheck(db) ){ + return SQLITE_MISUSE; + } + db->xCollNeeded = xCollNeeded; + db->xCollNeeded16 = 0; + db->pCollNeededArg = pCollNeededArg; + return SQLITE_OK; +} + +#ifndef SQLITE_OMIT_UTF16 +/* +** Register a collation sequence factory callback with the database handle +** db. Replace any previously installed collation sequence factory. +*/ +int sqlite3_collation_needed16( + sqlite3 *db, + void *pCollNeededArg, + void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*) +){ + if( sqlite3SafetyCheck(db) ){ + return SQLITE_MISUSE; + } + db->xCollNeeded = 0; + db->xCollNeeded16 = xCollNeeded16; + db->pCollNeededArg = pCollNeededArg; + return SQLITE_OK; +} +#endif /* SQLITE_OMIT_UTF16 */ + +#ifndef SQLITE_OMIT_GLOBALRECOVER +/* +** This function is now an anachronism. It used to be used to recover from a +** malloc() failure, but SQLite now does this automatically. +*/ +int sqlite3_global_recover(){ + return SQLITE_OK; +} +#endif + +/* +** Test to see whether or not the database connection is in autocommit +** mode. Return TRUE if it is and FALSE if not. Autocommit mode is on +** by default. Autocommit is disabled by a BEGIN statement and reenabled +** by the next COMMIT or ROLLBACK. +** +******* THIS IS AN EXPERIMENTAL API AND IS SUBJECT TO CHANGE ****** +*/ +int sqlite3_get_autocommit(sqlite3 *db){ + return db->autoCommit; +} + +#ifdef SQLITE_DEBUG +/* +** The following routine is subtituted for constant SQLITE_CORRUPT in +** debugging builds. This provides a way to set a breakpoint for when +** corruption is first detected. +*/ +int sqlite3Corrupt(void){ + return SQLITE_CORRUPT; +} +#endif + + +#ifndef SQLITE_OMIT_SHARED_CACHE +/* +** Enable or disable the shared pager and schema features for the +** current thread. +** +** This routine should only be called when there are no open +** database connections. +*/ +int sqlite3_enable_shared_cache(int enable){ + ThreadData *pTd = sqlite3ThreadData(); + if( pTd ){ + /* It is only legal to call sqlite3_enable_shared_cache() when there + ** are no currently open b-trees that were opened by the calling thread. + ** This condition is only easy to detect if the shared-cache were + ** previously enabled (and is being disabled). + */ + if( pTd->pBtree && !enable ){ + assert( pTd->useSharedData ); + return SQLITE_MISUSE; + } + + pTd->useSharedData = enable; + sqlite3ReleaseThreadData(); + } + return sqlite3ApiExit(0, SQLITE_OK); +} +#endif + +/* +** This is a convenience routine that makes sure that all thread-specific +** data for this thread has been deallocated. +*/ +void sqlite3_thread_cleanup(void){ + ThreadData *pTd = sqlite3OsThreadSpecificData(0); + if( pTd ){ + memset(pTd, 0, sizeof(*pTd)); + sqlite3OsThreadSpecificData(-1); + } +} + +/* +** Return meta information about a specific column of a database table. +** See comment in sqlite3.h (sqlite.h.in) for details. +*/ +#ifdef SQLITE_ENABLE_COLUMN_METADATA +int sqlite3_table_column_metadata( + sqlite3 *db, /* Connection handle */ + const char *zDbName, /* Database name or NULL */ + const char *zTableName, /* Table name */ + const char *zColumnName, /* Column name */ + char const **pzDataType, /* OUTPUT: Declared data type */ + char const **pzCollSeq, /* OUTPUT: Collation sequence name */ + int *pNotNull, /* OUTPUT: True if NOT NULL constraint exists */ + int *pPrimaryKey, /* OUTPUT: True if column part of PK */ + int *pAutoinc /* OUTPUT: True if colums is auto-increment */ +){ + int rc; + char *zErrMsg = 0; + Table *pTab = 0; + Column *pCol = 0; + int iCol; + + char const *zDataType = 0; + char const *zCollSeq = 0; + int notnull = 0; + int primarykey = 0; + int autoinc = 0; + + /* Ensure the database schema has been loaded */ + if( sqlite3SafetyOn(db) ){ + return SQLITE_MISUSE; + } + rc = sqlite3Init(db, &zErrMsg); + if( SQLITE_OK!=rc ){ + goto error_out; + } + + /* Locate the table in question */ + pTab = sqlite3FindTable(db, zTableName, zDbName); + if( !pTab || pTab->pSelect ){ + pTab = 0; + goto error_out; + } + + /* Find the column for which info is requested */ + if( sqlite3IsRowid(zColumnName) ){ + iCol = pTab->iPKey; + if( iCol>=0 ){ + pCol = &pTab->aCol[iCol]; + } + }else{ + for(iCol=0; iColnCol; iCol++){ + pCol = &pTab->aCol[iCol]; + if( 0==sqlite3StrICmp(pCol->zName, zColumnName) ){ + break; + } + } + if( iCol==pTab->nCol ){ + pTab = 0; + goto error_out; + } + } + + /* The following block stores the meta information that will be returned + ** to the caller in local variables zDataType, zCollSeq, notnull, primarykey + ** and autoinc. At this point there are two possibilities: + ** + ** 1. The specified column name was rowid", "oid" or "_rowid_" + ** and there is no explicitly declared IPK column. + ** + ** 2. The table is not a view and the column name identified an + ** explicitly declared column. Copy meta information from *pCol. + */ + if( pCol ){ + zDataType = pCol->zType; + zCollSeq = pCol->zColl; + notnull = (pCol->notNull?1:0); + primarykey = (pCol->isPrimKey?1:0); + autoinc = ((pTab->iPKey==iCol && pTab->autoInc)?1:0); + }else{ + zDataType = "INTEGER"; + primarykey = 1; + } + if( !zCollSeq ){ + zCollSeq = "BINARY"; + } + +error_out: + if( sqlite3SafetyOff(db) ){ + rc = SQLITE_MISUSE; + } + + /* Whether the function call succeeded or failed, set the output parameters + ** to whatever their local counterparts contain. If an error did occur, + ** this has the effect of zeroing all output parameters. + */ + if( pzDataType ) *pzDataType = zDataType; + if( pzCollSeq ) *pzCollSeq = zCollSeq; + if( pNotNull ) *pNotNull = notnull; + if( pPrimaryKey ) *pPrimaryKey = primarykey; + if( pAutoinc ) *pAutoinc = autoinc; + + if( SQLITE_OK==rc && !pTab ){ + sqlite3SetString(&zErrMsg, "no such table column: ", zTableName, ".", + zColumnName, 0); + rc = SQLITE_ERROR; + } + sqlite3Error(db, rc, (zErrMsg?"%s":0), zErrMsg); + sqliteFree(zErrMsg); + return sqlite3ApiExit(db, rc); +} +#endif + Added: external/sqlite-source-3.3.4/opcodes.c ============================================================================== --- (empty file) +++ external/sqlite-source-3.3.4/opcodes.c Mon Apr 3 07:54:59 2006 @@ -0,0 +1,147 @@ +/* Automatically generated. Do not edit */ +/* See the mkopcodec.awk script for details. */ +#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) +const char *const sqlite3OpcodeNames[] = { "?", + /* 1 */ "MemLoad", + /* 2 */ "Column", + /* 3 */ "SetCookie", + /* 4 */ "IfMemPos", + /* 5 */ "Sequence", + /* 6 */ "MoveGt", + /* 7 */ "RowKey", + /* 8 */ "OpenWrite", + /* 9 */ "If", + /* 10 */ "Pop", + /* 11 */ "CollSeq", + /* 12 */ "OpenRead", + /* 13 */ "Expire", + /* 14 */ "AutoCommit", + /* 15 */ "IntegrityCk", + /* 16 */ "Not", + /* 17 */ "Sort", + /* 18 */ "Function", + /* 19 */ "Noop", + /* 20 */ "Return", + /* 21 */ "NewRowid", + /* 22 */ "IfMemNeg", + /* 23 */ "Variable", + /* 24 */ "String", + /* 25 */ "RealAffinity", + /* 26 */ "ParseSchema", + /* 27 */ "Close", + /* 28 */ "CreateIndex", + /* 29 */ "IsUnique", + /* 30 */ "IdxIsNull", + /* 31 */ "NotFound", + /* 32 */ "Int64", + /* 33 */ "MustBeInt", + /* 34 */ "Halt", + /* 35 */ "Rowid", + /* 36 */ "IdxLT", + /* 37 */ "AddImm", + /* 38 */ "Statement", + /* 39 */ "RowData", + /* 40 */ "MemMax", + /* 41 */ "Push", + /* 42 */ "NotExists", + /* 43 */ "MemIncr", + /* 44 */ "Gosub", + /* 45 */ "Integer", + /* 46 */ "MemInt", + /* 47 */ "Prev", + /* 48 */ "CreateTable", + /* 49 */ "Last", + /* 50 */ "IdxRowid", + /* 51 */ "MakeIdxRec", + /* 52 */ "ResetCount", + /* 53 */ "FifoWrite", + /* 54 */ "Callback", + /* 55 */ "ContextPush", + /* 56 */ "DropTrigger", + /* 57 */ "DropIndex", + /* 58 */ "IdxGE", + /* 59 */ "Or", + /* 60 */ "And", + /* 61 */ "IdxDelete", + /* 62 */ "Vacuum", + /* 63 */ "MoveLe", + /* 64 */ "IsNull", + /* 65 */ "NotNull", + /* 66 */ "Ne", + /* 67 */ "Eq", + /* 68 */ "Gt", + /* 69 */ "Le", + /* 70 */ "Lt", + /* 71 */ "Ge", + /* 72 */ "IfNot", + /* 73 */ "BitAnd", + /* 74 */ "BitOr", + /* 75 */ "ShiftLeft", + /* 76 */ "ShiftRight", + /* 77 */ "Add", + /* 78 */ "Subtract", + /* 79 */ "Multiply", + /* 80 */ "Divide", + /* 81 */ "Remainder", + /* 82 */ "Concat", + /* 83 */ "Negative", + /* 84 */ "DropTable", + /* 85 */ "BitNot", + /* 86 */ "String8", + /* 87 */ "MakeRecord", + /* 88 */ "Delete", + /* 89 */ "AggFinal", + /* 90 */ "Dup", + /* 91 */ "Goto", + /* 92 */ "TableLock", + /* 93 */ "FifoRead", + /* 94 */ "Clear", + /* 95 */ "IdxGT", + /* 96 */ "MoveLt", + /* 97 */ "VerifyCookie", + /* 98 */ "AggStep", + /* 99 */ "Pull", + /* 100 */ "SetNumColumns", + /* 101 */ "AbsValue", + /* 102 */ "Transaction", + /* 103 */ "ContextPop", + /* 104 */ "Next", + /* 105 */ "IdxInsert", + /* 106 */ "Distinct", + /* 107 */ "Insert", + /* 108 */ "Destroy", + /* 109 */ "ReadCookie", + /* 110 */ "ForceInt", + /* 111 */ "LoadAnalysis", + /* 112 */ "OpenVirtual", + /* 113 */ "Explain", + /* 114 */ "IfMemZero", + /* 115 */ "OpenPseudo", + /* 116 */ "Null", + /* 117 */ "Blob", + /* 118 */ "MemStore", + /* 119 */ "Rewind", + /* 120 */ "MoveGe", + /* 121 */ "MemMove", + /* 122 */ "MemNull", + /* 123 */ "Found", + /* 124 */ "Real", + /* 125 */ "HexBlob", + /* 126 */ "NullRow", + /* 127 */ "NotUsed_127", + /* 128 */ "NotUsed_128", + /* 129 */ "NotUsed_129", + /* 130 */ "NotUsed_130", + /* 131 */ "NotUsed_131", + /* 132 */ "NotUsed_132", + /* 133 */ "NotUsed_133", + /* 134 */ "NotUsed_134", + /* 135 */ "NotUsed_135", + /* 136 */ "NotUsed_136", + /* 137 */ "ToText", + /* 138 */ "ToBlob", + /* 139 */ "ToNumeric", + /* 140 */ "ToInt", + /* 141 */ "ToReal", +}; +#endif Added: external/sqlite-source-3.3.4/opcodes.h ============================================================================== --- (empty file) +++ external/sqlite-source-3.3.4/opcodes.h Mon Apr 3 07:54:59 2006 @@ -0,0 +1,159 @@ +/* Automatically generated. Do not edit */ +/* See the mkopcodeh.awk script for details */ +#define OP_MemLoad 1 +#define OP_HexBlob 125 /* same as TK_BLOB */ +#define OP_Column 2 +#define OP_SetCookie 3 +#define OP_IfMemPos 4 +#define OP_Real 124 /* same as TK_FLOAT */ +#define OP_Sequence 5 +#define OP_MoveGt 6 +#define OP_Ge 71 /* same as TK_GE */ +#define OP_RowKey 7 +#define OP_Eq 67 /* same as TK_EQ */ +#define OP_OpenWrite 8 +#define OP_NotNull 65 /* same as TK_NOTNULL */ +#define OP_If 9 +#define OP_ToInt 140 /* same as TK_TO_INT */ +#define OP_String8 86 /* same as TK_STRING */ +#define OP_Pop 10 +#define OP_CollSeq 11 +#define OP_OpenRead 12 +#define OP_Expire 13 +#define OP_AutoCommit 14 +#define OP_Gt 68 /* same as TK_GT */ +#define OP_IntegrityCk 15 +#define OP_Sort 17 +#define OP_Function 18 +#define OP_And 60 /* same as TK_AND */ +#define OP_Subtract 78 /* same as TK_MINUS */ +#define OP_Noop 19 +#define OP_Return 20 +#define OP_Remainder 81 /* same as TK_REM */ +#define OP_NewRowid 21 +#define OP_Multiply 79 /* same as TK_STAR */ +#define OP_IfMemNeg 22 +#define OP_Variable 23 +#define OP_String 24 +#define OP_RealAffinity 25 +#define OP_ParseSchema 26 +#define OP_Close 27 +#define OP_CreateIndex 28 +#define OP_IsUnique 29 +#define OP_IdxIsNull 30 +#define OP_NotFound 31 +#define OP_Int64 32 +#define OP_MustBeInt 33 +#define OP_Halt 34 +#define OP_Rowid 35 +#define OP_IdxLT 36 +#define OP_AddImm 37 +#define OP_Statement 38 +#define OP_RowData 39 +#define OP_MemMax 40 +#define OP_Push 41 +#define OP_Or 59 /* same as TK_OR */ +#define OP_NotExists 42 +#define OP_MemIncr 43 +#define OP_Gosub 44 +#define OP_Divide 80 /* same as TK_SLASH */ +#define OP_Integer 45 +#define OP_ToNumeric 139 /* same as TK_TO_NUMERIC*/ +#define OP_MemInt 46 +#define OP_Prev 47 +#define OP_Concat 82 /* same as TK_CONCAT */ +#define OP_BitAnd 73 /* same as TK_BITAND */ +#define OP_CreateTable 48 +#define OP_Last 49 +#define OP_IsNull 64 /* same as TK_ISNULL */ +#define OP_IdxRowid 50 +#define OP_MakeIdxRec 51 +#define OP_ShiftRight 76 /* same as TK_RSHIFT */ +#define OP_ResetCount 52 +#define OP_FifoWrite 53 +#define OP_Callback 54 +#define OP_ContextPush 55 +#define OP_DropTrigger 56 +#define OP_DropIndex 57 +#define OP_IdxGE 58 +#define OP_IdxDelete 61 +#define OP_Vacuum 62 +#define OP_MoveLe 63 +#define OP_IfNot 72 +#define OP_DropTable 84 +#define OP_MakeRecord 87 +#define OP_ToBlob 138 /* same as TK_TO_BLOB */ +#define OP_Delete 88 +#define OP_AggFinal 89 +#define OP_ShiftLeft 75 /* same as TK_LSHIFT */ +#define OP_Dup 90 +#define OP_Goto 91 +#define OP_TableLock 92 +#define OP_FifoRead 93 +#define OP_Clear 94 +#define OP_IdxGT 95 +#define OP_MoveLt 96 +#define OP_Le 69 /* same as TK_LE */ +#define OP_VerifyCookie 97 +#define OP_AggStep 98 +#define OP_Pull 99 +#define OP_ToText 137 /* same as TK_TO_TEXT */ +#define OP_Not 16 /* same as TK_NOT */ +#define OP_ToReal 141 /* same as TK_TO_REAL */ +#define OP_SetNumColumns 100 +#define OP_AbsValue 101 +#define OP_Transaction 102 +#define OP_Negative 83 /* same as TK_UMINUS */ +#define OP_Ne 66 /* same as TK_NE */ +#define OP_ContextPop 103 +#define OP_BitOr 74 /* same as TK_BITOR */ +#define OP_Next 104 +#define OP_IdxInsert 105 +#define OP_Distinct 106 +#define OP_Lt 70 /* same as TK_LT */ +#define OP_Insert 107 +#define OP_Destroy 108 +#define OP_ReadCookie 109 +#define OP_ForceInt 110 +#define OP_LoadAnalysis 111 +#define OP_OpenVirtual 112 +#define OP_Explain 113 +#define OP_IfMemZero 114 +#define OP_OpenPseudo 115 +#define OP_Null 116 +#define OP_Blob 117 +#define OP_Add 77 /* same as TK_PLUS */ +#define OP_MemStore 118 +#define OP_Rewind 119 +#define OP_MoveGe 120 +#define OP_BitNot 85 /* same as TK_BITNOT */ +#define OP_MemMove 121 +#define OP_MemNull 122 +#define OP_Found 123 +#define OP_NullRow 126 + +/* The following opcode values are never used */ +#define OP_NotUsed_127 127 +#define OP_NotUsed_128 128 +#define OP_NotUsed_129 129 +#define OP_NotUsed_130 130 +#define OP_NotUsed_131 131 +#define OP_NotUsed_132 132 +#define OP_NotUsed_133 133 +#define OP_NotUsed_134 134 +#define OP_NotUsed_135 135 +#define OP_NotUsed_136 136 + +/* Opcodes that are guaranteed to never push a value onto the stack +** contain a 1 their corresponding position of the following mask +** set. See the opcodeNoPush() function in vdbeaux.c */ +#define NOPUSH_MASK_0 0x7f58 +#define NOPUSH_MASK_1 0xee5b +#define NOPUSH_MASK_2 0x9f76 +#define NOPUSH_MASK_3 0xfff2 +#define NOPUSH_MASK_4 0xffff +#define NOPUSH_MASK_5 0xdb3b +#define NOPUSH_MASK_6 0xcfdf +#define NOPUSH_MASK_7 0x49cd +#define NOPUSH_MASK_8 0x3e00 +#define NOPUSH_MASK_9 0x0000 Added: external/sqlite-source-3.3.4/os.c ============================================================================== --- (empty file) +++ external/sqlite-source-3.3.4/os.c Mon Apr 3 07:54:59 2006 @@ -0,0 +1,92 @@ +/* +** 2005 November 29 +** +** 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 OS interface code that is common to all +** architectures. +*/ +#define _SQLITE_OS_C_ 1 +#include "sqliteInt.h" +#include "os.h" + +/* +** The following routines are convenience wrappers around methods +** of the OsFile object. This is mostly just syntactic sugar. All +** of this would be completely automatic if SQLite were coded using +** C++ instead of plain old C. +*/ +int sqlite3OsClose(OsFile **pId){ + OsFile *id; + if( pId!=0 && (id = *pId)!=0 ){ + return id->pMethod->xClose(pId); + }else{ + return SQLITE_OK; + } +} +int sqlite3OsOpenDirectory(OsFile *id, const char *zName){ + return id->pMethod->xOpenDirectory(id, zName); +} +int sqlite3OsRead(OsFile *id, void *pBuf, int amt){ + return id->pMethod->xRead(id, pBuf, amt); +} +int sqlite3OsWrite(OsFile *id, const void *pBuf, int amt){ + return id->pMethod->xWrite(id, pBuf, amt); +} +int sqlite3OsSeek(OsFile *id, i64 offset){ + return id->pMethod->xSeek(id, offset); +} +int sqlite3OsTruncate(OsFile *id, i64 size){ + return id->pMethod->xTruncate(id, size); +} +int sqlite3OsSync(OsFile *id, int fullsync){ + return id->pMethod->xSync(id, fullsync); +} +void sqlite3OsSetFullSync(OsFile *id, int value){ + id->pMethod->xSetFullSync(id, value); +} +#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG) +/* This method is currently only used while interactively debugging the +** pager. More specificly, it can only be used when sqlite3DebugPrintf() is +** included in the build. */ +int sqlite3OsFileHandle(OsFile *id){ + return id->pMethod->xFileHandle(id); +} +#endif +int sqlite3OsFileSize(OsFile *id, i64 *pSize){ + return id->pMethod->xFileSize(id, pSize); +} +int sqlite3OsLock(OsFile *id, int lockType){ + return id->pMethod->xLock(id, lockType); +} +int sqlite3OsUnlock(OsFile *id, int lockType){ + return id->pMethod->xUnlock(id, lockType); +} +int sqlite3OsLockState(OsFile *id){ + return id->pMethod->xLockState(id); +} +int sqlite3OsCheckReservedLock(OsFile *id){ + return id->pMethod->xCheckReservedLock(id); +} + +#ifdef SQLITE_ENABLE_REDEF_IO +/* +** A function to return a pointer to the virtual function table. +** This routine really does not accomplish very much since the +** virtual function table is a global variable and anybody who +** can call this function can just as easily access the variable +** for themselves. Nevertheless, we include this routine for +** backwards compatibility with an earlier redefinable I/O +** interface design. +*/ +struct sqlite3OsVtbl *sqlite3_os_switch(void){ + return &sqlite3Os; +} +#endif Added: external/sqlite-source-3.3.4/os.h ============================================================================== --- (empty file) +++ external/sqlite-source-3.3.4/os.h Mon Apr 3 07:54:59 2006 @@ -0,0 +1,440 @@ +/* +** 2001 September 16 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This header file (together with is companion C source-code file +** "os.c") attempt to abstract the underlying operating system so that +** the SQLite library will work on both POSIX and windows systems. +*/ +#ifndef _SQLITE_OS_H_ +#define _SQLITE_OS_H_ + +/* +** Figure out if we are dealing with Unix, Windows, or some other +** operating system. +*/ +#if !defined(OS_UNIX) && !defined(OS_OTHER) +# define OS_OTHER 0 +# ifndef OS_WIN +# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__) +# define OS_WIN 1 +# define OS_UNIX 0 +# else +# define OS_WIN 0 +# define OS_UNIX 1 +# endif +# else +# define OS_UNIX 0 +# endif +#else +# ifndef OS_WIN +# define OS_WIN 0 +# endif +#endif + + +/* +** Define the maximum size of a temporary filename +*/ +#if OS_WIN +# include +# define SQLITE_TEMPNAME_SIZE (MAX_PATH+50) +#else +# define SQLITE_TEMPNAME_SIZE 200 +#endif + +/* If the SET_FULLSYNC macro is not defined above, then make it +** a no-op +*/ +#ifndef SET_FULLSYNC +# define SET_FULLSYNC(x,y) +#endif + +/* +** Temporary files are named starting with this prefix followed by 16 random +** alphanumeric characters, and no file extension. They are stored in the +** OS's standard temporary file directory, and are deleted prior to exit. +** If sqlite is being embedded in another program, you may wish to change the +** prefix to reflect your program's name, so that if your program exits +** prematurely, old temporary files can be easily identified. This can be done +** using -DTEMP_FILE_PREFIX=myprefix_ on the compiler command line. +*/ +#ifndef TEMP_FILE_PREFIX +# define TEMP_FILE_PREFIX "sqlite_" +#endif + +/* +** Define the interfaces for Unix and for Windows. +*/ +#if OS_UNIX +#define sqlite3OsOpenReadWrite sqlite3UnixOpenReadWrite +#define sqlite3OsOpenExclusive sqlite3UnixOpenExclusive +#define sqlite3OsOpenReadOnly sqlite3UnixOpenReadOnly +#define sqlite3OsDelete sqlite3UnixDelete +#define sqlite3OsFileExists sqlite3UnixFileExists +#define sqlite3OsFullPathname sqlite3UnixFullPathname +#define sqlite3OsIsDirWritable sqlite3UnixIsDirWritable +#define sqlite3OsSyncDirectory sqlite3UnixSyncDirectory +#define sqlite3OsTempFileName sqlite3UnixTempFileName +#define sqlite3OsRandomSeed sqlite3UnixRandomSeed +#define sqlite3OsSleep sqlite3UnixSleep +#define sqlite3OsCurrentTime sqlite3UnixCurrentTime +#define sqlite3OsEnterMutex sqlite3UnixEnterMutex +#define sqlite3OsLeaveMutex sqlite3UnixLeaveMutex +#define sqlite3OsInMutex sqlite3UnixInMutex +#define sqlite3OsThreadSpecificData sqlite3UnixThreadSpecificData +#define sqlite3OsMalloc sqlite3GenericMalloc +#define sqlite3OsRealloc sqlite3GenericRealloc +#define sqlite3OsFree sqlite3GenericFree +#define sqlite3OsAllocationSize sqlite3GenericAllocationSize +#endif +#if OS_WIN +#define sqlite3OsOpenReadWrite sqlite3WinOpenReadWrite +#define sqlite3OsOpenExclusive sqlite3WinOpenExclusive +#define sqlite3OsOpenReadOnly sqlite3WinOpenReadOnly +#define sqlite3OsDelete sqlite3WinDelete +#define sqlite3OsFileExists sqlite3WinFileExists +#define sqlite3OsFullPathname sqlite3WinFullPathname +#define sqlite3OsIsDirWritable sqlite3WinIsDirWritable +#define sqlite3OsSyncDirectory sqlite3WinSyncDirectory +#define sqlite3OsTempFileName sqlite3WinTempFileName +#define sqlite3OsRandomSeed sqlite3WinRandomSeed +#define sqlite3OsSleep sqlite3WinSleep +#define sqlite3OsCurrentTime sqlite3WinCurrentTime +#define sqlite3OsEnterMutex sqlite3WinEnterMutex +#define sqlite3OsLeaveMutex sqlite3WinLeaveMutex +#define sqlite3OsInMutex sqlite3WinInMutex +#define sqlite3OsThreadSpecificData sqlite3WinThreadSpecificData +#define sqlite3OsMalloc sqlite3GenericMalloc +#define sqlite3OsRealloc sqlite3GenericRealloc +#define sqlite3OsFree sqlite3GenericFree +#define sqlite3OsAllocationSize sqlite3GenericAllocationSize +#endif + +/* +** If using an alternative OS interface, then we must have an "os_other.h" +** header file available for that interface. Presumably the "os_other.h" +** header file contains #defines similar to those above. +*/ +#if OS_OTHER +# include "os_other.h" +#endif + + + +/* +** Forward declarations +*/ +typedef struct OsFile OsFile; +typedef struct IoMethod IoMethod; + +/* +** An instance of the following structure contains pointers to all +** methods on an OsFile object. +*/ +struct IoMethod { + int (*xClose)(OsFile**); + int (*xOpenDirectory)(OsFile*, const char*); + int (*xRead)(OsFile*, void*, int amt); + int (*xWrite)(OsFile*, const void*, int amt); + int (*xSeek)(OsFile*, i64 offset); + int (*xTruncate)(OsFile*, i64 size); + int (*xSync)(OsFile*, int); + void (*xSetFullSync)(OsFile *id, int setting); + int (*xFileHandle)(OsFile *id); + int (*xFileSize)(OsFile*, i64 *pSize); + int (*xLock)(OsFile*, int); + int (*xUnlock)(OsFile*, int); + int (*xLockState)(OsFile *id); + int (*xCheckReservedLock)(OsFile *id); +}; + +/* +** The OsFile object describes an open disk file in an OS-dependent way. +** The version of OsFile defined here is a generic version. Each OS +** implementation defines its own subclass of this structure that contains +** additional information needed to handle file I/O. But the pMethod +** entry (pointing to the virtual function table) always occurs first +** so that we can always find the appropriate methods. +*/ +struct OsFile { + IoMethod const *pMethod; +}; + +/* +** The following values may be passed as the second argument to +** sqlite3OsLock(). The various locks exhibit the following semantics: +** +** SHARED: Any number of processes may hold a SHARED lock simultaneously. +** RESERVED: A single process may hold a RESERVED lock on a file at +** any time. Other processes may hold and obtain new SHARED locks. +** PENDING: A single process may hold a PENDING lock on a file at +** any one time. Existing SHARED locks may persist, but no new +** SHARED locks may be obtained by other processes. +** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks. +** +** PENDING_LOCK may not be passed directly to sqlite3OsLock(). Instead, a +** process that requests an EXCLUSIVE lock may actually obtain a PENDING +** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to +** sqlite3OsLock(). +*/ +#define NO_LOCK 0 +#define SHARED_LOCK 1 +#define RESERVED_LOCK 2 +#define PENDING_LOCK 3 +#define EXCLUSIVE_LOCK 4 + +/* +** File Locking Notes: (Mostly about windows but also some info for Unix) +** +** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because +** those functions are not available. So we use only LockFile() and +** UnlockFile(). +** +** LockFile() prevents not just writing but also reading by other processes. +** A SHARED_LOCK is obtained by locking a single randomly-chosen +** byte out of a specific range of bytes. The lock byte is obtained at +** random so two separate readers can probably access the file at the +** same time, unless they are unlucky and choose the same lock byte. +** An EXCLUSIVE_LOCK is obtained by locking all bytes in the range. +** There can only be one writer. A RESERVED_LOCK is obtained by locking +** a single byte of the file that is designated as the reserved lock byte. +** A PENDING_LOCK is obtained by locking a designated byte different from +** the RESERVED_LOCK byte. +** +** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available, +** which means we can use reader/writer locks. When reader/writer locks +** are used, the lock is placed on the same range of bytes that is used +** for probabilistic locking in Win95/98/ME. Hence, the locking scheme +** will support two or more Win95 readers or two or more WinNT readers. +** But a single Win95 reader will lock out all WinNT readers and a single +** WinNT reader will lock out all other Win95 readers. +** +** The following #defines specify the range of bytes used for locking. +** SHARED_SIZE is the number of bytes available in the pool from which +** a random byte is selected for a shared lock. The pool of bytes for +** shared locks begins at SHARED_FIRST. +** +** These #defines are available in sqlite_aux.h so that adaptors for +** connecting SQLite to other operating systems can use the same byte +** ranges for locking. In particular, the same locking strategy and +** byte ranges are used for Unix. This leaves open the possiblity of having +** clients on win95, winNT, and unix all talking to the same shared file +** and all locking correctly. To do so would require that samba (or whatever +** tool is being used for file sharing) implements locks correctly between +** windows and unix. I'm guessing that isn't likely to happen, but by +** using the same locking range we are at least open to the possibility. +** +** Locking in windows is manditory. For this reason, we cannot store +** actual data in the bytes used for locking. The pager never allocates +** the pages involved in locking therefore. SHARED_SIZE is selected so +** that all locks will fit on a single page even at the minimum page size. +** PENDING_BYTE defines the beginning of the locks. By default PENDING_BYTE +** is set high so that we don't have to allocate an unused page except +** for very large databases. But one should test the page skipping logic +** by setting PENDING_BYTE low and running the entire regression suite. +** +** Changing the value of PENDING_BYTE results in a subtly incompatible +** file format. Depending on how it is changed, you might not notice +** the incompatibility right away, even running a full regression test. +** The default location of PENDING_BYTE is the first byte past the +** 1GB boundary. +** +*/ +#ifndef SQLITE_TEST +#define PENDING_BYTE 0x40000000 /* First byte past the 1GB boundary */ +#else +extern unsigned int sqlite3_pending_byte; +#define PENDING_BYTE sqlite3_pending_byte +#endif + +#define RESERVED_BYTE (PENDING_BYTE+1) +#define SHARED_FIRST (PENDING_BYTE+2) +#define SHARED_SIZE 510 + +/* +** Prototypes for operating system interface routines. +*/ +int sqlite3OsClose(OsFile**); +int sqlite3OsOpenDirectory(OsFile*, const char*); +int sqlite3OsRead(OsFile*, void*, int amt); +int sqlite3OsWrite(OsFile*, const void*, int amt); +int sqlite3OsSeek(OsFile*, i64 offset); +int sqlite3OsTruncate(OsFile*, i64 size); +int sqlite3OsSync(OsFile*, int); +void sqlite3OsSetFullSync(OsFile *id, int setting); +int sqlite3OsFileHandle(OsFile *id); +int sqlite3OsFileSize(OsFile*, i64 *pSize); +int sqlite3OsLock(OsFile*, int); +int sqlite3OsUnlock(OsFile*, int); +int sqlite3OsLockState(OsFile *id); +int sqlite3OsCheckReservedLock(OsFile *id); +int sqlite3OsOpenReadWrite(const char*, OsFile**, int*); +int sqlite3OsOpenExclusive(const char*, OsFile**, int); +int sqlite3OsOpenReadOnly(const char*, OsFile**); +int sqlite3OsDelete(const char*); +int sqlite3OsFileExists(const char*); +char *sqlite3OsFullPathname(const char*); +int sqlite3OsIsDirWritable(char*); +int sqlite3OsSyncDirectory(const char*); +int sqlite3OsTempFileName(char*); +int sqlite3OsRandomSeed(char*); +int sqlite3OsSleep(int ms); +int sqlite3OsCurrentTime(double*); +void sqlite3OsEnterMutex(void); +void sqlite3OsLeaveMutex(void); +int sqlite3OsInMutex(int); +ThreadData *sqlite3OsThreadSpecificData(int); +void *sqlite3OsMalloc(int); +void *sqlite3OsRealloc(void *, int); +void sqlite3OsFree(void *); +int sqlite3OsAllocationSize(void *); + +/* +** If the SQLITE_ENABLE_REDEF_IO macro is defined, then the OS-layer +** interface routines are not called directly but are invoked using +** pointers to functions. This allows the implementation of various +** OS-layer interface routines to be modified at run-time. There are +** obscure but legitimate reasons for wanting to do this. But for +** most users, a direct call to the underlying interface is preferable +** so the the redefinable I/O interface is turned off by default. +*/ +#ifdef SQLITE_ENABLE_REDEF_IO + +/* +** When redefinable I/O is enabled, a single global instance of the +** following structure holds pointers to the routines that SQLite +** uses to talk with the underlying operating system. Modify this +** structure (before using any SQLite API!) to accomodate perculiar +** operating system interfaces or behaviors. +*/ +struct sqlite3OsVtbl { + int (*xOpenReadWrite)(const char*, OsFile**, int*); + int (*xOpenExclusive)(const char*, OsFile**, int); + int (*xOpenReadOnly)(const char*, OsFile**); + + int (*xDelete)(const char*); + int (*xFileExists)(const char*); + char *(*xFullPathname)(const char*); + int (*xIsDirWritable)(char*); + int (*xSyncDirectory)(const char*); + int (*xTempFileName)(char*); + + int (*xRandomSeed)(char*); + int (*xSleep)(int ms); + int (*xCurrentTime)(double*); + + void (*xEnterMutex)(void); + void (*xLeaveMutex)(void); + int (*xInMutex)(int); + ThreadData *(*xThreadSpecificData)(int); + + void *(*xMalloc)(int); + void *(*xRealloc)(void *, int); + void (*xFree)(void *); + int (*xAllocationSize)(void *); +}; + +/* Macro used to comment out routines that do not exists when there is +** no disk I/O +*/ +#ifdef SQLITE_OMIT_DISKIO +# define IF_DISKIO(X) 0 +#else +# define IF_DISKIO(X) X +#endif + +#ifdef _SQLITE_OS_C_ + /* + ** The os.c file implements the global virtual function table. + */ + struct sqlite3OsVtbl sqlite3Os = { + IF_DISKIO( sqlite3OsOpenReadWrite ), + IF_DISKIO( sqlite3OsOpenExclusive ), + IF_DISKIO( sqlite3OsOpenReadOnly ), + IF_DISKIO( sqlite3OsDelete ), + IF_DISKIO( sqlite3OsFileExists ), + IF_DISKIO( sqlite3OsFullPathname ), + IF_DISKIO( sqlite3OsIsDirWritable ), + IF_DISKIO( sqlite3OsSyncDirectory ), + IF_DISKIO( sqlite3OsTempFileName ), + sqlite3OsRandomSeed, + sqlite3OsSleep, + sqlite3OsCurrentTime, + sqlite3OsEnterMutex, + sqlite3OsLeaveMutex, + sqlite3OsInMutex, + sqlite3OsThreadSpecificData, + sqlite3OsMalloc, + sqlite3OsRealloc, + sqlite3OsFree, + sqlite3OsAllocationSize + }; +#else + /* + ** Files other than os.c just reference the global virtual function table. + */ + extern struct sqlite3OsVtbl sqlite3Os; +#endif /* _SQLITE_OS_C_ */ + + +/* This additional API routine is available with redefinable I/O */ +struct sqlite3OsVtbl *sqlite3_os_switch(void); + + +/* +** Redefine the OS interface to go through the virtual function table +** rather than calling routines directly. +*/ +#undef sqlite3OsOpenReadWrite +#undef sqlite3OsOpenExclusive +#undef sqlite3OsOpenReadOnly +#undef sqlite3OsDelete +#undef sqlite3OsFileExists +#undef sqlite3OsFullPathname +#undef sqlite3OsIsDirWritable +#undef sqlite3OsSyncDirectory +#undef sqlite3OsTempFileName +#undef sqlite3OsRandomSeed +#undef sqlite3OsSleep +#undef sqlite3OsCurrentTime +#undef sqlite3OsEnterMutex +#undef sqlite3OsLeaveMutex +#undef sqlite3OsInMutex +#undef sqlite3OsThreadSpecificData +#undef sqlite3OsMalloc +#undef sqlite3OsRealloc +#undef sqlite3OsFree +#undef sqlite3OsAllocationSize +#define sqlite3OsOpenReadWrite sqlite3Os.xOpenReadWrite +#define sqlite3OsOpenExclusive sqlite3Os.xOpenExclusive +#define sqlite3OsOpenReadOnly sqlite3Os.xOpenReadOnly +#define sqlite3OsDelete sqlite3Os.xDelete +#define sqlite3OsFileExists sqlite3Os.xFileExists +#define sqlite3OsFullPathname sqlite3Os.xFullPathname +#define sqlite3OsIsDirWritable sqlite3Os.xIsDirWritable +#define sqlite3OsSyncDirectory sqlite3Os.xSyncDirectory +#define sqlite3OsTempFileName sqlite3Os.xTempFileName +#define sqlite3OsRandomSeed sqlite3Os.xRandomSeed +#define sqlite3OsSleep sqlite3Os.xSleep +#define sqlite3OsCurrentTime sqlite3Os.xCurrentTime +#define sqlite3OsEnterMutex sqlite3Os.xEnterMutex +#define sqlite3OsLeaveMutex sqlite3Os.xLeaveMutex +#define sqlite3OsInMutex sqlite3Os.xInMutex +#define sqlite3OsThreadSpecificData sqlite3Os.xThreadSpecificData +#define sqlite3OsMalloc sqlite3Os.xMalloc +#define sqlite3OsRealloc sqlite3Os.xRealloc +#define sqlite3OsFree sqlite3Os.xFree +#define sqlite3OsAllocationSize sqlite3Os.xAllocationSize + +#endif /* SQLITE_ENABLE_REDEF_IO */ + +#endif /* _SQLITE_OS_H_ */ Added: external/sqlite-source-3.3.4/os_common.h ============================================================================== --- (empty file) +++ external/sqlite-source-3.3.4/os_common.h Mon Apr 3 07:54:59 2006 @@ -0,0 +1,188 @@ +/* +** 2004 May 22 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This file contains macros and a little bit of code that is common to +** all of the platform-specific files (os_*.c) and is #included into those +** files. +** +** This file should be #included by the os_*.c files only. It is not a +** general purpose header file. +*/ + +/* +** At least two bugs have slipped in because we changed the MEMORY_DEBUG +** macro to SQLITE_DEBUG and some older makefiles have not yet made the +** switch. The following code should catch this problem at compile-time. +*/ +#ifdef MEMORY_DEBUG +# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." +#endif + + +/* + * When testing, this global variable stores the location of the + * pending-byte in the database file. + */ +#ifdef SQLITE_TEST +unsigned int sqlite3_pending_byte = 0x40000000; +#endif + +int sqlite3_os_trace = 0; +#ifdef SQLITE_DEBUG +static int last_page = 0; +#define SEEK(X) last_page=(X) +#define TRACE1(X) if( sqlite3_os_trace ) sqlite3DebugPrintf(X) +#define TRACE2(X,Y) if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y) +#define TRACE3(X,Y,Z) if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y,Z) +#define TRACE4(X,Y,Z,A) if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y,Z,A) +#define TRACE5(X,Y,Z,A,B) if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y,Z,A,B) +#define TRACE6(X,Y,Z,A,B,C) if(sqlite3_os_trace) sqlite3DebugPrintf(X,Y,Z,A,B,C) +#define TRACE7(X,Y,Z,A,B,C,D) \ + if(sqlite3_os_trace) sqlite3DebugPrintf(X,Y,Z,A,B,C,D) +#else +#define SEEK(X) +#define TRACE1(X) +#define TRACE2(X,Y) +#define TRACE3(X,Y,Z) +#define TRACE4(X,Y,Z,A) +#define TRACE5(X,Y,Z,A,B) +#define TRACE6(X,Y,Z,A,B,C) +#define TRACE7(X,Y,Z,A,B,C,D) +#endif + +/* +** Macros for performance tracing. Normally turned off. Only works +** on i486 hardware. +*/ +#ifdef SQLITE_PERFORMANCE_TRACE +__inline__ unsigned long long int hwtime(void){ + unsigned long long int x; + __asm__("rdtsc\n\t" + "mov %%edx, %%ecx\n\t" + :"=A" (x)); + return x; +} +static unsigned long long int g_start; +static unsigned int elapse; +#define TIMER_START g_start=hwtime() +#define TIMER_END elapse=hwtime()-g_start +#define TIMER_ELAPSED elapse +#else +#define TIMER_START +#define TIMER_END +#define TIMER_ELAPSED 0 +#endif + +/* +** If we compile with the SQLITE_TEST macro set, then the following block +** of code will give us the ability to simulate a disk I/O error. This +** is used for testing the I/O recovery logic. +*/ +#ifdef SQLITE_TEST +int sqlite3_io_error_hit = 0; +int sqlite3_io_error_pending = 0; +int sqlite3_diskfull_pending = 0; +int sqlite3_diskfull = 0; +#define SimulateIOError(A) \ + if( sqlite3_io_error_pending ) \ + if( sqlite3_io_error_pending-- == 1 ){ local_ioerr(); return A; } +static void local_ioerr(){ + sqlite3_io_error_hit = 1; /* Really just a place to set a breakpoint */ +} +#define SimulateDiskfullError \ + if( sqlite3_diskfull_pending ){ \ + if( sqlite3_diskfull_pending == 1 ){ \ + local_ioerr(); \ + sqlite3_diskfull = 1; \ + return SQLITE_FULL; \ + }else{ \ + sqlite3_diskfull_pending--; \ + } \ + } +#else +#define SimulateIOError(A) +#define SimulateDiskfullError +#endif + +/* +** When testing, keep a count of the number of open files. +*/ +#ifdef SQLITE_TEST +int sqlite3_open_file_count = 0; +#define OpenCounter(X) sqlite3_open_file_count+=(X) +#else +#define OpenCounter(X) +#endif + +/* +** sqlite3GenericMalloc +** sqlite3GenericRealloc +** sqlite3GenericOsFree +** sqlite3GenericAllocationSize +** +** Implementation of the os level dynamic memory allocation interface in terms +** of the standard malloc(), realloc() and free() found in many operating +** systems. No rocket science here. +** +** There are two versions of these four functions here. The version +** implemented here is only used if memory-management or memory-debugging is +** enabled. This version allocates an extra 8-bytes at the beginning of each +** block and stores the size of the allocation there. +** +** If neither memory-management or debugging is enabled, the second +** set of implementations is used instead. +*/ +#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) || defined (SQLITE_MEMDEBUG) +void *sqlite3GenericMalloc(int n){ + char *p = (char *)malloc(n+8); + assert(n>0); + assert(sizeof(int)<=8); + if( p ){ + *(int *)p = n; + p += 8; + } + return (void *)p; +} +void *sqlite3GenericRealloc(void *p, int n){ + char *p2 = ((char *)p - 8); + assert(n>0); + p2 = (char*)realloc(p2, n+8); + if( p2 ){ + *(int *)p2 = n; + p2 += 8; + } + return (void *)p2; +} +void sqlite3GenericFree(void *p){ + assert(p); + free((void *)((char *)p - 8)); +} +int sqlite3GenericAllocationSize(void *p){ + return p ? *(int *)((char *)p - 8) : 0; +} +#else +void *sqlite3GenericMalloc(int n){ + char *p = (char *)malloc(n); + return (void *)p; +} +void *sqlite3GenericRealloc(void *p, int n){ + assert(n>0); + p = realloc(p, n); + return p; +} +void sqlite3GenericFree(void *p){ + assert(p); + free(p); +} +/* Never actually used, but needed for the linker */ +int sqlite3GenericAllocationSize(void *p){ return 0; } +#endif Added: external/sqlite-source-3.3.4/os_unix.c ============================================================================== --- (empty file) +++ external/sqlite-source-3.3.4/os_unix.c Mon Apr 3 07:54:59 2006 @@ -0,0 +1,1928 @@ +/* +** 2004 May 22 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This file contains code that is specific to Unix systems. +*/ +#include "sqliteInt.h" +#include "os.h" +#if OS_UNIX /* This file is used on unix only */ + +/* +** These #defines should enable >2GB file support on Posix if the +** underlying operating system supports it. If the OS lacks +** large file support, these should be no-ops. +** +** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch +** on the compiler command line. This is necessary if you are compiling +** on a recent machine (ex: RedHat 7.2) but you want your code to work +** on an older machine (ex: RedHat 6.0). If you compile on RedHat 7.2 +** without this option, LFS is enable. But LFS does not exist in the kernel +** in RedHat 6.0, so the code won't work. Hence, for maximum binary +** portability you should omit LFS. +*/ +#ifndef SQLITE_DISABLE_LFS +# define _LARGE_FILE 1 +# ifndef _FILE_OFFSET_BITS +# define _FILE_OFFSET_BITS 64 +# endif +# define _LARGEFILE_SOURCE 1 +#endif + +/* +** standard include files. +*/ +#include +#include +#include +#include +#include +#include +#include + +/* +** If we are to be thread-safe, include the pthreads header and define +** the SQLITE_UNIX_THREADS macro. +*/ +#if defined(THREADSAFE) && THREADSAFE +# include +# define SQLITE_UNIX_THREADS 1 +#endif + +/* +** Default permissions when creating a new file +*/ +#ifndef SQLITE_DEFAULT_FILE_PERMISSIONS +# define SQLITE_DEFAULT_FILE_PERMISSIONS 0644 +#endif + + + +/* +** The unixFile structure is subclass of OsFile specific for the unix +** protability layer. +*/ +typedef struct unixFile unixFile; +struct unixFile { + IoMethod const *pMethod; /* Always the first entry */ + struct openCnt *pOpen; /* Info about all open fd's on this inode */ + struct lockInfo *pLock; /* Info about locks on this inode */ + int h; /* The file descriptor */ + unsigned char locktype; /* The type of lock held on this fd */ + unsigned char isOpen; /* True if needs to be closed */ + unsigned char fullSync; /* Use F_FULLSYNC if available */ + int dirfd; /* File descriptor for the directory */ +#ifdef SQLITE_UNIX_THREADS + pthread_t tid; /* The thread that "owns" this OsFile */ +#endif +}; + +/* +** Provide the ability to override some OS-layer functions during +** testing. This is used to simulate OS crashes to verify that +** commits are atomic even in the event of an OS crash. +*/ +#ifdef SQLITE_CRASH_TEST + extern int sqlite3CrashTestEnable; + extern int sqlite3CrashOpenReadWrite(const char*, OsFile**, int*); + extern int sqlite3CrashOpenExclusive(const char*, OsFile**, int); + extern int sqlite3CrashOpenReadOnly(const char*, OsFile**, int); +# define CRASH_TEST_OVERRIDE(X,A,B,C) \ + if(sqlite3CrashTestEnable){ return X(A,B,C); } +#else +# define CRASH_TEST_OVERRIDE(X,A,B,C) /* no-op */ +#endif + + +/* +** Include code that is common to all os_*.c files +*/ +#include "os_common.h" + +/* +** Do not include any of the File I/O interface procedures if the +** SQLITE_OMIT_DISKIO macro is defined (indicating that the database +** will be in-memory only) +*/ +#ifndef SQLITE_OMIT_DISKIO + + +/* +** Define various macros that are missing from some systems. +*/ +#ifndef O_LARGEFILE +# define O_LARGEFILE 0 +#endif +#ifdef SQLITE_DISABLE_LFS +# undef O_LARGEFILE +# define O_LARGEFILE 0 +#endif +#ifndef O_NOFOLLOW +# define O_NOFOLLOW 0 +#endif +#ifndef O_BINARY +# define O_BINARY 0 +#endif + +/* +** The DJGPP compiler environment looks mostly like Unix, but it +** lacks the fcntl() system call. So redefine fcntl() to be something +** that always succeeds. This means that locking does not occur under +** DJGPP. But it's DOS - what did you expect? +*/ +#ifdef __DJGPP__ +# define fcntl(A,B,C) 0 +#endif + +/* +** The threadid macro resolves to the thread-id or to 0. Used for +** testing and debugging only. +*/ +#ifdef SQLITE_UNIX_THREADS +#define threadid pthread_self() +#else +#define threadid 0 +#endif + +/* +** Set or check the OsFile.tid field. This field is set when an OsFile +** is first opened. All subsequent uses of the OsFile verify that the +** same thread is operating on the OsFile. Some operating systems do +** not allow locks to be overridden by other threads and that restriction +** means that sqlite3* database handles cannot be moved from one thread +** to another. This logic makes sure a user does not try to do that +** by mistake. +** +** Version 3.3.1 (2006-01-15): OsFiles can be moved from one thread to +** another as long as we are running on a system that supports threads +** overriding each others locks (which now the most common behavior) +** or if no locks are held. But the OsFile.pLock field needs to be +** recomputed because its key includes the thread-id. See the +** transferOwnership() function below for additional information +*/ +#if defined(SQLITE_UNIX_THREADS) +# define SET_THREADID(X) (X)->tid = pthread_self() +# define CHECK_THREADID(X) (threadsOverrideEachOthersLocks==0 && \ + !pthread_equal((X)->tid, pthread_self())) +#else +# define SET_THREADID(X) +# define CHECK_THREADID(X) 0 +#endif + +/* +** Here is the dirt on POSIX advisory locks: ANSI STD 1003.1 (1996) +** section 6.5.2.2 lines 483 through 490 specify that when a process +** sets or clears a lock, that operation overrides any prior locks set +** by the same process. It does not explicitly say so, but this implies +** that it overrides locks set by the same process using a different +** file descriptor. Consider this test case: +** +** int fd1 = open("./file1", O_RDWR|O_CREAT, 0644); +** int fd2 = open("./file2", O_RDWR|O_CREAT, 0644); +** +** Suppose ./file1 and ./file2 are really the same file (because +** one is a hard or symbolic link to the other) then if you set +** an exclusive lock on fd1, then try to get an exclusive lock +** on fd2, it works. I would have expected the second lock to +** fail since there was already a lock on the file due to fd1. +** But not so. Since both locks came from the same process, the +** second overrides the first, even though they were on different +** file descriptors opened on different file names. +** +** Bummer. If you ask me, this is broken. Badly broken. It means +** that we cannot use POSIX locks to synchronize file access among +** competing threads of the same process. POSIX locks will work fine +** to synchronize access for threads in separate processes, but not +** threads within the same process. +** +** To work around the problem, SQLite has to manage file locks internally +** on its own. Whenever a new database is opened, we have to find the +** specific inode of the database file (the inode is determined by the +** st_dev and st_ino fields of the stat structure that fstat() fills in) +** and check for locks already existing on that inode. When locks are +** created or removed, we have to look at our own internal record of the +** locks to see if another thread has previously set a lock on that same +** inode. +** +** The OsFile structure for POSIX is no longer just an integer file +** descriptor. It is now a structure that holds the integer file +** descriptor and a pointer to a structure that describes the internal +** locks on the corresponding inode. There is one locking structure +** per inode, so if the same inode is opened twice, both OsFile structures +** point to the same locking structure. The locking structure keeps +** a reference count (so we will know when to delete it) and a "cnt" +** field that tells us its internal lock status. cnt==0 means the +** file is unlocked. cnt==-1 means the file has an exclusive lock. +** cnt>0 means there are cnt shared locks on the file. +** +** Any attempt to lock or unlock a file first checks the locking +** structure. The fcntl() system call is only invoked to set a +** POSIX lock if the internal lock structure transitions between +** a locked and an unlocked state. +** +** 2004-Jan-11: +** More recent discoveries about POSIX advisory locks. (The more +** I discover, the more I realize the a POSIX advisory locks are +** an abomination.) +** +** If you close a file descriptor that points to a file that has locks, +** all locks on that file that are owned by the current process are +** released. To work around this problem, each OsFile structure contains +** a pointer to an openCnt structure. There is one openCnt structure +** per open inode, which means that multiple OsFiles can point to a single +** openCnt. When an attempt is made to close an OsFile, if there are +** other OsFiles open on the same inode that are holding locks, the call +** to close() the file descriptor is deferred until all of the locks clear. +** The openCnt structure keeps a list of file descriptors that need to +** be closed and that list is walked (and cleared) when the last lock +** clears. +** +** First, under Linux threads, because each thread has a separate +** process ID, lock operations in one thread do not override locks +** to the same file in other threads. Linux threads behave like +** separate processes in this respect. But, if you close a file +** descriptor in linux threads, all locks are cleared, even locks +** on other threads and even though the other threads have different +** process IDs. Linux threads is inconsistent in this respect. +** (I'm beginning to think that linux threads is an abomination too.) +** The consequence of this all is that the hash table for the lockInfo +** structure has to include the process id as part of its key because +** locks in different threads are treated as distinct. But the +** openCnt structure should not include the process id in its +** key because close() clears lock on all threads, not just the current +** thread. Were it not for this goofiness in linux threads, we could +** combine the lockInfo and openCnt structures into a single structure. +** +** 2004-Jun-28: +** On some versions of linux, threads can override each others locks. +** On others not. Sometimes you can change the behavior on the same +** system by setting the LD_ASSUME_KERNEL environment variable. The +** POSIX standard is silent as to which behavior is correct, as far +** as I can tell, so other versions of unix might show the same +** inconsistency. There is no little doubt in my mind that posix +** advisory locks and linux threads are profoundly broken. +** +** To work around the inconsistencies, we have to test at runtime +** whether or not threads can override each others locks. This test +** is run once, the first time any lock is attempted. A static +** variable is set to record the results of this test for future +** use. +*/ + +/* +** An instance of the following structure serves as the key used +** to locate a particular lockInfo structure given its inode. +** +** If threads cannot override each others locks, then we set the +** lockKey.tid field to the thread ID. If threads can override +** each others locks then tid is always set to zero. tid is omitted +** if we compile without threading support. +*/ +struct lockKey { + dev_t dev; /* Device number */ + ino_t ino; /* Inode number */ +#ifdef SQLITE_UNIX_THREADS + pthread_t tid; /* Thread ID or zero if threads can override each other */ +#endif +}; + +/* +** An instance of the following structure is allocated for each open +** inode on each thread with a different process ID. (Threads have +** different process IDs on linux, but not on most other unixes.) +** +** A single inode can have multiple file descriptors, so each OsFile +** structure contains a pointer to an instance of this object and this +** object keeps a count of the number of OsFiles pointing to it. +*/ +struct lockInfo { + struct lockKey key; /* The lookup key */ + int cnt; /* Number of SHARED locks held */ + int locktype; /* One of SHARED_LOCK, RESERVED_LOCK etc. */ + int nRef; /* Number of pointers to this structure */ +}; + +/* +** An instance of the following structure serves as the key used +** to locate a particular openCnt structure given its inode. This +** is the same as the lockKey except that the thread ID is omitted. +*/ +struct openKey { + dev_t dev; /* Device number */ + ino_t ino; /* Inode number */ +}; + +/* +** An instance of the following structure is allocated for each open +** inode. This structure keeps track of the number of locks on that +** inode. If a close is attempted against an inode that is holding +** locks, the close is deferred until all locks clear by adding the +** file descriptor to be closed to the pending list. +*/ +struct openCnt { + struct openKey key; /* The lookup key */ + int nRef; /* Number of pointers to this structure */ + int nLock; /* Number of outstanding locks */ + int nPending; /* Number of pending close() operations */ + int *aPending; /* Malloced space holding fd's awaiting a close() */ +}; + +/* +** These hash tables map inodes and file descriptors (really, lockKey and +** openKey structures) into lockInfo and openCnt structures. Access to +** these hash tables must be protected by a mutex. +*/ +static Hash lockHash = { SQLITE_HASH_BINARY, 0, 0, 0, 0, 0 }; +static Hash openHash = { SQLITE_HASH_BINARY, 0, 0, 0, 0, 0 }; + + +#ifdef SQLITE_UNIX_THREADS +/* +** This variable records whether or not threads can override each others +** locks. +** +** 0: No. Threads cannot override each others locks. +** 1: Yes. Threads can override each others locks. +** -1: We don't know yet. +** +** On some systems, we know at compile-time if threads can override each +** others locks. On those systems, the SQLITE_THREAD_OVERRIDE_LOCK macro +** will be set appropriately. On other systems, we have to check at +** runtime. On these latter systems, SQLTIE_THREAD_OVERRIDE_LOCK is +** undefined. +** +** This variable normally has file scope only. But during testing, we make +** it a global so that the test code can change its value in order to verify +** that the right stuff happens in either case. +*/ +#ifndef SQLITE_THREAD_OVERRIDE_LOCK +# define SQLITE_THREAD_OVERRIDE_LOCK -1 +#endif +#ifdef SQLITE_TEST +int threadsOverrideEachOthersLocks = SQLITE_THREAD_OVERRIDE_LOCK; +#else +static int threadsOverrideEachOthersLocks = SQLITE_THREAD_OVERRIDE_LOCK; +#endif + +/* +** This structure holds information passed into individual test +** threads by the testThreadLockingBehavior() routine. +*/ +struct threadTestData { + int fd; /* File to be locked */ + struct flock lock; /* The locking operation */ + int result; /* Result of the locking operation */ +}; + +#ifdef SQLITE_LOCK_TRACE +/* +** Print out information about all locking operations. +** +** This routine is used for troubleshooting locks on multithreaded +** platforms. Enable by compiling with the -DSQLITE_LOCK_TRACE +** command-line option on the compiler. This code is normally +** turned off. +*/ +static int lockTrace(int fd, int op, struct flock *p){ + char *zOpName, *zType; + int s; + int savedErrno; + if( op==F_GETLK ){ + zOpName = "GETLK"; + }else if( op==F_SETLK ){ + zOpName = "SETLK"; + }else{ + s = fcntl(fd, op, p); + sqlite3DebugPrintf("fcntl unknown %d %d %d\n", fd, op, s); + return s; + } + if( p->l_type==F_RDLCK ){ + zType = "RDLCK"; + }else if( p->l_type==F_WRLCK ){ + zType = "WRLCK"; + }else if( p->l_type==F_UNLCK ){ + zType = "UNLCK"; + }else{ + assert( 0 ); + } + assert( p->l_whence==SEEK_SET ); + s = fcntl(fd, op, p); + savedErrno = errno; + sqlite3DebugPrintf("fcntl %d %d %s %s %d %d %d %d\n", + threadid, fd, zOpName, zType, (int)p->l_start, (int)p->l_len, + (int)p->l_pid, s); + if( s && op==F_SETLK && (p->l_type==F_RDLCK || p->l_type==F_WRLCK) ){ + struct flock l2; + l2 = *p; + fcntl(fd, F_GETLK, &l2); + if( l2.l_type==F_RDLCK ){ + zType = "RDLCK"; + }else if( l2.l_type==F_WRLCK ){ + zType = "WRLCK"; + }else if( l2.l_type==F_UNLCK ){ + zType = "UNLCK"; + }else{ + assert( 0 ); + } + sqlite3DebugPrintf("fcntl-failure-reason: %s %d %d %d\n", + zType, (int)l2.l_start, (int)l2.l_len, (int)l2.l_pid); + } + errno = savedErrno; + return s; +} +#define fcntl lockTrace +#endif /* SQLITE_LOCK_TRACE */ + +/* +** The testThreadLockingBehavior() routine launches two separate +** threads on this routine. This routine attempts to lock a file +** descriptor then returns. The success or failure of that attempt +** allows the testThreadLockingBehavior() procedure to determine +** whether or not threads can override each others locks. +*/ +static void *threadLockingTest(void *pArg){ + struct threadTestData *pData = (struct threadTestData*)pArg; + pData->result = fcntl(pData->fd, F_SETLK, &pData->lock); + return pArg; +} + +/* +** This procedure attempts to determine whether or not threads +** can override each others locks then sets the +** threadsOverrideEachOthersLocks variable appropriately. +*/ +static void testThreadLockingBehavior(int fd_orig){ + int fd; + struct threadTestData d[2]; + pthread_t t[2]; + + fd = dup(fd_orig); + if( fd<0 ) return; + memset(d, 0, sizeof(d)); + d[0].fd = fd; + d[0].lock.l_type = F_RDLCK; + d[0].lock.l_len = 1; + d[0].lock.l_start = 0; + d[0].lock.l_whence = SEEK_SET; + d[1] = d[0]; + d[1].lock.l_type = F_WRLCK; + pthread_create(&t[0], 0, threadLockingTest, &d[0]); + pthread_create(&t[1], 0, threadLockingTest, &d[1]); + pthread_join(t[0], 0); + pthread_join(t[1], 0); + close(fd); + threadsOverrideEachOthersLocks = d[0].result==0 && d[1].result==0; +} +#endif /* SQLITE_UNIX_THREADS */ + +/* +** Release a lockInfo structure previously allocated by findLockInfo(). +*/ +static void releaseLockInfo(struct lockInfo *pLock){ + assert( sqlite3OsInMutex(1) ); + pLock->nRef--; + if( pLock->nRef==0 ){ + sqlite3HashInsert(&lockHash, &pLock->key, sizeof(pLock->key), 0); + sqliteFree(pLock); + } +} + +/* +** Release a openCnt structure previously allocated by findLockInfo(). +*/ +static void releaseOpenCnt(struct openCnt *pOpen){ + assert( sqlite3OsInMutex(1) ); + pOpen->nRef--; + if( pOpen->nRef==0 ){ + sqlite3HashInsert(&openHash, &pOpen->key, sizeof(pOpen->key), 0); + free(pOpen->aPending); + sqliteFree(pOpen); + } +} + +/* +** Given a file descriptor, locate lockInfo and openCnt structures that +** describes that file descriptor. Create new ones if necessary. The +** return values might be uninitialized if an error occurs. +** +** Return the number of errors. +*/ +static int findLockInfo( + int fd, /* The file descriptor used in the key */ + struct lockInfo **ppLock, /* Return the lockInfo structure here */ + struct openCnt **ppOpen /* Return the openCnt structure here */ +){ + int rc; + struct lockKey key1; + struct openKey key2; + struct stat statbuf; + struct lockInfo *pLock; + struct openCnt *pOpen; + rc = fstat(fd, &statbuf); + if( rc!=0 ) return 1; + + assert( sqlite3OsInMutex(1) ); + memset(&key1, 0, sizeof(key1)); + key1.dev = statbuf.st_dev; + key1.ino = statbuf.st_ino; +#ifdef SQLITE_UNIX_THREADS + if( threadsOverrideEachOthersLocks<0 ){ + testThreadLockingBehavior(fd); + } + key1.tid = threadsOverrideEachOthersLocks ? 0 : pthread_self(); +#endif + memset(&key2, 0, sizeof(key2)); + key2.dev = statbuf.st_dev; + key2.ino = statbuf.st_ino; + pLock = (struct lockInfo*)sqlite3HashFind(&lockHash, &key1, sizeof(key1)); + if( pLock==0 ){ + struct lockInfo *pOld; + pLock = sqliteMallocRaw( sizeof(*pLock) ); + if( pLock==0 ){ + rc = 1; + goto exit_findlockinfo; + } + pLock->key = key1; + pLock->nRef = 1; + pLock->cnt = 0; + pLock->locktype = 0; + pOld = sqlite3HashInsert(&lockHash, &pLock->key, sizeof(key1), pLock); + if( pOld!=0 ){ + assert( pOld==pLock ); + sqliteFree(pLock); + rc = 1; + goto exit_findlockinfo; + } + }else{ + pLock->nRef++; + } + *ppLock = pLock; + if( ppOpen!=0 ){ + pOpen = (struct openCnt*)sqlite3HashFind(&openHash, &key2, sizeof(key2)); + if( pOpen==0 ){ + struct openCnt *pOld; + pOpen = sqliteMallocRaw( sizeof(*pOpen) ); + if( pOpen==0 ){ + releaseLockInfo(pLock); + rc = 1; + goto exit_findlockinfo; + } + pOpen->key = key2; + pOpen->nRef = 1; + pOpen->nLock = 0; + pOpen->nPending = 0; + pOpen->aPending = 0; + pOld = sqlite3HashInsert(&openHash, &pOpen->key, sizeof(key2), pOpen); + if( pOld!=0 ){ + assert( pOld==pOpen ); + sqliteFree(pOpen); + releaseLockInfo(pLock); + rc = 1; + goto exit_findlockinfo; + } + }else{ + pOpen->nRef++; + } + *ppOpen = pOpen; + } + +exit_findlockinfo: + return rc; +} + +#ifdef SQLITE_DEBUG +/* +** Helper function for printing out trace information from debugging +** binaries. This returns the string represetation of the supplied +** integer lock-type. +*/ +static const char *locktypeName(int locktype){ + switch( locktype ){ + case NO_LOCK: return "NONE"; + case SHARED_LOCK: return "SHARED"; + case RESERVED_LOCK: return "RESERVED"; + case PENDING_LOCK: return "PENDING"; + case EXCLUSIVE_LOCK: return "EXCLUSIVE"; + } + return "ERROR"; +} +#endif + +/* +** If we are currently in a different thread than the thread that the +** unixFile argument belongs to, then transfer ownership of the unixFile +** over to the current thread. +** +** A unixFile is only owned by a thread on systems where one thread is +** unable to override locks created by a different thread. RedHat9 is +** an example of such a system. +** +** Ownership transfer is only allowed if the unixFile is currently unlocked. +** If the unixFile is locked and an ownership is wrong, then return +** SQLITE_MISUSE. SQLITE_OK is returned if everything works. +*/ +#ifdef SQLITE_UNIX_THREADS +static int transferOwnership(unixFile *pFile){ + int rc; + pthread_t hSelf; + if( threadsOverrideEachOthersLocks ){ + /* Ownership transfers not needed on this system */ + return SQLITE_OK; + } + hSelf = pthread_self(); + if( pthread_equal(pFile->tid, hSelf) ){ + /* We are still in the same thread */ + TRACE1("No-transfer, same thread\n"); + return SQLITE_OK; + } + if( pFile->locktype!=NO_LOCK ){ + /* We cannot change ownership while we are holding a lock! */ + return SQLITE_MISUSE; + } + TRACE4("Transfer ownership of %d from %d to %d\n", pFile->h,pFile->tid,hSelf); + pFile->tid = hSelf; + releaseLockInfo