passagemath-categories 10.6.32__cp314-cp314t-musllinux_1_2_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (719) hide show
  1. passagemath_categories-10.6.32.dist-info/METADATA +156 -0
  2. passagemath_categories-10.6.32.dist-info/RECORD +719 -0
  3. passagemath_categories-10.6.32.dist-info/WHEEL +5 -0
  4. passagemath_categories-10.6.32.dist-info/top_level.txt +2 -0
  5. passagemath_categories.libs/libgcc_s-2d945d6c.so.1 +0 -0
  6. passagemath_categories.libs/libgmp-28992bcb.so.10.5.0 +0 -0
  7. passagemath_categories.libs/libstdc++-85f2cd6d.so.6.0.33 +0 -0
  8. sage/all__sagemath_categories.py +28 -0
  9. sage/arith/all.py +38 -0
  10. sage/arith/constants.pxd +27 -0
  11. sage/arith/functions.cpython-314t-aarch64-linux-musl.so +0 -0
  12. sage/arith/functions.pxd +4 -0
  13. sage/arith/functions.pyx +221 -0
  14. sage/arith/misc.py +6552 -0
  15. sage/arith/multi_modular.cpython-314t-aarch64-linux-musl.so +0 -0
  16. sage/arith/multi_modular.pxd +39 -0
  17. sage/arith/multi_modular.pyx +994 -0
  18. sage/arith/rational_reconstruction.cpython-314t-aarch64-linux-musl.so +0 -0
  19. sage/arith/rational_reconstruction.pxd +4 -0
  20. sage/arith/rational_reconstruction.pyx +115 -0
  21. sage/arith/srange.cpython-314t-aarch64-linux-musl.so +0 -0
  22. sage/arith/srange.pyx +571 -0
  23. sage/calculus/all__sagemath_categories.py +2 -0
  24. sage/calculus/functional.py +481 -0
  25. sage/calculus/functions.py +151 -0
  26. sage/categories/additive_groups.py +73 -0
  27. sage/categories/additive_magmas.py +1044 -0
  28. sage/categories/additive_monoids.py +114 -0
  29. sage/categories/additive_semigroups.py +184 -0
  30. sage/categories/affine_weyl_groups.py +238 -0
  31. sage/categories/algebra_ideals.py +95 -0
  32. sage/categories/algebra_modules.py +96 -0
  33. sage/categories/algebras.py +349 -0
  34. sage/categories/algebras_with_basis.py +377 -0
  35. sage/categories/all.py +160 -0
  36. sage/categories/aperiodic_semigroups.py +29 -0
  37. sage/categories/associative_algebras.py +47 -0
  38. sage/categories/bialgebras.py +101 -0
  39. sage/categories/bialgebras_with_basis.py +414 -0
  40. sage/categories/bimodules.py +206 -0
  41. sage/categories/chain_complexes.py +268 -0
  42. sage/categories/classical_crystals.py +480 -0
  43. sage/categories/coalgebras.py +405 -0
  44. sage/categories/coalgebras_with_basis.py +232 -0
  45. sage/categories/coercion_methods.cpython-314t-aarch64-linux-musl.so +0 -0
  46. sage/categories/coercion_methods.pyx +52 -0
  47. sage/categories/commutative_additive_groups.py +104 -0
  48. sage/categories/commutative_additive_monoids.py +45 -0
  49. sage/categories/commutative_additive_semigroups.py +48 -0
  50. sage/categories/commutative_algebra_ideals.py +87 -0
  51. sage/categories/commutative_algebras.py +94 -0
  52. sage/categories/commutative_ring_ideals.py +58 -0
  53. sage/categories/commutative_rings.py +736 -0
  54. sage/categories/complete_discrete_valuation.py +293 -0
  55. sage/categories/complex_reflection_groups.py +145 -0
  56. sage/categories/complex_reflection_or_generalized_coxeter_groups.py +1249 -0
  57. sage/categories/coxeter_group_algebras.py +186 -0
  58. sage/categories/coxeter_groups.py +3402 -0
  59. sage/categories/crystals.py +2628 -0
  60. sage/categories/cw_complexes.py +216 -0
  61. sage/categories/dedekind_domains.py +137 -0
  62. sage/categories/discrete_valuation.py +325 -0
  63. sage/categories/distributive_magmas_and_additive_magmas.py +100 -0
  64. sage/categories/division_rings.py +114 -0
  65. sage/categories/domains.py +95 -0
  66. sage/categories/drinfeld_modules.py +789 -0
  67. sage/categories/dual.py +42 -0
  68. sage/categories/enumerated_sets.py +1146 -0
  69. sage/categories/euclidean_domains.py +271 -0
  70. sage/categories/examples/algebras_with_basis.py +102 -0
  71. sage/categories/examples/all.py +1 -0
  72. sage/categories/examples/commutative_additive_monoids.py +130 -0
  73. sage/categories/examples/commutative_additive_semigroups.py +199 -0
  74. sage/categories/examples/coxeter_groups.py +8 -0
  75. sage/categories/examples/crystals.py +236 -0
  76. sage/categories/examples/cw_complexes.py +163 -0
  77. sage/categories/examples/facade_sets.py +187 -0
  78. sage/categories/examples/filtered_algebras_with_basis.py +204 -0
  79. sage/categories/examples/filtered_modules_with_basis.py +154 -0
  80. sage/categories/examples/finite_coxeter_groups.py +252 -0
  81. sage/categories/examples/finite_dimensional_algebras_with_basis.py +148 -0
  82. sage/categories/examples/finite_dimensional_lie_algebras_with_basis.py +495 -0
  83. sage/categories/examples/finite_enumerated_sets.py +208 -0
  84. sage/categories/examples/finite_monoids.py +150 -0
  85. sage/categories/examples/finite_semigroups.py +190 -0
  86. sage/categories/examples/finite_weyl_groups.py +191 -0
  87. sage/categories/examples/graded_connected_hopf_algebras_with_basis.py +152 -0
  88. sage/categories/examples/graded_modules_with_basis.py +168 -0
  89. sage/categories/examples/graphs.py +122 -0
  90. sage/categories/examples/hopf_algebras_with_basis.py +145 -0
  91. sage/categories/examples/infinite_enumerated_sets.py +190 -0
  92. sage/categories/examples/lie_algebras.py +352 -0
  93. sage/categories/examples/lie_algebras_with_basis.py +196 -0
  94. sage/categories/examples/magmas.py +162 -0
  95. sage/categories/examples/manifolds.py +94 -0
  96. sage/categories/examples/monoids.py +144 -0
  97. sage/categories/examples/posets.py +178 -0
  98. sage/categories/examples/semigroups.py +580 -0
  99. sage/categories/examples/semigroups_cython.cpython-314t-aarch64-linux-musl.so +0 -0
  100. sage/categories/examples/semigroups_cython.pyx +221 -0
  101. sage/categories/examples/semirings.py +249 -0
  102. sage/categories/examples/sets_cat.py +706 -0
  103. sage/categories/examples/sets_with_grading.py +101 -0
  104. sage/categories/examples/with_realizations.py +542 -0
  105. sage/categories/fields.py +991 -0
  106. sage/categories/filtered_algebras.py +63 -0
  107. sage/categories/filtered_algebras_with_basis.py +548 -0
  108. sage/categories/filtered_hopf_algebras_with_basis.py +138 -0
  109. sage/categories/filtered_modules.py +210 -0
  110. sage/categories/filtered_modules_with_basis.py +1209 -0
  111. sage/categories/finite_complex_reflection_groups.py +1506 -0
  112. sage/categories/finite_coxeter_groups.py +1138 -0
  113. sage/categories/finite_crystals.py +103 -0
  114. sage/categories/finite_dimensional_algebras_with_basis.py +1860 -0
  115. sage/categories/finite_dimensional_bialgebras_with_basis.py +33 -0
  116. sage/categories/finite_dimensional_coalgebras_with_basis.py +33 -0
  117. sage/categories/finite_dimensional_graded_lie_algebras_with_basis.py +231 -0
  118. sage/categories/finite_dimensional_hopf_algebras_with_basis.py +38 -0
  119. sage/categories/finite_dimensional_lie_algebras_with_basis.py +2774 -0
  120. sage/categories/finite_dimensional_modules_with_basis.py +1407 -0
  121. sage/categories/finite_dimensional_nilpotent_lie_algebras_with_basis.py +167 -0
  122. sage/categories/finite_dimensional_semisimple_algebras_with_basis.py +270 -0
  123. sage/categories/finite_enumerated_sets.py +769 -0
  124. sage/categories/finite_fields.py +252 -0
  125. sage/categories/finite_groups.py +256 -0
  126. sage/categories/finite_lattice_posets.py +242 -0
  127. sage/categories/finite_monoids.py +316 -0
  128. sage/categories/finite_permutation_groups.py +339 -0
  129. sage/categories/finite_posets.py +1994 -0
  130. sage/categories/finite_semigroups.py +136 -0
  131. sage/categories/finite_sets.py +93 -0
  132. sage/categories/finite_weyl_groups.py +39 -0
  133. sage/categories/finitely_generated_lambda_bracket_algebras.py +112 -0
  134. sage/categories/finitely_generated_lie_conformal_algebras.py +114 -0
  135. sage/categories/finitely_generated_magmas.py +57 -0
  136. sage/categories/finitely_generated_semigroups.py +214 -0
  137. sage/categories/function_fields.py +76 -0
  138. sage/categories/g_sets.py +77 -0
  139. sage/categories/gcd_domains.py +65 -0
  140. sage/categories/generalized_coxeter_groups.py +94 -0
  141. sage/categories/graded_algebras.py +85 -0
  142. sage/categories/graded_algebras_with_basis.py +258 -0
  143. sage/categories/graded_bialgebras.py +32 -0
  144. sage/categories/graded_bialgebras_with_basis.py +32 -0
  145. sage/categories/graded_coalgebras.py +65 -0
  146. sage/categories/graded_coalgebras_with_basis.py +51 -0
  147. sage/categories/graded_hopf_algebras.py +41 -0
  148. sage/categories/graded_hopf_algebras_with_basis.py +169 -0
  149. sage/categories/graded_lie_algebras.py +91 -0
  150. sage/categories/graded_lie_algebras_with_basis.py +44 -0
  151. sage/categories/graded_lie_conformal_algebras.py +74 -0
  152. sage/categories/graded_modules.py +133 -0
  153. sage/categories/graded_modules_with_basis.py +329 -0
  154. sage/categories/graphs.py +138 -0
  155. sage/categories/group_algebras.py +430 -0
  156. sage/categories/groupoid.py +94 -0
  157. sage/categories/groups.py +667 -0
  158. sage/categories/h_trivial_semigroups.py +64 -0
  159. sage/categories/hecke_modules.py +185 -0
  160. sage/categories/highest_weight_crystals.py +980 -0
  161. sage/categories/hopf_algebras.py +219 -0
  162. sage/categories/hopf_algebras_with_basis.py +309 -0
  163. sage/categories/infinite_enumerated_sets.py +115 -0
  164. sage/categories/integral_domains.py +203 -0
  165. sage/categories/j_trivial_semigroups.py +29 -0
  166. sage/categories/kac_moody_algebras.py +82 -0
  167. sage/categories/kahler_algebras.py +203 -0
  168. sage/categories/l_trivial_semigroups.py +63 -0
  169. sage/categories/lambda_bracket_algebras.py +280 -0
  170. sage/categories/lambda_bracket_algebras_with_basis.py +107 -0
  171. sage/categories/lattice_posets.py +89 -0
  172. sage/categories/left_modules.py +49 -0
  173. sage/categories/lie_algebras.py +1070 -0
  174. sage/categories/lie_algebras_with_basis.py +261 -0
  175. sage/categories/lie_conformal_algebras.py +350 -0
  176. sage/categories/lie_conformal_algebras_with_basis.py +147 -0
  177. sage/categories/lie_groups.py +73 -0
  178. sage/categories/loop_crystals.py +1290 -0
  179. sage/categories/magmas.py +1189 -0
  180. sage/categories/magmas_and_additive_magmas.py +149 -0
  181. sage/categories/magmatic_algebras.py +365 -0
  182. sage/categories/manifolds.py +352 -0
  183. sage/categories/matrix_algebras.py +40 -0
  184. sage/categories/metric_spaces.py +387 -0
  185. sage/categories/modular_abelian_varieties.py +78 -0
  186. sage/categories/modules.py +989 -0
  187. sage/categories/modules_with_basis.py +2794 -0
  188. sage/categories/monoid_algebras.py +38 -0
  189. sage/categories/monoids.py +739 -0
  190. sage/categories/noetherian_rings.py +87 -0
  191. sage/categories/number_fields.py +242 -0
  192. sage/categories/ore_modules.py +189 -0
  193. sage/categories/partially_ordered_monoids.py +49 -0
  194. sage/categories/permutation_groups.py +63 -0
  195. sage/categories/pointed_sets.py +42 -0
  196. sage/categories/polyhedra.py +74 -0
  197. sage/categories/poor_man_map.py +270 -0
  198. sage/categories/posets.py +722 -0
  199. sage/categories/principal_ideal_domains.py +270 -0
  200. sage/categories/quantum_group_representations.py +543 -0
  201. sage/categories/quotient_fields.py +728 -0
  202. sage/categories/r_trivial_semigroups.py +45 -0
  203. sage/categories/regular_crystals.py +898 -0
  204. sage/categories/regular_supercrystals.py +170 -0
  205. sage/categories/right_modules.py +49 -0
  206. sage/categories/ring_ideals.py +74 -0
  207. sage/categories/rings.py +1904 -0
  208. sage/categories/rngs.py +175 -0
  209. sage/categories/schemes.py +393 -0
  210. sage/categories/semigroups.py +1060 -0
  211. sage/categories/semirings.py +71 -0
  212. sage/categories/semisimple_algebras.py +114 -0
  213. sage/categories/sets_with_grading.py +235 -0
  214. sage/categories/shephard_groups.py +43 -0
  215. sage/categories/signed_tensor.py +120 -0
  216. sage/categories/simplicial_complexes.py +134 -0
  217. sage/categories/simplicial_sets.py +1206 -0
  218. sage/categories/super_algebras.py +149 -0
  219. sage/categories/super_algebras_with_basis.py +144 -0
  220. sage/categories/super_hopf_algebras_with_basis.py +126 -0
  221. sage/categories/super_lie_conformal_algebras.py +193 -0
  222. sage/categories/super_modules.py +229 -0
  223. sage/categories/super_modules_with_basis.py +193 -0
  224. sage/categories/supercommutative_algebras.py +99 -0
  225. sage/categories/supercrystals.py +406 -0
  226. sage/categories/tensor.py +110 -0
  227. sage/categories/topological_spaces.py +170 -0
  228. sage/categories/triangular_kac_moody_algebras.py +439 -0
  229. sage/categories/tutorial.py +58 -0
  230. sage/categories/unique_factorization_domains.py +318 -0
  231. sage/categories/unital_algebras.py +426 -0
  232. sage/categories/vector_bundles.py +159 -0
  233. sage/categories/vector_spaces.py +357 -0
  234. sage/categories/weyl_groups.py +853 -0
  235. sage/combinat/all__sagemath_categories.py +34 -0
  236. sage/combinat/backtrack.py +180 -0
  237. sage/combinat/combinat.py +2269 -0
  238. sage/combinat/combinat_cython.cpython-314t-aarch64-linux-musl.so +0 -0
  239. sage/combinat/combinat_cython.pxd +6 -0
  240. sage/combinat/combinat_cython.pyx +390 -0
  241. sage/combinat/combination.py +796 -0
  242. sage/combinat/combinatorial_map.py +416 -0
  243. sage/combinat/composition.py +2192 -0
  244. sage/combinat/dlx.py +510 -0
  245. sage/combinat/integer_lists/__init__.py +7 -0
  246. sage/combinat/integer_lists/base.cpython-314t-aarch64-linux-musl.so +0 -0
  247. sage/combinat/integer_lists/base.pxd +16 -0
  248. sage/combinat/integer_lists/base.pyx +713 -0
  249. sage/combinat/integer_lists/invlex.cpython-314t-aarch64-linux-musl.so +0 -0
  250. sage/combinat/integer_lists/invlex.pxd +4 -0
  251. sage/combinat/integer_lists/invlex.pyx +1650 -0
  252. sage/combinat/integer_lists/lists.py +328 -0
  253. sage/combinat/integer_lists/nn.py +48 -0
  254. sage/combinat/integer_vector.py +1818 -0
  255. sage/combinat/integer_vector_weighted.py +413 -0
  256. sage/combinat/matrices/all__sagemath_categories.py +5 -0
  257. sage/combinat/matrices/dancing_links.cpython-314t-aarch64-linux-musl.so +0 -0
  258. sage/combinat/matrices/dancing_links.pyx +1159 -0
  259. sage/combinat/matrices/dancing_links_c.h +380 -0
  260. sage/combinat/matrices/dlxcpp.py +136 -0
  261. sage/combinat/partition.py +10070 -0
  262. sage/combinat/partitions.cpython-314t-aarch64-linux-musl.so +0 -0
  263. sage/combinat/partitions.pyx +743 -0
  264. sage/combinat/permutation.py +10168 -0
  265. sage/combinat/permutation_cython.cpython-314t-aarch64-linux-musl.so +0 -0
  266. sage/combinat/permutation_cython.pxd +11 -0
  267. sage/combinat/permutation_cython.pyx +407 -0
  268. sage/combinat/q_analogues.py +1090 -0
  269. sage/combinat/ranker.py +268 -0
  270. sage/combinat/subset.py +1561 -0
  271. sage/combinat/subsets_hereditary.py +202 -0
  272. sage/combinat/subsets_pairwise.py +184 -0
  273. sage/combinat/tools.py +63 -0
  274. sage/combinat/tuple.py +348 -0
  275. sage/data_structures/all.py +2 -0
  276. sage/data_structures/all__sagemath_categories.py +2 -0
  277. sage/data_structures/binary_matrix.pxd +138 -0
  278. sage/data_structures/binary_search.cpython-314t-aarch64-linux-musl.so +0 -0
  279. sage/data_structures/binary_search.pxd +3 -0
  280. sage/data_structures/binary_search.pyx +66 -0
  281. sage/data_structures/bitset.cpython-314t-aarch64-linux-musl.so +0 -0
  282. sage/data_structures/bitset.pxd +40 -0
  283. sage/data_structures/bitset.pyx +2385 -0
  284. sage/data_structures/bitset_base.cpython-314t-aarch64-linux-musl.so +0 -0
  285. sage/data_structures/bitset_base.pxd +926 -0
  286. sage/data_structures/bitset_base.pyx +117 -0
  287. sage/data_structures/bitset_intrinsics.h +487 -0
  288. sage/data_structures/blas_dict.cpython-314t-aarch64-linux-musl.so +0 -0
  289. sage/data_structures/blas_dict.pxd +12 -0
  290. sage/data_structures/blas_dict.pyx +469 -0
  291. sage/data_structures/list_of_pairs.cpython-314t-aarch64-linux-musl.so +0 -0
  292. sage/data_structures/list_of_pairs.pxd +16 -0
  293. sage/data_structures/list_of_pairs.pyx +122 -0
  294. sage/data_structures/mutable_poset.py +3312 -0
  295. sage/data_structures/pairing_heap.cpython-314t-aarch64-linux-musl.so +0 -0
  296. sage/data_structures/pairing_heap.h +346 -0
  297. sage/data_structures/pairing_heap.pxd +88 -0
  298. sage/data_structures/pairing_heap.pyx +1464 -0
  299. sage/data_structures/sparse_bitset.pxd +62 -0
  300. sage/data_structures/stream.py +5070 -0
  301. sage/databases/all__sagemath_categories.py +7 -0
  302. sage/databases/sql_db.py +2236 -0
  303. sage/ext/all__sagemath_categories.py +3 -0
  304. sage/ext/fast_callable.cpython-314t-aarch64-linux-musl.so +0 -0
  305. sage/ext/fast_callable.pxd +4 -0
  306. sage/ext/fast_callable.pyx +2746 -0
  307. sage/ext/fast_eval.cpython-314t-aarch64-linux-musl.so +0 -0
  308. sage/ext/fast_eval.pxd +1 -0
  309. sage/ext/fast_eval.pyx +102 -0
  310. sage/ext/interpreters/__init__.py +1 -0
  311. sage/ext/interpreters/all__sagemath_categories.py +2 -0
  312. sage/ext/interpreters/wrapper_el.cpython-314t-aarch64-linux-musl.so +0 -0
  313. sage/ext/interpreters/wrapper_el.pxd +18 -0
  314. sage/ext/interpreters/wrapper_el.pyx +148 -0
  315. sage/ext/interpreters/wrapper_py.cpython-314t-aarch64-linux-musl.so +0 -0
  316. sage/ext/interpreters/wrapper_py.pxd +17 -0
  317. sage/ext/interpreters/wrapper_py.pyx +133 -0
  318. sage/functions/airy.py +937 -0
  319. sage/functions/all.py +97 -0
  320. sage/functions/bessel.py +2102 -0
  321. sage/functions/error.py +784 -0
  322. sage/functions/exp_integral.py +1529 -0
  323. sage/functions/gamma.py +1087 -0
  324. sage/functions/generalized.py +672 -0
  325. sage/functions/hyperbolic.py +747 -0
  326. sage/functions/hypergeometric.py +1156 -0
  327. sage/functions/jacobi.py +1705 -0
  328. sage/functions/log.py +1402 -0
  329. sage/functions/min_max.py +338 -0
  330. sage/functions/orthogonal_polys.py +3106 -0
  331. sage/functions/other.py +2303 -0
  332. sage/functions/piecewise.py +1505 -0
  333. sage/functions/prime_pi.cpython-314t-aarch64-linux-musl.so +0 -0
  334. sage/functions/prime_pi.pyx +262 -0
  335. sage/functions/special.py +1212 -0
  336. sage/functions/spike_function.py +278 -0
  337. sage/functions/transcendental.py +690 -0
  338. sage/functions/trig.py +1062 -0
  339. sage/functions/wigner.py +726 -0
  340. sage/geometry/abc.cpython-314t-aarch64-linux-musl.so +0 -0
  341. sage/geometry/abc.pyx +82 -0
  342. sage/geometry/all__sagemath_categories.py +1 -0
  343. sage/groups/all__sagemath_categories.py +11 -0
  344. sage/groups/generic.py +1733 -0
  345. sage/groups/groups_catalog.py +113 -0
  346. sage/groups/perm_gps/all__sagemath_categories.py +1 -0
  347. sage/groups/perm_gps/partn_ref/all.py +1 -0
  348. sage/groups/perm_gps/partn_ref/all__sagemath_categories.py +1 -0
  349. sage/groups/perm_gps/partn_ref/automorphism_group_canonical_label.cpython-314t-aarch64-linux-musl.so +0 -0
  350. sage/groups/perm_gps/partn_ref/automorphism_group_canonical_label.pxd +52 -0
  351. sage/groups/perm_gps/partn_ref/automorphism_group_canonical_label.pyx +906 -0
  352. sage/groups/perm_gps/partn_ref/canonical_augmentation.cpython-314t-aarch64-linux-musl.so +0 -0
  353. sage/groups/perm_gps/partn_ref/canonical_augmentation.pxd +85 -0
  354. sage/groups/perm_gps/partn_ref/canonical_augmentation.pyx +534 -0
  355. sage/groups/perm_gps/partn_ref/data_structures.cpython-314t-aarch64-linux-musl.so +0 -0
  356. sage/groups/perm_gps/partn_ref/data_structures.pxd +576 -0
  357. sage/groups/perm_gps/partn_ref/data_structures.pyx +1792 -0
  358. sage/groups/perm_gps/partn_ref/double_coset.cpython-314t-aarch64-linux-musl.so +0 -0
  359. sage/groups/perm_gps/partn_ref/double_coset.pxd +45 -0
  360. sage/groups/perm_gps/partn_ref/double_coset.pyx +739 -0
  361. sage/groups/perm_gps/partn_ref/refinement_lists.cpython-314t-aarch64-linux-musl.so +0 -0
  362. sage/groups/perm_gps/partn_ref/refinement_lists.pxd +18 -0
  363. sage/groups/perm_gps/partn_ref/refinement_lists.pyx +82 -0
  364. sage/groups/perm_gps/partn_ref/refinement_python.cpython-314t-aarch64-linux-musl.so +0 -0
  365. sage/groups/perm_gps/partn_ref/refinement_python.pxd +16 -0
  366. sage/groups/perm_gps/partn_ref/refinement_python.pyx +564 -0
  367. sage/groups/perm_gps/partn_ref/refinement_sets.cpython-314t-aarch64-linux-musl.so +0 -0
  368. sage/groups/perm_gps/partn_ref/refinement_sets.pxd +60 -0
  369. sage/groups/perm_gps/partn_ref/refinement_sets.pyx +858 -0
  370. sage/interfaces/abc.py +140 -0
  371. sage/interfaces/all.py +58 -0
  372. sage/interfaces/all__sagemath_categories.py +1 -0
  373. sage/interfaces/expect.py +1643 -0
  374. sage/interfaces/interface.py +1682 -0
  375. sage/interfaces/process.cpython-314t-aarch64-linux-musl.so +0 -0
  376. sage/interfaces/process.pxd +5 -0
  377. sage/interfaces/process.pyx +288 -0
  378. sage/interfaces/quit.py +167 -0
  379. sage/interfaces/sage0.py +604 -0
  380. sage/interfaces/sagespawn.cpython-314t-aarch64-linux-musl.so +0 -0
  381. sage/interfaces/sagespawn.pyx +308 -0
  382. sage/interfaces/tab_completion.py +101 -0
  383. sage/misc/all__sagemath_categories.py +78 -0
  384. sage/misc/allocator.cpython-314t-aarch64-linux-musl.so +0 -0
  385. sage/misc/allocator.pxd +6 -0
  386. sage/misc/allocator.pyx +47 -0
  387. sage/misc/binary_tree.cpython-314t-aarch64-linux-musl.so +0 -0
  388. sage/misc/binary_tree.pxd +29 -0
  389. sage/misc/binary_tree.pyx +537 -0
  390. sage/misc/callable_dict.cpython-314t-aarch64-linux-musl.so +0 -0
  391. sage/misc/callable_dict.pyx +89 -0
  392. sage/misc/citation.cpython-314t-aarch64-linux-musl.so +0 -0
  393. sage/misc/citation.pyx +159 -0
  394. sage/misc/converting_dict.py +293 -0
  395. sage/misc/defaults.py +129 -0
  396. sage/misc/derivative.cpython-314t-aarch64-linux-musl.so +0 -0
  397. sage/misc/derivative.pyx +223 -0
  398. sage/misc/functional.py +2005 -0
  399. sage/misc/html.py +589 -0
  400. sage/misc/latex.py +2673 -0
  401. sage/misc/latex_macros.py +236 -0
  402. sage/misc/latex_standalone.py +1833 -0
  403. sage/misc/map_threaded.py +38 -0
  404. sage/misc/mathml.py +76 -0
  405. sage/misc/method_decorator.py +88 -0
  406. sage/misc/mrange.py +755 -0
  407. sage/misc/multireplace.py +41 -0
  408. sage/misc/object_multiplexer.py +92 -0
  409. sage/misc/parser.cpython-314t-aarch64-linux-musl.so +0 -0
  410. sage/misc/parser.pyx +1107 -0
  411. sage/misc/random_testing.py +264 -0
  412. sage/misc/rest_index_of_methods.py +377 -0
  413. sage/misc/search.cpython-314t-aarch64-linux-musl.so +0 -0
  414. sage/misc/search.pxd +2 -0
  415. sage/misc/search.pyx +68 -0
  416. sage/misc/stopgap.cpython-314t-aarch64-linux-musl.so +0 -0
  417. sage/misc/stopgap.pyx +95 -0
  418. sage/misc/table.py +853 -0
  419. sage/monoids/all__sagemath_categories.py +1 -0
  420. sage/monoids/indexed_free_monoid.py +1071 -0
  421. sage/monoids/monoid.py +82 -0
  422. sage/numerical/all__sagemath_categories.py +1 -0
  423. sage/numerical/backends/all__sagemath_categories.py +1 -0
  424. sage/numerical/backends/generic_backend.cpython-314t-aarch64-linux-musl.so +0 -0
  425. sage/numerical/backends/generic_backend.pxd +61 -0
  426. sage/numerical/backends/generic_backend.pyx +1893 -0
  427. sage/numerical/backends/generic_sdp_backend.cpython-314t-aarch64-linux-musl.so +0 -0
  428. sage/numerical/backends/generic_sdp_backend.pxd +38 -0
  429. sage/numerical/backends/generic_sdp_backend.pyx +755 -0
  430. sage/parallel/all.py +6 -0
  431. sage/parallel/decorate.py +575 -0
  432. sage/parallel/map_reduce.py +1997 -0
  433. sage/parallel/multiprocessing_sage.py +76 -0
  434. sage/parallel/ncpus.py +35 -0
  435. sage/parallel/parallelism.py +364 -0
  436. sage/parallel/reference.py +47 -0
  437. sage/parallel/use_fork.py +333 -0
  438. sage/rings/abc.cpython-314t-aarch64-linux-musl.so +0 -0
  439. sage/rings/abc.pxd +31 -0
  440. sage/rings/abc.pyx +526 -0
  441. sage/rings/algebraic_closure_finite_field.py +1154 -0
  442. sage/rings/all__sagemath_categories.py +91 -0
  443. sage/rings/big_oh.py +227 -0
  444. sage/rings/continued_fraction.py +2754 -0
  445. sage/rings/continued_fraction_gosper.py +220 -0
  446. sage/rings/factorint.cpython-314t-aarch64-linux-musl.so +0 -0
  447. sage/rings/factorint.pyx +295 -0
  448. sage/rings/fast_arith.cpython-314t-aarch64-linux-musl.so +0 -0
  449. sage/rings/fast_arith.pxd +21 -0
  450. sage/rings/fast_arith.pyx +535 -0
  451. sage/rings/finite_rings/all__sagemath_categories.py +9 -0
  452. sage/rings/finite_rings/conway_polynomials.py +542 -0
  453. sage/rings/finite_rings/element_base.cpython-314t-aarch64-linux-musl.so +0 -0
  454. sage/rings/finite_rings/element_base.pxd +12 -0
  455. sage/rings/finite_rings/element_base.pyx +1176 -0
  456. sage/rings/finite_rings/finite_field_base.cpython-314t-aarch64-linux-musl.so +0 -0
  457. sage/rings/finite_rings/finite_field_base.pxd +7 -0
  458. sage/rings/finite_rings/finite_field_base.pyx +2171 -0
  459. sage/rings/finite_rings/finite_field_constructor.py +827 -0
  460. sage/rings/finite_rings/finite_field_prime_modn.py +372 -0
  461. sage/rings/finite_rings/galois_group.py +154 -0
  462. sage/rings/finite_rings/hom_finite_field.cpython-314t-aarch64-linux-musl.so +0 -0
  463. sage/rings/finite_rings/hom_finite_field.pxd +23 -0
  464. sage/rings/finite_rings/hom_finite_field.pyx +856 -0
  465. sage/rings/finite_rings/hom_prime_finite_field.cpython-314t-aarch64-linux-musl.so +0 -0
  466. sage/rings/finite_rings/hom_prime_finite_field.pxd +15 -0
  467. sage/rings/finite_rings/hom_prime_finite_field.pyx +164 -0
  468. sage/rings/finite_rings/homset.py +357 -0
  469. sage/rings/finite_rings/integer_mod.cpython-314t-aarch64-linux-musl.so +0 -0
  470. sage/rings/finite_rings/integer_mod.pxd +56 -0
  471. sage/rings/finite_rings/integer_mod.pyx +4586 -0
  472. sage/rings/finite_rings/integer_mod_limits.h +11 -0
  473. sage/rings/finite_rings/integer_mod_ring.py +2044 -0
  474. sage/rings/finite_rings/residue_field.cpython-314t-aarch64-linux-musl.so +0 -0
  475. sage/rings/finite_rings/residue_field.pxd +30 -0
  476. sage/rings/finite_rings/residue_field.pyx +1811 -0
  477. sage/rings/finite_rings/stdint.pxd +19 -0
  478. sage/rings/fraction_field.py +1452 -0
  479. sage/rings/fraction_field_element.cpython-314t-aarch64-linux-musl.so +0 -0
  480. sage/rings/fraction_field_element.pyx +1357 -0
  481. sage/rings/function_field/all.py +7 -0
  482. sage/rings/function_field/all__sagemath_categories.py +2 -0
  483. sage/rings/function_field/constructor.py +218 -0
  484. sage/rings/function_field/element.cpython-314t-aarch64-linux-musl.so +0 -0
  485. sage/rings/function_field/element.pxd +11 -0
  486. sage/rings/function_field/element.pyx +1008 -0
  487. sage/rings/function_field/element_rational.cpython-314t-aarch64-linux-musl.so +0 -0
  488. sage/rings/function_field/element_rational.pyx +513 -0
  489. sage/rings/function_field/extensions.py +230 -0
  490. sage/rings/function_field/function_field.py +1468 -0
  491. sage/rings/function_field/function_field_rational.py +1005 -0
  492. sage/rings/function_field/ideal.py +1155 -0
  493. sage/rings/function_field/ideal_rational.py +629 -0
  494. sage/rings/function_field/jacobian_base.py +826 -0
  495. sage/rings/function_field/jacobian_hess.py +1053 -0
  496. sage/rings/function_field/jacobian_khuri_makdisi.py +1027 -0
  497. sage/rings/function_field/maps.py +1039 -0
  498. sage/rings/function_field/order.py +281 -0
  499. sage/rings/function_field/order_basis.py +586 -0
  500. sage/rings/function_field/order_rational.py +576 -0
  501. sage/rings/function_field/place.py +426 -0
  502. sage/rings/function_field/place_rational.py +181 -0
  503. sage/rings/generic.py +320 -0
  504. sage/rings/homset.py +332 -0
  505. sage/rings/ideal.py +1885 -0
  506. sage/rings/ideal_monoid.py +215 -0
  507. sage/rings/infinity.py +1890 -0
  508. sage/rings/integer.cpython-314t-aarch64-linux-musl.so +0 -0
  509. sage/rings/integer.pxd +45 -0
  510. sage/rings/integer.pyx +7874 -0
  511. sage/rings/integer_ring.cpython-314t-aarch64-linux-musl.so +0 -0
  512. sage/rings/integer_ring.pxd +8 -0
  513. sage/rings/integer_ring.pyx +1693 -0
  514. sage/rings/laurent_series_ring.py +931 -0
  515. sage/rings/laurent_series_ring_element.cpython-314t-aarch64-linux-musl.so +0 -0
  516. sage/rings/laurent_series_ring_element.pxd +11 -0
  517. sage/rings/laurent_series_ring_element.pyx +1927 -0
  518. sage/rings/lazy_series.py +7815 -0
  519. sage/rings/lazy_series_ring.py +4356 -0
  520. sage/rings/localization.py +1043 -0
  521. sage/rings/morphism.cpython-314t-aarch64-linux-musl.so +0 -0
  522. sage/rings/morphism.pxd +39 -0
  523. sage/rings/morphism.pyx +3299 -0
  524. sage/rings/multi_power_series_ring.py +1145 -0
  525. sage/rings/multi_power_series_ring_element.py +2184 -0
  526. sage/rings/noncommutative_ideals.cpython-314t-aarch64-linux-musl.so +0 -0
  527. sage/rings/noncommutative_ideals.pyx +423 -0
  528. sage/rings/number_field/all__sagemath_categories.py +1 -0
  529. sage/rings/number_field/number_field_base.cpython-314t-aarch64-linux-musl.so +0 -0
  530. sage/rings/number_field/number_field_base.pxd +8 -0
  531. sage/rings/number_field/number_field_base.pyx +507 -0
  532. sage/rings/number_field/number_field_element_base.cpython-314t-aarch64-linux-musl.so +0 -0
  533. sage/rings/number_field/number_field_element_base.pxd +6 -0
  534. sage/rings/number_field/number_field_element_base.pyx +36 -0
  535. sage/rings/number_field/number_field_ideal.py +3550 -0
  536. sage/rings/padics/all__sagemath_categories.py +4 -0
  537. sage/rings/padics/local_generic.py +1670 -0
  538. sage/rings/padics/local_generic_element.cpython-314t-aarch64-linux-musl.so +0 -0
  539. sage/rings/padics/local_generic_element.pxd +5 -0
  540. sage/rings/padics/local_generic_element.pyx +1017 -0
  541. sage/rings/padics/misc.py +256 -0
  542. sage/rings/padics/padic_generic.py +1911 -0
  543. sage/rings/padics/pow_computer.cpython-314t-aarch64-linux-musl.so +0 -0
  544. sage/rings/padics/pow_computer.pxd +38 -0
  545. sage/rings/padics/pow_computer.pyx +671 -0
  546. sage/rings/padics/precision_error.py +24 -0
  547. sage/rings/polynomial/all__sagemath_categories.py +25 -0
  548. sage/rings/polynomial/commutative_polynomial.cpython-314t-aarch64-linux-musl.so +0 -0
  549. sage/rings/polynomial/commutative_polynomial.pxd +6 -0
  550. sage/rings/polynomial/commutative_polynomial.pyx +24 -0
  551. sage/rings/polynomial/cyclotomic.cpython-314t-aarch64-linux-musl.so +0 -0
  552. sage/rings/polynomial/cyclotomic.pyx +404 -0
  553. sage/rings/polynomial/flatten.py +711 -0
  554. sage/rings/polynomial/ideal.py +102 -0
  555. sage/rings/polynomial/infinite_polynomial_element.py +1768 -0
  556. sage/rings/polynomial/infinite_polynomial_ring.py +1653 -0
  557. sage/rings/polynomial/laurent_polynomial.cpython-314t-aarch64-linux-musl.so +0 -0
  558. sage/rings/polynomial/laurent_polynomial.pxd +18 -0
  559. sage/rings/polynomial/laurent_polynomial.pyx +2190 -0
  560. sage/rings/polynomial/laurent_polynomial_ideal.py +590 -0
  561. sage/rings/polynomial/laurent_polynomial_ring.py +832 -0
  562. sage/rings/polynomial/laurent_polynomial_ring_base.py +708 -0
  563. sage/rings/polynomial/multi_polynomial.cpython-314t-aarch64-linux-musl.so +0 -0
  564. sage/rings/polynomial/multi_polynomial.pxd +12 -0
  565. sage/rings/polynomial/multi_polynomial.pyx +3082 -0
  566. sage/rings/polynomial/multi_polynomial_element.py +2570 -0
  567. sage/rings/polynomial/multi_polynomial_ideal.py +5771 -0
  568. sage/rings/polynomial/multi_polynomial_ring.py +947 -0
  569. sage/rings/polynomial/multi_polynomial_ring_base.cpython-314t-aarch64-linux-musl.so +0 -0
  570. sage/rings/polynomial/multi_polynomial_ring_base.pxd +15 -0
  571. sage/rings/polynomial/multi_polynomial_ring_base.pyx +1855 -0
  572. sage/rings/polynomial/multi_polynomial_sequence.py +2204 -0
  573. sage/rings/polynomial/polydict.cpython-314t-aarch64-linux-musl.so +0 -0
  574. sage/rings/polynomial/polydict.pxd +45 -0
  575. sage/rings/polynomial/polydict.pyx +2701 -0
  576. sage/rings/polynomial/polynomial_compiled.cpython-314t-aarch64-linux-musl.so +0 -0
  577. sage/rings/polynomial/polynomial_compiled.pxd +59 -0
  578. sage/rings/polynomial/polynomial_compiled.pyx +509 -0
  579. sage/rings/polynomial/polynomial_element.cpython-314t-aarch64-linux-musl.so +0 -0
  580. sage/rings/polynomial/polynomial_element.pxd +64 -0
  581. sage/rings/polynomial/polynomial_element.pyx +13255 -0
  582. sage/rings/polynomial/polynomial_element_generic.py +1637 -0
  583. sage/rings/polynomial/polynomial_fateman.py +97 -0
  584. sage/rings/polynomial/polynomial_quotient_ring.py +2465 -0
  585. sage/rings/polynomial/polynomial_quotient_ring_element.py +779 -0
  586. sage/rings/polynomial/polynomial_ring.py +3784 -0
  587. sage/rings/polynomial/polynomial_ring_constructor.py +1051 -0
  588. sage/rings/polynomial/polynomial_ring_homomorphism.cpython-314t-aarch64-linux-musl.so +0 -0
  589. sage/rings/polynomial/polynomial_ring_homomorphism.pxd +5 -0
  590. sage/rings/polynomial/polynomial_ring_homomorphism.pyx +121 -0
  591. sage/rings/polynomial/polynomial_singular_interface.py +549 -0
  592. sage/rings/polynomial/symmetric_ideal.py +989 -0
  593. sage/rings/polynomial/symmetric_reduction.cpython-314t-aarch64-linux-musl.so +0 -0
  594. sage/rings/polynomial/symmetric_reduction.pxd +8 -0
  595. sage/rings/polynomial/symmetric_reduction.pyx +669 -0
  596. sage/rings/polynomial/term_order.py +2279 -0
  597. sage/rings/polynomial/toy_buchberger.py +449 -0
  598. sage/rings/polynomial/toy_d_basis.py +387 -0
  599. sage/rings/polynomial/toy_variety.py +362 -0
  600. sage/rings/power_series_mpoly.cpython-314t-aarch64-linux-musl.so +0 -0
  601. sage/rings/power_series_mpoly.pxd +9 -0
  602. sage/rings/power_series_mpoly.pyx +161 -0
  603. sage/rings/power_series_poly.cpython-314t-aarch64-linux-musl.so +0 -0
  604. sage/rings/power_series_poly.pxd +10 -0
  605. sage/rings/power_series_poly.pyx +1317 -0
  606. sage/rings/power_series_ring.py +1441 -0
  607. sage/rings/power_series_ring_element.cpython-314t-aarch64-linux-musl.so +0 -0
  608. sage/rings/power_series_ring_element.pxd +12 -0
  609. sage/rings/power_series_ring_element.pyx +3028 -0
  610. sage/rings/puiseux_series_ring.py +487 -0
  611. sage/rings/puiseux_series_ring_element.cpython-314t-aarch64-linux-musl.so +0 -0
  612. sage/rings/puiseux_series_ring_element.pxd +7 -0
  613. sage/rings/puiseux_series_ring_element.pyx +1055 -0
  614. sage/rings/qqbar_decorators.py +167 -0
  615. sage/rings/quotient_ring.py +1598 -0
  616. sage/rings/quotient_ring_element.py +979 -0
  617. sage/rings/rational.cpython-314t-aarch64-linux-musl.so +0 -0
  618. sage/rings/rational.pxd +20 -0
  619. sage/rings/rational.pyx +4284 -0
  620. sage/rings/rational_field.py +1730 -0
  621. sage/rings/real_double.cpython-314t-aarch64-linux-musl.so +0 -0
  622. sage/rings/real_double.pxd +16 -0
  623. sage/rings/real_double.pyx +2218 -0
  624. sage/rings/real_lazy.cpython-314t-aarch64-linux-musl.so +0 -0
  625. sage/rings/real_lazy.pxd +30 -0
  626. sage/rings/real_lazy.pyx +1773 -0
  627. sage/rings/ring.cpython-314t-aarch64-linux-musl.so +0 -0
  628. sage/rings/ring.pxd +30 -0
  629. sage/rings/ring.pyx +850 -0
  630. sage/rings/semirings/all.py +3 -0
  631. sage/rings/semirings/non_negative_integer_semiring.py +107 -0
  632. sage/rings/semirings/tropical_mpolynomial.py +972 -0
  633. sage/rings/semirings/tropical_polynomial.py +997 -0
  634. sage/rings/semirings/tropical_semiring.cpython-314t-aarch64-linux-musl.so +0 -0
  635. sage/rings/semirings/tropical_semiring.pyx +676 -0
  636. sage/rings/semirings/tropical_variety.py +1701 -0
  637. sage/rings/sum_of_squares.cpython-314t-aarch64-linux-musl.so +0 -0
  638. sage/rings/sum_of_squares.pxd +3 -0
  639. sage/rings/sum_of_squares.pyx +336 -0
  640. sage/rings/tests.py +504 -0
  641. sage/schemes/affine/affine_homset.py +508 -0
  642. sage/schemes/affine/affine_morphism.py +1574 -0
  643. sage/schemes/affine/affine_point.py +460 -0
  644. sage/schemes/affine/affine_rational_point.py +308 -0
  645. sage/schemes/affine/affine_space.py +1264 -0
  646. sage/schemes/affine/affine_subscheme.py +592 -0
  647. sage/schemes/affine/all.py +25 -0
  648. sage/schemes/all__sagemath_categories.py +5 -0
  649. sage/schemes/generic/algebraic_scheme.py +2092 -0
  650. sage/schemes/generic/all.py +5 -0
  651. sage/schemes/generic/ambient_space.py +400 -0
  652. sage/schemes/generic/divisor.py +465 -0
  653. sage/schemes/generic/divisor_group.py +313 -0
  654. sage/schemes/generic/glue.py +84 -0
  655. sage/schemes/generic/homset.py +820 -0
  656. sage/schemes/generic/hypersurface.py +234 -0
  657. sage/schemes/generic/morphism.py +2107 -0
  658. sage/schemes/generic/point.py +237 -0
  659. sage/schemes/generic/scheme.py +1190 -0
  660. sage/schemes/generic/spec.py +199 -0
  661. sage/schemes/product_projective/all.py +6 -0
  662. sage/schemes/product_projective/homset.py +236 -0
  663. sage/schemes/product_projective/morphism.py +517 -0
  664. sage/schemes/product_projective/point.py +568 -0
  665. sage/schemes/product_projective/rational_point.py +550 -0
  666. sage/schemes/product_projective/space.py +1301 -0
  667. sage/schemes/product_projective/subscheme.py +466 -0
  668. sage/schemes/projective/all.py +24 -0
  669. sage/schemes/projective/proj_bdd_height.py +453 -0
  670. sage/schemes/projective/projective_homset.py +718 -0
  671. sage/schemes/projective/projective_morphism.py +2792 -0
  672. sage/schemes/projective/projective_point.py +1484 -0
  673. sage/schemes/projective/projective_rational_point.py +569 -0
  674. sage/schemes/projective/projective_space.py +2571 -0
  675. sage/schemes/projective/projective_subscheme.py +1574 -0
  676. sage/sets/all.py +17 -0
  677. sage/sets/cartesian_product.py +376 -0
  678. sage/sets/condition_set.py +525 -0
  679. sage/sets/disjoint_set.cpython-314t-aarch64-linux-musl.so +0 -0
  680. sage/sets/disjoint_set.pxd +36 -0
  681. sage/sets/disjoint_set.pyx +998 -0
  682. sage/sets/disjoint_union_enumerated_sets.py +625 -0
  683. sage/sets/family.cpython-314t-aarch64-linux-musl.so +0 -0
  684. sage/sets/family.pxd +12 -0
  685. sage/sets/family.pyx +1556 -0
  686. sage/sets/finite_enumerated_set.py +406 -0
  687. sage/sets/finite_set_map_cy.cpython-314t-aarch64-linux-musl.so +0 -0
  688. sage/sets/finite_set_map_cy.pxd +34 -0
  689. sage/sets/finite_set_map_cy.pyx +708 -0
  690. sage/sets/finite_set_maps.py +591 -0
  691. sage/sets/image_set.py +448 -0
  692. sage/sets/integer_range.py +829 -0
  693. sage/sets/non_negative_integers.py +241 -0
  694. sage/sets/positive_integers.py +93 -0
  695. sage/sets/primes.py +188 -0
  696. sage/sets/real_set.py +2760 -0
  697. sage/sets/recursively_enumerated_set.cpython-314t-aarch64-linux-musl.so +0 -0
  698. sage/sets/recursively_enumerated_set.pxd +31 -0
  699. sage/sets/recursively_enumerated_set.pyx +2082 -0
  700. sage/sets/set.py +2083 -0
  701. sage/sets/set_from_iterator.py +1021 -0
  702. sage/sets/totally_ordered_finite_set.py +329 -0
  703. sage/symbolic/all__sagemath_categories.py +1 -0
  704. sage/symbolic/function.cpython-314t-aarch64-linux-musl.so +0 -0
  705. sage/symbolic/function.pxd +29 -0
  706. sage/symbolic/function.pyx +1488 -0
  707. sage/symbolic/symbols.py +56 -0
  708. sage/tests/all__sagemath_categories.py +1 -0
  709. sage/tests/cython.cpython-314t-aarch64-linux-musl.so +0 -0
  710. sage/tests/cython.pyx +37 -0
  711. sage/tests/stl_vector.cpython-314t-aarch64-linux-musl.so +0 -0
  712. sage/tests/stl_vector.pyx +171 -0
  713. sage/typeset/all.py +6 -0
  714. sage/typeset/ascii_art.py +295 -0
  715. sage/typeset/character_art.py +789 -0
  716. sage/typeset/character_art_factory.py +572 -0
  717. sage/typeset/symbols.py +334 -0
  718. sage/typeset/unicode_art.py +183 -0
  719. sage/typeset/unicode_characters.py +101 -0
