August 8, 2010 (Modified August 8, 2010, August 10, 2010)
I am not much of a supporter of True or False type questions, nor do I much care for multiple choice type questions. It seems that essay questions are usually the only appropriate type of questions on exams. Take, for example the following question that might appear on a SQL exam:
Three, and only three, user sessions are connected to the database. Session 1 creates 2 tables using the following commands:
DROP TABLE T1 PURGE; DROP TABLE T2 PURGE; CREATE TABLE T1(COL1 NUMBER); CREATE TABLE T2(COL1 NUMBER);These are the only tables in the database named T1 and T2, as shown by the following:
SELECT TABLE_NAME FROM DBA_TABLES WHERE TABLE_NAME IN ('T1','T2'); TABLE_NAME ------------------------------ T1 T2The following operations are performed in order:
In Session 1:
INSERT INTO T1 SELECT ROWNUM FROM DUAL CONNECT BY LEVEL<=100; COMMIT; INSERT INTO T1 SELECT ROWNUM FROM DUAL CONNECT BY LEVEL<=100;In Session 2:
INSERT INTO T1 SELECT ROWNUM FROM DUAL CONNECT BY LEVEL<=100; INSERT INTO T2 SELECT * FROM T1; COMMIT; INSERT INTO T2 SELECT * FROM T2;In Session 3:
SELECT COUNT(*) FROM T2;
What value is displayed when session 3 executes its query against table T2?
a. 600
b. 200
c. 100
d. 0
e. All of the above
f. None of the above
After you answer the multiple choice question, explain why your answer is correct.
————–
Edit roughly 4.5 hours after the initial publishing of this blog article: 7 people, including the first person to comment (Sean) saw a value of 1600 for answer a. – that value was modified within minutes of the first comment appearing in this article to the value 600. The number 1600 was either a typo or an answer that I thought no one would ever select. For fun, let’s add the following to the list of possible answers for the question… could it be the correct answer?:
a2. 1600
—————-
Edit August 10, 2010: The Test Answers:
As I stated a couple of days ago, I intend to reveal the correct answer to the question posed by this blog article. As mentioned by Gary, just because a person (or certification board) designs a test question, that by default does not mean that the person knows the correct answer to the question.
Within roughly 15 minutes of this blog post appearing on Sunday Sean provided a very good answer with strong justification. If I only had 60 seconds to answer the question, I would hope to be able to work out the same solution. That said, the WordPress blog article category for this article is “Quiz – Whose Answer is it Anyway?” and answers E and F seem to be a bit of a clue that something is a bit strange about this question. I had more than 60 seconds to think about the answer, so I will pick one of the other answers.
I suspect that several readers were a bit suspicious about the question in this blog article that just might appear on some sort of standardized test (I designed the question, so I hope that it does not). Gary, Joel Garry, and Martin Berger stepped up to the challenge and offered suggestions regarding how there *might* be more than one answer to the provided question. As you might guess, the numbers selected for the answers are not random. I thought that I would share with you the thoughts that I had when putting together the potential test answers.
——
b. 200 – assumes that we are using SQL*Plus or some other tool that does not auto-commit after every DML call, have not modified the environment of the tool, and do not closed the tool after the script for each session was executed – SQL*Plus commits by default when exiting. This is probably the right answer.
——
a. 600 – assumes that we are using a tool that auto-commits after every DML statement by default, or possibly using a database (other than Oracle) that auto-commits after every DML statement. This could be a Visual Basic (or VBScript) program using ODBC or OLEDB (I might have stated ADO before) that does not start a transaction with a controllable end-point unless the BeginTrans method of the connection object is called first – or it could just as easily be a Java program using JDBC that does not call the setAutoCommit method of the connection object with a parameter of false. Gary mentioned another possibility.
——
d. 0 – the question states that there are three and exactly three sessions connected to the database, so it probably is not reasonable to connect or disconnect from the database. This is an instance where a pre-existing condition in one of the sessions might cause problems. For example, assume that these three sessions had been connected for a while and one of the previous questions in the test asked about the serializable isolation level. So, at some point in session 3 the command ALTER SESSION SET ISOLATION_LEVEL=SERIALIZABLE; was executed and a transaction started – maybe something as simple as: SELECT * FROM TABLE2 WHERE 0=1 FOR UPDATE; With this isolation level, once a transaction begins, the answers to all SQL statements are as of the start time for the transaction. Session 3 will therefore see a count of 0 for table T2.
Another possibility is that either session 2 or (mutually exclusive) session 3 is connected as a different user. The problem is that the test showed that there is only one table T1 and one table T2 in the database. Suppose that one of the earlier test questions asked about synonyms and a table named TABLE2 was created in session 1 with a public synonym name of T2. If session 2 is connected as a different user, it will actually insert into TABLE2 when attempting to insert into table T2, and session 3 will report the number of rows in the real table T2. Note that the output does not show whether or not the inserts were successful, so it is possible that session 2 could not resolve the name T2 and returned an error. If session 3 is connected as a different user, it will report the number of rows in table TABLE2, rather than T2.
Another possibility is that either session 2 or session 3 is connected as a different user and a public synonym points to a view created on table T2 that includes a WHERE clause of COL1=0. Note that the output does not show whether or not the inserts were successful, so the view could have been created WITH CHECK OPTION.
——
c. 100 – the easiest way to obtain this value is if session 2 had at some point issued the command ALTER SESSION SET ISOLATION_LEVEL=SERIALIZABLE; and already had an in-process transaction (maybe SELECT * FROM TABLE2 WHERE 0=1 FOR UPDATE;).
A second possibility of obtaining a value of 100 is if at some point in the past session 1 executed the following commands:
CREATE OR REPLACE VIEW VIEW_T2 AS SELECT * FROM T2 WHERE COL1<=50; GRANT ALL ON VIEW_T2 TO PUBLIC; CREATE PUBLIC SYNONYM T1 FOR T1; CREATE PUBLIC SYNONYM T2 FOR VIEW_T2;
In this case, sessions 2 and 3 are logged in as the same user, which is different from session 1.
——
a2. 1600 – you might be able to guess how I obtained this number. I performed the test several times, obtaining different results each time, not realizing that session 1 could not drop tables T1 and T2 because session 2 had an active transaction referencing one or both of those tables (maybe you could not guess how I obtained that number).
How else might you obtain a value of 1600? Assume that at some point in the past session 1 executed the following commands:
CREATE TABLE TABLE2(COL1 NUMBER); GRANT ALL ON TABLE2 TO PUBLIC; CREATE PUBLIC SYNONYM T1 FOR T1; CREATE PUBLIC SYNONYM T2 FOR TABLE2; CREATE OR REPLACE TRIGGER T2_POPULATE AFTER INSERT ON TABLE2 BEGIN DELETE FROM T2; INSERT INTO T2 SELECT ROWNUM FROM DUAL CONNECT BY LEVEL<=1600; END; /
Now every time a transaction inserts into table TABLE2 all rows will be deleted from table T2 and 1600 new rows will be inserted into table T2. Session 1 and session 3 are connected to the database as the same user. Session 2 is connected to the database as a different user.
——
e. All of the Above – all answers were obtained without violating what the question asked, so I guess that e is the correct answer.
——
f. None of the Above – the count of table T2 will produce a single resulting value, not 5 values, and the value returned could very well be something other than the 5 values listed, depending on the prior state of the database.
————————
How else might you obtain the different potential answers listed for the test question?
b.
because:
session 1 inserts into t1 100 rows, commits, inserts into t1 100 more, transactions still open
session 2 inserts into t1 100 rows, inserts into t2 its own 100 rows in t1 plus 100 committed rows by session 1 (committed read), then commits, inserts into t2 200 rows, transaction still open
session 3 counts 200 committed rows in t2.
I am assuming that inserted rows row locks will not be escalated to exclusive locks on t1,t2. I reserve the right to be wrong 🙂
Sean,
That was a very quick answer. I will wait a couple of days before stating which answer is correct (I must have the correct answer if I designed the test question, right?).
This question seems to be very specific, yet ask yourself what the question does not state.
Steven Feuerstein has been facing these with his PL/SQL Challenge. There’s a bunch of assumptions that go with it (no hidden triggers etc), and he still has to do the odd ‘fix’. And no, designing the question doesn’t always mean you know the answer.
IF you have done a SET AUTOCOMMIT ON in SQL*Plus then session 1 would have inserted 200 row into T1, followed by session 2 inserting another hundred. Those three hundred would have been copied to T2 twice, giving answer (A).
In 11gR2, with deferred segment creation, you can have the strange situation where you can successfully create a table (in your default tablespace) but not have any quota on it. In which case any attempt to insert will fail (when it tries to create the segment). So case (D) can be the answer. Oh, and in 8.0 (maybe 8i) the CONNECT BY LEVEL didn’t work, so that would give (D) as well
Haven’t worked out how to get (C) yet without some sort of constraint.
You can probably cheat with your own version
d. 0
b. 200
I noticed that if I didn’t have all the sessions going at the start of the test, it was 0. If I did, it was 200. But the question seems to say all three sessions would be going at the beginning of the test.
Oooooohhh… Statement level transaction consistency, each session sees the db as of the start of its own transaction. So that can depend on other unstated factors… like what-all you have in a global or local sql login file.
I would have answered b had I not tried it. But I still have a nagging feeling I’m forgetting about something else.
Charles, what’s your goal with this quiz? Do you want to show how useless are multiple choice questions if not all possible parameters are defined? Or do you want to get as many possible methods to create any of these numbers by additional setups not mentioned in your quiz?
I managed to get the numbers 0, 200 and 600 by playing around with the startup time of the sessions and ‘alter session set isolation_level=serializable;’. If you consider this statement directly applied before does not match the quiz rules, I’d could make it with a login.sql file or a login trigger. These should match the rules.
Other methods whould be different users (the quiz only talks about sessions) and views/synonyms which can make any result possible.
As next idea I’d like to play with FGAC. This could make even more possibilities!
Martin,
Good question regarding the goal of this quiz. Several years ago I was state certified to teach mathematics and computer science classes. One of the interesting parts of teaching is discovering how an answer other than the “right” answer was obtained by the people taking a particular test – more or less by reviewing the work of the person answering the questions it is quite possible to discover that more than one answer is correct.
I read Cary Millsap’s recent blog articles about showing one’s work (the intermediate steps to the answer), heard about some of the difficulties with the PL/SQL Challenge mentioned by Gary, and I am currently reading Tom Kyte’s latest book – those were the inspirations for this blog article. I thought that this quiz would offer a chance to stop, think, and understand how the different answers might be obtained – that 100 number is hard to obtain.
All 3 of the sessions were connected at the start of the test script. What are some of the items that we do not know (that were not already mentioned):
* Were the sessions connected using SQL*Plus or something like JDBC, ODBC, or ADO that automatically commits by default
* Were the sessions, if SQL*Plus was used, closed after executing each set of commands
* You and Joel mentioned serializable – do we know if the sessions were just connected to the database, or could it be possible that the sessions had just completed another experiment that left an in-process transaction in place.
* What do we know about the pre-existing database – there might be views and public synonyms hanging around.
———————
Edit:
The original comment was posted using a netbook with a very small screen – I must have over-looked some of Martin’s response when typing the last bullet point. The question is more or less how is it possible to obtain different answers for this particular multiple choice question. Martin, I would like to see the answers using FGA and the other solutions that you develop.
If I had just 60 seconds to answer the question, I would try to produce an answer as clearly written as the first comment. If I had more than 60 seconds to answer the question I would consider picking one of the other answers…
What if… this is not an Oracle database? The CONNECT BY LEVEL syntax is probably Oracle Database specific, but what if it were not (or an equivalent syntax were used) and the database was actually SQL Server, Informix, or Sybase that commit by default after every DML statement (reference page 284 in Tom Kyte’s latest book).
> What if… this is not an Oracle database?
Yeah, that was the first ‘gotcha’ I thought about… but the select from DBA_TABLES killed that idea even before I got to connect by
Cool question, though.
Charles,
sorry for the late reply.
I found some minutes to develop the FCA method:
assume (for whatever reason) this package and trigger exists in the sandbox-DB where you are running the tests:
with this approach you can do nearly anything. With some more logic and a status table you can even give all answers in a row, for each repeated test run. 😉
something went wrong during copy/paste.
there is a problem with ‘return rownum “less than” 101’.
here the correct code: (executed as sys before the rest of the testcase)
Martin,
Thank you for providing the code again – I was a little confused by the code (in your previous reply) that remained after WordPress deleted the code between the less than and greater than signs. This feature has damaged my comments a couple of times also. “& l t ;” without spaces will be understood as a less than sign, while “& g t ;” without spaces will be understood as greater than.
That code is impressive. If someone were to create the trigger with the rest of your code, every new table will automatically adopt the security policy – that is something that certainly could be forgotten.
Here is a demonstration of the fine-grain access control code in action:
A bonus side-effect is that the optimizer mode might change to FIRST_ROWS_100 when the SQL statement is excuted by a normal user, but remain unaffected when run by SYS. The optimizer mode did not change on 11.2.0.1, but that does not mean the same results will be achieved in older release versions.
—
I know that this was just a quick demo, but why can’t I create a new user now? 🙂
Continuing immediately after creating the trigger:
There is a way to check the type of object that is being created, so it would be possible to make certain that the policy is only automatically applied to tables that are being created. For those who decide to try this experiment, please make certain that you drop the trigger after the experiment ends.
I have updated the blog article to include the reasons why the potential answers were selected. I am impressed with the different answers that were provided, and the justifications for those answers. There were definitely some justifications that I had not considered.
How else might you obtain the “wrong” answers for the test question?