I can't set auto_vacuum option via peewee.
Run this snippet:
from playhouse.sqlite_ext import SqliteExtDatabase, Model, TextField, IntegerField, JSONField
db = SqliteExtDatabase('my_app.db', pragmas=(('cache_size', -1024 * 64), ('journal_mode', 'wal'), ('auto_vacuum', 1)))
class EventLog(Model):
key = TextField()
value = JSONField()
class Meta:
database = db
EventLog.create_table()
After that:
I've connected to sqlite database
sqlite3 my_app.db
SQLite version 3.22.0 2018-01-22 18:45:57
Enter ".help" for usage hints.
sqlite> PRAGMA auto_vacuum;
0
sqlite> PRAGMA journal_mode;
wal
Why auto_vacuum variable doesn't change?
Perhaps this snippet from the sqlite Pragma doc [emphasis added] explains what is happening
The database connection can be changed between full and incremental
autovacuum mode at any time. However, changing from "none" to "full"
or "incremental" can only occur when the database is new (no tables
have yet been created) or by running the VACUUM command. To change
auto-vacuum modes, first use the auto_vacuum pragma to set the new
desired mode, then invoke the VACUUM command to reorganize the entire
database file. To change from "full" or "incremental" back to "none"
always requires running VACUUM even on an empty database.**
Related
I am using following code to create the function and trigger to update the created_at and updated_at fields. with upgrade of new module getting the deprecated API warning.
How can I replace engine.execute(sa.text(create_refresh_updated_at_func.format(schema=my_schema))) line to remove the warning message?
Code:
mapper_registry.metadata.create_all(engine, checkfirst=True)
create_refresh_updated_at_func = """
CREATE OR REPLACE FUNCTION {schema}.refresh_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
"""
my_schema = "public"
engine.execute(sa.text(create_refresh_updated_at_func.format(schema=my_schema)))
Warrning:
RemovedIn20Warning: Deprecated API features detected! These
feature(s) are not compatible with SQLAlchemy 2.0. To prevent
incompatible upgrades prior to updating applications, ensure
requirements files are pinned to "sqlalchemy<2.0". Set environment
variable SQLALCHEMY_WARN_20=1 to show all deprecation warnings. Set
environment variable SQLALCHEMY_SILENCE_UBER_WARNING=1 to silence this
message. (Background on SQLAlchemy 2.0 at: https://sqlalche.me/e/b8d9)
engine.execute(sa.text(create_refresh_updated_at_func.format(schema=my_schema)))
SQLAlchemy no longer supports autocommit at the library level. You need to run the execute within a transaction.
This should work:
with engine.begin() as conn:
conn.execute(text(create_refresh_updated_at_func.format(schema=my_schema)))
migration-core-connection-transaction
You could also use the driver-level isolation level like this but I think the connections from this pool will all be set to autocommit:
engine2 = create_engine(f"postgresql+psycopg2://{username}:{password}#/{db}", isolation_level='AUTOCOMMIT')
with engine2.connect() as conn:
conn.execute(text(create_refresh_updated_at_func.format(schema=my_schema)))
Background
I maintain a Python application that automatically applies SQL schema migrations (adding/removing tables and columns, adjusting the data, etc) to our database (SQL2016). Each migration is executed via PyODBC within a transaction so that it can be rolled back if something goes wrong. Sometimes a migration requires one or more batch statements (GO) to execute correctly. Since GO is not actually a T-SQL command but rather a special keyword in SSMS, I've been splitting each SQL migration on GO and executing each SQL fragment separately within the same transaction.
import pyodbc
import re
conn_args = {
'driver': '{ODBC Driver 17 for SQL Server}',
'hostname': 'MyServer',
'port': 1298,
'server': r'MyServer\MyInstance',
'database': 'MyDatabase',
'user': 'MyUser',
'password': '********',
'autocommit': False,
}
connection = pyodbc.connect(**conn_args)
cursor = connection.cursor()
sql = '''
ALTER TABLE MyTable ADD NewForeignKeyID INT NULL FOREIGN KEY REFERENCES MyParentTable(ID)
GO
UPDATE MyTable
SET NewForeignKeyID = 1
'''
sql_fragments = re.split(r'^\s*GO;?\s*$', sql, flags=re.IGNORECASE|re.MULTILINE)
for sql_frag in sql_fragments:
cursor.execute(sql_frag)
# Wait for the command to complete. This is necessary for some database system commands
# (backup, restore, etc). Probably not necessary for schema migrations, but included
# for completeness.
while cursor.nextset():
pass
connection.commit()
Problem
SQL statement batches aren't being executed like I expected. When the above schema migration is executed in SSMS, it succeeds. When executed in Python, the first batch (adding the foreign key) executes just fine, but the second batch (setting the foreign key value) fails because it isn't aware of the new foreign key.
('42S22', "[42S22] [FreeTDS][SQL Server]Invalid column name 'NewForeignKeyID'. (207) (SQLExecDirectW)")
Goal
Execute a hierarchy of SQL statement batches (i.e. where each statement batch depends upon the previous batch) within a single transaction in PyODBC.
What I've Tried
Searching the PyODBC documentation for information on how PyODBC supports or doesn't support batch statements / the GO command. No references found.
Searching StackOverflow & Google for how to batch statements within PyODBC.
Introducing a small sleep between SQL fragment executions just in case there's some sort of race condition. Seemed unlikely to be a solution, and didn't change the behavior.
I've considered separating each batch of statements out into a separate transaction that is committed before the next batch is executed, but that would reduce/eliminate our ability to automatically roll back a schema migration that fails.
EDIT: I just found this question, which is pretty much exactly what I want to do. However, upon testing (in SSMS) the answer that recommends using EXEC I discovered that the second EXEC command (setting the value) fails because it isn't aware of the new foreign key. I'm bad at testing and it actually does succeed. This solution might work but isn't ideal since EXEC isn't compatible with parameters. Also, this won't work if variables are used across fragments.
BEGIN TRAN
EXEC('ALTER TABLE MyTable ADD NewForeignKeyID INT NULL FOREIGN KEY REFERENCES MyParentTable(ID)')
EXEC('UPDATE MyTable SET NewForeignKeyID = 1')
ROLLBACK TRAN
Invalid column name 'FK_TestID'.
If you are reading the SQL statements from a text file (such as one produced by scripting objects in SSMS) then you could just use Python's subprocess module to run the sqlcmd utility with that file as the input (-i). In its simplest form that would look like
server = "localhost"
port = 49242
uid = "scott"
pwd = "tiger^5HHH"
database = "myDb"
script_file = r"C:\__tmp\batch_test.sql"
"""contents of the above file:
DROP TABLE IF EXISTS so69020084;
CREATE TABLE so69020084 (src varchar(10), var_value varchar(10));
INSERT INTO so69020084 (src, var_value) VALUES ('1st batch', 'foo');
GO
INSERT INTO so69020084 (src, var_value) VALUES ('2nd batch', 'bar');
GO
"""
import subprocess
cmd = [
"sqlcmd",
"-S", f"{server},{port}",
"-U", uid,
"-P", pwd,
"-d", database,
"-i", script_file,
]
subprocess.run(cmd)
I can't seem to correctly connect and pull from a test postgreSQL database in python. I installed PostgreSQL using Homebrew. Here's how I have been accessing the database table and value from the terminal:
xxx-macbook:~ xxx$ psql
psql (9.4.0)
Type "help" for help.
xxx=# \dn
List of schemas
Name | Owner
--------+---------
public | xxx
(1 row)
xxx=# \connect postgres
You are now connected to database "postgres" as user "xxx".
postgres=# SELECT * from test.test;
coltest
-----------
It works!
(1 row)
But when trying to access it from python, using the code below, it doesn't work. Any suggestions?
########################################################################################
# Importing variables from PostgreSQL database via SQL commands
db_conn = psycopg2.connect(database='postgres',
user='xxx')
cursor = db_conn.cursor()
#querying the database
result = cursor.execute("""
Select * From test.test
""")
print "Result: ", result
>>> Result: None
It should say: Result: It works!
You need to fetch the results.
From the docs:
The [execute()-]method returns None. If a query was executed, the returned values can be retrieved using fetch*() methods.
Example:
result = cursor.fetchall()
For reference:
http://initd.org/psycopg/docs/cursor.html#execute
http://initd.org/psycopg/docs/cursor.html#fetch
Note that (unlike psql) psycopg2 wraps anything in transactions. So if you intend to issue persistent changes to the database (INSERT, UPDATE, DELETE, ...) you need to commit them explicitly. Otherwise changes will be rolled back automatically when the connection object is destroyed. Read more on that topic here:
http://initd.org/psycopg/docs/usage.html
http://initd.org/psycopg/docs/usage.html#transactions-control
In sqlalchemy, I make the connection:
conn = engine.connect()
I found this will set autocommit = 0 in my mysqld log.
Now I want to set autocommit = 1 because I do not want to query in a transaction.
Is there a way to do this?
From The SQLAlchemy documentation: Understanding autocommit
conn = engine.connect()
conn.execute("INSERT INTO users VALUES (1, 'john')") # autocommits
The “autocommit” feature is only in effect when no Transaction has otherwise been declared. This means the feature is not generally used with the ORM, as the Session object by default always maintains an ongoing Transaction.
Full control of the “autocommit” behavior is available using the generative Connection.execution_options() method provided on Connection, Engine, Executable, using the “autocommit” flag which will turn on or off the autocommit for the selected scope. For example, a text() construct representing a stored procedure that commits might use it so that a SELECT statement will issue a COMMIT:
engine.execute(text("SELECT my_mutating_procedure()").execution_options(autocommit=True))
What is your dialect for mysql connection?
You can set the autocommit to True to solve the problem, like this mysql+mysqldb://user:password#host:port/db?charset=foo&autocommit=true
You can use this:
from sqlalchemy.sql import text
engine = create_engine(host, user, password, dbname)
engine.execute(text(sql).execution_options(autocommit=True))
In case you're configuring sqlalchemy for a python application using flask / django, you can create the engine like this:
# Configure the SqlAlchemy part of the app instance
app.config['SQLALCHEMY_DATABASE_URI'] = conn_url
session_options = {
'autocommit': True
}
# Create the SqlAlchemy db instance
db = SQLAlchemy(app, session_options=session_options)
I might be little late here, but for fox who is using sqlalchemy >= 2.0.*, above solution might not work as it did not work for me.
So, I went through the official documentation, and below solution worked for me.
from sqlalchemy import create_engine
db_engine = create_engine(database_uri, isolation_level="AUTOCOMMIT")
Above code works if you want to set autocommit engine wide.
But if you want use autocommit for a particular query then you can use below -
with engine.connect().execution_options(isolation_level="AUTOCOMMIT") as connection:
with connection.begin():
connection.execute("<statement>")
Official Documentation
This can be done using the autocommit option in the execution_option() method:
engine.execute("UPDATE table SET field1 = 'test'").execution_options(autocommit=True))
This information is available within the documentation on Autocommit
I've got a program which has a database with a certain schema, v0.1.0.
In my next version (v0.1.1) I've made changes to the database schema.
So when I update to (0.1.1) I would like those changes to take effect without effecting the original data in (0.1.0) and subsequent versions.
How do I go about making the changes without affecting (0.1.0) database data and keeping track of those changes in subsequent versions?
I'm using python with sqlite3.
Update
There is no support for multiple versions of the software. The database is dependent on the verion that you are using.
The is no concurrent access to the database, 1 database per version.
So the user can use the old version but when they upgrade to the new version the .sqlite schema will be changed.
Track the schema version in the database with the user_version PRAGMA, and keep a sequence of upgrade steps:
def get_schema_version(conn):
cursor = conn.execute('PRAGMA user_version')
return cursor.fetchone()[0]
def set_schema_version(conn, version):
conn.execute('PRAGMA user_version={:d}'.format(version))
def initial_schema(conn):
# install initial schema
# ...
def ugrade_1_2(conn):
# modify schema, alter data, etc.
# map target schema version to upgrade step from previous version
upgrade_steps = {
1: initial_schema,
2: ugrade_1_2,
}
def check_upgrade(conn):
current = get_schema_version(conn)
target = max(upgrade_steps)
for step in range(current + 1, target + 1):
if step in upgrade_steps:
upgrade_steps[step](conn)
set_schema_version(conn, step)
There are a few ways to do this, I will only mention one.
It seems like you already track the version within the database. On your application start, you will want to check this version against the running application's version and run any sql scripts that will perform the schema changes.
update
An example of this in action:
import os
import sqlite3 as sqlite
def make_movie_table(cursor):
cursor.execute('CREATE TABLE movies(id INTEGER PRIMARY KEY, title VARCHAR(20), link VARCHAR(20))')
def make_series_table(cursor):
cursor.execute('CREATE TABLE series(title VARCHAR(30) PRIMARY KEY,series_link VARCHAR(60),number_of_episodes INTEGER,number_of_seasons INTEGER)')
def make_episode_table(cursor):
cursor.execute('CREATE TABLE episodes(id INTEGER PRIMARY KEY,title VARCHAR(30),episode_name VARCHAR(15), episode_link VARCHAR(40), Date TIMESTAMP, FOREIGN KEY (title) REFERENCES series(title) ON DELETE CASCADE)')
def make_version_table(cursor):
cursor.execute('CREATE TABLE schema_versions(version VARCHAR(6))')
cursor.execute('insert into schema_versions(version) values ("0.1.0")')
def create_database(sqlite_file):
if not os.path.exists(sqlite_file):
connection = sqlite.connect(sqlite_file)
cursor = connection.cursor()
cursor.execute("PRAGMA foreign_keys = ON")
make_movie_table(cursor)
make_series_table(cursor)
make_episode_table(cursor)
make_version_table(cursor)
connection.commit()
connection.close()
def upgrade_database(sqlite_file):
if os.path.exists(sqlite_file):
connection = sqlite.connect(sqlite_file)
cursor = connection.cursor()
cursor.execute("select max(version) from schema_versions")
row = cursor.fetchone()
database_version = row[0]
print('current version is %s' % database_version)
if database_version == '0.1.0':
print('upgrading version to 0.1.1')
cursor.execute('alter table series ADD COLUMN new_column1 VARCHAR(10)')
cursor.execute('alter table series ADD COLUMN new_column2 INTEGER')
cursor.execute('insert into schema_versions(version) values ("0.1.1")')
#if database_version == '0.1.1':
#print('upgrading version to 0.1.2')
#etc cetera
connection.commit()
connection.close()
#I need to add 2 columns to the series table, when the user upgrade the software.
if __name__ == '__main__':
create_database('/tmp/db.sqlite')
upgrade_database('/tmp/db.sqlite')
Each upgrade script will take care of making your database changes, and then update the version inside the DB to the latest version. Note that we do not use elif statements, this is so that you can upgrade a database over multiple versions if it needs to.
There are some caveats to watch out for:
run upgrades inside transactions, you will want to rollback on any errors to avoid leaving the database in an unusable state. -- update this is incorrect as pointed out, thanks Martijn!
avoid renames and column deletes if you can, and if you must, ensure any views using them are updated too.
Long term you're going to be better off using an ORM such as SQLAlchemy with a migration tool like Alembic.