@@ -0,0 +1,2236 @@
1
+ # sage_setup: distribution = sagemath-categories
2
+ r"""
3
+ Relational (sqlite) Databases Module
4
+
5
+ INFO:
6
+
7
+ This module implements classes (SQLDatabase and SQLQuery (pythonic
8
+ implementation for the user with little or no knowledge of sqlite))
9
+ that wrap the basic functionality of sqlite.
10
+
11
+ Databases are constructed via a triple indexed dictionary called a
12
+ skeleton. A skeleton should be constructed to fit the following format::
13
+
14
+ | - skeleton -- a triple-indexed dictionary
15
+ | - outer key -- table name
16
+ | - inner key -- column name
17
+ | - inner inner key -- one of the following:
18
+ | - ``primary_key`` -- boolean, whether column has been set as
19
+ primary key
20
+ | - ``index`` -- boolean, whether column has been set as index
21
+ | - ``unique`` -- boolean, whether column has been set as unique
22
+ | - ``sql`` -- one of ``'TEXT'``, ``'BOOLEAN'``, ``'INTEGER'``,
23
+ ``'REAL'``, or other user defined type
24
+
25
+ An example skeleton of a database with one table, that table with one
26
+ column::
27
+
28
+ {'table1':{'col1':{'primary_key':False, 'index':True, 'sql':'REAL'}}}
29
+
30
+ SQLDatabases can also be constructed via the add, drop, and commit
31
+ functions. The vacuum function is also useful for restoring hard disk space
32
+ after a database has shrunk in size.
33
+
34
+ A SQLQuery can be constructed by providing a query_dict, which is a
35
+ dictionary with the following sample format::
36
+
37
+ {'table_name': 'tblname', 'display_cols': ['col1', 'col2', 'col3'], 'expression':[col, operator, value]}
38
+
39
+ Finally a SQLQuery also allows the user to directly input the query
40
+ string for a database, and also supports the '?' syntax by allowing an
41
+ argument for a tuple of parameters to query.
42
+
43
+ For full details, please see the tutorial. sage.graphs.graph_database.py
44
+ is an example of implementing a database class in Sage using this
45
+ interface.
46
+
47
+ AUTHORS:
48
+
49
+ - R. Andrew Ohana (2011-07-16): refactored and rewrote most of the code;
50
+ merged the Generic classes into the non-Generic versions; changed the
51
+ skeleton format to include a boolean indicating whether the column stores
52
+ unique keys; changed the index names so as to avoid potential ambiguity
53
+
54
+ - Emily A. Kirkman (2008-09-20): added functionality to generate plots and
55
+ reformat output in show
56
+
57
+ - Emily A. Kirkman and Robert L. Miller (2007-06-17): initial version
58
+ """
59
+ # FUTURE TODOs (Ignore for now):
60
+ # - order by clause in query strings
61
+ # - delete from query containing joins
62
+ # - add data by column
63
+ # - wrap sqlalchemy
64
+ # - create query interface (with interact)
65
+ # - allow kwds arguments to SQLQuery (like GraphQuery)
66
+
67
+ # ****************************************************************************
68
+ # Copyright (C) 2011 R. Andrew Ohana <andrew.ohana@gmail.com>
69
+ # Copyright (C) 2007 Emily A. Kirkman
70
+ # Robert L. Miller
71
+ #
72
+ # This program is free software: you can redistribute it and/or modify
73
+ # it under the terms of the GNU General Public License as published by
74
+ # the Free Software Foundation, either version 2 of the License, or
75
+ # (at your option) any later version.
76
+ # https://www.gnu.org/licenses/
77
+ # ****************************************************************************
78
+
79
+ import sqlite3 as sqlite
80
+ import os
81
+ import re
82
+
83
+ from sage.structure.sage_object import SageObject
84
+
85
+ sqlite_keywords = ['ABORT','ACTION','ADD','AFTER','ALL','ALTER','ANALYZE',
86
+ 'AND','AS','ASC','ATTACH','AUTOINCREMENT','BEFORE','BEGIN','BETWEEN','BY',
87
+ 'CASCADE','CASE','CAST','CHECK','COLLATE','COLUMN','COMMIT','CONFLICT',
88
+ 'CONSTRAINT','CREATE','CROSS','CURRENT_DATE','CURRENT_TIME',
89
+ 'CURRENT_TIMESTAMP','DATABASE','DEFAULT','DEFERRABLE','DEFERRED','DELETE',
90
+ 'DESC','DETACH','DISTINCT','DROP','EACH','ELSE','END','ESCAPE','EXCEPT',
91
+ 'EXCLUSIVE','EXISTS','EXPLAIN','FAIL','FOR','FOREIGN','FROM','FULL',
92
+ 'GLOB','GROUP','HAVING','IF','IGNORE','IMMEDIATE','IN','INDEX','INDEXED',
93
+ 'INITIALLY','INNER','INSERT','INSTEAD','INTERSECT','INTO','IS','ISNULL',
94
+ 'JOIN','KEY','LEFT','LIKE','LIMIT','MATCH','NATURAL','NO','NOT','NOTNULL',
95
+ 'NULL','OF','OFFSET','ON','OR','ORDER','OUTER','PLAN','PRAGMA','PRIMARY',
96
+ 'QUERY','RAISE','REFERENCES','REGEXP','REINDEX','RELEASE','RENAME',
97
+ 'REPLACE','RESTRICT','RIGHT','ROLLBACK','ROW','SAVEPOINT','SELECT','SET',
98
+ 'TABLE','TEMP','TEMPORARY','THEN','TO','TRANSACTION','TRIGGER','UNION',
99
+ 'UNIQUE','UPDATE','USING','VACUUM','VALUES','VIEW','VIRTUAL','WHEN',
100
+ 'WHERE']
101
+
102
+
103
+ def regexp(expr, item):
104
+ """
105
+ Function to define regular expressions in pysqlite.
106
+
107
+ OUTPUT:
108
+
109
+ - ``True`` if parameter ``item`` matches the regular expression
110
+ parameter ``expr``
111
+ - ``False`` otherwise (i.e.: no match)
112
+
113
+ REFERENCES:
114
+
115
+ - [Ha2005]_
116
+
117
+ EXAMPLES::
118
+
119
+ sage: from sage.databases.sql_db import regexp
120
+ sage: regexp('.s.*','cs')
121
+ True
122
+ sage: regexp('.s.*','ccs')
123
+ False
124
+ sage: regexp('.s.*','cscccc')
125
+ True
126
+ """
127
+ r = re.compile(expr)
128
+ return r.match(item) is not None
129
+
130
+
131
+ def verify_type(type):
132
+ """
133
+ Verify that the specified ``type`` is one of the allowed strings.
134
+
135
+ EXAMPLES::
136
+
137
+ sage: from sage.databases.sql_db import verify_type
138
+ sage: verify_type('INT')
139
+ True
140
+ sage: verify_type('int')
141
+ True
142
+ sage: verify_type('float')
143
+ Traceback (most recent call last):
144
+ ...
145
+ TypeError: float is not a legal type.
146
+ """
147
+ types = ['INTEGER','INT','BOOLEAN','REAL','TEXT','BOOL','BLOB','NOTYPE']
148
+ if type.upper() not in types:
149
+ raise TypeError('%s is not a legal type.' % type)
150
+ return True
151
+
152
+
153
+ def verify_column(col_dict):
154
+ """
155
+ Verify that ``col_dict`` is in proper format, and return a dict with
156
+ default values filled in. Proper format::
157
+
158
+ {'primary_key':False, 'index':False, 'unique': False, 'sql':'REAL'}
159
+
160
+ EXAMPLES::
161
+
162
+ sage: from sage.databases.sql_db import verify_column
163
+ sage: col = {'sql':'BOOLEAN'}
164
+ sage: verify_column(col)
165
+ {'index': False, 'primary_key': False, 'sql': 'BOOLEAN', 'unique': False}
166
+ sage: col = {'primary_key':True, 'sql':'INTEGER'}
167
+ sage: verify_column(col)
168
+ {'index': True, 'primary_key': True, 'sql': 'INTEGER', 'unique': True}
169
+ sage: verify_column({})
170
+ Traceback (most recent call last):
171
+ ...
172
+ ValueError: SQL type must be declared, e.g. {'sql':'REAL'}.
173
+ """
174
+ d = {}
175
+ d['primary_key'] = col_dict.get('primary_key', False)
176
+ d['index'] = col_dict.get('index', False) or d['primary_key']
177
+ d['unique'] = col_dict.get('unique', False) or d['primary_key']
178
+ if 'sql' not in col_dict:
179
+ raise ValueError("SQL type must be declared, e.g. {'sql':'REAL'}.")
180
+ if verify_type(col_dict['sql']):
181
+ d['sql'] = col_dict['sql']
182
+ return d
183
+
184
+
185
+ def verify_operator(operator):
186
+ """
187
+ Check that ``operator`` is one of the allowed strings.
188
+ Legal operators include the following strings:
189
+
190
+ - '='
191
+ - '<='
192
+ - '>='
193
+ - '<'
194
+ - '>'
195
+ - '<>'
196
+ - 'like'
197
+ - 'regexp'
198
+ - 'is null'
199
+ - 'is not null'
200
+
201
+ EXAMPLES::
202
+
203
+ sage: from sage.databases.sql_db import verify_operator
204
+ sage: verify_operator('<=')
205
+ True
206
+ sage: verify_operator('regexp')
207
+ True
208
+ sage: verify_operator('is null')
209
+ True
210
+ sage: verify_operator('not_an_operator')
211
+ Traceback (most recent call last):
212
+ ...
213
+ TypeError: not_an_operator is not a legal operator.
214
+ """
215
+ binaries = ['=','<=','>=','like','<','>','<>','regexp']
216
+ unaries = ['is null','is not null']
217
+ if operator not in binaries and operator not in unaries:
218
+ raise TypeError('%s is not a legal operator.' % operator)
219
+ return True
220
+
221
+
222
+ def construct_skeleton(database):
223
+ """
224
+ Construct a database skeleton from the sql data. The skeleton data
225
+ structure is a triple indexed dictionary of the following format::
226
+
227
+ | - skeleton -- a triple-indexed dictionary
228
+ | - outer key -- table name
229
+ | - inner key -- column name
230
+ | - inner inner key -- one of the following:
231
+ | - ``primary_key`` -- boolean, whether column has been set as
232
+ primary key
233
+ | - ``index`` -- boolean, whether column has been set as index
234
+ | - ``unique`` -- boolean, whether column has been set as unique
235
+ | - ``sql`` -- one of ``'TEXT'``, ``'BOOLEAN'``, ``'INTEGER'``,
236
+ ``'REAL'``, or other user defined type
237
+
238
+ An example skeleton of a database with one table, that table with one
239
+ column::
240
+
241
+ {'table1':{'col1':{'primary_key':False, 'unique': True, 'index':True, 'sql':'REAL'}}}
242
+
243
+ EXAMPLES::
244
+
245
+ sage: # needs sage.graphs graph_database
246
+ sage: G = SQLDatabase(GraphDatabase().__dblocation__, False)
247
+ sage: from sage.databases.sql_db import construct_skeleton
248
+ sage: sorted(construct_skeleton(G))
249
+ ['aut_grp', 'degrees', 'graph_data', 'misc', 'spectrum']
250
+ """
251
+ skeleton = {}
252
+ cur = database.__connection__.cursor()
253
+ exe = cur.execute("SELECT name FROM sqlite_master WHERE TYPE='table'")
254
+ for table in exe.fetchall():
255
+ skeleton[table[0]] = {}
256
+ exe1 = cur.execute("PRAGMA table_info(%s)" % table[0])
257
+ for col in exe1.fetchall():
258
+ if not col[2]:
259
+ typ = 'NOTYPE'
260
+ else:
261
+ typ = col[2]
262
+ skeleton[table[0]][col[1]] = {'sql':typ,
263
+ 'primary_key':(col[5] != 0), 'index':(col[5] != 0), 'unique': False}
264
+ exe2 = cur.execute("PRAGMA index_list(%s)" % table[0])
265
+ for col in exe2.fetchall():
266
+ if col[1].find('sqlite') == -1:
267
+ if os.path.basename(database.__dblocation__) == 'graphs.db':
268
+ name = col[1]
269
+ else:
270
+ name = col[1][len(table[0])+3:]
271
+ skeleton[table[0]][name]['index'] = True
272
+ skeleton[table[0]][name]['unique'] = bool(col[2])
273
+ else:
274
+ name = cur.execute("PRAGMA index_info(%s)" % col[1])
275
+ name = name.fetchone()[2]
276
+ skeleton[table[0]][name]['unique'] = bool(col[2])
277
+ return skeleton
278
+
279
+
280
+ p = 0
281
+
282
+
283
+ def _create_print_table(cur, col_titles, **kwds):
284
+ r"""
285
+ Create a nice printable table from the cursor given with the given
286
+ column titles.
287
+
288
+ KEYWORDS:
289
+
290
+ - ``max_field_size`` -- how wide each field can be
291
+ - ``format_cols`` -- dictionary that allows the user to specify the
292
+ format of a column's output by supplying a function. The format of
293
+ the dictionary is::
294
+
295
+ {'column_name':(lambda x: format_function(x))}
296
+
297
+ - ``plot_cols`` -- dictionary that allows the user to specify that a
298
+ plot should be drawn by the object generated by a data slice. Note
299
+ that plot kwds are permitted. The dictionary format is::
300
+
301
+ {'column_name':((lambda x: plot_function(x)), **kwds)}
302
+
303
+ - ``relabel_cols`` -- dictionary to specify a relabeling of column
304
+ headers. The dictionary format is::
305
+
306
+ {'table_name':{'old_col_name':'new_col_name'}}
307
+
308
+ - ``id_col`` -- reference to a column that can be used as an object
309
+ identifier for each row
310
+
311
+ - ``html_table`` -- boolean that if ``True`` creates an html table instead of
312
+ a print table. Always set to ``True`` in the notebook
313
+
314
+ EXAMPLES::
315
+
316
+ sage: DB = SQLDatabase()
317
+ sage: DB.create_table('simon',{'a1':{'sql':'bool', 'primary_key':False}, 'b2':{'sql':'int'}})
318
+ sage: DB.add_data('simon',[(0,0),(1,1),(1,2)])
319
+ sage: r = SQLQuery(DB, {'table_name':'simon', 'display_cols':['a1'], 'expression':['b2','<=', 6]})
320
+ sage: from sage.databases.sql_db import _create_print_table
321
+ sage: cur = r.__database__.__connection__.cursor()
322
+ sage: exe = cur.execute(r.__query_string__, r.__param_tuple__)
323
+ sage: _create_print_table(cur, [des[0] for des in cur.description])
324
+ 'a1 \n--------------------\n0 \n1 \n1 '
325
+ """
326
+ fcol_index = []
327
+ pcol_index = []
328
+
329
+ if 'format_cols' in kwds:
330
+ fcol_map = []
331
+ for col in kwds['format_cols']:
332
+ fcol_map.append(kwds['format_cols'][col])
333
+ fcol_index.append(col_titles.index(col))
334
+ if 'plot_cols' in kwds:
335
+ pcol_map = []
336
+ for col in kwds['plot_cols']:
337
+ pcol_map.append(kwds['plot_cols'][col])
338
+ pcol_index.append(col_titles.index(col))
339
+
340
+ max_field_size = kwds['max_field_size'] if 'max_field_size' in kwds \
341
+ else 20
342
+ id_col_index = col_titles.index(kwds['id_col']) if 'id_col' in kwds \
343
+ else None
344
+
345
+ if 'relabel_cols' in kwds:
346
+ relabel_cols = kwds['relabel_cols']
347
+ for i in range(len(col_titles)):
348
+ try:
349
+ col_titles[i] = relabel_cols[col_titles[i]]
350
+ except KeyError:
351
+ continue
352
+ global p
353
+ p = 0
354
+
355
+ def row_str(row, html):
356
+ f = 0
357
+ global p
358
+ cur_str = []
359
+ for index in range(len(col_titles)):
360
+ if index in pcol_index:
361
+ if html:
362
+ plot = pcol_map[p % len(pcol_map)](row[index])
363
+ plot.save('%d.png' % p, figsize=[1,1])
364
+ field_val = ' <td bgcolor=white align=center> ' \
365
+ + '%s <br> <img src="cell://%d.png"> ' % (row[index],p) \
366
+ + '</td>\n'
367
+ p += 1
368
+ else:
369
+ raise NotImplementedError('Cannot display plot on '
370
+ 'command line.')
371
+ else:
372
+ if index in fcol_index:
373
+ if id_col_index is None:
374
+ field_val = fcol_map[f](row[index])
375
+ else:
376
+ field_val = fcol_map[f](row[index], row[id_col_index])
377
+ f += 1
378
+ else:
379
+ field_val = row[index]
380
+ if html:
381
+ field_val = ' <td bgcolor=white align=center> ' \
382
+ + str(field_val) + ' </td>\n'
383
+ else:
384
+ field_val = str(field_val).ljust(max_field_size)
385
+ cur_str.append(field_val)
386
+ return ' '.join(cur_str)
387
+
388
+ if 'html_table' in kwds and kwds['html_table']:
389
+ # Notebook Version
390
+ ret = '<html><!--notruncate-->\n'
391
+ ret += ' <table bgcolor=lightgrey cellpadding=0>\n'
392
+ ret += ' <tr>\n <td bgcolor=white align=center> '
393
+ ret += (' </td>\n <td bgcolor=white '
394
+ + 'align=center> ').join(col_titles)
395
+ ret += ' </td>\n </tr>\n'
396
+ ret += '\n'.join([' <tr>\n ' + row_str(row, True) + ' </tr>'
397
+ for row in cur])
398
+ ret += '\n </table>\n</html>'
399
+ else:
400
+ # Command Prompt Version
401
+ ret = ' '.join([col.ljust(max_field_size) for col in col_titles])
402
+ ret += '\n' + '-' * max_field_size * len(col_titles) + '\n'
403
+ ret += '\n'.join([row_str(row, False) for row in cur])
404
+ return ret
405
+
406
+
407
+ class SQLQuery(SageObject):
408
+ def __init__(self, database, *args, **kwds):
409
+ """
410
+ A query for a SQLite database.
411
+
412
+ INPUT:
413
+
414
+ - ``database`` -- a SQLDatabase object
415
+ - ``query_dict`` -- dictionary specifying the query itself. The
416
+ format is::
417
+
418
+ {'table_name':'tblname', 'display_cols':['col1', 'col2','col3'], 'expression': [col, operator, value]}
419
+
420
+ NOTE:
421
+ Every SQL type we are using is ultimately represented as a string,
422
+ so if you wish to save actual strings to a database, you actually
423
+ need to do something like: '"value"'.
424
+
425
+ See the documentation of ``SQLDatabase`` for an introduction to using
426
+ SQLite in Sage.
427
+
428
+ EXAMPLES::
429
+
430
+ sage: D = SQLDatabase()
431
+ sage: D.create_table('simon',{'a1':{'sql':'bool', 'primary_key':False}, 'b2':{'sql':'int'}})
432
+ sage: D.add_data('simon',[(0,0),(1,2),(2,4)])
433
+ sage: r = SQLQuery(D, {'table_name':'simon', 'display_cols':['a1'], 'expression':['b2','<=', 3]})
434
+ sage: r.show()
435
+ a1
436
+ --------------------
437
+ 0
438
+ 1
439
+
440
+ Test that :issue:`27562` is fixed::
441
+
442
+ sage: D = SQLDatabase()
443
+ sage: r = SQLQuery(D, {'table_name':'simon', 'display_cols':['a1'], 'expression':['b2','<=', 3]})
444
+ Traceback (most recent call last):
445
+ ...
446
+ ValueError: Database has no table simon
447
+ sage: D.create_table('simon',{'a1':{'sql':'bool', 'primary_key':False}, 'b2':{'sql':'int'}})
448
+ sage: D.create_table('simon',{'a1':{'sql':'bool', 'primary_key':False}, 'b2':{'sql':'int'}})
449
+ Traceback (most recent call last):
450
+ ...
451
+ ValueError: Database already has a table named simon
452
+ sage: SQLQuery(D, {'table_name':'simon', 'display_cols':['a1'], 'expression':['c1','>',2]})
453
+ Traceback (most recent call last):
454
+ ...
455
+ ValueError: Table has no column c1
456
+ """
457
+ if not isinstance(database, SQLDatabase):
458
+ raise TypeError('%s is not a valid SQLDatabase' % database)
459
+ self.__database__ = database
460
+ total_args = len(args) + len(kwds)
461
+ if total_args == 0:
462
+ self.__query_string__ = ''
463
+ self.__param_tuple__ = tuple()
464
+ self.__query_dict__ = {}
465
+ return
466
+ for x in args:
467
+ if isinstance(x,dict):
468
+ if 'query_dict' not in kwds:
469
+ kwds['query_dict'] = x
470
+ elif isinstance(x, str):
471
+ if 'query_string' not in kwds:
472
+ kwds['query_string'] = x
473
+ elif isinstance(x, tuple):
474
+ if 'param_tuple' not in kwds:
475
+ kwds['param_tuple'] = x
476
+ if total_args > 2 or not ('query_dict' in kwds or
477
+ 'query_string' in kwds) or ('query_dict' in kwds and
478
+ ('param_tuple' in kwds or 'query_string' in kwds)):
479
+ raise ValueError('Query must be constructed with either a '
480
+ + 'dictionary or a string and tuple')
481
+
482
+ if 'query_dict' in kwds:
483
+ query_dict = kwds['query_dict']
484
+ else:
485
+ self.__query_string__ = kwds['query_string']
486
+ if 'param_tuple' in kwds:
487
+ self.__param_tuple__ = tuple(str(x) for x in kwds['param_tuple'])
488
+ else:
489
+ self.__param_tuple__ = tuple()
490
+ return
491
+ if query_dict:
492
+ skel = database.__skeleton__
493
+ if query_dict['table_name'] not in skel:
494
+ raise ValueError("Database has no table %s"
495
+ % query_dict['table_name'])
496
+ table_name = query_dict['table_name']
497
+ if query_dict['display_cols'] is not None:
498
+ for column in query_dict['display_cols']:
499
+ if column not in skel[table_name]:
500
+ raise ValueError("Table has no column %s" % column)
501
+ if query_dict['expression'][0] not in skel[table_name]:
502
+ raise ValueError("Table has no column %s"
503
+ % query_dict['expression'][0])
504
+
505
+ self.__query_dict__ = query_dict
506
+ self.__param_tuple__ = (str(query_dict['expression'][2]),)
507
+ verify_operator(query_dict['expression'][1])
508
+ if query_dict['display_cols'] is None:
509
+ self.__query_string__ = 'SELECT , FROM %s WHERE ' % table_name \
510
+ + '%s.%s ' % (table_name, query_dict['expression'][0]) \
511
+ + '%s ?' % query_dict['expression'][1]
512
+ else:
513
+ query_dict['display_cols'] = ['%s.%s' % (table_name, x)
514
+ for x in query_dict['display_cols']]
515
+ self.__query_string__ = 'SELECT ' \
516
+ + ', '.join(query_dict['display_cols']) + ' FROM ' \
517
+ + '%s WHERE %s.' % (table_name, table_name) \
518
+ + '%s ' % query_dict['expression'][0] \
519
+ + '%s ?' % query_dict['expression'][1]
520
+ else:
521
+ self.__query_dict__ = {}
522
+ self.__param_tuple__ = tuple()
523
+ self.__query_string__ = ''
524
+
525
+ def __repr__(self):
526
+ """
527
+ Override the print output to display useful info regarding the
528
+ query.
529
+
530
+ EXAMPLES::
531
+
532
+ sage: # needs sage.graphs graph_database
533
+ sage: G = GraphDatabase()
534
+ sage: q = 'SELECT graph_id,graph6,num_vertices,num_edges FROM graph_data WHERE graph_id<=(?) AND num_vertices=(?)'
535
+ sage: param = (22,5)
536
+ sage: SQLQuery(G,q,param)
537
+ Query for sql database: ...graphs.db
538
+ Query string: SELECT graph_id,graph6,num_vertices,num_edges FROM
539
+ graph_data WHERE graph_id<=(?) AND num_vertices=(?)
540
+ Parameter tuple: ('22', '5')
541
+ sage: r = 'SELECT graph6 FROM graph_data WHERE num_vertices<=3'
542
+ sage: SQLQuery(G,r)
543
+ Query for sql database: ...graphs.db
544
+ Query string: SELECT graph6 FROM graph_data WHERE num_vertices<=3
545
+ """
546
+ if not self.__query_string__:
547
+ return 'Empty query on %s.' % self.__database__.__dblocation__
548
+ return "Query for sql database: %s" % self.__database__.__dblocation__ \
549
+ + "\nQuery string: %s" % self.__query_string__ \
550
+ + ("\nParameter tuple: %s" % str(self.__param_tuple__) if
551
+ self.__param_tuple__ else "")
552
+
553
+ def get_query_string(self):
554
+ """
555
+ Return a copy of the query string.
556
+
557
+ EXAMPLES::
558
+
559
+ sage: # needs sage.graphs graph_database
560
+ sage: G = GraphDatabase()
561
+ sage: q = 'SELECT graph_id,graph6,num_vertices,num_edges FROM graph_data WHERE graph_id<=(?) AND num_vertices=(?)'
562
+ sage: param = (22,5)
563
+ sage: SQLQuery(G,q,param).get_query_string()
564
+ 'SELECT graph_id,graph6,num_vertices,num_edges FROM graph_data
565
+ WHERE graph_id<=(?) AND num_vertices=(?)'
566
+ sage: r = 'SELECT graph6 FROM graph_data WHERE num_vertices<=3'
567
+ sage: SQLQuery(G,r).get_query_string()
568
+ 'SELECT graph6 FROM graph_data WHERE num_vertices<=3'
569
+ """
570
+ from copy import copy
571
+ return copy(self.__query_string__)
572
+
573
+ def __iter__(self):
574
+ """
575
+ Return an iterator over the results of the query.
576
+
577
+ EXAMPLES::
578
+
579
+ sage: # needs sage.graphs graph_database
580
+ sage: G = GraphDatabase()
581
+ sage: q = 'SELECT graph_id,graph6 FROM graph_data WHERE num_vertices=(?)'
582
+ sage: param = (5,)
583
+ sage: Q = SQLQuery(G,q,param)
584
+ sage: it = Q.__iter__()
585
+ sage: next(it)
586
+ (18, 'D??')
587
+ sage: next(it)
588
+ (19, 'D?C')
589
+ sage: skip = [next(it) for _ in range(15)]
590
+ sage: next(it)
591
+ (35, 'DBk')
592
+ """
593
+ try:
594
+ cur = self.__database__.__connection__.cursor()
595
+ return cur.execute(self.__query_string__, self.__param_tuple__)
596
+ except sqlite.OperationalError:
597
+ raise RuntimeError('Failure to fetch query.')
598
+
599
+ def query_results(self):
600
+ """
601
+ Run the query by executing the ``__query_string__``. Return the
602
+ results of the query in a list.
603
+
604
+ EXAMPLES::
605
+
606
+ sage: # needs sage.graphs graph_database
607
+ sage: G = GraphDatabase()
608
+ sage: q = 'SELECT graph_id,graph6,num_vertices,num_edges FROM graph_data WHERE graph_id<=(?) AND num_vertices=(?)'
609
+ sage: param = (22,5)
610
+ sage: Q = SQLQuery(G,q,param)
611
+ sage: Q.query_results()
612
+ [(18, 'D??', 5, 0), (19, 'D?C', 5, 1), (20, 'D?K', 5, 2),
613
+ (21, 'D@O', 5, 2), (22, 'D?[', 5, 3)]
614
+ sage: R = SQLQuery(G,{'table_name':'graph_data', 'display_cols':['graph6'], 'expression':['num_vertices','=',4]})
615
+ sage: R.query_results()
616
+ [('C?',), ('C@',), ('CB',), ('CK',), ('CF',), ('CJ',),
617
+ ('CL',), ('CN',), ('C]',), ('C^',), ('C~',)]
618
+ """
619
+ return list(self)
620
+
621
+ def show(self, **kwds):
622
+ """
623
+ Display the result of the query in table format.
624
+
625
+ KEYWORDS:
626
+
627
+ - ``max_field_size`` -- how wide each field can be
628
+ - ``format_cols`` -- dictionary that allows the user to specify the
629
+ format of a column's output by supplying a function. The format of
630
+ the dictionary is::
631
+
632
+ {'column_name':(lambda x: format_function(x))}
633
+
634
+ - ``plot_cols`` -- dictionary that allows the user to specify that a
635
+ plot should be drawn by the object generated by a data slice. Note
636
+ that plot kwds are permitted. The dictionary format is::
637
+
638
+ {'column_name':((lambda x: plot_function(x)), **kwds)}
639
+
640
+ - ``relabel_cols`` -- dictionary to specify a relabeling of column
641
+ headers. The dictionary format is::
642
+
643
+ {'table_name':{'old_col_name':'new_col_name'}}
644
+
645
+ - ``id_col`` -- reference to a column that can be used as an object
646
+ identifier for each row
647
+
648
+ EXAMPLES::
649
+
650
+ sage: DB = SQLDatabase()
651
+ sage: DB.create_table('simon',{'a1':{'sql':'bool', 'primary_key':False}, 'b2':{'sql':'int'}})
652
+ sage: DB.add_data('simon',[(0,0),(1,1),(1,2)])
653
+ sage: r = SQLQuery(DB, {'table_name':'simon', 'display_cols':['a1'], 'expression':['b2','<=', 6]})
654
+ sage: r.show()
655
+ a1
656
+ --------------------
657
+ 0
658
+ 1
659
+ 1
660
+
661
+ sage: # needs sage.graphs graph_database
662
+ sage: D = GraphDatabase()
663
+ sage: from sage.graphs.graph_database import valid_kwds, data_to_degseq
664
+ sage: relabel = {}
665
+ sage: for col in valid_kwds:
666
+ ....: relabel[col] = ' '.join([word.capitalize() for word in col.split('_')])
667
+ sage: q = GraphQuery(display_cols=['graph6','degree_sequence'], num_vertices=4)
668
+ sage: SQLQuery.show(q, format_cols={'degree_sequence':(lambda x,y: data_to_degseq(x,y))}, relabel_cols=relabel, id_col='graph6')
669
+ Graph6 Degree Sequence
670
+ ----------------------------------------
671
+ C? [0, 0, 0, 0]
672
+ C@ [0, 0, 1, 1]
673
+ CB [0, 1, 1, 2]
674
+ CF [1, 1, 1, 3]
675
+ CJ [0, 2, 2, 2]
676
+ CK [1, 1, 1, 1]
677
+ CL [1, 1, 2, 2]
678
+ CN [1, 2, 2, 3]
679
+ C] [2, 2, 2, 2]
680
+ C^ [2, 2, 3, 3]
681
+ C~ [3, 3, 3, 3]
682
+ """
683
+ if not self.__query_string__:
684
+ return self.__database__.show()
685
+
686
+ try:
687
+ cur = self.__database__.__connection__.cursor()
688
+ cur.execute(self.__query_string__, self.__param_tuple__)
689
+ except Exception:
690
+ raise RuntimeError('Failure to fetch query.')
691
+
692
+ print(_create_print_table(cur, [des[0] for des in cur.description],
693
+ **kwds))
694
+
695
+ def __copy__(self):
696
+ """
697
+ Return a copy of itself.
698
+
699
+ EXAMPLES::
700
+
701
+ sage: # needs sage.graphs graph_database
702
+ sage: G = GraphDatabase()
703
+ sage: Q = SQLQuery(G, {'table_name':'graph_data', 'display_cols':['graph_id','graph6','num_vertices'], 'expression':['num_edges','<',3]})
704
+ sage: R = copy(Q)
705
+ sage: R.__query_string__ = ''
706
+ sage: Q.__query_string__ == ''
707
+ False
708
+ """
709
+ d = SQLQuery(self.__database__)
710
+ d.__query_dict__ = self.__query_dict__
711
+ d.__query_string__ = self.__query_string__
712
+ d.__param_tuple__ = self.__param_tuple__
713
+ return d
714
+
715
+ def intersect(self, other, join_table=None, join_dict=None,
716
+ in_place=False):
717
+ """
718
+ Return a new ``SQLQuery`` that is the intersection of ``self`` and
719
+ ``other``. ``join_table`` and ``join_dict`` can be ``None`` iff the
720
+ two queries only search one table in the database. All display columns
721
+ will be concatenated in order: ``self`` display cols + other display cols.
722
+
723
+ INPUT:
724
+
725
+ - ``other`` -- the ``SQLQuery`` to intersect with
726
+ - ``join_table`` -- base table to join on (This table should have at
727
+ least one column in each table to join on).
728
+ - ``join_dict`` -- dictionary that represents the join structure for
729
+ the new query. (Must include a mapping for all tables, including
730
+ those previously joined in either query). Structure is given by::
731
+
732
+ {'join_table1':('corr_base_col1', 'col1'), 'join_table2':('corr_base_col2', 'col2')}
733
+
734
+ where ``join_table1`` is to be joined with ``join_table`` on
735
+ ``join_table.corr_base_col1 = join_table1.col1``
736
+
737
+ EXAMPLES::
738
+
739
+ sage: DB = SQLDatabase()
740
+ sage: DB.create_table('simon',{'a1':{'sql':'bool'}, 'b2':{'sql':'int'}})
741
+ sage: DB.create_table('lucy',{'a1':{'sql':'bool'}, 'b2':{'sql':'int'}})
742
+ sage: DB.add_data('simon', [(0,5),(1,4)])
743
+ sage: DB.add_data('lucy', [(1,1),(1,4)])
744
+ sage: q = SQLQuery(DB, {'table_name':'lucy', 'display_cols':['b2'], 'expression':['a1','=',1]})
745
+ sage: r = SQLQuery(DB, {'table_name':'simon', 'display_cols':['a1'], 'expression':['b2','<=', 6]})
746
+ sage: s = q.intersect(r, 'simon', {'lucy':('a1','a1')})
747
+ sage: s.get_query_string()
748
+ 'SELECT lucy.b2,simon.a1 FROM simon INNER JOIN lucy ON
749
+ simon.a1=lucy.a1 WHERE ( lucy.a1 = ? ) AND ( simon.b2 <= ? )'
750
+ sage: s.query_results()
751
+ [(1, 1), (4, 1)]
752
+ sage: s = q.intersect(r)
753
+ Traceback (most recent call last):
754
+ ...
755
+ ValueError: Input queries query different tables but join
756
+ parameters are NoneType
757
+ sage: s.__query_string__ == q.__query_string__
758
+ False
759
+ sage: q.intersect(r, 'simon', {'lucy':('a1','a1')}, True)
760
+ sage: q.__query_string__ == s.__query_string__
761
+ True
762
+ """
763
+ if self.__query_dict__ is None or other.__query_dict__ is None:
764
+ raise RuntimeError('Queries must be constructed using a '
765
+ + 'dictionary in order to be intersected.')
766
+ if self.__database__ != other.__database__:
767
+ raise TypeError('Queries %s and %s must be ' % (self, other)
768
+ + 'attached to the same database.')
769
+
770
+ if in_place:
771
+ if not self.__query_string__:
772
+ self.__query_string__ = other.__query_string__
773
+ self.__param_tuple__ = other.__param_tuple__
774
+ elif not other.__query_string__:
775
+ return
776
+ else:
777
+ self._merge_queries(other, self, join_table, join_dict, 'AND')
778
+ else:
779
+ from copy import copy
780
+ if not self.__query_string__:
781
+ return copy(other)
782
+ if not other.__query_string__:
783
+ return copy(self)
784
+ return self._merge_queries(other, copy(self), join_table,
785
+ join_dict, 'AND')
786
+
787
+ def _merge_queries(self, other, ret, join_table, join_dict, operator):
788
+ """
789
+ The query ``ret`` is set to a new ``SQLQuery`` that is combines self
790
+ and other through ``operator``. The other arguments are the same as
791
+ ``intersect`` and ``union``.
792
+
793
+ EXAMPLES::
794
+
795
+ sage: DB = SQLDatabase()
796
+ sage: DB.create_table('simon',{'a1':{'sql':'bool'}, 'b2':{'sql':'int'}})
797
+ sage: DB.create_table('lucy',{'a1':{'sql':'bool'}, 'b2':{'sql':'int'}})
798
+ sage: DB.add_data('simon', [(0,5),(1,4)])
799
+ sage: DB.add_data('lucy', [(1,1),(1,4)])
800
+ sage: q = SQLQuery(DB, {'table_name':'lucy', 'display_cols':['b2'], 'expression':['a1','=',1]})
801
+ sage: r = SQLQuery(DB, {'table_name':'simon', 'display_cols':['a1'], 'expression':['b2','<=', 6]})
802
+ sage: s = q._merge_queries(r, copy(q), 'simon', {'lucy':('a1','a1')}, 'OR')
803
+ sage: s.get_query_string()
804
+ 'SELECT lucy.b2,simon.a1 FROM simon INNER JOIN lucy ON
805
+ simon.a1=lucy.a1 WHERE ( lucy.a1 = ? ) OR ( simon.b2 <= ? )'
806
+ sage: s.query_results()
807
+ [(1, 1), (4, 1)]
808
+ """
809
+ if join_table is None or join_dict is None:
810
+ pattern = ' JOIN '
811
+ if re.search(pattern, self.__query_string__) \
812
+ or re.search(pattern, other.__query_string__):
813
+ raise TypeError('Input queries have joins but join '
814
+ + 'parameters are NoneType')
815
+ s = ((self.__query_string__).upper()).split('FROM ')
816
+ o = ((other.__query_string__).upper()).split('FROM ')
817
+ s = s[1].split(' WHERE ')
818
+ o = o[1].split(' WHERE ')
819
+ if s[0] != o[0]:
820
+ raise ValueError('Input queries query different tables but '
821
+ + 'join parameters are NoneType')
822
+
823
+ # inner join clause
824
+ if join_dict is not None:
825
+ joins = join_table
826
+ for table in join_dict:
827
+ joins += ' INNER JOIN %s ON %s.' % (table, join_table) \
828
+ + '%s=%s.' % (join_dict[table][0], table) \
829
+ + '%s ' % join_dict[table][1]
830
+ ret.__query_string__ = re.sub(' FROM .* WHERE ', ' FROM ' + joins
831
+ + 'WHERE ', self.__query_string__)
832
+
833
+ # concatenate display cols
834
+ disp1 = ret.__query_string__.split(' FROM')
835
+ disp2 = other.__query_string__.split(' FROM')
836
+ disp1.insert(1, ',%s FROM' % disp2[0].split('SELECT ')[1])
837
+ new_query = ''.join(disp1)
838
+
839
+ # concatenate where clause
840
+ new_query = re.sub(' WHERE ', ' WHERE ( ',
841
+ new_query)
842
+ new_query += re.sub('^.* WHERE ', f' ) {operator} ( ',
843
+ other.__query_string__)
844
+ ret.__query_string__ = new_query + ' )'
845
+
846
+ ret.__param_tuple__ = self.__param_tuple__ + other.__param_tuple__
847
+
848
+ return ret
849
+
850
+ def union(self, other, join_table=None, join_dict=None, in_place=False):
851
+ """
852
+ Return a new ``SQLQuery`` that is the union of ``self`` and ``other``.
853
+ ``join_table`` and ``join_dict`` can be ``None`` iff the two queries
854
+ only search one table in the database. All display columns will be
855
+ concatenated in order: ``self`` display cols + other display cols.
856
+
857
+ INPUT:
858
+
859
+ - ``other`` -- the ``SQLQuery`` to union with
860
+ - ``join_table`` -- base table to join on (This table should have at
861
+ least one column in each table to join on).
862
+ - ``join_dict`` -- dictionary that represents the join structure for
863
+ the new query. (Must include a mapping for all tables, including
864
+ those previously joined in either query). Structure is given by::
865
+
866
+ {'join_table1':('corr_base_col1', 'col1'), 'join_table2':('corr_base_col2', 'col2')}
867
+
868
+ where ``join_table1`` is to be joined with ``join_table`` on
869
+ ``join_table.corr_base_col1=join_table1.col1``
870
+
871
+ EXAMPLES::
872
+
873
+ sage: DB = SQLDatabase()
874
+ sage: DB.create_table('simon',{'a1':{'sql':'bool'}, 'b2':{'sql':'int'}})
875
+ sage: DB.create_table('lucy',{'a1':{'sql':'bool'}, 'b2':{'sql':'int'}})
876
+ sage: DB.add_data('simon', [(0,5),(1,4)])
877
+ sage: DB.add_data('lucy', [(1,1),(1,4)])
878
+ sage: q = SQLQuery(DB, {'table_name':'lucy', 'display_cols':['b2'], 'expression':['a1','=',1]})
879
+ sage: r = SQLQuery(DB, {'table_name':'simon', 'display_cols':['a1'], 'expression':['b2','<=', 6]})
880
+ sage: s = q.union(r, 'simon', {'lucy':('a1','a1')})
881
+ sage: s.get_query_string()
882
+ 'SELECT lucy.b2,simon.a1 FROM simon INNER JOIN lucy ON
883
+ simon.a1=lucy.a1 WHERE ( lucy.a1 = ? ) OR ( simon.b2 <= ? )'
884
+ sage: s.query_results()
885
+ [(1, 1), (4, 1)]
886
+ """
887
+ if self.__query_dict__ is None or other.__query_dict__ is None:
888
+ raise RuntimeError('Queries must be constructed using a '
889
+ + 'dictionary in order to be unioned.')
890
+ if self.__database__ != other.__database__:
891
+ raise TypeError('Queries %s and %s must be ' % (self, other)
892
+ + 'attached to the same database.')
893
+
894
+ if in_place:
895
+ if self.__query_string__ and other.__query_string__:
896
+ self._merge_queries(other, self, join_table, join_dict, 'OR')
897
+ else:
898
+ from copy import copy
899
+ if not self.__query_string__:
900
+ return copy(self)
901
+ if not other.__query_string__:
902
+ return copy(other)
903
+ return self._merge_queries(other, copy(self), join_table,
904
+ join_dict, 'OR')
905
+
906
+
907
+ class SQLDatabase(SageObject):
908
+ def __init__(self, filename=None, read_only=None, skeleton=None):
909
+ r"""
910
+ A SQL Database object corresponding to a database file.
911
+
912
+ INPUT:
913
+
914
+ - ``filename`` -- string
915
+ - ``skeleton`` -- a triple-indexed dictionary::
916
+
917
+ | - outer key -- table name
918
+ | - inner key -- column name
919
+ | - inner inner key -- one of the following:
920
+ | - ``primary_key`` -- boolean, whether column has been set
921
+ as primary key
922
+ | - ``index`` -- boolean, whether column has been set as
923
+ index
924
+ | - ``unique`` -- boolean, whether column has been set as
925
+ unique
926
+ | - ``sql`` -- one of ``'TEXT'``, ``'BOOLEAN'``,
927
+ ``'INTEGER'``, ``'REAL'``, or other user defined type
928
+
929
+ TUTORIAL:
930
+
931
+ The ``SQLDatabase`` class is for interactively building databases
932
+ intended for queries. This may sound redundant, but it is important. If
933
+ you want a database intended for quick lookup of entries in very large
934
+ tables, much like a hash table (such as a Python dictionary), a
935
+ ``SQLDatabase`` may not be what you are looking for. The strength of
936
+ ``SQLDatabases`` is in queries, searches through the database with
937
+ complicated criteria.
938
+
939
+ For example, we create a new database for storing isomorphism classes
940
+ of simple graphs::
941
+
942
+ sage: D = SQLDatabase()
943
+
944
+ In order to generate representatives for the classes, we will import a
945
+ function which generates all labeled graphs (noting that this is not
946
+ the optimal way)::
947
+
948
+ sage: # needs sage.graphs
949
+ sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import all_labeled_graphs
950
+
951
+ We will need a table in the database in which to store the graphs, and
952
+ we specify its structure with a Python dictionary, each of whose keys
953
+ is the name of a column::
954
+
955
+ sage: from collections import OrderedDict
956
+ sage: table_skeleton = OrderedDict([
957
+ ....: ('graph6',{'sql':'TEXT', 'index':True, 'primary_key':True}),
958
+ ....: ('vertices', {'sql':'INTEGER'}),
959
+ ....: ('edges', {'sql':'INTEGER'})
960
+ ....: ])
961
+
962
+ Then we create the table::
963
+
964
+ sage: D.create_table('simon', table_skeleton)
965
+ sage: D.show('simon')
966
+ graph6 vertices edges
967
+ ------------------------------------------------------------
968
+
969
+ Now that we have the table, we will begin to populate the table with
970
+ rows. First, add the graph on zero vertices.::
971
+
972
+ sage: # needs sage.graphs
973
+ sage: G = Graph()
974
+ sage: D.add_row('simon',(G.graph6_string(), 0, 0))
975
+ sage: D.show('simon')
976
+ graph6 vertices edges
977
+ ------------------------------------------------------------
978
+ ? 0 0
979
+
980
+ Next, add the graph on one vertex.::
981
+
982
+ sage: # needs sage.graphs
983
+ sage: G.add_vertex()
984
+ 0
985
+ sage: D.add_row('simon',(G.graph6_string(), 1, 0))
986
+ sage: D.show('simon')
987
+ graph6 vertices edges
988
+ ------------------------------------------------------------
989
+ ? 0 0
990
+ @ 1 0
991
+
992
+ Say we want a database of graphs on four or less vertices::
993
+
994
+ sage: # needs sage.graphs
995
+ sage: labels = {}
996
+ sage: for i in range(2, 5):
997
+ ....: labels[i] = []
998
+ ....: for g in all_labeled_graphs(i):
999
+ ....: g = g.canonical_label(algorithm='sage')
1000
+ ....: if g not in labels[i]:
1001
+ ....: labels[i].append(g)
1002
+ ....: D.add_row('simon', (g.graph6_string(), g.order(), g.size()))
1003
+ sage: D.show('simon')
1004
+ graph6 vertices edges
1005
+ ------------------------------------------------------------
1006
+ ? 0 0
1007
+ @ 1 0
1008
+ A? 2 0
1009
+ A_ 2 1
1010
+ B? 3 0
1011
+ BG 3 1
1012
+ BW 3 2
1013
+ Bw 3 3
1014
+ C? 4 0
1015
+ C@ 4 1
1016
+ CB 4 2
1017
+ CF 4 3
1018
+ CJ 4 3
1019
+ CK 4 2
1020
+ CL 4 3
1021
+ CN 4 4
1022
+ C] 4 4
1023
+ C^ 4 5
1024
+ C~ 4 6
1025
+
1026
+ We can then query the database -- let's ask for all the graphs on four
1027
+ vertices with three edges. We do so by creating two queries and asking
1028
+ for rows that satisfy them both::
1029
+
1030
+ sage: # needs sage.graphs
1031
+ sage: Q = SQLQuery(D, {'table_name':'simon', 'display_cols':['graph6'], 'expression':['vertices','=',4]})
1032
+ sage: Q2 = SQLQuery(D, {'table_name':'simon', 'display_cols':['graph6'], 'expression':['edges','=',3]})
1033
+ sage: Q = Q.intersect(Q2)
1034
+ sage: len(Q.query_results())
1035
+ 3
1036
+ sage: Q.query_results() # random
1037
+ [('CF', 'CF'), ('CJ', 'CJ'), ('CL', 'CL')]
1038
+
1039
+ NOTE: The values of ``display_cols`` are always concatenated in
1040
+ intersections and unions.
1041
+
1042
+ Of course, we can save the database to file. Here we use a
1043
+ temporary directory that we clean up afterwards::
1044
+
1045
+ sage: import tempfile
1046
+ sage: d = tempfile.TemporaryDirectory()
1047
+ sage: dbpath = os.path.join(d.name, 'simon.db')
1048
+ sage: D.save(dbpath)
1049
+
1050
+ Now the database's hard link is to this file, and not the temporary db
1051
+ file. For example, let's say we open the same file with another class
1052
+ instance. We can load the file as an immutable database::
1053
+
1054
+ sage: # needs sage.graphs
1055
+ sage: E = SQLDatabase(dbpath)
1056
+ sage: E.show('simon')
1057
+ graph6 vertices edges
1058
+ ------------------------------------------------------------
1059
+ ? 0 0
1060
+ @ 1 0
1061
+ A? 2 0
1062
+ A_ 2 1
1063
+ B? 3 0
1064
+ BG 3 1
1065
+ BW 3 2
1066
+ Bw 3 3
1067
+ C? 4 0
1068
+ C@ 4 1
1069
+ CB 4 2
1070
+ CF 4 3
1071
+ CJ 4 3
1072
+ CK 4 2
1073
+ CL 4 3
1074
+ CN 4 4
1075
+ C] 4 4
1076
+ C^ 4 5
1077
+ C~ 4 6
1078
+ sage: E.drop_table('simon')
1079
+ Traceback (most recent call last):
1080
+ ...
1081
+ RuntimeError: Cannot drop tables from a read only database.
1082
+
1083
+ Call ``cleanup()`` on the temporary directory to, well, clean it up::
1084
+
1085
+ sage: d.cleanup()
1086
+ """
1087
+ if filename is None:
1088
+ if read_only is None:
1089
+ read_only = False
1090
+ from sage.misc.temporary_file import tmp_filename
1091
+ filename = tmp_filename() + '.db'
1092
+ elif (filename[-3:] != '.db'):
1093
+ raise ValueError('Please enter a valid database path (file name '
1094
+ + '%s does not end in .db).' % filename)
1095
+ if read_only is None:
1096
+ read_only = True
1097
+
1098
+ self.__read_only__ = read_only
1099
+ self.ignore_warnings = False
1100
+ self.__dblocation__ = filename
1101
+ self.__connection__ = sqlite.connect(self.__dblocation__,
1102
+ check_same_thread=False)
1103
+ # this is to avoid the multiple thread problem with dsage:
1104
+ # pysqlite does not trust multiple threads for the same connection
1105
+ self.__connection__.create_function("regexp", 2, regexp)
1106
+
1107
+ # construct skeleton (from provided database)
1108
+ self.__skeleton__ = construct_skeleton(self)
1109
+
1110
+ # add bones from new skeleton to database,
1111
+ # without changing existing structure
1112
+ if skeleton is not None and not read_only:
1113
+ for table in skeleton:
1114
+ if table not in self.__skeleton__:
1115
+ self.create_table(table, skeleton[table])
1116
+ else:
1117
+ for column in skeleton[table]:
1118
+ if column not in self.__skeleton__[table]:
1119
+ self.add_column(table, column,
1120
+ skeleton[table][column])
1121
+ else:
1122
+ print('Column attributes were ignored for '
1123
+ 'table {}, column {} -- column is '
1124
+ 'already in table.'.format(table, column))
1125
+ elif skeleton is not None:
1126
+ raise RuntimeError('Cannot update skeleton of a read only '
1127
+ + 'database.')
1128
+
1129
+ def __repr__(self):
1130
+ """
1131
+ Override the print output to display useful info regarding the
1132
+ database.
1133
+
1134
+ EXAMPLES::
1135
+
1136
+ sage: import tempfile
1137
+ sage: with tempfile.TemporaryDirectory() as d:
1138
+ ....: dbpath = os.path.join(d, "test.db")
1139
+ ....: SD = SQLDatabase(dbpath, False)
1140
+ ....: SD.create_table('simon', {'n':{'sql':'INTEGER', 'index':True}})
1141
+ ....: print(SD)
1142
+ table simon:
1143
+ column n: index: True; primary_key: False; sql: INTEGER;
1144
+ unique: False;
1145
+ """
1146
+ s = ''
1147
+ for table in self.__skeleton__:
1148
+ s += 'table ' + table + ':\n'
1149
+ for column in self.__skeleton__[table]:
1150
+ s += ' column ' + column + ': '
1151
+ for data in sorted(self.__skeleton__[table][column]):
1152
+ s += data + ': ' \
1153
+ + str(self.__skeleton__[table][column][data]) + '; '
1154
+ s += '\n'
1155
+ return s
1156
+
1157
+ def __copy__(self):
1158
+ """
1159
+ Return an instance of ``SQLDatabase`` that points to a copy database,
1160
+ and allows modification.
1161
+
1162
+ EXAMPLES::
1163
+
1164
+ sage: from collections import OrderedDict
1165
+ sage: DB = SQLDatabase()
1166
+ sage: DB.create_table('lucy', OrderedDict([
1167
+ ....: ('id', {'sql':'INTEGER', 'primary_key':True, 'index':True}),
1168
+ ....: ('a1', {'sql':'bool'}),
1169
+ ....: ('b2', {'sql':'int', 'primary_key':False})
1170
+ ....: ]))
1171
+ sage: DB.add_rows('lucy', [(0,1,1),(1,1,4),(2,0,7),(3,1,384), (4,1,978932)],['id','a1','b2'])
1172
+ sage: d = copy(DB)
1173
+ sage: d == DB
1174
+ False
1175
+ sage: d.show('lucy')
1176
+ id a1 b2
1177
+ ------------------------------------------------------------
1178
+ 0 1 1
1179
+ 1 1 4
1180
+ 2 0 7
1181
+ 3 1 384
1182
+ 4 1 978932
1183
+ sage: DB.show('lucy')
1184
+ id a1 b2
1185
+ ------------------------------------------------------------
1186
+ 0 1 1
1187
+ 1 1 4
1188
+ 2 0 7
1189
+ 3 1 384
1190
+ 4 1 978932
1191
+ """
1192
+ # copy .db file
1193
+ from sage.misc.temporary_file import tmp_filename
1194
+ new_loc = tmp_filename() + '.db'
1195
+ if not self.__read_only__:
1196
+ self.commit()
1197
+ os.system('cp ' + self.__dblocation__ + ' ' + new_loc)
1198
+ D = SQLDatabase(filename=new_loc, read_only=False)
1199
+ return D
1200
+
1201
+ def save(self, filename):
1202
+ """
1203
+ Save the database to the specified location.
1204
+
1205
+ EXAMPLES::
1206
+
1207
+ sage: MonicPolys = SQLDatabase()
1208
+ sage: MonicPolys.create_table('simon', {'n':{'sql':'INTEGER', 'index':True}})
1209
+ sage: for n in range(20): MonicPolys.add_row('simon', (n,))
1210
+ sage: import tempfile
1211
+ sage: with tempfile.TemporaryDirectory() as d:
1212
+ ....: dbpath = os.path.join(d, "sage.db")
1213
+ ....: MonicPolys.save(dbpath)
1214
+ ....: N = SQLDatabase(dbpath)
1215
+ ....: N.show('simon')
1216
+ n
1217
+ --------------------
1218
+ 0
1219
+ 1
1220
+ 2
1221
+ 3
1222
+ 4
1223
+ 5
1224
+ 6
1225
+ 7
1226
+ 8
1227
+ 9
1228
+ 10
1229
+ 11
1230
+ 12
1231
+ 13
1232
+ 14
1233
+ 15
1234
+ 16
1235
+ 17
1236
+ 18
1237
+ 19
1238
+ """
1239
+ if not self.__read_only__:
1240
+ self.commit()
1241
+ os.system('cp ' + self.__dblocation__ + ' ' + filename)
1242
+
1243
+ def get_skeleton(self, check=False):
1244
+ """
1245
+ Return a dictionary representing the hierarchical structure of the
1246
+ database, in the following format::
1247
+
1248
+ | - skeleton -- a triple-indexed dictionary
1249
+ | - outer key -- table name
1250
+ | - inner key -- column name
1251
+ | - inner inner key -- one of the following:
1252
+ | - ``primary_key`` -- boolean, whether column has been set as
1253
+ primary key
1254
+ | - ``index`` -- boolean, whether column has been set as index
1255
+ | - ``unique`` -- boolean, whether column has been set as unique
1256
+ | - ``sql`` -- one of ``'TEXT'``, ``'BOOLEAN'``, ``'INTEGER'``,
1257
+ ``'REAL'``, or other user defined type
1258
+
1259
+ For example::
1260
+
1261
+ {'table1':{'col1':{'primary_key':False, 'index':True, 'unique': False,'sql':'REAL'}}}
1262
+
1263
+ INPUT:
1264
+
1265
+ - ``check`` -- if ``True``, checks to make sure the database's actual
1266
+ structure matches the skeleton on record
1267
+
1268
+ EXAMPLES::
1269
+
1270
+ sage: # needs sage.graphs graph_database
1271
+ sage: GDB = GraphDatabase()
1272
+ sage: GDB.get_skeleton() # slightly random output
1273
+ {'aut_grp': {'aut_grp_size': {'index': True,
1274
+ 'unique': False,
1275
+ 'primary_key': False,
1276
+ 'sql': 'INTEGER'},
1277
+ ...
1278
+ 'num_vertices': {'index': True,
1279
+ 'unique': False,
1280
+ 'primary_key': False,
1281
+ 'sql': 'INTEGER'}}}
1282
+ """
1283
+ if check:
1284
+ d = construct_skeleton(self)
1285
+ if d == self.__skeleton__:
1286
+ return d
1287
+ raise RuntimeError("skeleton structure is out of whack")
1288
+ return self.__skeleton__
1289
+
1290
+ def query(self, *args, **kwds):
1291
+ """
1292
+ Create a ``SQLQuery`` on this database.
1293
+
1294
+ For full class details,
1295
+ type ``SQLQuery?`` and press :kbd:`Shift` + :kbd:`Enter`.
1296
+
1297
+ EXAMPLES::
1298
+
1299
+ sage: D = SQLDatabase()
1300
+ sage: D.create_table('simon', {'wolf':{'sql':'BOOLEAN'}, 'tag':{'sql':'INTEGER'}})
1301
+ sage: q = D.query({'table_name':'simon', 'display_cols':['tag'], 'expression':['wolf','=',1]})
1302
+ sage: q.get_query_string()
1303
+ 'SELECT simon.tag FROM simon WHERE simon.wolf = ?'
1304
+ sage: q.__param_tuple__
1305
+ ('1',)
1306
+ sage: q = D.query(query_string='SELECT tag FROM simon WHERE wolf=?',param_tuple=(1,))
1307
+ sage: q.get_query_string()
1308
+ 'SELECT tag FROM simon WHERE wolf=?'
1309
+ sage: q.__param_tuple__
1310
+ ('1',)
1311
+ """
1312
+ return SQLQuery(self, *args, **kwds)
1313
+
1314
+ __call__ = query
1315
+
1316
+ def show(self, table_name, **kwds):
1317
+ """
1318
+ Show an entire table from the database.
1319
+
1320
+ EXAMPLES::
1321
+
1322
+ sage: DB = SQLDatabase()
1323
+ sage: DB.create_table('simon',{'a1':{'sql':'bool'}, 'b2':{'sql':'int'}})
1324
+ sage: DB.add_data('simon',[(0,0),(1,1),(1,2)])
1325
+ sage: DB.show('simon')
1326
+ a1 b2
1327
+ ----------------------------------------
1328
+ 0 0
1329
+ 1 1
1330
+ 1 2
1331
+ """
1332
+ try:
1333
+ cur = self.__connection__.cursor()
1334
+ cur.execute('SELECT * FROM ' + table_name)
1335
+ except Exception:
1336
+ raise RuntimeError('Failure to fetch data.')
1337
+ print(_create_print_table(cur, [des[0] for des in cur.description],
1338
+ **kwds))
1339
+
1340
+ def get_cursor(self, ignore_warning=None):
1341
+ """
1342
+ Return a pysqlite cursor for the database connection.
1343
+
1344
+ A cursor is an input from which you can execute sqlite commands on the
1345
+ database.
1346
+
1347
+ Recommended for more advanced users only.
1348
+
1349
+ EXAMPLES::
1350
+
1351
+ sage: DB = SQLDatabase()
1352
+ sage: DB.create_table('simon',{'a1':{'sql':'bool'}, 'b2':{'sql':'int'}})
1353
+ sage: DB.add_row('simon',(0,1))
1354
+ sage: DB.add_rows('simon',[(0,0),(1,1),(1,2)])
1355
+ sage: DB.add_rows('simon',[(0,0),(4,0),(5,1)], ['b2','a1'])
1356
+ sage: cur = DB.get_cursor()
1357
+ sage: (cur.execute('select * from simon')).fetchall()
1358
+ [(0, 1), (0, 0), (1, 1), (1, 2), (0, 0), (0, 4), (1, 5)]
1359
+ """
1360
+ if self.__read_only__:
1361
+ if ignore_warning is None:
1362
+ ignore_warning = self.ignore_warnings
1363
+ if not ignore_warning:
1364
+ import warnings
1365
+ warnings.warn('Database is read only, using the cursor can '
1366
+ + 'alter the stored data. Set self.ignore_warnings to '
1367
+ + 'True in order to mute future warnings.', RuntimeWarning)
1368
+ return self.__connection__.cursor()
1369
+
1370
+ def get_connection(self, ignore_warning=None):
1371
+ """
1372
+ Return a pysqlite connection to the database.
1373
+
1374
+ You most likely want ``get_cursor()`` instead, which is used for
1375
+ executing sqlite commands on the database.
1376
+
1377
+ Recommended for more advanced users only.
1378
+
1379
+ EXAMPLES::
1380
+
1381
+ sage: D = SQLDatabase(read_only=True)
1382
+ sage: con = D.get_connection()
1383
+ doctest:...: RuntimeWarning: Database is read only, using the connection can alter the stored data. Set self.ignore_warnings to True in order to mute future warnings.
1384
+ sage: con = D.get_connection(True)
1385
+ sage: D.ignore_warnings = True
1386
+ sage: con = D.get_connection()
1387
+ sage: t = con.execute('CREATE TABLE simon(n INTEGER, n2 INTEGER)')
1388
+ sage: for n in range(10):
1389
+ ....: t = con.execute('INSERT INTO simon VALUES(%d,%d)'%(n,n^2))
1390
+ sage: D.show('simon')
1391
+ n n2
1392
+ ----------------------------------------
1393
+ 0 0
1394
+ 1 1
1395
+ 2 4
1396
+ 3 9
1397
+ 4 16
1398
+ 5 25
1399
+ 6 36
1400
+ 7 49
1401
+ 8 64
1402
+ 9 81
1403
+ """
1404
+ if self.__read_only__:
1405
+ if ignore_warning is None:
1406
+ ignore_warning = self.ignore_warnings
1407
+ if not ignore_warning:
1408
+ import warnings
1409
+ warnings.warn('Database is read only, using the connection '
1410
+ 'can alter the stored data. Set self.ignore_warnings '
1411
+ 'to True in order to mute future warnings.',
1412
+ RuntimeWarning)
1413
+ return self.__connection__
1414
+
1415
+ def create_table(self, table_name, table_skeleton):
1416
+ """
1417
+ Create a new table in the database.
1418
+
1419
+ To create a table, a column structure must be specified. The form for
1420
+ this is a Python dict, for example::
1421
+
1422
+ {'col1': {'sql':'INTEGER', 'index':False, 'unique':True, 'primary_key':False}, ...}
1423
+
1424
+ INPUT:
1425
+
1426
+ - ``table_name`` -- string
1427
+ - ``table_skeleton`` -- a double-indexed dictionary
1428
+
1429
+ - outer key -- column name
1430
+
1431
+ - inner key -- one of the following:
1432
+
1433
+ - ``primary_key`` -- boolean, whether column has been set
1434
+ asprimary key
1435
+ - ``index`` -- boolean, whether column has been set as
1436
+ index
1437
+ - ``unique`` -- boolean, whether column has been set as
1438
+ unique
1439
+ - ``sql`` -- one of ``'TEXT'``, ``'BOOLEAN'``,
1440
+ ``'INTEGER'``, ``'REAL'``, or other user defined type
1441
+
1442
+ NOTE:
1443
+
1444
+ Some SQL features, such as automatically incrementing primary key,
1445
+ require the full word ``'INTEGER'``, not just ``'INT'``.
1446
+
1447
+ EXAMPLES::
1448
+
1449
+ sage: from collections import OrderedDict
1450
+ sage: D = SQLDatabase()
1451
+ sage: table_skeleton = OrderedDict([
1452
+ ....: ('graph6', {'sql':'TEXT', 'index':True, 'primary_key':True}),
1453
+ ....: ('vertices', {'sql':'INTEGER'}),
1454
+ ....: ('edges', {'sql':'INTEGER'})
1455
+ ....: ])
1456
+ sage: D.create_table('simon', table_skeleton)
1457
+ sage: D.show('simon')
1458
+ graph6 vertices edges
1459
+ ------------------------------------------------------------
1460
+ """
1461
+ if self.__read_only__:
1462
+ raise RuntimeError('Cannot add table to a read only database.')
1463
+ if table_name in self.__skeleton__:
1464
+ raise ValueError('Database already has a table named %s'
1465
+ % table_name)
1466
+ if table_name.find(' ') != -1:
1467
+ raise ValueError('Table names cannot contain spaces.')
1468
+ if table_name.upper() in sqlite_keywords:
1469
+ raise ValueError('Table names cannot be a SQLite keyword.')
1470
+ create_statement = 'CREATE TABLE ' + table_name + '('
1471
+ statement = []
1472
+ index_statement = ''
1473
+ for col in table_skeleton:
1474
+ if col.find('sqlite') != -1:
1475
+ raise ValueError("Column names cannot contain 'sqlite'.")
1476
+ if col.upper() in sqlite_keywords:
1477
+ raise ValueError('Column names cannot be a SQLite keyword.')
1478
+ table_skeleton[col] = verify_column(table_skeleton[col])
1479
+ typ = table_skeleton[col]['sql']
1480
+ if verify_type(typ):
1481
+ if typ.upper() == 'NOTYPE':
1482
+ typ = ''
1483
+ if table_skeleton[col]['primary_key']:
1484
+ statement.append(col + ' ' + typ + ' PRIMARY KEY')
1485
+ elif table_skeleton[col]['unique']:
1486
+ statement.append(col + ' ' + typ + ' UNIQUE')
1487
+ else:
1488
+ statement.append(col + ' ' + typ)
1489
+ if table_skeleton[col]['index']:
1490
+ index_statement += 'CREATE INDEX i_%s_%s' % (table_name,
1491
+ col) + ' ON %s(%s);\n' % (table_name, col)
1492
+ create_statement += ', '.join(statement) + ') '
1493
+
1494
+ self.__connection__.execute(create_statement)
1495
+ if index_statement:
1496
+ self.__connection__.executescript(index_statement)
1497
+
1498
+ self.__skeleton__[table_name] = table_skeleton
1499
+
1500
+ def add_column(self, table_name, col_name, col_dict, default='NULL'):
1501
+ """
1502
+ Add a column named ``col_name`` to table ``table_name``, whose data
1503
+ types are described by ``col_dict``. The format for this is::
1504
+
1505
+ {'col1':{'primary_key':False, 'unique': True, 'index':True, 'sql':'REAL'}}
1506
+
1507
+ INPUT:
1508
+
1509
+ - ``col_dict`` -- dictionary:
1510
+
1511
+ - key -- column name
1512
+
1513
+ - inner key -- one of the following:
1514
+
1515
+ - ``primary_key`` -- boolean, whether column has been set
1516
+ as primary key
1517
+ - ``index`` -- boolean, whether column has been set as
1518
+ index
1519
+ - ``unique`` -- boolean, whether column has been set as
1520
+ unique
1521
+ - ``sql`` -- one of ``'TEXT'``, ``'BOOLEAN'``,
1522
+ ``'INTEGER'``, ``'REAL'``, or other user defined type
1523
+
1524
+ EXAMPLES::
1525
+
1526
+ sage: from collections import OrderedDict
1527
+ sage: MonicPolys = SQLDatabase()
1528
+ sage: MonicPolys.create_table('simon', OrderedDict([('n', {'sql':'INTEGER', 'index':True})]))
1529
+ sage: for n in range(20): MonicPolys.add_row('simon', (n,))
1530
+ sage: MonicPolys.add_column('simon', 'n_squared', {'sql':'INTEGER', 'index':False}, 0)
1531
+ sage: MonicPolys.show('simon')
1532
+ n n_squared
1533
+ ----------------------------------------
1534
+ 0 0
1535
+ 1 0
1536
+ 2 0
1537
+ 3 0
1538
+ 4 0
1539
+ 5 0
1540
+ 6 0
1541
+ 7 0
1542
+ 8 0
1543
+ 9 0
1544
+ 10 0
1545
+ 11 0
1546
+ 12 0
1547
+ 13 0
1548
+ 14 0
1549
+ 15 0
1550
+ 16 0
1551
+ 17 0
1552
+ 18 0
1553
+ 19 0
1554
+ """
1555
+ if self.__read_only__:
1556
+ raise RuntimeError('Cannot add columns to a read only database.')
1557
+ # Check input:
1558
+ if col_name.find('sqlite') != -1:
1559
+ raise ValueError("Column names cannot contain 'sqlite'.")
1560
+ if col_name.upper() in sqlite_keywords:
1561
+ raise ValueError("Column names cannot be SQLite keywords.")
1562
+ if table_name not in self.__skeleton__:
1563
+ raise ValueError("Database has no table %s." % table_name)
1564
+ if col_name in self.__skeleton__[table_name]:
1565
+ raise ValueError("Table %s already has column %s." % (table_name,col_name))
1566
+
1567
+ # Update the skeleton:
1568
+ self.__skeleton__[table_name][col_name] = verify_column(col_dict)
1569
+
1570
+ try:
1571
+ self._rebuild_table(table_name, col_name, default)
1572
+ except sqlite.Error as e:
1573
+ # delete added column from skeleton
1574
+ self.__skeleton__[table_name].pop(col_name)
1575
+
1576
+ print('A sqlite error occurred: ', e.args[0])
1577
+
1578
+ def _rebuild_table(self, table_name, col_name=None, default=''):
1579
+ """
1580
+ Rebuild the table ``table_name`` adding column ``col_name`` if not
1581
+ ``None``. If a new column is added, each rows' value is set to
1582
+ ``default``.
1583
+
1584
+ Used in the methods ``add_column``, ``drop_column``, ``make_unique``,
1585
+ ``drop_unique``, ``make_primary_key``, and ``drop_primary_key`` to get
1586
+ around the limitations of SQLite's ``ALTER TABLE``. We have set up the
1587
+ creation of a temporary database in this method. Please feel free to
1588
+ improve on this by sending a patch or suggestion.
1589
+
1590
+ EXAMPLES::
1591
+
1592
+ sage: MonicPolys = SQLDatabase()
1593
+ sage: MonicPolys.create_table('simon', {'n':{'sql':'INTEGER', 'index':True}})
1594
+ sage: for n in range(20): MonicPolys.add_row('simon', (n,))
1595
+ sage: MonicPolys.show('simon')
1596
+ n
1597
+ --------------------
1598
+ 0
1599
+ 1
1600
+ 2
1601
+ 3
1602
+ 4
1603
+ 5
1604
+ 6
1605
+ 7
1606
+ 8
1607
+ 9
1608
+ 10
1609
+ 11
1610
+ 12
1611
+ 13
1612
+ 14
1613
+ 15
1614
+ 16
1615
+ 17
1616
+ 18
1617
+ 19
1618
+ sage: MonicPolys._rebuild_table('simon')
1619
+ sage: MonicPolys.show('simon')
1620
+ n
1621
+ --------------------
1622
+ 0
1623
+ 1
1624
+ 2
1625
+ 3
1626
+ 4
1627
+ 5
1628
+ 6
1629
+ 7
1630
+ 8
1631
+ 9
1632
+ 10
1633
+ 11
1634
+ 12
1635
+ 13
1636
+ 14
1637
+ 15
1638
+ 16
1639
+ 17
1640
+ 18
1641
+ 19
1642
+ """
1643
+ cols = []
1644
+ cols_attr = []
1645
+ table_skeleton = self.__skeleton__[table_name]
1646
+ # gather column names and attributes for new table
1647
+ for col in table_skeleton:
1648
+ cols.append(col)
1649
+ attr_str = col + ' ' + table_skeleton[col]['sql']
1650
+ if table_skeleton[col]['primary_key']:
1651
+ attr_str += ' PRIMARY KEY'
1652
+ elif table_skeleton[col]['unique']:
1653
+ attr_str += ' UNIQUE'
1654
+ cols_attr.append(attr_str)
1655
+
1656
+ original = list(cols)
1657
+
1658
+ if col_name is not None:
1659
+ original[original.index(col_name)] = str(default)
1660
+
1661
+ original = ', '.join(original)
1662
+ cols = ', '.join(cols)
1663
+ cols_attr = ', '.join(cols_attr)
1664
+
1665
+ # Silly SQLite -- we have to make a temp table to hold info...
1666
+ self.__connection__.executescript("""
1667
+ CREATE TEMPORARY TABLE spam(%s);
1668
+ INSERT INTO spam SELECT %s FROM %s;
1669
+ DROP TABLE %s;
1670
+ CREATE TABLE %s (%s);
1671
+ """ % (cols_attr, original, table_name, table_name, table_name, cols_attr))
1672
+ # Update indices in new table
1673
+ skeleton = self.__skeleton__[table_name]
1674
+ index_statement = ''.join(f'CREATE INDEX i_{table_name}_{col} ON '
1675
+ + f'{table_name}({col});\n'
1676
+ for col in skeleton
1677
+ if skeleton[col]['index']
1678
+ and not skeleton[col]['primary_key'])
1679
+ if index_statement:
1680
+ self.__connection__.executescript(index_statement)
1681
+
1682
+ # Now we can plop our data into the *new* table:
1683
+ self.__connection__.executescript("""
1684
+ INSERT INTO %s SELECT %s FROM spam;
1685
+ DROP TABLE spam;
1686
+ """ % (table_name, cols))
1687
+
1688
+ self.vacuum()
1689
+
1690
+ def drop_column(self, table_name, col_name):
1691
+ """
1692
+ Drop the column ``col_name`` from table ``table_name``.
1693
+
1694
+ EXAMPLES::
1695
+
1696
+ sage: MonicPolys = SQLDatabase()
1697
+ sage: MonicPolys.create_table('simon', {'n':{'sql':'INTEGER', 'index':True}})
1698
+ sage: for n in range(20): MonicPolys.add_row('simon', (n,))
1699
+ sage: MonicPolys.add_column('simon', 'n_squared', {'sql':'INTEGER'}, 0)
1700
+ sage: MonicPolys.drop_column('simon', 'n_squared')
1701
+ sage: MonicPolys.show('simon')
1702
+ n
1703
+ --------------------
1704
+ 0
1705
+ 1
1706
+ 2
1707
+ 3
1708
+ 4
1709
+ 5
1710
+ 6
1711
+ 7
1712
+ 8
1713
+ 9
1714
+ 10
1715
+ 11
1716
+ 12
1717
+ 13
1718
+ 14
1719
+ 15
1720
+ 16
1721
+ 17
1722
+ 18
1723
+ 19
1724
+ """
1725
+ if self.__read_only__:
1726
+ raise RuntimeError('Cannot drop columns in a read only database.')
1727
+ # Check input:
1728
+ if table_name not in self.__skeleton__:
1729
+ raise ValueError("Database has no table %s." % table_name)
1730
+ if col_name not in self.__skeleton__[table_name]:
1731
+ raise ValueError("Table %s has no column %s." % (table_name,col_name))
1732
+
1733
+ # Update the skeleton:
1734
+ self.__skeleton__[table_name].pop(col_name)
1735
+
1736
+ self._rebuild_table(table_name)
1737
+
1738
+ def rename_table(self, table_name, new_name):
1739
+ """
1740
+ Rename the table ``table_name`` to ``new_name``.
1741
+
1742
+ EXAMPLES::
1743
+
1744
+ sage: D = SQLDatabase()
1745
+ sage: D.create_table('simon',{'col1':{'sql':'INTEGER'}})
1746
+ sage: D.show('simon')
1747
+ col1
1748
+ --------------------
1749
+ sage: D.rename_table('simon', 'lucy')
1750
+ sage: D.show('simon')
1751
+ Traceback (most recent call last):
1752
+ ...
1753
+ RuntimeError: Failure to fetch data.
1754
+ sage: D.show('lucy')
1755
+ col1
1756
+ --------------------
1757
+ """
1758
+ if self.__read_only__:
1759
+ raise RuntimeError('Cannot rename tables in a read only database.')
1760
+ # Check input:
1761
+ if table_name not in self.__skeleton__:
1762
+ raise ValueError('Database has no table %s.' % table_name)
1763
+ if new_name in self.__skeleton__:
1764
+ raise ValueError('Database already has table %s.' % new_name)
1765
+
1766
+ self.__connection__.execute('ALTER TABLE %s RENAME TO ' % table_name
1767
+ + new_name)
1768
+
1769
+ # Update skeleton:
1770
+ self.__skeleton__[new_name] = self.__skeleton__.pop(table_name)
1771
+
1772
+ def drop_table(self, table_name):
1773
+ """
1774
+ Delete table ``table_name`` from database.
1775
+
1776
+ INPUT:
1777
+
1778
+ - ``table_name`` -- string
1779
+
1780
+ EXAMPLES::
1781
+
1782
+ sage: D = SQLDatabase()
1783
+ sage: D.create_table('simon',{'col1':{'sql':'INTEGER'}})
1784
+ sage: D.show('simon')
1785
+ col1
1786
+ --------------------
1787
+ sage: D.drop_table('simon')
1788
+ sage: D.get_skeleton()
1789
+ {}
1790
+ """
1791
+ if self.__read_only__:
1792
+ raise RuntimeError('Cannot drop tables from a read only '
1793
+ + 'database.')
1794
+ if table_name not in self.__skeleton__:
1795
+ raise ValueError("Database has no table %s." % table_name)
1796
+
1797
+ self.__connection__.execute('DROP TABLE ' + table_name)
1798
+
1799
+ # Update Skeleton
1800
+ self.__skeleton__.pop(table_name)
1801
+
1802
+ def drop_data_from_table(self, table_name):
1803
+ """
1804
+ Remove all rows from ``table_name``.
1805
+
1806
+ EXAMPLES::
1807
+
1808
+ sage: D = SQLDatabase()
1809
+ sage: D.create_table('simon',{'col1':{'sql':'INTEGER'}})
1810
+ sage: D.add_row('simon',(9,))
1811
+ sage: D.show('simon')
1812
+ col1
1813
+ --------------------
1814
+ 9
1815
+ sage: D.drop_data_from_table('simon')
1816
+ sage: D.show('simon')
1817
+ col1
1818
+ --------------------
1819
+ """
1820
+ if self.__read_only__:
1821
+ raise RuntimeError('Cannot remove data from a read only database.')
1822
+ if table_name not in self.__skeleton__:
1823
+ raise ValueError("Database has no table %s." % table_name)
1824
+ self.__connection__.execute('DELETE FROM ' + table_name)
1825
+
1826
+ def make_index(self, col_name, table_name, unique=False):
1827
+ """
1828
+ Set the column ``col_name`` in table ``table_name`` to be an index,
1829
+ that is, a column set up to do quick searches on.
1830
+
1831
+ INPUT:
1832
+
1833
+ - ``col_name`` -- string
1834
+ - ``table_name`` -- string
1835
+ - ``unique`` -- requires that there are no multiple entries in the
1836
+ column, makes searching faster
1837
+
1838
+ EXAMPLES::
1839
+
1840
+ sage: MonicPolys = SQLDatabase()
1841
+ sage: MonicPolys.create_table('simon', {'n':{'sql':'INTEGER', 'index':True}, 'n2':{'sql':'INTEGER'}})
1842
+ sage: MonicPolys.make_index('n2','simon')
1843
+ sage: MonicPolys.get_skeleton()
1844
+ {'simon': {'n': {'index': True,
1845
+ 'primary_key': False,
1846
+ 'sql': 'INTEGER',
1847
+ 'unique': False},
1848
+ 'n2': {'index': True,
1849
+ 'primary_key': False,
1850
+ 'sql': 'INTEGER',
1851
+ 'unique': False}}}
1852
+ """
1853
+ if self.__read_only__:
1854
+ raise RuntimeError('Cannot modify a read only database.')
1855
+ if table_name not in self.__skeleton__:
1856
+ raise ValueError("Database has no table %s." % table_name)
1857
+ if col_name not in self.__skeleton__[table_name]:
1858
+ raise ValueError("Table %s has no column %s." % (table_name,col_name))
1859
+
1860
+ if unique:
1861
+ index_string = 'CREATE UNIQUE INDEX ' + col_name + ' ON ' \
1862
+ + table_name + ' (' + col_name + ')'
1863
+ else:
1864
+ index_string = 'CREATE INDEX ' + col_name + ' ON ' + table_name \
1865
+ + ' (' + col_name + ')'
1866
+ cur = self.__connection__.cursor()
1867
+ cur.execute(index_string)
1868
+
1869
+ # Update Skeleton
1870
+ self.__skeleton__[table_name][col_name]['index'] = True
1871
+ if unique:
1872
+ self.__skeleton[table_name][col_name]['unique'] = True
1873
+
1874
+ def drop_index(self, table_name, index_name):
1875
+ """
1876
+ Set the column ``index_name`` in table ``table_name`` to not be an
1877
+ index. See ``make_index()``
1878
+
1879
+ EXAMPLES::
1880
+
1881
+ sage: MonicPolys = SQLDatabase()
1882
+ sage: MonicPolys.create_table('simon', {'n':{'sql':'INTEGER', 'index':True}, 'n2':{'sql':'INTEGER'}})
1883
+ sage: MonicPolys.drop_index('simon', 'n')
1884
+ sage: MonicPolys.get_skeleton()
1885
+ {'simon': {'n': {'index': False,
1886
+ 'primary_key': False,
1887
+ 'sql': 'INTEGER',
1888
+ 'unique': False},
1889
+ 'n2': {'index': False,
1890
+ 'primary_key': False,
1891
+ 'sql': 'INTEGER',
1892
+ 'unique': False}}}
1893
+ """
1894
+ if self.__read_only__:
1895
+ raise RuntimeError('Cannot modify a read only database.')
1896
+ if table_name not in self.__skeleton__:
1897
+ raise ValueError("Database has no table %s." % table_name)
1898
+ if index_name not in self.__skeleton__[table_name]:
1899
+ raise ValueError("Table %s has no column %s." % (table_name,
1900
+ index_name))
1901
+ if not self.__skeleton__[table_name][index_name]['index']:
1902
+ return # silently
1903
+
1904
+ cur = self.__connection__.cursor()
1905
+ cur.execute('DROP INDEX i_' + table_name + '_' + index_name)
1906
+
1907
+ # Update Skeleton
1908
+ self.__skeleton__[table_name][index_name]['index'] = False
1909
+
1910
+ def make_unique(self, table_name, col_name):
1911
+ """
1912
+ Set the column ``col_name`` in table ``table_name`` to store unique
1913
+ values.
1914
+
1915
+ NOTE:
1916
+
1917
+ This function only adds the requirement for entries in ``col_name`` to
1918
+ be unique, it does not change the values.
1919
+
1920
+ EXAMPLES::
1921
+
1922
+ sage: MonicPolys = SQLDatabase()
1923
+ sage: MonicPolys.create_table('simon', {'n':{'sql':'INTEGER', 'index':True}, 'n2':{'sql':'INTEGER'}})
1924
+ sage: MonicPolys.make_unique('simon', 'n2')
1925
+ sage: MonicPolys.get_skeleton()
1926
+ {'simon': {'n': {'index': True,
1927
+ 'primary_key': False,
1928
+ 'sql': 'INTEGER',
1929
+ 'unique': False},
1930
+ 'n2': {'index': False,
1931
+ 'primary_key': False,
1932
+ 'sql': 'INTEGER',
1933
+ 'unique': True}}}
1934
+ """
1935
+ if self.__read_only__:
1936
+ raise RuntimeError('Cannot modify a read only database')
1937
+ if table_name not in self.__skeleton__:
1938
+ raise ValueError("Database has no table %s." % table_name)
1939
+ if col_name not in self.__skeleton__[table_name]:
1940
+ raise ValueError("Table %s has no column %s." % (table_name, col_name))
1941
+
1942
+ # Update the skeleton:
1943
+ self.__skeleton__[table_name][col_name]['unique'] = True
1944
+
1945
+ self._rebuild_table(table_name)
1946
+
1947
+ def drop_unique(self, table_name, col_name):
1948
+ """
1949
+ Set the column ``col_name`` in table ``table_name`` not store unique
1950
+ values.
1951
+
1952
+ NOTE:
1953
+
1954
+ This function only removes the requirement for entries in ``col_name``
1955
+ to be unique, it does not delete it.
1956
+
1957
+ EXAMPLES::
1958
+
1959
+ sage: MonicPolys = SQLDatabase()
1960
+ sage: MonicPolys.create_table('simon', {'n':{'sql':'INTEGER', 'index':True}, 'n2':{'sql':'INTEGER'}})
1961
+ sage: MonicPolys.make_unique('simon', 'n2')
1962
+ sage: MonicPolys.drop_unique('simon', 'n2')
1963
+ sage: MonicPolys.get_skeleton()
1964
+ {'simon': {'n': {'index': True,
1965
+ 'primary_key': False,
1966
+ 'sql': 'INTEGER',
1967
+ 'unique': False},
1968
+ 'n2': {'index': False,
1969
+ 'primary_key': False,
1970
+ 'sql': 'INTEGER',
1971
+ 'unique': False}}}
1972
+ """
1973
+ if self.__read_only__:
1974
+ raise RuntimeError('Cannot modify a read only database.')
1975
+ if table_name not in self.__skeleton__:
1976
+ raise ValueError("Database has no table %s." % table_name)
1977
+ if col_name not in self.__skeleton__[table_name]:
1978
+ raise ValueError("Table %s has no column %s." % (table_name, col_name))
1979
+ if self.__skeleton__[table_name][col_name]['primary_key']:
1980
+ raise ValueError("Primary keys must be unique.")
1981
+
1982
+ # Update the skeleton:
1983
+ self.__skeleton__[table_name][col_name]['unique'] = False
1984
+
1985
+ self._rebuild_table(table_name)
1986
+
1987
+ def make_primary_key(self, table_name, col_name):
1988
+ """
1989
+ Set the column ``col_name`` in table ``table_name`` to be a primary key.
1990
+
1991
+ A primary key is something like an index, but its main purpose is to
1992
+ link different tables together. This allows searches to be executed on
1993
+ multiple tables that represent maybe different data about the same
1994
+ objects.
1995
+
1996
+ NOTE:
1997
+
1998
+ Some SQL features, such as automatically incrementing primary key,
1999
+ require the full word ``'INTEGER'``, not just ``'INT'``.
2000
+
2001
+ EXAMPLES::
2002
+
2003
+ sage: MonicPolys = SQLDatabase()
2004
+ sage: MonicPolys.create_table('simon', {'n':{'sql':'INTEGER', 'index':True}, 'n2':{'sql':'INTEGER'}})
2005
+ sage: MonicPolys.make_primary_key('simon', 'n2')
2006
+ sage: MonicPolys.get_skeleton()
2007
+ {'simon': {'n': {'index': True,
2008
+ 'primary_key': False,
2009
+ 'sql': 'INTEGER',
2010
+ 'unique': False},
2011
+ 'n2': {'index': False,
2012
+ 'primary_key': True,
2013
+ 'sql': 'INTEGER',
2014
+ 'unique': True}}}
2015
+ """
2016
+ if self.__read_only__:
2017
+ raise RuntimeError('Cannot modify a read only database.')
2018
+ if table_name not in self.__skeleton__:
2019
+ raise ValueError("Database has no table %s." % table_name)
2020
+ if col_name not in self.__skeleton__[table_name]:
2021
+ raise ValueError("Table %s has no column %s." % (table_name, col_name))
2022
+
2023
+ # Update the skeleton:
2024
+ self.__skeleton__[table_name][col_name]['primary_key'] = True
2025
+ self.__skeleton__[table_name][col_name]['unique'] = True
2026
+
2027
+ self._rebuild_table(table_name)
2028
+
2029
+ def drop_primary_key(self, table_name, col_name):
2030
+ """
2031
+ Set the column ``col_name`` in table ``table_name`` not to be a primary
2032
+ key.
2033
+
2034
+ A primary key is something like an index, but its main purpose is to
2035
+ link different tables together. This allows searches to be executed on
2036
+ multiple tables that represent maybe different data about the same
2037
+ objects.
2038
+
2039
+ NOTE:
2040
+
2041
+ This function only changes the column to be non-primary, it does not
2042
+ delete it.
2043
+
2044
+ EXAMPLES::
2045
+
2046
+ sage: MonicPolys = SQLDatabase()
2047
+ sage: MonicPolys.create_table('simon', {'n':{'sql':'INTEGER', 'index':True}, 'n2':{'sql':'INTEGER'}})
2048
+ sage: MonicPolys.make_primary_key('simon', 'n2')
2049
+ sage: MonicPolys.drop_primary_key('simon', 'n2')
2050
+ sage: MonicPolys.get_skeleton()
2051
+ {'simon': {'n': {'index': True,
2052
+ 'primary_key': False,
2053
+ 'sql': 'INTEGER',
2054
+ 'unique': False},
2055
+ 'n2': {'index': False,
2056
+ 'primary_key': False,
2057
+ 'sql': 'INTEGER',
2058
+ 'unique': True}}}
2059
+ """
2060
+ if self.__read_only__:
2061
+ raise RuntimeError('Cannot modify a read only database.')
2062
+ if table_name not in self.__skeleton__:
2063
+ raise ValueError("Database has no table %s." % table_name)
2064
+ if col_name not in self.__skeleton__[table_name]:
2065
+ raise ValueError("Table %s has no column %s." % (table_name,col_name))
2066
+ if not self.__skeleton__[table_name][col_name]['primary_key']:
2067
+ return # silently
2068
+
2069
+ # Update the skeleton:
2070
+ self.__skeleton__[table_name][col_name]['primary_key'] = False
2071
+
2072
+ self._rebuild_table(table_name)
2073
+
2074
+ def add_row(self, table_name, values, entry_order=None):
2075
+ """
2076
+ Add the row described by ``values`` to the table ``table_name``. Values
2077
+ should be a tuple, of same length and order as columns in given table.
2078
+
2079
+ NOTE:
2080
+
2081
+ If ``values`` is of length one, be sure to specify that it is a tuple of
2082
+ length one, by using a comma, e.g.::
2083
+
2084
+ sage: values = (6,)
2085
+
2086
+ EXAMPLES::
2087
+
2088
+ sage: DB = SQLDatabase()
2089
+ sage: DB.create_table('simon',{'a1':{'sql':'bool'}, 'b2':{'sql':'int'}})
2090
+ sage: DB.add_row('simon',(0,1))
2091
+ sage: cur = DB.get_cursor()
2092
+ sage: (cur.execute('select * from simon')).fetchall()
2093
+ [(0, 1)]
2094
+ """
2095
+ self.add_rows(table_name, [values], entry_order)
2096
+
2097
+ def delete_rows(self, query):
2098
+ """
2099
+ Use a ``SQLQuery`` instance to modify (delete rows from) the
2100
+ database.
2101
+
2102
+ ``SQLQuery`` must have no join statements. (As of now, you can only
2103
+ delete from one table at a time -- ideas and patches are welcome).
2104
+
2105
+ To remove all data that satisfies a ``SQLQuery``, send the query as an
2106
+ argument to ``delete_rows``. Be careful, test your query first.
2107
+
2108
+ Recommended use: have some kind of row identification primary
2109
+ key column that you use as a parameter in the query. (See example
2110
+ below).
2111
+
2112
+ INPUT:
2113
+
2114
+ - ``query`` -- a ``SQLQuery`` (Delete the rows returned when query is
2115
+ run)
2116
+
2117
+ EXAMPLES::
2118
+
2119
+ sage: from collections import OrderedDict
2120
+ sage: DB = SQLDatabase()
2121
+ sage: DB.create_table('lucy', OrderedDict([
2122
+ ....: ('id', {'sql':'INTEGER', 'primary_key':True, 'index':True}),
2123
+ ....: ('a1', {'sql':'bool'}),
2124
+ ....: ('b2', {'sql':'int'})]))
2125
+ sage: DB.add_rows('lucy', [(0,1,1),(1,1,4),(2,0,7),(3,1,384), (4,1,978932)],['id','a1','b2'])
2126
+ sage: DB.show('lucy')
2127
+ id a1 b2
2128
+ ------------------------------------------------------------
2129
+ 0 1 1
2130
+ 1 1 4
2131
+ 2 0 7
2132
+ 3 1 384
2133
+ 4 1 978932
2134
+ sage: Q = SQLQuery(DB, {'table_name':'lucy', 'display_cols':['id','a1','b2'], 'expression':['id','>=',3]})
2135
+ sage: DB.delete_rows(Q)
2136
+ sage: DB.show('lucy')
2137
+ id a1 b2
2138
+ ------------------------------------------------------------
2139
+ 0 1 1
2140
+ 1 1 4
2141
+ 2 0 7
2142
+ """
2143
+ if self.__read_only__:
2144
+ raise RuntimeError('Cannot delete rows from a read only database.')
2145
+ # Check query is associated with this database
2146
+ if not isinstance(query, SQLQuery):
2147
+ raise TypeError('%s is not a valid SQLQuery' % query)
2148
+ if query.__database__ is not self:
2149
+ raise ValueError('%s is not associated to this database.' % query)
2150
+ if (query.__query_string__).find(' JOIN ') != -1:
2151
+ raise ValueError(f'{query} is not a valid query. Can only '
2152
+ 'delete from one table at a time.')
2153
+
2154
+ delete_statement = re.sub('SELECT .* FROM', 'DELETE FROM',
2155
+ query.__query_string__)
2156
+
2157
+ try:
2158
+ cur = self.get_cursor()
2159
+ cur.execute(delete_statement, query.__param_tuple__)
2160
+ except Exception:
2161
+ raise RuntimeError('Failure to complete delete. Check your data.')
2162
+
2163
+ def add_rows(self, table_name, rows, entry_order=None):
2164
+ """
2165
+ INPUT:
2166
+
2167
+ - ``rows`` -- list of tuples that represent one row of data to add
2168
+ (types should match col types in order)
2169
+ - ``entry_order`` -- an ordered list or tuple overrides normal order
2170
+ with user defined order
2171
+
2172
+ EXAMPLES::
2173
+
2174
+ sage: DB = SQLDatabase()
2175
+ sage: DB.create_table('simon',{'a1':{'sql':'bool'}, 'b2':{'sql':'int'}})
2176
+ sage: DB.add_rows('simon',[(0,0),(1,1),(1,2)])
2177
+ sage: DB.add_rows('simon',[(0,0),(4,0),(5,1)], ['b2','a1'])
2178
+ sage: cur = DB.get_cursor()
2179
+ sage: (cur.execute('select * from simon')).fetchall()
2180
+ [(0, 0), (1, 1), (1, 2), (0, 0), (0, 4), (1, 5)]
2181
+ """
2182
+ if self.__read_only__:
2183
+ raise RuntimeError('Cannot add rows to read only database.')
2184
+ quest = '(' + ', '.join('?' for i in rows[0]) + ')'
2185
+ strows = [tuple(str(entry) for entry in row) for row in rows]
2186
+
2187
+ if entry_order is not None:
2188
+ self.__connection__.executemany('INSERT INTO ' + table_name
2189
+ + str(tuple(entry_order)) + ' VALUES ' + quest, strows)
2190
+ else:
2191
+ self.__connection__.executemany('INSERT INTO ' + table_name
2192
+ + ' VALUES ' + quest, strows)
2193
+
2194
+ add_data = add_rows
2195
+
2196
+ def vacuum(self):
2197
+ """
2198
+ Clean the extra hard disk space used up by a database that has
2199
+ recently shrunk.
2200
+
2201
+ EXAMPLES::
2202
+
2203
+ sage: DB = SQLDatabase()
2204
+ sage: DB.create_table('simon',{'a1':{'sql':'bool'}, 'b2':{'sql':'int'}})
2205
+ sage: DB.add_row('simon',(0,1))
2206
+ sage: DB.add_data('simon',[(0,0),(1,1),(1,2)])
2207
+ sage: DB.add_data('simon',[(0,0),(4,0),(5,1)], ['b2','a1'])
2208
+ sage: DB.drop_column('simon','b2')
2209
+ sage: DB.commit()
2210
+ sage: DB.vacuum()
2211
+ """
2212
+ self.__connection__.execute('VACUUM')
2213
+
2214
+ def commit(self):
2215
+ """
2216
+ Commit changes to file.
2217
+
2218
+ EXAMPLES::
2219
+
2220
+ sage: DB = SQLDatabase()
2221
+ sage: DB.create_table('simon',{'a1':{'sql':'bool'}, 'b2':{'sql':'int'}})
2222
+ sage: DB.add_row('simon',(0,1))
2223
+ sage: DB.add_data('simon',[(0,0),(1,1),(1,2)])
2224
+ sage: DB.add_data('simon',[(0,0),(4,0),(5,1)], ['b2','a1'])
2225
+ sage: DB.drop_column('simon','b2')
2226
+ sage: DB.commit()
2227
+ sage: DB.vacuum()
2228
+ """
2229
+ if self.__read_only__:
2230
+ raise RuntimeError("Cannot commit read only database.")
2231
+ try:
2232
+ self.__connection__.execute('COMMIT')
2233
+ except sqlite.OperationalError:
2234
+ # Not sure why this throws an exception - but without it,
2235
+ # the changes are not committed so it is necessary.
2236
+ pass