Re: getting the number of affected rows
Hans Bulfone <[email protected]> Mon, 23 Oct 2006 01:40:31 +0200
| Newsgroups | gmane.lisp.clsql.devel |
|---|---|
| Message-ID | <[email protected]> |
--liOOAslEiF7prFVr
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
hi,
the patch is now ready. it includes support for all backends
except aodbc and db2. i haven't tested oracle.
for odbc there was nothing to do.
there were a few problems were i'm unsure if i picked
the best solution:
1. in the :fdml/transaction/3 test the result of update-records
is part of the test result. in my patch they are simply
removed from the test result as it seems there is no easy
way to make the test result depend on the database backend.
(except perhaps putting
(eql (update-records ...) (dbtype-dependent-expected-value))
in the test).
the patch includes a new test for the rowcount feature
which is skipped on aodbc.
2. the change in sql/utils.lisp is not related to the rest of
the patch but was required in my case to get the test suite
working with sqlite2 and sqlite3 below 3.2.4.
the problem is that on sbcl
(write-to-string 8e8 :readably t) => "8.e8"
which is not a valid floating point literal in those sqlite
versions. on clisp
(write-to-string 8e8 :readably t) => "8.0f8"
which is fine. i don't know about other cl implementations.
bye,
hans.
--liOOAslEiF7prFVr
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="clsql-rowcount.patch"
diff -Nru clsql-3.7.7.ORIG/db-mysql/mysql-sql.lisp clsql-3.7.7/db-mysql/mysql-sql.lisp
--- clsql-3.7.7.ORIG/db-mysql/mysql-sql.lisp 2005-11-10 23:03:45.000000000 +0100
+++ clsql-3.7.7/db-mysql/mysql-sql.lisp 2006-10-21 18:11:40.000000000 +0200
@@ -226,7 +226,7 @@
(declare (type mysql-mysql-ptr-def mysql-ptr))
(if (zerop (mysql-real-query mysql-ptr sql-native
(expression-length sql-expression)))
- t
+ (mysql-affected-rows mysql-ptr)
(error 'sql-database-data-error
:database database
:expression sql-expression
diff -Nru clsql-3.7.7.ORIG/db-oracle/oracle-sql.lisp clsql-3.7.7/db-oracle/oracle-sql.lisp
--- clsql-3.7.7.ORIG/db-oracle/oracle-sql.lisp 2006-05-10 02:45:15.000000000 +0200
+++ clsql-3.7.7/db-oracle/oracle-sql.lisp 2006-10-22 18:24:46.000000000 +0200
@@ -502,7 +502,7 @@
(with-slots (envhp svchp errhp) db
(uffi:with-foreign-strings ((c-stmt-string sql-stmt-string))
(let ((stmthp (uffi:allocate-foreign-object :pointer-void))
- select-p)
+ select-p changed-rows)
(uffi:with-foreign-object (stmttype :unsigned-short)
(unwind-protect
@@ -530,7 +530,17 @@
(deref-vp stmthp)
(deref-vp errhp)
iters 0 +null-void-pointer+ +null-void-pointer+ +oci-default+
- :database db)))
+ :database db))
+ (unless select-p
+ (uffi:with-foreign-object (rowcount 'ub4)
+ (oci-attr-get (deref-vp stmthp)
+ +oci-htype-stmt+
+ rowcount
+ +unsigned-int-null-pointer+
+ +oci-attr-row-count+
+ (deref-vp errhp)
+ :database db)
+ (setq changed-rows (uffi:deref-pointer rowcount 'ub4)))))
;; free resources unless a query
(unless select-p
(oci-handle-free (deref-vp stmthp) +oci-htype-stmt+)
@@ -540,7 +550,7 @@
(select-p
(make-query-cursor db stmthp result-types field-names))
(t
- nil))))))
+ changed-rows))))))
;; Return a QUERY-CURSOR representing the table returned from the OCI
@@ -892,8 +902,8 @@
(defmethod database-query (query-expression (database oracle-database) result-types field-names)
(let ((cursor (sql-stmt-exec query-expression database result-types field-names)))
;; (declare (type (or query-cursor null) cursor))
- (if (null cursor) ; No table was returned.
- (values)
+ (if (or (null cursor) (numberp cursor)) ; No table was returned.
+ cursor
(do ((reversed-result nil))
(nil)
(let* ((eof-value :eof)
@@ -958,10 +968,10 @@
(mapcar #'car (database-query query database nil nil))))
(defmethod database-execute-command (sql-expression (database oracle-database))
- (database-query sql-expression database nil nil)
- (when (database-autocommit database)
- (oracle-commit database))
- t)
+ (prog1
+ (database-query sql-expression database nil nil)
+ (when (database-autocommit database)
+ (oracle-commit database))))
(defstruct (cd (:constructor make-cd)
diff -Nru clsql-3.7.7.ORIG/db-postgresql/postgresql-sql.lisp clsql-3.7.7/db-postgresql/postgresql-sql.lisp
--- clsql-3.7.7.ORIG/db-postgresql/postgresql-sql.lisp 2006-10-16 21:38:09.000000000 +0200
+++ clsql-3.7.7/db-postgresql/postgresql-sql.lisp 2006-10-18 16:37:37.000000000 +0200
@@ -213,7 +213,7 @@
(unwind-protect
(case (PQresultStatus result)
(#.pgsql-exec-status-type#command-ok
- t)
+ (parse-integer (uffi:convert-from-cstring (PQcmdTuples result)) :junk-allowed t))
((#.pgsql-exec-status-type#empty-query
#.pgsql-exec-status-type#tuples-ok)
(warn "Strange result...")
diff -Nru clsql-3.7.7.ORIG/db-postgresql-socket/postgresql-socket-sql.lisp clsql-3.7.7/db-postgresql-socket/postgresql-socket-sql.lisp
--- clsql-3.7.7.ORIG/db-postgresql-socket/postgresql-socket-sql.lisp 2005-09-18 02:13:11.000000000 +0200
+++ clsql-3.7.7/db-postgresql-socket/postgresql-socket-sql.lisp 2006-10-21 19:00:15.000000000 +0200
@@ -102,6 +102,21 @@
new-types)))
(nreverse new-types)))
+;; Result message parsing
+(defun parse-command-result (result)
+ (let ((i (min 7 (length result))))
+ (cond
+ ((string= result "INSERT " :end1 i)
+ (multiple-value-bind (oid pos)
+ (parse-integer result :start 7 :junk-allowed t)
+ (declare (ignore oid))
+ (values (parse-integer result :start (1+ pos) :junk-allowed t))))
+ ((string= result "UPDATE " :end1 i)
+ (values (parse-integer result :start 7 :junk-allowed t)))
+ ((string= result "DELETE " :end1 i)
+ (values (parse-integer result :start 7 :junk-allowed t)))
+ (t nil))))
+
(defun convert-to-clsql-warning (database condition)
(ecase *backend-warning-behavior*
@@ -264,7 +279,7 @@
:expression expression
:error-id "multiple-results"
:message "Received multiple results for command."))
- result)
+ (parse-command-result result))
(t
(close-postgresql-connection connection)
(error 'sql-database-data-error
diff -Nru clsql-3.7.7.ORIG/db-sqlite/sqlite-api.lisp clsql-3.7.7/db-sqlite/sqlite-api.lisp
--- clsql-3.7.7.ORIG/db-sqlite/sqlite-api.lisp 2004-10-03 17:45:47.000000000 +0200
+++ clsql-3.7.7/db-sqlite/sqlite-api.lisp 2006-10-22 18:10:21.000000000 +0200
@@ -42,6 +42,7 @@
#:sqlite-version ; Defined as constant.
#:sqlite-encoding ; Defined as constant.
#:sqlite-last-insert-rowid
+ #:sqlite-changes
;;; Utility functions.
#:make-null-row
@@ -185,6 +186,12 @@
((db sqlite-db))
:returning :int)
+(declaim (inline sqlite-changes))
+(def-sqlite-function
+ "sqlite_changes"
+ ((db sqlite-db))
+ :returning :int)
+
(declaim (inline %get-table))
(def-sqlite-function
("sqlite_get_table" %get-table)
diff -Nru clsql-3.7.7.ORIG/db-sqlite/sqlite-sql.lisp clsql-3.7.7/db-sqlite/sqlite-sql.lisp
--- clsql-3.7.7.ORIG/db-sqlite/sqlite-sql.lisp 2005-01-23 06:28:03.000000000 +0100
+++ clsql-3.7.7/db-sqlite/sqlite-sql.lisp 2006-10-22 18:12:07.000000000 +0200
@@ -72,7 +72,7 @@
:expression sql-expression
:error-id (sqlite:sqlite-error-code err)
:message (sqlite:sqlite-error-message err))))
- t)
+ (sqlite:sqlite-changes (sqlite-db database)))
(defstruct sqlite-result-set
(vm (sqlite:make-null-vm)
diff -Nru clsql-3.7.7.ORIG/db-sqlite3/sqlite3-api.lisp clsql-3.7.7/db-sqlite3/sqlite3-api.lisp
--- clsql-3.7.7.ORIG/db-sqlite3/sqlite3-api.lisp 2004-12-07 07:11:55.000000000 +0100
+++ clsql-3.7.7/db-sqlite3/sqlite3-api.lisp 2006-10-22 17:01:35.000000000 +0200
@@ -33,6 +33,7 @@
#:sqlite3-prepare
#:sqlite3-step
#:sqlite3-finalize
+ #:sqlite3-changes
#:sqlite3-column-count
#:sqlite3-column-name
@@ -245,6 +246,12 @@
((stmt sqlite3-stmt))
:returning :int)
+(declaim (inline sqlite3-changes))
+(def-sqlite3-function
+ "sqlite3_changes"
+ ((db sqlite3-db))
+ :returning :int)
+
(declaim (inline sqlite3-column-count))
(def-sqlite3-function
"sqlite3_column_count"
diff -Nru clsql-3.7.7.ORIG/db-sqlite3/sqlite3-sql.lisp clsql-3.7.7/db-sqlite3/sqlite3-sql.lisp
--- clsql-3.7.7.ORIG/db-sqlite3/sqlite3-sql.lisp 2005-01-23 06:28:03.000000000 +0100
+++ clsql-3.7.7/db-sqlite3/sqlite3-sql.lisp 2006-10-22 17:09:16.000000000 +0200
@@ -79,7 +79,7 @@
:expression sql-expression
:error-id (sqlite3:sqlite3-error-code err)
:message (sqlite3:sqlite3-error-message err))))
- t)
+ (sqlite3:sqlite3-changes (sqlite3-db database)))
(defstruct sqlite3-result-set
(stmt sqlite3:null-stmt
diff -Nru clsql-3.7.7.ORIG/doc/ref-fdml.xml clsql-3.7.7/doc/ref-fdml.xml
--- clsql-3.7.7.ORIG/doc/ref-fdml.xml 2006-08-30 13:08:18.000000000 +0200
+++ clsql-3.7.7/doc/ref-fdml.xml 2006-10-23 00:24:40.423911716 +0200
@@ -232,7 +232,7 @@
<refsect1>
<title>Syntax</title>
<synopsis>
- <function>insert-records</function> &key <replaceable>into</replaceable> <replaceable>attributes</replaceable> <replaceable>values</replaceable> <replaceable>av-pairs</replaceable> <replaceable>query</replaceable> <replaceable>database</replaceable> => <returnvalue></returnvalue></synopsis>
+ <function>insert-records</function> &key <replaceable>into</replaceable> <replaceable>attributes</replaceable> <replaceable>values</replaceable> <replaceable>av-pairs</replaceable> <replaceable>query</replaceable> <replaceable>database</replaceable> => <returnvalue>rowcount</returnvalue></synopsis>
</refsect1>
<refsect1>
<title>Arguments and Values</title>
@@ -287,7 +287,13 @@
object</glossterm>. This will default to the value
of <symbol>*default-database*</symbol>.</para>
</listitem>
- </varlistentry>
+ </varlistentry>
+ <varlistentry>
+ <term><returnvalue>rowcount</returnvalue></term>
+ <listitem>
+ <para>The number of rows inserted.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
<refsect1>
@@ -339,7 +345,7 @@
ecompanyid managerid)
:values '(11 1 "Yuri" "Gagarin" "[email protected]"
1 1))
-=>
+=> 1
(select [first-name] [last-name] [email]
:from [employee]
:where [= [emplid] 11]
@@ -395,7 +401,7 @@
<refsect1>
<title>Syntax</title>
<synopsis>
- <function>update-records</function> <replaceable>table</replaceable> &key <replaceable>attributes</replaceable> <replaceable>values</replaceable> <replaceable>av-pairs</replaceable> <replaceable>where</replaceable> <replaceable>database</replaceable> => <returnvalue></returnvalue></synopsis>
+ <function>update-records</function> <replaceable>table</replaceable> &key <replaceable>attributes</replaceable> <replaceable>values</replaceable> <replaceable>av-pairs</replaceable> <replaceable>where</replaceable> <replaceable>database</replaceable> => <returnvalue>rowcount</returnvalue></synopsis>
</refsect1>
<refsect1>
<title>Arguments and Values</title>
@@ -450,7 +456,13 @@
object</glossterm>. This will default to the value
of <symbol>*default-database*</symbol>.</para>
</listitem>
- </varlistentry>
+ </varlistentry>
+ <varlistentry>
+ <term><returnvalue>rowcount</returnvalue></term>
+ <listitem>
+ <para>The number of rows updated.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
<refsect1>
@@ -492,7 +504,7 @@
(last_name "Gagarin")
(email "[email protected]"))
:where [= [emplid] 1])
-=>
+=> 1
(select [first-name] [last-name] [email]
:from [employee]
:where [= [emplid] 1]
@@ -555,7 +567,7 @@
<refsect1>
<title>Syntax</title>
<synopsis>
- <function>delete-records</function> &key <replaceable>from</replaceable> <replaceable>where</replaceable> <replaceable>database</replaceable> => <returnvalue></returnvalue></synopsis>
+ <function>delete-records</function> &key <replaceable>from</replaceable> <replaceable>where</replaceable> <replaceable>database</replaceable> => <returnvalue>rowcount</returnvalue></synopsis>
</refsect1>
<refsect1>
<title>Arguments and Values</title>
@@ -587,6 +599,12 @@
of <symbol>*default-database*</symbol>.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><returnvalue>rowcount</returnvalue></term>
+ <listitem>
+ <para>The number of rows deleted.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
<refsect1>
@@ -607,7 +625,7 @@
:field-names nil)
=> (("Yuri" "Gagarin" "[email protected]"))
(delete-records :from [employee] :where [= [emplid] 11])
-=>
+=> 1
(select [first-name] [last-name] [email]
:from [employee]
:where [= [emplid] 11]
@@ -669,7 +687,7 @@
<refsect1>
<title>Syntax</title>
<synopsis>
- <function>execute-command</function> <replaceable>sql-expression</replaceable> &key <replaceable>database</replaceable> => <returnvalue></returnvalue></synopsis>
+ <function>execute-command</function> <replaceable>sql-expression</replaceable> &key <replaceable>database</replaceable> => <returnvalue>rowcount</returnvalue></synopsis>
</refsect1>
<refsect1>
<title>Arguments and Values</title>
@@ -690,6 +708,12 @@
object</glossterm>. This will default to the value
of <symbol>*default-database*</symbol>.</para>
</listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><returnvalue>rowcount</returnvalue></term>
+ <listitem>
+ <para>The number of rows affected by the query.</para>
+ </listitem>
</varlistentry></variablelist>
</refsect1>
<refsect1>
diff -Nru clsql-3.7.7.ORIG/sql/fddl.lisp clsql-3.7.7/sql/fddl.lisp
--- clsql-3.7.7.ORIG/sql/fddl.lisp 2005-11-26 17:08:12.000000000 +0100
+++ clsql-3.7.7/sql/fddl.lisp 2006-10-23 00:28:20.282001885 +0200
@@ -76,7 +76,8 @@
:columns description
:modifiers constraints
:transactions transactions)))
- (execute-command stmt :database database)))
+ (execute-command stmt :database database)
+ (values)))
(defun drop-table (name &key (if-does-not-exist :error)
(database *default-database*)
@@ -103,7 +104,8 @@
(symbol-name '#:clsql-oracle)))))
(setq expr (concatenate 'string expr " PURGE")))
- (execute-command expr :database database))))
+ (execute-command expr :database database)
+ (values))))
(defun list-tables (&key (owner nil) (database *default-database*))
"Returns a list of strings representing table names in DATABASE
@@ -145,7 +147,8 @@
:column-list column-list
:query as
:with-check-option with-check-option)))
- (execute-command stmt :database database)))
+ (execute-command stmt :database database)
+ (values)))
(defun drop-view (name &key (if-does-not-exist :error)
(database *default-database*))
@@ -161,7 +164,8 @@
(:error
t))
(let ((expr (concatenate 'string "DROP VIEW " view-name)))
- (execute-command expr :database database))))
+ (execute-command expr :database database)
+ (values))))
(defun list-views (&key (owner nil) (database *default-database*))
"Returns a list of strings representing view names in DATABASE
@@ -199,7 +203,8 @@
(stmt (format nil "CREATE ~A INDEX ~A ON ~A (~{~A~^, ~})"
(if unique "UNIQUE" "")
index-name table-name attributes)))
- (execute-command stmt :database database)))
+ (execute-command stmt :database database)
+ (values)))
(defun drop-index (name &key (if-does-not-exist :error)
(on nil)
@@ -223,7 +228,8 @@
(format nil "~A ON ~A" index-name (database-identifier on database)))
(t index-name))))
(execute-command (format nil "DROP INDEX ~A" index-identifier)
- :database database))))
+ :database database)
+ (values))))
(defun list-indexes (&key (owner nil) (database *default-database*) (on nil))
"Returns a list of strings representing index names in DATABASE
diff -Nru clsql-3.7.7.ORIG/sql/fdml.lisp clsql-3.7.7/sql/fdml.lisp
--- clsql-3.7.7.ORIG/sql/fdml.lisp 2004-10-19 20:40:27.000000000 +0200
+++ clsql-3.7.7/sql/fdml.lisp 2006-10-18 16:38:40.000000000 +0200
@@ -25,13 +25,12 @@
&key (database *default-database*))
(record-sql-command sql-expression database)
(let ((res (database-execute-command sql-expression database)))
- (record-sql-result res database))
- (values))
+ (record-sql-result res database)
+ res))
(defmethod execute-command ((expr %sql-expression)
&key (database *default-database*))
- (execute-command (sql-output expr database) :database database)
- (values))
+ (execute-command (sql-output expr database) :database database))
(defmethod query ((query-expression string) &key (database *default-database*)
(result-types :auto) (flatp nil) (field-names t))
diff -Nru clsql-3.7.7.ORIG/sql/utils.lisp clsql-3.7.7/sql/utils.lisp
--- clsql-3.7.7.ORIG/sql/utils.lisp 2005-11-11 17:26:08.000000000 +0100
+++ clsql-3.7.7/sql/utils.lisp 2006-10-22 18:07:04.000000000 +0200
@@ -27,28 +27,41 @@
(number
(float-to-sql-string num))))
+#+sbcl
+(defun fixup-empty-fraction (str)
+ (let ((comma (position #\. str)))
+ (if (and comma (< comma (1- (length str)))
+ (char= (char str (1+ comma)) #\e))
+ (concatenate 'string (subseq str 0 comma)
+ ".0" (subseq str (1+ comma)))
+ str)))
+
+#-sbcl
+(defun fixup-empty-fraction (str) str)
+
(defun float-to-sql-string (num)
"Convert exponent character for SQL"
(let ((str (write-to-string num :readably t)))
- (cond
- ((find #\f str)
- (substitute #\e #\f str))
- ((find #\d str)
- (substitute #\e #\d str))
- ((find #\l str)
- (substitute #\e #\l str))
- ((find #\s str)
- (substitute #\e #\S str))
- ((find #\F str)
- (substitute #\e #\F str))
- ((find #\D str)
- (substitute #\e #\D str))
- ((find #\L str)
- (substitute #\e #\L str))
- ((find #\S str)
- (substitute #\e #\S str))
- (t
- str))))
+ (fixup-empty-fraction
+ (cond
+ ((find #\f str)
+ (substitute #\e #\f str))
+ ((find #\d str)
+ (substitute #\e #\d str))
+ ((find #\l str)
+ (substitute #\e #\l str))
+ ((find #\s str)
+ (substitute #\e #\S str))
+ ((find #\F str)
+ (substitute #\e #\F str))
+ ((find #\D str)
+ (substitute #\e #\D str))
+ ((find #\L str)
+ (substitute #\e #\L str))
+ ((find #\S str)
+ (substitute #\e #\S str))
+ (t
+ str)))))
(defun sql-escape (identifier)
"Change hyphens to underscores, ensure string"
diff -Nru clsql-3.7.7.ORIG/tests/test-fdml.lisp clsql-3.7.7/tests/test-fdml.lisp
--- clsql-3.7.7.ORIG/tests/test-fdml.lisp 2006-05-03 16:39:10.000000000 +0200
+++ clsql-3.7.7/tests/test-fdml.lisp 2006-10-21 17:55:07.000000000 +0200
@@ -634,12 +634,10 @@
;; check status
(push (clsql:in-transaction-p) results)
;; update records
- (push
- (clsql:with-transaction ()
- (clsql:update-records [employee]
+ (clsql:with-transaction ()
+ (clsql:update-records [employee]
:av-pairs '((email "[email protected]"))
:where [= [emplid] 1]))
- results)
;; check status
(push (clsql:in-transaction-p) results)
;; check that was committed
@@ -647,12 +645,10 @@
:flatp t)
results)
;; undo the changes
- (push
- (clsql:with-transaction ()
- (clsql:update-records [employee]
+ (clsql:with-transaction ()
+ (clsql:update-records [employee]
:av-pairs '((email "[email protected]"))
:where [= [emplid] 1]))
- results)
;; and check status
(push (clsql:in-transaction-p) results)
;; check that was committed
@@ -660,7 +656,7 @@
:flatp t)
results)
(apply #'values (nreverse results)))
- nil nil nil ("[email protected]") nil nil ("[email protected]"))
+ nil nil ("[email protected]") nil ("[email protected]"))
;; runs a valid update and an invalid one within a transaction and checks
;; that the valid update is rolled back when the invalid one fails.
@@ -690,6 +686,24 @@
(apply #'values (nreverse results))))))
nil nil ("[email protected]"))
+;; creates a table with test data, runs update and delete queries and checks
+;; if the number of affected rows is returned correctly
+(deftest :fdml/rowcount/1
+ (values
+ (progn
+ (clsql:create-table [rowtest] '(([x] integer) ([y] integer)))
+ (loop for x upfrom 0 below 10
+ sum (let ((r (clsql:insert-records
+ :into [rowtest]
+ :av-pairs `((x ,x) (y ,(1+ (random 10)))))))
+ (if (numberp r) r 0))))
+ (clsql:update-records [rowtest] :av-pairs '((y 0))
+ :where [< [x] 5])
+ (prog1
+ (clsql:delete-records :from [rowtest] :where [= [y] 0])
+ (clsql:drop-table [rowtest] :if-does-not-exist :ignore)))
+ 10 5 5)
+
))
diff -Nru clsql-3.7.7.ORIG/tests/test-init.lisp clsql-3.7.7/tests/test-init.lisp
--- clsql-3.7.7.ORIG/tests/test-init.lisp 2006-05-03 16:39:10.000000000 +0200
+++ clsql-3.7.7/tests/test-init.lisp 2006-10-23 00:07:33.173204957 +0200
@@ -627,6 +627,9 @@
'(:postgresql :mysql :sqlite3)))
(clsql-sys:in test :fdml/select/37 :fdml/select/38))
(push (cons test "LIMIT keyword not supported in SELECT") skip-tests))
+ ((and (eq *test-database-type* :aodbc)
+ (clsql-sys:in test :fdml/rowcount/1))
+ (push (cons test "number of affected rows not available in aodbc") skip-tests))
(t
(push test-form test-forms)))))
(values (nreverse test-forms) (nreverse skip-tests))))
--liOOAslEiF7prFVr
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
_______________________________________________
CLSQL-Devel mailing list
[email protected]
http://lists.b9.com/mailman/listinfo/clsql-devel
--liOOAslEiF7prFVr--