回答ではありません
環境構築&調査用の中途半端なスクリプトです。
すでに一度解決済みになっているので、解説はしません。
bash
1 mkdir data python_old python_new
2 chmod a+w data
3 cat > entry.sh << EOF
4 entrypoint=/sleep.sh
5 kill_child() {
6 output=\ $( ps -o pid,pgid,args )
7 pids=\ $(
8 echo "\ $output " |
9 sed '/ps -o pid,pgid,args *\$/d;/bash \\ /entry.sh *\$/d' |
10 awk '\ $2 ~/^' \ $ \ $ '\$/{print \ $1 ;}' |
11 sed '/' \ $ \ $ '/d'
12 )
13 kill \ $pids
14 }
15 trap "echo trapped;kill_child;wait;exit 1" INT TERM
16 bash -c "sleep infinity" &
17 wait
18 echo "entry finished"
19 exit 0
20 EOF
21 cat > docker-compose.yml << EOF
22 services:
23 db:
24 image: mcr.microsoft.com/mssql/server:2019-latest
25 volumes:
26 - ./data:/var/opt/mssql
27 ports:
28 - 1433:1433
29 environment:
30 ACCEPT_EULA: Y
31 MSSQL_PID: Express
32 MSSQL_SA_PASSWORD: yourStrong(!)Password
33 python_old:
34 image: python:3.8
35 volumes:
36 - ./python_old:/home/python
37 - ./entry.sh:/entry.sh
38 user: "1000:1000"
39 working_dir: /home/python
40 environment:
41 HOME: /home/python
42 command: bash /entry.sh
43 python_new:
44 image: python:3.12
45 volumes:
46 - ./python_new:/home/python
47 - ./entry.sh:/entry.sh
48 user: "1000:1000"
49 working_dir: /home/python
50 environment:
51 HOME: /home/python
52 command: bash /entry.sh
53 EOF
54 docker compose up -d
55 SA_PASS = 'yourStrong(!)Password'
56 cat > wait_db_started.sh << EOF
57 while ! /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P ' ${SA_PASS} ' </dev/null 2>/dev/null;do
58 sleep 1
59 done
60 EOF
61 docker compose exec -T db bash -x < wait_db_started.sh
62 cat > initialize_db.sql << EOF
63 CREATE DATABASE TestDB COLLATE Japanese_CI_AS;
64 go
65 SELECT Name from sys.databases;
66 go
67 CREATE LOGIN testuser WITH PASSWORD = ' ${SA_PASS} ';
68 go
69 USE TestDB;
70 go
71 CREATE USER testuser FROM LOGIN testuser;
72 go
73 ALTER ROLE db_ddladmin ADD MEMBER testuser;
74 go
75 ALTER ROLE db_datawriter ADD MEMBER testuser;
76 go
77 ALTER ROLE db_datareader ADD MEMBER testuser;
78 go
79 EOF
80 docker compose exec -T db /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P " ${SA_PASS} " -e < initialize_db.sql
81 cat > initialize_tables.sql << EOF
82 USE TestDB;
83 go
84 CREATE TABLE Inventory (id INT PRIMARY KEY, name NVARCHAR(50), quantity INT);
85 go
86 SET IMPLICIT_TRANSACTIONS ON;
87 go
88 INSERT INTO Inventory VALUES (1, 'banana', 150);
89 go
90 INSERT INTO Inventory VALUES (2, 'orange', 154);
91 go
92 INSERT INTO Inventory VALUES (3, 'バナナ', 180);
93 go
94 SELECT * FROM Inventory;
95 go
96 commit;
97 go
98 EOF
99 docker compose exec -T db /opt/mssql-tools/bin/sqlcmd -S localhost -U testuser -P " ${SA_PASS} " -e < initialize_tables.sql
100 cat > setup_drivers.sh << EOF
101 curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o /usr/share/keyrings/microsoft-prod.gpg
102 curl https://packages.microsoft.com/config/debian/12/prod.list | tee /etc/apt/sources.list.d/mssql-release.list
103 apt-get update
104 ACCEPT_EULA=Y apt-get install -y msodbcsql18
105 ACCEPT_EULA=Y apt-get install -y mssql-tools18
106 apt-get install -y unixodbc-dev
107 apt-get install -y libgssapi-krb5-2
108 EOF
109 cat > setup_venv_1.sh << EOF
110 if [ ! -d env1 ]; then
111 python3 -m venv env1
112 fi
113 . env1/bin/activate
114 pip install -U pip setuptools
115 pip install sqlalchemy==1.4.22 pandas==1.1.3 pyodbc
116 deactivate
117 EOF
118 cat > setup_venv_2.sh << EOF
119 if [ ! -d env2 ]; then
120 python3 -m venv env2
121 fi
122 . env2/bin/activate
123 pip install -U pip setuptools
124 pip install sqlalchemy==1.4.22 pandas==2.2.2 pyodbc
125 deactivate
126 EOF
127 cat > setup_venv_3.sh << EOF
128 if [ ! -d env3 ]; then
129 python3 -m venv env3
130 fi
131 . env3/bin/activate
132 pip install -U pip setuptools
133 pip install sqlalchemy==1.4.36 pandas==2.1.4 pyodbc
134 deactivate
135 EOF
136 cat > setup_venv_4.sh << EOF
137 if [ ! -d env4 ]; then
138 python3 -m venv env4
139 fi
140 . env4/bin/activate
141 pip install -U pip setuptools
142 pip install sqlalchemy pandas pyodbc
143 deactivate
144 EOF
145 for service in python_old python_new ; do
146 docker compose exec -u root:root -T ${service} bash -x < setup_drivers.sh
147 for venvsh in ./setup_venv_*.sh ; do
148 docker compose exec -T ${service} bash < ${venvsh}
149 done
150 cat > $service /sqlalchemy_test_1.py << EOF
151 import traceback
152 import pandas as pd
153 from sqlalchemy import create_engine
154 engine = create_engine(
155 "mssql+pyodbc://{user_id}:{password}@{host}:{port}/{dbname}?driver={driver}".format(
156 host = "db",
157 port = "1433",
158 dbname = "TestDB",
159 user_id = "testuser",
160 password = "yourStrong(!)Password",
161 driver = "ODBC Driver 18 for SQL Server"
162 ),
163 connect_args={'TrustServerCertificate': 'yes'}
164 )
165
166 df1 = pd.DataFrame([[4,"みかん", 300],], columns=["id", "name", "quantity"])
167 df2 = pd.DataFrame([[5,"りんご", 200],], columns=["id", "name", "quantity"])
168 df3 = pd.DataFrame([[6,"もも", 250],], columns=["id", "name", "quantity"])
169 tablename= 'inventory'
170
171 with engine.connect() as conn:
172 with conn.begin() as tran:
173 try:
174 df1.to_sql(tablename, conn, index=False, if_exists="append")
175 raise Exception("test")
176 df2.to_sql(tablename, conn, index=False, if_exists="append")
177 tran.commit()
178 except Exception:
179 tran.rollback()
180 print(traceback.format_exc())
181 with conn.begin() as tran:
182 try:
183 df2.to_sql(tablename, conn, index=False, if_exists="append")
184 df3.to_sql(tablename, conn, index=False, if_exists="append")
185 tran.commit()
186 print("success")
187 except Exception:
188 tran.rollback()
189 print(traceback.format_exc())
190 EOF
191 cat > $service /sqlalchemy_test_2.py << EOF
192 import traceback
193 import pandas as pd
194 from sqlalchemy import create_engine
195 engine = create_engine(
196 "mssql+pyodbc://{user_id}:{password}@{host}:{port}/{dbname}?driver={driver}".format(
197 host = "db",
198 port = "1433",
199 dbname = "TestDB",
200 user_id = "testuser",
201 password = "yourStrong(!)Password",
202 driver = "ODBC Driver 18 for SQL Server"
203 ),
204 connect_args={'TrustServerCertificate': 'yes'}
205 #, isolation_level="READ COMMITTED"
206 )
207
208 df1 = pd.DataFrame([[4,"みかん", 300],], columns=["id", "name", "quantity"])
209 df2 = pd.DataFrame([[5,"りんご", 200],], columns=["id", "name", "quantity"])
210 df3 = pd.DataFrame([[6,"もも", 250],], columns=["id", "name", "quantity"])
211 tablename= 'inventory'
212
213 with engine.connect() as conn:
214 with conn.begin():
215 try:
216 df1.to_sql(tablename, conn, index=False, if_exists="append")
217 raise Exception("test")
218 df2.to_sql(tablename, conn, index=False, if_exists="append")
219 conn.commit()
220 except Exception:
221 conn.rollback()
222 print(traceback.format_exc())
223 with conn.begin():
224 try:
225 df2.to_sql(tablename, conn, index=False, if_exists="append")
226 df3.to_sql(tablename, conn, index=False, if_exists="append")
227 conn.commit()
228 print("success")
229 except Exception:
230 conn.rollback()
231 print(traceback.format_exc())
232 EOF
233 cat > $service /assert_reset.sql << EOF
234 select * from inventory;
235 go
236 delete from inventory where id between 4 and 6;
237 go
238 EOF
239 for env in 1 2 3 4 ; do
240 docker compose exec -T ${service} bash << EOF
241 . env ${env} /bin/activate
242 pip list
243 python sqlalchemy_test_1.py
244 /opt/mssql-tools18/bin/sqlcmd -S db -d TestDB -U testuser -P " ${SA_PASS} " -C -e <assert_reset.sql
245 python sqlalchemy_test_2.py
246 /opt/mssql-tools18/bin/sqlcmd -S db -d TestDB -U testuser -P " ${SA_PASS} " -C -e <assert_reset.sql
247 deactivate
248 EOF
249 done
250 done 2 > &1 | tee setup.log